Compare commits
37 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| fd3cc596c7 | |||
| 708f9cdfbd | |||
| c3e78d2c32 | |||
| 1b8e71ca1f | |||
| 3e497e3712 | |||
| 030da4b6b9 | |||
| 3a437d4891 | |||
| 8c2f26718e | |||
| 076596cd3b | |||
| f541cd59ed | |||
| 4b6891d6d5 | |||
| 4746fc682d | |||
| f6dbc3e78a | |||
| 03d79a2c39 | |||
| 349a300213 | |||
| dfc5d76108 | |||
| 77a29fc2f8 | |||
| a588808270 | |||
| b9b1cdf75f | |||
| 2ac5fe894c | |||
| fbb9429910 | |||
| cb359e8064 | |||
| a9bec7945e | |||
| 505cbd2591 | |||
| 25fddf3199 | |||
| 166df2ce1d | |||
| 755395faf8 | |||
| b958fe7a54 | |||
| cc133ea250 | |||
| 4ea4c0570f | |||
| a665e6b822 | |||
| 2de91e4862 | |||
| 3a11820a41 | |||
| 0e3fcb589b | |||
| e6596a0a1d | |||
| a915ec8e05 | |||
| 9b641c9bfd |
+16
-3
@@ -37,15 +37,19 @@ commands:
|
|||||||
parameters:
|
parameters:
|
||||||
GOOS:
|
GOOS:
|
||||||
type: string
|
type: string
|
||||||
|
GOARCH:
|
||||||
|
default: "amd64"
|
||||||
|
type: string
|
||||||
steps:
|
steps:
|
||||||
- checkout
|
- checkout
|
||||||
- run: GOOS=<< parameters.GOOS >> go build -ldflags="-s -w -X github.com/hashicorp/packer/version.GitCommit=${CIRCLE_SHA1}" -o ./pkg/packer_<< parameters.GOOS >>_$(go env GOARCH) .
|
- run: GOOS=<< parameters.GOOS >> GOARCH=<<parameters.GOARCH>> go build -ldflags="-s -w -X github.com/hashicorp/packer/version.GitCommit=${CIRCLE_SHA1}" -o ./pkg/packer_<< parameters.GOOS >>_<< parameters.GOARCH >> .
|
||||||
- run: zip ./pkg/packer_<< parameters.GOOS >>_$(go env GOARCH).zip ./pkg/packer_<< parameters.GOOS >>_$(go env GOARCH)
|
- run: zip ./pkg/packer_<< parameters.GOOS >>_<< parameters.GOARCH >>.zip ./pkg/packer_<< parameters.GOOS >>_<< parameters.GOARCH >>
|
||||||
- run: rm ./pkg/packer_<< parameters.GOOS >>_$(go env GOARCH)
|
- run: rm ./pkg/packer_<< parameters.GOOS >>_<< parameters.GOARCH >>
|
||||||
- persist_to_workspace:
|
- persist_to_workspace:
|
||||||
root: .
|
root: .
|
||||||
paths:
|
paths:
|
||||||
- ./pkg/
|
- ./pkg/
|
||||||
|
|
||||||
# Golang CircleCI 2.0 configuration file
|
# Golang CircleCI 2.0 configuration file
|
||||||
#
|
#
|
||||||
# Check https://circleci.com/docs/2.0/language-go/ for more details
|
# Check https://circleci.com/docs/2.0/language-go/ for more details
|
||||||
@@ -123,6 +127,13 @@ jobs:
|
|||||||
steps:
|
steps:
|
||||||
- build-and-persist-packer-binary:
|
- build-and-persist-packer-binary:
|
||||||
GOOS: darwin
|
GOOS: darwin
|
||||||
|
build_darwin_arm64:
|
||||||
|
executor: golang
|
||||||
|
working_directory: /go/src/github.com/hashicorp/packer
|
||||||
|
steps:
|
||||||
|
- build-and-persist-packer-binary:
|
||||||
|
GOOS: darwin
|
||||||
|
GOARCH: arm64
|
||||||
build_freebsd:
|
build_freebsd:
|
||||||
executor: golang
|
executor: golang
|
||||||
working_directory: /go/src/github.com/hashicorp/packer
|
working_directory: /go/src/github.com/hashicorp/packer
|
||||||
@@ -203,6 +214,7 @@ workflows:
|
|||||||
jobs:
|
jobs:
|
||||||
- build_linux
|
- build_linux
|
||||||
- build_darwin
|
- build_darwin
|
||||||
|
- build_darwin_arm64
|
||||||
- build_windows
|
- build_windows
|
||||||
- build_freebsd
|
- build_freebsd
|
||||||
- build_openbsd
|
- build_openbsd
|
||||||
@@ -211,6 +223,7 @@ workflows:
|
|||||||
requires:
|
requires:
|
||||||
- build_linux
|
- build_linux
|
||||||
- build_darwin
|
- build_darwin
|
||||||
|
- build_darwin_arm64
|
||||||
- build_windows
|
- build_windows
|
||||||
- build_freebsd
|
- build_freebsd
|
||||||
- build_openbsd
|
- build_openbsd
|
||||||
|
|||||||
+37
-5
@@ -1,21 +1,32 @@
|
|||||||
## 1.7.1 (Upcoming)
|
## 1.7.2 (Upcoming)
|
||||||
|
|
||||||
|
## 1.7.1 (March 31, 2021)
|
||||||
|
|
||||||
### NOTES:
|
### NOTES:
|
||||||
|
|
||||||
|
* builder/amazon: Has been vendored in this release and will no longer be
|
||||||
|
updated with Packer core. In Packer v1.8.0 the plugin will be removed
|
||||||
|
entirely. The `amazon` components will continue to work as expected until
|
||||||
|
then, but for the latest offerings of the Amazon plugin, users are
|
||||||
|
encourage to use the `packer init` command to install the latest release
|
||||||
|
version. For more details see [Installing Packer
|
||||||
|
Plugins](https://www.packer.io/docs/plugins#installing-plugins)
|
||||||
* builder/docker: Has been vendored in this release and will no longer be
|
* builder/docker: Has been vendored in this release and will no longer be
|
||||||
updated with Packer core. In Packer v1.8.0 the plugin will be removed
|
updated with Packer core. In Packer v1.8.0 the plugin will be removed
|
||||||
entirely. The `docker` builder will continue to work as expected until
|
entirely. The `docker` builder will continue to work as expected until
|
||||||
then, but for the latest offerings of the Docker plugin, users are
|
then, but for the latest offerings of the Docker plugin, users are
|
||||||
encourage to use the `packer init` command to install the latest release
|
encourage to use the `packer init` command to install the latest release
|
||||||
version. For more details see [Installing Packer
|
version. For more details see [Installing Packer
|
||||||
Plugins](https://www.packer.io/docs/plugins#installing- plugins)
|
Plugins](https://www.packer.io/docs/plugins#installing-plugins)
|
||||||
|
* darwin/arm64: Packer now includes the darwin/arm64 binary to its releases to
|
||||||
|
supports the new OSX M1. [GH-10804]
|
||||||
* post-processor/docker-\*: Have been vendored in this release and will no
|
* post-processor/docker-\*: Have been vendored in this release and will no
|
||||||
longer be updated with Packer core. In Packer v1.8.0 the plugin will be
|
longer be updated with Packer core. In Packer v1.8.0 the plugin will be
|
||||||
removed entirely. The `docker` builder will continue to work as expected
|
removed entirely. The `docker` builder will continue to work as expected
|
||||||
until then, but for the latest offerings of the Docker plugin, users are
|
until then, but for the latest offerings of the Docker plugin, users are
|
||||||
encourage to use the `packer init` command to install the latest release
|
encourage to use the `packer init` command to install the latest release
|
||||||
version. For more details see [Installing Packer
|
version. For more details see [Installing Packer
|
||||||
Plugins](https://www.packer.io/docs/plugins#installing- plugins)
|
Plugins](https://www.packer.io/docs/plugins#installing-plugins)
|
||||||
* post-processor/exoscale-import: Has been vendored in this release and will no
|
* post-processor/exoscale-import: Has been vendored in this release and will no
|
||||||
longer be updated with Packer core. In Packer v1.8.0 the plugin will be
|
longer be updated with Packer core. In Packer v1.8.0 the plugin will be
|
||||||
removed entirely. The `exoscale-import` post-processor will continue to
|
removed entirely. The `exoscale-import` post-processor will continue to
|
||||||
@@ -25,13 +36,17 @@
|
|||||||
Repostiroy](https://github.com/exoscale/packer-plugin-exoscale). [GH-10709]
|
Repostiroy](https://github.com/exoscale/packer-plugin-exoscale). [GH-10709]
|
||||||
|
|
||||||
### IMPROVEMENTS
|
### IMPROVEMENTS
|
||||||
* builder/amazon: allow creation of ebs snapshots wihtout volumes. [GH-9591]
|
* builder/amazon: allow creation of ebs snapshots without volumes. [GH-9591]
|
||||||
* builder/amazon: Fix issue for multi-region AMI build that fail when
|
* builder/amazon: Fix issue for multi-region AMI build that fail when
|
||||||
encrypting with KMS and sharing across accounts. [GH-10754]
|
encrypting with KMS and sharing across accounts. [GH-10754]
|
||||||
* builder/azure: Add client_cert_token_timeout option. [GH-10528]
|
* builder/azure: Add client_cert_token_timeout option. [GH-10528]
|
||||||
* builder/google: Make Windows password timeout configurable. [GH-10727]
|
* builder/google: Make Windows password timeout configurable. [GH-10727]
|
||||||
* builder/google: Update public GCP image project as gce-uefi-images are
|
* builder/google: Update public GCP image project as gce-uefi-images are
|
||||||
deprecated. [GH-10724]
|
deprecated. [GH-10724]
|
||||||
|
* builder/oracle-oci: Update Oracle Go SDK to add support for OCI flexible
|
||||||
|
shapes. [GH-10833]
|
||||||
|
* builder/proxmox: Allow using API tokens for Proxmox authentication.
|
||||||
|
[GH-10797]
|
||||||
* builder/qemu: Added firmware option. [GH-10683]
|
* builder/qemu: Added firmware option. [GH-10683]
|
||||||
* builder/scaleway: add support for timeout in shutdown step. [GH-10503]
|
* builder/scaleway: add support for timeout in shutdown step. [GH-10503]
|
||||||
* builder/vagrant: Fix logging to be clearer when Vagrant builder overrides
|
* builder/vagrant: Fix logging to be clearer when Vagrant builder overrides
|
||||||
@@ -47,11 +62,21 @@
|
|||||||
[GH-10651]
|
[GH-10651]
|
||||||
* command/fmt: Adding recursive flag to formatter to format subdirectories.
|
* command/fmt: Adding recursive flag to formatter to format subdirectories.
|
||||||
[GH-10457]
|
[GH-10457]
|
||||||
|
* core/hcl2: Add legacy_isotime function. [GH-10780]
|
||||||
|
* core/hcl2: Add support for generating `dynamic` blocks within a `build`
|
||||||
|
block. [GH-10825]
|
||||||
|
* core/hcl2: Add templatefile function. [GH-10776]
|
||||||
|
* core/hcl2_upgrade: hcl2_upgrade command can now upgrade json var-files.
|
||||||
|
[GH-10676]
|
||||||
|
* core/init: Add implicit required_plugin blocks feature. [GH-10732]
|
||||||
|
* core: Add http_content option to serve variables from HTTP at preseed.
|
||||||
|
[GH-10801]
|
||||||
* core: Change template parsing error to include warning about file extensions.
|
* core: Change template parsing error to include warning about file extensions.
|
||||||
[GH-10652]
|
[GH-10652]
|
||||||
* core: Update to gopsutil v3.21.1 to allow builds to work for darwin arm64.
|
* core: Update to gopsutil v3.21.1 to allow builds to work for darwin arm64.
|
||||||
[GH-10697]
|
[GH-10697]
|
||||||
* hcl2_upgrade: hcl2_upgrade command can now upgrade json var-files [GH-10676]
|
* provisioner/inspec: Allow non-zero exit codes for inspec provisioner.
|
||||||
|
[GH-10723]
|
||||||
|
|
||||||
### BUG FIXES
|
### BUG FIXES
|
||||||
* buider/azure: Update builder to ensure a proper clean up Azure temporary
|
* buider/azure: Update builder to ensure a proper clean up Azure temporary
|
||||||
@@ -68,6 +93,8 @@
|
|||||||
[GH-10748]
|
[GH-10748]
|
||||||
* builder/oracle-oci: Update Oracle Go SDK to fix issue with reading key file.
|
* builder/oracle-oci: Update Oracle Go SDK to fix issue with reading key file.
|
||||||
[GH-10560] [GH-10774]
|
[GH-10560] [GH-10774]
|
||||||
|
* builder/outscale: Fix omi_description that was ignored in Osc builder
|
||||||
|
[GH-10792]
|
||||||
* builder/parallels: Make Packer respect winrm_host flag in winrm connect func.
|
* builder/parallels: Make Packer respect winrm_host flag in winrm connect func.
|
||||||
[GH-10748]
|
[GH-10748]
|
||||||
* builder/proxmox: Fixes issue when using `additional_iso_files` in HCL enabled
|
* builder/proxmox: Fixes issue when using `additional_iso_files` in HCL enabled
|
||||||
@@ -78,11 +105,14 @@
|
|||||||
func. [GH-10748]
|
func. [GH-10748]
|
||||||
* builder/vmware: Added a fallback file check when trying to determine the
|
* builder/vmware: Added a fallback file check when trying to determine the
|
||||||
network-mapping configuration. [GH-10543]
|
network-mapping configuration. [GH-10543]
|
||||||
|
* builder/vsphere: Fix invalid device configuration issue when creating a
|
||||||
|
vm with multiple disk on the same controller. [GH-10844]
|
||||||
* builder/vsphere: Fix issue where boot command would fail the build do to a
|
* builder/vsphere: Fix issue where boot command would fail the build do to a
|
||||||
key typing error. This change will now retry to type the key on error
|
key typing error. This change will now retry to type the key on error
|
||||||
before giving up. [GH-10541]
|
before giving up. [GH-10541]
|
||||||
* core/hcl2_upgrade: Check for nil config map when provisioner/post-processor
|
* core/hcl2_upgrade: Check for nil config map when provisioner/post-processor
|
||||||
doesn't have config. [GH-10730]
|
doesn't have config. [GH-10730]
|
||||||
|
* core/hcl2_upgrade: Fix escaped quotes in template functions [GH-10794]
|
||||||
* core/hcl2_upgrade: Make hcl2_upgrade command correctly translate
|
* core/hcl2_upgrade: Make hcl2_upgrade command correctly translate
|
||||||
pause_before. [GH-10654]
|
pause_before. [GH-10654]
|
||||||
* core/hcl2_upgrade: Make json variables using template engines get stored as
|
* core/hcl2_upgrade: Make json variables using template engines get stored as
|
||||||
@@ -94,6 +124,8 @@
|
|||||||
* core: Pin Packer to Golang 1.16 to fix code generation issues. [GH-10702]
|
* core: Pin Packer to Golang 1.16 to fix code generation issues. [GH-10702]
|
||||||
* core: Templates previously could not interpolate the environment variable
|
* core: Templates previously could not interpolate the environment variable
|
||||||
PACKER_LOG_PATH. [GH-10660]
|
PACKER_LOG_PATH. [GH-10660]
|
||||||
|
* post-processor/vagrant-cloud: Override direct upload based on box size
|
||||||
|
[GH-10820]
|
||||||
* provisioner/chef-solo: HCL2 templates can support the json_string option.
|
* provisioner/chef-solo: HCL2 templates can support the json_string option.
|
||||||
[GH-10655]
|
[GH-10655]
|
||||||
* provisioner/inspec: Add new configuration field `valid_exit_codes` to allow
|
* provisioner/inspec: Add new configuration field `valid_exit_codes` to allow
|
||||||
|
|||||||
@@ -49,7 +49,7 @@ package:
|
|||||||
@sh -c "$(CURDIR)/scripts/dist.sh $(VERSION)"
|
@sh -c "$(CURDIR)/scripts/dist.sh $(VERSION)"
|
||||||
|
|
||||||
install-build-deps: ## Install dependencies for bin build
|
install-build-deps: ## Install dependencies for bin build
|
||||||
@go get github.com/mitchellh/gox
|
@go install github.com/mitchellh/gox@v1.0.1
|
||||||
|
|
||||||
install-gen-deps: ## Install dependencies for code generation
|
install-gen-deps: ## Install dependencies for code generation
|
||||||
# to avoid having to tidy our go deps, we `go get` our binaries from a temp
|
# to avoid having to tidy our go deps, we `go get` our binaries from a temp
|
||||||
|
|||||||
@@ -1,251 +0,0 @@
|
|||||||
package chroot
|
|
||||||
|
|
||||||
import (
|
|
||||||
"testing"
|
|
||||||
|
|
||||||
packersdk "github.com/hashicorp/packer-plugin-sdk/packer"
|
|
||||||
)
|
|
||||||
|
|
||||||
func testConfig() map[string]interface{} {
|
|
||||||
return map[string]interface{}{
|
|
||||||
"ami_name": "foo",
|
|
||||||
"source_ami": "foo",
|
|
||||||
"region": "us-east-1",
|
|
||||||
// region validation logic is checked in ami_config_test
|
|
||||||
"skip_region_validation": true,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestBuilder_ImplementsBuilder(t *testing.T) {
|
|
||||||
var raw interface{}
|
|
||||||
raw = &Builder{}
|
|
||||||
if _, ok := raw.(packersdk.Builder); !ok {
|
|
||||||
t.Fatalf("Builder should be a builder")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestBuilderPrepare_AMIName(t *testing.T) {
|
|
||||||
var b Builder
|
|
||||||
config := testConfig()
|
|
||||||
|
|
||||||
// Test good
|
|
||||||
config["ami_name"] = "foo"
|
|
||||||
config["skip_region_validation"] = true
|
|
||||||
_, warnings, err := b.Prepare(config)
|
|
||||||
if len(warnings) > 0 {
|
|
||||||
t.Fatalf("bad: %#v", warnings)
|
|
||||||
}
|
|
||||||
if err != nil {
|
|
||||||
t.Fatalf("should not have error: %s", err)
|
|
||||||
}
|
|
||||||
|
|
||||||
// Test bad
|
|
||||||
config["ami_name"] = "foo {{"
|
|
||||||
b = Builder{}
|
|
||||||
_, warnings, err = b.Prepare(config)
|
|
||||||
if len(warnings) > 0 {
|
|
||||||
t.Fatalf("bad: %#v", warnings)
|
|
||||||
}
|
|
||||||
if err == nil {
|
|
||||||
t.Fatal("should have error")
|
|
||||||
}
|
|
||||||
|
|
||||||
// Test bad
|
|
||||||
delete(config, "ami_name")
|
|
||||||
b = Builder{}
|
|
||||||
_, warnings, err = b.Prepare(config)
|
|
||||||
if len(warnings) > 0 {
|
|
||||||
t.Fatalf("bad: %#v", warnings)
|
|
||||||
}
|
|
||||||
if err == nil {
|
|
||||||
t.Fatal("should have error")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestBuilderPrepare_ChrootMounts(t *testing.T) {
|
|
||||||
b := &Builder{}
|
|
||||||
config := testConfig()
|
|
||||||
|
|
||||||
config["chroot_mounts"] = nil
|
|
||||||
_, warnings, err := b.Prepare(config)
|
|
||||||
if len(warnings) > 0 {
|
|
||||||
t.Fatalf("bad: %#v", warnings)
|
|
||||||
}
|
|
||||||
if err != nil {
|
|
||||||
t.Errorf("err: %s", err)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestBuilderPrepare_ChrootMountsBadDefaults(t *testing.T) {
|
|
||||||
b := &Builder{}
|
|
||||||
config := testConfig()
|
|
||||||
|
|
||||||
config["chroot_mounts"] = [][]string{
|
|
||||||
{"bad"},
|
|
||||||
}
|
|
||||||
_, warnings, err := b.Prepare(config)
|
|
||||||
if len(warnings) > 0 {
|
|
||||||
t.Fatalf("bad: %#v", warnings)
|
|
||||||
}
|
|
||||||
if err == nil {
|
|
||||||
t.Fatal("should have error")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
func TestBuilderPrepare_SourceAmi(t *testing.T) {
|
|
||||||
b := &Builder{}
|
|
||||||
config := testConfig()
|
|
||||||
|
|
||||||
config["source_ami"] = ""
|
|
||||||
_, warnings, err := b.Prepare(config)
|
|
||||||
if len(warnings) > 0 {
|
|
||||||
t.Fatalf("bad: %#v", warnings)
|
|
||||||
}
|
|
||||||
if err == nil {
|
|
||||||
t.Fatal("should have error")
|
|
||||||
}
|
|
||||||
|
|
||||||
config["source_ami"] = "foo"
|
|
||||||
_, warnings, err = b.Prepare(config)
|
|
||||||
if len(warnings) > 0 {
|
|
||||||
t.Fatalf("bad: %#v", warnings)
|
|
||||||
}
|
|
||||||
if err != nil {
|
|
||||||
t.Errorf("err: %s", err)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestBuilderPrepare_CommandWrapper(t *testing.T) {
|
|
||||||
b := &Builder{}
|
|
||||||
config := testConfig()
|
|
||||||
|
|
||||||
config["command_wrapper"] = "echo hi; {{.Command}}"
|
|
||||||
_, warnings, err := b.Prepare(config)
|
|
||||||
if len(warnings) > 0 {
|
|
||||||
t.Fatalf("bad: %#v", warnings)
|
|
||||||
}
|
|
||||||
if err != nil {
|
|
||||||
t.Errorf("err: %s", err)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestBuilderPrepare_CopyFiles(t *testing.T) {
|
|
||||||
b := &Builder{}
|
|
||||||
config := testConfig()
|
|
||||||
|
|
||||||
_, warnings, err := b.Prepare(config)
|
|
||||||
if len(warnings) > 0 {
|
|
||||||
t.Fatalf("bad: %#v", warnings)
|
|
||||||
}
|
|
||||||
if err != nil {
|
|
||||||
t.Errorf("err: %s", err)
|
|
||||||
}
|
|
||||||
|
|
||||||
if len(b.config.CopyFiles) != 1 && b.config.CopyFiles[0] != "/etc/resolv.conf" {
|
|
||||||
t.Errorf("Was expecting default value for copy_files.")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestBuilderPrepare_CopyFilesNoDefault(t *testing.T) {
|
|
||||||
b := &Builder{}
|
|
||||||
config := testConfig()
|
|
||||||
|
|
||||||
config["copy_files"] = []string{}
|
|
||||||
_, warnings, err := b.Prepare(config)
|
|
||||||
if len(warnings) > 0 {
|
|
||||||
t.Fatalf("bad: %#v", warnings)
|
|
||||||
}
|
|
||||||
if err != nil {
|
|
||||||
t.Errorf("err: %s", err)
|
|
||||||
}
|
|
||||||
|
|
||||||
if len(b.config.CopyFiles) > 0 {
|
|
||||||
t.Errorf("Was expecting no default value for copy_files. Found %v",
|
|
||||||
b.config.CopyFiles)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestBuilderPrepare_RootDeviceNameAndAMIMappings(t *testing.T) {
|
|
||||||
var b Builder
|
|
||||||
config := testConfig()
|
|
||||||
|
|
||||||
config["root_device_name"] = "/dev/sda"
|
|
||||||
config["ami_block_device_mappings"] = []interface{}{map[string]string{}}
|
|
||||||
config["root_volume_size"] = 15
|
|
||||||
_, warnings, err := b.Prepare(config)
|
|
||||||
if len(warnings) == 0 {
|
|
||||||
t.Fatal("Missing warning, stating block device mappings will be overwritten")
|
|
||||||
} else if len(warnings) > 1 {
|
|
||||||
t.Fatalf("excessive warnings: %#v", warnings)
|
|
||||||
}
|
|
||||||
if err != nil {
|
|
||||||
t.Fatalf("should not have error: %s", err)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestBuilderPrepare_AMIMappingsNoRootDeviceName(t *testing.T) {
|
|
||||||
var b Builder
|
|
||||||
config := testConfig()
|
|
||||||
|
|
||||||
config["ami_block_device_mappings"] = []interface{}{map[string]string{}}
|
|
||||||
_, warnings, err := b.Prepare(config)
|
|
||||||
if len(warnings) > 0 {
|
|
||||||
t.Fatalf("bad: %#v", warnings)
|
|
||||||
}
|
|
||||||
if err == nil {
|
|
||||||
t.Fatalf("should have error")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestBuilderPrepare_RootDeviceNameNoAMIMappings(t *testing.T) {
|
|
||||||
var b Builder
|
|
||||||
config := testConfig()
|
|
||||||
|
|
||||||
config["root_device_name"] = "/dev/sda"
|
|
||||||
_, warnings, err := b.Prepare(config)
|
|
||||||
if len(warnings) > 0 {
|
|
||||||
t.Fatalf("bad: %#v", warnings)
|
|
||||||
}
|
|
||||||
if err == nil {
|
|
||||||
t.Fatalf("should have error")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestBuilderPrepare_ReturnGeneratedData(t *testing.T) {
|
|
||||||
var b Builder
|
|
||||||
config := testConfig()
|
|
||||||
|
|
||||||
generatedData, warnings, err := b.Prepare(config)
|
|
||||||
if len(warnings) > 0 {
|
|
||||||
t.Fatalf("bad: %#v", warnings)
|
|
||||||
}
|
|
||||||
if err != nil {
|
|
||||||
t.Fatalf("should not have error: %s", err)
|
|
||||||
}
|
|
||||||
if len(generatedData) == 0 {
|
|
||||||
t.Fatalf("Generated data should not be empty")
|
|
||||||
}
|
|
||||||
if generatedData[0] != "SourceAMIName" {
|
|
||||||
t.Fatalf("Generated data should contain SourceAMIName")
|
|
||||||
}
|
|
||||||
if generatedData[1] != "BuildRegion" {
|
|
||||||
t.Fatalf("Generated data should contain BuildRegion")
|
|
||||||
}
|
|
||||||
if generatedData[2] != "SourceAMI" {
|
|
||||||
t.Fatalf("Generated data should contain SourceAMI")
|
|
||||||
}
|
|
||||||
if generatedData[3] != "SourceAMICreationDate" {
|
|
||||||
t.Fatalf("Generated data should contain SourceAMICreationDate")
|
|
||||||
}
|
|
||||||
if generatedData[4] != "SourceAMIOwner" {
|
|
||||||
t.Fatalf("Generated data should contain SourceAMIOwner")
|
|
||||||
}
|
|
||||||
if generatedData[5] != "SourceAMIOwnerName" {
|
|
||||||
t.Fatalf("Generated data should contain SourceAMIOwnerName")
|
|
||||||
}
|
|
||||||
if generatedData[6] != "Device" {
|
|
||||||
t.Fatalf("Generated data should contain Device")
|
|
||||||
}
|
|
||||||
if generatedData[7] != "MountPath" {
|
|
||||||
t.Fatalf("Generated data should contain MountPath")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,51 +0,0 @@
|
|||||||
package chroot
|
|
||||||
|
|
||||||
import (
|
|
||||||
"fmt"
|
|
||||||
"io/ioutil"
|
|
||||||
"os"
|
|
||||||
"runtime"
|
|
||||||
"testing"
|
|
||||||
|
|
||||||
"github.com/hashicorp/packer-plugin-sdk/common"
|
|
||||||
)
|
|
||||||
|
|
||||||
func TestCopyFile(t *testing.T) {
|
|
||||||
if runtime.GOOS == "windows" {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
first, err := ioutil.TempFile("", "copy_files_test")
|
|
||||||
if err != nil {
|
|
||||||
t.Fatalf("couldn't create temp file.")
|
|
||||||
}
|
|
||||||
defer os.Remove(first.Name())
|
|
||||||
newName := first.Name() + "-new"
|
|
||||||
|
|
||||||
payload := "copy_files_test.go payload"
|
|
||||||
if _, err = first.WriteString(payload); err != nil {
|
|
||||||
t.Fatalf("Couldn't write payload to first file.")
|
|
||||||
}
|
|
||||||
first.Sync()
|
|
||||||
|
|
||||||
cmd := common.ShellCommand(fmt.Sprintf("cp %s %s", first.Name(), newName))
|
|
||||||
if err := cmd.Run(); err != nil {
|
|
||||||
t.Fatalf("Couldn't copy file")
|
|
||||||
}
|
|
||||||
defer os.Remove(newName)
|
|
||||||
|
|
||||||
second, err := os.Open(newName)
|
|
||||||
if err != nil {
|
|
||||||
t.Fatalf("Couldn't open copied file.")
|
|
||||||
}
|
|
||||||
defer second.Close()
|
|
||||||
|
|
||||||
var copiedPayload = make([]byte, len(payload))
|
|
||||||
if _, err := second.Read(copiedPayload); err != nil {
|
|
||||||
t.Fatalf("Couldn't open copied file for reading.")
|
|
||||||
}
|
|
||||||
|
|
||||||
if string(copiedPayload) != payload {
|
|
||||||
t.Fatalf("payload not copied.")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,10 +0,0 @@
|
|||||||
package chroot
|
|
||||||
|
|
||||||
import "testing"
|
|
||||||
|
|
||||||
func TestDevicePrefixMatch(t *testing.T) {
|
|
||||||
/*
|
|
||||||
if devicePrefixMatch("nvme0n1") != "" {
|
|
||||||
}
|
|
||||||
*/
|
|
||||||
}
|
|
||||||
@@ -1,15 +0,0 @@
|
|||||||
package chroot
|
|
||||||
|
|
||||||
import (
|
|
||||||
"testing"
|
|
||||||
|
|
||||||
"github.com/hashicorp/packer-plugin-sdk/chroot"
|
|
||||||
)
|
|
||||||
|
|
||||||
func TestAttachVolumeCleanupFunc_ImplementsCleanupFunc(t *testing.T) {
|
|
||||||
var raw interface{}
|
|
||||||
raw = new(StepAttachVolume)
|
|
||||||
if _, ok := raw.(chroot.Cleanup); !ok {
|
|
||||||
t.Fatalf("cleanup func should be a CleanupFunc")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,97 +0,0 @@
|
|||||||
package chroot
|
|
||||||
|
|
||||||
import (
|
|
||||||
"testing"
|
|
||||||
|
|
||||||
"github.com/aws/aws-sdk-go/aws"
|
|
||||||
"github.com/aws/aws-sdk-go/service/ec2"
|
|
||||||
confighelper "github.com/hashicorp/packer-plugin-sdk/template/config"
|
|
||||||
"github.com/stretchr/testify/assert"
|
|
||||||
)
|
|
||||||
|
|
||||||
func buildTestRootDevice() *ec2.BlockDeviceMapping {
|
|
||||||
return &ec2.BlockDeviceMapping{
|
|
||||||
Ebs: &ec2.EbsBlockDevice{
|
|
||||||
VolumeSize: aws.Int64(10),
|
|
||||||
SnapshotId: aws.String("snap-1234"),
|
|
||||||
VolumeType: aws.String("gp2"),
|
|
||||||
Encrypted: aws.Bool(false),
|
|
||||||
},
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestCreateVolume_Default(t *testing.T) {
|
|
||||||
stepCreateVolume := new(StepCreateVolume)
|
|
||||||
_, err := stepCreateVolume.buildCreateVolumeInput("test-az", buildTestRootDevice())
|
|
||||||
assert.NoError(t, err)
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestCreateVolume_Shrink(t *testing.T) {
|
|
||||||
stepCreateVolume := StepCreateVolume{RootVolumeSize: 1}
|
|
||||||
testRootDevice := buildTestRootDevice()
|
|
||||||
ret, err := stepCreateVolume.buildCreateVolumeInput("test-az", testRootDevice)
|
|
||||||
assert.NoError(t, err)
|
|
||||||
// Ensure that the new value is equal to the size of the old root device
|
|
||||||
assert.Equal(t, *ret.Size, *testRootDevice.Ebs.VolumeSize)
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestCreateVolume_Expand(t *testing.T) {
|
|
||||||
stepCreateVolume := StepCreateVolume{RootVolumeSize: 25}
|
|
||||||
testRootDevice := buildTestRootDevice()
|
|
||||||
ret, err := stepCreateVolume.buildCreateVolumeInput("test-az", testRootDevice)
|
|
||||||
assert.NoError(t, err)
|
|
||||||
// Ensure that the new value is equal to the size of the value passed in
|
|
||||||
assert.Equal(t, *ret.Size, stepCreateVolume.RootVolumeSize)
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestCreateVolume_io1_to_io1(t *testing.T) {
|
|
||||||
stepCreateVolume := StepCreateVolume{RootVolumeType: "io1"}
|
|
||||||
testRootDevice := buildTestRootDevice()
|
|
||||||
testRootDevice.Ebs.VolumeType = aws.String("io1")
|
|
||||||
testRootDevice.Ebs.Iops = aws.Int64(1000)
|
|
||||||
ret, err := stepCreateVolume.buildCreateVolumeInput("test-az", testRootDevice)
|
|
||||||
assert.NoError(t, err)
|
|
||||||
assert.Equal(t, *ret.VolumeType, stepCreateVolume.RootVolumeType)
|
|
||||||
assert.Equal(t, *ret.Iops, *testRootDevice.Ebs.Iops)
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestCreateVolume_io1_to_gp2(t *testing.T) {
|
|
||||||
stepCreateVolume := StepCreateVolume{RootVolumeType: "gp2"}
|
|
||||||
testRootDevice := buildTestRootDevice()
|
|
||||||
testRootDevice.Ebs.VolumeType = aws.String("io1")
|
|
||||||
testRootDevice.Ebs.Iops = aws.Int64(1000)
|
|
||||||
|
|
||||||
ret, err := stepCreateVolume.buildCreateVolumeInput("test-az", testRootDevice)
|
|
||||||
assert.NoError(t, err)
|
|
||||||
assert.Equal(t, *ret.VolumeType, stepCreateVolume.RootVolumeType)
|
|
||||||
assert.Nil(t, ret.Iops)
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestCreateVolume_gp2_to_io1(t *testing.T) {
|
|
||||||
stepCreateVolume := StepCreateVolume{RootVolumeType: "io1"}
|
|
||||||
testRootDevice := buildTestRootDevice()
|
|
||||||
|
|
||||||
_, err := stepCreateVolume.buildCreateVolumeInput("test-az", testRootDevice)
|
|
||||||
assert.Error(t, err)
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestCreateVolume_Encrypted(t *testing.T) {
|
|
||||||
stepCreateVolume := StepCreateVolume{RootVolumeEncryptBoot: confighelper.TrileanFromBool(true)}
|
|
||||||
testRootDevice := buildTestRootDevice()
|
|
||||||
ret, err := stepCreateVolume.buildCreateVolumeInput("test-az", testRootDevice)
|
|
||||||
assert.NoError(t, err)
|
|
||||||
// Ensure that the new value is equal to the the value passed in
|
|
||||||
assert.Equal(t, confighelper.TrileanFromBool(*ret.Encrypted), stepCreateVolume.RootVolumeEncryptBoot)
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestCreateVolume_Custom_KMS_Key_Encrypted(t *testing.T) {
|
|
||||||
stepCreateVolume := StepCreateVolume{
|
|
||||||
RootVolumeEncryptBoot: confighelper.TrileanFromBool(true),
|
|
||||||
RootVolumeKmsKeyId: "alias/1234",
|
|
||||||
}
|
|
||||||
testRootDevice := buildTestRootDevice()
|
|
||||||
ret, err := stepCreateVolume.buildCreateVolumeInput("test-az", testRootDevice)
|
|
||||||
assert.NoError(t, err)
|
|
||||||
// Ensure that the new value is equal to the value passed in
|
|
||||||
assert.Equal(t, *ret.KmsKeyId, stepCreateVolume.RootVolumeKmsKeyId)
|
|
||||||
}
|
|
||||||
@@ -1,15 +0,0 @@
|
|||||||
package chroot
|
|
||||||
|
|
||||||
import (
|
|
||||||
"testing"
|
|
||||||
|
|
||||||
"github.com/hashicorp/packer-plugin-sdk/chroot"
|
|
||||||
)
|
|
||||||
|
|
||||||
func TestFlockCleanupFunc_ImplementsCleanupFunc(t *testing.T) {
|
|
||||||
var raw interface{}
|
|
||||||
raw = new(StepFlock)
|
|
||||||
if _, ok := raw.(chroot.Cleanup); !ok {
|
|
||||||
t.Fatalf("cleanup func should be a CleanupFunc")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,15 +0,0 @@
|
|||||||
package chroot
|
|
||||||
|
|
||||||
import (
|
|
||||||
"testing"
|
|
||||||
|
|
||||||
"github.com/hashicorp/packer-plugin-sdk/chroot"
|
|
||||||
)
|
|
||||||
|
|
||||||
func TestMountDeviceCleanupFunc_ImplementsCleanupFunc(t *testing.T) {
|
|
||||||
var raw interface{}
|
|
||||||
raw = new(StepMountDevice)
|
|
||||||
if _, ok := raw.(chroot.Cleanup); !ok {
|
|
||||||
t.Fatalf("cleanup func should be a CleanupFunc")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,216 +0,0 @@
|
|||||||
package chroot
|
|
||||||
|
|
||||||
import (
|
|
||||||
"testing"
|
|
||||||
|
|
||||||
"github.com/hashicorp/packer-plugin-sdk/common"
|
|
||||||
amazon "github.com/hashicorp/packer/builder/amazon/common"
|
|
||||||
|
|
||||||
"github.com/aws/aws-sdk-go/aws"
|
|
||||||
"github.com/aws/aws-sdk-go/service/ec2"
|
|
||||||
)
|
|
||||||
|
|
||||||
func testImage() ec2.Image {
|
|
||||||
return ec2.Image{
|
|
||||||
ImageId: aws.String("ami-abcd1234"),
|
|
||||||
Name: aws.String("ami_test_name"),
|
|
||||||
Architecture: aws.String("x86_64"),
|
|
||||||
KernelId: aws.String("aki-abcd1234"),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestStepRegisterAmi_buildRegisterOpts_pv(t *testing.T) {
|
|
||||||
config := Config{}
|
|
||||||
config.AMIName = "test_ami_name"
|
|
||||||
config.AMIDescription = "test_ami_description"
|
|
||||||
config.AMIVirtType = "paravirtual"
|
|
||||||
rootDeviceName := "foo"
|
|
||||||
|
|
||||||
image := testImage()
|
|
||||||
|
|
||||||
blockDevices := []*ec2.BlockDeviceMapping{}
|
|
||||||
|
|
||||||
opts := buildRegisterOptsFromExistingImage(&config, &image, blockDevices, rootDeviceName, config.AMIName)
|
|
||||||
|
|
||||||
expected := config.AMIVirtType
|
|
||||||
if *opts.VirtualizationType != expected {
|
|
||||||
t.Fatalf("Unexpected VirtType value: expected %s got %s\n", expected, *opts.VirtualizationType)
|
|
||||||
}
|
|
||||||
|
|
||||||
expected = config.AMIName
|
|
||||||
if *opts.Name != expected {
|
|
||||||
t.Fatalf("Unexpected Name value: expected %s got %s\n", expected, *opts.Name)
|
|
||||||
}
|
|
||||||
|
|
||||||
expected = *image.KernelId
|
|
||||||
if *opts.KernelId != expected {
|
|
||||||
t.Fatalf("Unexpected KernelId value: expected %s got %s\n", expected, *opts.KernelId)
|
|
||||||
}
|
|
||||||
|
|
||||||
expected = rootDeviceName
|
|
||||||
if *opts.RootDeviceName != expected {
|
|
||||||
t.Fatalf("Unexpected RootDeviceName value: expected %s got %s\n", expected, *opts.RootDeviceName)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestStepRegisterAmi_buildRegisterOpts_hvm(t *testing.T) {
|
|
||||||
config := Config{}
|
|
||||||
config.AMIName = "test_ami_name"
|
|
||||||
config.AMIDescription = "test_ami_description"
|
|
||||||
config.AMIVirtType = "hvm"
|
|
||||||
rootDeviceName := "foo"
|
|
||||||
|
|
||||||
image := testImage()
|
|
||||||
|
|
||||||
blockDevices := []*ec2.BlockDeviceMapping{}
|
|
||||||
|
|
||||||
opts := buildRegisterOptsFromExistingImage(&config, &image, blockDevices, rootDeviceName, config.AMIName)
|
|
||||||
|
|
||||||
expected := config.AMIVirtType
|
|
||||||
if *opts.VirtualizationType != expected {
|
|
||||||
t.Fatalf("Unexpected VirtType value: expected %s got %s\n", expected, *opts.VirtualizationType)
|
|
||||||
}
|
|
||||||
|
|
||||||
expected = config.AMIName
|
|
||||||
if *opts.Name != expected {
|
|
||||||
t.Fatalf("Unexpected Name value: expected %s got %s\n", expected, *opts.Name)
|
|
||||||
}
|
|
||||||
|
|
||||||
if opts.KernelId != nil {
|
|
||||||
t.Fatalf("Unexpected KernelId value: expected nil got %s\n", *opts.KernelId)
|
|
||||||
}
|
|
||||||
|
|
||||||
expected = rootDeviceName
|
|
||||||
if *opts.RootDeviceName != expected {
|
|
||||||
t.Fatalf("Unexpected RootDeviceName value: expected %s got %s\n", expected, *opts.RootDeviceName)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestStepRegisterAmi_buildRegisterOptsFromScratch(t *testing.T) {
|
|
||||||
rootDeviceName := "/dev/sda"
|
|
||||||
snapshotID := "foo"
|
|
||||||
config := Config{
|
|
||||||
FromScratch: true,
|
|
||||||
PackerConfig: common.PackerConfig{},
|
|
||||||
AMIMappings: []amazon.BlockDevice{
|
|
||||||
{
|
|
||||||
DeviceName: rootDeviceName,
|
|
||||||
},
|
|
||||||
},
|
|
||||||
RootDeviceName: rootDeviceName,
|
|
||||||
}
|
|
||||||
registerOpts := buildBaseRegisterOpts(&config, nil, 10, snapshotID, config.AMIName)
|
|
||||||
|
|
||||||
if len(registerOpts.BlockDeviceMappings) != 1 {
|
|
||||||
t.Fatal("Expected block device mapping of length 1")
|
|
||||||
}
|
|
||||||
|
|
||||||
if *registerOpts.BlockDeviceMappings[0].Ebs.SnapshotId != snapshotID {
|
|
||||||
t.Fatalf("Snapshot ID of root disk not set to snapshot id %s", rootDeviceName)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestStepRegisterAmi_buildRegisterOptFromExistingImage(t *testing.T) {
|
|
||||||
rootDeviceName := "/dev/sda"
|
|
||||||
snapshotID := "foo"
|
|
||||||
|
|
||||||
config := Config{
|
|
||||||
FromScratch: false,
|
|
||||||
PackerConfig: common.PackerConfig{},
|
|
||||||
}
|
|
||||||
sourceImage := ec2.Image{
|
|
||||||
RootDeviceName: &rootDeviceName,
|
|
||||||
BlockDeviceMappings: []*ec2.BlockDeviceMapping{
|
|
||||||
{
|
|
||||||
DeviceName: &rootDeviceName,
|
|
||||||
Ebs: &ec2.EbsBlockDevice{
|
|
||||||
VolumeSize: aws.Int64(10),
|
|
||||||
},
|
|
||||||
},
|
|
||||||
// Throw in an ephemeral device, it seems like all devices in the return struct in a source AMI have
|
|
||||||
// a size, even if it's for ephemeral
|
|
||||||
{
|
|
||||||
DeviceName: aws.String("/dev/sdb"),
|
|
||||||
VirtualName: aws.String("ephemeral0"),
|
|
||||||
Ebs: &ec2.EbsBlockDevice{
|
|
||||||
VolumeSize: aws.Int64(0),
|
|
||||||
},
|
|
||||||
},
|
|
||||||
},
|
|
||||||
}
|
|
||||||
registerOpts := buildBaseRegisterOpts(&config, &sourceImage, 15, snapshotID, config.AMIName)
|
|
||||||
|
|
||||||
if len(registerOpts.BlockDeviceMappings) != 2 {
|
|
||||||
t.Fatal("Expected block device mapping of length 2")
|
|
||||||
}
|
|
||||||
|
|
||||||
for _, dev := range registerOpts.BlockDeviceMappings {
|
|
||||||
if dev.Ebs.SnapshotId != nil && *dev.Ebs.SnapshotId == snapshotID {
|
|
||||||
// Even though root volume size is in config, it isn't used, instead we use the root volume size
|
|
||||||
// that's derived when we build the step
|
|
||||||
if *dev.Ebs.VolumeSize != 15 {
|
|
||||||
t.Fatalf("Root volume size not 15 GB instead %d", *dev.Ebs.VolumeSize)
|
|
||||||
}
|
|
||||||
return
|
|
||||||
}
|
|
||||||
}
|
|
||||||
t.Fatalf("Could not find device with snapshot ID %s", snapshotID)
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestStepRegisterAmi_buildRegisterOptFromExistingImageWithBlockDeviceMappings(t *testing.T) {
|
|
||||||
const (
|
|
||||||
rootDeviceName = "/dev/xvda"
|
|
||||||
oldRootDevice = "/dev/sda1"
|
|
||||||
)
|
|
||||||
snapshotId := "foo"
|
|
||||||
|
|
||||||
config := Config{
|
|
||||||
FromScratch: false,
|
|
||||||
PackerConfig: common.PackerConfig{},
|
|
||||||
AMIMappings: []amazon.BlockDevice{
|
|
||||||
{
|
|
||||||
DeviceName: rootDeviceName,
|
|
||||||
},
|
|
||||||
},
|
|
||||||
RootDeviceName: rootDeviceName,
|
|
||||||
}
|
|
||||||
|
|
||||||
// Intentionally try to use a different root devicename
|
|
||||||
sourceImage := ec2.Image{
|
|
||||||
RootDeviceName: aws.String(oldRootDevice),
|
|
||||||
BlockDeviceMappings: []*ec2.BlockDeviceMapping{
|
|
||||||
{
|
|
||||||
DeviceName: aws.String(oldRootDevice),
|
|
||||||
Ebs: &ec2.EbsBlockDevice{
|
|
||||||
VolumeSize: aws.Int64(10),
|
|
||||||
},
|
|
||||||
},
|
|
||||||
// Throw in an ephemeral device, it seems like all devices in the return struct in a source AMI have
|
|
||||||
// a size, even if it's for ephemeral
|
|
||||||
{
|
|
||||||
DeviceName: aws.String("/dev/sdb"),
|
|
||||||
VirtualName: aws.String("ephemeral0"),
|
|
||||||
Ebs: &ec2.EbsBlockDevice{
|
|
||||||
VolumeSize: aws.Int64(0),
|
|
||||||
},
|
|
||||||
},
|
|
||||||
},
|
|
||||||
}
|
|
||||||
registerOpts := buildBaseRegisterOpts(&config, &sourceImage, 15, snapshotId, config.AMIName)
|
|
||||||
|
|
||||||
if len(registerOpts.BlockDeviceMappings) != 1 {
|
|
||||||
t.Fatal("Expected block device mapping of length 1")
|
|
||||||
}
|
|
||||||
|
|
||||||
if *registerOpts.BlockDeviceMappings[0].Ebs.SnapshotId != snapshotId {
|
|
||||||
t.Fatalf("Snapshot ID of root disk set to '%s' expected '%s'", *registerOpts.BlockDeviceMappings[0].Ebs.SnapshotId, rootDeviceName)
|
|
||||||
}
|
|
||||||
|
|
||||||
if *registerOpts.RootDeviceName != rootDeviceName {
|
|
||||||
t.Fatalf("Root device set to '%s' expected %s", *registerOpts.RootDeviceName, rootDeviceName)
|
|
||||||
}
|
|
||||||
|
|
||||||
if *registerOpts.BlockDeviceMappings[0].Ebs.VolumeSize != 15 {
|
|
||||||
t.Fatalf("Size of root disk not set to 15 GB, instead %d", *registerOpts.BlockDeviceMappings[0].Ebs.VolumeSize)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,47 +0,0 @@
|
|||||||
package common
|
|
||||||
|
|
||||||
import (
|
|
||||||
"testing"
|
|
||||||
|
|
||||||
"github.com/aws/aws-sdk-go/aws"
|
|
||||||
"github.com/aws/aws-sdk-go/aws/session"
|
|
||||||
)
|
|
||||||
|
|
||||||
func TestAccessConfigPrepare_Region(t *testing.T) {
|
|
||||||
c := FakeAccessConfig()
|
|
||||||
|
|
||||||
c.RawRegion = "us-east-12"
|
|
||||||
err := c.ValidateRegion(c.RawRegion)
|
|
||||||
if err == nil {
|
|
||||||
t.Fatalf("should have region validation err: %s", c.RawRegion)
|
|
||||||
}
|
|
||||||
|
|
||||||
c.RawRegion = "us-east-1"
|
|
||||||
err = c.ValidateRegion(c.RawRegion)
|
|
||||||
if err != nil {
|
|
||||||
t.Fatalf("shouldn't have region validation err: %s", c.RawRegion)
|
|
||||||
}
|
|
||||||
|
|
||||||
c.RawRegion = "custom"
|
|
||||||
err = c.ValidateRegion(c.RawRegion)
|
|
||||||
if err == nil {
|
|
||||||
t.Fatalf("should have region validation err: %s", c.RawRegion)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestAccessConfigPrepare_RegionRestricted(t *testing.T) {
|
|
||||||
c := FakeAccessConfig()
|
|
||||||
|
|
||||||
// Create a Session with a custom region
|
|
||||||
c.session = session.Must(session.NewSession(&aws.Config{
|
|
||||||
Region: aws.String("us-gov-west-1"),
|
|
||||||
}))
|
|
||||||
|
|
||||||
if err := c.Prepare(); err != nil {
|
|
||||||
t.Fatalf("shouldn't have err: %s", err)
|
|
||||||
}
|
|
||||||
|
|
||||||
if !c.IsGovCloud() {
|
|
||||||
t.Fatal("We should be in gov region.")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,244 +0,0 @@
|
|||||||
package common
|
|
||||||
|
|
||||||
import (
|
|
||||||
"fmt"
|
|
||||||
"reflect"
|
|
||||||
"testing"
|
|
||||||
|
|
||||||
"github.com/aws/aws-sdk-go/aws"
|
|
||||||
"github.com/aws/aws-sdk-go/service/ec2"
|
|
||||||
"github.com/hashicorp/packer-plugin-sdk/template/config"
|
|
||||||
)
|
|
||||||
|
|
||||||
func testAMIConfig() *AMIConfig {
|
|
||||||
return &AMIConfig{
|
|
||||||
AMIName: "foo",
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func getFakeAccessConfig(region string) *AccessConfig {
|
|
||||||
c := FakeAccessConfig()
|
|
||||||
c.RawRegion = region
|
|
||||||
return c
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestAMIConfigPrepare_name(t *testing.T) {
|
|
||||||
c := testAMIConfig()
|
|
||||||
accessConf := FakeAccessConfig()
|
|
||||||
if err := c.Prepare(accessConf, nil); err != nil {
|
|
||||||
t.Fatalf("shouldn't have err: %s", err)
|
|
||||||
}
|
|
||||||
|
|
||||||
c.AMIName = ""
|
|
||||||
if err := c.Prepare(accessConf, nil); err == nil {
|
|
||||||
t.Fatal("should have error")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func (m *mockEC2Client) DescribeRegions(*ec2.DescribeRegionsInput) (*ec2.DescribeRegionsOutput, error) {
|
|
||||||
return &ec2.DescribeRegionsOutput{
|
|
||||||
Regions: []*ec2.Region{
|
|
||||||
{RegionName: aws.String("us-east-1")},
|
|
||||||
{RegionName: aws.String("us-east-2")},
|
|
||||||
{RegionName: aws.String("us-west-1")},
|
|
||||||
},
|
|
||||||
}, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestAMIConfigPrepare_regions(t *testing.T) {
|
|
||||||
c := testAMIConfig()
|
|
||||||
c.AMIRegions = nil
|
|
||||||
|
|
||||||
var errs []error
|
|
||||||
var err error
|
|
||||||
accessConf := FakeAccessConfig()
|
|
||||||
mockConn := &mockEC2Client{}
|
|
||||||
if errs = c.prepareRegions(accessConf); len(errs) > 0 {
|
|
||||||
t.Fatalf("shouldn't have err: %#v", errs)
|
|
||||||
}
|
|
||||||
|
|
||||||
c.AMIRegions, err = listEC2Regions(mockConn)
|
|
||||||
if err != nil {
|
|
||||||
t.Fatalf("shouldn't have err: %s", err.Error())
|
|
||||||
}
|
|
||||||
if errs = c.prepareRegions(accessConf); len(errs) > 0 {
|
|
||||||
t.Fatalf("shouldn't have err: %#v", errs)
|
|
||||||
}
|
|
||||||
|
|
||||||
c.AMIRegions = []string{"us-east-1", "us-west-1", "us-east-1"}
|
|
||||||
if errs = c.prepareRegions(accessConf); len(errs) > 0 {
|
|
||||||
t.Fatalf("bad: %s", errs[0])
|
|
||||||
}
|
|
||||||
|
|
||||||
expected := []string{"us-east-1", "us-west-1"}
|
|
||||||
if !reflect.DeepEqual(c.AMIRegions, expected) {
|
|
||||||
t.Fatalf("bad: %#v", c.AMIRegions)
|
|
||||||
}
|
|
||||||
|
|
||||||
c.AMIRegions = []string{"custom"}
|
|
||||||
if errs = c.prepareRegions(accessConf); len(errs) > 0 {
|
|
||||||
t.Fatal("shouldn't have error")
|
|
||||||
}
|
|
||||||
|
|
||||||
c.AMIRegions = []string{"us-east-1", "us-east-2", "us-west-1"}
|
|
||||||
c.AMIRegionKMSKeyIDs = map[string]string{
|
|
||||||
"us-east-1": "123-456-7890",
|
|
||||||
"us-west-1": "789-012-3456",
|
|
||||||
"us-east-2": "456-789-0123",
|
|
||||||
}
|
|
||||||
if errs = c.prepareRegions(accessConf); len(errs) > 0 {
|
|
||||||
t.Fatal(fmt.Sprintf("shouldn't have error: %s", errs[0]))
|
|
||||||
}
|
|
||||||
|
|
||||||
c.AMIRegions = []string{"us-east-1", "us-east-2", "us-west-1"}
|
|
||||||
c.AMIRegionKMSKeyIDs = map[string]string{
|
|
||||||
"us-east-1": "123-456-7890",
|
|
||||||
"us-west-1": "789-012-3456",
|
|
||||||
"us-east-2": "",
|
|
||||||
}
|
|
||||||
if errs = c.prepareRegions(accessConf); len(errs) > 0 {
|
|
||||||
t.Fatal("should have passed; we are able to use default KMS key if not sharing")
|
|
||||||
}
|
|
||||||
|
|
||||||
c.SnapshotUsers = []string{"user-foo", "user-bar"}
|
|
||||||
c.AMIRegions = []string{"us-east-1", "us-east-2", "us-west-1"}
|
|
||||||
c.AMIRegionKMSKeyIDs = map[string]string{
|
|
||||||
"us-east-1": "123-456-7890",
|
|
||||||
"us-west-1": "789-012-3456",
|
|
||||||
"us-east-2": "",
|
|
||||||
}
|
|
||||||
if errs = c.prepareRegions(accessConf); len(errs) > 0 {
|
|
||||||
t.Fatal("should have an error b/c can't use default KMS key if sharing")
|
|
||||||
}
|
|
||||||
|
|
||||||
c.AMIRegions = []string{"us-east-1", "us-west-1"}
|
|
||||||
c.AMIRegionKMSKeyIDs = map[string]string{
|
|
||||||
"us-east-1": "123-456-7890",
|
|
||||||
"us-west-1": "789-012-3456",
|
|
||||||
"us-east-2": "456-789-0123",
|
|
||||||
}
|
|
||||||
if errs = c.prepareRegions(accessConf); len(errs) > 0 {
|
|
||||||
t.Fatal("should have error b/c theres a region in the key map that isn't in ami_regions")
|
|
||||||
}
|
|
||||||
|
|
||||||
c.AMIRegions = []string{"us-east-1", "us-west-1", "us-east-2"}
|
|
||||||
c.AMIRegionKMSKeyIDs = map[string]string{
|
|
||||||
"us-east-1": "123-456-7890",
|
|
||||||
"us-west-1": "789-012-3456",
|
|
||||||
}
|
|
||||||
|
|
||||||
if err := c.Prepare(accessConf, nil); err == nil {
|
|
||||||
t.Fatal("should have error b/c theres a region in in ami_regions that isn't in the key map")
|
|
||||||
}
|
|
||||||
|
|
||||||
c.SnapshotUsers = []string{"foo", "bar"}
|
|
||||||
c.AMIKmsKeyId = "123-abc-456"
|
|
||||||
c.AMIEncryptBootVolume = config.TriTrue
|
|
||||||
c.AMIRegions = []string{"us-east-1", "us-west-1"}
|
|
||||||
c.AMIRegionKMSKeyIDs = map[string]string{
|
|
||||||
"us-east-1": "123-456-7890",
|
|
||||||
"us-west-1": "",
|
|
||||||
}
|
|
||||||
if errs = c.prepareRegions(accessConf); len(errs) > 0 {
|
|
||||||
t.Fatal("should have error b/c theres a region in in ami_regions that isn't in the key map")
|
|
||||||
}
|
|
||||||
|
|
||||||
// allow rawregion to exist in ami_regions list.
|
|
||||||
accessConf = getFakeAccessConfig("us-east-1")
|
|
||||||
c.AMIRegions = []string{"us-east-1", "us-west-1", "us-east-2"}
|
|
||||||
c.AMIRegionKMSKeyIDs = nil
|
|
||||||
if errs = c.prepareRegions(accessConf); len(errs) > 0 {
|
|
||||||
t.Fatal("should allow user to have the raw region in ami_regions")
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestAMIConfigPrepare_Share_EncryptedBoot(t *testing.T) {
|
|
||||||
c := testAMIConfig()
|
|
||||||
c.AMIUsers = []string{"testAccountID"}
|
|
||||||
c.AMIEncryptBootVolume = config.TriTrue
|
|
||||||
|
|
||||||
accessConf := FakeAccessConfig()
|
|
||||||
|
|
||||||
c.AMIKmsKeyId = ""
|
|
||||||
if err := c.Prepare(accessConf, nil); err == nil {
|
|
||||||
t.Fatal("shouldn't be able to share ami with encrypted boot volume")
|
|
||||||
}
|
|
||||||
c.AMIKmsKeyId = "89c3fb9a-de87-4f2a-aedc-fddc5138193c"
|
|
||||||
if err := c.Prepare(accessConf, nil); err != nil {
|
|
||||||
t.Fatal("should be able to share ami with encrypted boot volume")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestAMIConfigPrepare_ValidateKmsKey(t *testing.T) {
|
|
||||||
c := testAMIConfig()
|
|
||||||
c.AMIEncryptBootVolume = config.TriTrue
|
|
||||||
|
|
||||||
accessConf := FakeAccessConfig()
|
|
||||||
|
|
||||||
validCases := []string{
|
|
||||||
"abcd1234-e567-890f-a12b-a123b4cd56ef",
|
|
||||||
"alias/foo/bar",
|
|
||||||
"arn:aws:kms:us-east-1:012345678910:key/abcd1234-a123-456a-a12b-a123b4cd56ef",
|
|
||||||
"arn:aws:kms:us-east-1:012345678910:alias/foo/bar",
|
|
||||||
"arn:aws-us-gov:kms:us-gov-east-1:123456789012:key/12345678-1234-abcd-0000-123456789012",
|
|
||||||
}
|
|
||||||
for _, validCase := range validCases {
|
|
||||||
c.AMIKmsKeyId = validCase
|
|
||||||
if err := c.Prepare(accessConf, nil); err != nil {
|
|
||||||
t.Fatalf("%s should not have failed KMS key validation", validCase)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
invalidCases := []string{
|
|
||||||
"ABCD1234-e567-890f-a12b-a123b4cd56ef",
|
|
||||||
"ghij1234-e567-890f-a12b-a123b4cd56ef",
|
|
||||||
"ghij1234+e567_890f-a12b-a123b4cd56ef",
|
|
||||||
"foo/bar",
|
|
||||||
"arn:aws:kms:us-east-1:012345678910:foo/bar",
|
|
||||||
"arn:foo:kms:us-east-1:012345678910:key/abcd1234-a123-456a-a12b-a123b4cd56ef",
|
|
||||||
}
|
|
||||||
for _, invalidCase := range invalidCases {
|
|
||||||
c.AMIKmsKeyId = invalidCase
|
|
||||||
if err := c.Prepare(accessConf, nil); err == nil {
|
|
||||||
t.Fatalf("%s should have failed KMS key validation", invalidCase)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestAMINameValidation(t *testing.T) {
|
|
||||||
c := testAMIConfig()
|
|
||||||
|
|
||||||
accessConf := FakeAccessConfig()
|
|
||||||
|
|
||||||
c.AMIName = "aa"
|
|
||||||
if err := c.Prepare(accessConf, nil); err == nil {
|
|
||||||
t.Fatal("shouldn't be able to have an ami name with less than 3 characters")
|
|
||||||
}
|
|
||||||
|
|
||||||
var longAmiName string
|
|
||||||
for i := 0; i < 129; i++ {
|
|
||||||
longAmiName += "a"
|
|
||||||
}
|
|
||||||
c.AMIName = longAmiName
|
|
||||||
if err := c.Prepare(accessConf, nil); err == nil {
|
|
||||||
t.Fatal("shouldn't be able to have an ami name with great than 128 characters")
|
|
||||||
}
|
|
||||||
|
|
||||||
c.AMIName = "+aaa"
|
|
||||||
if err := c.Prepare(accessConf, nil); err == nil {
|
|
||||||
t.Fatal("shouldn't be able to have an ami name with invalid characters")
|
|
||||||
}
|
|
||||||
|
|
||||||
c.AMIName = "fooBAR1()[] ./-'@_"
|
|
||||||
if err := c.Prepare(accessConf, nil); err != nil {
|
|
||||||
t.Fatal("should be able to use all of the allowed AMI characters")
|
|
||||||
}
|
|
||||||
|
|
||||||
c.AMIName = `xyz-base-2017-04-05-1934`
|
|
||||||
if err := c.Prepare(accessConf, nil); err != nil {
|
|
||||||
t.Fatalf("expected `xyz-base-2017-04-05-1934` to pass validation.")
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
@@ -1,90 +0,0 @@
|
|||||||
package common
|
|
||||||
|
|
||||||
import (
|
|
||||||
"reflect"
|
|
||||||
"testing"
|
|
||||||
|
|
||||||
packersdk "github.com/hashicorp/packer-plugin-sdk/packer"
|
|
||||||
)
|
|
||||||
|
|
||||||
func TestArtifact_Impl(t *testing.T) {
|
|
||||||
var _ packersdk.Artifact = new(Artifact)
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestArtifactId(t *testing.T) {
|
|
||||||
expected := `east:foo,west:bar`
|
|
||||||
|
|
||||||
amis := make(map[string]string)
|
|
||||||
amis["east"] = "foo"
|
|
||||||
amis["west"] = "bar"
|
|
||||||
|
|
||||||
a := &Artifact{
|
|
||||||
Amis: amis,
|
|
||||||
}
|
|
||||||
|
|
||||||
result := a.Id()
|
|
||||||
if result != expected {
|
|
||||||
t.Fatalf("bad: %s", result)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestArtifactState_atlasMetadata(t *testing.T) {
|
|
||||||
a := &Artifact{
|
|
||||||
Amis: map[string]string{
|
|
||||||
"east": "foo",
|
|
||||||
"west": "bar",
|
|
||||||
},
|
|
||||||
}
|
|
||||||
|
|
||||||
actual := a.State("atlas.artifact.metadata")
|
|
||||||
expected := map[string]string{
|
|
||||||
"region.east": "foo",
|
|
||||||
"region.west": "bar",
|
|
||||||
}
|
|
||||||
if !reflect.DeepEqual(actual, expected) {
|
|
||||||
t.Fatalf("bad: %#v", actual)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestArtifactString(t *testing.T) {
|
|
||||||
expected := `AMIs were created:
|
|
||||||
east: foo
|
|
||||||
west: bar
|
|
||||||
`
|
|
||||||
|
|
||||||
amis := make(map[string]string)
|
|
||||||
amis["east"] = "foo"
|
|
||||||
amis["west"] = "bar"
|
|
||||||
|
|
||||||
a := &Artifact{Amis: amis}
|
|
||||||
result := a.String()
|
|
||||||
if result != expected {
|
|
||||||
t.Fatalf("bad: %s", result)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestArtifactState(t *testing.T) {
|
|
||||||
expectedData := "this is the data"
|
|
||||||
artifact := &Artifact{
|
|
||||||
StateData: map[string]interface{}{"state_data": expectedData},
|
|
||||||
}
|
|
||||||
|
|
||||||
// Valid state
|
|
||||||
result := artifact.State("state_data")
|
|
||||||
if result != expectedData {
|
|
||||||
t.Fatalf("Bad: State data was %s instead of %s", result, expectedData)
|
|
||||||
}
|
|
||||||
|
|
||||||
// Invalid state
|
|
||||||
result = artifact.State("invalid_key")
|
|
||||||
if result != nil {
|
|
||||||
t.Fatalf("Bad: State should be nil for invalid state data name")
|
|
||||||
}
|
|
||||||
|
|
||||||
// Nil StateData should not fail and should return nil
|
|
||||||
artifact = &Artifact{}
|
|
||||||
result = artifact.State("key")
|
|
||||||
if result != nil {
|
|
||||||
t.Fatalf("Bad: State should be nil for nil StateData")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,401 +0,0 @@
|
|||||||
package common
|
|
||||||
|
|
||||||
import (
|
|
||||||
"testing"
|
|
||||||
|
|
||||||
"github.com/aws/aws-sdk-go/aws"
|
|
||||||
"github.com/aws/aws-sdk-go/service/ec2"
|
|
||||||
"github.com/google/go-cmp/cmp"
|
|
||||||
"github.com/hashicorp/packer-plugin-sdk/template/config"
|
|
||||||
"github.com/hashicorp/packer-plugin-sdk/template/interpolate"
|
|
||||||
)
|
|
||||||
|
|
||||||
func TestBlockDevice(t *testing.T) {
|
|
||||||
cases := []struct {
|
|
||||||
Config *BlockDevice
|
|
||||||
Result *ec2.BlockDeviceMapping
|
|
||||||
}{
|
|
||||||
{
|
|
||||||
Config: &BlockDevice{
|
|
||||||
DeviceName: "/dev/sdb",
|
|
||||||
SnapshotId: "snap-1234",
|
|
||||||
VolumeType: "standard",
|
|
||||||
VolumeSize: 8,
|
|
||||||
DeleteOnTermination: true,
|
|
||||||
},
|
|
||||||
|
|
||||||
Result: &ec2.BlockDeviceMapping{
|
|
||||||
DeviceName: aws.String("/dev/sdb"),
|
|
||||||
Ebs: &ec2.EbsBlockDevice{
|
|
||||||
SnapshotId: aws.String("snap-1234"),
|
|
||||||
VolumeType: aws.String("standard"),
|
|
||||||
VolumeSize: aws.Int64(8),
|
|
||||||
DeleteOnTermination: aws.Bool(true),
|
|
||||||
},
|
|
||||||
},
|
|
||||||
},
|
|
||||||
{
|
|
||||||
Config: &BlockDevice{
|
|
||||||
DeviceName: "/dev/sdb",
|
|
||||||
VolumeSize: 8,
|
|
||||||
},
|
|
||||||
|
|
||||||
Result: &ec2.BlockDeviceMapping{
|
|
||||||
DeviceName: aws.String("/dev/sdb"),
|
|
||||||
Ebs: &ec2.EbsBlockDevice{
|
|
||||||
VolumeSize: aws.Int64(8),
|
|
||||||
DeleteOnTermination: aws.Bool(false),
|
|
||||||
},
|
|
||||||
},
|
|
||||||
},
|
|
||||||
{
|
|
||||||
Config: &BlockDevice{
|
|
||||||
DeviceName: "/dev/sdb",
|
|
||||||
VolumeType: "io1",
|
|
||||||
VolumeSize: 8,
|
|
||||||
DeleteOnTermination: true,
|
|
||||||
IOPS: aws.Int64(1000),
|
|
||||||
},
|
|
||||||
|
|
||||||
Result: &ec2.BlockDeviceMapping{
|
|
||||||
DeviceName: aws.String("/dev/sdb"),
|
|
||||||
Ebs: &ec2.EbsBlockDevice{
|
|
||||||
VolumeType: aws.String("io1"),
|
|
||||||
VolumeSize: aws.Int64(8),
|
|
||||||
DeleteOnTermination: aws.Bool(true),
|
|
||||||
Iops: aws.Int64(1000),
|
|
||||||
},
|
|
||||||
},
|
|
||||||
},
|
|
||||||
{
|
|
||||||
Config: &BlockDevice{
|
|
||||||
DeviceName: "/dev/sdb",
|
|
||||||
VolumeType: "io2",
|
|
||||||
VolumeSize: 8,
|
|
||||||
DeleteOnTermination: true,
|
|
||||||
IOPS: aws.Int64(1000),
|
|
||||||
},
|
|
||||||
|
|
||||||
Result: &ec2.BlockDeviceMapping{
|
|
||||||
DeviceName: aws.String("/dev/sdb"),
|
|
||||||
Ebs: &ec2.EbsBlockDevice{
|
|
||||||
VolumeType: aws.String("io2"),
|
|
||||||
VolumeSize: aws.Int64(8),
|
|
||||||
DeleteOnTermination: aws.Bool(true),
|
|
||||||
Iops: aws.Int64(1000),
|
|
||||||
},
|
|
||||||
},
|
|
||||||
},
|
|
||||||
{
|
|
||||||
Config: &BlockDevice{
|
|
||||||
DeviceName: "/dev/sdb",
|
|
||||||
VolumeType: "gp2",
|
|
||||||
VolumeSize: 8,
|
|
||||||
DeleteOnTermination: true,
|
|
||||||
Encrypted: config.TriTrue,
|
|
||||||
},
|
|
||||||
|
|
||||||
Result: &ec2.BlockDeviceMapping{
|
|
||||||
DeviceName: aws.String("/dev/sdb"),
|
|
||||||
Ebs: &ec2.EbsBlockDevice{
|
|
||||||
VolumeType: aws.String("gp2"),
|
|
||||||
VolumeSize: aws.Int64(8),
|
|
||||||
DeleteOnTermination: aws.Bool(true),
|
|
||||||
Encrypted: aws.Bool(true),
|
|
||||||
},
|
|
||||||
},
|
|
||||||
},
|
|
||||||
{
|
|
||||||
Config: &BlockDevice{
|
|
||||||
DeviceName: "/dev/sdb",
|
|
||||||
VolumeType: "gp2",
|
|
||||||
VolumeSize: 8,
|
|
||||||
DeleteOnTermination: true,
|
|
||||||
Encrypted: config.TriTrue,
|
|
||||||
KmsKeyId: "2Fa48a521f-3aff-4b34-a159-376ac5d37812",
|
|
||||||
},
|
|
||||||
|
|
||||||
Result: &ec2.BlockDeviceMapping{
|
|
||||||
DeviceName: aws.String("/dev/sdb"),
|
|
||||||
Ebs: &ec2.EbsBlockDevice{
|
|
||||||
VolumeType: aws.String("gp2"),
|
|
||||||
VolumeSize: aws.Int64(8),
|
|
||||||
DeleteOnTermination: aws.Bool(true),
|
|
||||||
Encrypted: aws.Bool(true),
|
|
||||||
KmsKeyId: aws.String("2Fa48a521f-3aff-4b34-a159-376ac5d37812"),
|
|
||||||
},
|
|
||||||
},
|
|
||||||
},
|
|
||||||
{
|
|
||||||
Config: &BlockDevice{
|
|
||||||
DeviceName: "/dev/sdb",
|
|
||||||
VolumeType: "standard",
|
|
||||||
DeleteOnTermination: true,
|
|
||||||
},
|
|
||||||
|
|
||||||
Result: &ec2.BlockDeviceMapping{
|
|
||||||
DeviceName: aws.String("/dev/sdb"),
|
|
||||||
Ebs: &ec2.EbsBlockDevice{
|
|
||||||
VolumeType: aws.String("standard"),
|
|
||||||
DeleteOnTermination: aws.Bool(true),
|
|
||||||
},
|
|
||||||
},
|
|
||||||
},
|
|
||||||
{
|
|
||||||
Config: &BlockDevice{
|
|
||||||
DeviceName: "/dev/sdb",
|
|
||||||
VirtualName: "ephemeral0",
|
|
||||||
},
|
|
||||||
|
|
||||||
Result: &ec2.BlockDeviceMapping{
|
|
||||||
DeviceName: aws.String("/dev/sdb"),
|
|
||||||
VirtualName: aws.String("ephemeral0"),
|
|
||||||
},
|
|
||||||
},
|
|
||||||
{
|
|
||||||
Config: &BlockDevice{
|
|
||||||
DeviceName: "/dev/sdb",
|
|
||||||
NoDevice: true,
|
|
||||||
},
|
|
||||||
|
|
||||||
Result: &ec2.BlockDeviceMapping{
|
|
||||||
DeviceName: aws.String("/dev/sdb"),
|
|
||||||
NoDevice: aws.String(""),
|
|
||||||
},
|
|
||||||
},
|
|
||||||
{
|
|
||||||
Config: &BlockDevice{
|
|
||||||
DeviceName: "/dev/sdb",
|
|
||||||
VolumeType: "gp3",
|
|
||||||
VolumeSize: 8,
|
|
||||||
Throughput: aws.Int64(125),
|
|
||||||
IOPS: aws.Int64(3000),
|
|
||||||
DeleteOnTermination: true,
|
|
||||||
Encrypted: config.TriTrue,
|
|
||||||
},
|
|
||||||
|
|
||||||
Result: &ec2.BlockDeviceMapping{
|
|
||||||
DeviceName: aws.String("/dev/sdb"),
|
|
||||||
Ebs: &ec2.EbsBlockDevice{
|
|
||||||
VolumeType: aws.String("gp3"),
|
|
||||||
VolumeSize: aws.Int64(8),
|
|
||||||
Throughput: aws.Int64(125),
|
|
||||||
Iops: aws.Int64(3000),
|
|
||||||
DeleteOnTermination: aws.Bool(true),
|
|
||||||
Encrypted: aws.Bool(true),
|
|
||||||
},
|
|
||||||
},
|
|
||||||
},
|
|
||||||
}
|
|
||||||
|
|
||||||
for _, tc := range cases {
|
|
||||||
var amiBlockDevices BlockDevices = []BlockDevice{*tc.Config}
|
|
||||||
|
|
||||||
var launchBlockDevices BlockDevices = []BlockDevice{*tc.Config}
|
|
||||||
|
|
||||||
expected := []*ec2.BlockDeviceMapping{tc.Result}
|
|
||||||
|
|
||||||
amiResults := amiBlockDevices.BuildEC2BlockDeviceMappings()
|
|
||||||
if diff := cmp.Diff(expected, amiResults); diff != "" {
|
|
||||||
t.Fatalf("Bad block device: %s", diff)
|
|
||||||
}
|
|
||||||
|
|
||||||
launchResults := launchBlockDevices.BuildEC2BlockDeviceMappings()
|
|
||||||
if diff := cmp.Diff(expected, launchResults); diff != "" {
|
|
||||||
t.Fatalf("Bad block device: %s", diff)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestIOPSValidation(t *testing.T) {
|
|
||||||
|
|
||||||
cases := []struct {
|
|
||||||
device BlockDevice
|
|
||||||
ok bool
|
|
||||||
msg string
|
|
||||||
}{
|
|
||||||
// volume size unknown
|
|
||||||
{
|
|
||||||
device: BlockDevice{
|
|
||||||
DeviceName: "/dev/sdb",
|
|
||||||
VolumeType: "io1",
|
|
||||||
IOPS: aws.Int64(1000),
|
|
||||||
},
|
|
||||||
ok: true,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
device: BlockDevice{
|
|
||||||
DeviceName: "/dev/sdb",
|
|
||||||
VolumeType: "io2",
|
|
||||||
IOPS: aws.Int64(1000),
|
|
||||||
},
|
|
||||||
ok: true,
|
|
||||||
},
|
|
||||||
// ratio requirement satisfied
|
|
||||||
{
|
|
||||||
device: BlockDevice{
|
|
||||||
DeviceName: "/dev/sdb",
|
|
||||||
VolumeType: "io1",
|
|
||||||
VolumeSize: 50,
|
|
||||||
IOPS: aws.Int64(1000),
|
|
||||||
},
|
|
||||||
ok: true,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
device: BlockDevice{
|
|
||||||
DeviceName: "/dev/sdb",
|
|
||||||
VolumeType: "io2",
|
|
||||||
VolumeSize: 100,
|
|
||||||
IOPS: aws.Int64(1000),
|
|
||||||
},
|
|
||||||
ok: true,
|
|
||||||
},
|
|
||||||
// ratio requirement not satisfied
|
|
||||||
{
|
|
||||||
device: BlockDevice{
|
|
||||||
DeviceName: "/dev/sdb",
|
|
||||||
VolumeType: "io1",
|
|
||||||
VolumeSize: 10,
|
|
||||||
IOPS: aws.Int64(2000),
|
|
||||||
},
|
|
||||||
ok: false,
|
|
||||||
msg: "/dev/sdb: the maximum ratio of provisioned IOPS to requested volume size (in GiB) is 50:1 for io1 volumes",
|
|
||||||
},
|
|
||||||
{
|
|
||||||
device: BlockDevice{
|
|
||||||
DeviceName: "/dev/sdb",
|
|
||||||
VolumeType: "io2",
|
|
||||||
VolumeSize: 50,
|
|
||||||
IOPS: aws.Int64(30000),
|
|
||||||
},
|
|
||||||
ok: false,
|
|
||||||
msg: "/dev/sdb: the maximum ratio of provisioned IOPS to requested volume size (in GiB) is 500:1 for io2 volumes",
|
|
||||||
},
|
|
||||||
// exceed max iops
|
|
||||||
{
|
|
||||||
device: BlockDevice{
|
|
||||||
DeviceName: "/dev/sdb",
|
|
||||||
VolumeType: "io2",
|
|
||||||
VolumeSize: 500,
|
|
||||||
IOPS: aws.Int64(99999),
|
|
||||||
},
|
|
||||||
ok: false,
|
|
||||||
msg: "IOPS must be between 100 and 64000 for device /dev/sdb",
|
|
||||||
},
|
|
||||||
// lower than min iops
|
|
||||||
{
|
|
||||||
device: BlockDevice{
|
|
||||||
DeviceName: "/dev/sdb",
|
|
||||||
VolumeType: "io2",
|
|
||||||
VolumeSize: 50,
|
|
||||||
IOPS: aws.Int64(10),
|
|
||||||
},
|
|
||||||
ok: false,
|
|
||||||
msg: "IOPS must be between 100 and 64000 for device /dev/sdb",
|
|
||||||
},
|
|
||||||
// exceed max iops
|
|
||||||
{
|
|
||||||
device: BlockDevice{
|
|
||||||
DeviceName: "/dev/sdb",
|
|
||||||
VolumeType: "gp3",
|
|
||||||
VolumeSize: 50,
|
|
||||||
Throughput: aws.Int64(125),
|
|
||||||
IOPS: aws.Int64(99999),
|
|
||||||
},
|
|
||||||
ok: false,
|
|
||||||
msg: "IOPS must be between 3000 and 16000 for device /dev/sdb",
|
|
||||||
},
|
|
||||||
// lower than min iops
|
|
||||||
{
|
|
||||||
device: BlockDevice{
|
|
||||||
DeviceName: "/dev/sdb",
|
|
||||||
VolumeType: "gp3",
|
|
||||||
VolumeSize: 50,
|
|
||||||
Throughput: aws.Int64(125),
|
|
||||||
IOPS: aws.Int64(10),
|
|
||||||
},
|
|
||||||
ok: false,
|
|
||||||
msg: "IOPS must be between 3000 and 16000 for device /dev/sdb",
|
|
||||||
},
|
|
||||||
}
|
|
||||||
|
|
||||||
ctx := interpolate.Context{}
|
|
||||||
for _, testCase := range cases {
|
|
||||||
err := testCase.device.Prepare(&ctx)
|
|
||||||
if testCase.ok && err != nil {
|
|
||||||
t.Fatalf("should not error, but: %v", err)
|
|
||||||
}
|
|
||||||
if !testCase.ok {
|
|
||||||
if err == nil {
|
|
||||||
t.Fatalf("should error")
|
|
||||||
} else if err.Error() != testCase.msg {
|
|
||||||
t.Fatalf("wrong error: expected %s, found: %v", testCase.msg, err)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestThroughputValidation(t *testing.T) {
|
|
||||||
|
|
||||||
cases := []struct {
|
|
||||||
device BlockDevice
|
|
||||||
ok bool
|
|
||||||
msg string
|
|
||||||
}{
|
|
||||||
{
|
|
||||||
device: BlockDevice{
|
|
||||||
DeviceName: "/dev/sdb",
|
|
||||||
VolumeType: "gp3",
|
|
||||||
Throughput: aws.Int64(125),
|
|
||||||
IOPS: aws.Int64(3000),
|
|
||||||
},
|
|
||||||
ok: true,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
device: BlockDevice{
|
|
||||||
DeviceName: "/dev/sdb",
|
|
||||||
VolumeType: "gp3",
|
|
||||||
Throughput: aws.Int64(1000),
|
|
||||||
IOPS: aws.Int64(3000),
|
|
||||||
},
|
|
||||||
ok: true,
|
|
||||||
},
|
|
||||||
// exceed max Throughput
|
|
||||||
{
|
|
||||||
device: BlockDevice{
|
|
||||||
DeviceName: "/dev/sdb",
|
|
||||||
VolumeType: "gp3",
|
|
||||||
Throughput: aws.Int64(1001),
|
|
||||||
IOPS: aws.Int64(3000),
|
|
||||||
},
|
|
||||||
ok: false,
|
|
||||||
msg: "Throughput must be between 125 and 1000 for device /dev/sdb",
|
|
||||||
},
|
|
||||||
// lower than min Throughput
|
|
||||||
{
|
|
||||||
device: BlockDevice{
|
|
||||||
DeviceName: "/dev/sdb",
|
|
||||||
VolumeType: "gp3",
|
|
||||||
Throughput: aws.Int64(124),
|
|
||||||
IOPS: aws.Int64(3000),
|
|
||||||
},
|
|
||||||
ok: false,
|
|
||||||
msg: "Throughput must be between 125 and 1000 for device /dev/sdb",
|
|
||||||
},
|
|
||||||
}
|
|
||||||
|
|
||||||
ctx := interpolate.Context{}
|
|
||||||
for _, testCase := range cases {
|
|
||||||
err := testCase.device.Prepare(&ctx)
|
|
||||||
if testCase.ok && err != nil {
|
|
||||||
t.Fatalf("should not error, but: %v", err)
|
|
||||||
}
|
|
||||||
if !testCase.ok {
|
|
||||||
if err == nil {
|
|
||||||
t.Fatalf("should error")
|
|
||||||
} else if err.Error() != testCase.msg {
|
|
||||||
t.Fatalf("wrong error: expected %s, found: %v", testCase.msg, err)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,31 +0,0 @@
|
|||||||
package common
|
|
||||||
|
|
||||||
import (
|
|
||||||
"testing"
|
|
||||||
)
|
|
||||||
|
|
||||||
func TestStepSourceAmiInfo_BuildFilter(t *testing.T) {
|
|
||||||
filter_key := "name"
|
|
||||||
filter_value := "foo"
|
|
||||||
filter_key2 := "name2"
|
|
||||||
filter_value2 := "foo2"
|
|
||||||
|
|
||||||
inputFilter := map[string]string{filter_key: filter_value, filter_key2: filter_value2}
|
|
||||||
outputFilter := buildEc2Filters(inputFilter)
|
|
||||||
|
|
||||||
// deconstruct filter back into things we can test
|
|
||||||
foundMap := map[string]bool{filter_key: false, filter_key2: false}
|
|
||||||
for _, filter := range outputFilter {
|
|
||||||
for key, value := range inputFilter {
|
|
||||||
if *filter.Name == key && *filter.Values[0] == value {
|
|
||||||
foundMap[key] = true
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
for k, v := range foundMap {
|
|
||||||
if !v {
|
|
||||||
t.Fatalf("Fail: should have found value for key: %s", k)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,70 +0,0 @@
|
|||||||
package common
|
|
||||||
|
|
||||||
import (
|
|
||||||
"fmt"
|
|
||||||
"strings"
|
|
||||||
"testing"
|
|
||||||
|
|
||||||
"github.com/aws/aws-sdk-go/aws"
|
|
||||||
"github.com/aws/aws-sdk-go/service/sts"
|
|
||||||
|
|
||||||
"github.com/aws/aws-sdk-go/aws/awserr"
|
|
||||||
)
|
|
||||||
|
|
||||||
type mockSTS struct {
|
|
||||||
}
|
|
||||||
|
|
||||||
func (m *mockSTS) DecodeAuthorizationMessage(input *sts.DecodeAuthorizationMessageInput) (*sts.DecodeAuthorizationMessageOutput, error) {
|
|
||||||
return &sts.DecodeAuthorizationMessageOutput{
|
|
||||||
DecodedMessage: aws.String(`{
|
|
||||||
"allowed": false,
|
|
||||||
"explicitDeny": true,
|
|
||||||
"matchedStatements": {}
|
|
||||||
}`),
|
|
||||||
}, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestErrorsParsing_RequestFailure(t *testing.T) {
|
|
||||||
|
|
||||||
ae := awserr.New("UnauthorizedOperation",
|
|
||||||
`You are not authorized to perform this operation. Encoded authorization failure message: D9Q7oicjOMr9l2CC-NPP1FiZXK9Ijia1k-3l0siBFCcrK3oSuMFMkBIO5TNj0HdXE-WfwnAcdycFOohfKroNO6toPJEns8RFVfy_M_IjNGmrEFJ6E62pnmBW0OLrMsXxR9FQE4gB4gJzSM0AD6cV6S3FOfqYzWBRX-sQdOT4HryGkFNRoFBr9Xbp-tRwiadwkbdHdfnV9fbRkXmnwCdULml16NBSofC4ZPepLMKmIB5rKjwk-m179UUh2XA-J5no0si6XcRo5GbHQB5QfCIwSHL4vsro2wLZUd16-8OWKyr3tVlTbQe0ERZskqRqRQ5E28QuiBCVV6XstUyo-T4lBSr75Fgnyr3wCO-dS3b_5Ns3WzA2JD4E2AJOAStXIU8IH5YuKkAg7C-dJMuBMPpmKCBEXhNoHDwCyOo5PsV3xMlc0jSb0qYGpfst_TDDtejcZfn7NssUjxVq9qkdH-OXz2gPoQB-hX8ycmZCL5UZwKc3TCLUr7TGnudHjmnMrE9cUo-yTCWfyHPLprhiYhTCKW18EikJ0O1EKI3FJ_b4F19_jFBPARjSwQc7Ut6MNCVzrPdZGYSF6acj5gPaxdy9uSkVQwWXK7Pd5MFP7EBDE1_DgYbzodgwDO2PXeVFUbSLBHKWo_ebZS9ZX2nYPcGss_sYaly0ZVSIJXp7G58B5BoFVhvVH6jYnF9XiAOjMltuP_ycu1pQP1lki500RY3baLvfeYeAsB38XZHKEgWZzq7Fei-uh89q0cjJTmlVyrfRU3q6`,
|
|
||||||
fmt.Errorf("You can't do it!!"))
|
|
||||||
rf := awserr.NewRequestFailure(ae, 400, "abc-def-123-456")
|
|
||||||
|
|
||||||
result := decodeAWSError(&mockSTS{}, rf)
|
|
||||||
if result == nil {
|
|
||||||
t.Error("Expected resulting error")
|
|
||||||
}
|
|
||||||
if !strings.Contains(result.Error(), "Authorization failure message:") {
|
|
||||||
t.Error("Expected authorization failure message")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestErrorsParsing_NonAuthorizationFailure(t *testing.T) {
|
|
||||||
|
|
||||||
ae := awserr.New("BadRequest",
|
|
||||||
`You did something wrong. Try again`,
|
|
||||||
fmt.Errorf("Request was no good."))
|
|
||||||
rf := awserr.NewRequestFailure(ae, 400, "abc-def-123-456")
|
|
||||||
|
|
||||||
result := decodeAWSError(&mockSTS{}, rf)
|
|
||||||
if result == nil {
|
|
||||||
t.Error("Expected resulting error")
|
|
||||||
}
|
|
||||||
if result != rf {
|
|
||||||
t.Error("Expected original error to be returned unchanged")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestErrorsParsing_NonAWSError(t *testing.T) {
|
|
||||||
|
|
||||||
err := fmt.Errorf("Random error occurred")
|
|
||||||
|
|
||||||
result := decodeAWSError(&mockSTS{}, err)
|
|
||||||
if result == nil {
|
|
||||||
t.Error("Expected resulting error")
|
|
||||||
}
|
|
||||||
if result != err {
|
|
||||||
t.Error("Expected original error to be returned unchanged")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,91 +0,0 @@
|
|||||||
package common
|
|
||||||
|
|
||||||
import (
|
|
||||||
"reflect"
|
|
||||||
"testing"
|
|
||||||
|
|
||||||
"github.com/aws/aws-sdk-go/aws"
|
|
||||||
"github.com/aws/aws-sdk-go/service/ec2"
|
|
||||||
"github.com/hashicorp/packer-plugin-sdk/multistep"
|
|
||||||
"github.com/hashicorp/packer-plugin-sdk/packerbuilderdata"
|
|
||||||
)
|
|
||||||
|
|
||||||
func testImage() *ec2.Image {
|
|
||||||
return &ec2.Image{
|
|
||||||
ImageId: aws.String("ami-abcd1234"),
|
|
||||||
CreationDate: aws.String("ami_test_creation_date"),
|
|
||||||
Name: aws.String("ami_test_name"),
|
|
||||||
OwnerId: aws.String("ami_test_owner_id"),
|
|
||||||
ImageOwnerAlias: aws.String("ami_test_owner_alias"),
|
|
||||||
RootDeviceType: aws.String("ebs"),
|
|
||||||
Tags: []*ec2.Tag{
|
|
||||||
{
|
|
||||||
Key: aws.String("key-1"),
|
|
||||||
Value: aws.String("value-1"),
|
|
||||||
},
|
|
||||||
{
|
|
||||||
Key: aws.String("key-2"),
|
|
||||||
Value: aws.String("value-2"),
|
|
||||||
},
|
|
||||||
},
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func testState() multistep.StateBag {
|
|
||||||
state := new(multistep.BasicStateBag)
|
|
||||||
return state
|
|
||||||
}
|
|
||||||
|
|
||||||
func testGeneratedData(state multistep.StateBag) packerbuilderdata.GeneratedData {
|
|
||||||
generatedData := packerbuilderdata.GeneratedData{State: state}
|
|
||||||
return generatedData
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestInterpolateBuildInfo_extractBuildInfo_noSourceImage(t *testing.T) {
|
|
||||||
state := testState()
|
|
||||||
generatedData := testGeneratedData(state)
|
|
||||||
buildInfo := extractBuildInfo("foo", state, &generatedData)
|
|
||||||
|
|
||||||
expected := BuildInfoTemplate{
|
|
||||||
BuildRegion: "foo",
|
|
||||||
}
|
|
||||||
if !reflect.DeepEqual(*buildInfo, expected) {
|
|
||||||
t.Fatalf("Unexpected BuildInfoTemplate: expected %#v got %#v\n", expected, *buildInfo)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestInterpolateBuildInfo_extractBuildInfo_withSourceImage(t *testing.T) {
|
|
||||||
state := testState()
|
|
||||||
state.Put("source_image", testImage())
|
|
||||||
generatedData := testGeneratedData(state)
|
|
||||||
buildInfo := extractBuildInfo("foo", state, &generatedData)
|
|
||||||
|
|
||||||
expected := BuildInfoTemplate{
|
|
||||||
BuildRegion: "foo",
|
|
||||||
SourceAMI: "ami-abcd1234",
|
|
||||||
SourceAMICreationDate: "ami_test_creation_date",
|
|
||||||
SourceAMIName: "ami_test_name",
|
|
||||||
SourceAMIOwner: "ami_test_owner_id",
|
|
||||||
SourceAMIOwnerName: "ami_test_owner_alias",
|
|
||||||
SourceAMITags: map[string]string{
|
|
||||||
"key-1": "value-1",
|
|
||||||
"key-2": "value-2",
|
|
||||||
},
|
|
||||||
}
|
|
||||||
if !reflect.DeepEqual(*buildInfo, expected) {
|
|
||||||
t.Fatalf("Unexpected BuildInfoTemplate: expected %#v got %#v\n", expected, *buildInfo)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestInterpolateBuildInfo_extractBuildInfo_GeneratedDataWithSourceImageName(t *testing.T) {
|
|
||||||
state := testState()
|
|
||||||
state.Put("source_image", testImage())
|
|
||||||
generatedData := testGeneratedData(state)
|
|
||||||
extractBuildInfo("foo", state, &generatedData)
|
|
||||||
|
|
||||||
generatedDataState := state.Get("generated_data").(map[string]interface{})
|
|
||||||
|
|
||||||
if generatedDataState["SourceAMIName"] != "ami_test_name" {
|
|
||||||
t.Fatalf("Unexpected state SourceAMIName: expected %#v got %#v\n", "ami_test_name", generatedDataState["SourceAMIName"])
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,251 +0,0 @@
|
|||||||
package common
|
|
||||||
|
|
||||||
import (
|
|
||||||
"io/ioutil"
|
|
||||||
"os"
|
|
||||||
"regexp"
|
|
||||||
"testing"
|
|
||||||
|
|
||||||
"github.com/hashicorp/packer-plugin-sdk/communicator"
|
|
||||||
)
|
|
||||||
|
|
||||||
func init() {
|
|
||||||
// Clear out the AWS access key env vars so they don't
|
|
||||||
// affect our tests.
|
|
||||||
os.Setenv("AWS_ACCESS_KEY_ID", "")
|
|
||||||
os.Setenv("AWS_ACCESS_KEY", "")
|
|
||||||
os.Setenv("AWS_SECRET_ACCESS_KEY", "")
|
|
||||||
os.Setenv("AWS_SECRET_KEY", "")
|
|
||||||
}
|
|
||||||
|
|
||||||
func testConfig() *RunConfig {
|
|
||||||
return &RunConfig{
|
|
||||||
SourceAmi: "abcd",
|
|
||||||
InstanceType: "m1.small",
|
|
||||||
|
|
||||||
Comm: communicator.Config{
|
|
||||||
SSH: communicator.SSH{
|
|
||||||
SSHUsername: "foo",
|
|
||||||
},
|
|
||||||
},
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func testConfigFilter() *RunConfig {
|
|
||||||
config := testConfig()
|
|
||||||
config.SourceAmi = ""
|
|
||||||
config.SourceAmiFilter = AmiFilterOptions{}
|
|
||||||
return config
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestRunConfigPrepare(t *testing.T) {
|
|
||||||
c := testConfig()
|
|
||||||
err := c.Prepare(nil)
|
|
||||||
if len(err) > 0 {
|
|
||||||
t.Fatalf("err: %s", err)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestRunConfigPrepare_InstanceType(t *testing.T) {
|
|
||||||
c := testConfig()
|
|
||||||
c.InstanceType = ""
|
|
||||||
if err := c.Prepare(nil); len(err) != 1 {
|
|
||||||
t.Fatalf("Should error if an instance_type is not specified")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestRunConfigPrepare_SourceAmi(t *testing.T) {
|
|
||||||
c := testConfig()
|
|
||||||
c.SourceAmi = ""
|
|
||||||
if err := c.Prepare(nil); len(err) != 2 {
|
|
||||||
t.Fatalf("Should error if a source_ami (or source_ami_filter) is not specified")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestRunConfigPrepare_SourceAmiFilterBlank(t *testing.T) {
|
|
||||||
c := testConfigFilter()
|
|
||||||
if err := c.Prepare(nil); len(err) != 2 {
|
|
||||||
t.Fatalf("Should error if source_ami_filter is empty or not specified (and source_ami is not specified)")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestRunConfigPrepare_SourceAmiFilterOwnersBlank(t *testing.T) {
|
|
||||||
c := testConfigFilter()
|
|
||||||
filter_key := "name"
|
|
||||||
filter_value := "foo"
|
|
||||||
c.SourceAmiFilter.Filters = map[string]string{filter_key: filter_value}
|
|
||||||
if err := c.Prepare(nil); len(err) != 1 {
|
|
||||||
t.Fatalf("Should error if Owners is not specified)")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestRunConfigPrepare_SourceAmiFilterGood(t *testing.T) {
|
|
||||||
c := testConfigFilter()
|
|
||||||
owner := "123"
|
|
||||||
filter_key := "name"
|
|
||||||
filter_value := "foo"
|
|
||||||
goodFilter := AmiFilterOptions{
|
|
||||||
Owners: []string{owner},
|
|
||||||
Filters: map[string]string{filter_key: filter_value},
|
|
||||||
}
|
|
||||||
c.SourceAmiFilter = goodFilter
|
|
||||||
if err := c.Prepare(nil); len(err) != 0 {
|
|
||||||
t.Fatalf("err: %s", err)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestRunConfigPrepare_EnableT2UnlimitedGood(t *testing.T) {
|
|
||||||
c := testConfig()
|
|
||||||
// Must have a T2 instance type if T2 Unlimited is enabled
|
|
||||||
c.InstanceType = "t2.micro"
|
|
||||||
c.EnableT2Unlimited = true
|
|
||||||
err := c.Prepare(nil)
|
|
||||||
if len(err) > 0 {
|
|
||||||
t.Fatalf("err: %s", err)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestRunConfigPrepare_EnableT2UnlimitedBadInstanceType(t *testing.T) {
|
|
||||||
c := testConfig()
|
|
||||||
// T2 Unlimited cannot be used with instance types other than T2
|
|
||||||
c.InstanceType = "m5.large"
|
|
||||||
c.EnableT2Unlimited = true
|
|
||||||
err := c.Prepare(nil)
|
|
||||||
if len(err) != 1 {
|
|
||||||
t.Fatalf("Should error if T2 Unlimited is enabled with non-T2 instance_type")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestRunConfigPrepare_EnableT2UnlimitedBadWithSpotInstanceRequest(t *testing.T) {
|
|
||||||
c := testConfig()
|
|
||||||
// T2 Unlimited cannot be used with Spot Instances
|
|
||||||
c.InstanceType = "t2.micro"
|
|
||||||
c.EnableT2Unlimited = true
|
|
||||||
c.SpotPrice = "auto"
|
|
||||||
err := c.Prepare(nil)
|
|
||||||
if len(err) != 1 {
|
|
||||||
t.Fatalf("Should error if T2 Unlimited has been used in conjuntion with a Spot Price request")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestRunConfigPrepare_SpotAuto(t *testing.T) {
|
|
||||||
c := testConfig()
|
|
||||||
c.SpotPrice = "auto"
|
|
||||||
if err := c.Prepare(nil); len(err) != 0 {
|
|
||||||
t.Fatalf("err: %s", err)
|
|
||||||
}
|
|
||||||
|
|
||||||
// Shouldn't error (YET) even though SpotPriceAutoProduct is deprecated
|
|
||||||
c.SpotPriceAutoProduct = "Linux/Unix"
|
|
||||||
if err := c.Prepare(nil); len(err) != 0 {
|
|
||||||
t.Fatalf("err: %s", err)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestRunConfigPrepare_SSHPort(t *testing.T) {
|
|
||||||
c := testConfig()
|
|
||||||
c.Comm.SSHPort = 0
|
|
||||||
if err := c.Prepare(nil); len(err) != 0 {
|
|
||||||
t.Fatalf("err: %s", err)
|
|
||||||
}
|
|
||||||
|
|
||||||
if c.Comm.SSHPort != 22 {
|
|
||||||
t.Fatalf("invalid value: %d", c.Comm.SSHPort)
|
|
||||||
}
|
|
||||||
|
|
||||||
c.Comm.SSHPort = 44
|
|
||||||
if err := c.Prepare(nil); len(err) != 0 {
|
|
||||||
t.Fatalf("err: %s", err)
|
|
||||||
}
|
|
||||||
|
|
||||||
if c.Comm.SSHPort != 44 {
|
|
||||||
t.Fatalf("invalid value: %d", c.Comm.SSHPort)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestRunConfigPrepare_UserData(t *testing.T) {
|
|
||||||
c := testConfig()
|
|
||||||
tf, err := ioutil.TempFile("", "packer")
|
|
||||||
if err != nil {
|
|
||||||
t.Fatalf("err: %s", err)
|
|
||||||
}
|
|
||||||
defer os.Remove(tf.Name())
|
|
||||||
defer tf.Close()
|
|
||||||
|
|
||||||
c.UserData = "foo"
|
|
||||||
c.UserDataFile = tf.Name()
|
|
||||||
if err := c.Prepare(nil); len(err) != 1 {
|
|
||||||
t.Fatalf("Should error if user_data string and user_data_file have both been specified")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestRunConfigPrepare_UserDataFile(t *testing.T) {
|
|
||||||
c := testConfig()
|
|
||||||
if err := c.Prepare(nil); len(err) != 0 {
|
|
||||||
t.Fatalf("err: %s", err)
|
|
||||||
}
|
|
||||||
|
|
||||||
c.UserDataFile = "idontexistidontthink"
|
|
||||||
if err := c.Prepare(nil); len(err) != 1 {
|
|
||||||
t.Fatalf("Should error if the file specified by user_data_file does not exist")
|
|
||||||
}
|
|
||||||
|
|
||||||
tf, err := ioutil.TempFile("", "packer")
|
|
||||||
if err != nil {
|
|
||||||
t.Fatalf("err: %s", err)
|
|
||||||
}
|
|
||||||
defer os.Remove(tf.Name())
|
|
||||||
defer tf.Close()
|
|
||||||
|
|
||||||
c.UserDataFile = tf.Name()
|
|
||||||
if err := c.Prepare(nil); len(err) != 0 {
|
|
||||||
t.Fatalf("err: %s", err)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestRunConfigPrepare_TemporaryKeyPairName(t *testing.T) {
|
|
||||||
c := testConfig()
|
|
||||||
c.Comm.SSHTemporaryKeyPairName = ""
|
|
||||||
if err := c.Prepare(nil); len(err) != 0 {
|
|
||||||
t.Fatalf("err: %s", err)
|
|
||||||
}
|
|
||||||
|
|
||||||
if c.Comm.SSHTemporaryKeyPairName == "" {
|
|
||||||
t.Fatal("keypair name is empty")
|
|
||||||
}
|
|
||||||
|
|
||||||
// Match prefix and UUID, e.g. "packer_5790d491-a0b8-c84c-c9d2-2aea55086550".
|
|
||||||
r := regexp.MustCompile(`\Apacker_(?:(?i)[a-f\d]{8}(?:-[a-f\d]{4}){3}-[a-f\d]{12}?)\z`)
|
|
||||||
if !r.MatchString(c.Comm.SSHTemporaryKeyPairName) {
|
|
||||||
t.Fatal("keypair name is not valid")
|
|
||||||
}
|
|
||||||
|
|
||||||
c.Comm.SSHTemporaryKeyPairName = "ssh-key-123"
|
|
||||||
if err := c.Prepare(nil); len(err) != 0 {
|
|
||||||
t.Fatalf("err: %s", err)
|
|
||||||
}
|
|
||||||
|
|
||||||
if c.Comm.SSHTemporaryKeyPairName != "ssh-key-123" {
|
|
||||||
t.Fatal("keypair name does not match")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestRunConfigPrepare_TenancyBad(t *testing.T) {
|
|
||||||
c := testConfig()
|
|
||||||
c.Tenancy = "not_real"
|
|
||||||
|
|
||||||
if err := c.Prepare(nil); len(err) != 1 {
|
|
||||||
t.Fatal("Should error if tenancy is set to an invalid type")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestRunConfigPrepare_TenancyGood(t *testing.T) {
|
|
||||||
validTenancy := []string{"", "default", "dedicated", "host"}
|
|
||||||
for _, vt := range validTenancy {
|
|
||||||
c := testConfig()
|
|
||||||
c.Tenancy = vt
|
|
||||||
if err := c.Prepare(nil); len(err) != 0 {
|
|
||||||
t.Fatalf("Should not error if tenancy is set to %s", vt)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,141 +0,0 @@
|
|||||||
package common
|
|
||||||
|
|
||||||
import (
|
|
||||||
"testing"
|
|
||||||
|
|
||||||
"github.com/aws/aws-sdk-go/aws"
|
|
||||||
"github.com/aws/aws-sdk-go/service/ec2"
|
|
||||||
"github.com/hashicorp/packer-plugin-sdk/multistep"
|
|
||||||
)
|
|
||||||
|
|
||||||
const (
|
|
||||||
privateIP = "10.0.0.1"
|
|
||||||
publicIP = "192.168.1.1"
|
|
||||||
privateDNS = "private.dns.test"
|
|
||||||
publicDNS = "public.dns.test"
|
|
||||||
localhost = "localhost"
|
|
||||||
sshHostTemplate = "custom.host.value"
|
|
||||||
)
|
|
||||||
|
|
||||||
func TestSSHHost(t *testing.T) {
|
|
||||||
origSshHostSleepDuration := sshHostSleepDuration
|
|
||||||
defer func() { sshHostSleepDuration = origSshHostSleepDuration }()
|
|
||||||
sshHostSleepDuration = 0
|
|
||||||
|
|
||||||
var cases = []struct {
|
|
||||||
allowTries int
|
|
||||||
vpcId string
|
|
||||||
sshInterface string
|
|
||||||
|
|
||||||
ok bool
|
|
||||||
wantHost string
|
|
||||||
sshHostOverride string
|
|
||||||
}{
|
|
||||||
{1, "", "", true, publicDNS, ""},
|
|
||||||
{1, "", "private_ip", true, privateIP, ""},
|
|
||||||
{1, "", "session_manager", true, localhost, ""},
|
|
||||||
{1, "vpc-id", "", true, publicIP, ""},
|
|
||||||
{1, "vpc-id", "private_ip", true, privateIP, ""},
|
|
||||||
{1, "vpc-id", "private_dns", true, privateDNS, ""},
|
|
||||||
{1, "vpc-id", "public_dns", true, publicDNS, ""},
|
|
||||||
{1, "vpc-id", "public_ip", true, publicIP, ""},
|
|
||||||
{1, "vpc-id", "session_manager", true, localhost, ""},
|
|
||||||
{2, "", "", true, publicDNS, ""},
|
|
||||||
{2, "", "private_ip", true, privateIP, ""},
|
|
||||||
{2, "vpc-id", "", true, publicIP, ""},
|
|
||||||
{2, "vpc-id", "private_ip", true, privateIP, ""},
|
|
||||||
{2, "vpc-id", "private_dns", true, privateDNS, ""},
|
|
||||||
{2, "vpc-id", "public_dns", true, publicDNS, ""},
|
|
||||||
{2, "vpc-id", "public_ip", true, publicIP, ""},
|
|
||||||
{3, "", "", false, "", ""},
|
|
||||||
{3, "", "private_ip", false, "", ""},
|
|
||||||
{3, "vpc-id", "", false, "", ""},
|
|
||||||
{3, "vpc-id", "private_ip", false, "", ""},
|
|
||||||
{3, "vpc-id", "private_dns", false, "", ""},
|
|
||||||
{3, "vpc-id", "public_dns", false, "", ""},
|
|
||||||
{3, "vpc-id", "public_ip", false, "", ""},
|
|
||||||
{1, "", "", true, sshHostTemplate, sshHostTemplate},
|
|
||||||
{1, "vpc-id", "", true, sshHostTemplate, sshHostTemplate},
|
|
||||||
{2, "vpc-id", "private_dns", true, sshHostTemplate, sshHostTemplate},
|
|
||||||
}
|
|
||||||
|
|
||||||
for _, c := range cases {
|
|
||||||
testSSHHost(t, c.allowTries, c.vpcId, c.sshInterface, c.ok, c.wantHost,
|
|
||||||
c.sshHostOverride)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func testSSHHost(t *testing.T, allowTries int, vpcId string, sshInterface string,
|
|
||||||
ok bool, wantHost string, sshHostOverride string) {
|
|
||||||
t.Logf("allowTries=%d vpcId=%s sshInterface=%s ok=%t wantHost=%q sshHostOverride=%s",
|
|
||||||
allowTries, vpcId, sshInterface, ok, wantHost, sshHostOverride)
|
|
||||||
|
|
||||||
e := &fakeEC2Describer{
|
|
||||||
allowTries: allowTries,
|
|
||||||
vpcId: vpcId,
|
|
||||||
privateIP: privateIP,
|
|
||||||
publicIP: publicIP,
|
|
||||||
privateDNS: privateDNS,
|
|
||||||
publicDNS: publicDNS,
|
|
||||||
}
|
|
||||||
|
|
||||||
f := SSHHost(e, sshInterface, sshHostOverride)
|
|
||||||
st := &multistep.BasicStateBag{}
|
|
||||||
st.Put("instance", &ec2.Instance{
|
|
||||||
InstanceId: aws.String("instance-id"),
|
|
||||||
})
|
|
||||||
|
|
||||||
host, err := f(st)
|
|
||||||
|
|
||||||
if e.tries > allowTries {
|
|
||||||
t.Fatalf("got %d ec2 DescribeInstances tries, want %d", e.tries, allowTries)
|
|
||||||
}
|
|
||||||
|
|
||||||
switch {
|
|
||||||
case ok && err != nil:
|
|
||||||
t.Fatalf("expected no error, got %+v", err)
|
|
||||||
case !ok && err == nil:
|
|
||||||
t.Fatalf("expected error, got none and host %s", host)
|
|
||||||
}
|
|
||||||
|
|
||||||
if host != wantHost {
|
|
||||||
t.Fatalf("got host %s, want %s", host, wantHost)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
type fakeEC2Describer struct {
|
|
||||||
allowTries int
|
|
||||||
tries int
|
|
||||||
|
|
||||||
vpcId string
|
|
||||||
privateIP, publicIP, privateDNS, publicDNS string
|
|
||||||
}
|
|
||||||
|
|
||||||
func (d *fakeEC2Describer) DescribeInstances(in *ec2.DescribeInstancesInput) (*ec2.DescribeInstancesOutput, error) {
|
|
||||||
d.tries++
|
|
||||||
|
|
||||||
instance := &ec2.Instance{
|
|
||||||
InstanceId: aws.String("instance-id"),
|
|
||||||
}
|
|
||||||
|
|
||||||
if d.vpcId != "" {
|
|
||||||
instance.VpcId = aws.String(d.vpcId)
|
|
||||||
}
|
|
||||||
|
|
||||||
if d.tries >= d.allowTries {
|
|
||||||
instance.PublicIpAddress = aws.String(d.publicIP)
|
|
||||||
instance.PrivateIpAddress = aws.String(d.privateIP)
|
|
||||||
instance.PublicDnsName = aws.String(d.publicDNS)
|
|
||||||
instance.PrivateDnsName = aws.String(d.privateDNS)
|
|
||||||
}
|
|
||||||
|
|
||||||
out := &ec2.DescribeInstancesOutput{
|
|
||||||
Reservations: []*ec2.Reservation{
|
|
||||||
{
|
|
||||||
Instances: []*ec2.Instance{instance},
|
|
||||||
},
|
|
||||||
},
|
|
||||||
}
|
|
||||||
|
|
||||||
return out, nil
|
|
||||||
}
|
|
||||||
@@ -1,66 +0,0 @@
|
|||||||
package common
|
|
||||||
|
|
||||||
import (
|
|
||||||
"reflect"
|
|
||||||
"testing"
|
|
||||||
"time"
|
|
||||||
|
|
||||||
"github.com/aws/aws-sdk-go/aws/request"
|
|
||||||
)
|
|
||||||
|
|
||||||
func testGetWaiterOptions(t *testing.T) {
|
|
||||||
// no vars are set
|
|
||||||
envValues := overridableWaitVars{
|
|
||||||
envInfo{"AWS_POLL_DELAY_SECONDS", 2, false},
|
|
||||||
envInfo{"AWS_MAX_ATTEMPTS", 0, false},
|
|
||||||
envInfo{"AWS_TIMEOUT_SECONDS", 300, false},
|
|
||||||
}
|
|
||||||
options := applyEnvOverrides(envValues)
|
|
||||||
if len(options) > 0 {
|
|
||||||
t.Fatalf("Did not expect any waiter options to be generated; actual: %#v", options)
|
|
||||||
}
|
|
||||||
|
|
||||||
// all vars are set
|
|
||||||
envValues = overridableWaitVars{
|
|
||||||
envInfo{"AWS_POLL_DELAY_SECONDS", 1, true},
|
|
||||||
envInfo{"AWS_MAX_ATTEMPTS", 800, true},
|
|
||||||
envInfo{"AWS_TIMEOUT_SECONDS", 20, true},
|
|
||||||
}
|
|
||||||
options = applyEnvOverrides(envValues)
|
|
||||||
expected := []request.WaiterOption{
|
|
||||||
request.WithWaiterDelay(request.ConstantWaiterDelay(time.Duration(1) * time.Second)),
|
|
||||||
request.WithWaiterMaxAttempts(800),
|
|
||||||
}
|
|
||||||
if !reflect.DeepEqual(options, expected) {
|
|
||||||
t.Fatalf("expected != actual!! Expected: %#v; Actual: %#v.", expected, options)
|
|
||||||
}
|
|
||||||
|
|
||||||
// poll delay is not set
|
|
||||||
envValues = overridableWaitVars{
|
|
||||||
envInfo{"AWS_POLL_DELAY_SECONDS", 2, false},
|
|
||||||
envInfo{"AWS_MAX_ATTEMPTS", 800, true},
|
|
||||||
envInfo{"AWS_TIMEOUT_SECONDS", 300, false},
|
|
||||||
}
|
|
||||||
options = applyEnvOverrides(envValues)
|
|
||||||
expected = []request.WaiterOption{
|
|
||||||
request.WithWaiterMaxAttempts(800),
|
|
||||||
}
|
|
||||||
if !reflect.DeepEqual(options, expected) {
|
|
||||||
t.Fatalf("expected != actual!! Expected: %#v; Actual: %#v.", expected, options)
|
|
||||||
}
|
|
||||||
|
|
||||||
// poll delay is not set but timeout seconds is
|
|
||||||
envValues = overridableWaitVars{
|
|
||||||
envInfo{"AWS_POLL_DELAY_SECONDS", 2, false},
|
|
||||||
envInfo{"AWS_MAX_ATTEMPTS", 0, false},
|
|
||||||
envInfo{"AWS_TIMEOUT_SECONDS", 20, true},
|
|
||||||
}
|
|
||||||
options = applyEnvOverrides(envValues)
|
|
||||||
expected = []request.WaiterOption{
|
|
||||||
request.WithWaiterDelay(request.ConstantWaiterDelay(time.Duration(2) * time.Second)),
|
|
||||||
request.WithWaiterMaxAttempts(10),
|
|
||||||
}
|
|
||||||
if !reflect.DeepEqual(options, expected) {
|
|
||||||
t.Fatalf("expected != actual!! Expected: %#v; Actual: %#v.", expected, options)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,404 +0,0 @@
|
|||||||
package common
|
|
||||||
|
|
||||||
import (
|
|
||||||
"bytes"
|
|
||||||
"context"
|
|
||||||
"fmt"
|
|
||||||
"sync"
|
|
||||||
"testing"
|
|
||||||
|
|
||||||
"github.com/aws/aws-sdk-go/aws"
|
|
||||||
"github.com/aws/aws-sdk-go/aws/request"
|
|
||||||
"github.com/aws/aws-sdk-go/service/ec2"
|
|
||||||
"github.com/aws/aws-sdk-go/service/ec2/ec2iface"
|
|
||||||
"github.com/hashicorp/packer-plugin-sdk/multistep"
|
|
||||||
packersdk "github.com/hashicorp/packer-plugin-sdk/packer"
|
|
||||||
"github.com/hashicorp/packer-plugin-sdk/template/config"
|
|
||||||
)
|
|
||||||
|
|
||||||
// Define a mock struct to be used in unit tests for common aws steps.
|
|
||||||
type mockEC2Conn struct {
|
|
||||||
ec2iface.EC2API
|
|
||||||
Config *aws.Config
|
|
||||||
|
|
||||||
// Counters to figure out what code path was taken
|
|
||||||
copyImageCount int
|
|
||||||
describeImagesCount int
|
|
||||||
deregisterImageCount int
|
|
||||||
deleteSnapshotCount int
|
|
||||||
waitCount int
|
|
||||||
|
|
||||||
lock sync.Mutex
|
|
||||||
}
|
|
||||||
|
|
||||||
func (m *mockEC2Conn) CopyImage(copyInput *ec2.CopyImageInput) (*ec2.CopyImageOutput, error) {
|
|
||||||
m.lock.Lock()
|
|
||||||
m.copyImageCount++
|
|
||||||
m.lock.Unlock()
|
|
||||||
copiedImage := fmt.Sprintf("%s-copied-%d", *copyInput.SourceImageId, m.copyImageCount)
|
|
||||||
output := &ec2.CopyImageOutput{
|
|
||||||
ImageId: &copiedImage,
|
|
||||||
}
|
|
||||||
return output, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// functions we have to create mock responses for in order for test to run
|
|
||||||
func (m *mockEC2Conn) DescribeImages(*ec2.DescribeImagesInput) (*ec2.DescribeImagesOutput, error) {
|
|
||||||
m.lock.Lock()
|
|
||||||
m.describeImagesCount++
|
|
||||||
m.lock.Unlock()
|
|
||||||
output := &ec2.DescribeImagesOutput{
|
|
||||||
Images: []*ec2.Image{{}},
|
|
||||||
}
|
|
||||||
return output, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func (m *mockEC2Conn) DeregisterImage(*ec2.DeregisterImageInput) (*ec2.DeregisterImageOutput, error) {
|
|
||||||
m.lock.Lock()
|
|
||||||
m.deregisterImageCount++
|
|
||||||
m.lock.Unlock()
|
|
||||||
output := &ec2.DeregisterImageOutput{}
|
|
||||||
return output, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func (m *mockEC2Conn) DeleteSnapshot(*ec2.DeleteSnapshotInput) (*ec2.DeleteSnapshotOutput, error) {
|
|
||||||
m.lock.Lock()
|
|
||||||
m.deleteSnapshotCount++
|
|
||||||
m.lock.Unlock()
|
|
||||||
output := &ec2.DeleteSnapshotOutput{}
|
|
||||||
return output, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func (m *mockEC2Conn) WaitUntilImageAvailableWithContext(aws.Context, *ec2.DescribeImagesInput, ...request.WaiterOption) error {
|
|
||||||
m.lock.Lock()
|
|
||||||
m.waitCount++
|
|
||||||
m.lock.Unlock()
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func getMockConn(config *AccessConfig, target string) (ec2iface.EC2API, error) {
|
|
||||||
mockConn := &mockEC2Conn{
|
|
||||||
Config: aws.NewConfig(),
|
|
||||||
}
|
|
||||||
|
|
||||||
return mockConn, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// Create statebag for running test
|
|
||||||
func tState() multistep.StateBag {
|
|
||||||
state := new(multistep.BasicStateBag)
|
|
||||||
state.Put("ui", &packersdk.BasicUi{
|
|
||||||
Reader: new(bytes.Buffer),
|
|
||||||
Writer: new(bytes.Buffer),
|
|
||||||
})
|
|
||||||
state.Put("amis", map[string]string{"us-east-1": "ami-12345"})
|
|
||||||
state.Put("snapshots", map[string][]string{"us-east-1": {"snap-0012345"}})
|
|
||||||
conn, _ := getMockConn(&AccessConfig{}, "us-east-2")
|
|
||||||
state.Put("ec2", conn)
|
|
||||||
return state
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestStepAMIRegionCopy_duplicates(t *testing.T) {
|
|
||||||
// ------------------------------------------------------------------------
|
|
||||||
// Test that if the original region is added to both Regions and Region,
|
|
||||||
// the ami is only copied once (with encryption).
|
|
||||||
// ------------------------------------------------------------------------
|
|
||||||
|
|
||||||
stepAMIRegionCopy := StepAMIRegionCopy{
|
|
||||||
AccessConfig: FakeAccessConfig(),
|
|
||||||
Regions: []string{"us-east-1"},
|
|
||||||
AMIKmsKeyId: "12345",
|
|
||||||
// Original region key in regionkeyids is different than in amikmskeyid
|
|
||||||
RegionKeyIds: map[string]string{"us-east-1": "12345"},
|
|
||||||
EncryptBootVolume: config.TriTrue,
|
|
||||||
Name: "fake-ami-name",
|
|
||||||
OriginalRegion: "us-east-1",
|
|
||||||
}
|
|
||||||
// mock out the region connection code
|
|
||||||
stepAMIRegionCopy.getRegionConn = getMockConn
|
|
||||||
|
|
||||||
state := tState()
|
|
||||||
state.Put("intermediary_image", true)
|
|
||||||
stepAMIRegionCopy.Run(context.Background(), state)
|
|
||||||
|
|
||||||
if len(stepAMIRegionCopy.Regions) != 1 {
|
|
||||||
t.Fatalf("Should have added original ami to Regions one time only")
|
|
||||||
}
|
|
||||||
|
|
||||||
// ------------------------------------------------------------------------
|
|
||||||
// Both Region and Regions set, but no encryption - shouldn't copy anything
|
|
||||||
// ------------------------------------------------------------------------
|
|
||||||
|
|
||||||
// the ami is only copied once.
|
|
||||||
stepAMIRegionCopy = StepAMIRegionCopy{
|
|
||||||
AccessConfig: FakeAccessConfig(),
|
|
||||||
Regions: []string{"us-east-1"},
|
|
||||||
Name: "fake-ami-name",
|
|
||||||
OriginalRegion: "us-east-1",
|
|
||||||
}
|
|
||||||
// mock out the region connection code
|
|
||||||
state.Put("intermediary_image", false)
|
|
||||||
stepAMIRegionCopy.getRegionConn = getMockConn
|
|
||||||
stepAMIRegionCopy.Run(context.Background(), state)
|
|
||||||
|
|
||||||
if len(stepAMIRegionCopy.Regions) != 0 {
|
|
||||||
t.Fatalf("Should not have added original ami to Regions; not encrypting")
|
|
||||||
}
|
|
||||||
|
|
||||||
// ------------------------------------------------------------------------
|
|
||||||
// Both Region and Regions set, but no encryption - shouldn't copy anything,
|
|
||||||
// this tests false as opposed to nil value above.
|
|
||||||
// ------------------------------------------------------------------------
|
|
||||||
|
|
||||||
// the ami is only copied once.
|
|
||||||
stepAMIRegionCopy = StepAMIRegionCopy{
|
|
||||||
AccessConfig: FakeAccessConfig(),
|
|
||||||
Regions: []string{"us-east-1"},
|
|
||||||
EncryptBootVolume: config.TriFalse,
|
|
||||||
Name: "fake-ami-name",
|
|
||||||
OriginalRegion: "us-east-1",
|
|
||||||
}
|
|
||||||
// mock out the region connection code
|
|
||||||
state.Put("intermediary_image", false)
|
|
||||||
stepAMIRegionCopy.getRegionConn = getMockConn
|
|
||||||
stepAMIRegionCopy.Run(context.Background(), state)
|
|
||||||
|
|
||||||
if len(stepAMIRegionCopy.Regions) != 0 {
|
|
||||||
t.Fatalf("Should not have added original ami to Regions once; not" +
|
|
||||||
"encrypting")
|
|
||||||
}
|
|
||||||
|
|
||||||
// ------------------------------------------------------------------------
|
|
||||||
// Multiple regions, many duplicates, and encryption (this shouldn't ever
|
|
||||||
// happen because of our template validation, but good to test it.)
|
|
||||||
// ------------------------------------------------------------------------
|
|
||||||
|
|
||||||
stepAMIRegionCopy = StepAMIRegionCopy{
|
|
||||||
AccessConfig: FakeAccessConfig(),
|
|
||||||
// Many duplicates for only 3 actual values
|
|
||||||
Regions: []string{"us-east-1", "us-west-2", "us-west-2", "ap-east-1", "ap-east-1", "ap-east-1"},
|
|
||||||
AMIKmsKeyId: "IlikePancakes",
|
|
||||||
// Original region key in regionkeyids is different than in amikmskeyid
|
|
||||||
RegionKeyIds: map[string]string{"us-east-1": "12345", "us-west-2": "abcde", "ap-east-1": "xyz"},
|
|
||||||
EncryptBootVolume: config.TriTrue,
|
|
||||||
Name: "fake-ami-name",
|
|
||||||
OriginalRegion: "us-east-1",
|
|
||||||
}
|
|
||||||
// mock out the region connection code
|
|
||||||
stepAMIRegionCopy.getRegionConn = getMockConn
|
|
||||||
state.Put("intermediary_image", true)
|
|
||||||
stepAMIRegionCopy.Run(context.Background(), state)
|
|
||||||
|
|
||||||
if len(stepAMIRegionCopy.Regions) != 3 {
|
|
||||||
t.Fatalf("Each AMI should have been added to Regions one time only.")
|
|
||||||
}
|
|
||||||
|
|
||||||
// Also verify that we respect RegionKeyIds over AMIKmsKeyIds:
|
|
||||||
if stepAMIRegionCopy.RegionKeyIds["us-east-1"] != "12345" {
|
|
||||||
t.Fatalf("RegionKeyIds should take precedence over AmiKmsKeyIds")
|
|
||||||
}
|
|
||||||
|
|
||||||
// ------------------------------------------------------------------------
|
|
||||||
// Multiple regions, many duplicates, NO encryption
|
|
||||||
// ------------------------------------------------------------------------
|
|
||||||
|
|
||||||
stepAMIRegionCopy = StepAMIRegionCopy{
|
|
||||||
AccessConfig: FakeAccessConfig(),
|
|
||||||
// Many duplicates for only 3 actual values
|
|
||||||
Regions: []string{"us-east-1", "us-west-2", "us-west-2", "ap-east-1", "ap-east-1", "ap-east-1"},
|
|
||||||
Name: "fake-ami-name",
|
|
||||||
OriginalRegion: "us-east-1",
|
|
||||||
}
|
|
||||||
// mock out the region connection code
|
|
||||||
stepAMIRegionCopy.getRegionConn = getMockConn
|
|
||||||
state.Put("intermediary_image", false)
|
|
||||||
stepAMIRegionCopy.Run(context.Background(), state)
|
|
||||||
|
|
||||||
if len(stepAMIRegionCopy.Regions) != 2 {
|
|
||||||
t.Fatalf("Each AMI should have been added to Regions one time only, " +
|
|
||||||
"and original region shouldn't be added at all")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestStepAmiRegionCopy_nil_encryption(t *testing.T) {
|
|
||||||
// create step
|
|
||||||
stepAMIRegionCopy := StepAMIRegionCopy{
|
|
||||||
AccessConfig: FakeAccessConfig(),
|
|
||||||
Regions: make([]string, 0),
|
|
||||||
AMIKmsKeyId: "",
|
|
||||||
RegionKeyIds: make(map[string]string),
|
|
||||||
EncryptBootVolume: config.TriUnset,
|
|
||||||
Name: "fake-ami-name",
|
|
||||||
OriginalRegion: "us-east-1",
|
|
||||||
}
|
|
||||||
// mock out the region connection code
|
|
||||||
stepAMIRegionCopy.getRegionConn = getMockConn
|
|
||||||
|
|
||||||
state := tState()
|
|
||||||
state.Put("intermediary_image", false)
|
|
||||||
stepAMIRegionCopy.Run(context.Background(), state)
|
|
||||||
|
|
||||||
if stepAMIRegionCopy.toDelete != "" {
|
|
||||||
t.Fatalf("Shouldn't have an intermediary ami if encrypt is nil")
|
|
||||||
}
|
|
||||||
if len(stepAMIRegionCopy.Regions) != 0 {
|
|
||||||
t.Fatalf("Should not have added original ami to original region")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestStepAmiRegionCopy_true_encryption(t *testing.T) {
|
|
||||||
// create step
|
|
||||||
stepAMIRegionCopy := StepAMIRegionCopy{
|
|
||||||
AccessConfig: FakeAccessConfig(),
|
|
||||||
Regions: make([]string, 0),
|
|
||||||
AMIKmsKeyId: "",
|
|
||||||
RegionKeyIds: make(map[string]string),
|
|
||||||
EncryptBootVolume: config.TriTrue,
|
|
||||||
Name: "fake-ami-name",
|
|
||||||
OriginalRegion: "us-east-1",
|
|
||||||
}
|
|
||||||
// mock out the region connection code
|
|
||||||
stepAMIRegionCopy.getRegionConn = getMockConn
|
|
||||||
|
|
||||||
state := tState()
|
|
||||||
state.Put("intermediary_image", true)
|
|
||||||
stepAMIRegionCopy.Run(context.Background(), state)
|
|
||||||
|
|
||||||
if stepAMIRegionCopy.toDelete == "" {
|
|
||||||
t.Fatalf("Should delete original AMI if encrypted=true")
|
|
||||||
}
|
|
||||||
if len(stepAMIRegionCopy.Regions) == 0 {
|
|
||||||
t.Fatalf("Should have added original ami to Regions")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestStepAmiRegionCopy_nil_intermediary(t *testing.T) {
|
|
||||||
// create step
|
|
||||||
stepAMIRegionCopy := StepAMIRegionCopy{
|
|
||||||
AccessConfig: FakeAccessConfig(),
|
|
||||||
Regions: make([]string, 0),
|
|
||||||
AMIKmsKeyId: "",
|
|
||||||
RegionKeyIds: make(map[string]string),
|
|
||||||
EncryptBootVolume: config.TriFalse,
|
|
||||||
Name: "fake-ami-name",
|
|
||||||
OriginalRegion: "us-east-1",
|
|
||||||
}
|
|
||||||
// mock out the region connection code
|
|
||||||
stepAMIRegionCopy.getRegionConn = getMockConn
|
|
||||||
|
|
||||||
state := tState()
|
|
||||||
stepAMIRegionCopy.Run(context.Background(), state)
|
|
||||||
|
|
||||||
if stepAMIRegionCopy.toDelete != "" {
|
|
||||||
t.Fatalf("Should not delete original AMI if no intermediary")
|
|
||||||
}
|
|
||||||
if len(stepAMIRegionCopy.Regions) != 0 {
|
|
||||||
t.Fatalf("Should not have added original ami to Regions")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestStepAmiRegionCopy_AMISkipBuildRegion(t *testing.T) {
|
|
||||||
// ------------------------------------------------------------------------
|
|
||||||
// skip build region is true
|
|
||||||
// ------------------------------------------------------------------------
|
|
||||||
|
|
||||||
stepAMIRegionCopy := StepAMIRegionCopy{
|
|
||||||
AccessConfig: FakeAccessConfig(),
|
|
||||||
Regions: []string{"us-west-1"},
|
|
||||||
AMIKmsKeyId: "",
|
|
||||||
RegionKeyIds: map[string]string{"us-west-1": "abcde"},
|
|
||||||
Name: "fake-ami-name",
|
|
||||||
OriginalRegion: "us-east-1",
|
|
||||||
AMISkipBuildRegion: true,
|
|
||||||
}
|
|
||||||
// mock out the region connection code
|
|
||||||
stepAMIRegionCopy.getRegionConn = getMockConn
|
|
||||||
|
|
||||||
state := tState()
|
|
||||||
state.Put("intermediary_image", true)
|
|
||||||
stepAMIRegionCopy.Run(context.Background(), state)
|
|
||||||
|
|
||||||
if stepAMIRegionCopy.toDelete == "" {
|
|
||||||
t.Fatalf("Should delete original AMI if skip_save_build_region=true")
|
|
||||||
}
|
|
||||||
if len(stepAMIRegionCopy.Regions) != 1 {
|
|
||||||
t.Fatalf("Should not have added original ami to Regions; Regions: %#v", stepAMIRegionCopy.Regions)
|
|
||||||
}
|
|
||||||
|
|
||||||
// ------------------------------------------------------------------------
|
|
||||||
// skip build region is false.
|
|
||||||
// ------------------------------------------------------------------------
|
|
||||||
stepAMIRegionCopy = StepAMIRegionCopy{
|
|
||||||
AccessConfig: FakeAccessConfig(),
|
|
||||||
Regions: []string{"us-west-1"},
|
|
||||||
AMIKmsKeyId: "",
|
|
||||||
RegionKeyIds: make(map[string]string),
|
|
||||||
Name: "fake-ami-name",
|
|
||||||
OriginalRegion: "us-east-1",
|
|
||||||
AMISkipBuildRegion: false,
|
|
||||||
}
|
|
||||||
// mock out the region connection code
|
|
||||||
stepAMIRegionCopy.getRegionConn = getMockConn
|
|
||||||
|
|
||||||
state.Put("intermediary_image", false) // not encrypted
|
|
||||||
stepAMIRegionCopy.Run(context.Background(), state)
|
|
||||||
|
|
||||||
if stepAMIRegionCopy.toDelete != "" {
|
|
||||||
t.Fatalf("Shouldn't have an intermediary AMI, so dont delete original ami")
|
|
||||||
}
|
|
||||||
if len(stepAMIRegionCopy.Regions) != 1 {
|
|
||||||
t.Fatalf("Should not have added original ami to Regions; Regions: %#v", stepAMIRegionCopy.Regions)
|
|
||||||
}
|
|
||||||
|
|
||||||
// ------------------------------------------------------------------------
|
|
||||||
// skip build region is false, but encrypt is true
|
|
||||||
// ------------------------------------------------------------------------
|
|
||||||
stepAMIRegionCopy = StepAMIRegionCopy{
|
|
||||||
AccessConfig: FakeAccessConfig(),
|
|
||||||
Regions: []string{"us-west-1"},
|
|
||||||
AMIKmsKeyId: "",
|
|
||||||
RegionKeyIds: map[string]string{"us-west-1": "abcde"},
|
|
||||||
Name: "fake-ami-name",
|
|
||||||
OriginalRegion: "us-east-1",
|
|
||||||
AMISkipBuildRegion: false,
|
|
||||||
EncryptBootVolume: config.TriTrue,
|
|
||||||
}
|
|
||||||
// mock out the region connection code
|
|
||||||
stepAMIRegionCopy.getRegionConn = getMockConn
|
|
||||||
|
|
||||||
state.Put("intermediary_image", true) //encrypted
|
|
||||||
stepAMIRegionCopy.Run(context.Background(), state)
|
|
||||||
|
|
||||||
if stepAMIRegionCopy.toDelete == "" {
|
|
||||||
t.Fatalf("Have to delete intermediary AMI")
|
|
||||||
}
|
|
||||||
if len(stepAMIRegionCopy.Regions) != 2 {
|
|
||||||
t.Fatalf("Should have added original ami to Regions; Regions: %#v", stepAMIRegionCopy.Regions)
|
|
||||||
}
|
|
||||||
|
|
||||||
// ------------------------------------------------------------------------
|
|
||||||
// skip build region is true, and encrypt is true
|
|
||||||
// ------------------------------------------------------------------------
|
|
||||||
stepAMIRegionCopy = StepAMIRegionCopy{
|
|
||||||
AccessConfig: FakeAccessConfig(),
|
|
||||||
Regions: []string{"us-west-1"},
|
|
||||||
AMIKmsKeyId: "",
|
|
||||||
RegionKeyIds: map[string]string{"us-west-1": "abcde"},
|
|
||||||
Name: "fake-ami-name",
|
|
||||||
OriginalRegion: "us-east-1",
|
|
||||||
AMISkipBuildRegion: true,
|
|
||||||
EncryptBootVolume: config.TriTrue,
|
|
||||||
}
|
|
||||||
// mock out the region connection code
|
|
||||||
stepAMIRegionCopy.getRegionConn = getMockConn
|
|
||||||
|
|
||||||
state.Put("intermediary_image", true) //encrypted
|
|
||||||
stepAMIRegionCopy.Run(context.Background(), state)
|
|
||||||
|
|
||||||
if stepAMIRegionCopy.toDelete == "" {
|
|
||||||
t.Fatalf("Have to delete intermediary AMI")
|
|
||||||
}
|
|
||||||
if len(stepAMIRegionCopy.Regions) != 1 {
|
|
||||||
t.Fatalf("Should not have added original ami to Regions; Regions: %#v", stepAMIRegionCopy.Regions)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,70 +0,0 @@
|
|||||||
package common
|
|
||||||
|
|
||||||
import (
|
|
||||||
"fmt"
|
|
||||||
"strings"
|
|
||||||
"testing"
|
|
||||||
|
|
||||||
"github.com/aws/aws-sdk-go/aws"
|
|
||||||
"github.com/aws/aws-sdk-go/service/ec2"
|
|
||||||
)
|
|
||||||
|
|
||||||
//DescribeVpcs mocks an ec2.DescribeVpcsOutput for a given input
|
|
||||||
func (m *mockEC2Conn) DescribeVpcs(input *ec2.DescribeVpcsInput) (*ec2.DescribeVpcsOutput, error) {
|
|
||||||
|
|
||||||
if input == nil || aws.StringValue(input.VpcIds[0]) == "" {
|
|
||||||
return nil, fmt.Errorf("oops looks like we need more input")
|
|
||||||
}
|
|
||||||
|
|
||||||
var isDefault bool
|
|
||||||
vpcID := aws.StringValue(input.VpcIds[0])
|
|
||||||
|
|
||||||
//only one default VPC per region
|
|
||||||
if strings.Contains("vpc-default-id", vpcID) {
|
|
||||||
isDefault = true
|
|
||||||
}
|
|
||||||
|
|
||||||
output := &ec2.DescribeVpcsOutput{
|
|
||||||
Vpcs: []*ec2.Vpc{
|
|
||||||
{IsDefault: aws.Bool(isDefault),
|
|
||||||
VpcId: aws.String(vpcID),
|
|
||||||
},
|
|
||||||
},
|
|
||||||
}
|
|
||||||
return output, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestStepPreValidate_checkVpc(t *testing.T) {
|
|
||||||
tt := []struct {
|
|
||||||
name string
|
|
||||||
step StepPreValidate
|
|
||||||
errorExpected bool
|
|
||||||
}{
|
|
||||||
{"DefaultVpc", StepPreValidate{VpcId: "vpc-default-id"}, false},
|
|
||||||
{"NonDefaultVpcNoSubnet", StepPreValidate{VpcId: "vpc-1234567890"}, true},
|
|
||||||
{"NonDefaultVpcWithSubnet", StepPreValidate{VpcId: "vpc-1234567890", SubnetId: "subnet-1234567890"}, false},
|
|
||||||
{"SubnetWithNoVpc", StepPreValidate{SubnetId: "subnet-1234567890"}, false},
|
|
||||||
{"NoVpcInformation", StepPreValidate{}, false},
|
|
||||||
{"NonDefaultVpcWithSubnetFilter", StepPreValidate{VpcId: "vpc-1234567890", HasSubnetFilter: true}, false},
|
|
||||||
}
|
|
||||||
|
|
||||||
mockConn, err := getMockConn(nil, "")
|
|
||||||
if err != nil {
|
|
||||||
t.Fatal("unable to get a mock connection")
|
|
||||||
}
|
|
||||||
|
|
||||||
for _, tc := range tt {
|
|
||||||
t.Run(tc.name, func(t *testing.T) {
|
|
||||||
err := tc.step.checkVpc(mockConn)
|
|
||||||
|
|
||||||
if tc.errorExpected && err == nil {
|
|
||||||
t.Errorf("expected a validation error for %q but got %q", tc.name, err)
|
|
||||||
}
|
|
||||||
|
|
||||||
if !tc.errorExpected && err != nil {
|
|
||||||
t.Errorf("expected a validation to pass for %q but got %q", tc.name, err)
|
|
||||||
}
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
@@ -1,366 +0,0 @@
|
|||||||
package common
|
|
||||||
|
|
||||||
import (
|
|
||||||
"bytes"
|
|
||||||
"context"
|
|
||||||
"fmt"
|
|
||||||
"testing"
|
|
||||||
|
|
||||||
"github.com/aws/aws-sdk-go/aws"
|
|
||||||
"github.com/aws/aws-sdk-go/service/ec2"
|
|
||||||
"github.com/aws/aws-sdk-go/service/ec2/ec2iface"
|
|
||||||
"github.com/hashicorp/packer-plugin-sdk/communicator"
|
|
||||||
"github.com/hashicorp/packer-plugin-sdk/multistep"
|
|
||||||
packersdk "github.com/hashicorp/packer-plugin-sdk/packer"
|
|
||||||
)
|
|
||||||
|
|
||||||
// Create statebag for running test
|
|
||||||
func tStateSpot() multistep.StateBag {
|
|
||||||
state := new(multistep.BasicStateBag)
|
|
||||||
state.Put("ui", &packersdk.BasicUi{
|
|
||||||
Reader: new(bytes.Buffer),
|
|
||||||
Writer: new(bytes.Buffer),
|
|
||||||
})
|
|
||||||
state.Put("availability_zone", "us-east-1c")
|
|
||||||
state.Put("securityGroupIds", []string{"sg-0b8984db72f213dc3"})
|
|
||||||
state.Put("iamInstanceProfile", "packer-123")
|
|
||||||
state.Put("subnet_id", "subnet-077fde4e")
|
|
||||||
state.Put("source_image", "")
|
|
||||||
return state
|
|
||||||
}
|
|
||||||
|
|
||||||
func getBasicStep() *StepRunSpotInstance {
|
|
||||||
stepRunSpotInstance := StepRunSpotInstance{
|
|
||||||
PollingConfig: new(AWSPollingConfig),
|
|
||||||
AssociatePublicIpAddress: false,
|
|
||||||
LaunchMappings: BlockDevices{},
|
|
||||||
BlockDurationMinutes: 0,
|
|
||||||
Debug: false,
|
|
||||||
Comm: &communicator.Config{
|
|
||||||
SSH: communicator.SSH{
|
|
||||||
SSHKeyPairName: "foo",
|
|
||||||
},
|
|
||||||
},
|
|
||||||
EbsOptimized: false,
|
|
||||||
ExpectedRootDevice: "ebs",
|
|
||||||
InstanceInitiatedShutdownBehavior: "stop",
|
|
||||||
InstanceType: "t2.micro",
|
|
||||||
Region: "us-east-1",
|
|
||||||
SourceAMI: "",
|
|
||||||
SpotPrice: "auto",
|
|
||||||
SpotTags: nil,
|
|
||||||
Tags: map[string]string{},
|
|
||||||
VolumeTags: nil,
|
|
||||||
UserData: "",
|
|
||||||
UserDataFile: "",
|
|
||||||
}
|
|
||||||
|
|
||||||
return &stepRunSpotInstance
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestCreateTemplateData(t *testing.T) {
|
|
||||||
state := tStateSpot()
|
|
||||||
stepRunSpotInstance := getBasicStep()
|
|
||||||
template := stepRunSpotInstance.CreateTemplateData(aws.String("userdata"), "az", state,
|
|
||||||
&ec2.LaunchTemplateInstanceMarketOptionsRequest{})
|
|
||||||
|
|
||||||
// expected := []*ec2.LaunchTemplateInstanceNetworkInterfaceSpecificationRequest{
|
|
||||||
// &ec2.LaunchTemplateInstanceNetworkInterfaceSpecificationRequest{
|
|
||||||
// DeleteOnTermination: aws.Bool(true),
|
|
||||||
// DeviceIndex: aws.Int64(0),
|
|
||||||
// Groups: aws.StringSlice([]string{"sg-0b8984db72f213dc3"}),
|
|
||||||
// SubnetId: aws.String("subnet-077fde4e"),
|
|
||||||
// },
|
|
||||||
// }
|
|
||||||
// if expected != template.NetworkInterfaces {
|
|
||||||
if template.NetworkInterfaces == nil {
|
|
||||||
t.Fatalf("Template should have contained a networkInterface object: recieved %#v", template.NetworkInterfaces)
|
|
||||||
}
|
|
||||||
|
|
||||||
if *template.IamInstanceProfile.Name != state.Get("iamInstanceProfile") {
|
|
||||||
t.Fatalf("Template should have contained a InstanceProfile name: recieved %#v", template.IamInstanceProfile.Name)
|
|
||||||
}
|
|
||||||
|
|
||||||
// Rerun, this time testing that we set security group IDs
|
|
||||||
state.Put("subnet_id", "")
|
|
||||||
template = stepRunSpotInstance.CreateTemplateData(aws.String("userdata"), "az", state,
|
|
||||||
&ec2.LaunchTemplateInstanceMarketOptionsRequest{})
|
|
||||||
if template.NetworkInterfaces != nil {
|
|
||||||
t.Fatalf("Template shouldn't contain network interfaces object if subnet_id is unset.")
|
|
||||||
}
|
|
||||||
|
|
||||||
// Rerun, this time testing that instance doesn't have instance profile is iamInstanceProfile is unset
|
|
||||||
state.Put("iamInstanceProfile", "")
|
|
||||||
template = stepRunSpotInstance.CreateTemplateData(aws.String("userdata"), "az", state,
|
|
||||||
&ec2.LaunchTemplateInstanceMarketOptionsRequest{})
|
|
||||||
fmt.Println(template.IamInstanceProfile)
|
|
||||||
if *template.IamInstanceProfile.Name != "" {
|
|
||||||
t.Fatalf("Template shouldn't contain instance profile if iamInstanceProfile is unset.")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestCreateTemplateData_NoEphemeral(t *testing.T) {
|
|
||||||
state := tStateSpot()
|
|
||||||
stepRunSpotInstance := getBasicStep()
|
|
||||||
stepRunSpotInstance.NoEphemeral = true
|
|
||||||
template := stepRunSpotInstance.CreateTemplateData(aws.String("userdata"), "az", state,
|
|
||||||
&ec2.LaunchTemplateInstanceMarketOptionsRequest{})
|
|
||||||
if len(template.BlockDeviceMappings) != 26 {
|
|
||||||
t.Fatalf("Should have created 26 mappings to keep ephemeral drives from appearing.")
|
|
||||||
}
|
|
||||||
|
|
||||||
// Now check that noEphemeral doesn't mess with the mappings in real life.
|
|
||||||
// state = tStateSpot()
|
|
||||||
// stepRunSpotInstance = getBasicStep()
|
|
||||||
// stepRunSpotInstance.NoEphemeral = true
|
|
||||||
// mappings := []*ec2.InstanceBlockDeviceMapping{
|
|
||||||
// &ec2.InstanceBlockDeviceMapping{
|
|
||||||
// DeviceName: "xvda",
|
|
||||||
// Ebs: {
|
|
||||||
// DeleteOnTermination: true,
|
|
||||||
// Status: "attaching",
|
|
||||||
// VolumeId: "vol-044cd49c330f21c05",
|
|
||||||
// },
|
|
||||||
// },
|
|
||||||
// &ec2.InstanceBlockDeviceMapping{
|
|
||||||
// DeviceName: "/dev/xvdf",
|
|
||||||
// Ebs: {
|
|
||||||
// DeleteOnTermination: false,
|
|
||||||
// Status: "attaching",
|
|
||||||
// VolumeId: "vol-0eefaf2d6ae35827e",
|
|
||||||
// },
|
|
||||||
// },
|
|
||||||
// }
|
|
||||||
// template = stepRunSpotInstance.CreateTemplateData(aws.String("userdata"), "az", state,
|
|
||||||
// &ec2.LaunchTemplateInstanceMarketOptionsRequest{})
|
|
||||||
// if len(*template.BlockDeviceMappings) != 26 {
|
|
||||||
// t.Fatalf("Should have created 26 mappings to keep ephemeral drives from appearing.")
|
|
||||||
// }
|
|
||||||
}
|
|
||||||
|
|
||||||
type runSpotEC2ConnMock struct {
|
|
||||||
ec2iface.EC2API
|
|
||||||
|
|
||||||
CreateLaunchTemplateParams []*ec2.CreateLaunchTemplateInput
|
|
||||||
CreateLaunchTemplateFn func(*ec2.CreateLaunchTemplateInput) (*ec2.CreateLaunchTemplateOutput, error)
|
|
||||||
|
|
||||||
CreateFleetParams []*ec2.CreateFleetInput
|
|
||||||
CreateFleetFn func(*ec2.CreateFleetInput) (*ec2.CreateFleetOutput, error)
|
|
||||||
|
|
||||||
CreateTagsParams []*ec2.CreateTagsInput
|
|
||||||
CreateTagsFn func(*ec2.CreateTagsInput) (*ec2.CreateTagsOutput, error)
|
|
||||||
|
|
||||||
DescribeInstancesParams []*ec2.DescribeInstancesInput
|
|
||||||
DescribeInstancesFn func(input *ec2.DescribeInstancesInput) (*ec2.DescribeInstancesOutput, error)
|
|
||||||
}
|
|
||||||
|
|
||||||
func (m *runSpotEC2ConnMock) CreateLaunchTemplate(req *ec2.CreateLaunchTemplateInput) (*ec2.CreateLaunchTemplateOutput, error) {
|
|
||||||
m.CreateLaunchTemplateParams = append(m.CreateLaunchTemplateParams, req)
|
|
||||||
resp, err := m.CreateLaunchTemplateFn(req)
|
|
||||||
return resp, err
|
|
||||||
}
|
|
||||||
|
|
||||||
func (m *runSpotEC2ConnMock) CreateFleet(req *ec2.CreateFleetInput) (*ec2.CreateFleetOutput, error) {
|
|
||||||
m.CreateFleetParams = append(m.CreateFleetParams, req)
|
|
||||||
if m.CreateFleetFn != nil {
|
|
||||||
resp, err := m.CreateFleetFn(req)
|
|
||||||
return resp, err
|
|
||||||
} else {
|
|
||||||
return nil, nil
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func (m *runSpotEC2ConnMock) DescribeInstances(req *ec2.DescribeInstancesInput) (*ec2.DescribeInstancesOutput, error) {
|
|
||||||
m.DescribeInstancesParams = append(m.DescribeInstancesParams, req)
|
|
||||||
if m.DescribeInstancesFn != nil {
|
|
||||||
resp, err := m.DescribeInstancesFn(req)
|
|
||||||
return resp, err
|
|
||||||
} else {
|
|
||||||
return nil, nil
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func (m *runSpotEC2ConnMock) CreateTags(req *ec2.CreateTagsInput) (*ec2.CreateTagsOutput, error) {
|
|
||||||
m.CreateTagsParams = append(m.CreateTagsParams, req)
|
|
||||||
if m.CreateTagsFn != nil {
|
|
||||||
resp, err := m.CreateTagsFn(req)
|
|
||||||
return resp, err
|
|
||||||
} else {
|
|
||||||
return nil, nil
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func defaultEc2Mock(instanceId, spotRequestId, volumeId, launchTemplateId *string) *runSpotEC2ConnMock {
|
|
||||||
instance := &ec2.Instance{
|
|
||||||
InstanceId: instanceId,
|
|
||||||
SpotInstanceRequestId: spotRequestId,
|
|
||||||
BlockDeviceMappings: []*ec2.InstanceBlockDeviceMapping{
|
|
||||||
{
|
|
||||||
Ebs: &ec2.EbsInstanceBlockDevice{
|
|
||||||
VolumeId: volumeId,
|
|
||||||
},
|
|
||||||
},
|
|
||||||
},
|
|
||||||
}
|
|
||||||
return &runSpotEC2ConnMock{
|
|
||||||
CreateLaunchTemplateFn: func(in *ec2.CreateLaunchTemplateInput) (*ec2.CreateLaunchTemplateOutput, error) {
|
|
||||||
return &ec2.CreateLaunchTemplateOutput{
|
|
||||||
LaunchTemplate: &ec2.LaunchTemplate{
|
|
||||||
LaunchTemplateId: launchTemplateId,
|
|
||||||
},
|
|
||||||
Warning: nil,
|
|
||||||
}, nil
|
|
||||||
},
|
|
||||||
CreateFleetFn: func(*ec2.CreateFleetInput) (*ec2.CreateFleetOutput, error) {
|
|
||||||
return &ec2.CreateFleetOutput{
|
|
||||||
Errors: nil,
|
|
||||||
FleetId: nil,
|
|
||||||
Instances: []*ec2.CreateFleetInstance{
|
|
||||||
{
|
|
||||||
InstanceIds: []*string{instanceId},
|
|
||||||
},
|
|
||||||
},
|
|
||||||
}, nil
|
|
||||||
},
|
|
||||||
DescribeInstancesFn: func(input *ec2.DescribeInstancesInput) (*ec2.DescribeInstancesOutput, error) {
|
|
||||||
return &ec2.DescribeInstancesOutput{
|
|
||||||
NextToken: nil,
|
|
||||||
Reservations: []*ec2.Reservation{
|
|
||||||
{
|
|
||||||
Instances: []*ec2.Instance{instance},
|
|
||||||
},
|
|
||||||
},
|
|
||||||
}, nil
|
|
||||||
},
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestRun(t *testing.T) {
|
|
||||||
instanceId := aws.String("test-instance-id")
|
|
||||||
spotRequestId := aws.String("spot-id")
|
|
||||||
volumeId := aws.String("volume-id")
|
|
||||||
launchTemplateId := aws.String("launchTemplateId")
|
|
||||||
ec2Mock := defaultEc2Mock(instanceId, spotRequestId, volumeId, launchTemplateId)
|
|
||||||
|
|
||||||
uiMock := packersdk.TestUi(t)
|
|
||||||
|
|
||||||
state := tStateSpot()
|
|
||||||
state.Put("ec2", ec2Mock)
|
|
||||||
state.Put("ui", uiMock)
|
|
||||||
state.Put("source_image", testImage())
|
|
||||||
|
|
||||||
stepRunSpotInstance := getBasicStep()
|
|
||||||
stepRunSpotInstance.Tags["Name"] = "Packer Builder"
|
|
||||||
stepRunSpotInstance.Tags["test-tag"] = "test-value"
|
|
||||||
stepRunSpotInstance.SpotTags = map[string]string{
|
|
||||||
"spot-tag": "spot-tag-value",
|
|
||||||
}
|
|
||||||
stepRunSpotInstance.VolumeTags = map[string]string{
|
|
||||||
"volume-tag": "volume-tag-value",
|
|
||||||
}
|
|
||||||
|
|
||||||
ctx := context.TODO()
|
|
||||||
action := stepRunSpotInstance.Run(ctx, state)
|
|
||||||
|
|
||||||
if err := state.Get("error"); err != nil {
|
|
||||||
t.Fatalf("should not error, but: %v", err)
|
|
||||||
}
|
|
||||||
|
|
||||||
if action != multistep.ActionContinue {
|
|
||||||
t.Fatalf("shoul continue, but: %v", action)
|
|
||||||
}
|
|
||||||
|
|
||||||
if len(ec2Mock.CreateLaunchTemplateParams) != 1 {
|
|
||||||
t.Fatalf("createLaunchTemplate should be invoked once, but invoked %v", len(ec2Mock.CreateLaunchTemplateParams))
|
|
||||||
}
|
|
||||||
launchTemplateName := ec2Mock.CreateLaunchTemplateParams[0].LaunchTemplateName
|
|
||||||
|
|
||||||
if len(ec2Mock.CreateLaunchTemplateParams[0].TagSpecifications) != 1 {
|
|
||||||
t.Fatalf("exactly one launch template tag specification expected")
|
|
||||||
}
|
|
||||||
if *ec2Mock.CreateLaunchTemplateParams[0].TagSpecifications[0].ResourceType != "launch-template" {
|
|
||||||
t.Fatalf("resource type 'launch-template' expected")
|
|
||||||
}
|
|
||||||
if len(ec2Mock.CreateLaunchTemplateParams[0].TagSpecifications[0].Tags) != 1 {
|
|
||||||
t.Fatalf("1 launch template tag expected")
|
|
||||||
}
|
|
||||||
|
|
||||||
nameTag := ec2Mock.CreateLaunchTemplateParams[0].TagSpecifications[0].Tags[0]
|
|
||||||
if *nameTag.Key != "spot-tag" || *nameTag.Value != "spot-tag-value" {
|
|
||||||
t.Fatalf("expected spot-tag: spot-tag-value")
|
|
||||||
}
|
|
||||||
|
|
||||||
if len(ec2Mock.CreateFleetParams) != 1 {
|
|
||||||
t.Fatalf("createFleet should be invoked once, but invoked %v", len(ec2Mock.CreateLaunchTemplateParams))
|
|
||||||
}
|
|
||||||
if *ec2Mock.CreateFleetParams[0].TargetCapacitySpecification.DefaultTargetCapacityType != "spot" {
|
|
||||||
t.Fatalf("capacity type should be spot")
|
|
||||||
}
|
|
||||||
if *ec2Mock.CreateFleetParams[0].TargetCapacitySpecification.TotalTargetCapacity != 1 {
|
|
||||||
t.Fatalf("target capacity should be 1")
|
|
||||||
}
|
|
||||||
if len(ec2Mock.CreateFleetParams[0].LaunchTemplateConfigs) != 1 {
|
|
||||||
t.Fatalf("exactly one launch config template expected")
|
|
||||||
}
|
|
||||||
if *ec2Mock.CreateFleetParams[0].LaunchTemplateConfigs[0].LaunchTemplateSpecification.LaunchTemplateName != *launchTemplateName {
|
|
||||||
t.Fatalf("launchTemplateName should match in createLaunchTemplate and createFleet requests")
|
|
||||||
}
|
|
||||||
|
|
||||||
if len(ec2Mock.DescribeInstancesParams) != 1 {
|
|
||||||
t.Fatalf("describeInstancesParams should be invoked once, but invoked %v", len(ec2Mock.DescribeInstancesParams))
|
|
||||||
}
|
|
||||||
if *ec2Mock.DescribeInstancesParams[0].InstanceIds[0] != *instanceId {
|
|
||||||
t.Fatalf("instanceId should match from createFleet response")
|
|
||||||
}
|
|
||||||
|
|
||||||
uiMock.Say(fmt.Sprintf("%v", ec2Mock.CreateTagsParams))
|
|
||||||
if len(ec2Mock.CreateTagsParams) != 3 {
|
|
||||||
t.Fatalf("createTags should be invoked 3 times")
|
|
||||||
}
|
|
||||||
if len(ec2Mock.CreateTagsParams[0].Resources) != 1 || *ec2Mock.CreateTagsParams[0].Resources[0] != *spotRequestId {
|
|
||||||
t.Fatalf("should create tags for spot request")
|
|
||||||
}
|
|
||||||
if len(ec2Mock.CreateTagsParams[1].Resources) != 1 || *ec2Mock.CreateTagsParams[1].Resources[0] != *instanceId {
|
|
||||||
t.Fatalf("should create tags for instance")
|
|
||||||
}
|
|
||||||
if len(ec2Mock.CreateTagsParams[2].Resources) != 1 || ec2Mock.CreateTagsParams[2].Resources[0] != volumeId {
|
|
||||||
t.Fatalf("should create tags for volume")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestRun_NoSpotTags(t *testing.T) {
|
|
||||||
instanceId := aws.String("test-instance-id")
|
|
||||||
spotRequestId := aws.String("spot-id")
|
|
||||||
volumeId := aws.String("volume-id")
|
|
||||||
launchTemplateId := aws.String("lt-id")
|
|
||||||
ec2Mock := defaultEc2Mock(instanceId, spotRequestId, volumeId, launchTemplateId)
|
|
||||||
|
|
||||||
uiMock := packersdk.TestUi(t)
|
|
||||||
|
|
||||||
state := tStateSpot()
|
|
||||||
state.Put("ec2", ec2Mock)
|
|
||||||
state.Put("ui", uiMock)
|
|
||||||
state.Put("source_image", testImage())
|
|
||||||
|
|
||||||
stepRunSpotInstance := getBasicStep()
|
|
||||||
stepRunSpotInstance.Tags["Name"] = "Packer Builder"
|
|
||||||
stepRunSpotInstance.Tags["test-tag"] = "test-value"
|
|
||||||
stepRunSpotInstance.VolumeTags = map[string]string{
|
|
||||||
"volume-tag": "volume-tag-value",
|
|
||||||
}
|
|
||||||
|
|
||||||
ctx := context.TODO()
|
|
||||||
action := stepRunSpotInstance.Run(ctx, state)
|
|
||||||
|
|
||||||
if err := state.Get("error"); err != nil {
|
|
||||||
t.Fatalf("should not error, but: %v", err)
|
|
||||||
}
|
|
||||||
|
|
||||||
if action != multistep.ActionContinue {
|
|
||||||
t.Fatalf("shoul continue, but: %v", action)
|
|
||||||
}
|
|
||||||
|
|
||||||
if len(ec2Mock.CreateLaunchTemplateParams[0].TagSpecifications) != 0 {
|
|
||||||
t.Fatalf("0 launch template tags expected")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,43 +0,0 @@
|
|||||||
package common
|
|
||||||
|
|
||||||
import (
|
|
||||||
"testing"
|
|
||||||
|
|
||||||
"github.com/aws/aws-sdk-go/aws"
|
|
||||||
"github.com/aws/aws-sdk-go/service/ec2"
|
|
||||||
"github.com/stretchr/testify/assert"
|
|
||||||
)
|
|
||||||
|
|
||||||
func TestStepSourceAmiInfo_PVImage(t *testing.T) {
|
|
||||||
err := new(StepSourceAMIInfo).canEnableEnhancedNetworking(&ec2.Image{
|
|
||||||
VirtualizationType: aws.String("paravirtual"),
|
|
||||||
})
|
|
||||||
assert.Error(t, err)
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestStepSourceAmiInfo_HVMImage(t *testing.T) {
|
|
||||||
err := new(StepSourceAMIInfo).canEnableEnhancedNetworking(&ec2.Image{
|
|
||||||
VirtualizationType: aws.String("hvm"),
|
|
||||||
})
|
|
||||||
assert.NoError(t, err)
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestStepSourceAmiInfo_PVImageWithAMIVirtPV(t *testing.T) {
|
|
||||||
stepSourceAMIInfo := StepSourceAMIInfo{
|
|
||||||
AMIVirtType: "paravirtual",
|
|
||||||
}
|
|
||||||
err := stepSourceAMIInfo.canEnableEnhancedNetworking(&ec2.Image{
|
|
||||||
VirtualizationType: aws.String("paravirtual"),
|
|
||||||
})
|
|
||||||
assert.Error(t, err)
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestStepSourceAmiInfo_PVImageWithAMIVirtHVM(t *testing.T) {
|
|
||||||
stepSourceAMIInfo := StepSourceAMIInfo{
|
|
||||||
AMIVirtType: "hvm",
|
|
||||||
}
|
|
||||||
err := stepSourceAMIInfo.canEnableEnhancedNetworking(&ec2.Image{
|
|
||||||
VirtualizationType: aws.String("paravirtual"),
|
|
||||||
})
|
|
||||||
assert.NoError(t, err)
|
|
||||||
}
|
|
||||||
@@ -1,16 +0,0 @@
|
|||||||
package common
|
|
||||||
|
|
||||||
import (
|
|
||||||
"testing"
|
|
||||||
)
|
|
||||||
|
|
||||||
func TestAMITemplatePrepare_clean(t *testing.T) {
|
|
||||||
origName := "AMZamz09()./-_:&^ $%[]#'@"
|
|
||||||
expected := "AMZamz09()./-_--- --[]-'@"
|
|
||||||
|
|
||||||
name := templateCleanAMIName(origName)
|
|
||||||
|
|
||||||
if name != expected {
|
|
||||||
t.Fatalf("template names do not match: expected %s got %s\n", expected, name)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,46 +0,0 @@
|
|||||||
package amazon_acc
|
|
||||||
|
|
||||||
import (
|
|
||||||
"fmt"
|
|
||||||
|
|
||||||
"github.com/aws/aws-sdk-go/aws"
|
|
||||||
"github.com/aws/aws-sdk-go/service/ec2"
|
|
||||||
awscommon "github.com/hashicorp/packer/builder/amazon/common"
|
|
||||||
)
|
|
||||||
|
|
||||||
type AWSHelper struct {
|
|
||||||
Region string
|
|
||||||
AMIName string
|
|
||||||
}
|
|
||||||
|
|
||||||
func (a *AWSHelper) CleanUpAmi() error {
|
|
||||||
accessConfig := &awscommon.AccessConfig{}
|
|
||||||
session, err := accessConfig.Session()
|
|
||||||
if err != nil {
|
|
||||||
return fmt.Errorf("AWSAMICleanUp: Unable to create aws session %s", err.Error())
|
|
||||||
}
|
|
||||||
|
|
||||||
regionconn := ec2.New(session.Copy(&aws.Config{
|
|
||||||
Region: aws.String(a.Region),
|
|
||||||
}))
|
|
||||||
|
|
||||||
resp, err := regionconn.DescribeImages(&ec2.DescribeImagesInput{
|
|
||||||
Owners: aws.StringSlice([]string{"self"}),
|
|
||||||
Filters: []*ec2.Filter{{
|
|
||||||
Name: aws.String("name"),
|
|
||||||
Values: aws.StringSlice([]string{a.AMIName}),
|
|
||||||
}}})
|
|
||||||
if err != nil {
|
|
||||||
return fmt.Errorf("AWSAMICleanUp: Unable to find Image %s: %s", a.AMIName, err.Error())
|
|
||||||
}
|
|
||||||
|
|
||||||
if resp != nil && len(resp.Images) > 0 {
|
|
||||||
_, err = regionconn.DeregisterImage(&ec2.DeregisterImageInput{
|
|
||||||
ImageId: resp.Images[0].ImageId,
|
|
||||||
})
|
|
||||||
if err != nil {
|
|
||||||
return fmt.Errorf("AWSAMICleanUp: Unable to Deregister Image %s", err.Error())
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
@@ -1,59 +0,0 @@
|
|||||||
package amazon_acc
|
|
||||||
|
|
||||||
// This is the code necessary for running the provisioner acceptance tests.
|
|
||||||
// It provides the builder config and cleans up created resource.
|
|
||||||
|
|
||||||
import (
|
|
||||||
"fmt"
|
|
||||||
"io/ioutil"
|
|
||||||
"os"
|
|
||||||
"path/filepath"
|
|
||||||
|
|
||||||
amazonebsbuilder "github.com/hashicorp/packer/builder/amazon/ebs"
|
|
||||||
|
|
||||||
packersdk "github.com/hashicorp/packer-plugin-sdk/packer"
|
|
||||||
)
|
|
||||||
|
|
||||||
type AmazonEBSAccTest struct{}
|
|
||||||
|
|
||||||
func (s *AmazonEBSAccTest) GetConfigs() (map[string]string, error) {
|
|
||||||
fixtures := map[string]string{
|
|
||||||
"linux": "amazon-ebs.txt",
|
|
||||||
"windows": "amazon-ebs_windows.txt",
|
|
||||||
}
|
|
||||||
|
|
||||||
configs := make(map[string]string)
|
|
||||||
|
|
||||||
for distro, fixture := range fixtures {
|
|
||||||
fileName := fixture
|
|
||||||
filePath := filepath.Join("../../builder/amazon/ebs/acceptance/test-fixtures/", fileName)
|
|
||||||
config, err := os.Open(filePath)
|
|
||||||
if err != nil {
|
|
||||||
return nil, fmt.Errorf("Expected to find %s", filePath)
|
|
||||||
}
|
|
||||||
defer config.Close()
|
|
||||||
|
|
||||||
file, err := ioutil.ReadAll(config)
|
|
||||||
if err != nil {
|
|
||||||
return nil, fmt.Errorf("Unable to read %s", filePath)
|
|
||||||
}
|
|
||||||
|
|
||||||
configs[distro] = string(file)
|
|
||||||
|
|
||||||
}
|
|
||||||
return configs, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func (s *AmazonEBSAccTest) CleanUp() error {
|
|
||||||
helper := AWSHelper{
|
|
||||||
Region: "us-east-1",
|
|
||||||
AMIName: "packer-acc-test",
|
|
||||||
}
|
|
||||||
return helper.CleanUpAmi()
|
|
||||||
}
|
|
||||||
|
|
||||||
func (s *AmazonEBSAccTest) GetBuilderStore() packersdk.MapOfBuilder {
|
|
||||||
return packersdk.MapOfBuilder{
|
|
||||||
"amazon-ebs": func() (packersdk.Builder, error) { return &amazonebsbuilder.Builder{}, nil },
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,12 +0,0 @@
|
|||||||
{
|
|
||||||
"type": "amazon-ebs",
|
|
||||||
"ami_name": "packer-acc-test",
|
|
||||||
"instance_type": "m1.small",
|
|
||||||
"region": "us-east-1",
|
|
||||||
"ssh_username": "ubuntu",
|
|
||||||
"source_ami": "ami-0568456c",
|
|
||||||
"force_deregister" : true,
|
|
||||||
"tags": {
|
|
||||||
"packer-test": "true"
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,23 +0,0 @@
|
|||||||
{
|
|
||||||
"type": "amazon-ebs",
|
|
||||||
"region": "us-east-1",
|
|
||||||
"instance_type": "t2.micro",
|
|
||||||
"source_ami_filter": {
|
|
||||||
"filters": {
|
|
||||||
"virtualization-type": "hvm",
|
|
||||||
"name": "*Windows_Server-2012-R2*English-64Bit-Base*",
|
|
||||||
"root-device-type": "ebs"
|
|
||||||
},
|
|
||||||
"most_recent": true,
|
|
||||||
"owners": "amazon"
|
|
||||||
},
|
|
||||||
"ami_name": "packer-acc-test",
|
|
||||||
"user_data_file": "../../builder/amazon/ebs/acceptance/test-fixtures/scripts/bootstrap_win.txt",
|
|
||||||
"communicator": "winrm",
|
|
||||||
"winrm_username": "Administrator",
|
|
||||||
"winrm_password": "SuperS3cr3t!!!!",
|
|
||||||
"force_deregister" : true,
|
|
||||||
"tags": {
|
|
||||||
"packer-test": "true"
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,40 +0,0 @@
|
|||||||
<powershell>
|
|
||||||
# Set administrator password
|
|
||||||
net user Administrator SuperS3cr3t!!!!
|
|
||||||
wmic useraccount where "name='Administrator'" set PasswordExpires=FALSE
|
|
||||||
|
|
||||||
# First, make sure WinRM can't be connected to
|
|
||||||
netsh advfirewall firewall set rule name="Windows Remote Management (HTTP-In)" new enable=yes action=block
|
|
||||||
|
|
||||||
# Delete any existing WinRM listeners
|
|
||||||
winrm delete winrm/config/listener?Address=*+Transport=HTTP 2>$Null
|
|
||||||
winrm delete winrm/config/listener?Address=*+Transport=HTTPS 2>$Null
|
|
||||||
|
|
||||||
# Disable group policies which block basic authentication and unencrypted login
|
|
||||||
|
|
||||||
Set-ItemProperty -Path HKLM:\Software\Policies\Microsoft\Windows\WinRM\Client -Name AllowBasic -Value 1
|
|
||||||
Set-ItemProperty -Path HKLM:\Software\Policies\Microsoft\Windows\WinRM\Client -Name AllowUnencryptedTraffic -Value 1
|
|
||||||
Set-ItemProperty -Path HKLM:\Software\Policies\Microsoft\Windows\WinRM\Service -Name AllowBasic -Value 1
|
|
||||||
Set-ItemProperty -Path HKLM:\Software\Policies\Microsoft\Windows\WinRM\Service -Name AllowUnencryptedTraffic -Value 1
|
|
||||||
|
|
||||||
|
|
||||||
# Create a new WinRM listener and configure
|
|
||||||
winrm create winrm/config/listener?Address=*+Transport=HTTP
|
|
||||||
winrm set winrm/config/winrs '@{MaxMemoryPerShellMB="0"}'
|
|
||||||
winrm set winrm/config '@{MaxTimeoutms="7200000"}'
|
|
||||||
winrm set winrm/config/service '@{AllowUnencrypted="true"}'
|
|
||||||
winrm set winrm/config/service '@{MaxConcurrentOperationsPerUser="12000"}'
|
|
||||||
winrm set winrm/config/service/auth '@{Basic="true"}'
|
|
||||||
winrm set winrm/config/client/auth '@{Basic="true"}'
|
|
||||||
|
|
||||||
# Configure UAC to allow privilege elevation in remote shells
|
|
||||||
$Key = 'HKLM:\SOFTWARE\Microsoft\Windows\CurrentVersion\Policies\System'
|
|
||||||
$Setting = 'LocalAccountTokenFilterPolicy'
|
|
||||||
Set-ItemProperty -Path $Key -Name $Setting -Value 1 -Force
|
|
||||||
|
|
||||||
# Configure and restart the WinRM Service; Enable the required firewall exception
|
|
||||||
Stop-Service -Name WinRM
|
|
||||||
Set-Service -Name WinRM -StartupType Automatic
|
|
||||||
netsh advfirewall firewall set rule name="Windows Remote Management (HTTP-In)" new action=allow localip=any remoteip=any
|
|
||||||
Start-Service -Name WinRM
|
|
||||||
</powershell>
|
|
||||||
@@ -1,396 +0,0 @@
|
|||||||
/*
|
|
||||||
Deregister the test image with
|
|
||||||
aws ec2 deregister-image --image-id $(aws ec2 describe-images --output text --filters "Name=name,Values=packer-test-packer-test-dereg" --query 'Images[*].{ID:ImageId}')
|
|
||||||
*/
|
|
||||||
package ebs
|
|
||||||
|
|
||||||
import (
|
|
||||||
"fmt"
|
|
||||||
"os"
|
|
||||||
"testing"
|
|
||||||
|
|
||||||
"github.com/aws/aws-sdk-go/aws"
|
|
||||||
"github.com/aws/aws-sdk-go/service/ec2"
|
|
||||||
builderT "github.com/hashicorp/packer-plugin-sdk/acctest"
|
|
||||||
packersdk "github.com/hashicorp/packer-plugin-sdk/packer"
|
|
||||||
"github.com/hashicorp/packer/builder/amazon/common"
|
|
||||||
)
|
|
||||||
|
|
||||||
func TestBuilderAcc_basic(t *testing.T) {
|
|
||||||
builderT.Test(t, builderT.TestCase{
|
|
||||||
PreCheck: func() { testAccPreCheck(t) },
|
|
||||||
Builder: &Builder{},
|
|
||||||
Template: testBuilderAccBasic,
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestBuilderAcc_regionCopy(t *testing.T) {
|
|
||||||
builderT.Test(t, builderT.TestCase{
|
|
||||||
PreCheck: func() { testAccPreCheck(t) },
|
|
||||||
Builder: &Builder{},
|
|
||||||
Template: testBuilderAccRegionCopy,
|
|
||||||
Check: checkRegionCopy([]string{"us-east-1", "us-west-2"}),
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestBuilderAcc_forceDeregister(t *testing.T) {
|
|
||||||
// Build the same AMI name twice, with force_deregister on the second run
|
|
||||||
builderT.Test(t, builderT.TestCase{
|
|
||||||
PreCheck: func() { testAccPreCheck(t) },
|
|
||||||
Builder: &Builder{},
|
|
||||||
Template: buildForceDeregisterConfig("false", "dereg"),
|
|
||||||
SkipArtifactTeardown: true,
|
|
||||||
})
|
|
||||||
|
|
||||||
builderT.Test(t, builderT.TestCase{
|
|
||||||
PreCheck: func() { testAccPreCheck(t) },
|
|
||||||
Builder: &Builder{},
|
|
||||||
Template: buildForceDeregisterConfig("true", "dereg"),
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestBuilderAcc_forceDeleteSnapshot(t *testing.T) {
|
|
||||||
amiName := "packer-test-dereg"
|
|
||||||
|
|
||||||
// Build the same AMI name twice, with force_delete_snapshot on the second run
|
|
||||||
builderT.Test(t, builderT.TestCase{
|
|
||||||
PreCheck: func() { testAccPreCheck(t) },
|
|
||||||
Builder: &Builder{},
|
|
||||||
Template: buildForceDeleteSnapshotConfig("false", amiName),
|
|
||||||
SkipArtifactTeardown: true,
|
|
||||||
})
|
|
||||||
|
|
||||||
// Get image data by AMI name
|
|
||||||
ec2conn, _ := testEC2Conn()
|
|
||||||
describeInput := &ec2.DescribeImagesInput{Filters: []*ec2.Filter{
|
|
||||||
{
|
|
||||||
Name: aws.String("name"),
|
|
||||||
Values: []*string{aws.String(amiName)},
|
|
||||||
},
|
|
||||||
}}
|
|
||||||
ec2conn.WaitUntilImageExists(describeInput)
|
|
||||||
imageResp, _ := ec2conn.DescribeImages(describeInput)
|
|
||||||
image := imageResp.Images[0]
|
|
||||||
|
|
||||||
// Get snapshot ids for image
|
|
||||||
snapshotIds := []*string{}
|
|
||||||
for _, device := range image.BlockDeviceMappings {
|
|
||||||
if device.Ebs != nil && device.Ebs.SnapshotId != nil {
|
|
||||||
snapshotIds = append(snapshotIds, device.Ebs.SnapshotId)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
builderT.Test(t, builderT.TestCase{
|
|
||||||
PreCheck: func() { testAccPreCheck(t) },
|
|
||||||
Builder: &Builder{},
|
|
||||||
Template: buildForceDeleteSnapshotConfig("true", amiName),
|
|
||||||
Check: checkSnapshotsDeleted(snapshotIds),
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
func checkSnapshotsDeleted(snapshotIds []*string) builderT.TestCheckFunc {
|
|
||||||
return func(artifacts []packersdk.Artifact) error {
|
|
||||||
// Verify the snapshots are gone
|
|
||||||
ec2conn, _ := testEC2Conn()
|
|
||||||
snapshotResp, _ := ec2conn.DescribeSnapshots(
|
|
||||||
&ec2.DescribeSnapshotsInput{SnapshotIds: snapshotIds},
|
|
||||||
)
|
|
||||||
|
|
||||||
if len(snapshotResp.Snapshots) > 0 {
|
|
||||||
return fmt.Errorf("Snapshots weren't successfully deleted by `force_delete_snapshot`")
|
|
||||||
}
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestBuilderAcc_amiSharing(t *testing.T) {
|
|
||||||
builderT.Test(t, builderT.TestCase{
|
|
||||||
PreCheck: func() { testAccSharingPreCheck(t) },
|
|
||||||
Builder: &Builder{},
|
|
||||||
Template: buildSharingConfig(os.Getenv("TESTACC_AWS_ACCOUNT_ID")),
|
|
||||||
Check: checkAMISharing(2, os.Getenv("TESTACC_AWS_ACCOUNT_ID"), "all"),
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestBuilderAcc_encryptedBoot(t *testing.T) {
|
|
||||||
builderT.Test(t, builderT.TestCase{
|
|
||||||
PreCheck: func() { testAccPreCheck(t) },
|
|
||||||
Builder: &Builder{},
|
|
||||||
Template: testBuilderAccEncrypted,
|
|
||||||
Check: checkBootEncrypted(),
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
func checkAMISharing(count int, uid, group string) builderT.TestCheckFunc {
|
|
||||||
return func(artifacts []packersdk.Artifact) error {
|
|
||||||
if len(artifacts) > 1 {
|
|
||||||
return fmt.Errorf("more than 1 artifact")
|
|
||||||
}
|
|
||||||
|
|
||||||
// Get the actual *Artifact pointer so we can access the AMIs directly
|
|
||||||
artifactRaw := artifacts[0]
|
|
||||||
artifact, ok := artifactRaw.(*common.Artifact)
|
|
||||||
if !ok {
|
|
||||||
return fmt.Errorf("unknown artifact: %#v", artifactRaw)
|
|
||||||
}
|
|
||||||
|
|
||||||
// describe the image, get block devices with a snapshot
|
|
||||||
ec2conn, _ := testEC2Conn()
|
|
||||||
imageResp, err := ec2conn.DescribeImageAttribute(&ec2.DescribeImageAttributeInput{
|
|
||||||
Attribute: aws.String("launchPermission"),
|
|
||||||
ImageId: aws.String(artifact.Amis["us-east-1"]),
|
|
||||||
})
|
|
||||||
|
|
||||||
if err != nil {
|
|
||||||
return fmt.Errorf("Error retrieving Image Attributes for AMI Artifact (%#v) in AMI Sharing Test: %s", artifact, err)
|
|
||||||
}
|
|
||||||
|
|
||||||
// Launch Permissions are in addition to the userid that created it, so if
|
|
||||||
// you add 3 additional ami_users, you expect 2 Launch Permissions here
|
|
||||||
if len(imageResp.LaunchPermissions) != count {
|
|
||||||
return fmt.Errorf("Error in Image Attributes, expected (%d) Launch Permissions, got (%d)", count, len(imageResp.LaunchPermissions))
|
|
||||||
}
|
|
||||||
|
|
||||||
userFound := false
|
|
||||||
for _, lp := range imageResp.LaunchPermissions {
|
|
||||||
if lp.UserId != nil && uid == *lp.UserId {
|
|
||||||
userFound = true
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if !userFound {
|
|
||||||
return fmt.Errorf("Error in Image Attributes, expected User ID (%s) to have Launch Permissions, but was not found", uid)
|
|
||||||
}
|
|
||||||
|
|
||||||
groupFound := false
|
|
||||||
for _, lp := range imageResp.LaunchPermissions {
|
|
||||||
if lp.Group != nil && group == *lp.Group {
|
|
||||||
groupFound = true
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if !groupFound {
|
|
||||||
return fmt.Errorf("Error in Image Attributes, expected Group ID (%s) to have Launch Permissions, but was not found", group)
|
|
||||||
}
|
|
||||||
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func checkRegionCopy(regions []string) builderT.TestCheckFunc {
|
|
||||||
return func(artifacts []packersdk.Artifact) error {
|
|
||||||
if len(artifacts) > 1 {
|
|
||||||
return fmt.Errorf("more than 1 artifact")
|
|
||||||
}
|
|
||||||
|
|
||||||
// Get the actual *Artifact pointer so we can access the AMIs directly
|
|
||||||
artifactRaw := artifacts[0]
|
|
||||||
artifact, ok := artifactRaw.(*common.Artifact)
|
|
||||||
if !ok {
|
|
||||||
return fmt.Errorf("unknown artifact: %#v", artifactRaw)
|
|
||||||
}
|
|
||||||
|
|
||||||
// Verify that we copied to only the regions given
|
|
||||||
regionSet := make(map[string]struct{})
|
|
||||||
for _, r := range regions {
|
|
||||||
regionSet[r] = struct{}{}
|
|
||||||
}
|
|
||||||
for r := range artifact.Amis {
|
|
||||||
if _, ok := regionSet[r]; !ok {
|
|
||||||
return fmt.Errorf("unknown region: %s", r)
|
|
||||||
}
|
|
||||||
|
|
||||||
delete(regionSet, r)
|
|
||||||
}
|
|
||||||
if len(regionSet) > 0 {
|
|
||||||
return fmt.Errorf("didn't copy to: %#v", regionSet)
|
|
||||||
}
|
|
||||||
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func checkBootEncrypted() builderT.TestCheckFunc {
|
|
||||||
return func(artifacts []packersdk.Artifact) error {
|
|
||||||
|
|
||||||
// Get the actual *Artifact pointer so we can access the AMIs directly
|
|
||||||
artifactRaw := artifacts[0]
|
|
||||||
artifact, ok := artifactRaw.(*common.Artifact)
|
|
||||||
if !ok {
|
|
||||||
return fmt.Errorf("unknown artifact: %#v", artifactRaw)
|
|
||||||
}
|
|
||||||
|
|
||||||
// describe the image, get block devices with a snapshot
|
|
||||||
ec2conn, _ := testEC2Conn()
|
|
||||||
imageResp, err := ec2conn.DescribeImages(&ec2.DescribeImagesInput{
|
|
||||||
ImageIds: []*string{aws.String(artifact.Amis["us-east-1"])},
|
|
||||||
})
|
|
||||||
|
|
||||||
if err != nil {
|
|
||||||
return fmt.Errorf("Error retrieving Image Attributes for AMI (%s) in AMI Encrypted Boot Test: %s", artifact, err)
|
|
||||||
}
|
|
||||||
|
|
||||||
image := imageResp.Images[0] // Only requested a single AMI ID
|
|
||||||
|
|
||||||
rootDeviceName := image.RootDeviceName
|
|
||||||
|
|
||||||
for _, bd := range image.BlockDeviceMappings {
|
|
||||||
if *bd.DeviceName == *rootDeviceName {
|
|
||||||
if *bd.Ebs.Encrypted != true {
|
|
||||||
return fmt.Errorf("volume not encrypted: %s", *bd.Ebs.SnapshotId)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestBuilderAcc_SessionManagerInterface(t *testing.T) {
|
|
||||||
builderT.Test(t, builderT.TestCase{
|
|
||||||
PreCheck: func() { testAccPreCheck(t) },
|
|
||||||
Builder: &Builder{},
|
|
||||||
Template: testBuilderAccSessionManagerInterface,
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
func testAccPreCheck(t *testing.T) {
|
|
||||||
}
|
|
||||||
|
|
||||||
func testAccSharingPreCheck(t *testing.T) {
|
|
||||||
if v := os.Getenv("TESTACC_AWS_ACCOUNT_ID"); v == "" {
|
|
||||||
t.Fatal(fmt.Sprintf("TESTACC_AWS_ACCOUNT_ID must be set for acceptance tests"))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func testEC2Conn() (*ec2.EC2, error) {
|
|
||||||
access := &common.AccessConfig{RawRegion: "us-east-1"}
|
|
||||||
session, err := access.Session()
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
|
|
||||||
return ec2.New(session), nil
|
|
||||||
}
|
|
||||||
|
|
||||||
const testBuilderAccBasic = `
|
|
||||||
{
|
|
||||||
"builders": [{
|
|
||||||
"type": "test",
|
|
||||||
"region": "us-east-1",
|
|
||||||
"instance_type": "m3.medium",
|
|
||||||
"source_ami": "ami-76b2a71e",
|
|
||||||
"ssh_username": "ubuntu",
|
|
||||||
"ami_name": "packer-test {{timestamp}}"
|
|
||||||
}]
|
|
||||||
}
|
|
||||||
`
|
|
||||||
|
|
||||||
const testBuilderAccRegionCopy = `
|
|
||||||
{
|
|
||||||
"builders": [{
|
|
||||||
"type": "test",
|
|
||||||
"region": "us-east-1",
|
|
||||||
"instance_type": "m3.medium",
|
|
||||||
"source_ami": "ami-76b2a71e",
|
|
||||||
"ssh_username": "ubuntu",
|
|
||||||
"ami_name": "packer-test {{timestamp}}",
|
|
||||||
"ami_regions": ["us-east-1", "us-west-2"]
|
|
||||||
}]
|
|
||||||
}
|
|
||||||
`
|
|
||||||
|
|
||||||
const testBuilderAccForceDeregister = `
|
|
||||||
{
|
|
||||||
"builders": [{
|
|
||||||
"type": "test",
|
|
||||||
"region": "us-east-1",
|
|
||||||
"instance_type": "m3.medium",
|
|
||||||
"source_ami": "ami-76b2a71e",
|
|
||||||
"ssh_username": "ubuntu",
|
|
||||||
"force_deregister": "%s",
|
|
||||||
"ami_name": "%s"
|
|
||||||
}]
|
|
||||||
}
|
|
||||||
`
|
|
||||||
|
|
||||||
const testBuilderAccForceDeleteSnapshot = `
|
|
||||||
{
|
|
||||||
"builders": [{
|
|
||||||
"type": "test",
|
|
||||||
"region": "us-east-1",
|
|
||||||
"instance_type": "m3.medium",
|
|
||||||
"source_ami": "ami-76b2a71e",
|
|
||||||
"ssh_username": "ubuntu",
|
|
||||||
"force_deregister": "%s",
|
|
||||||
"force_delete_snapshot": "%s",
|
|
||||||
"ami_name": "%s"
|
|
||||||
}]
|
|
||||||
}
|
|
||||||
`
|
|
||||||
|
|
||||||
const testBuilderAccSharing = `
|
|
||||||
{
|
|
||||||
"builders": [{
|
|
||||||
"type": "test",
|
|
||||||
"region": "us-east-1",
|
|
||||||
"instance_type": "m3.medium",
|
|
||||||
"source_ami": "ami-76b2a71e",
|
|
||||||
"ssh_username": "ubuntu",
|
|
||||||
"ami_name": "packer-test {{timestamp}}",
|
|
||||||
"ami_users":["%s"],
|
|
||||||
"ami_groups":["all"]
|
|
||||||
}]
|
|
||||||
}
|
|
||||||
`
|
|
||||||
|
|
||||||
const testBuilderAccEncrypted = `
|
|
||||||
{
|
|
||||||
"builders": [{
|
|
||||||
"type": "test",
|
|
||||||
"region": "us-east-1",
|
|
||||||
"instance_type": "m3.medium",
|
|
||||||
"source_ami":"ami-c15bebaa",
|
|
||||||
"ssh_username": "ubuntu",
|
|
||||||
"ami_name": "packer-enc-test {{timestamp}}",
|
|
||||||
"encrypt_boot": true
|
|
||||||
}]
|
|
||||||
}
|
|
||||||
`
|
|
||||||
|
|
||||||
const testBuilderAccSessionManagerInterface = `
|
|
||||||
{
|
|
||||||
"builders": [{
|
|
||||||
"type": "test",
|
|
||||||
"region": "us-east-1",
|
|
||||||
"instance_type": "m3.medium",
|
|
||||||
"source_ami_filter": {
|
|
||||||
"filters": {
|
|
||||||
"virtualization-type": "hvm",
|
|
||||||
"name": "ubuntu/images/*ubuntu-xenial-16.04-amd64-server-*",
|
|
||||||
"root-device-type": "ebs"
|
|
||||||
},
|
|
||||||
"owners": [
|
|
||||||
"099720109477"
|
|
||||||
],
|
|
||||||
"most_recent": true
|
|
||||||
},
|
|
||||||
"ssh_username": "ubuntu",
|
|
||||||
"ssh_interface": "session_manager",
|
|
||||||
"iam_instance_profile": "SSMInstanceProfile",
|
|
||||||
"ami_name": "packer-ssm-test-{{timestamp}}"
|
|
||||||
}]
|
|
||||||
}
|
|
||||||
`
|
|
||||||
|
|
||||||
func buildForceDeregisterConfig(val, name string) string {
|
|
||||||
return fmt.Sprintf(testBuilderAccForceDeregister, val, name)
|
|
||||||
}
|
|
||||||
|
|
||||||
func buildForceDeleteSnapshotConfig(val, name string) string {
|
|
||||||
return fmt.Sprintf(testBuilderAccForceDeleteSnapshot, val, val, name)
|
|
||||||
}
|
|
||||||
|
|
||||||
func buildSharingConfig(val string) string {
|
|
||||||
return fmt.Sprintf(testBuilderAccSharing, val)
|
|
||||||
}
|
|
||||||
@@ -1,166 +0,0 @@
|
|||||||
package ebs
|
|
||||||
|
|
||||||
import (
|
|
||||||
"testing"
|
|
||||||
|
|
||||||
packersdk "github.com/hashicorp/packer-plugin-sdk/packer"
|
|
||||||
)
|
|
||||||
|
|
||||||
func testConfig() map[string]interface{} {
|
|
||||||
return map[string]interface{}{
|
|
||||||
"access_key": "foo",
|
|
||||||
"secret_key": "bar",
|
|
||||||
"source_ami": "foo",
|
|
||||||
"instance_type": "foo",
|
|
||||||
"region": "us-east-1",
|
|
||||||
"ssh_username": "root",
|
|
||||||
"ami_name": "foo",
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestBuilder_ImplementsBuilder(t *testing.T) {
|
|
||||||
var raw interface{}
|
|
||||||
raw = &Builder{}
|
|
||||||
if _, ok := raw.(packersdk.Builder); !ok {
|
|
||||||
t.Fatalf("Builder should be a builder")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestBuilder_Prepare_BadType(t *testing.T) {
|
|
||||||
b := &Builder{}
|
|
||||||
c := map[string]interface{}{
|
|
||||||
"access_key": []string{},
|
|
||||||
}
|
|
||||||
|
|
||||||
_, warnings, err := b.Prepare(c)
|
|
||||||
if len(warnings) > 0 {
|
|
||||||
t.Fatalf("bad: %#v", warnings)
|
|
||||||
}
|
|
||||||
if err == nil {
|
|
||||||
t.Fatalf("prepare should fail")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestBuilderPrepare_AMIName(t *testing.T) {
|
|
||||||
var b Builder
|
|
||||||
config := testConfig()
|
|
||||||
|
|
||||||
// Test good
|
|
||||||
config["ami_name"] = "foo"
|
|
||||||
_, warnings, err := b.Prepare(config)
|
|
||||||
if len(warnings) > 0 {
|
|
||||||
t.Fatalf("bad: %#v", warnings)
|
|
||||||
}
|
|
||||||
if err != nil {
|
|
||||||
t.Fatalf("should not have error: %s", err)
|
|
||||||
}
|
|
||||||
|
|
||||||
// Test bad
|
|
||||||
config["ami_name"] = "foo {{"
|
|
||||||
b = Builder{}
|
|
||||||
_, warnings, err = b.Prepare(config)
|
|
||||||
if len(warnings) > 0 {
|
|
||||||
t.Fatalf("bad: %#v", warnings)
|
|
||||||
}
|
|
||||||
if err == nil {
|
|
||||||
t.Fatal("should have error")
|
|
||||||
}
|
|
||||||
|
|
||||||
// Test bad
|
|
||||||
delete(config, "ami_name")
|
|
||||||
b = Builder{}
|
|
||||||
_, warnings, err = b.Prepare(config)
|
|
||||||
if len(warnings) > 0 {
|
|
||||||
t.Fatalf("bad: %#v", warnings)
|
|
||||||
}
|
|
||||||
if err == nil {
|
|
||||||
t.Fatal("should have error")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestBuilderPrepare_InvalidKey(t *testing.T) {
|
|
||||||
var b Builder
|
|
||||||
config := testConfig()
|
|
||||||
|
|
||||||
// Add a random key
|
|
||||||
config["i_should_not_be_valid"] = true
|
|
||||||
_, warnings, err := b.Prepare(config)
|
|
||||||
if len(warnings) > 0 {
|
|
||||||
t.Fatalf("bad: %#v", warnings)
|
|
||||||
}
|
|
||||||
if err == nil {
|
|
||||||
t.Fatal("should have error")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestBuilderPrepare_InvalidShutdownBehavior(t *testing.T) {
|
|
||||||
var b Builder
|
|
||||||
config := testConfig()
|
|
||||||
|
|
||||||
// Test good
|
|
||||||
config["shutdown_behavior"] = "terminate"
|
|
||||||
_, warnings, err := b.Prepare(config)
|
|
||||||
if len(warnings) > 0 {
|
|
||||||
t.Fatalf("bad: %#v", warnings)
|
|
||||||
}
|
|
||||||
if err != nil {
|
|
||||||
t.Fatalf("should not have error: %s", err)
|
|
||||||
}
|
|
||||||
|
|
||||||
// Test good
|
|
||||||
config["shutdown_behavior"] = "stop"
|
|
||||||
_, warnings, err = b.Prepare(config)
|
|
||||||
if len(warnings) > 0 {
|
|
||||||
t.Fatalf("bad: %#v", warnings)
|
|
||||||
}
|
|
||||||
if err != nil {
|
|
||||||
t.Fatalf("should not have error: %s", err)
|
|
||||||
}
|
|
||||||
|
|
||||||
// Test bad
|
|
||||||
config["shutdown_behavior"] = "foobar"
|
|
||||||
_, warnings, err = b.Prepare(config)
|
|
||||||
if len(warnings) > 0 {
|
|
||||||
t.Fatalf("bad: %#v", warnings)
|
|
||||||
}
|
|
||||||
if err == nil {
|
|
||||||
t.Fatal("should have error")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestBuilderPrepare_ReturnGeneratedData(t *testing.T) {
|
|
||||||
var b Builder
|
|
||||||
config := testConfig()
|
|
||||||
|
|
||||||
generatedData, warnings, err := b.Prepare(config)
|
|
||||||
if len(warnings) > 0 {
|
|
||||||
t.Fatalf("bad: %#v", warnings)
|
|
||||||
}
|
|
||||||
if err != nil {
|
|
||||||
t.Fatalf("should not have error: %s", err)
|
|
||||||
}
|
|
||||||
if len(generatedData) == 0 {
|
|
||||||
t.Fatalf("Generated data should not be empty")
|
|
||||||
}
|
|
||||||
if len(generatedData) == 0 {
|
|
||||||
t.Fatalf("Generated data should not be empty")
|
|
||||||
}
|
|
||||||
if generatedData[0] != "SourceAMIName" {
|
|
||||||
t.Fatalf("Generated data should contain SourceAMIName")
|
|
||||||
}
|
|
||||||
if generatedData[1] != "BuildRegion" {
|
|
||||||
t.Fatalf("Generated data should contain BuildRegion")
|
|
||||||
}
|
|
||||||
if generatedData[2] != "SourceAMI" {
|
|
||||||
t.Fatalf("Generated data should contain SourceAMI")
|
|
||||||
}
|
|
||||||
if generatedData[3] != "SourceAMICreationDate" {
|
|
||||||
t.Fatalf("Generated data should contain SourceAMICreationDate")
|
|
||||||
}
|
|
||||||
if generatedData[4] != "SourceAMIOwner" {
|
|
||||||
t.Fatalf("Generated data should contain SourceAMIOwner")
|
|
||||||
}
|
|
||||||
if generatedData[5] != "SourceAMIOwnerName" {
|
|
||||||
t.Fatalf("Generated data should contain SourceAMIOwnerName")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,137 +0,0 @@
|
|||||||
package ebs
|
|
||||||
|
|
||||||
import (
|
|
||||||
"encoding/json"
|
|
||||||
"fmt"
|
|
||||||
"testing"
|
|
||||||
|
|
||||||
"github.com/aws/aws-sdk-go/aws"
|
|
||||||
"github.com/aws/aws-sdk-go/service/ec2"
|
|
||||||
builderT "github.com/hashicorp/packer-plugin-sdk/acctest"
|
|
||||||
packersdk "github.com/hashicorp/packer-plugin-sdk/packer"
|
|
||||||
"github.com/hashicorp/packer/builder/amazon/common"
|
|
||||||
)
|
|
||||||
|
|
||||||
type TFBuilder struct {
|
|
||||||
Type string `json:"type"`
|
|
||||||
Region string `json:"region"`
|
|
||||||
SourceAmi string `json:"source_ami"`
|
|
||||||
InstanceType string `json:"instance_type"`
|
|
||||||
SshUsername string `json:"ssh_username"`
|
|
||||||
AmiName string `json:"ami_name"`
|
|
||||||
Tags map[string]string `json:"tags"`
|
|
||||||
SnapshotTags map[string]string `json:"snapshot_tags"`
|
|
||||||
}
|
|
||||||
|
|
||||||
type TFConfig struct {
|
|
||||||
Builders []TFBuilder `json:"builders"`
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestBuilderTagsAcc_basic(t *testing.T) {
|
|
||||||
builderT.Test(t, builderT.TestCase{
|
|
||||||
PreCheck: func() { testAccPreCheck(t) },
|
|
||||||
Builder: &Builder{},
|
|
||||||
Template: testBuilderTagsAccBasic,
|
|
||||||
Check: checkTags(),
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
func checkTags() builderT.TestCheckFunc {
|
|
||||||
return func(artifacts []packersdk.Artifact) error {
|
|
||||||
if len(artifacts) > 1 {
|
|
||||||
return fmt.Errorf("more than 1 artifact")
|
|
||||||
}
|
|
||||||
|
|
||||||
config := TFConfig{}
|
|
||||||
json.Unmarshal([]byte(testBuilderTagsAccBasic), &config)
|
|
||||||
tags := config.Builders[0].Tags
|
|
||||||
snapshotTags := config.Builders[0].SnapshotTags
|
|
||||||
|
|
||||||
// Get the actual *Artifact pointer so we can access the AMIs directly
|
|
||||||
artifactRaw := artifacts[0]
|
|
||||||
artifact, ok := artifactRaw.(*common.Artifact)
|
|
||||||
if !ok {
|
|
||||||
return fmt.Errorf("unknown artifact: %#v", artifactRaw)
|
|
||||||
}
|
|
||||||
|
|
||||||
// Describe the image, get block devices with a snapshot
|
|
||||||
ec2conn, _ := testEC2Conn()
|
|
||||||
imageResp, err := ec2conn.DescribeImages(&ec2.DescribeImagesInput{
|
|
||||||
ImageIds: []*string{aws.String(artifact.Amis["us-east-1"])},
|
|
||||||
})
|
|
||||||
|
|
||||||
if err != nil {
|
|
||||||
return fmt.Errorf("Error retrieving details for AMI Artifact (%#v) in Tags Test: %s", artifact, err)
|
|
||||||
}
|
|
||||||
|
|
||||||
if len(imageResp.Images) == 0 {
|
|
||||||
return fmt.Errorf("No images found for AMI Artifact (%#v) in Tags Test: %s", artifact, err)
|
|
||||||
}
|
|
||||||
|
|
||||||
image := imageResp.Images[0]
|
|
||||||
|
|
||||||
// Check only those with a Snapshot ID, i.e. not Ephemeral
|
|
||||||
var snapshots []*string
|
|
||||||
for _, device := range image.BlockDeviceMappings {
|
|
||||||
if device.Ebs != nil && device.Ebs.SnapshotId != nil {
|
|
||||||
snapshots = append(snapshots, device.Ebs.SnapshotId)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Grab matching snapshot info
|
|
||||||
resp, err := ec2conn.DescribeSnapshots(&ec2.DescribeSnapshotsInput{
|
|
||||||
SnapshotIds: snapshots,
|
|
||||||
})
|
|
||||||
|
|
||||||
if err != nil {
|
|
||||||
return fmt.Errorf("Error retrieving Snapshots for AMI Artifact (%#v) in Tags Test: %s", artifact, err)
|
|
||||||
}
|
|
||||||
|
|
||||||
if len(resp.Snapshots) == 0 {
|
|
||||||
return fmt.Errorf("No Snapshots found for AMI Artifact (%#v) in Tags Test", artifact)
|
|
||||||
}
|
|
||||||
|
|
||||||
// Grab the snapshots, check the tags
|
|
||||||
for _, s := range resp.Snapshots {
|
|
||||||
expected := len(tags)
|
|
||||||
for _, t := range s.Tags {
|
|
||||||
for key, value := range tags {
|
|
||||||
if val, ok := snapshotTags[key]; ok && val == *t.Value {
|
|
||||||
expected--
|
|
||||||
} else if key == *t.Key && value == *t.Value {
|
|
||||||
expected--
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if expected > 0 {
|
|
||||||
return fmt.Errorf("Not all tags found")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
const testBuilderTagsAccBasic = `
|
|
||||||
{
|
|
||||||
"builders": [
|
|
||||||
{
|
|
||||||
"type": "test",
|
|
||||||
"region": "us-east-1",
|
|
||||||
"source_ami": "ami-9eaa1cf6",
|
|
||||||
"instance_type": "t2.micro",
|
|
||||||
"ssh_username": "ubuntu",
|
|
||||||
"ami_name": "packer-tags-testing-{{timestamp}}",
|
|
||||||
"tags": {
|
|
||||||
"OS_Version": "Ubuntu",
|
|
||||||
"Release": "Latest",
|
|
||||||
"Name": "Bleep"
|
|
||||||
},
|
|
||||||
"snapshot_tags": {
|
|
||||||
"Name": "Foobar"
|
|
||||||
}
|
|
||||||
}
|
|
||||||
]
|
|
||||||
}
|
|
||||||
`
|
|
||||||
@@ -1,110 +0,0 @@
|
|||||||
package ebssurrogate
|
|
||||||
|
|
||||||
import (
|
|
||||||
"testing"
|
|
||||||
|
|
||||||
"github.com/hashicorp/packer/builder/amazon/common"
|
|
||||||
|
|
||||||
packersdk "github.com/hashicorp/packer-plugin-sdk/packer"
|
|
||||||
)
|
|
||||||
|
|
||||||
func testConfig() map[string]interface{} {
|
|
||||||
return map[string]interface{}{
|
|
||||||
"access_key": "foo",
|
|
||||||
"secret_key": "bar",
|
|
||||||
"source_ami": "foo",
|
|
||||||
"instance_type": "foo",
|
|
||||||
"region": "us-east-1",
|
|
||||||
"ssh_username": "root",
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestBuilder_ImplementsBuilder(t *testing.T) {
|
|
||||||
var raw interface{}
|
|
||||||
raw = &Builder{}
|
|
||||||
if _, ok := raw.(packersdk.Builder); !ok {
|
|
||||||
t.Fatal("Builder should be a builder")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestBuilder_Prepare_BadType(t *testing.T) {
|
|
||||||
b := &Builder{}
|
|
||||||
c := map[string]interface{}{
|
|
||||||
"access_key": []string{},
|
|
||||||
}
|
|
||||||
|
|
||||||
_, warnings, err := b.Prepare(c)
|
|
||||||
if len(warnings) > 0 {
|
|
||||||
t.Fatalf("bad: %#v", warnings)
|
|
||||||
}
|
|
||||||
if err == nil {
|
|
||||||
t.Fatal("prepare should fail")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestBuilderPrepare_InvalidKey(t *testing.T) {
|
|
||||||
var b Builder
|
|
||||||
config := testConfig()
|
|
||||||
|
|
||||||
// Add a random key
|
|
||||||
config["i_should_not_be_valid"] = true
|
|
||||||
_, warnings, err := b.Prepare(config)
|
|
||||||
if len(warnings) > 0 {
|
|
||||||
t.Fatalf("bad: %#v", warnings)
|
|
||||||
}
|
|
||||||
if err == nil {
|
|
||||||
t.Fatal("should have error")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestBuilderPrepare_ReturnGeneratedData(t *testing.T) {
|
|
||||||
var b Builder
|
|
||||||
// Basic configuration
|
|
||||||
b.config.RootDevice = RootBlockDevice{
|
|
||||||
SourceDeviceName: "device name",
|
|
||||||
DeviceName: "device name",
|
|
||||||
}
|
|
||||||
b.config.LaunchMappings = BlockDevices{
|
|
||||||
BlockDevice{
|
|
||||||
BlockDevice: common.BlockDevice{
|
|
||||||
DeviceName: "device name",
|
|
||||||
},
|
|
||||||
OmitFromArtifact: false,
|
|
||||||
},
|
|
||||||
}
|
|
||||||
b.config.AMIVirtType = "type"
|
|
||||||
config := testConfig()
|
|
||||||
config["ami_name"] = "name"
|
|
||||||
|
|
||||||
generatedData, warnings, err := b.Prepare(config)
|
|
||||||
if len(warnings) > 0 {
|
|
||||||
t.Fatalf("bad: %#v", warnings)
|
|
||||||
}
|
|
||||||
if err != nil {
|
|
||||||
t.Fatalf("should not have error: %s", err)
|
|
||||||
}
|
|
||||||
if len(generatedData) == 0 {
|
|
||||||
t.Fatalf("Generated data should not be empty")
|
|
||||||
}
|
|
||||||
if len(generatedData) == 0 {
|
|
||||||
t.Fatalf("Generated data should not be empty")
|
|
||||||
}
|
|
||||||
if generatedData[0] != "SourceAMIName" {
|
|
||||||
t.Fatalf("Generated data should contain SourceAMIName")
|
|
||||||
}
|
|
||||||
if generatedData[1] != "BuildRegion" {
|
|
||||||
t.Fatalf("Generated data should contain BuildRegion")
|
|
||||||
}
|
|
||||||
if generatedData[2] != "SourceAMI" {
|
|
||||||
t.Fatalf("Generated data should contain SourceAMI")
|
|
||||||
}
|
|
||||||
if generatedData[3] != "SourceAMICreationDate" {
|
|
||||||
t.Fatalf("Generated data should contain SourceAMICreationDate")
|
|
||||||
}
|
|
||||||
if generatedData[4] != "SourceAMIOwner" {
|
|
||||||
t.Fatalf("Generated data should contain SourceAMIOwner")
|
|
||||||
}
|
|
||||||
if generatedData[5] != "SourceAMIOwnerName" {
|
|
||||||
t.Fatalf("Generated data should contain SourceAMIOwnerName")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,249 +0,0 @@
|
|||||||
package ebssurrogate
|
|
||||||
|
|
||||||
import (
|
|
||||||
"reflect"
|
|
||||||
"sort"
|
|
||||||
"testing"
|
|
||||||
|
|
||||||
"github.com/aws/aws-sdk-go/aws"
|
|
||||||
"github.com/aws/aws-sdk-go/service/ec2"
|
|
||||||
"github.com/hashicorp/packer/builder/amazon/common"
|
|
||||||
)
|
|
||||||
|
|
||||||
const sourceDeviceName = "/dev/xvdf"
|
|
||||||
const rootDeviceName = "/dev/xvda"
|
|
||||||
|
|
||||||
func newStepRegisterAMI(amiDevices, launchDevices []*ec2.BlockDeviceMapping) *StepRegisterAMI {
|
|
||||||
return &StepRegisterAMI{
|
|
||||||
RootDevice: RootBlockDevice{
|
|
||||||
SourceDeviceName: sourceDeviceName,
|
|
||||||
DeviceName: rootDeviceName,
|
|
||||||
DeleteOnTermination: true,
|
|
||||||
VolumeType: "ebs",
|
|
||||||
VolumeSize: 10,
|
|
||||||
},
|
|
||||||
AMIDevices: amiDevices,
|
|
||||||
LaunchDevices: launchDevices,
|
|
||||||
PollingConfig: new(common.AWSPollingConfig),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func sorted(devices []*ec2.BlockDeviceMapping) []*ec2.BlockDeviceMapping {
|
|
||||||
sort.SliceStable(devices, func(i, j int) bool {
|
|
||||||
return *devices[i].DeviceName < *devices[j].DeviceName
|
|
||||||
})
|
|
||||||
return devices
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestStepRegisterAmi_combineDevices(t *testing.T) {
|
|
||||||
cases := []struct {
|
|
||||||
snapshotIds map[string]string
|
|
||||||
amiDevices []*ec2.BlockDeviceMapping
|
|
||||||
launchDevices []*ec2.BlockDeviceMapping
|
|
||||||
allDevices []*ec2.BlockDeviceMapping
|
|
||||||
}{
|
|
||||||
{
|
|
||||||
snapshotIds: map[string]string{},
|
|
||||||
amiDevices: []*ec2.BlockDeviceMapping{},
|
|
||||||
launchDevices: []*ec2.BlockDeviceMapping{},
|
|
||||||
allDevices: []*ec2.BlockDeviceMapping{},
|
|
||||||
},
|
|
||||||
{
|
|
||||||
snapshotIds: map[string]string{},
|
|
||||||
amiDevices: []*ec2.BlockDeviceMapping{},
|
|
||||||
launchDevices: []*ec2.BlockDeviceMapping{
|
|
||||||
{
|
|
||||||
Ebs: &ec2.EbsBlockDevice{},
|
|
||||||
DeviceName: aws.String(sourceDeviceName),
|
|
||||||
},
|
|
||||||
},
|
|
||||||
allDevices: []*ec2.BlockDeviceMapping{
|
|
||||||
{
|
|
||||||
Ebs: &ec2.EbsBlockDevice{},
|
|
||||||
DeviceName: aws.String(rootDeviceName),
|
|
||||||
},
|
|
||||||
},
|
|
||||||
},
|
|
||||||
{
|
|
||||||
// Minimal single device
|
|
||||||
snapshotIds: map[string]string{
|
|
||||||
sourceDeviceName: "snap-0123456789abcdef1",
|
|
||||||
},
|
|
||||||
amiDevices: []*ec2.BlockDeviceMapping{},
|
|
||||||
launchDevices: []*ec2.BlockDeviceMapping{
|
|
||||||
{
|
|
||||||
Ebs: &ec2.EbsBlockDevice{},
|
|
||||||
DeviceName: aws.String(sourceDeviceName),
|
|
||||||
},
|
|
||||||
},
|
|
||||||
allDevices: []*ec2.BlockDeviceMapping{
|
|
||||||
{
|
|
||||||
Ebs: &ec2.EbsBlockDevice{
|
|
||||||
SnapshotId: aws.String("snap-0123456789abcdef1"),
|
|
||||||
},
|
|
||||||
DeviceName: aws.String(rootDeviceName),
|
|
||||||
},
|
|
||||||
},
|
|
||||||
},
|
|
||||||
{
|
|
||||||
// Single launch device with AMI device
|
|
||||||
snapshotIds: map[string]string{
|
|
||||||
sourceDeviceName: "snap-0123456789abcdef1",
|
|
||||||
},
|
|
||||||
amiDevices: []*ec2.BlockDeviceMapping{
|
|
||||||
{
|
|
||||||
Ebs: &ec2.EbsBlockDevice{},
|
|
||||||
DeviceName: aws.String("/dev/xvdg"),
|
|
||||||
},
|
|
||||||
},
|
|
||||||
launchDevices: []*ec2.BlockDeviceMapping{
|
|
||||||
{
|
|
||||||
Ebs: &ec2.EbsBlockDevice{},
|
|
||||||
DeviceName: aws.String(sourceDeviceName),
|
|
||||||
},
|
|
||||||
},
|
|
||||||
allDevices: []*ec2.BlockDeviceMapping{
|
|
||||||
{
|
|
||||||
Ebs: &ec2.EbsBlockDevice{
|
|
||||||
SnapshotId: aws.String("snap-0123456789abcdef1"),
|
|
||||||
},
|
|
||||||
DeviceName: aws.String(rootDeviceName),
|
|
||||||
},
|
|
||||||
{
|
|
||||||
Ebs: &ec2.EbsBlockDevice{},
|
|
||||||
DeviceName: aws.String("/dev/xvdg"),
|
|
||||||
},
|
|
||||||
},
|
|
||||||
},
|
|
||||||
{
|
|
||||||
// Multiple launch devices
|
|
||||||
snapshotIds: map[string]string{
|
|
||||||
sourceDeviceName: "snap-0123456789abcdef1",
|
|
||||||
"/dev/xvdg": "snap-0123456789abcdef2",
|
|
||||||
},
|
|
||||||
amiDevices: []*ec2.BlockDeviceMapping{},
|
|
||||||
launchDevices: []*ec2.BlockDeviceMapping{
|
|
||||||
{
|
|
||||||
Ebs: &ec2.EbsBlockDevice{},
|
|
||||||
DeviceName: aws.String(sourceDeviceName),
|
|
||||||
},
|
|
||||||
{
|
|
||||||
Ebs: &ec2.EbsBlockDevice{},
|
|
||||||
DeviceName: aws.String("/dev/xvdg"),
|
|
||||||
},
|
|
||||||
},
|
|
||||||
allDevices: []*ec2.BlockDeviceMapping{
|
|
||||||
{
|
|
||||||
Ebs: &ec2.EbsBlockDevice{
|
|
||||||
SnapshotId: aws.String("snap-0123456789abcdef1"),
|
|
||||||
},
|
|
||||||
DeviceName: aws.String(rootDeviceName),
|
|
||||||
},
|
|
||||||
{
|
|
||||||
Ebs: &ec2.EbsBlockDevice{
|
|
||||||
SnapshotId: aws.String("snap-0123456789abcdef2"),
|
|
||||||
},
|
|
||||||
DeviceName: aws.String("/dev/xvdg"),
|
|
||||||
},
|
|
||||||
},
|
|
||||||
},
|
|
||||||
{
|
|
||||||
// Multiple launch devices with encryption
|
|
||||||
snapshotIds: map[string]string{
|
|
||||||
sourceDeviceName: "snap-0123456789abcdef1",
|
|
||||||
"/dev/xvdg": "snap-0123456789abcdef2",
|
|
||||||
},
|
|
||||||
amiDevices: []*ec2.BlockDeviceMapping{},
|
|
||||||
launchDevices: []*ec2.BlockDeviceMapping{
|
|
||||||
{
|
|
||||||
Ebs: &ec2.EbsBlockDevice{
|
|
||||||
Encrypted: aws.Bool(true),
|
|
||||||
},
|
|
||||||
DeviceName: aws.String(sourceDeviceName),
|
|
||||||
},
|
|
||||||
{
|
|
||||||
Ebs: &ec2.EbsBlockDevice{
|
|
||||||
Encrypted: aws.Bool(true),
|
|
||||||
},
|
|
||||||
DeviceName: aws.String("/dev/xvdg"),
|
|
||||||
},
|
|
||||||
},
|
|
||||||
allDevices: []*ec2.BlockDeviceMapping{
|
|
||||||
{
|
|
||||||
Ebs: &ec2.EbsBlockDevice{
|
|
||||||
SnapshotId: aws.String("snap-0123456789abcdef1"),
|
|
||||||
// Encrypted: true stripped from snapshotted devices
|
|
||||||
},
|
|
||||||
DeviceName: aws.String(rootDeviceName),
|
|
||||||
},
|
|
||||||
{
|
|
||||||
Ebs: &ec2.EbsBlockDevice{
|
|
||||||
SnapshotId: aws.String("snap-0123456789abcdef2"),
|
|
||||||
},
|
|
||||||
DeviceName: aws.String("/dev/xvdg"),
|
|
||||||
},
|
|
||||||
},
|
|
||||||
},
|
|
||||||
{
|
|
||||||
// Multiple launch devices and AMI devices with encryption
|
|
||||||
snapshotIds: map[string]string{
|
|
||||||
sourceDeviceName: "snap-0123456789abcdef1",
|
|
||||||
"/dev/xvdg": "snap-0123456789abcdef2",
|
|
||||||
},
|
|
||||||
amiDevices: []*ec2.BlockDeviceMapping{
|
|
||||||
{
|
|
||||||
Ebs: &ec2.EbsBlockDevice{
|
|
||||||
Encrypted: aws.Bool(true),
|
|
||||||
KmsKeyId: aws.String("keyId"),
|
|
||||||
},
|
|
||||||
// Source device name can be used in AMI devices
|
|
||||||
// since launch device of same name gets renamed
|
|
||||||
// to root device name
|
|
||||||
DeviceName: aws.String(sourceDeviceName),
|
|
||||||
},
|
|
||||||
},
|
|
||||||
launchDevices: []*ec2.BlockDeviceMapping{
|
|
||||||
{
|
|
||||||
Ebs: &ec2.EbsBlockDevice{
|
|
||||||
Encrypted: aws.Bool(true),
|
|
||||||
},
|
|
||||||
DeviceName: aws.String(sourceDeviceName),
|
|
||||||
},
|
|
||||||
{
|
|
||||||
Ebs: &ec2.EbsBlockDevice{
|
|
||||||
Encrypted: aws.Bool(true),
|
|
||||||
},
|
|
||||||
DeviceName: aws.String("/dev/xvdg"),
|
|
||||||
},
|
|
||||||
},
|
|
||||||
allDevices: []*ec2.BlockDeviceMapping{
|
|
||||||
{
|
|
||||||
Ebs: &ec2.EbsBlockDevice{
|
|
||||||
Encrypted: aws.Bool(true),
|
|
||||||
KmsKeyId: aws.String("keyId"),
|
|
||||||
},
|
|
||||||
DeviceName: aws.String(sourceDeviceName),
|
|
||||||
},
|
|
||||||
{
|
|
||||||
Ebs: &ec2.EbsBlockDevice{
|
|
||||||
SnapshotId: aws.String("snap-0123456789abcdef1"),
|
|
||||||
},
|
|
||||||
DeviceName: aws.String(rootDeviceName),
|
|
||||||
},
|
|
||||||
{
|
|
||||||
Ebs: &ec2.EbsBlockDevice{
|
|
||||||
SnapshotId: aws.String("snap-0123456789abcdef2"),
|
|
||||||
},
|
|
||||||
DeviceName: aws.String("/dev/xvdg"),
|
|
||||||
},
|
|
||||||
},
|
|
||||||
},
|
|
||||||
}
|
|
||||||
for _, tc := range cases {
|
|
||||||
stepRegisterAmi := newStepRegisterAMI(tc.amiDevices, tc.launchDevices)
|
|
||||||
allDevices := stepRegisterAmi.combineDevices(tc.snapshotIds)
|
|
||||||
if !reflect.DeepEqual(sorted(allDevices), sorted(tc.allDevices)) {
|
|
||||||
t.Fatalf("Unexpected output from combineDevices")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,29 +0,0 @@
|
|||||||
package ebsvolume
|
|
||||||
|
|
||||||
import "testing"
|
|
||||||
|
|
||||||
func TestArtifactState(t *testing.T) {
|
|
||||||
expectedData := "this is the data"
|
|
||||||
artifact := &Artifact{
|
|
||||||
StateData: map[string]interface{}{"state_data": expectedData},
|
|
||||||
}
|
|
||||||
|
|
||||||
// Valid state
|
|
||||||
result := artifact.State("state_data")
|
|
||||||
if result != expectedData {
|
|
||||||
t.Fatalf("Bad: State data was %s instead of %s", result, expectedData)
|
|
||||||
}
|
|
||||||
|
|
||||||
// Invalid state
|
|
||||||
result = artifact.State("invalid_key")
|
|
||||||
if result != nil {
|
|
||||||
t.Fatalf("Bad: State should be nil for invalid state data name")
|
|
||||||
}
|
|
||||||
|
|
||||||
// Nil StateData should not fail and should return nil
|
|
||||||
artifact = &Artifact{}
|
|
||||||
result = artifact.State("key")
|
|
||||||
if result != nil {
|
|
||||||
t.Fatalf("Bad: State should be nil for nil StateData")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,166 +0,0 @@
|
|||||||
package ebsvolume
|
|
||||||
|
|
||||||
import (
|
|
||||||
"testing"
|
|
||||||
|
|
||||||
packersdk "github.com/hashicorp/packer-plugin-sdk/packer"
|
|
||||||
)
|
|
||||||
|
|
||||||
func testConfig() map[string]interface{} {
|
|
||||||
return map[string]interface{}{
|
|
||||||
"access_key": "foo",
|
|
||||||
"secret_key": "bar",
|
|
||||||
"source_ami": "foo",
|
|
||||||
"instance_type": "foo",
|
|
||||||
"region": "us-east-1",
|
|
||||||
"ssh_username": "root",
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestBuilder_ImplementsBuilder(t *testing.T) {
|
|
||||||
var raw interface{}
|
|
||||||
raw = &Builder{}
|
|
||||||
if _, ok := raw.(packersdk.Builder); !ok {
|
|
||||||
t.Fatalf("Builder should be a builder")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestBuilder_Prepare_BadType(t *testing.T) {
|
|
||||||
b := &Builder{}
|
|
||||||
c := map[string]interface{}{
|
|
||||||
"access_key": []string{},
|
|
||||||
}
|
|
||||||
|
|
||||||
_, warnings, err := b.Prepare(c)
|
|
||||||
if len(warnings) > 0 {
|
|
||||||
t.Fatalf("bad: %#v", warnings)
|
|
||||||
}
|
|
||||||
if err == nil {
|
|
||||||
t.Fatalf("prepare should fail")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestBuilderPrepare_InvalidKey(t *testing.T) {
|
|
||||||
var b Builder
|
|
||||||
config := testConfig()
|
|
||||||
|
|
||||||
// Add a random key
|
|
||||||
config["i_should_not_be_valid"] = true
|
|
||||||
_, warnings, err := b.Prepare(config)
|
|
||||||
if len(warnings) > 0 {
|
|
||||||
t.Fatalf("bad: %#v", warnings)
|
|
||||||
}
|
|
||||||
if err == nil {
|
|
||||||
t.Fatal("should have error")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestBuilderPrepare_InvalidShutdownBehavior(t *testing.T) {
|
|
||||||
var b Builder
|
|
||||||
config := testConfig()
|
|
||||||
|
|
||||||
// Test good
|
|
||||||
config["shutdown_behavior"] = "terminate"
|
|
||||||
_, warnings, err := b.Prepare(config)
|
|
||||||
if len(warnings) > 0 {
|
|
||||||
t.Fatalf("bad: %#v", warnings)
|
|
||||||
}
|
|
||||||
if err != nil {
|
|
||||||
t.Fatalf("should not have error: %s", err)
|
|
||||||
}
|
|
||||||
|
|
||||||
// Test good
|
|
||||||
config["shutdown_behavior"] = "stop"
|
|
||||||
_, warnings, err = b.Prepare(config)
|
|
||||||
if len(warnings) > 0 {
|
|
||||||
t.Fatalf("bad: %#v", warnings)
|
|
||||||
}
|
|
||||||
if err != nil {
|
|
||||||
t.Fatalf("should not have error: %s", err)
|
|
||||||
}
|
|
||||||
|
|
||||||
// Test bad
|
|
||||||
config["shutdown_behavior"] = "foobar"
|
|
||||||
_, warnings, err = b.Prepare(config)
|
|
||||||
if len(warnings) > 0 {
|
|
||||||
t.Fatalf("bad: %#v", warnings)
|
|
||||||
}
|
|
||||||
if err == nil {
|
|
||||||
t.Fatal("should have error")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestBuilderPrepare_ReturnGeneratedData(t *testing.T) {
|
|
||||||
var b Builder
|
|
||||||
config := testConfig()
|
|
||||||
|
|
||||||
generatedData, warnings, err := b.Prepare(config)
|
|
||||||
if len(warnings) > 0 {
|
|
||||||
t.Fatalf("bad: %#v", warnings)
|
|
||||||
}
|
|
||||||
if err != nil {
|
|
||||||
t.Fatalf("should not have error: %s", err)
|
|
||||||
}
|
|
||||||
if len(generatedData) == 0 {
|
|
||||||
t.Fatalf("Generated data should not be empty")
|
|
||||||
}
|
|
||||||
if generatedData[0] != "SourceAMIName" {
|
|
||||||
t.Fatalf("Generated data should contain SourceAMIName")
|
|
||||||
}
|
|
||||||
if generatedData[1] != "BuildRegion" {
|
|
||||||
t.Fatalf("Generated data should contain BuildRegion")
|
|
||||||
}
|
|
||||||
if generatedData[2] != "SourceAMI" {
|
|
||||||
t.Fatalf("Generated data should contain SourceAMI")
|
|
||||||
}
|
|
||||||
if generatedData[3] != "SourceAMICreationDate" {
|
|
||||||
t.Fatalf("Generated data should contain SourceAMICreationDate")
|
|
||||||
}
|
|
||||||
if generatedData[4] != "SourceAMIOwner" {
|
|
||||||
t.Fatalf("Generated data should contain SourceAMIOwner")
|
|
||||||
}
|
|
||||||
if generatedData[5] != "SourceAMIOwnerName" {
|
|
||||||
t.Fatalf("Generated data should contain SourceAMIOwnerName")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestBuidler_ConfigBlockdevicemapping(t *testing.T) {
|
|
||||||
var b Builder
|
|
||||||
config := testConfig()
|
|
||||||
|
|
||||||
//Set some snapshot settings
|
|
||||||
config["ebs_volumes"] = []map[string]interface{}{
|
|
||||||
{
|
|
||||||
"device_name": "/dev/xvdb",
|
|
||||||
"volume_size": "32",
|
|
||||||
"delete_on_termination": true,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"device_name": "/dev/xvdc",
|
|
||||||
"volume_size": "32",
|
|
||||||
"delete_on_termination": true,
|
|
||||||
"snapshot_tags": map[string]string{
|
|
||||||
"Test_Tag": "tag_value",
|
|
||||||
"another tag": "another value",
|
|
||||||
},
|
|
||||||
"snapshot_users": []string{
|
|
||||||
"123", "456",
|
|
||||||
},
|
|
||||||
},
|
|
||||||
}
|
|
||||||
|
|
||||||
generatedData, warnings, err := b.Prepare(config)
|
|
||||||
|
|
||||||
if len(warnings) > 0 {
|
|
||||||
t.Fatalf("bad: %#v", warnings)
|
|
||||||
}
|
|
||||||
if err != nil {
|
|
||||||
t.Fatalf("should not have error: %s", err)
|
|
||||||
}
|
|
||||||
if len(generatedData) == 0 {
|
|
||||||
t.Fatalf("Generated data should not be empty")
|
|
||||||
}
|
|
||||||
|
|
||||||
t.Logf("Test gen %+v", b.config.VolumeMappings)
|
|
||||||
|
|
||||||
}
|
|
||||||
@@ -1,170 +0,0 @@
|
|||||||
package ebsvolume
|
|
||||||
|
|
||||||
import (
|
|
||||||
"bytes"
|
|
||||||
"context"
|
|
||||||
"fmt"
|
|
||||||
"testing"
|
|
||||||
|
|
||||||
"github.com/aws/aws-sdk-go/aws"
|
|
||||||
"github.com/aws/aws-sdk-go/aws/request"
|
|
||||||
|
|
||||||
//"github.com/aws/aws-sdk-go/service/ec2"
|
|
||||||
"github.com/aws/aws-sdk-go/service/ec2"
|
|
||||||
"github.com/aws/aws-sdk-go/service/ec2/ec2iface"
|
|
||||||
"github.com/hashicorp/packer-plugin-sdk/multistep"
|
|
||||||
"github.com/hashicorp/packer-plugin-sdk/packer"
|
|
||||||
"github.com/hashicorp/packer/builder/amazon/common"
|
|
||||||
)
|
|
||||||
|
|
||||||
// Define a mock struct to be used in unit tests for common aws steps.
|
|
||||||
type mockEC2Conn struct {
|
|
||||||
ec2iface.EC2API
|
|
||||||
Config *aws.Config
|
|
||||||
}
|
|
||||||
|
|
||||||
func (m *mockEC2Conn) CreateSnapshot(input *ec2.CreateSnapshotInput) (*ec2.Snapshot, error) {
|
|
||||||
snap := &ec2.Snapshot{
|
|
||||||
// This isn't typical amazon format, but injecting the volume id into
|
|
||||||
// this field lets us verify that the right volume was snapshotted with
|
|
||||||
// a simple string comparison
|
|
||||||
SnapshotId: aws.String(fmt.Sprintf("snap-of-%s", *input.VolumeId)),
|
|
||||||
}
|
|
||||||
|
|
||||||
return snap, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func (m *mockEC2Conn) WaitUntilSnapshotCompletedWithContext(aws.Context, *ec2.DescribeSnapshotsInput, ...request.WaiterOption) error {
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func getMockConn(config *common.AccessConfig, target string) (ec2iface.EC2API, error) {
|
|
||||||
mockConn := &mockEC2Conn{
|
|
||||||
Config: aws.NewConfig(),
|
|
||||||
}
|
|
||||||
return mockConn, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// Create statebag for running test
|
|
||||||
func tState(t *testing.T) multistep.StateBag {
|
|
||||||
state := new(multistep.BasicStateBag)
|
|
||||||
state.Put("ui", &packer.BasicUi{
|
|
||||||
Reader: new(bytes.Buffer),
|
|
||||||
Writer: new(bytes.Buffer),
|
|
||||||
})
|
|
||||||
// state.Put("amis", map[string]string{"us-east-1": "ami-12345"})
|
|
||||||
// state.Put("snapshots", map[string][]string{"us-east-1": {"snap-0012345"}})
|
|
||||||
conn, _ := getMockConn(&common.AccessConfig{}, "us-east-2")
|
|
||||||
|
|
||||||
state.Put("ec2", conn)
|
|
||||||
// Store a fake instance that contains a block device that matches the
|
|
||||||
// volumes defined in the config above
|
|
||||||
state.Put("instance", &ec2.Instance{
|
|
||||||
InstanceId: aws.String("instance-id"),
|
|
||||||
BlockDeviceMappings: []*ec2.InstanceBlockDeviceMapping{
|
|
||||||
{
|
|
||||||
DeviceName: aws.String("/dev/xvda"),
|
|
||||||
Ebs: &ec2.EbsInstanceBlockDevice{
|
|
||||||
VolumeId: aws.String("vol-1234"),
|
|
||||||
},
|
|
||||||
},
|
|
||||||
{
|
|
||||||
DeviceName: aws.String("/dev/xvdb"),
|
|
||||||
Ebs: &ec2.EbsInstanceBlockDevice{
|
|
||||||
VolumeId: aws.String("vol-5678"),
|
|
||||||
},
|
|
||||||
},
|
|
||||||
},
|
|
||||||
})
|
|
||||||
return state
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestStepSnapshot_run_simple(t *testing.T) {
|
|
||||||
var b Builder
|
|
||||||
config := testConfig() //from builder_test
|
|
||||||
|
|
||||||
//Set some snapshot settings
|
|
||||||
config["ebs_volumes"] = []map[string]interface{}{
|
|
||||||
{
|
|
||||||
"device_name": "/dev/xvdb",
|
|
||||||
"volume_size": "32",
|
|
||||||
"delete_on_termination": true,
|
|
||||||
"snapshot_volume": true,
|
|
||||||
},
|
|
||||||
}
|
|
||||||
|
|
||||||
generatedData, warnings, err := b.Prepare(config)
|
|
||||||
if len(warnings) > 0 {
|
|
||||||
t.Fatalf("bad: %#v", warnings)
|
|
||||||
}
|
|
||||||
if err != nil {
|
|
||||||
t.Fatalf("should not have error: %s", err)
|
|
||||||
}
|
|
||||||
if len(generatedData) == 0 {
|
|
||||||
t.Fatalf("Generated data should not be empty")
|
|
||||||
}
|
|
||||||
|
|
||||||
state := tState(t)
|
|
||||||
|
|
||||||
accessConfig := common.FakeAccessConfig()
|
|
||||||
|
|
||||||
step := stepSnapshotEBSVolumes{
|
|
||||||
PollingConfig: new(common.AWSPollingConfig),
|
|
||||||
AccessConfig: accessConfig,
|
|
||||||
VolumeMapping: b.config.VolumeMappings,
|
|
||||||
Ctx: b.config.ctx,
|
|
||||||
}
|
|
||||||
|
|
||||||
step.Run(context.Background(), state)
|
|
||||||
|
|
||||||
if len(step.snapshotMap) != 1 {
|
|
||||||
t.Fatalf("Missing Snapshot from step")
|
|
||||||
}
|
|
||||||
|
|
||||||
if volmapping := step.snapshotMap["snap-of-vol-5678"]; volmapping == nil {
|
|
||||||
t.Fatalf("Didn't snapshot correct volume: Map is %#v", step.snapshotMap)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestStepSnapshot_run_no_snaps(t *testing.T) {
|
|
||||||
var b Builder
|
|
||||||
config := testConfig() //from builder_test
|
|
||||||
|
|
||||||
//Set some snapshot settings
|
|
||||||
config["ebs_volumes"] = []map[string]interface{}{
|
|
||||||
{
|
|
||||||
"device_name": "/dev/xvdb",
|
|
||||||
"volume_size": "32",
|
|
||||||
"delete_on_termination": true,
|
|
||||||
"snapshot_volume": false,
|
|
||||||
},
|
|
||||||
}
|
|
||||||
|
|
||||||
generatedData, warnings, err := b.Prepare(config)
|
|
||||||
if len(warnings) > 0 {
|
|
||||||
t.Fatalf("bad: %#v", warnings)
|
|
||||||
}
|
|
||||||
if err != nil {
|
|
||||||
t.Fatalf("should not have error: %s", err)
|
|
||||||
}
|
|
||||||
if len(generatedData) == 0 {
|
|
||||||
t.Fatalf("Generated data should not be empty")
|
|
||||||
}
|
|
||||||
|
|
||||||
state := tState(t)
|
|
||||||
|
|
||||||
accessConfig := common.FakeAccessConfig()
|
|
||||||
|
|
||||||
step := stepSnapshotEBSVolumes{
|
|
||||||
PollingConfig: new(common.AWSPollingConfig),
|
|
||||||
AccessConfig: accessConfig,
|
|
||||||
VolumeMapping: b.config.VolumeMappings,
|
|
||||||
Ctx: b.config.ctx,
|
|
||||||
}
|
|
||||||
|
|
||||||
step.Run(context.Background(), state)
|
|
||||||
|
|
||||||
if len(step.snapshotMap) != 0 {
|
|
||||||
t.Fatalf("Shouldn't have snapshotted any volumes")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1 +0,0 @@
|
|||||||
command: packer build -var "accesskey=*" -var "secretkey=" -var "shellpath=packages.sh" .\apache.json
|
|
||||||
@@ -1,31 +0,0 @@
|
|||||||
{
|
|
||||||
"variables":
|
|
||||||
{
|
|
||||||
"accesskey": "",
|
|
||||||
"secretkey": "",
|
|
||||||
"shellpath": "packages.sh"
|
|
||||||
},
|
|
||||||
"builders":[
|
|
||||||
{
|
|
||||||
"type": "amazon-ebs",
|
|
||||||
"access_key": "{{user `accesskey`}}",
|
|
||||||
"secret_key": "{{user `secretkey`}}",
|
|
||||||
"region": "ap-south-1",
|
|
||||||
"source_ami": "ami-sa7608343426b",
|
|
||||||
"instance_type": "t2.micro",
|
|
||||||
"ssh_username": "ubuntu",
|
|
||||||
"ami_name": "apache",
|
|
||||||
"tags": {
|
|
||||||
"OS_Version": "Ubuntu",
|
|
||||||
"Release": "Latest"
|
|
||||||
}
|
|
||||||
}
|
|
||||||
],
|
|
||||||
"provisioners":[
|
|
||||||
{
|
|
||||||
"type": "shell",
|
|
||||||
"script": "{{user `shellpath`}}"
|
|
||||||
}
|
|
||||||
]
|
|
||||||
|
|
||||||
}
|
|
||||||
@@ -1,6 +0,0 @@
|
|||||||
echo "installing apache "
|
|
||||||
sudo apt-get update
|
|
||||||
sudo apt-get install apache2 -y
|
|
||||||
sudo apt-get update
|
|
||||||
sudo service apache2 restart
|
|
||||||
sudo apache2 --version
|
|
||||||
@@ -1 +0,0 @@
|
|||||||
command: packer build -var "accesskey=*" -var "secretkey=" -var "shellpath=packages.sh" .\nginx.json
|
|
||||||
@@ -1,31 +0,0 @@
|
|||||||
{
|
|
||||||
"variables":
|
|
||||||
{
|
|
||||||
"accesskey": "",
|
|
||||||
"secretkey": "",
|
|
||||||
"shellpath": "packages.sh"
|
|
||||||
},
|
|
||||||
"builders":[
|
|
||||||
{
|
|
||||||
"type": "amazon-ebs",
|
|
||||||
"access_key": "{{user `accesskey`}}",
|
|
||||||
"secret_key": "{{user `secretkey`}}",
|
|
||||||
"region": "ap-south-1",
|
|
||||||
"source_ami": "ami-sa7608343426b",
|
|
||||||
"instance_type": "t2.micro",
|
|
||||||
"ssh_username": "ubuntu",
|
|
||||||
"ami_name": "nginx",
|
|
||||||
"tags": {
|
|
||||||
"OS_Version": "Ubuntu",
|
|
||||||
"Release": "Latest"
|
|
||||||
}
|
|
||||||
}
|
|
||||||
],
|
|
||||||
"provisioners":[
|
|
||||||
{
|
|
||||||
"type": "shell",
|
|
||||||
"script": "{{user `shellpath`}}"
|
|
||||||
}
|
|
||||||
]
|
|
||||||
|
|
||||||
}
|
|
||||||
@@ -1,4 +0,0 @@
|
|||||||
echo "installing nginx "
|
|
||||||
sudo apt-get update
|
|
||||||
sudo apt-get install nginx -y
|
|
||||||
sudo service nginx restart
|
|
||||||
@@ -1,343 +0,0 @@
|
|||||||
package instance
|
|
||||||
|
|
||||||
import (
|
|
||||||
"io/ioutil"
|
|
||||||
"os"
|
|
||||||
"testing"
|
|
||||||
|
|
||||||
packersdk "github.com/hashicorp/packer-plugin-sdk/packer"
|
|
||||||
)
|
|
||||||
|
|
||||||
func testConfig() (config map[string]interface{}, tf *os.File) {
|
|
||||||
tf, err := ioutil.TempFile("", "packer")
|
|
||||||
if err != nil {
|
|
||||||
panic(err)
|
|
||||||
}
|
|
||||||
|
|
||||||
config = map[string]interface{}{
|
|
||||||
"account_id": "foo",
|
|
||||||
"ami_name": "foo",
|
|
||||||
"instance_type": "m1.small",
|
|
||||||
"region": "us-east-1",
|
|
||||||
"s3_bucket": "foo",
|
|
||||||
"source_ami": "foo",
|
|
||||||
"ssh_username": "bob",
|
|
||||||
"x509_cert_path": tf.Name(),
|
|
||||||
"x509_key_path": tf.Name(),
|
|
||||||
"x509_upload_path": "/foo",
|
|
||||||
}
|
|
||||||
|
|
||||||
return config, tf
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestBuilder_ImplementsBuilder(t *testing.T) {
|
|
||||||
var raw interface{}
|
|
||||||
raw = &Builder{}
|
|
||||||
if _, ok := raw.(packersdk.Builder); !ok {
|
|
||||||
t.Fatalf("Builder should be a builder")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestBuilderPrepare_AccountId(t *testing.T) {
|
|
||||||
b := &Builder{}
|
|
||||||
config, tempfile := testConfig()
|
|
||||||
config["skip_region_validation"] = true
|
|
||||||
|
|
||||||
defer os.Remove(tempfile.Name())
|
|
||||||
defer tempfile.Close()
|
|
||||||
|
|
||||||
config["account_id"] = ""
|
|
||||||
_, warnings, err := b.Prepare(config)
|
|
||||||
if len(warnings) > 0 {
|
|
||||||
t.Fatalf("bad: %#v", warnings)
|
|
||||||
}
|
|
||||||
if err == nil {
|
|
||||||
t.Fatal("should have error")
|
|
||||||
}
|
|
||||||
|
|
||||||
config["account_id"] = "foo"
|
|
||||||
_, warnings, err = b.Prepare(config)
|
|
||||||
if len(warnings) > 0 {
|
|
||||||
t.Fatalf("bad: %#v", warnings)
|
|
||||||
}
|
|
||||||
if err != nil {
|
|
||||||
t.Errorf("err: %s", err)
|
|
||||||
}
|
|
||||||
|
|
||||||
config["account_id"] = "0123-0456-7890"
|
|
||||||
_, warnings, err = b.Prepare(config)
|
|
||||||
if len(warnings) > 0 {
|
|
||||||
t.Fatalf("bad: %#v", warnings)
|
|
||||||
}
|
|
||||||
if err != nil {
|
|
||||||
t.Fatalf("err: %s", err)
|
|
||||||
}
|
|
||||||
|
|
||||||
if b.config.AccountId != "012304567890" {
|
|
||||||
t.Errorf("should strip hyphens: %s", b.config.AccountId)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestBuilderPrepare_AMIName(t *testing.T) {
|
|
||||||
var b Builder
|
|
||||||
config, tempfile := testConfig()
|
|
||||||
defer os.Remove(tempfile.Name())
|
|
||||||
defer tempfile.Close()
|
|
||||||
|
|
||||||
// Test good
|
|
||||||
config["ami_name"] = "foo"
|
|
||||||
config["skip_region_validation"] = true
|
|
||||||
_, warnings, err := b.Prepare(config)
|
|
||||||
if len(warnings) > 0 {
|
|
||||||
t.Fatalf("bad: %#v", warnings)
|
|
||||||
}
|
|
||||||
if err != nil {
|
|
||||||
t.Fatalf("should not have error: %s", err)
|
|
||||||
}
|
|
||||||
|
|
||||||
// Test bad
|
|
||||||
config["ami_name"] = "foo {{"
|
|
||||||
b = Builder{}
|
|
||||||
_, warnings, err = b.Prepare(config)
|
|
||||||
if len(warnings) > 0 {
|
|
||||||
t.Fatalf("bad: %#v", warnings)
|
|
||||||
}
|
|
||||||
if err == nil {
|
|
||||||
t.Fatal("should have error")
|
|
||||||
}
|
|
||||||
|
|
||||||
// Test bad
|
|
||||||
delete(config, "ami_name")
|
|
||||||
b = Builder{}
|
|
||||||
_, warnings, err = b.Prepare(config)
|
|
||||||
if len(warnings) > 0 {
|
|
||||||
t.Fatalf("bad: %#v", warnings)
|
|
||||||
}
|
|
||||||
if err == nil {
|
|
||||||
t.Fatal("should have error")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestBuilderPrepare_BundleDestination(t *testing.T) {
|
|
||||||
b := &Builder{}
|
|
||||||
config, tempfile := testConfig()
|
|
||||||
config["skip_region_validation"] = true
|
|
||||||
defer os.Remove(tempfile.Name())
|
|
||||||
defer tempfile.Close()
|
|
||||||
|
|
||||||
config["bundle_destination"] = ""
|
|
||||||
_, warnings, err := b.Prepare(config)
|
|
||||||
if len(warnings) > 0 {
|
|
||||||
t.Fatalf("bad: %#v", warnings)
|
|
||||||
}
|
|
||||||
if err != nil {
|
|
||||||
t.Fatalf("err: %s", err)
|
|
||||||
}
|
|
||||||
|
|
||||||
if b.config.BundleDestination != "/tmp" {
|
|
||||||
t.Fatalf("bad: %s", b.config.BundleDestination)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestBuilderPrepare_BundlePrefix(t *testing.T) {
|
|
||||||
b := &Builder{}
|
|
||||||
config, tempfile := testConfig()
|
|
||||||
config["skip_region_validation"] = true
|
|
||||||
defer os.Remove(tempfile.Name())
|
|
||||||
defer tempfile.Close()
|
|
||||||
|
|
||||||
_, warnings, err := b.Prepare(config)
|
|
||||||
if len(warnings) > 0 {
|
|
||||||
t.Fatalf("bad: %#v", warnings)
|
|
||||||
}
|
|
||||||
if err != nil {
|
|
||||||
t.Fatalf("err: %s", err)
|
|
||||||
}
|
|
||||||
|
|
||||||
if b.config.BundlePrefix == "" {
|
|
||||||
t.Fatalf("bad: %s", b.config.BundlePrefix)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestBuilderPrepare_InvalidKey(t *testing.T) {
|
|
||||||
var b Builder
|
|
||||||
config, tempfile := testConfig()
|
|
||||||
defer os.Remove(tempfile.Name())
|
|
||||||
defer tempfile.Close()
|
|
||||||
|
|
||||||
// Add a random key
|
|
||||||
config["i_should_not_be_valid"] = true
|
|
||||||
_, warnings, err := b.Prepare(config)
|
|
||||||
if len(warnings) > 0 {
|
|
||||||
t.Fatalf("bad: %#v", warnings)
|
|
||||||
}
|
|
||||||
if err == nil {
|
|
||||||
t.Fatal("should have error")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestBuilderPrepare_S3Bucket(t *testing.T) {
|
|
||||||
b := &Builder{}
|
|
||||||
config, tempfile := testConfig()
|
|
||||||
config["skip_region_validation"] = true
|
|
||||||
defer os.Remove(tempfile.Name())
|
|
||||||
defer tempfile.Close()
|
|
||||||
|
|
||||||
config["s3_bucket"] = ""
|
|
||||||
_, warnings, err := b.Prepare(config)
|
|
||||||
if len(warnings) > 0 {
|
|
||||||
t.Fatalf("bad: %#v", warnings)
|
|
||||||
}
|
|
||||||
if err == nil {
|
|
||||||
t.Fatal("should have error")
|
|
||||||
}
|
|
||||||
|
|
||||||
config["s3_bucket"] = "foo"
|
|
||||||
_, warnings, err = b.Prepare(config)
|
|
||||||
if len(warnings) > 0 {
|
|
||||||
t.Fatalf("bad: %#v", warnings)
|
|
||||||
}
|
|
||||||
if err != nil {
|
|
||||||
t.Errorf("err: %s", err)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestBuilderPrepare_X509CertPath(t *testing.T) {
|
|
||||||
b := &Builder{}
|
|
||||||
config, tempfile := testConfig()
|
|
||||||
config["skip_region_validation"] = true
|
|
||||||
defer os.Remove(tempfile.Name())
|
|
||||||
defer tempfile.Close()
|
|
||||||
|
|
||||||
config["x509_cert_path"] = ""
|
|
||||||
_, warnings, err := b.Prepare(config)
|
|
||||||
if len(warnings) > 0 {
|
|
||||||
t.Fatalf("bad: %#v", warnings)
|
|
||||||
}
|
|
||||||
if err == nil {
|
|
||||||
t.Fatal("should have error")
|
|
||||||
}
|
|
||||||
|
|
||||||
config["x509_cert_path"] = "i/am/a/file/that/doesnt/exist"
|
|
||||||
_, warnings, err = b.Prepare(config)
|
|
||||||
if len(warnings) > 0 {
|
|
||||||
t.Fatalf("bad: %#v", warnings)
|
|
||||||
}
|
|
||||||
if err == nil {
|
|
||||||
t.Error("should have error")
|
|
||||||
}
|
|
||||||
|
|
||||||
tf, err := ioutil.TempFile("", "packer")
|
|
||||||
if err != nil {
|
|
||||||
t.Fatalf("error tempfile: %s", err)
|
|
||||||
}
|
|
||||||
defer os.Remove(tf.Name())
|
|
||||||
defer tf.Close()
|
|
||||||
|
|
||||||
config["x509_cert_path"] = tf.Name()
|
|
||||||
_, warnings, err = b.Prepare(config)
|
|
||||||
if len(warnings) > 0 {
|
|
||||||
t.Fatalf("bad: %#v", warnings)
|
|
||||||
}
|
|
||||||
if err != nil {
|
|
||||||
t.Fatalf("should not have error: %s", err)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestBuilderPrepare_X509KeyPath(t *testing.T) {
|
|
||||||
b := &Builder{}
|
|
||||||
config, tempfile := testConfig()
|
|
||||||
config["skip_region_validation"] = true
|
|
||||||
defer os.Remove(tempfile.Name())
|
|
||||||
defer tempfile.Close()
|
|
||||||
|
|
||||||
config["x509_key_path"] = ""
|
|
||||||
_, warnings, err := b.Prepare(config)
|
|
||||||
if len(warnings) > 0 {
|
|
||||||
t.Fatalf("bad: %#v", warnings)
|
|
||||||
}
|
|
||||||
if err == nil {
|
|
||||||
t.Fatal("should have error")
|
|
||||||
}
|
|
||||||
|
|
||||||
config["x509_key_path"] = "i/am/a/file/that/doesnt/exist"
|
|
||||||
_, warnings, err = b.Prepare(config)
|
|
||||||
if len(warnings) > 0 {
|
|
||||||
t.Fatalf("bad: %#v", warnings)
|
|
||||||
}
|
|
||||||
if err == nil {
|
|
||||||
t.Error("should have error")
|
|
||||||
}
|
|
||||||
|
|
||||||
tf, err := ioutil.TempFile("", "packer")
|
|
||||||
if err != nil {
|
|
||||||
t.Fatalf("error tempfile: %s", err)
|
|
||||||
}
|
|
||||||
defer os.Remove(tf.Name())
|
|
||||||
defer tf.Close()
|
|
||||||
|
|
||||||
config["x509_key_path"] = tf.Name()
|
|
||||||
_, warnings, err = b.Prepare(config)
|
|
||||||
if len(warnings) > 0 {
|
|
||||||
t.Fatalf("bad: %#v", warnings)
|
|
||||||
}
|
|
||||||
if err != nil {
|
|
||||||
t.Fatalf("should not have error: %s", err)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestBuilderPrepare_X509UploadPath(t *testing.T) {
|
|
||||||
b := &Builder{}
|
|
||||||
config, tempfile := testConfig()
|
|
||||||
config["skip_region_validation"] = true
|
|
||||||
defer os.Remove(tempfile.Name())
|
|
||||||
defer tempfile.Close()
|
|
||||||
|
|
||||||
config["x509_upload_path"] = ""
|
|
||||||
_, warnings, err := b.Prepare(config)
|
|
||||||
if len(warnings) > 0 {
|
|
||||||
t.Fatalf("bad: %#v", warnings)
|
|
||||||
}
|
|
||||||
if err != nil {
|
|
||||||
t.Fatalf("should not have error: %s", err)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestBuilderPrepare_ReturnGeneratedData(t *testing.T) {
|
|
||||||
var b Builder
|
|
||||||
config, tempfile := testConfig()
|
|
||||||
defer os.Remove(tempfile.Name())
|
|
||||||
defer tempfile.Close()
|
|
||||||
|
|
||||||
generatedData, warnings, err := b.Prepare(config)
|
|
||||||
if len(warnings) > 0 {
|
|
||||||
t.Fatalf("bad: %#v", warnings)
|
|
||||||
}
|
|
||||||
if err != nil {
|
|
||||||
t.Fatalf("should not have error: %s", err)
|
|
||||||
}
|
|
||||||
if len(generatedData) == 0 {
|
|
||||||
t.Fatalf("Generated data should not be empty")
|
|
||||||
}
|
|
||||||
if len(generatedData) == 0 {
|
|
||||||
t.Fatalf("Generated data should not be empty")
|
|
||||||
}
|
|
||||||
if generatedData[0] != "SourceAMIName" {
|
|
||||||
t.Fatalf("Generated data should contain SourceAMIName")
|
|
||||||
}
|
|
||||||
if generatedData[1] != "BuildRegion" {
|
|
||||||
t.Fatalf("Generated data should contain BuildRegion")
|
|
||||||
}
|
|
||||||
if generatedData[2] != "SourceAMI" {
|
|
||||||
t.Fatalf("Generated data should contain SourceAMI")
|
|
||||||
}
|
|
||||||
if generatedData[3] != "SourceAMICreationDate" {
|
|
||||||
t.Fatalf("Generated data should contain SourceAMICreationDate")
|
|
||||||
}
|
|
||||||
if generatedData[4] != "SourceAMIOwner" {
|
|
||||||
t.Fatalf("Generated data should contain SourceAMIOwner")
|
|
||||||
}
|
|
||||||
if generatedData[5] != "SourceAMIOwnerName" {
|
|
||||||
t.Fatalf("Generated data should contain SourceAMIOwnerName")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,13 +0,0 @@
|
|||||||
package version
|
|
||||||
|
|
||||||
import (
|
|
||||||
"github.com/hashicorp/packer-plugin-sdk/version"
|
|
||||||
packerVersion "github.com/hashicorp/packer/version"
|
|
||||||
)
|
|
||||||
|
|
||||||
var AWSPluginVersion *version.PluginVersion
|
|
||||||
|
|
||||||
func init() {
|
|
||||||
AWSPluginVersion = version.InitializePluginVersion(
|
|
||||||
packerVersion.Version, packerVersion.VersionPrerelease)
|
|
||||||
}
|
|
||||||
@@ -4,7 +4,7 @@ import (
|
|||||||
"context"
|
"context"
|
||||||
"fmt"
|
"fmt"
|
||||||
|
|
||||||
"github.com/oracle/oci-go-sdk/core"
|
"github.com/oracle/oci-go-sdk/v36/core"
|
||||||
)
|
)
|
||||||
|
|
||||||
// Artifact is an artifact implementation that contains a built Custom Image.
|
// Artifact is an artifact implementation that contains a built Custom Image.
|
||||||
|
|||||||
@@ -12,7 +12,7 @@ import (
|
|||||||
"github.com/hashicorp/packer-plugin-sdk/multistep/commonsteps"
|
"github.com/hashicorp/packer-plugin-sdk/multistep/commonsteps"
|
||||||
packersdk "github.com/hashicorp/packer-plugin-sdk/packer"
|
packersdk "github.com/hashicorp/packer-plugin-sdk/packer"
|
||||||
ocommon "github.com/hashicorp/packer/builder/oracle/common"
|
ocommon "github.com/hashicorp/packer/builder/oracle/common"
|
||||||
"github.com/oracle/oci-go-sdk/core"
|
"github.com/oracle/oci-go-sdk/v36/core"
|
||||||
)
|
)
|
||||||
|
|
||||||
// BuilderId uniquely identifies the builder
|
// BuilderId uniquely identifies the builder
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
//go:generate mapstructure-to-hcl2 -type Config,CreateVNICDetails,ListImagesRequest
|
//go:generate mapstructure-to-hcl2 -type Config,CreateVNICDetails,ListImagesRequest,FlexShapeConfig
|
||||||
|
|
||||||
package oci
|
package oci
|
||||||
|
|
||||||
@@ -19,8 +19,8 @@ import (
|
|||||||
"github.com/hashicorp/packer-plugin-sdk/pathing"
|
"github.com/hashicorp/packer-plugin-sdk/pathing"
|
||||||
"github.com/hashicorp/packer-plugin-sdk/template/config"
|
"github.com/hashicorp/packer-plugin-sdk/template/config"
|
||||||
"github.com/hashicorp/packer-plugin-sdk/template/interpolate"
|
"github.com/hashicorp/packer-plugin-sdk/template/interpolate"
|
||||||
ocicommon "github.com/oracle/oci-go-sdk/common"
|
ocicommon "github.com/oracle/oci-go-sdk/v36/common"
|
||||||
ociauth "github.com/oracle/oci-go-sdk/common/auth"
|
ociauth "github.com/oracle/oci-go-sdk/v36/common/auth"
|
||||||
)
|
)
|
||||||
|
|
||||||
type CreateVNICDetails struct {
|
type CreateVNICDetails struct {
|
||||||
@@ -46,6 +46,11 @@ type ListImagesRequest struct {
|
|||||||
Shape *string `mapstructure:"shape"`
|
Shape *string `mapstructure:"shape"`
|
||||||
}
|
}
|
||||||
|
|
||||||
|
type FlexShapeConfig struct {
|
||||||
|
Ocpus *float32 `mapstructure:"ocpus" required:"false"`
|
||||||
|
MemoryInGBs *float32 `mapstructure:"memory_in_gbs" required:"false"`
|
||||||
|
}
|
||||||
|
|
||||||
type Config struct {
|
type Config struct {
|
||||||
common.PackerConfig `mapstructure:",squash"`
|
common.PackerConfig `mapstructure:",squash"`
|
||||||
Comm communicator.Config `mapstructure:",squash"`
|
Comm communicator.Config `mapstructure:",squash"`
|
||||||
@@ -91,6 +96,7 @@ type Config struct {
|
|||||||
InstanceTags map[string]string `mapstructure:"instance_tags"`
|
InstanceTags map[string]string `mapstructure:"instance_tags"`
|
||||||
InstanceDefinedTags map[string]map[string]interface{} `mapstructure:"instance_defined_tags"`
|
InstanceDefinedTags map[string]map[string]interface{} `mapstructure:"instance_defined_tags"`
|
||||||
Shape string `mapstructure:"shape"`
|
Shape string `mapstructure:"shape"`
|
||||||
|
ShapeConfig FlexShapeConfig `mapstructure:"shape_config"`
|
||||||
BootVolumeSizeInGBs int64 `mapstructure:"disk_size"`
|
BootVolumeSizeInGBs int64 `mapstructure:"disk_size"`
|
||||||
|
|
||||||
// Metadata optionally contains custom metadata key/value pairs provided in the
|
// Metadata optionally contains custom metadata key/value pairs provided in the
|
||||||
@@ -222,7 +228,7 @@ func (c *Config) Prepare(raws ...interface{}) error {
|
|||||||
}
|
}
|
||||||
|
|
||||||
providers := []ocicommon.ConfigurationProvider{
|
providers := []ocicommon.ConfigurationProvider{
|
||||||
NewRawConfigurationProvider(c.TenancyID, c.UserID, c.Region, c.Fingerprint, string(keyContent), &c.PassPhrase),
|
ocicommon.NewRawConfigurationProvider(c.TenancyID, c.UserID, c.Region, c.Fingerprint, string(keyContent), &c.PassPhrase),
|
||||||
}
|
}
|
||||||
|
|
||||||
if fileProvider != nil {
|
if fileProvider != nil {
|
||||||
@@ -277,6 +283,18 @@ func (c *Config) Prepare(raws ...interface{}) error {
|
|||||||
errs, errors.New("'shape' must be specified"))
|
errs, errors.New("'shape' must be specified"))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if strings.HasSuffix(c.Shape, "Flex") {
|
||||||
|
if c.ShapeConfig.Ocpus == nil {
|
||||||
|
errs = packersdk.MultiErrorAppend(
|
||||||
|
errs, errors.New("'Ocpus' must be specified when using flexible shapes"))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if c.ShapeConfig.MemoryInGBs != nil && c.ShapeConfig.Ocpus == nil {
|
||||||
|
errs = packersdk.MultiErrorAppend(
|
||||||
|
errs, errors.New("'Ocpus' must be specified if memory_in_gbs is specified"))
|
||||||
|
}
|
||||||
|
|
||||||
if (c.SubnetID == "") && (c.CreateVnicDetails.SubnetId == nil) {
|
if (c.SubnetID == "") && (c.CreateVnicDetails.SubnetId == nil) {
|
||||||
errs = packersdk.MultiErrorAppend(
|
errs = packersdk.MultiErrorAppend(
|
||||||
errs, errors.New("'subnet_ocid' must be specified"))
|
errs, errors.New("'subnet_ocid' must be specified"))
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
// Code generated by "mapstructure-to-hcl2 -type Config,CreateVNICDetails,ListImagesRequest"; DO NOT EDIT.
|
// Code generated by "mapstructure-to-hcl2 -type Config,CreateVNICDetails,ListImagesRequest,FlexShapeConfig"; DO NOT EDIT.
|
||||||
|
|
||||||
package oci
|
package oci
|
||||||
|
|
||||||
@@ -88,6 +88,7 @@ type FlatConfig struct {
|
|||||||
InstanceTags map[string]string `mapstructure:"instance_tags" cty:"instance_tags" hcl:"instance_tags"`
|
InstanceTags map[string]string `mapstructure:"instance_tags" cty:"instance_tags" hcl:"instance_tags"`
|
||||||
InstanceDefinedTags map[string]map[string]interface{} `mapstructure:"instance_defined_tags" cty:"instance_defined_tags" hcl:"instance_defined_tags"`
|
InstanceDefinedTags map[string]map[string]interface{} `mapstructure:"instance_defined_tags" cty:"instance_defined_tags" hcl:"instance_defined_tags"`
|
||||||
Shape *string `mapstructure:"shape" cty:"shape" hcl:"shape"`
|
Shape *string `mapstructure:"shape" cty:"shape" hcl:"shape"`
|
||||||
|
ShapeConfig *FlatFlexShapeConfig `mapstructure:"shape_config" cty:"shape_config" hcl:"shape_config"`
|
||||||
BootVolumeSizeInGBs *int64 `mapstructure:"disk_size" cty:"disk_size" hcl:"disk_size"`
|
BootVolumeSizeInGBs *int64 `mapstructure:"disk_size" cty:"disk_size" hcl:"disk_size"`
|
||||||
Metadata map[string]string `mapstructure:"metadata" cty:"metadata" hcl:"metadata"`
|
Metadata map[string]string `mapstructure:"metadata" cty:"metadata" hcl:"metadata"`
|
||||||
UserData *string `mapstructure:"user_data" cty:"user_data" hcl:"user_data"`
|
UserData *string `mapstructure:"user_data" cty:"user_data" hcl:"user_data"`
|
||||||
@@ -188,6 +189,7 @@ func (*FlatConfig) HCL2Spec() map[string]hcldec.Spec {
|
|||||||
"instance_tags": &hcldec.AttrSpec{Name: "instance_tags", Type: cty.Map(cty.String), Required: false},
|
"instance_tags": &hcldec.AttrSpec{Name: "instance_tags", Type: cty.Map(cty.String), Required: false},
|
||||||
"instance_defined_tags": &hcldec.AttrSpec{Name: "instance_defined_tags", Type: cty.Map(cty.String), Required: false},
|
"instance_defined_tags": &hcldec.AttrSpec{Name: "instance_defined_tags", Type: cty.Map(cty.String), Required: false},
|
||||||
"shape": &hcldec.AttrSpec{Name: "shape", Type: cty.String, Required: false},
|
"shape": &hcldec.AttrSpec{Name: "shape", Type: cty.String, Required: false},
|
||||||
|
"shape_config": &hcldec.BlockSpec{TypeName: "shape_config", Nested: hcldec.ObjectSpec((*FlatFlexShapeConfig)(nil).HCL2Spec())},
|
||||||
"disk_size": &hcldec.AttrSpec{Name: "disk_size", Type: cty.Number, Required: false},
|
"disk_size": &hcldec.AttrSpec{Name: "disk_size", Type: cty.Number, Required: false},
|
||||||
"metadata": &hcldec.AttrSpec{Name: "metadata", Type: cty.Map(cty.String), Required: false},
|
"metadata": &hcldec.AttrSpec{Name: "metadata", Type: cty.Map(cty.String), Required: false},
|
||||||
"user_data": &hcldec.AttrSpec{Name: "user_data", Type: cty.String, Required: false},
|
"user_data": &hcldec.AttrSpec{Name: "user_data", Type: cty.String, Required: false},
|
||||||
@@ -239,6 +241,31 @@ func (*FlatCreateVNICDetails) HCL2Spec() map[string]hcldec.Spec {
|
|||||||
return s
|
return s
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// FlatFlexShapeConfig is an auto-generated flat version of FlexShapeConfig.
|
||||||
|
// Where the contents of a field with a `mapstructure:,squash` tag are bubbled up.
|
||||||
|
type FlatFlexShapeConfig struct {
|
||||||
|
Ocpus *float32 `mapstructure:"ocpus" required:"false" cty:"ocpus" hcl:"ocpus"`
|
||||||
|
MemoryInGBs *float32 `mapstructure:"memory_in_gbs" required:"false" cty:"memory_in_gbs" hcl:"memory_in_gbs"`
|
||||||
|
}
|
||||||
|
|
||||||
|
// FlatMapstructure returns a new FlatFlexShapeConfig.
|
||||||
|
// FlatFlexShapeConfig is an auto-generated flat version of FlexShapeConfig.
|
||||||
|
// Where the contents a fields with a `mapstructure:,squash` tag are bubbled up.
|
||||||
|
func (*FlexShapeConfig) FlatMapstructure() interface{ HCL2Spec() map[string]hcldec.Spec } {
|
||||||
|
return new(FlatFlexShapeConfig)
|
||||||
|
}
|
||||||
|
|
||||||
|
// HCL2Spec returns the hcl spec of a FlexShapeConfig.
|
||||||
|
// This spec is used by HCL to read the fields of FlexShapeConfig.
|
||||||
|
// The decoded values from this spec will then be applied to a FlatFlexShapeConfig.
|
||||||
|
func (*FlatFlexShapeConfig) HCL2Spec() map[string]hcldec.Spec {
|
||||||
|
s := map[string]hcldec.Spec{
|
||||||
|
"ocpus": &hcldec.AttrSpec{Name: "ocpus", Type: cty.Number, Required: false},
|
||||||
|
"memory_in_gbs": &hcldec.AttrSpec{Name: "memory_in_gbs", Type: cty.Number, Required: false},
|
||||||
|
}
|
||||||
|
return s
|
||||||
|
}
|
||||||
|
|
||||||
// FlatListImagesRequest is an auto-generated flat version of ListImagesRequest.
|
// FlatListImagesRequest is an auto-generated flat version of ListImagesRequest.
|
||||||
// Where the contents of a field with a `mapstructure:,squash` tag are bubbled up.
|
// Where the contents of a field with a `mapstructure:,squash` tag are bubbled up.
|
||||||
type FlatListImagesRequest struct {
|
type FlatListImagesRequest struct {
|
||||||
|
|||||||
@@ -1,76 +0,0 @@
|
|||||||
package oci
|
|
||||||
|
|
||||||
import (
|
|
||||||
"crypto/rsa"
|
|
||||||
"errors"
|
|
||||||
"fmt"
|
|
||||||
|
|
||||||
"github.com/oracle/oci-go-sdk/common"
|
|
||||||
)
|
|
||||||
|
|
||||||
// rawConfigurationProvider allows a user to simply construct a configuration
|
|
||||||
// provider from raw values. It errors on access when those values are empty.
|
|
||||||
type rawConfigurationProvider struct {
|
|
||||||
tenancy string
|
|
||||||
user string
|
|
||||||
region string
|
|
||||||
fingerprint string
|
|
||||||
privateKey string
|
|
||||||
privateKeyPassphrase *string
|
|
||||||
}
|
|
||||||
|
|
||||||
// NewRawConfigurationProvider will create a rawConfigurationProvider.
|
|
||||||
func NewRawConfigurationProvider(tenancy, user, region, fingerprint, privateKey string, privateKeyPassphrase *string) common.ConfigurationProvider {
|
|
||||||
return rawConfigurationProvider{tenancy, user, region, fingerprint, privateKey, privateKeyPassphrase}
|
|
||||||
}
|
|
||||||
|
|
||||||
func (p rawConfigurationProvider) PrivateRSAKey() (key *rsa.PrivateKey, err error) {
|
|
||||||
return common.PrivateKeyFromBytes([]byte(p.privateKey), p.privateKeyPassphrase)
|
|
||||||
}
|
|
||||||
|
|
||||||
func (p rawConfigurationProvider) KeyID() (keyID string, err error) {
|
|
||||||
tenancy, err := p.TenancyOCID()
|
|
||||||
if err != nil {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
user, err := p.UserOCID()
|
|
||||||
if err != nil {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
fingerprint, err := p.KeyFingerprint()
|
|
||||||
if err != nil {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
return fmt.Sprintf("%s/%s/%s", tenancy, user, fingerprint), nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func (p rawConfigurationProvider) TenancyOCID() (string, error) {
|
|
||||||
if p.tenancy == "" {
|
|
||||||
return "", errors.New("no tenancy provided")
|
|
||||||
}
|
|
||||||
return p.tenancy, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func (p rawConfigurationProvider) UserOCID() (string, error) {
|
|
||||||
if p.user == "" {
|
|
||||||
return "", errors.New("no user provided")
|
|
||||||
}
|
|
||||||
return p.user, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func (p rawConfigurationProvider) KeyFingerprint() (string, error) {
|
|
||||||
if p.fingerprint == "" {
|
|
||||||
return "", errors.New("no fingerprint provided")
|
|
||||||
}
|
|
||||||
return p.fingerprint, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func (p rawConfigurationProvider) Region() (string, error) {
|
|
||||||
if p.region == "" {
|
|
||||||
return "", errors.New("no region provided")
|
|
||||||
}
|
|
||||||
return p.region, nil
|
|
||||||
}
|
|
||||||
@@ -131,7 +131,7 @@ func TestConfig(t *testing.T) {
|
|||||||
|
|
||||||
t.Run("NoAccessConfig", func(t *testing.T) {
|
t.Run("NoAccessConfig", func(t *testing.T) {
|
||||||
raw := testConfig(cfgFile)
|
raw := testConfig(cfgFile)
|
||||||
delete(raw, "access_cfg_file")
|
raw["access_cfg_file"] = "/tmp/random/access/config/file/should/not/exist"
|
||||||
|
|
||||||
var c Config
|
var c Config
|
||||||
errs := c.Prepare(raw)
|
errs := c.Prepare(raw)
|
||||||
@@ -140,6 +140,10 @@ func TestConfig(t *testing.T) {
|
|||||||
"'user_ocid'", "'tenancy_ocid'", "'fingerprint'", "'key_file'",
|
"'user_ocid'", "'tenancy_ocid'", "'fingerprint'", "'key_file'",
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if errs == nil {
|
||||||
|
t.Fatalf("Expected errors %q but got none", expectedErrors)
|
||||||
|
}
|
||||||
|
|
||||||
s := errs.Error()
|
s := errs.Error()
|
||||||
for _, expected := range expectedErrors {
|
for _, expected := range expectedErrors {
|
||||||
if !strings.Contains(s, expected) {
|
if !strings.Contains(s, expected) {
|
||||||
|
|||||||
@@ -3,7 +3,7 @@ package oci
|
|||||||
import (
|
import (
|
||||||
"context"
|
"context"
|
||||||
|
|
||||||
"github.com/oracle/oci-go-sdk/core"
|
"github.com/oracle/oci-go-sdk/v36/core"
|
||||||
)
|
)
|
||||||
|
|
||||||
// Driver interfaces between the builder steps and the OCI SDK.
|
// Driver interfaces between the builder steps and the OCI SDK.
|
||||||
|
|||||||
@@ -3,7 +3,7 @@ package oci
|
|||||||
import (
|
import (
|
||||||
"context"
|
"context"
|
||||||
|
|
||||||
"github.com/oracle/oci-go-sdk/core"
|
"github.com/oracle/oci-go-sdk/v36/core"
|
||||||
)
|
)
|
||||||
|
|
||||||
// driverMock implements the Driver interface and communicates with Oracle
|
// driverMock implements the Driver interface and communicates with Oracle
|
||||||
|
|||||||
@@ -11,8 +11,8 @@ import (
|
|||||||
"sync/atomic"
|
"sync/atomic"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
"github.com/oracle/oci-go-sdk/common"
|
"github.com/oracle/oci-go-sdk/v36/common"
|
||||||
core "github.com/oracle/oci-go-sdk/core"
|
core "github.com/oracle/oci-go-sdk/v36/core"
|
||||||
)
|
)
|
||||||
|
|
||||||
// driverOCI implements the Driver interface and communicates with Oracle
|
// driverOCI implements the Driver interface and communicates with Oracle
|
||||||
@@ -158,6 +158,14 @@ func (d *driverOCI) CreateInstance(ctx context.Context, publicKey string) (strin
|
|||||||
Metadata: metadata,
|
Metadata: metadata,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if d.cfg.ShapeConfig.Ocpus != nil {
|
||||||
|
LaunchInstanceShapeConfigDetails := core.LaunchInstanceShapeConfigDetails{
|
||||||
|
Ocpus: d.cfg.ShapeConfig.Ocpus,
|
||||||
|
MemoryInGBs: d.cfg.ShapeConfig.MemoryInGBs,
|
||||||
|
}
|
||||||
|
instanceDetails.ShapeConfig = &LaunchInstanceShapeConfigDetails
|
||||||
|
}
|
||||||
|
|
||||||
instance, err := d.computeClient.LaunchInstance(context.TODO(), core.LaunchInstanceRequest{
|
instance, err := d.computeClient.LaunchInstance(context.TODO(), core.LaunchInstanceRequest{
|
||||||
LaunchInstanceDetails: instanceDetails,
|
LaunchInstanceDetails: instanceDetails,
|
||||||
RequestMetadata: requestMetadata,
|
RequestMetadata: requestMetadata,
|
||||||
|
|||||||
@@ -3,6 +3,8 @@ package oci
|
|||||||
import (
|
import (
|
||||||
"crypto/rand"
|
"crypto/rand"
|
||||||
"crypto/rsa"
|
"crypto/rsa"
|
||||||
|
|
||||||
|
"github.com/oracle/oci-go-sdk/v36/common"
|
||||||
)
|
)
|
||||||
|
|
||||||
// Mock struct to be used during testing to obtain Instance Principals.
|
// Mock struct to be used during testing to obtain Instance Principals.
|
||||||
@@ -32,3 +34,10 @@ func (p instancePrincipalConfigurationProviderMock) KeyFingerprint() (string, er
|
|||||||
func (p instancePrincipalConfigurationProviderMock) Region() (string, error) {
|
func (p instancePrincipalConfigurationProviderMock) Region() (string, error) {
|
||||||
return "some_random_region", nil
|
return "some_random_region", nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (p instancePrincipalConfigurationProviderMock) AuthType() (common.AuthConfig, error) {
|
||||||
|
return common.AuthConfig{
|
||||||
|
AuthType: common.InstancePrincipal,
|
||||||
|
IsFromConfigFile: false,
|
||||||
|
OboToken: nil}, nil
|
||||||
|
}
|
||||||
|
|||||||
@@ -59,6 +59,7 @@ func (c *StorageConfig) AddStorageDevices(existingDevices object.VirtualDeviceLi
|
|||||||
}
|
}
|
||||||
|
|
||||||
existingDevices.AssignController(disk, controllers[dc.ControllerIndex])
|
existingDevices.AssignController(disk, controllers[dc.ControllerIndex])
|
||||||
|
existingDevices = append(existingDevices, disk)
|
||||||
newDevices = append(newDevices, disk)
|
newDevices = append(newDevices, disk)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -0,0 +1,46 @@
|
|||||||
|
package driver
|
||||||
|
|
||||||
|
import (
|
||||||
|
"testing"
|
||||||
|
|
||||||
|
"github.com/vmware/govmomi/object"
|
||||||
|
)
|
||||||
|
|
||||||
|
func TestAddStorageDevices(t *testing.T) {
|
||||||
|
config := &StorageConfig{
|
||||||
|
DiskControllerType: []string{"pvscsi"},
|
||||||
|
Storage: []Disk{
|
||||||
|
{
|
||||||
|
DiskSize: 3072,
|
||||||
|
DiskThinProvisioned: true,
|
||||||
|
ControllerIndex: 0,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
DiskSize: 20480,
|
||||||
|
DiskThinProvisioned: true,
|
||||||
|
ControllerIndex: 0,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
noExistingDevices := object.VirtualDeviceList{}
|
||||||
|
storageConfigSpec, err := config.AddStorageDevices(noExistingDevices)
|
||||||
|
if err != nil {
|
||||||
|
t.Fatalf("unexpected erro: %q", err.Error())
|
||||||
|
}
|
||||||
|
if len(storageConfigSpec) != 3 {
|
||||||
|
t.Fatalf("Expecting VirtualDeviceList to have 3 storage devices but had %d", len(storageConfigSpec))
|
||||||
|
}
|
||||||
|
|
||||||
|
existingDevices := object.VirtualDeviceList{}
|
||||||
|
device, err := existingDevices.CreateNVMEController()
|
||||||
|
existingDevices = append(existingDevices, device)
|
||||||
|
|
||||||
|
storageConfigSpec, err = config.AddStorageDevices(existingDevices)
|
||||||
|
if err != nil {
|
||||||
|
t.Fatalf("unexpected erro: %q", err.Error())
|
||||||
|
}
|
||||||
|
if len(storageConfigSpec) != 3 {
|
||||||
|
t.Fatalf("Expecting VirtualDeviceList to have 3 storage devices but had %d", len(storageConfigSpec))
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -60,3 +60,46 @@ func TestVirtualMachineDriver_Configure(t *testing.T) {
|
|||||||
t.Fatalf("Configure should fail")
|
t.Fatalf("Configure should fail")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func TestVirtualMachineDriver_CreateVM(t *testing.T) {
|
||||||
|
sim, err := NewVCenterSimulator()
|
||||||
|
if err != nil {
|
||||||
|
t.Fatalf("should not fail: %s", err.Error())
|
||||||
|
}
|
||||||
|
defer sim.Close()
|
||||||
|
|
||||||
|
_, datastore := sim.ChooseSimulatorPreCreatedDatastore()
|
||||||
|
|
||||||
|
config := &CreateConfig{
|
||||||
|
Annotation: "mock annotation",
|
||||||
|
Name: "mock name",
|
||||||
|
Host: "DC0_H0",
|
||||||
|
Datastore: datastore.Name,
|
||||||
|
NICs: []NIC{
|
||||||
|
{
|
||||||
|
Network: "VM Network",
|
||||||
|
NetworkCard: "vmxnet3",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
StorageConfig: StorageConfig{
|
||||||
|
DiskControllerType: []string{"pvscsi"},
|
||||||
|
Storage: []Disk{
|
||||||
|
{
|
||||||
|
DiskSize: 3072,
|
||||||
|
DiskThinProvisioned: true,
|
||||||
|
ControllerIndex: 0,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
DiskSize: 20480,
|
||||||
|
DiskThinProvisioned: true,
|
||||||
|
ControllerIndex: 0,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
_, err = sim.driver.CreateVM(config)
|
||||||
|
if err != nil {
|
||||||
|
t.Fatalf("unexpected error %s", err.Error())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|||||||
@@ -1,31 +0,0 @@
|
|||||||
package main
|
|
||||||
|
|
||||||
import (
|
|
||||||
"fmt"
|
|
||||||
"os"
|
|
||||||
|
|
||||||
"github.com/hashicorp/packer-plugin-sdk/plugin"
|
|
||||||
"github.com/hashicorp/packer/builder/amazon/ebs"
|
|
||||||
"github.com/hashicorp/packer/builder/amazon/ebssurrogate"
|
|
||||||
"github.com/hashicorp/packer/builder/amazon/ebsvolume"
|
|
||||||
"github.com/hashicorp/packer/builder/osc/chroot"
|
|
||||||
amazonami "github.com/hashicorp/packer/datasource/amazon/ami"
|
|
||||||
"github.com/hashicorp/packer/datasource/amazon/secretsmanager"
|
|
||||||
amazonimport "github.com/hashicorp/packer/post-processor/amazon-import"
|
|
||||||
)
|
|
||||||
|
|
||||||
func main() {
|
|
||||||
pps := plugin.NewSet()
|
|
||||||
pps.RegisterBuilder("ebs", new(ebs.Builder))
|
|
||||||
pps.RegisterBuilder("chroot", new(chroot.Builder))
|
|
||||||
pps.RegisterBuilder("ebssurrogate", new(ebssurrogate.Builder))
|
|
||||||
pps.RegisterBuilder("ebsvolume", new(ebsvolume.Builder))
|
|
||||||
pps.RegisterPostProcessor("import", new(amazonimport.PostProcessor))
|
|
||||||
pps.RegisterDatasource("ami", new(amazonami.Datasource))
|
|
||||||
pps.RegisterDatasource("secretsmanager", new(secretsmanager.Datasource))
|
|
||||||
err := pps.Run()
|
|
||||||
if err != nil {
|
|
||||||
fmt.Fprintln(os.Stderr, err.Error())
|
|
||||||
os.Exit(1)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -383,6 +383,19 @@ func TestBuild(t *testing.T) {
|
|||||||
},
|
},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
name: "hcl - dynamic source blocks in a build block",
|
||||||
|
args: []string{
|
||||||
|
testFixture("hcl", "dynamic", "build.pkr.hcl"),
|
||||||
|
},
|
||||||
|
fileCheck: fileCheck{
|
||||||
|
expectedContent: map[string]string{
|
||||||
|
"dummy.txt": "layers/base/main/files",
|
||||||
|
"postgres/13.txt": "layers/base/main/files\nlayers/base/init/files\nlayers/postgres/files",
|
||||||
|
},
|
||||||
|
expected: []string{"dummy-fooo.txt", "dummy-baar.txt", "postgres/13-fooo.txt", "postgres/13-baar.txt"},
|
||||||
|
},
|
||||||
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
for _, tt := range tc {
|
for _, tt := range tc {
|
||||||
|
|||||||
@@ -8,8 +8,8 @@ import (
|
|||||||
"runtime"
|
"runtime"
|
||||||
"testing"
|
"testing"
|
||||||
|
|
||||||
|
"github.com/hashicorp/packer-plugin-amazon/builder/ebs"
|
||||||
packersdk "github.com/hashicorp/packer-plugin-sdk/packer"
|
packersdk "github.com/hashicorp/packer-plugin-sdk/packer"
|
||||||
"github.com/hashicorp/packer/builder/amazon/ebs"
|
|
||||||
"github.com/hashicorp/packer/builder/file"
|
"github.com/hashicorp/packer/builder/file"
|
||||||
"github.com/hashicorp/packer/builder/null"
|
"github.com/hashicorp/packer/builder/null"
|
||||||
"github.com/hashicorp/packer/packer"
|
"github.com/hashicorp/packer/packer"
|
||||||
@@ -95,6 +95,8 @@ func TestHelperProcess(*testing.T) {
|
|||||||
os.Exit((&BuildCommand{Meta: commandMeta()}).Run(args))
|
os.Exit((&BuildCommand{Meta: commandMeta()}).Run(args))
|
||||||
case "hcl2_upgrade":
|
case "hcl2_upgrade":
|
||||||
os.Exit((&HCL2UpgradeCommand{Meta: commandMeta()}).Run(args))
|
os.Exit((&HCL2UpgradeCommand{Meta: commandMeta()}).Run(args))
|
||||||
|
case "version":
|
||||||
|
os.Exit((&VersionCommand{Meta: commandMeta()}).Run(args))
|
||||||
default:
|
default:
|
||||||
fmt.Fprintf(os.Stderr, "Unknown command %q\n", cmd)
|
fmt.Fprintf(os.Stderr, "Unknown command %q\n", cmd)
|
||||||
os.Exit(2)
|
os.Exit(2)
|
||||||
|
|||||||
@@ -16,9 +16,9 @@ import (
|
|||||||
|
|
||||||
"github.com/hashicorp/go-multierror"
|
"github.com/hashicorp/go-multierror"
|
||||||
"github.com/hashicorp/hcl/v2/hclwrite"
|
"github.com/hashicorp/hcl/v2/hclwrite"
|
||||||
|
awscommon "github.com/hashicorp/packer-plugin-amazon/builder/common"
|
||||||
hcl2shim "github.com/hashicorp/packer-plugin-sdk/hcl2helper"
|
hcl2shim "github.com/hashicorp/packer-plugin-sdk/hcl2helper"
|
||||||
"github.com/hashicorp/packer-plugin-sdk/template"
|
"github.com/hashicorp/packer-plugin-sdk/template"
|
||||||
awscommon "github.com/hashicorp/packer/builder/amazon/common"
|
|
||||||
"github.com/hashicorp/packer/packer"
|
"github.com/hashicorp/packer/packer"
|
||||||
"github.com/mitchellh/mapstructure"
|
"github.com/mitchellh/mapstructure"
|
||||||
"github.com/posener/complete"
|
"github.com/posener/complete"
|
||||||
|
|||||||
@@ -170,7 +170,6 @@ packer {
|
|||||||
ui.Say(msg)
|
ui.Say(msg)
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
return ret
|
return ret
|
||||||
}
|
}
|
||||||
|
|||||||
+47
-64
@@ -14,11 +14,6 @@ import (
|
|||||||
"github.com/hashicorp/packer-plugin-sdk/plugin"
|
"github.com/hashicorp/packer-plugin-sdk/plugin"
|
||||||
|
|
||||||
alicloudecsbuilder "github.com/hashicorp/packer/builder/alicloud/ecs"
|
alicloudecsbuilder "github.com/hashicorp/packer/builder/alicloud/ecs"
|
||||||
amazonchrootbuilder "github.com/hashicorp/packer/builder/amazon/chroot"
|
|
||||||
amazonebsbuilder "github.com/hashicorp/packer/builder/amazon/ebs"
|
|
||||||
amazonebssurrogatebuilder "github.com/hashicorp/packer/builder/amazon/ebssurrogate"
|
|
||||||
amazonebsvolumebuilder "github.com/hashicorp/packer/builder/amazon/ebsvolume"
|
|
||||||
amazoninstancebuilder "github.com/hashicorp/packer/builder/amazon/instance"
|
|
||||||
azurearmbuilder "github.com/hashicorp/packer/builder/azure/arm"
|
azurearmbuilder "github.com/hashicorp/packer/builder/azure/arm"
|
||||||
azurechrootbuilder "github.com/hashicorp/packer/builder/azure/chroot"
|
azurechrootbuilder "github.com/hashicorp/packer/builder/azure/chroot"
|
||||||
azuredtlbuilder "github.com/hashicorp/packer/builder/azure/dtl"
|
azuredtlbuilder "github.com/hashicorp/packer/builder/azure/dtl"
|
||||||
@@ -64,10 +59,7 @@ import (
|
|||||||
vsphereclonebuilder "github.com/hashicorp/packer/builder/vsphere/clone"
|
vsphereclonebuilder "github.com/hashicorp/packer/builder/vsphere/clone"
|
||||||
vsphereisobuilder "github.com/hashicorp/packer/builder/vsphere/iso"
|
vsphereisobuilder "github.com/hashicorp/packer/builder/vsphere/iso"
|
||||||
yandexbuilder "github.com/hashicorp/packer/builder/yandex"
|
yandexbuilder "github.com/hashicorp/packer/builder/yandex"
|
||||||
amazonamidatasource "github.com/hashicorp/packer/datasource/amazon/ami"
|
|
||||||
amazonsecretsmanagerdatasource "github.com/hashicorp/packer/datasource/amazon/secretsmanager"
|
|
||||||
alicloudimportpostprocessor "github.com/hashicorp/packer/post-processor/alicloud-import"
|
alicloudimportpostprocessor "github.com/hashicorp/packer/post-processor/alicloud-import"
|
||||||
amazonimportpostprocessor "github.com/hashicorp/packer/post-processor/amazon-import"
|
|
||||||
artificepostprocessor "github.com/hashicorp/packer/post-processor/artifice"
|
artificepostprocessor "github.com/hashicorp/packer/post-processor/artifice"
|
||||||
checksumpostprocessor "github.com/hashicorp/packer/post-processor/checksum"
|
checksumpostprocessor "github.com/hashicorp/packer/post-processor/checksum"
|
||||||
compresspostprocessor "github.com/hashicorp/packer/post-processor/compress"
|
compresspostprocessor "github.com/hashicorp/packer/post-processor/compress"
|
||||||
@@ -108,57 +100,52 @@ type PluginCommand struct {
|
|||||||
}
|
}
|
||||||
|
|
||||||
var Builders = map[string]packersdk.Builder{
|
var Builders = map[string]packersdk.Builder{
|
||||||
"alicloud-ecs": new(alicloudecsbuilder.Builder),
|
"alicloud-ecs": new(alicloudecsbuilder.Builder),
|
||||||
"amazon-chroot": new(amazonchrootbuilder.Builder),
|
"azure-arm": new(azurearmbuilder.Builder),
|
||||||
"amazon-ebs": new(amazonebsbuilder.Builder),
|
"azure-chroot": new(azurechrootbuilder.Builder),
|
||||||
"amazon-ebssurrogate": new(amazonebssurrogatebuilder.Builder),
|
"azure-dtl": new(azuredtlbuilder.Builder),
|
||||||
"amazon-ebsvolume": new(amazonebsvolumebuilder.Builder),
|
"cloudstack": new(cloudstackbuilder.Builder),
|
||||||
"amazon-instance": new(amazoninstancebuilder.Builder),
|
"digitalocean": new(digitaloceanbuilder.Builder),
|
||||||
"azure-arm": new(azurearmbuilder.Builder),
|
"file": new(filebuilder.Builder),
|
||||||
"azure-chroot": new(azurechrootbuilder.Builder),
|
"googlecompute": new(googlecomputebuilder.Builder),
|
||||||
"azure-dtl": new(azuredtlbuilder.Builder),
|
"hcloud": new(hcloudbuilder.Builder),
|
||||||
"cloudstack": new(cloudstackbuilder.Builder),
|
"hyperone": new(hyperonebuilder.Builder),
|
||||||
"digitalocean": new(digitaloceanbuilder.Builder),
|
"hyperv-iso": new(hypervisobuilder.Builder),
|
||||||
"file": new(filebuilder.Builder),
|
"hyperv-vmcx": new(hypervvmcxbuilder.Builder),
|
||||||
"googlecompute": new(googlecomputebuilder.Builder),
|
"jdcloud": new(jdcloudbuilder.Builder),
|
||||||
"hcloud": new(hcloudbuilder.Builder),
|
"linode": new(linodebuilder.Builder),
|
||||||
"hyperone": new(hyperonebuilder.Builder),
|
"lxc": new(lxcbuilder.Builder),
|
||||||
"hyperv-iso": new(hypervisobuilder.Builder),
|
"lxd": new(lxdbuilder.Builder),
|
||||||
"hyperv-vmcx": new(hypervvmcxbuilder.Builder),
|
"ncloud": new(ncloudbuilder.Builder),
|
||||||
"jdcloud": new(jdcloudbuilder.Builder),
|
"null": new(nullbuilder.Builder),
|
||||||
"linode": new(linodebuilder.Builder),
|
"oneandone": new(oneandonebuilder.Builder),
|
||||||
"lxc": new(lxcbuilder.Builder),
|
"openstack": new(openstackbuilder.Builder),
|
||||||
"lxd": new(lxdbuilder.Builder),
|
"oracle-classic": new(oracleclassicbuilder.Builder),
|
||||||
"ncloud": new(ncloudbuilder.Builder),
|
"oracle-oci": new(oracleocibuilder.Builder),
|
||||||
"null": new(nullbuilder.Builder),
|
"osc-bsu": new(oscbsubuilder.Builder),
|
||||||
"oneandone": new(oneandonebuilder.Builder),
|
"osc-bsusurrogate": new(oscbsusurrogatebuilder.Builder),
|
||||||
"openstack": new(openstackbuilder.Builder),
|
"osc-bsuvolume": new(oscbsuvolumebuilder.Builder),
|
||||||
"oracle-classic": new(oracleclassicbuilder.Builder),
|
"osc-chroot": new(oscchrootbuilder.Builder),
|
||||||
"oracle-oci": new(oracleocibuilder.Builder),
|
"parallels-iso": new(parallelsisobuilder.Builder),
|
||||||
"osc-bsu": new(oscbsubuilder.Builder),
|
"parallels-pvm": new(parallelspvmbuilder.Builder),
|
||||||
"osc-bsusurrogate": new(oscbsusurrogatebuilder.Builder),
|
"profitbricks": new(profitbricksbuilder.Builder),
|
||||||
"osc-bsuvolume": new(oscbsuvolumebuilder.Builder),
|
"proxmox": new(proxmoxbuilder.Builder),
|
||||||
"osc-chroot": new(oscchrootbuilder.Builder),
|
"proxmox-clone": new(proxmoxclonebuilder.Builder),
|
||||||
"parallels-iso": new(parallelsisobuilder.Builder),
|
"proxmox-iso": new(proxmoxisobuilder.Builder),
|
||||||
"parallels-pvm": new(parallelspvmbuilder.Builder),
|
"qemu": new(qemubuilder.Builder),
|
||||||
"profitbricks": new(profitbricksbuilder.Builder),
|
"scaleway": new(scalewaybuilder.Builder),
|
||||||
"proxmox": new(proxmoxbuilder.Builder),
|
"tencentcloud-cvm": new(tencentcloudcvmbuilder.Builder),
|
||||||
"proxmox-clone": new(proxmoxclonebuilder.Builder),
|
"triton": new(tritonbuilder.Builder),
|
||||||
"proxmox-iso": new(proxmoxisobuilder.Builder),
|
"ucloud-uhost": new(uclouduhostbuilder.Builder),
|
||||||
"qemu": new(qemubuilder.Builder),
|
"vagrant": new(vagrantbuilder.Builder),
|
||||||
"scaleway": new(scalewaybuilder.Builder),
|
"virtualbox-iso": new(virtualboxisobuilder.Builder),
|
||||||
"tencentcloud-cvm": new(tencentcloudcvmbuilder.Builder),
|
"virtualbox-ovf": new(virtualboxovfbuilder.Builder),
|
||||||
"triton": new(tritonbuilder.Builder),
|
"virtualbox-vm": new(virtualboxvmbuilder.Builder),
|
||||||
"ucloud-uhost": new(uclouduhostbuilder.Builder),
|
"vmware-iso": new(vmwareisobuilder.Builder),
|
||||||
"vagrant": new(vagrantbuilder.Builder),
|
"vmware-vmx": new(vmwarevmxbuilder.Builder),
|
||||||
"virtualbox-iso": new(virtualboxisobuilder.Builder),
|
"vsphere-clone": new(vsphereclonebuilder.Builder),
|
||||||
"virtualbox-ovf": new(virtualboxovfbuilder.Builder),
|
"vsphere-iso": new(vsphereisobuilder.Builder),
|
||||||
"virtualbox-vm": new(virtualboxvmbuilder.Builder),
|
"yandex": new(yandexbuilder.Builder),
|
||||||
"vmware-iso": new(vmwareisobuilder.Builder),
|
|
||||||
"vmware-vmx": new(vmwarevmxbuilder.Builder),
|
|
||||||
"vsphere-clone": new(vsphereclonebuilder.Builder),
|
|
||||||
"vsphere-iso": new(vsphereisobuilder.Builder),
|
|
||||||
"yandex": new(yandexbuilder.Builder),
|
|
||||||
}
|
}
|
||||||
|
|
||||||
var Provisioners = map[string]packersdk.Provisioner{
|
var Provisioners = map[string]packersdk.Provisioner{
|
||||||
@@ -184,7 +171,6 @@ var Provisioners = map[string]packersdk.Provisioner{
|
|||||||
|
|
||||||
var PostProcessors = map[string]packersdk.PostProcessor{
|
var PostProcessors = map[string]packersdk.PostProcessor{
|
||||||
"alicloud-import": new(alicloudimportpostprocessor.PostProcessor),
|
"alicloud-import": new(alicloudimportpostprocessor.PostProcessor),
|
||||||
"amazon-import": new(amazonimportpostprocessor.PostProcessor),
|
|
||||||
"artifice": new(artificepostprocessor.PostProcessor),
|
"artifice": new(artificepostprocessor.PostProcessor),
|
||||||
"checksum": new(checksumpostprocessor.PostProcessor),
|
"checksum": new(checksumpostprocessor.PostProcessor),
|
||||||
"compress": new(compresspostprocessor.PostProcessor),
|
"compress": new(compresspostprocessor.PostProcessor),
|
||||||
@@ -202,10 +188,7 @@ var PostProcessors = map[string]packersdk.PostProcessor{
|
|||||||
"yandex-import": new(yandeximportpostprocessor.PostProcessor),
|
"yandex-import": new(yandeximportpostprocessor.PostProcessor),
|
||||||
}
|
}
|
||||||
|
|
||||||
var Datasources = map[string]packersdk.Datasource{
|
var Datasources = map[string]packersdk.Datasource{}
|
||||||
"amazon-ami": new(amazonamidatasource.Datasource),
|
|
||||||
"amazon-secretsmanager": new(amazonsecretsmanagerdatasource.Datasource),
|
|
||||||
}
|
|
||||||
|
|
||||||
var pluginRegexp = regexp.MustCompile("packer-(builder|post-processor|provisioner|datasource)-(.+)")
|
var pluginRegexp = regexp.MustCompile("packer-(builder|post-processor|provisioner|datasource)-(.+)")
|
||||||
|
|
||||||
|
|||||||
@@ -0,0 +1,47 @@
|
|||||||
|
|
||||||
|
source "file" "base" {
|
||||||
|
}
|
||||||
|
|
||||||
|
variables {
|
||||||
|
images = {
|
||||||
|
dummy = {
|
||||||
|
image = "dummy"
|
||||||
|
layers = ["base/main"]
|
||||||
|
}
|
||||||
|
postgres = {
|
||||||
|
image = "postgres/13"
|
||||||
|
layers = ["base/main", "base/init", "postgres"]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
locals {
|
||||||
|
files = {
|
||||||
|
foo = {
|
||||||
|
destination = "fooo"
|
||||||
|
}
|
||||||
|
bar = {
|
||||||
|
destination = "baar"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
build {
|
||||||
|
dynamic "source" {
|
||||||
|
for_each = var.images
|
||||||
|
labels = ["file.base"]
|
||||||
|
content {
|
||||||
|
name = source.key
|
||||||
|
target = "${source.value.image}.txt"
|
||||||
|
content = join("\n", formatlist("layers/%s/files", var.images[source.key].layers))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
dynamic "provisioner" {
|
||||||
|
for_each = local.files
|
||||||
|
labels = ["shell-local"]
|
||||||
|
content {
|
||||||
|
inline = ["echo '' > ${var.images[source.name].image}-${provisioner.value.destination}.txt"]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -7,6 +7,14 @@ import (
|
|||||||
// still vendored with Packer for now. Importing as library instead of
|
// still vendored with Packer for now. Importing as library instead of
|
||||||
// forcing use of packer init, until packer v1.8.0
|
// forcing use of packer init, until packer v1.8.0
|
||||||
exoscaleimportpostprocessor "github.com/exoscale/packer-plugin-exoscale/post-processor/exoscale-import"
|
exoscaleimportpostprocessor "github.com/exoscale/packer-plugin-exoscale/post-processor/exoscale-import"
|
||||||
|
amazonchrootbuilder "github.com/hashicorp/packer-plugin-amazon/builder/chroot"
|
||||||
|
amazonebsbuilder "github.com/hashicorp/packer-plugin-amazon/builder/ebs"
|
||||||
|
amazonebssurrogatebuilder "github.com/hashicorp/packer-plugin-amazon/builder/ebssurrogate"
|
||||||
|
amazonebsvolumebuilder "github.com/hashicorp/packer-plugin-amazon/builder/ebsvolume"
|
||||||
|
amazoninstancebuilder "github.com/hashicorp/packer-plugin-amazon/builder/instance"
|
||||||
|
amazonamidatasource "github.com/hashicorp/packer-plugin-amazon/datasource/ami"
|
||||||
|
amazonsecretsmanagerdatasource "github.com/hashicorp/packer-plugin-amazon/datasource/secretsmanager"
|
||||||
|
anazibimportpostprocessor "github.com/hashicorp/packer-plugin-amazon/post-processor/import"
|
||||||
dockerbuilder "github.com/hashicorp/packer-plugin-docker/builder/docker"
|
dockerbuilder "github.com/hashicorp/packer-plugin-docker/builder/docker"
|
||||||
dockerimportpostprocessor "github.com/hashicorp/packer-plugin-docker/post-processor/docker-import"
|
dockerimportpostprocessor "github.com/hashicorp/packer-plugin-docker/post-processor/docker-import"
|
||||||
dockerpushpostprocessor "github.com/hashicorp/packer-plugin-docker/post-processor/docker-push"
|
dockerpushpostprocessor "github.com/hashicorp/packer-plugin-docker/post-processor/docker-push"
|
||||||
@@ -14,10 +22,22 @@ import (
|
|||||||
dockertagpostprocessor "github.com/hashicorp/packer-plugin-docker/post-processor/docker-tag"
|
dockertagpostprocessor "github.com/hashicorp/packer-plugin-docker/post-processor/docker-tag"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
// VendoredDatasources are datasource components that were once bundled with the
|
||||||
|
// Packer core, but are now being imported from their counterpart plugin repos
|
||||||
|
var VendoredDatasources = map[string]packersdk.Datasource{
|
||||||
|
"amazon-ami": new(amazonamidatasource.Datasource),
|
||||||
|
"amazon-secretsmanager": new(amazonsecretsmanagerdatasource.Datasource),
|
||||||
|
}
|
||||||
|
|
||||||
// VendoredBuilders are builder components that were once bundled with the
|
// VendoredBuilders are builder components that were once bundled with the
|
||||||
// Packer core, but are now being imported from their counterpart plugin repos
|
// Packer core, but are now being imported from their counterpart plugin repos
|
||||||
var VendoredBuilders = map[string]packersdk.Builder{
|
var VendoredBuilders = map[string]packersdk.Builder{
|
||||||
"docker": new(dockerbuilder.Builder),
|
"docker": new(dockerbuilder.Builder),
|
||||||
|
"amazon-ebs": new(amazonebsbuilder.Builder),
|
||||||
|
"amazon-chroot": new(amazonchrootbuilder.Builder),
|
||||||
|
"amazon-ebssurrogate": new(amazonebssurrogatebuilder.Builder),
|
||||||
|
"amazon-ebsvolume": new(amazonebsvolumebuilder.Builder),
|
||||||
|
"amazon-instance": new(amazoninstancebuilder.Builder),
|
||||||
}
|
}
|
||||||
|
|
||||||
// VendoredProvisioners are provisioner components that were once bundled with the
|
// VendoredProvisioners are provisioner components that were once bundled with the
|
||||||
@@ -32,11 +52,19 @@ var VendoredPostProcessors = map[string]packersdk.PostProcessor{
|
|||||||
"docker-save": new(dockersavepostprocessor.PostProcessor),
|
"docker-save": new(dockersavepostprocessor.PostProcessor),
|
||||||
"docker-tag": new(dockertagpostprocessor.PostProcessor),
|
"docker-tag": new(dockertagpostprocessor.PostProcessor),
|
||||||
"exoscale-import": new(exoscaleimportpostprocessor.PostProcessor),
|
"exoscale-import": new(exoscaleimportpostprocessor.PostProcessor),
|
||||||
|
"amazon-import": new(anazibimportpostprocessor.PostProcessor),
|
||||||
}
|
}
|
||||||
|
|
||||||
// Upon init lets load up any plugins that were vendored manually into the default
|
// Upon init lets load up any plugins that were vendored manually into the default
|
||||||
// set of plugins.
|
// set of plugins.
|
||||||
func init() {
|
func init() {
|
||||||
|
for k, v := range VendoredDatasources {
|
||||||
|
if _, ok := Datasources[k]; ok {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
Datasources[k] = v
|
||||||
|
}
|
||||||
|
|
||||||
for k, v := range VendoredBuilders {
|
for k, v := range VendoredBuilders {
|
||||||
if _, ok := Builders[k]; ok {
|
if _, ok := Builders[k]; ok {
|
||||||
continue
|
continue
|
||||||
|
|||||||
@@ -1,11 +1,37 @@
|
|||||||
package command
|
package command
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"fmt"
|
||||||
"testing"
|
"testing"
|
||||||
|
|
||||||
|
"github.com/hashicorp/packer/version"
|
||||||
"github.com/mitchellh/cli"
|
"github.com/mitchellh/cli"
|
||||||
|
"github.com/stretchr/testify/assert"
|
||||||
)
|
)
|
||||||
|
|
||||||
func TestVersionCommand_implements(t *testing.T) {
|
func TestVersionCommand_implements(t *testing.T) {
|
||||||
var _ cli.Command = &VersionCommand{}
|
var _ cli.Command = &VersionCommand{}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func Test_version(t *testing.T) {
|
||||||
|
tc := []struct {
|
||||||
|
command []string
|
||||||
|
env []string
|
||||||
|
expected string
|
||||||
|
}{
|
||||||
|
{[]string{"version"}, nil, fmt.Sprintf("Packer v%s", version.FormattedVersion()) + "\n"},
|
||||||
|
{[]string{"version", "&"}, nil, fmt.Sprintf("Packer v%s", version.FormattedVersion()) + "\n"},
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, tc := range tc {
|
||||||
|
t.Run(fmt.Sprintf("packer %s", tc.command), func(t *testing.T) {
|
||||||
|
p := helperCommand(t, tc.command...)
|
||||||
|
bs, err := p.Output()
|
||||||
|
fmt.Println(err)
|
||||||
|
if err != nil {
|
||||||
|
t.Fatalf("%v: %s", err, bs)
|
||||||
|
}
|
||||||
|
assert.Equal(t, tc.expected, string(bs))
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|||||||
@@ -1,64 +0,0 @@
|
|||||||
package ami
|
|
||||||
|
|
||||||
import (
|
|
||||||
"fmt"
|
|
||||||
"os/exec"
|
|
||||||
"testing"
|
|
||||||
|
|
||||||
"github.com/hashicorp/packer-plugin-sdk/acctest"
|
|
||||||
amazonacc "github.com/hashicorp/packer/builder/amazon/ebs/acceptance"
|
|
||||||
)
|
|
||||||
|
|
||||||
func TestAmazonAmi(t *testing.T) {
|
|
||||||
testCase := &acctest.DatasourceTestCase{
|
|
||||||
Name: "amazon_ami_datasource_basic_test",
|
|
||||||
Teardown: func() error {
|
|
||||||
helper := amazonacc.AWSHelper{
|
|
||||||
Region: "us-west-2",
|
|
||||||
AMIName: "packer-amazon-ami-test",
|
|
||||||
}
|
|
||||||
return helper.CleanUpAmi()
|
|
||||||
},
|
|
||||||
Template: testDatasourceBasic,
|
|
||||||
Type: "amazon-ami",
|
|
||||||
Check: func(buildCommand *exec.Cmd, logfile string) error {
|
|
||||||
if buildCommand.ProcessState != nil {
|
|
||||||
if buildCommand.ProcessState.ExitCode() != 0 {
|
|
||||||
return fmt.Errorf("Bad exit code. Logfile: %s", logfile)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return nil
|
|
||||||
},
|
|
||||||
}
|
|
||||||
acctest.TestDatasource(t, testCase)
|
|
||||||
}
|
|
||||||
|
|
||||||
const testDatasourceBasic = `
|
|
||||||
data "amazon-ami" "test" {
|
|
||||||
filters = {
|
|
||||||
virtualization-type = "hvm"
|
|
||||||
name = "Windows_Server-2016-English-Full-Base-*"
|
|
||||||
root-device-type = "ebs"
|
|
||||||
}
|
|
||||||
most_recent = true
|
|
||||||
owners = ["801119661308"]
|
|
||||||
}
|
|
||||||
|
|
||||||
source "amazon-ebs" "basic-example" {
|
|
||||||
user_data_file = "./test-fixtures/configure-source-ssh.ps1"
|
|
||||||
region = "us-west-2"
|
|
||||||
source_ami = data.amazon-ami.test.id
|
|
||||||
instance_type = "t2.small"
|
|
||||||
ssh_agent_auth = false
|
|
||||||
ami_name = "packer-amazon-ami-test"
|
|
||||||
communicator = "ssh"
|
|
||||||
ssh_timeout = "10m"
|
|
||||||
ssh_username = "Administrator"
|
|
||||||
}
|
|
||||||
|
|
||||||
build {
|
|
||||||
sources = [
|
|
||||||
"source.amazon-ebs.basic-example"
|
|
||||||
]
|
|
||||||
}
|
|
||||||
`
|
|
||||||
@@ -1,45 +0,0 @@
|
|||||||
package ami
|
|
||||||
|
|
||||||
import (
|
|
||||||
"testing"
|
|
||||||
|
|
||||||
awscommon "github.com/hashicorp/packer/builder/amazon/common"
|
|
||||||
)
|
|
||||||
|
|
||||||
func TestDatasourceConfigure_FilterBlank(t *testing.T) {
|
|
||||||
datasource := Datasource{
|
|
||||||
config: Config{
|
|
||||||
AmiFilterOptions: awscommon.AmiFilterOptions{},
|
|
||||||
},
|
|
||||||
}
|
|
||||||
if err := datasource.Configure(nil); err == nil {
|
|
||||||
t.Fatalf("Should error if filters map is empty or not specified")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestRunConfigPrepare_SourceAmiFilterOwnersBlank(t *testing.T) {
|
|
||||||
datasource := Datasource{
|
|
||||||
config: Config{
|
|
||||||
AmiFilterOptions: awscommon.AmiFilterOptions{
|
|
||||||
Filters: map[string]string{"foo": "bar"},
|
|
||||||
},
|
|
||||||
},
|
|
||||||
}
|
|
||||||
if err := datasource.Configure(nil); err == nil {
|
|
||||||
t.Fatalf("Should error if Owners is not specified)")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestRunConfigPrepare_SourceAmiFilterGood(t *testing.T) {
|
|
||||||
datasource := Datasource{
|
|
||||||
config: Config{
|
|
||||||
AmiFilterOptions: awscommon.AmiFilterOptions{
|
|
||||||
Owners: []string{"1234567"},
|
|
||||||
Filters: map[string]string{"foo": "bar"},
|
|
||||||
},
|
|
||||||
},
|
|
||||||
}
|
|
||||||
if err := datasource.Configure(nil); err != nil {
|
|
||||||
t.Fatalf("err: %s", err)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,161 +0,0 @@
|
|||||||
<powershell>
|
|
||||||
# Version and download URL
|
|
||||||
$openSSHVersion = "8.1.0.0p1-Beta"
|
|
||||||
$openSSHURL = "https://github.com/PowerShell/Win32-OpenSSH/releases/download/v$openSSHVersion/OpenSSH-Win64.zip"
|
|
||||||
|
|
||||||
Set-ExecutionPolicy Unrestricted
|
|
||||||
|
|
||||||
# GitHub became TLS 1.2 only on Feb 22, 2018
|
|
||||||
[System.Net.ServicePointManager]::SecurityProtocol = [System.Net.SecurityProtocolType]::Tls12;
|
|
||||||
|
|
||||||
# Function to unzip an archive to a given destination
|
|
||||||
Add-Type -AssemblyName System.IO.Compression.FileSystem
|
|
||||||
Function Unzip
|
|
||||||
{
|
|
||||||
[CmdletBinding()]
|
|
||||||
param(
|
|
||||||
[Parameter(Mandatory=$true, Position=0)]
|
|
||||||
[string] $ZipFile,
|
|
||||||
[Parameter(Mandatory=$true, Position=1)]
|
|
||||||
[string] $OutPath
|
|
||||||
)
|
|
||||||
|
|
||||||
[System.IO.Compression.ZipFile]::ExtractToDirectory($zipFile, $outPath)
|
|
||||||
}
|
|
||||||
|
|
||||||
# Set various known paths
|
|
||||||
$openSSHZip = Join-Path $env:TEMP 'OpenSSH.zip'
|
|
||||||
$openSSHInstallDir = Join-Path $env:ProgramFiles 'OpenSSH'
|
|
||||||
$openSSHInstallScript = Join-Path $openSSHInstallDir 'install-sshd.ps1'
|
|
||||||
$openSSHDownloadKeyScript = Join-Path $openSSHInstallDir 'download-key-pair.ps1'
|
|
||||||
$openSSHDaemon = Join-Path $openSSHInstallDir 'sshd.exe'
|
|
||||||
$openSSHDaemonConfig = [io.path]::combine($env:ProgramData, 'ssh', 'sshd_config')
|
|
||||||
|
|
||||||
# Download and unpack the binary distribution of OpenSSH
|
|
||||||
Invoke-WebRequest -Uri $openSSHURL `
|
|
||||||
-OutFile $openSSHZip `
|
|
||||||
-ErrorAction Stop
|
|
||||||
|
|
||||||
Unzip -ZipFile $openSSHZip `
|
|
||||||
-OutPath "$env:TEMP" `
|
|
||||||
-ErrorAction Stop
|
|
||||||
|
|
||||||
Remove-Item $openSSHZip `
|
|
||||||
-ErrorAction SilentlyContinue
|
|
||||||
|
|
||||||
# Move into Program Files
|
|
||||||
Move-Item -Path (Join-Path $env:TEMP 'OpenSSH-Win64') `
|
|
||||||
-Destination $openSSHInstallDir `
|
|
||||||
-ErrorAction Stop
|
|
||||||
|
|
||||||
# Run the install script, terminate if it fails
|
|
||||||
& Powershell.exe -ExecutionPolicy Bypass -File $openSSHInstallScript
|
|
||||||
if ($LASTEXITCODE -ne 0) {
|
|
||||||
throw("Failed to install OpenSSH Server")
|
|
||||||
}
|
|
||||||
|
|
||||||
# Add a firewall rule to allow inbound SSH connections to sshd.exe
|
|
||||||
New-NetFirewallRule -Name sshd `
|
|
||||||
-DisplayName "OpenSSH Server (sshd)" `
|
|
||||||
-Group "Remote Access" `
|
|
||||||
-Description "Allow access via TCP port 22 to the OpenSSH Daemon" `
|
|
||||||
-Enabled True `
|
|
||||||
-Direction Inbound `
|
|
||||||
-Protocol TCP `
|
|
||||||
-LocalPort 22 `
|
|
||||||
-Program "$openSSHDaemon" `
|
|
||||||
-Action Allow `
|
|
||||||
-ErrorAction Stop
|
|
||||||
|
|
||||||
# Ensure sshd automatically starts on boot
|
|
||||||
Set-Service sshd -StartupType Automatic `
|
|
||||||
-ErrorAction Stop
|
|
||||||
|
|
||||||
# Set the default login shell for SSH connections to Powershell
|
|
||||||
New-Item -Path HKLM:\SOFTWARE\OpenSSH -Force
|
|
||||||
New-ItemProperty -Path HKLM:\SOFTWARE\OpenSSH `
|
|
||||||
-Name DefaultShell `
|
|
||||||
-Value "C:\Windows\System32\WindowsPowerShell\v1.0\powershell.exe" `
|
|
||||||
-ErrorAction Stop
|
|
||||||
|
|
||||||
$keyDownloadScript = @'
|
|
||||||
# Download the instance key pair and authorize Administrator logins using it
|
|
||||||
$openSSHAdminUser = 'c:\ProgramData\ssh'
|
|
||||||
$openSSHAuthorizedKeys = Join-Path $openSSHAdminUser 'authorized_keys'
|
|
||||||
|
|
||||||
If (-Not (Test-Path $openSSHAdminUser)) {
|
|
||||||
New-Item -Path $openSSHAdminUser -Type Directory
|
|
||||||
}
|
|
||||||
|
|
||||||
$keyUrl = "http://169.254.169.254/latest/meta-data/public-keys/0/openssh-key"
|
|
||||||
$keyReq = [System.Net.WebRequest]::Create($keyUrl)
|
|
||||||
$keyResp = $keyReq.GetResponse()
|
|
||||||
$keyRespStream = $keyResp.GetResponseStream()
|
|
||||||
$streamReader = New-Object System.IO.StreamReader $keyRespStream
|
|
||||||
$keyMaterial = $streamReader.ReadToEnd()
|
|
||||||
|
|
||||||
$keyMaterial | Out-File -Append -FilePath $openSSHAuthorizedKeys -Encoding ASCII
|
|
||||||
|
|
||||||
# Ensure access control on authorized_keys meets the requirements
|
|
||||||
$acl = Get-ACL -Path $openSSHAuthorizedKeys
|
|
||||||
$acl.SetAccessRuleProtection($True, $True)
|
|
||||||
Set-Acl -Path $openSSHAuthorizedKeys -AclObject $acl
|
|
||||||
|
|
||||||
$acl = Get-ACL -Path $openSSHAuthorizedKeys
|
|
||||||
$ar = New-Object System.Security.AccessControl.FileSystemAccessRule( `
|
|
||||||
"NT Authority\Authenticated Users", "ReadAndExecute", "Allow")
|
|
||||||
$acl.RemoveAccessRule($ar)
|
|
||||||
$ar = New-Object System.Security.AccessControl.FileSystemAccessRule( `
|
|
||||||
"BUILTIN\Administrators", "FullControl", "Allow")
|
|
||||||
$acl.RemoveAccessRule($ar)
|
|
||||||
$ar = New-Object System.Security.AccessControl.FileSystemAccessRule( `
|
|
||||||
"BUILTIN\Users", "FullControl", "Allow")
|
|
||||||
$acl.RemoveAccessRule($ar)
|
|
||||||
Set-Acl -Path $openSSHAuthorizedKeys -AclObject $acl
|
|
||||||
|
|
||||||
Disable-ScheduledTask -TaskName "Download Key Pair"
|
|
||||||
|
|
||||||
$sshdConfigContent = @"
|
|
||||||
# Modified sshd_config, created by Packer provisioner
|
|
||||||
|
|
||||||
PasswordAuthentication yes
|
|
||||||
PubKeyAuthentication yes
|
|
||||||
PidFile __PROGRAMDATA__/ssh/logs/sshd.pid
|
|
||||||
AuthorizedKeysFile __PROGRAMDATA__/ssh/authorized_keys
|
|
||||||
AllowUsers Administrator
|
|
||||||
|
|
||||||
Subsystem sftp sftp-server.exe
|
|
||||||
"@
|
|
||||||
|
|
||||||
Set-Content -Path C:\ProgramData\ssh\sshd_config `
|
|
||||||
-Value $sshdConfigContent
|
|
||||||
|
|
||||||
'@
|
|
||||||
$keyDownloadScript | Out-File $openSSHDownloadKeyScript
|
|
||||||
|
|
||||||
# Create Task - Ensure the name matches the verbatim version above
|
|
||||||
$taskName = "Download Key Pair"
|
|
||||||
$principal = New-ScheduledTaskPrincipal `
|
|
||||||
-UserID "NT AUTHORITY\SYSTEM" `
|
|
||||||
-LogonType ServiceAccount `
|
|
||||||
-RunLevel Highest
|
|
||||||
$action = New-ScheduledTaskAction -Execute 'Powershell.exe' `
|
|
||||||
-Argument "-NoProfile -File ""$openSSHDownloadKeyScript"""
|
|
||||||
$trigger = New-ScheduledTaskTrigger -AtStartup
|
|
||||||
Register-ScheduledTask -Action $action `
|
|
||||||
-Trigger $trigger `
|
|
||||||
-Principal $principal `
|
|
||||||
-TaskName $taskName `
|
|
||||||
-Description $taskName
|
|
||||||
Disable-ScheduledTask -TaskName $taskName
|
|
||||||
|
|
||||||
# Run the install script, terminate if it fails
|
|
||||||
& Powershell.exe -ExecutionPolicy Bypass -File $openSSHDownloadKeyScript
|
|
||||||
if ($LASTEXITCODE -ne 0) {
|
|
||||||
throw("Failed to download key pair")
|
|
||||||
}
|
|
||||||
|
|
||||||
# Restart to ensure public key authentication works and SSH comes up
|
|
||||||
Restart-Computer
|
|
||||||
</powershell>
|
|
||||||
<runAsLocalSystem>true</runAsLocalSystem>
|
|
||||||
@@ -1,179 +0,0 @@
|
|||||||
package secretsmanager
|
|
||||||
|
|
||||||
import (
|
|
||||||
"context"
|
|
||||||
"fmt"
|
|
||||||
"io/ioutil"
|
|
||||||
"os"
|
|
||||||
"os/exec"
|
|
||||||
"regexp"
|
|
||||||
"testing"
|
|
||||||
"time"
|
|
||||||
|
|
||||||
"github.com/aws/aws-sdk-go/aws"
|
|
||||||
"github.com/aws/aws-sdk-go/service/secretsmanager"
|
|
||||||
"github.com/hashicorp/packer-plugin-sdk/acctest"
|
|
||||||
"github.com/hashicorp/packer-plugin-sdk/retry"
|
|
||||||
awscommon "github.com/hashicorp/packer/builder/amazon/common"
|
|
||||||
"github.com/hashicorp/packer/builder/amazon/common/awserrors"
|
|
||||||
)
|
|
||||||
|
|
||||||
func TestAmazonSecretsManager(t *testing.T) {
|
|
||||||
secret := &AmazonSecret{
|
|
||||||
Name: "packer_datasource_secretsmanager_test_secret",
|
|
||||||
Key: "packer_test_key",
|
|
||||||
Value: "this_is_the_packer_test_secret_value",
|
|
||||||
Description: "this is a secret used in a packer acc test",
|
|
||||||
}
|
|
||||||
|
|
||||||
testCase := &acctest.DatasourceTestCase{
|
|
||||||
Name: "amazon_secretsmanager_datasource_basic_test",
|
|
||||||
Setup: func() error {
|
|
||||||
return secret.Create()
|
|
||||||
},
|
|
||||||
Teardown: func() error {
|
|
||||||
return secret.Delete()
|
|
||||||
},
|
|
||||||
Template: testDatasourceBasic,
|
|
||||||
Type: "amazon-secrestmanager",
|
|
||||||
Check: func(buildCommand *exec.Cmd, logfile string) error {
|
|
||||||
if buildCommand.ProcessState != nil {
|
|
||||||
if buildCommand.ProcessState.ExitCode() != 0 {
|
|
||||||
return fmt.Errorf("Bad exit code. Logfile: %s", logfile)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
logs, err := os.Open(logfile)
|
|
||||||
if err != nil {
|
|
||||||
return fmt.Errorf("Unable find %s", logfile)
|
|
||||||
}
|
|
||||||
defer logs.Close()
|
|
||||||
|
|
||||||
logsBytes, err := ioutil.ReadAll(logs)
|
|
||||||
if err != nil {
|
|
||||||
return fmt.Errorf("Unable to read %s", logfile)
|
|
||||||
}
|
|
||||||
logsString := string(logsBytes)
|
|
||||||
|
|
||||||
valueLog := fmt.Sprintf("null.basic-example: secret value: %s", secret.Value)
|
|
||||||
secretStringLog := fmt.Sprintf("null.basic-example: secret secret_string: %s", fmt.Sprintf("{%s:%s}", secret.Key, secret.Value))
|
|
||||||
versionIdLog := fmt.Sprintf("null.basic-example: secret version_id: %s", aws.StringValue(secret.Info.VersionId))
|
|
||||||
secretValueLog := fmt.Sprintf("null.basic-example: secret value: %s", secret.Value)
|
|
||||||
|
|
||||||
if matched, _ := regexp.MatchString(valueLog+".*", logsString); !matched {
|
|
||||||
t.Fatalf("logs doesn't contain expected arn %q", logsString)
|
|
||||||
}
|
|
||||||
if matched, _ := regexp.MatchString(secretStringLog+".*", logsString); !matched {
|
|
||||||
t.Fatalf("logs doesn't contain expected secret_string %q", logsString)
|
|
||||||
}
|
|
||||||
if matched, _ := regexp.MatchString(versionIdLog+".*", logsString); !matched {
|
|
||||||
t.Fatalf("logs doesn't contain expected version_id %q", logsString)
|
|
||||||
}
|
|
||||||
if matched, _ := regexp.MatchString(secretValueLog+".*", logsString); !matched {
|
|
||||||
t.Fatalf("logs doesn't contain expected value %q", logsString)
|
|
||||||
}
|
|
||||||
return nil
|
|
||||||
},
|
|
||||||
}
|
|
||||||
acctest.TestDatasource(t, testCase)
|
|
||||||
}
|
|
||||||
|
|
||||||
const testDatasourceBasic = `
|
|
||||||
data "amazon-secretsmanager" "test" {
|
|
||||||
name = "packer_datasource_secretsmanager_test_secret"
|
|
||||||
key = "packer_test_key"
|
|
||||||
}
|
|
||||||
|
|
||||||
locals {
|
|
||||||
value = data.amazon-secretsmanager.test.value
|
|
||||||
secret_string = data.amazon-secretsmanager.test.secret_string
|
|
||||||
version_id = data.amazon-secretsmanager.test.version_id
|
|
||||||
secret_value = jsondecode(data.amazon-secretsmanager.test.secret_string)["packer_test_key"]
|
|
||||||
}
|
|
||||||
|
|
||||||
source "null" "basic-example" {
|
|
||||||
communicator = "none"
|
|
||||||
}
|
|
||||||
|
|
||||||
build {
|
|
||||||
sources = [
|
|
||||||
"source.null.basic-example"
|
|
||||||
]
|
|
||||||
|
|
||||||
provisioner "shell-local" {
|
|
||||||
inline = [
|
|
||||||
"echo secret value: ${local.value}",
|
|
||||||
"echo secret secret_string: ${local.secret_string}",
|
|
||||||
"echo secret version_id: ${local.version_id}",
|
|
||||||
"echo secret value: ${local.secret_value}"
|
|
||||||
]
|
|
||||||
}
|
|
||||||
}
|
|
||||||
`
|
|
||||||
|
|
||||||
type AmazonSecret struct {
|
|
||||||
Name string
|
|
||||||
Key string
|
|
||||||
Value string
|
|
||||||
Description string
|
|
||||||
|
|
||||||
Info *secretsmanager.CreateSecretOutput
|
|
||||||
manager *secretsmanager.SecretsManager
|
|
||||||
}
|
|
||||||
|
|
||||||
func (as *AmazonSecret) Create() error {
|
|
||||||
if as.manager == nil {
|
|
||||||
accessConfig := &awscommon.AccessConfig{}
|
|
||||||
session, err := accessConfig.Session()
|
|
||||||
if err != nil {
|
|
||||||
return fmt.Errorf("Unable to create aws session %s", err.Error())
|
|
||||||
}
|
|
||||||
as.manager = secretsmanager.New(session)
|
|
||||||
}
|
|
||||||
|
|
||||||
newSecret := &secretsmanager.CreateSecretInput{
|
|
||||||
Description: aws.String(as.Description),
|
|
||||||
Name: aws.String(as.Name),
|
|
||||||
SecretString: aws.String(fmt.Sprintf(`{%q:%q}`, as.Key, as.Value)),
|
|
||||||
}
|
|
||||||
|
|
||||||
secret := new(secretsmanager.CreateSecretOutput)
|
|
||||||
var err error
|
|
||||||
err = retry.Config{
|
|
||||||
Tries: 11,
|
|
||||||
ShouldRetry: func(err error) bool {
|
|
||||||
if awserrors.Matches(err, "ResourceExistsException", "") {
|
|
||||||
_ = as.Delete()
|
|
||||||
return true
|
|
||||||
}
|
|
||||||
if awserrors.Matches(err, "InvalidRequestException", "already scheduled for deletion") {
|
|
||||||
return true
|
|
||||||
}
|
|
||||||
return false
|
|
||||||
},
|
|
||||||
RetryDelay: (&retry.Backoff{InitialBackoff: 200 * time.Millisecond, MaxBackoff: 30 * time.Second, Multiplier: 2}).Linear,
|
|
||||||
}.Run(context.TODO(), func(_ context.Context) error {
|
|
||||||
secret, err = as.manager.CreateSecret(newSecret)
|
|
||||||
return err
|
|
||||||
})
|
|
||||||
as.Info = secret
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
func (as *AmazonSecret) Delete() error {
|
|
||||||
if as.manager == nil {
|
|
||||||
accessConfig := &awscommon.AccessConfig{}
|
|
||||||
session, err := accessConfig.Session()
|
|
||||||
if err != nil {
|
|
||||||
return fmt.Errorf("Unable to create aws session %s", err.Error())
|
|
||||||
}
|
|
||||||
as.manager = secretsmanager.New(session)
|
|
||||||
}
|
|
||||||
|
|
||||||
secret := &secretsmanager.DeleteSecretInput{
|
|
||||||
ForceDeleteWithoutRecovery: aws.Bool(true),
|
|
||||||
SecretId: aws.String(as.Name),
|
|
||||||
}
|
|
||||||
_, err := as.manager.DeleteSecret(secret)
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
@@ -1,39 +0,0 @@
|
|||||||
package secretsmanager
|
|
||||||
|
|
||||||
import (
|
|
||||||
"testing"
|
|
||||||
)
|
|
||||||
|
|
||||||
func TestDatasourceConfigure_EmptySecretId(t *testing.T) {
|
|
||||||
datasource := Datasource{
|
|
||||||
config: Config{},
|
|
||||||
}
|
|
||||||
if err := datasource.Configure(nil); err == nil {
|
|
||||||
t.Fatalf("Should error if secret id is not specified")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestDatasourceConfigure_Dafaults(t *testing.T) {
|
|
||||||
datasource := Datasource{
|
|
||||||
config: Config{
|
|
||||||
Name: "arn:1223",
|
|
||||||
},
|
|
||||||
}
|
|
||||||
if err := datasource.Configure(nil); err != nil {
|
|
||||||
t.Fatalf("err: %s", err)
|
|
||||||
}
|
|
||||||
if datasource.config.VersionStage != "AWSCURRENT" {
|
|
||||||
t.Fatalf("VersionStage not set correctly")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestDatasourceConfigure(t *testing.T) {
|
|
||||||
datasource := Datasource{
|
|
||||||
config: Config{
|
|
||||||
Name: "arn:1223",
|
|
||||||
},
|
|
||||||
}
|
|
||||||
if err := datasource.Configure(nil); err != nil {
|
|
||||||
t.Fatalf("err: %s", err)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -17,7 +17,7 @@ require (
|
|||||||
github.com/aliyun/aliyun-oss-go-sdk v0.0.0-20170113022742-e6dbea820a9f
|
github.com/aliyun/aliyun-oss-go-sdk v0.0.0-20170113022742-e6dbea820a9f
|
||||||
github.com/antihax/optional v1.0.0
|
github.com/antihax/optional v1.0.0
|
||||||
github.com/approvals/go-approval-tests v0.0.0-20160714161514-ad96e53bea43
|
github.com/approvals/go-approval-tests v0.0.0-20160714161514-ad96e53bea43
|
||||||
github.com/aws/aws-sdk-go v1.37.15
|
github.com/aws/aws-sdk-go v1.38.0
|
||||||
github.com/biogo/hts v0.0.0-20160420073057-50da7d4131a3
|
github.com/biogo/hts v0.0.0-20160420073057-50da7d4131a3
|
||||||
github.com/c2h5oh/datasize v0.0.0-20200112174442-28bbd4740fee
|
github.com/c2h5oh/datasize v0.0.0-20200112174442-28bbd4740fee
|
||||||
github.com/cheggaaa/pb v1.0.27
|
github.com/cheggaaa/pb v1.0.27
|
||||||
@@ -32,26 +32,26 @@ require (
|
|||||||
github.com/go-resty/resty/v2 v2.3.0
|
github.com/go-resty/resty/v2 v2.3.0
|
||||||
github.com/gobwas/glob v0.2.3
|
github.com/gobwas/glob v0.2.3
|
||||||
github.com/golang-collections/collections v0.0.0-20130729185459-604e922904d3
|
github.com/golang-collections/collections v0.0.0-20130729185459-604e922904d3
|
||||||
github.com/google/go-cmp v0.5.2
|
github.com/google/go-cmp v0.5.5
|
||||||
github.com/google/go-github/v33 v33.0.1-0.20210113204525-9318e629ec69
|
github.com/google/go-github/v33 v33.0.1-0.20210113204525-9318e629ec69
|
||||||
github.com/google/uuid v1.1.2
|
github.com/google/uuid v1.1.2
|
||||||
github.com/gophercloud/gophercloud v0.12.0
|
github.com/gophercloud/gophercloud v0.12.0
|
||||||
github.com/gophercloud/utils v0.0.0-20200508015959-b0167b94122c
|
github.com/gophercloud/utils v0.0.0-20200508015959-b0167b94122c
|
||||||
github.com/grpc-ecosystem/go-grpc-middleware v1.1.0
|
github.com/grpc-ecosystem/go-grpc-middleware v1.1.0
|
||||||
github.com/hako/durafmt v0.0.0-20200710122514-c0fb7b4da026
|
github.com/hako/durafmt v0.0.0-20200710122514-c0fb7b4da026
|
||||||
github.com/hashicorp/aws-sdk-go-base v0.6.0
|
|
||||||
github.com/hashicorp/errwrap v1.0.0
|
github.com/hashicorp/errwrap v1.0.0
|
||||||
github.com/hashicorp/go-checkpoint v0.0.0-20171009173528-1545e56e46de
|
github.com/hashicorp/go-checkpoint v0.0.0-20171009173528-1545e56e46de
|
||||||
github.com/hashicorp/go-cleanhttp v0.5.1
|
github.com/hashicorp/go-cleanhttp v0.5.2
|
||||||
github.com/hashicorp/go-cty-funcs v0.0.0-20200930094925-2721b1e36840
|
github.com/hashicorp/go-cty-funcs v0.0.0-20200930094925-2721b1e36840
|
||||||
github.com/hashicorp/go-getter/v2 v2.0.0-20200604122502-a6995fa1edad
|
github.com/hashicorp/go-getter/v2 v2.0.0-20200604122502-a6995fa1edad
|
||||||
github.com/hashicorp/go-multierror v1.1.0
|
github.com/hashicorp/go-multierror v1.1.1
|
||||||
github.com/hashicorp/go-oracle-terraform v0.0.0-20181016190316-007121241b79
|
github.com/hashicorp/go-oracle-terraform v0.0.0-20181016190316-007121241b79
|
||||||
github.com/hashicorp/go-uuid v1.0.2
|
github.com/hashicorp/go-uuid v1.0.2
|
||||||
github.com/hashicorp/go-version v1.2.0
|
github.com/hashicorp/go-version v1.2.0
|
||||||
github.com/hashicorp/hcl/v2 v2.8.0
|
github.com/hashicorp/hcl/v2 v2.9.1
|
||||||
github.com/hashicorp/packer-plugin-docker v0.0.2
|
github.com/hashicorp/packer-plugin-amazon v0.0.1
|
||||||
github.com/hashicorp/packer-plugin-sdk v0.1.1-0.20210323111710-5a7ab7f7a1c0
|
github.com/hashicorp/packer-plugin-docker v0.0.7
|
||||||
|
github.com/hashicorp/packer-plugin-sdk v0.1.1
|
||||||
github.com/hashicorp/vault/api v1.0.4
|
github.com/hashicorp/vault/api v1.0.4
|
||||||
github.com/hetznercloud/hcloud-go v1.15.1
|
github.com/hetznercloud/hcloud-go v1.15.1
|
||||||
github.com/hyperonecom/h1-client-go v0.0.0-20191203060043-b46280e4c4a4
|
github.com/hyperonecom/h1-client-go v0.0.0-20191203060043-b46280e4c4a4
|
||||||
@@ -64,13 +64,12 @@ require (
|
|||||||
github.com/mitchellh/cli v1.1.0
|
github.com/mitchellh/cli v1.1.0
|
||||||
github.com/mitchellh/go-homedir v1.1.0
|
github.com/mitchellh/go-homedir v1.1.0
|
||||||
github.com/mitchellh/go-vnc v0.0.0-20150629162542-723ed9867aed
|
github.com/mitchellh/go-vnc v0.0.0-20150629162542-723ed9867aed
|
||||||
github.com/mitchellh/gox v1.0.1 // indirect
|
|
||||||
github.com/mitchellh/mapstructure v1.4.0
|
github.com/mitchellh/mapstructure v1.4.0
|
||||||
github.com/mitchellh/panicwrap v1.0.0
|
github.com/mitchellh/panicwrap v1.0.0
|
||||||
github.com/mitchellh/prefixedio v0.0.0-20151214002211-6e6954073784
|
github.com/mitchellh/prefixedio v0.0.0-20151214002211-6e6954073784
|
||||||
github.com/mitchellh/reflectwalk v1.0.0
|
github.com/mitchellh/reflectwalk v1.0.0
|
||||||
github.com/olekukonko/tablewriter v0.0.0-20180105111133-96aac992fc8b
|
github.com/olekukonko/tablewriter v0.0.0-20180105111133-96aac992fc8b
|
||||||
github.com/oracle/oci-go-sdk v24.3.0+incompatible
|
github.com/oracle/oci-go-sdk/v36 v36.2.0
|
||||||
github.com/outscale/osc-sdk-go/osc v0.0.0-20200722135656-d654809d0699
|
github.com/outscale/osc-sdk-go/osc v0.0.0-20200722135656-d654809d0699
|
||||||
github.com/pierrec/lz4 v2.0.5+incompatible
|
github.com/pierrec/lz4 v2.0.5+incompatible
|
||||||
github.com/pkg/errors v0.9.1
|
github.com/pkg/errors v0.9.1
|
||||||
@@ -87,7 +86,7 @@ require (
|
|||||||
github.com/xanzy/go-cloudstack v0.0.0-20190526095453-42f262b63ed0
|
github.com/xanzy/go-cloudstack v0.0.0-20190526095453-42f262b63ed0
|
||||||
github.com/yandex-cloud/go-genproto v0.0.0-20200915125933-33de72a328bd
|
github.com/yandex-cloud/go-genproto v0.0.0-20200915125933-33de72a328bd
|
||||||
github.com/yandex-cloud/go-sdk v0.0.0-20200921111412-ef15ded2014c
|
github.com/yandex-cloud/go-sdk v0.0.0-20200921111412-ef15ded2014c
|
||||||
github.com/zclconf/go-cty v1.7.0
|
github.com/zclconf/go-cty v1.8.1
|
||||||
github.com/zclconf/go-cty-yaml v1.0.1
|
github.com/zclconf/go-cty-yaml v1.0.1
|
||||||
golang.org/x/crypto v0.0.0-20201221181555-eec23a3978ad
|
golang.org/x/crypto v0.0.0-20201221181555-eec23a3978ad
|
||||||
golang.org/x/mobile v0.0.0-20201208152944-da85bec010a2
|
golang.org/x/mobile v0.0.0-20201208152944-da85bec010a2
|
||||||
@@ -95,7 +94,7 @@ require (
|
|||||||
golang.org/x/net v0.0.0-20210119194325-5f4716e94777
|
golang.org/x/net v0.0.0-20210119194325-5f4716e94777
|
||||||
golang.org/x/oauth2 v0.0.0-20200902213428-5d25da1a8d43
|
golang.org/x/oauth2 v0.0.0-20200902213428-5d25da1a8d43
|
||||||
golang.org/x/sync v0.0.0-20201207232520-09787c993a3a
|
golang.org/x/sync v0.0.0-20201207232520-09787c993a3a
|
||||||
golang.org/x/sys v0.0.0-20210124154548-22da62e12c0c
|
golang.org/x/sys v0.0.0-20210319071255-635bc2c9138d
|
||||||
golang.org/x/tools v0.0.0-20201111133315-69daaf961d65
|
golang.org/x/tools v0.0.0-20201111133315-69daaf961d65
|
||||||
google.golang.org/api v0.32.0
|
google.golang.org/api v0.32.0
|
||||||
google.golang.org/grpc v1.32.0
|
google.golang.org/grpc v1.32.0
|
||||||
|
|||||||
@@ -106,8 +106,9 @@ github.com/apparentlymart/go-dump v0.0.0-20180507223929-23540a00eaa3 h1:ZSTrOEhi
|
|||||||
github.com/apparentlymart/go-dump v0.0.0-20180507223929-23540a00eaa3/go.mod h1:oL81AME2rN47vu18xqj1S1jPIPuN7afo62yKTNn3XMM=
|
github.com/apparentlymart/go-dump v0.0.0-20180507223929-23540a00eaa3/go.mod h1:oL81AME2rN47vu18xqj1S1jPIPuN7afo62yKTNn3XMM=
|
||||||
github.com/apparentlymart/go-textseg v1.0.0 h1:rRmlIsPEEhUTIKQb7T++Nz/A5Q6C9IuX2wFoYVvnCs0=
|
github.com/apparentlymart/go-textseg v1.0.0 h1:rRmlIsPEEhUTIKQb7T++Nz/A5Q6C9IuX2wFoYVvnCs0=
|
||||||
github.com/apparentlymart/go-textseg v1.0.0/go.mod h1:z96Txxhf3xSFMPmb5X/1W05FF/Nj9VFpLOpjS5yuumk=
|
github.com/apparentlymart/go-textseg v1.0.0/go.mod h1:z96Txxhf3xSFMPmb5X/1W05FF/Nj9VFpLOpjS5yuumk=
|
||||||
github.com/apparentlymart/go-textseg/v12 v12.0.0 h1:bNEQyAGak9tojivJNkoqWErVCQbjdL7GzRt3F8NvfJ0=
|
|
||||||
github.com/apparentlymart/go-textseg/v12 v12.0.0/go.mod h1:S/4uRK2UtaQttw1GenVJEynmyUenKwP++x/+DdGV/Ec=
|
github.com/apparentlymart/go-textseg/v12 v12.0.0/go.mod h1:S/4uRK2UtaQttw1GenVJEynmyUenKwP++x/+DdGV/Ec=
|
||||||
|
github.com/apparentlymart/go-textseg/v13 v13.0.0 h1:Y+KvPE1NYz0xl601PVImeQfFyEy6iT90AvPUL1NNfNw=
|
||||||
|
github.com/apparentlymart/go-textseg/v13 v13.0.0/go.mod h1:ZK2fH7c4NqDTLtiYLvIkEghdlcqw7yxLeM89kiTRPUo=
|
||||||
github.com/approvals/go-approval-tests v0.0.0-20160714161514-ad96e53bea43 h1:ePCAQPf5tUc5IMcUvu6euhSGna7jzs7eiXtJXHig6Zc=
|
github.com/approvals/go-approval-tests v0.0.0-20160714161514-ad96e53bea43 h1:ePCAQPf5tUc5IMcUvu6euhSGna7jzs7eiXtJXHig6Zc=
|
||||||
github.com/approvals/go-approval-tests v0.0.0-20160714161514-ad96e53bea43/go.mod h1:S6puKjZ9ZeqUPBv2hEBnMZGcM2J6mOsDRQcmxkMAND0=
|
github.com/approvals/go-approval-tests v0.0.0-20160714161514-ad96e53bea43/go.mod h1:S6puKjZ9ZeqUPBv2hEBnMZGcM2J6mOsDRQcmxkMAND0=
|
||||||
github.com/armon/circbuf v0.0.0-20150827004946-bbbad097214e/go.mod h1:3U/XgcO3hCbHZ8TKRvWD2dDTCfh9M9ya+I9JpbB7O8o=
|
github.com/armon/circbuf v0.0.0-20150827004946-bbbad097214e/go.mod h1:3U/XgcO3hCbHZ8TKRvWD2dDTCfh9M9ya+I9JpbB7O8o=
|
||||||
@@ -123,8 +124,8 @@ github.com/aws/aws-sdk-go v1.30.8/go.mod h1:5zCpMtNQVjRREroY7sYe8lOMRSxkhG6MZveU
|
|||||||
github.com/aws/aws-sdk-go v1.31.9/go.mod h1:5zCpMtNQVjRREroY7sYe8lOMRSxkhG6MZveU8YkpAk0=
|
github.com/aws/aws-sdk-go v1.31.9/go.mod h1:5zCpMtNQVjRREroY7sYe8lOMRSxkhG6MZveU8YkpAk0=
|
||||||
github.com/aws/aws-sdk-go v1.36.0/go.mod h1:hcU610XS61/+aQV88ixoOzUoG7v3b31pl2zKMmprdro=
|
github.com/aws/aws-sdk-go v1.36.0/go.mod h1:hcU610XS61/+aQV88ixoOzUoG7v3b31pl2zKMmprdro=
|
||||||
github.com/aws/aws-sdk-go v1.36.5/go.mod h1:hcU610XS61/+aQV88ixoOzUoG7v3b31pl2zKMmprdro=
|
github.com/aws/aws-sdk-go v1.36.5/go.mod h1:hcU610XS61/+aQV88ixoOzUoG7v3b31pl2zKMmprdro=
|
||||||
github.com/aws/aws-sdk-go v1.37.15 h1:W7l7gLLMcYRlg6a+uvf3Zz4jYwdqYzhe5ymqwWoOhp4=
|
github.com/aws/aws-sdk-go v1.38.0 h1:mqnmtdW8rGIQmp2d0WRFLua0zW0Pel0P6/vd3gJuViY=
|
||||||
github.com/aws/aws-sdk-go v1.37.15/go.mod h1:hcU610XS61/+aQV88ixoOzUoG7v3b31pl2zKMmprdro=
|
github.com/aws/aws-sdk-go v1.38.0/go.mod h1:hcU610XS61/+aQV88ixoOzUoG7v3b31pl2zKMmprdro=
|
||||||
github.com/beorn7/perks v0.0.0-20180321164747-3a771d992973/go.mod h1:Dwedo/Wpr24TaqPxmxbtue+5NUziq4I4S80YR8gNf3Q=
|
github.com/beorn7/perks v0.0.0-20180321164747-3a771d992973/go.mod h1:Dwedo/Wpr24TaqPxmxbtue+5NUziq4I4S80YR8gNf3Q=
|
||||||
github.com/bgentry/go-netrc v0.0.0-20140422174119-9fd32a8b3d3d h1:xDfNPAt8lFiC1UJrqV3uuy861HCTo708pDMbjHHdCas=
|
github.com/bgentry/go-netrc v0.0.0-20140422174119-9fd32a8b3d3d h1:xDfNPAt8lFiC1UJrqV3uuy861HCTo708pDMbjHHdCas=
|
||||||
github.com/bgentry/go-netrc v0.0.0-20140422174119-9fd32a8b3d3d/go.mod h1:6QX/PXZ00z/TKoufEY6K/a0k6AhaJrQKdFe6OfVXsa4=
|
github.com/bgentry/go-netrc v0.0.0-20140422174119-9fd32a8b3d3d/go.mod h1:6QX/PXZ00z/TKoufEY6K/a0k6AhaJrQKdFe6OfVXsa4=
|
||||||
@@ -268,8 +269,9 @@ github.com/google/go-cmp v0.4.0/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/
|
|||||||
github.com/google/go-cmp v0.4.1/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
|
github.com/google/go-cmp v0.4.1/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
|
||||||
github.com/google/go-cmp v0.5.0/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
|
github.com/google/go-cmp v0.5.0/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
|
||||||
github.com/google/go-cmp v0.5.1/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
|
github.com/google/go-cmp v0.5.1/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
|
||||||
github.com/google/go-cmp v0.5.2 h1:X2ev0eStA3AbceY54o37/0PQ/UWqKEiiO2dKL5OPaFM=
|
|
||||||
github.com/google/go-cmp v0.5.2/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
|
github.com/google/go-cmp v0.5.2/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
|
||||||
|
github.com/google/go-cmp v0.5.5 h1:Khx7svrCpmxxtHBq5j2mp/xVjsi8hQMfNLvJFAlrGgU=
|
||||||
|
github.com/google/go-cmp v0.5.5/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
|
||||||
github.com/google/go-github/v33 v33.0.0/go.mod h1:GMdDnVZY/2TsWgp/lkYnpSAh6TrzhANBBwm6k6TTEXg=
|
github.com/google/go-github/v33 v33.0.0/go.mod h1:GMdDnVZY/2TsWgp/lkYnpSAh6TrzhANBBwm6k6TTEXg=
|
||||||
github.com/google/go-github/v33 v33.0.1-0.20210113204525-9318e629ec69 h1:zL0/Ug5CMhV0XRb3A6vnK1SQ9kJM3VIyRxPQ5t9w8Bg=
|
github.com/google/go-github/v33 v33.0.1-0.20210113204525-9318e629ec69 h1:zL0/Ug5CMhV0XRb3A6vnK1SQ9kJM3VIyRxPQ5t9w8Bg=
|
||||||
github.com/google/go-github/v33 v33.0.1-0.20210113204525-9318e629ec69/go.mod h1:GMdDnVZY/2TsWgp/lkYnpSAh6TrzhANBBwm6k6TTEXg=
|
github.com/google/go-github/v33 v33.0.1-0.20210113204525-9318e629ec69/go.mod h1:GMdDnVZY/2TsWgp/lkYnpSAh6TrzhANBBwm6k6TTEXg=
|
||||||
@@ -310,8 +312,9 @@ github.com/grpc-ecosystem/go-grpc-middleware v1.1.0 h1:THDBEeQ9xZ8JEaCLyLQqXMMdR
|
|||||||
github.com/grpc-ecosystem/go-grpc-middleware v1.1.0/go.mod h1:f5nM7jw/oeRSadq3xCzHAvxcr8HZnzsqU6ILg/0NiiE=
|
github.com/grpc-ecosystem/go-grpc-middleware v1.1.0/go.mod h1:f5nM7jw/oeRSadq3xCzHAvxcr8HZnzsqU6ILg/0NiiE=
|
||||||
github.com/hako/durafmt v0.0.0-20200710122514-c0fb7b4da026 h1:BpJ2o0OR5FV7vrkDYfXYVJQeMNWa8RhklZOpW2ITAIQ=
|
github.com/hako/durafmt v0.0.0-20200710122514-c0fb7b4da026 h1:BpJ2o0OR5FV7vrkDYfXYVJQeMNWa8RhklZOpW2ITAIQ=
|
||||||
github.com/hako/durafmt v0.0.0-20200710122514-c0fb7b4da026/go.mod h1:5Scbynm8dF1XAPwIwkGPqzkM/shndPm79Jd1003hTjE=
|
github.com/hako/durafmt v0.0.0-20200710122514-c0fb7b4da026/go.mod h1:5Scbynm8dF1XAPwIwkGPqzkM/shndPm79Jd1003hTjE=
|
||||||
github.com/hashicorp/aws-sdk-go-base v0.6.0 h1:qmUbzM36msbBF59YctwuO5w0M2oNXjlilgKpnEhx1uw=
|
|
||||||
github.com/hashicorp/aws-sdk-go-base v0.6.0/go.mod h1:2fRjWDv3jJBeN6mVWFHV6hFTNeFBx2gpDLQaZNxUVAY=
|
github.com/hashicorp/aws-sdk-go-base v0.6.0/go.mod h1:2fRjWDv3jJBeN6mVWFHV6hFTNeFBx2gpDLQaZNxUVAY=
|
||||||
|
github.com/hashicorp/aws-sdk-go-base v0.7.0 h1:Umcq11kcoARameDgxPiYBbyltTZqO7GgBVSdq4pzX/w=
|
||||||
|
github.com/hashicorp/aws-sdk-go-base v0.7.0/go.mod h1:2fRjWDv3jJBeN6mVWFHV6hFTNeFBx2gpDLQaZNxUVAY=
|
||||||
github.com/hashicorp/consul/api v1.4.0 h1:jfESivXnO5uLdH650JU/6AnjRoHrLhULq0FnC3Kp9EY=
|
github.com/hashicorp/consul/api v1.4.0 h1:jfESivXnO5uLdH650JU/6AnjRoHrLhULq0FnC3Kp9EY=
|
||||||
github.com/hashicorp/consul/api v1.4.0/go.mod h1:xc8u05kyMa3Wjr9eEAsIAo3dg8+LywT5E/Cl7cNS5nU=
|
github.com/hashicorp/consul/api v1.4.0/go.mod h1:xc8u05kyMa3Wjr9eEAsIAo3dg8+LywT5E/Cl7cNS5nU=
|
||||||
github.com/hashicorp/consul/sdk v0.4.0 h1:zBtCfKJZcJDBvSCkQJch4ulp59m1rATFLKwNo/LYY30=
|
github.com/hashicorp/consul/sdk v0.4.0 h1:zBtCfKJZcJDBvSCkQJch4ulp59m1rATFLKwNo/LYY30=
|
||||||
@@ -321,8 +324,9 @@ github.com/hashicorp/errwrap v1.0.0/go.mod h1:YH+1FKiLXxHSkmPseP+kNlulaMuP3n2brv
|
|||||||
github.com/hashicorp/go-checkpoint v0.0.0-20171009173528-1545e56e46de h1:XDCSythtg8aWSRSO29uwhgh7b127fWr+m5SemqjSUL8=
|
github.com/hashicorp/go-checkpoint v0.0.0-20171009173528-1545e56e46de h1:XDCSythtg8aWSRSO29uwhgh7b127fWr+m5SemqjSUL8=
|
||||||
github.com/hashicorp/go-checkpoint v0.0.0-20171009173528-1545e56e46de/go.mod h1:xIwEieBHERyEvaeKF/TcHh1Hu+lxPM+n2vT1+g9I4m4=
|
github.com/hashicorp/go-checkpoint v0.0.0-20171009173528-1545e56e46de/go.mod h1:xIwEieBHERyEvaeKF/TcHh1Hu+lxPM+n2vT1+g9I4m4=
|
||||||
github.com/hashicorp/go-cleanhttp v0.5.0/go.mod h1:JpRdi6/HCYpAwUzNwuwqhbovhLtngrth3wmdIIUrZ80=
|
github.com/hashicorp/go-cleanhttp v0.5.0/go.mod h1:JpRdi6/HCYpAwUzNwuwqhbovhLtngrth3wmdIIUrZ80=
|
||||||
github.com/hashicorp/go-cleanhttp v0.5.1 h1:dH3aiDG9Jvb5r5+bYHsikaOUIpcM0xvgMXVoDkXMzJM=
|
|
||||||
github.com/hashicorp/go-cleanhttp v0.5.1/go.mod h1:JpRdi6/HCYpAwUzNwuwqhbovhLtngrth3wmdIIUrZ80=
|
github.com/hashicorp/go-cleanhttp v0.5.1/go.mod h1:JpRdi6/HCYpAwUzNwuwqhbovhLtngrth3wmdIIUrZ80=
|
||||||
|
github.com/hashicorp/go-cleanhttp v0.5.2 h1:035FKYIWjmULyFRBKPs8TBQoi0x6d9G4xc9neXJWAZQ=
|
||||||
|
github.com/hashicorp/go-cleanhttp v0.5.2/go.mod h1:kO/YDlP8L1346E6Sodw+PrpBSV4/SoxCXGY6BqNFT48=
|
||||||
github.com/hashicorp/go-cty-funcs v0.0.0-20200930094925-2721b1e36840 h1:kgvybwEeu0SXktbB2y3uLHX9lklLo+nzUwh59A3jzQc=
|
github.com/hashicorp/go-cty-funcs v0.0.0-20200930094925-2721b1e36840 h1:kgvybwEeu0SXktbB2y3uLHX9lklLo+nzUwh59A3jzQc=
|
||||||
github.com/hashicorp/go-cty-funcs v0.0.0-20200930094925-2721b1e36840/go.mod h1:Abjk0jbRkDaNCzsRhOv2iDCofYpX1eVsjozoiK63qLA=
|
github.com/hashicorp/go-cty-funcs v0.0.0-20200930094925-2721b1e36840/go.mod h1:Abjk0jbRkDaNCzsRhOv2iDCofYpX1eVsjozoiK63qLA=
|
||||||
github.com/hashicorp/go-getter v1.4.1 h1:3A2Mh8smGFcf5M+gmcv898mZdrxpseik45IpcyISLsA=
|
github.com/hashicorp/go-getter v1.4.1 h1:3A2Mh8smGFcf5M+gmcv898mZdrxpseik45IpcyISLsA=
|
||||||
@@ -345,8 +349,9 @@ github.com/hashicorp/go-msgpack v0.5.3/go.mod h1:ahLV/dePpqEmjfWmKiqvPkv/twdG7iP
|
|||||||
github.com/hashicorp/go-msgpack v0.5.5 h1:i9R9JSrqIz0QVLz3sz+i3YJdT7TTSLcfLLzJi9aZTuI=
|
github.com/hashicorp/go-msgpack v0.5.5 h1:i9R9JSrqIz0QVLz3sz+i3YJdT7TTSLcfLLzJi9aZTuI=
|
||||||
github.com/hashicorp/go-msgpack v0.5.5/go.mod h1:ahLV/dePpqEmjfWmKiqvPkv/twdG7iPBM1vqhUKIvfM=
|
github.com/hashicorp/go-msgpack v0.5.5/go.mod h1:ahLV/dePpqEmjfWmKiqvPkv/twdG7iPBM1vqhUKIvfM=
|
||||||
github.com/hashicorp/go-multierror v1.0.0/go.mod h1:dHtQlpGsu+cZNNAkkCN/P3hoUDHhCYQXV3UM06sGGrk=
|
github.com/hashicorp/go-multierror v1.0.0/go.mod h1:dHtQlpGsu+cZNNAkkCN/P3hoUDHhCYQXV3UM06sGGrk=
|
||||||
github.com/hashicorp/go-multierror v1.1.0 h1:B9UzwGQJehnUY1yNrnwREHc3fGbC2xefo8g4TbElacI=
|
|
||||||
github.com/hashicorp/go-multierror v1.1.0/go.mod h1:spPvp8C1qA32ftKqdAHm4hHTbPw+vmowP0z+KUhOZdA=
|
github.com/hashicorp/go-multierror v1.1.0/go.mod h1:spPvp8C1qA32ftKqdAHm4hHTbPw+vmowP0z+KUhOZdA=
|
||||||
|
github.com/hashicorp/go-multierror v1.1.1 h1:H5DkEtf6CXdFp0N0Em5UCwQpXMWke8IA0+lD48awMYo=
|
||||||
|
github.com/hashicorp/go-multierror v1.1.1/go.mod h1:iw975J/qwKPdAO1clOe2L8331t/9/fmwbPZ6JB6eMoM=
|
||||||
github.com/hashicorp/go-oracle-terraform v0.0.0-20181016190316-007121241b79 h1:RKu7yAXZTaQsxj1K9GDsh+QVw0+Wu1SWHxtbFN0n+hE=
|
github.com/hashicorp/go-oracle-terraform v0.0.0-20181016190316-007121241b79 h1:RKu7yAXZTaQsxj1K9GDsh+QVw0+Wu1SWHxtbFN0n+hE=
|
||||||
github.com/hashicorp/go-oracle-terraform v0.0.0-20181016190316-007121241b79/go.mod h1:09jT3Y/OIsjTjQ2+3bkVNPDKqWcGIYYvjB2BEKVUdvc=
|
github.com/hashicorp/go-oracle-terraform v0.0.0-20181016190316-007121241b79/go.mod h1:09jT3Y/OIsjTjQ2+3bkVNPDKqWcGIYYvjB2BEKVUdvc=
|
||||||
github.com/hashicorp/go-plugin v1.0.1/go.mod h1:++UyYGoz3o5w9ZzAdZxtQKrWWP+iqPBn3cQptSMzBuY=
|
github.com/hashicorp/go-plugin v1.0.1/go.mod h1:++UyYGoz3o5w9ZzAdZxtQKrWWP+iqPBn3cQptSMzBuY=
|
||||||
@@ -378,8 +383,9 @@ github.com/hashicorp/golang-lru v0.5.3/go.mod h1:iADmTwqILo4mZ8BN3D2Q6+9jd8WM5uG
|
|||||||
github.com/hashicorp/hcl v1.0.0 h1:0Anlzjpi4vEasTeNFn2mLJgTSwt0+6sfsiTG8qcWGx4=
|
github.com/hashicorp/hcl v1.0.0 h1:0Anlzjpi4vEasTeNFn2mLJgTSwt0+6sfsiTG8qcWGx4=
|
||||||
github.com/hashicorp/hcl v1.0.0/go.mod h1:E5yfLk+7swimpb2L/Alb/PJmXilQ/rhwaUYs4T20WEQ=
|
github.com/hashicorp/hcl v1.0.0/go.mod h1:E5yfLk+7swimpb2L/Alb/PJmXilQ/rhwaUYs4T20WEQ=
|
||||||
github.com/hashicorp/hcl/v2 v2.6.0/go.mod h1:bQTN5mpo+jewjJgh8jr0JUguIi7qPHUF6yIfAEN3jqY=
|
github.com/hashicorp/hcl/v2 v2.6.0/go.mod h1:bQTN5mpo+jewjJgh8jr0JUguIi7qPHUF6yIfAEN3jqY=
|
||||||
github.com/hashicorp/hcl/v2 v2.8.0 h1:iHLEAsNDp3N2MtqroP1wf0nF/zB2+McHN5YCzwqIm80=
|
|
||||||
github.com/hashicorp/hcl/v2 v2.8.0/go.mod h1:bQTN5mpo+jewjJgh8jr0JUguIi7qPHUF6yIfAEN3jqY=
|
github.com/hashicorp/hcl/v2 v2.8.0/go.mod h1:bQTN5mpo+jewjJgh8jr0JUguIi7qPHUF6yIfAEN3jqY=
|
||||||
|
github.com/hashicorp/hcl/v2 v2.9.1 h1:eOy4gREY0/ZQHNItlfuEZqtcQbXIxzojlP301hDpnac=
|
||||||
|
github.com/hashicorp/hcl/v2 v2.9.1/go.mod h1:FwWsfWEjyV/CMj8s/gqAuiviY72rJ1/oayI9WftqcKg=
|
||||||
github.com/hashicorp/logutils v1.0.0/go.mod h1:QIAnNjmIWmVIIkWDTG1z5v++HQmx9WQRO+LraFDTW64=
|
github.com/hashicorp/logutils v1.0.0/go.mod h1:QIAnNjmIWmVIIkWDTG1z5v++HQmx9WQRO+LraFDTW64=
|
||||||
github.com/hashicorp/mdns v1.0.0/go.mod h1:tL+uN++7HEJ6SQLQ2/p+z2pH24WQKWjBPkE0mNTz8vQ=
|
github.com/hashicorp/mdns v1.0.0/go.mod h1:tL+uN++7HEJ6SQLQ2/p+z2pH24WQKWjBPkE0mNTz8vQ=
|
||||||
github.com/hashicorp/mdns v1.0.1/go.mod h1:4gW7WsVCke5TE7EPeYliwHlRUyBtfCwuFwuMg2DmyNY=
|
github.com/hashicorp/mdns v1.0.1/go.mod h1:4gW7WsVCke5TE7EPeYliwHlRUyBtfCwuFwuMg2DmyNY=
|
||||||
@@ -395,8 +401,10 @@ github.com/hashicorp/packer v1.6.7-0.20210126105722-aef4ced967ec/go.mod h1:2+Vo/
|
|||||||
github.com/hashicorp/packer v1.6.7-0.20210208125835-f616955ebcb6/go.mod h1:7f5ZpTTRG53rQ58BcTADuTnpiBcB3wapuxl4sF2sGMM=
|
github.com/hashicorp/packer v1.6.7-0.20210208125835-f616955ebcb6/go.mod h1:7f5ZpTTRG53rQ58BcTADuTnpiBcB3wapuxl4sF2sGMM=
|
||||||
github.com/hashicorp/packer v1.6.7-0.20210217093213-201869d627bf/go.mod h1:+EWPPcqee4h8S/y913Dnta1eJkgiqsGXBQgB75A2qV0=
|
github.com/hashicorp/packer v1.6.7-0.20210217093213-201869d627bf/go.mod h1:+EWPPcqee4h8S/y913Dnta1eJkgiqsGXBQgB75A2qV0=
|
||||||
github.com/hashicorp/packer v1.7.0/go.mod h1:3KRJcwOctl2JaAGpQMI1bWQRArfWNWqcYjO6AOsVVGQ=
|
github.com/hashicorp/packer v1.7.0/go.mod h1:3KRJcwOctl2JaAGpQMI1bWQRArfWNWqcYjO6AOsVVGQ=
|
||||||
github.com/hashicorp/packer-plugin-docker v0.0.2 h1:j/hQTogaN2pZfZohlZTRu5YvNZg2/qtYYHkxPBxv2Oo=
|
github.com/hashicorp/packer-plugin-amazon v0.0.1 h1:EuyjNK9bL7WhQeIJzhBJxOx8nyc61ai5UbOsb1PIVwI=
|
||||||
github.com/hashicorp/packer-plugin-docker v0.0.2/go.mod h1:A2p9qztS4n88KsNF+qBM7BWw2HndW636GpFIjNSvbKM=
|
github.com/hashicorp/packer-plugin-amazon v0.0.1/go.mod h1:12c9msibyHdId+Mk/pCbdRb1KaLIhaNyxeJ6n8bZt30=
|
||||||
|
github.com/hashicorp/packer-plugin-docker v0.0.7 h1:hMTrH7vrkFIjphtbbtpuzffTzSjMNgxayo2DPLz9y+c=
|
||||||
|
github.com/hashicorp/packer-plugin-docker v0.0.7/go.mod h1:IpeKlwOSy2kdgQcysqd3gCsoqjME9jtmpFoKxn7RRNI=
|
||||||
github.com/hashicorp/packer-plugin-sdk v0.0.6/go.mod h1:Nvh28f+Jmpp2rcaN79bULTouNkGNDRfHckhHKTAXtyU=
|
github.com/hashicorp/packer-plugin-sdk v0.0.6/go.mod h1:Nvh28f+Jmpp2rcaN79bULTouNkGNDRfHckhHKTAXtyU=
|
||||||
github.com/hashicorp/packer-plugin-sdk v0.0.7-0.20210111224258-fd30ebb797f0/go.mod h1:YdWTt5w6cYfaQG7IOi5iorL+3SXnz8hI0gJCi8Db/LI=
|
github.com/hashicorp/packer-plugin-sdk v0.0.7-0.20210111224258-fd30ebb797f0/go.mod h1:YdWTt5w6cYfaQG7IOi5iorL+3SXnz8hI0gJCi8Db/LI=
|
||||||
github.com/hashicorp/packer-plugin-sdk v0.0.7-0.20210120105339-f6fd68d2570a/go.mod h1:exN0C+Pe+3zu18l4nxueNjX5cfmslxUX/m/xk4IVmZQ=
|
github.com/hashicorp/packer-plugin-sdk v0.0.7-0.20210120105339-f6fd68d2570a/go.mod h1:exN0C+Pe+3zu18l4nxueNjX5cfmslxUX/m/xk4IVmZQ=
|
||||||
@@ -405,8 +413,9 @@ github.com/hashicorp/packer-plugin-sdk v0.0.10-0.20210126105622-8e1648006d93/go.
|
|||||||
github.com/hashicorp/packer-plugin-sdk v0.0.11/go.mod h1:GNb0WNs7zibb8vzUZce1As64z2AW0FEMwhe2J7/NW5I=
|
github.com/hashicorp/packer-plugin-sdk v0.0.11/go.mod h1:GNb0WNs7zibb8vzUZce1As64z2AW0FEMwhe2J7/NW5I=
|
||||||
github.com/hashicorp/packer-plugin-sdk v0.0.12/go.mod h1:hs82OYeufirGG6KRENMpjBWomnIlte99X6wXAPThJ5I=
|
github.com/hashicorp/packer-plugin-sdk v0.0.12/go.mod h1:hs82OYeufirGG6KRENMpjBWomnIlte99X6wXAPThJ5I=
|
||||||
github.com/hashicorp/packer-plugin-sdk v0.0.14/go.mod h1:tNb3XzJPnjMl3QuUdKmF47B5ImerdTakalHzUAvW0aw=
|
github.com/hashicorp/packer-plugin-sdk v0.0.14/go.mod h1:tNb3XzJPnjMl3QuUdKmF47B5ImerdTakalHzUAvW0aw=
|
||||||
github.com/hashicorp/packer-plugin-sdk v0.1.1-0.20210323111710-5a7ab7f7a1c0 h1:nw4RqF7C4jUWGo5PGDG4dSclU+G/vXyVBHIu5j7akd4=
|
github.com/hashicorp/packer-plugin-sdk v0.1.0/go.mod h1:CFsC20uZjtER/EnTn/CSMKD0kEdkqOVev8mtOmfnZiI=
|
||||||
github.com/hashicorp/packer-plugin-sdk v0.1.1-0.20210323111710-5a7ab7f7a1c0/go.mod h1:1d3nqB9LUsXMQaNUiL67Q+WYEtjsVcLNTX8ikVlpBrc=
|
github.com/hashicorp/packer-plugin-sdk v0.1.1 h1:foqSy6m+2MsEf9ygNoBlIoi3SPvlkInS+yT0Uyj3yvw=
|
||||||
|
github.com/hashicorp/packer-plugin-sdk v0.1.1/go.mod h1:1d3nqB9LUsXMQaNUiL67Q+WYEtjsVcLNTX8ikVlpBrc=
|
||||||
github.com/hashicorp/serf v0.8.2/go.mod h1:6hOLApaqBFA1NXqRQAsxw9QxuDEvNxSQRwA/JwenrHc=
|
github.com/hashicorp/serf v0.8.2/go.mod h1:6hOLApaqBFA1NXqRQAsxw9QxuDEvNxSQRwA/JwenrHc=
|
||||||
github.com/hashicorp/serf v0.9.2 h1:yJoyfZXo4Pk2p/M/viW+YLibBFiIbKoP79gu7kDAFP0=
|
github.com/hashicorp/serf v0.9.2 h1:yJoyfZXo4Pk2p/M/viW+YLibBFiIbKoP79gu7kDAFP0=
|
||||||
github.com/hashicorp/serf v0.9.2/go.mod h1:UWDWwZeL5cuWDJdl0C6wrvrUwEqtQ4ZKBKKENpqIUyk=
|
github.com/hashicorp/serf v0.9.2/go.mod h1:UWDWwZeL5cuWDJdl0C6wrvrUwEqtQ4ZKBKKENpqIUyk=
|
||||||
@@ -529,7 +538,6 @@ github.com/mitchellh/go-wordwrap v0.0.0-20150314170334-ad45545899c7/go.mod h1:ZX
|
|||||||
github.com/mitchellh/go-wordwrap v1.0.0 h1:6GlHJ/LTGMrIJbwgdqdl2eEH8o+Exx/0m8ir9Gns0u4=
|
github.com/mitchellh/go-wordwrap v1.0.0 h1:6GlHJ/LTGMrIJbwgdqdl2eEH8o+Exx/0m8ir9Gns0u4=
|
||||||
github.com/mitchellh/go-wordwrap v1.0.0/go.mod h1:ZXFpozHsX6DPmq2I0TCekCxypsnAUbP2oI0UX1GXzOo=
|
github.com/mitchellh/go-wordwrap v1.0.0/go.mod h1:ZXFpozHsX6DPmq2I0TCekCxypsnAUbP2oI0UX1GXzOo=
|
||||||
github.com/mitchellh/gox v0.4.0/go.mod h1:Sd9lOJ0+aimLBi73mGofS1ycjY8lL3uZM3JPS42BGNg=
|
github.com/mitchellh/gox v0.4.0/go.mod h1:Sd9lOJ0+aimLBi73mGofS1ycjY8lL3uZM3JPS42BGNg=
|
||||||
github.com/mitchellh/gox v1.0.1 h1:x0jD3dcHk9a9xPSDN6YEL4xL6Qz0dvNYm8yZqui5chI=
|
|
||||||
github.com/mitchellh/gox v1.0.1/go.mod h1:ED6BioOGXMswlXa2zxfh/xdd5QhwYliBFn9V18Ap4z4=
|
github.com/mitchellh/gox v1.0.1/go.mod h1:ED6BioOGXMswlXa2zxfh/xdd5QhwYliBFn9V18Ap4z4=
|
||||||
github.com/mitchellh/iochan v1.0.0 h1:C+X3KsSTLFVBr/tK1eYN/vs4rJcvsiLU338UhYPJWeY=
|
github.com/mitchellh/iochan v1.0.0 h1:C+X3KsSTLFVBr/tK1eYN/vs4rJcvsiLU338UhYPJWeY=
|
||||||
github.com/mitchellh/iochan v1.0.0/go.mod h1:JwYml1nuB7xOzsp52dPpHFffvOCDupsG0QubkSMEySY=
|
github.com/mitchellh/iochan v1.0.0/go.mod h1:JwYml1nuB7xOzsp52dPpHFffvOCDupsG0QubkSMEySY=
|
||||||
@@ -556,9 +564,10 @@ github.com/oklog/run v1.0.0/go.mod h1:dlhp/R75TPv97u0XWUtDeV/lRKWPKSdTuV0TZvrmrQ
|
|||||||
github.com/olekukonko/tablewriter v0.0.0-20180105111133-96aac992fc8b h1:LGItPaClbzopugAomw5VFKnG3h1dUr9QW5KOU+m8gu0=
|
github.com/olekukonko/tablewriter v0.0.0-20180105111133-96aac992fc8b h1:LGItPaClbzopugAomw5VFKnG3h1dUr9QW5KOU+m8gu0=
|
||||||
github.com/olekukonko/tablewriter v0.0.0-20180105111133-96aac992fc8b/go.mod h1:vsDQFd/mU46D+Z4whnwzcISnGGzXWMclvtLoiIKAKIo=
|
github.com/olekukonko/tablewriter v0.0.0-20180105111133-96aac992fc8b/go.mod h1:vsDQFd/mU46D+Z4whnwzcISnGGzXWMclvtLoiIKAKIo=
|
||||||
github.com/opentracing/opentracing-go v1.1.0/go.mod h1:UkNAQd3GIcIGf0SeVgPpRdFStlNbqXla1AfSYxPUl2o=
|
github.com/opentracing/opentracing-go v1.1.0/go.mod h1:UkNAQd3GIcIGf0SeVgPpRdFStlNbqXla1AfSYxPUl2o=
|
||||||
|
github.com/oracle/oci-go-sdk v18.0.0+incompatible h1:FLV4KixsVfF3rwyVTMI6Ryp/Q+OSb9sR5TawbfjFLN4=
|
||||||
github.com/oracle/oci-go-sdk v18.0.0+incompatible/go.mod h1:VQb79nF8Z2cwLkLS35ukwStZIg5F66tcBccjip/j888=
|
github.com/oracle/oci-go-sdk v18.0.0+incompatible/go.mod h1:VQb79nF8Z2cwLkLS35ukwStZIg5F66tcBccjip/j888=
|
||||||
github.com/oracle/oci-go-sdk v24.3.0+incompatible h1:x4mcfb4agelf1O4/1/auGlZ1lr97jXRSSN5MxTgG/zU=
|
github.com/oracle/oci-go-sdk/v36 v36.2.0 h1:oBaN/FnBDy3ohMyVZ/rKfekYxnyksG2KK0YAhT5HSnk=
|
||||||
github.com/oracle/oci-go-sdk v24.3.0+incompatible/go.mod h1:VQb79nF8Z2cwLkLS35ukwStZIg5F66tcBccjip/j888=
|
github.com/oracle/oci-go-sdk/v36 v36.2.0/go.mod h1:t8Y/M3Lh8X4BOJhtThJKe1skRTg7qom7oWyHiNjo4RM=
|
||||||
github.com/outscale/osc-sdk-go/osc v0.0.0-20200722135656-d654809d0699 h1:SHe9i7h5cHe+cB77fQ6lsEgIwKg3ckNU90P03CjGMnI=
|
github.com/outscale/osc-sdk-go/osc v0.0.0-20200722135656-d654809d0699 h1:SHe9i7h5cHe+cB77fQ6lsEgIwKg3ckNU90P03CjGMnI=
|
||||||
github.com/outscale/osc-sdk-go/osc v0.0.0-20200722135656-d654809d0699/go.mod h1:5AqqNH1X8zCHescKVlpSHRzrat1KCKDXqZoQPe8fY3A=
|
github.com/outscale/osc-sdk-go/osc v0.0.0-20200722135656-d654809d0699/go.mod h1:5AqqNH1X8zCHescKVlpSHRzrat1KCKDXqZoQPe8fY3A=
|
||||||
github.com/packer-community/winrmcp v0.0.0-20180921204643-0fd363d6159a h1:A3QMuteviunoaY/8ex+RKFqwhcZJ/Cf3fCW3IwL2wx4=
|
github.com/packer-community/winrmcp v0.0.0-20180921204643-0fd363d6159a h1:A3QMuteviunoaY/8ex+RKFqwhcZJ/Cf3fCW3IwL2wx4=
|
||||||
@@ -665,8 +674,11 @@ github.com/yuin/goldmark v1.2.1/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9dec
|
|||||||
github.com/zclconf/go-cty v1.0.0/go.mod h1:xnAOWiHeOqg2nWS62VtQ7pbOu17FtxJNW8RLEih+O3s=
|
github.com/zclconf/go-cty v1.0.0/go.mod h1:xnAOWiHeOqg2nWS62VtQ7pbOu17FtxJNW8RLEih+O3s=
|
||||||
github.com/zclconf/go-cty v1.2.0/go.mod h1:hOPWgoHbaTUnI5k4D2ld+GRpFJSCe6bCM7m1q/N4PQ8=
|
github.com/zclconf/go-cty v1.2.0/go.mod h1:hOPWgoHbaTUnI5k4D2ld+GRpFJSCe6bCM7m1q/N4PQ8=
|
||||||
github.com/zclconf/go-cty v1.4.0/go.mod h1:nHzOclRkoj++EU9ZjSrZvRG0BXIWt8c7loYc0qXAFGQ=
|
github.com/zclconf/go-cty v1.4.0/go.mod h1:nHzOclRkoj++EU9ZjSrZvRG0BXIWt8c7loYc0qXAFGQ=
|
||||||
github.com/zclconf/go-cty v1.7.0 h1:yMqLinUwNCYkmiHjEH+luio1yGl35cjqVzjvdRg2WlY=
|
|
||||||
github.com/zclconf/go-cty v1.7.0/go.mod h1:VDR4+I79ubFBGm1uJac1226K5yANQFHeauxPBoP54+o=
|
github.com/zclconf/go-cty v1.7.0/go.mod h1:VDR4+I79ubFBGm1uJac1226K5yANQFHeauxPBoP54+o=
|
||||||
|
github.com/zclconf/go-cty v1.8.0/go.mod h1:vVKLxnk3puL4qRAv72AO+W99LUD4da90g3uUAzyuvAk=
|
||||||
|
github.com/zclconf/go-cty v1.8.1 h1:SI0LqNeNxAgv2WWqWJMlG2/Ad/6aYJ7IVYYMigmfkuI=
|
||||||
|
github.com/zclconf/go-cty v1.8.1/go.mod h1:vVKLxnk3puL4qRAv72AO+W99LUD4da90g3uUAzyuvAk=
|
||||||
|
github.com/zclconf/go-cty-debug v0.0.0-20191215020915-b22d67c1ba0b/go.mod h1:ZRKQfBXbGkpdV6QMzT3rU1kSTAnfu1dO8dPKjYprgj8=
|
||||||
github.com/zclconf/go-cty-yaml v1.0.1 h1:up11wlgAaDvlAGENcFDnZgkn0qUJurso7k6EpURKNF8=
|
github.com/zclconf/go-cty-yaml v1.0.1 h1:up11wlgAaDvlAGENcFDnZgkn0qUJurso7k6EpURKNF8=
|
||||||
github.com/zclconf/go-cty-yaml v1.0.1/go.mod h1:IP3Ylp0wQpYm50IHK8OZWKMu6sPJIUgKa8XhiVHura0=
|
github.com/zclconf/go-cty-yaml v1.0.1/go.mod h1:IP3Ylp0wQpYm50IHK8OZWKMu6sPJIUgKa8XhiVHura0=
|
||||||
go.opencensus.io v0.21.0/go.mod h1:mSImk1erAIZhrmZN+AvHh14ztQfjbGwt4TtuofqLduU=
|
go.opencensus.io v0.21.0/go.mod h1:mSImk1erAIZhrmZN+AvHh14ztQfjbGwt4TtuofqLduU=
|
||||||
@@ -839,8 +851,9 @@ golang.org/x/sys v0.0.0-20200828194041-157a740278f4/go.mod h1:h1NjWce9XRLGQEsW7w
|
|||||||
golang.org/x/sys v0.0.0-20200905004654-be1d3432aa8f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
golang.org/x/sys v0.0.0-20200905004654-be1d3432aa8f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||||
golang.org/x/sys v0.0.0-20200930185726-fdedc70b468f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
golang.org/x/sys v0.0.0-20200930185726-fdedc70b468f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||||
golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||||
golang.org/x/sys v0.0.0-20210124154548-22da62e12c0c h1:VwygUrnw9jn88c4u8GD3rZQbqrP/tgas88tPUbBxQrk=
|
|
||||||
golang.org/x/sys v0.0.0-20210124154548-22da62e12c0c/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
golang.org/x/sys v0.0.0-20210124154548-22da62e12c0c/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||||
|
golang.org/x/sys v0.0.0-20210319071255-635bc2c9138d h1:jbzgAvDZn8aEnytae+4ou0J0GwFZoHR0hOrTg4qH8GA=
|
||||||
|
golang.org/x/sys v0.0.0-20210319071255-635bc2c9138d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||||
golang.org/x/term v0.0.0-20201117132131-f5c789dd3221/go.mod h1:Nr5EML6q2oocZ2LXRh80K7BxOlk5/8JxuGnuhpl+muw=
|
golang.org/x/term v0.0.0-20201117132131-f5c789dd3221/go.mod h1:Nr5EML6q2oocZ2LXRh80K7BxOlk5/8JxuGnuhpl+muw=
|
||||||
golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1 h1:v+OssWQX+hTHEmOBgwxdZxK4zHq3yOs8F9J7mk0PY8E=
|
golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1 h1:v+OssWQX+hTHEmOBgwxdZxK4zHq3yOs8F9J7mk0PY8E=
|
||||||
golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo=
|
golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo=
|
||||||
|
|||||||
+16
-11
@@ -76,12 +76,15 @@ const (
|
|||||||
|
|
||||||
// Parse will Parse all HCL files in filename. Path can be a folder or a file.
|
// Parse will Parse all HCL files in filename. Path can be a folder or a file.
|
||||||
//
|
//
|
||||||
// Parse will first Parse variables and then the rest; so that interpolation
|
// Parse will first Parse packer and variables blocks, omitting the rest, which
|
||||||
// can happen.
|
// can be expanded with dynamic blocks. We need to evaluate all variables for
|
||||||
|
// that, so that data sources can expand dynamic blocks too.
|
||||||
//
|
//
|
||||||
// Parse returns a PackerConfig that contains configuration layout of a packer
|
// Parse returns a PackerConfig that contains configuration layout of a packer
|
||||||
// build; sources(builders)/provisioners/posts-processors will not be started
|
// build; sources(builders)/provisioners/posts-processors will not be started
|
||||||
// and their contents wont be verified; Most syntax errors will cause an error.
|
// and their contents wont be verified; Most syntax errors will cause an error,
|
||||||
|
// init should be called next to expand dynamic blocks and verify that used
|
||||||
|
// things do exist.
|
||||||
func (p *Parser) Parse(filename string, varFiles []string, argVars map[string]string) (*PackerConfig, hcl.Diagnostics) {
|
func (p *Parser) Parse(filename string, varFiles []string, argVars map[string]string) (*PackerConfig, hcl.Diagnostics) {
|
||||||
var files []*hcl.File
|
var files []*hcl.File
|
||||||
var diags hcl.Diagnostics
|
var diags hcl.Diagnostics
|
||||||
@@ -235,10 +238,6 @@ func (p *Parser) Parse(filename string, varFiles []string, argVars map[string]st
|
|||||||
diags = append(diags, cfg.collectInputVariableValues(os.Environ(), varFiles, argVars)...)
|
diags = append(diags, cfg.collectInputVariableValues(os.Environ(), varFiles, argVars)...)
|
||||||
}
|
}
|
||||||
|
|
||||||
// parse the actual content // rest
|
|
||||||
for _, file := range cfg.files {
|
|
||||||
diags = append(diags, cfg.parser.parseConfig(file, cfg)...)
|
|
||||||
}
|
|
||||||
return cfg, diags
|
return cfg, diags
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -291,7 +290,7 @@ func filterVarsFromLogs(inputOrLocal Variables) {
|
|||||||
if !variable.Sensitive {
|
if !variable.Sensitive {
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
value, _ := variable.Value()
|
value := variable.Value()
|
||||||
_ = cty.Walk(value, func(_ cty.Path, nested cty.Value) (bool, error) {
|
_ = cty.Walk(value, func(_ cty.Path, nested cty.Value) (bool, error) {
|
||||||
if nested.IsWhollyKnown() && !nested.IsNull() && nested.Type().Equals(cty.String) {
|
if nested.IsWhollyKnown() && !nested.IsNull() && nested.Type().Equals(cty.String) {
|
||||||
packersdk.LogSecretFilter.Set(nested.AsString())
|
packersdk.LogSecretFilter.Set(nested.AsString())
|
||||||
@@ -311,9 +310,9 @@ func (cfg *PackerConfig) Initialize(opts packer.InitializeOptions) hcl.Diagnosti
|
|||||||
return diags
|
return diags
|
||||||
}
|
}
|
||||||
|
|
||||||
_, moreDiags = cfg.InputVariables.Values()
|
moreDiags = cfg.InputVariables.ValidateValues()
|
||||||
diags = append(diags, moreDiags...)
|
diags = append(diags, moreDiags...)
|
||||||
_, moreDiags = cfg.LocalVariables.Values()
|
moreDiags = cfg.LocalVariables.ValidateValues()
|
||||||
diags = append(diags, moreDiags...)
|
diags = append(diags, moreDiags...)
|
||||||
diags = append(diags, cfg.evaluateDatasources(opts.SkipDatasourcesExecution)...)
|
diags = append(diags, cfg.evaluateDatasources(opts.SkipDatasourcesExecution)...)
|
||||||
diags = append(diags, cfg.evaluateLocalVariables(cfg.LocalBlocks)...)
|
diags = append(diags, cfg.evaluateLocalVariables(cfg.LocalBlocks)...)
|
||||||
@@ -321,6 +320,11 @@ func (cfg *PackerConfig) Initialize(opts packer.InitializeOptions) hcl.Diagnosti
|
|||||||
filterVarsFromLogs(cfg.InputVariables)
|
filterVarsFromLogs(cfg.InputVariables)
|
||||||
filterVarsFromLogs(cfg.LocalVariables)
|
filterVarsFromLogs(cfg.LocalVariables)
|
||||||
|
|
||||||
|
// parse the actual content // rest
|
||||||
|
for _, file := range cfg.files {
|
||||||
|
diags = append(diags, cfg.parser.parseConfig(file, cfg)...)
|
||||||
|
}
|
||||||
|
|
||||||
diags = append(diags, cfg.initializeBlocks()...)
|
diags = append(diags, cfg.initializeBlocks()...)
|
||||||
|
|
||||||
return diags
|
return diags
|
||||||
@@ -332,6 +336,7 @@ func (p *Parser) parseConfig(f *hcl.File, cfg *PackerConfig) hcl.Diagnostics {
|
|||||||
var diags hcl.Diagnostics
|
var diags hcl.Diagnostics
|
||||||
|
|
||||||
body := f.Body
|
body := f.Body
|
||||||
|
body = dynblock.Expand(body, cfg.EvalContext(DatasourceContext, nil))
|
||||||
content, moreDiags := body.Content(configSchema)
|
content, moreDiags := body.Content(configSchema)
|
||||||
diags = append(diags, moreDiags...)
|
diags = append(diags, moreDiags...)
|
||||||
|
|
||||||
@@ -380,7 +385,7 @@ func (p *Parser) parseConfig(f *hcl.File, cfg *PackerConfig) hcl.Diagnostics {
|
|||||||
func (p *Parser) decodeDatasources(file *hcl.File, cfg *PackerConfig) hcl.Diagnostics {
|
func (p *Parser) decodeDatasources(file *hcl.File, cfg *PackerConfig) hcl.Diagnostics {
|
||||||
var diags hcl.Diagnostics
|
var diags hcl.Diagnostics
|
||||||
|
|
||||||
body := dynblock.Expand(file.Body, cfg.EvalContext(DatasourceContext, nil))
|
body := file.Body
|
||||||
content, moreDiags := body.Content(configSchema)
|
content, moreDiags := body.Content(configSchema)
|
||||||
diags = append(diags, moreDiags...)
|
diags = append(diags, moreDiags...)
|
||||||
|
|
||||||
|
|||||||
+9
-13
@@ -7,7 +7,7 @@ import (
|
|||||||
"runtime"
|
"runtime"
|
||||||
|
|
||||||
"github.com/hashicorp/hcl/v2"
|
"github.com/hashicorp/hcl/v2"
|
||||||
"github.com/hashicorp/hcl/v2/ext/dynblock"
|
"github.com/hashicorp/packer-plugin-sdk/didyoumean"
|
||||||
pluginsdk "github.com/hashicorp/packer-plugin-sdk/plugin"
|
pluginsdk "github.com/hashicorp/packer-plugin-sdk/plugin"
|
||||||
plugingetter "github.com/hashicorp/packer/packer/plugin-getter"
|
plugingetter "github.com/hashicorp/packer/packer/plugin-getter"
|
||||||
)
|
)
|
||||||
@@ -109,7 +109,7 @@ func (cfg *PackerConfig) detectPluginBinaries() hcl.Diagnostics {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func (cfg *PackerConfig) initializeBlocks() hcl.Diagnostics {
|
func (cfg *PackerConfig) initializeBlocks() hcl.Diagnostics {
|
||||||
// verify that all used plugins do exist and expand dynamic bodies
|
// verify that all used plugins do exist
|
||||||
var diags hcl.Diagnostics
|
var diags hcl.Diagnostics
|
||||||
|
|
||||||
for _, build := range cfg.Builds {
|
for _, build := range cfg.Builds {
|
||||||
@@ -129,12 +129,16 @@ func (cfg *PackerConfig) initializeBlocks() hcl.Diagnostics {
|
|||||||
|
|
||||||
sourceDefinition, found := cfg.Sources[srcUsage.SourceRef]
|
sourceDefinition, found := cfg.Sources[srcUsage.SourceRef]
|
||||||
if !found {
|
if !found {
|
||||||
|
availableSrcs := listAvailableSourceNames(cfg.Sources)
|
||||||
|
detail := fmt.Sprintf("Known: %v", availableSrcs)
|
||||||
|
if sugg := didyoumean.NameSuggestion(srcUsage.SourceRef.String(), availableSrcs); sugg != "" {
|
||||||
|
detail = fmt.Sprintf("Did you mean to use %q?", sugg)
|
||||||
|
}
|
||||||
diags = append(diags, &hcl.Diagnostic{
|
diags = append(diags, &hcl.Diagnostic{
|
||||||
Summary: "Unknown " + sourceLabel + " " + srcUsage.String(),
|
Summary: "Unknown " + sourceLabel + " " + srcUsage.SourceRef.String(),
|
||||||
Subject: build.HCL2Ref.DefRange.Ptr(),
|
Subject: build.HCL2Ref.DefRange.Ptr(),
|
||||||
Severity: hcl.DiagError,
|
Severity: hcl.DiagError,
|
||||||
Detail: fmt.Sprintf("Known: %v", cfg.Sources),
|
Detail: detail,
|
||||||
// TODO: show known sources as a string slice here ^.
|
|
||||||
})
|
})
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
@@ -144,8 +148,6 @@ func (cfg *PackerConfig) initializeBlocks() hcl.Diagnostics {
|
|||||||
// merge additions into source definition to get a new body.
|
// merge additions into source definition to get a new body.
|
||||||
body = hcl.MergeBodies([]hcl.Body{body, srcUsage.Body})
|
body = hcl.MergeBodies([]hcl.Body{body, srcUsage.Body})
|
||||||
}
|
}
|
||||||
// expand any dynamic block.
|
|
||||||
body = dynblock.Expand(body, cfg.EvalContext(BuildContext, nil))
|
|
||||||
|
|
||||||
srcUsage.Body = body
|
srcUsage.Body = body
|
||||||
}
|
}
|
||||||
@@ -159,8 +161,6 @@ func (cfg *PackerConfig) initializeBlocks() hcl.Diagnostics {
|
|||||||
Severity: hcl.DiagError,
|
Severity: hcl.DiagError,
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
// Allow rest of the body to have dynamic blocks
|
|
||||||
provBlock.HCL2Ref.Rest = dynblock.Expand(provBlock.HCL2Ref.Rest, cfg.EvalContext(BuildContext, nil))
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if build.ErrorCleanupProvisionerBlock != nil {
|
if build.ErrorCleanupProvisionerBlock != nil {
|
||||||
@@ -172,8 +172,6 @@ func (cfg *PackerConfig) initializeBlocks() hcl.Diagnostics {
|
|||||||
Severity: hcl.DiagError,
|
Severity: hcl.DiagError,
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
// Allow rest of the body to have dynamic blocks
|
|
||||||
build.ErrorCleanupProvisionerBlock.HCL2Ref.Rest = dynblock.Expand(build.ErrorCleanupProvisionerBlock.HCL2Ref.Rest, cfg.EvalContext(BuildContext, nil))
|
|
||||||
}
|
}
|
||||||
|
|
||||||
for _, ppList := range build.PostProcessorsLists {
|
for _, ppList := range build.PostProcessorsLists {
|
||||||
@@ -186,8 +184,6 @@ func (cfg *PackerConfig) initializeBlocks() hcl.Diagnostics {
|
|||||||
Severity: hcl.DiagError,
|
Severity: hcl.DiagError,
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
// Allow the rest of the body to have dynamic blocks
|
|
||||||
ppBlock.HCL2Ref.Rest = dynblock.Expand(ppBlock.HCL2Ref.Rest, cfg.EvalContext(BuildContext, nil))
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -81,6 +81,7 @@ type Builds []*BuildBlock
|
|||||||
// load the references to the contents of the build block.
|
// load the references to the contents of the build block.
|
||||||
func (p *Parser) decodeBuildConfig(block *hcl.Block, cfg *PackerConfig) (*BuildBlock, hcl.Diagnostics) {
|
func (p *Parser) decodeBuildConfig(block *hcl.Block, cfg *PackerConfig) (*BuildBlock, hcl.Diagnostics) {
|
||||||
build := &BuildBlock{}
|
build := &BuildBlock{}
|
||||||
|
body := block.Body
|
||||||
|
|
||||||
var b struct {
|
var b struct {
|
||||||
Name string `hcl:"name,optional"`
|
Name string `hcl:"name,optional"`
|
||||||
@@ -88,7 +89,7 @@ func (p *Parser) decodeBuildConfig(block *hcl.Block, cfg *PackerConfig) (*BuildB
|
|||||||
FromSources []string `hcl:"sources,optional"`
|
FromSources []string `hcl:"sources,optional"`
|
||||||
Config hcl.Body `hcl:",remain"`
|
Config hcl.Body `hcl:",remain"`
|
||||||
}
|
}
|
||||||
diags := gohcl.DecodeBody(block.Body, nil, &b)
|
diags := gohcl.DecodeBody(body, nil, &b)
|
||||||
if diags.HasErrors() {
|
if diags.HasErrors() {
|
||||||
return nil, diags
|
return nil, diags
|
||||||
}
|
}
|
||||||
@@ -118,7 +119,8 @@ func (p *Parser) decodeBuildConfig(block *hcl.Block, cfg *PackerConfig) (*BuildB
|
|||||||
build.Sources = append(build.Sources, SourceUseBlock{SourceRef: ref})
|
build.Sources = append(build.Sources, SourceUseBlock{SourceRef: ref})
|
||||||
}
|
}
|
||||||
|
|
||||||
content, moreDiags := b.Config.Content(buildSchema)
|
body = b.Config
|
||||||
|
content, moreDiags := body.Content(buildSchema)
|
||||||
diags = append(diags, moreDiags...)
|
diags = append(diags, moreDiags...)
|
||||||
if diags.HasErrors() {
|
if diags.HasErrors() {
|
||||||
return nil, diags
|
return nil, diags
|
||||||
|
|||||||
@@ -91,8 +91,8 @@ const (
|
|||||||
// decoder in order to tell what is the actual value of a var or a local and
|
// decoder in order to tell what is the actual value of a var or a local and
|
||||||
// the list of defined functions.
|
// the list of defined functions.
|
||||||
func (cfg *PackerConfig) EvalContext(ctx BlockContext, variables map[string]cty.Value) *hcl.EvalContext {
|
func (cfg *PackerConfig) EvalContext(ctx BlockContext, variables map[string]cty.Value) *hcl.EvalContext {
|
||||||
inputVariables, _ := cfg.InputVariables.Values()
|
inputVariables := cfg.InputVariables.Values()
|
||||||
localVariables, _ := cfg.LocalVariables.Values()
|
localVariables := cfg.LocalVariables.Values()
|
||||||
ectx := &hcl.EvalContext{
|
ectx := &hcl.EvalContext{
|
||||||
Functions: Functions(cfg.Basedir),
|
Functions: Functions(cfg.Basedir),
|
||||||
Variables: map[string]cty.Value{
|
Variables: map[string]cty.Value{
|
||||||
@@ -594,7 +594,7 @@ func (p *PackerConfig) printVariables() string {
|
|||||||
sort.Strings(keys)
|
sort.Strings(keys)
|
||||||
for _, key := range keys {
|
for _, key := range keys {
|
||||||
v := p.InputVariables[key]
|
v := p.InputVariables[key]
|
||||||
val, _ := v.Value()
|
val := v.Value()
|
||||||
fmt.Fprintf(out, "var.%s: %q\n", v.Name, PrintableCtyValue(val))
|
fmt.Fprintf(out, "var.%s: %q\n", v.Name, PrintableCtyValue(val))
|
||||||
}
|
}
|
||||||
out.WriteString("\n> local-variables:\n\n")
|
out.WriteString("\n> local-variables:\n\n")
|
||||||
@@ -602,7 +602,7 @@ func (p *PackerConfig) printVariables() string {
|
|||||||
sort.Strings(keys)
|
sort.Strings(keys)
|
||||||
for _, key := range keys {
|
for _, key := range keys {
|
||||||
v := p.LocalVariables[key]
|
v := p.LocalVariables[key]
|
||||||
val, _ := v.Value()
|
val := v.Value()
|
||||||
fmt.Fprintf(out, "local.%s: %q\n", v.Name, PrintableCtyValue(val))
|
fmt.Fprintf(out, "local.%s: %q\n", v.Name, PrintableCtyValue(val))
|
||||||
}
|
}
|
||||||
return out.String()
|
return out.String()
|
||||||
|
|||||||
@@ -538,65 +538,8 @@ func TestParser_no_init(t *testing.T) {
|
|||||||
Type: cty.List(cty.String),
|
Type: cty.List(cty.String),
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
Sources: map[SourceRef]SourceBlock{
|
Sources: nil,
|
||||||
refVBIsoUbuntu1204: {Type: "virtualbox-iso", Name: "ubuntu-1204"},
|
Builds: nil,
|
||||||
refAWSV3MyImage: {Type: "amazon-v3-ebs", Name: "my-image"},
|
|
||||||
},
|
|
||||||
Builds: Builds{
|
|
||||||
&BuildBlock{
|
|
||||||
Sources: []SourceUseBlock{
|
|
||||||
{
|
|
||||||
SourceRef: refVBIsoUbuntu1204,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
SourceRef: refAWSV3MyImage,
|
|
||||||
},
|
|
||||||
},
|
|
||||||
ProvisionerBlocks: []*ProvisionerBlock{
|
|
||||||
{
|
|
||||||
PType: "shell",
|
|
||||||
PName: "provisioner that does something",
|
|
||||||
},
|
|
||||||
{
|
|
||||||
PType: "file",
|
|
||||||
},
|
|
||||||
},
|
|
||||||
PostProcessorsLists: [][]*PostProcessorBlock{
|
|
||||||
{
|
|
||||||
{
|
|
||||||
PType: "amazon-import",
|
|
||||||
PName: "something",
|
|
||||||
KeepInputArtifact: pTrue,
|
|
||||||
},
|
|
||||||
},
|
|
||||||
{
|
|
||||||
{
|
|
||||||
PType: "amazon-import",
|
|
||||||
},
|
|
||||||
},
|
|
||||||
{
|
|
||||||
{
|
|
||||||
PType: "amazon-import",
|
|
||||||
PName: "first-nested-post-processor",
|
|
||||||
},
|
|
||||||
{
|
|
||||||
PType: "amazon-import",
|
|
||||||
PName: "second-nested-post-processor",
|
|
||||||
},
|
|
||||||
},
|
|
||||||
{
|
|
||||||
{
|
|
||||||
PType: "amazon-import",
|
|
||||||
PName: "third-nested-post-processor",
|
|
||||||
},
|
|
||||||
{
|
|
||||||
PType: "amazon-import",
|
|
||||||
PName: "fourth-nested-post-processor",
|
|
||||||
},
|
|
||||||
},
|
|
||||||
},
|
|
||||||
},
|
|
||||||
},
|
|
||||||
},
|
},
|
||||||
false, false,
|
false, false,
|
||||||
[]packersdk.Build{},
|
[]packersdk.Build{},
|
||||||
|
|||||||
@@ -2,6 +2,7 @@ package hcl2template
|
|||||||
|
|
||||||
import (
|
import (
|
||||||
"fmt"
|
"fmt"
|
||||||
|
"sort"
|
||||||
"strconv"
|
"strconv"
|
||||||
|
|
||||||
"github.com/hashicorp/hcl/v2"
|
"github.com/hashicorp/hcl/v2"
|
||||||
@@ -170,3 +171,12 @@ var NoSource SourceRef
|
|||||||
func (r SourceRef) String() string {
|
func (r SourceRef) String() string {
|
||||||
return fmt.Sprintf("%s.%s", r.Type, r.Name)
|
return fmt.Sprintf("%s.%s", r.Type, r.Name)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func listAvailableSourceNames(srcs map[SourceRef]SourceBlock) []string {
|
||||||
|
res := make([]string, 0, len(srcs))
|
||||||
|
for k := range srcs {
|
||||||
|
res = append(res, k.String())
|
||||||
|
}
|
||||||
|
sort.Strings(res)
|
||||||
|
return res
|
||||||
|
}
|
||||||
|
|||||||
@@ -152,9 +152,19 @@ func (v *Variable) validateValue(val VariableAssignment) (diags hcl.Diagnostics)
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Value returns the last found value from the list of variable settings.
|
// Value returns the last found value from the list of variable settings.
|
||||||
func (v *Variable) Value() (cty.Value, hcl.Diagnostics) {
|
func (v *Variable) Value() cty.Value {
|
||||||
if len(v.Values) == 0 {
|
if len(v.Values) == 0 {
|
||||||
return cty.UnknownVal(v.Type), hcl.Diagnostics{&hcl.Diagnostic{
|
return cty.UnknownVal(v.Type)
|
||||||
|
}
|
||||||
|
val := v.Values[len(v.Values)-1]
|
||||||
|
return val.Value
|
||||||
|
}
|
||||||
|
|
||||||
|
// ValidateValue tells if the selected value for the Variable is valid according
|
||||||
|
// to its validation settings.
|
||||||
|
func (v *Variable) ValidateValue() hcl.Diagnostics {
|
||||||
|
if len(v.Values) == 0 {
|
||||||
|
return hcl.Diagnostics{&hcl.Diagnostic{
|
||||||
Severity: hcl.DiagError,
|
Severity: hcl.DiagError,
|
||||||
Summary: fmt.Sprintf("Unset variable %q", v.Name),
|
Summary: fmt.Sprintf("Unset variable %q", v.Name),
|
||||||
Detail: "A used variable must be set or have a default value; see " +
|
Detail: "A used variable must be set or have a default value; see " +
|
||||||
@@ -163,8 +173,8 @@ func (v *Variable) Value() (cty.Value, hcl.Diagnostics) {
|
|||||||
Context: v.Range.Ptr(),
|
Context: v.Range.Ptr(),
|
||||||
}}
|
}}
|
||||||
}
|
}
|
||||||
val := v.Values[len(v.Values)-1]
|
|
||||||
return val.Value, v.validateValue(v.Values[len(v.Values)-1])
|
return v.validateValue(v.Values[len(v.Values)-1])
|
||||||
}
|
}
|
||||||
|
|
||||||
type Variables map[string]*Variable
|
type Variables map[string]*Variable
|
||||||
@@ -177,15 +187,21 @@ func (variables Variables) Keys() []string {
|
|||||||
return keys
|
return keys
|
||||||
}
|
}
|
||||||
|
|
||||||
func (variables Variables) Values() (map[string]cty.Value, hcl.Diagnostics) {
|
func (variables Variables) Values() map[string]cty.Value {
|
||||||
res := map[string]cty.Value{}
|
res := map[string]cty.Value{}
|
||||||
var diags hcl.Diagnostics
|
|
||||||
for k, v := range variables {
|
for k, v := range variables {
|
||||||
value, moreDiags := v.Value()
|
value := v.Value()
|
||||||
diags = append(diags, moreDiags...)
|
|
||||||
res[k] = value
|
res[k] = value
|
||||||
}
|
}
|
||||||
return res, diags
|
return res
|
||||||
|
}
|
||||||
|
|
||||||
|
func (variables Variables) ValidateValues() hcl.Diagnostics {
|
||||||
|
var diags hcl.Diagnostics
|
||||||
|
for _, v := range variables {
|
||||||
|
diags = append(diags, v.ValidateValue()...)
|
||||||
|
}
|
||||||
|
return diags
|
||||||
}
|
}
|
||||||
|
|
||||||
// decodeVariable decodes a variable key and value into Variables
|
// decodeVariable decodes a variable key and value into Variables
|
||||||
|
|||||||
@@ -868,7 +868,7 @@ func TestVariables_collectVariableValues(t *testing.T) {
|
|||||||
}
|
}
|
||||||
values := map[string]cty.Value{}
|
values := map[string]cty.Value{}
|
||||||
for k, v := range tt.variables {
|
for k, v := range tt.variables {
|
||||||
value, diag := v.Value()
|
value, diag := v.Value(), v.ValidateValue()
|
||||||
if diag != nil {
|
if diag != nil {
|
||||||
t.Fatalf("Value %s: %v", k, diag)
|
t.Fatalf("Value %s: %v", k, diag)
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -152,6 +152,10 @@ func wrappedMain() int {
|
|||||||
// passed into commands like `packer build`
|
// passed into commands like `packer build`
|
||||||
config, err := loadConfig()
|
config, err := loadConfig()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
// Writing to Stdout here so that the error message bypasses panicwrap. By using the
|
||||||
|
// ErrorPrefix this output will be redirected to Stderr by the copyOutput func.
|
||||||
|
// TODO: nywilken need to revisit this setup to better output errors to Stderr, and output to Stdout
|
||||||
|
// without panicwrap
|
||||||
fmt.Fprintf(os.Stdout, "%s Error loading configuration: \n\n%s\n", ErrorPrefix, err)
|
fmt.Fprintf(os.Stdout, "%s Error loading configuration: \n\n%s\n", ErrorPrefix, err)
|
||||||
return 1
|
return 1
|
||||||
}
|
}
|
||||||
@@ -166,6 +170,10 @@ func wrappedMain() int {
|
|||||||
|
|
||||||
cacheDir, err := packersdk.CachePath()
|
cacheDir, err := packersdk.CachePath()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
// Writing to Stdout here so that the error message bypasses panicwrap. By using the
|
||||||
|
// ErrorPrefix this output will be redirected to Stderr by the copyOutput func.
|
||||||
|
// TODO: nywilken need to revisit this setup to better output errors to Stderr, and output to Stdout
|
||||||
|
// without panicwrap
|
||||||
fmt.Fprintf(os.Stdout, "%s Error preparing cache directory: \n\n%s\n", ErrorPrefix, err)
|
fmt.Fprintf(os.Stdout, "%s Error preparing cache directory: \n\n%s\n", ErrorPrefix, err)
|
||||||
return 1
|
return 1
|
||||||
}
|
}
|
||||||
@@ -187,7 +195,8 @@ func wrappedMain() int {
|
|||||||
// Set this so that we don't get colored output in our machine-
|
// Set this so that we don't get colored output in our machine-
|
||||||
// readable UI.
|
// readable UI.
|
||||||
if err := os.Setenv("PACKER_NO_COLOR", "1"); err != nil {
|
if err := os.Setenv("PACKER_NO_COLOR", "1"); err != nil {
|
||||||
fmt.Fprintf(os.Stdout, "%s Packer failed to initialize UI: %s\n", ErrorPrefix, err)
|
// Outputting error using Ui here to conform to the machine readable format.
|
||||||
|
ui.Error(fmt.Sprintf("Packer failed to initialize UI: %s\n", err))
|
||||||
return 1
|
return 1
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
@@ -202,13 +211,16 @@ func wrappedMain() int {
|
|||||||
currentPID := os.Getpid()
|
currentPID := os.Getpid()
|
||||||
backgrounded, err := checkProcess(currentPID)
|
backgrounded, err := checkProcess(currentPID)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
fmt.Fprintf(os.Stdout, "%s cannot determine if process is in "+
|
// Writing to Stderr will ensure that the output gets captured by panicwrap.
|
||||||
"background: %s\n", ErrorPrefix, err)
|
// This error message and any other message writing to Stderr after this point will only show up with PACKER_LOG=1
|
||||||
|
// TODO: nywilken need to revisit this setup to better output errors to Stderr, and output to Stdout without panicwrap.
|
||||||
|
fmt.Fprintf(os.Stderr, "%s cannot determine if process is in background: %s\n", ErrorPrefix, err)
|
||||||
}
|
}
|
||||||
|
|
||||||
if backgrounded {
|
if backgrounded {
|
||||||
fmt.Fprintf(os.Stdout, "%s Running in background, not using a TTY\n", ErrorPrefix)
|
fmt.Fprintf(os.Stderr, "%s Running in background, not using a TTY\n", ErrorPrefix)
|
||||||
} else if TTY, err := openTTY(); err != nil {
|
} else if TTY, err := openTTY(); err != nil {
|
||||||
fmt.Fprintf(os.Stdout, "%s No tty available: %s\n", ErrorPrefix, err)
|
fmt.Fprintf(os.Stderr, "%s No tty available: %s\n", ErrorPrefix, err)
|
||||||
} else {
|
} else {
|
||||||
basicUi.TTY = TTY
|
basicUi.TTY = TTY
|
||||||
basicUi.PB = &packer.UiProgressBar{}
|
basicUi.PB = &packer.UiProgressBar{}
|
||||||
@@ -246,6 +258,10 @@ func wrappedMain() int {
|
|||||||
}
|
}
|
||||||
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
// Writing to Stdout here so that the error message bypasses panicwrap. By using the
|
||||||
|
// ErrorPrefix this output will be redirected to Stderr by the copyOutput func.
|
||||||
|
// TODO: nywilken need to revisit this setup to better output errors to Stderr, and output to Stdout
|
||||||
|
// without panicwrap
|
||||||
fmt.Fprintf(os.Stdout, "%s Error executing CLI: %s\n", ErrorPrefix, err)
|
fmt.Fprintf(os.Stdout, "%s Error executing CLI: %s\n", ErrorPrefix, err)
|
||||||
return 1
|
return 1
|
||||||
}
|
}
|
||||||
@@ -454,3 +470,5 @@ func init() {
|
|||||||
// Seed the random number generator
|
// Seed the random number generator
|
||||||
rand.Seed(time.Now().UTC().UnixNano())
|
rand.Seed(time.Now().UTC().UnixNano())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
var backgroundCheckFn func(int) (bool, error)
|
||||||
|
|||||||
@@ -2,6 +2,7 @@ package main
|
|||||||
|
|
||||||
import (
|
import (
|
||||||
"math/rand"
|
"math/rand"
|
||||||
|
"os"
|
||||||
"reflect"
|
"reflect"
|
||||||
"strings"
|
"strings"
|
||||||
"testing"
|
"testing"
|
||||||
@@ -67,3 +68,11 @@ func TestRandom(t *testing.T) {
|
|||||||
t.Fatal("math.rand is not seeded properly")
|
t.Fatal("math.rand is not seeded properly")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func ExampleWrappedMain() {
|
||||||
|
os.Setenv("PACKER_WRAP_COOKIE", "49C22B1A-3A93-4C98-97FA-E07D18C787B5")
|
||||||
|
backgroundCheckFn = func(_ int) (bool, error) { return true, nil }
|
||||||
|
os.Args = []string{"packer", "version"}
|
||||||
|
wrappedMain()
|
||||||
|
//Output: Packer v1.7.2-dev
|
||||||
|
}
|
||||||
|
|||||||
@@ -1,13 +0,0 @@
|
|||||||
package version
|
|
||||||
|
|
||||||
import (
|
|
||||||
"github.com/hashicorp/packer-plugin-sdk/version"
|
|
||||||
packerVersion "github.com/hashicorp/packer/version"
|
|
||||||
)
|
|
||||||
|
|
||||||
var AmazonImportPluginVersion *version.PluginVersion
|
|
||||||
|
|
||||||
func init() {
|
|
||||||
AmazonImportPluginVersion = version.InitializePluginVersion(
|
|
||||||
packerVersion.Version, packerVersion.VersionPrerelease)
|
|
||||||
}
|
|
||||||
@@ -188,10 +188,10 @@ func (p *PostProcessor) PostProcess(ctx context.Context, ui packersdk.Ui, artifa
|
|||||||
new(stepCreateProvider),
|
new(stepCreateProvider),
|
||||||
}
|
}
|
||||||
if p.config.BoxDownloadUrl == "" {
|
if p.config.BoxDownloadUrl == "" {
|
||||||
steps = append(steps, new(stepPrepareUpload), new(stepUpload))
|
steps = append(steps,
|
||||||
if !p.config.NoDirectUpload {
|
new(stepPrepareUpload),
|
||||||
steps = append(steps, new(stepConfirmUpload))
|
new(stepUpload),
|
||||||
}
|
new(stepConfirmUpload))
|
||||||
}
|
}
|
||||||
steps = append(steps, new(stepReleaseVersion))
|
steps = append(steps, new(stepReleaseVersion))
|
||||||
|
|
||||||
|
|||||||
@@ -10,6 +10,7 @@ import (
|
|||||||
"net/http"
|
"net/http"
|
||||||
"net/http/httptest"
|
"net/http/httptest"
|
||||||
"os"
|
"os"
|
||||||
|
"runtime"
|
||||||
"strings"
|
"strings"
|
||||||
"testing"
|
"testing"
|
||||||
|
|
||||||
@@ -361,6 +362,138 @@ func TestPostProcessor_PostProcess_uploadsAndNoRelease(t *testing.T) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func TestPostProcessor_PostProcess_directUpload5GFile(t *testing.T) {
|
||||||
|
// Disable test on Windows due to unreliable sparse file creation
|
||||||
|
if runtime.GOOS == "windows" {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
// Boxes up to 5GB are supported for direct upload so
|
||||||
|
// set the box asset to be 5GB exactly
|
||||||
|
fSize := int64(5368709120)
|
||||||
|
files := tarFiles{
|
||||||
|
{"foo.txt", "This is a foo file"},
|
||||||
|
{"bar.txt", "This is a bar file"},
|
||||||
|
{"metadata.json", `{"provider": "virtualbox"}`},
|
||||||
|
}
|
||||||
|
f, err := createBox(files)
|
||||||
|
if err != nil {
|
||||||
|
t.Fatalf("%s", err)
|
||||||
|
}
|
||||||
|
defer os.Remove(f.Name())
|
||||||
|
|
||||||
|
if err := expandFile(f, fSize); err != nil {
|
||||||
|
t.Fatalf("failed to expand box file - %s", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
artifact := &packersdk.MockArtifact{
|
||||||
|
BuilderIdValue: "mitchellh.post-processor.vagrant",
|
||||||
|
FilesValue: []string{f.Name()},
|
||||||
|
}
|
||||||
|
f.Close()
|
||||||
|
|
||||||
|
s := newStackServer(
|
||||||
|
[]stubResponse{
|
||||||
|
stubResponse{StatusCode: 200, Method: "PUT", Path: "/box-upload-path"},
|
||||||
|
},
|
||||||
|
)
|
||||||
|
defer s.Close()
|
||||||
|
|
||||||
|
stack := []stubResponse{
|
||||||
|
stubResponse{StatusCode: 200, Method: "GET", Path: "/authenticate"},
|
||||||
|
stubResponse{StatusCode: 200, Method: "GET", Path: "/box/hashicorp/precise64", Response: `{"tag": "hashicorp/precise64"}`},
|
||||||
|
stubResponse{StatusCode: 200, Method: "POST", Path: "/box/hashicorp/precise64/versions", Response: `{}`},
|
||||||
|
stubResponse{StatusCode: 200, Method: "POST", Path: "/box/hashicorp/precise64/version/0.5/providers", Response: `{}`},
|
||||||
|
stubResponse{StatusCode: 200, Method: "GET", Path: "/box/hashicorp/precise64/version/0.5/provider/id/upload/direct"},
|
||||||
|
stubResponse{StatusCode: 200, Method: "PUT", Path: "/box-upload-complete"},
|
||||||
|
}
|
||||||
|
|
||||||
|
server := newStackServer(stack)
|
||||||
|
defer server.Close()
|
||||||
|
config := testGoodConfig()
|
||||||
|
config["vagrant_cloud_url"] = server.URL
|
||||||
|
config["no_release"] = true
|
||||||
|
|
||||||
|
// Set response here so we have API server URL available
|
||||||
|
stack[4].Response = `{"upload_path": "` + s.URL + `/box-upload-path", "callback": "` + server.URL + `/box-upload-complete"}`
|
||||||
|
|
||||||
|
var p PostProcessor
|
||||||
|
|
||||||
|
err = p.Configure(config)
|
||||||
|
if err != nil {
|
||||||
|
t.Fatalf("err: %s", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
_, _, _, err = p.PostProcess(context.Background(), testUi(), artifact)
|
||||||
|
if err != nil {
|
||||||
|
t.Fatalf("err: %s", err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestPostProcessor_PostProcess_directUploadOver5GFile(t *testing.T) {
|
||||||
|
// Disable test on Windows due to unreliable sparse file creation
|
||||||
|
if runtime.GOOS == "windows" {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
// Boxes over 5GB are not supported for direct upload so
|
||||||
|
// set the box asset to be one byte over 5GB
|
||||||
|
fSize := int64(5368709121)
|
||||||
|
files := tarFiles{
|
||||||
|
{"foo.txt", "This is a foo file"},
|
||||||
|
{"bar.txt", "This is a bar file"},
|
||||||
|
{"metadata.json", `{"provider": "virtualbox"}`},
|
||||||
|
}
|
||||||
|
f, err := createBox(files)
|
||||||
|
if err != nil {
|
||||||
|
t.Fatalf("%s", err)
|
||||||
|
}
|
||||||
|
defer os.Remove(f.Name())
|
||||||
|
|
||||||
|
if err := expandFile(f, fSize); err != nil {
|
||||||
|
t.Fatalf("failed to expand box file - %s", err)
|
||||||
|
}
|
||||||
|
f.Close()
|
||||||
|
|
||||||
|
artifact := &packersdk.MockArtifact{
|
||||||
|
BuilderIdValue: "mitchellh.post-processor.vagrant",
|
||||||
|
FilesValue: []string{f.Name()},
|
||||||
|
}
|
||||||
|
|
||||||
|
s := newStackServer(
|
||||||
|
[]stubResponse{
|
||||||
|
stubResponse{StatusCode: 200, Method: "PUT", Path: "/box-upload-path"},
|
||||||
|
},
|
||||||
|
)
|
||||||
|
defer s.Close()
|
||||||
|
|
||||||
|
stack := []stubResponse{
|
||||||
|
stubResponse{StatusCode: 200, Method: "GET", Path: "/authenticate"},
|
||||||
|
stubResponse{StatusCode: 200, Method: "GET", Path: "/box/hashicorp/precise64", Response: `{"tag": "hashicorp/precise64"}`},
|
||||||
|
stubResponse{StatusCode: 200, Method: "POST", Path: "/box/hashicorp/precise64/versions", Response: `{}`},
|
||||||
|
stubResponse{StatusCode: 200, Method: "POST", Path: "/box/hashicorp/precise64/version/0.5/providers", Response: `{}`},
|
||||||
|
stubResponse{StatusCode: 200, Method: "GET", Path: "/box/hashicorp/precise64/version/0.5/provider/id/upload", Response: `{"upload_path": "` + s.URL + `/box-upload-path"}`},
|
||||||
|
}
|
||||||
|
|
||||||
|
server := newStackServer(stack)
|
||||||
|
defer server.Close()
|
||||||
|
config := testGoodConfig()
|
||||||
|
config["vagrant_cloud_url"] = server.URL
|
||||||
|
config["no_release"] = true
|
||||||
|
|
||||||
|
var p PostProcessor
|
||||||
|
|
||||||
|
err = p.Configure(config)
|
||||||
|
if err != nil {
|
||||||
|
t.Fatalf("err: %s", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
_, _, _, err = p.PostProcess(context.Background(), testUi(), artifact)
|
||||||
|
if err != nil {
|
||||||
|
t.Fatalf("err: %s", err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
func TestPostProcessor_PostProcess_uploadsDirectAndReleases(t *testing.T) {
|
func TestPostProcessor_PostProcess_uploadsDirectAndReleases(t *testing.T) {
|
||||||
files := tarFiles{
|
files := tarFiles{
|
||||||
{"foo.txt", "This is a foo file"},
|
{"foo.txt", "This is a foo file"},
|
||||||
@@ -697,3 +830,21 @@ func createBox(files tarFiles) (boxfile *os.File, err error) {
|
|||||||
|
|
||||||
return boxfile, nil
|
return boxfile, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func expandFile(f *os.File, finalSize int64) (err error) {
|
||||||
|
s, err := f.Stat()
|
||||||
|
if err != nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
size := finalSize - s.Size()
|
||||||
|
if size < 1 {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
if _, err = f.Seek(size-1, 2); err != nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
if _, err = f.Write([]byte{0}); err != nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|||||||
@@ -16,6 +16,11 @@ func (s *stepConfirmUpload) Run(ctx context.Context, state multistep.StateBag) m
|
|||||||
ui := state.Get("ui").(packersdk.Ui)
|
ui := state.Get("ui").(packersdk.Ui)
|
||||||
upload := state.Get("upload").(*Upload)
|
upload := state.Get("upload").(*Upload)
|
||||||
url := upload.CallbackPath
|
url := upload.CallbackPath
|
||||||
|
config := state.Get("config").(*Config)
|
||||||
|
|
||||||
|
if config.NoDirectUpload {
|
||||||
|
return multistep.ActionContinue
|
||||||
|
}
|
||||||
|
|
||||||
ui.Say("Confirming direct box upload completion")
|
ui.Say("Confirming direct box upload completion")
|
||||||
|
|
||||||
@@ -23,7 +28,7 @@ func (s *stepConfirmUpload) Run(ctx context.Context, state multistep.StateBag) m
|
|||||||
|
|
||||||
if err != nil || resp.StatusCode != 200 {
|
if err != nil || resp.StatusCode != 200 {
|
||||||
if resp == nil || resp.Body == nil {
|
if resp == nil || resp.Body == nil {
|
||||||
state.Put("error", "No response from server.")
|
state.Put("error", fmt.Errorf("No response from server."))
|
||||||
} else {
|
} else {
|
||||||
cloudErrors := &VagrantCloudErrors{}
|
cloudErrors := &VagrantCloudErrors{}
|
||||||
err = decodeBody(resp, cloudErrors)
|
err = decodeBody(resp, cloudErrors)
|
||||||
|
|||||||
@@ -3,11 +3,14 @@ package vagrantcloud
|
|||||||
import (
|
import (
|
||||||
"context"
|
"context"
|
||||||
"fmt"
|
"fmt"
|
||||||
|
"os"
|
||||||
|
|
||||||
"github.com/hashicorp/packer-plugin-sdk/multistep"
|
"github.com/hashicorp/packer-plugin-sdk/multistep"
|
||||||
packersdk "github.com/hashicorp/packer-plugin-sdk/packer"
|
packersdk "github.com/hashicorp/packer-plugin-sdk/packer"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
const VAGRANT_CLOUD_DIRECT_UPLOAD_LIMIT = 5368709120 // Upload limit is 5G
|
||||||
|
|
||||||
type Upload struct {
|
type Upload struct {
|
||||||
UploadPath string `json:"upload_path"`
|
UploadPath string `json:"upload_path"`
|
||||||
CallbackPath string `json:"callback"`
|
CallbackPath string `json:"callback"`
|
||||||
@@ -25,6 +28,18 @@ func (s *stepPrepareUpload) Run(ctx context.Context, state multistep.StateBag) m
|
|||||||
provider := state.Get("provider").(*Provider)
|
provider := state.Get("provider").(*Provider)
|
||||||
artifactFilePath := state.Get("artifactFilePath").(string)
|
artifactFilePath := state.Get("artifactFilePath").(string)
|
||||||
|
|
||||||
|
// If direct upload is enabled, the asset size must be <= 5 GB
|
||||||
|
if config.NoDirectUpload == false {
|
||||||
|
f, err := os.Stat(artifactFilePath)
|
||||||
|
if err != nil {
|
||||||
|
ui.Error(fmt.Sprintf("error determining size of upload artifact: %s", artifactFilePath))
|
||||||
|
}
|
||||||
|
if f.Size() > VAGRANT_CLOUD_DIRECT_UPLOAD_LIMIT {
|
||||||
|
ui.Say(fmt.Sprintf("Asset %s is larger than the direct upload limit. Setting `NoDirectUpload` to true", artifactFilePath))
|
||||||
|
config.NoDirectUpload = true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
path := fmt.Sprintf("box/%s/version/%v/provider/%s/upload", box.Tag, version.Version, provider.Name)
|
path := fmt.Sprintf("box/%s/version/%v/provider/%s/upload", box.Tag, version.Version, provider.Name)
|
||||||
if !config.NoDirectUpload {
|
if !config.NoDirectUpload {
|
||||||
path = path + "/direct"
|
path = path + "/direct"
|
||||||
@@ -37,7 +52,7 @@ func (s *stepPrepareUpload) Run(ctx context.Context, state multistep.StateBag) m
|
|||||||
|
|
||||||
if err != nil || (resp.StatusCode != 200) {
|
if err != nil || (resp.StatusCode != 200) {
|
||||||
if resp == nil || resp.Body == nil {
|
if resp == nil || resp.Body == nil {
|
||||||
state.Put("error", "No response from server.")
|
state.Put("error", fmt.Errorf("No response from server."))
|
||||||
} else {
|
} else {
|
||||||
cloudErrors := &VagrantCloudErrors{}
|
cloudErrors := &VagrantCloudErrors{}
|
||||||
err = decodeBody(resp, cloudErrors)
|
err = decodeBody(resp, cloudErrors)
|
||||||
|
|||||||
+1
-1
@@ -4,7 +4,7 @@
|
|||||||
# Determine the arch/os combos we're building for
|
# Determine the arch/os combos we're building for
|
||||||
ALL_XC_ARCH="386 amd64 arm arm64 ppc64le mips mips64 mipsle mipsle64 s390x"
|
ALL_XC_ARCH="386 amd64 arm arm64 ppc64le mips mips64 mipsle mipsle64 s390x"
|
||||||
ALL_XC_OS="linux darwin windows freebsd openbsd solaris"
|
ALL_XC_OS="linux darwin windows freebsd openbsd solaris"
|
||||||
SKIPPED_OSARCH="!darwin/arm !darwin/arm64 !freebsd/arm !freebsd/arm64"
|
SKIPPED_OSARCH="!darwin/arm !freebsd/arm !freebsd/arm64"
|
||||||
|
|
||||||
# Exit immediately if a command fails
|
# Exit immediately if a command fails
|
||||||
set -e
|
set -e
|
||||||
|
|||||||
-290
@@ -1,290 +0,0 @@
|
|||||||
# The following Ragel file was autogenerated with unicode2ragel.rb
|
|
||||||
# from: https://www.unicode.org/Public/emoji/12.0/emoji-data.txt
|
|
||||||
#
|
|
||||||
# It defines ["Extended_Pictographic"].
|
|
||||||
#
|
|
||||||
# To use this, make sure that your alphtype is set to byte,
|
|
||||||
# and that your input is in utf8.
|
|
||||||
|
|
||||||
%%{
|
|
||||||
machine Emoji;
|
|
||||||
|
|
||||||
Extended_Pictographic =
|
|
||||||
0xC2 0xA9 #1.1 [1] (©️) copyright
|
|
||||||
| 0xC2 0xAE #1.1 [1] (®️) registered
|
|
||||||
| 0xE2 0x80 0xBC #1.1 [1] (‼️) double exclamation mark
|
|
||||||
| 0xE2 0x81 0x89 #3.0 [1] (⁉️) exclamation question mark
|
|
||||||
| 0xE2 0x84 0xA2 #1.1 [1] (™️) trade mark
|
|
||||||
| 0xE2 0x84 0xB9 #3.0 [1] (ℹ️) information
|
|
||||||
| 0xE2 0x86 0x94..0x99 #1.1 [6] (↔️..↙️) left-right arrow..down...
|
|
||||||
| 0xE2 0x86 0xA9..0xAA #1.1 [2] (↩️..↪️) right arrow curving le...
|
|
||||||
| 0xE2 0x8C 0x9A..0x9B #1.1 [2] (⌚..⌛) watch..hourglass done
|
|
||||||
| 0xE2 0x8C 0xA8 #1.1 [1] (⌨️) keyboard
|
|
||||||
| 0xE2 0x8E 0x88 #3.0 [1] (⎈) HELM SYMBOL
|
|
||||||
| 0xE2 0x8F 0x8F #4.0 [1] (⏏️) eject button
|
|
||||||
| 0xE2 0x8F 0xA9..0xB3 #6.0 [11] (⏩..⏳) fast-forward button..hou...
|
|
||||||
| 0xE2 0x8F 0xB8..0xBA #7.0 [3] (⏸️..⏺️) pause button..record b...
|
|
||||||
| 0xE2 0x93 0x82 #1.1 [1] (Ⓜ️) circled M
|
|
||||||
| 0xE2 0x96 0xAA..0xAB #1.1 [2] (▪️..▫️) black small square..wh...
|
|
||||||
| 0xE2 0x96 0xB6 #1.1 [1] (▶️) play button
|
|
||||||
| 0xE2 0x97 0x80 #1.1 [1] (◀️) reverse button
|
|
||||||
| 0xE2 0x97 0xBB..0xBE #3.2 [4] (◻️..◾) white medium square..bl...
|
|
||||||
| 0xE2 0x98 0x80..0x85 #1.1 [6] (☀️..★) sun..BLACK STAR
|
|
||||||
| 0xE2 0x98 0x87..0x92 #1.1 [12] (☇..☒) LIGHTNING..BALLOT BOX WI...
|
|
||||||
| 0xE2 0x98 0x94..0x95 #4.0 [2] (☔..☕) umbrella with rain drops...
|
|
||||||
| 0xE2 0x98 0x96..0x97 #3.2 [2] (☖..☗) WHITE SHOGI PIECE..BLACK...
|
|
||||||
| 0xE2 0x98 0x98 #4.1 [1] (☘️) shamrock
|
|
||||||
| 0xE2 0x98 0x99 #3.0 [1] (☙) REVERSED ROTATED FLORAL ...
|
|
||||||
| 0xE2 0x98 0x9A..0xFF #1.1 [86] (☚..♯) BLACK LEFT POINTING INDE...
|
|
||||||
| 0xE2 0x99 0x00..0xAF #
|
|
||||||
| 0xE2 0x99 0xB0..0xB1 #3.0 [2] (♰..♱) WEST SYRIAC CROSS..EAST ...
|
|
||||||
| 0xE2 0x99 0xB2..0xBD #3.2 [12] (♲..♽) UNIVERSAL RECYCLING SYMB...
|
|
||||||
| 0xE2 0x99 0xBE..0xBF #4.1 [2] (♾️..♿) infinity..wheelchair sy...
|
|
||||||
| 0xE2 0x9A 0x80..0x85 #3.2 [6] (⚀..⚅) DIE FACE-1..DIE FACE-6
|
|
||||||
| 0xE2 0x9A 0x90..0x91 #4.0 [2] (⚐..⚑) WHITE FLAG..BLACK FLAG
|
|
||||||
| 0xE2 0x9A 0x92..0x9C #4.1 [11] (⚒️..⚜️) hammer and pick..fleur...
|
|
||||||
| 0xE2 0x9A 0x9D #5.1 [1] (⚝) OUTLINED WHITE STAR
|
|
||||||
| 0xE2 0x9A 0x9E..0x9F #5.2 [2] (⚞..⚟) THREE LINES CONVERGING R...
|
|
||||||
| 0xE2 0x9A 0xA0..0xA1 #4.0 [2] (⚠️..⚡) warning..high voltage
|
|
||||||
| 0xE2 0x9A 0xA2..0xB1 #4.1 [16] (⚢..⚱️) DOUBLED FEMALE SIGN..fu...
|
|
||||||
| 0xE2 0x9A 0xB2 #5.0 [1] (⚲) NEUTER
|
|
||||||
| 0xE2 0x9A 0xB3..0xBC #5.1 [10] (⚳..⚼) CERES..SESQUIQUADRATE
|
|
||||||
| 0xE2 0x9A 0xBD..0xBF #5.2 [3] (⚽..⚿) soccer ball..SQUARED KEY
|
|
||||||
| 0xE2 0x9B 0x80..0x83 #5.1 [4] (⛀..⛃) WHITE DRAUGHTS MAN..BLAC...
|
|
||||||
| 0xE2 0x9B 0x84..0x8D #5.2 [10] (⛄..⛍) snowman without snow..DI...
|
|
||||||
| 0xE2 0x9B 0x8E #6.0 [1] (⛎) Ophiuchus
|
|
||||||
| 0xE2 0x9B 0x8F..0xA1 #5.2 [19] (⛏️..⛡) pick..RESTRICTED LEFT E...
|
|
||||||
| 0xE2 0x9B 0xA2 #6.0 [1] (⛢) ASTRONOMICAL SYMBOL FOR ...
|
|
||||||
| 0xE2 0x9B 0xA3 #5.2 [1] (⛣) HEAVY CIRCLE WITH STROKE...
|
|
||||||
| 0xE2 0x9B 0xA4..0xA7 #6.0 [4] (⛤..⛧) PENTAGRAM..INVERTED PENT...
|
|
||||||
| 0xE2 0x9B 0xA8..0xBF #5.2 [24] (⛨..⛿) BLACK CROSS ON SHIELD..W...
|
|
||||||
| 0xE2 0x9C 0x80 #7.0 [1] (✀) BLACK SAFETY SCISSORS
|
|
||||||
| 0xE2 0x9C 0x81..0x84 #1.1 [4] (✁..✄) UPPER BLADE SCISSORS..WH...
|
|
||||||
| 0xE2 0x9C 0x85 #6.0 [1] (✅) check mark button
|
|
||||||
| 0xE2 0x9C 0x88..0x89 #1.1 [2] (✈️..✉️) airplane..envelope
|
|
||||||
| 0xE2 0x9C 0x8A..0x8B #6.0 [2] (✊..✋) raised fist..raised hand
|
|
||||||
| 0xE2 0x9C 0x8C..0x92 #1.1 [7] (✌️..✒️) victory hand..black nib
|
|
||||||
| 0xE2 0x9C 0x94 #1.1 [1] (✔️) check mark
|
|
||||||
| 0xE2 0x9C 0x96 #1.1 [1] (✖️) multiplication sign
|
|
||||||
| 0xE2 0x9C 0x9D #1.1 [1] (✝️) latin cross
|
|
||||||
| 0xE2 0x9C 0xA1 #1.1 [1] (✡️) star of David
|
|
||||||
| 0xE2 0x9C 0xA8 #6.0 [1] (✨) sparkles
|
|
||||||
| 0xE2 0x9C 0xB3..0xB4 #1.1 [2] (✳️..✴️) eight-spoked asterisk....
|
|
||||||
| 0xE2 0x9D 0x84 #1.1 [1] (❄️) snowflake
|
|
||||||
| 0xE2 0x9D 0x87 #1.1 [1] (❇️) sparkle
|
|
||||||
| 0xE2 0x9D 0x8C #6.0 [1] (❌) cross mark
|
|
||||||
| 0xE2 0x9D 0x8E #6.0 [1] (❎) cross mark button
|
|
||||||
| 0xE2 0x9D 0x93..0x95 #6.0 [3] (❓..❕) question mark..white exc...
|
|
||||||
| 0xE2 0x9D 0x97 #5.2 [1] (❗) exclamation mark
|
|
||||||
| 0xE2 0x9D 0xA3..0xA7 #1.1 [5] (❣️..❧) heart exclamation..ROTA...
|
|
||||||
| 0xE2 0x9E 0x95..0x97 #6.0 [3] (➕..➗) plus sign..division sign
|
|
||||||
| 0xE2 0x9E 0xA1 #1.1 [1] (➡️) right arrow
|
|
||||||
| 0xE2 0x9E 0xB0 #6.0 [1] (➰) curly loop
|
|
||||||
| 0xE2 0x9E 0xBF #6.0 [1] (➿) double curly loop
|
|
||||||
| 0xE2 0xA4 0xB4..0xB5 #3.2 [2] (⤴️..⤵️) right arrow curving up...
|
|
||||||
| 0xE2 0xAC 0x85..0x87 #4.0 [3] (⬅️..⬇️) left arrow..down arrow
|
|
||||||
| 0xE2 0xAC 0x9B..0x9C #5.1 [2] (⬛..⬜) black large square..whit...
|
|
||||||
| 0xE2 0xAD 0x90 #5.1 [1] (⭐) star
|
|
||||||
| 0xE2 0xAD 0x95 #5.2 [1] (⭕) hollow red circle
|
|
||||||
| 0xE3 0x80 0xB0 #1.1 [1] (〰️) wavy dash
|
|
||||||
| 0xE3 0x80 0xBD #3.2 [1] (〽️) part alternation mark
|
|
||||||
| 0xE3 0x8A 0x97 #1.1 [1] (㊗️) Japanese “congratulatio...
|
|
||||||
| 0xE3 0x8A 0x99 #1.1 [1] (㊙️) Japanese “secret” button
|
|
||||||
| 0xF0 0x9F 0x80 0x80..0xAB #5.1 [44] (🀀..🀫) MAHJONG TILE EAST WIN...
|
|
||||||
| 0xF0 0x9F 0x80 0xAC..0xAF #NA [4] (..) <reserved-1F02C>..<res...
|
|
||||||
| 0xF0 0x9F 0x80 0xB0..0xFF #5.1[100] (🀰..🂓) DOMINO TILE HOR...
|
|
||||||
| 0xF0 0x9F 0x81..0x81 0x00..0xFF #
|
|
||||||
| 0xF0 0x9F 0x82 0x00..0x93 #
|
|
||||||
| 0xF0 0x9F 0x82 0x94..0x9F #NA [12] (..) <reserved-1F094>..<res...
|
|
||||||
| 0xF0 0x9F 0x82 0xA0..0xAE #6.0 [15] (🂠..🂮) PLAYING CARD BACK..PL...
|
|
||||||
| 0xF0 0x9F 0x82 0xAF..0xB0 #NA [2] (..) <reserved-1F0AF>..<res...
|
|
||||||
| 0xF0 0x9F 0x82 0xB1..0xBE #6.0 [14] (🂱..🂾) PLAYING CARD ACE OF H...
|
|
||||||
| 0xF0 0x9F 0x82 0xBF #7.0 [1] (🂿) PLAYING CARD RED JOKER
|
|
||||||
| 0xF0 0x9F 0x83 0x80 #NA [1] () <reserved-1F0C0>
|
|
||||||
| 0xF0 0x9F 0x83 0x81..0x8F #6.0 [15] (🃁..🃏) PLAYING CARD ACE OF D...
|
|
||||||
| 0xF0 0x9F 0x83 0x90 #NA [1] () <reserved-1F0D0>
|
|
||||||
| 0xF0 0x9F 0x83 0x91..0x9F #6.0 [15] (🃑..🃟) PLAYING CARD ACE OF C...
|
|
||||||
| 0xF0 0x9F 0x83 0xA0..0xB5 #7.0 [22] (🃠..🃵) PLAYING CARD FOOL..PL...
|
|
||||||
| 0xF0 0x9F 0x83 0xB6..0xBF #NA [10] (..) <reserved-1F0F6>..<res...
|
|
||||||
| 0xF0 0x9F 0x84 0x8D..0x8F #NA [3] (🄍..🄏) <reserved-1F10D>..<res...
|
|
||||||
| 0xF0 0x9F 0x84 0xAF #11.0 [1] (🄯) COPYLEFT SYMBOL
|
|
||||||
| 0xF0 0x9F 0x85 0xAC #12.0 [1] (🅬) RAISED MR SIGN
|
|
||||||
| 0xF0 0x9F 0x85 0xAD..0xAF #NA [3] (🅭..🅯) <reserved-1F16D>..<res...
|
|
||||||
| 0xF0 0x9F 0x85 0xB0..0xB1 #6.0 [2] (🅰️..🅱️) A button (blood typ...
|
|
||||||
| 0xF0 0x9F 0x85 0xBE #6.0 [1] (🅾️) O button (blood type)
|
|
||||||
| 0xF0 0x9F 0x85 0xBF #5.2 [1] (🅿️) P button
|
|
||||||
| 0xF0 0x9F 0x86 0x8E #6.0 [1] (🆎) AB button (blood type)
|
|
||||||
| 0xF0 0x9F 0x86 0x91..0x9A #6.0 [10] (🆑..🆚) CL button..VS button
|
|
||||||
| 0xF0 0x9F 0x86 0xAD..0xFF #NA [57] (🆭..) <reserved-1F1AD>..<res...
|
|
||||||
| 0xF0 0x9F 0x87 0x00..0xA5 #
|
|
||||||
| 0xF0 0x9F 0x88 0x81..0x82 #6.0 [2] (🈁..🈂️) Japanese “here” butt...
|
|
||||||
| 0xF0 0x9F 0x88 0x83..0x8F #NA [13] (..) <reserved-1F203>..<res...
|
|
||||||
| 0xF0 0x9F 0x88 0x9A #5.2 [1] (🈚) Japanese “free of charge...
|
|
||||||
| 0xF0 0x9F 0x88 0xAF #5.2 [1] (🈯) Japanese “reserved” button
|
|
||||||
| 0xF0 0x9F 0x88 0xB2..0xBA #6.0 [9] (🈲..🈺) Japanese “prohibited”...
|
|
||||||
| 0xF0 0x9F 0x88 0xBC..0xBF #NA [4] (..) <reserved-1F23C>..<res...
|
|
||||||
| 0xF0 0x9F 0x89 0x89..0x8F #NA [7] (..) <reserved-1F249>..<res...
|
|
||||||
| 0xF0 0x9F 0x89 0x90..0x91 #6.0 [2] (🉐..🉑) Japanese “bargain” bu...
|
|
||||||
| 0xF0 0x9F 0x89 0x92..0x9F #NA [14] (..) <reserved-1F252>..<res...
|
|
||||||
| 0xF0 0x9F 0x89 0xA0..0xA5 #10.0 [6] (🉠..🉥) ROUNDED SYMBOL FOR F...
|
|
||||||
| 0xF0 0x9F 0x89 0xA6..0xFF #NA[154] (..) <reserved-1F266>...
|
|
||||||
| 0xF0 0x9F 0x8A..0x8A 0x00..0xFF #
|
|
||||||
| 0xF0 0x9F 0x8B 0x00..0xBF #
|
|
||||||
| 0xF0 0x9F 0x8C 0x80..0xA0 #6.0 [33] (🌀..🌠) cyclone..shooting star
|
|
||||||
| 0xF0 0x9F 0x8C 0xA1..0xAC #7.0 [12] (🌡️..🌬️) thermometer..wind face
|
|
||||||
| 0xF0 0x9F 0x8C 0xAD..0xAF #8.0 [3] (🌭..🌯) hot dog..burrito
|
|
||||||
| 0xF0 0x9F 0x8C 0xB0..0xB5 #6.0 [6] (🌰..🌵) chestnut..cactus
|
|
||||||
| 0xF0 0x9F 0x8C 0xB6 #7.0 [1] (🌶️) hot pepper
|
|
||||||
| 0xF0 0x9F 0x8C 0xB7..0xFF #6.0 [70] (🌷..🍼) tulip..baby bottle
|
|
||||||
| 0xF0 0x9F 0x8D 0x00..0xBC #
|
|
||||||
| 0xF0 0x9F 0x8D 0xBD #7.0 [1] (🍽️) fork and knife with plate
|
|
||||||
| 0xF0 0x9F 0x8D 0xBE..0xBF #8.0 [2] (🍾..🍿) bottle with popping c...
|
|
||||||
| 0xF0 0x9F 0x8E 0x80..0x93 #6.0 [20] (🎀..🎓) ribbon..graduation cap
|
|
||||||
| 0xF0 0x9F 0x8E 0x94..0x9F #7.0 [12] (🎔..🎟️) HEART WITH TIP ON TH...
|
|
||||||
| 0xF0 0x9F 0x8E 0xA0..0xFF #6.0 [37] (🎠..🏄) carousel horse..perso...
|
|
||||||
| 0xF0 0x9F 0x8F 0x00..0x84 #
|
|
||||||
| 0xF0 0x9F 0x8F 0x85 #7.0 [1] (🏅) sports medal
|
|
||||||
| 0xF0 0x9F 0x8F 0x86..0x8A #6.0 [5] (🏆..🏊) trophy..person swimming
|
|
||||||
| 0xF0 0x9F 0x8F 0x8B..0x8E #7.0 [4] (🏋️..🏎️) person lifting weig...
|
|
||||||
| 0xF0 0x9F 0x8F 0x8F..0x93 #8.0 [5] (🏏..🏓) cricket game..ping pong
|
|
||||||
| 0xF0 0x9F 0x8F 0x94..0x9F #7.0 [12] (🏔️..🏟️) snow-capped mountai...
|
|
||||||
| 0xF0 0x9F 0x8F 0xA0..0xB0 #6.0 [17] (🏠..🏰) house..castle
|
|
||||||
| 0xF0 0x9F 0x8F 0xB1..0xB7 #7.0 [7] (🏱..🏷️) WHITE PENNANT..label
|
|
||||||
| 0xF0 0x9F 0x8F 0xB8..0xBA #8.0 [3] (🏸..🏺) badminton..amphora
|
|
||||||
| 0xF0 0x9F 0x90 0x80..0xBE #6.0 [63] (🐀..🐾) rat..paw prints
|
|
||||||
| 0xF0 0x9F 0x90 0xBF #7.0 [1] (🐿️) chipmunk
|
|
||||||
| 0xF0 0x9F 0x91 0x80 #6.0 [1] (👀) eyes
|
|
||||||
| 0xF0 0x9F 0x91 0x81 #7.0 [1] (👁️) eye
|
|
||||||
| 0xF0 0x9F 0x91 0x82..0xFF #6.0[182] (👂..📷) ear..camera
|
|
||||||
| 0xF0 0x9F 0x92..0x92 0x00..0xFF #
|
|
||||||
| 0xF0 0x9F 0x93 0x00..0xB7 #
|
|
||||||
| 0xF0 0x9F 0x93 0xB8 #7.0 [1] (📸) camera with flash
|
|
||||||
| 0xF0 0x9F 0x93 0xB9..0xBC #6.0 [4] (📹..📼) video camera..videoca...
|
|
||||||
| 0xF0 0x9F 0x93 0xBD..0xBE #7.0 [2] (📽️..📾) film projector..PORT...
|
|
||||||
| 0xF0 0x9F 0x93 0xBF #8.0 [1] (📿) prayer beads
|
|
||||||
| 0xF0 0x9F 0x94 0x80..0xBD #6.0 [62] (🔀..🔽) shuffle tracks button...
|
|
||||||
| 0xF0 0x9F 0x95 0x86..0x8A #7.0 [5] (🕆..🕊️) WHITE LATIN CROSS..dove
|
|
||||||
| 0xF0 0x9F 0x95 0x8B..0x8F #8.0 [5] (🕋..🕏) kaaba..BOWL OF HYGIEIA
|
|
||||||
| 0xF0 0x9F 0x95 0x90..0xA7 #6.0 [24] (🕐..🕧) one o’clock..twelve-t...
|
|
||||||
| 0xF0 0x9F 0x95 0xA8..0xB9 #7.0 [18] (🕨..🕹️) RIGHT SPEAKER..joystick
|
|
||||||
| 0xF0 0x9F 0x95 0xBA #9.0 [1] (🕺) man dancing
|
|
||||||
| 0xF0 0x9F 0x95 0xBB..0xFF #7.0 [41] (🕻..🖣) LEFT HAND TELEPHONE R...
|
|
||||||
| 0xF0 0x9F 0x96 0x00..0xA3 #
|
|
||||||
| 0xF0 0x9F 0x96 0xA4 #9.0 [1] (🖤) black heart
|
|
||||||
| 0xF0 0x9F 0x96 0xA5..0xFF #7.0 [86] (🖥️..🗺️) desktop computer..w...
|
|
||||||
| 0xF0 0x9F 0x97 0x00..0xBA #
|
|
||||||
| 0xF0 0x9F 0x97 0xBB..0xBF #6.0 [5] (🗻..🗿) mount fuji..moai
|
|
||||||
| 0xF0 0x9F 0x98 0x80 #6.1 [1] (😀) grinning face
|
|
||||||
| 0xF0 0x9F 0x98 0x81..0x90 #6.0 [16] (😁..😐) beaming face with smi...
|
|
||||||
| 0xF0 0x9F 0x98 0x91 #6.1 [1] (😑) expressionless face
|
|
||||||
| 0xF0 0x9F 0x98 0x92..0x94 #6.0 [3] (😒..😔) unamused face..pensiv...
|
|
||||||
| 0xF0 0x9F 0x98 0x95 #6.1 [1] (😕) confused face
|
|
||||||
| 0xF0 0x9F 0x98 0x96 #6.0 [1] (😖) confounded face
|
|
||||||
| 0xF0 0x9F 0x98 0x97 #6.1 [1] (😗) kissing face
|
|
||||||
| 0xF0 0x9F 0x98 0x98 #6.0 [1] (😘) face blowing a kiss
|
|
||||||
| 0xF0 0x9F 0x98 0x99 #6.1 [1] (😙) kissing face with smilin...
|
|
||||||
| 0xF0 0x9F 0x98 0x9A #6.0 [1] (😚) kissing face with closed...
|
|
||||||
| 0xF0 0x9F 0x98 0x9B #6.1 [1] (😛) face with tongue
|
|
||||||
| 0xF0 0x9F 0x98 0x9C..0x9E #6.0 [3] (😜..😞) winking face with ton...
|
|
||||||
| 0xF0 0x9F 0x98 0x9F #6.1 [1] (😟) worried face
|
|
||||||
| 0xF0 0x9F 0x98 0xA0..0xA5 #6.0 [6] (😠..😥) angry face..sad but r...
|
|
||||||
| 0xF0 0x9F 0x98 0xA6..0xA7 #6.1 [2] (😦..😧) frowning face with op...
|
|
||||||
| 0xF0 0x9F 0x98 0xA8..0xAB #6.0 [4] (😨..😫) fearful face..tired face
|
|
||||||
| 0xF0 0x9F 0x98 0xAC #6.1 [1] (😬) grimacing face
|
|
||||||
| 0xF0 0x9F 0x98 0xAD #6.0 [1] (😭) loudly crying face
|
|
||||||
| 0xF0 0x9F 0x98 0xAE..0xAF #6.1 [2] (😮..😯) face with open mouth....
|
|
||||||
| 0xF0 0x9F 0x98 0xB0..0xB3 #6.0 [4] (😰..😳) anxious face with swe...
|
|
||||||
| 0xF0 0x9F 0x98 0xB4 #6.1 [1] (😴) sleeping face
|
|
||||||
| 0xF0 0x9F 0x98 0xB5..0xFF #6.0 [12] (😵..🙀) dizzy face..weary cat
|
|
||||||
| 0xF0 0x9F 0x99 0x00..0x80 #
|
|
||||||
| 0xF0 0x9F 0x99 0x81..0x82 #7.0 [2] (🙁..🙂) slightly frowning fac...
|
|
||||||
| 0xF0 0x9F 0x99 0x83..0x84 #8.0 [2] (🙃..🙄) upside-down face..fac...
|
|
||||||
| 0xF0 0x9F 0x99 0x85..0x8F #6.0 [11] (🙅..🙏) person gesturing NO.....
|
|
||||||
| 0xF0 0x9F 0x9A 0x80..0xFF #6.0 [70] (🚀..🛅) rocket..left luggage
|
|
||||||
| 0xF0 0x9F 0x9B 0x00..0x85 #
|
|
||||||
| 0xF0 0x9F 0x9B 0x86..0x8F #7.0 [10] (🛆..🛏️) TRIANGLE WITH ROUNDE...
|
|
||||||
| 0xF0 0x9F 0x9B 0x90 #8.0 [1] (🛐) place of worship
|
|
||||||
| 0xF0 0x9F 0x9B 0x91..0x92 #9.0 [2] (🛑..🛒) stop sign..shopping cart
|
|
||||||
| 0xF0 0x9F 0x9B 0x93..0x94 #10.0 [2] (🛓..🛔) STUPA..PAGODA
|
|
||||||
| 0xF0 0x9F 0x9B 0x95 #12.0 [1] (🛕) hindu temple
|
|
||||||
| 0xF0 0x9F 0x9B 0x96..0x9F #NA [10] (🛖..🛟) <reserved-1F6D6>..<res...
|
|
||||||
| 0xF0 0x9F 0x9B 0xA0..0xAC #7.0 [13] (🛠️..🛬) hammer and wrench..a...
|
|
||||||
| 0xF0 0x9F 0x9B 0xAD..0xAF #NA [3] (..) <reserved-1F6ED>..<res...
|
|
||||||
| 0xF0 0x9F 0x9B 0xB0..0xB3 #7.0 [4] (🛰️..🛳️) satellite..passenge...
|
|
||||||
| 0xF0 0x9F 0x9B 0xB4..0xB6 #9.0 [3] (🛴..🛶) kick scooter..canoe
|
|
||||||
| 0xF0 0x9F 0x9B 0xB7..0xB8 #10.0 [2] (🛷..🛸) sled..flying saucer
|
|
||||||
| 0xF0 0x9F 0x9B 0xB9 #11.0 [1] (🛹) skateboard
|
|
||||||
| 0xF0 0x9F 0x9B 0xBA #12.0 [1] (🛺) auto rickshaw
|
|
||||||
| 0xF0 0x9F 0x9B 0xBB..0xBF #NA [5] (🛻..) <reserved-1F6FB>..<res...
|
|
||||||
| 0xF0 0x9F 0x9D 0xB4..0xBF #NA [12] (🝴..🝿) <reserved-1F774>..<res...
|
|
||||||
| 0xF0 0x9F 0x9F 0x95..0x98 #11.0 [4] (🟕..🟘) CIRCLED TRIANGLE..NE...
|
|
||||||
| 0xF0 0x9F 0x9F 0x99..0x9F #NA [7] (🟙..) <reserved-1F7D9>..<res...
|
|
||||||
| 0xF0 0x9F 0x9F 0xA0..0xAB #12.0 [12] (🟠..🟫) orange circle..brown...
|
|
||||||
| 0xF0 0x9F 0x9F 0xAC..0xBF #NA [20] (..) <reserved-1F7EC>..<res...
|
|
||||||
| 0xF0 0x9F 0xA0 0x8C..0x8F #NA [4] (..) <reserved-1F80C>..<res...
|
|
||||||
| 0xF0 0x9F 0xA1 0x88..0x8F #NA [8] (..) <reserved-1F848>..<res...
|
|
||||||
| 0xF0 0x9F 0xA1 0x9A..0x9F #NA [6] (..) <reserved-1F85A>..<res...
|
|
||||||
| 0xF0 0x9F 0xA2 0x88..0x8F #NA [8] (..) <reserved-1F888>..<res...
|
|
||||||
| 0xF0 0x9F 0xA2 0xAE..0xFF #NA [82] (..) <reserved-1F8AE>..<res...
|
|
||||||
| 0xF0 0x9F 0xA3 0x00..0xBF #
|
|
||||||
| 0xF0 0x9F 0xA4 0x8C #NA [1] (🤌) <reserved-1F90C>
|
|
||||||
| 0xF0 0x9F 0xA4 0x8D..0x8F #12.0 [3] (🤍..🤏) white heart..pinchin...
|
|
||||||
| 0xF0 0x9F 0xA4 0x90..0x98 #8.0 [9] (🤐..🤘) zipper-mouth face..si...
|
|
||||||
| 0xF0 0x9F 0xA4 0x99..0x9E #9.0 [6] (🤙..🤞) call me hand..crossed...
|
|
||||||
| 0xF0 0x9F 0xA4 0x9F #10.0 [1] (🤟) love-you gesture
|
|
||||||
| 0xF0 0x9F 0xA4 0xA0..0xA7 #9.0 [8] (🤠..🤧) cowboy hat face..snee...
|
|
||||||
| 0xF0 0x9F 0xA4 0xA8..0xAF #10.0 [8] (🤨..🤯) face with raised eye...
|
|
||||||
| 0xF0 0x9F 0xA4 0xB0 #9.0 [1] (🤰) pregnant woman
|
|
||||||
| 0xF0 0x9F 0xA4 0xB1..0xB2 #10.0 [2] (🤱..🤲) breast-feeding..palm...
|
|
||||||
| 0xF0 0x9F 0xA4 0xB3..0xBA #9.0 [8] (🤳..🤺) selfie..person fencing
|
|
||||||
| 0xF0 0x9F 0xA4 0xBC..0xBE #9.0 [3] (🤼..🤾) people wrestling..per...
|
|
||||||
| 0xF0 0x9F 0xA4 0xBF #12.0 [1] (🤿) diving mask
|
|
||||||
| 0xF0 0x9F 0xA5 0x80..0x85 #9.0 [6] (🥀..🥅) wilted flower..goal net
|
|
||||||
| 0xF0 0x9F 0xA5 0x87..0x8B #9.0 [5] (🥇..🥋) 1st place medal..mart...
|
|
||||||
| 0xF0 0x9F 0xA5 0x8C #10.0 [1] (🥌) curling stone
|
|
||||||
| 0xF0 0x9F 0xA5 0x8D..0x8F #11.0 [3] (🥍..🥏) lacrosse..flying disc
|
|
||||||
| 0xF0 0x9F 0xA5 0x90..0x9E #9.0 [15] (🥐..🥞) croissant..pancakes
|
|
||||||
| 0xF0 0x9F 0xA5 0x9F..0xAB #10.0 [13] (🥟..🥫) dumpling..canned food
|
|
||||||
| 0xF0 0x9F 0xA5 0xAC..0xB0 #11.0 [5] (🥬..🥰) leafy green..smiling...
|
|
||||||
| 0xF0 0x9F 0xA5 0xB1 #12.0 [1] (🥱) yawning face
|
|
||||||
| 0xF0 0x9F 0xA5 0xB2 #NA [1] (🥲) <reserved-1F972>
|
|
||||||
| 0xF0 0x9F 0xA5 0xB3..0xB6 #11.0 [4] (🥳..🥶) partying face..cold ...
|
|
||||||
| 0xF0 0x9F 0xA5 0xB7..0xB9 #NA [3] (🥷..🥹) <reserved-1F977>..<res...
|
|
||||||
| 0xF0 0x9F 0xA5 0xBA #11.0 [1] (🥺) pleading face
|
|
||||||
| 0xF0 0x9F 0xA5 0xBB #12.0 [1] (🥻) sari
|
|
||||||
| 0xF0 0x9F 0xA5 0xBC..0xBF #11.0 [4] (🥼..🥿) lab coat..flat shoe
|
|
||||||
| 0xF0 0x9F 0xA6 0x80..0x84 #8.0 [5] (🦀..🦄) crab..unicorn
|
|
||||||
| 0xF0 0x9F 0xA6 0x85..0x91 #9.0 [13] (🦅..🦑) eagle..squid
|
|
||||||
| 0xF0 0x9F 0xA6 0x92..0x97 #10.0 [6] (🦒..🦗) giraffe..cricket
|
|
||||||
| 0xF0 0x9F 0xA6 0x98..0xA2 #11.0 [11] (🦘..🦢) kangaroo..swan
|
|
||||||
| 0xF0 0x9F 0xA6 0xA3..0xA4 #NA [2] (🦣..🦤) <reserved-1F9A3>..<res...
|
|
||||||
| 0xF0 0x9F 0xA6 0xA5..0xAA #12.0 [6] (🦥..🦪) sloth..oyster
|
|
||||||
| 0xF0 0x9F 0xA6 0xAB..0xAD #NA [3] (🦫..🦭) <reserved-1F9AB>..<res...
|
|
||||||
| 0xF0 0x9F 0xA6 0xAE..0xAF #12.0 [2] (🦮..🦯) guide dog..probing cane
|
|
||||||
| 0xF0 0x9F 0xA6 0xB0..0xB9 #11.0 [10] (🦰..🦹) red hair..supervillain
|
|
||||||
| 0xF0 0x9F 0xA6 0xBA..0xBF #12.0 [6] (🦺..🦿) safety vest..mechani...
|
|
||||||
| 0xF0 0x9F 0xA7 0x80 #8.0 [1] (🧀) cheese wedge
|
|
||||||
| 0xF0 0x9F 0xA7 0x81..0x82 #11.0 [2] (🧁..🧂) cupcake..salt
|
|
||||||
| 0xF0 0x9F 0xA7 0x83..0x8A #12.0 [8] (🧃..🧊) beverage box..ice cube
|
|
||||||
| 0xF0 0x9F 0xA7 0x8B..0x8C #NA [2] (🧋..🧌) <reserved-1F9CB>..<res...
|
|
||||||
| 0xF0 0x9F 0xA7 0x8D..0x8F #12.0 [3] (🧍..🧏) person standing..dea...
|
|
||||||
| 0xF0 0x9F 0xA7 0x90..0xA6 #10.0 [23] (🧐..🧦) face with monocle..s...
|
|
||||||
| 0xF0 0x9F 0xA7 0xA7..0xBF #11.0 [25] (🧧..🧿) red envelope..nazar ...
|
|
||||||
| 0xF0 0x9F 0xA8 0x80..0xFF #12.0 [84] (🨀..🩓) NEUTRAL CHESS KING.....
|
|
||||||
| 0xF0 0x9F 0xA9 0x00..0x93 #
|
|
||||||
| 0xF0 0x9F 0xA9 0x94..0x9F #NA [12] (..) <reserved-1FA54>..<res...
|
|
||||||
| 0xF0 0x9F 0xA9 0xA0..0xAD #11.0 [14] (🩠..🩭) XIANGQI RED GENERAL....
|
|
||||||
| 0xF0 0x9F 0xA9 0xAE..0xAF #NA [2] (..) <reserved-1FA6E>..<res...
|
|
||||||
| 0xF0 0x9F 0xA9 0xB0..0xB3 #12.0 [4] (🩰..🩳) ballet shoes..shorts
|
|
||||||
| 0xF0 0x9F 0xA9 0xB4..0xB7 #NA [4] (🩴..🩷) <reserved-1FA74>..<res...
|
|
||||||
| 0xF0 0x9F 0xA9 0xB8..0xBA #12.0 [3] (🩸..🩺) drop of blood..steth...
|
|
||||||
| 0xF0 0x9F 0xA9 0xBB..0xBF #NA [5] (🩻..) <reserved-1FA7B>..<res...
|
|
||||||
| 0xF0 0x9F 0xAA 0x80..0x82 #12.0 [3] (🪀..🪂) yo-yo..parachute
|
|
||||||
| 0xF0 0x9F 0xAA 0x83..0x8F #NA [13] (🪃..) <reserved-1FA83>..<res...
|
|
||||||
| 0xF0 0x9F 0xAA 0x90..0x95 #12.0 [6] (🪐..🪕) ringed planet..banjo
|
|
||||||
| 0xF0 0x9F 0xAA 0x96..0xFF #NA[1384] (🪖..) <reserved-1FA96>...
|
|
||||||
| 0xF0 0x9F 0xAB..0xBE 0x00..0xFF #
|
|
||||||
| 0xF0 0x9F 0xBF 0x00..0xBD #
|
|
||||||
;
|
|
||||||
|
|
||||||
}%%
|
|
||||||
-4078
File diff suppressed because it is too large
Load Diff
Generated
Vendored
Generated
Vendored
+525
@@ -0,0 +1,525 @@
|
|||||||
|
# The following Ragel file was autogenerated with unicode2ragel.rb
|
||||||
|
# from: https://www.unicode.org/Public/13.0.0/ucd/emoji/emoji-data.txt
|
||||||
|
#
|
||||||
|
# It defines ["Extended_Pictographic"].
|
||||||
|
#
|
||||||
|
# To use this, make sure that your alphtype is set to byte,
|
||||||
|
# and that your input is in utf8.
|
||||||
|
|
||||||
|
%%{
|
||||||
|
machine Emoji;
|
||||||
|
|
||||||
|
Extended_Pictographic =
|
||||||
|
0xC2 0xA9 #E0.6 [1] (©️) copyright
|
||||||
|
| 0xC2 0xAE #E0.6 [1] (®️) registered
|
||||||
|
| 0xE2 0x80 0xBC #E0.6 [1] (‼️) double exclamation mark
|
||||||
|
| 0xE2 0x81 0x89 #E0.6 [1] (⁉️) exclamation question ...
|
||||||
|
| 0xE2 0x84 0xA2 #E0.6 [1] (™️) trade mark
|
||||||
|
| 0xE2 0x84 0xB9 #E0.6 [1] (ℹ️) information
|
||||||
|
| 0xE2 0x86 0x94..0x99 #E0.6 [6] (↔️..↙️) left-right arrow..do...
|
||||||
|
| 0xE2 0x86 0xA9..0xAA #E0.6 [2] (↩️..↪️) right arrow curving ...
|
||||||
|
| 0xE2 0x8C 0x9A..0x9B #E0.6 [2] (⌚..⌛) watch..hourglass done
|
||||||
|
| 0xE2 0x8C 0xA8 #E1.0 [1] (⌨️) keyboard
|
||||||
|
| 0xE2 0x8E 0x88 #E0.0 [1] (⎈) HELM SYMBOL
|
||||||
|
| 0xE2 0x8F 0x8F #E1.0 [1] (⏏️) eject button
|
||||||
|
| 0xE2 0x8F 0xA9..0xAC #E0.6 [4] (⏩..⏬) fast-forward button..f...
|
||||||
|
| 0xE2 0x8F 0xAD..0xAE #E0.7 [2] (⏭️..⏮️) next track button..l...
|
||||||
|
| 0xE2 0x8F 0xAF #E1.0 [1] (⏯️) play or pause button
|
||||||
|
| 0xE2 0x8F 0xB0 #E0.6 [1] (⏰) alarm clock
|
||||||
|
| 0xE2 0x8F 0xB1..0xB2 #E1.0 [2] (⏱️..⏲️) stopwatch..timer clock
|
||||||
|
| 0xE2 0x8F 0xB3 #E0.6 [1] (⏳) hourglass not done
|
||||||
|
| 0xE2 0x8F 0xB8..0xBA #E0.7 [3] (⏸️..⏺️) pause button..record...
|
||||||
|
| 0xE2 0x93 0x82 #E0.6 [1] (Ⓜ️) circled M
|
||||||
|
| 0xE2 0x96 0xAA..0xAB #E0.6 [2] (▪️..▫️) black small square.....
|
||||||
|
| 0xE2 0x96 0xB6 #E0.6 [1] (▶️) play button
|
||||||
|
| 0xE2 0x97 0x80 #E0.6 [1] (◀️) reverse button
|
||||||
|
| 0xE2 0x97 0xBB..0xBE #E0.6 [4] (◻️..◾) white medium square.....
|
||||||
|
| 0xE2 0x98 0x80..0x81 #E0.6 [2] (☀️..☁️) sun..cloud
|
||||||
|
| 0xE2 0x98 0x82..0x83 #E0.7 [2] (☂️..☃️) umbrella..snowman
|
||||||
|
| 0xE2 0x98 0x84 #E1.0 [1] (☄️) comet
|
||||||
|
| 0xE2 0x98 0x85 #E0.0 [1] (★) BLACK STAR
|
||||||
|
| 0xE2 0x98 0x87..0x8D #E0.0 [7] (☇..☍) LIGHTNING..OPPOSITION
|
||||||
|
| 0xE2 0x98 0x8E #E0.6 [1] (☎️) telephone
|
||||||
|
| 0xE2 0x98 0x8F..0x90 #E0.0 [2] (☏..☐) WHITE TELEPHONE..BALLO...
|
||||||
|
| 0xE2 0x98 0x91 #E0.6 [1] (☑️) check box with check
|
||||||
|
| 0xE2 0x98 0x92 #E0.0 [1] (☒) BALLOT BOX WITH X
|
||||||
|
| 0xE2 0x98 0x94..0x95 #E0.6 [2] (☔..☕) umbrella with rain dro...
|
||||||
|
| 0xE2 0x98 0x96..0x97 #E0.0 [2] (☖..☗) WHITE SHOGI PIECE..BLA...
|
||||||
|
| 0xE2 0x98 0x98 #E1.0 [1] (☘️) shamrock
|
||||||
|
| 0xE2 0x98 0x99..0x9C #E0.0 [4] (☙..☜) REVERSED ROTATED FLORA...
|
||||||
|
| 0xE2 0x98 0x9D #E0.6 [1] (☝️) index pointing up
|
||||||
|
| 0xE2 0x98 0x9E..0x9F #E0.0 [2] (☞..☟) WHITE RIGHT POINTING I...
|
||||||
|
| 0xE2 0x98 0xA0 #E1.0 [1] (☠️) skull and crossbones
|
||||||
|
| 0xE2 0x98 0xA1 #E0.0 [1] (☡) CAUTION SIGN
|
||||||
|
| 0xE2 0x98 0xA2..0xA3 #E1.0 [2] (☢️..☣️) radioactive..biohazard
|
||||||
|
| 0xE2 0x98 0xA4..0xA5 #E0.0 [2] (☤..☥) CADUCEUS..ANKH
|
||||||
|
| 0xE2 0x98 0xA6 #E1.0 [1] (☦️) orthodox cross
|
||||||
|
| 0xE2 0x98 0xA7..0xA9 #E0.0 [3] (☧..☩) CHI RHO..CROSS OF JERU...
|
||||||
|
| 0xE2 0x98 0xAA #E0.7 [1] (☪️) star and crescent
|
||||||
|
| 0xE2 0x98 0xAB..0xAD #E0.0 [3] (☫..☭) FARSI SYMBOL..HAMMER A...
|
||||||
|
| 0xE2 0x98 0xAE #E1.0 [1] (☮️) peace symbol
|
||||||
|
| 0xE2 0x98 0xAF #E0.7 [1] (☯️) yin yang
|
||||||
|
| 0xE2 0x98 0xB0..0xB7 #E0.0 [8] (☰..☷) TRIGRAM FOR HEAVEN..TR...
|
||||||
|
| 0xE2 0x98 0xB8..0xB9 #E0.7 [2] (☸️..☹️) wheel of dharma..fro...
|
||||||
|
| 0xE2 0x98 0xBA #E0.6 [1] (☺️) smiling face
|
||||||
|
| 0xE2 0x98 0xBB..0xBF #E0.0 [5] (☻..☿) BLACK SMILING FACE..ME...
|
||||||
|
| 0xE2 0x99 0x80 #E4.0 [1] (♀️) female sign
|
||||||
|
| 0xE2 0x99 0x81 #E0.0 [1] (♁) EARTH
|
||||||
|
| 0xE2 0x99 0x82 #E4.0 [1] (♂️) male sign
|
||||||
|
| 0xE2 0x99 0x83..0x87 #E0.0 [5] (♃..♇) JUPITER..PLUTO
|
||||||
|
| 0xE2 0x99 0x88..0x93 #E0.6 [12] (♈..♓) Aries..Pisces
|
||||||
|
| 0xE2 0x99 0x94..0x9E #E0.0 [11] (♔..♞) WHITE CHESS KING..BLAC...
|
||||||
|
| 0xE2 0x99 0x9F #E11.0 [1] (♟️) chess pawn
|
||||||
|
| 0xE2 0x99 0xA0 #E0.6 [1] (♠️) spade suit
|
||||||
|
| 0xE2 0x99 0xA1..0xA2 #E0.0 [2] (♡..♢) WHITE HEART SUIT..WHIT...
|
||||||
|
| 0xE2 0x99 0xA3 #E0.6 [1] (♣️) club suit
|
||||||
|
| 0xE2 0x99 0xA4 #E0.0 [1] (♤) WHITE SPADE SUIT
|
||||||
|
| 0xE2 0x99 0xA5..0xA6 #E0.6 [2] (♥️..♦️) heart suit..diamond ...
|
||||||
|
| 0xE2 0x99 0xA7 #E0.0 [1] (♧) WHITE CLUB SUIT
|
||||||
|
| 0xE2 0x99 0xA8 #E0.6 [1] (♨️) hot springs
|
||||||
|
| 0xE2 0x99 0xA9..0xBA #E0.0 [18] (♩..♺) QUARTER NOTE..RECYCLIN...
|
||||||
|
| 0xE2 0x99 0xBB #E0.6 [1] (♻️) recycling symbol
|
||||||
|
| 0xE2 0x99 0xBC..0xBD #E0.0 [2] (♼..♽) RECYCLED PAPER SYMBOL....
|
||||||
|
| 0xE2 0x99 0xBE #E11.0 [1] (♾️) infinity
|
||||||
|
| 0xE2 0x99 0xBF #E0.6 [1] (♿) wheelchair symbol
|
||||||
|
| 0xE2 0x9A 0x80..0x85 #E0.0 [6] (⚀..⚅) DIE FACE-1..DIE FACE-6
|
||||||
|
| 0xE2 0x9A 0x90..0x91 #E0.0 [2] (⚐..⚑) WHITE FLAG..BLACK FLAG
|
||||||
|
| 0xE2 0x9A 0x92 #E1.0 [1] (⚒️) hammer and pick
|
||||||
|
| 0xE2 0x9A 0x93 #E0.6 [1] (⚓) anchor
|
||||||
|
| 0xE2 0x9A 0x94 #E1.0 [1] (⚔️) crossed swords
|
||||||
|
| 0xE2 0x9A 0x95 #E4.0 [1] (⚕️) medical symbol
|
||||||
|
| 0xE2 0x9A 0x96..0x97 #E1.0 [2] (⚖️..⚗️) balance scale..alembic
|
||||||
|
| 0xE2 0x9A 0x98 #E0.0 [1] (⚘) FLOWER
|
||||||
|
| 0xE2 0x9A 0x99 #E1.0 [1] (⚙️) gear
|
||||||
|
| 0xE2 0x9A 0x9A #E0.0 [1] (⚚) STAFF OF HERMES
|
||||||
|
| 0xE2 0x9A 0x9B..0x9C #E1.0 [2] (⚛️..⚜️) atom symbol..fleur-d...
|
||||||
|
| 0xE2 0x9A 0x9D..0x9F #E0.0 [3] (⚝..⚟) OUTLINED WHITE STAR..T...
|
||||||
|
| 0xE2 0x9A 0xA0..0xA1 #E0.6 [2] (⚠️..⚡) warning..high voltage
|
||||||
|
| 0xE2 0x9A 0xA2..0xA6 #E0.0 [5] (⚢..⚦) DOUBLED FEMALE SIGN..M...
|
||||||
|
| 0xE2 0x9A 0xA7 #E13.0 [1] (⚧️) transgender symbol
|
||||||
|
| 0xE2 0x9A 0xA8..0xA9 #E0.0 [2] (⚨..⚩) VERTICAL MALE WITH STR...
|
||||||
|
| 0xE2 0x9A 0xAA..0xAB #E0.6 [2] (⚪..⚫) white circle..black ci...
|
||||||
|
| 0xE2 0x9A 0xAC..0xAF #E0.0 [4] (⚬..⚯) MEDIUM SMALL WHITE CIR...
|
||||||
|
| 0xE2 0x9A 0xB0..0xB1 #E1.0 [2] (⚰️..⚱️) coffin..funeral urn
|
||||||
|
| 0xE2 0x9A 0xB2..0xBC #E0.0 [11] (⚲..⚼) NEUTER..SESQUIQUADRATE
|
||||||
|
| 0xE2 0x9A 0xBD..0xBE #E0.6 [2] (⚽..⚾) soccer ball..baseball
|
||||||
|
| 0xE2 0x9A 0xBF..0xFF #E0.0 [5] (⚿..⛃) SQUARED KEY..BLACK DRA...
|
||||||
|
| 0xE2 0x9B 0x00..0x83 #
|
||||||
|
| 0xE2 0x9B 0x84..0x85 #E0.6 [2] (⛄..⛅) snowman without snow.....
|
||||||
|
| 0xE2 0x9B 0x86..0x87 #E0.0 [2] (⛆..⛇) RAIN..BLACK SNOWMAN
|
||||||
|
| 0xE2 0x9B 0x88 #E0.7 [1] (⛈️) cloud with lightning ...
|
||||||
|
| 0xE2 0x9B 0x89..0x8D #E0.0 [5] (⛉..⛍) TURNED WHITE SHOGI PIE...
|
||||||
|
| 0xE2 0x9B 0x8E #E0.6 [1] (⛎) Ophiuchus
|
||||||
|
| 0xE2 0x9B 0x8F #E0.7 [1] (⛏️) pick
|
||||||
|
| 0xE2 0x9B 0x90 #E0.0 [1] (⛐) CAR SLIDING
|
||||||
|
| 0xE2 0x9B 0x91 #E0.7 [1] (⛑️) rescue worker’s helmet
|
||||||
|
| 0xE2 0x9B 0x92 #E0.0 [1] (⛒) CIRCLED CROSSING LANES
|
||||||
|
| 0xE2 0x9B 0x93 #E0.7 [1] (⛓️) chains
|
||||||
|
| 0xE2 0x9B 0x94 #E0.6 [1] (⛔) no entry
|
||||||
|
| 0xE2 0x9B 0x95..0xA8 #E0.0 [20] (⛕..⛨) ALTERNATE ONE-WAY LEFT...
|
||||||
|
| 0xE2 0x9B 0xA9 #E0.7 [1] (⛩️) shinto shrine
|
||||||
|
| 0xE2 0x9B 0xAA #E0.6 [1] (⛪) church
|
||||||
|
| 0xE2 0x9B 0xAB..0xAF #E0.0 [5] (⛫..⛯) CASTLE..MAP SYMBOL FOR...
|
||||||
|
| 0xE2 0x9B 0xB0..0xB1 #E0.7 [2] (⛰️..⛱️) mountain..umbrella o...
|
||||||
|
| 0xE2 0x9B 0xB2..0xB3 #E0.6 [2] (⛲..⛳) fountain..flag in hole
|
||||||
|
| 0xE2 0x9B 0xB4 #E0.7 [1] (⛴️) ferry
|
||||||
|
| 0xE2 0x9B 0xB5 #E0.6 [1] (⛵) sailboat
|
||||||
|
| 0xE2 0x9B 0xB6 #E0.0 [1] (⛶) SQUARE FOUR CORNERS
|
||||||
|
| 0xE2 0x9B 0xB7..0xB9 #E0.7 [3] (⛷️..⛹️) skier..person bounci...
|
||||||
|
| 0xE2 0x9B 0xBA #E0.6 [1] (⛺) tent
|
||||||
|
| 0xE2 0x9B 0xBB..0xBC #E0.0 [2] (⛻..⛼) JAPANESE BANK SYMBOL.....
|
||||||
|
| 0xE2 0x9B 0xBD #E0.6 [1] (⛽) fuel pump
|
||||||
|
| 0xE2 0x9B 0xBE..0xFF #E0.0 [4] (⛾..✁) CUP ON BLACK SQUARE..U...
|
||||||
|
| 0xE2 0x9C 0x00..0x81 #
|
||||||
|
| 0xE2 0x9C 0x82 #E0.6 [1] (✂️) scissors
|
||||||
|
| 0xE2 0x9C 0x83..0x84 #E0.0 [2] (✃..✄) LOWER BLADE SCISSORS.....
|
||||||
|
| 0xE2 0x9C 0x85 #E0.6 [1] (✅) check mark button
|
||||||
|
| 0xE2 0x9C 0x88..0x8C #E0.6 [5] (✈️..✌️) airplane..victory hand
|
||||||
|
| 0xE2 0x9C 0x8D #E0.7 [1] (✍️) writing hand
|
||||||
|
| 0xE2 0x9C 0x8E #E0.0 [1] (✎) LOWER RIGHT PENCIL
|
||||||
|
| 0xE2 0x9C 0x8F #E0.6 [1] (✏️) pencil
|
||||||
|
| 0xE2 0x9C 0x90..0x91 #E0.0 [2] (✐..✑) UPPER RIGHT PENCIL..WH...
|
||||||
|
| 0xE2 0x9C 0x92 #E0.6 [1] (✒️) black nib
|
||||||
|
| 0xE2 0x9C 0x94 #E0.6 [1] (✔️) check mark
|
||||||
|
| 0xE2 0x9C 0x96 #E0.6 [1] (✖️) multiply
|
||||||
|
| 0xE2 0x9C 0x9D #E0.7 [1] (✝️) latin cross
|
||||||
|
| 0xE2 0x9C 0xA1 #E0.7 [1] (✡️) star of David
|
||||||
|
| 0xE2 0x9C 0xA8 #E0.6 [1] (✨) sparkles
|
||||||
|
| 0xE2 0x9C 0xB3..0xB4 #E0.6 [2] (✳️..✴️) eight-spoked asteris...
|
||||||
|
| 0xE2 0x9D 0x84 #E0.6 [1] (❄️) snowflake
|
||||||
|
| 0xE2 0x9D 0x87 #E0.6 [1] (❇️) sparkle
|
||||||
|
| 0xE2 0x9D 0x8C #E0.6 [1] (❌) cross mark
|
||||||
|
| 0xE2 0x9D 0x8E #E0.6 [1] (❎) cross mark button
|
||||||
|
| 0xE2 0x9D 0x93..0x95 #E0.6 [3] (❓..❕) question mark..white e...
|
||||||
|
| 0xE2 0x9D 0x97 #E0.6 [1] (❗) exclamation mark
|
||||||
|
| 0xE2 0x9D 0xA3 #E1.0 [1] (❣️) heart exclamation
|
||||||
|
| 0xE2 0x9D 0xA4 #E0.6 [1] (❤️) red heart
|
||||||
|
| 0xE2 0x9D 0xA5..0xA7 #E0.0 [3] (❥..❧) ROTATED HEAVY BLACK HE...
|
||||||
|
| 0xE2 0x9E 0x95..0x97 #E0.6 [3] (➕..➗) plus..divide
|
||||||
|
| 0xE2 0x9E 0xA1 #E0.6 [1] (➡️) right arrow
|
||||||
|
| 0xE2 0x9E 0xB0 #E0.6 [1] (➰) curly loop
|
||||||
|
| 0xE2 0x9E 0xBF #E1.0 [1] (➿) double curly loop
|
||||||
|
| 0xE2 0xA4 0xB4..0xB5 #E0.6 [2] (⤴️..⤵️) right arrow curving ...
|
||||||
|
| 0xE2 0xAC 0x85..0x87 #E0.6 [3] (⬅️..⬇️) left arrow..down arrow
|
||||||
|
| 0xE2 0xAC 0x9B..0x9C #E0.6 [2] (⬛..⬜) black large square..wh...
|
||||||
|
| 0xE2 0xAD 0x90 #E0.6 [1] (⭐) star
|
||||||
|
| 0xE2 0xAD 0x95 #E0.6 [1] (⭕) hollow red circle
|
||||||
|
| 0xE3 0x80 0xB0 #E0.6 [1] (〰️) wavy dash
|
||||||
|
| 0xE3 0x80 0xBD #E0.6 [1] (〽️) part alternation mark
|
||||||
|
| 0xE3 0x8A 0x97 #E0.6 [1] (㊗️) Japanese “congratulat...
|
||||||
|
| 0xE3 0x8A 0x99 #E0.6 [1] (㊙️) Japanese “secret” button
|
||||||
|
| 0xF0 0x9F 0x80 0x80..0x83 #E0.0 [4] (🀀..🀃) MAHJONG TILE EAST W...
|
||||||
|
| 0xF0 0x9F 0x80 0x84 #E0.6 [1] (🀄) mahjong red dragon
|
||||||
|
| 0xF0 0x9F 0x80 0x85..0xFF #E0.0 [202] (🀅..🃎) MAHJONG TILE ...
|
||||||
|
| 0xF0 0x9F 0x81..0x82 0x00..0xFF #
|
||||||
|
| 0xF0 0x9F 0x83 0x00..0x8E #
|
||||||
|
| 0xF0 0x9F 0x83 0x8F #E0.6 [1] (🃏) joker
|
||||||
|
| 0xF0 0x9F 0x83 0x90..0xBF #E0.0 [48] (..) <reserved-1F0D0>..<...
|
||||||
|
| 0xF0 0x9F 0x84 0x8D..0x8F #E0.0 [3] (🄍..🄏) CIRCLED ZERO WITH S...
|
||||||
|
| 0xF0 0x9F 0x84 0xAF #E0.0 [1] (🄯) COPYLEFT SYMBOL
|
||||||
|
| 0xF0 0x9F 0x85 0xAC..0xAF #E0.0 [4] (🅬..🅯) RAISED MR SIGN..CIR...
|
||||||
|
| 0xF0 0x9F 0x85 0xB0..0xB1 #E0.6 [2] (🅰️..🅱️) A button (blood t...
|
||||||
|
| 0xF0 0x9F 0x85 0xBE..0xBF #E0.6 [2] (🅾️..🅿️) O button (blood t...
|
||||||
|
| 0xF0 0x9F 0x86 0x8E #E0.6 [1] (🆎) AB button (blood type)
|
||||||
|
| 0xF0 0x9F 0x86 0x91..0x9A #E0.6 [10] (🆑..🆚) CL button..VS button
|
||||||
|
| 0xF0 0x9F 0x86 0xAD..0xFF #E0.0 [57] (🆭..) MASK WORK SYMBOL..<...
|
||||||
|
| 0xF0 0x9F 0x87 0x00..0xA5 #
|
||||||
|
| 0xF0 0x9F 0x88 0x81..0x82 #E0.6 [2] (🈁..🈂️) Japanese “here” bu...
|
||||||
|
| 0xF0 0x9F 0x88 0x83..0x8F #E0.0 [13] (..) <reserved-1F203>..<...
|
||||||
|
| 0xF0 0x9F 0x88 0x9A #E0.6 [1] (🈚) Japanese “free of char...
|
||||||
|
| 0xF0 0x9F 0x88 0xAF #E0.6 [1] (🈯) Japanese “reserved” bu...
|
||||||
|
| 0xF0 0x9F 0x88 0xB2..0xBA #E0.6 [9] (🈲..🈺) Japanese “prohibite...
|
||||||
|
| 0xF0 0x9F 0x88 0xBC..0xBF #E0.0 [4] (..) <reserved-1F23C>..<...
|
||||||
|
| 0xF0 0x9F 0x89 0x89..0x8F #E0.0 [7] (..) <reserved-1F249>..<...
|
||||||
|
| 0xF0 0x9F 0x89 0x90..0x91 #E0.6 [2] (🉐..🉑) Japanese “bargain” ...
|
||||||
|
| 0xF0 0x9F 0x89 0x92..0xFF #E0.0 [174] (..) <reserved-1F2...
|
||||||
|
| 0xF0 0x9F 0x8A..0x8A 0x00..0xFF #
|
||||||
|
| 0xF0 0x9F 0x8B 0x00..0xBF #
|
||||||
|
| 0xF0 0x9F 0x8C 0x80..0x8C #E0.6 [13] (🌀..🌌) cyclone..milky way
|
||||||
|
| 0xF0 0x9F 0x8C 0x8D..0x8E #E0.7 [2] (🌍..🌎) globe showing Europ...
|
||||||
|
| 0xF0 0x9F 0x8C 0x8F #E0.6 [1] (🌏) globe showing Asia-Aus...
|
||||||
|
| 0xF0 0x9F 0x8C 0x90 #E1.0 [1] (🌐) globe with meridians
|
||||||
|
| 0xF0 0x9F 0x8C 0x91 #E0.6 [1] (🌑) new moon
|
||||||
|
| 0xF0 0x9F 0x8C 0x92 #E1.0 [1] (🌒) waxing crescent moon
|
||||||
|
| 0xF0 0x9F 0x8C 0x93..0x95 #E0.6 [3] (🌓..🌕) first quarter moon....
|
||||||
|
| 0xF0 0x9F 0x8C 0x96..0x98 #E1.0 [3] (🌖..🌘) waning gibbous moon...
|
||||||
|
| 0xF0 0x9F 0x8C 0x99 #E0.6 [1] (🌙) crescent moon
|
||||||
|
| 0xF0 0x9F 0x8C 0x9A #E1.0 [1] (🌚) new moon face
|
||||||
|
| 0xF0 0x9F 0x8C 0x9B #E0.6 [1] (🌛) first quarter moon face
|
||||||
|
| 0xF0 0x9F 0x8C 0x9C #E0.7 [1] (🌜) last quarter moon face
|
||||||
|
| 0xF0 0x9F 0x8C 0x9D..0x9E #E1.0 [2] (🌝..🌞) full moon face..sun...
|
||||||
|
| 0xF0 0x9F 0x8C 0x9F..0xA0 #E0.6 [2] (🌟..🌠) glowing star..shoot...
|
||||||
|
| 0xF0 0x9F 0x8C 0xA1 #E0.7 [1] (🌡️) thermometer
|
||||||
|
| 0xF0 0x9F 0x8C 0xA2..0xA3 #E0.0 [2] (🌢..🌣) BLACK DROPLET..WHIT...
|
||||||
|
| 0xF0 0x9F 0x8C 0xA4..0xAC #E0.7 [9] (🌤️..🌬️) sun behind small ...
|
||||||
|
| 0xF0 0x9F 0x8C 0xAD..0xAF #E1.0 [3] (🌭..🌯) hot dog..burrito
|
||||||
|
| 0xF0 0x9F 0x8C 0xB0..0xB1 #E0.6 [2] (🌰..🌱) chestnut..seedling
|
||||||
|
| 0xF0 0x9F 0x8C 0xB2..0xB3 #E1.0 [2] (🌲..🌳) evergreen tree..dec...
|
||||||
|
| 0xF0 0x9F 0x8C 0xB4..0xB5 #E0.6 [2] (🌴..🌵) palm tree..cactus
|
||||||
|
| 0xF0 0x9F 0x8C 0xB6 #E0.7 [1] (🌶️) hot pepper
|
||||||
|
| 0xF0 0x9F 0x8C 0xB7..0xFF #E0.6 [20] (🌷..🍊) tulip..tangerine
|
||||||
|
| 0xF0 0x9F 0x8D 0x00..0x8A #
|
||||||
|
| 0xF0 0x9F 0x8D 0x8B #E1.0 [1] (🍋) lemon
|
||||||
|
| 0xF0 0x9F 0x8D 0x8C..0x8F #E0.6 [4] (🍌..🍏) banana..green apple
|
||||||
|
| 0xF0 0x9F 0x8D 0x90 #E1.0 [1] (🍐) pear
|
||||||
|
| 0xF0 0x9F 0x8D 0x91..0xBB #E0.6 [43] (🍑..🍻) peach..clinking bee...
|
||||||
|
| 0xF0 0x9F 0x8D 0xBC #E1.0 [1] (🍼) baby bottle
|
||||||
|
| 0xF0 0x9F 0x8D 0xBD #E0.7 [1] (🍽️) fork and knife with p...
|
||||||
|
| 0xF0 0x9F 0x8D 0xBE..0xBF #E1.0 [2] (🍾..🍿) bottle with popping...
|
||||||
|
| 0xF0 0x9F 0x8E 0x80..0x93 #E0.6 [20] (🎀..🎓) ribbon..graduation cap
|
||||||
|
| 0xF0 0x9F 0x8E 0x94..0x95 #E0.0 [2] (🎔..🎕) HEART WITH TIP ON T...
|
||||||
|
| 0xF0 0x9F 0x8E 0x96..0x97 #E0.7 [2] (🎖️..🎗️) military medal..r...
|
||||||
|
| 0xF0 0x9F 0x8E 0x98 #E0.0 [1] (🎘) MUSICAL KEYBOARD WITH ...
|
||||||
|
| 0xF0 0x9F 0x8E 0x99..0x9B #E0.7 [3] (🎙️..🎛️) studio microphone...
|
||||||
|
| 0xF0 0x9F 0x8E 0x9C..0x9D #E0.0 [2] (🎜..🎝) BEAMED ASCENDING MU...
|
||||||
|
| 0xF0 0x9F 0x8E 0x9E..0x9F #E0.7 [2] (🎞️..🎟️) film frames..admi...
|
||||||
|
| 0xF0 0x9F 0x8E 0xA0..0xFF #E0.6 [37] (🎠..🏄) carousel horse..per...
|
||||||
|
| 0xF0 0x9F 0x8F 0x00..0x84 #
|
||||||
|
| 0xF0 0x9F 0x8F 0x85 #E1.0 [1] (🏅) sports medal
|
||||||
|
| 0xF0 0x9F 0x8F 0x86 #E0.6 [1] (🏆) trophy
|
||||||
|
| 0xF0 0x9F 0x8F 0x87 #E1.0 [1] (🏇) horse racing
|
||||||
|
| 0xF0 0x9F 0x8F 0x88 #E0.6 [1] (🏈) american football
|
||||||
|
| 0xF0 0x9F 0x8F 0x89 #E1.0 [1] (🏉) rugby football
|
||||||
|
| 0xF0 0x9F 0x8F 0x8A #E0.6 [1] (🏊) person swimming
|
||||||
|
| 0xF0 0x9F 0x8F 0x8B..0x8E #E0.7 [4] (🏋️..🏎️) person lifting we...
|
||||||
|
| 0xF0 0x9F 0x8F 0x8F..0x93 #E1.0 [5] (🏏..🏓) cricket game..ping ...
|
||||||
|
| 0xF0 0x9F 0x8F 0x94..0x9F #E0.7 [12] (🏔️..🏟️) snow-capped mount...
|
||||||
|
| 0xF0 0x9F 0x8F 0xA0..0xA3 #E0.6 [4] (🏠..🏣) house..Japanese pos...
|
||||||
|
| 0xF0 0x9F 0x8F 0xA4 #E1.0 [1] (🏤) post office
|
||||||
|
| 0xF0 0x9F 0x8F 0xA5..0xB0 #E0.6 [12] (🏥..🏰) hospital..castle
|
||||||
|
| 0xF0 0x9F 0x8F 0xB1..0xB2 #E0.0 [2] (🏱..🏲) WHITE PENNANT..BLAC...
|
||||||
|
| 0xF0 0x9F 0x8F 0xB3 #E0.7 [1] (🏳️) white flag
|
||||||
|
| 0xF0 0x9F 0x8F 0xB4 #E1.0 [1] (🏴) black flag
|
||||||
|
| 0xF0 0x9F 0x8F 0xB5 #E0.7 [1] (🏵️) rosette
|
||||||
|
| 0xF0 0x9F 0x8F 0xB6 #E0.0 [1] (🏶) BLACK ROSETTE
|
||||||
|
| 0xF0 0x9F 0x8F 0xB7 #E0.7 [1] (🏷️) label
|
||||||
|
| 0xF0 0x9F 0x8F 0xB8..0xBA #E1.0 [3] (🏸..🏺) badminton..amphora
|
||||||
|
| 0xF0 0x9F 0x90 0x80..0x87 #E1.0 [8] (🐀..🐇) rat..rabbit
|
||||||
|
| 0xF0 0x9F 0x90 0x88 #E0.7 [1] (🐈) cat
|
||||||
|
| 0xF0 0x9F 0x90 0x89..0x8B #E1.0 [3] (🐉..🐋) dragon..whale
|
||||||
|
| 0xF0 0x9F 0x90 0x8C..0x8E #E0.6 [3] (🐌..🐎) snail..horse
|
||||||
|
| 0xF0 0x9F 0x90 0x8F..0x90 #E1.0 [2] (🐏..🐐) ram..goat
|
||||||
|
| 0xF0 0x9F 0x90 0x91..0x92 #E0.6 [2] (🐑..🐒) ewe..monkey
|
||||||
|
| 0xF0 0x9F 0x90 0x93 #E1.0 [1] (🐓) rooster
|
||||||
|
| 0xF0 0x9F 0x90 0x94 #E0.6 [1] (🐔) chicken
|
||||||
|
| 0xF0 0x9F 0x90 0x95 #E0.7 [1] (🐕) dog
|
||||||
|
| 0xF0 0x9F 0x90 0x96 #E1.0 [1] (🐖) pig
|
||||||
|
| 0xF0 0x9F 0x90 0x97..0xA9 #E0.6 [19] (🐗..🐩) boar..poodle
|
||||||
|
| 0xF0 0x9F 0x90 0xAA #E1.0 [1] (🐪) camel
|
||||||
|
| 0xF0 0x9F 0x90 0xAB..0xBE #E0.6 [20] (🐫..🐾) two-hump camel..paw...
|
||||||
|
| 0xF0 0x9F 0x90 0xBF #E0.7 [1] (🐿️) chipmunk
|
||||||
|
| 0xF0 0x9F 0x91 0x80 #E0.6 [1] (👀) eyes
|
||||||
|
| 0xF0 0x9F 0x91 0x81 #E0.7 [1] (👁️) eye
|
||||||
|
| 0xF0 0x9F 0x91 0x82..0xA4 #E0.6 [35] (👂..👤) ear..bust in silhou...
|
||||||
|
| 0xF0 0x9F 0x91 0xA5 #E1.0 [1] (👥) busts in silhouette
|
||||||
|
| 0xF0 0x9F 0x91 0xA6..0xAB #E0.6 [6] (👦..👫) boy..woman and man ...
|
||||||
|
| 0xF0 0x9F 0x91 0xAC..0xAD #E1.0 [2] (👬..👭) men holding hands.....
|
||||||
|
| 0xF0 0x9F 0x91 0xAE..0xFF #E0.6 [63] (👮..💬) police officer..spe...
|
||||||
|
| 0xF0 0x9F 0x92 0x00..0xAC #
|
||||||
|
| 0xF0 0x9F 0x92 0xAD #E1.0 [1] (💭) thought balloon
|
||||||
|
| 0xF0 0x9F 0x92 0xAE..0xB5 #E0.6 [8] (💮..💵) white flower..dolla...
|
||||||
|
| 0xF0 0x9F 0x92 0xB6..0xB7 #E1.0 [2] (💶..💷) euro banknote..poun...
|
||||||
|
| 0xF0 0x9F 0x92 0xB8..0xFF #E0.6 [52] (💸..📫) money with wings..c...
|
||||||
|
| 0xF0 0x9F 0x93 0x00..0xAB #
|
||||||
|
| 0xF0 0x9F 0x93 0xAC..0xAD #E0.7 [2] (📬..📭) open mailbox with r...
|
||||||
|
| 0xF0 0x9F 0x93 0xAE #E0.6 [1] (📮) postbox
|
||||||
|
| 0xF0 0x9F 0x93 0xAF #E1.0 [1] (📯) postal horn
|
||||||
|
| 0xF0 0x9F 0x93 0xB0..0xB4 #E0.6 [5] (📰..📴) newspaper..mobile p...
|
||||||
|
| 0xF0 0x9F 0x93 0xB5 #E1.0 [1] (📵) no mobile phones
|
||||||
|
| 0xF0 0x9F 0x93 0xB6..0xB7 #E0.6 [2] (📶..📷) antenna bars..camera
|
||||||
|
| 0xF0 0x9F 0x93 0xB8 #E1.0 [1] (📸) camera with flash
|
||||||
|
| 0xF0 0x9F 0x93 0xB9..0xBC #E0.6 [4] (📹..📼) video camera..video...
|
||||||
|
| 0xF0 0x9F 0x93 0xBD #E0.7 [1] (📽️) film projector
|
||||||
|
| 0xF0 0x9F 0x93 0xBE #E0.0 [1] (📾) PORTABLE STEREO
|
||||||
|
| 0xF0 0x9F 0x93 0xBF..0xFF #E1.0 [4] (📿..🔂) prayer beads..repea...
|
||||||
|
| 0xF0 0x9F 0x94 0x00..0x82 #
|
||||||
|
| 0xF0 0x9F 0x94 0x83 #E0.6 [1] (🔃) clockwise vertical arrows
|
||||||
|
| 0xF0 0x9F 0x94 0x84..0x87 #E1.0 [4] (🔄..🔇) counterclockwise ar...
|
||||||
|
| 0xF0 0x9F 0x94 0x88 #E0.7 [1] (🔈) speaker low volume
|
||||||
|
| 0xF0 0x9F 0x94 0x89 #E1.0 [1] (🔉) speaker medium volume
|
||||||
|
| 0xF0 0x9F 0x94 0x8A..0x94 #E0.6 [11] (🔊..🔔) speaker high volume...
|
||||||
|
| 0xF0 0x9F 0x94 0x95 #E1.0 [1] (🔕) bell with slash
|
||||||
|
| 0xF0 0x9F 0x94 0x96..0xAB #E0.6 [22] (🔖..🔫) bookmark..pistol
|
||||||
|
| 0xF0 0x9F 0x94 0xAC..0xAD #E1.0 [2] (🔬..🔭) microscope..telescope
|
||||||
|
| 0xF0 0x9F 0x94 0xAE..0xBD #E0.6 [16] (🔮..🔽) crystal ball..downw...
|
||||||
|
| 0xF0 0x9F 0x95 0x86..0x88 #E0.0 [3] (🕆..🕈) WHITE LATIN CROSS.....
|
||||||
|
| 0xF0 0x9F 0x95 0x89..0x8A #E0.7 [2] (🕉️..🕊️) om..dove
|
||||||
|
| 0xF0 0x9F 0x95 0x8B..0x8E #E1.0 [4] (🕋..🕎) kaaba..menorah
|
||||||
|
| 0xF0 0x9F 0x95 0x8F #E0.0 [1] (🕏) BOWL OF HYGIEIA
|
||||||
|
| 0xF0 0x9F 0x95 0x90..0x9B #E0.6 [12] (🕐..🕛) one o’clock..twelve...
|
||||||
|
| 0xF0 0x9F 0x95 0x9C..0xA7 #E0.7 [12] (🕜..🕧) one-thirty..twelve-...
|
||||||
|
| 0xF0 0x9F 0x95 0xA8..0xAE #E0.0 [7] (🕨..🕮) RIGHT SPEAKER..BOOK
|
||||||
|
| 0xF0 0x9F 0x95 0xAF..0xB0 #E0.7 [2] (🕯️..🕰️) candle..mantelpie...
|
||||||
|
| 0xF0 0x9F 0x95 0xB1..0xB2 #E0.0 [2] (🕱..🕲) BLACK SKULL AND CRO...
|
||||||
|
| 0xF0 0x9F 0x95 0xB3..0xB9 #E0.7 [7] (🕳️..🕹️) hole..joystick
|
||||||
|
| 0xF0 0x9F 0x95 0xBA #E3.0 [1] (🕺) man dancing
|
||||||
|
| 0xF0 0x9F 0x95 0xBB..0xFF #E0.0 [12] (🕻..🖆) LEFT HAND TELEPHONE...
|
||||||
|
| 0xF0 0x9F 0x96 0x00..0x86 #
|
||||||
|
| 0xF0 0x9F 0x96 0x87 #E0.7 [1] (🖇️) linked paperclips
|
||||||
|
| 0xF0 0x9F 0x96 0x88..0x89 #E0.0 [2] (🖈..🖉) BLACK PUSHPIN..LOWE...
|
||||||
|
| 0xF0 0x9F 0x96 0x8A..0x8D #E0.7 [4] (🖊️..🖍️) pen..crayon
|
||||||
|
| 0xF0 0x9F 0x96 0x8E..0x8F #E0.0 [2] (🖎..🖏) LEFT WRITING HAND.....
|
||||||
|
| 0xF0 0x9F 0x96 0x90 #E0.7 [1] (🖐️) hand with fingers spl...
|
||||||
|
| 0xF0 0x9F 0x96 0x91..0x94 #E0.0 [4] (🖑..🖔) REVERSED RAISED HAN...
|
||||||
|
| 0xF0 0x9F 0x96 0x95..0x96 #E1.0 [2] (🖕..🖖) middle finger..vulc...
|
||||||
|
| 0xF0 0x9F 0x96 0x97..0xA3 #E0.0 [13] (🖗..🖣) WHITE DOWN POINTING...
|
||||||
|
| 0xF0 0x9F 0x96 0xA4 #E3.0 [1] (🖤) black heart
|
||||||
|
| 0xF0 0x9F 0x96 0xA5 #E0.7 [1] (🖥️) desktop computer
|
||||||
|
| 0xF0 0x9F 0x96 0xA6..0xA7 #E0.0 [2] (🖦..🖧) KEYBOARD AND MOUSE....
|
||||||
|
| 0xF0 0x9F 0x96 0xA8 #E0.7 [1] (🖨️) printer
|
||||||
|
| 0xF0 0x9F 0x96 0xA9..0xB0 #E0.0 [8] (🖩..🖰) POCKET CALCULATOR.....
|
||||||
|
| 0xF0 0x9F 0x96 0xB1..0xB2 #E0.7 [2] (🖱️..🖲️) computer mouse..t...
|
||||||
|
| 0xF0 0x9F 0x96 0xB3..0xBB #E0.0 [9] (🖳..🖻) OLD PERSONAL COMPUT...
|
||||||
|
| 0xF0 0x9F 0x96 0xBC #E0.7 [1] (🖼️) framed picture
|
||||||
|
| 0xF0 0x9F 0x96 0xBD..0xFF #E0.0 [5] (🖽..🗁) FRAME WITH TILES..O...
|
||||||
|
| 0xF0 0x9F 0x97 0x00..0x81 #
|
||||||
|
| 0xF0 0x9F 0x97 0x82..0x84 #E0.7 [3] (🗂️..🗄️) card index divide...
|
||||||
|
| 0xF0 0x9F 0x97 0x85..0x90 #E0.0 [12] (🗅..🗐) EMPTY NOTE..PAGES
|
||||||
|
| 0xF0 0x9F 0x97 0x91..0x93 #E0.7 [3] (🗑️..🗓️) wastebasket..spir...
|
||||||
|
| 0xF0 0x9F 0x97 0x94..0x9B #E0.0 [8] (🗔..🗛) DESKTOP WINDOW..DEC...
|
||||||
|
| 0xF0 0x9F 0x97 0x9C..0x9E #E0.7 [3] (🗜️..🗞️) clamp..rolled-up ...
|
||||||
|
| 0xF0 0x9F 0x97 0x9F..0xA0 #E0.0 [2] (🗟..🗠) PAGE WITH CIRCLED T...
|
||||||
|
| 0xF0 0x9F 0x97 0xA1 #E0.7 [1] (🗡️) dagger
|
||||||
|
| 0xF0 0x9F 0x97 0xA2 #E0.0 [1] (🗢) LIPS
|
||||||
|
| 0xF0 0x9F 0x97 0xA3 #E0.7 [1] (🗣️) speaking head
|
||||||
|
| 0xF0 0x9F 0x97 0xA4..0xA7 #E0.0 [4] (🗤..🗧) THREE RAYS ABOVE..T...
|
||||||
|
| 0xF0 0x9F 0x97 0xA8 #E2.0 [1] (🗨️) left speech bubble
|
||||||
|
| 0xF0 0x9F 0x97 0xA9..0xAE #E0.0 [6] (🗩..🗮) RIGHT SPEECH BUBBLE...
|
||||||
|
| 0xF0 0x9F 0x97 0xAF #E0.7 [1] (🗯️) right anger bubble
|
||||||
|
| 0xF0 0x9F 0x97 0xB0..0xB2 #E0.0 [3] (🗰..🗲) MOOD BUBBLE..LIGHTN...
|
||||||
|
| 0xF0 0x9F 0x97 0xB3 #E0.7 [1] (🗳️) ballot box with ballot
|
||||||
|
| 0xF0 0x9F 0x97 0xB4..0xB9 #E0.0 [6] (🗴..🗹) BALLOT SCRIPT X..BA...
|
||||||
|
| 0xF0 0x9F 0x97 0xBA #E0.7 [1] (🗺️) world map
|
||||||
|
| 0xF0 0x9F 0x97 0xBB..0xBF #E0.6 [5] (🗻..🗿) mount fuji..moai
|
||||||
|
| 0xF0 0x9F 0x98 0x80 #E1.0 [1] (😀) grinning face
|
||||||
|
| 0xF0 0x9F 0x98 0x81..0x86 #E0.6 [6] (😁..😆) beaming face with s...
|
||||||
|
| 0xF0 0x9F 0x98 0x87..0x88 #E1.0 [2] (😇..😈) smiling face with h...
|
||||||
|
| 0xF0 0x9F 0x98 0x89..0x8D #E0.6 [5] (😉..😍) winking face..smili...
|
||||||
|
| 0xF0 0x9F 0x98 0x8E #E1.0 [1] (😎) smiling face with sung...
|
||||||
|
| 0xF0 0x9F 0x98 0x8F #E0.6 [1] (😏) smirking face
|
||||||
|
| 0xF0 0x9F 0x98 0x90 #E0.7 [1] (😐) neutral face
|
||||||
|
| 0xF0 0x9F 0x98 0x91 #E1.0 [1] (😑) expressionless face
|
||||||
|
| 0xF0 0x9F 0x98 0x92..0x94 #E0.6 [3] (😒..😔) unamused face..pens...
|
||||||
|
| 0xF0 0x9F 0x98 0x95 #E1.0 [1] (😕) confused face
|
||||||
|
| 0xF0 0x9F 0x98 0x96 #E0.6 [1] (😖) confounded face
|
||||||
|
| 0xF0 0x9F 0x98 0x97 #E1.0 [1] (😗) kissing face
|
||||||
|
| 0xF0 0x9F 0x98 0x98 #E0.6 [1] (😘) face blowing a kiss
|
||||||
|
| 0xF0 0x9F 0x98 0x99 #E1.0 [1] (😙) kissing face with smil...
|
||||||
|
| 0xF0 0x9F 0x98 0x9A #E0.6 [1] (😚) kissing face with clos...
|
||||||
|
| 0xF0 0x9F 0x98 0x9B #E1.0 [1] (😛) face with tongue
|
||||||
|
| 0xF0 0x9F 0x98 0x9C..0x9E #E0.6 [3] (😜..😞) winking face with t...
|
||||||
|
| 0xF0 0x9F 0x98 0x9F #E1.0 [1] (😟) worried face
|
||||||
|
| 0xF0 0x9F 0x98 0xA0..0xA5 #E0.6 [6] (😠..😥) angry face..sad but...
|
||||||
|
| 0xF0 0x9F 0x98 0xA6..0xA7 #E1.0 [2] (😦..😧) frowning face with ...
|
||||||
|
| 0xF0 0x9F 0x98 0xA8..0xAB #E0.6 [4] (😨..😫) fearful face..tired...
|
||||||
|
| 0xF0 0x9F 0x98 0xAC #E1.0 [1] (😬) grimacing face
|
||||||
|
| 0xF0 0x9F 0x98 0xAD #E0.6 [1] (😭) loudly crying face
|
||||||
|
| 0xF0 0x9F 0x98 0xAE..0xAF #E1.0 [2] (😮..😯) face with open mout...
|
||||||
|
| 0xF0 0x9F 0x98 0xB0..0xB3 #E0.6 [4] (😰..😳) anxious face with s...
|
||||||
|
| 0xF0 0x9F 0x98 0xB4 #E1.0 [1] (😴) sleeping face
|
||||||
|
| 0xF0 0x9F 0x98 0xB5 #E0.6 [1] (😵) dizzy face
|
||||||
|
| 0xF0 0x9F 0x98 0xB6 #E1.0 [1] (😶) face without mouth
|
||||||
|
| 0xF0 0x9F 0x98 0xB7..0xFF #E0.6 [10] (😷..🙀) face with medical m...
|
||||||
|
| 0xF0 0x9F 0x99 0x00..0x80 #
|
||||||
|
| 0xF0 0x9F 0x99 0x81..0x84 #E1.0 [4] (🙁..🙄) slightly frowning f...
|
||||||
|
| 0xF0 0x9F 0x99 0x85..0x8F #E0.6 [11] (🙅..🙏) person gesturing NO...
|
||||||
|
| 0xF0 0x9F 0x9A 0x80 #E0.6 [1] (🚀) rocket
|
||||||
|
| 0xF0 0x9F 0x9A 0x81..0x82 #E1.0 [2] (🚁..🚂) helicopter..locomotive
|
||||||
|
| 0xF0 0x9F 0x9A 0x83..0x85 #E0.6 [3] (🚃..🚅) railway car..bullet...
|
||||||
|
| 0xF0 0x9F 0x9A 0x86 #E1.0 [1] (🚆) train
|
||||||
|
| 0xF0 0x9F 0x9A 0x87 #E0.6 [1] (🚇) metro
|
||||||
|
| 0xF0 0x9F 0x9A 0x88 #E1.0 [1] (🚈) light rail
|
||||||
|
| 0xF0 0x9F 0x9A 0x89 #E0.6 [1] (🚉) station
|
||||||
|
| 0xF0 0x9F 0x9A 0x8A..0x8B #E1.0 [2] (🚊..🚋) tram..tram car
|
||||||
|
| 0xF0 0x9F 0x9A 0x8C #E0.6 [1] (🚌) bus
|
||||||
|
| 0xF0 0x9F 0x9A 0x8D #E0.7 [1] (🚍) oncoming bus
|
||||||
|
| 0xF0 0x9F 0x9A 0x8E #E1.0 [1] (🚎) trolleybus
|
||||||
|
| 0xF0 0x9F 0x9A 0x8F #E0.6 [1] (🚏) bus stop
|
||||||
|
| 0xF0 0x9F 0x9A 0x90 #E1.0 [1] (🚐) minibus
|
||||||
|
| 0xF0 0x9F 0x9A 0x91..0x93 #E0.6 [3] (🚑..🚓) ambulance..police car
|
||||||
|
| 0xF0 0x9F 0x9A 0x94 #E0.7 [1] (🚔) oncoming police car
|
||||||
|
| 0xF0 0x9F 0x9A 0x95 #E0.6 [1] (🚕) taxi
|
||||||
|
| 0xF0 0x9F 0x9A 0x96 #E1.0 [1] (🚖) oncoming taxi
|
||||||
|
| 0xF0 0x9F 0x9A 0x97 #E0.6 [1] (🚗) automobile
|
||||||
|
| 0xF0 0x9F 0x9A 0x98 #E0.7 [1] (🚘) oncoming automobile
|
||||||
|
| 0xF0 0x9F 0x9A 0x99..0x9A #E0.6 [2] (🚙..🚚) sport utility vehic...
|
||||||
|
| 0xF0 0x9F 0x9A 0x9B..0xA1 #E1.0 [7] (🚛..🚡) articulated lorry.....
|
||||||
|
| 0xF0 0x9F 0x9A 0xA2 #E0.6 [1] (🚢) ship
|
||||||
|
| 0xF0 0x9F 0x9A 0xA3 #E1.0 [1] (🚣) person rowing boat
|
||||||
|
| 0xF0 0x9F 0x9A 0xA4..0xA5 #E0.6 [2] (🚤..🚥) speedboat..horizont...
|
||||||
|
| 0xF0 0x9F 0x9A 0xA6 #E1.0 [1] (🚦) vertical traffic light
|
||||||
|
| 0xF0 0x9F 0x9A 0xA7..0xAD #E0.6 [7] (🚧..🚭) construction..no sm...
|
||||||
|
| 0xF0 0x9F 0x9A 0xAE..0xB1 #E1.0 [4] (🚮..🚱) litter in bin sign....
|
||||||
|
| 0xF0 0x9F 0x9A 0xB2 #E0.6 [1] (🚲) bicycle
|
||||||
|
| 0xF0 0x9F 0x9A 0xB3..0xB5 #E1.0 [3] (🚳..🚵) no bicycles..person...
|
||||||
|
| 0xF0 0x9F 0x9A 0xB6 #E0.6 [1] (🚶) person walking
|
||||||
|
| 0xF0 0x9F 0x9A 0xB7..0xB8 #E1.0 [2] (🚷..🚸) no pedestrians..chi...
|
||||||
|
| 0xF0 0x9F 0x9A 0xB9..0xBE #E0.6 [6] (🚹..🚾) men’s room..water c...
|
||||||
|
| 0xF0 0x9F 0x9A 0xBF #E1.0 [1] (🚿) shower
|
||||||
|
| 0xF0 0x9F 0x9B 0x80 #E0.6 [1] (🛀) person taking bath
|
||||||
|
| 0xF0 0x9F 0x9B 0x81..0x85 #E1.0 [5] (🛁..🛅) bathtub..left luggage
|
||||||
|
| 0xF0 0x9F 0x9B 0x86..0x8A #E0.0 [5] (🛆..🛊) TRIANGLE WITH ROUND...
|
||||||
|
| 0xF0 0x9F 0x9B 0x8B #E0.7 [1] (🛋️) couch and lamp
|
||||||
|
| 0xF0 0x9F 0x9B 0x8C #E1.0 [1] (🛌) person in bed
|
||||||
|
| 0xF0 0x9F 0x9B 0x8D..0x8F #E0.7 [3] (🛍️..🛏️) shopping bags..bed
|
||||||
|
| 0xF0 0x9F 0x9B 0x90 #E1.0 [1] (🛐) place of worship
|
||||||
|
| 0xF0 0x9F 0x9B 0x91..0x92 #E3.0 [2] (🛑..🛒) stop sign..shopping...
|
||||||
|
| 0xF0 0x9F 0x9B 0x93..0x94 #E0.0 [2] (🛓..🛔) STUPA..PAGODA
|
||||||
|
| 0xF0 0x9F 0x9B 0x95 #E12.0 [1] (🛕) hindu temple
|
||||||
|
| 0xF0 0x9F 0x9B 0x96..0x97 #E13.0 [2] (🛖..🛗) hut..elevator
|
||||||
|
| 0xF0 0x9F 0x9B 0x98..0x9F #E0.0 [8] (..🛟) <reserved-1F6D8>..<...
|
||||||
|
| 0xF0 0x9F 0x9B 0xA0..0xA5 #E0.7 [6] (🛠️..🛥️) hammer and wrench...
|
||||||
|
| 0xF0 0x9F 0x9B 0xA6..0xA8 #E0.0 [3] (🛦..🛨) UP-POINTING MILITAR...
|
||||||
|
| 0xF0 0x9F 0x9B 0xA9 #E0.7 [1] (🛩️) small airplane
|
||||||
|
| 0xF0 0x9F 0x9B 0xAA #E0.0 [1] (🛪) NORTHEAST-POINTING AIR...
|
||||||
|
| 0xF0 0x9F 0x9B 0xAB..0xAC #E1.0 [2] (🛫..🛬) airplane departure....
|
||||||
|
| 0xF0 0x9F 0x9B 0xAD..0xAF #E0.0 [3] (..) <reserved-1F6ED>..<...
|
||||||
|
| 0xF0 0x9F 0x9B 0xB0 #E0.7 [1] (🛰️) satellite
|
||||||
|
| 0xF0 0x9F 0x9B 0xB1..0xB2 #E0.0 [2] (🛱..🛲) ONCOMING FIRE ENGIN...
|
||||||
|
| 0xF0 0x9F 0x9B 0xB3 #E0.7 [1] (🛳️) passenger ship
|
||||||
|
| 0xF0 0x9F 0x9B 0xB4..0xB6 #E3.0 [3] (🛴..🛶) kick scooter..canoe
|
||||||
|
| 0xF0 0x9F 0x9B 0xB7..0xB8 #E5.0 [2] (🛷..🛸) sled..flying saucer
|
||||||
|
| 0xF0 0x9F 0x9B 0xB9 #E11.0 [1] (🛹) skateboard
|
||||||
|
| 0xF0 0x9F 0x9B 0xBA #E12.0 [1] (🛺) auto rickshaw
|
||||||
|
| 0xF0 0x9F 0x9B 0xBB..0xBC #E13.0 [2] (🛻..🛼) pickup truck..rolle...
|
||||||
|
| 0xF0 0x9F 0x9B 0xBD..0xBF #E0.0 [3] (..) <reserved-1F6FD>..<...
|
||||||
|
| 0xF0 0x9F 0x9D 0xB4..0xBF #E0.0 [12] (🝴..🝿) <reserved-1F774>..<...
|
||||||
|
| 0xF0 0x9F 0x9F 0x95..0x9F #E0.0 [11] (🟕..) CIRCLED TRIANGLE..<...
|
||||||
|
| 0xF0 0x9F 0x9F 0xA0..0xAB #E12.0 [12] (🟠..🟫) orange circle..brow...
|
||||||
|
| 0xF0 0x9F 0x9F 0xAC..0xBF #E0.0 [20] (..) <reserved-1F7EC>..<...
|
||||||
|
| 0xF0 0x9F 0xA0 0x8C..0x8F #E0.0 [4] (..) <reserved-1F80C>..<...
|
||||||
|
| 0xF0 0x9F 0xA1 0x88..0x8F #E0.0 [8] (..) <reserved-1F848>..<...
|
||||||
|
| 0xF0 0x9F 0xA1 0x9A..0x9F #E0.0 [6] (..) <reserved-1F85A>..<...
|
||||||
|
| 0xF0 0x9F 0xA2 0x88..0x8F #E0.0 [8] (..) <reserved-1F888>..<...
|
||||||
|
| 0xF0 0x9F 0xA2 0xAE..0xFF #E0.0 [82] (..) <reserved-1F8AE>..<...
|
||||||
|
| 0xF0 0x9F 0xA3 0x00..0xBF #
|
||||||
|
| 0xF0 0x9F 0xA4 0x8C #E13.0 [1] (🤌) pinched fingers
|
||||||
|
| 0xF0 0x9F 0xA4 0x8D..0x8F #E12.0 [3] (🤍..🤏) white heart..pinchi...
|
||||||
|
| 0xF0 0x9F 0xA4 0x90..0x98 #E1.0 [9] (🤐..🤘) zipper-mouth face.....
|
||||||
|
| 0xF0 0x9F 0xA4 0x99..0x9E #E3.0 [6] (🤙..🤞) call me hand..cross...
|
||||||
|
| 0xF0 0x9F 0xA4 0x9F #E5.0 [1] (🤟) love-you gesture
|
||||||
|
| 0xF0 0x9F 0xA4 0xA0..0xA7 #E3.0 [8] (🤠..🤧) cowboy hat face..sn...
|
||||||
|
| 0xF0 0x9F 0xA4 0xA8..0xAF #E5.0 [8] (🤨..🤯) face with raised ey...
|
||||||
|
| 0xF0 0x9F 0xA4 0xB0 #E3.0 [1] (🤰) pregnant woman
|
||||||
|
| 0xF0 0x9F 0xA4 0xB1..0xB2 #E5.0 [2] (🤱..🤲) breast-feeding..pal...
|
||||||
|
| 0xF0 0x9F 0xA4 0xB3..0xBA #E3.0 [8] (🤳..🤺) selfie..person fencing
|
||||||
|
| 0xF0 0x9F 0xA4 0xBC..0xBE #E3.0 [3] (🤼..🤾) people wrestling..p...
|
||||||
|
| 0xF0 0x9F 0xA4 0xBF #E12.0 [1] (🤿) diving mask
|
||||||
|
| 0xF0 0x9F 0xA5 0x80..0x85 #E3.0 [6] (🥀..🥅) wilted flower..goal...
|
||||||
|
| 0xF0 0x9F 0xA5 0x87..0x8B #E3.0 [5] (🥇..🥋) 1st place medal..ma...
|
||||||
|
| 0xF0 0x9F 0xA5 0x8C #E5.0 [1] (🥌) curling stone
|
||||||
|
| 0xF0 0x9F 0xA5 0x8D..0x8F #E11.0 [3] (🥍..🥏) lacrosse..flying disc
|
||||||
|
| 0xF0 0x9F 0xA5 0x90..0x9E #E3.0 [15] (🥐..🥞) croissant..pancakes
|
||||||
|
| 0xF0 0x9F 0xA5 0x9F..0xAB #E5.0 [13] (🥟..🥫) dumpling..canned food
|
||||||
|
| 0xF0 0x9F 0xA5 0xAC..0xB0 #E11.0 [5] (🥬..🥰) leafy green..smilin...
|
||||||
|
| 0xF0 0x9F 0xA5 0xB1 #E12.0 [1] (🥱) yawning face
|
||||||
|
| 0xF0 0x9F 0xA5 0xB2 #E13.0 [1] (🥲) smiling face with tear
|
||||||
|
| 0xF0 0x9F 0xA5 0xB3..0xB6 #E11.0 [4] (🥳..🥶) partying face..cold...
|
||||||
|
| 0xF0 0x9F 0xA5 0xB7..0xB8 #E13.0 [2] (🥷..🥸) ninja..disguised face
|
||||||
|
| 0xF0 0x9F 0xA5 0xB9 #E0.0 [1] (🥹) <reserved-1F979>
|
||||||
|
| 0xF0 0x9F 0xA5 0xBA #E11.0 [1] (🥺) pleading face
|
||||||
|
| 0xF0 0x9F 0xA5 0xBB #E12.0 [1] (🥻) sari
|
||||||
|
| 0xF0 0x9F 0xA5 0xBC..0xBF #E11.0 [4] (🥼..🥿) lab coat..flat shoe
|
||||||
|
| 0xF0 0x9F 0xA6 0x80..0x84 #E1.0 [5] (🦀..🦄) crab..unicorn
|
||||||
|
| 0xF0 0x9F 0xA6 0x85..0x91 #E3.0 [13] (🦅..🦑) eagle..squid
|
||||||
|
| 0xF0 0x9F 0xA6 0x92..0x97 #E5.0 [6] (🦒..🦗) giraffe..cricket
|
||||||
|
| 0xF0 0x9F 0xA6 0x98..0xA2 #E11.0 [11] (🦘..🦢) kangaroo..swan
|
||||||
|
| 0xF0 0x9F 0xA6 0xA3..0xA4 #E13.0 [2] (🦣..🦤) mammoth..dodo
|
||||||
|
| 0xF0 0x9F 0xA6 0xA5..0xAA #E12.0 [6] (🦥..🦪) sloth..oyster
|
||||||
|
| 0xF0 0x9F 0xA6 0xAB..0xAD #E13.0 [3] (🦫..🦭) beaver..seal
|
||||||
|
| 0xF0 0x9F 0xA6 0xAE..0xAF #E12.0 [2] (🦮..🦯) guide dog..white cane
|
||||||
|
| 0xF0 0x9F 0xA6 0xB0..0xB9 #E11.0 [10] (🦰..🦹) red hair..supervillain
|
||||||
|
| 0xF0 0x9F 0xA6 0xBA..0xBF #E12.0 [6] (🦺..🦿) safety vest..mechan...
|
||||||
|
| 0xF0 0x9F 0xA7 0x80 #E1.0 [1] (🧀) cheese wedge
|
||||||
|
| 0xF0 0x9F 0xA7 0x81..0x82 #E11.0 [2] (🧁..🧂) cupcake..salt
|
||||||
|
| 0xF0 0x9F 0xA7 0x83..0x8A #E12.0 [8] (🧃..🧊) beverage box..ice
|
||||||
|
| 0xF0 0x9F 0xA7 0x8B #E13.0 [1] (🧋) bubble tea
|
||||||
|
| 0xF0 0x9F 0xA7 0x8C #E0.0 [1] (🧌) <reserved-1F9CC>
|
||||||
|
| 0xF0 0x9F 0xA7 0x8D..0x8F #E12.0 [3] (🧍..🧏) person standing..de...
|
||||||
|
| 0xF0 0x9F 0xA7 0x90..0xA6 #E5.0 [23] (🧐..🧦) face with monocle.....
|
||||||
|
| 0xF0 0x9F 0xA7 0xA7..0xBF #E11.0 [25] (🧧..🧿) red envelope..nazar...
|
||||||
|
| 0xF0 0x9F 0xA8 0x80..0xFF #E0.0 [112] (🨀..) NEUTRAL CHESS KING....
|
||||||
|
| 0xF0 0x9F 0xA9 0x00..0xAF #
|
||||||
|
| 0xF0 0x9F 0xA9 0xB0..0xB3 #E12.0 [4] (🩰..🩳) ballet shoes..shorts
|
||||||
|
| 0xF0 0x9F 0xA9 0xB4 #E13.0 [1] (🩴) thong sandal
|
||||||
|
| 0xF0 0x9F 0xA9 0xB5..0xB7 #E0.0 [3] (🩵..🩷) <reserved-1FA75>..<...
|
||||||
|
| 0xF0 0x9F 0xA9 0xB8..0xBA #E12.0 [3] (🩸..🩺) drop of blood..stet...
|
||||||
|
| 0xF0 0x9F 0xA9 0xBB..0xBF #E0.0 [5] (🩻..) <reserved-1FA7B>..<...
|
||||||
|
| 0xF0 0x9F 0xAA 0x80..0x82 #E12.0 [3] (🪀..🪂) yo-yo..parachute
|
||||||
|
| 0xF0 0x9F 0xAA 0x83..0x86 #E13.0 [4] (🪃..🪆) boomerang..nesting ...
|
||||||
|
| 0xF0 0x9F 0xAA 0x87..0x8F #E0.0 [9] (🪇..) <reserved-1FA87>..<...
|
||||||
|
| 0xF0 0x9F 0xAA 0x90..0x95 #E12.0 [6] (🪐..🪕) ringed planet..banjo
|
||||||
|
| 0xF0 0x9F 0xAA 0x96..0xA8 #E13.0 [19] (🪖..🪨) military helmet..rock
|
||||||
|
| 0xF0 0x9F 0xAA 0xA9..0xAF #E0.0 [7] (🪩..🪯) <reserved-1FAA9>..<...
|
||||||
|
| 0xF0 0x9F 0xAA 0xB0..0xB6 #E13.0 [7] (🪰..🪶) fly..feather
|
||||||
|
| 0xF0 0x9F 0xAA 0xB7..0xBF #E0.0 [9] (🪷..🪿) <reserved-1FAB7>..<...
|
||||||
|
| 0xF0 0x9F 0xAB 0x80..0x82 #E13.0 [3] (🫀..🫂) anatomical heart..p...
|
||||||
|
| 0xF0 0x9F 0xAB 0x83..0x8F #E0.0 [13] (🫃..🫏) <reserved-1FAC3>..<...
|
||||||
|
| 0xF0 0x9F 0xAB 0x90..0x96 #E13.0 [7] (🫐..🫖) blueberries..teapot
|
||||||
|
| 0xF0 0x9F 0xAB 0x97..0xBF #E0.0 [41] (🫗..) <reserved-1FAD7>..<...
|
||||||
|
| 0xF0 0x9F 0xB0 0x80..0xFF #E0.0[1022] (..) <reserved-1FC...
|
||||||
|
| 0xF0 0x9F 0xB1..0xBE 0x00..0xFF #
|
||||||
|
| 0xF0 0x9F 0xBF 0x00..0xBD #
|
||||||
|
;
|
||||||
|
|
||||||
|
}%%
|
||||||
Generated
Vendored
+2
-2
@@ -2,7 +2,7 @@ package textseg
|
|||||||
|
|
||||||
//go:generate go run make_tables.go -output tables.go
|
//go:generate go run make_tables.go -output tables.go
|
||||||
//go:generate go run make_test_tables.go -output tables_test.go
|
//go:generate go run make_test_tables.go -output tables_test.go
|
||||||
//go:generate ruby unicode2ragel.rb --url=https://www.unicode.org/Public/12.0.0/ucd/auxiliary/GraphemeBreakProperty.txt -m GraphemeCluster -p "Prepend,CR,LF,Control,Extend,Regional_Indicator,SpacingMark,L,V,T,LV,LVT,ZWJ" -o grapheme_clusters_table.rl
|
//go:generate ruby unicode2ragel.rb --url=https://www.unicode.org/Public/13.0.0/ucd/auxiliary/GraphemeBreakProperty.txt -m GraphemeCluster -p "Prepend,CR,LF,Control,Extend,Regional_Indicator,SpacingMark,L,V,T,LV,LVT,ZWJ" -o grapheme_clusters_table.rl
|
||||||
//go:generate ruby unicode2ragel.rb --url=https://www.unicode.org/Public/emoji/12.0/emoji-data.txt -m Emoji -p "Extended_Pictographic" -o emoji_table.rl
|
//go:generate ruby unicode2ragel.rb --url=https://www.unicode.org/Public/13.0.0/ucd/emoji/emoji-data.txt -m Emoji -p "Extended_Pictographic" -o emoji_table.rl
|
||||||
//go:generate ragel -Z grapheme_clusters.rl
|
//go:generate ragel -Z grapheme_clusters.rl
|
||||||
//go:generate gofmt -w grapheme_clusters.go
|
//go:generate gofmt -w grapheme_clusters.go
|
||||||
+4138
File diff suppressed because it is too large
Load Diff
Generated
Vendored
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user