Compare commits

..

32 Commits

Author SHA1 Message Date
packer-ci 030da4b6b9 Cut version 1.7.1 2021-03-31 16:43:47 +00:00
packer-ci 3a437d4891 cut version 1.7.1 2021-03-31 16:43:45 +00:00
packer-ci 8c2f26718e update changelog 2021-03-31 16:43:45 +00:00
Wilken Rivera 076596cd3b Makefile: Update install steps for gox 2021-03-31 11:53:41 -04:00
Wilken Rivera f541cd59ed Update packer-plugin-sdk to latest release
```
go get github.com/hashicorp/packer-plugin-sdk@v0.1.1
go mod tidy
go mod vendor
```
2021-03-31 10:36:41 -04:00
Wilken Rivera 4b6891d6d5 Update CHANGELOG 2021-03-30 17:37:13 -04:00
Wilken Rivera 4746fc682d Update version of packer-plugin-docker (#10847)
```
go get github.com/hashicorp/packer-plugin-docker@v0.0.7
go mod tidy
go mod vendor
```
2021-03-30 17:12:43 -04:00
Wilken Rivera f6dbc3e78a Update steps for generating the remote plugin docs.zip file (#10846)
* Update steps for generating the remote plugin docs.zip file

* Update a few typos

* Fix tabbing issue
2021-03-30 17:12:28 -04:00
Adrien Delorme 03d79a2c39 HCL2 variables: split validation from getting value (#10843)
* HCL2 variables: split validation from getting value, to only

This way we do this only once and log this only once. The errors were being ignored anyways.

* Update types.variables_test.go
2021-03-30 14:58:26 -04:00
Sylvia Moss 349a300213 Add new disk to existingDevices list (#10844)
* add new disk to existingDevices list

* add tests
2021-03-30 14:48:06 -04:00
Megan Marsh dfc5d76108 Merge pull request #10833 from harveylowndes/add-oci-flex-shape-support
Support OCI flexible shapes
2021-03-30 10:11:47 -07:00
Adrien Delorme 77a29fc2f8 Allow to have dynamic blocks in a build block + tests (#10825)
This :
* allows to have a `build.dynamic` block
* add tests
* makes sure to show a correct message when a source was not found
  * display only name of source (instead of a weird map printout) 
  * use a "Did you mean %q" feature where possible 


Because dynamic blocks need all variables to be evaluated and available, I moved parsing of everything that is not a variable to "after" variables are extrapolated. Meaning that dynamic block get expanded in the `init` phase and then only we start interpreting HCL2 content.

After #10819 fix #10657
2021-03-30 15:53:04 +02:00
Sylvia Moss a588808270 update v1.7.1 changelog (#10837) 2021-03-29 14:04:32 +02:00
Harvey Lowndes b9b1cdf75f Address review comments 2021-03-29 12:57:10 +01:00
Kerim Satirli 2ac5fe894c adds missing word (#10836) 2021-03-29 11:01:03 +02:00
Kaivalya Shah fbb9429910 HCL2 example syntax changes (#10832)
An HCL2 example contained commas to separate the lines, and the metadata block did not have the equals sign, which is not valid.
2021-03-29 11:00:42 +02:00
Harvey Lowndes cb359e8064 Update OCI docs for flex shapes
Update the docs to reflect flex shape additions. Also makes a fix to the mapstructure.
2021-03-26 12:26:52 +00:00
Adrien Delorme a9bec7945e remove .mdx extensions in links (#10823)
Some of them were added in #10800 some where already there
2021-03-25 14:02:21 +01:00
Sylvia Moss 505cbd2591 Vendor amazon plugin (#10800)
* remove amazon from core
* vendor amazon plugin
* remove website content
* Add amazon to docs-remote-plugins
* update amazon reference links in the documentation
* update amazon docs version to latest

Co-authored-by: Adrien Delorme <adrien.delorme@icloud.com>
2021-03-25 13:37:48 +01:00
GennadySpb 25fddf3199 Add release build for darwin/arm64 (#10804)
* Add release build for darwin/arm64

Co-authored-by: Adrien Delorme <azr@users.noreply.github.com>
2021-03-25 13:34:21 +01:00
Ricardo Katz 166df2ce1d correct provisioner typo (#10822)
Correct provision typo
2021-03-25 13:27:05 +01:00
Megan Marsh 755395faf8 Merge pull request #10820 from chrisroberts/force-no-direct-upload-to-vagrantcloud
Override direct upload based on box size
2021-03-24 14:50:49 -07:00
Megan Marsh b958fe7a54 Merge pull request #10821 from zchsh/zs.fix-nested-plugin-urls
website: fix issue with .mdx in plugin docs URL paths
2021-03-24 14:10:14 -07:00
Zach Shilton cc133ea250 website: fix issue with .mdx in URL paths 2021-03-24 16:39:29 -04:00
Chris Roberts 4ea4c0570f Add test coverage for direct upload file size limits 2021-03-24 10:54:11 -07:00
Chris Roberts a665e6b822 Always include all upload steps regardless of configuration 2021-03-24 10:53:38 -07:00
Chris Roberts 2de91e4862 Check configuration before running callback for upload confirmation 2021-03-24 10:52:56 -07:00
Chris Roberts 3a11820a41 Adjust upload limit value and fix error value stored in state bag 2021-03-24 10:51:10 -07:00
Adrien Delorme 0e3fcb589b Implicit required_plugin blocks (#10732)
* used components that don't have a required_plugin block will make Packer 'implicitly' require those. These components are manually selected and commented for now.
* add tests
 * docs
2021-03-24 11:31:39 +01:00
Avi Miller e6596a0a1d [oracle-oci] Add support for E3/E4.Flex shapes
This addes an optional shape_config stanza which allows
you to specify how many ocpus and memory the Flex
instance should be allocated.

This required an upgrade of the OCI Go SDK version.

Signed-off-by: Avi Miller <avi.miller@oracle.com>
2021-03-21 12:46:21 +11:00
Andrew Pryde a915ec8e05 Upgrade oci-go-sdk to latest 2021-03-20 02:16:17 +00:00
sophia 9b641c9bfd Force NoDirectUpload for vagrantcloud if asset size > 5 GB 2021-03-18 17:22:27 -05:00
1383 changed files with 34529 additions and 19224 deletions
+16 -3
View File
@@ -37,15 +37,19 @@ commands:
parameters:
GOOS:
type: string
GOARCH:
default: "amd64"
type: string
steps:
- 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: zip ./pkg/packer_<< parameters.GOOS >>_$(go env GOARCH).zip ./pkg/packer_<< parameters.GOOS >>_$(go env GOARCH)
- run: rm ./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 >>_<< parameters.GOARCH >>.zip ./pkg/packer_<< parameters.GOOS >>_<< parameters.GOARCH >>
- run: rm ./pkg/packer_<< parameters.GOOS >>_<< parameters.GOARCH >>
- persist_to_workspace:
root: .
paths:
- ./pkg/
# Golang CircleCI 2.0 configuration file
#
# Check https://circleci.com/docs/2.0/language-go/ for more details
@@ -123,6 +127,13 @@ jobs:
steps:
- build-and-persist-packer-binary:
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:
executor: golang
working_directory: /go/src/github.com/hashicorp/packer
@@ -203,6 +214,7 @@ workflows:
jobs:
- build_linux
- build_darwin
- build_darwin_arm64
- build_windows
- build_freebsd
- build_openbsd
@@ -211,6 +223,7 @@ workflows:
requires:
- build_linux
- build_darwin
- build_darwin_arm64
- build_windows
- build_freebsd
- build_openbsd
+35 -5
View File
@@ -1,21 +1,30 @@
## 1.7.1 (Upcoming)
## 1.7.1 (March 31, 2021)
### 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
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
then, but for the latest offerings of the Docker 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)
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
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
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
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
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
@@ -25,13 +34,17 @@
Repostiroy](https://github.com/exoscale/packer-plugin-exoscale). [GH-10709]
### 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
encrypting with KMS and sharing across accounts. [GH-10754]
* builder/azure: Add client_cert_token_timeout option. [GH-10528]
* builder/google: Make Windows password timeout configurable. [GH-10727]
* builder/google: Update public GCP image project as gce-uefi-images are
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/scaleway: add support for timeout in shutdown step. [GH-10503]
* builder/vagrant: Fix logging to be clearer when Vagrant builder overrides
@@ -47,11 +60,21 @@
[GH-10651]
* command/fmt: Adding recursive flag to formatter to format subdirectories.
[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.
[GH-10652]
* core: Update to gopsutil v3.21.1 to allow builds to work for darwin arm64.
[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
* buider/azure: Update builder to ensure a proper clean up Azure temporary
@@ -68,6 +91,8 @@
[GH-10748]
* builder/oracle-oci: Update Oracle Go SDK to fix issue with reading key file.
[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.
[GH-10748]
* builder/proxmox: Fixes issue when using `additional_iso_files` in HCL enabled
@@ -78,11 +103,14 @@
func. [GH-10748]
* builder/vmware: Added a fallback file check when trying to determine the
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
key typing error. This change will now retry to type the key on error
before giving up. [GH-10541]
* core/hcl2_upgrade: Check for nil config map when provisioner/post-processor
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
pause_before. [GH-10654]
* core/hcl2_upgrade: Make json variables using template engines get stored as
@@ -94,6 +122,8 @@
* core: Pin Packer to Golang 1.16 to fix code generation issues. [GH-10702]
* core: Templates previously could not interpolate the environment variable
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.
[GH-10655]
* provisioner/inspec: Add new configuration field `valid_exit_codes` to allow
+1 -1
View File
@@ -49,7 +49,7 @@ package:
@sh -c "$(CURDIR)/scripts/dist.sh $(VERSION)"
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
# to avoid having to tidy our go deps, we `go get` our binaries from a temp
-251
View File
@@ -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")
}
}
-51
View File
@@ -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.")
}
}
-10
View File
@@ -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)
}
-15
View File
@@ -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.")
}
}
-244
View File
@@ -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.")
}
}
-90
View File
@@ -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")
}
}
-401
View File
@@ -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)
}
}
}
-70
View File
@@ -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"])
}
}
-251
View File
@@ -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)
}
}
}
-141
View File
@@ -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
}
-66
View File
@@ -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)
}
}
-46
View File
@@ -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>
-396
View File
@@ -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)
}
-166
View File
@@ -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")
}
}
-137
View File
@@ -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"
}
}
]
}
`
-110
View File
@@ -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")
}
}
}
-29
View File
@@ -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")
}
}
-166
View File
@@ -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
-343
View File
@@ -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")
}
}
-13
View File
@@ -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)
}
+1 -1
View File
@@ -4,7 +4,7 @@ import (
"context"
"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.
+1 -1
View File
@@ -12,7 +12,7 @@ import (
"github.com/hashicorp/packer-plugin-sdk/multistep/commonsteps"
packersdk "github.com/hashicorp/packer-plugin-sdk/packer"
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
+22 -4
View File
@@ -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
@@ -19,8 +19,8 @@ import (
"github.com/hashicorp/packer-plugin-sdk/pathing"
"github.com/hashicorp/packer-plugin-sdk/template/config"
"github.com/hashicorp/packer-plugin-sdk/template/interpolate"
ocicommon "github.com/oracle/oci-go-sdk/common"
ociauth "github.com/oracle/oci-go-sdk/common/auth"
ocicommon "github.com/oracle/oci-go-sdk/v36/common"
ociauth "github.com/oracle/oci-go-sdk/v36/common/auth"
)
type CreateVNICDetails struct {
@@ -46,6 +46,11 @@ type ListImagesRequest struct {
Shape *string `mapstructure:"shape"`
}
type FlexShapeConfig struct {
Ocpus *float32 `mapstructure:"ocpus" required:"false"`
MemoryInGBs *float32 `mapstructure:"memory_in_gbs" required:"false"`
}
type Config struct {
common.PackerConfig `mapstructure:",squash"`
Comm communicator.Config `mapstructure:",squash"`
@@ -91,6 +96,7 @@ type Config struct {
InstanceTags map[string]string `mapstructure:"instance_tags"`
InstanceDefinedTags map[string]map[string]interface{} `mapstructure:"instance_defined_tags"`
Shape string `mapstructure:"shape"`
ShapeConfig FlexShapeConfig `mapstructure:"shape_config"`
BootVolumeSizeInGBs int64 `mapstructure:"disk_size"`
// 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{
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 {
@@ -277,6 +283,18 @@ func (c *Config) Prepare(raws ...interface{}) error {
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) {
errs = packersdk.MultiErrorAppend(
errs, errors.New("'subnet_ocid' must be specified"))
+28 -1
View File
@@ -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
@@ -88,6 +88,7 @@ type FlatConfig struct {
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"`
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"`
Metadata map[string]string `mapstructure:"metadata" cty:"metadata" hcl:"metadata"`
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_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_config": &hcldec.BlockSpec{TypeName: "shape_config", Nested: hcldec.ObjectSpec((*FlatFlexShapeConfig)(nil).HCL2Spec())},
"disk_size": &hcldec.AttrSpec{Name: "disk_size", Type: cty.Number, 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},
@@ -239,6 +241,31 @@ func (*FlatCreateVNICDetails) HCL2Spec() map[string]hcldec.Spec {
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.
// Where the contents of a field with a `mapstructure:,squash` tag are bubbled up.
type FlatListImagesRequest struct {
-76
View File
@@ -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
}
+5 -1
View File
@@ -131,7 +131,7 @@ func TestConfig(t *testing.T) {
t.Run("NoAccessConfig", func(t *testing.T) {
raw := testConfig(cfgFile)
delete(raw, "access_cfg_file")
raw["access_cfg_file"] = "/tmp/random/access/config/file/should/not/exist"
var c Config
errs := c.Prepare(raw)
@@ -140,6 +140,10 @@ func TestConfig(t *testing.T) {
"'user_ocid'", "'tenancy_ocid'", "'fingerprint'", "'key_file'",
}
if errs == nil {
t.Fatalf("Expected errors %q but got none", expectedErrors)
}
s := errs.Error()
for _, expected := range expectedErrors {
if !strings.Contains(s, expected) {
+1 -1
View File
@@ -3,7 +3,7 @@ package oci
import (
"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.
+1 -1
View File
@@ -3,7 +3,7 @@ package oci
import (
"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
+10 -2
View File
@@ -11,8 +11,8 @@ import (
"sync/atomic"
"time"
"github.com/oracle/oci-go-sdk/common"
core "github.com/oracle/oci-go-sdk/core"
"github.com/oracle/oci-go-sdk/v36/common"
core "github.com/oracle/oci-go-sdk/v36/core"
)
// 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,
}
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{
LaunchInstanceDetails: instanceDetails,
RequestMetadata: requestMetadata,
@@ -3,6 +3,8 @@ package oci
import (
"crypto/rand"
"crypto/rsa"
"github.com/oracle/oci-go-sdk/v36/common"
)
// 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) {
return "some_random_region", nil
}
func (p instancePrincipalConfigurationProviderMock) AuthType() (common.AuthConfig, error) {
return common.AuthConfig{
AuthType: common.InstancePrincipal,
IsFromConfigFile: false,
OboToken: nil}, nil
}
+1
View File
@@ -59,6 +59,7 @@ func (c *StorageConfig) AddStorageDevices(existingDevices object.VirtualDeviceLi
}
existingDevices.AssignController(disk, controllers[dc.ControllerIndex])
existingDevices = append(existingDevices, disk)
newDevices = append(newDevices, disk)
}
+46
View File
@@ -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))
}
}
+43
View File
@@ -60,3 +60,46 @@ func TestVirtualMachineDriver_Configure(t *testing.T) {
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())
}
}
-31
View File
@@ -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)
}
}
+13
View File
@@ -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 {
+1 -1
View File
@@ -8,8 +8,8 @@ import (
"runtime"
"testing"
"github.com/hashicorp/packer-plugin-amazon/builder/ebs"
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/null"
"github.com/hashicorp/packer/packer"
+1 -1
View File
@@ -16,9 +16,9 @@ import (
"github.com/hashicorp/go-multierror"
"github.com/hashicorp/hcl/v2/hclwrite"
awscommon "github.com/hashicorp/packer-plugin-amazon/builder/common"
hcl2shim "github.com/hashicorp/packer-plugin-sdk/hcl2helper"
"github.com/hashicorp/packer-plugin-sdk/template"
awscommon "github.com/hashicorp/packer/builder/amazon/common"
"github.com/hashicorp/packer/packer"
"github.com/mitchellh/mapstructure"
"github.com/posener/complete"
-1
View File
@@ -170,7 +170,6 @@ packer {
ui.Say(msg)
}
}
return ret
}
+47 -64
View File
@@ -14,11 +14,6 @@ import (
"github.com/hashicorp/packer-plugin-sdk/plugin"
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"
azurechrootbuilder "github.com/hashicorp/packer/builder/azure/chroot"
azuredtlbuilder "github.com/hashicorp/packer/builder/azure/dtl"
@@ -64,10 +59,7 @@ import (
vsphereclonebuilder "github.com/hashicorp/packer/builder/vsphere/clone"
vsphereisobuilder "github.com/hashicorp/packer/builder/vsphere/iso"
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"
amazonimportpostprocessor "github.com/hashicorp/packer/post-processor/amazon-import"
artificepostprocessor "github.com/hashicorp/packer/post-processor/artifice"
checksumpostprocessor "github.com/hashicorp/packer/post-processor/checksum"
compresspostprocessor "github.com/hashicorp/packer/post-processor/compress"
@@ -108,57 +100,52 @@ type PluginCommand struct {
}
var Builders = map[string]packersdk.Builder{
"alicloud-ecs": new(alicloudecsbuilder.Builder),
"amazon-chroot": new(amazonchrootbuilder.Builder),
"amazon-ebs": new(amazonebsbuilder.Builder),
"amazon-ebssurrogate": new(amazonebssurrogatebuilder.Builder),
"amazon-ebsvolume": new(amazonebsvolumebuilder.Builder),
"amazon-instance": new(amazoninstancebuilder.Builder),
"azure-arm": new(azurearmbuilder.Builder),
"azure-chroot": new(azurechrootbuilder.Builder),
"azure-dtl": new(azuredtlbuilder.Builder),
"cloudstack": new(cloudstackbuilder.Builder),
"digitalocean": new(digitaloceanbuilder.Builder),
"file": new(filebuilder.Builder),
"googlecompute": new(googlecomputebuilder.Builder),
"hcloud": new(hcloudbuilder.Builder),
"hyperone": new(hyperonebuilder.Builder),
"hyperv-iso": new(hypervisobuilder.Builder),
"hyperv-vmcx": new(hypervvmcxbuilder.Builder),
"jdcloud": new(jdcloudbuilder.Builder),
"linode": new(linodebuilder.Builder),
"lxc": new(lxcbuilder.Builder),
"lxd": new(lxdbuilder.Builder),
"ncloud": new(ncloudbuilder.Builder),
"null": new(nullbuilder.Builder),
"oneandone": new(oneandonebuilder.Builder),
"openstack": new(openstackbuilder.Builder),
"oracle-classic": new(oracleclassicbuilder.Builder),
"oracle-oci": new(oracleocibuilder.Builder),
"osc-bsu": new(oscbsubuilder.Builder),
"osc-bsusurrogate": new(oscbsusurrogatebuilder.Builder),
"osc-bsuvolume": new(oscbsuvolumebuilder.Builder),
"osc-chroot": new(oscchrootbuilder.Builder),
"parallels-iso": new(parallelsisobuilder.Builder),
"parallels-pvm": new(parallelspvmbuilder.Builder),
"profitbricks": new(profitbricksbuilder.Builder),
"proxmox": new(proxmoxbuilder.Builder),
"proxmox-clone": new(proxmoxclonebuilder.Builder),
"proxmox-iso": new(proxmoxisobuilder.Builder),
"qemu": new(qemubuilder.Builder),
"scaleway": new(scalewaybuilder.Builder),
"tencentcloud-cvm": new(tencentcloudcvmbuilder.Builder),
"triton": new(tritonbuilder.Builder),
"ucloud-uhost": new(uclouduhostbuilder.Builder),
"vagrant": new(vagrantbuilder.Builder),
"virtualbox-iso": new(virtualboxisobuilder.Builder),
"virtualbox-ovf": new(virtualboxovfbuilder.Builder),
"virtualbox-vm": new(virtualboxvmbuilder.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),
"alicloud-ecs": new(alicloudecsbuilder.Builder),
"azure-arm": new(azurearmbuilder.Builder),
"azure-chroot": new(azurechrootbuilder.Builder),
"azure-dtl": new(azuredtlbuilder.Builder),
"cloudstack": new(cloudstackbuilder.Builder),
"digitalocean": new(digitaloceanbuilder.Builder),
"file": new(filebuilder.Builder),
"googlecompute": new(googlecomputebuilder.Builder),
"hcloud": new(hcloudbuilder.Builder),
"hyperone": new(hyperonebuilder.Builder),
"hyperv-iso": new(hypervisobuilder.Builder),
"hyperv-vmcx": new(hypervvmcxbuilder.Builder),
"jdcloud": new(jdcloudbuilder.Builder),
"linode": new(linodebuilder.Builder),
"lxc": new(lxcbuilder.Builder),
"lxd": new(lxdbuilder.Builder),
"ncloud": new(ncloudbuilder.Builder),
"null": new(nullbuilder.Builder),
"oneandone": new(oneandonebuilder.Builder),
"openstack": new(openstackbuilder.Builder),
"oracle-classic": new(oracleclassicbuilder.Builder),
"oracle-oci": new(oracleocibuilder.Builder),
"osc-bsu": new(oscbsubuilder.Builder),
"osc-bsusurrogate": new(oscbsusurrogatebuilder.Builder),
"osc-bsuvolume": new(oscbsuvolumebuilder.Builder),
"osc-chroot": new(oscchrootbuilder.Builder),
"parallels-iso": new(parallelsisobuilder.Builder),
"parallels-pvm": new(parallelspvmbuilder.Builder),
"profitbricks": new(profitbricksbuilder.Builder),
"proxmox": new(proxmoxbuilder.Builder),
"proxmox-clone": new(proxmoxclonebuilder.Builder),
"proxmox-iso": new(proxmoxisobuilder.Builder),
"qemu": new(qemubuilder.Builder),
"scaleway": new(scalewaybuilder.Builder),
"tencentcloud-cvm": new(tencentcloudcvmbuilder.Builder),
"triton": new(tritonbuilder.Builder),
"ucloud-uhost": new(uclouduhostbuilder.Builder),
"vagrant": new(vagrantbuilder.Builder),
"virtualbox-iso": new(virtualboxisobuilder.Builder),
"virtualbox-ovf": new(virtualboxovfbuilder.Builder),
"virtualbox-vm": new(virtualboxvmbuilder.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{
@@ -184,7 +171,6 @@ var Provisioners = map[string]packersdk.Provisioner{
var PostProcessors = map[string]packersdk.PostProcessor{
"alicloud-import": new(alicloudimportpostprocessor.PostProcessor),
"amazon-import": new(amazonimportpostprocessor.PostProcessor),
"artifice": new(artificepostprocessor.PostProcessor),
"checksum": new(checksumpostprocessor.PostProcessor),
"compress": new(compresspostprocessor.PostProcessor),
@@ -202,10 +188,7 @@ var PostProcessors = map[string]packersdk.PostProcessor{
"yandex-import": new(yandeximportpostprocessor.PostProcessor),
}
var Datasources = map[string]packersdk.Datasource{
"amazon-ami": new(amazonamidatasource.Datasource),
"amazon-secretsmanager": new(amazonsecretsmanagerdatasource.Datasource),
}
var Datasources = map[string]packersdk.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"]
}
}
}
+29 -1
View File
@@ -7,6 +7,14 @@ import (
// still vendored with Packer for now. Importing as library instead of
// forcing use of packer init, until packer v1.8.0
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"
dockerimportpostprocessor "github.com/hashicorp/packer-plugin-docker/post-processor/docker-import"
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"
)
// 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
// Packer core, but are now being imported from their counterpart plugin repos
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
@@ -32,11 +52,19 @@ var VendoredPostProcessors = map[string]packersdk.PostProcessor{
"docker-save": new(dockersavepostprocessor.PostProcessor),
"docker-tag": new(dockertagpostprocessor.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
// set of plugins.
func init() {
for k, v := range VendoredDatasources {
if _, ok := Datasources[k]; ok {
continue
}
Datasources[k] = v
}
for k, v := range VendoredBuilders {
if _, ok := Builders[k]; ok {
continue
-64
View File
@@ -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"
]
}
`
-45
View File
@@ -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)
}
}
+11 -12
View File
@@ -17,7 +17,7 @@ require (
github.com/aliyun/aliyun-oss-go-sdk v0.0.0-20170113022742-e6dbea820a9f
github.com/antihax/optional v1.0.0
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/c2h5oh/datasize v0.0.0-20200112174442-28bbd4740fee
github.com/cheggaaa/pb v1.0.27
@@ -32,26 +32,26 @@ require (
github.com/go-resty/resty/v2 v2.3.0
github.com/gobwas/glob v0.2.3
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/uuid v1.1.2
github.com/gophercloud/gophercloud v0.12.0
github.com/gophercloud/utils v0.0.0-20200508015959-b0167b94122c
github.com/grpc-ecosystem/go-grpc-middleware v1.1.0
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/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-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-uuid v1.0.2
github.com/hashicorp/go-version v1.2.0
github.com/hashicorp/hcl/v2 v2.8.0
github.com/hashicorp/packer-plugin-docker v0.0.2
github.com/hashicorp/packer-plugin-sdk v0.1.1-0.20210323111710-5a7ab7f7a1c0
github.com/hashicorp/hcl/v2 v2.9.1
github.com/hashicorp/packer-plugin-amazon v0.0.1
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/hetznercloud/hcloud-go v1.15.1
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/go-homedir v1.1.0
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/panicwrap v1.0.0
github.com/mitchellh/prefixedio v0.0.0-20151214002211-6e6954073784
github.com/mitchellh/reflectwalk v1.0.0
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/pierrec/lz4 v2.0.5+incompatible
github.com/pkg/errors v0.9.1
@@ -87,7 +86,7 @@ require (
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-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
golang.org/x/crypto v0.0.0-20201221181555-eec23a3978ad
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/oauth2 v0.0.0-20200902213428-5d25da1a8d43
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
google.golang.org/api v0.32.0
google.golang.org/grpc v1.32.0
+30 -17
View File
@@ -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-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/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/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/go.mod h1:S6puKjZ9ZeqUPBv2hEBnMZGcM2J6mOsDRQcmxkMAND0=
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.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.37.15 h1:W7l7gLLMcYRlg6a+uvf3Zz4jYwdqYzhe5ymqwWoOhp4=
github.com/aws/aws-sdk-go v1.37.15/go.mod h1:hcU610XS61/+aQV88ixoOzUoG7v3b31pl2zKMmprdro=
github.com/aws/aws-sdk-go v1.38.0 h1:mqnmtdW8rGIQmp2d0WRFLua0zW0Pel0P6/vd3gJuViY=
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/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=
@@ -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.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.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.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.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=
@@ -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/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/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.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/go.mod h1:xc8u05kyMa3Wjr9eEAsIAo3dg8+LywT5E/Cl7cNS5nU=
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/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.1 h1:dH3aiDG9Jvb5r5+bYHsikaOUIpcM0xvgMXVoDkXMzJM=
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/go.mod h1:Abjk0jbRkDaNCzsRhOv2iDCofYpX1eVsjozoiK63qLA=
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/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.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.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/go.mod h1:09jT3Y/OIsjTjQ2+3bkVNPDKqWcGIYYvjB2BEKVUdvc=
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/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.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.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/mdns v1.0.0/go.mod h1:tL+uN++7HEJ6SQLQ2/p+z2pH24WQKWjBPkE0mNTz8vQ=
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.20210217093213-201869d627bf/go.mod h1:+EWPPcqee4h8S/y913Dnta1eJkgiqsGXBQgB75A2qV0=
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-docker v0.0.2/go.mod h1:A2p9qztS4n88KsNF+qBM7BWw2HndW636GpFIjNSvbKM=
github.com/hashicorp/packer-plugin-amazon v0.0.1 h1:EuyjNK9bL7WhQeIJzhBJxOx8nyc61ai5UbOsb1PIVwI=
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.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=
@@ -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.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.1.1-0.20210323111710-5a7ab7f7a1c0 h1:nw4RqF7C4jUWGo5PGDG4dSclU+G/vXyVBHIu5j7akd4=
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.0/go.mod h1:CFsC20uZjtER/EnTn/CSMKD0kEdkqOVev8mtOmfnZiI=
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.9.2 h1:yJoyfZXo4Pk2p/M/viW+YLibBFiIbKoP79gu7kDAFP0=
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/go.mod h1:ZXFpozHsX6DPmq2I0TCekCxypsnAUbP2oI0UX1GXzOo=
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/iochan v1.0.0 h1:C+X3KsSTLFVBr/tK1eYN/vs4rJcvsiLU338UhYPJWeY=
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/go.mod h1:vsDQFd/mU46D+Z4whnwzcISnGGzXWMclvtLoiIKAKIo=
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 v24.3.0+incompatible h1:x4mcfb4agelf1O4/1/auGlZ1lr97jXRSSN5MxTgG/zU=
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 h1:oBaN/FnBDy3ohMyVZ/rKfekYxnyksG2KK0YAhT5HSnk=
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/go.mod h1:5AqqNH1X8zCHescKVlpSHRzrat1KCKDXqZoQPe8fY3A=
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.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.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.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/go.mod h1:IP3Ylp0wQpYm50IHK8OZWKMu6sPJIUgKa8XhiVHura0=
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-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-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-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-20201126162022-7de9c90e9dd1 h1:v+OssWQX+hTHEmOBgwxdZxK4zHq3yOs8F9J7mk0PY8E=
golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo=
+16 -11
View File
@@ -76,12 +76,15 @@ const (
// 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
// can happen.
// Parse will first Parse packer and variables blocks, omitting the rest, which
// 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
// 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) {
var files []*hcl.File
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)...)
}
// parse the actual content // rest
for _, file := range cfg.files {
diags = append(diags, cfg.parser.parseConfig(file, cfg)...)
}
return cfg, diags
}
@@ -291,7 +290,7 @@ func filterVarsFromLogs(inputOrLocal Variables) {
if !variable.Sensitive {
continue
}
value, _ := variable.Value()
value := variable.Value()
_ = cty.Walk(value, func(_ cty.Path, nested cty.Value) (bool, error) {
if nested.IsWhollyKnown() && !nested.IsNull() && nested.Type().Equals(cty.String) {
packersdk.LogSecretFilter.Set(nested.AsString())
@@ -311,9 +310,9 @@ func (cfg *PackerConfig) Initialize(opts packer.InitializeOptions) hcl.Diagnosti
return diags
}
_, moreDiags = cfg.InputVariables.Values()
moreDiags = cfg.InputVariables.ValidateValues()
diags = append(diags, moreDiags...)
_, moreDiags = cfg.LocalVariables.Values()
moreDiags = cfg.LocalVariables.ValidateValues()
diags = append(diags, moreDiags...)
diags = append(diags, cfg.evaluateDatasources(opts.SkipDatasourcesExecution)...)
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.LocalVariables)
// parse the actual content // rest
for _, file := range cfg.files {
diags = append(diags, cfg.parser.parseConfig(file, cfg)...)
}
diags = append(diags, cfg.initializeBlocks()...)
return diags
@@ -332,6 +336,7 @@ func (p *Parser) parseConfig(f *hcl.File, cfg *PackerConfig) hcl.Diagnostics {
var diags hcl.Diagnostics
body := f.Body
body = dynblock.Expand(body, cfg.EvalContext(DatasourceContext, nil))
content, moreDiags := body.Content(configSchema)
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 {
var diags hcl.Diagnostics
body := dynblock.Expand(file.Body, cfg.EvalContext(DatasourceContext, nil))
body := file.Body
content, moreDiags := body.Content(configSchema)
diags = append(diags, moreDiags...)
+9 -13
View File
@@ -7,7 +7,7 @@ import (
"runtime"
"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"
plugingetter "github.com/hashicorp/packer/packer/plugin-getter"
)
@@ -109,7 +109,7 @@ func (cfg *PackerConfig) detectPluginBinaries() 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
for _, build := range cfg.Builds {
@@ -129,12 +129,16 @@ func (cfg *PackerConfig) initializeBlocks() hcl.Diagnostics {
sourceDefinition, found := cfg.Sources[srcUsage.SourceRef]
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{
Summary: "Unknown " + sourceLabel + " " + srcUsage.String(),
Summary: "Unknown " + sourceLabel + " " + srcUsage.SourceRef.String(),
Subject: build.HCL2Ref.DefRange.Ptr(),
Severity: hcl.DiagError,
Detail: fmt.Sprintf("Known: %v", cfg.Sources),
// TODO: show known sources as a string slice here ^.
Detail: detail,
})
continue
}
@@ -144,8 +148,6 @@ func (cfg *PackerConfig) initializeBlocks() hcl.Diagnostics {
// merge additions into source definition to get a new body.
body = hcl.MergeBodies([]hcl.Body{body, srcUsage.Body})
}
// expand any dynamic block.
body = dynblock.Expand(body, cfg.EvalContext(BuildContext, nil))
srcUsage.Body = body
}
@@ -159,8 +161,6 @@ func (cfg *PackerConfig) initializeBlocks() hcl.Diagnostics {
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 {
@@ -172,8 +172,6 @@ func (cfg *PackerConfig) initializeBlocks() hcl.Diagnostics {
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 {
@@ -186,8 +184,6 @@ func (cfg *PackerConfig) initializeBlocks() hcl.Diagnostics {
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))
}
}
+4 -2
View File
@@ -81,6 +81,7 @@ type Builds []*BuildBlock
// load the references to the contents of the build block.
func (p *Parser) decodeBuildConfig(block *hcl.Block, cfg *PackerConfig) (*BuildBlock, hcl.Diagnostics) {
build := &BuildBlock{}
body := block.Body
var b struct {
Name string `hcl:"name,optional"`
@@ -88,7 +89,7 @@ func (p *Parser) decodeBuildConfig(block *hcl.Block, cfg *PackerConfig) (*BuildB
FromSources []string `hcl:"sources,optional"`
Config hcl.Body `hcl:",remain"`
}
diags := gohcl.DecodeBody(block.Body, nil, &b)
diags := gohcl.DecodeBody(body, nil, &b)
if diags.HasErrors() {
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})
}
content, moreDiags := b.Config.Content(buildSchema)
body = b.Config
content, moreDiags := body.Content(buildSchema)
diags = append(diags, moreDiags...)
if diags.HasErrors() {
return nil, diags
+4 -4
View File
@@ -91,8 +91,8 @@ const (
// decoder in order to tell what is the actual value of a var or a local and
// the list of defined functions.
func (cfg *PackerConfig) EvalContext(ctx BlockContext, variables map[string]cty.Value) *hcl.EvalContext {
inputVariables, _ := cfg.InputVariables.Values()
localVariables, _ := cfg.LocalVariables.Values()
inputVariables := cfg.InputVariables.Values()
localVariables := cfg.LocalVariables.Values()
ectx := &hcl.EvalContext{
Functions: Functions(cfg.Basedir),
Variables: map[string]cty.Value{
@@ -594,7 +594,7 @@ func (p *PackerConfig) printVariables() string {
sort.Strings(keys)
for _, key := range keys {
v := p.InputVariables[key]
val, _ := v.Value()
val := v.Value()
fmt.Fprintf(out, "var.%s: %q\n", v.Name, PrintableCtyValue(val))
}
out.WriteString("\n> local-variables:\n\n")
@@ -602,7 +602,7 @@ func (p *PackerConfig) printVariables() string {
sort.Strings(keys)
for _, key := range keys {
v := p.LocalVariables[key]
val, _ := v.Value()
val := v.Value()
fmt.Fprintf(out, "local.%s: %q\n", v.Name, PrintableCtyValue(val))
}
return out.String()
+2 -59
View File
@@ -538,65 +538,8 @@ func TestParser_no_init(t *testing.T) {
Type: cty.List(cty.String),
},
},
Sources: map[SourceRef]SourceBlock{
refVBIsoUbuntu1204: {Type: "virtualbox-iso", Name: "ubuntu-1204"},
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",
},
},
},
},
},
Sources: nil,
Builds: nil,
},
false, false,
[]packersdk.Build{},
+10
View File
@@ -2,6 +2,7 @@ package hcl2template
import (
"fmt"
"sort"
"strconv"
"github.com/hashicorp/hcl/v2"
@@ -170,3 +171,12 @@ var NoSource SourceRef
func (r SourceRef) String() string {
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
}
+25 -9
View File
@@ -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.
func (v *Variable) Value() (cty.Value, hcl.Diagnostics) {
func (v *Variable) Value() cty.Value {
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,
Summary: fmt.Sprintf("Unset variable %q", v.Name),
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(),
}}
}
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
@@ -177,15 +187,21 @@ func (variables Variables) Keys() []string {
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{}
var diags hcl.Diagnostics
for k, v := range variables {
value, moreDiags := v.Value()
diags = append(diags, moreDiags...)
value := v.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
+1 -1
View File
@@ -868,7 +868,7 @@ func TestVariables_collectVariableValues(t *testing.T) {
}
values := map[string]cty.Value{}
for k, v := range tt.variables {
value, diag := v.Value()
value, diag := v.Value(), v.ValidateValue()
if diag != nil {
t.Fatalf("Value %s: %v", k, diag)
}
@@ -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),
}
if p.config.BoxDownloadUrl == "" {
steps = append(steps, new(stepPrepareUpload), new(stepUpload))
if !p.config.NoDirectUpload {
steps = append(steps, new(stepConfirmUpload))
}
steps = append(steps,
new(stepPrepareUpload),
new(stepUpload),
new(stepConfirmUpload))
}
steps = append(steps, new(stepReleaseVersion))
@@ -10,6 +10,7 @@ import (
"net/http"
"net/http/httptest"
"os"
"runtime"
"strings"
"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) {
files := tarFiles{
{"foo.txt", "This is a foo file"},
@@ -697,3 +830,21 @@ func createBox(files tarFiles) (boxfile *os.File, err error) {
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)
upload := state.Get("upload").(*Upload)
url := upload.CallbackPath
config := state.Get("config").(*Config)
if config.NoDirectUpload {
return multistep.ActionContinue
}
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 resp == nil || resp.Body == nil {
state.Put("error", "No response from server.")
state.Put("error", fmt.Errorf("No response from server."))
} else {
cloudErrors := &VagrantCloudErrors{}
err = decodeBody(resp, cloudErrors)
@@ -3,11 +3,14 @@ package vagrantcloud
import (
"context"
"fmt"
"os"
"github.com/hashicorp/packer-plugin-sdk/multistep"
packersdk "github.com/hashicorp/packer-plugin-sdk/packer"
)
const VAGRANT_CLOUD_DIRECT_UPLOAD_LIMIT = 5368709120 // Upload limit is 5G
type Upload struct {
UploadPath string `json:"upload_path"`
CallbackPath string `json:"callback"`
@@ -25,6 +28,18 @@ func (s *stepPrepareUpload) Run(ctx context.Context, state multistep.StateBag) m
provider := state.Get("provider").(*Provider)
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)
if !config.NoDirectUpload {
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 resp == nil || resp.Body == nil {
state.Put("error", "No response from server.")
state.Put("error", fmt.Errorf("No response from server."))
} else {
cloudErrors := &VagrantCloudErrors{}
err = decodeBody(resp, cloudErrors)
+1 -1
View File
@@ -4,7 +4,7 @@
# Determine the arch/os combos we're building for
ALL_XC_ARCH="386 amd64 arm arm64 ppc64le mips mips64 mipsle mipsle64 s390x"
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
set -e
-290
View File
@@ -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 oclock..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 #
;
}%%
File diff suppressed because it is too large Load Diff
+525
View File
@@ -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 workers 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 oclock..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] (🚹..🚾) mens 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 #
;
}%%
@@ -2,7 +2,7 @@ package textseg
//go:generate go run make_tables.go -output tables.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/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/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/emoji/emoji-data.txt -m Emoji -p "Extended_Pictographic" -o emoji_table.rl
//go:generate ragel -Z grapheme_clusters.rl
//go:generate gofmt -w grapheme_clusters.go
File diff suppressed because it is too large Load Diff
@@ -1,5 +1,5 @@
# The following Ragel file was autogenerated with unicode2ragel.rb
# from: https://www.unicode.org/Public/12.0.0/ucd/auxiliary/GraphemeBreakProperty.txt
# from: https://www.unicode.org/Public/13.0.0/ucd/auxiliary/GraphemeBreakProperty.txt
#
# It defines ["Prepend", "CR", "LF", "Control", "Extend", "Regional_Indicator", "SpacingMark", "L", "V", "T", "LV", "LVT", "ZWJ"].
#
@@ -18,6 +18,8 @@
| 0xF0 0x91 0x82 0xBD #Cf KAITHI NUMBER SIGN
| 0xF0 0x91 0x83 0x8D #Cf KAITHI NUMBER SIGN ABOVE
| 0xF0 0x91 0x87 0x82..0x83 #Lo [2] SHARADA SIGN JIHVAMULIYA..SHARA...
| 0xF0 0x91 0xA4 0xBF #Lo DIVES AKURU PREFIXED NASAL SIGN
| 0xF0 0x91 0xA5 0x81 #Lo DIVES AKURU INITIAL RA
| 0xF0 0x91 0xA8 0xBA #Lo ZANABAZAR SQUARE CLUSTER-INITIAL L...
| 0xF0 0x91 0xAA 0x84..0x89 #Lo [6] SOYOMBO SIGN JIHVAMULIYA..SOYOM...
| 0xF0 0x91 0xB5 0x86 #Lo MASARAM GONDI REPHA
@@ -130,7 +132,7 @@
| 0xE0 0xAC 0xBF #Mn ORIYA VOWEL SIGN I
| 0xE0 0xAD 0x81..0x84 #Mn [4] ORIYA VOWEL SIGN U..ORIYA VOWEL SI...
| 0xE0 0xAD 0x8D #Mn ORIYA SIGN VIRAMA
| 0xE0 0xAD 0x96 #Mn ORIYA AI LENGTH MARK
| 0xE0 0xAD 0x95..0x96 #Mn [2] ORIYA SIGN OVERLINE..ORIYA AI LENG...
| 0xE0 0xAD 0x97 #Mc ORIYA AU LENGTH MARK
| 0xE0 0xAD 0xA2..0xA3 #Mn [2] ORIYA VOWEL SIGN VOCALIC L..ORIYA ...
| 0xE0 0xAE 0x82 #Mn TAMIL SIGN ANUSVARA
@@ -161,6 +163,7 @@
| 0xE0 0xB5 0x8D #Mn MALAYALAM SIGN VIRAMA
| 0xE0 0xB5 0x97 #Mc MALAYALAM AU LENGTH MARK
| 0xE0 0xB5 0xA2..0xA3 #Mn [2] MALAYALAM VOWEL SIGN VOCALIC L..MA...
| 0xE0 0xB6 0x81 #Mn SINHALA SIGN CANDRABINDU
| 0xE0 0xB7 0x8A #Mn SINHALA SIGN AL-LAKUNA
| 0xE0 0xB7 0x8F #Mc SINHALA VOWEL SIGN AELA-PILLA
| 0xE0 0xB7 0x92..0x94 #Mn [3] SINHALA VOWEL SIGN KETTI IS-PILLA....
@@ -221,6 +224,8 @@
| 0xE1 0xA9 0xBF #Mn TAI THAM COMBINING CRYPTOGRAMMIC DOT
| 0xE1 0xAA 0xB0..0xBD #Mn [14] COMBINING DOUBLED CIRCUMFLEX ACCEN...
| 0xE1 0xAA 0xBE #Me COMBINING PARENTHESES OVERLAY
| 0xE1 0xAA 0xBF..0xFF #Mn [2] COMBINING LATIN SMALL LETTER W BEL...
| 0xE1 0xAB 0x00..0x80 #
| 0xE1 0xAC 0x80..0x83 #Mn [4] BALINESE SIGN ULU RICEM..BALINESE ...
| 0xE1 0xAC 0xB4 #Mn BALINESE SIGN REREKAN
| 0xE1 0xAC 0xB5 #Mc BALINESE VOWEL SIGN TEDUNG
@@ -267,6 +272,7 @@
| 0xEA 0xA0 0x86 #Mn SYLOTI NAGRI SIGN HASANTA
| 0xEA 0xA0 0x8B #Mn SYLOTI NAGRI SIGN ANUSVARA
| 0xEA 0xA0 0xA5..0xA6 #Mn [2] SYLOTI NAGRI VOWEL SIGN U..SYLOTI ...
| 0xEA 0xA0 0xAC #Mn SYLOTI NAGRI SIGN ALTERNATE HASANTA
| 0xEA 0xA3 0x84..0x85 #Mn [2] SAURASHTRA SIGN VIRAMA..SAURASHTRA...
| 0xEA 0xA3 0xA0..0xB1 #Mn [18] COMBINING DEVANAGARI DIGIT ZERO..C...
| 0xEA 0xA3 0xBF #Mn DEVANAGARI VOWEL SIGN AY
@@ -307,6 +313,7 @@
| 0xF0 0x90 0xA8 0xBF #Mn KHAROSHTHI VIRAMA
| 0xF0 0x90 0xAB 0xA5..0xA6 #Mn [2] MANICHAEAN ABBREVIATION MARK AB...
| 0xF0 0x90 0xB4 0xA4..0xA7 #Mn [4] HANIFI ROHINGYA SIGN HARBAHAY.....
| 0xF0 0x90 0xBA 0xAB..0xAC #Mn [2] YEZIDI COMBINING HAMZA MARK..YE...
| 0xF0 0x90 0xBD 0x86..0x90 #Mn [11] SOGDIAN COMBINING DOT BELOW..SO...
| 0xF0 0x91 0x80 0x81 #Mn BRAHMI SIGN ANUSVARA
| 0xF0 0x91 0x80 0xB8..0xFF #Mn [15] BRAHMI VOWEL SIGN AA..BRAHMI VI...
@@ -322,6 +329,7 @@
| 0xF0 0x91 0x86 0x80..0x81 #Mn [2] SHARADA SIGN CANDRABINDU..SHARA...
| 0xF0 0x91 0x86 0xB6..0xBE #Mn [9] SHARADA VOWEL SIGN U..SHARADA V...
| 0xF0 0x91 0x87 0x89..0x8C #Mn [4] SHARADA SANDHI MARK..SHARADA EX...
| 0xF0 0x91 0x87 0x8F #Mn SHARADA SIGN INVERTED CANDRABINDU
| 0xF0 0x91 0x88 0xAF..0xB1 #Mn [3] KHOJKI VOWEL SIGN U..KHOJKI VOW...
| 0xF0 0x91 0x88 0xB4 #Mn KHOJKI SIGN ANUSVARA
| 0xF0 0x91 0x88 0xB6..0xB7 #Mn [2] KHOJKI SIGN NUKTA..KHOJKI SIGN ...
@@ -365,6 +373,10 @@
| 0xF0 0x91 0x9C 0xA7..0xAB #Mn [5] AHOM VOWEL SIGN AW..AHOM SIGN K...
| 0xF0 0x91 0xA0 0xAF..0xB7 #Mn [9] DOGRA VOWEL SIGN U..DOGRA SIGN ...
| 0xF0 0x91 0xA0 0xB9..0xBA #Mn [2] DOGRA SIGN VIRAMA..DOGRA SIGN N...
| 0xF0 0x91 0xA4 0xB0 #Mc DIVES AKURU VOWEL SIGN AA
| 0xF0 0x91 0xA4 0xBB..0xBC #Mn [2] DIVES AKURU SIGN ANUSVARA..DIVE...
| 0xF0 0x91 0xA4 0xBE #Mn DIVES AKURU VIRAMA
| 0xF0 0x91 0xA5 0x83 #Mn DIVES AKURU SIGN NUKTA
| 0xF0 0x91 0xA7 0x94..0x97 #Mn [4] NANDINAGARI VOWEL SIGN U..NANDI...
| 0xF0 0x91 0xA7 0x9A..0x9B #Mn [2] NANDINAGARI VOWEL SIGN E..NANDI...
| 0xF0 0x91 0xA7 0xA0 #Mn NANDINAGARI SIGN VIRAMA
@@ -397,6 +409,7 @@
| 0xF0 0x96 0xAC 0xB0..0xB6 #Mn [7] PAHAWH HMONG MARK CIM TUB..PAHA...
| 0xF0 0x96 0xBD 0x8F #Mn MIAO SIGN CONSONANT MODIFIER BAR
| 0xF0 0x96 0xBE 0x8F..0x92 #Mn [4] MIAO TONE RIGHT..MIAO TONE BELOW
| 0xF0 0x96 0xBF 0xA4 #Mn KHITAN SMALL SCRIPT FILLER
| 0xF0 0x9B 0xB2 0x9D..0x9E #Mn [2] DUPLOYAN THICK LETTER SELECTOR....
| 0xF0 0x9D 0x85 0xA5 #Mc MUSICAL SYMBOL COMBINING STEM
| 0xF0 0x9D 0x85 0xA7..0xA9 #Mn [3] MUSICAL SYMBOL COMBINING TREMOL...
@@ -548,6 +561,7 @@
| 0xF0 0x91 0x86 0xB3..0xB5 #Mc [3] SHARADA VOWEL SIGN AA..SHARADA ...
| 0xF0 0x91 0x86 0xBF..0xFF #Mc [2] SHARADA VOWEL SIGN AU..SHARADA ...
| 0xF0 0x91 0x87 0x00..0x80 #
| 0xF0 0x91 0x87 0x8E #Mc SHARADA VOWEL SIGN PRISHTHAMATRA E
| 0xF0 0x91 0x88 0xAC..0xAE #Mc [3] KHOJKI VOWEL SIGN AA..KHOJKI VO...
| 0xF0 0x91 0x88 0xB2..0xB3 #Mc [2] KHOJKI VOWEL SIGN O..KHOJKI VOW...
| 0xF0 0x91 0x88 0xB5 #Mc KHOJKI SIGN VIRAMA
@@ -579,6 +593,11 @@
| 0xF0 0x91 0x9C 0xA6 #Mc AHOM VOWEL SIGN E
| 0xF0 0x91 0xA0 0xAC..0xAE #Mc [3] DOGRA VOWEL SIGN AA..DOGRA VOWE...
| 0xF0 0x91 0xA0 0xB8 #Mc DOGRA SIGN VISARGA
| 0xF0 0x91 0xA4 0xB1..0xB5 #Mc [5] DIVES AKURU VOWEL SIGN I..DIVES...
| 0xF0 0x91 0xA4 0xB7..0xB8 #Mc [2] DIVES AKURU VOWEL SIGN AI..DIVE...
| 0xF0 0x91 0xA4 0xBD #Mc DIVES AKURU SIGN HALANTA
| 0xF0 0x91 0xA5 0x80 #Mc DIVES AKURU MEDIAL YA
| 0xF0 0x91 0xA5 0x82 #Mc DIVES AKURU MEDIAL RA
| 0xF0 0x91 0xA7 0x91..0x93 #Mc [3] NANDINAGARI VOWEL SIGN AA..NAND...
| 0xF0 0x91 0xA7 0x9C..0x9F #Mc [4] NANDINAGARI VOWEL SIGN O..NANDI...
| 0xF0 0x91 0xA7 0xA4 #Mc NANDINAGARI VOWEL SIGN PRISHTHAMAT...
@@ -596,6 +615,7 @@
| 0xF0 0x91 0xBB 0xB5..0xB6 #Mc [2] MAKASAR VOWEL SIGN E..MAKASAR V...
| 0xF0 0x96 0xBD 0x91..0xFF #Mc [55] MIAO SIGN ASPIRATION..MIAO VOWE...
| 0xF0 0x96 0xBE 0x00..0x87 #
| 0xF0 0x96 0xBF 0xB0..0xB1 #Mc [2] VIETNAMESE ALTERNATE READING MA...
| 0xF0 0x9D 0x85 0xA6 #Mc MUSICAL SYMBOL COMBINING SPRECHGES...
| 0xF0 0x9D 0x85 0xAD #Mc MUSICAL SYMBOL COMBINING AUGMENTAT...
;
@@ -79,7 +79,7 @@ end
# range and description.
def each_alpha( url, property )
open( url ) do |file|
URI.open( url ) do |file|
file.each_line do |line|
next if line =~ /^#/;
next if line !~ /; #{property} *#/;

Some files were not shown because too many files have changed in this diff Show More