From 0785c2f6fca9c22bf25528e0176042799dd79df9 Mon Sep 17 00:00:00 2001 From: Adrien Delorme Date: Tue, 17 Dec 2019 11:25:56 +0100 Subject: [PATCH] build using HCL2 (#8423) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit This follows #8232 which added the code to generate the code required to parse HCL files for each packer component. All old config files of packer will keep on working the same. Packer takes one argument. When a directory is passed, all files in the folder with a name ending with “.pkr.hcl” or “.pkr.json” will be parsed using the HCL2 format. When a file ending with “.pkr.hcl” or “.pkr.json” is passed it will be parsed using the HCL2 format. For every other case; the old packer style will be used. ## 1. the hcl2template pkg can create a packer.Build from a set of HCL (v2) files I had to make the packer.coreBuild (which is our one and only packer.Build ) a public struct with public fields ## 2. Components interfaces get a new ConfigSpec Method to read a file from an HCL file. This is a breaking change for packer plugins. a packer component can be a: builder/provisioner/post-processor each component interface now gets a `ConfigSpec() hcldec.ObjectSpec` which allows packer to tell what is the layout of the hcl2 config meant to configure that specific component. This ObjectSpec is sent through the wire (RPC) and a cty.Value is now sent through the already existing configuration entrypoints: Provisioner.Prepare(raws ...interface{}) error Builder.Prepare(raws ...interface{}) ([]string, error) PostProcessor.Configure(raws ...interface{}) error close #1768 Example hcl files: ```hcl // file amazon-ebs-kms-key/run.pkr.hcl build { sources = [ "source.amazon-ebs.first", ] provisioner "shell" { inline = [ "sleep 5" ] } post-processor "shell-local" { inline = [ "sleep 5" ] } } // amazon-ebs-kms-key/source.pkr.hcl source "amazon-ebs" "first" { ami_name = "hcl2-test" region = "us-east-1" instance_type = "t2.micro" kms_key_id = "c729958f-c6ba-44cd-ab39-35ab68ce0a6c" encrypt_boot = true source_ami_filter { filters { virtualization-type = "hvm" name = "amzn-ami-hvm-????.??.?.????????-x86_64-gp2" root-device-type = "ebs" } most_recent = true owners = ["amazon"] } launch_block_device_mappings { device_name = "/dev/xvda" volume_size = 20 volume_type = "gp2" delete_on_termination = "true" } launch_block_device_mappings { device_name = "/dev/xvdf" volume_size = 500 volume_type = "gp2" delete_on_termination = true encrypted = true } ami_regions = ["eu-central-1"] run_tags { Name = "packer-solr-something" stack-name = "DevOps Tools" } communicator = "ssh" ssh_pty = true ssh_username = "ec2-user" associate_public_ip_address = true } ``` --- builder/alicloud/ecs/builder.go | 3 + builder/alicloud/ecs/builder.hcl2spec.go | 24 +- builder/alicloud/ecs/step_create_snapshot.go | 2 +- builder/amazon/chroot/builder.go | 3 + builder/amazon/chroot/builder.hcl2spec.go | 11 +- .../amazon/common/access_config.hcl2spec.go | 9 +- .../amazon/common/block_device.hcl2spec.go | 9 +- builder/amazon/common/run_config.hcl2spec.go | 54 ++- .../common/step_iam_instance_profile.go | 3 +- builder/amazon/ebs/builder.go | 3 + builder/amazon/ebs/builder.hcl2spec.go | 17 +- builder/amazon/ebssurrogate/builder.go | 4 + .../amazon/ebssurrogate/builder.hcl2spec.go | 331 ++++++++++++++++ builder/amazon/ebsvolume/builder.go | 4 + builder/amazon/ebsvolume/builder.hcl2spec.go | 258 +++++++++++++ builder/amazon/instance/builder.go | 4 + builder/amazon/instance/builder.hcl2spec.go | 267 +++++++++++++ builder/azure/arm/builder.go | 39 +- builder/azure/arm/builder_test.go | 2 +- builder/azure/arm/config.go | 35 +- builder/azure/arm/config.hcl2spec.go | 38 +- builder/azure/arm/config_test.go | 233 ++++++++---- builder/azure/arm/resource_resolver_test.go | 15 +- .../step_publish_to_shared_image_gallery.go | 1 + ...ep_publish_to_shared_image_gallery_test.go | 3 +- builder/azure/arm/template_factory_test.go | 100 +++-- builder/azure/chroot/builder.go | 4 + builder/azure/chroot/builder.hcl2spec.go | 92 +++++ builder/azure/chroot/diskattacher_test.go | 3 +- builder/azure/chroot/step_create_image.go | 3 +- builder/azure/common/client/platform_image.go | 5 +- builder/cloudstack/builder.go | 11 +- builder/cloudstack/builder_test.go | 4 +- builder/cloudstack/config.go | 9 +- builder/cloudstack/config.hcl2spec.go | 13 +- builder/cloudstack/config_test.go | 3 +- builder/digitalocean/builder.go | 6 +- builder/digitalocean/config.go | 9 +- builder/digitalocean/config.hcl2spec.go | 13 +- builder/docker/builder.go | 8 +- builder/docker/config.go | 9 +- builder/docker/config.hcl2spec.go | 13 +- builder/docker/config_test.go | 34 +- builder/file/builder.go | 8 +- builder/file/config.go | 9 +- builder/file/config.hcl2spec.go | 9 +- builder/file/config_test.go | 9 +- builder/googlecompute/builder.go | 13 +- builder/googlecompute/config.go | 9 +- builder/googlecompute/config.hcl2spec.go | 22 +- builder/googlecompute/config_test.go | 28 +- builder/hcloud/builder.go | 6 +- builder/hcloud/config.go | 10 +- builder/hcloud/config.hcl2spec.go | 22 +- builder/hyperone/builder.go | 7 +- builder/hyperone/config.go | 17 +- builder/hyperone/config.hcl2spec.go | 13 +- .../hyperv/common/output_config.hcl2spec.go | 9 +- builder/hyperv/iso/builder.go | 4 +- builder/hyperv/iso/builder.hcl2spec.go | 13 +- builder/hyperv/vmcx/builder.go | 4 +- builder/hyperv/vmcx/builder.hcl2spec.go | 13 +- builder/jdcloud/builder.go | 4 + builder/jdcloud/common.hcl2spec.go | 13 +- builder/jdcloud/step_config_credentials.go | 3 +- builder/jdcloud/step_create_image.go | 3 +- builder/jdcloud/step_create_instance.go | 5 +- builder/jdcloud/step_stop_instance.go | 1 + builder/jdcloud/step_validate_parameters.go | 1 + builder/linode/builder.go | 8 +- builder/linode/config.go | 9 +- builder/linode/config.hcl2spec.go | 13 +- builder/lxc/builder.go | 8 +- builder/lxc/config.go | 12 +- builder/lxc/config.hcl2spec.go | 66 ++++ builder/lxd/builder.go | 8 +- builder/lxd/config.go | 11 +- builder/lxd/config.hcl2spec.go | 9 +- builder/ncloud/builder.go | 36 +- builder/ncloud/config.go | 11 +- builder/ncloud/config.hcl2spec.go | 13 +- builder/ncloud/config_test.go | 12 +- builder/null/builder.go | 8 +- builder/null/config.go | 9 +- builder/null/config.hcl2spec.go | 13 +- builder/null/config_test.go | 21 +- builder/oneandone/builder.go | 8 +- builder/oneandone/config.go | 11 +- builder/oneandone/config.hcl2spec.go | 13 +- builder/openstack/builder.go | 3 + builder/openstack/builder.hcl2spec.go | 31 +- builder/oracle/classic/builder.go | 12 +- ...config.hcl2spec.go => builder.hcl2spec.go} | 13 +- builder/oracle/classic/config.go | 13 +- builder/oracle/classic/config_test.go | 9 +- builder/oracle/oci/builder.go | 12 +- builder/oracle/oci/config.go | 15 +- builder/oracle/oci/config.hcl2spec.go | 13 +- builder/oracle/oci/config_test.go | 33 +- builder/oracle/oci/step_test.go | 5 +- builder/osc/bsu/builder.go | 3 + builder/osc/bsu/builder.hcl2spec.go | 17 +- builder/osc/bsusurrogate/builder.go | 5 + builder/osc/bsusurrogate/builder.hcl2spec.go | 264 +++++++++++++ builder/osc/bsuvolume/builder.go | 3 + builder/osc/bsuvolume/builder.hcl2spec.go | 24 +- builder/osc/chroot/builder.go | 3 + builder/osc/chroot/builder.hcl2spec.go | 11 +- builder/osc/common/run_config.hcl2spec.go | 43 ++- builder/parallels/iso/builder.go | 3 + builder/parallels/iso/builder.hcl2spec.go | 13 +- builder/parallels/pvm/builder.go | 9 +- builder/parallels/pvm/config.go | 9 +- builder/parallels/pvm/config.hcl2spec.go | 13 +- builder/parallels/pvm/config_test.go | 22 +- builder/profitbricks/builder.go | 8 +- builder/profitbricks/config.go | 9 +- builder/profitbricks/config.hcl2spec.go | 13 +- builder/proxmox/builder.go | 7 +- builder/proxmox/config.go | 9 +- builder/proxmox/config.hcl2spec.go | 35 +- builder/proxmox/config_test.go | 3 +- builder/qemu/builder.go | 3 + builder/qemu/builder.hcl2spec.go | 13 +- builder/scaleway/builder.go | 8 +- builder/scaleway/config.go | 9 +- builder/scaleway/config.hcl2spec.go | 13 +- builder/tencentcloud/cvm/builder.go | 5 +- builder/tencentcloud/cvm/builder.hcl2spec.go | 15 +- .../tencentcloud/cvm/run_config.hcl2spec.go | 9 +- builder/triton/builder.go | 3 + builder/triton/config.hcl2spec.go | 13 +- .../triton/source_machine_config.hcl2spec.go | 9 +- builder/ucloud/common/artifact.go | 5 +- builder/ucloud/common/artifact_test.go | 3 +- builder/ucloud/common/client.go | 2 +- .../ucloud/common/image_config.hcl2spec.go | 9 +- builder/ucloud/common/utils.go | 3 +- builder/ucloud/uhost/builder.go | 3 + builder/ucloud/uhost/builder.hcl2spec.go | 15 +- builder/ucloud/uhost/builder_acc_test.go | 5 +- builder/ucloud/uhost/builder_test.go | 5 +- .../ucloud/uhost/step_check_source_image.go | 1 + .../uhost/step_config_security_group.go | 1 + builder/ucloud/uhost/step_config_subnet.go | 1 + builder/ucloud/uhost/step_config_vpc.go | 1 + builder/ucloud/uhost/step_copy_image.go | 5 +- builder/ucloud/uhost/step_create_image.go | 3 +- builder/ucloud/uhost/step_create_instance.go | 7 +- builder/ucloud/uhost/step_pre_validate.go | 1 + builder/ucloud/uhost/step_stop_instance.go | 3 +- builder/vagrant/builder.go | 7 +- builder/vagrant/builder.hcl2spec.go | 13 +- builder/vagrant/builder_test.go | 3 +- builder/virtualbox/iso/builder.go | 3 + builder/virtualbox/iso/builder.hcl2spec.go | 13 +- builder/virtualbox/ovf/builder.go | 9 +- builder/virtualbox/ovf/config.go | 9 +- builder/virtualbox/ovf/config.hcl2spec.go | 13 +- builder/virtualbox/ovf/config_test.go | 40 +- builder/virtualbox/ovf/step_import_test.go | 11 +- builder/virtualbox/vm/builder.go | 7 +- builder/virtualbox/vm/config.go | 9 +- builder/virtualbox/vm/config.hcl2spec.go | 13 +- builder/vmware/iso/builder.go | 8 +- builder/vmware/iso/config.go | 9 +- builder/vmware/iso/config.hcl2spec.go | 13 +- builder/vmware/iso/step_create_vmx_test.go | 12 +- builder/vmware/vmx/builder.go | 9 +- builder/vmware/vmx/config.go | 9 +- builder/vmware/vmx/config.hcl2spec.go | 13 +- builder/vmware/vmx/config_test.go | 18 +- builder/yandex/builder.go | 15 +- builder/yandex/config.go | 9 +- builder/yandex/config.hcl2spec.go | 13 +- builder/yandex/config_test.go | 28 +- cmd/hcl2-schema/hcl2-schema.go | 313 ---------------- .../mapstructure-to-hcl2.go | 41 +- command/build.go | 105 ++++-- command/build_parallel_test.go | 27 +- command/build_test.go | 31 +- command/build_timeout_test.go | 21 +- command/utils.go | 22 ++ common/shell-local/config.go | 2 +- common/shell-local/config.hcl2spec.go | 9 +- config.go | 148 +++++--- go.mod | 5 +- go.sum | 14 + hcl2template/common_test.go | 236 ++++++++++++ hcl2template/config_load.go | 101 ----- hcl2template/decode.go | 15 + hcl2template/load_test.go | 352 ------------------ hcl2template/mock.go | 120 ++++++ hcl2template/mock.hcl2spec.go | 85 +++++ hcl2template/parser.go | 74 ++-- hcl2template/parser_test.go | 213 ----------- hcl2template/testdata/build/basic.pkr.hcl | 163 +++++--- .../build/post-processor_inexistent.pkr.hcl | 6 + .../build/post-processor_untyped.pkr.hcl | 6 + .../build/provisioner_inexistent.pkr.hcl | 6 + .../build/provisioner_untyped.pkr.hcl | 6 + .../testdata/communicator/basic.pkr.hcl | 60 +-- hcl2template/testdata/complete/build.pkr.hcl | 162 +++++--- .../testdata/complete/communicator.pkr.hcl | 27 -- .../testdata/complete/sources.pkr.hcl | 94 +++-- .../complete/subfolder/shouldnotload.pkr.hcl | 61 --- .../testdata/complete/variables.pkr.hcl | 6 - hcl2template/testdata/sources/basic.pkr.hcl | 93 +++-- .../testdata/sources/duplicate.pkr.hcl | 3 + .../testdata/sources/inexistent.pkr.hcl | 4 + hcl2template/testdata/sources/unnamed.pkr.hcl | 4 + hcl2template/testdata/sources/untyped.pkr.hcl | 4 + hcl2template/types.build.from.go | 50 +-- hcl2template/types.build.go | 92 +++-- hcl2template/types.build.post-processor.go | 61 +++ hcl2template/types.build.provisioners.go | 98 +++-- hcl2template/types.build_test.go | 88 +++++ hcl2template/types.communicator.go | 88 ----- hcl2template/types.decodable.go | 56 --- hcl2template/types.hcl_ref.go | 6 - hcl2template/types.packer_config.go | 203 ++++------ hcl2template/types.packer_config_test.go | 55 +++ hcl2template/types.source.go | 57 ++- hcl2template/types.source_test.go | 75 ++++ .../{types.variable.go => types.variables.go} | 0 hcl2template/types.variables_test.go | 29 ++ hcl2template/utils.go | 23 ++ helper/builder/testing/testing.go | 19 +- helper/communicator/config.go | 24 +- helper/communicator/config.hcl2spec.go | 31 +- helper/config/decode.go | 35 ++ main.go | 20 +- packer/build.go | 138 +++---- packer/build_test.go | 62 +-- packer/builder.go | 6 +- packer/builder_mock.go | 8 + packer/builder_mock.hcl2spec.go | 207 ++++++++++ packer/communicator.go | 19 +- packer/config_file.go | 2 - packer/core.go | 80 ++-- packer/hcl2spec.go | 12 + packer/maps.go | 74 ++++ packer/plugin/builder.go | 10 + packer/plugin/post_processor.go | 10 + packer/plugin/post_processor_test.go | 3 + packer/plugin/provisioner.go | 10 + packer/plugin/server.go | 3 +- packer/post_processor.go | 2 + packer/post_processor_mock.go | 8 +- packer/provisioner.go | 8 + packer/provisioner_mock.go | 6 + packer/rpc/artifact.go | 5 +- packer/rpc/build.go | 4 +- packer/rpc/builder.go | 24 +- packer/rpc/client.go | 53 ++- packer/rpc/client_test.go | 5 +- packer/rpc/common.go | 71 ++++ packer/rpc/communicator.go | 24 +- packer/rpc/cty_encode.go | 35 ++ packer/rpc/hook.go | 8 +- packer/rpc/post_processor.go | 32 +- packer/rpc/post_processor_test.go | 5 +- packer/rpc/provisioner.go | 30 +- packer/rpc/server.go | 25 +- packer/rpc/ui.go | 3 +- packer/testing.go | 32 +- .../alicloud-import/post-processor.go | 4 +- .../post-processor.hcl2spec.go | 15 +- .../amazon-import/post-processor.go | 4 +- .../amazon-import/post-processor.hcl2spec.go | 9 +- post-processor/artifice/post-processor.go | 3 + .../artifice/post-processor.hcl2spec.go | 9 +- post-processor/checksum/post-processor.go | 3 + .../checksum/post-processor.hcl2spec.go | 9 +- post-processor/compress/post-processor.go | 3 + .../compress/post-processor.hcl2spec.go | 9 +- .../digitalocean-import/post-processor.go | 3 + .../post-processor.hcl2spec.go | 9 +- .../docker-import/post-processor.go | 3 + .../docker-import/post-processor.hcl2spec.go | 9 +- post-processor/docker-push/post-processor.go | 3 + .../docker-push/post-processor.hcl2spec.go | 9 +- post-processor/docker-save/post-processor.go | 3 + .../docker-save/post-processor.hcl2spec.go | 9 +- post-processor/docker-tag/post-processor.go | 3 + .../docker-tag/post-processor.hcl2spec.go | 9 +- .../exoscale-import/post-processor.go | 3 + .../post-processor.hcl2spec.go | 9 +- .../googlecompute-export/post-processor.go | 3 + .../post-processor.hcl2spec.go | 9 +- .../googlecompute-import/post-processor.go | 3 + .../post-processor.hcl2spec.go | 9 +- post-processor/manifest/post-processor.go | 3 + .../manifest/post-processor.hcl2spec.go | 9 +- post-processor/shell-local/post-processor.go | 3 + .../ucloud-import/post-processor.go | 15 +- .../ucloud-import/post-processor.hcl2spec.go | 70 ++++ .../vagrant-cloud/post-processor.go | 3 + .../vagrant-cloud/post-processor.hcl2spec.go | 9 +- post-processor/vagrant/post-processor.go | 6 + .../vagrant/post-processor.hcl2spec.go | 9 +- .../vsphere-template/post-processor.go | 3 + .../post-processor.hcl2spec.go | 9 +- post-processor/vsphere/post-processor.go | 3 + .../vsphere/post-processor.hcl2spec.go | 9 +- provisioner/ansible-local/provisioner.go | 3 + .../ansible-local/provisioner.hcl2spec.go | 9 +- provisioner/ansible/provisioner.go | 3 + provisioner/ansible/provisioner.hcl2spec.go | 9 +- provisioner/breakpoint/provisioner.go | 3 + .../breakpoint/provisioner.hcl2spec.go | 9 +- provisioner/chef-client/provisioner.go | 3 + .../chef-client/provisioner.hcl2spec.go | 9 +- provisioner/chef-solo/provisioner.go | 3 + provisioner/chef-solo/provisioner.hcl2spec.go | 9 +- provisioner/converge/provisioner.go | 4 +- provisioner/converge/provisioner.hcl2spec.go | 20 +- provisioner/file/provisioner.go | 3 + provisioner/file/provisioner.hcl2spec.go | 9 +- provisioner/inspec/provisioner.go | 3 + provisioner/inspec/provisioner.hcl2spec.go | 9 +- provisioner/powershell/provisioner.go | 2 + .../powershell/provisioner.hcl2spec.go | 9 +- provisioner/puppet-masterless/provisioner.go | 3 + .../puppet-masterless/provisioner.hcl2spec.go | 9 +- provisioner/puppet-server/provisioner.go | 3 + .../puppet-server/provisioner.hcl2spec.go | 9 +- provisioner/salt-masterless/provisioner.go | 3 + .../salt-masterless/provisioner.hcl2spec.go | 9 +- provisioner/shell-local/provisioner.go | 3 + provisioner/shell/provisioner.go | 3 + provisioner/shell/provisioner.hcl2spec.go | 9 +- provisioner/sleep/provisioner.go | 5 + provisioner/sleep/provisioner.hcl2spec.go | 9 +- provisioner/windows-restart/provisioner.go | 3 + .../windows-restart/provisioner.hcl2spec.go | 9 +- provisioner/windows-shell/provisioner.go | 3 + .../windows-shell/provisioner.hcl2spec.go | 9 +- template/template.hcl2spec.go | 9 +- .../cty/convert/conversion_primitive.go | 11 +- .../go-cty/cty/function/stdlib/format.go | 18 +- vendor/github.com/zclconf/go-cty/cty/gob.go | 84 ++++- .../zclconf/go-cty/cty/set_internals.go | 11 + vendor/modules.txt | 2 +- .../guides/hcl/from-json-v1/index.html.md.erb | 179 +++++++++ website/source/guides/hcl/index.html.md.erb | 52 +++ website/source/layouts/guides.erb | 8 + .../communicator/_SSH-not-required.html.md | 5 +- 348 files changed, 5944 insertions(+), 3160 deletions(-) create mode 100644 builder/amazon/ebssurrogate/builder.hcl2spec.go create mode 100644 builder/amazon/ebsvolume/builder.hcl2spec.go create mode 100644 builder/amazon/instance/builder.hcl2spec.go create mode 100644 builder/azure/chroot/builder.hcl2spec.go create mode 100644 builder/lxc/config.hcl2spec.go rename builder/oracle/classic/{config.hcl2spec.go => builder.hcl2spec.go} (96%) create mode 100644 builder/osc/bsusurrogate/builder.hcl2spec.go delete mode 100644 cmd/hcl2-schema/hcl2-schema.go create mode 100644 command/utils.go create mode 100644 hcl2template/common_test.go delete mode 100644 hcl2template/config_load.go create mode 100644 hcl2template/decode.go delete mode 100644 hcl2template/load_test.go create mode 100644 hcl2template/mock.go create mode 100644 hcl2template/mock.hcl2spec.go delete mode 100644 hcl2template/parser_test.go create mode 100644 hcl2template/testdata/build/post-processor_inexistent.pkr.hcl create mode 100644 hcl2template/testdata/build/post-processor_untyped.pkr.hcl create mode 100644 hcl2template/testdata/build/provisioner_inexistent.pkr.hcl create mode 100644 hcl2template/testdata/build/provisioner_untyped.pkr.hcl delete mode 100644 hcl2template/testdata/complete/communicator.pkr.hcl delete mode 100644 hcl2template/testdata/complete/subfolder/shouldnotload.pkr.hcl delete mode 100644 hcl2template/testdata/complete/variables.pkr.hcl create mode 100644 hcl2template/testdata/sources/duplicate.pkr.hcl create mode 100644 hcl2template/testdata/sources/inexistent.pkr.hcl create mode 100644 hcl2template/testdata/sources/unnamed.pkr.hcl create mode 100644 hcl2template/testdata/sources/untyped.pkr.hcl create mode 100644 hcl2template/types.build.post-processor.go create mode 100644 hcl2template/types.build_test.go delete mode 100644 hcl2template/types.communicator.go delete mode 100644 hcl2template/types.decodable.go create mode 100644 hcl2template/types.packer_config_test.go create mode 100644 hcl2template/types.source_test.go rename hcl2template/{types.variable.go => types.variables.go} (100%) create mode 100644 hcl2template/types.variables_test.go create mode 100644 hcl2template/utils.go create mode 100644 packer/builder_mock.hcl2spec.go create mode 100644 packer/hcl2spec.go create mode 100644 packer/maps.go create mode 100644 packer/rpc/common.go create mode 100644 packer/rpc/cty_encode.go create mode 100644 post-processor/ucloud-import/post-processor.hcl2spec.go create mode 100644 website/source/guides/hcl/from-json-v1/index.html.md.erb create mode 100644 website/source/guides/hcl/index.html.md.erb diff --git a/builder/alicloud/ecs/builder.go b/builder/alicloud/ecs/builder.go index 5b593cee9..20c070f6a 100644 --- a/builder/alicloud/ecs/builder.go +++ b/builder/alicloud/ecs/builder.go @@ -8,6 +8,7 @@ import ( "context" "fmt" + "github.com/hashicorp/hcl/v2/hcldec" "github.com/hashicorp/packer/common" "github.com/hashicorp/packer/helper/communicator" "github.com/hashicorp/packer/helper/config" @@ -41,6 +42,8 @@ const ( ALICLOUD_DEFAULT_LONG_TIMEOUT = 3600 ) +func (b *Builder) ConfigSpec() hcldec.ObjectSpec { return b.config.FlatMapstructure().HCL2Spec() } + func (b *Builder) Prepare(raws ...interface{}) ([]string, error) { err := config.Decode(&b.config, &config.DecodeOpts{ Interpolate: true, diff --git a/builder/alicloud/ecs/builder.hcl2spec.go b/builder/alicloud/ecs/builder.hcl2spec.go index 595ada017..9b5a36e73 100644 --- a/builder/alicloud/ecs/builder.hcl2spec.go +++ b/builder/alicloud/ecs/builder.hcl2spec.go @@ -22,10 +22,13 @@ type FlatAlicloudDiskDevice struct { // FlatMapstructure returns a new FlatAlicloudDiskDevice. // FlatAlicloudDiskDevice is an auto-generated flat version of AlicloudDiskDevice. // Where the contents a fields with a `mapstructure:,squash` tag are bubbled up. -func (*AlicloudDiskDevice) FlatMapstructure() interface{} { return new(FlatAlicloudDiskDevice) } +func (*AlicloudDiskDevice) FlatMapstructure() interface{ HCL2Spec() map[string]hcldec.Spec } { + return new(FlatAlicloudDiskDevice) +} -// HCL2Spec returns the hcldec.Spec of a FlatAlicloudDiskDevice. -// This spec is used by HCL to read the fields of FlatAlicloudDiskDevice. +// HCL2Spec returns the hcl spec of a AlicloudDiskDevice. +// This spec is used by HCL to read the fields of AlicloudDiskDevice. +// The decoded values from this spec will then be applied to a FlatAlicloudDiskDevice. func (*FlatAlicloudDiskDevice) HCL2Spec() map[string]hcldec.Spec { s := map[string]hcldec.Spec{ "disk_name": &hcldec.AttrSpec{Name: "disk_name", Type: cty.String, Required: false}, @@ -121,8 +124,8 @@ type FlatConfig struct { SSHReadWriteTimeout *string `mapstructure:"ssh_read_write_timeout" cty:"ssh_read_write_timeout"` SSHRemoteTunnels []string `mapstructure:"ssh_remote_tunnels" cty:"ssh_remote_tunnels"` SSHLocalTunnels []string `mapstructure:"ssh_local_tunnels" cty:"ssh_local_tunnels"` - SSHPublicKey []byte `cty:"ssh_public_key"` - SSHPrivateKey []byte `cty:"ssh_private_key"` + SSHPublicKey []byte `mapstructure:"ssh_public_key" cty:"ssh_public_key"` + SSHPrivateKey []byte `mapstructure:"ssh_private_key" cty:"ssh_private_key"` WinRMUser *string `mapstructure:"winrm_username" cty:"winrm_username"` WinRMPassword *string `mapstructure:"winrm_password" cty:"winrm_password"` WinRMHost *string `mapstructure:"winrm_host" cty:"winrm_host"` @@ -137,10 +140,13 @@ type FlatConfig struct { // FlatMapstructure returns a new FlatConfig. // FlatConfig is an auto-generated flat version of Config. // Where the contents a fields with a `mapstructure:,squash` tag are bubbled up. -func (*Config) FlatMapstructure() interface{} { return new(FlatConfig) } +func (*Config) FlatMapstructure() interface{ HCL2Spec() map[string]hcldec.Spec } { + return new(FlatConfig) +} -// HCL2Spec returns the hcldec.Spec of a FlatConfig. -// This spec is used by HCL to read the fields of FlatConfig. +// HCL2Spec returns the hcl spec of a Config. +// This spec is used by HCL to read the fields of Config. +// The decoded values from this spec will then be applied to a FlatConfig. func (*FlatConfig) HCL2Spec() map[string]hcldec.Spec { s := map[string]hcldec.Spec{ "packer_build_name": &hcldec.AttrSpec{Name: "packer_build_name", Type: cty.String, Required: false}, @@ -169,7 +175,7 @@ func (*FlatConfig) HCL2Spec() map[string]hcldec.Spec { "image_ignore_data_disks": &hcldec.AttrSpec{Name: "image_ignore_data_disks", Type: cty.Bool, Required: false}, "tags": &hcldec.BlockAttrsSpec{TypeName: "tags", ElementType: cty.String, Required: false}, "system_disk_mapping": &hcldec.BlockSpec{TypeName: "system_disk_mapping", Nested: hcldec.ObjectSpec((*FlatAlicloudDiskDevice)(nil).HCL2Spec())}, - "image_disk_mappings": &hcldec.BlockListSpec{TypeName: "image_disk_mappings", Nested: &hcldec.BlockSpec{TypeName: "image_disk_mappings", Nested: hcldec.ObjectSpec((*FlatAlicloudDiskDevice)(nil).HCL2Spec())}}, + "image_disk_mappings": &hcldec.BlockListSpec{TypeName: "image_disk_mappings", Nested: hcldec.ObjectSpec((*FlatAlicloudDiskDevice)(nil).HCL2Spec())}, "associate_public_ip_address": &hcldec.AttrSpec{Name: "associate_public_ip_address", Type: cty.Bool, Required: false}, "zone_id": &hcldec.AttrSpec{Name: "zone_id", Type: cty.String, Required: false}, "io_optimized": &hcldec.AttrSpec{Name: "io_optimized", Type: cty.Bool, Required: false}, diff --git a/builder/alicloud/ecs/step_create_snapshot.go b/builder/alicloud/ecs/step_create_snapshot.go index c9e934d60..32f155e08 100644 --- a/builder/alicloud/ecs/step_create_snapshot.go +++ b/builder/alicloud/ecs/step_create_snapshot.go @@ -3,9 +3,9 @@ package ecs import ( "context" "fmt" - "github.com/aliyun/alibaba-cloud-sdk-go/sdk/errors" "time" + "github.com/aliyun/alibaba-cloud-sdk-go/sdk/errors" "github.com/aliyun/alibaba-cloud-sdk-go/services/ecs" "github.com/hashicorp/packer/helper/multistep" "github.com/hashicorp/packer/packer" diff --git a/builder/amazon/chroot/builder.go b/builder/amazon/chroot/builder.go index f1cf7432a..91d00f749 100644 --- a/builder/amazon/chroot/builder.go +++ b/builder/amazon/chroot/builder.go @@ -13,6 +13,7 @@ import ( "runtime" "github.com/aws/aws-sdk-go/service/ec2" + "github.com/hashicorp/hcl/v2/hcldec" awscommon "github.com/hashicorp/packer/builder/amazon/common" "github.com/hashicorp/packer/common" "github.com/hashicorp/packer/common/chroot" @@ -182,6 +183,8 @@ type Builder struct { runner multistep.Runner } +func (b *Builder) ConfigSpec() hcldec.ObjectSpec { return b.config.FlatMapstructure().HCL2Spec() } + func (b *Builder) Prepare(raws ...interface{}) ([]string, error) { b.config.ctx.Funcs = awscommon.TemplateFuncs err := config.Decode(&b.config, &config.DecodeOpts{ diff --git a/builder/amazon/chroot/builder.hcl2spec.go b/builder/amazon/chroot/builder.hcl2spec.go index f1f0a2c50..e040855ee 100644 --- a/builder/amazon/chroot/builder.hcl2spec.go +++ b/builder/amazon/chroot/builder.hcl2spec.go @@ -72,10 +72,13 @@ type FlatConfig struct { // FlatMapstructure returns a new FlatConfig. // FlatConfig is an auto-generated flat version of Config. // Where the contents a fields with a `mapstructure:,squash` tag are bubbled up. -func (*Config) FlatMapstructure() interface{} { return new(FlatConfig) } +func (*Config) FlatMapstructure() interface{ HCL2Spec() map[string]hcldec.Spec } { + return new(FlatConfig) +} -// HCL2Spec returns the hcldec.Spec of a FlatConfig. -// This spec is used by HCL to read the fields of FlatConfig. +// HCL2Spec returns the hcl spec of a Config. +// This spec is used by HCL to read the fields of Config. +// The decoded values from this spec will then be applied to a FlatConfig. func (*FlatConfig) HCL2Spec() map[string]hcldec.Spec { s := map[string]hcldec.Spec{ "packer_build_name": &hcldec.AttrSpec{Name: "packer_build_name", Type: cty.String, Required: false}, @@ -116,7 +119,7 @@ func (*FlatConfig) HCL2Spec() map[string]hcldec.Spec { "skip_metadata_api_check": &hcldec.AttrSpec{Name: "skip_metadata_api_check", Type: cty.Bool, Required: false}, "token": &hcldec.AttrSpec{Name: "token", Type: cty.String, Required: false}, "vault_aws_engine": &hcldec.BlockSpec{TypeName: "vault_aws_engine", Nested: hcldec.ObjectSpec((*common.FlatVaultAWSEngineOptions)(nil).HCL2Spec())}, - "ami_block_device_mappings": &hcldec.BlockListSpec{TypeName: "ami_block_device_mappings", Nested: &hcldec.BlockSpec{TypeName: "ami_block_device_mappings", Nested: hcldec.ObjectSpec((*common.FlatBlockDevice)(nil).HCL2Spec())}}, + "ami_block_device_mappings": &hcldec.BlockListSpec{TypeName: "ami_block_device_mappings", Nested: hcldec.ObjectSpec((*common.FlatBlockDevice)(nil).HCL2Spec())}, "chroot_mounts": &hcldec.BlockListSpec{TypeName: "chroot_mounts", Nested: &hcldec.AttrSpec{Name: "chroot_mounts", Type: cty.List(cty.String), Required: false}}, "command_wrapper": &hcldec.AttrSpec{Name: "command_wrapper", Type: cty.String, Required: false}, "copy_files": &hcldec.AttrSpec{Name: "copy_files", Type: cty.List(cty.String), Required: false}, diff --git a/builder/amazon/common/access_config.hcl2spec.go b/builder/amazon/common/access_config.hcl2spec.go index b3bdad7e0..f61fa5e40 100644 --- a/builder/amazon/common/access_config.hcl2spec.go +++ b/builder/amazon/common/access_config.hcl2spec.go @@ -18,10 +18,13 @@ type FlatVaultAWSEngineOptions struct { // FlatMapstructure returns a new FlatVaultAWSEngineOptions. // FlatVaultAWSEngineOptions is an auto-generated flat version of VaultAWSEngineOptions. // Where the contents a fields with a `mapstructure:,squash` tag are bubbled up. -func (*VaultAWSEngineOptions) FlatMapstructure() interface{} { return new(FlatVaultAWSEngineOptions) } +func (*VaultAWSEngineOptions) FlatMapstructure() interface{ HCL2Spec() map[string]hcldec.Spec } { + return new(FlatVaultAWSEngineOptions) +} -// HCL2Spec returns the hcldec.Spec of a FlatVaultAWSEngineOptions. -// This spec is used by HCL to read the fields of FlatVaultAWSEngineOptions. +// HCL2Spec returns the hcl spec of a VaultAWSEngineOptions. +// This spec is used by HCL to read the fields of VaultAWSEngineOptions. +// The decoded values from this spec will then be applied to a FlatVaultAWSEngineOptions. func (*FlatVaultAWSEngineOptions) HCL2Spec() map[string]hcldec.Spec { s := map[string]hcldec.Spec{ "name": &hcldec.AttrSpec{Name: "name", Type: cty.String, Required: false}, diff --git a/builder/amazon/common/block_device.hcl2spec.go b/builder/amazon/common/block_device.hcl2spec.go index 1865bda3a..e0f3013f3 100644 --- a/builder/amazon/common/block_device.hcl2spec.go +++ b/builder/amazon/common/block_device.hcl2spec.go @@ -24,10 +24,13 @@ type FlatBlockDevice struct { // FlatMapstructure returns a new FlatBlockDevice. // FlatBlockDevice is an auto-generated flat version of BlockDevice. // Where the contents a fields with a `mapstructure:,squash` tag are bubbled up. -func (*BlockDevice) FlatMapstructure() interface{} { return new(FlatBlockDevice) } +func (*BlockDevice) FlatMapstructure() interface{ HCL2Spec() map[string]hcldec.Spec } { + return new(FlatBlockDevice) +} -// HCL2Spec returns the hcldec.Spec of a FlatBlockDevice. -// This spec is used by HCL to read the fields of FlatBlockDevice. +// HCL2Spec returns the hcl spec of a BlockDevice. +// This spec is used by HCL to read the fields of BlockDevice. +// The decoded values from this spec will then be applied to a FlatBlockDevice. func (*FlatBlockDevice) HCL2Spec() map[string]hcldec.Spec { s := map[string]hcldec.Spec{ "delete_on_termination": &hcldec.AttrSpec{Name: "delete_on_termination", Type: cty.Bool, Required: false}, diff --git a/builder/amazon/common/run_config.hcl2spec.go b/builder/amazon/common/run_config.hcl2spec.go index 2ef6ce62d..d27b01148 100644 --- a/builder/amazon/common/run_config.hcl2spec.go +++ b/builder/amazon/common/run_config.hcl2spec.go @@ -17,10 +17,13 @@ type FlatAmiFilterOptions struct { // FlatMapstructure returns a new FlatAmiFilterOptions. // FlatAmiFilterOptions is an auto-generated flat version of AmiFilterOptions. // Where the contents a fields with a `mapstructure:,squash` tag are bubbled up. -func (*AmiFilterOptions) FlatMapstructure() interface{} { return new(FlatAmiFilterOptions) } +func (*AmiFilterOptions) FlatMapstructure() interface{ HCL2Spec() map[string]hcldec.Spec } { + return new(FlatAmiFilterOptions) +} -// HCL2Spec returns the hcldec.Spec of a FlatAmiFilterOptions. -// This spec is used by HCL to read the fields of FlatAmiFilterOptions. +// HCL2Spec returns the hcl spec of a AmiFilterOptions. +// This spec is used by HCL to read the fields of AmiFilterOptions. +// The decoded values from this spec will then be applied to a FlatAmiFilterOptions. func (*FlatAmiFilterOptions) HCL2Spec() map[string]hcldec.Spec { s := map[string]hcldec.Spec{ "filters": &hcldec.BlockAttrsSpec{TypeName: "filters", ElementType: cty.String, Required: false}, @@ -40,14 +43,17 @@ type FlatPolicyDocument struct { // FlatMapstructure returns a new FlatPolicyDocument. // FlatPolicyDocument is an auto-generated flat version of PolicyDocument. // Where the contents a fields with a `mapstructure:,squash` tag are bubbled up. -func (*PolicyDocument) FlatMapstructure() interface{} { return new(FlatPolicyDocument) } +func (*PolicyDocument) FlatMapstructure() interface{ HCL2Spec() map[string]hcldec.Spec } { + return new(FlatPolicyDocument) +} -// HCL2Spec returns the hcldec.Spec of a FlatPolicyDocument. -// This spec is used by HCL to read the fields of FlatPolicyDocument. +// HCL2Spec returns the hcl spec of a PolicyDocument. +// This spec is used by HCL to read the fields of PolicyDocument. +// The decoded values from this spec will then be applied to a FlatPolicyDocument. func (*FlatPolicyDocument) HCL2Spec() map[string]hcldec.Spec { s := map[string]hcldec.Spec{ "version": &hcldec.AttrSpec{Name: "version", Type: cty.String, Required: false}, - "statement": &hcldec.BlockListSpec{TypeName: "statement", Nested: &hcldec.BlockSpec{TypeName: "statement", Nested: hcldec.ObjectSpec((*FlatStatement)(nil).HCL2Spec())}}, + "statement": &hcldec.BlockListSpec{TypeName: "statement", Nested: hcldec.ObjectSpec((*FlatStatement)(nil).HCL2Spec())}, } return s } @@ -61,12 +67,13 @@ type FlatSecurityGroupFilterOptions struct { // FlatMapstructure returns a new FlatSecurityGroupFilterOptions. // FlatSecurityGroupFilterOptions is an auto-generated flat version of SecurityGroupFilterOptions. // Where the contents a fields with a `mapstructure:,squash` tag are bubbled up. -func (*SecurityGroupFilterOptions) FlatMapstructure() interface{} { +func (*SecurityGroupFilterOptions) FlatMapstructure() interface{ HCL2Spec() map[string]hcldec.Spec } { return new(FlatSecurityGroupFilterOptions) } -// HCL2Spec returns the hcldec.Spec of a FlatSecurityGroupFilterOptions. -// This spec is used by HCL to read the fields of FlatSecurityGroupFilterOptions. +// HCL2Spec returns the hcl spec of a SecurityGroupFilterOptions. +// This spec is used by HCL to read the fields of SecurityGroupFilterOptions. +// The decoded values from this spec will then be applied to a FlatSecurityGroupFilterOptions. func (*FlatSecurityGroupFilterOptions) HCL2Spec() map[string]hcldec.Spec { s := map[string]hcldec.Spec{ "filters": &hcldec.BlockAttrsSpec{TypeName: "filters", ElementType: cty.String, Required: false}, @@ -85,10 +92,13 @@ type FlatStatement struct { // FlatMapstructure returns a new FlatStatement. // FlatStatement is an auto-generated flat version of Statement. // Where the contents a fields with a `mapstructure:,squash` tag are bubbled up. -func (*Statement) FlatMapstructure() interface{} { return new(FlatStatement) } +func (*Statement) FlatMapstructure() interface{ HCL2Spec() map[string]hcldec.Spec } { + return new(FlatStatement) +} -// HCL2Spec returns the hcldec.Spec of a FlatStatement. -// This spec is used by HCL to read the fields of FlatStatement. +// HCL2Spec returns the hcl spec of a Statement. +// This spec is used by HCL to read the fields of Statement. +// The decoded values from this spec will then be applied to a FlatStatement. func (*FlatStatement) HCL2Spec() map[string]hcldec.Spec { s := map[string]hcldec.Spec{ "effect": &hcldec.AttrSpec{Name: "effect", Type: cty.String, Required: false}, @@ -109,10 +119,13 @@ type FlatSubnetFilterOptions struct { // FlatMapstructure returns a new FlatSubnetFilterOptions. // FlatSubnetFilterOptions is an auto-generated flat version of SubnetFilterOptions. // Where the contents a fields with a `mapstructure:,squash` tag are bubbled up. -func (*SubnetFilterOptions) FlatMapstructure() interface{} { return new(FlatSubnetFilterOptions) } +func (*SubnetFilterOptions) FlatMapstructure() interface{ HCL2Spec() map[string]hcldec.Spec } { + return new(FlatSubnetFilterOptions) +} -// HCL2Spec returns the hcldec.Spec of a FlatSubnetFilterOptions. -// This spec is used by HCL to read the fields of FlatSubnetFilterOptions. +// HCL2Spec returns the hcl spec of a SubnetFilterOptions. +// This spec is used by HCL to read the fields of SubnetFilterOptions. +// The decoded values from this spec will then be applied to a FlatSubnetFilterOptions. func (*FlatSubnetFilterOptions) HCL2Spec() map[string]hcldec.Spec { s := map[string]hcldec.Spec{ "filters": &hcldec.BlockAttrsSpec{TypeName: "filters", ElementType: cty.String, Required: false}, @@ -131,10 +144,13 @@ type FlatVpcFilterOptions struct { // FlatMapstructure returns a new FlatVpcFilterOptions. // FlatVpcFilterOptions is an auto-generated flat version of VpcFilterOptions. // Where the contents a fields with a `mapstructure:,squash` tag are bubbled up. -func (*VpcFilterOptions) FlatMapstructure() interface{} { return new(FlatVpcFilterOptions) } +func (*VpcFilterOptions) FlatMapstructure() interface{ HCL2Spec() map[string]hcldec.Spec } { + return new(FlatVpcFilterOptions) +} -// HCL2Spec returns the hcldec.Spec of a FlatVpcFilterOptions. -// This spec is used by HCL to read the fields of FlatVpcFilterOptions. +// HCL2Spec returns the hcl spec of a VpcFilterOptions. +// This spec is used by HCL to read the fields of VpcFilterOptions. +// The decoded values from this spec will then be applied to a FlatVpcFilterOptions. func (*FlatVpcFilterOptions) HCL2Spec() map[string]hcldec.Spec { s := map[string]hcldec.Spec{ "filters": &hcldec.BlockAttrsSpec{TypeName: "filters", ElementType: cty.String, Required: false}, diff --git a/builder/amazon/common/step_iam_instance_profile.go b/builder/amazon/common/step_iam_instance_profile.go index eba357769..3ef72e9ee 100644 --- a/builder/amazon/common/step_iam_instance_profile.go +++ b/builder/amazon/common/step_iam_instance_profile.go @@ -2,11 +2,10 @@ package common import ( "context" + "encoding/json" "fmt" "log" - "encoding/json" - "github.com/aws/aws-sdk-go/aws" "github.com/aws/aws-sdk-go/service/iam" "github.com/hashicorp/packer/common/uuid" diff --git a/builder/amazon/ebs/builder.go b/builder/amazon/ebs/builder.go index f8648e204..6e9b25de4 100644 --- a/builder/amazon/ebs/builder.go +++ b/builder/amazon/ebs/builder.go @@ -14,6 +14,7 @@ import ( "github.com/aws/aws-sdk-go/service/ec2" "github.com/aws/aws-sdk-go/service/iam" + "github.com/hashicorp/hcl/v2/hcldec" awscommon "github.com/hashicorp/packer/builder/amazon/common" "github.com/hashicorp/packer/common" "github.com/hashicorp/packer/helper/communicator" @@ -72,6 +73,8 @@ type Builder struct { runner multistep.Runner } +func (b *Builder) ConfigSpec() hcldec.ObjectSpec { return b.config.FlatMapstructure().HCL2Spec() } + func (b *Builder) Prepare(raws ...interface{}) ([]string, error) { b.config.ctx.Funcs = awscommon.TemplateFuncs err := config.Decode(&b.config, &config.DecodeOpts{ diff --git a/builder/amazon/ebs/builder.hcl2spec.go b/builder/amazon/ebs/builder.hcl2spec.go index 991b443db..cb738d843 100644 --- a/builder/amazon/ebs/builder.hcl2spec.go +++ b/builder/amazon/ebs/builder.hcl2spec.go @@ -107,8 +107,8 @@ type FlatConfig struct { SSHReadWriteTimeout *string `mapstructure:"ssh_read_write_timeout" cty:"ssh_read_write_timeout"` SSHRemoteTunnels []string `mapstructure:"ssh_remote_tunnels" cty:"ssh_remote_tunnels"` SSHLocalTunnels []string `mapstructure:"ssh_local_tunnels" cty:"ssh_local_tunnels"` - SSHPublicKey []byte `cty:"ssh_public_key"` - SSHPrivateKey []byte `cty:"ssh_private_key"` + SSHPublicKey []byte `mapstructure:"ssh_public_key" cty:"ssh_public_key"` + SSHPrivateKey []byte `mapstructure:"ssh_private_key" cty:"ssh_private_key"` WinRMUser *string `mapstructure:"winrm_username" cty:"winrm_username"` WinRMPassword *string `mapstructure:"winrm_password" cty:"winrm_password"` WinRMHost *string `mapstructure:"winrm_host" cty:"winrm_host"` @@ -127,10 +127,13 @@ type FlatConfig struct { // FlatMapstructure returns a new FlatConfig. // FlatConfig is an auto-generated flat version of Config. // Where the contents a fields with a `mapstructure:,squash` tag are bubbled up. -func (*Config) FlatMapstructure() interface{} { return new(FlatConfig) } +func (*Config) FlatMapstructure() interface{ HCL2Spec() map[string]hcldec.Spec } { + return new(FlatConfig) +} -// HCL2Spec returns the hcldec.Spec of a FlatConfig. -// This spec is used by HCL to read the fields of FlatConfig. +// HCL2Spec returns the hcl spec of a Config. +// This spec is used by HCL to read the fields of Config. +// The decoded values from this spec will then be applied to a FlatConfig. func (*FlatConfig) HCL2Spec() map[string]hcldec.Spec { s := map[string]hcldec.Spec{ "packer_build_name": &hcldec.AttrSpec{Name: "packer_build_name", Type: cty.String, Required: false}, @@ -241,8 +244,8 @@ func (*FlatConfig) HCL2Spec() map[string]hcldec.Spec { "winrm_insecure": &hcldec.AttrSpec{Name: "winrm_insecure", Type: cty.Bool, Required: false}, "winrm_use_ntlm": &hcldec.AttrSpec{Name: "winrm_use_ntlm", Type: cty.Bool, Required: false}, "ssh_interface": &hcldec.AttrSpec{Name: "ssh_interface", Type: cty.String, Required: false}, - "ami_block_device_mappings": &hcldec.BlockListSpec{TypeName: "ami_block_device_mappings", Nested: &hcldec.BlockSpec{TypeName: "ami_block_device_mappings", Nested: hcldec.ObjectSpec((*common.FlatBlockDevice)(nil).HCL2Spec())}}, - "launch_block_device_mappings": &hcldec.BlockListSpec{TypeName: "launch_block_device_mappings", Nested: &hcldec.BlockSpec{TypeName: "launch_block_device_mappings", Nested: hcldec.ObjectSpec((*common.FlatBlockDevice)(nil).HCL2Spec())}}, + "ami_block_device_mappings": &hcldec.BlockListSpec{TypeName: "ami_block_device_mappings", Nested: hcldec.ObjectSpec((*common.FlatBlockDevice)(nil).HCL2Spec())}, + "launch_block_device_mappings": &hcldec.BlockListSpec{TypeName: "launch_block_device_mappings", Nested: hcldec.ObjectSpec((*common.FlatBlockDevice)(nil).HCL2Spec())}, "run_volume_tags": &hcldec.BlockAttrsSpec{TypeName: "common.TagMap", ElementType: cty.String, Required: false}, "no_ephemeral": &hcldec.AttrSpec{Name: "no_ephemeral", Type: cty.Bool, Required: false}, } diff --git a/builder/amazon/ebssurrogate/builder.go b/builder/amazon/ebssurrogate/builder.go index abf159fad..4a52c1de9 100644 --- a/builder/amazon/ebssurrogate/builder.go +++ b/builder/amazon/ebssurrogate/builder.go @@ -1,4 +1,5 @@ //go:generate struct-markdown +//go:generate mapstructure-to-hcl2 -type Config,RootBlockDevice,BlockDevice // The ebssurrogate package contains a packer.Builder implementation that // builds a new EBS-backed AMI using an ephemeral instance. @@ -11,6 +12,7 @@ import ( "github.com/aws/aws-sdk-go/service/ec2" "github.com/aws/aws-sdk-go/service/iam" + "github.com/hashicorp/hcl/v2/hcldec" awscommon "github.com/hashicorp/packer/builder/amazon/common" "github.com/hashicorp/packer/common" "github.com/hashicorp/packer/helper/communicator" @@ -70,6 +72,8 @@ type Builder struct { runner multistep.Runner } +func (b *Builder) ConfigSpec() hcldec.ObjectSpec { return b.config.FlatMapstructure().HCL2Spec() } + func (b *Builder) Prepare(raws ...interface{}) ([]string, error) { b.config.ctx.Funcs = awscommon.TemplateFuncs err := config.Decode(&b.config, &config.DecodeOpts{ diff --git a/builder/amazon/ebssurrogate/builder.hcl2spec.go b/builder/amazon/ebssurrogate/builder.hcl2spec.go new file mode 100644 index 000000000..5b9f1c1dd --- /dev/null +++ b/builder/amazon/ebssurrogate/builder.hcl2spec.go @@ -0,0 +1,331 @@ +// Code generated by "mapstructure-to-hcl2 -type Config,RootBlockDevice,BlockDevice"; DO NOT EDIT. +package ebssurrogate + +import ( + "github.com/hashicorp/hcl/v2/hcldec" + "github.com/hashicorp/packer/builder/amazon/common" + "github.com/zclconf/go-cty/cty" +) + +// FlatBlockDevice is an auto-generated flat version of BlockDevice. +// Where the contents of a field with a `mapstructure:,squash` tag are bubbled up. +type FlatBlockDevice struct { + DeleteOnTermination *bool `mapstructure:"delete_on_termination" required:"false" cty:"delete_on_termination"` + DeviceName *string `mapstructure:"device_name" required:"false" cty:"device_name"` + Encrypted *bool `mapstructure:"encrypted" required:"false" cty:"encrypted"` + IOPS *int64 `mapstructure:"iops" required:"false" cty:"iops"` + NoDevice *bool `mapstructure:"no_device" required:"false" cty:"no_device"` + SnapshotId *string `mapstructure:"snapshot_id" required:"false" cty:"snapshot_id"` + VirtualName *string `mapstructure:"virtual_name" required:"false" cty:"virtual_name"` + VolumeType *string `mapstructure:"volume_type" required:"false" cty:"volume_type"` + VolumeSize *int64 `mapstructure:"volume_size" required:"false" cty:"volume_size"` + KmsKeyId *string `mapstructure:"kms_key_id" required:"false" cty:"kms_key_id"` + OmitFromArtifact *bool `mapstructure:"omit_from_artifact" cty:"omit_from_artifact"` +} + +// FlatMapstructure returns a new FlatBlockDevice. +// FlatBlockDevice is an auto-generated flat version of BlockDevice. +// Where the contents a fields with a `mapstructure:,squash` tag are bubbled up. +func (*BlockDevice) FlatMapstructure() interface{ HCL2Spec() map[string]hcldec.Spec } { + return new(FlatBlockDevice) +} + +// HCL2Spec returns the hcl spec of a BlockDevice. +// This spec is used by HCL to read the fields of BlockDevice. +// The decoded values from this spec will then be applied to a FlatBlockDevice. +func (*FlatBlockDevice) HCL2Spec() map[string]hcldec.Spec { + s := map[string]hcldec.Spec{ + "delete_on_termination": &hcldec.AttrSpec{Name: "delete_on_termination", Type: cty.Bool, Required: false}, + "device_name": &hcldec.AttrSpec{Name: "device_name", Type: cty.String, Required: false}, + "encrypted": &hcldec.AttrSpec{Name: "encrypted", Type: cty.Bool, Required: false}, + "iops": &hcldec.AttrSpec{Name: "iops", Type: cty.Number, Required: false}, + "no_device": &hcldec.AttrSpec{Name: "no_device", Type: cty.Bool, Required: false}, + "snapshot_id": &hcldec.AttrSpec{Name: "snapshot_id", Type: cty.String, Required: false}, + "virtual_name": &hcldec.AttrSpec{Name: "virtual_name", Type: cty.String, Required: false}, + "volume_type": &hcldec.AttrSpec{Name: "volume_type", Type: cty.String, Required: false}, + "volume_size": &hcldec.AttrSpec{Name: "volume_size", Type: cty.Number, Required: false}, + "kms_key_id": &hcldec.AttrSpec{Name: "kms_key_id", Type: cty.String, Required: false}, + "omit_from_artifact": &hcldec.AttrSpec{Name: "omit_from_artifact", Type: cty.Bool, Required: false}, + } + return s +} + +// FlatConfig is an auto-generated flat version of Config. +// Where the contents of a field with a `mapstructure:,squash` tag are bubbled up. +type FlatConfig struct { + PackerBuildName *string `mapstructure:"packer_build_name" cty:"packer_build_name"` + PackerBuilderType *string `mapstructure:"packer_builder_type" cty:"packer_builder_type"` + PackerDebug *bool `mapstructure:"packer_debug" cty:"packer_debug"` + PackerForce *bool `mapstructure:"packer_force" cty:"packer_force"` + PackerOnError *string `mapstructure:"packer_on_error" cty:"packer_on_error"` + PackerUserVars map[string]string `mapstructure:"packer_user_variables" cty:"packer_user_variables"` + PackerSensitiveVars []string `mapstructure:"packer_sensitive_variables" cty:"packer_sensitive_variables"` + AccessKey *string `mapstructure:"access_key" required:"true" cty:"access_key"` + CustomEndpointEc2 *string `mapstructure:"custom_endpoint_ec2" required:"false" cty:"custom_endpoint_ec2"` + DecodeAuthZMessages *bool `mapstructure:"decode_authorization_messages" required:"false" cty:"decode_authorization_messages"` + InsecureSkipTLSVerify *bool `mapstructure:"insecure_skip_tls_verify" required:"false" cty:"insecure_skip_tls_verify"` + MFACode *string `mapstructure:"mfa_code" required:"false" cty:"mfa_code"` + ProfileName *string `mapstructure:"profile" required:"false" cty:"profile"` + RawRegion *string `mapstructure:"region" required:"true" cty:"region"` + SecretKey *string `mapstructure:"secret_key" required:"true" cty:"secret_key"` + SkipValidation *bool `mapstructure:"skip_region_validation" required:"false" cty:"skip_region_validation"` + SkipMetadataApiCheck *bool `mapstructure:"skip_metadata_api_check" cty:"skip_metadata_api_check"` + Token *string `mapstructure:"token" required:"false" cty:"token"` + VaultAWSEngine *common.FlatVaultAWSEngineOptions `mapstructure:"vault_aws_engine" required:"false" cty:"vault_aws_engine"` + AssociatePublicIpAddress *bool `mapstructure:"associate_public_ip_address" required:"false" cty:"associate_public_ip_address"` + AvailabilityZone *string `mapstructure:"availability_zone" required:"false" cty:"availability_zone"` + BlockDurationMinutes *int64 `mapstructure:"block_duration_minutes" required:"false" cty:"block_duration_minutes"` + DisableStopInstance *bool `mapstructure:"disable_stop_instance" required:"false" cty:"disable_stop_instance"` + EbsOptimized *bool `mapstructure:"ebs_optimized" required:"false" cty:"ebs_optimized"` + EnableT2Unlimited *bool `mapstructure:"enable_t2_unlimited" required:"false" cty:"enable_t2_unlimited"` + IamInstanceProfile *string `mapstructure:"iam_instance_profile" required:"false" cty:"iam_instance_profile"` + SkipProfileValidation *bool `mapstructure:"skip_profile_validation" required:"false" cty:"skip_profile_validation"` + TemporaryIamInstanceProfilePolicyDocument *common.FlatPolicyDocument `mapstructure:"temporary_iam_instance_profile_policy_document" required:"false" cty:"temporary_iam_instance_profile_policy_document"` + InstanceInitiatedShutdownBehavior *string `mapstructure:"shutdown_behavior" required:"false" cty:"shutdown_behavior"` + InstanceType *string `mapstructure:"instance_type" required:"true" cty:"instance_type"` + SecurityGroupFilter *common.FlatSecurityGroupFilterOptions `mapstructure:"security_group_filter" required:"false" cty:"security_group_filter"` + RunTags map[string]string `mapstructure:"run_tags" required:"false" cty:"run_tags"` + SecurityGroupId *string `mapstructure:"security_group_id" required:"false" cty:"security_group_id"` + SecurityGroupIds []string `mapstructure:"security_group_ids" required:"false" cty:"security_group_ids"` + SourceAmi *string `mapstructure:"source_ami" required:"true" cty:"source_ami"` + SourceAmiFilter *common.FlatAmiFilterOptions `mapstructure:"source_ami_filter" required:"false" cty:"source_ami_filter"` + SpotInstanceTypes []string `mapstructure:"spot_instance_types" required:"false" cty:"spot_instance_types"` + SpotPrice *string `mapstructure:"spot_price" required:"false" cty:"spot_price"` + SpotPriceAutoProduct *string `mapstructure:"spot_price_auto_product" required:"false" cty:"spot_price_auto_product"` + SpotTags map[string]string `mapstructure:"spot_tags" required:"false" cty:"spot_tags"` + SubnetFilter *common.FlatSubnetFilterOptions `mapstructure:"subnet_filter" required:"false" cty:"subnet_filter"` + SubnetId *string `mapstructure:"subnet_id" required:"false" cty:"subnet_id"` + TemporaryKeyPairName *string `mapstructure:"temporary_key_pair_name" required:"false" cty:"temporary_key_pair_name"` + TemporarySGSourceCidrs []string `mapstructure:"temporary_security_group_source_cidrs" required:"false" cty:"temporary_security_group_source_cidrs"` + UserData *string `mapstructure:"user_data" required:"false" cty:"user_data"` + UserDataFile *string `mapstructure:"user_data_file" required:"false" cty:"user_data_file"` + VpcFilter *common.FlatVpcFilterOptions `mapstructure:"vpc_filter" required:"false" cty:"vpc_filter"` + VpcId *string `mapstructure:"vpc_id" required:"false" cty:"vpc_id"` + WindowsPasswordTimeout *string `mapstructure:"windows_password_timeout" required:"false" cty:"windows_password_timeout"` + Type *string `mapstructure:"communicator" cty:"communicator"` + PauseBeforeConnect *string `mapstructure:"pause_before_connecting" cty:"pause_before_connecting"` + SSHHost *string `mapstructure:"ssh_host" cty:"ssh_host"` + SSHPort *int `mapstructure:"ssh_port" cty:"ssh_port"` + SSHUsername *string `mapstructure:"ssh_username" cty:"ssh_username"` + SSHPassword *string `mapstructure:"ssh_password" cty:"ssh_password"` + SSHKeyPairName *string `mapstructure:"ssh_keypair_name" cty:"ssh_keypair_name"` + SSHClearAuthorizedKeys *bool `mapstructure:"ssh_clear_authorized_keys" cty:"ssh_clear_authorized_keys"` + SSHPrivateKeyFile *string `mapstructure:"ssh_private_key_file" cty:"ssh_private_key_file"` + SSHPty *bool `mapstructure:"ssh_pty" cty:"ssh_pty"` + SSHTimeout *string `mapstructure:"ssh_timeout" cty:"ssh_timeout"` + SSHAgentAuth *bool `mapstructure:"ssh_agent_auth" cty:"ssh_agent_auth"` + SSHDisableAgentForwarding *bool `mapstructure:"ssh_disable_agent_forwarding" cty:"ssh_disable_agent_forwarding"` + SSHHandshakeAttempts *int `mapstructure:"ssh_handshake_attempts" cty:"ssh_handshake_attempts"` + SSHBastionHost *string `mapstructure:"ssh_bastion_host" cty:"ssh_bastion_host"` + SSHBastionPort *int `mapstructure:"ssh_bastion_port" cty:"ssh_bastion_port"` + SSHBastionAgentAuth *bool `mapstructure:"ssh_bastion_agent_auth" cty:"ssh_bastion_agent_auth"` + SSHBastionUsername *string `mapstructure:"ssh_bastion_username" cty:"ssh_bastion_username"` + SSHBastionPassword *string `mapstructure:"ssh_bastion_password" cty:"ssh_bastion_password"` + SSHBastionPrivateKeyFile *string `mapstructure:"ssh_bastion_private_key_file" cty:"ssh_bastion_private_key_file"` + SSHFileTransferMethod *string `mapstructure:"ssh_file_transfer_method" cty:"ssh_file_transfer_method"` + SSHProxyHost *string `mapstructure:"ssh_proxy_host" cty:"ssh_proxy_host"` + SSHProxyPort *int `mapstructure:"ssh_proxy_port" cty:"ssh_proxy_port"` + SSHProxyUsername *string `mapstructure:"ssh_proxy_username" cty:"ssh_proxy_username"` + SSHProxyPassword *string `mapstructure:"ssh_proxy_password" cty:"ssh_proxy_password"` + SSHKeepAliveInterval *string `mapstructure:"ssh_keep_alive_interval" cty:"ssh_keep_alive_interval"` + SSHReadWriteTimeout *string `mapstructure:"ssh_read_write_timeout" cty:"ssh_read_write_timeout"` + SSHRemoteTunnels []string `mapstructure:"ssh_remote_tunnels" cty:"ssh_remote_tunnels"` + SSHLocalTunnels []string `mapstructure:"ssh_local_tunnels" cty:"ssh_local_tunnels"` + SSHPublicKey []byte `mapstructure:"ssh_public_key" cty:"ssh_public_key"` + SSHPrivateKey []byte `mapstructure:"ssh_private_key" cty:"ssh_private_key"` + WinRMUser *string `mapstructure:"winrm_username" cty:"winrm_username"` + WinRMPassword *string `mapstructure:"winrm_password" cty:"winrm_password"` + WinRMHost *string `mapstructure:"winrm_host" cty:"winrm_host"` + WinRMPort *int `mapstructure:"winrm_port" cty:"winrm_port"` + WinRMTimeout *string `mapstructure:"winrm_timeout" cty:"winrm_timeout"` + WinRMUseSSL *bool `mapstructure:"winrm_use_ssl" cty:"winrm_use_ssl"` + WinRMInsecure *bool `mapstructure:"winrm_insecure" cty:"winrm_insecure"` + WinRMUseNTLM *bool `mapstructure:"winrm_use_ntlm" cty:"winrm_use_ntlm"` + SSHInterface *string `mapstructure:"ssh_interface" cty:"ssh_interface"` + AMIName *string `mapstructure:"ami_name" required:"true" cty:"ami_name"` + AMIDescription *string `mapstructure:"ami_description" required:"false" cty:"ami_description"` + AMIVirtType *string `mapstructure:"ami_virtualization_type" required:"false" cty:"ami_virtualization_type"` + AMIUsers []string `mapstructure:"ami_users" required:"false" cty:"ami_users"` + AMIGroups []string `mapstructure:"ami_groups" required:"false" cty:"ami_groups"` + AMIProductCodes []string `mapstructure:"ami_product_codes" required:"false" cty:"ami_product_codes"` + AMIRegions []string `mapstructure:"ami_regions" required:"false" cty:"ami_regions"` + AMITags common.TagMap `mapstructure:"tags" required:"false" cty:"tags"` + AMIENASupport *bool `mapstructure:"ena_support" required:"false" cty:"ena_support"` + AMISriovNetSupport *bool `mapstructure:"sriov_support" required:"false" cty:"sriov_support"` + AMIForceDeregister *bool `mapstructure:"force_deregister" required:"false" cty:"force_deregister"` + AMIForceDeleteSnapshot *bool `mapstructure:"force_delete_snapshot" required:"false" cty:"force_delete_snapshot"` + AMIEncryptBootVolume *bool `mapstructure:"encrypt_boot" required:"false" cty:"encrypt_boot"` + AMIKmsKeyId *string `mapstructure:"kms_key_id" required:"false" cty:"kms_key_id"` + AMIRegionKMSKeyIDs map[string]string `mapstructure:"region_kms_key_ids" required:"false" cty:"region_kms_key_ids"` + AMISkipBuildRegion *bool `mapstructure:"skip_save_build_region" cty:"skip_save_build_region"` + SnapshotTags common.TagMap `mapstructure:"snapshot_tags" required:"false" cty:"snapshot_tags"` + SnapshotUsers []string `mapstructure:"snapshot_users" required:"false" cty:"snapshot_users"` + SnapshotGroups []string `mapstructure:"snapshot_groups" required:"false" cty:"snapshot_groups"` + AMIMappings []common.FlatBlockDevice `mapstructure:"ami_block_device_mappings" required:"false" cty:"ami_block_device_mappings"` + LaunchMappings []FlatBlockDevice `mapstructure:"launch_block_device_mappings" required:"false" cty:"launch_block_device_mappings"` + RootDevice *FlatRootBlockDevice `mapstructure:"ami_root_device" required:"true" cty:"ami_root_device"` + VolumeRunTags common.TagMap `mapstructure:"run_volume_tags" cty:"run_volume_tags"` + Architecture *string `mapstructure:"ami_architecture" required:"false" cty:"ami_architecture"` +} + +// FlatMapstructure returns a new FlatConfig. +// FlatConfig is an auto-generated flat version of Config. +// Where the contents a fields with a `mapstructure:,squash` tag are bubbled up. +func (*Config) FlatMapstructure() interface{ HCL2Spec() map[string]hcldec.Spec } { + return new(FlatConfig) +} + +// HCL2Spec returns the hcl spec of a Config. +// This spec is used by HCL to read the fields of Config. +// The decoded values from this spec will then be applied to a FlatConfig. +func (*FlatConfig) HCL2Spec() map[string]hcldec.Spec { + s := map[string]hcldec.Spec{ + "packer_build_name": &hcldec.AttrSpec{Name: "packer_build_name", Type: cty.String, Required: false}, + "packer_builder_type": &hcldec.AttrSpec{Name: "packer_builder_type", Type: cty.String, Required: false}, + "packer_debug": &hcldec.AttrSpec{Name: "packer_debug", Type: cty.Bool, Required: false}, + "packer_force": &hcldec.AttrSpec{Name: "packer_force", Type: cty.Bool, Required: false}, + "packer_on_error": &hcldec.AttrSpec{Name: "packer_on_error", Type: cty.String, Required: false}, + "packer_user_variables": &hcldec.BlockAttrsSpec{TypeName: "packer_user_variables", ElementType: cty.String, Required: false}, + "packer_sensitive_variables": &hcldec.AttrSpec{Name: "packer_sensitive_variables", Type: cty.List(cty.String), Required: false}, + "access_key": &hcldec.AttrSpec{Name: "access_key", Type: cty.String, Required: false}, + "custom_endpoint_ec2": &hcldec.AttrSpec{Name: "custom_endpoint_ec2", Type: cty.String, Required: false}, + "decode_authorization_messages": &hcldec.AttrSpec{Name: "decode_authorization_messages", Type: cty.Bool, Required: false}, + "insecure_skip_tls_verify": &hcldec.AttrSpec{Name: "insecure_skip_tls_verify", Type: cty.Bool, Required: false}, + "mfa_code": &hcldec.AttrSpec{Name: "mfa_code", Type: cty.String, Required: false}, + "profile": &hcldec.AttrSpec{Name: "profile", Type: cty.String, Required: false}, + "region": &hcldec.AttrSpec{Name: "region", Type: cty.String, Required: false}, + "secret_key": &hcldec.AttrSpec{Name: "secret_key", Type: cty.String, Required: false}, + "skip_region_validation": &hcldec.AttrSpec{Name: "skip_region_validation", Type: cty.Bool, Required: false}, + "skip_metadata_api_check": &hcldec.AttrSpec{Name: "skip_metadata_api_check", Type: cty.Bool, Required: false}, + "token": &hcldec.AttrSpec{Name: "token", Type: cty.String, Required: false}, + "vault_aws_engine": &hcldec.BlockSpec{TypeName: "vault_aws_engine", Nested: hcldec.ObjectSpec((*common.FlatVaultAWSEngineOptions)(nil).HCL2Spec())}, + "associate_public_ip_address": &hcldec.AttrSpec{Name: "associate_public_ip_address", Type: cty.Bool, Required: false}, + "availability_zone": &hcldec.AttrSpec{Name: "availability_zone", Type: cty.String, Required: false}, + "block_duration_minutes": &hcldec.AttrSpec{Name: "block_duration_minutes", Type: cty.Number, Required: false}, + "disable_stop_instance": &hcldec.AttrSpec{Name: "disable_stop_instance", Type: cty.Bool, Required: false}, + "ebs_optimized": &hcldec.AttrSpec{Name: "ebs_optimized", Type: cty.Bool, Required: false}, + "enable_t2_unlimited": &hcldec.AttrSpec{Name: "enable_t2_unlimited", Type: cty.Bool, Required: false}, + "iam_instance_profile": &hcldec.AttrSpec{Name: "iam_instance_profile", Type: cty.String, Required: false}, + "skip_profile_validation": &hcldec.AttrSpec{Name: "skip_profile_validation", Type: cty.Bool, Required: false}, + "temporary_iam_instance_profile_policy_document": &hcldec.BlockSpec{TypeName: "temporary_iam_instance_profile_policy_document", Nested: hcldec.ObjectSpec((*common.FlatPolicyDocument)(nil).HCL2Spec())}, + "shutdown_behavior": &hcldec.AttrSpec{Name: "shutdown_behavior", Type: cty.String, Required: false}, + "instance_type": &hcldec.AttrSpec{Name: "instance_type", Type: cty.String, Required: false}, + "security_group_filter": &hcldec.BlockSpec{TypeName: "security_group_filter", Nested: hcldec.ObjectSpec((*common.FlatSecurityGroupFilterOptions)(nil).HCL2Spec())}, + "run_tags": &hcldec.BlockAttrsSpec{TypeName: "run_tags", ElementType: cty.String, Required: false}, + "security_group_id": &hcldec.AttrSpec{Name: "security_group_id", Type: cty.String, Required: false}, + "security_group_ids": &hcldec.AttrSpec{Name: "security_group_ids", Type: cty.List(cty.String), Required: false}, + "source_ami": &hcldec.AttrSpec{Name: "source_ami", Type: cty.String, Required: false}, + "source_ami_filter": &hcldec.BlockSpec{TypeName: "source_ami_filter", Nested: hcldec.ObjectSpec((*common.FlatAmiFilterOptions)(nil).HCL2Spec())}, + "spot_instance_types": &hcldec.AttrSpec{Name: "spot_instance_types", Type: cty.List(cty.String), Required: false}, + "spot_price": &hcldec.AttrSpec{Name: "spot_price", Type: cty.String, Required: false}, + "spot_price_auto_product": &hcldec.AttrSpec{Name: "spot_price_auto_product", Type: cty.String, Required: false}, + "spot_tags": &hcldec.BlockAttrsSpec{TypeName: "spot_tags", ElementType: cty.String, Required: false}, + "subnet_filter": &hcldec.BlockSpec{TypeName: "subnet_filter", Nested: hcldec.ObjectSpec((*common.FlatSubnetFilterOptions)(nil).HCL2Spec())}, + "subnet_id": &hcldec.AttrSpec{Name: "subnet_id", Type: cty.String, Required: false}, + "temporary_key_pair_name": &hcldec.AttrSpec{Name: "temporary_key_pair_name", Type: cty.String, Required: false}, + "temporary_security_group_source_cidrs": &hcldec.AttrSpec{Name: "temporary_security_group_source_cidrs", Type: cty.List(cty.String), Required: false}, + "user_data": &hcldec.AttrSpec{Name: "user_data", Type: cty.String, Required: false}, + "user_data_file": &hcldec.AttrSpec{Name: "user_data_file", Type: cty.String, Required: false}, + "vpc_filter": &hcldec.BlockSpec{TypeName: "vpc_filter", Nested: hcldec.ObjectSpec((*common.FlatVpcFilterOptions)(nil).HCL2Spec())}, + "vpc_id": &hcldec.AttrSpec{Name: "vpc_id", Type: cty.String, Required: false}, + "windows_password_timeout": &hcldec.AttrSpec{Name: "windows_password_timeout", Type: cty.String, Required: false}, + "communicator": &hcldec.AttrSpec{Name: "communicator", Type: cty.String, Required: false}, + "pause_before_connecting": &hcldec.AttrSpec{Name: "pause_before_connecting", Type: cty.String, Required: false}, + "ssh_host": &hcldec.AttrSpec{Name: "ssh_host", Type: cty.String, Required: false}, + "ssh_port": &hcldec.AttrSpec{Name: "ssh_port", Type: cty.Number, Required: false}, + "ssh_username": &hcldec.AttrSpec{Name: "ssh_username", Type: cty.String, Required: false}, + "ssh_password": &hcldec.AttrSpec{Name: "ssh_password", Type: cty.String, Required: false}, + "ssh_keypair_name": &hcldec.AttrSpec{Name: "ssh_keypair_name", Type: cty.String, Required: false}, + "ssh_clear_authorized_keys": &hcldec.AttrSpec{Name: "ssh_clear_authorized_keys", Type: cty.Bool, Required: false}, + "ssh_private_key_file": &hcldec.AttrSpec{Name: "ssh_private_key_file", Type: cty.String, Required: false}, + "ssh_pty": &hcldec.AttrSpec{Name: "ssh_pty", Type: cty.Bool, Required: false}, + "ssh_timeout": &hcldec.AttrSpec{Name: "ssh_timeout", Type: cty.String, Required: false}, + "ssh_agent_auth": &hcldec.AttrSpec{Name: "ssh_agent_auth", Type: cty.Bool, Required: false}, + "ssh_disable_agent_forwarding": &hcldec.AttrSpec{Name: "ssh_disable_agent_forwarding", Type: cty.Bool, Required: false}, + "ssh_handshake_attempts": &hcldec.AttrSpec{Name: "ssh_handshake_attempts", Type: cty.Number, Required: false}, + "ssh_bastion_host": &hcldec.AttrSpec{Name: "ssh_bastion_host", Type: cty.String, Required: false}, + "ssh_bastion_port": &hcldec.AttrSpec{Name: "ssh_bastion_port", Type: cty.Number, Required: false}, + "ssh_bastion_agent_auth": &hcldec.AttrSpec{Name: "ssh_bastion_agent_auth", Type: cty.Bool, Required: false}, + "ssh_bastion_username": &hcldec.AttrSpec{Name: "ssh_bastion_username", Type: cty.String, Required: false}, + "ssh_bastion_password": &hcldec.AttrSpec{Name: "ssh_bastion_password", Type: cty.String, Required: false}, + "ssh_bastion_private_key_file": &hcldec.AttrSpec{Name: "ssh_bastion_private_key_file", Type: cty.String, Required: false}, + "ssh_file_transfer_method": &hcldec.AttrSpec{Name: "ssh_file_transfer_method", Type: cty.String, Required: false}, + "ssh_proxy_host": &hcldec.AttrSpec{Name: "ssh_proxy_host", Type: cty.String, Required: false}, + "ssh_proxy_port": &hcldec.AttrSpec{Name: "ssh_proxy_port", Type: cty.Number, Required: false}, + "ssh_proxy_username": &hcldec.AttrSpec{Name: "ssh_proxy_username", Type: cty.String, Required: false}, + "ssh_proxy_password": &hcldec.AttrSpec{Name: "ssh_proxy_password", Type: cty.String, Required: false}, + "ssh_keep_alive_interval": &hcldec.AttrSpec{Name: "ssh_keep_alive_interval", Type: cty.String, Required: false}, + "ssh_read_write_timeout": &hcldec.AttrSpec{Name: "ssh_read_write_timeout", Type: cty.String, Required: false}, + "ssh_remote_tunnels": &hcldec.AttrSpec{Name: "ssh_remote_tunnels", Type: cty.List(cty.String), Required: false}, + "ssh_local_tunnels": &hcldec.AttrSpec{Name: "ssh_local_tunnels", Type: cty.List(cty.String), Required: false}, + "ssh_public_key": &hcldec.AttrSpec{Name: "ssh_public_key", Type: cty.List(cty.Number), Required: false}, + "ssh_private_key": &hcldec.AttrSpec{Name: "ssh_private_key", Type: cty.List(cty.Number), Required: false}, + "winrm_username": &hcldec.AttrSpec{Name: "winrm_username", Type: cty.String, Required: false}, + "winrm_password": &hcldec.AttrSpec{Name: "winrm_password", Type: cty.String, Required: false}, + "winrm_host": &hcldec.AttrSpec{Name: "winrm_host", Type: cty.String, Required: false}, + "winrm_port": &hcldec.AttrSpec{Name: "winrm_port", Type: cty.Number, Required: false}, + "winrm_timeout": &hcldec.AttrSpec{Name: "winrm_timeout", Type: cty.String, Required: false}, + "winrm_use_ssl": &hcldec.AttrSpec{Name: "winrm_use_ssl", Type: cty.Bool, Required: false}, + "winrm_insecure": &hcldec.AttrSpec{Name: "winrm_insecure", Type: cty.Bool, Required: false}, + "winrm_use_ntlm": &hcldec.AttrSpec{Name: "winrm_use_ntlm", Type: cty.Bool, Required: false}, + "ssh_interface": &hcldec.AttrSpec{Name: "ssh_interface", Type: cty.String, Required: false}, + "ami_name": &hcldec.AttrSpec{Name: "ami_name", Type: cty.String, Required: false}, + "ami_description": &hcldec.AttrSpec{Name: "ami_description", Type: cty.String, Required: false}, + "ami_virtualization_type": &hcldec.AttrSpec{Name: "ami_virtualization_type", Type: cty.String, Required: false}, + "ami_users": &hcldec.AttrSpec{Name: "ami_users", Type: cty.List(cty.String), Required: false}, + "ami_groups": &hcldec.AttrSpec{Name: "ami_groups", Type: cty.List(cty.String), Required: false}, + "ami_product_codes": &hcldec.AttrSpec{Name: "ami_product_codes", Type: cty.List(cty.String), Required: false}, + "ami_regions": &hcldec.AttrSpec{Name: "ami_regions", Type: cty.List(cty.String), Required: false}, + "tags": &hcldec.BlockAttrsSpec{TypeName: "common.TagMap", ElementType: cty.String, Required: false}, + "ena_support": &hcldec.AttrSpec{Name: "ena_support", Type: cty.Bool, Required: false}, + "sriov_support": &hcldec.AttrSpec{Name: "sriov_support", Type: cty.Bool, Required: false}, + "force_deregister": &hcldec.AttrSpec{Name: "force_deregister", Type: cty.Bool, Required: false}, + "force_delete_snapshot": &hcldec.AttrSpec{Name: "force_delete_snapshot", Type: cty.Bool, Required: false}, + "encrypt_boot": &hcldec.AttrSpec{Name: "encrypt_boot", Type: cty.Bool, Required: false}, + "kms_key_id": &hcldec.AttrSpec{Name: "kms_key_id", Type: cty.String, Required: false}, + "region_kms_key_ids": &hcldec.BlockAttrsSpec{TypeName: "region_kms_key_ids", ElementType: cty.String, Required: false}, + "skip_save_build_region": &hcldec.AttrSpec{Name: "skip_save_build_region", Type: cty.Bool, Required: false}, + "snapshot_tags": &hcldec.BlockAttrsSpec{TypeName: "common.TagMap", ElementType: cty.String, Required: false}, + "snapshot_users": &hcldec.AttrSpec{Name: "snapshot_users", Type: cty.List(cty.String), Required: false}, + "snapshot_groups": &hcldec.AttrSpec{Name: "snapshot_groups", Type: cty.List(cty.String), Required: false}, + "ami_block_device_mappings": &hcldec.BlockListSpec{TypeName: "ami_block_device_mappings", Nested: hcldec.ObjectSpec((*common.FlatBlockDevice)(nil).HCL2Spec())}, + "launch_block_device_mappings": &hcldec.BlockListSpec{TypeName: "launch_block_device_mappings", Nested: hcldec.ObjectSpec((*FlatBlockDevice)(nil).HCL2Spec())}, + "ami_root_device": &hcldec.BlockSpec{TypeName: "ami_root_device", Nested: hcldec.ObjectSpec((*FlatRootBlockDevice)(nil).HCL2Spec())}, + "run_volume_tags": &hcldec.BlockAttrsSpec{TypeName: "common.TagMap", ElementType: cty.String, Required: false}, + "ami_architecture": &hcldec.AttrSpec{Name: "ami_architecture", Type: cty.String, Required: false}, + } + return s +} + +// FlatRootBlockDevice is an auto-generated flat version of RootBlockDevice. +// Where the contents of a field with a `mapstructure:,squash` tag are bubbled up. +type FlatRootBlockDevice struct { + SourceDeviceName *string `mapstructure:"source_device_name" cty:"source_device_name"` + DeviceName *string `mapstructure:"device_name" required:"false" cty:"device_name"` + DeleteOnTermination *bool `mapstructure:"delete_on_termination" required:"false" cty:"delete_on_termination"` + IOPS *int64 `mapstructure:"iops" required:"false" cty:"iops"` + VolumeType *string `mapstructure:"volume_type" required:"false" cty:"volume_type"` + VolumeSize *int64 `mapstructure:"volume_size" required:"false" cty:"volume_size"` +} + +// FlatMapstructure returns a new FlatRootBlockDevice. +// FlatRootBlockDevice is an auto-generated flat version of RootBlockDevice. +// Where the contents a fields with a `mapstructure:,squash` tag are bubbled up. +func (*RootBlockDevice) FlatMapstructure() interface{ HCL2Spec() map[string]hcldec.Spec } { + return new(FlatRootBlockDevice) +} + +// HCL2Spec returns the hcl spec of a RootBlockDevice. +// This spec is used by HCL to read the fields of RootBlockDevice. +// The decoded values from this spec will then be applied to a FlatRootBlockDevice. +func (*FlatRootBlockDevice) HCL2Spec() map[string]hcldec.Spec { + s := map[string]hcldec.Spec{ + "source_device_name": &hcldec.AttrSpec{Name: "source_device_name", Type: cty.String, Required: false}, + "device_name": &hcldec.AttrSpec{Name: "device_name", Type: cty.String, Required: false}, + "delete_on_termination": &hcldec.AttrSpec{Name: "delete_on_termination", Type: cty.Bool, Required: false}, + "iops": &hcldec.AttrSpec{Name: "iops", Type: cty.Number, Required: false}, + "volume_type": &hcldec.AttrSpec{Name: "volume_type", Type: cty.String, Required: false}, + "volume_size": &hcldec.AttrSpec{Name: "volume_size", Type: cty.Number, Required: false}, + } + return s +} diff --git a/builder/amazon/ebsvolume/builder.go b/builder/amazon/ebsvolume/builder.go index b3d05299d..886123cb4 100644 --- a/builder/amazon/ebsvolume/builder.go +++ b/builder/amazon/ebsvolume/builder.go @@ -1,4 +1,5 @@ //go:generate struct-markdown +//go:generate mapstructure-to-hcl2 -type Config,BlockDevice // The ebsvolume package contains a packer.Builder implementation that builds // EBS volumes for Amazon EC2 using an ephemeral instance, @@ -10,6 +11,7 @@ import ( "github.com/aws/aws-sdk-go/service/ec2" "github.com/aws/aws-sdk-go/service/iam" + "github.com/hashicorp/hcl/v2/hcldec" awscommon "github.com/hashicorp/packer/builder/amazon/common" "github.com/hashicorp/packer/common" "github.com/hashicorp/packer/helper/communicator" @@ -79,6 +81,8 @@ type EngineVarsTemplate struct { SourceAMI string } +func (b *Builder) ConfigSpec() hcldec.ObjectSpec { return b.config.FlatMapstructure().HCL2Spec() } + func (b *Builder) Prepare(raws ...interface{}) ([]string, error) { b.config.ctx.Funcs = awscommon.TemplateFuncs // Create passthrough for {{ .BuildRegion }} and {{ .SourceAMI }} variables diff --git a/builder/amazon/ebsvolume/builder.hcl2spec.go b/builder/amazon/ebsvolume/builder.hcl2spec.go new file mode 100644 index 000000000..20aab5aef --- /dev/null +++ b/builder/amazon/ebsvolume/builder.hcl2spec.go @@ -0,0 +1,258 @@ +// Code generated by "mapstructure-to-hcl2 -type Config,BlockDevice"; DO NOT EDIT. +package ebsvolume + +import ( + "github.com/hashicorp/hcl/v2/hcldec" + "github.com/hashicorp/packer/builder/amazon/common" + "github.com/zclconf/go-cty/cty" +) + +// FlatBlockDevice is an auto-generated flat version of BlockDevice. +// Where the contents of a field with a `mapstructure:,squash` tag are bubbled up. +type FlatBlockDevice struct { + DeleteOnTermination *bool `mapstructure:"delete_on_termination" required:"false" cty:"delete_on_termination"` + DeviceName *string `mapstructure:"device_name" required:"false" cty:"device_name"` + Encrypted *bool `mapstructure:"encrypted" required:"false" cty:"encrypted"` + IOPS *int64 `mapstructure:"iops" required:"false" cty:"iops"` + NoDevice *bool `mapstructure:"no_device" required:"false" cty:"no_device"` + SnapshotId *string `mapstructure:"snapshot_id" required:"false" cty:"snapshot_id"` + VirtualName *string `mapstructure:"virtual_name" required:"false" cty:"virtual_name"` + VolumeType *string `mapstructure:"volume_type" required:"false" cty:"volume_type"` + VolumeSize *int64 `mapstructure:"volume_size" required:"false" cty:"volume_size"` + KmsKeyId *string `mapstructure:"kms_key_id" required:"false" cty:"kms_key_id"` + Tags common.TagMap `mapstructure:"tags" required:"false" cty:"tags"` +} + +// FlatMapstructure returns a new FlatBlockDevice. +// FlatBlockDevice is an auto-generated flat version of BlockDevice. +// Where the contents a fields with a `mapstructure:,squash` tag are bubbled up. +func (*BlockDevice) FlatMapstructure() interface{ HCL2Spec() map[string]hcldec.Spec } { + return new(FlatBlockDevice) +} + +// HCL2Spec returns the hcl spec of a BlockDevice. +// This spec is used by HCL to read the fields of BlockDevice. +// The decoded values from this spec will then be applied to a FlatBlockDevice. +func (*FlatBlockDevice) HCL2Spec() map[string]hcldec.Spec { + s := map[string]hcldec.Spec{ + "delete_on_termination": &hcldec.AttrSpec{Name: "delete_on_termination", Type: cty.Bool, Required: false}, + "device_name": &hcldec.AttrSpec{Name: "device_name", Type: cty.String, Required: false}, + "encrypted": &hcldec.AttrSpec{Name: "encrypted", Type: cty.Bool, Required: false}, + "iops": &hcldec.AttrSpec{Name: "iops", Type: cty.Number, Required: false}, + "no_device": &hcldec.AttrSpec{Name: "no_device", Type: cty.Bool, Required: false}, + "snapshot_id": &hcldec.AttrSpec{Name: "snapshot_id", Type: cty.String, Required: false}, + "virtual_name": &hcldec.AttrSpec{Name: "virtual_name", Type: cty.String, Required: false}, + "volume_type": &hcldec.AttrSpec{Name: "volume_type", Type: cty.String, Required: false}, + "volume_size": &hcldec.AttrSpec{Name: "volume_size", Type: cty.Number, Required: false}, + "kms_key_id": &hcldec.AttrSpec{Name: "kms_key_id", Type: cty.String, Required: false}, + "tags": &hcldec.BlockAttrsSpec{TypeName: "common.TagMap", ElementType: cty.String, Required: false}, + } + return s +} + +// FlatConfig is an auto-generated flat version of Config. +// Where the contents of a field with a `mapstructure:,squash` tag are bubbled up. +type FlatConfig struct { + PackerBuildName *string `mapstructure:"packer_build_name" cty:"packer_build_name"` + PackerBuilderType *string `mapstructure:"packer_builder_type" cty:"packer_builder_type"` + PackerDebug *bool `mapstructure:"packer_debug" cty:"packer_debug"` + PackerForce *bool `mapstructure:"packer_force" cty:"packer_force"` + PackerOnError *string `mapstructure:"packer_on_error" cty:"packer_on_error"` + PackerUserVars map[string]string `mapstructure:"packer_user_variables" cty:"packer_user_variables"` + PackerSensitiveVars []string `mapstructure:"packer_sensitive_variables" cty:"packer_sensitive_variables"` + AccessKey *string `mapstructure:"access_key" required:"true" cty:"access_key"` + CustomEndpointEc2 *string `mapstructure:"custom_endpoint_ec2" required:"false" cty:"custom_endpoint_ec2"` + DecodeAuthZMessages *bool `mapstructure:"decode_authorization_messages" required:"false" cty:"decode_authorization_messages"` + InsecureSkipTLSVerify *bool `mapstructure:"insecure_skip_tls_verify" required:"false" cty:"insecure_skip_tls_verify"` + MFACode *string `mapstructure:"mfa_code" required:"false" cty:"mfa_code"` + ProfileName *string `mapstructure:"profile" required:"false" cty:"profile"` + RawRegion *string `mapstructure:"region" required:"true" cty:"region"` + SecretKey *string `mapstructure:"secret_key" required:"true" cty:"secret_key"` + SkipValidation *bool `mapstructure:"skip_region_validation" required:"false" cty:"skip_region_validation"` + SkipMetadataApiCheck *bool `mapstructure:"skip_metadata_api_check" cty:"skip_metadata_api_check"` + Token *string `mapstructure:"token" required:"false" cty:"token"` + VaultAWSEngine *common.FlatVaultAWSEngineOptions `mapstructure:"vault_aws_engine" required:"false" cty:"vault_aws_engine"` + AssociatePublicIpAddress *bool `mapstructure:"associate_public_ip_address" required:"false" cty:"associate_public_ip_address"` + AvailabilityZone *string `mapstructure:"availability_zone" required:"false" cty:"availability_zone"` + BlockDurationMinutes *int64 `mapstructure:"block_duration_minutes" required:"false" cty:"block_duration_minutes"` + DisableStopInstance *bool `mapstructure:"disable_stop_instance" required:"false" cty:"disable_stop_instance"` + EbsOptimized *bool `mapstructure:"ebs_optimized" required:"false" cty:"ebs_optimized"` + EnableT2Unlimited *bool `mapstructure:"enable_t2_unlimited" required:"false" cty:"enable_t2_unlimited"` + IamInstanceProfile *string `mapstructure:"iam_instance_profile" required:"false" cty:"iam_instance_profile"` + SkipProfileValidation *bool `mapstructure:"skip_profile_validation" required:"false" cty:"skip_profile_validation"` + TemporaryIamInstanceProfilePolicyDocument *common.FlatPolicyDocument `mapstructure:"temporary_iam_instance_profile_policy_document" required:"false" cty:"temporary_iam_instance_profile_policy_document"` + InstanceInitiatedShutdownBehavior *string `mapstructure:"shutdown_behavior" required:"false" cty:"shutdown_behavior"` + InstanceType *string `mapstructure:"instance_type" required:"true" cty:"instance_type"` + SecurityGroupFilter *common.FlatSecurityGroupFilterOptions `mapstructure:"security_group_filter" required:"false" cty:"security_group_filter"` + RunTags map[string]string `mapstructure:"run_tags" required:"false" cty:"run_tags"` + SecurityGroupId *string `mapstructure:"security_group_id" required:"false" cty:"security_group_id"` + SecurityGroupIds []string `mapstructure:"security_group_ids" required:"false" cty:"security_group_ids"` + SourceAmi *string `mapstructure:"source_ami" required:"true" cty:"source_ami"` + SourceAmiFilter *common.FlatAmiFilterOptions `mapstructure:"source_ami_filter" required:"false" cty:"source_ami_filter"` + SpotInstanceTypes []string `mapstructure:"spot_instance_types" required:"false" cty:"spot_instance_types"` + SpotPrice *string `mapstructure:"spot_price" required:"false" cty:"spot_price"` + SpotPriceAutoProduct *string `mapstructure:"spot_price_auto_product" required:"false" cty:"spot_price_auto_product"` + SpotTags map[string]string `mapstructure:"spot_tags" required:"false" cty:"spot_tags"` + SubnetFilter *common.FlatSubnetFilterOptions `mapstructure:"subnet_filter" required:"false" cty:"subnet_filter"` + SubnetId *string `mapstructure:"subnet_id" required:"false" cty:"subnet_id"` + TemporaryKeyPairName *string `mapstructure:"temporary_key_pair_name" required:"false" cty:"temporary_key_pair_name"` + TemporarySGSourceCidrs []string `mapstructure:"temporary_security_group_source_cidrs" required:"false" cty:"temporary_security_group_source_cidrs"` + UserData *string `mapstructure:"user_data" required:"false" cty:"user_data"` + UserDataFile *string `mapstructure:"user_data_file" required:"false" cty:"user_data_file"` + VpcFilter *common.FlatVpcFilterOptions `mapstructure:"vpc_filter" required:"false" cty:"vpc_filter"` + VpcId *string `mapstructure:"vpc_id" required:"false" cty:"vpc_id"` + WindowsPasswordTimeout *string `mapstructure:"windows_password_timeout" required:"false" cty:"windows_password_timeout"` + Type *string `mapstructure:"communicator" cty:"communicator"` + PauseBeforeConnect *string `mapstructure:"pause_before_connecting" cty:"pause_before_connecting"` + SSHHost *string `mapstructure:"ssh_host" cty:"ssh_host"` + SSHPort *int `mapstructure:"ssh_port" cty:"ssh_port"` + SSHUsername *string `mapstructure:"ssh_username" cty:"ssh_username"` + SSHPassword *string `mapstructure:"ssh_password" cty:"ssh_password"` + SSHKeyPairName *string `mapstructure:"ssh_keypair_name" cty:"ssh_keypair_name"` + SSHClearAuthorizedKeys *bool `mapstructure:"ssh_clear_authorized_keys" cty:"ssh_clear_authorized_keys"` + SSHPrivateKeyFile *string `mapstructure:"ssh_private_key_file" cty:"ssh_private_key_file"` + SSHPty *bool `mapstructure:"ssh_pty" cty:"ssh_pty"` + SSHTimeout *string `mapstructure:"ssh_timeout" cty:"ssh_timeout"` + SSHAgentAuth *bool `mapstructure:"ssh_agent_auth" cty:"ssh_agent_auth"` + SSHDisableAgentForwarding *bool `mapstructure:"ssh_disable_agent_forwarding" cty:"ssh_disable_agent_forwarding"` + SSHHandshakeAttempts *int `mapstructure:"ssh_handshake_attempts" cty:"ssh_handshake_attempts"` + SSHBastionHost *string `mapstructure:"ssh_bastion_host" cty:"ssh_bastion_host"` + SSHBastionPort *int `mapstructure:"ssh_bastion_port" cty:"ssh_bastion_port"` + SSHBastionAgentAuth *bool `mapstructure:"ssh_bastion_agent_auth" cty:"ssh_bastion_agent_auth"` + SSHBastionUsername *string `mapstructure:"ssh_bastion_username" cty:"ssh_bastion_username"` + SSHBastionPassword *string `mapstructure:"ssh_bastion_password" cty:"ssh_bastion_password"` + SSHBastionPrivateKeyFile *string `mapstructure:"ssh_bastion_private_key_file" cty:"ssh_bastion_private_key_file"` + SSHFileTransferMethod *string `mapstructure:"ssh_file_transfer_method" cty:"ssh_file_transfer_method"` + SSHProxyHost *string `mapstructure:"ssh_proxy_host" cty:"ssh_proxy_host"` + SSHProxyPort *int `mapstructure:"ssh_proxy_port" cty:"ssh_proxy_port"` + SSHProxyUsername *string `mapstructure:"ssh_proxy_username" cty:"ssh_proxy_username"` + SSHProxyPassword *string `mapstructure:"ssh_proxy_password" cty:"ssh_proxy_password"` + SSHKeepAliveInterval *string `mapstructure:"ssh_keep_alive_interval" cty:"ssh_keep_alive_interval"` + SSHReadWriteTimeout *string `mapstructure:"ssh_read_write_timeout" cty:"ssh_read_write_timeout"` + SSHRemoteTunnels []string `mapstructure:"ssh_remote_tunnels" cty:"ssh_remote_tunnels"` + SSHLocalTunnels []string `mapstructure:"ssh_local_tunnels" cty:"ssh_local_tunnels"` + SSHPublicKey []byte `mapstructure:"ssh_public_key" cty:"ssh_public_key"` + SSHPrivateKey []byte `mapstructure:"ssh_private_key" cty:"ssh_private_key"` + WinRMUser *string `mapstructure:"winrm_username" cty:"winrm_username"` + WinRMPassword *string `mapstructure:"winrm_password" cty:"winrm_password"` + WinRMHost *string `mapstructure:"winrm_host" cty:"winrm_host"` + WinRMPort *int `mapstructure:"winrm_port" cty:"winrm_port"` + WinRMTimeout *string `mapstructure:"winrm_timeout" cty:"winrm_timeout"` + WinRMUseSSL *bool `mapstructure:"winrm_use_ssl" cty:"winrm_use_ssl"` + WinRMInsecure *bool `mapstructure:"winrm_insecure" cty:"winrm_insecure"` + WinRMUseNTLM *bool `mapstructure:"winrm_use_ntlm" cty:"winrm_use_ntlm"` + SSHInterface *string `mapstructure:"ssh_interface" cty:"ssh_interface"` + AMIENASupport *bool `mapstructure:"ena_support" required:"false" cty:"ena_support"` + AMISriovNetSupport *bool `mapstructure:"sriov_support" required:"false" cty:"sriov_support"` + VolumeMappings []FlatBlockDevice `mapstructure:"ebs_volumes" required:"false" cty:"ebs_volumes"` + VolumeRunTags common.TagMap `mapstructure:"run_volume_tags" cty:"run_volume_tags"` +} + +// FlatMapstructure returns a new FlatConfig. +// FlatConfig is an auto-generated flat version of Config. +// Where the contents a fields with a `mapstructure:,squash` tag are bubbled up. +func (*Config) FlatMapstructure() interface{ HCL2Spec() map[string]hcldec.Spec } { + return new(FlatConfig) +} + +// HCL2Spec returns the hcl spec of a Config. +// This spec is used by HCL to read the fields of Config. +// The decoded values from this spec will then be applied to a FlatConfig. +func (*FlatConfig) HCL2Spec() map[string]hcldec.Spec { + s := map[string]hcldec.Spec{ + "packer_build_name": &hcldec.AttrSpec{Name: "packer_build_name", Type: cty.String, Required: false}, + "packer_builder_type": &hcldec.AttrSpec{Name: "packer_builder_type", Type: cty.String, Required: false}, + "packer_debug": &hcldec.AttrSpec{Name: "packer_debug", Type: cty.Bool, Required: false}, + "packer_force": &hcldec.AttrSpec{Name: "packer_force", Type: cty.Bool, Required: false}, + "packer_on_error": &hcldec.AttrSpec{Name: "packer_on_error", Type: cty.String, Required: false}, + "packer_user_variables": &hcldec.BlockAttrsSpec{TypeName: "packer_user_variables", ElementType: cty.String, Required: false}, + "packer_sensitive_variables": &hcldec.AttrSpec{Name: "packer_sensitive_variables", Type: cty.List(cty.String), Required: false}, + "access_key": &hcldec.AttrSpec{Name: "access_key", Type: cty.String, Required: false}, + "custom_endpoint_ec2": &hcldec.AttrSpec{Name: "custom_endpoint_ec2", Type: cty.String, Required: false}, + "decode_authorization_messages": &hcldec.AttrSpec{Name: "decode_authorization_messages", Type: cty.Bool, Required: false}, + "insecure_skip_tls_verify": &hcldec.AttrSpec{Name: "insecure_skip_tls_verify", Type: cty.Bool, Required: false}, + "mfa_code": &hcldec.AttrSpec{Name: "mfa_code", Type: cty.String, Required: false}, + "profile": &hcldec.AttrSpec{Name: "profile", Type: cty.String, Required: false}, + "region": &hcldec.AttrSpec{Name: "region", Type: cty.String, Required: false}, + "secret_key": &hcldec.AttrSpec{Name: "secret_key", Type: cty.String, Required: false}, + "skip_region_validation": &hcldec.AttrSpec{Name: "skip_region_validation", Type: cty.Bool, Required: false}, + "skip_metadata_api_check": &hcldec.AttrSpec{Name: "skip_metadata_api_check", Type: cty.Bool, Required: false}, + "token": &hcldec.AttrSpec{Name: "token", Type: cty.String, Required: false}, + "vault_aws_engine": &hcldec.BlockSpec{TypeName: "vault_aws_engine", Nested: hcldec.ObjectSpec((*common.FlatVaultAWSEngineOptions)(nil).HCL2Spec())}, + "associate_public_ip_address": &hcldec.AttrSpec{Name: "associate_public_ip_address", Type: cty.Bool, Required: false}, + "availability_zone": &hcldec.AttrSpec{Name: "availability_zone", Type: cty.String, Required: false}, + "block_duration_minutes": &hcldec.AttrSpec{Name: "block_duration_minutes", Type: cty.Number, Required: false}, + "disable_stop_instance": &hcldec.AttrSpec{Name: "disable_stop_instance", Type: cty.Bool, Required: false}, + "ebs_optimized": &hcldec.AttrSpec{Name: "ebs_optimized", Type: cty.Bool, Required: false}, + "enable_t2_unlimited": &hcldec.AttrSpec{Name: "enable_t2_unlimited", Type: cty.Bool, Required: false}, + "iam_instance_profile": &hcldec.AttrSpec{Name: "iam_instance_profile", Type: cty.String, Required: false}, + "skip_profile_validation": &hcldec.AttrSpec{Name: "skip_profile_validation", Type: cty.Bool, Required: false}, + "temporary_iam_instance_profile_policy_document": &hcldec.BlockSpec{TypeName: "temporary_iam_instance_profile_policy_document", Nested: hcldec.ObjectSpec((*common.FlatPolicyDocument)(nil).HCL2Spec())}, + "shutdown_behavior": &hcldec.AttrSpec{Name: "shutdown_behavior", Type: cty.String, Required: false}, + "instance_type": &hcldec.AttrSpec{Name: "instance_type", Type: cty.String, Required: false}, + "security_group_filter": &hcldec.BlockSpec{TypeName: "security_group_filter", Nested: hcldec.ObjectSpec((*common.FlatSecurityGroupFilterOptions)(nil).HCL2Spec())}, + "run_tags": &hcldec.BlockAttrsSpec{TypeName: "run_tags", ElementType: cty.String, Required: false}, + "security_group_id": &hcldec.AttrSpec{Name: "security_group_id", Type: cty.String, Required: false}, + "security_group_ids": &hcldec.AttrSpec{Name: "security_group_ids", Type: cty.List(cty.String), Required: false}, + "source_ami": &hcldec.AttrSpec{Name: "source_ami", Type: cty.String, Required: false}, + "source_ami_filter": &hcldec.BlockSpec{TypeName: "source_ami_filter", Nested: hcldec.ObjectSpec((*common.FlatAmiFilterOptions)(nil).HCL2Spec())}, + "spot_instance_types": &hcldec.AttrSpec{Name: "spot_instance_types", Type: cty.List(cty.String), Required: false}, + "spot_price": &hcldec.AttrSpec{Name: "spot_price", Type: cty.String, Required: false}, + "spot_price_auto_product": &hcldec.AttrSpec{Name: "spot_price_auto_product", Type: cty.String, Required: false}, + "spot_tags": &hcldec.BlockAttrsSpec{TypeName: "spot_tags", ElementType: cty.String, Required: false}, + "subnet_filter": &hcldec.BlockSpec{TypeName: "subnet_filter", Nested: hcldec.ObjectSpec((*common.FlatSubnetFilterOptions)(nil).HCL2Spec())}, + "subnet_id": &hcldec.AttrSpec{Name: "subnet_id", Type: cty.String, Required: false}, + "temporary_key_pair_name": &hcldec.AttrSpec{Name: "temporary_key_pair_name", Type: cty.String, Required: false}, + "temporary_security_group_source_cidrs": &hcldec.AttrSpec{Name: "temporary_security_group_source_cidrs", Type: cty.List(cty.String), Required: false}, + "user_data": &hcldec.AttrSpec{Name: "user_data", Type: cty.String, Required: false}, + "user_data_file": &hcldec.AttrSpec{Name: "user_data_file", Type: cty.String, Required: false}, + "vpc_filter": &hcldec.BlockSpec{TypeName: "vpc_filter", Nested: hcldec.ObjectSpec((*common.FlatVpcFilterOptions)(nil).HCL2Spec())}, + "vpc_id": &hcldec.AttrSpec{Name: "vpc_id", Type: cty.String, Required: false}, + "windows_password_timeout": &hcldec.AttrSpec{Name: "windows_password_timeout", Type: cty.String, Required: false}, + "communicator": &hcldec.AttrSpec{Name: "communicator", Type: cty.String, Required: false}, + "pause_before_connecting": &hcldec.AttrSpec{Name: "pause_before_connecting", Type: cty.String, Required: false}, + "ssh_host": &hcldec.AttrSpec{Name: "ssh_host", Type: cty.String, Required: false}, + "ssh_port": &hcldec.AttrSpec{Name: "ssh_port", Type: cty.Number, Required: false}, + "ssh_username": &hcldec.AttrSpec{Name: "ssh_username", Type: cty.String, Required: false}, + "ssh_password": &hcldec.AttrSpec{Name: "ssh_password", Type: cty.String, Required: false}, + "ssh_keypair_name": &hcldec.AttrSpec{Name: "ssh_keypair_name", Type: cty.String, Required: false}, + "ssh_clear_authorized_keys": &hcldec.AttrSpec{Name: "ssh_clear_authorized_keys", Type: cty.Bool, Required: false}, + "ssh_private_key_file": &hcldec.AttrSpec{Name: "ssh_private_key_file", Type: cty.String, Required: false}, + "ssh_pty": &hcldec.AttrSpec{Name: "ssh_pty", Type: cty.Bool, Required: false}, + "ssh_timeout": &hcldec.AttrSpec{Name: "ssh_timeout", Type: cty.String, Required: false}, + "ssh_agent_auth": &hcldec.AttrSpec{Name: "ssh_agent_auth", Type: cty.Bool, Required: false}, + "ssh_disable_agent_forwarding": &hcldec.AttrSpec{Name: "ssh_disable_agent_forwarding", Type: cty.Bool, Required: false}, + "ssh_handshake_attempts": &hcldec.AttrSpec{Name: "ssh_handshake_attempts", Type: cty.Number, Required: false}, + "ssh_bastion_host": &hcldec.AttrSpec{Name: "ssh_bastion_host", Type: cty.String, Required: false}, + "ssh_bastion_port": &hcldec.AttrSpec{Name: "ssh_bastion_port", Type: cty.Number, Required: false}, + "ssh_bastion_agent_auth": &hcldec.AttrSpec{Name: "ssh_bastion_agent_auth", Type: cty.Bool, Required: false}, + "ssh_bastion_username": &hcldec.AttrSpec{Name: "ssh_bastion_username", Type: cty.String, Required: false}, + "ssh_bastion_password": &hcldec.AttrSpec{Name: "ssh_bastion_password", Type: cty.String, Required: false}, + "ssh_bastion_private_key_file": &hcldec.AttrSpec{Name: "ssh_bastion_private_key_file", Type: cty.String, Required: false}, + "ssh_file_transfer_method": &hcldec.AttrSpec{Name: "ssh_file_transfer_method", Type: cty.String, Required: false}, + "ssh_proxy_host": &hcldec.AttrSpec{Name: "ssh_proxy_host", Type: cty.String, Required: false}, + "ssh_proxy_port": &hcldec.AttrSpec{Name: "ssh_proxy_port", Type: cty.Number, Required: false}, + "ssh_proxy_username": &hcldec.AttrSpec{Name: "ssh_proxy_username", Type: cty.String, Required: false}, + "ssh_proxy_password": &hcldec.AttrSpec{Name: "ssh_proxy_password", Type: cty.String, Required: false}, + "ssh_keep_alive_interval": &hcldec.AttrSpec{Name: "ssh_keep_alive_interval", Type: cty.String, Required: false}, + "ssh_read_write_timeout": &hcldec.AttrSpec{Name: "ssh_read_write_timeout", Type: cty.String, Required: false}, + "ssh_remote_tunnels": &hcldec.AttrSpec{Name: "ssh_remote_tunnels", Type: cty.List(cty.String), Required: false}, + "ssh_local_tunnels": &hcldec.AttrSpec{Name: "ssh_local_tunnels", Type: cty.List(cty.String), Required: false}, + "ssh_public_key": &hcldec.AttrSpec{Name: "ssh_public_key", Type: cty.List(cty.Number), Required: false}, + "ssh_private_key": &hcldec.AttrSpec{Name: "ssh_private_key", Type: cty.List(cty.Number), Required: false}, + "winrm_username": &hcldec.AttrSpec{Name: "winrm_username", Type: cty.String, Required: false}, + "winrm_password": &hcldec.AttrSpec{Name: "winrm_password", Type: cty.String, Required: false}, + "winrm_host": &hcldec.AttrSpec{Name: "winrm_host", Type: cty.String, Required: false}, + "winrm_port": &hcldec.AttrSpec{Name: "winrm_port", Type: cty.Number, Required: false}, + "winrm_timeout": &hcldec.AttrSpec{Name: "winrm_timeout", Type: cty.String, Required: false}, + "winrm_use_ssl": &hcldec.AttrSpec{Name: "winrm_use_ssl", Type: cty.Bool, Required: false}, + "winrm_insecure": &hcldec.AttrSpec{Name: "winrm_insecure", Type: cty.Bool, Required: false}, + "winrm_use_ntlm": &hcldec.AttrSpec{Name: "winrm_use_ntlm", Type: cty.Bool, Required: false}, + "ssh_interface": &hcldec.AttrSpec{Name: "ssh_interface", Type: cty.String, Required: false}, + "ena_support": &hcldec.AttrSpec{Name: "ena_support", Type: cty.Bool, Required: false}, + "sriov_support": &hcldec.AttrSpec{Name: "sriov_support", Type: cty.Bool, Required: false}, + "ebs_volumes": &hcldec.BlockListSpec{TypeName: "ebs_volumes", Nested: hcldec.ObjectSpec((*FlatBlockDevice)(nil).HCL2Spec())}, + "run_volume_tags": &hcldec.BlockAttrsSpec{TypeName: "common.TagMap", ElementType: cty.String, Required: false}, + } + return s +} diff --git a/builder/amazon/instance/builder.go b/builder/amazon/instance/builder.go index 92a761459..ba87b61d8 100644 --- a/builder/amazon/instance/builder.go +++ b/builder/amazon/instance/builder.go @@ -1,4 +1,5 @@ //go:generate struct-markdown +//go:generate mapstructure-to-hcl2 -type Config // The instance package contains a packer.Builder implementation that builds // AMIs for Amazon EC2 backed by instance storage, as opposed to EBS storage. @@ -13,6 +14,7 @@ import ( "github.com/aws/aws-sdk-go/service/ec2" "github.com/aws/aws-sdk-go/service/iam" + "github.com/hashicorp/hcl/v2/hcldec" awscommon "github.com/hashicorp/packer/builder/amazon/common" "github.com/hashicorp/packer/common" "github.com/hashicorp/packer/helper/communicator" @@ -93,6 +95,8 @@ type Builder struct { runner multistep.Runner } +func (b *Builder) ConfigSpec() hcldec.ObjectSpec { return b.config.FlatMapstructure().HCL2Spec() } + func (b *Builder) Prepare(raws ...interface{}) ([]string, error) { configs := make([]interface{}, len(raws)+1) configs[0] = map[string]interface{}{ diff --git a/builder/amazon/instance/builder.hcl2spec.go b/builder/amazon/instance/builder.hcl2spec.go new file mode 100644 index 000000000..a78c4fa2d --- /dev/null +++ b/builder/amazon/instance/builder.hcl2spec.go @@ -0,0 +1,267 @@ +// Code generated by "mapstructure-to-hcl2 -type Config"; DO NOT EDIT. +package instance + +import ( + "github.com/hashicorp/hcl/v2/hcldec" + "github.com/hashicorp/packer/builder/amazon/common" + "github.com/zclconf/go-cty/cty" +) + +// FlatConfig is an auto-generated flat version of Config. +// Where the contents of a field with a `mapstructure:,squash` tag are bubbled up. +type FlatConfig struct { + PackerBuildName *string `mapstructure:"packer_build_name" cty:"packer_build_name"` + PackerBuilderType *string `mapstructure:"packer_builder_type" cty:"packer_builder_type"` + PackerDebug *bool `mapstructure:"packer_debug" cty:"packer_debug"` + PackerForce *bool `mapstructure:"packer_force" cty:"packer_force"` + PackerOnError *string `mapstructure:"packer_on_error" cty:"packer_on_error"` + PackerUserVars map[string]string `mapstructure:"packer_user_variables" cty:"packer_user_variables"` + PackerSensitiveVars []string `mapstructure:"packer_sensitive_variables" cty:"packer_sensitive_variables"` + AccessKey *string `mapstructure:"access_key" required:"true" cty:"access_key"` + CustomEndpointEc2 *string `mapstructure:"custom_endpoint_ec2" required:"false" cty:"custom_endpoint_ec2"` + DecodeAuthZMessages *bool `mapstructure:"decode_authorization_messages" required:"false" cty:"decode_authorization_messages"` + InsecureSkipTLSVerify *bool `mapstructure:"insecure_skip_tls_verify" required:"false" cty:"insecure_skip_tls_verify"` + MFACode *string `mapstructure:"mfa_code" required:"false" cty:"mfa_code"` + ProfileName *string `mapstructure:"profile" required:"false" cty:"profile"` + RawRegion *string `mapstructure:"region" required:"true" cty:"region"` + SecretKey *string `mapstructure:"secret_key" required:"true" cty:"secret_key"` + SkipValidation *bool `mapstructure:"skip_region_validation" required:"false" cty:"skip_region_validation"` + SkipMetadataApiCheck *bool `mapstructure:"skip_metadata_api_check" cty:"skip_metadata_api_check"` + Token *string `mapstructure:"token" required:"false" cty:"token"` + VaultAWSEngine *common.FlatVaultAWSEngineOptions `mapstructure:"vault_aws_engine" required:"false" cty:"vault_aws_engine"` + AMIName *string `mapstructure:"ami_name" required:"true" cty:"ami_name"` + AMIDescription *string `mapstructure:"ami_description" required:"false" cty:"ami_description"` + AMIVirtType *string `mapstructure:"ami_virtualization_type" required:"false" cty:"ami_virtualization_type"` + AMIUsers []string `mapstructure:"ami_users" required:"false" cty:"ami_users"` + AMIGroups []string `mapstructure:"ami_groups" required:"false" cty:"ami_groups"` + AMIProductCodes []string `mapstructure:"ami_product_codes" required:"false" cty:"ami_product_codes"` + AMIRegions []string `mapstructure:"ami_regions" required:"false" cty:"ami_regions"` + AMITags common.TagMap `mapstructure:"tags" required:"false" cty:"tags"` + AMIENASupport *bool `mapstructure:"ena_support" required:"false" cty:"ena_support"` + AMISriovNetSupport *bool `mapstructure:"sriov_support" required:"false" cty:"sriov_support"` + AMIForceDeregister *bool `mapstructure:"force_deregister" required:"false" cty:"force_deregister"` + AMIForceDeleteSnapshot *bool `mapstructure:"force_delete_snapshot" required:"false" cty:"force_delete_snapshot"` + AMIEncryptBootVolume *bool `mapstructure:"encrypt_boot" required:"false" cty:"encrypt_boot"` + AMIKmsKeyId *string `mapstructure:"kms_key_id" required:"false" cty:"kms_key_id"` + AMIRegionKMSKeyIDs map[string]string `mapstructure:"region_kms_key_ids" required:"false" cty:"region_kms_key_ids"` + AMISkipBuildRegion *bool `mapstructure:"skip_save_build_region" cty:"skip_save_build_region"` + SnapshotTags common.TagMap `mapstructure:"snapshot_tags" required:"false" cty:"snapshot_tags"` + SnapshotUsers []string `mapstructure:"snapshot_users" required:"false" cty:"snapshot_users"` + SnapshotGroups []string `mapstructure:"snapshot_groups" required:"false" cty:"snapshot_groups"` + AssociatePublicIpAddress *bool `mapstructure:"associate_public_ip_address" required:"false" cty:"associate_public_ip_address"` + AvailabilityZone *string `mapstructure:"availability_zone" required:"false" cty:"availability_zone"` + BlockDurationMinutes *int64 `mapstructure:"block_duration_minutes" required:"false" cty:"block_duration_minutes"` + DisableStopInstance *bool `mapstructure:"disable_stop_instance" required:"false" cty:"disable_stop_instance"` + EbsOptimized *bool `mapstructure:"ebs_optimized" required:"false" cty:"ebs_optimized"` + EnableT2Unlimited *bool `mapstructure:"enable_t2_unlimited" required:"false" cty:"enable_t2_unlimited"` + IamInstanceProfile *string `mapstructure:"iam_instance_profile" required:"false" cty:"iam_instance_profile"` + SkipProfileValidation *bool `mapstructure:"skip_profile_validation" required:"false" cty:"skip_profile_validation"` + TemporaryIamInstanceProfilePolicyDocument *common.FlatPolicyDocument `mapstructure:"temporary_iam_instance_profile_policy_document" required:"false" cty:"temporary_iam_instance_profile_policy_document"` + InstanceInitiatedShutdownBehavior *string `mapstructure:"shutdown_behavior" required:"false" cty:"shutdown_behavior"` + InstanceType *string `mapstructure:"instance_type" required:"true" cty:"instance_type"` + SecurityGroupFilter *common.FlatSecurityGroupFilterOptions `mapstructure:"security_group_filter" required:"false" cty:"security_group_filter"` + RunTags map[string]string `mapstructure:"run_tags" required:"false" cty:"run_tags"` + SecurityGroupId *string `mapstructure:"security_group_id" required:"false" cty:"security_group_id"` + SecurityGroupIds []string `mapstructure:"security_group_ids" required:"false" cty:"security_group_ids"` + SourceAmi *string `mapstructure:"source_ami" required:"true" cty:"source_ami"` + SourceAmiFilter *common.FlatAmiFilterOptions `mapstructure:"source_ami_filter" required:"false" cty:"source_ami_filter"` + SpotInstanceTypes []string `mapstructure:"spot_instance_types" required:"false" cty:"spot_instance_types"` + SpotPrice *string `mapstructure:"spot_price" required:"false" cty:"spot_price"` + SpotPriceAutoProduct *string `mapstructure:"spot_price_auto_product" required:"false" cty:"spot_price_auto_product"` + SpotTags map[string]string `mapstructure:"spot_tags" required:"false" cty:"spot_tags"` + SubnetFilter *common.FlatSubnetFilterOptions `mapstructure:"subnet_filter" required:"false" cty:"subnet_filter"` + SubnetId *string `mapstructure:"subnet_id" required:"false" cty:"subnet_id"` + TemporaryKeyPairName *string `mapstructure:"temporary_key_pair_name" required:"false" cty:"temporary_key_pair_name"` + TemporarySGSourceCidrs []string `mapstructure:"temporary_security_group_source_cidrs" required:"false" cty:"temporary_security_group_source_cidrs"` + UserData *string `mapstructure:"user_data" required:"false" cty:"user_data"` + UserDataFile *string `mapstructure:"user_data_file" required:"false" cty:"user_data_file"` + VpcFilter *common.FlatVpcFilterOptions `mapstructure:"vpc_filter" required:"false" cty:"vpc_filter"` + VpcId *string `mapstructure:"vpc_id" required:"false" cty:"vpc_id"` + WindowsPasswordTimeout *string `mapstructure:"windows_password_timeout" required:"false" cty:"windows_password_timeout"` + Type *string `mapstructure:"communicator" cty:"communicator"` + PauseBeforeConnect *string `mapstructure:"pause_before_connecting" cty:"pause_before_connecting"` + SSHHost *string `mapstructure:"ssh_host" cty:"ssh_host"` + SSHPort *int `mapstructure:"ssh_port" cty:"ssh_port"` + SSHUsername *string `mapstructure:"ssh_username" cty:"ssh_username"` + SSHPassword *string `mapstructure:"ssh_password" cty:"ssh_password"` + SSHKeyPairName *string `mapstructure:"ssh_keypair_name" cty:"ssh_keypair_name"` + SSHClearAuthorizedKeys *bool `mapstructure:"ssh_clear_authorized_keys" cty:"ssh_clear_authorized_keys"` + SSHPrivateKeyFile *string `mapstructure:"ssh_private_key_file" cty:"ssh_private_key_file"` + SSHPty *bool `mapstructure:"ssh_pty" cty:"ssh_pty"` + SSHTimeout *string `mapstructure:"ssh_timeout" cty:"ssh_timeout"` + SSHAgentAuth *bool `mapstructure:"ssh_agent_auth" cty:"ssh_agent_auth"` + SSHDisableAgentForwarding *bool `mapstructure:"ssh_disable_agent_forwarding" cty:"ssh_disable_agent_forwarding"` + SSHHandshakeAttempts *int `mapstructure:"ssh_handshake_attempts" cty:"ssh_handshake_attempts"` + SSHBastionHost *string `mapstructure:"ssh_bastion_host" cty:"ssh_bastion_host"` + SSHBastionPort *int `mapstructure:"ssh_bastion_port" cty:"ssh_bastion_port"` + SSHBastionAgentAuth *bool `mapstructure:"ssh_bastion_agent_auth" cty:"ssh_bastion_agent_auth"` + SSHBastionUsername *string `mapstructure:"ssh_bastion_username" cty:"ssh_bastion_username"` + SSHBastionPassword *string `mapstructure:"ssh_bastion_password" cty:"ssh_bastion_password"` + SSHBastionPrivateKeyFile *string `mapstructure:"ssh_bastion_private_key_file" cty:"ssh_bastion_private_key_file"` + SSHFileTransferMethod *string `mapstructure:"ssh_file_transfer_method" cty:"ssh_file_transfer_method"` + SSHProxyHost *string `mapstructure:"ssh_proxy_host" cty:"ssh_proxy_host"` + SSHProxyPort *int `mapstructure:"ssh_proxy_port" cty:"ssh_proxy_port"` + SSHProxyUsername *string `mapstructure:"ssh_proxy_username" cty:"ssh_proxy_username"` + SSHProxyPassword *string `mapstructure:"ssh_proxy_password" cty:"ssh_proxy_password"` + SSHKeepAliveInterval *string `mapstructure:"ssh_keep_alive_interval" cty:"ssh_keep_alive_interval"` + SSHReadWriteTimeout *string `mapstructure:"ssh_read_write_timeout" cty:"ssh_read_write_timeout"` + SSHRemoteTunnels []string `mapstructure:"ssh_remote_tunnels" cty:"ssh_remote_tunnels"` + SSHLocalTunnels []string `mapstructure:"ssh_local_tunnels" cty:"ssh_local_tunnels"` + SSHPublicKey []byte `mapstructure:"ssh_public_key" cty:"ssh_public_key"` + SSHPrivateKey []byte `mapstructure:"ssh_private_key" cty:"ssh_private_key"` + WinRMUser *string `mapstructure:"winrm_username" cty:"winrm_username"` + WinRMPassword *string `mapstructure:"winrm_password" cty:"winrm_password"` + WinRMHost *string `mapstructure:"winrm_host" cty:"winrm_host"` + WinRMPort *int `mapstructure:"winrm_port" cty:"winrm_port"` + WinRMTimeout *string `mapstructure:"winrm_timeout" cty:"winrm_timeout"` + WinRMUseSSL *bool `mapstructure:"winrm_use_ssl" cty:"winrm_use_ssl"` + WinRMInsecure *bool `mapstructure:"winrm_insecure" cty:"winrm_insecure"` + WinRMUseNTLM *bool `mapstructure:"winrm_use_ntlm" cty:"winrm_use_ntlm"` + SSHInterface *string `mapstructure:"ssh_interface" cty:"ssh_interface"` + AMIMappings []common.FlatBlockDevice `mapstructure:"ami_block_device_mappings" required:"false" cty:"ami_block_device_mappings"` + LaunchMappings []common.FlatBlockDevice `mapstructure:"launch_block_device_mappings" required:"false" cty:"launch_block_device_mappings"` + AccountId *string `mapstructure:"account_id" required:"true" cty:"account_id"` + BundleDestination *string `mapstructure:"bundle_destination" required:"false" cty:"bundle_destination"` + BundlePrefix *string `mapstructure:"bundle_prefix" required:"false" cty:"bundle_prefix"` + BundleUploadCommand *string `mapstructure:"bundle_upload_command" required:"false" cty:"bundle_upload_command"` + BundleVolCommand *string `mapstructure:"bundle_vol_command" required:"false" cty:"bundle_vol_command"` + S3Bucket *string `mapstructure:"s3_bucket" required:"true" cty:"s3_bucket"` + X509CertPath *string `mapstructure:"x509_cert_path" required:"true" cty:"x509_cert_path"` + X509KeyPath *string `mapstructure:"x509_key_path" required:"true" cty:"x509_key_path"` + X509UploadPath *string `mapstructure:"x509_upload_path" required:"false" cty:"x509_upload_path"` +} + +// FlatMapstructure returns a new FlatConfig. +// FlatConfig is an auto-generated flat version of Config. +// Where the contents a fields with a `mapstructure:,squash` tag are bubbled up. +func (*Config) FlatMapstructure() interface{ HCL2Spec() map[string]hcldec.Spec } { + return new(FlatConfig) +} + +// HCL2Spec returns the hcl spec of a Config. +// This spec is used by HCL to read the fields of Config. +// The decoded values from this spec will then be applied to a FlatConfig. +func (*FlatConfig) HCL2Spec() map[string]hcldec.Spec { + s := map[string]hcldec.Spec{ + "packer_build_name": &hcldec.AttrSpec{Name: "packer_build_name", Type: cty.String, Required: false}, + "packer_builder_type": &hcldec.AttrSpec{Name: "packer_builder_type", Type: cty.String, Required: false}, + "packer_debug": &hcldec.AttrSpec{Name: "packer_debug", Type: cty.Bool, Required: false}, + "packer_force": &hcldec.AttrSpec{Name: "packer_force", Type: cty.Bool, Required: false}, + "packer_on_error": &hcldec.AttrSpec{Name: "packer_on_error", Type: cty.String, Required: false}, + "packer_user_variables": &hcldec.BlockAttrsSpec{TypeName: "packer_user_variables", ElementType: cty.String, Required: false}, + "packer_sensitive_variables": &hcldec.AttrSpec{Name: "packer_sensitive_variables", Type: cty.List(cty.String), Required: false}, + "access_key": &hcldec.AttrSpec{Name: "access_key", Type: cty.String, Required: false}, + "custom_endpoint_ec2": &hcldec.AttrSpec{Name: "custom_endpoint_ec2", Type: cty.String, Required: false}, + "decode_authorization_messages": &hcldec.AttrSpec{Name: "decode_authorization_messages", Type: cty.Bool, Required: false}, + "insecure_skip_tls_verify": &hcldec.AttrSpec{Name: "insecure_skip_tls_verify", Type: cty.Bool, Required: false}, + "mfa_code": &hcldec.AttrSpec{Name: "mfa_code", Type: cty.String, Required: false}, + "profile": &hcldec.AttrSpec{Name: "profile", Type: cty.String, Required: false}, + "region": &hcldec.AttrSpec{Name: "region", Type: cty.String, Required: false}, + "secret_key": &hcldec.AttrSpec{Name: "secret_key", Type: cty.String, Required: false}, + "skip_region_validation": &hcldec.AttrSpec{Name: "skip_region_validation", Type: cty.Bool, Required: false}, + "skip_metadata_api_check": &hcldec.AttrSpec{Name: "skip_metadata_api_check", Type: cty.Bool, Required: false}, + "token": &hcldec.AttrSpec{Name: "token", Type: cty.String, Required: false}, + "vault_aws_engine": &hcldec.BlockSpec{TypeName: "vault_aws_engine", Nested: hcldec.ObjectSpec((*common.FlatVaultAWSEngineOptions)(nil).HCL2Spec())}, + "ami_name": &hcldec.AttrSpec{Name: "ami_name", Type: cty.String, Required: false}, + "ami_description": &hcldec.AttrSpec{Name: "ami_description", Type: cty.String, Required: false}, + "ami_virtualization_type": &hcldec.AttrSpec{Name: "ami_virtualization_type", Type: cty.String, Required: false}, + "ami_users": &hcldec.AttrSpec{Name: "ami_users", Type: cty.List(cty.String), Required: false}, + "ami_groups": &hcldec.AttrSpec{Name: "ami_groups", Type: cty.List(cty.String), Required: false}, + "ami_product_codes": &hcldec.AttrSpec{Name: "ami_product_codes", Type: cty.List(cty.String), Required: false}, + "ami_regions": &hcldec.AttrSpec{Name: "ami_regions", Type: cty.List(cty.String), Required: false}, + "tags": &hcldec.BlockAttrsSpec{TypeName: "common.TagMap", ElementType: cty.String, Required: false}, + "ena_support": &hcldec.AttrSpec{Name: "ena_support", Type: cty.Bool, Required: false}, + "sriov_support": &hcldec.AttrSpec{Name: "sriov_support", Type: cty.Bool, Required: false}, + "force_deregister": &hcldec.AttrSpec{Name: "force_deregister", Type: cty.Bool, Required: false}, + "force_delete_snapshot": &hcldec.AttrSpec{Name: "force_delete_snapshot", Type: cty.Bool, Required: false}, + "encrypt_boot": &hcldec.AttrSpec{Name: "encrypt_boot", Type: cty.Bool, Required: false}, + "kms_key_id": &hcldec.AttrSpec{Name: "kms_key_id", Type: cty.String, Required: false}, + "region_kms_key_ids": &hcldec.BlockAttrsSpec{TypeName: "region_kms_key_ids", ElementType: cty.String, Required: false}, + "skip_save_build_region": &hcldec.AttrSpec{Name: "skip_save_build_region", Type: cty.Bool, Required: false}, + "snapshot_tags": &hcldec.BlockAttrsSpec{TypeName: "common.TagMap", ElementType: cty.String, Required: false}, + "snapshot_users": &hcldec.AttrSpec{Name: "snapshot_users", Type: cty.List(cty.String), Required: false}, + "snapshot_groups": &hcldec.AttrSpec{Name: "snapshot_groups", Type: cty.List(cty.String), Required: false}, + "associate_public_ip_address": &hcldec.AttrSpec{Name: "associate_public_ip_address", Type: cty.Bool, Required: false}, + "availability_zone": &hcldec.AttrSpec{Name: "availability_zone", Type: cty.String, Required: false}, + "block_duration_minutes": &hcldec.AttrSpec{Name: "block_duration_minutes", Type: cty.Number, Required: false}, + "disable_stop_instance": &hcldec.AttrSpec{Name: "disable_stop_instance", Type: cty.Bool, Required: false}, + "ebs_optimized": &hcldec.AttrSpec{Name: "ebs_optimized", Type: cty.Bool, Required: false}, + "enable_t2_unlimited": &hcldec.AttrSpec{Name: "enable_t2_unlimited", Type: cty.Bool, Required: false}, + "iam_instance_profile": &hcldec.AttrSpec{Name: "iam_instance_profile", Type: cty.String, Required: false}, + "skip_profile_validation": &hcldec.AttrSpec{Name: "skip_profile_validation", Type: cty.Bool, Required: false}, + "temporary_iam_instance_profile_policy_document": &hcldec.BlockSpec{TypeName: "temporary_iam_instance_profile_policy_document", Nested: hcldec.ObjectSpec((*common.FlatPolicyDocument)(nil).HCL2Spec())}, + "shutdown_behavior": &hcldec.AttrSpec{Name: "shutdown_behavior", Type: cty.String, Required: false}, + "instance_type": &hcldec.AttrSpec{Name: "instance_type", Type: cty.String, Required: false}, + "security_group_filter": &hcldec.BlockSpec{TypeName: "security_group_filter", Nested: hcldec.ObjectSpec((*common.FlatSecurityGroupFilterOptions)(nil).HCL2Spec())}, + "run_tags": &hcldec.BlockAttrsSpec{TypeName: "run_tags", ElementType: cty.String, Required: false}, + "security_group_id": &hcldec.AttrSpec{Name: "security_group_id", Type: cty.String, Required: false}, + "security_group_ids": &hcldec.AttrSpec{Name: "security_group_ids", Type: cty.List(cty.String), Required: false}, + "source_ami": &hcldec.AttrSpec{Name: "source_ami", Type: cty.String, Required: false}, + "source_ami_filter": &hcldec.BlockSpec{TypeName: "source_ami_filter", Nested: hcldec.ObjectSpec((*common.FlatAmiFilterOptions)(nil).HCL2Spec())}, + "spot_instance_types": &hcldec.AttrSpec{Name: "spot_instance_types", Type: cty.List(cty.String), Required: false}, + "spot_price": &hcldec.AttrSpec{Name: "spot_price", Type: cty.String, Required: false}, + "spot_price_auto_product": &hcldec.AttrSpec{Name: "spot_price_auto_product", Type: cty.String, Required: false}, + "spot_tags": &hcldec.BlockAttrsSpec{TypeName: "spot_tags", ElementType: cty.String, Required: false}, + "subnet_filter": &hcldec.BlockSpec{TypeName: "subnet_filter", Nested: hcldec.ObjectSpec((*common.FlatSubnetFilterOptions)(nil).HCL2Spec())}, + "subnet_id": &hcldec.AttrSpec{Name: "subnet_id", Type: cty.String, Required: false}, + "temporary_key_pair_name": &hcldec.AttrSpec{Name: "temporary_key_pair_name", Type: cty.String, Required: false}, + "temporary_security_group_source_cidrs": &hcldec.AttrSpec{Name: "temporary_security_group_source_cidrs", Type: cty.List(cty.String), Required: false}, + "user_data": &hcldec.AttrSpec{Name: "user_data", Type: cty.String, Required: false}, + "user_data_file": &hcldec.AttrSpec{Name: "user_data_file", Type: cty.String, Required: false}, + "vpc_filter": &hcldec.BlockSpec{TypeName: "vpc_filter", Nested: hcldec.ObjectSpec((*common.FlatVpcFilterOptions)(nil).HCL2Spec())}, + "vpc_id": &hcldec.AttrSpec{Name: "vpc_id", Type: cty.String, Required: false}, + "windows_password_timeout": &hcldec.AttrSpec{Name: "windows_password_timeout", Type: cty.String, Required: false}, + "communicator": &hcldec.AttrSpec{Name: "communicator", Type: cty.String, Required: false}, + "pause_before_connecting": &hcldec.AttrSpec{Name: "pause_before_connecting", Type: cty.String, Required: false}, + "ssh_host": &hcldec.AttrSpec{Name: "ssh_host", Type: cty.String, Required: false}, + "ssh_port": &hcldec.AttrSpec{Name: "ssh_port", Type: cty.Number, Required: false}, + "ssh_username": &hcldec.AttrSpec{Name: "ssh_username", Type: cty.String, Required: false}, + "ssh_password": &hcldec.AttrSpec{Name: "ssh_password", Type: cty.String, Required: false}, + "ssh_keypair_name": &hcldec.AttrSpec{Name: "ssh_keypair_name", Type: cty.String, Required: false}, + "ssh_clear_authorized_keys": &hcldec.AttrSpec{Name: "ssh_clear_authorized_keys", Type: cty.Bool, Required: false}, + "ssh_private_key_file": &hcldec.AttrSpec{Name: "ssh_private_key_file", Type: cty.String, Required: false}, + "ssh_pty": &hcldec.AttrSpec{Name: "ssh_pty", Type: cty.Bool, Required: false}, + "ssh_timeout": &hcldec.AttrSpec{Name: "ssh_timeout", Type: cty.String, Required: false}, + "ssh_agent_auth": &hcldec.AttrSpec{Name: "ssh_agent_auth", Type: cty.Bool, Required: false}, + "ssh_disable_agent_forwarding": &hcldec.AttrSpec{Name: "ssh_disable_agent_forwarding", Type: cty.Bool, Required: false}, + "ssh_handshake_attempts": &hcldec.AttrSpec{Name: "ssh_handshake_attempts", Type: cty.Number, Required: false}, + "ssh_bastion_host": &hcldec.AttrSpec{Name: "ssh_bastion_host", Type: cty.String, Required: false}, + "ssh_bastion_port": &hcldec.AttrSpec{Name: "ssh_bastion_port", Type: cty.Number, Required: false}, + "ssh_bastion_agent_auth": &hcldec.AttrSpec{Name: "ssh_bastion_agent_auth", Type: cty.Bool, Required: false}, + "ssh_bastion_username": &hcldec.AttrSpec{Name: "ssh_bastion_username", Type: cty.String, Required: false}, + "ssh_bastion_password": &hcldec.AttrSpec{Name: "ssh_bastion_password", Type: cty.String, Required: false}, + "ssh_bastion_private_key_file": &hcldec.AttrSpec{Name: "ssh_bastion_private_key_file", Type: cty.String, Required: false}, + "ssh_file_transfer_method": &hcldec.AttrSpec{Name: "ssh_file_transfer_method", Type: cty.String, Required: false}, + "ssh_proxy_host": &hcldec.AttrSpec{Name: "ssh_proxy_host", Type: cty.String, Required: false}, + "ssh_proxy_port": &hcldec.AttrSpec{Name: "ssh_proxy_port", Type: cty.Number, Required: false}, + "ssh_proxy_username": &hcldec.AttrSpec{Name: "ssh_proxy_username", Type: cty.String, Required: false}, + "ssh_proxy_password": &hcldec.AttrSpec{Name: "ssh_proxy_password", Type: cty.String, Required: false}, + "ssh_keep_alive_interval": &hcldec.AttrSpec{Name: "ssh_keep_alive_interval", Type: cty.String, Required: false}, + "ssh_read_write_timeout": &hcldec.AttrSpec{Name: "ssh_read_write_timeout", Type: cty.String, Required: false}, + "ssh_remote_tunnels": &hcldec.AttrSpec{Name: "ssh_remote_tunnels", Type: cty.List(cty.String), Required: false}, + "ssh_local_tunnels": &hcldec.AttrSpec{Name: "ssh_local_tunnels", Type: cty.List(cty.String), Required: false}, + "ssh_public_key": &hcldec.AttrSpec{Name: "ssh_public_key", Type: cty.List(cty.Number), Required: false}, + "ssh_private_key": &hcldec.AttrSpec{Name: "ssh_private_key", Type: cty.List(cty.Number), Required: false}, + "winrm_username": &hcldec.AttrSpec{Name: "winrm_username", Type: cty.String, Required: false}, + "winrm_password": &hcldec.AttrSpec{Name: "winrm_password", Type: cty.String, Required: false}, + "winrm_host": &hcldec.AttrSpec{Name: "winrm_host", Type: cty.String, Required: false}, + "winrm_port": &hcldec.AttrSpec{Name: "winrm_port", Type: cty.Number, Required: false}, + "winrm_timeout": &hcldec.AttrSpec{Name: "winrm_timeout", Type: cty.String, Required: false}, + "winrm_use_ssl": &hcldec.AttrSpec{Name: "winrm_use_ssl", Type: cty.Bool, Required: false}, + "winrm_insecure": &hcldec.AttrSpec{Name: "winrm_insecure", Type: cty.Bool, Required: false}, + "winrm_use_ntlm": &hcldec.AttrSpec{Name: "winrm_use_ntlm", Type: cty.Bool, Required: false}, + "ssh_interface": &hcldec.AttrSpec{Name: "ssh_interface", Type: cty.String, Required: false}, + "ami_block_device_mappings": &hcldec.BlockListSpec{TypeName: "ami_block_device_mappings", Nested: hcldec.ObjectSpec((*common.FlatBlockDevice)(nil).HCL2Spec())}, + "launch_block_device_mappings": &hcldec.BlockListSpec{TypeName: "launch_block_device_mappings", Nested: hcldec.ObjectSpec((*common.FlatBlockDevice)(nil).HCL2Spec())}, + "account_id": &hcldec.AttrSpec{Name: "account_id", Type: cty.String, Required: false}, + "bundle_destination": &hcldec.AttrSpec{Name: "bundle_destination", Type: cty.String, Required: false}, + "bundle_prefix": &hcldec.AttrSpec{Name: "bundle_prefix", Type: cty.String, Required: false}, + "bundle_upload_command": &hcldec.AttrSpec{Name: "bundle_upload_command", Type: cty.String, Required: false}, + "bundle_vol_command": &hcldec.AttrSpec{Name: "bundle_vol_command", Type: cty.String, Required: false}, + "s3_bucket": &hcldec.AttrSpec{Name: "s3_bucket", Type: cty.String, Required: false}, + "x509_cert_path": &hcldec.AttrSpec{Name: "x509_cert_path", Type: cty.String, Required: false}, + "x509_key_path": &hcldec.AttrSpec{Name: "x509_key_path", Type: cty.String, Required: false}, + "x509_upload_path": &hcldec.AttrSpec{Name: "x509_upload_path", Type: cty.String, Required: false}, + } + return s +} diff --git a/builder/azure/arm/builder.go b/builder/azure/arm/builder.go index 2a06f1b84..39917f0a2 100644 --- a/builder/azure/arm/builder.go +++ b/builder/azure/arm/builder.go @@ -14,6 +14,7 @@ import ( "github.com/Azure/azure-sdk-for-go/storage" "github.com/Azure/go-autorest/autorest/adal" "github.com/dgrijalva/jwt-go" + "github.com/hashicorp/hcl/v2/hcldec" packerAzureCommon "github.com/hashicorp/packer/builder/azure/common" "github.com/hashicorp/packer/builder/azure/common/constants" "github.com/hashicorp/packer/builder/azure/common/lin" @@ -24,7 +25,7 @@ import ( ) type Builder struct { - config *Config + config Config stateBag multistep.StateBag runner multistep.Runner } @@ -34,14 +35,14 @@ const ( DefaultSecretName = "packerKeyVaultSecret" ) +func (b *Builder) ConfigSpec() hcldec.ObjectSpec { return b.config.FlatMapstructure().HCL2Spec() } + func (b *Builder) Prepare(raws ...interface{}) ([]string, error) { - c, warnings, errs := newConfig(raws...) + warnings, errs := b.config.Prepare(raws...) if errs != nil { return warnings, errs } - b.config = c - b.stateBag = new(multistep.BasicStateBag) b.configureStateBag(b.stateBag) b.setTemplateParameters(b.stateBag) @@ -64,7 +65,7 @@ func (b *Builder) Run(ctx context.Context, ui packer.Ui, hook packer.Hook) (pack } log.Print(":: Configuration") - packerAzureCommon.DumpConfig(b.config, func(s string) { log.Print(s) }) + packerAzureCommon.DumpConfig(&b.config, func(s string) { log.Print(s) }) b.stateBag.Put("hook", hook) b.stateBag.Put(constants.Ui, ui) @@ -90,7 +91,7 @@ func (b *Builder) Run(ctx context.Context, ui packer.Ui, hook packer.Hook) (pack } resolver := newResourceResolver(azureClient) - if err := resolver.Resolve(b.config); err != nil { + if err := resolver.Resolve(&b.config); err != nil { return nil, err } if b.config.ClientConfig.ObjectID == "" { @@ -197,8 +198,8 @@ func (b *Builder) Run(ctx context.Context, ui packer.Ui, hook packer.Hook) (pack if b.config.OSType == constants.Target_Linux { steps = []multistep.Step{ NewStepCreateResourceGroup(azureClient, ui), - NewStepValidateTemplate(azureClient, ui, b.config, GetVirtualMachineDeployment), - NewStepDeployTemplate(azureClient, ui, b.config, deploymentName, GetVirtualMachineDeployment), + NewStepValidateTemplate(azureClient, ui, &b.config, GetVirtualMachineDeployment), + NewStepDeployTemplate(azureClient, ui, &b.config, deploymentName, GetVirtualMachineDeployment), NewStepGetIPAddress(azureClient, ui, endpointConnectType), &communicator.StepConnectSSH{ Config: &b.config.Comm, @@ -212,10 +213,10 @@ func (b *Builder) Run(ctx context.Context, ui packer.Ui, hook packer.Hook) (pack NewStepGetOSDisk(azureClient, ui), NewStepGetAdditionalDisks(azureClient, ui), NewStepPowerOffCompute(azureClient, ui), - NewStepSnapshotOSDisk(azureClient, ui, b.config), - NewStepSnapshotDataDisks(azureClient, ui, b.config), + NewStepSnapshotOSDisk(azureClient, ui, &b.config), + NewStepSnapshotDataDisks(azureClient, ui, &b.config), NewStepCaptureImage(azureClient, ui), - NewStepPublishToSharedImageGallery(azureClient, ui, b.config), + NewStepPublishToSharedImageGallery(azureClient, ui, &b.config), NewStepDeleteResourceGroup(azureClient, ui), NewStepDeleteOSDisk(azureClient, ui), NewStepDeleteAdditionalDisks(azureClient, ui), @@ -224,12 +225,12 @@ func (b *Builder) Run(ctx context.Context, ui packer.Ui, hook packer.Hook) (pack keyVaultDeploymentName := b.stateBag.Get(constants.ArmKeyVaultDeploymentName).(string) steps = []multistep.Step{ NewStepCreateResourceGroup(azureClient, ui), - NewStepValidateTemplate(azureClient, ui, b.config, GetKeyVaultDeployment), - NewStepDeployTemplate(azureClient, ui, b.config, keyVaultDeploymentName, GetKeyVaultDeployment), + NewStepValidateTemplate(azureClient, ui, &b.config, GetKeyVaultDeployment), + NewStepDeployTemplate(azureClient, ui, &b.config, keyVaultDeploymentName, GetKeyVaultDeployment), NewStepGetCertificate(azureClient, ui), - NewStepSetCertificate(b.config, ui), - NewStepValidateTemplate(azureClient, ui, b.config, GetVirtualMachineDeployment), - NewStepDeployTemplate(azureClient, ui, b.config, deploymentName, GetVirtualMachineDeployment), + NewStepSetCertificate(&b.config, ui), + NewStepValidateTemplate(azureClient, ui, &b.config, GetVirtualMachineDeployment), + NewStepDeployTemplate(azureClient, ui, &b.config, deploymentName, GetVirtualMachineDeployment), NewStepGetIPAddress(azureClient, ui, endpointConnectType), &StepSaveWinRMPassword{ Password: b.config.tmpAdminPassword, @@ -251,10 +252,10 @@ func (b *Builder) Run(ctx context.Context, ui packer.Ui, hook packer.Hook) (pack NewStepGetOSDisk(azureClient, ui), NewStepGetAdditionalDisks(azureClient, ui), NewStepPowerOffCompute(azureClient, ui), - NewStepSnapshotOSDisk(azureClient, ui, b.config), - NewStepSnapshotDataDisks(azureClient, ui, b.config), + NewStepSnapshotOSDisk(azureClient, ui, &b.config), + NewStepSnapshotDataDisks(azureClient, ui, &b.config), NewStepCaptureImage(azureClient, ui), - NewStepPublishToSharedImageGallery(azureClient, ui, b.config), + NewStepPublishToSharedImageGallery(azureClient, ui, &b.config), NewStepDeleteResourceGroup(azureClient, ui), NewStepDeleteOSDisk(azureClient, ui), NewStepDeleteAdditionalDisks(azureClient, ui), diff --git a/builder/azure/arm/builder_test.go b/builder/azure/arm/builder_test.go index a71f92874..d22a49314 100644 --- a/builder/azure/arm/builder_test.go +++ b/builder/azure/arm/builder_test.go @@ -7,7 +7,7 @@ import ( ) func TestStateBagShouldBePopulatedExpectedValues(t *testing.T) { - var testSubject = &Builder{} + var testSubject Builder _, err := testSubject.Prepare(getArmBuilderConfiguration(), getPackerConfiguration()) if err != nil { t.Fatalf("failed to prepare: %s", err) diff --git a/builder/azure/arm/config.go b/builder/azure/arm/config.go index 86ffab134..271ff967c 100644 --- a/builder/azure/arm/config.go +++ b/builder/azure/arm/config.go @@ -493,58 +493,57 @@ func (c *Config) createCertificate() (string, error) { return base64.StdEncoding.EncodeToString(bytes), nil } -func newConfig(raws ...interface{}) (*Config, []string, error) { - var c Config +func (c *Config) Prepare(raws ...interface{}) ([]string, error) { c.ctx.Funcs = azcommon.TemplateFuncs - err := config.Decode(&c, &config.DecodeOpts{ + err := config.Decode(c, &config.DecodeOpts{ Interpolate: true, InterpolateContext: &c.ctx, }, raws...) if err != nil { - return nil, nil, err + return nil, err } - provideDefaultValues(&c) - setRuntimeValues(&c) - setUserNamePassword(&c) + provideDefaultValues(c) + setRuntimeValues(c) + setUserNamePassword(c) err = c.ClientConfig.SetDefaultValues() if err != nil { - return nil, nil, err + return nil, err } - err = setCustomData(&c) + err = setCustomData(c) if err != nil { - return nil, nil, err + return nil, err } // NOTE: if the user did not specify a communicator, then default to both // SSH and WinRM. This is for backwards compatibility because the code did // not specifically force the user to set a communicator. if c.Comm.Type == "" || strings.EqualFold(c.Comm.Type, "ssh") { - err = setSshValues(&c) + err = setSshValues(c) if err != nil { - return nil, nil, err + return nil, err } } if c.Comm.Type == "" || strings.EqualFold(c.Comm.Type, "winrm") { - err = setWinRMCertificate(&c) + err = setWinRMCertificate(c) if err != nil { - return nil, nil, err + return nil, err } } var errs *packer.MultiError errs = packer.MultiErrorAppend(errs, c.Comm.Prepare(&c.ctx)...) - assertRequiredParametersSet(&c, errs) - assertTagProperties(&c, errs) + assertRequiredParametersSet(c, errs) + assertTagProperties(c, errs) if errs != nil && len(errs.Errors) > 0 { - return nil, nil, errs + return nil, errs } - return &c, nil, nil + return nil, nil } func setSshValues(c *Config) error { diff --git a/builder/azure/arm/config.hcl2spec.go b/builder/azure/arm/config.hcl2spec.go index 264120891..317528478 100644 --- a/builder/azure/arm/config.hcl2spec.go +++ b/builder/azure/arm/config.hcl2spec.go @@ -94,8 +94,8 @@ type FlatConfig struct { SSHReadWriteTimeout *string `mapstructure:"ssh_read_write_timeout" cty:"ssh_read_write_timeout"` SSHRemoteTunnels []string `mapstructure:"ssh_remote_tunnels" cty:"ssh_remote_tunnels"` SSHLocalTunnels []string `mapstructure:"ssh_local_tunnels" cty:"ssh_local_tunnels"` - SSHPublicKey []byte `cty:"ssh_public_key"` - SSHPrivateKey []byte `cty:"ssh_private_key"` + SSHPublicKey []byte `mapstructure:"ssh_public_key" cty:"ssh_public_key"` + SSHPrivateKey []byte `mapstructure:"ssh_private_key" cty:"ssh_private_key"` WinRMUser *string `mapstructure:"winrm_username" cty:"winrm_username"` WinRMPassword *string `mapstructure:"winrm_password" cty:"winrm_password"` WinRMHost *string `mapstructure:"winrm_host" cty:"winrm_host"` @@ -110,10 +110,13 @@ type FlatConfig struct { // FlatMapstructure returns a new FlatConfig. // FlatConfig is an auto-generated flat version of Config. // Where the contents a fields with a `mapstructure:,squash` tag are bubbled up. -func (*Config) FlatMapstructure() interface{} { return new(FlatConfig) } +func (*Config) FlatMapstructure() interface{ HCL2Spec() map[string]hcldec.Spec } { + return new(FlatConfig) +} -// HCL2Spec returns the hcldec.Spec of a FlatConfig. -// This spec is used by HCL to read the fields of FlatConfig. +// HCL2Spec returns the hcl spec of a Config. +// This spec is used by HCL to read the fields of Config. +// The decoded values from this spec will then be applied to a FlatConfig. func (*FlatConfig) HCL2Spec() map[string]hcldec.Spec { s := map[string]hcldec.Spec{ "packer_build_name": &hcldec.AttrSpec{Name: "packer_build_name", Type: cty.String, Required: false}, @@ -228,10 +231,13 @@ type FlatPlanInformation struct { // FlatMapstructure returns a new FlatPlanInformation. // FlatPlanInformation is an auto-generated flat version of PlanInformation. // Where the contents a fields with a `mapstructure:,squash` tag are bubbled up. -func (*PlanInformation) FlatMapstructure() interface{} { return new(FlatPlanInformation) } +func (*PlanInformation) FlatMapstructure() interface{ HCL2Spec() map[string]hcldec.Spec } { + return new(FlatPlanInformation) +} -// HCL2Spec returns the hcldec.Spec of a FlatPlanInformation. -// This spec is used by HCL to read the fields of FlatPlanInformation. +// HCL2Spec returns the hcl spec of a PlanInformation. +// This spec is used by HCL to read the fields of PlanInformation. +// The decoded values from this spec will then be applied to a FlatPlanInformation. func (*FlatPlanInformation) HCL2Spec() map[string]hcldec.Spec { s := map[string]hcldec.Spec{ "plan_name": &hcldec.AttrSpec{Name: "plan_name", Type: cty.String, Required: false}, @@ -255,10 +261,13 @@ type FlatSharedImageGallery struct { // FlatMapstructure returns a new FlatSharedImageGallery. // FlatSharedImageGallery is an auto-generated flat version of SharedImageGallery. // Where the contents a fields with a `mapstructure:,squash` tag are bubbled up. -func (*SharedImageGallery) FlatMapstructure() interface{} { return new(FlatSharedImageGallery) } +func (*SharedImageGallery) FlatMapstructure() interface{ HCL2Spec() map[string]hcldec.Spec } { + return new(FlatSharedImageGallery) +} -// HCL2Spec returns the hcldec.Spec of a FlatSharedImageGallery. -// This spec is used by HCL to read the fields of FlatSharedImageGallery. +// HCL2Spec returns the hcl spec of a SharedImageGallery. +// This spec is used by HCL to read the fields of SharedImageGallery. +// The decoded values from this spec will then be applied to a FlatSharedImageGallery. func (*FlatSharedImageGallery) HCL2Spec() map[string]hcldec.Spec { s := map[string]hcldec.Spec{ "subscription": &hcldec.AttrSpec{Name: "subscription", Type: cty.String, Required: false}, @@ -283,12 +292,13 @@ type FlatSharedImageGalleryDestination struct { // FlatMapstructure returns a new FlatSharedImageGalleryDestination. // FlatSharedImageGalleryDestination is an auto-generated flat version of SharedImageGalleryDestination. // Where the contents a fields with a `mapstructure:,squash` tag are bubbled up. -func (*SharedImageGalleryDestination) FlatMapstructure() interface{} { +func (*SharedImageGalleryDestination) FlatMapstructure() interface{ HCL2Spec() map[string]hcldec.Spec } { return new(FlatSharedImageGalleryDestination) } -// HCL2Spec returns the hcldec.Spec of a FlatSharedImageGalleryDestination. -// This spec is used by HCL to read the fields of FlatSharedImageGalleryDestination. +// HCL2Spec returns the hcl spec of a SharedImageGalleryDestination. +// This spec is used by HCL to read the fields of SharedImageGalleryDestination. +// The decoded values from this spec will then be applied to a FlatSharedImageGalleryDestination. func (*FlatSharedImageGalleryDestination) HCL2Spec() map[string]hcldec.Spec { s := map[string]hcldec.Spec{ "resource_group": &hcldec.AttrSpec{Name: "resource_group", Type: cty.String, Required: false}, diff --git a/builder/azure/arm/config_test.go b/builder/azure/arm/config_test.go index 52ad4814e..128df2be3 100644 --- a/builder/azure/arm/config_test.go +++ b/builder/azure/arm/config_test.go @@ -26,7 +26,8 @@ var requiredConfigValues = []string{ } func TestConfigShouldProvideReasonableDefaultValues(t *testing.T) { - c, _, err := newConfig(getArmBuilderConfiguration(), getPackerConfiguration()) + var c Config + _, err := c.Prepare(getArmBuilderConfiguration(), getPackerConfiguration()) if err != nil { t.Error("Expected configuration creation to succeed, but it failed!\n") @@ -63,7 +64,8 @@ func TestConfigShouldBeAbleToOverrideDefaultedValues(t *testing.T) { builderValues["managed_image_storage_account_type"] = "Premium_LRS" builderValues["disk_caching_type"] = "None" - c, _, err := newConfig(builderValues, getPackerConfiguration()) + var c Config + _, err := c.Prepare(builderValues, getPackerConfiguration()) if err != nil { t.Fatalf("newConfig failed: %s", err) @@ -99,7 +101,8 @@ func TestConfigShouldBeAbleToOverrideDefaultedValues(t *testing.T) { } func TestConfigShouldDefaultVMSizeToStandardA1(t *testing.T) { - c, _, _ := newConfig(getArmBuilderConfiguration(), getPackerConfiguration()) + var c Config + c.Prepare(getArmBuilderConfiguration(), getPackerConfiguration()) if c.VMSize != "Standard_A1" { t.Errorf("Expected 'VMSize' to default to 'Standard_A1', but got '%s'.", c.VMSize) @@ -107,7 +110,8 @@ func TestConfigShouldDefaultVMSizeToStandardA1(t *testing.T) { } func TestConfigShouldDefaultImageVersionToLatest(t *testing.T) { - c, _, _ := newConfig(getArmBuilderConfiguration(), getPackerConfiguration()) + var c Config + c.Prepare(getArmBuilderConfiguration(), getPackerConfiguration()) if c.ImageVersion != "latest" { t.Errorf("Expected 'ImageVersion' to default to 'latest', but got '%s'.", c.ImageVersion) @@ -127,7 +131,8 @@ func TestConfigShouldNotDefaultImageVersionIfCustomImage(t *testing.T) { "communicator": "none", } - c, _, _ := newConfig(config, getPackerConfiguration()) + var c Config + c.Prepare(config, getPackerConfiguration()) if c.ImageVersion != "" { t.Errorf("Expected 'ImageVersion' to empty, but got '%s'.", c.ImageVersion) } @@ -153,7 +158,8 @@ func TestConfigShouldNormalizeOSTypeCase(t *testing.T) { for k, v := range os_types { for _, os_type := range v { config["os_type"] = os_type - c, _, err := newConfig(config, getPackerConfiguration()) + var c Config + _, err := c.Prepare(config, getPackerConfiguration()) if err != nil { t.Fatalf("Expected config to accept the value %q, but it did not", os_type) } @@ -167,7 +173,8 @@ func TestConfigShouldNormalizeOSTypeCase(t *testing.T) { bad_os_types := []string{"", "does-not-exist"} for _, os_type := range bad_os_types { config["os_type"] = os_type - _, _, err := newConfig(config, getPackerConfiguration()) + var c Config + _, err := c.Prepare(config, getPackerConfiguration()) if err == nil { t.Fatalf("Expected config to not accept the value %q, but it did", os_type) } @@ -191,7 +198,8 @@ func TestConfigShouldRejectCustomImageAndMarketPlace(t *testing.T) { for _, x := range marketPlace { config[x] = "ignore" - _, _, err := newConfig(config, packerConfiguration) + var c Config + _, err := c.Prepare(config, packerConfiguration) if err == nil { t.Errorf("Expected Config to reject image_url and %s, but it did not", x) } @@ -212,7 +220,8 @@ func TestConfigVirtualNetworkNameIsOptional(t *testing.T) { "virtual_network_name": "MyVirtualNetwork", } - c, _, _ := newConfig(config, getPackerConfiguration()) + var c Config + c.Prepare(config, getPackerConfiguration()) if c.VirtualNetworkName != "MyVirtualNetwork" { t.Errorf("Expected Config to set virtual_network_name to MyVirtualNetwork, but got %q", c.VirtualNetworkName) } @@ -241,7 +250,8 @@ func TestConfigVirtualNetworkResourceGroupNameMustBeSetWithVirtualNetworkName(t "virtual_network_resource_group_name": "MyVirtualNetworkRG", } - _, _, err := newConfig(config, getPackerConfiguration()) + var c Config + _, err := c.Prepare(config, getPackerConfiguration()) if err == nil { t.Error("Expected Config to reject virtual_network_resource_group_name, if virtual_network_name is not set.") } @@ -264,7 +274,8 @@ func TestConfigVirtualNetworkSubnetNameMustBeSetWithVirtualNetworkName(t *testin "virtual_network_subnet_name": "MyVirtualNetworkRG", } - _, _, err := newConfig(config, getPackerConfiguration()) + var c Config + _, err := c.Prepare(config, getPackerConfiguration()) if err == nil { t.Error("Expected Config to reject virtual_network_subnet_name, if virtual_network_name is not set.") } @@ -284,7 +295,8 @@ func TestConfigAllowedInboundIpAddressesIsOptional(t *testing.T) { "virtual_network_name": "MyVirtualNetwork", } - c, _, err := newConfig(config, getPackerConfiguration()) + var c Config + _, err := c.Prepare(config, getPackerConfiguration()) if err != nil { t.Fatal(err) } @@ -311,7 +323,8 @@ func TestConfigShouldAcceptCorrectInboundIpAddresses(t *testing.T) { } config["allowed_inbound_ip_addresses"] = ipValue0 - c, _, err := newConfig(config, getPackerConfiguration()) + var c Config + _, err := c.Prepare(config, getPackerConfiguration()) if err != nil { t.Fatal(err) } @@ -321,7 +334,7 @@ func TestConfigShouldAcceptCorrectInboundIpAddresses(t *testing.T) { } config["allowed_inbound_ip_addresses"] = cidrValue2 - c, _, err = newConfig(config, getPackerConfiguration()) + _, err = c.Prepare(config, getPackerConfiguration()) if err != nil { t.Fatal(err) } @@ -331,7 +344,7 @@ func TestConfigShouldAcceptCorrectInboundIpAddresses(t *testing.T) { } config["allowed_inbound_ip_addresses"] = []string{ipValue0, cidrValue2, ipValue1, cidrValue3} - c, _, err = newConfig(config, getPackerConfiguration()) + _, err = c.Prepare(config, getPackerConfiguration()) if err != nil { t.Fatal(err) } @@ -357,13 +370,14 @@ func TestConfigShouldRejectIncorrectInboundIpAddresses(t *testing.T) { } config["allowed_inbound_ip_addresses"] = []string{"127.0.0.1", "127.0.0.two"} - c, _, err := newConfig(config, getPackerConfiguration()) + var c Config + _, err := c.Prepare(config, getPackerConfiguration()) if err == nil { t.Errorf("Expected configuration creation to fail, but it succeeded with the malformed allowed_inbound_ip_addresses set to %v", c.AllowedInboundIpAddresses) } config["allowed_inbound_ip_addresses"] = []string{"192.168.100.1000/24", "10.10.1.16/32"} - c, _, err = newConfig(config, getPackerConfiguration()) + _, err = c.Prepare(config, getPackerConfiguration()) if err == nil { // 192.168.100.1000/24 is invalid t.Errorf("Expected configuration creation to fail, but it succeeded with the malformed allowed_inbound_ip_addresses set to %v", c.AllowedInboundIpAddresses) @@ -384,20 +398,22 @@ func TestConfigShouldRejectInboundIpAddressesWithVirtualNetwork(t *testing.T) { "allowed_inbound_ip_addresses": "127.0.0.1", } - _, _, err := newConfig(config, getPackerConfiguration()) + var c Config + _, err := c.Prepare(config, getPackerConfiguration()) if err != nil { t.Fatal(err) } config["virtual_network_name"] = "some_vnet_name" - _, _, err = newConfig(config, getPackerConfiguration()) + _, err = c.Prepare(config, getPackerConfiguration()) if err == nil { t.Errorf("Expected configuration creation to fail, but it succeeded with allowed_inbound_ip_addresses and virtual_network_name both specified") } } func TestConfigShouldDefaultToPublicCloud(t *testing.T) { - c, _, _ := newConfig(getArmBuilderConfiguration(), getPackerConfiguration()) + var c Config + c.Prepare(getArmBuilderConfiguration(), getPackerConfiguration()) if c.ClientConfig.CloudEnvironmentName != "Public" { t.Errorf("Expected 'CloudEnvironmentName' to default to 'Public', but got '%s'.", c.ClientConfig.CloudEnvironmentName) @@ -448,7 +464,8 @@ func TestConfigInstantiatesCorrectAzureEnvironment(t *testing.T) { for _, x := range table { config["cloud_environment_name"] = x.name - c, _, err := newConfig(config, packerConfiguration) + var c Config + _, err := c.Prepare(config, packerConfiguration) if err != nil { t.Fatal(err) } @@ -463,7 +480,8 @@ func TestUserShouldProvideRequiredValues(t *testing.T) { builderValues := getArmBuilderConfiguration() // Ensure we can successfully create a config. - _, _, err := newConfig(builderValues, getPackerConfiguration()) + var c Config + _, err := c.Prepare(builderValues, getPackerConfiguration()) if err != nil { t.Error("Expected configuration creation to succeed, but it failed!\n") t.Fatalf(" -> %+v\n", builderValues) @@ -474,7 +492,8 @@ func TestUserShouldProvideRequiredValues(t *testing.T) { originalValue := builderValues[v] delete(builderValues, v) - _, _, err := newConfig(builderValues, getPackerConfiguration()) + var c Config + _, err := c.Prepare(builderValues, getPackerConfiguration()) if err == nil { t.Error("Expected configuration creation to fail, but it succeeded!\n") t.Fatalf(" -> %+v\n", builderValues) @@ -485,7 +504,8 @@ func TestUserShouldProvideRequiredValues(t *testing.T) { } func TestSystemShouldDefineRuntimeValues(t *testing.T) { - c, _, _ := newConfig(getArmBuilderConfiguration(), getPackerConfiguration()) + var c Config + c.Prepare(getArmBuilderConfiguration(), getPackerConfiguration()) if c.Password == "" { t.Errorf("Expected Password to not be empty, but it was '%s'!", c.Password) @@ -513,7 +533,8 @@ func TestSystemShouldDefineRuntimeValues(t *testing.T) { } func TestConfigShouldTransformToVirtualMachineCaptureParameters(t *testing.T) { - c, _, _ := newConfig(getArmBuilderConfiguration(), getPackerConfiguration()) + var c Config + c.Prepare(getArmBuilderConfiguration(), getPackerConfiguration()) parameters := c.toVirtualMachineCaptureParameters() if *parameters.DestinationContainerName != c.CaptureContainerName { @@ -530,7 +551,8 @@ func TestConfigShouldTransformToVirtualMachineCaptureParameters(t *testing.T) { } func TestConfigShouldSupportPackersConfigElements(t *testing.T) { - c, _, err := newConfig( + var c Config + _, err := c.Prepare( getArmBuilderConfiguration(), getPackerConfiguration(), getPackerCommunicatorConfiguration()) @@ -554,7 +576,8 @@ func TestWinRMConfigShouldSetRoundTripDecorator(t *testing.T) { config["winrm_username"] = "username" config["winrm_password"] = "password" - c, _, err := newConfig(config, getPackerConfiguration()) + var c Config + _, err := c.Prepare(config, getPackerConfiguration()) if err != nil { t.Fatal(err) } @@ -579,7 +602,8 @@ func TestUserDeviceLoginIsEnabledForLinux(t *testing.T) { "communicator": "none", } - _, _, err := newConfig(config, getPackerConfiguration()) + var c Config + _, err := c.Prepare(config, getPackerConfiguration()) if err != nil { t.Fatalf("failed to use device login for Linux: %s", err) } @@ -610,7 +634,8 @@ func TestConfigShouldRejectMalformedCaptureNamePrefix(t *testing.T) { for _, x := range wellFormedCaptureNamePrefix { config["capture_name_prefix"] = x - _, _, err := newConfig(config, getPackerConfiguration()) + var c Config + _, err := c.Prepare(config, getPackerConfiguration()) if err != nil { t.Errorf("Expected test to pass, but it failed with the well-formed capture_name_prefix set to %q.", x) @@ -628,7 +653,8 @@ func TestConfigShouldRejectMalformedCaptureNamePrefix(t *testing.T) { for _, x := range malformedCaptureNamePrefix { config["capture_name_prefix"] = x - _, _, err := newConfig(config, getPackerConfiguration()) + var c Config + _, err := c.Prepare(config, getPackerConfiguration()) if err == nil { t.Errorf("Expected test to fail, but it succeeded with the malformed capture_name_prefix set to %q.", x) @@ -660,7 +686,8 @@ func TestConfigShouldRejectMalformedCaptureContainerName(t *testing.T) { for _, x := range wellFormedCaptureContainerName { config["capture_container_name"] = x - _, _, err := newConfig(config, getPackerConfiguration()) + var c Config + _, err := c.Prepare(config, getPackerConfiguration()) if err != nil { t.Errorf("Expected test to pass, but it failed with the well-formed capture_container_name set to %q.", x) @@ -678,7 +705,8 @@ func TestConfigShouldRejectMalformedCaptureContainerName(t *testing.T) { for _, x := range malformedCaptureContainerName { config["capture_container_name"] = x - _, _, err := newConfig(config, getPackerConfiguration()) + var c Config + _, err := c.Prepare(config, getPackerConfiguration()) if err == nil { t.Errorf("Expected test to fail, but it succeeded with the malformed capture_container_name set to %q.", x) @@ -710,7 +738,8 @@ func TestConfigShouldRejectMalformedManagedImageOSDiskSnapshotName(t *testing.T) for _, x := range wellFormedManagedImageOSDiskSnapshotName { config["managed_image_os_disk_snapshot_name"] = x - _, _, err := newConfig(config, getPackerConfiguration()) + var c Config + _, err := c.Prepare(config, getPackerConfiguration()) if err != nil { t.Errorf("Expected test to pass, but it failed with the well-formed managed_image_os_disk_snapshot_name set to %q.", x) @@ -727,7 +756,8 @@ func TestConfigShouldRejectMalformedManagedImageOSDiskSnapshotName(t *testing.T) for _, x := range malformedManagedImageOSDiskSnapshotName { config["managed_image_os_disk_snapshot_name"] = x - _, _, err := newConfig(config, getPackerConfiguration()) + var c Config + _, err := c.Prepare(config, getPackerConfiguration()) if err == nil { t.Errorf("Expected test to fail, but it succeeded with the malformed managed_image_os_disk_snapshot_name set to %q.", x) @@ -760,7 +790,8 @@ func TestConfigShouldRejectMalformedManagedImageDataDiskSnapshotPrefix(t *testin for _, x := range wellFormedManagedImageDataDiskSnapshotPrefix { config["managed_image_data_disk_snapshot_prefix"] = x - _, _, err := newConfig(config, getPackerConfiguration()) + var c Config + _, err := c.Prepare(config, getPackerConfiguration()) if err != nil { t.Errorf("Expected test to pass, but it failed with the well-formed managed_image_data_disk_snapshot_prefix set to %q.", x) @@ -777,7 +808,8 @@ func TestConfigShouldRejectMalformedManagedImageDataDiskSnapshotPrefix(t *testin for _, x := range malformedManagedImageDataDiskSnapshotPrefix { config["managed_image_data_disk_snapshot_prefix"] = x - _, _, err := newConfig(config, getPackerConfiguration()) + var c Config + _, err := c.Prepare(config, getPackerConfiguration()) if err == nil { t.Errorf("Expected test to fail, but it succeeded with the malformed managed_image_data_disk_snapshot_prefix set to %q.", x) @@ -805,7 +837,8 @@ func TestConfigShouldAcceptTags(t *testing.T) { }, } - c, _, err := newConfig(config, getPackerConfiguration()) + var c Config + _, err := c.Prepare(config, getPackerConfiguration()) if err != nil { t.Fatal(err) @@ -855,7 +888,8 @@ func TestConfigShouldRejectTagsInExcessOf15AcceptTags(t *testing.T) { "azure_tags": tooManyTags, } - _, _, err := newConfig(config, getPackerConfiguration()) + var c Config + _, err := c.Prepare(config, getPackerConfiguration()) if err == nil { t.Fatal("expected config to reject based on an excessive amount of tags (> 15)") @@ -887,7 +921,8 @@ func TestConfigShouldRejectExcessiveTagNameLength(t *testing.T) { "azure_tags": tags, } - _, _, err := newConfig(config, getPackerConfiguration()) + var c Config + _, err := c.Prepare(config, getPackerConfiguration()) if err == nil { t.Fatal("expected config to reject tag name based on length (> 512)") } @@ -918,7 +953,8 @@ func TestConfigShouldRejectExcessiveTagValueLength(t *testing.T) { "azure_tags": tags, } - _, _, err := newConfig(config, getPackerConfiguration()) + var c Config + _, err := c.Prepare(config, getPackerConfiguration()) if err == nil { t.Fatal("expected config to reject tag value based on length (> 256)") } @@ -935,7 +971,8 @@ func TestConfigZoneResilientShouldDefaultToFalse(t *testing.T) { "os_type": "linux", } - c, _, err := newConfig(config, getPackerConfiguration()) + var c Config + _, err := c.Prepare(config, getPackerConfiguration()) if err != nil { t.Fatal(err) } @@ -958,7 +995,8 @@ func TestConfigZoneResilientSetFromConfig(t *testing.T) { "managed_image_zone_resilient": true, } - c, _, err := newConfig(config, getPackerConfiguration()) + var c Config + _, err := c.Prepare(config, getPackerConfiguration()) if err != nil { t.Fatal(err) } @@ -986,7 +1024,8 @@ func TestConfigShouldRejectMissingCustomDataFile(t *testing.T) { "custom_data_file": "/this/file/does/not/exist", } - _, _, err := newConfig(config, getPackerConfiguration()) + var c Config + _, err := c.Prepare(config, getPackerConfiguration()) if err == nil { t.Fatal("expected config to reject missing custom data file") } @@ -1006,7 +1045,8 @@ func TestConfigShouldRejectManagedImageOSDiskSnapshotNameWithoutManagedImageName "os_type": constants.Target_Linux, } - _, _, err := newConfig(config, getPackerConfiguration()) + var c Config + _, err := c.Prepare(config, getPackerConfiguration()) if err == nil { t.Fatal("expected config to reject Managed Image build with OS disk snapshot name but without managed image name") } @@ -1026,7 +1066,8 @@ func TestConfigShouldRejectManagedImageOSDiskSnapshotNameWithoutManagedImageReso "os_type": constants.Target_Linux, } - _, _, err := newConfig(config, getPackerConfiguration()) + var c Config + _, err := c.Prepare(config, getPackerConfiguration()) if err == nil { t.Fatal("expected config to reject Managed Image build with OS disk snapshot name but without managed image resource group name") } @@ -1046,7 +1087,8 @@ func TestConfigShouldRejectImageDataDiskSnapshotPrefixWithoutManagedImageName(t "os_type": constants.Target_Linux, } - _, _, err := newConfig(config, getPackerConfiguration()) + var c Config + _, err := c.Prepare(config, getPackerConfiguration()) if err == nil { t.Fatal("expected config to reject Managed Image build with data disk snapshot prefix but without managed image name") } @@ -1066,7 +1108,8 @@ func TestConfigShouldRejectImageDataDiskSnapshotPrefixWithoutManagedImageResourc "os_type": constants.Target_Linux, } - _, _, err := newConfig(config, getPackerConfiguration()) + var c Config + _, err := c.Prepare(config, getPackerConfiguration()) if err == nil { t.Fatal("expected config to reject Managed Image build with data disk snapshot prefix but without managed image resource group name") } @@ -1088,7 +1131,8 @@ func TestConfigShouldAcceptManagedImageOSDiskSnapshotNameAndManagedImageDataDisk "os_type": constants.Target_Linux, } - _, _, err := newConfig(config, getPackerConfiguration()) + var c Config + _, err := c.Prepare(config, getPackerConfiguration()) if err != nil { t.Fatal("expected config to accept platform managed image build") } @@ -1109,7 +1153,8 @@ func TestConfigShouldRejectManagedImageOSDiskSnapshotNameAndManagedImageDataDisk "os_type": constants.Target_Linux, } - _, _, err := newConfig(config, getPackerConfiguration()) + var c Config + _, err := c.Prepare(config, getPackerConfiguration()) if err == nil { t.Fatal("expected config to reject Managed Image build with data disk snapshot prefix and OS disk snapshot name with capture container name") } @@ -1130,7 +1175,8 @@ func TestConfigShouldRejectManagedImageOSDiskSnapshotNameAndManagedImageDataDisk "os_type": constants.Target_Linux, } - _, _, err := newConfig(config, getPackerConfiguration()) + var c Config + _, err := c.Prepare(config, getPackerConfiguration()) if err == nil { t.Fatal("expected config to reject Managed Image build with data disk snapshot prefix and OS disk snapshot name with capture name prefix") } @@ -1151,7 +1197,8 @@ func TestConfigShouldAcceptPlatformManagedImageBuild(t *testing.T) { "os_type": constants.Target_Linux, } - _, _, err := newConfig(config, getPackerConfiguration()) + var c Config + _, err := c.Prepare(config, getPackerConfiguration()) if err != nil { t.Fatal("expected config to accept platform managed image build") } @@ -1175,7 +1222,8 @@ func TestConfigShouldRejectVhdAndManagedImageOutput(t *testing.T) { "os_type": constants.Target_Linux, } - _, _, err := newConfig(config, getPackerConfiguration()) + var c Config + _, err := c.Prepare(config, getPackerConfiguration()) if err == nil { t.Fatal("expected config to reject VHD and Managed Image build") } @@ -1195,7 +1243,8 @@ func TestConfigShouldRejectManagedImageSourceAndVhdOutput(t *testing.T) { "os_type": constants.Target_Linux, } - _, _, err := newConfig(config, getPackerConfiguration()) + var c Config + _, err := c.Prepare(config, getPackerConfiguration()) if err == nil { t.Fatal("expected config to reject VHD and Managed Image build") } @@ -1218,7 +1267,8 @@ func TestConfigShouldRejectCustomAndPlatformManagedImageBuild(t *testing.T) { "os_type": constants.Target_Linux, } - _, _, err := newConfig(config, getPackerConfiguration()) + var c Config + _, err := c.Prepare(config, getPackerConfiguration()) if err == nil { t.Fatal("expected config to reject custom and platform input for a managed image build") } @@ -1239,7 +1289,8 @@ func TestConfigShouldRejectCustomAndImageUrlForManagedImageBuild(t *testing.T) { "os_type": constants.Target_Linux, } - _, _, err := newConfig(config, getPackerConfiguration()) + var c Config + _, err := c.Prepare(config, getPackerConfiguration()) if err == nil { t.Fatal("expected config to reject custom and platform input for a managed image build") } @@ -1260,7 +1311,8 @@ func TestConfigShouldRejectMalformedManageImageStorageAccountTypes(t *testing.T) "os_type": constants.Target_Linux, } - _, _, err := newConfig(config, getPackerConfiguration()) + var c Config + _, err := c.Prepare(config, getPackerConfiguration()) if err == nil { t.Fatal("expected config to reject custom and platform input for a managed image build") } @@ -1281,7 +1333,8 @@ func TestConfigShouldRejectMalformedDiskCachingType(t *testing.T) { "os_type": constants.Target_Linux, } - _, _, err := newConfig(config, getPackerConfiguration()) + var c Config + _, err := c.Prepare(config, getPackerConfiguration()) if err == nil { t.Fatal("expected config to reject custom and platform input for a managed image build") } @@ -1305,7 +1358,8 @@ func TestConfigShouldAcceptManagedImageStorageAccountTypes(t *testing.T) { for _, x := range storage_account_types { config["managed_image_storage_account_type"] = x - _, _, err := newConfig(config, getPackerConfiguration()) + var c Config + _, err := c.Prepare(config, getPackerConfiguration()) if err != nil { t.Fatalf("expected config to accept a managed_image_storage_account_type of %q", x) } @@ -1330,7 +1384,8 @@ func TestConfigShouldAcceptDiskCachingTypes(t *testing.T) { for _, x := range storage_account_types { config["disk_caching_type"] = x - _, _, err := newConfig(config, getPackerConfiguration()) + var c Config + _, err := c.Prepare(config, getPackerConfiguration()) if err != nil { t.Fatalf("expected config to accept a disk_caching_type of %q", x) } @@ -1355,7 +1410,8 @@ func TestConfigShouldRejectTempAndBuildResourceGroupName(t *testing.T) { "build_resource_group_name": "rgn00", } - _, _, err := newConfig(config, getPackerConfiguration()) + var c Config + _, err := c.Prepare(config, getPackerConfiguration()) if err == nil { t.Fatal("expected config to reject the use of both temp_resource_group_name and build_resource_group_name") } @@ -1402,7 +1458,8 @@ func TestConfigShouldRejectInvalidResourceGroupNames(t *testing.T) { for _, y := range tests { config[x] = y.name - _, _, err := newConfig(config, getPackerConfiguration()) + var c Config + _, err := c.Prepare(config, getPackerConfiguration()) if !y.ok && err == nil { t.Errorf("expected config to reject %q for setting %q", y.name, x) } else if y.ok && err != nil { @@ -1452,7 +1509,8 @@ func TestConfigShouldRejectManagedDiskNames(t *testing.T) { for _, y := range testsResourceGroupNames { config[settingUnderTest] = y.name - _, _, err := newConfig(config, getPackerConfiguration()) + var c Config + _, err := c.Prepare(config, getPackerConfiguration()) if !y.ok && err == nil { t.Errorf("expected config to reject %q for setting %q", y.name, settingUnderTest) } else if y.ok && err != nil { @@ -1486,7 +1544,8 @@ func TestConfigShouldRejectManagedDiskNames(t *testing.T) { for _, y := range testNames { config[settingUnderTest] = y.name - _, _, err := newConfig(config, getPackerConfiguration()) + var c Config + _, err := c.Prepare(config, getPackerConfiguration()) if !y.ok && err == nil { t.Logf("expected config to reject %q for setting %q", y.name, settingUnderTest) } else if y.ok && err != nil { @@ -1496,7 +1555,8 @@ func TestConfigShouldRejectManagedDiskNames(t *testing.T) { } func TestConfigAdditionalDiskDefaultIsNil(t *testing.T) { - c, _, _ := newConfig(getArmBuilderConfiguration(), getPackerConfiguration()) + var c Config + c.Prepare(getArmBuilderConfiguration(), getPackerConfiguration()) if c.AdditionalDiskSize != nil { t.Errorf("Expected Config to not have a set of additional disks, but got a non nil value") } @@ -1519,7 +1579,8 @@ func TestConfigAdditionalDiskOverrideDefault(t *testing.T) { "disk_additional_size": {32, 64}, } - c, _, _ := newConfig(config, diskconfig, getPackerConfiguration()) + var c Config + c.Prepare(config, diskconfig, getPackerConfiguration()) if c.AdditionalDiskSize == nil { t.Errorf("Expected Config to have a set of additional disks, but got nil") } @@ -1561,19 +1622,20 @@ func TestPlanInfoConfiguration(t *testing.T) { } config["plan_info"] = planInfo - _, _, err := newConfig(config, getPackerConfiguration()) + var c Config + _, err := c.Prepare(config, getPackerConfiguration()) if err == nil { t.Fatal("expected config to reject the use of plan_name without plan_product and plan_publisher") } planInfo["plan_product"] = "--plan-product--" - _, _, err = newConfig(config, getPackerConfiguration()) + _, err = c.Prepare(config, getPackerConfiguration()) if err == nil { t.Fatal("expected config to reject the use of plan_name and plan_product without plan_publisher") } planInfo["plan_publisher"] = "--plan-publisher--" - c, _, err := newConfig(config, getPackerConfiguration()) + _, err = c.Prepare(config, getPackerConfiguration()) if err != nil { t.Fatalf("expected config to accept a complete plan configuration: %s", err) } @@ -1610,7 +1672,8 @@ func TestPlanInfoPromotionCode(t *testing.T) { }, } - c, _, err := newConfig(config, getPackerConfiguration()) + var c Config + _, err := c.Prepare(config, getPackerConfiguration()) if err != nil { t.Fatalf("expected config to accept plan_info configuration, but got %s", err) } @@ -1659,7 +1722,8 @@ func TestPlanInfoTooManyTagsErrors(t *testing.T) { }, } - _, _, err := newConfig(config, getPackerConfiguration()) + var c Config + _, err := c.Prepare(config, getPackerConfiguration()) if err == nil { t.Fatal("expected config to reject configuration due to excess tags") } @@ -1682,7 +1746,8 @@ func TestConfigShouldAllowTempNameOverrides(t *testing.T) { "temp_compute_name": "myTempComputeName", } - c, _, err := newConfig(config, getPackerConfiguration()) + var c Config + _, err := c.Prepare(config, getPackerConfiguration()) if err != nil { t.Errorf("newConfig failed with %q", err) } @@ -1716,7 +1781,8 @@ func TestConfigShouldAllowAsyncResourceGroupOverride(t *testing.T) { "async_resourcegroup_delete": "true", } - c, _, err := newConfig(config, getPackerConfiguration()) + var c Config + _, err := c.Prepare(config, getPackerConfiguration()) if err != nil { t.Errorf("newConfig failed with %q", err) } @@ -1738,7 +1804,8 @@ func TestConfigShouldAllowAsyncResourceGroupOverrideNoValue(t *testing.T) { "managed_image_resource_group_name": "ignore", } - c, _, err := newConfig(config, getPackerConfiguration()) + var c Config + _, err := c.Prepare(config, getPackerConfiguration()) if err != nil { t.Errorf("newConfig failed with %q", err) } @@ -1761,10 +1828,10 @@ func TestConfigShouldAllowAsyncResourceGroupOverrideBadValue(t *testing.T) { "async_resourcegroup_delete": "asdasda", } - c, _, err := newConfig(config, getPackerConfiguration()) - if err != nil && c == nil { - t.Log("newConfig failed which is expected ", err) - + var c Config + _, err := c.Prepare(config, getPackerConfiguration()) + if err != nil { + t.Log("newConfig failed which is expected ", err) } } @@ -1782,7 +1849,8 @@ func TestConfigShouldAllowSharedImageGalleryOptions(t *testing.T) { }, } - _, _, err := newConfig(config, getPackerConfiguration()) + var c Config + _, err := c.Prepare(config, getPackerConfiguration()) if err == nil { t.Log("expected config to accept Shared Image Gallery options", err) } @@ -1807,7 +1875,8 @@ func TestConfigShouldRejectSharedImageGalleryWithVhdTarget(t *testing.T) { "capture_name_prefix": "ignore", } - _, _, err := newConfig(config, getPackerConfiguration()) + var c Config + _, err := c.Prepare(config, getPackerConfiguration()) if err != nil { t.Log("expected an error if Shared Image Gallery source is used with VHD target", err) } @@ -1818,7 +1887,8 @@ func Test_GivenZoneNotSupportingResiliency_ConfigValidate_ShouldWarn(t *testing. builderValues["managed_image_zone_resilient"] = "true" builderValues["location"] = "ukwest" - c, _, err := newConfig(builderValues, getPackerConfiguration()) + var c Config + _, err := c.Prepare(builderValues, getPackerConfiguration()) if err != nil { t.Errorf("newConfig failed with %q", err) } @@ -1836,7 +1906,8 @@ func Test_GivenZoneSupportingResiliency_ConfigValidate_ShouldNotWarn(t *testing. builderValues["managed_image_zone_resilient"] = "true" builderValues["location"] = "westeurope" - c, _, err := newConfig(builderValues, getPackerConfiguration()) + var c Config + _, err := c.Prepare(builderValues, getPackerConfiguration()) if err != nil { t.Errorf("newConfig failed with %q", err) } diff --git a/builder/azure/arm/resource_resolver_test.go b/builder/azure/arm/resource_resolver_test.go index f8e339687..68b227ee0 100644 --- a/builder/azure/arm/resource_resolver_test.go +++ b/builder/azure/arm/resource_resolver_test.go @@ -5,14 +5,15 @@ import ( ) func TestResourceResolverIgnoresEmptyVirtualNetworkName(t *testing.T) { - c, _, _ := newConfig(getArmBuilderConfiguration(), getPackerConfiguration()) + var c Config + c.Prepare(getArmBuilderConfiguration(), getPackerConfiguration()) if c.VirtualNetworkName != "" { t.Fatalf("Expected VirtualNetworkName to be empty by default") } sut := newTestResourceResolver() sut.findVirtualNetworkResourceGroup = nil // assert that this is not even called - sut.Resolve(c) + sut.Resolve(&c) if c.VirtualNetworkName != "" { t.Fatalf("Expected VirtualNetworkName to be empty") @@ -25,7 +26,8 @@ func TestResourceResolverIgnoresEmptyVirtualNetworkName(t *testing.T) { // If the user fully specified the virtual network name and resource group then // there is no need to do a lookup. func TestResourceResolverIgnoresSetVirtualNetwork(t *testing.T) { - c, _, _ := newConfig(getArmBuilderConfiguration(), getPackerConfiguration()) + var c Config + c.Prepare(getArmBuilderConfiguration(), getPackerConfiguration()) c.VirtualNetworkName = "--virtual-network-name--" c.VirtualNetworkResourceGroupName = "--virtual-network-resource-group-name--" c.VirtualNetworkSubnetName = "--virtual-network-subnet-name--" @@ -33,7 +35,7 @@ func TestResourceResolverIgnoresSetVirtualNetwork(t *testing.T) { sut := newTestResourceResolver() sut.findVirtualNetworkResourceGroup = nil // assert that this is not even called sut.findVirtualNetworkSubnet = nil // assert that this is not even called - sut.Resolve(c) + sut.Resolve(&c) if c.VirtualNetworkName != "--virtual-network-name--" { t.Fatalf("Expected VirtualNetworkName to be --virtual-network-name--") @@ -49,11 +51,12 @@ func TestResourceResolverIgnoresSetVirtualNetwork(t *testing.T) { // If the user set virtual network name then the code should resolve virtual network // resource group name. func TestResourceResolverSetVirtualNetworkResourceGroupName(t *testing.T) { - c, _, _ := newConfig(getArmBuilderConfiguration(), getPackerConfiguration()) + var c Config + c.Prepare(getArmBuilderConfiguration(), getPackerConfiguration()) c.VirtualNetworkName = "--virtual-network-name--" sut := newTestResourceResolver() - sut.Resolve(c) + sut.Resolve(&c) if c.VirtualNetworkResourceGroupName != "findVirtualNetworkResourceGroup is mocked" { t.Fatalf("Expected VirtualNetworkResourceGroupName to be 'findVirtualNetworkResourceGroup is mocked'") diff --git a/builder/azure/arm/step_publish_to_shared_image_gallery.go b/builder/azure/arm/step_publish_to_shared_image_gallery.go index 347a9002c..f30b6fa96 100644 --- a/builder/azure/arm/step_publish_to_shared_image_gallery.go +++ b/builder/azure/arm/step_publish_to_shared_image_gallery.go @@ -3,6 +3,7 @@ package arm import ( "context" "fmt" + "github.com/Azure/azure-sdk-for-go/services/compute/mgmt/2019-03-01/compute" "github.com/hashicorp/packer/builder/azure/common/constants" "github.com/hashicorp/packer/helper/multistep" diff --git a/builder/azure/arm/step_publish_to_shared_image_gallery_test.go b/builder/azure/arm/step_publish_to_shared_image_gallery_test.go index 97c7aa289..ebcc681f8 100644 --- a/builder/azure/arm/step_publish_to_shared_image_gallery_test.go +++ b/builder/azure/arm/step_publish_to_shared_image_gallery_test.go @@ -2,9 +2,10 @@ package arm import ( "context" + "testing" + "github.com/hashicorp/packer/builder/azure/common/constants" "github.com/hashicorp/packer/helper/multistep" - "testing" ) func TestStepPublishToSharedImageGalleryShouldNotPublishForVhd(t *testing.T) { diff --git a/builder/azure/arm/template_factory_test.go b/builder/azure/arm/template_factory_test.go index 069415893..7c465a791 100644 --- a/builder/azure/arm/template_factory_test.go +++ b/builder/azure/arm/template_factory_test.go @@ -13,8 +13,9 @@ import ( // Ensure the link values are not set, and the concrete values are set. func TestVirtualMachineDeployment00(t *testing.T) { - c, _, _ := newConfig(getArmBuilderConfiguration(), getPackerConfiguration()) - deployment, err := GetVirtualMachineDeployment(c) + var c Config + c.Prepare(getArmBuilderConfiguration(), getPackerConfiguration()) + deployment, err := GetVirtualMachineDeployment(&c) if err != nil { t.Fatal(err) } @@ -42,8 +43,9 @@ func TestVirtualMachineDeployment00(t *testing.T) { // Ensure the Virtual Machine template is a valid JSON document. func TestVirtualMachineDeployment01(t *testing.T) { - c, _, _ := newConfig(getArmBuilderConfiguration(), getPackerConfiguration()) - deployment, err := GetVirtualMachineDeployment(c) + var c Config + c.Prepare(getArmBuilderConfiguration(), getPackerConfiguration()) + deployment, err := GetVirtualMachineDeployment(&c) if err != nil { t.Fatal(err) } @@ -56,8 +58,9 @@ func TestVirtualMachineDeployment01(t *testing.T) { // Ensure the Virtual Machine template parameters are correct. func TestVirtualMachineDeployment02(t *testing.T) { - c, _, _ := newConfig(getArmBuilderConfiguration(), getPackerConfiguration()) - deployment, err := GetVirtualMachineDeployment(c) + var c Config + c.Prepare(getArmBuilderConfiguration(), getPackerConfiguration()) + deployment, err := GetVirtualMachineDeployment(&c) if err != nil { t.Fatal(err) } @@ -104,8 +107,9 @@ func TestVirtualMachineDeployment03(t *testing.T) { m["image_sku"] = "ImageSku" m["image_version"] = "ImageVersion" - c, _, _ := newConfig(m, getPackerConfiguration()) - deployment, err := GetVirtualMachineDeployment(c) + var c Config + c.Prepare(m, getPackerConfiguration()) + deployment, err := GetVirtualMachineDeployment(&c) if err != nil { t.Fatal(err) } @@ -130,12 +134,13 @@ func TestVirtualMachineDeployment04(t *testing.T) { "communicator": "none", } - c, _, err := newConfig(config, getPackerConfiguration()) + var c Config + _, err := c.Prepare(config, getPackerConfiguration()) if err != nil { t.Fatal(err) } - deployment, err := GetVirtualMachineDeployment(c) + deployment, err := GetVirtualMachineDeployment(&c) if err != nil { t.Fatal(err) } @@ -162,12 +167,13 @@ func TestVirtualMachineDeployment05(t *testing.T) { "virtual_network_subnet_name": "virtualNetworkSubnetName", } - c, _, err := newConfig(config, getPackerConfiguration()) + var c Config + _, err := c.Prepare(config, getPackerConfiguration()) if err != nil { t.Fatal(err) } - deployment, err := GetVirtualMachineDeployment(c) + deployment, err := GetVirtualMachineDeployment(&c) if err != nil { t.Fatal(err) } @@ -197,12 +203,13 @@ func TestVirtualMachineDeployment06(t *testing.T) { }, } - c, _, err := newConfig(config, getPackerConfiguration()) + var c Config + _, err := c.Prepare(config, getPackerConfiguration()) if err != nil { t.Fatal(err) } - deployment, err := GetVirtualMachineDeployment(c) + deployment, err := GetVirtualMachineDeployment(&c) if err != nil { t.Fatal(err) } @@ -227,7 +234,8 @@ func TestVirtualMachineDeployment07(t *testing.T) { "communicator": "none", } - c, _, err := newConfig(config, getPackerConfiguration()) + var c Config + _, err := c.Prepare(config, getPackerConfiguration()) if err != nil { t.Fatal(err) } @@ -247,7 +255,7 @@ growpart: base64CustomData := base64.StdEncoding.EncodeToString([]byte(customData)) c.customData = base64CustomData - deployment, err := GetVirtualMachineDeployment(c) + deployment, err := GetVirtualMachineDeployment(&c) if err != nil { t.Fatal(err) } @@ -271,12 +279,13 @@ func TestVirtualMachineDeployment08(t *testing.T) { "managed_image_resource_group_name": "ManagedImageResourceGroupName", } - c, _, err := newConfig(config, getPackerConfiguration()) + var c Config + _, err := c.Prepare(config, getPackerConfiguration()) if err != nil { t.Fatal(err) } - deployment, err := GetVirtualMachineDeployment(c) + deployment, err := GetVirtualMachineDeployment(&c) if err != nil { t.Fatal(err) } @@ -302,12 +311,13 @@ func TestVirtualMachineDeployment09(t *testing.T) { "managed_image_resource_group_name": "ManagedImageResourceGroupName", } - c, _, err := newConfig(config, getPackerConfiguration()) + var c Config + _, err := c.Prepare(config, getPackerConfiguration()) if err != nil { t.Fatal(err) } - deployment, err := GetVirtualMachineDeployment(c) + deployment, err := GetVirtualMachineDeployment(&c) if err != nil { t.Fatal(err) } @@ -339,12 +349,13 @@ func TestVirtualMachineDeployment10(t *testing.T) { "managed_image_resource_group_name": "ManagedImageResourceGroupName", } - c, _, err := newConfig(config, getPackerConfiguration()) + var c Config + _, err := c.Prepare(config, getPackerConfiguration()) if err != nil { t.Fatal(err) } - deployment, err := GetVirtualMachineDeployment(c) + deployment, err := GetVirtualMachineDeployment(&c) if err != nil { t.Fatal(err) } @@ -375,12 +386,13 @@ func TestVirtualMachineDeployment11(t *testing.T) { "capture_container_name": "packerimages", } - c, _, err := newConfig(config, getPackerConfiguration()) + var c Config + _, err := c.Prepare(config, getPackerConfiguration()) if err != nil { t.Fatal(err) } - deployment, err := GetVirtualMachineDeployment(c) + deployment, err := GetVirtualMachineDeployment(&c) if err != nil { t.Fatal(err) } @@ -409,12 +421,13 @@ func TestVirtualMachineDeployment12(t *testing.T) { "managed_image_resource_group_name": "ManagedImageResourceGroupName", } - c, _, err := newConfig(config, getPackerConfiguration()) + var c Config + _, err := c.Prepare(config, getPackerConfiguration()) if err != nil { t.Fatal(err) } - deployment, err := GetVirtualMachineDeployment(c) + deployment, err := GetVirtualMachineDeployment(&c) if err != nil { t.Fatal(err) } @@ -442,13 +455,14 @@ func TestVirtualMachineDeployment13(t *testing.T) { "allowed_inbound_ip_addresses": []string{"127.0.0.1", "192.168.100.0/24"}, } - c, _, err := newConfig(config, getPackerConfiguration()) + var c Config + _, err := c.Prepare(config, getPackerConfiguration()) if err != nil { t.Fatal(err) } c.tmpKeyVaultName = "--keyvault-name--" - deployment, err := GetVirtualMachineDeployment(c) + deployment, err := GetVirtualMachineDeployment(&c) if err != nil { t.Fatal(err) } @@ -461,8 +475,9 @@ func TestVirtualMachineDeployment13(t *testing.T) { // Ensure the link values are not set, and the concrete values are set. func TestKeyVaultDeployment00(t *testing.T) { - c, _, _ := newConfig(getArmBuilderConfiguration(), getPackerConfiguration()) - deployment, err := GetKeyVaultDeployment(c) + var c Config + c.Prepare(getArmBuilderConfiguration(), getPackerConfiguration()) + deployment, err := GetKeyVaultDeployment(&c) if err != nil { t.Fatal(err) } @@ -490,8 +505,9 @@ func TestKeyVaultDeployment00(t *testing.T) { // Ensure the KeyVault template is a valid JSON document. func TestKeyVaultDeployment01(t *testing.T) { - c, _, _ := newConfig(getArmBuilderConfiguration(), getPackerConfiguration()) - deployment, err := GetKeyVaultDeployment(c) + var c Config + c.Prepare(getArmBuilderConfiguration(), getPackerConfiguration()) + deployment, err := GetKeyVaultDeployment(&c) if err != nil { t.Fatal(err) } @@ -504,9 +520,10 @@ func TestKeyVaultDeployment01(t *testing.T) { // Ensure the KeyVault template parameters are correct. func TestKeyVaultDeployment02(t *testing.T) { - c, _, _ := newConfig(getArmBuilderConfigurationWithWindows(), getPackerConfiguration()) + var c Config + c.Prepare(getArmBuilderConfigurationWithWindows(), getPackerConfiguration()) - deployment, err := GetKeyVaultDeployment(c) + deployment, err := GetKeyVaultDeployment(&c) if err != nil { t.Fatal(err) } @@ -546,8 +563,9 @@ func TestKeyVaultDeployment03(t *testing.T) { }, } - c, _, _ := newConfig(tags, getArmBuilderConfigurationWithWindows(), getPackerConfiguration()) - deployment, err := GetKeyVaultDeployment(c) + var c Config + c.Prepare(tags, getArmBuilderConfigurationWithWindows(), getPackerConfiguration()) + deployment, err := GetKeyVaultDeployment(&c) if err != nil { t.Fatal(err) } @@ -567,8 +585,9 @@ func TestPlanInfo01(t *testing.T) { }, } - c, _, _ := newConfig(planInfo, getArmBuilderConfiguration(), getPackerConfiguration()) - deployment, err := GetVirtualMachineDeployment(c) + var c Config + c.Prepare(planInfo, getArmBuilderConfiguration(), getPackerConfiguration()) + deployment, err := GetVirtualMachineDeployment(&c) if err != nil { t.Fatal(err) } @@ -592,8 +611,9 @@ func TestPlanInfo02(t *testing.T) { }, } - c, _, _ := newConfig(planInfo, getArmBuilderConfiguration(), getPackerConfiguration()) - deployment, err := GetVirtualMachineDeployment(c) + var c Config + c.Prepare(planInfo, getArmBuilderConfiguration(), getPackerConfiguration()) + deployment, err := GetVirtualMachineDeployment(&c) if err != nil { t.Fatal(err) } diff --git a/builder/azure/chroot/builder.go b/builder/azure/chroot/builder.go index 91ceca991..8cf4ba205 100644 --- a/builder/azure/chroot/builder.go +++ b/builder/azure/chroot/builder.go @@ -1,4 +1,5 @@ //go:generate struct-markdown +//go:generate mapstructure-to-hcl2 -type Config // Package chroot is able to create an Azure managed image without requiring the // launch of a new virtual machine for every build. It does this by attaching and @@ -14,6 +15,7 @@ import ( "runtime" "strings" + "github.com/hashicorp/hcl/v2/hcldec" azcommon "github.com/hashicorp/packer/builder/azure/common" "github.com/hashicorp/packer/builder/azure/common/client" "github.com/hashicorp/packer/common" @@ -116,6 +118,8 @@ type Builder struct { runner multistep.Runner } +func (b *Builder) ConfigSpec() hcldec.ObjectSpec { return b.config.FlatMapstructure().HCL2Spec() } + func (b *Builder) Prepare(raws ...interface{}) ([]string, error) { b.config.ctx.Funcs = azcommon.TemplateFuncs b.config.ctx.Funcs["vm"] = CreateVMMetadataTemplateFunc() diff --git a/builder/azure/chroot/builder.hcl2spec.go b/builder/azure/chroot/builder.hcl2spec.go new file mode 100644 index 000000000..50a42e314 --- /dev/null +++ b/builder/azure/chroot/builder.hcl2spec.go @@ -0,0 +1,92 @@ +// Code generated by "mapstructure-to-hcl2 -type Config"; DO NOT EDIT. +package chroot + +import ( + "github.com/hashicorp/hcl/v2/hcldec" + "github.com/zclconf/go-cty/cty" +) + +// FlatConfig is an auto-generated flat version of Config. +// Where the contents of a field with a `mapstructure:,squash` tag are bubbled up. +type FlatConfig struct { + PackerBuildName *string `mapstructure:"packer_build_name" cty:"packer_build_name"` + PackerBuilderType *string `mapstructure:"packer_builder_type" cty:"packer_builder_type"` + PackerDebug *bool `mapstructure:"packer_debug" cty:"packer_debug"` + PackerForce *bool `mapstructure:"packer_force" cty:"packer_force"` + PackerOnError *string `mapstructure:"packer_on_error" cty:"packer_on_error"` + PackerUserVars map[string]string `mapstructure:"packer_user_variables" cty:"packer_user_variables"` + PackerSensitiveVars []string `mapstructure:"packer_sensitive_variables" cty:"packer_sensitive_variables"` + CloudEnvironmentName *string `mapstructure:"cloud_environment_name" required:"false" cty:"cloud_environment_name"` + ClientID *string `mapstructure:"client_id" cty:"client_id"` + ClientSecret *string `mapstructure:"client_secret" cty:"client_secret"` + ClientCertPath *string `mapstructure:"client_cert_path" cty:"client_cert_path"` + ClientJWT *string `mapstructure:"client_jwt" cty:"client_jwt"` + ObjectID *string `mapstructure:"object_id" cty:"object_id"` + TenantID *string `mapstructure:"tenant_id" required:"false" cty:"tenant_id"` + SubscriptionID *string `mapstructure:"subscription_id" cty:"subscription_id"` + FromScratch *bool `mapstructure:"from_scratch" cty:"from_scratch"` + Source *string `mapstructure:"source" required:"true" cty:"source"` + CommandWrapper *string `mapstructure:"command_wrapper" cty:"command_wrapper"` + PreMountCommands []string `mapstructure:"pre_mount_commands" cty:"pre_mount_commands"` + MountOptions []string `mapstructure:"mount_options" cty:"mount_options"` + MountPartition *string `mapstructure:"mount_partition" cty:"mount_partition"` + MountPath *string `mapstructure:"mount_path" cty:"mount_path"` + PostMountCommands []string `mapstructure:"post_mount_commands" cty:"post_mount_commands"` + ChrootMounts [][]string `mapstructure:"chroot_mounts" cty:"chroot_mounts"` + CopyFiles []string `mapstructure:"copy_files" cty:"copy_files"` + TemporaryOSDiskName *string `mapstructure:"temporary_os_disk_name" cty:"temporary_os_disk_name"` + OSDiskSizeGB *int32 `mapstructure:"os_disk_size_gb" cty:"os_disk_size_gb"` + OSDiskStorageAccountType *string `mapstructure:"os_disk_storage_account_type" cty:"os_disk_storage_account_type"` + OSDiskCacheType *string `mapstructure:"os_disk_cache_type" cty:"os_disk_cache_type"` + OSDiskSkipCleanup *bool `mapstructure:"os_disk_skip_cleanup" cty:"os_disk_skip_cleanup"` + ImageResourceID *string `mapstructure:"image_resource_id" required:"true" cty:"image_resource_id"` + ImageHyperVGeneration *string `mapstructure:"image_hyperv_generation" cty:"image_hyperv_generation"` +} + +// FlatMapstructure returns a new FlatConfig. +// FlatConfig is an auto-generated flat version of Config. +// Where the contents a fields with a `mapstructure:,squash` tag are bubbled up. +func (*Config) FlatMapstructure() interface{ HCL2Spec() map[string]hcldec.Spec } { + return new(FlatConfig) +} + +// HCL2Spec returns the hcl spec of a Config. +// This spec is used by HCL to read the fields of Config. +// The decoded values from this spec will then be applied to a FlatConfig. +func (*FlatConfig) HCL2Spec() map[string]hcldec.Spec { + s := map[string]hcldec.Spec{ + "packer_build_name": &hcldec.AttrSpec{Name: "packer_build_name", Type: cty.String, Required: false}, + "packer_builder_type": &hcldec.AttrSpec{Name: "packer_builder_type", Type: cty.String, Required: false}, + "packer_debug": &hcldec.AttrSpec{Name: "packer_debug", Type: cty.Bool, Required: false}, + "packer_force": &hcldec.AttrSpec{Name: "packer_force", Type: cty.Bool, Required: false}, + "packer_on_error": &hcldec.AttrSpec{Name: "packer_on_error", Type: cty.String, Required: false}, + "packer_user_variables": &hcldec.BlockAttrsSpec{TypeName: "packer_user_variables", ElementType: cty.String, Required: false}, + "packer_sensitive_variables": &hcldec.AttrSpec{Name: "packer_sensitive_variables", Type: cty.List(cty.String), Required: false}, + "cloud_environment_name": &hcldec.AttrSpec{Name: "cloud_environment_name", Type: cty.String, Required: false}, + "client_id": &hcldec.AttrSpec{Name: "client_id", Type: cty.String, Required: false}, + "client_secret": &hcldec.AttrSpec{Name: "client_secret", Type: cty.String, Required: false}, + "client_cert_path": &hcldec.AttrSpec{Name: "client_cert_path", Type: cty.String, Required: false}, + "client_jwt": &hcldec.AttrSpec{Name: "client_jwt", Type: cty.String, Required: false}, + "object_id": &hcldec.AttrSpec{Name: "object_id", Type: cty.String, Required: false}, + "tenant_id": &hcldec.AttrSpec{Name: "tenant_id", Type: cty.String, Required: false}, + "subscription_id": &hcldec.AttrSpec{Name: "subscription_id", Type: cty.String, Required: false}, + "from_scratch": &hcldec.AttrSpec{Name: "from_scratch", Type: cty.Bool, Required: false}, + "source": &hcldec.AttrSpec{Name: "source", Type: cty.String, Required: false}, + "command_wrapper": &hcldec.AttrSpec{Name: "command_wrapper", Type: cty.String, Required: false}, + "pre_mount_commands": &hcldec.AttrSpec{Name: "pre_mount_commands", Type: cty.List(cty.String), Required: false}, + "mount_options": &hcldec.AttrSpec{Name: "mount_options", Type: cty.List(cty.String), Required: false}, + "mount_partition": &hcldec.AttrSpec{Name: "mount_partition", Type: cty.String, Required: false}, + "mount_path": &hcldec.AttrSpec{Name: "mount_path", Type: cty.String, Required: false}, + "post_mount_commands": &hcldec.AttrSpec{Name: "post_mount_commands", Type: cty.List(cty.String), Required: false}, + "chroot_mounts": &hcldec.BlockListSpec{TypeName: "chroot_mounts", Nested: &hcldec.AttrSpec{Name: "chroot_mounts", Type: cty.List(cty.String), Required: false}}, + "copy_files": &hcldec.AttrSpec{Name: "copy_files", Type: cty.List(cty.String), Required: false}, + "temporary_os_disk_name": &hcldec.AttrSpec{Name: "temporary_os_disk_name", Type: cty.String, Required: false}, + "os_disk_size_gb": &hcldec.AttrSpec{Name: "os_disk_size_gb", Type: cty.Number, Required: false}, + "os_disk_storage_account_type": &hcldec.AttrSpec{Name: "os_disk_storage_account_type", Type: cty.String, Required: false}, + "os_disk_cache_type": &hcldec.AttrSpec{Name: "os_disk_cache_type", Type: cty.String, Required: false}, + "os_disk_skip_cleanup": &hcldec.AttrSpec{Name: "os_disk_skip_cleanup", Type: cty.Bool, Required: false}, + "image_resource_id": &hcldec.AttrSpec{Name: "image_resource_id", Type: cty.String, Required: false}, + "image_hyperv_generation": &hcldec.AttrSpec{Name: "image_hyperv_generation", Type: cty.String, Required: false}, + } + return s +} diff --git a/builder/azure/chroot/diskattacher_test.go b/builder/azure/chroot/diskattacher_test.go index 3b8c55e4d..fb66fb9e3 100644 --- a/builder/azure/chroot/diskattacher_test.go +++ b/builder/azure/chroot/diskattacher_test.go @@ -2,9 +2,10 @@ package chroot import ( "context" - "github.com/Azure/go-autorest/autorest/to" "testing" + "github.com/Azure/go-autorest/autorest/to" + "github.com/Azure/azure-sdk-for-go/services/compute/mgmt/2019-03-01/compute" "github.com/hashicorp/packer/builder/azure/common/client" "github.com/stretchr/testify/assert" diff --git a/builder/azure/chroot/step_create_image.go b/builder/azure/chroot/step_create_image.go index 4ae90d2fe..2b0640a50 100644 --- a/builder/azure/chroot/step_create_image.go +++ b/builder/azure/chroot/step_create_image.go @@ -3,13 +3,14 @@ package chroot import ( "context" "fmt" + "log" + "github.com/Azure/azure-sdk-for-go/services/compute/mgmt/2019-03-01/compute" "github.com/Azure/go-autorest/autorest/azure" "github.com/Azure/go-autorest/autorest/to" "github.com/hashicorp/packer/builder/azure/common/client" "github.com/hashicorp/packer/helper/multistep" "github.com/hashicorp/packer/packer" - "log" ) var _ multistep.Step = &StepCreateImage{} diff --git a/builder/azure/common/client/platform_image.go b/builder/azure/common/client/platform_image.go index ac11ec935..9b8afb566 100644 --- a/builder/azure/common/client/platform_image.go +++ b/builder/azure/common/client/platform_image.go @@ -3,11 +3,12 @@ package client import ( "context" "fmt" + "regexp" + "strings" + "github.com/Azure/azure-sdk-for-go/profiles/latest/compute/mgmt/compute" "github.com/Azure/azure-sdk-for-go/profiles/latest/compute/mgmt/compute/computeapi" "github.com/Azure/go-autorest/autorest/to" - "regexp" - "strings" ) var platformImageRegex = regexp.MustCompile(`^[-_.a-zA-Z0-9]+:[-_.a-zA-Z0-9]+:[-_.a-zA-Z0-9]+:[-_.a-zA-Z0-9]+$`) diff --git a/builder/cloudstack/builder.go b/builder/cloudstack/builder.go index dc1dace69..ae18e93bc 100644 --- a/builder/cloudstack/builder.go +++ b/builder/cloudstack/builder.go @@ -4,6 +4,7 @@ import ( "context" "fmt" + "github.com/hashicorp/hcl/v2/hcldec" "github.com/hashicorp/packer/common" "github.com/hashicorp/packer/helper/communicator" "github.com/hashicorp/packer/helper/multistep" @@ -15,18 +16,18 @@ const BuilderId = "packer.cloudstack" // Builder represents the CloudStack builder. type Builder struct { - config *Config + config Config runner multistep.Runner ui packer.Ui } -// Prepare implements the packer.Builder interface. +func (b *Builder) ConfigSpec() hcldec.ObjectSpec { return b.config.FlatMapstructure().HCL2Spec() } + func (b *Builder) Prepare(raws ...interface{}) ([]string, error) { - config, errs := NewConfig(raws...) + errs := b.config.Prepare(raws...) if errs != nil { return nil, errs } - b.config = config return nil, nil } @@ -109,7 +110,7 @@ func (b *Builder) Run(ctx context.Context, ui packer.Ui, hook packer.Hook) (pack // Build the artifact and return it artifact := &Artifact{ client: client, - config: b.config, + config: &b.config, template: state.Get("template").(*cloudstack.CreateTemplateResponse), } diff --git a/builder/cloudstack/builder_test.go b/builder/cloudstack/builder_test.go index 6d7ab1444..158ff5d0b 100644 --- a/builder/cloudstack/builder_test.go +++ b/builder/cloudstack/builder_test.go @@ -40,10 +40,8 @@ func TestBuilder_Prepare(t *testing.T) { }, } - b := &Builder{} - for desc, tc := range cases { - _, errs := b.Prepare(tc.Config) + _, errs := (&Builder{}).Prepare(tc.Config) if tc.Err { if errs == nil { diff --git a/builder/cloudstack/config.go b/builder/cloudstack/config.go index cff9e2472..c3487afba 100644 --- a/builder/cloudstack/config.go +++ b/builder/cloudstack/config.go @@ -167,8 +167,7 @@ type Config struct { } // NewConfig parses and validates the given config. -func NewConfig(raws ...interface{}) (*Config, error) { - c := new(Config) +func (c *Config) Prepare(raws ...interface{}) error { err := config.Decode(c, &config.DecodeOpts{ Interpolate: true, InterpolateContext: &c.ctx, @@ -179,7 +178,7 @@ func NewConfig(raws ...interface{}) (*Config, error) { }, }, raws...) if err != nil { - return nil, err + return err } var errs *packer.MultiError @@ -309,8 +308,8 @@ func NewConfig(raws ...interface{}) (*Config, error) { // Check for errors and return if we have any. if errs != nil && len(errs.Errors) > 0 { - return nil, errs + return errs } - return c, nil + return nil } diff --git a/builder/cloudstack/config.hcl2spec.go b/builder/cloudstack/config.hcl2spec.go index b92d27d0c..79839316b 100644 --- a/builder/cloudstack/config.hcl2spec.go +++ b/builder/cloudstack/config.hcl2spec.go @@ -49,8 +49,8 @@ type FlatConfig struct { SSHReadWriteTimeout *string `mapstructure:"ssh_read_write_timeout" cty:"ssh_read_write_timeout"` SSHRemoteTunnels []string `mapstructure:"ssh_remote_tunnels" cty:"ssh_remote_tunnels"` SSHLocalTunnels []string `mapstructure:"ssh_local_tunnels" cty:"ssh_local_tunnels"` - SSHPublicKey []byte `cty:"ssh_public_key"` - SSHPrivateKey []byte `cty:"ssh_private_key"` + SSHPublicKey []byte `mapstructure:"ssh_public_key" cty:"ssh_public_key"` + SSHPrivateKey []byte `mapstructure:"ssh_private_key" cty:"ssh_private_key"` WinRMUser *string `mapstructure:"winrm_username" cty:"winrm_username"` WinRMPassword *string `mapstructure:"winrm_password" cty:"winrm_password"` WinRMHost *string `mapstructure:"winrm_host" cty:"winrm_host"` @@ -104,10 +104,13 @@ type FlatConfig struct { // FlatMapstructure returns a new FlatConfig. // FlatConfig is an auto-generated flat version of Config. // Where the contents a fields with a `mapstructure:,squash` tag are bubbled up. -func (*Config) FlatMapstructure() interface{} { return new(FlatConfig) } +func (*Config) FlatMapstructure() interface{ HCL2Spec() map[string]hcldec.Spec } { + return new(FlatConfig) +} -// HCL2Spec returns the hcldec.Spec of a FlatConfig. -// This spec is used by HCL to read the fields of FlatConfig. +// HCL2Spec returns the hcl spec of a Config. +// This spec is used by HCL to read the fields of Config. +// The decoded values from this spec will then be applied to a FlatConfig. func (*FlatConfig) HCL2Spec() map[string]hcldec.Spec { s := map[string]hcldec.Spec{ "packer_build_name": &hcldec.AttrSpec{Name: "packer_build_name", Type: cty.String, Required: false}, diff --git a/builder/cloudstack/config_test.go b/builder/cloudstack/config_test.go index 54fb3248a..bff6c55e0 100644 --- a/builder/cloudstack/config_test.go +++ b/builder/cloudstack/config_test.go @@ -132,7 +132,8 @@ func TestNewConfig(t *testing.T) { raw[tc.Nullify] = nil } - _, errs := NewConfig(raw) + var c Config + errs := c.Prepare(raw) if tc.Err { if errs == nil { diff --git a/builder/digitalocean/builder.go b/builder/digitalocean/builder.go index 661e2c2a4..0812f3c78 100644 --- a/builder/digitalocean/builder.go +++ b/builder/digitalocean/builder.go @@ -10,6 +10,7 @@ import ( "net/url" "github.com/digitalocean/godo" + "github.com/hashicorp/hcl/v2/hcldec" "github.com/hashicorp/packer/common" "github.com/hashicorp/packer/helper/communicator" "github.com/hashicorp/packer/helper/multistep" @@ -25,12 +26,13 @@ type Builder struct { runner multistep.Runner } +func (b *Builder) ConfigSpec() hcldec.ObjectSpec { return b.config.FlatMapstructure().HCL2Spec() } + func (b *Builder) Prepare(raws ...interface{}) ([]string, error) { - c, warnings, errs := NewConfig(raws...) + warnings, errs := b.config.Prepare(raws...) if errs != nil { return warnings, errs } - b.config = *c return nil, nil } diff --git a/builder/digitalocean/config.go b/builder/digitalocean/config.go index b087cdb9e..a2f42fdae 100644 --- a/builder/digitalocean/config.go +++ b/builder/digitalocean/config.go @@ -89,8 +89,7 @@ type Config struct { ctx interpolate.Context } -func NewConfig(raws ...interface{}) (*Config, []string, error) { - c := new(Config) +func (c *Config) Prepare(raws ...interface{}) ([]string, error) { var md mapstructure.Metadata err := config.Decode(c, &config.DecodeOpts{ @@ -104,7 +103,7 @@ func NewConfig(raws ...interface{}) (*Config, []string, error) { }, }, raws...) if err != nil { - return nil, nil, err + return nil, err } // Defaults @@ -189,9 +188,9 @@ func NewConfig(raws ...interface{}) (*Config, []string, error) { } if errs != nil && len(errs.Errors) > 0 { - return nil, nil, errs + return nil, errs } packer.LogSecretFilter.Set(c.APIToken) - return c, nil, nil + return nil, nil } diff --git a/builder/digitalocean/config.hcl2spec.go b/builder/digitalocean/config.hcl2spec.go index f39cc404d..a6f895ac7 100644 --- a/builder/digitalocean/config.hcl2spec.go +++ b/builder/digitalocean/config.hcl2spec.go @@ -46,8 +46,8 @@ type FlatConfig struct { SSHReadWriteTimeout *string `mapstructure:"ssh_read_write_timeout" cty:"ssh_read_write_timeout"` SSHRemoteTunnels []string `mapstructure:"ssh_remote_tunnels" cty:"ssh_remote_tunnels"` SSHLocalTunnels []string `mapstructure:"ssh_local_tunnels" cty:"ssh_local_tunnels"` - SSHPublicKey []byte `cty:"ssh_public_key"` - SSHPrivateKey []byte `cty:"ssh_private_key"` + SSHPublicKey []byte `mapstructure:"ssh_public_key" cty:"ssh_public_key"` + SSHPrivateKey []byte `mapstructure:"ssh_private_key" cty:"ssh_private_key"` WinRMUser *string `mapstructure:"winrm_username" cty:"winrm_username"` WinRMPassword *string `mapstructure:"winrm_password" cty:"winrm_password"` WinRMHost *string `mapstructure:"winrm_host" cty:"winrm_host"` @@ -77,10 +77,13 @@ type FlatConfig struct { // FlatMapstructure returns a new FlatConfig. // FlatConfig is an auto-generated flat version of Config. // Where the contents a fields with a `mapstructure:,squash` tag are bubbled up. -func (*Config) FlatMapstructure() interface{} { return new(FlatConfig) } +func (*Config) FlatMapstructure() interface{ HCL2Spec() map[string]hcldec.Spec } { + return new(FlatConfig) +} -// HCL2Spec returns the hcldec.Spec of a FlatConfig. -// This spec is used by HCL to read the fields of FlatConfig. +// HCL2Spec returns the hcl spec of a Config. +// This spec is used by HCL to read the fields of Config. +// The decoded values from this spec will then be applied to a FlatConfig. func (*FlatConfig) HCL2Spec() map[string]hcldec.Spec { s := map[string]hcldec.Spec{ "packer_build_name": &hcldec.AttrSpec{Name: "packer_build_name", Type: cty.String, Required: false}, diff --git a/builder/docker/builder.go b/builder/docker/builder.go index 63cb8f55d..470fa0f6e 100644 --- a/builder/docker/builder.go +++ b/builder/docker/builder.go @@ -4,6 +4,7 @@ import ( "context" "log" + "github.com/hashicorp/hcl/v2/hcldec" "github.com/hashicorp/packer/common" "github.com/hashicorp/packer/helper/communicator" "github.com/hashicorp/packer/helper/multistep" @@ -16,16 +17,17 @@ const ( ) type Builder struct { - config *Config + config Config runner multistep.Runner } +func (b *Builder) ConfigSpec() hcldec.ObjectSpec { return b.config.FlatMapstructure().HCL2Spec() } + func (b *Builder) Prepare(raws ...interface{}) ([]string, error) { - c, warnings, errs := NewConfig(raws...) + warnings, errs := b.config.Prepare(raws...) if errs != nil { return warnings, errs } - b.config = c return warnings, nil } diff --git a/builder/docker/config.go b/builder/docker/config.go index f67b48974..3785ad079 100644 --- a/builder/docker/config.go +++ b/builder/docker/config.go @@ -107,8 +107,7 @@ type Config struct { ctx interpolate.Context } -func NewConfig(raws ...interface{}) (*Config, []string, error) { - c := new(Config) +func (c *Config) Prepare(raws ...interface{}) ([]string, error) { c.FixUploadOwner = true @@ -124,7 +123,7 @@ func NewConfig(raws ...interface{}) (*Config, []string, error) { }, }, raws...) if err != nil { - return nil, nil, err + return nil, err } // Defaults @@ -191,8 +190,8 @@ func NewConfig(raws ...interface{}) (*Config, []string, error) { } if errs != nil && len(errs.Errors) > 0 { - return nil, nil, errs + return nil, errs } - return c, nil, nil + return nil, nil } diff --git a/builder/docker/config.hcl2spec.go b/builder/docker/config.hcl2spec.go index fe278055e..9183f7770 100644 --- a/builder/docker/config.hcl2spec.go +++ b/builder/docker/config.hcl2spec.go @@ -46,8 +46,8 @@ type FlatConfig struct { SSHReadWriteTimeout *string `mapstructure:"ssh_read_write_timeout" cty:"ssh_read_write_timeout"` SSHRemoteTunnels []string `mapstructure:"ssh_remote_tunnels" cty:"ssh_remote_tunnels"` SSHLocalTunnels []string `mapstructure:"ssh_local_tunnels" cty:"ssh_local_tunnels"` - SSHPublicKey []byte `cty:"ssh_public_key"` - SSHPrivateKey []byte `cty:"ssh_private_key"` + SSHPublicKey []byte `mapstructure:"ssh_public_key" cty:"ssh_public_key"` + SSHPrivateKey []byte `mapstructure:"ssh_private_key" cty:"ssh_private_key"` WinRMUser *string `mapstructure:"winrm_username" cty:"winrm_username"` WinRMPassword *string `mapstructure:"winrm_password" cty:"winrm_password"` WinRMHost *string `mapstructure:"winrm_host" cty:"winrm_host"` @@ -86,10 +86,13 @@ type FlatConfig struct { // FlatMapstructure returns a new FlatConfig. // FlatConfig is an auto-generated flat version of Config. // Where the contents a fields with a `mapstructure:,squash` tag are bubbled up. -func (*Config) FlatMapstructure() interface{} { return new(FlatConfig) } +func (*Config) FlatMapstructure() interface{ HCL2Spec() map[string]hcldec.Spec } { + return new(FlatConfig) +} -// HCL2Spec returns the hcldec.Spec of a FlatConfig. -// This spec is used by HCL to read the fields of FlatConfig. +// HCL2Spec returns the hcl spec of a Config. +// This spec is used by HCL to read the fields of Config. +// The decoded values from this spec will then be applied to a FlatConfig. func (*FlatConfig) HCL2Spec() map[string]hcldec.Spec { s := map[string]hcldec.Spec{ "packer_build_name": &hcldec.AttrSpec{Name: "packer_build_name", Type: cty.String, Required: false}, diff --git a/builder/docker/config_test.go b/builder/docker/config_test.go index 442c6ffe2..cf66e1a7d 100644 --- a/builder/docker/config_test.go +++ b/builder/docker/config_test.go @@ -14,7 +14,8 @@ func testConfig() map[string]interface{} { } func testConfigStruct(t *testing.T) *Config { - c, warns, errs := NewConfig(testConfig()) + var c Config + warns, errs := c.Prepare(testConfig()) if len(warns) > 0 { t.Fatalf("bad: %#v", len(warns)) } @@ -22,7 +23,7 @@ func testConfigStruct(t *testing.T) *Config { t.Fatalf("bad: %#v", errs) } - return c + return &c } func testConfigErr(t *testing.T, warns []string, err error) { @@ -55,17 +56,18 @@ func TestConfigPrepare_exportPath(t *testing.T) { // No export path. This is invalid. Previously this would not error during // validation and as a result the failure would happen at build time. delete(raw, "export_path") - _, warns, errs := NewConfig(raw) + var c Config + warns, errs := c.Prepare(raw) testConfigErr(t, warns, errs) // Good export path raw["export_path"] = "good" - _, warns, errs = NewConfig(raw) + warns, errs = c.Prepare(raw) testConfigOk(t, warns, errs) // Bad export path (directory) raw["export_path"] = td - _, warns, errs = NewConfig(raw) + warns, errs = c.Prepare(raw) testConfigErr(t, warns, errs) } @@ -74,17 +76,17 @@ func TestConfigPrepare_exportPathAndCommit(t *testing.T) { // Export but no commit (explicit default) raw["commit"] = false - _, warns, errs := NewConfig(raw) + warns, errs := (&Config{}).Prepare(raw) testConfigOk(t, warns, errs) // Commit AND export specified (invalid) raw["commit"] = true - _, warns, errs = NewConfig(raw) + warns, errs = (&Config{}).Prepare(raw) testConfigErr(t, warns, errs) // Commit but no export delete(raw, "export_path") - _, warns, errs = NewConfig(raw) + warns, errs = (&Config{}).Prepare(raw) testConfigOk(t, warns, errs) } @@ -93,18 +95,18 @@ func TestConfigPrepare_exportDiscard(t *testing.T) { // Export but no discard (explicit default) raw["discard"] = false - _, warns, errs := NewConfig(raw) + warns, errs := (&Config{}).Prepare(raw) testConfigOk(t, warns, errs) // Discard AND export (invalid) raw["discard"] = true - _, warns, errs = NewConfig(raw) + warns, errs = (&Config{}).Prepare(raw) testConfigErr(t, warns, errs) // Discard but no export raw["discard"] = true delete(raw, "export_path") - _, warns, errs = NewConfig(raw) + warns, errs = (&Config{}).Prepare(raw) testConfigOk(t, warns, errs) } @@ -113,12 +115,13 @@ func TestConfigPrepare_image(t *testing.T) { // No image delete(raw, "image") - _, warns, errs := NewConfig(raw) + var c Config + warns, errs := c.Prepare(raw) testConfigErr(t, warns, errs) // Good image raw["image"] = "path" - _, warns, errs = NewConfig(raw) + warns, errs = c.Prepare(raw) testConfigOk(t, warns, errs) } @@ -127,7 +130,8 @@ func TestConfigPrepare_pull(t *testing.T) { // No pull set delete(raw, "pull") - c, warns, errs := NewConfig(raw) + var c Config + warns, errs := c.Prepare(raw) testConfigOk(t, warns, errs) if !c.Pull { t.Fatal("should pull by default") @@ -135,7 +139,7 @@ func TestConfigPrepare_pull(t *testing.T) { // Pull set raw["pull"] = false - c, warns, errs = NewConfig(raw) + warns, errs = c.Prepare(raw) testConfigOk(t, warns, errs) if c.Pull { t.Fatal("should not pull") diff --git a/builder/file/builder.go b/builder/file/builder.go index 0defa6049..4178e6f1c 100644 --- a/builder/file/builder.go +++ b/builder/file/builder.go @@ -12,6 +12,7 @@ import ( "io/ioutil" "os" + "github.com/hashicorp/hcl/v2/hcldec" "github.com/hashicorp/packer/helper/multistep" "github.com/hashicorp/packer/packer" ) @@ -19,16 +20,17 @@ import ( const BuilderId = "packer.file" type Builder struct { - config *Config + config Config runner multistep.Runner } +func (b *Builder) ConfigSpec() hcldec.ObjectSpec { return b.config.FlatMapstructure().HCL2Spec() } + func (b *Builder) Prepare(raws ...interface{}) ([]string, error) { - c, warnings, errs := NewConfig(raws...) + warnings, errs := b.config.Prepare(raws...) if errs != nil { return warnings, errs } - b.config = c return warnings, nil } diff --git a/builder/file/config.go b/builder/file/config.go index fea112684..730c497f5 100644 --- a/builder/file/config.go +++ b/builder/file/config.go @@ -22,8 +22,7 @@ type Config struct { Content string `mapstructure:"content"` } -func NewConfig(raws ...interface{}) (*Config, []string, error) { - c := new(Config) +func (c *Config) Prepare(raws ...interface{}) ([]string, error) { warnings := []string{} err := config.Decode(c, &config.DecodeOpts{ @@ -33,7 +32,7 @@ func NewConfig(raws ...interface{}) (*Config, []string, error) { }, }, raws...) if err != nil { - return nil, warnings, err + return warnings, err } var errs *packer.MultiError @@ -51,8 +50,8 @@ func NewConfig(raws ...interface{}) (*Config, []string, error) { } if errs != nil && len(errs.Errors) > 0 { - return nil, warnings, errs + return warnings, errs } - return c, warnings, nil + return warnings, nil } diff --git a/builder/file/config.hcl2spec.go b/builder/file/config.hcl2spec.go index f47193d7d..627b92840 100644 --- a/builder/file/config.hcl2spec.go +++ b/builder/file/config.hcl2spec.go @@ -24,10 +24,13 @@ type FlatConfig struct { // FlatMapstructure returns a new FlatConfig. // FlatConfig is an auto-generated flat version of Config. // Where the contents a fields with a `mapstructure:,squash` tag are bubbled up. -func (*Config) FlatMapstructure() interface{} { return new(FlatConfig) } +func (*Config) FlatMapstructure() interface{ HCL2Spec() map[string]hcldec.Spec } { + return new(FlatConfig) +} -// HCL2Spec returns the hcldec.Spec of a FlatConfig. -// This spec is used by HCL to read the fields of FlatConfig. +// HCL2Spec returns the hcl spec of a Config. +// This spec is used by HCL to read the fields of Config. +// The decoded values from this spec will then be applied to a FlatConfig. func (*FlatConfig) HCL2Spec() map[string]hcldec.Spec { s := map[string]hcldec.Spec{ "packer_build_name": &hcldec.AttrSpec{Name: "packer_build_name", Type: cty.String, Required: false}, diff --git a/builder/file/config_test.go b/builder/file/config_test.go index 9d8f346fc..a701b9609 100644 --- a/builder/file/config_test.go +++ b/builder/file/config_test.go @@ -16,7 +16,8 @@ func testConfig() map[string]interface{} { func TestContentSourceConflict(t *testing.T) { raw := testConfig() - _, _, errs := NewConfig(raw) + var c Config + _, errs := c.Prepare(raw) if !strings.Contains(errs.Error(), ErrContentSourceConflict.Error()) { t.Errorf("Expected config error: %s", ErrContentSourceConflict.Error()) } @@ -26,7 +27,8 @@ func TestNoFilename(t *testing.T) { raw := testConfig() delete(raw, "filename") - _, _, errs := NewConfig(raw) + var c Config + _, errs := c.Prepare(raw) if errs == nil { t.Errorf("Expected config error: %s", ErrTargetRequired.Error()) } @@ -37,7 +39,8 @@ func TestNoContent(t *testing.T) { delete(raw, "content") delete(raw, "source") - _, warns, _ := NewConfig(raw) + var c Config + warns, _ := c.Prepare(raw) if len(warns) == 0 { t.Error("Expected config warning without any content") diff --git a/builder/googlecompute/builder.go b/builder/googlecompute/builder.go index 2a30d39c5..163cc9d2c 100644 --- a/builder/googlecompute/builder.go +++ b/builder/googlecompute/builder.go @@ -7,6 +7,7 @@ import ( "fmt" "log" + "github.com/hashicorp/hcl/v2/hcldec" "github.com/hashicorp/packer/common" "github.com/hashicorp/packer/helper/communicator" "github.com/hashicorp/packer/helper/multistep" @@ -18,17 +19,17 @@ const BuilderId = "packer.googlecompute" // Builder represents a Packer Builder. type Builder struct { - config *Config + config Config runner multistep.Runner } -// Prepare processes the build configuration parameters. +func (b *Builder) ConfigSpec() hcldec.ObjectSpec { return b.config.FlatMapstructure().HCL2Spec() } + func (b *Builder) Prepare(raws ...interface{}) ([]string, error) { - c, warnings, errs := NewConfig(raws...) + warnings, errs := b.config.Prepare(raws...) if errs != nil { return warnings, errs } - b.config = c return warnings, nil } @@ -43,7 +44,7 @@ func (b *Builder) Run(ctx context.Context, ui packer.Ui, hook packer.Hook) (pack // Set up the state. state := new(multistep.BasicStateBag) - state.Put("config", b.config) + state.Put("config", &b.config) state.Put("driver", driver) state.Put("hook", hook) state.Put("ui", ui) @@ -97,7 +98,7 @@ func (b *Builder) Run(ctx context.Context, ui packer.Ui, hook packer.Hook) (pack artifact := &Artifact{ image: state.Get("image").(*Image), driver: driver, - config: b.config, + config: &b.config, } return artifact, nil } diff --git a/builder/googlecompute/config.go b/builder/googlecompute/config.go index 7aeeb4cb5..0b6c3c267 100644 --- a/builder/googlecompute/config.go +++ b/builder/googlecompute/config.go @@ -187,8 +187,7 @@ type Config struct { ctx interpolate.Context } -func NewConfig(raws ...interface{}) (*Config, []string, error) { - c := new(Config) +func (c *Config) Prepare(raws ...interface{}) ([]string, error) { c.ctx.Funcs = TemplateFuncs err := config.Decode(c, &config.DecodeOpts{ Interpolate: true, @@ -200,7 +199,7 @@ func NewConfig(raws ...interface{}) (*Config, []string, error) { }, }, raws...) if err != nil { - return nil, nil, err + return nil, err } var errs *packer.MultiError @@ -372,10 +371,10 @@ func NewConfig(raws ...interface{}) (*Config, []string, error) { // Check for any errors. if errs != nil && len(errs.Errors) > 0 { - return nil, nil, errs + return nil, errs } - return c, nil, nil + return nil, nil } type CustomerEncryptionKey struct { diff --git a/builder/googlecompute/config.hcl2spec.go b/builder/googlecompute/config.hcl2spec.go index cfa169a02..acc64f1c9 100644 --- a/builder/googlecompute/config.hcl2spec.go +++ b/builder/googlecompute/config.hcl2spec.go @@ -46,8 +46,8 @@ type FlatConfig struct { SSHReadWriteTimeout *string `mapstructure:"ssh_read_write_timeout" cty:"ssh_read_write_timeout"` SSHRemoteTunnels []string `mapstructure:"ssh_remote_tunnels" cty:"ssh_remote_tunnels"` SSHLocalTunnels []string `mapstructure:"ssh_local_tunnels" cty:"ssh_local_tunnels"` - SSHPublicKey []byte `cty:"ssh_public_key"` - SSHPrivateKey []byte `cty:"ssh_private_key"` + SSHPublicKey []byte `mapstructure:"ssh_public_key" cty:"ssh_public_key"` + SSHPrivateKey []byte `mapstructure:"ssh_private_key" cty:"ssh_private_key"` WinRMUser *string `mapstructure:"winrm_username" cty:"winrm_username"` WinRMPassword *string `mapstructure:"winrm_password" cty:"winrm_password"` WinRMHost *string `mapstructure:"winrm_host" cty:"winrm_host"` @@ -100,10 +100,13 @@ type FlatConfig struct { // FlatMapstructure returns a new FlatConfig. // FlatConfig is an auto-generated flat version of Config. // Where the contents a fields with a `mapstructure:,squash` tag are bubbled up. -func (*Config) FlatMapstructure() interface{} { return new(FlatConfig) } +func (*Config) FlatMapstructure() interface{ HCL2Spec() map[string]hcldec.Spec } { + return new(FlatConfig) +} -// HCL2Spec returns the hcldec.Spec of a FlatConfig. -// This spec is used by HCL to read the fields of FlatConfig. +// HCL2Spec returns the hcl spec of a Config. +// This spec is used by HCL to read the fields of Config. +// The decoded values from this spec will then be applied to a FlatConfig. func (*FlatConfig) HCL2Spec() map[string]hcldec.Spec { s := map[string]hcldec.Spec{ "packer_build_name": &hcldec.AttrSpec{Name: "packer_build_name", Type: cty.String, Required: false}, @@ -206,10 +209,13 @@ type FlatCustomerEncryptionKey struct { // FlatMapstructure returns a new FlatCustomerEncryptionKey. // FlatCustomerEncryptionKey is an auto-generated flat version of CustomerEncryptionKey. // Where the contents a fields with a `mapstructure:,squash` tag are bubbled up. -func (*CustomerEncryptionKey) FlatMapstructure() interface{} { return new(FlatCustomerEncryptionKey) } +func (*CustomerEncryptionKey) FlatMapstructure() interface{ HCL2Spec() map[string]hcldec.Spec } { + return new(FlatCustomerEncryptionKey) +} -// HCL2Spec returns the hcldec.Spec of a FlatCustomerEncryptionKey. -// This spec is used by HCL to read the fields of FlatCustomerEncryptionKey. +// HCL2Spec returns the hcl spec of a CustomerEncryptionKey. +// This spec is used by HCL to read the fields of CustomerEncryptionKey. +// The decoded values from this spec will then be applied to a FlatCustomerEncryptionKey. func (*FlatCustomerEncryptionKey) HCL2Spec() map[string]hcldec.Spec { s := map[string]hcldec.Spec{ "kms_key_name": &hcldec.AttrSpec{Name: "kms_key_name", Type: cty.String, Required: false}, diff --git a/builder/googlecompute/config_test.go b/builder/googlecompute/config_test.go index b2f05e139..7753e1d8a 100644 --- a/builder/googlecompute/config_test.go +++ b/builder/googlecompute/config_test.go @@ -242,7 +242,8 @@ func TestConfigPrepare(t *testing.T) { raw[tc.Key] = tc.Value } - _, warns, errs := NewConfig(raw) + var c Config + warns, errs := c.Prepare(raw) if tc.Err { testConfigErr(t, warns, errs, tc.Key) @@ -302,7 +303,8 @@ func TestConfigPrepareAccelerator(t *testing.T) { } } - _, warns, errs := NewConfig(raw) + var c Config + warns, errs := c.Prepare(raw) if tc.Err { testConfigErr(t, warns, errs, strings.TrimRight(errStr, ", ")) @@ -352,7 +354,8 @@ func TestConfigPrepareServiceAccount(t *testing.T) { } } - _, warns, errs := NewConfig(raw) + var c Config + warns, errs := c.Prepare(raw) if tc.Err { testConfigErr(t, warns, errs, strings.TrimRight(errStr, ", ")) @@ -371,7 +374,8 @@ func TestConfigPrepareStartupScriptFile(t *testing.T) { "zone": "us-central1-a", } - _, _, errs := NewConfig(config) + var c Config + _, errs := c.Prepare(config) if errs == nil || !strings.Contains(errs.Error(), "startup_script_file") { t.Fatalf("should error: startup_script_file") @@ -398,10 +402,11 @@ func TestConfigDefaults(t *testing.T) { raw, tempfile := testConfig(t) defer os.Remove(tempfile) - c, warns, errs := NewConfig(raw) + var c Config + warns, errs := c.Prepare(raw) testConfigOk(t, warns, errs) - actual := tc.Read(c) + actual := tc.Read(&c) if actual != tc.Value { t.Fatalf("bad: %#v", actual) } @@ -412,7 +417,8 @@ func TestImageName(t *testing.T) { raw, tempfile := testConfig(t) defer os.Remove(tempfile) - c, _, _ := NewConfig(raw) + var c Config + c.Prepare(raw) if !strings.HasPrefix(c.ImageName, "packer-") { t.Fatalf("ImageName should have 'packer-' prefix, found %s", c.ImageName) } @@ -425,7 +431,8 @@ func TestRegion(t *testing.T) { raw, tempfile := testConfig(t) defer os.Remove(tempfile) - c, _, _ := NewConfig(raw) + var c Config + c.Prepare(raw) if c.Region != "us-east1" { t.Fatalf("Region should be 'us-east1' given Zone of 'us-east1-a', but is %s", c.Region) } @@ -460,7 +467,8 @@ func testConfigStruct(t *testing.T) *Config { raw, tempfile := testConfig(t) defer os.Remove(tempfile) - c, warns, errs := NewConfig(raw) + var c Config + warns, errs := c.Prepare(raw) if len(warns) > 0 { t.Fatalf("bad: %#v", len(warns)) } @@ -468,7 +476,7 @@ func testConfigStruct(t *testing.T) *Config { t.Fatalf("bad: %#v", errs) } - return c + return &c } func testConfigErr(t *testing.T, warns []string, err error, extra string) { diff --git a/builder/hcloud/builder.go b/builder/hcloud/builder.go index 92f9f474b..efd29adc9 100644 --- a/builder/hcloud/builder.go +++ b/builder/hcloud/builder.go @@ -4,6 +4,7 @@ import ( "context" "fmt" + "github.com/hashicorp/hcl/v2/hcldec" "github.com/hashicorp/packer/common" "github.com/hashicorp/packer/helper/communicator" "github.com/hashicorp/packer/helper/multistep" @@ -22,12 +23,13 @@ type Builder struct { var pluginVersion = "1.0.0" +func (b *Builder) ConfigSpec() hcldec.ObjectSpec { return b.config.FlatMapstructure().HCL2Spec() } + func (b *Builder) Prepare(raws ...interface{}) ([]string, error) { - config, warnings, errs := NewConfig(raws...) + warnings, errs := b.config.Prepare(raws...) if errs != nil { return warnings, errs } - b.config = *config return nil, nil } diff --git a/builder/hcloud/config.go b/builder/hcloud/config.go index 6e8b56ba6..878c7cc1f 100644 --- a/builder/hcloud/config.go +++ b/builder/hcloud/config.go @@ -50,9 +50,7 @@ type imageFilter struct { MostRecent bool `mapstructure:"most_recent"` } -func NewConfig(raws ...interface{}) (*Config, []string, error) { - c := new(Config) - +func (c *Config) Prepare(raws ...interface{}) ([]string, error) { var md mapstructure.Metadata err := config.Decode(c, &config.DecodeOpts{ Metadata: &md, @@ -65,7 +63,7 @@ func NewConfig(raws ...interface{}) (*Config, []string, error) { }, }, raws...) if err != nil { - return nil, nil, err + return nil, err } // Defaults @@ -142,11 +140,11 @@ func NewConfig(raws ...interface{}) (*Config, []string, error) { } if errs != nil && len(errs.Errors) > 0 { - return nil, nil, errs + return nil, errs } packer.LogSecretFilter.Set(c.HCloudToken) - return c, nil, nil + return nil, nil } func getServerIP(state multistep.StateBag) (string, error) { diff --git a/builder/hcloud/config.hcl2spec.go b/builder/hcloud/config.hcl2spec.go index 36ea266ff..cf1ff3186 100644 --- a/builder/hcloud/config.hcl2spec.go +++ b/builder/hcloud/config.hcl2spec.go @@ -46,8 +46,8 @@ type FlatConfig struct { SSHReadWriteTimeout *string `mapstructure:"ssh_read_write_timeout" cty:"ssh_read_write_timeout"` SSHRemoteTunnels []string `mapstructure:"ssh_remote_tunnels" cty:"ssh_remote_tunnels"` SSHLocalTunnels []string `mapstructure:"ssh_local_tunnels" cty:"ssh_local_tunnels"` - SSHPublicKey []byte `cty:"ssh_public_key"` - SSHPrivateKey []byte `cty:"ssh_private_key"` + SSHPublicKey []byte `mapstructure:"ssh_public_key" cty:"ssh_public_key"` + SSHPrivateKey []byte `mapstructure:"ssh_private_key" cty:"ssh_private_key"` WinRMUser *string `mapstructure:"winrm_username" cty:"winrm_username"` WinRMPassword *string `mapstructure:"winrm_password" cty:"winrm_password"` WinRMHost *string `mapstructure:"winrm_host" cty:"winrm_host"` @@ -75,10 +75,13 @@ type FlatConfig struct { // FlatMapstructure returns a new FlatConfig. // FlatConfig is an auto-generated flat version of Config. // Where the contents a fields with a `mapstructure:,squash` tag are bubbled up. -func (*Config) FlatMapstructure() interface{} { return new(FlatConfig) } +func (*Config) FlatMapstructure() interface{ HCL2Spec() map[string]hcldec.Spec } { + return new(FlatConfig) +} -// HCL2Spec returns the hcldec.Spec of a FlatConfig. -// This spec is used by HCL to read the fields of FlatConfig. +// HCL2Spec returns the hcl spec of a Config. +// This spec is used by HCL to read the fields of Config. +// The decoded values from this spec will then be applied to a FlatConfig. func (*FlatConfig) HCL2Spec() map[string]hcldec.Spec { s := map[string]hcldec.Spec{ "packer_build_name": &hcldec.AttrSpec{Name: "packer_build_name", Type: cty.String, Required: false}, @@ -156,10 +159,13 @@ type FlatimageFilter struct { // FlatMapstructure returns a new FlatimageFilter. // FlatimageFilter is an auto-generated flat version of imageFilter. // Where the contents a fields with a `mapstructure:,squash` tag are bubbled up. -func (*imageFilter) FlatMapstructure() interface{} { return new(FlatimageFilter) } +func (*imageFilter) FlatMapstructure() interface{ HCL2Spec() map[string]hcldec.Spec } { + return new(FlatimageFilter) +} -// HCL2Spec returns the hcldec.Spec of a FlatimageFilter. -// This spec is used by HCL to read the fields of FlatimageFilter. +// HCL2Spec returns the hcl spec of a imageFilter. +// This spec is used by HCL to read the fields of imageFilter. +// The decoded values from this spec will then be applied to a FlatimageFilter. func (*FlatimageFilter) HCL2Spec() map[string]hcldec.Spec { s := map[string]hcldec.Spec{ "with_selector": &hcldec.AttrSpec{Name: "with_selector", Type: cty.List(cty.String), Required: false}, diff --git a/builder/hyperone/builder.go b/builder/hyperone/builder.go index d9e85e5f0..93c0b84c4 100644 --- a/builder/hyperone/builder.go +++ b/builder/hyperone/builder.go @@ -4,6 +4,7 @@ import ( "context" "fmt" + "github.com/hashicorp/hcl/v2/hcldec" "github.com/hashicorp/packer/common" "github.com/hashicorp/packer/helper/communicator" "github.com/hashicorp/packer/helper/multistep" @@ -20,14 +21,14 @@ type Builder struct { client *openapi.APIClient } +func (b *Builder) ConfigSpec() hcldec.ObjectSpec { return b.config.FlatMapstructure().HCL2Spec() } + func (b *Builder) Prepare(raws ...interface{}) ([]string, error) { - config, warnings, errs := NewConfig(raws...) + warnings, errs := b.config.Prepare(raws...) if errs != nil { return warnings, errs } - b.config = *config - cfg := openapi.NewConfiguration() cfg.AddDefaultHeader("x-auth-token", b.config.Token) if b.config.Project != "" { diff --git a/builder/hyperone/config.go b/builder/hyperone/config.go index 6c5d5eefb..603651bfc 100644 --- a/builder/hyperone/config.go +++ b/builder/hyperone/config.go @@ -114,8 +114,7 @@ type Config struct { ctx interpolate.Context } -func NewConfig(raws ...interface{}) (*Config, []string, error) { - c := &Config{} +func (c *Config) Prepare(raws ...interface{}) ([]string, error) { var md mapstructure.Metadata err := config.Decode(c, &config.DecodeOpts{ @@ -133,12 +132,12 @@ func NewConfig(raws ...interface{}) (*Config, []string, error) { }, }, raws...) if err != nil { - return nil, nil, err + return nil, err } cliConfig, err := loadCLIConfig() if err != nil { - return nil, nil, err + return nil, err } // Defaults @@ -165,7 +164,7 @@ func NewConfig(raws ...interface{}) (*Config, []string, error) { if c.TokenLogin != "" && c.APIURL == "" { c.Token, err = fetchTokenBySSH(c.TokenLogin) if err != nil { - return nil, nil, err + return nil, err } } } @@ -181,7 +180,7 @@ func NewConfig(raws ...interface{}) (*Config, []string, error) { if c.ImageName == "" { name, err := interpolate.Render("packer-{{timestamp}}", nil) if err != nil { - return nil, nil, err + return nil, err } c.ImageName = name } @@ -217,7 +216,7 @@ func NewConfig(raws ...interface{}) (*Config, []string, error) { if c.ChrootMountPath == "" { path, err := interpolate.Render("/mnt/packer-hyperone-volumes/{{timestamp}}", nil) if err != nil { - return nil, nil, err + return nil, err } c.ChrootMountPath = path } @@ -281,12 +280,12 @@ func NewConfig(raws ...interface{}) (*Config, []string, error) { } if errs != nil && len(errs.Errors) > 0 { - return nil, nil, errs + return nil, errs } packer.LogSecretFilter.Set(c.Token) - return c, nil, nil + return nil, nil } type cliConfig struct { diff --git a/builder/hyperone/config.hcl2spec.go b/builder/hyperone/config.hcl2spec.go index 4978d71b8..1fa450196 100644 --- a/builder/hyperone/config.hcl2spec.go +++ b/builder/hyperone/config.hcl2spec.go @@ -46,8 +46,8 @@ type FlatConfig struct { SSHReadWriteTimeout *string `mapstructure:"ssh_read_write_timeout" cty:"ssh_read_write_timeout"` SSHRemoteTunnels []string `mapstructure:"ssh_remote_tunnels" cty:"ssh_remote_tunnels"` SSHLocalTunnels []string `mapstructure:"ssh_local_tunnels" cty:"ssh_local_tunnels"` - SSHPublicKey []byte `cty:"ssh_public_key"` - SSHPrivateKey []byte `cty:"ssh_private_key"` + SSHPublicKey []byte `mapstructure:"ssh_public_key" cty:"ssh_public_key"` + SSHPrivateKey []byte `mapstructure:"ssh_private_key" cty:"ssh_private_key"` WinRMUser *string `mapstructure:"winrm_username" cty:"winrm_username"` WinRMPassword *string `mapstructure:"winrm_password" cty:"winrm_password"` WinRMHost *string `mapstructure:"winrm_host" cty:"winrm_host"` @@ -94,10 +94,13 @@ type FlatConfig struct { // FlatMapstructure returns a new FlatConfig. // FlatConfig is an auto-generated flat version of Config. // Where the contents a fields with a `mapstructure:,squash` tag are bubbled up. -func (*Config) FlatMapstructure() interface{} { return new(FlatConfig) } +func (*Config) FlatMapstructure() interface{ HCL2Spec() map[string]hcldec.Spec } { + return new(FlatConfig) +} -// HCL2Spec returns the hcldec.Spec of a FlatConfig. -// This spec is used by HCL to read the fields of FlatConfig. +// HCL2Spec returns the hcl spec of a Config. +// This spec is used by HCL to read the fields of Config. +// The decoded values from this spec will then be applied to a FlatConfig. func (*FlatConfig) HCL2Spec() map[string]hcldec.Spec { s := map[string]hcldec.Spec{ "packer_build_name": &hcldec.AttrSpec{Name: "packer_build_name", Type: cty.String, Required: false}, diff --git a/builder/hyperv/common/output_config.hcl2spec.go b/builder/hyperv/common/output_config.hcl2spec.go index c3794c70d..683b8f314 100644 --- a/builder/hyperv/common/output_config.hcl2spec.go +++ b/builder/hyperv/common/output_config.hcl2spec.go @@ -15,10 +15,13 @@ type FlatOutputConfig struct { // FlatMapstructure returns a new FlatOutputConfig. // FlatOutputConfig is an auto-generated flat version of OutputConfig. // Where the contents a fields with a `mapstructure:,squash` tag are bubbled up. -func (*OutputConfig) FlatMapstructure() interface{} { return new(FlatOutputConfig) } +func (*OutputConfig) FlatMapstructure() interface{ HCL2Spec() map[string]hcldec.Spec } { + return new(FlatOutputConfig) +} -// HCL2Spec returns the hcldec.Spec of a FlatOutputConfig. -// This spec is used by HCL to read the fields of FlatOutputConfig. +// HCL2Spec returns the hcl spec of a OutputConfig. +// This spec is used by HCL to read the fields of OutputConfig. +// The decoded values from this spec will then be applied to a FlatOutputConfig. func (*FlatOutputConfig) HCL2Spec() map[string]hcldec.Spec { s := map[string]hcldec.Spec{ "output_directory": &hcldec.AttrSpec{Name: "output_directory", Type: cty.String, Required: false}, diff --git a/builder/hyperv/iso/builder.go b/builder/hyperv/iso/builder.go index 1b40fac84..193cce388 100644 --- a/builder/hyperv/iso/builder.go +++ b/builder/hyperv/iso/builder.go @@ -11,6 +11,7 @@ import ( "path/filepath" "strings" + "github.com/hashicorp/hcl/v2/hcldec" hypervcommon "github.com/hashicorp/packer/builder/hyperv/common" "github.com/hashicorp/packer/common" "github.com/hashicorp/packer/common/bootcommand" @@ -84,7 +85,8 @@ type Config struct { ctx interpolate.Context } -// Prepare processes the build configuration parameters. +func (b *Builder) ConfigSpec() hcldec.ObjectSpec { return b.config.FlatMapstructure().HCL2Spec() } + func (b *Builder) Prepare(raws ...interface{}) ([]string, error) { err := config.Decode(&b.config, &config.DecodeOpts{ Interpolate: true, diff --git a/builder/hyperv/iso/builder.hcl2spec.go b/builder/hyperv/iso/builder.hcl2spec.go index 09f3fa0d3..d06b4b3af 100644 --- a/builder/hyperv/iso/builder.hcl2spec.go +++ b/builder/hyperv/iso/builder.hcl2spec.go @@ -60,8 +60,8 @@ type FlatConfig struct { SSHReadWriteTimeout *string `mapstructure:"ssh_read_write_timeout" cty:"ssh_read_write_timeout"` SSHRemoteTunnels []string `mapstructure:"ssh_remote_tunnels" cty:"ssh_remote_tunnels"` SSHLocalTunnels []string `mapstructure:"ssh_local_tunnels" cty:"ssh_local_tunnels"` - SSHPublicKey []byte `cty:"ssh_public_key"` - SSHPrivateKey []byte `cty:"ssh_private_key"` + SSHPublicKey []byte `mapstructure:"ssh_public_key" cty:"ssh_public_key"` + SSHPrivateKey []byte `mapstructure:"ssh_private_key" cty:"ssh_private_key"` WinRMUser *string `mapstructure:"winrm_username" cty:"winrm_username"` WinRMPassword *string `mapstructure:"winrm_password" cty:"winrm_password"` WinRMHost *string `mapstructure:"winrm_host" cty:"winrm_host"` @@ -108,10 +108,13 @@ type FlatConfig struct { // FlatMapstructure returns a new FlatConfig. // FlatConfig is an auto-generated flat version of Config. // Where the contents a fields with a `mapstructure:,squash` tag are bubbled up. -func (*Config) FlatMapstructure() interface{} { return new(FlatConfig) } +func (*Config) FlatMapstructure() interface{ HCL2Spec() map[string]hcldec.Spec } { + return new(FlatConfig) +} -// HCL2Spec returns the hcldec.Spec of a FlatConfig. -// This spec is used by HCL to read the fields of FlatConfig. +// HCL2Spec returns the hcl spec of a Config. +// This spec is used by HCL to read the fields of Config. +// The decoded values from this spec will then be applied to a FlatConfig. func (*FlatConfig) HCL2Spec() map[string]hcldec.Spec { s := map[string]hcldec.Spec{ "packer_build_name": &hcldec.AttrSpec{Name: "packer_build_name", Type: cty.String, Required: false}, diff --git a/builder/hyperv/vmcx/builder.go b/builder/hyperv/vmcx/builder.go index af93007e5..3ab84a6f2 100644 --- a/builder/hyperv/vmcx/builder.go +++ b/builder/hyperv/vmcx/builder.go @@ -10,6 +10,7 @@ import ( "os" "strings" + "github.com/hashicorp/hcl/v2/hcldec" hypervcommon "github.com/hashicorp/packer/builder/hyperv/common" "github.com/hashicorp/packer/common" "github.com/hashicorp/packer/common/bootcommand" @@ -77,7 +78,8 @@ type Config struct { ctx interpolate.Context } -// Prepare processes the build configuration parameters. +func (b *Builder) ConfigSpec() hcldec.ObjectSpec { return b.config.FlatMapstructure().HCL2Spec() } + func (b *Builder) Prepare(raws ...interface{}) ([]string, error) { err := config.Decode(&b.config, &config.DecodeOpts{ Interpolate: true, diff --git a/builder/hyperv/vmcx/builder.hcl2spec.go b/builder/hyperv/vmcx/builder.hcl2spec.go index 1ba1e83ee..628436bf0 100644 --- a/builder/hyperv/vmcx/builder.hcl2spec.go +++ b/builder/hyperv/vmcx/builder.hcl2spec.go @@ -60,8 +60,8 @@ type FlatConfig struct { SSHReadWriteTimeout *string `mapstructure:"ssh_read_write_timeout" cty:"ssh_read_write_timeout"` SSHRemoteTunnels []string `mapstructure:"ssh_remote_tunnels" cty:"ssh_remote_tunnels"` SSHLocalTunnels []string `mapstructure:"ssh_local_tunnels" cty:"ssh_local_tunnels"` - SSHPublicKey []byte `cty:"ssh_public_key"` - SSHPrivateKey []byte `cty:"ssh_private_key"` + SSHPublicKey []byte `mapstructure:"ssh_public_key" cty:"ssh_public_key"` + SSHPrivateKey []byte `mapstructure:"ssh_private_key" cty:"ssh_private_key"` WinRMUser *string `mapstructure:"winrm_username" cty:"winrm_username"` WinRMPassword *string `mapstructure:"winrm_password" cty:"winrm_password"` WinRMHost *string `mapstructure:"winrm_host" cty:"winrm_host"` @@ -110,10 +110,13 @@ type FlatConfig struct { // FlatMapstructure returns a new FlatConfig. // FlatConfig is an auto-generated flat version of Config. // Where the contents a fields with a `mapstructure:,squash` tag are bubbled up. -func (*Config) FlatMapstructure() interface{} { return new(FlatConfig) } +func (*Config) FlatMapstructure() interface{ HCL2Spec() map[string]hcldec.Spec } { + return new(FlatConfig) +} -// HCL2Spec returns the hcldec.Spec of a FlatConfig. -// This spec is used by HCL to read the fields of FlatConfig. +// HCL2Spec returns the hcl spec of a Config. +// This spec is used by HCL to read the fields of Config. +// The decoded values from this spec will then be applied to a FlatConfig. func (*FlatConfig) HCL2Spec() map[string]hcldec.Spec { s := map[string]hcldec.Spec{ "packer_build_name": &hcldec.AttrSpec{Name: "packer_build_name", Type: cty.String, Required: false}, diff --git a/builder/jdcloud/builder.go b/builder/jdcloud/builder.go index 4b3700f11..2115827b4 100644 --- a/builder/jdcloud/builder.go +++ b/builder/jdcloud/builder.go @@ -3,6 +3,8 @@ package jdcloud import ( "context" "fmt" + + "github.com/hashicorp/hcl/v2/hcldec" "github.com/hashicorp/packer/common" "github.com/hashicorp/packer/helper/communicator" "github.com/hashicorp/packer/helper/config" @@ -11,6 +13,8 @@ import ( "github.com/hashicorp/packer/template/interpolate" ) +func (b *Builder) ConfigSpec() hcldec.ObjectSpec { return b.config.FlatMapstructure().HCL2Spec() } + func (b *Builder) Prepare(raws ...interface{}) ([]string, error) { err := config.Decode(&b.config, &config.DecodeOpts{ Interpolate: true, diff --git a/builder/jdcloud/common.hcl2spec.go b/builder/jdcloud/common.hcl2spec.go index 0bc486d27..486aeb51d 100644 --- a/builder/jdcloud/common.hcl2spec.go +++ b/builder/jdcloud/common.hcl2spec.go @@ -48,8 +48,8 @@ type FlatConfig struct { SSHReadWriteTimeout *string `mapstructure:"ssh_read_write_timeout" cty:"ssh_read_write_timeout"` SSHRemoteTunnels []string `mapstructure:"ssh_remote_tunnels" cty:"ssh_remote_tunnels"` SSHLocalTunnels []string `mapstructure:"ssh_local_tunnels" cty:"ssh_local_tunnels"` - SSHPublicKey []byte `cty:"ssh_public_key"` - SSHPrivateKey []byte `cty:"ssh_private_key"` + SSHPublicKey []byte `mapstructure:"ssh_public_key" cty:"ssh_public_key"` + SSHPrivateKey []byte `mapstructure:"ssh_private_key" cty:"ssh_private_key"` WinRMUser *string `mapstructure:"winrm_username" cty:"winrm_username"` WinRMPassword *string `mapstructure:"winrm_password" cty:"winrm_password"` WinRMHost *string `mapstructure:"winrm_host" cty:"winrm_host"` @@ -74,10 +74,13 @@ type FlatConfig struct { // FlatMapstructure returns a new FlatConfig. // FlatConfig is an auto-generated flat version of Config. // Where the contents a fields with a `mapstructure:,squash` tag are bubbled up. -func (*Config) FlatMapstructure() interface{} { return new(FlatConfig) } +func (*Config) FlatMapstructure() interface{ HCL2Spec() map[string]hcldec.Spec } { + return new(FlatConfig) +} -// HCL2Spec returns the hcldec.Spec of a FlatConfig. -// This spec is used by HCL to read the fields of FlatConfig. +// HCL2Spec returns the hcl spec of a Config. +// This spec is used by HCL to read the fields of Config. +// The decoded values from this spec will then be applied to a FlatConfig. func (*FlatConfig) HCL2Spec() map[string]hcldec.Spec { s := map[string]hcldec.Spec{ "access_key": &hcldec.AttrSpec{Name: "access_key", Type: cty.String, Required: false}, diff --git a/builder/jdcloud/step_config_credentials.go b/builder/jdcloud/step_config_credentials.go index 1c6fdfc76..360a27629 100644 --- a/builder/jdcloud/step_config_credentials.go +++ b/builder/jdcloud/step_config_credentials.go @@ -3,10 +3,11 @@ package jdcloud import ( "context" "fmt" + "io/ioutil" + "github.com/hashicorp/packer/helper/multistep" "github.com/hashicorp/packer/packer" "github.com/jdcloud-api/jdcloud-sdk-go/services/vm/apis" - "io/ioutil" ) type stepConfigCredentials struct { diff --git a/builder/jdcloud/step_create_image.go b/builder/jdcloud/step_create_image.go index 03e2d3b60..c126d8047 100644 --- a/builder/jdcloud/step_create_image.go +++ b/builder/jdcloud/step_create_image.go @@ -3,10 +3,11 @@ package jdcloud import ( "context" "fmt" + "time" + "github.com/hashicorp/packer/helper/multistep" "github.com/hashicorp/packer/packer" "github.com/jdcloud-api/jdcloud-sdk-go/services/vm/apis" - "time" ) type stepCreateJDCloudImage struct { diff --git a/builder/jdcloud/step_create_instance.go b/builder/jdcloud/step_create_instance.go index fe66f464d..950f360d4 100644 --- a/builder/jdcloud/step_create_instance.go +++ b/builder/jdcloud/step_create_instance.go @@ -3,6 +3,9 @@ package jdcloud import ( "context" "fmt" + "regexp" + "time" + "github.com/hashicorp/packer/helper/multistep" "github.com/hashicorp/packer/packer" "github.com/jdcloud-api/jdcloud-sdk-go/core" @@ -11,8 +14,6 @@ import ( vpcApis "github.com/jdcloud-api/jdcloud-sdk-go/services/vpc/apis" vpcClient "github.com/jdcloud-api/jdcloud-sdk-go/services/vpc/client" vpc "github.com/jdcloud-api/jdcloud-sdk-go/services/vpc/models" - "regexp" - "time" ) type stepCreateJDCloudInstance struct { diff --git a/builder/jdcloud/step_stop_instance.go b/builder/jdcloud/step_stop_instance.go index 1cf249c2c..0468b7469 100644 --- a/builder/jdcloud/step_stop_instance.go +++ b/builder/jdcloud/step_stop_instance.go @@ -3,6 +3,7 @@ package jdcloud import ( "context" "fmt" + "github.com/hashicorp/packer/helper/multistep" "github.com/hashicorp/packer/packer" "github.com/jdcloud-api/jdcloud-sdk-go/services/vm/apis" diff --git a/builder/jdcloud/step_validate_parameters.go b/builder/jdcloud/step_validate_parameters.go index 62c04078b..adfab5e14 100644 --- a/builder/jdcloud/step_validate_parameters.go +++ b/builder/jdcloud/step_validate_parameters.go @@ -3,6 +3,7 @@ package jdcloud import ( "context" "fmt" + "github.com/hashicorp/packer/helper/multistep" "github.com/hashicorp/packer/packer" vm "github.com/jdcloud-api/jdcloud-sdk-go/services/vm/apis" diff --git a/builder/linode/builder.go b/builder/linode/builder.go index 3bc3dfb3f..65a6c30c5 100644 --- a/builder/linode/builder.go +++ b/builder/linode/builder.go @@ -8,6 +8,7 @@ import ( "fmt" "log" + "github.com/hashicorp/hcl/v2/hcldec" "github.com/hashicorp/packer/common" "github.com/linode/linodego" @@ -21,16 +22,17 @@ const BuilderID = "packer.linode" // Builder represents a Packer Builder. type Builder struct { - config *Config + config Config runner multistep.Runner } +func (b *Builder) ConfigSpec() hcldec.ObjectSpec { return b.config.FlatMapstructure().HCL2Spec() } + func (b *Builder) Prepare(raws ...interface{}) ([]string, error) { - c, warnings, errs := NewConfig(raws...) + warnings, errs := b.config.Prepare(raws...) if errs != nil { return warnings, errs } - b.config = c return nil, nil } diff --git a/builder/linode/config.go b/builder/linode/config.go index ea8eac073..fd4233827 100644 --- a/builder/linode/config.go +++ b/builder/linode/config.go @@ -48,8 +48,7 @@ func createRandomRootPassword() (string, error) { return rootPass, nil } -func NewConfig(raws ...interface{}) (*Config, []string, error) { - c := new(Config) +func (c *Config) Prepare(raws ...interface{}) ([]string, error) { if err := config.Decode(c, &config.DecodeOpts{ Interpolate: true, @@ -60,7 +59,7 @@ func NewConfig(raws ...interface{}) (*Config, []string, error) { }, }, }, raws...); err != nil { - return nil, nil, err + return nil, err } var errs *packer.MultiError @@ -136,9 +135,9 @@ func NewConfig(raws ...interface{}) (*Config, []string, error) { } if errs != nil && len(errs.Errors) > 0 { - return nil, nil, errs + return nil, errs } packer.LogSecretFilter.Set(c.PersonalAccessToken) - return c, nil, nil + return nil, nil } diff --git a/builder/linode/config.hcl2spec.go b/builder/linode/config.hcl2spec.go index e22bc7507..5f9e8c8ed 100644 --- a/builder/linode/config.hcl2spec.go +++ b/builder/linode/config.hcl2spec.go @@ -46,8 +46,8 @@ type FlatConfig struct { SSHReadWriteTimeout *string `mapstructure:"ssh_read_write_timeout" cty:"ssh_read_write_timeout"` SSHRemoteTunnels []string `mapstructure:"ssh_remote_tunnels" cty:"ssh_remote_tunnels"` SSHLocalTunnels []string `mapstructure:"ssh_local_tunnels" cty:"ssh_local_tunnels"` - SSHPublicKey []byte `cty:"ssh_public_key"` - SSHPrivateKey []byte `cty:"ssh_private_key"` + SSHPublicKey []byte `mapstructure:"ssh_public_key" cty:"ssh_public_key"` + SSHPrivateKey []byte `mapstructure:"ssh_private_key" cty:"ssh_private_key"` WinRMUser *string `mapstructure:"winrm_username" cty:"winrm_username"` WinRMPassword *string `mapstructure:"winrm_password" cty:"winrm_password"` WinRMHost *string `mapstructure:"winrm_host" cty:"winrm_host"` @@ -72,10 +72,13 @@ type FlatConfig struct { // FlatMapstructure returns a new FlatConfig. // FlatConfig is an auto-generated flat version of Config. // Where the contents a fields with a `mapstructure:,squash` tag are bubbled up. -func (*Config) FlatMapstructure() interface{} { return new(FlatConfig) } +func (*Config) FlatMapstructure() interface{ HCL2Spec() map[string]hcldec.Spec } { + return new(FlatConfig) +} -// HCL2Spec returns the hcldec.Spec of a FlatConfig. -// This spec is used by HCL to read the fields of FlatConfig. +// HCL2Spec returns the hcl spec of a Config. +// This spec is used by HCL to read the fields of Config. +// The decoded values from this spec will then be applied to a FlatConfig. func (*FlatConfig) HCL2Spec() map[string]hcldec.Spec { s := map[string]hcldec.Spec{ "packer_build_name": &hcldec.AttrSpec{Name: "packer_build_name", Type: cty.String, Required: false}, diff --git a/builder/lxc/builder.go b/builder/lxc/builder.go index 5c8747c7e..54044ebc0 100644 --- a/builder/lxc/builder.go +++ b/builder/lxc/builder.go @@ -5,6 +5,7 @@ import ( "os" "path/filepath" + "github.com/hashicorp/hcl/v2/hcldec" "github.com/hashicorp/packer/common" "github.com/hashicorp/packer/helper/multistep" "github.com/hashicorp/packer/packer" @@ -19,16 +20,17 @@ type wrappedCommandTemplate struct { } type Builder struct { - config *Config + config Config runner multistep.Runner } +func (b *Builder) ConfigSpec() hcldec.ObjectSpec { return b.config.FlatMapstructure().HCL2Spec() } + func (b *Builder) Prepare(raws ...interface{}) ([]string, error) { - c, errs := NewConfig(raws...) + errs := b.config.Prepare(raws...) if errs != nil { return nil, errs } - b.config = c return nil, nil } diff --git a/builder/lxc/config.go b/builder/lxc/config.go index 587e205a0..fbbd0e117 100644 --- a/builder/lxc/config.go +++ b/builder/lxc/config.go @@ -1,4 +1,5 @@ //go:generate struct-markdown +//go:generate mapstructure-to-hcl2 -type Config package lxc @@ -67,16 +68,15 @@ type Config struct { ctx interpolate.Context } -func NewConfig(raws ...interface{}) (*Config, error) { - var c Config +func (c *Config) Prepare(raws ...interface{}) error { var md mapstructure.Metadata - err := config.Decode(&c, &config.DecodeOpts{ + err := config.Decode(c, &config.DecodeOpts{ Metadata: &md, Interpolate: true, }, raws...) if err != nil { - return nil, err + return err } // Accumulate any errors @@ -107,8 +107,8 @@ func NewConfig(raws ...interface{}) (*Config, error) { } if errs != nil && len(errs.Errors) > 0 { - return nil, errs + return errs } - return &c, nil + return nil } diff --git a/builder/lxc/config.hcl2spec.go b/builder/lxc/config.hcl2spec.go new file mode 100644 index 000000000..22e0ffc8b --- /dev/null +++ b/builder/lxc/config.hcl2spec.go @@ -0,0 +1,66 @@ +// Code generated by "mapstructure-to-hcl2 -type Config"; DO NOT EDIT. +package lxc + +import ( + "github.com/hashicorp/hcl/v2/hcldec" + "github.com/zclconf/go-cty/cty" +) + +// FlatConfig is an auto-generated flat version of Config. +// Where the contents of a field with a `mapstructure:,squash` tag are bubbled up. +type FlatConfig struct { + PackerBuildName *string `mapstructure:"packer_build_name" cty:"packer_build_name"` + PackerBuilderType *string `mapstructure:"packer_builder_type" cty:"packer_builder_type"` + PackerDebug *bool `mapstructure:"packer_debug" cty:"packer_debug"` + PackerForce *bool `mapstructure:"packer_force" cty:"packer_force"` + PackerOnError *string `mapstructure:"packer_on_error" cty:"packer_on_error"` + PackerUserVars map[string]string `mapstructure:"packer_user_variables" cty:"packer_user_variables"` + PackerSensitiveVars []string `mapstructure:"packer_sensitive_variables" cty:"packer_sensitive_variables"` + ConfigFile *string `mapstructure:"config_file" required:"true" cty:"config_file"` + OutputDir *string `mapstructure:"output_directory" required:"false" cty:"output_directory"` + ContainerName *string `mapstructure:"container_name" required:"false" cty:"container_name"` + CommandWrapper *string `mapstructure:"command_wrapper" required:"false" cty:"command_wrapper"` + InitTimeout *string `mapstructure:"init_timeout" required:"false" cty:"init_timeout"` + CreateOptions []string `mapstructure:"create_options" required:"false" cty:"create_options"` + StartOptions []string `mapstructure:"start_options" required:"false" cty:"start_options"` + AttachOptions []string `mapstructure:"attach_options" required:"false" cty:"attach_options"` + Name *string `mapstructure:"template_name" required:"true" cty:"template_name"` + Parameters []string `mapstructure:"template_parameters" required:"false" cty:"template_parameters"` + EnvVars []string `mapstructure:"template_environment_vars" required:"true" cty:"template_environment_vars"` + TargetRunlevel *int `mapstructure:"target_runlevel" required:"false" cty:"target_runlevel"` +} + +// FlatMapstructure returns a new FlatConfig. +// FlatConfig is an auto-generated flat version of Config. +// Where the contents a fields with a `mapstructure:,squash` tag are bubbled up. +func (*Config) FlatMapstructure() interface{ HCL2Spec() map[string]hcldec.Spec } { + return new(FlatConfig) +} + +// HCL2Spec returns the hcl spec of a Config. +// This spec is used by HCL to read the fields of Config. +// The decoded values from this spec will then be applied to a FlatConfig. +func (*FlatConfig) HCL2Spec() map[string]hcldec.Spec { + s := map[string]hcldec.Spec{ + "packer_build_name": &hcldec.AttrSpec{Name: "packer_build_name", Type: cty.String, Required: false}, + "packer_builder_type": &hcldec.AttrSpec{Name: "packer_builder_type", Type: cty.String, Required: false}, + "packer_debug": &hcldec.AttrSpec{Name: "packer_debug", Type: cty.Bool, Required: false}, + "packer_force": &hcldec.AttrSpec{Name: "packer_force", Type: cty.Bool, Required: false}, + "packer_on_error": &hcldec.AttrSpec{Name: "packer_on_error", Type: cty.String, Required: false}, + "packer_user_variables": &hcldec.BlockAttrsSpec{TypeName: "packer_user_variables", ElementType: cty.String, Required: false}, + "packer_sensitive_variables": &hcldec.AttrSpec{Name: "packer_sensitive_variables", Type: cty.List(cty.String), Required: false}, + "config_file": &hcldec.AttrSpec{Name: "config_file", Type: cty.String, Required: false}, + "output_directory": &hcldec.AttrSpec{Name: "output_directory", Type: cty.String, Required: false}, + "container_name": &hcldec.AttrSpec{Name: "container_name", Type: cty.String, Required: false}, + "command_wrapper": &hcldec.AttrSpec{Name: "command_wrapper", Type: cty.String, Required: false}, + "init_timeout": &hcldec.AttrSpec{Name: "init_timeout", Type: cty.String, Required: false}, + "create_options": &hcldec.AttrSpec{Name: "create_options", Type: cty.List(cty.String), Required: false}, + "start_options": &hcldec.AttrSpec{Name: "start_options", Type: cty.List(cty.String), Required: false}, + "attach_options": &hcldec.AttrSpec{Name: "attach_options", Type: cty.List(cty.String), Required: false}, + "template_name": &hcldec.AttrSpec{Name: "template_name", Type: cty.String, Required: false}, + "template_parameters": &hcldec.AttrSpec{Name: "template_parameters", Type: cty.List(cty.String), Required: false}, + "template_environment_vars": &hcldec.AttrSpec{Name: "template_environment_vars", Type: cty.List(cty.String), Required: false}, + "target_runlevel": &hcldec.AttrSpec{Name: "target_runlevel", Type: cty.Number, Required: false}, + } + return s +} diff --git a/builder/lxd/builder.go b/builder/lxd/builder.go index ce1656217..9312dd158 100644 --- a/builder/lxd/builder.go +++ b/builder/lxd/builder.go @@ -3,6 +3,7 @@ package lxd import ( "context" + "github.com/hashicorp/hcl/v2/hcldec" "github.com/hashicorp/packer/common" "github.com/hashicorp/packer/helper/multistep" "github.com/hashicorp/packer/packer" @@ -17,16 +18,17 @@ type wrappedCommandTemplate struct { } type Builder struct { - config *Config + config Config runner multistep.Runner } +func (b *Builder) ConfigSpec() hcldec.ObjectSpec { return b.config.FlatMapstructure().HCL2Spec() } + func (b *Builder) Prepare(raws ...interface{}) ([]string, error) { - c, errs := NewConfig(raws...) + errs := b.config.Prepare(raws...) if errs != nil { return nil, errs } - b.config = c return nil, nil } diff --git a/builder/lxd/config.go b/builder/lxd/config.go index 6914392b4..220c47ca0 100644 --- a/builder/lxd/config.go +++ b/builder/lxd/config.go @@ -43,16 +43,15 @@ type Config struct { ctx interpolate.Context } -func NewConfig(raws ...interface{}) (*Config, error) { - var c Config +func (c *Config) Prepare(raws ...interface{}) error { var md mapstructure.Metadata - err := config.Decode(&c, &config.DecodeOpts{ + err := config.Decode(c, &config.DecodeOpts{ Metadata: &md, Interpolate: true, }, raws...) if err != nil { - return nil, err + return err } // Accumulate any errors @@ -85,8 +84,8 @@ func NewConfig(raws ...interface{}) (*Config, error) { } if errs != nil && len(errs.Errors) > 0 { - return nil, errs + return errs } - return &c, nil + return nil } diff --git a/builder/lxd/config.hcl2spec.go b/builder/lxd/config.hcl2spec.go index 61cc74e19..e8d2d3b0d 100644 --- a/builder/lxd/config.hcl2spec.go +++ b/builder/lxd/config.hcl2spec.go @@ -29,10 +29,13 @@ type FlatConfig struct { // FlatMapstructure returns a new FlatConfig. // FlatConfig is an auto-generated flat version of Config. // Where the contents a fields with a `mapstructure:,squash` tag are bubbled up. -func (*Config) FlatMapstructure() interface{} { return new(FlatConfig) } +func (*Config) FlatMapstructure() interface{ HCL2Spec() map[string]hcldec.Spec } { + return new(FlatConfig) +} -// HCL2Spec returns the hcldec.Spec of a FlatConfig. -// This spec is used by HCL to read the fields of FlatConfig. +// HCL2Spec returns the hcl spec of a Config. +// This spec is used by HCL to read the fields of Config. +// The decoded values from this spec will then be applied to a FlatConfig. func (*FlatConfig) HCL2Spec() map[string]hcldec.Spec { s := map[string]hcldec.Spec{ "packer_build_name": &hcldec.AttrSpec{Name: "packer_build_name", Type: cty.String, Required: false}, diff --git a/builder/ncloud/builder.go b/builder/ncloud/builder.go index 36702c933..45c616e92 100644 --- a/builder/ncloud/builder.go +++ b/builder/ncloud/builder.go @@ -4,6 +4,7 @@ import ( "context" ncloud "github.com/NaverCloudPlatform/ncloud-sdk-go/sdk" + "github.com/hashicorp/hcl/v2/hcldec" "github.com/hashicorp/packer/common" "github.com/hashicorp/packer/helper/communicator" "github.com/hashicorp/packer/helper/multistep" @@ -12,17 +13,18 @@ import ( // Builder assume this implements packer.Builder type Builder struct { - config *Config + config Config stateBag multistep.StateBag runner multistep.Runner } +func (b *Builder) ConfigSpec() hcldec.ObjectSpec { return b.config.FlatMapstructure().HCL2Spec() } + func (b *Builder) Prepare(raws ...interface{}) ([]string, error) { - c, warnings, errs := NewConfig(raws...) + warnings, errs := b.config.Prepare(raws...) if errs != nil { return warnings, errs } - b.config = c b.stateBag = new(multistep.BasicStateBag) @@ -42,12 +44,12 @@ func (b *Builder) Run(ctx context.Context, ui packer.Ui, hook packer.Hook) (pack if b.config.Comm.Type == "ssh" { steps = []multistep.Step{ - NewStepValidateTemplate(conn, ui, b.config), + NewStepValidateTemplate(conn, ui, &b.config), NewStepCreateLoginKey(conn, ui), - NewStepCreateServerInstance(conn, ui, b.config), - NewStepCreateBlockStorageInstance(conn, ui, b.config), - NewStepGetRootPassword(conn, ui, b.config), - NewStepCreatePublicIPInstance(conn, ui, b.config), + NewStepCreateServerInstance(conn, ui, &b.config), + NewStepCreateBlockStorageInstance(conn, ui, &b.config), + NewStepGetRootPassword(conn, ui, &b.config), + NewStepCreatePublicIPInstance(conn, ui, &b.config), &communicator.StepConnectSSH{ Config: &b.config.Comm, Host: func(stateBag multistep.StateBag) (string, error) { @@ -60,18 +62,18 @@ func (b *Builder) Run(ctx context.Context, ui packer.Ui, hook packer.Hook) (pack Comm: &b.config.Comm, }, NewStepStopServerInstance(conn, ui), - NewStepCreateServerImage(conn, ui, b.config), - NewStepDeleteBlockStorageInstance(conn, ui, b.config), + NewStepCreateServerImage(conn, ui, &b.config), + NewStepDeleteBlockStorageInstance(conn, ui, &b.config), NewStepTerminateServerInstance(conn, ui), } } else if b.config.Comm.Type == "winrm" { steps = []multistep.Step{ - NewStepValidateTemplate(conn, ui, b.config), + NewStepValidateTemplate(conn, ui, &b.config), NewStepCreateLoginKey(conn, ui), - NewStepCreateServerInstance(conn, ui, b.config), - NewStepCreateBlockStorageInstance(conn, ui, b.config), - NewStepGetRootPassword(conn, ui, b.config), - NewStepCreatePublicIPInstance(conn, ui, b.config), + NewStepCreateServerInstance(conn, ui, &b.config), + NewStepCreateBlockStorageInstance(conn, ui, &b.config), + NewStepGetRootPassword(conn, ui, &b.config), + NewStepCreatePublicIPInstance(conn, ui, &b.config), &communicator.StepConnectWinRM{ Config: &b.config.Comm, Host: func(stateBag multistep.StateBag) (string, error) { @@ -86,8 +88,8 @@ func (b *Builder) Run(ctx context.Context, ui packer.Ui, hook packer.Hook) (pack }, &common.StepProvision{}, NewStepStopServerInstance(conn, ui), - NewStepCreateServerImage(conn, ui, b.config), - NewStepDeleteBlockStorageInstance(conn, ui, b.config), + NewStepCreateServerImage(conn, ui, &b.config), + NewStepDeleteBlockStorageInstance(conn, ui, &b.config), NewStepTerminateServerInstance(conn, ui), } } diff --git a/builder/ncloud/config.go b/builder/ncloud/config.go index b1f7f4416..aca3e04d8 100644 --- a/builder/ncloud/config.go +++ b/builder/ncloud/config.go @@ -60,8 +60,7 @@ type Config struct { } // NewConfig checks parameters -func NewConfig(raws ...interface{}) (*Config, []string, error) { - c := new(Config) +func (c *Config) Prepare(raws ...interface{}) ([]string, error) { warnings := []string{} err := config.Decode(c, &config.DecodeOpts{ @@ -71,7 +70,7 @@ func NewConfig(raws ...interface{}) (*Config, []string, error) { }, }, raws...) if err != nil { - return nil, warnings, err + return warnings, err } var errs *packer.MultiError @@ -115,7 +114,7 @@ func NewConfig(raws ...interface{}) (*Config, []string, error) { if c.BlockStorageSize < 10 || c.BlockStorageSize > 2000 { errs = packer.MultiErrorAppend(errs, errors.New("The size of BlockStorageSize is at least 10 GB and up to 2000GB")) } else if int(c.BlockStorageSize/10)*10 != c.BlockStorageSize { - return nil, nil, errors.New("BlockStorageSize must be a multiple of 10 GB") + return nil, errors.New("BlockStorageSize must be a multiple of 10 GB") } } @@ -136,8 +135,8 @@ func NewConfig(raws ...interface{}) (*Config, []string, error) { } if errs != nil && len(errs.Errors) > 0 { - return nil, warnings, errs + return warnings, errs } - return c, warnings, nil + return warnings, nil } diff --git a/builder/ncloud/config.hcl2spec.go b/builder/ncloud/config.hcl2spec.go index f72c02456..90559be6e 100644 --- a/builder/ncloud/config.hcl2spec.go +++ b/builder/ncloud/config.hcl2spec.go @@ -58,8 +58,8 @@ type FlatConfig struct { SSHReadWriteTimeout *string `mapstructure:"ssh_read_write_timeout" cty:"ssh_read_write_timeout"` SSHRemoteTunnels []string `mapstructure:"ssh_remote_tunnels" cty:"ssh_remote_tunnels"` SSHLocalTunnels []string `mapstructure:"ssh_local_tunnels" cty:"ssh_local_tunnels"` - SSHPublicKey []byte `cty:"ssh_public_key"` - SSHPrivateKey []byte `cty:"ssh_private_key"` + SSHPublicKey []byte `mapstructure:"ssh_public_key" cty:"ssh_public_key"` + SSHPrivateKey []byte `mapstructure:"ssh_private_key" cty:"ssh_private_key"` WinRMUser *string `mapstructure:"winrm_username" cty:"winrm_username"` WinRMPassword *string `mapstructure:"winrm_password" cty:"winrm_password"` WinRMHost *string `mapstructure:"winrm_host" cty:"winrm_host"` @@ -73,10 +73,13 @@ type FlatConfig struct { // FlatMapstructure returns a new FlatConfig. // FlatConfig is an auto-generated flat version of Config. // Where the contents a fields with a `mapstructure:,squash` tag are bubbled up. -func (*Config) FlatMapstructure() interface{} { return new(FlatConfig) } +func (*Config) FlatMapstructure() interface{ HCL2Spec() map[string]hcldec.Spec } { + return new(FlatConfig) +} -// HCL2Spec returns the hcldec.Spec of a FlatConfig. -// This spec is used by HCL to read the fields of FlatConfig. +// HCL2Spec returns the hcl spec of a Config. +// This spec is used by HCL to read the fields of Config. +// The decoded values from this spec will then be applied to a FlatConfig. func (*FlatConfig) HCL2Spec() map[string]hcldec.Spec { s := map[string]hcldec.Spec{ "packer_build_name": &hcldec.AttrSpec{Name: "packer_build_name", Type: cty.String, Required: false}, diff --git a/builder/ncloud/config_test.go b/builder/ncloud/config_test.go index 7ccc23478..0c793b05e 100644 --- a/builder/ncloud/config_test.go +++ b/builder/ncloud/config_test.go @@ -42,7 +42,8 @@ func testConfigForMemberServerImage() map[string]interface{} { func TestConfigWithServerImageProductCode(t *testing.T) { raw := testConfig() - c, _, _ := NewConfig(raw) + var c Config + c.Prepare(raw) if c.AccessKey != "access_key" { t.Errorf("Expected 'access_key' to be set to '%s', but got '%s'.", raw["access_key"], c.AccessKey) @@ -76,7 +77,8 @@ func TestConfigWithServerImageProductCode(t *testing.T) { func TestConfigWithMemberServerImageCode(t *testing.T) { raw := testConfigForMemberServerImage() - c, _, _ := NewConfig(raw) + var c Config + c.Prepare(raw) if c.AccessKey != "access_key" { t.Errorf("Expected 'access_key' to be set to '%s', but got '%s'.", raw["access_key"], c.AccessKey) @@ -110,7 +112,8 @@ func TestConfigWithMemberServerImageCode(t *testing.T) { func TestEmptyConfig(t *testing.T) { raw := new(map[string]interface{}) - _, _, err := NewConfig(raw) + var c Config + _, err := c.Prepare(raw) if err == nil { t.Error("Expected Config to require 'access_key', 'secret_key' and some mandatory fields, but it did not") @@ -138,7 +141,8 @@ func TestExistsBothServerImageProductCodeAndMemberServerImageNoConfig(t *testing "member_server_image_no": "2440", } - _, _, err := NewConfig(raw) + var c Config + _, err := c.Prepare(raw) if !strings.Contains(err.Error(), "Only one of server_image_product_code and member_server_image_no can be set") { t.Error("Expected Config to require Only one of 'server_image_product_code' and 'member_server_image_no' can be set, but it did not") diff --git a/builder/null/builder.go b/builder/null/builder.go index 5939cd024..d6d2f1c70 100644 --- a/builder/null/builder.go +++ b/builder/null/builder.go @@ -3,6 +3,7 @@ package null import ( "context" + "github.com/hashicorp/hcl/v2/hcldec" "github.com/hashicorp/packer/common" "github.com/hashicorp/packer/helper/communicator" "github.com/hashicorp/packer/helper/multistep" @@ -12,16 +13,17 @@ import ( const BuilderId = "fnoeding.null" type Builder struct { - config *Config + config Config runner multistep.Runner } +func (b *Builder) ConfigSpec() hcldec.ObjectSpec { return b.config.FlatMapstructure().HCL2Spec() } + func (b *Builder) Prepare(raws ...interface{}) ([]string, error) { - c, warnings, errs := NewConfig(raws...) + warnings, errs := b.config.Prepare(raws...) if errs != nil { return warnings, errs } - b.config = c return warnings, nil } diff --git a/builder/null/config.go b/builder/null/config.go index a589b297b..79ed530b4 100644 --- a/builder/null/config.go +++ b/builder/null/config.go @@ -18,15 +18,14 @@ type Config struct { CommConfig communicator.Config `mapstructure:",squash"` } -func NewConfig(raws ...interface{}) (*Config, []string, error) { - var c Config +func (c *Config) Prepare(raws ...interface{}) ([]string, error) { err := config.Decode(&c, &config.DecodeOpts{ Interpolate: true, InterpolateFilter: &interpolate.RenderFilter{}, }, raws...) if err != nil { - return nil, nil, err + return nil, err } var errs *packer.MultiError @@ -60,8 +59,8 @@ func NewConfig(raws ...interface{}) (*Config, []string, error) { } if errs != nil && len(errs.Errors) > 0 { - return nil, nil, errs + return nil, errs } - return &c, nil, nil + return nil, nil } diff --git a/builder/null/config.hcl2spec.go b/builder/null/config.hcl2spec.go index cceed507b..fb2f31145 100644 --- a/builder/null/config.hcl2spec.go +++ b/builder/null/config.hcl2spec.go @@ -46,8 +46,8 @@ type FlatConfig struct { SSHReadWriteTimeout *string `mapstructure:"ssh_read_write_timeout" cty:"ssh_read_write_timeout"` SSHRemoteTunnels []string `mapstructure:"ssh_remote_tunnels" cty:"ssh_remote_tunnels"` SSHLocalTunnels []string `mapstructure:"ssh_local_tunnels" cty:"ssh_local_tunnels"` - SSHPublicKey []byte `cty:"ssh_public_key"` - SSHPrivateKey []byte `cty:"ssh_private_key"` + SSHPublicKey []byte `mapstructure:"ssh_public_key" cty:"ssh_public_key"` + SSHPrivateKey []byte `mapstructure:"ssh_private_key" cty:"ssh_private_key"` WinRMUser *string `mapstructure:"winrm_username" cty:"winrm_username"` WinRMPassword *string `mapstructure:"winrm_password" cty:"winrm_password"` WinRMHost *string `mapstructure:"winrm_host" cty:"winrm_host"` @@ -61,10 +61,13 @@ type FlatConfig struct { // FlatMapstructure returns a new FlatConfig. // FlatConfig is an auto-generated flat version of Config. // Where the contents a fields with a `mapstructure:,squash` tag are bubbled up. -func (*Config) FlatMapstructure() interface{} { return new(FlatConfig) } +func (*Config) FlatMapstructure() interface{ HCL2Spec() map[string]hcldec.Spec } { + return new(FlatConfig) +} -// HCL2Spec returns the hcldec.Spec of a FlatConfig. -// This spec is used by HCL to read the fields of FlatConfig. +// HCL2Spec returns the hcl spec of a Config. +// This spec is used by HCL to read the fields of Config. +// The decoded values from this spec will then be applied to a FlatConfig. func (*FlatConfig) HCL2Spec() map[string]hcldec.Spec { s := map[string]hcldec.Spec{ "packer_build_name": &hcldec.AttrSpec{Name: "packer_build_name", Type: cty.String, Required: false}, diff --git a/builder/null/config_test.go b/builder/null/config_test.go index 29114806b..4a922d629 100644 --- a/builder/null/config_test.go +++ b/builder/null/config_test.go @@ -16,7 +16,8 @@ func testConfig() map[string]interface{} { } func testConfigStruct(t *testing.T) *Config { - c, warns, errs := NewConfig(testConfig()) + var c Config + warns, errs := c.Prepare(testConfig()) if len(warns) > 0 { t.Fatalf("bad: %#v", len(warns)) } @@ -24,7 +25,7 @@ func testConfigStruct(t *testing.T) *Config { t.Fatalf("bad: %#v", errs) } - return c + return &c } func testConfigErr(t *testing.T, warns []string, err error) { @@ -50,7 +51,8 @@ func TestConfigPrepare_port(t *testing.T) { // default port should be 22 delete(raw, "port") - c, warns, errs := NewConfig(raw) + var c Config + warns, errs := c.Prepare(raw) if c.CommConfig.SSHPort != 22 { t.Fatalf("bad: port should default to 22, not %d", c.CommConfig.SSHPort) } @@ -62,12 +64,13 @@ func TestConfigPrepare_host(t *testing.T) { // No host delete(raw, "ssh_host") - _, warns, errs := NewConfig(raw) + var c Config + warns, errs := c.Prepare(raw) testConfigErr(t, warns, errs) // Good host raw["ssh_host"] = "good" - _, warns, errs = NewConfig(raw) + warns, errs = c.Prepare(raw) testConfigOk(t, warns, errs) } @@ -77,12 +80,12 @@ func TestConfigPrepare_sshCredential(t *testing.T) { // no ssh_password and no ssh_private_key_file delete(raw, "ssh_password") delete(raw, "ssh_private_key_file") - _, warns, errs := NewConfig(raw) + warns, errs := (&Config{}).Prepare(raw) testConfigErr(t, warns, errs) // only ssh_password raw["ssh_password"] = "good" - _, warns, errs = NewConfig(raw) + warns, errs = (&Config{}).Prepare(raw) testConfigOk(t, warns, errs) // only ssh_private_key_file @@ -90,11 +93,11 @@ func TestConfigPrepare_sshCredential(t *testing.T) { defer os.Remove(testFile) raw["ssh_private_key_file"] = testFile delete(raw, "ssh_password") - _, warns, errs = NewConfig(raw) + warns, errs = (&Config{}).Prepare(raw) testConfigOk(t, warns, errs) // both ssh_password and ssh_private_key_file set raw["ssh_password"] = "bad" - _, warns, errs = NewConfig(raw) + warns, errs = (&Config{}).Prepare(raw) testConfigErr(t, warns, errs) } diff --git a/builder/oneandone/builder.go b/builder/oneandone/builder.go index 5ea7674bc..759f9bd00 100644 --- a/builder/oneandone/builder.go +++ b/builder/oneandone/builder.go @@ -5,6 +5,7 @@ import ( "errors" "fmt" + "github.com/hashicorp/hcl/v2/hcldec" "github.com/hashicorp/packer/common" "github.com/hashicorp/packer/helper/communicator" "github.com/hashicorp/packer/helper/multistep" @@ -14,16 +15,17 @@ import ( const BuilderId = "packer.oneandone" type Builder struct { - config *Config + config Config runner multistep.Runner } +func (b *Builder) ConfigSpec() hcldec.ObjectSpec { return b.config.FlatMapstructure().HCL2Spec() } + func (b *Builder) Prepare(raws ...interface{}) ([]string, error) { - c, warnings, errs := NewConfig(raws...) + warnings, errs := b.config.Prepare(raws...) if errs != nil { return warnings, errs } - b.config = c return warnings, nil } diff --git a/builder/oneandone/config.go b/builder/oneandone/config.go index 68f2203f3..73914a13f 100644 --- a/builder/oneandone/config.go +++ b/builder/oneandone/config.go @@ -31,11 +31,10 @@ type Config struct { ctx interpolate.Context } -func NewConfig(raws ...interface{}) (*Config, []string, error) { - var c Config +func (c *Config) Prepare(raws ...interface{}) ([]string, error) { var md mapstructure.Metadata - err := config.Decode(&c, &config.DecodeOpts{ + err := config.Decode(c, &config.DecodeOpts{ Metadata: &md, Interpolate: true, InterpolateContext: &c.ctx, @@ -46,7 +45,7 @@ func NewConfig(raws ...interface{}) (*Config, []string, error) { }, }, raws...) if err != nil { - return nil, nil, err + return nil, err } var errs *packer.MultiError @@ -107,8 +106,8 @@ func NewConfig(raws ...interface{}) (*Config, []string, error) { } if errs != nil && len(errs.Errors) > 0 { - return nil, nil, errs + return nil, errs } packer.LogSecretFilter.Set(c.Token) - return &c, nil, nil + return nil, nil } diff --git a/builder/oneandone/config.hcl2spec.go b/builder/oneandone/config.hcl2spec.go index ebed17e63..ed73682a2 100644 --- a/builder/oneandone/config.hcl2spec.go +++ b/builder/oneandone/config.hcl2spec.go @@ -46,8 +46,8 @@ type FlatConfig struct { SSHReadWriteTimeout *string `mapstructure:"ssh_read_write_timeout" cty:"ssh_read_write_timeout"` SSHRemoteTunnels []string `mapstructure:"ssh_remote_tunnels" cty:"ssh_remote_tunnels"` SSHLocalTunnels []string `mapstructure:"ssh_local_tunnels" cty:"ssh_local_tunnels"` - SSHPublicKey []byte `cty:"ssh_public_key"` - SSHPrivateKey []byte `cty:"ssh_private_key"` + SSHPublicKey []byte `mapstructure:"ssh_public_key" cty:"ssh_public_key"` + SSHPrivateKey []byte `mapstructure:"ssh_private_key" cty:"ssh_private_key"` WinRMUser *string `mapstructure:"winrm_username" cty:"winrm_username"` WinRMPassword *string `mapstructure:"winrm_password" cty:"winrm_password"` WinRMHost *string `mapstructure:"winrm_host" cty:"winrm_host"` @@ -69,10 +69,13 @@ type FlatConfig struct { // FlatMapstructure returns a new FlatConfig. // FlatConfig is an auto-generated flat version of Config. // Where the contents a fields with a `mapstructure:,squash` tag are bubbled up. -func (*Config) FlatMapstructure() interface{} { return new(FlatConfig) } +func (*Config) FlatMapstructure() interface{ HCL2Spec() map[string]hcldec.Spec } { + return new(FlatConfig) +} -// HCL2Spec returns the hcldec.Spec of a FlatConfig. -// This spec is used by HCL to read the fields of FlatConfig. +// HCL2Spec returns the hcl spec of a Config. +// This spec is used by HCL to read the fields of Config. +// The decoded values from this spec will then be applied to a FlatConfig. func (*FlatConfig) HCL2Spec() map[string]hcldec.Spec { s := map[string]hcldec.Spec{ "packer_build_name": &hcldec.AttrSpec{Name: "packer_build_name", Type: cty.String, Required: false}, diff --git a/builder/openstack/builder.go b/builder/openstack/builder.go index 8411f803d..ca4c31e5a 100644 --- a/builder/openstack/builder.go +++ b/builder/openstack/builder.go @@ -9,6 +9,7 @@ import ( "context" "fmt" + "github.com/hashicorp/hcl/v2/hcldec" "github.com/hashicorp/packer/common" "github.com/hashicorp/packer/helper/communicator" "github.com/hashicorp/packer/helper/config" @@ -35,6 +36,8 @@ type Builder struct { runner multistep.Runner } +func (b *Builder) ConfigSpec() hcldec.ObjectSpec { return b.config.FlatMapstructure().HCL2Spec() } + func (b *Builder) Prepare(raws ...interface{}) ([]string, error) { err := config.Decode(&b.config, &config.DecodeOpts{ Interpolate: true, diff --git a/builder/openstack/builder.hcl2spec.go b/builder/openstack/builder.hcl2spec.go index 7977cee1f..567b30688 100644 --- a/builder/openstack/builder.hcl2spec.go +++ b/builder/openstack/builder.hcl2spec.go @@ -73,8 +73,8 @@ type FlatConfig struct { SSHReadWriteTimeout *string `mapstructure:"ssh_read_write_timeout" cty:"ssh_read_write_timeout"` SSHRemoteTunnels []string `mapstructure:"ssh_remote_tunnels" cty:"ssh_remote_tunnels"` SSHLocalTunnels []string `mapstructure:"ssh_local_tunnels" cty:"ssh_local_tunnels"` - SSHPublicKey []byte `cty:"ssh_public_key"` - SSHPrivateKey []byte `cty:"ssh_private_key"` + SSHPublicKey []byte `mapstructure:"ssh_public_key" cty:"ssh_public_key"` + SSHPrivateKey []byte `mapstructure:"ssh_private_key" cty:"ssh_private_key"` WinRMUser *string `mapstructure:"winrm_username" cty:"winrm_username"` WinRMPassword *string `mapstructure:"winrm_password" cty:"winrm_password"` WinRMHost *string `mapstructure:"winrm_host" cty:"winrm_host"` @@ -118,10 +118,13 @@ type FlatConfig struct { // FlatMapstructure returns a new FlatConfig. // FlatConfig is an auto-generated flat version of Config. // Where the contents a fields with a `mapstructure:,squash` tag are bubbled up. -func (*Config) FlatMapstructure() interface{} { return new(FlatConfig) } +func (*Config) FlatMapstructure() interface{ HCL2Spec() map[string]hcldec.Spec } { + return new(FlatConfig) +} -// HCL2Spec returns the hcldec.Spec of a FlatConfig. -// This spec is used by HCL to read the fields of FlatConfig. +// HCL2Spec returns the hcl spec of a Config. +// This spec is used by HCL to read the fields of Config. +// The decoded values from this spec will then be applied to a FlatConfig. func (*FlatConfig) HCL2Spec() map[string]hcldec.Spec { s := map[string]hcldec.Spec{ "packer_build_name": &hcldec.AttrSpec{Name: "packer_build_name", Type: cty.String, Required: false}, @@ -241,10 +244,13 @@ type FlatImageFilter struct { // FlatMapstructure returns a new FlatImageFilter. // FlatImageFilter is an auto-generated flat version of ImageFilter. // Where the contents a fields with a `mapstructure:,squash` tag are bubbled up. -func (*ImageFilter) FlatMapstructure() interface{} { return new(FlatImageFilter) } +func (*ImageFilter) FlatMapstructure() interface{ HCL2Spec() map[string]hcldec.Spec } { + return new(FlatImageFilter) +} -// HCL2Spec returns the hcldec.Spec of a FlatImageFilter. -// This spec is used by HCL to read the fields of FlatImageFilter. +// HCL2Spec returns the hcl spec of a ImageFilter. +// This spec is used by HCL to read the fields of ImageFilter. +// The decoded values from this spec will then be applied to a FlatImageFilter. func (*FlatImageFilter) HCL2Spec() map[string]hcldec.Spec { s := map[string]hcldec.Spec{ "filters": &hcldec.BlockSpec{TypeName: "filters", Nested: hcldec.ObjectSpec((*FlatImageFilterOptions)(nil).HCL2Spec())}, @@ -266,10 +272,13 @@ type FlatImageFilterOptions struct { // FlatMapstructure returns a new FlatImageFilterOptions. // FlatImageFilterOptions is an auto-generated flat version of ImageFilterOptions. // Where the contents a fields with a `mapstructure:,squash` tag are bubbled up. -func (*ImageFilterOptions) FlatMapstructure() interface{} { return new(FlatImageFilterOptions) } +func (*ImageFilterOptions) FlatMapstructure() interface{ HCL2Spec() map[string]hcldec.Spec } { + return new(FlatImageFilterOptions) +} -// HCL2Spec returns the hcldec.Spec of a FlatImageFilterOptions. -// This spec is used by HCL to read the fields of FlatImageFilterOptions. +// HCL2Spec returns the hcl spec of a ImageFilterOptions. +// This spec is used by HCL to read the fields of ImageFilterOptions. +// The decoded values from this spec will then be applied to a FlatImageFilterOptions. func (*FlatImageFilterOptions) HCL2Spec() map[string]hcldec.Spec { s := map[string]hcldec.Spec{ "name": &hcldec.AttrSpec{Name: "name", Type: cty.String, Required: false}, diff --git a/builder/oracle/classic/builder.go b/builder/oracle/classic/builder.go index d97b3be10..e91717c75 100644 --- a/builder/oracle/classic/builder.go +++ b/builder/oracle/classic/builder.go @@ -1,3 +1,5 @@ +//go:generate mapstructure-to-hcl2 -type Config + package classic import ( @@ -8,6 +10,7 @@ import ( "github.com/hashicorp/go-cleanhttp" "github.com/hashicorp/go-oracle-terraform/compute" "github.com/hashicorp/go-oracle-terraform/opc" + "github.com/hashicorp/hcl/v2/hcldec" ocommon "github.com/hashicorp/packer/builder/oracle/common" "github.com/hashicorp/packer/common" "github.com/hashicorp/packer/helper/communicator" @@ -20,16 +23,17 @@ const BuilderId = "packer.oracle.classic" // Builder is a builder implementation that creates Oracle OCI custom images. type Builder struct { - config *Config + config Config runner multistep.Runner } -func (b *Builder) Prepare(rawConfig ...interface{}) ([]string, error) { - config, err := NewConfig(rawConfig...) +func (b *Builder) ConfigSpec() hcldec.ObjectSpec { return b.config.FlatMapstructure().HCL2Spec() } + +func (b *Builder) Prepare(raws ...interface{}) ([]string, error) { + err := b.config.Prepare(raws...) if err != nil { return nil, err } - b.config = config var errs *packer.MultiError diff --git a/builder/oracle/classic/config.hcl2spec.go b/builder/oracle/classic/builder.hcl2spec.go similarity index 96% rename from builder/oracle/classic/config.hcl2spec.go rename to builder/oracle/classic/builder.hcl2spec.go index a65bc849e..d8bcf0079 100644 --- a/builder/oracle/classic/config.hcl2spec.go +++ b/builder/oracle/classic/builder.hcl2spec.go @@ -53,8 +53,8 @@ type FlatConfig struct { SSHReadWriteTimeout *string `mapstructure:"ssh_read_write_timeout" cty:"ssh_read_write_timeout"` SSHRemoteTunnels []string `mapstructure:"ssh_remote_tunnels" cty:"ssh_remote_tunnels"` SSHLocalTunnels []string `mapstructure:"ssh_local_tunnels" cty:"ssh_local_tunnels"` - SSHPublicKey []byte `cty:"ssh_public_key"` - SSHPrivateKey []byte `cty:"ssh_private_key"` + SSHPublicKey []byte `mapstructure:"ssh_public_key" cty:"ssh_public_key"` + SSHPrivateKey []byte `mapstructure:"ssh_private_key" cty:"ssh_private_key"` WinRMUser *string `mapstructure:"winrm_username" cty:"winrm_username"` WinRMPassword *string `mapstructure:"winrm_password" cty:"winrm_password"` WinRMHost *string `mapstructure:"winrm_host" cty:"winrm_host"` @@ -82,10 +82,13 @@ type FlatConfig struct { // FlatMapstructure returns a new FlatConfig. // FlatConfig is an auto-generated flat version of Config. // Where the contents a fields with a `mapstructure:,squash` tag are bubbled up. -func (*Config) FlatMapstructure() interface{} { return new(FlatConfig) } +func (*Config) FlatMapstructure() interface{ HCL2Spec() map[string]hcldec.Spec } { + return new(FlatConfig) +} -// HCL2Spec returns the hcldec.Spec of a FlatConfig. -// This spec is used by HCL to read the fields of FlatConfig. +// HCL2Spec returns the hcl spec of a Config. +// This spec is used by HCL to read the fields of Config. +// The decoded values from this spec will then be applied to a FlatConfig. func (*FlatConfig) HCL2Spec() map[string]hcldec.Spec { s := map[string]hcldec.Spec{ "packer_build_name": &hcldec.AttrSpec{Name: "packer_build_name", Type: cty.String, Required: false}, diff --git a/builder/oracle/classic/config.go b/builder/oracle/classic/config.go index 4fc1ddb15..6f66d7a81 100644 --- a/builder/oracle/classic/config.go +++ b/builder/oracle/classic/config.go @@ -1,5 +1,3 @@ -//go:generate mapstructure-to-hcl2 -type Config - package classic import ( @@ -57,8 +55,7 @@ func (c *Config) Identifier(s string) string { return fmt.Sprintf("/Compute-%s/%s/%s", c.IdentityDomain, c.Username, s) } -func NewConfig(raws ...interface{}) (*Config, error) { - c := &Config{} +func (c *Config) Prepare(raws ...interface{}) error { // Decode from template err := config.Decode(c, &config.DecodeOpts{ @@ -66,12 +63,12 @@ func NewConfig(raws ...interface{}) (*Config, error) { InterpolateContext: &c.ctx, }, raws...) if err != nil { - return nil, fmt.Errorf("Failed to mapstructure Config: %+v", err) + return fmt.Errorf("Failed to mapstructure Config: %+v", err) } c.apiEndpointURL, err = url.Parse(c.APIEndpoint) if err != nil { - return nil, fmt.Errorf("Error parsing API Endpoint: %s", err) + return fmt.Errorf("Error parsing API Endpoint: %s", err) } // Set default source list if c.SSHSourceList == "" { @@ -127,7 +124,7 @@ func NewConfig(raws ...interface{}) (*Config, error) { } if errs != nil && len(errs.Errors) > 0 { - return nil, errs + return errs } // unpack attributes from json into config @@ -155,5 +152,5 @@ func NewConfig(raws ...interface{}) (*Config, error) { c.attribs = data } - return c, nil + return nil } diff --git a/builder/oracle/classic/config_test.go b/builder/oracle/classic/config_test.go index b120e70a3..8d55a1626 100644 --- a/builder/oracle/classic/config_test.go +++ b/builder/oracle/classic/config_test.go @@ -22,7 +22,8 @@ func testConfig() map[string]interface{} { func TestConfigAutoFillsSourceList(t *testing.T) { tc := testConfig() - conf, err := NewConfig(tc) + var conf Config + err := conf.Prepare(tc) if err != nil { t.Fatalf("Should not have error: %s", err.Error()) } @@ -47,7 +48,8 @@ func TestConfigValidationCatchesMissing(t *testing.T) { for _, key := range required { tc := testConfig() delete(tc, key) - _, err := NewConfig(tc) + var c Config + err := c.Prepare(tc) if err == nil { t.Fatalf("Test should have failed when config lacked %s!", key) } @@ -68,7 +70,8 @@ func TestConfigValidatesObjects(t *testing.T) { for _, tt := range objectTests { tc := testConfig() tc[s] = tt.object - _, err := NewConfig(tc) + var c Config + err := c.Prepare(tc) if tt.valid { assert.NoError(t, err, tt.object) } else { diff --git a/builder/oracle/oci/builder.go b/builder/oracle/oci/builder.go index a4b31b8e6..5b0922018 100644 --- a/builder/oracle/oci/builder.go +++ b/builder/oracle/oci/builder.go @@ -6,6 +6,7 @@ import ( "context" "fmt" + "github.com/hashicorp/hcl/v2/hcldec" ocommon "github.com/hashicorp/packer/builder/oracle/common" "github.com/hashicorp/packer/common" "github.com/hashicorp/packer/helper/communicator" @@ -22,22 +23,23 @@ const ociAPIVersion = "20160918" // Builder is a builder implementation that creates Oracle OCI custom images. type Builder struct { - config *Config + config Config runner multistep.Runner } -func (b *Builder) Prepare(rawConfig ...interface{}) ([]string, error) { - config, err := NewConfig(rawConfig...) +func (b *Builder) ConfigSpec() hcldec.ObjectSpec { return b.config.FlatMapstructure().HCL2Spec() } + +func (b *Builder) Prepare(raws ...interface{}) ([]string, error) { + err := b.config.Prepare(raws...) if err != nil { return nil, err } - b.config = config return nil, nil } func (b *Builder) Run(ctx context.Context, ui packer.Ui, hook packer.Hook) (packer.Artifact, error) { - driver, err := NewDriverOCI(b.config) + driver, err := NewDriverOCI(&b.config) if err != nil { return nil, err } diff --git a/builder/oracle/oci/config.go b/builder/oracle/oci/config.go index 2d11cb05a..52229cc48 100644 --- a/builder/oracle/oci/config.go +++ b/builder/oracle/oci/config.go @@ -75,8 +75,7 @@ func (c *Config) ConfigProvider() ocicommon.ConfigurationProvider { return c.configProvider } -func NewConfig(raws ...interface{}) (*Config, error) { - c := &Config{} +func (c *Config) Prepare(raws ...interface{}) error { // Decode from template err := config.Decode(c, &config.DecodeOpts{ @@ -84,7 +83,7 @@ func NewConfig(raws ...interface{}) (*Config, error) { InterpolateContext: &c.ctx, }, raws...) if err != nil { - return nil, fmt.Errorf("Failed to mapstructure Config: %+v", err) + return fmt.Errorf("Failed to mapstructure Config: %+v", err) } // Determine where the SDK config is located @@ -103,13 +102,13 @@ func NewConfig(raws ...interface{}) (*Config, error) { if c.KeyFile != "" { path, err := packer.ExpandUser(c.KeyFile) if err != nil { - return nil, err + return err } // Read API signing key keyContent, err = ioutil.ReadFile(path) if err != nil { - return nil, err + return err } } @@ -135,7 +134,7 @@ func NewConfig(raws ...interface{}) (*Config, error) { // Load API access configuration from SDK configProvider, err := ocicommon.ComposingConfigurationProvider(providers) if err != nil { - return nil, err + return err } var errs *packer.MultiError @@ -249,10 +248,10 @@ func NewConfig(raws ...interface{}) (*Config, error) { } if errs != nil && len(errs.Errors) > 0 { - return nil, errs + return errs } - return c, nil + return nil } // getDefaultOCISettingsPath uses os/user to compute the default diff --git a/builder/oracle/oci/config.hcl2spec.go b/builder/oracle/oci/config.hcl2spec.go index 78980b963..107c1ae43 100644 --- a/builder/oracle/oci/config.hcl2spec.go +++ b/builder/oracle/oci/config.hcl2spec.go @@ -46,8 +46,8 @@ type FlatConfig struct { SSHReadWriteTimeout *string `mapstructure:"ssh_read_write_timeout" cty:"ssh_read_write_timeout"` SSHRemoteTunnels []string `mapstructure:"ssh_remote_tunnels" cty:"ssh_remote_tunnels"` SSHLocalTunnels []string `mapstructure:"ssh_local_tunnels" cty:"ssh_local_tunnels"` - SSHPublicKey []byte `cty:"ssh_public_key"` - SSHPrivateKey []byte `cty:"ssh_private_key"` + SSHPublicKey []byte `mapstructure:"ssh_public_key" cty:"ssh_public_key"` + SSHPrivateKey []byte `mapstructure:"ssh_private_key" cty:"ssh_private_key"` WinRMUser *string `mapstructure:"winrm_username" cty:"winrm_username"` WinRMPassword *string `mapstructure:"winrm_password" cty:"winrm_password"` WinRMHost *string `mapstructure:"winrm_host" cty:"winrm_host"` @@ -82,10 +82,13 @@ type FlatConfig struct { // FlatMapstructure returns a new FlatConfig. // FlatConfig is an auto-generated flat version of Config. // Where the contents a fields with a `mapstructure:,squash` tag are bubbled up. -func (*Config) FlatMapstructure() interface{} { return new(FlatConfig) } +func (*Config) FlatMapstructure() interface{ HCL2Spec() map[string]hcldec.Spec } { + return new(FlatConfig) +} -// HCL2Spec returns the hcldec.Spec of a FlatConfig. -// This spec is used by HCL to read the fields of FlatConfig. +// HCL2Spec returns the hcl spec of a Config. +// This spec is used by HCL to read the fields of Config. +// The decoded values from this spec will then be applied to a FlatConfig. func (*FlatConfig) HCL2Spec() map[string]hcldec.Spec { s := map[string]hcldec.Spec{ "packer_build_name": &hcldec.AttrSpec{Name: "packer_build_name", Type: cty.String, Required: false}, diff --git a/builder/oracle/oci/config_test.go b/builder/oracle/oci/config_test.go index 37d23a258..117f1190d 100644 --- a/builder/oracle/oci/config_test.go +++ b/builder/oracle/oci/config_test.go @@ -69,7 +69,8 @@ func TestConfig(t *testing.T) { // Config tests t.Run("BaseConfig", func(t *testing.T) { raw := testConfig(cfgFile) - _, errs := NewConfig(raw) + var c Config + errs := c.Prepare(raw) if errs != nil { t.Fatalf("Unexpected error in configuration %+v", errs) @@ -80,7 +81,8 @@ func TestConfig(t *testing.T) { raw := testConfig(cfgFile) delete(raw, "access_cfg_file") - _, errs := NewConfig(raw) + var c Config + errs := c.Prepare(raw) expectedErrors := []string{ "'user_ocid'", "'tenancy_ocid'", "'fingerprint'", "'key_file'", @@ -102,7 +104,8 @@ func TestConfig(t *testing.T) { raw["fingerprint"] = "00:00..." raw["key_file"] = keyFile.Name() - _, errs := NewConfig(raw) + var c Config + errs := c.Prepare(raw) if errs != nil { t.Fatalf("err: %+v", errs) @@ -112,7 +115,8 @@ func TestConfig(t *testing.T) { t.Run("TenancyReadFromAccessCfgFile", func(t *testing.T) { raw := testConfig(cfgFile) - c, errs := NewConfig(raw) + var c Config + errs := c.Prepare(raw) if errs != nil { t.Fatalf("Unexpected error in configuration %+v", errs) } @@ -131,7 +135,8 @@ func TestConfig(t *testing.T) { t.Run("RegionNotDefaultedToPHXWhenSetInOCISettings", func(t *testing.T) { raw := testConfig(cfgFile) - c, errs := NewConfig(raw) + var c Config + errs := c.Prepare(raw) if errs != nil { t.Fatalf("Unexpected error in configuration %+v", errs) } @@ -156,7 +161,8 @@ func TestConfig(t *testing.T) { raw := testConfig(cfgFile) delete(raw, k) - _, errs := NewConfig(raw) + var c Config + errs := c.Prepare(raw) if !strings.Contains(errs.Error(), k) { t.Errorf("Expected '%s' to contain '%s'", errs.Error(), k) @@ -168,7 +174,8 @@ func TestConfig(t *testing.T) { raw := testConfig(cfgFile) delete(raw, "image_name") - c, errs := NewConfig(raw) + var c Config + errs := c.Prepare(raw) if errs != nil { t.Fatalf("Unexpected error in configuration %+v", errs) } @@ -183,7 +190,8 @@ func TestConfig(t *testing.T) { raw := testConfig(cfgFile) raw["user_ocid"] = expected - c, errs := NewConfig(raw) + var c Config + errs := c.Prepare(raw) if errs != nil { t.Fatalf("Unexpected error in configuration %+v", errs) } @@ -199,7 +207,8 @@ func TestConfig(t *testing.T) { raw := testConfig(cfgFile) raw["tenancy_ocid"] = expected - c, errs := NewConfig(raw) + var c Config + errs := c.Prepare(raw) if errs != nil { t.Fatalf("Unexpected error in configuration %+v", errs) } @@ -215,7 +224,8 @@ func TestConfig(t *testing.T) { raw := testConfig(cfgFile) raw["region"] = expected - c, errs := NewConfig(raw) + var c Config + errs := c.Prepare(raw) if errs != nil { t.Fatalf("Unexpected error in configuration %+v", errs) } @@ -231,7 +241,8 @@ func TestConfig(t *testing.T) { raw := testConfig(cfgFile) raw["fingerprint"] = expected - c, errs := NewConfig(raw) + var c Config + errs := c.Prepare(raw) if errs != nil { t.Fatalf("Unexpected error in configuration: %+v", errs) } diff --git a/builder/oracle/oci/step_test.go b/builder/oracle/oci/step_test.go index 55afba523..deeff1b1a 100644 --- a/builder/oracle/oci/step_test.go +++ b/builder/oracle/oci/step_test.go @@ -16,7 +16,8 @@ func baseTestConfig() *Config { panic(err) } - cfg, err := NewConfig(map[string]interface{}{ + var c Config + err = c.Prepare(map[string]interface{}{ "availability_domain": "aaaa:US-ASHBURN-AD-1", // Image @@ -46,7 +47,7 @@ func baseTestConfig() *Config { if err != nil { panic(err) } - return cfg + return &c } func testState() multistep.StateBag { diff --git a/builder/osc/bsu/builder.go b/builder/osc/bsu/builder.go index ab4280417..bc93a7a58 100644 --- a/builder/osc/bsu/builder.go +++ b/builder/osc/bsu/builder.go @@ -13,6 +13,7 @@ import ( "fmt" "net/http" + "github.com/hashicorp/hcl/v2/hcldec" osccommon "github.com/hashicorp/packer/builder/osc/common" "github.com/hashicorp/packer/common" "github.com/hashicorp/packer/helper/communicator" @@ -42,6 +43,8 @@ type Builder struct { runner multistep.Runner } +func (b *Builder) ConfigSpec() hcldec.ObjectSpec { return b.config.FlatMapstructure().HCL2Spec() } + func (b *Builder) Prepare(raws ...interface{}) ([]string, error) { b.config.ctx.Funcs = osccommon.TemplateFuncs err := config.Decode(&b.config, &config.DecodeOpts{ diff --git a/builder/osc/bsu/builder.hcl2spec.go b/builder/osc/bsu/builder.hcl2spec.go index 8bc75d0f7..4da22cf6a 100644 --- a/builder/osc/bsu/builder.hcl2spec.go +++ b/builder/osc/bsu/builder.hcl2spec.go @@ -98,8 +98,8 @@ type FlatConfig struct { SSHReadWriteTimeout *string `mapstructure:"ssh_read_write_timeout" cty:"ssh_read_write_timeout"` SSHRemoteTunnels []string `mapstructure:"ssh_remote_tunnels" cty:"ssh_remote_tunnels"` SSHLocalTunnels []string `mapstructure:"ssh_local_tunnels" cty:"ssh_local_tunnels"` - SSHPublicKey []byte `cty:"ssh_public_key"` - SSHPrivateKey []byte `cty:"ssh_private_key"` + SSHPublicKey []byte `mapstructure:"ssh_public_key" cty:"ssh_public_key"` + SSHPrivateKey []byte `mapstructure:"ssh_private_key" cty:"ssh_private_key"` WinRMUser *string `mapstructure:"winrm_username" cty:"winrm_username"` WinRMPassword *string `mapstructure:"winrm_password" cty:"winrm_password"` WinRMHost *string `mapstructure:"winrm_host" cty:"winrm_host"` @@ -115,10 +115,13 @@ type FlatConfig struct { // FlatMapstructure returns a new FlatConfig. // FlatConfig is an auto-generated flat version of Config. // Where the contents a fields with a `mapstructure:,squash` tag are bubbled up. -func (*Config) FlatMapstructure() interface{} { return new(FlatConfig) } +func (*Config) FlatMapstructure() interface{ HCL2Spec() map[string]hcldec.Spec } { + return new(FlatConfig) +} -// HCL2Spec returns the hcldec.Spec of a FlatConfig. -// This spec is used by HCL to read the fields of FlatConfig. +// HCL2Spec returns the hcl spec of a Config. +// This spec is used by HCL to read the fields of Config. +// The decoded values from this spec will then be applied to a FlatConfig. func (*FlatConfig) HCL2Spec() map[string]hcldec.Spec { s := map[string]hcldec.Spec{ "packer_build_name": &hcldec.AttrSpec{Name: "packer_build_name", Type: cty.String, Required: false}, @@ -151,8 +154,8 @@ func (*FlatConfig) HCL2Spec() map[string]hcldec.Spec { "snapshot_tags": &hcldec.BlockAttrsSpec{TypeName: "common.TagMap", ElementType: cty.String, Required: false}, "snapshot_account_ids": &hcldec.AttrSpec{Name: "snapshot_account_ids", Type: cty.List(cty.String), Required: false}, "snapshot_groups": &hcldec.AttrSpec{Name: "snapshot_groups", Type: cty.List(cty.String), Required: false}, - "omi_block_device_mappings": &hcldec.BlockListSpec{TypeName: "omi_block_device_mappings", Nested: &hcldec.BlockSpec{TypeName: "omi_block_device_mappings", Nested: hcldec.ObjectSpec((*common.FlatBlockDevice)(nil).HCL2Spec())}}, - "launch_block_device_mappings": &hcldec.BlockListSpec{TypeName: "launch_block_device_mappings", Nested: &hcldec.BlockSpec{TypeName: "launch_block_device_mappings", Nested: hcldec.ObjectSpec((*common.FlatBlockDevice)(nil).HCL2Spec())}}, + "omi_block_device_mappings": &hcldec.BlockListSpec{TypeName: "omi_block_device_mappings", Nested: hcldec.ObjectSpec((*common.FlatBlockDevice)(nil).HCL2Spec())}, + "launch_block_device_mappings": &hcldec.BlockListSpec{TypeName: "launch_block_device_mappings", Nested: hcldec.ObjectSpec((*common.FlatBlockDevice)(nil).HCL2Spec())}, "associate_public_ip_address": &hcldec.AttrSpec{Name: "associate_public_ip_address", Type: cty.Bool, Required: false}, "subregion_name": &hcldec.AttrSpec{Name: "subregion_name", Type: cty.String, Required: false}, "block_duration_minutes": &hcldec.AttrSpec{Name: "block_duration_minutes", Type: cty.Number, Required: false}, diff --git a/builder/osc/bsusurrogate/builder.go b/builder/osc/bsusurrogate/builder.go index 1186a5bdc..120f81413 100644 --- a/builder/osc/bsusurrogate/builder.go +++ b/builder/osc/bsusurrogate/builder.go @@ -1,3 +1,5 @@ +//go:generate mapstructure-to-hcl2 -type Config,RootBlockDevice + // Package bsusurrogate contains a packer.Builder implementation that // builds a new EBS-backed OMI using an ephemeral instance. package bsusurrogate @@ -9,6 +11,7 @@ import ( "fmt" "net/http" + "github.com/hashicorp/hcl/v2/hcldec" osccommon "github.com/hashicorp/packer/builder/osc/common" "github.com/hashicorp/packer/common" "github.com/hashicorp/packer/helper/communicator" @@ -39,6 +42,8 @@ type Builder struct { runner multistep.Runner } +func (b *Builder) ConfigSpec() hcldec.ObjectSpec { return b.config.FlatMapstructure().HCL2Spec() } + func (b *Builder) Prepare(raws ...interface{}) ([]string, error) { b.config.ctx.Funcs = osccommon.TemplateFuncs diff --git a/builder/osc/bsusurrogate/builder.hcl2spec.go b/builder/osc/bsusurrogate/builder.hcl2spec.go new file mode 100644 index 000000000..aa4ddf94e --- /dev/null +++ b/builder/osc/bsusurrogate/builder.hcl2spec.go @@ -0,0 +1,264 @@ +// Code generated by "mapstructure-to-hcl2 -type Config,RootBlockDevice"; DO NOT EDIT. +package bsusurrogate + +import ( + "github.com/hashicorp/hcl/v2/hcldec" + "github.com/hashicorp/packer/builder/osc/common" + "github.com/zclconf/go-cty/cty" +) + +// FlatConfig is an auto-generated flat version of Config. +// Where the contents of a field with a `mapstructure:,squash` tag are bubbled up. +type FlatConfig struct { + PackerBuildName *string `mapstructure:"packer_build_name" cty:"packer_build_name"` + PackerBuilderType *string `mapstructure:"packer_builder_type" cty:"packer_builder_type"` + PackerDebug *bool `mapstructure:"packer_debug" cty:"packer_debug"` + PackerForce *bool `mapstructure:"packer_force" cty:"packer_force"` + PackerOnError *string `mapstructure:"packer_on_error" cty:"packer_on_error"` + PackerUserVars map[string]string `mapstructure:"packer_user_variables" cty:"packer_user_variables"` + PackerSensitiveVars []string `mapstructure:"packer_sensitive_variables" cty:"packer_sensitive_variables"` + AccessKey *string `mapstructure:"access_key" cty:"access_key"` + CustomEndpointOAPI *string `mapstructure:"custom_endpoint_oapi" cty:"custom_endpoint_oapi"` + InsecureSkipTLSVerify *bool `mapstructure:"insecure_skip_tls_verify" cty:"insecure_skip_tls_verify"` + MFACode *string `mapstructure:"mfa_code" cty:"mfa_code"` + ProfileName *string `mapstructure:"profile" cty:"profile"` + RawRegion *string `mapstructure:"region" cty:"region"` + SecretKey *string `mapstructure:"secret_key" cty:"secret_key"` + SkipValidation *bool `mapstructure:"skip_region_validation" cty:"skip_region_validation"` + SkipMetadataApiCheck *bool `mapstructure:"skip_metadata_api_check" cty:"skip_metadata_api_check"` + Token *string `mapstructure:"token" cty:"token"` + AssociatePublicIpAddress *bool `mapstructure:"associate_public_ip_address" cty:"associate_public_ip_address"` + Subregion *string `mapstructure:"subregion_name" cty:"subregion_name"` + BlockDurationMinutes *int64 `mapstructure:"block_duration_minutes" cty:"block_duration_minutes"` + DisableStopVm *bool `mapstructure:"disable_stop_vm" cty:"disable_stop_vm"` + BsuOptimized *bool `mapstructure:"bsu_optimized" cty:"bsu_optimized"` + EnableT2Unlimited *bool `mapstructure:"enable_t2_unlimited" cty:"enable_t2_unlimited"` + IamVmProfile *string `mapstructure:"iam_vm_profile" cty:"iam_vm_profile"` + VmInitiatedShutdownBehavior *string `mapstructure:"shutdown_behavior" cty:"shutdown_behavior"` + VmType *string `mapstructure:"vm_type" cty:"vm_type"` + SecurityGroupFilter *common.FlatSecurityGroupFilterOptions `mapstructure:"security_group_filter" cty:"security_group_filter"` + RunTags map[string]string `mapstructure:"run_tags" cty:"run_tags"` + SecurityGroupId *string `mapstructure:"security_group_id" cty:"security_group_id"` + SecurityGroupIds []string `mapstructure:"security_group_ids" cty:"security_group_ids"` + SourceOmi *string `mapstructure:"source_omi" cty:"source_omi"` + SourceOmiFilter *common.FlatOmiFilterOptions `mapstructure:"source_omi_filter" cty:"source_omi_filter"` + SpotPrice *string `mapstructure:"spot_price" cty:"spot_price"` + SpotPriceAutoProduct *string `mapstructure:"spot_price_auto_product" cty:"spot_price_auto_product"` + SpotTags map[string]string `mapstructure:"spot_tags" cty:"spot_tags"` + SubnetFilter *common.FlatSubnetFilterOptions `mapstructure:"subnet_filter" cty:"subnet_filter"` + SubnetId *string `mapstructure:"subnet_id" cty:"subnet_id"` + TemporaryKeyPairName *string `mapstructure:"temporary_key_pair_name" cty:"temporary_key_pair_name"` + TemporarySGSourceCidr *string `mapstructure:"temporary_security_group_source_cidr" cty:"temporary_security_group_source_cidr"` + UserData *string `mapstructure:"user_data" cty:"user_data"` + UserDataFile *string `mapstructure:"user_data_file" cty:"user_data_file"` + NetFilter *common.FlatNetFilterOptions `mapstructure:"net_filter" cty:"net_filter"` + NetId *string `mapstructure:"net_id" cty:"net_id"` + WindowsPasswordTimeout *string `mapstructure:"windows_password_timeout" cty:"windows_password_timeout"` + Type *string `mapstructure:"communicator" cty:"communicator"` + PauseBeforeConnect *string `mapstructure:"pause_before_connecting" cty:"pause_before_connecting"` + SSHHost *string `mapstructure:"ssh_host" cty:"ssh_host"` + SSHPort *int `mapstructure:"ssh_port" cty:"ssh_port"` + SSHUsername *string `mapstructure:"ssh_username" cty:"ssh_username"` + SSHPassword *string `mapstructure:"ssh_password" cty:"ssh_password"` + SSHKeyPairName *string `mapstructure:"ssh_keypair_name" cty:"ssh_keypair_name"` + SSHClearAuthorizedKeys *bool `mapstructure:"ssh_clear_authorized_keys" cty:"ssh_clear_authorized_keys"` + SSHPrivateKeyFile *string `mapstructure:"ssh_private_key_file" cty:"ssh_private_key_file"` + SSHPty *bool `mapstructure:"ssh_pty" cty:"ssh_pty"` + SSHTimeout *string `mapstructure:"ssh_timeout" cty:"ssh_timeout"` + SSHAgentAuth *bool `mapstructure:"ssh_agent_auth" cty:"ssh_agent_auth"` + SSHDisableAgentForwarding *bool `mapstructure:"ssh_disable_agent_forwarding" cty:"ssh_disable_agent_forwarding"` + SSHHandshakeAttempts *int `mapstructure:"ssh_handshake_attempts" cty:"ssh_handshake_attempts"` + SSHBastionHost *string `mapstructure:"ssh_bastion_host" cty:"ssh_bastion_host"` + SSHBastionPort *int `mapstructure:"ssh_bastion_port" cty:"ssh_bastion_port"` + SSHBastionAgentAuth *bool `mapstructure:"ssh_bastion_agent_auth" cty:"ssh_bastion_agent_auth"` + SSHBastionUsername *string `mapstructure:"ssh_bastion_username" cty:"ssh_bastion_username"` + SSHBastionPassword *string `mapstructure:"ssh_bastion_password" cty:"ssh_bastion_password"` + SSHBastionPrivateKeyFile *string `mapstructure:"ssh_bastion_private_key_file" cty:"ssh_bastion_private_key_file"` + SSHFileTransferMethod *string `mapstructure:"ssh_file_transfer_method" cty:"ssh_file_transfer_method"` + SSHProxyHost *string `mapstructure:"ssh_proxy_host" cty:"ssh_proxy_host"` + SSHProxyPort *int `mapstructure:"ssh_proxy_port" cty:"ssh_proxy_port"` + SSHProxyUsername *string `mapstructure:"ssh_proxy_username" cty:"ssh_proxy_username"` + SSHProxyPassword *string `mapstructure:"ssh_proxy_password" cty:"ssh_proxy_password"` + SSHKeepAliveInterval *string `mapstructure:"ssh_keep_alive_interval" cty:"ssh_keep_alive_interval"` + SSHReadWriteTimeout *string `mapstructure:"ssh_read_write_timeout" cty:"ssh_read_write_timeout"` + SSHRemoteTunnels []string `mapstructure:"ssh_remote_tunnels" cty:"ssh_remote_tunnels"` + SSHLocalTunnels []string `mapstructure:"ssh_local_tunnels" cty:"ssh_local_tunnels"` + SSHPublicKey []byte `mapstructure:"ssh_public_key" cty:"ssh_public_key"` + SSHPrivateKey []byte `mapstructure:"ssh_private_key" cty:"ssh_private_key"` + WinRMUser *string `mapstructure:"winrm_username" cty:"winrm_username"` + WinRMPassword *string `mapstructure:"winrm_password" cty:"winrm_password"` + WinRMHost *string `mapstructure:"winrm_host" cty:"winrm_host"` + WinRMPort *int `mapstructure:"winrm_port" cty:"winrm_port"` + WinRMTimeout *string `mapstructure:"winrm_timeout" cty:"winrm_timeout"` + WinRMUseSSL *bool `mapstructure:"winrm_use_ssl" cty:"winrm_use_ssl"` + WinRMInsecure *bool `mapstructure:"winrm_insecure" cty:"winrm_insecure"` + WinRMUseNTLM *bool `mapstructure:"winrm_use_ntlm" cty:"winrm_use_ntlm"` + SSHInterface *string `mapstructure:"ssh_interface" cty:"ssh_interface"` + OMIMappings []common.FlatBlockDevice `mapstructure:"omi_block_device_mappings" cty:"omi_block_device_mappings"` + LaunchMappings []common.FlatBlockDevice `mapstructure:"launch_block_device_mappings" cty:"launch_block_device_mappings"` + OMIName *string `mapstructure:"omi_name" cty:"omi_name"` + OMIDescription *string `mapstructure:"omi_description" cty:"omi_description"` + OMIVirtType *string `mapstructure:"omi_virtualization_type" cty:"omi_virtualization_type"` + OMIAccountIDs []string `mapstructure:"omi_account_ids" cty:"omi_account_ids"` + OMIGroups []string `mapstructure:"omi_groups" cty:"omi_groups"` + OMIProductCodes []string `mapstructure:"omi_product_codes" cty:"omi_product_codes"` + OMIRegions []string `mapstructure:"omi_regions" cty:"omi_regions"` + OMITags common.TagMap `mapstructure:"tags" cty:"tags"` + OMIForceDeregister *bool `mapstructure:"force_deregister" cty:"force_deregister"` + OMIForceDeleteSnapshot *bool `mapstructure:"force_delete_snapshot" cty:"force_delete_snapshot"` + SnapshotTags common.TagMap `mapstructure:"snapshot_tags" cty:"snapshot_tags"` + SnapshotAccountIDs []string `mapstructure:"snapshot_account_ids" cty:"snapshot_account_ids"` + SnapshotGroups []string `mapstructure:"snapshot_groups" cty:"snapshot_groups"` + RootDevice *FlatRootBlockDevice `mapstructure:"omi_root_device" cty:"omi_root_device"` + VolumeRunTags common.TagMap `mapstructure:"run_volume_tags" cty:"run_volume_tags"` +} + +// FlatMapstructure returns a new FlatConfig. +// FlatConfig is an auto-generated flat version of Config. +// Where the contents a fields with a `mapstructure:,squash` tag are bubbled up. +func (*Config) FlatMapstructure() interface{ HCL2Spec() map[string]hcldec.Spec } { + return new(FlatConfig) +} + +// HCL2Spec returns the hcl spec of a Config. +// This spec is used by HCL to read the fields of Config. +// The decoded values from this spec will then be applied to a FlatConfig. +func (*FlatConfig) HCL2Spec() map[string]hcldec.Spec { + s := map[string]hcldec.Spec{ + "packer_build_name": &hcldec.AttrSpec{Name: "packer_build_name", Type: cty.String, Required: false}, + "packer_builder_type": &hcldec.AttrSpec{Name: "packer_builder_type", Type: cty.String, Required: false}, + "packer_debug": &hcldec.AttrSpec{Name: "packer_debug", Type: cty.Bool, Required: false}, + "packer_force": &hcldec.AttrSpec{Name: "packer_force", Type: cty.Bool, Required: false}, + "packer_on_error": &hcldec.AttrSpec{Name: "packer_on_error", Type: cty.String, Required: false}, + "packer_user_variables": &hcldec.BlockAttrsSpec{TypeName: "packer_user_variables", ElementType: cty.String, Required: false}, + "packer_sensitive_variables": &hcldec.AttrSpec{Name: "packer_sensitive_variables", Type: cty.List(cty.String), Required: false}, + "access_key": &hcldec.AttrSpec{Name: "access_key", Type: cty.String, Required: false}, + "custom_endpoint_oapi": &hcldec.AttrSpec{Name: "custom_endpoint_oapi", Type: cty.String, Required: false}, + "insecure_skip_tls_verify": &hcldec.AttrSpec{Name: "insecure_skip_tls_verify", Type: cty.Bool, Required: false}, + "mfa_code": &hcldec.AttrSpec{Name: "mfa_code", Type: cty.String, Required: false}, + "profile": &hcldec.AttrSpec{Name: "profile", Type: cty.String, Required: false}, + "region": &hcldec.AttrSpec{Name: "region", Type: cty.String, Required: false}, + "secret_key": &hcldec.AttrSpec{Name: "secret_key", Type: cty.String, Required: false}, + "skip_region_validation": &hcldec.AttrSpec{Name: "skip_region_validation", Type: cty.Bool, Required: false}, + "skip_metadata_api_check": &hcldec.AttrSpec{Name: "skip_metadata_api_check", Type: cty.Bool, Required: false}, + "token": &hcldec.AttrSpec{Name: "token", Type: cty.String, Required: false}, + "associate_public_ip_address": &hcldec.AttrSpec{Name: "associate_public_ip_address", Type: cty.Bool, Required: false}, + "subregion_name": &hcldec.AttrSpec{Name: "subregion_name", Type: cty.String, Required: false}, + "block_duration_minutes": &hcldec.AttrSpec{Name: "block_duration_minutes", Type: cty.Number, Required: false}, + "disable_stop_vm": &hcldec.AttrSpec{Name: "disable_stop_vm", Type: cty.Bool, Required: false}, + "bsu_optimized": &hcldec.AttrSpec{Name: "bsu_optimized", Type: cty.Bool, Required: false}, + "enable_t2_unlimited": &hcldec.AttrSpec{Name: "enable_t2_unlimited", Type: cty.Bool, Required: false}, + "iam_vm_profile": &hcldec.AttrSpec{Name: "iam_vm_profile", Type: cty.String, Required: false}, + "shutdown_behavior": &hcldec.AttrSpec{Name: "shutdown_behavior", Type: cty.String, Required: false}, + "vm_type": &hcldec.AttrSpec{Name: "vm_type", Type: cty.String, Required: false}, + "security_group_filter": &hcldec.BlockSpec{TypeName: "security_group_filter", Nested: hcldec.ObjectSpec((*common.FlatSecurityGroupFilterOptions)(nil).HCL2Spec())}, + "run_tags": &hcldec.BlockAttrsSpec{TypeName: "run_tags", ElementType: cty.String, Required: false}, + "security_group_id": &hcldec.AttrSpec{Name: "security_group_id", Type: cty.String, Required: false}, + "security_group_ids": &hcldec.AttrSpec{Name: "security_group_ids", Type: cty.List(cty.String), Required: false}, + "source_omi": &hcldec.AttrSpec{Name: "source_omi", Type: cty.String, Required: false}, + "source_omi_filter": &hcldec.BlockSpec{TypeName: "source_omi_filter", Nested: hcldec.ObjectSpec((*common.FlatOmiFilterOptions)(nil).HCL2Spec())}, + "spot_price": &hcldec.AttrSpec{Name: "spot_price", Type: cty.String, Required: false}, + "spot_price_auto_product": &hcldec.AttrSpec{Name: "spot_price_auto_product", Type: cty.String, Required: false}, + "spot_tags": &hcldec.BlockAttrsSpec{TypeName: "spot_tags", ElementType: cty.String, Required: false}, + "subnet_filter": &hcldec.BlockSpec{TypeName: "subnet_filter", Nested: hcldec.ObjectSpec((*common.FlatSubnetFilterOptions)(nil).HCL2Spec())}, + "subnet_id": &hcldec.AttrSpec{Name: "subnet_id", Type: cty.String, Required: false}, + "temporary_key_pair_name": &hcldec.AttrSpec{Name: "temporary_key_pair_name", Type: cty.String, Required: false}, + "temporary_security_group_source_cidr": &hcldec.AttrSpec{Name: "temporary_security_group_source_cidr", Type: cty.String, Required: false}, + "user_data": &hcldec.AttrSpec{Name: "user_data", Type: cty.String, Required: false}, + "user_data_file": &hcldec.AttrSpec{Name: "user_data_file", Type: cty.String, Required: false}, + "net_filter": &hcldec.BlockSpec{TypeName: "net_filter", Nested: hcldec.ObjectSpec((*common.FlatNetFilterOptions)(nil).HCL2Spec())}, + "net_id": &hcldec.AttrSpec{Name: "net_id", Type: cty.String, Required: false}, + "windows_password_timeout": &hcldec.AttrSpec{Name: "windows_password_timeout", Type: cty.String, Required: false}, + "communicator": &hcldec.AttrSpec{Name: "communicator", Type: cty.String, Required: false}, + "pause_before_connecting": &hcldec.AttrSpec{Name: "pause_before_connecting", Type: cty.String, Required: false}, + "ssh_host": &hcldec.AttrSpec{Name: "ssh_host", Type: cty.String, Required: false}, + "ssh_port": &hcldec.AttrSpec{Name: "ssh_port", Type: cty.Number, Required: false}, + "ssh_username": &hcldec.AttrSpec{Name: "ssh_username", Type: cty.String, Required: false}, + "ssh_password": &hcldec.AttrSpec{Name: "ssh_password", Type: cty.String, Required: false}, + "ssh_keypair_name": &hcldec.AttrSpec{Name: "ssh_keypair_name", Type: cty.String, Required: false}, + "ssh_clear_authorized_keys": &hcldec.AttrSpec{Name: "ssh_clear_authorized_keys", Type: cty.Bool, Required: false}, + "ssh_private_key_file": &hcldec.AttrSpec{Name: "ssh_private_key_file", Type: cty.String, Required: false}, + "ssh_pty": &hcldec.AttrSpec{Name: "ssh_pty", Type: cty.Bool, Required: false}, + "ssh_timeout": &hcldec.AttrSpec{Name: "ssh_timeout", Type: cty.String, Required: false}, + "ssh_agent_auth": &hcldec.AttrSpec{Name: "ssh_agent_auth", Type: cty.Bool, Required: false}, + "ssh_disable_agent_forwarding": &hcldec.AttrSpec{Name: "ssh_disable_agent_forwarding", Type: cty.Bool, Required: false}, + "ssh_handshake_attempts": &hcldec.AttrSpec{Name: "ssh_handshake_attempts", Type: cty.Number, Required: false}, + "ssh_bastion_host": &hcldec.AttrSpec{Name: "ssh_bastion_host", Type: cty.String, Required: false}, + "ssh_bastion_port": &hcldec.AttrSpec{Name: "ssh_bastion_port", Type: cty.Number, Required: false}, + "ssh_bastion_agent_auth": &hcldec.AttrSpec{Name: "ssh_bastion_agent_auth", Type: cty.Bool, Required: false}, + "ssh_bastion_username": &hcldec.AttrSpec{Name: "ssh_bastion_username", Type: cty.String, Required: false}, + "ssh_bastion_password": &hcldec.AttrSpec{Name: "ssh_bastion_password", Type: cty.String, Required: false}, + "ssh_bastion_private_key_file": &hcldec.AttrSpec{Name: "ssh_bastion_private_key_file", Type: cty.String, Required: false}, + "ssh_file_transfer_method": &hcldec.AttrSpec{Name: "ssh_file_transfer_method", Type: cty.String, Required: false}, + "ssh_proxy_host": &hcldec.AttrSpec{Name: "ssh_proxy_host", Type: cty.String, Required: false}, + "ssh_proxy_port": &hcldec.AttrSpec{Name: "ssh_proxy_port", Type: cty.Number, Required: false}, + "ssh_proxy_username": &hcldec.AttrSpec{Name: "ssh_proxy_username", Type: cty.String, Required: false}, + "ssh_proxy_password": &hcldec.AttrSpec{Name: "ssh_proxy_password", Type: cty.String, Required: false}, + "ssh_keep_alive_interval": &hcldec.AttrSpec{Name: "ssh_keep_alive_interval", Type: cty.String, Required: false}, + "ssh_read_write_timeout": &hcldec.AttrSpec{Name: "ssh_read_write_timeout", Type: cty.String, Required: false}, + "ssh_remote_tunnels": &hcldec.AttrSpec{Name: "ssh_remote_tunnels", Type: cty.List(cty.String), Required: false}, + "ssh_local_tunnels": &hcldec.AttrSpec{Name: "ssh_local_tunnels", Type: cty.List(cty.String), Required: false}, + "ssh_public_key": &hcldec.AttrSpec{Name: "ssh_public_key", Type: cty.List(cty.Number), Required: false}, + "ssh_private_key": &hcldec.AttrSpec{Name: "ssh_private_key", Type: cty.List(cty.Number), Required: false}, + "winrm_username": &hcldec.AttrSpec{Name: "winrm_username", Type: cty.String, Required: false}, + "winrm_password": &hcldec.AttrSpec{Name: "winrm_password", Type: cty.String, Required: false}, + "winrm_host": &hcldec.AttrSpec{Name: "winrm_host", Type: cty.String, Required: false}, + "winrm_port": &hcldec.AttrSpec{Name: "winrm_port", Type: cty.Number, Required: false}, + "winrm_timeout": &hcldec.AttrSpec{Name: "winrm_timeout", Type: cty.String, Required: false}, + "winrm_use_ssl": &hcldec.AttrSpec{Name: "winrm_use_ssl", Type: cty.Bool, Required: false}, + "winrm_insecure": &hcldec.AttrSpec{Name: "winrm_insecure", Type: cty.Bool, Required: false}, + "winrm_use_ntlm": &hcldec.AttrSpec{Name: "winrm_use_ntlm", Type: cty.Bool, Required: false}, + "ssh_interface": &hcldec.AttrSpec{Name: "ssh_interface", Type: cty.String, Required: false}, + "omi_block_device_mappings": &hcldec.BlockListSpec{TypeName: "omi_block_device_mappings", Nested: hcldec.ObjectSpec((*common.FlatBlockDevice)(nil).HCL2Spec())}, + "launch_block_device_mappings": &hcldec.BlockListSpec{TypeName: "launch_block_device_mappings", Nested: hcldec.ObjectSpec((*common.FlatBlockDevice)(nil).HCL2Spec())}, + "omi_name": &hcldec.AttrSpec{Name: "omi_name", Type: cty.String, Required: false}, + "omi_description": &hcldec.AttrSpec{Name: "omi_description", Type: cty.String, Required: false}, + "omi_virtualization_type": &hcldec.AttrSpec{Name: "omi_virtualization_type", Type: cty.String, Required: false}, + "omi_account_ids": &hcldec.AttrSpec{Name: "omi_account_ids", Type: cty.List(cty.String), Required: false}, + "omi_groups": &hcldec.AttrSpec{Name: "omi_groups", Type: cty.List(cty.String), Required: false}, + "omi_product_codes": &hcldec.AttrSpec{Name: "omi_product_codes", Type: cty.List(cty.String), Required: false}, + "omi_regions": &hcldec.AttrSpec{Name: "omi_regions", Type: cty.List(cty.String), Required: false}, + "tags": &hcldec.BlockAttrsSpec{TypeName: "common.TagMap", ElementType: cty.String, Required: false}, + "force_deregister": &hcldec.AttrSpec{Name: "force_deregister", Type: cty.Bool, Required: false}, + "force_delete_snapshot": &hcldec.AttrSpec{Name: "force_delete_snapshot", Type: cty.Bool, Required: false}, + "snapshot_tags": &hcldec.BlockAttrsSpec{TypeName: "common.TagMap", ElementType: cty.String, Required: false}, + "snapshot_account_ids": &hcldec.AttrSpec{Name: "snapshot_account_ids", Type: cty.List(cty.String), Required: false}, + "snapshot_groups": &hcldec.AttrSpec{Name: "snapshot_groups", Type: cty.List(cty.String), Required: false}, + "omi_root_device": &hcldec.BlockSpec{TypeName: "omi_root_device", Nested: hcldec.ObjectSpec((*FlatRootBlockDevice)(nil).HCL2Spec())}, + "run_volume_tags": &hcldec.BlockAttrsSpec{TypeName: "common.TagMap", ElementType: cty.String, Required: false}, + } + return s +} + +// FlatRootBlockDevice is an auto-generated flat version of RootBlockDevice. +// Where the contents of a field with a `mapstructure:,squash` tag are bubbled up. +type FlatRootBlockDevice struct { + SourceDeviceName *string `mapstructure:"source_device_name" cty:"source_device_name"` + DeviceName *string `mapstructure:"device_name" cty:"device_name"` + DeleteOnVmDeletion *bool `mapstructure:"delete_on_vm_deletion" cty:"delete_on_vm_deletion"` + IOPS *int64 `mapstructure:"iops" cty:"iops"` + VolumeType *string `mapstructure:"volume_type" cty:"volume_type"` + VolumeSize *int64 `mapstructure:"volume_size" cty:"volume_size"` +} + +// FlatMapstructure returns a new FlatRootBlockDevice. +// FlatRootBlockDevice is an auto-generated flat version of RootBlockDevice. +// Where the contents a fields with a `mapstructure:,squash` tag are bubbled up. +func (*RootBlockDevice) FlatMapstructure() interface{ HCL2Spec() map[string]hcldec.Spec } { + return new(FlatRootBlockDevice) +} + +// HCL2Spec returns the hcl spec of a RootBlockDevice. +// This spec is used by HCL to read the fields of RootBlockDevice. +// The decoded values from this spec will then be applied to a FlatRootBlockDevice. +func (*FlatRootBlockDevice) HCL2Spec() map[string]hcldec.Spec { + s := map[string]hcldec.Spec{ + "source_device_name": &hcldec.AttrSpec{Name: "source_device_name", Type: cty.String, Required: false}, + "device_name": &hcldec.AttrSpec{Name: "device_name", Type: cty.String, Required: false}, + "delete_on_vm_deletion": &hcldec.AttrSpec{Name: "delete_on_vm_deletion", Type: cty.Bool, Required: false}, + "iops": &hcldec.AttrSpec{Name: "iops", Type: cty.Number, Required: false}, + "volume_type": &hcldec.AttrSpec{Name: "volume_type", Type: cty.String, Required: false}, + "volume_size": &hcldec.AttrSpec{Name: "volume_size", Type: cty.Number, Required: false}, + } + return s +} diff --git a/builder/osc/bsuvolume/builder.go b/builder/osc/bsuvolume/builder.go index e0b5a8fa7..b40503f60 100644 --- a/builder/osc/bsuvolume/builder.go +++ b/builder/osc/bsuvolume/builder.go @@ -11,6 +11,7 @@ import ( "log" "net/http" + "github.com/hashicorp/hcl/v2/hcldec" osccommon "github.com/hashicorp/packer/builder/osc/common" "github.com/hashicorp/packer/common" "github.com/hashicorp/packer/helper/communicator" @@ -44,6 +45,8 @@ type EngineVarsTemplate struct { SourceOMI string } +func (b *Builder) ConfigSpec() hcldec.ObjectSpec { return b.config.FlatMapstructure().HCL2Spec() } + func (b *Builder) Prepare(raws ...interface{}) ([]string, error) { b.config.ctx.Funcs = osccommon.TemplateFuncs // Create passthrough for {{ .BuildRegion }} and {{ .SourceOMI }} variables diff --git a/builder/osc/bsuvolume/builder.hcl2spec.go b/builder/osc/bsuvolume/builder.hcl2spec.go index 70789c634..ea8881b61 100644 --- a/builder/osc/bsuvolume/builder.hcl2spec.go +++ b/builder/osc/bsuvolume/builder.hcl2spec.go @@ -24,10 +24,13 @@ type FlatBlockDevice struct { // FlatMapstructure returns a new FlatBlockDevice. // FlatBlockDevice is an auto-generated flat version of BlockDevice. // Where the contents a fields with a `mapstructure:,squash` tag are bubbled up. -func (*BlockDevice) FlatMapstructure() interface{} { return new(FlatBlockDevice) } +func (*BlockDevice) FlatMapstructure() interface{ HCL2Spec() map[string]hcldec.Spec } { + return new(FlatBlockDevice) +} -// HCL2Spec returns the hcldec.Spec of a FlatBlockDevice. -// This spec is used by HCL to read the fields of FlatBlockDevice. +// HCL2Spec returns the hcl spec of a BlockDevice. +// This spec is used by HCL to read the fields of BlockDevice. +// The decoded values from this spec will then be applied to a FlatBlockDevice. func (*FlatBlockDevice) HCL2Spec() map[string]hcldec.Spec { s := map[string]hcldec.Spec{ "delete_on_vm_deletion": &hcldec.AttrSpec{Name: "delete_on_vm_deletion", Type: cty.Bool, Required: false}, @@ -119,8 +122,8 @@ type FlatConfig struct { SSHReadWriteTimeout *string `mapstructure:"ssh_read_write_timeout" cty:"ssh_read_write_timeout"` SSHRemoteTunnels []string `mapstructure:"ssh_remote_tunnels" cty:"ssh_remote_tunnels"` SSHLocalTunnels []string `mapstructure:"ssh_local_tunnels" cty:"ssh_local_tunnels"` - SSHPublicKey []byte `cty:"ssh_public_key"` - SSHPrivateKey []byte `cty:"ssh_private_key"` + SSHPublicKey []byte `mapstructure:"ssh_public_key" cty:"ssh_public_key"` + SSHPrivateKey []byte `mapstructure:"ssh_private_key" cty:"ssh_private_key"` WinRMUser *string `mapstructure:"winrm_username" cty:"winrm_username"` WinRMPassword *string `mapstructure:"winrm_password" cty:"winrm_password"` WinRMHost *string `mapstructure:"winrm_host" cty:"winrm_host"` @@ -136,10 +139,13 @@ type FlatConfig struct { // FlatMapstructure returns a new FlatConfig. // FlatConfig is an auto-generated flat version of Config. // Where the contents a fields with a `mapstructure:,squash` tag are bubbled up. -func (*Config) FlatMapstructure() interface{} { return new(FlatConfig) } +func (*Config) FlatMapstructure() interface{ HCL2Spec() map[string]hcldec.Spec } { + return new(FlatConfig) +} -// HCL2Spec returns the hcldec.Spec of a FlatConfig. -// This spec is used by HCL to read the fields of FlatConfig. +// HCL2Spec returns the hcl spec of a Config. +// This spec is used by HCL to read the fields of Config. +// The decoded values from this spec will then be applied to a FlatConfig. func (*FlatConfig) HCL2Spec() map[string]hcldec.Spec { s := map[string]hcldec.Spec{ "packer_build_name": &hcldec.AttrSpec{Name: "packer_build_name", Type: cty.String, Required: false}, @@ -226,7 +232,7 @@ func (*FlatConfig) HCL2Spec() map[string]hcldec.Spec { "winrm_insecure": &hcldec.AttrSpec{Name: "winrm_insecure", Type: cty.Bool, Required: false}, "winrm_use_ntlm": &hcldec.AttrSpec{Name: "winrm_use_ntlm", Type: cty.Bool, Required: false}, "ssh_interface": &hcldec.AttrSpec{Name: "ssh_interface", Type: cty.String, Required: false}, - "bsu_volumes": &hcldec.BlockListSpec{TypeName: "bsu_volumes", Nested: &hcldec.BlockSpec{TypeName: "bsu_volumes", Nested: hcldec.ObjectSpec((*FlatBlockDevice)(nil).HCL2Spec())}}, + "bsu_volumes": &hcldec.BlockListSpec{TypeName: "bsu_volumes", Nested: hcldec.ObjectSpec((*FlatBlockDevice)(nil).HCL2Spec())}, } return s } diff --git a/builder/osc/chroot/builder.go b/builder/osc/chroot/builder.go index e6813d263..939ef4f4f 100644 --- a/builder/osc/chroot/builder.go +++ b/builder/osc/chroot/builder.go @@ -13,6 +13,7 @@ import ( "net/http" "runtime" + "github.com/hashicorp/hcl/v2/hcldec" osccommon "github.com/hashicorp/packer/builder/osc/common" "github.com/hashicorp/packer/common" "github.com/hashicorp/packer/helper/config" @@ -63,6 +64,8 @@ type Builder struct { runner multistep.Runner } +func (b *Builder) ConfigSpec() hcldec.ObjectSpec { return b.config.FlatMapstructure().HCL2Spec() } + func (b *Builder) Prepare(raws ...interface{}) ([]string, error) { b.config.ctx.Funcs = osccommon.TemplateFuncs err := config.Decode(&b.config, &config.DecodeOpts{ diff --git a/builder/osc/chroot/builder.hcl2spec.go b/builder/osc/chroot/builder.hcl2spec.go index 0404b12c1..b8286e162 100644 --- a/builder/osc/chroot/builder.hcl2spec.go +++ b/builder/osc/chroot/builder.hcl2spec.go @@ -63,10 +63,13 @@ type FlatConfig struct { // FlatMapstructure returns a new FlatConfig. // FlatConfig is an auto-generated flat version of Config. // Where the contents a fields with a `mapstructure:,squash` tag are bubbled up. -func (*Config) FlatMapstructure() interface{} { return new(FlatConfig) } +func (*Config) FlatMapstructure() interface{ HCL2Spec() map[string]hcldec.Spec } { + return new(FlatConfig) +} -// HCL2Spec returns the hcldec.Spec of a FlatConfig. -// This spec is used by HCL to read the fields of FlatConfig. +// HCL2Spec returns the hcl spec of a Config. +// This spec is used by HCL to read the fields of Config. +// The decoded values from this spec will then be applied to a FlatConfig. func (*FlatConfig) HCL2Spec() map[string]hcldec.Spec { s := map[string]hcldec.Spec{ "packer_build_name": &hcldec.AttrSpec{Name: "packer_build_name", Type: cty.String, Required: false}, @@ -76,7 +79,7 @@ func (*FlatConfig) HCL2Spec() map[string]hcldec.Spec { "packer_on_error": &hcldec.AttrSpec{Name: "packer_on_error", Type: cty.String, Required: false}, "packer_user_variables": &hcldec.BlockAttrsSpec{TypeName: "packer_user_variables", ElementType: cty.String, Required: false}, "packer_sensitive_variables": &hcldec.AttrSpec{Name: "packer_sensitive_variables", Type: cty.List(cty.String), Required: false}, - "omi_block_device_mappings": &hcldec.BlockListSpec{TypeName: "omi_block_device_mappings", Nested: &hcldec.BlockSpec{TypeName: "omi_block_device_mappings", Nested: hcldec.ObjectSpec((*common.FlatBlockDevice)(nil).HCL2Spec())}}, + "omi_block_device_mappings": &hcldec.BlockListSpec{TypeName: "omi_block_device_mappings", Nested: hcldec.ObjectSpec((*common.FlatBlockDevice)(nil).HCL2Spec())}, "omi_name": &hcldec.AttrSpec{Name: "omi_name", Type: cty.String, Required: false}, "omi_description": &hcldec.AttrSpec{Name: "omi_description", Type: cty.String, Required: false}, "omi_virtualization_type": &hcldec.AttrSpec{Name: "omi_virtualization_type", Type: cty.String, Required: false}, diff --git a/builder/osc/common/run_config.hcl2spec.go b/builder/osc/common/run_config.hcl2spec.go index 53edc1f9f..a61eaac64 100644 --- a/builder/osc/common/run_config.hcl2spec.go +++ b/builder/osc/common/run_config.hcl2spec.go @@ -22,10 +22,13 @@ type FlatBlockDevice struct { // FlatMapstructure returns a new FlatBlockDevice. // FlatBlockDevice is an auto-generated flat version of BlockDevice. // Where the contents a fields with a `mapstructure:,squash` tag are bubbled up. -func (*BlockDevice) FlatMapstructure() interface{} { return new(FlatBlockDevice) } +func (*BlockDevice) FlatMapstructure() interface{ HCL2Spec() map[string]hcldec.Spec } { + return new(FlatBlockDevice) +} -// HCL2Spec returns the hcldec.Spec of a FlatBlockDevice. -// This spec is used by HCL to read the fields of FlatBlockDevice. +// HCL2Spec returns the hcl spec of a BlockDevice. +// This spec is used by HCL to read the fields of BlockDevice. +// The decoded values from this spec will then be applied to a FlatBlockDevice. func (*FlatBlockDevice) HCL2Spec() map[string]hcldec.Spec { s := map[string]hcldec.Spec{ "delete_on_vm_deletion": &hcldec.AttrSpec{Name: "delete_on_vm_deletion", Type: cty.Bool, Required: false}, @@ -49,10 +52,13 @@ type FlatNetFilterOptions struct { // FlatMapstructure returns a new FlatNetFilterOptions. // FlatNetFilterOptions is an auto-generated flat version of NetFilterOptions. // Where the contents a fields with a `mapstructure:,squash` tag are bubbled up. -func (*NetFilterOptions) FlatMapstructure() interface{} { return new(FlatNetFilterOptions) } +func (*NetFilterOptions) FlatMapstructure() interface{ HCL2Spec() map[string]hcldec.Spec } { + return new(FlatNetFilterOptions) +} -// HCL2Spec returns the hcldec.Spec of a FlatNetFilterOptions. -// This spec is used by HCL to read the fields of FlatNetFilterOptions. +// HCL2Spec returns the hcl spec of a NetFilterOptions. +// This spec is used by HCL to read the fields of NetFilterOptions. +// The decoded values from this spec will then be applied to a FlatNetFilterOptions. func (*FlatNetFilterOptions) HCL2Spec() map[string]hcldec.Spec { s := map[string]hcldec.Spec{ "filters": &hcldec.BlockAttrsSpec{TypeName: "filters", ElementType: cty.String, Required: false}, @@ -71,10 +77,13 @@ type FlatOmiFilterOptions struct { // FlatMapstructure returns a new FlatOmiFilterOptions. // FlatOmiFilterOptions is an auto-generated flat version of OmiFilterOptions. // Where the contents a fields with a `mapstructure:,squash` tag are bubbled up. -func (*OmiFilterOptions) FlatMapstructure() interface{} { return new(FlatOmiFilterOptions) } +func (*OmiFilterOptions) FlatMapstructure() interface{ HCL2Spec() map[string]hcldec.Spec } { + return new(FlatOmiFilterOptions) +} -// HCL2Spec returns the hcldec.Spec of a FlatOmiFilterOptions. -// This spec is used by HCL to read the fields of FlatOmiFilterOptions. +// HCL2Spec returns the hcl spec of a OmiFilterOptions. +// This spec is used by HCL to read the fields of OmiFilterOptions. +// The decoded values from this spec will then be applied to a FlatOmiFilterOptions. func (*FlatOmiFilterOptions) HCL2Spec() map[string]hcldec.Spec { s := map[string]hcldec.Spec{ "filters": &hcldec.BlockAttrsSpec{TypeName: "filters", ElementType: cty.String, Required: false}, @@ -93,12 +102,13 @@ type FlatSecurityGroupFilterOptions struct { // FlatMapstructure returns a new FlatSecurityGroupFilterOptions. // FlatSecurityGroupFilterOptions is an auto-generated flat version of SecurityGroupFilterOptions. // Where the contents a fields with a `mapstructure:,squash` tag are bubbled up. -func (*SecurityGroupFilterOptions) FlatMapstructure() interface{} { +func (*SecurityGroupFilterOptions) FlatMapstructure() interface{ HCL2Spec() map[string]hcldec.Spec } { return new(FlatSecurityGroupFilterOptions) } -// HCL2Spec returns the hcldec.Spec of a FlatSecurityGroupFilterOptions. -// This spec is used by HCL to read the fields of FlatSecurityGroupFilterOptions. +// HCL2Spec returns the hcl spec of a SecurityGroupFilterOptions. +// This spec is used by HCL to read the fields of SecurityGroupFilterOptions. +// The decoded values from this spec will then be applied to a FlatSecurityGroupFilterOptions. func (*FlatSecurityGroupFilterOptions) HCL2Spec() map[string]hcldec.Spec { s := map[string]hcldec.Spec{ "filters": &hcldec.BlockAttrsSpec{TypeName: "filters", ElementType: cty.String, Required: false}, @@ -117,10 +127,13 @@ type FlatSubnetFilterOptions struct { // FlatMapstructure returns a new FlatSubnetFilterOptions. // FlatSubnetFilterOptions is an auto-generated flat version of SubnetFilterOptions. // Where the contents a fields with a `mapstructure:,squash` tag are bubbled up. -func (*SubnetFilterOptions) FlatMapstructure() interface{} { return new(FlatSubnetFilterOptions) } +func (*SubnetFilterOptions) FlatMapstructure() interface{ HCL2Spec() map[string]hcldec.Spec } { + return new(FlatSubnetFilterOptions) +} -// HCL2Spec returns the hcldec.Spec of a FlatSubnetFilterOptions. -// This spec is used by HCL to read the fields of FlatSubnetFilterOptions. +// HCL2Spec returns the hcl spec of a SubnetFilterOptions. +// This spec is used by HCL to read the fields of SubnetFilterOptions. +// The decoded values from this spec will then be applied to a FlatSubnetFilterOptions. func (*FlatSubnetFilterOptions) HCL2Spec() map[string]hcldec.Spec { s := map[string]hcldec.Spec{ "filters": &hcldec.BlockAttrsSpec{TypeName: "filters", ElementType: cty.String, Required: false}, diff --git a/builder/parallels/iso/builder.go b/builder/parallels/iso/builder.go index b7ea4788f..e5afcc2c5 100644 --- a/builder/parallels/iso/builder.go +++ b/builder/parallels/iso/builder.go @@ -8,6 +8,7 @@ import ( "errors" "fmt" + "github.com/hashicorp/hcl/v2/hcldec" parallelscommon "github.com/hashicorp/packer/builder/parallels/common" "github.com/hashicorp/packer/common" "github.com/hashicorp/packer/common/bootcommand" @@ -82,6 +83,8 @@ type Config struct { ctx interpolate.Context } +func (b *Builder) ConfigSpec() hcldec.ObjectSpec { return b.config.FlatMapstructure().HCL2Spec() } + func (b *Builder) Prepare(raws ...interface{}) ([]string, error) { err := config.Decode(&b.config, &config.DecodeOpts{ Interpolate: true, diff --git a/builder/parallels/iso/builder.hcl2spec.go b/builder/parallels/iso/builder.hcl2spec.go index e8e6e506c..0a3bf313b 100644 --- a/builder/parallels/iso/builder.hcl2spec.go +++ b/builder/parallels/iso/builder.hcl2spec.go @@ -72,8 +72,8 @@ type FlatConfig struct { SSHReadWriteTimeout *string `mapstructure:"ssh_read_write_timeout" cty:"ssh_read_write_timeout"` SSHRemoteTunnels []string `mapstructure:"ssh_remote_tunnels" cty:"ssh_remote_tunnels"` SSHLocalTunnels []string `mapstructure:"ssh_local_tunnels" cty:"ssh_local_tunnels"` - SSHPublicKey []byte `cty:"ssh_public_key"` - SSHPrivateKey []byte `cty:"ssh_private_key"` + SSHPublicKey []byte `mapstructure:"ssh_public_key" cty:"ssh_public_key"` + SSHPrivateKey []byte `mapstructure:"ssh_private_key" cty:"ssh_private_key"` WinRMUser *string `mapstructure:"winrm_username" cty:"winrm_username"` WinRMPassword *string `mapstructure:"winrm_password" cty:"winrm_password"` WinRMHost *string `mapstructure:"winrm_host" cty:"winrm_host"` @@ -98,10 +98,13 @@ type FlatConfig struct { // FlatMapstructure returns a new FlatConfig. // FlatConfig is an auto-generated flat version of Config. // Where the contents a fields with a `mapstructure:,squash` tag are bubbled up. -func (*Config) FlatMapstructure() interface{} { return new(FlatConfig) } +func (*Config) FlatMapstructure() interface{ HCL2Spec() map[string]hcldec.Spec } { + return new(FlatConfig) +} -// HCL2Spec returns the hcldec.Spec of a FlatConfig. -// This spec is used by HCL to read the fields of FlatConfig. +// HCL2Spec returns the hcl spec of a Config. +// This spec is used by HCL to read the fields of Config. +// The decoded values from this spec will then be applied to a FlatConfig. func (*FlatConfig) HCL2Spec() map[string]hcldec.Spec { s := map[string]hcldec.Spec{ "packer_build_name": &hcldec.AttrSpec{Name: "packer_build_name", Type: cty.String, Required: false}, diff --git a/builder/parallels/pvm/builder.go b/builder/parallels/pvm/builder.go index a199650d1..41a118eeb 100644 --- a/builder/parallels/pvm/builder.go +++ b/builder/parallels/pvm/builder.go @@ -5,6 +5,7 @@ import ( "errors" "fmt" + "github.com/hashicorp/hcl/v2/hcldec" parallelscommon "github.com/hashicorp/packer/builder/parallels/common" "github.com/hashicorp/packer/common" "github.com/hashicorp/packer/helper/communicator" @@ -15,17 +16,17 @@ import ( // Builder implements packer.Builder and builds the actual Parallels // images. type Builder struct { - config *Config + config Config runner multistep.Runner } -// Prepare processes the build configuration parameters. +func (b *Builder) ConfigSpec() hcldec.ObjectSpec { return b.config.FlatMapstructure().HCL2Spec() } + func (b *Builder) Prepare(raws ...interface{}) ([]string, error) { - c, warnings, errs := NewConfig(raws...) + warnings, errs := b.config.Prepare(raws...) if errs != nil { return warnings, errs } - b.config = c return warnings, nil } diff --git a/builder/parallels/pvm/config.go b/builder/parallels/pvm/config.go index 3fffe7b98..4f88ee193 100644 --- a/builder/parallels/pvm/config.go +++ b/builder/parallels/pvm/config.go @@ -49,8 +49,7 @@ type Config struct { ctx interpolate.Context } -func NewConfig(raws ...interface{}) (*Config, []string, error) { - c := new(Config) +func (c *Config) Prepare(raws ...interface{}) ([]string, error) { err := config.Decode(c, &config.DecodeOpts{ Interpolate: true, InterpolateContext: &c.ctx, @@ -64,7 +63,7 @@ func NewConfig(raws ...interface{}) (*Config, []string, error) { }, }, raws...) if err != nil { - return nil, nil, err + return nil, err } if c.VMName == "" { @@ -102,8 +101,8 @@ func NewConfig(raws ...interface{}) (*Config, []string, error) { // Check for any errors. if errs != nil && len(errs.Errors) > 0 { - return nil, warnings, errs + return warnings, errs } - return c, warnings, nil + return warnings, nil } diff --git a/builder/parallels/pvm/config.hcl2spec.go b/builder/parallels/pvm/config.hcl2spec.go index 77d76625f..d15c6e839 100644 --- a/builder/parallels/pvm/config.hcl2spec.go +++ b/builder/parallels/pvm/config.hcl2spec.go @@ -53,8 +53,8 @@ type FlatConfig struct { SSHReadWriteTimeout *string `mapstructure:"ssh_read_write_timeout" cty:"ssh_read_write_timeout"` SSHRemoteTunnels []string `mapstructure:"ssh_remote_tunnels" cty:"ssh_remote_tunnels"` SSHLocalTunnels []string `mapstructure:"ssh_local_tunnels" cty:"ssh_local_tunnels"` - SSHPublicKey []byte `cty:"ssh_public_key"` - SSHPrivateKey []byte `cty:"ssh_private_key"` + SSHPublicKey []byte `mapstructure:"ssh_public_key" cty:"ssh_public_key"` + SSHPrivateKey []byte `mapstructure:"ssh_private_key" cty:"ssh_private_key"` WinRMUser *string `mapstructure:"winrm_username" cty:"winrm_username"` WinRMPassword *string `mapstructure:"winrm_password" cty:"winrm_password"` WinRMHost *string `mapstructure:"winrm_host" cty:"winrm_host"` @@ -81,10 +81,13 @@ type FlatConfig struct { // FlatMapstructure returns a new FlatConfig. // FlatConfig is an auto-generated flat version of Config. // Where the contents a fields with a `mapstructure:,squash` tag are bubbled up. -func (*Config) FlatMapstructure() interface{} { return new(FlatConfig) } +func (*Config) FlatMapstructure() interface{ HCL2Spec() map[string]hcldec.Spec } { + return new(FlatConfig) +} -// HCL2Spec returns the hcldec.Spec of a FlatConfig. -// This spec is used by HCL to read the fields of FlatConfig. +// HCL2Spec returns the hcl spec of a Config. +// This spec is used by HCL to read the fields of Config. +// The decoded values from this spec will then be applied to a FlatConfig. func (*FlatConfig) HCL2Spec() map[string]hcldec.Spec { s := map[string]hcldec.Spec{ "packer_build_name": &hcldec.AttrSpec{Name: "packer_build_name", Type: cty.String, Required: false}, diff --git a/builder/parallels/pvm/config_test.go b/builder/parallels/pvm/config_test.go index 8c869c9fd..e7a2a5d59 100644 --- a/builder/parallels/pvm/config_test.go +++ b/builder/parallels/pvm/config_test.go @@ -53,13 +53,13 @@ func TestNewConfig_sourcePath(t *testing.T) { // Bad c := testConfig(t) delete(c, "source_path") - _, warns, errs := NewConfig(c) + warns, errs := (&Config{}).Prepare(c) testConfigErr(t, warns, errs) // Bad c = testConfig(t) c["source_path"] = "/i/dont/exist" - _, warns, errs = NewConfig(c) + warns, errs = (&Config{}).Prepare(c) testConfigErr(t, warns, errs) // Good @@ -68,7 +68,7 @@ func TestNewConfig_sourcePath(t *testing.T) { c = testConfig(t) c["source_path"] = tf.Name() - _, warns, errs = NewConfig(c) + warns, errs = (&Config{}).Prepare(c) testConfigOk(t, warns, errs) } @@ -76,7 +76,7 @@ func TestNewConfig_FloppyFiles(t *testing.T) { c := testConfig(t) floppies_path := "../../../common/test-fixtures/floppies" c["floppy_files"] = []string{fmt.Sprintf("%s/bar.bat", floppies_path), fmt.Sprintf("%s/foo.ps1", floppies_path)} - _, _, err := NewConfig(c) + _, err := (&Config{}).Prepare(c) if err != nil { t.Fatalf("should not have error: %s", err) } @@ -85,7 +85,7 @@ func TestNewConfig_FloppyFiles(t *testing.T) { func TestNewConfig_InvalidFloppies(t *testing.T) { c := testConfig(t) c["floppy_files"] = []string{"nonexistent.bat", "nonexistent.ps1"} - _, _, errs := NewConfig(c) + _, errs := (&Config{}).Prepare(c) if errs == nil { t.Fatalf("Nonexistent floppies should trigger multierror") } @@ -96,18 +96,18 @@ func TestNewConfig_InvalidFloppies(t *testing.T) { } func TestNewConfig_shutdown_timeout(t *testing.T) { - c := testConfig(t) + cfg := testConfig(t) tf := getTempFile(t) defer os.Remove(tf.Name()) // Expect this to fail - c["source_path"] = tf.Name() - c["shutdown_timeout"] = "NaN" - _, warns, errs := NewConfig(c) + cfg["source_path"] = tf.Name() + cfg["shutdown_timeout"] = "NaN" + warns, errs := (&Config{}).Prepare(cfg) testConfigErr(t, warns, errs) // Passes when given a valid time duration - c["shutdown_timeout"] = "10s" - _, warns, errs = NewConfig(c) + cfg["shutdown_timeout"] = "10s" + warns, errs = (&Config{}).Prepare(cfg) testConfigOk(t, warns, errs) } diff --git a/builder/profitbricks/builder.go b/builder/profitbricks/builder.go index 3ab9d18fe..6c5cc4e61 100644 --- a/builder/profitbricks/builder.go +++ b/builder/profitbricks/builder.go @@ -4,6 +4,7 @@ import ( "context" "fmt" + "github.com/hashicorp/hcl/v2/hcldec" "github.com/hashicorp/packer/common" "github.com/hashicorp/packer/helper/communicator" "github.com/hashicorp/packer/helper/multistep" @@ -13,16 +14,17 @@ import ( const BuilderId = "packer.profitbricks" type Builder struct { - config *Config + config Config runner multistep.Runner } +func (b *Builder) ConfigSpec() hcldec.ObjectSpec { return b.config.FlatMapstructure().HCL2Spec() } + func (b *Builder) Prepare(raws ...interface{}) ([]string, error) { - c, warnings, errs := NewConfig(raws...) + warnings, errs := b.config.Prepare(raws...) if errs != nil { return warnings, errs } - b.config = c return warnings, nil } diff --git a/builder/profitbricks/config.go b/builder/profitbricks/config.go index f3d366c60..275f7b5f2 100644 --- a/builder/profitbricks/config.go +++ b/builder/profitbricks/config.go @@ -34,8 +34,7 @@ type Config struct { ctx interpolate.Context } -func NewConfig(raws ...interface{}) (*Config, []string, error) { - var c Config +func (c *Config) Prepare(raws ...interface{}) ([]string, error) { var md mapstructure.Metadata err := config.Decode(&c, &config.DecodeOpts{ @@ -49,7 +48,7 @@ func NewConfig(raws ...interface{}) (*Config, []string, error) { }, }, raws...) if err != nil { - return nil, nil, err + return nil, err } var errs *packer.MultiError @@ -121,9 +120,9 @@ func NewConfig(raws ...interface{}) (*Config, []string, error) { } if errs != nil && len(errs.Errors) > 0 { - return nil, nil, errs + return nil, errs } packer.LogSecretFilter.Set(c.PBUsername) - return &c, nil, nil + return nil, nil } diff --git a/builder/profitbricks/config.hcl2spec.go b/builder/profitbricks/config.hcl2spec.go index c0867c500..1dc8dc7c1 100644 --- a/builder/profitbricks/config.hcl2spec.go +++ b/builder/profitbricks/config.hcl2spec.go @@ -46,8 +46,8 @@ type FlatConfig struct { SSHReadWriteTimeout *string `mapstructure:"ssh_read_write_timeout" cty:"ssh_read_write_timeout"` SSHRemoteTunnels []string `mapstructure:"ssh_remote_tunnels" cty:"ssh_remote_tunnels"` SSHLocalTunnels []string `mapstructure:"ssh_local_tunnels" cty:"ssh_local_tunnels"` - SSHPublicKey []byte `cty:"ssh_public_key"` - SSHPrivateKey []byte `cty:"ssh_private_key"` + SSHPublicKey []byte `mapstructure:"ssh_public_key" cty:"ssh_public_key"` + SSHPrivateKey []byte `mapstructure:"ssh_private_key" cty:"ssh_private_key"` WinRMUser *string `mapstructure:"winrm_username" cty:"winrm_username"` WinRMPassword *string `mapstructure:"winrm_password" cty:"winrm_password"` WinRMHost *string `mapstructure:"winrm_host" cty:"winrm_host"` @@ -73,10 +73,13 @@ type FlatConfig struct { // FlatMapstructure returns a new FlatConfig. // FlatConfig is an auto-generated flat version of Config. // Where the contents a fields with a `mapstructure:,squash` tag are bubbled up. -func (*Config) FlatMapstructure() interface{} { return new(FlatConfig) } +func (*Config) FlatMapstructure() interface{ HCL2Spec() map[string]hcldec.Spec } { + return new(FlatConfig) +} -// HCL2Spec returns the hcldec.Spec of a FlatConfig. -// This spec is used by HCL to read the fields of FlatConfig. +// HCL2Spec returns the hcl spec of a Config. +// This spec is used by HCL to read the fields of Config. +// The decoded values from this spec will then be applied to a FlatConfig. func (*FlatConfig) HCL2Spec() map[string]hcldec.Spec { s := map[string]hcldec.Spec{ "packer_build_name": &hcldec.AttrSpec{Name: "packer_build_name", Type: cty.String, Required: false}, diff --git a/builder/proxmox/builder.go b/builder/proxmox/builder.go index 083cfeb3d..e671e67cb 100644 --- a/builder/proxmox/builder.go +++ b/builder/proxmox/builder.go @@ -7,6 +7,7 @@ import ( "fmt" "github.com/Telmate/proxmox-api-go/proxmox" + "github.com/hashicorp/hcl/v2/hcldec" "github.com/hashicorp/packer/common" "github.com/hashicorp/packer/helper/communicator" "github.com/hashicorp/packer/helper/multistep" @@ -27,12 +28,14 @@ var _ packer.Builder = &Builder{} var pluginVersion = "1.0.0" +func (b *Builder) ConfigSpec() hcldec.ObjectSpec { return b.config.FlatMapstructure().HCL2Spec() } + func (b *Builder) Prepare(raws ...interface{}) ([]string, error) { - config, warnings, errs := NewConfig(raws...) + warnings, errs := b.config.Prepare(raws...) if errs != nil { return warnings, errs } - b.config = *config + return nil, nil } diff --git a/builder/proxmox/config.go b/builder/proxmox/config.go index b3c08ecaf..00f082d07 100644 --- a/builder/proxmox/config.go +++ b/builder/proxmox/config.go @@ -71,8 +71,7 @@ type diskConfig struct { DiskFormat string `mapstructure:"format"` } -func NewConfig(raws ...interface{}) (*Config, []string, error) { - c := new(Config) +func (c *Config) Prepare(raws ...interface{}) ([]string, error) { // Agent defaults to true c.Agent = true @@ -88,7 +87,7 @@ func NewConfig(raws ...interface{}) (*Config, []string, error) { }, }, raws...) if err != nil { - return nil, nil, err + return nil, err } var errs *packer.MultiError @@ -207,11 +206,11 @@ func NewConfig(raws ...interface{}) (*Config, []string, error) { } if errs != nil && len(errs.Errors) > 0 { - return nil, nil, errs + return nil, errs } packer.LogSecretFilter.Set(c.Password) - return c, nil, nil + return nil, nil } func contains(haystack []string, needle string) bool { diff --git a/builder/proxmox/config.hcl2spec.go b/builder/proxmox/config.hcl2spec.go index 797bf117b..e9c208c12 100644 --- a/builder/proxmox/config.hcl2spec.go +++ b/builder/proxmox/config.hcl2spec.go @@ -53,8 +53,8 @@ type FlatConfig struct { SSHReadWriteTimeout *string `mapstructure:"ssh_read_write_timeout" cty:"ssh_read_write_timeout"` SSHRemoteTunnels []string `mapstructure:"ssh_remote_tunnels" cty:"ssh_remote_tunnels"` SSHLocalTunnels []string `mapstructure:"ssh_local_tunnels" cty:"ssh_local_tunnels"` - SSHPublicKey []byte `cty:"ssh_public_key"` - SSHPrivateKey []byte `cty:"ssh_private_key"` + SSHPublicKey []byte `mapstructure:"ssh_public_key" cty:"ssh_public_key"` + SSHPrivateKey []byte `mapstructure:"ssh_private_key" cty:"ssh_private_key"` WinRMUser *string `mapstructure:"winrm_username" cty:"winrm_username"` WinRMPassword *string `mapstructure:"winrm_password" cty:"winrm_password"` WinRMHost *string `mapstructure:"winrm_host" cty:"winrm_host"` @@ -89,10 +89,13 @@ type FlatConfig struct { // FlatMapstructure returns a new FlatConfig. // FlatConfig is an auto-generated flat version of Config. // Where the contents a fields with a `mapstructure:,squash` tag are bubbled up. -func (*Config) FlatMapstructure() interface{} { return new(FlatConfig) } +func (*Config) FlatMapstructure() interface{ HCL2Spec() map[string]hcldec.Spec } { + return new(FlatConfig) +} -// HCL2Spec returns the hcldec.Spec of a FlatConfig. -// This spec is used by HCL to read the fields of FlatConfig. +// HCL2Spec returns the hcl spec of a Config. +// This spec is used by HCL to read the fields of Config. +// The decoded values from this spec will then be applied to a FlatConfig. func (*FlatConfig) HCL2Spec() map[string]hcldec.Spec { s := map[string]hcldec.Spec{ "packer_build_name": &hcldec.AttrSpec{Name: "packer_build_name", Type: cty.String, Required: false}, @@ -162,8 +165,8 @@ func (*FlatConfig) HCL2Spec() map[string]hcldec.Spec { "cpu_type": &hcldec.AttrSpec{Name: "cpu_type", Type: cty.String, Required: false}, "sockets": &hcldec.AttrSpec{Name: "sockets", Type: cty.Number, Required: false}, "os": &hcldec.AttrSpec{Name: "os", Type: cty.String, Required: false}, - "network_adapters": &hcldec.BlockListSpec{TypeName: "network_adapters", Nested: &hcldec.BlockSpec{TypeName: "network_adapters", Nested: hcldec.ObjectSpec((*FlatnicConfig)(nil).HCL2Spec())}}, - "disks": &hcldec.BlockListSpec{TypeName: "disks", Nested: &hcldec.BlockSpec{TypeName: "disks", Nested: hcldec.ObjectSpec((*FlatdiskConfig)(nil).HCL2Spec())}}, + "network_adapters": &hcldec.BlockListSpec{TypeName: "network_adapters", Nested: hcldec.ObjectSpec((*FlatnicConfig)(nil).HCL2Spec())}, + "disks": &hcldec.BlockListSpec{TypeName: "disks", Nested: hcldec.ObjectSpec((*FlatdiskConfig)(nil).HCL2Spec())}, "iso_file": &hcldec.AttrSpec{Name: "iso_file", Type: cty.String, Required: false}, "qemu_agent": &hcldec.AttrSpec{Name: "qemu_agent", Type: cty.Bool, Required: false}, "scsi_controller": &hcldec.AttrSpec{Name: "scsi_controller", Type: cty.String, Required: false}, @@ -188,10 +191,13 @@ type FlatdiskConfig struct { // FlatMapstructure returns a new FlatdiskConfig. // FlatdiskConfig is an auto-generated flat version of diskConfig. // Where the contents a fields with a `mapstructure:,squash` tag are bubbled up. -func (*diskConfig) FlatMapstructure() interface{} { return new(FlatdiskConfig) } +func (*diskConfig) FlatMapstructure() interface{ HCL2Spec() map[string]hcldec.Spec } { + return new(FlatdiskConfig) +} -// HCL2Spec returns the hcldec.Spec of a FlatdiskConfig. -// This spec is used by HCL to read the fields of FlatdiskConfig. +// HCL2Spec returns the hcl spec of a diskConfig. +// This spec is used by HCL to read the fields of diskConfig. +// The decoded values from this spec will then be applied to a FlatdiskConfig. func (*FlatdiskConfig) HCL2Spec() map[string]hcldec.Spec { s := map[string]hcldec.Spec{ "type": &hcldec.AttrSpec{Name: "type", Type: cty.String, Required: false}, @@ -216,10 +222,13 @@ type FlatnicConfig struct { // FlatMapstructure returns a new FlatnicConfig. // FlatnicConfig is an auto-generated flat version of nicConfig. // Where the contents a fields with a `mapstructure:,squash` tag are bubbled up. -func (*nicConfig) FlatMapstructure() interface{} { return new(FlatnicConfig) } +func (*nicConfig) FlatMapstructure() interface{ HCL2Spec() map[string]hcldec.Spec } { + return new(FlatnicConfig) +} -// HCL2Spec returns the hcldec.Spec of a FlatnicConfig. -// This spec is used by HCL to read the fields of FlatnicConfig. +// HCL2Spec returns the hcl spec of a nicConfig. +// This spec is used by HCL to read the fields of nicConfig. +// The decoded values from this spec will then be applied to a FlatnicConfig. func (*FlatnicConfig) HCL2Spec() map[string]hcldec.Spec { s := map[string]hcldec.Spec{ "model": &hcldec.AttrSpec{Name: "model", Type: cty.String, Required: false}, diff --git a/builder/proxmox/config_test.go b/builder/proxmox/config_test.go index 84dd13c6d..3fa496c3f 100644 --- a/builder/proxmox/config_test.go +++ b/builder/proxmox/config_test.go @@ -9,7 +9,8 @@ import ( ) func TestRequiredParameters(t *testing.T) { - _, _, err := NewConfig(make(map[string]interface{})) + var c Config + _, err := c.Prepare(make(map[string]interface{})) if err == nil { t.Fatal("Expected empty configuration to fail") } diff --git a/builder/qemu/builder.go b/builder/qemu/builder.go index 6d85d2654..9cb2e54e6 100644 --- a/builder/qemu/builder.go +++ b/builder/qemu/builder.go @@ -16,6 +16,7 @@ import ( "strings" "time" + "github.com/hashicorp/hcl/v2/hcldec" "github.com/hashicorp/packer/common" "github.com/hashicorp/packer/common/bootcommand" "github.com/hashicorp/packer/common/shutdowncommand" @@ -336,6 +337,8 @@ type Config struct { ctx interpolate.Context } +func (b *Builder) ConfigSpec() hcldec.ObjectSpec { return b.config.FlatMapstructure().HCL2Spec() } + func (b *Builder) Prepare(raws ...interface{}) ([]string, error) { err := config.Decode(&b.config, &config.DecodeOpts{ Interpolate: true, diff --git a/builder/qemu/builder.hcl2spec.go b/builder/qemu/builder.hcl2spec.go index d82b4e464..5e08510d7 100644 --- a/builder/qemu/builder.hcl2spec.go +++ b/builder/qemu/builder.hcl2spec.go @@ -63,8 +63,8 @@ type FlatConfig struct { SSHReadWriteTimeout *string `mapstructure:"ssh_read_write_timeout" cty:"ssh_read_write_timeout"` SSHRemoteTunnels []string `mapstructure:"ssh_remote_tunnels" cty:"ssh_remote_tunnels"` SSHLocalTunnels []string `mapstructure:"ssh_local_tunnels" cty:"ssh_local_tunnels"` - SSHPublicKey []byte `cty:"ssh_public_key"` - SSHPrivateKey []byte `cty:"ssh_private_key"` + SSHPublicKey []byte `mapstructure:"ssh_public_key" cty:"ssh_public_key"` + SSHPrivateKey []byte `mapstructure:"ssh_private_key" cty:"ssh_private_key"` WinRMUser *string `mapstructure:"winrm_username" cty:"winrm_username"` WinRMPassword *string `mapstructure:"winrm_password" cty:"winrm_password"` WinRMHost *string `mapstructure:"winrm_host" cty:"winrm_host"` @@ -115,10 +115,13 @@ type FlatConfig struct { // FlatMapstructure returns a new FlatConfig. // FlatConfig is an auto-generated flat version of Config. // Where the contents a fields with a `mapstructure:,squash` tag are bubbled up. -func (*Config) FlatMapstructure() interface{} { return new(FlatConfig) } +func (*Config) FlatMapstructure() interface{ HCL2Spec() map[string]hcldec.Spec } { + return new(FlatConfig) +} -// HCL2Spec returns the hcldec.Spec of a FlatConfig. -// This spec is used by HCL to read the fields of FlatConfig. +// HCL2Spec returns the hcl spec of a Config. +// This spec is used by HCL to read the fields of Config. +// The decoded values from this spec will then be applied to a FlatConfig. func (*FlatConfig) HCL2Spec() map[string]hcldec.Spec { s := map[string]hcldec.Spec{ "packer_build_name": &hcldec.AttrSpec{Name: "packer_build_name", Type: cty.String, Required: false}, diff --git a/builder/scaleway/builder.go b/builder/scaleway/builder.go index 47ddc8376..751de3977 100644 --- a/builder/scaleway/builder.go +++ b/builder/scaleway/builder.go @@ -8,6 +8,7 @@ import ( "errors" "fmt" + "github.com/hashicorp/hcl/v2/hcldec" "github.com/hashicorp/packer/common" "github.com/hashicorp/packer/helper/communicator" "github.com/hashicorp/packer/helper/multistep" @@ -19,16 +20,17 @@ import ( const BuilderId = "hashicorp.scaleway" type Builder struct { - config *Config + config Config runner multistep.Runner } +func (b *Builder) ConfigSpec() hcldec.ObjectSpec { return b.config.FlatMapstructure().HCL2Spec() } + func (b *Builder) Prepare(raws ...interface{}) ([]string, error) { - c, warnings, errs := NewConfig(raws...) + warnings, errs := b.config.Prepare(raws...) if errs != nil { return warnings, errs } - b.config = c return nil, nil } diff --git a/builder/scaleway/config.go b/builder/scaleway/config.go index 6fd862974..8a8e17adc 100644 --- a/builder/scaleway/config.go +++ b/builder/scaleway/config.go @@ -71,8 +71,7 @@ type Config struct { ctx interpolate.Context } -func NewConfig(raws ...interface{}) (*Config, []string, error) { - c := new(Config) +func (c *Config) Prepare(raws ...interface{}) ([]string, error) { var md mapstructure.Metadata err := config.Decode(c, &config.DecodeOpts{ @@ -86,7 +85,7 @@ func NewConfig(raws ...interface{}) (*Config, []string, error) { }, }, raws...) if err != nil { - return nil, nil, err + return nil, err } c.UserAgent = useragent.String() @@ -161,9 +160,9 @@ func NewConfig(raws ...interface{}) (*Config, []string, error) { } if errs != nil && len(errs.Errors) > 0 { - return nil, nil, errs + return nil, errs } packer.LogSecretFilter.Set(c.Token) - return c, nil, nil + return nil, nil } diff --git a/builder/scaleway/config.hcl2spec.go b/builder/scaleway/config.hcl2spec.go index 7d91dba08..89e5d85d5 100644 --- a/builder/scaleway/config.hcl2spec.go +++ b/builder/scaleway/config.hcl2spec.go @@ -46,8 +46,8 @@ type FlatConfig struct { SSHReadWriteTimeout *string `mapstructure:"ssh_read_write_timeout" cty:"ssh_read_write_timeout"` SSHRemoteTunnels []string `mapstructure:"ssh_remote_tunnels" cty:"ssh_remote_tunnels"` SSHLocalTunnels []string `mapstructure:"ssh_local_tunnels" cty:"ssh_local_tunnels"` - SSHPublicKey []byte `cty:"ssh_public_key"` - SSHPrivateKey []byte `cty:"ssh_private_key"` + SSHPublicKey []byte `mapstructure:"ssh_public_key" cty:"ssh_public_key"` + SSHPrivateKey []byte `mapstructure:"ssh_private_key" cty:"ssh_private_key"` WinRMUser *string `mapstructure:"winrm_username" cty:"winrm_username"` WinRMPassword *string `mapstructure:"winrm_password" cty:"winrm_password"` WinRMHost *string `mapstructure:"winrm_host" cty:"winrm_host"` @@ -73,10 +73,13 @@ type FlatConfig struct { // FlatMapstructure returns a new FlatConfig. // FlatConfig is an auto-generated flat version of Config. // Where the contents a fields with a `mapstructure:,squash` tag are bubbled up. -func (*Config) FlatMapstructure() interface{} { return new(FlatConfig) } +func (*Config) FlatMapstructure() interface{ HCL2Spec() map[string]hcldec.Spec } { + return new(FlatConfig) +} -// HCL2Spec returns the hcldec.Spec of a FlatConfig. -// This spec is used by HCL to read the fields of FlatConfig. +// HCL2Spec returns the hcl spec of a Config. +// This spec is used by HCL to read the fields of Config. +// The decoded values from this spec will then be applied to a FlatConfig. func (*FlatConfig) HCL2Spec() map[string]hcldec.Spec { s := map[string]hcldec.Spec{ "packer_build_name": &hcldec.AttrSpec{Name: "packer_build_name", Type: cty.String, Required: false}, diff --git a/builder/tencentcloud/cvm/builder.go b/builder/tencentcloud/cvm/builder.go index 8934d0649..b180d873f 100644 --- a/builder/tencentcloud/cvm/builder.go +++ b/builder/tencentcloud/cvm/builder.go @@ -5,8 +5,8 @@ package cvm import ( "context" "fmt" - "log" + "github.com/hashicorp/hcl/v2/hcldec" "github.com/hashicorp/packer/common" "github.com/hashicorp/packer/helper/communicator" "github.com/hashicorp/packer/helper/config" @@ -31,6 +31,8 @@ type Builder struct { runner multistep.Runner } +func (b *Builder) ConfigSpec() hcldec.ObjectSpec { return b.config.FlatMapstructure().HCL2Spec() } + func (b *Builder) Prepare(raws ...interface{}) ([]string, error) { err := config.Decode(&b.config, &config.DecodeOpts{ Interpolate: true, @@ -56,7 +58,6 @@ func (b *Builder) Prepare(raws ...interface{}) ([]string, error) { } packer.LogSecretFilter.Set(b.config.SecretId, b.config.SecretKey) - log.Printf("[DEBUG]packer config: %v", b.config) return nil, nil } diff --git a/builder/tencentcloud/cvm/builder.hcl2spec.go b/builder/tencentcloud/cvm/builder.hcl2spec.go index 4b3c3914b..97ef30c46 100644 --- a/builder/tencentcloud/cvm/builder.hcl2spec.go +++ b/builder/tencentcloud/cvm/builder.hcl2spec.go @@ -81,8 +81,8 @@ type FlatConfig struct { SSHReadWriteTimeout *string `mapstructure:"ssh_read_write_timeout" cty:"ssh_read_write_timeout"` SSHRemoteTunnels []string `mapstructure:"ssh_remote_tunnels" cty:"ssh_remote_tunnels"` SSHLocalTunnels []string `mapstructure:"ssh_local_tunnels" cty:"ssh_local_tunnels"` - SSHPublicKey []byte `cty:"ssh_public_key"` - SSHPrivateKey []byte `cty:"ssh_private_key"` + SSHPublicKey []byte `mapstructure:"ssh_public_key" cty:"ssh_public_key"` + SSHPrivateKey []byte `mapstructure:"ssh_private_key" cty:"ssh_private_key"` WinRMUser *string `mapstructure:"winrm_username" cty:"winrm_username"` WinRMPassword *string `mapstructure:"winrm_password" cty:"winrm_password"` WinRMHost *string `mapstructure:"winrm_host" cty:"winrm_host"` @@ -97,10 +97,13 @@ type FlatConfig struct { // FlatMapstructure returns a new FlatConfig. // FlatConfig is an auto-generated flat version of Config. // Where the contents a fields with a `mapstructure:,squash` tag are bubbled up. -func (*Config) FlatMapstructure() interface{} { return new(FlatConfig) } +func (*Config) FlatMapstructure() interface{ HCL2Spec() map[string]hcldec.Spec } { + return new(FlatConfig) +} -// HCL2Spec returns the hcldec.Spec of a FlatConfig. -// This spec is used by HCL to read the fields of FlatConfig. +// HCL2Spec returns the hcl spec of a Config. +// This spec is used by HCL to read the fields of Config. +// The decoded values from this spec will then be applied to a FlatConfig. func (*FlatConfig) HCL2Spec() map[string]hcldec.Spec { s := map[string]hcldec.Spec{ "packer_build_name": &hcldec.AttrSpec{Name: "packer_build_name", Type: cty.String, Required: false}, @@ -129,7 +132,7 @@ func (*FlatConfig) HCL2Spec() map[string]hcldec.Spec { "instance_name": &hcldec.AttrSpec{Name: "instance_name", Type: cty.String, Required: false}, "disk_type": &hcldec.AttrSpec{Name: "disk_type", Type: cty.String, Required: false}, "disk_size": &hcldec.AttrSpec{Name: "disk_size", Type: cty.Number, Required: false}, - "data_disks": &hcldec.BlockListSpec{TypeName: "data_disks", Nested: &hcldec.BlockSpec{TypeName: "data_disks", Nested: hcldec.ObjectSpec((*FlattencentCloudDataDisk)(nil).HCL2Spec())}}, + "data_disks": &hcldec.BlockListSpec{TypeName: "data_disks", Nested: hcldec.ObjectSpec((*FlattencentCloudDataDisk)(nil).HCL2Spec())}, "vpc_id": &hcldec.AttrSpec{Name: "vpc_id", Type: cty.String, Required: false}, "vpc_name": &hcldec.AttrSpec{Name: "vpc_name", Type: cty.String, Required: false}, "vpc_ip": &hcldec.AttrSpec{Name: "vpc_ip", Type: cty.String, Required: false}, diff --git a/builder/tencentcloud/cvm/run_config.hcl2spec.go b/builder/tencentcloud/cvm/run_config.hcl2spec.go index ea01fe69e..8a2dea24e 100644 --- a/builder/tencentcloud/cvm/run_config.hcl2spec.go +++ b/builder/tencentcloud/cvm/run_config.hcl2spec.go @@ -17,10 +17,13 @@ type FlattencentCloudDataDisk struct { // FlatMapstructure returns a new FlattencentCloudDataDisk. // FlattencentCloudDataDisk is an auto-generated flat version of tencentCloudDataDisk. // Where the contents a fields with a `mapstructure:,squash` tag are bubbled up. -func (*tencentCloudDataDisk) FlatMapstructure() interface{} { return new(FlattencentCloudDataDisk) } +func (*tencentCloudDataDisk) FlatMapstructure() interface{ HCL2Spec() map[string]hcldec.Spec } { + return new(FlattencentCloudDataDisk) +} -// HCL2Spec returns the hcldec.Spec of a FlattencentCloudDataDisk. -// This spec is used by HCL to read the fields of FlattencentCloudDataDisk. +// HCL2Spec returns the hcl spec of a tencentCloudDataDisk. +// This spec is used by HCL to read the fields of tencentCloudDataDisk. +// The decoded values from this spec will then be applied to a FlattencentCloudDataDisk. func (*FlattencentCloudDataDisk) HCL2Spec() map[string]hcldec.Spec { s := map[string]hcldec.Spec{ "disk_type": &hcldec.AttrSpec{Name: "disk_type", Type: cty.String, Required: false}, diff --git a/builder/triton/builder.go b/builder/triton/builder.go index ba6e2baa8..c4bd477e1 100644 --- a/builder/triton/builder.go +++ b/builder/triton/builder.go @@ -4,6 +4,7 @@ import ( "context" "github.com/hashicorp/go-multierror" + "github.com/hashicorp/hcl/v2/hcldec" "github.com/hashicorp/packer/common" "github.com/hashicorp/packer/helper/communicator" "github.com/hashicorp/packer/helper/config" @@ -20,6 +21,8 @@ type Builder struct { runner multistep.Runner } +func (b *Builder) ConfigSpec() hcldec.ObjectSpec { return b.config.FlatMapstructure().HCL2Spec() } + func (b *Builder) Prepare(raws ...interface{}) ([]string, error) { errs := &multierror.Error{} diff --git a/builder/triton/config.hcl2spec.go b/builder/triton/config.hcl2spec.go index b6ee44651..4f1b5a871 100644 --- a/builder/triton/config.hcl2spec.go +++ b/builder/triton/config.hcl2spec.go @@ -67,8 +67,8 @@ type FlatConfig struct { SSHReadWriteTimeout *string `mapstructure:"ssh_read_write_timeout" cty:"ssh_read_write_timeout"` SSHRemoteTunnels []string `mapstructure:"ssh_remote_tunnels" cty:"ssh_remote_tunnels"` SSHLocalTunnels []string `mapstructure:"ssh_local_tunnels" cty:"ssh_local_tunnels"` - SSHPublicKey []byte `cty:"ssh_public_key"` - SSHPrivateKey []byte `cty:"ssh_private_key"` + SSHPublicKey []byte `mapstructure:"ssh_public_key" cty:"ssh_public_key"` + SSHPrivateKey []byte `mapstructure:"ssh_private_key" cty:"ssh_private_key"` WinRMUser *string `mapstructure:"winrm_username" cty:"winrm_username"` WinRMPassword *string `mapstructure:"winrm_password" cty:"winrm_password"` WinRMHost *string `mapstructure:"winrm_host" cty:"winrm_host"` @@ -82,10 +82,13 @@ type FlatConfig struct { // FlatMapstructure returns a new FlatConfig. // FlatConfig is an auto-generated flat version of Config. // Where the contents a fields with a `mapstructure:,squash` tag are bubbled up. -func (*Config) FlatMapstructure() interface{} { return new(FlatConfig) } +func (*Config) FlatMapstructure() interface{ HCL2Spec() map[string]hcldec.Spec } { + return new(FlatConfig) +} -// HCL2Spec returns the hcldec.Spec of a FlatConfig. -// This spec is used by HCL to read the fields of FlatConfig. +// HCL2Spec returns the hcl spec of a Config. +// This spec is used by HCL to read the fields of Config. +// The decoded values from this spec will then be applied to a FlatConfig. func (*FlatConfig) HCL2Spec() map[string]hcldec.Spec { s := map[string]hcldec.Spec{ "packer_build_name": &hcldec.AttrSpec{Name: "packer_build_name", Type: cty.String, Required: false}, diff --git a/builder/triton/source_machine_config.hcl2spec.go b/builder/triton/source_machine_config.hcl2spec.go index 7dfed2c69..a7ac52f84 100644 --- a/builder/triton/source_machine_config.hcl2spec.go +++ b/builder/triton/source_machine_config.hcl2spec.go @@ -22,10 +22,13 @@ type FlatMachineImageFilter struct { // FlatMapstructure returns a new FlatMachineImageFilter. // FlatMachineImageFilter is an auto-generated flat version of MachineImageFilter. // Where the contents a fields with a `mapstructure:,squash` tag are bubbled up. -func (*MachineImageFilter) FlatMapstructure() interface{} { return new(FlatMachineImageFilter) } +func (*MachineImageFilter) FlatMapstructure() interface{ HCL2Spec() map[string]hcldec.Spec } { + return new(FlatMachineImageFilter) +} -// HCL2Spec returns the hcldec.Spec of a FlatMachineImageFilter. -// This spec is used by HCL to read the fields of FlatMachineImageFilter. +// HCL2Spec returns the hcl spec of a MachineImageFilter. +// This spec is used by HCL to read the fields of MachineImageFilter. +// The decoded values from this spec will then be applied to a FlatMachineImageFilter. func (*FlatMachineImageFilter) HCL2Spec() map[string]hcldec.Spec { s := map[string]hcldec.Spec{ "most_recent": &hcldec.AttrSpec{Name: "most_recent", Type: cty.Bool, Required: false}, diff --git a/builder/ucloud/common/artifact.go b/builder/ucloud/common/artifact.go index 961bd9399..4daa8f68a 100644 --- a/builder/ucloud/common/artifact.go +++ b/builder/ucloud/common/artifact.go @@ -2,11 +2,12 @@ package common import ( "fmt" - "github.com/hashicorp/packer/packer" - "github.com/ucloud/ucloud-sdk-go/ucloud" "log" "sort" "strings" + + "github.com/hashicorp/packer/packer" + "github.com/ucloud/ucloud-sdk-go/ucloud" ) type Artifact struct { diff --git a/builder/ucloud/common/artifact_test.go b/builder/ucloud/common/artifact_test.go index d3f0f12d7..8154766e3 100644 --- a/builder/ucloud/common/artifact_test.go +++ b/builder/ucloud/common/artifact_test.go @@ -1,9 +1,10 @@ package common import ( - "github.com/hashicorp/packer/packer" "reflect" "testing" + + "github.com/hashicorp/packer/packer" ) func TestArtifact_Impl(t *testing.T) { diff --git a/builder/ucloud/common/client.go b/builder/ucloud/common/client.go index 0e1cf0434..58452ca18 100644 --- a/builder/ucloud/common/client.go +++ b/builder/ucloud/common/client.go @@ -7,7 +7,7 @@ import ( "github.com/ucloud/ucloud-sdk-go/services/unet" "github.com/ucloud/ucloud-sdk-go/services/vpc" "github.com/ucloud/ucloud-sdk-go/ucloud" - "github.com/ucloud/ucloud-sdk-go/ucloud/error" + uerr "github.com/ucloud/ucloud-sdk-go/ucloud/error" ) type UCloudClient struct { diff --git a/builder/ucloud/common/image_config.hcl2spec.go b/builder/ucloud/common/image_config.hcl2spec.go index 9839cad4c..a26a1372a 100644 --- a/builder/ucloud/common/image_config.hcl2spec.go +++ b/builder/ucloud/common/image_config.hcl2spec.go @@ -18,10 +18,13 @@ type FlatImageDestination struct { // FlatMapstructure returns a new FlatImageDestination. // FlatImageDestination is an auto-generated flat version of ImageDestination. // Where the contents a fields with a `mapstructure:,squash` tag are bubbled up. -func (*ImageDestination) FlatMapstructure() interface{} { return new(FlatImageDestination) } +func (*ImageDestination) FlatMapstructure() interface{ HCL2Spec() map[string]hcldec.Spec } { + return new(FlatImageDestination) +} -// HCL2Spec returns the hcldec.Spec of a FlatImageDestination. -// This spec is used by HCL to read the fields of FlatImageDestination. +// HCL2Spec returns the hcl spec of a ImageDestination. +// This spec is used by HCL to read the fields of ImageDestination. +// The decoded values from this spec will then be applied to a FlatImageDestination. func (*FlatImageDestination) HCL2Spec() map[string]hcldec.Spec { s := map[string]hcldec.Spec{ "project_id": &hcldec.AttrSpec{Name: "project_id", Type: cty.String, Required: false}, diff --git a/builder/ucloud/common/utils.go b/builder/ucloud/common/utils.go index 1f58444d5..b922608d1 100644 --- a/builder/ucloud/common/utils.go +++ b/builder/ucloud/common/utils.go @@ -2,10 +2,11 @@ package common import ( "fmt" + "strings" + "github.com/hashicorp/packer/helper/multistep" "github.com/hashicorp/packer/packer" "github.com/ucloud/ucloud-sdk-go/services/uhost" - "strings" ) func CheckStringIn(val string, available []string) error { diff --git a/builder/ucloud/uhost/builder.go b/builder/ucloud/uhost/builder.go index a9903bf4a..5b9f0b32a 100644 --- a/builder/ucloud/uhost/builder.go +++ b/builder/ucloud/uhost/builder.go @@ -7,6 +7,7 @@ package uhost import ( "context" + "github.com/hashicorp/hcl/v2/hcldec" ucloudcommon "github.com/hashicorp/packer/builder/ucloud/common" "github.com/hashicorp/packer/common" "github.com/hashicorp/packer/helper/communicator" @@ -33,6 +34,8 @@ type Builder struct { runner multistep.Runner } +func (b *Builder) ConfigSpec() hcldec.ObjectSpec { return b.config.FlatMapstructure().HCL2Spec() } + func (b *Builder) Prepare(raws ...interface{}) ([]string, error) { err := config.Decode(&b.config, &config.DecodeOpts{ Interpolate: true, diff --git a/builder/ucloud/uhost/builder.hcl2spec.go b/builder/ucloud/uhost/builder.hcl2spec.go index e35dee79e..262a64271 100644 --- a/builder/ucloud/uhost/builder.hcl2spec.go +++ b/builder/ucloud/uhost/builder.hcl2spec.go @@ -64,8 +64,8 @@ type FlatConfig struct { SSHReadWriteTimeout *string `mapstructure:"ssh_read_write_timeout" cty:"ssh_read_write_timeout"` SSHRemoteTunnels []string `mapstructure:"ssh_remote_tunnels" cty:"ssh_remote_tunnels"` SSHLocalTunnels []string `mapstructure:"ssh_local_tunnels" cty:"ssh_local_tunnels"` - SSHPublicKey []byte `cty:"ssh_public_key"` - SSHPrivateKey []byte `cty:"ssh_private_key"` + SSHPublicKey []byte `mapstructure:"ssh_public_key" cty:"ssh_public_key"` + SSHPrivateKey []byte `mapstructure:"ssh_private_key" cty:"ssh_private_key"` WinRMUser *string `mapstructure:"winrm_username" cty:"winrm_username"` WinRMPassword *string `mapstructure:"winrm_password" cty:"winrm_password"` WinRMHost *string `mapstructure:"winrm_host" cty:"winrm_host"` @@ -80,10 +80,13 @@ type FlatConfig struct { // FlatMapstructure returns a new FlatConfig. // FlatConfig is an auto-generated flat version of Config. // Where the contents a fields with a `mapstructure:,squash` tag are bubbled up. -func (*Config) FlatMapstructure() interface{} { return new(FlatConfig) } +func (*Config) FlatMapstructure() interface{ HCL2Spec() map[string]hcldec.Spec } { + return new(FlatConfig) +} -// HCL2Spec returns the hcldec.Spec of a FlatConfig. -// This spec is used by HCL to read the fields of FlatConfig. +// HCL2Spec returns the hcl spec of a Config. +// This spec is used by HCL to read the fields of Config. +// The decoded values from this spec will then be applied to a FlatConfig. func (*FlatConfig) HCL2Spec() map[string]hcldec.Spec { s := map[string]hcldec.Spec{ "packer_build_name": &hcldec.AttrSpec{Name: "packer_build_name", Type: cty.String, Required: false}, @@ -100,7 +103,7 @@ func (*FlatConfig) HCL2Spec() map[string]hcldec.Spec { "base_url": &hcldec.AttrSpec{Name: "base_url", Type: cty.String, Required: false}, "image_name": &hcldec.AttrSpec{Name: "image_name", Type: cty.String, Required: false}, "image_description": &hcldec.AttrSpec{Name: "image_description", Type: cty.String, Required: false}, - "image_copy_to_mappings": &hcldec.BlockListSpec{TypeName: "image_copy_to_mappings", Nested: &hcldec.BlockSpec{TypeName: "image_copy_to_mappings", Nested: hcldec.ObjectSpec((*common.FlatImageDestination)(nil).HCL2Spec())}}, + "image_copy_to_mappings": &hcldec.BlockListSpec{TypeName: "image_copy_to_mappings", Nested: hcldec.ObjectSpec((*common.FlatImageDestination)(nil).HCL2Spec())}, "wait_image_ready_timeout": &hcldec.AttrSpec{Name: "wait_image_ready_timeout", Type: cty.Number, Required: false}, "availability_zone": &hcldec.AttrSpec{Name: "availability_zone", Type: cty.String, Required: false}, "source_image_id": &hcldec.AttrSpec{Name: "source_image_id", Type: cty.String, Required: false}, diff --git a/builder/ucloud/uhost/builder_acc_test.go b/builder/ucloud/uhost/builder_acc_test.go index 0d5a82d6d..091ab8ab6 100644 --- a/builder/ucloud/uhost/builder_acc_test.go +++ b/builder/ucloud/uhost/builder_acc_test.go @@ -2,11 +2,12 @@ package uhost import ( "fmt" + "os" + "testing" + ucloudcommon "github.com/hashicorp/packer/builder/ucloud/common" "github.com/hashicorp/packer/packer" "github.com/stretchr/testify/assert" - "os" - "testing" builderT "github.com/hashicorp/packer/helper/builder/testing" ) diff --git a/builder/ucloud/uhost/builder_test.go b/builder/ucloud/uhost/builder_test.go index dca944bc5..7d0a2d5ce 100644 --- a/builder/ucloud/uhost/builder_test.go +++ b/builder/ucloud/uhost/builder_test.go @@ -1,10 +1,11 @@ package uhost import ( - ucloudcommon "github.com/hashicorp/packer/builder/ucloud/common" - "github.com/hashicorp/packer/packer" "reflect" "testing" + + ucloudcommon "github.com/hashicorp/packer/builder/ucloud/common" + "github.com/hashicorp/packer/packer" ) func testBuilderConfig() map[string]interface{} { diff --git a/builder/ucloud/uhost/step_check_source_image.go b/builder/ucloud/uhost/step_check_source_image.go index fd77d7eee..66260df79 100644 --- a/builder/ucloud/uhost/step_check_source_image.go +++ b/builder/ucloud/uhost/step_check_source_image.go @@ -3,6 +3,7 @@ package uhost import ( "context" "fmt" + ucloudcommon "github.com/hashicorp/packer/builder/ucloud/common" "github.com/hashicorp/packer/helper/multistep" "github.com/hashicorp/packer/packer" diff --git a/builder/ucloud/uhost/step_config_security_group.go b/builder/ucloud/uhost/step_config_security_group.go index 66c4cd27c..f561bd003 100644 --- a/builder/ucloud/uhost/step_config_security_group.go +++ b/builder/ucloud/uhost/step_config_security_group.go @@ -3,6 +3,7 @@ package uhost import ( "context" "fmt" + ucloudcommon "github.com/hashicorp/packer/builder/ucloud/common" "github.com/hashicorp/packer/helper/multistep" "github.com/hashicorp/packer/packer" diff --git a/builder/ucloud/uhost/step_config_subnet.go b/builder/ucloud/uhost/step_config_subnet.go index b6bb4bd53..affb43ccd 100644 --- a/builder/ucloud/uhost/step_config_subnet.go +++ b/builder/ucloud/uhost/step_config_subnet.go @@ -3,6 +3,7 @@ package uhost import ( "context" "fmt" + ucloudcommon "github.com/hashicorp/packer/builder/ucloud/common" "github.com/hashicorp/packer/helper/multistep" "github.com/hashicorp/packer/packer" diff --git a/builder/ucloud/uhost/step_config_vpc.go b/builder/ucloud/uhost/step_config_vpc.go index e8e2bcec4..b6792318c 100644 --- a/builder/ucloud/uhost/step_config_vpc.go +++ b/builder/ucloud/uhost/step_config_vpc.go @@ -3,6 +3,7 @@ package uhost import ( "context" "fmt" + ucloudcommon "github.com/hashicorp/packer/builder/ucloud/common" "github.com/hashicorp/packer/helper/multistep" diff --git a/builder/ucloud/uhost/step_copy_image.go b/builder/ucloud/uhost/step_copy_image.go index 690b9deca..60061a33c 100644 --- a/builder/ucloud/uhost/step_copy_image.go +++ b/builder/ucloud/uhost/step_copy_image.go @@ -3,11 +3,12 @@ package uhost import ( "context" "fmt" - ucloudcommon "github.com/hashicorp/packer/builder/ucloud/common" - "github.com/hashicorp/packer/common/retry" "strings" "time" + ucloudcommon "github.com/hashicorp/packer/builder/ucloud/common" + "github.com/hashicorp/packer/common/retry" + "github.com/hashicorp/packer/helper/multistep" "github.com/hashicorp/packer/packer" "github.com/ucloud/ucloud-sdk-go/ucloud" diff --git a/builder/ucloud/uhost/step_create_image.go b/builder/ucloud/uhost/step_create_image.go index 235cdb7ff..e0004a1ce 100644 --- a/builder/ucloud/uhost/step_create_image.go +++ b/builder/ucloud/uhost/step_create_image.go @@ -3,9 +3,10 @@ package uhost import ( "context" "fmt" + "time" + ucloudcommon "github.com/hashicorp/packer/builder/ucloud/common" "github.com/hashicorp/packer/common/retry" - "time" "github.com/hashicorp/packer/helper/multistep" "github.com/hashicorp/packer/packer" diff --git a/builder/ucloud/uhost/step_create_instance.go b/builder/ucloud/uhost/step_create_instance.go index 3c60daef8..a4a312ce8 100644 --- a/builder/ucloud/uhost/step_create_instance.go +++ b/builder/ucloud/uhost/step_create_instance.go @@ -3,15 +3,16 @@ package uhost import ( "context" "fmt" + "math/rand" + "strings" + "time" + ucloudcommon "github.com/hashicorp/packer/builder/ucloud/common" "github.com/hashicorp/packer/common/retry" "github.com/hashicorp/packer/helper/multistep" "github.com/hashicorp/packer/packer" "github.com/ucloud/ucloud-sdk-go/services/uhost" "github.com/ucloud/ucloud-sdk-go/ucloud" - "math/rand" - "strings" - "time" ) type stepCreateInstance struct { diff --git a/builder/ucloud/uhost/step_pre_validate.go b/builder/ucloud/uhost/step_pre_validate.go index 843616c10..becb80b29 100644 --- a/builder/ucloud/uhost/step_pre_validate.go +++ b/builder/ucloud/uhost/step_pre_validate.go @@ -2,6 +2,7 @@ package uhost import ( "context" + ucloudcommon "github.com/hashicorp/packer/builder/ucloud/common" "github.com/hashicorp/packer/helper/multistep" "github.com/hashicorp/packer/packer" diff --git a/builder/ucloud/uhost/step_stop_instance.go b/builder/ucloud/uhost/step_stop_instance.go index 463428d1a..d9376e15e 100644 --- a/builder/ucloud/uhost/step_stop_instance.go +++ b/builder/ucloud/uhost/step_stop_instance.go @@ -3,9 +3,10 @@ package uhost import ( "context" "fmt" + "time" + ucloudcommon "github.com/hashicorp/packer/builder/ucloud/common" "github.com/hashicorp/packer/common/retry" - "time" "github.com/hashicorp/packer/helper/multistep" "github.com/hashicorp/packer/packer" diff --git a/builder/vagrant/builder.go b/builder/vagrant/builder.go index 5fb189e13..984654c51 100644 --- a/builder/vagrant/builder.go +++ b/builder/vagrant/builder.go @@ -12,6 +12,7 @@ import ( "strings" "time" + "github.com/hashicorp/hcl/v2/hcldec" "github.com/hashicorp/packer/common" "github.com/hashicorp/packer/common/bootcommand" "github.com/hashicorp/packer/helper/communicator" @@ -24,7 +25,7 @@ import ( // Builder implements packer.Builder and builds the actual VirtualBox // images. type Builder struct { - config *Config + config Config runner multistep.Runner } @@ -131,9 +132,9 @@ type Config struct { ctx interpolate.Context } -// Prepare processes the build configuration parameters. +func (b *Builder) ConfigSpec() hcldec.ObjectSpec { return b.config.FlatMapstructure().HCL2Spec() } + func (b *Builder) Prepare(raws ...interface{}) ([]string, error) { - b.config = new(Config) err := config.Decode(&b.config, &config.DecodeOpts{ Interpolate: true, InterpolateContext: &b.config.ctx, diff --git a/builder/vagrant/builder.hcl2spec.go b/builder/vagrant/builder.hcl2spec.go index 1a9dece69..70aba5f49 100644 --- a/builder/vagrant/builder.hcl2spec.go +++ b/builder/vagrant/builder.hcl2spec.go @@ -62,8 +62,8 @@ type FlatConfig struct { SSHReadWriteTimeout *string `mapstructure:"ssh_read_write_timeout" cty:"ssh_read_write_timeout"` SSHRemoteTunnels []string `mapstructure:"ssh_remote_tunnels" cty:"ssh_remote_tunnels"` SSHLocalTunnels []string `mapstructure:"ssh_local_tunnels" cty:"ssh_local_tunnels"` - SSHPublicKey []byte `cty:"ssh_public_key"` - SSHPrivateKey []byte `cty:"ssh_private_key"` + SSHPublicKey []byte `mapstructure:"ssh_public_key" cty:"ssh_public_key"` + SSHPrivateKey []byte `mapstructure:"ssh_private_key" cty:"ssh_private_key"` WinRMUser *string `mapstructure:"winrm_username" cty:"winrm_username"` WinRMPassword *string `mapstructure:"winrm_password" cty:"winrm_password"` WinRMHost *string `mapstructure:"winrm_host" cty:"winrm_host"` @@ -100,10 +100,13 @@ type FlatConfig struct { // FlatMapstructure returns a new FlatConfig. // FlatConfig is an auto-generated flat version of Config. // Where the contents a fields with a `mapstructure:,squash` tag are bubbled up. -func (*Config) FlatMapstructure() interface{} { return new(FlatConfig) } +func (*Config) FlatMapstructure() interface{ HCL2Spec() map[string]hcldec.Spec } { + return new(FlatConfig) +} -// HCL2Spec returns the hcldec.Spec of a FlatConfig. -// This spec is used by HCL to read the fields of FlatConfig. +// HCL2Spec returns the hcl spec of a Config. +// This spec is used by HCL to read the fields of Config. +// The decoded values from this spec will then be applied to a FlatConfig. func (*FlatConfig) HCL2Spec() map[string]hcldec.Spec { s := map[string]hcldec.Spec{ "packer_build_name": &hcldec.AttrSpec{Name: "packer_build_name", Type: cty.String, Required: false}, diff --git a/builder/vagrant/builder_test.go b/builder/vagrant/builder_test.go index 65c2e5e9f..5327706af 100644 --- a/builder/vagrant/builder_test.go +++ b/builder/vagrant/builder_test.go @@ -15,7 +15,6 @@ func TestBuilder_ImplementsBuilder(t *testing.T) { } func TestBuilder_Prepare_ValidateSource(t *testing.T) { - b := &Builder{} type testCase struct { config map[string]interface{} errExpected bool @@ -83,7 +82,7 @@ func TestBuilder_Prepare_ValidateSource(t *testing.T) { } for _, tc := range cases { - _, err := b.Prepare(tc.config) + _, err := (&Builder{}).Prepare(tc.config) if (err != nil) != tc.errExpected { t.Fatalf("Unexpected behavior from test case %#v; %s.", tc.config, tc.reason) } diff --git a/builder/virtualbox/iso/builder.go b/builder/virtualbox/iso/builder.go index 02c7aeeac..fe1ba87a2 100644 --- a/builder/virtualbox/iso/builder.go +++ b/builder/virtualbox/iso/builder.go @@ -9,6 +9,7 @@ import ( "fmt" "strings" + "github.com/hashicorp/hcl/v2/hcldec" vboxcommon "github.com/hashicorp/packer/builder/virtualbox/common" "github.com/hashicorp/packer/common" "github.com/hashicorp/packer/common/bootcommand" @@ -130,6 +131,8 @@ type Config struct { ctx interpolate.Context } +func (b *Builder) ConfigSpec() hcldec.ObjectSpec { return b.config.FlatMapstructure().HCL2Spec() } + func (b *Builder) Prepare(raws ...interface{}) ([]string, error) { err := config.Decode(&b.config, &config.DecodeOpts{ Interpolate: true, diff --git a/builder/virtualbox/iso/builder.hcl2spec.go b/builder/virtualbox/iso/builder.hcl2spec.go index effec9e79..8f7497ac8 100644 --- a/builder/virtualbox/iso/builder.hcl2spec.go +++ b/builder/virtualbox/iso/builder.hcl2spec.go @@ -73,8 +73,8 @@ type FlatConfig struct { SSHReadWriteTimeout *string `mapstructure:"ssh_read_write_timeout" cty:"ssh_read_write_timeout"` SSHRemoteTunnels []string `mapstructure:"ssh_remote_tunnels" cty:"ssh_remote_tunnels"` SSHLocalTunnels []string `mapstructure:"ssh_local_tunnels" cty:"ssh_local_tunnels"` - SSHPublicKey []byte `cty:"ssh_public_key"` - SSHPrivateKey []byte `cty:"ssh_private_key"` + SSHPublicKey []byte `mapstructure:"ssh_public_key" cty:"ssh_public_key"` + SSHPrivateKey []byte `mapstructure:"ssh_private_key" cty:"ssh_private_key"` WinRMUser *string `mapstructure:"winrm_username" cty:"winrm_username"` WinRMPassword *string `mapstructure:"winrm_password" cty:"winrm_password"` WinRMHost *string `mapstructure:"winrm_host" cty:"winrm_host"` @@ -116,10 +116,13 @@ type FlatConfig struct { // FlatMapstructure returns a new FlatConfig. // FlatConfig is an auto-generated flat version of Config. // Where the contents a fields with a `mapstructure:,squash` tag are bubbled up. -func (*Config) FlatMapstructure() interface{} { return new(FlatConfig) } +func (*Config) FlatMapstructure() interface{ HCL2Spec() map[string]hcldec.Spec } { + return new(FlatConfig) +} -// HCL2Spec returns the hcldec.Spec of a FlatConfig. -// This spec is used by HCL to read the fields of FlatConfig. +// HCL2Spec returns the hcl spec of a Config. +// This spec is used by HCL to read the fields of Config. +// The decoded values from this spec will then be applied to a FlatConfig. func (*FlatConfig) HCL2Spec() map[string]hcldec.Spec { s := map[string]hcldec.Spec{ "packer_build_name": &hcldec.AttrSpec{Name: "packer_build_name", Type: cty.String, Required: false}, diff --git a/builder/virtualbox/ovf/builder.go b/builder/virtualbox/ovf/builder.go index b20b5b663..f0e92d5db 100644 --- a/builder/virtualbox/ovf/builder.go +++ b/builder/virtualbox/ovf/builder.go @@ -5,6 +5,7 @@ import ( "errors" "fmt" + "github.com/hashicorp/hcl/v2/hcldec" vboxcommon "github.com/hashicorp/packer/builder/virtualbox/common" "github.com/hashicorp/packer/common" "github.com/hashicorp/packer/helper/communicator" @@ -15,17 +16,17 @@ import ( // Builder implements packer.Builder and builds the actual VirtualBox // images. type Builder struct { - config *Config + config Config runner multistep.Runner } -// Prepare processes the build configuration parameters. +func (b *Builder) ConfigSpec() hcldec.ObjectSpec { return b.config.FlatMapstructure().HCL2Spec() } + func (b *Builder) Prepare(raws ...interface{}) ([]string, error) { - c, warnings, errs := NewConfig(raws...) + warnings, errs := b.config.Prepare(raws...) if errs != nil { return warnings, errs } - b.config = c return warnings, nil } diff --git a/builder/virtualbox/ovf/config.go b/builder/virtualbox/ovf/config.go index 3a6ae041a..ce5659bfe 100644 --- a/builder/virtualbox/ovf/config.go +++ b/builder/virtualbox/ovf/config.go @@ -104,8 +104,7 @@ type Config struct { ctx interpolate.Context } -func NewConfig(raws ...interface{}) (*Config, []string, error) { - c := new(Config) +func (c *Config) Prepare(raws ...interface{}) ([]string, error) { err := config.Decode(c, &config.DecodeOpts{ Interpolate: true, InterpolateContext: &c.ctx, @@ -120,7 +119,7 @@ func NewConfig(raws ...interface{}) (*Config, []string, error) { }, }, raws...) if err != nil { - return nil, nil, err + return nil, err } // Defaults @@ -195,7 +194,7 @@ func NewConfig(raws ...interface{}) (*Config, []string, error) { // Check for any errors. if errs != nil && len(errs.Errors) > 0 { - return nil, warnings, errs + return warnings, errs } // TODO: Write a packer fix and just remove import_opts @@ -203,5 +202,5 @@ func NewConfig(raws ...interface{}) (*Config, []string, error) { c.ImportFlags = append(c.ImportFlags, "--options", c.ImportOpts) } - return c, warnings, nil + return warnings, nil } diff --git a/builder/virtualbox/ovf/config.hcl2spec.go b/builder/virtualbox/ovf/config.hcl2spec.go index 4ac16c25a..e518e3cf7 100644 --- a/builder/virtualbox/ovf/config.hcl2spec.go +++ b/builder/virtualbox/ovf/config.hcl2spec.go @@ -62,8 +62,8 @@ type FlatConfig struct { SSHReadWriteTimeout *string `mapstructure:"ssh_read_write_timeout" cty:"ssh_read_write_timeout"` SSHRemoteTunnels []string `mapstructure:"ssh_remote_tunnels" cty:"ssh_remote_tunnels"` SSHLocalTunnels []string `mapstructure:"ssh_local_tunnels" cty:"ssh_local_tunnels"` - SSHPublicKey []byte `cty:"ssh_public_key"` - SSHPrivateKey []byte `cty:"ssh_private_key"` + SSHPublicKey []byte `mapstructure:"ssh_public_key" cty:"ssh_public_key"` + SSHPrivateKey []byte `mapstructure:"ssh_private_key" cty:"ssh_private_key"` WinRMUser *string `mapstructure:"winrm_username" cty:"winrm_username"` WinRMPassword *string `mapstructure:"winrm_password" cty:"winrm_password"` WinRMHost *string `mapstructure:"winrm_host" cty:"winrm_host"` @@ -102,10 +102,13 @@ type FlatConfig struct { // FlatMapstructure returns a new FlatConfig. // FlatConfig is an auto-generated flat version of Config. // Where the contents a fields with a `mapstructure:,squash` tag are bubbled up. -func (*Config) FlatMapstructure() interface{} { return new(FlatConfig) } +func (*Config) FlatMapstructure() interface{ HCL2Spec() map[string]hcldec.Spec } { + return new(FlatConfig) +} -// HCL2Spec returns the hcldec.Spec of a FlatConfig. -// This spec is used by HCL to read the fields of FlatConfig. +// HCL2Spec returns the hcl spec of a Config. +// This spec is used by HCL to read the fields of Config. +// The decoded values from this spec will then be applied to a FlatConfig. func (*FlatConfig) HCL2Spec() map[string]hcldec.Spec { s := map[string]hcldec.Spec{ "packer_build_name": &hcldec.AttrSpec{Name: "packer_build_name", Type: cty.String, Required: false}, diff --git a/builder/virtualbox/ovf/config_test.go b/builder/virtualbox/ovf/config_test.go index 517eda04b..f00d5bdfc 100644 --- a/builder/virtualbox/ovf/config_test.go +++ b/builder/virtualbox/ovf/config_test.go @@ -31,19 +31,21 @@ func getTempFile(t *testing.T) *os.File { } func TestNewConfig_FloppyFiles(t *testing.T) { - c := testConfig(t) + cfg := testConfig(t) floppies_path := "../../../common/test-fixtures/floppies" - c["floppy_files"] = []string{fmt.Sprintf("%s/bar.bat", floppies_path), fmt.Sprintf("%s/foo.ps1", floppies_path)} - _, _, err := NewConfig(c) + cfg["floppy_files"] = []string{fmt.Sprintf("%s/bar.bat", floppies_path), fmt.Sprintf("%s/foo.ps1", floppies_path)} + var c Config + _, err := c.Prepare(cfg) if err != nil { t.Fatalf("should not have error: %s", err) } } func TestNewConfig_InvalidFloppies(t *testing.T) { - c := testConfig(t) - c["floppy_files"] = []string{"nonexistent.bat", "nonexistent.ps1"} - _, _, errs := NewConfig(c) + cfg := testConfig(t) + cfg["floppy_files"] = []string{"nonexistent.bat", "nonexistent.ps1"} + var c Config + _, errs := c.Prepare(cfg) if errs == nil { t.Fatalf("Nonexistent floppies should trigger multierror") } @@ -55,9 +57,10 @@ func TestNewConfig_InvalidFloppies(t *testing.T) { func TestNewConfig_sourcePath(t *testing.T) { // Okay, because it gets caught during download - c := testConfig(t) - delete(c, "source_path") - _, warns, err := NewConfig(c) + cfg := testConfig(t) + delete(cfg, "source_path") + var c Config + warns, err := c.Prepare(cfg) if len(warns) > 0 { t.Fatalf("bad: %#v", warns) } @@ -69,9 +72,9 @@ func TestNewConfig_sourcePath(t *testing.T) { tf := getTempFile(t) defer os.Remove(tf.Name()) - c = testConfig(t) - c["source_path"] = tf.Name() - _, warns, err = NewConfig(c) + cfg = testConfig(t) + cfg["source_path"] = tf.Name() + warns, err = c.Prepare(cfg) if len(warns) > 0 { t.Fatalf("bad: %#v", warns) } @@ -81,14 +84,15 @@ func TestNewConfig_sourcePath(t *testing.T) { } func TestNewConfig_shutdown_timeout(t *testing.T) { - c := testConfig(t) + cfg := testConfig(t) tf := getTempFile(t) defer os.Remove(tf.Name()) // Expect this to fail - c["source_path"] = tf.Name() - c["shutdown_timeout"] = "NaN" - _, warns, err := NewConfig(c) + cfg["source_path"] = tf.Name() + cfg["shutdown_timeout"] = "NaN" + var c Config + warns, err := c.Prepare(cfg) if len(warns) > 0 { t.Fatalf("bad: %#v", warns) } @@ -97,8 +101,8 @@ func TestNewConfig_shutdown_timeout(t *testing.T) { } // Passes when given a valid time duration - c["shutdown_timeout"] = "10s" - _, warns, err = NewConfig(c) + cfg["shutdown_timeout"] = "10s" + warns, err = c.Prepare(cfg) if len(warns) > 0 { t.Fatalf("bad: %#v", warns) } diff --git a/builder/virtualbox/ovf/step_import_test.go b/builder/virtualbox/ovf/step_import_test.go index ffaecc0ba..482bfdcdc 100644 --- a/builder/virtualbox/ovf/step_import_test.go +++ b/builder/virtualbox/ovf/step_import_test.go @@ -14,10 +14,11 @@ func TestStepImport_impl(t *testing.T) { func TestStepImport(t *testing.T) { state := testState(t) - c := testConfig(t) - config, _, _ := NewConfig(c) + cfg := testConfig(t) + var c Config + c.Prepare(cfg) state.Put("vm_path", "foo") - state.Put("config", config) + state.Put("config", &c) step := new(StepImport) step.Name = "bar" @@ -47,14 +48,14 @@ func TestStepImport(t *testing.T) { } // Test cleanup - config.KeepRegistered = true + c.KeepRegistered = true step.Cleanup(state) if driver.DeleteCalled { t.Fatal("delete should not be called") } - config.KeepRegistered = false + c.KeepRegistered = false step.Cleanup(state) if !driver.DeleteCalled { t.Fatal("delete should be called") diff --git a/builder/virtualbox/vm/builder.go b/builder/virtualbox/vm/builder.go index 2336e58d5..3deda5ee7 100644 --- a/builder/virtualbox/vm/builder.go +++ b/builder/virtualbox/vm/builder.go @@ -5,6 +5,7 @@ import ( "errors" "fmt" + "github.com/hashicorp/hcl/v2/hcldec" vboxcommon "github.com/hashicorp/packer/builder/virtualbox/common" "github.com/hashicorp/packer/common" "github.com/hashicorp/packer/helper/communicator" @@ -19,13 +20,13 @@ type Builder struct { runner multistep.Runner } -// Prepare processes the build configuration parameters. +func (b *Builder) ConfigSpec() hcldec.ObjectSpec { return b.config.FlatMapstructure().HCL2Spec() } + func (b *Builder) Prepare(raws ...interface{}) ([]string, error) { - c, warnings, errs := NewConfig(raws...) + warnings, errs := b.config.Prepare(raws...) if errs != nil { return warnings, errs } - b.config = c return warnings, nil } diff --git a/builder/virtualbox/vm/config.go b/builder/virtualbox/vm/config.go index 14b57295d..dc1a1fd66 100644 --- a/builder/virtualbox/vm/config.go +++ b/builder/virtualbox/vm/config.go @@ -44,8 +44,7 @@ type Config struct { ctx interpolate.Context } -func NewConfig(raws ...interface{}) (*Config, []string, error) { - c := new(Config) +func (c *Config) Prepare(raws ...interface{}) ([]string, error) { err := config.Decode(c, &config.DecodeOpts{ Interpolate: true, InterpolateContext: &c.ctx, @@ -60,7 +59,7 @@ func NewConfig(raws ...interface{}) (*Config, []string, error) { }, }, raws...) if err != nil { - return nil, nil, err + return nil, err } // Defaults @@ -203,8 +202,8 @@ func NewConfig(raws ...interface{}) (*Config, []string, error) { } // Check for any errors. if errs != nil && len(errs.Errors) > 0 { - return nil, warnings, errs + return warnings, errs } - return c, warnings, nil + return warnings, nil } diff --git a/builder/virtualbox/vm/config.hcl2spec.go b/builder/virtualbox/vm/config.hcl2spec.go index eafdf2428..a180a65f6 100644 --- a/builder/virtualbox/vm/config.hcl2spec.go +++ b/builder/virtualbox/vm/config.hcl2spec.go @@ -62,8 +62,8 @@ type FlatConfig struct { SSHReadWriteTimeout *string `mapstructure:"ssh_read_write_timeout" cty:"ssh_read_write_timeout"` SSHRemoteTunnels []string `mapstructure:"ssh_remote_tunnels" cty:"ssh_remote_tunnels"` SSHLocalTunnels []string `mapstructure:"ssh_local_tunnels" cty:"ssh_local_tunnels"` - SSHPublicKey []byte `cty:"ssh_public_key"` - SSHPrivateKey []byte `cty:"ssh_private_key"` + SSHPublicKey []byte `mapstructure:"ssh_public_key" cty:"ssh_public_key"` + SSHPrivateKey []byte `mapstructure:"ssh_private_key" cty:"ssh_private_key"` WinRMUser *string `mapstructure:"winrm_username" cty:"winrm_username"` WinRMPassword *string `mapstructure:"winrm_password" cty:"winrm_password"` WinRMHost *string `mapstructure:"winrm_host" cty:"winrm_host"` @@ -98,10 +98,13 @@ type FlatConfig struct { // FlatMapstructure returns a new FlatConfig. // FlatConfig is an auto-generated flat version of Config. // Where the contents a fields with a `mapstructure:,squash` tag are bubbled up. -func (*Config) FlatMapstructure() interface{} { return new(FlatConfig) } +func (*Config) FlatMapstructure() interface{ HCL2Spec() map[string]hcldec.Spec } { + return new(FlatConfig) +} -// HCL2Spec returns the hcldec.Spec of a FlatConfig. -// This spec is used by HCL to read the fields of FlatConfig. +// HCL2Spec returns the hcl spec of a Config. +// This spec is used by HCL to read the fields of Config. +// The decoded values from this spec will then be applied to a FlatConfig. func (*FlatConfig) HCL2Spec() map[string]hcldec.Spec { s := map[string]hcldec.Spec{ "packer_build_name": &hcldec.AttrSpec{Name: "packer_build_name", Type: cty.String, Required: false}, diff --git a/builder/vmware/iso/builder.go b/builder/vmware/iso/builder.go index 7e94d9663..0c230d91b 100644 --- a/builder/vmware/iso/builder.go +++ b/builder/vmware/iso/builder.go @@ -6,6 +6,7 @@ import ( "fmt" "time" + "github.com/hashicorp/hcl/v2/hcldec" vmwcommon "github.com/hashicorp/packer/builder/vmware/common" "github.com/hashicorp/packer/common" "github.com/hashicorp/packer/helper/communicator" @@ -18,15 +19,14 @@ type Builder struct { runner multistep.Runner } -// Prepare processes the build configuration parameters. +func (b *Builder) ConfigSpec() hcldec.ObjectSpec { return b.config.FlatMapstructure().HCL2Spec() } + func (b *Builder) Prepare(raws ...interface{}) ([]string, error) { - c, warnings, errs := NewConfig(raws...) + warnings, errs := b.config.Prepare(raws...) if errs != nil { return warnings, errs } - b.config = *c - return warnings, nil } diff --git a/builder/vmware/iso/config.go b/builder/vmware/iso/config.go index 44d577058..2062ee4e5 100644 --- a/builder/vmware/iso/config.go +++ b/builder/vmware/iso/config.go @@ -115,8 +115,7 @@ type Config struct { ctx interpolate.Context } -func NewConfig(raws ...interface{}) (*Config, []string, error) { - c := new(Config) +func (c *Config) Prepare(raws ...interface{}) ([]string, error) { err := config.Decode(c, &config.DecodeOpts{ Interpolate: true, InterpolateContext: &c.ctx, @@ -128,7 +127,7 @@ func NewConfig(raws ...interface{}) (*Config, []string, error) { }, }, raws...) if err != nil { - return nil, nil, err + return nil, err } // Accumulate any errors and warnings @@ -263,10 +262,10 @@ func NewConfig(raws ...interface{}) (*Config, []string, error) { } if errs != nil && len(errs.Errors) > 0 { - return nil, warnings, errs + return warnings, errs } - return c, warnings, nil + return warnings, nil } func (c *Config) checkForVMXTemplateAndVMXDataCollisions() string { diff --git a/builder/vmware/iso/config.hcl2spec.go b/builder/vmware/iso/config.hcl2spec.go index 6dce149ae..21795f95a 100644 --- a/builder/vmware/iso/config.hcl2spec.go +++ b/builder/vmware/iso/config.hcl2spec.go @@ -92,8 +92,8 @@ type FlatConfig struct { SSHReadWriteTimeout *string `mapstructure:"ssh_read_write_timeout" cty:"ssh_read_write_timeout"` SSHRemoteTunnels []string `mapstructure:"ssh_remote_tunnels" cty:"ssh_remote_tunnels"` SSHLocalTunnels []string `mapstructure:"ssh_local_tunnels" cty:"ssh_local_tunnels"` - SSHPublicKey []byte `cty:"ssh_public_key"` - SSHPrivateKey []byte `cty:"ssh_private_key"` + SSHPublicKey []byte `mapstructure:"ssh_public_key" cty:"ssh_public_key"` + SSHPrivateKey []byte `mapstructure:"ssh_private_key" cty:"ssh_private_key"` WinRMUser *string `mapstructure:"winrm_username" cty:"winrm_username"` WinRMPassword *string `mapstructure:"winrm_password" cty:"winrm_password"` WinRMHost *string `mapstructure:"winrm_host" cty:"winrm_host"` @@ -131,10 +131,13 @@ type FlatConfig struct { // FlatMapstructure returns a new FlatConfig. // FlatConfig is an auto-generated flat version of Config. // Where the contents a fields with a `mapstructure:,squash` tag are bubbled up. -func (*Config) FlatMapstructure() interface{} { return new(FlatConfig) } +func (*Config) FlatMapstructure() interface{ HCL2Spec() map[string]hcldec.Spec } { + return new(FlatConfig) +} -// HCL2Spec returns the hcldec.Spec of a FlatConfig. -// This spec is used by HCL to read the fields of FlatConfig. +// HCL2Spec returns the hcl spec of a Config. +// This spec is used by HCL to read the fields of Config. +// The decoded values from this spec will then be applied to a FlatConfig. func (*FlatConfig) HCL2Spec() map[string]hcldec.Spec { s := map[string]hcldec.Spec{ "packer_build_name": &hcldec.AttrSpec{Name: "packer_build_name", Type: cty.String, Required: false}, diff --git a/builder/vmware/iso/step_create_vmx_test.go b/builder/vmware/iso/step_create_vmx_test.go index 9bd4d6d4c..0c6fe6e97 100644 --- a/builder/vmware/iso/step_create_vmx_test.go +++ b/builder/vmware/iso/step_create_vmx_test.go @@ -137,17 +137,17 @@ func setupVMwareBuild(t *testing.T, builderConfig map[string]string, provisioner // create our config to test the vmware-iso builder components := packer.ComponentFinder{ - Builder: func(n string) (packer.Builder, error) { - return &Builder{}, nil + BuilderStore: packer.MapOfBuilder{ + "vmware-iso": func() (packer.Builder, error) { return &Builder{}, nil }, }, Hook: func(n string) (packer.Hook, error) { return &packer.DispatchHook{}, nil }, - PostProcessor: func(n string) (packer.PostProcessor, error) { - return &packer.MockPostProcessor{}, nil + ProvisionerStore: packer.MapOfProvisioner{ + "shell": func() (packer.Provisioner, error) { return &shell.Provisioner{}, nil }, }, - Provisioner: func(n string) (packer.Provisioner, error) { - return &shell.Provisioner{}, nil + PostProcessorStore: packer.MapOfPostProcessor{ + "something": func() (packer.PostProcessor, error) { return &packer.MockPostProcessor{}, nil }, }, } config := packer.CoreConfig{ diff --git a/builder/vmware/vmx/builder.go b/builder/vmware/vmx/builder.go index e1e33dd7c..a37b0b313 100644 --- a/builder/vmware/vmx/builder.go +++ b/builder/vmware/vmx/builder.go @@ -7,6 +7,7 @@ import ( "log" "time" + "github.com/hashicorp/hcl/v2/hcldec" vmwcommon "github.com/hashicorp/packer/builder/vmware/common" "github.com/hashicorp/packer/common" "github.com/hashicorp/packer/helper/communicator" @@ -17,17 +18,17 @@ import ( // Builder implements packer.Builder and builds the actual VMware // images. type Builder struct { - config *Config + config Config runner multistep.Runner } -// Prepare processes the build configuration parameters. +func (b *Builder) ConfigSpec() hcldec.ObjectSpec { return b.config.FlatMapstructure().HCL2Spec() } + func (b *Builder) Prepare(raws ...interface{}) ([]string, error) { - c, warnings, errs := NewConfig(raws...) + warnings, errs := b.config.Prepare(raws...) if errs != nil { return warnings, errs } - b.config = c return warnings, nil } diff --git a/builder/vmware/vmx/config.go b/builder/vmware/vmx/config.go index 7e5d8d1f0..4b8dbd8b4 100644 --- a/builder/vmware/vmx/config.go +++ b/builder/vmware/vmx/config.go @@ -55,8 +55,7 @@ type Config struct { ctx interpolate.Context } -func NewConfig(raws ...interface{}) (*Config, []string, error) { - c := new(Config) +func (c *Config) Prepare(raws ...interface{}) ([]string, error) { err := config.Decode(c, &config.DecodeOpts{ Interpolate: true, InterpolateContext: &c.ctx, @@ -68,7 +67,7 @@ func NewConfig(raws ...interface{}) (*Config, []string, error) { }, }, raws...) if err != nil { - return nil, nil, err + return nil, err } // Defaults @@ -148,8 +147,8 @@ func NewConfig(raws ...interface{}) (*Config, []string, error) { // Check for any errors. if errs != nil && len(errs.Errors) > 0 { - return nil, warnings, errs + return warnings, errs } - return c, warnings, nil + return warnings, nil } diff --git a/builder/vmware/vmx/config.hcl2spec.go b/builder/vmware/vmx/config.hcl2spec.go index 15fc44b5e..72e0027e9 100644 --- a/builder/vmware/vmx/config.hcl2spec.go +++ b/builder/vmware/vmx/config.hcl2spec.go @@ -76,8 +76,8 @@ type FlatConfig struct { SSHReadWriteTimeout *string `mapstructure:"ssh_read_write_timeout" cty:"ssh_read_write_timeout"` SSHRemoteTunnels []string `mapstructure:"ssh_remote_tunnels" cty:"ssh_remote_tunnels"` SSHLocalTunnels []string `mapstructure:"ssh_local_tunnels" cty:"ssh_local_tunnels"` - SSHPublicKey []byte `cty:"ssh_public_key"` - SSHPrivateKey []byte `cty:"ssh_private_key"` + SSHPublicKey []byte `mapstructure:"ssh_public_key" cty:"ssh_public_key"` + SSHPrivateKey []byte `mapstructure:"ssh_private_key" cty:"ssh_private_key"` WinRMUser *string `mapstructure:"winrm_username" cty:"winrm_username"` WinRMPassword *string `mapstructure:"winrm_password" cty:"winrm_password"` WinRMHost *string `mapstructure:"winrm_host" cty:"winrm_host"` @@ -107,10 +107,13 @@ type FlatConfig struct { // FlatMapstructure returns a new FlatConfig. // FlatConfig is an auto-generated flat version of Config. // Where the contents a fields with a `mapstructure:,squash` tag are bubbled up. -func (*Config) FlatMapstructure() interface{} { return new(FlatConfig) } +func (*Config) FlatMapstructure() interface{ HCL2Spec() map[string]hcldec.Spec } { + return new(FlatConfig) +} -// HCL2Spec returns the hcldec.Spec of a FlatConfig. -// This spec is used by HCL to read the fields of FlatConfig. +// HCL2Spec returns the hcl spec of a Config. +// This spec is used by HCL to read the fields of Config. +// The decoded values from this spec will then be applied to a FlatConfig. func (*FlatConfig) HCL2Spec() map[string]hcldec.Spec { s := map[string]hcldec.Spec{ "packer_build_name": &hcldec.AttrSpec{Name: "packer_build_name", Type: cty.String, Required: false}, diff --git a/builder/vmware/vmx/config_test.go b/builder/vmware/vmx/config_test.go index 4ee0b9426..4b5c63483 100644 --- a/builder/vmware/vmx/config_test.go +++ b/builder/vmware/vmx/config_test.go @@ -34,15 +34,15 @@ func testConfigOk(t *testing.T, warns []string, err error) { func TestNewConfig_sourcePath(t *testing.T) { // Bad - c := testConfig(t) - delete(c, "source_path") - _, warns, errs := NewConfig(c) + cfg := testConfig(t) + delete(cfg, "source_path") + warns, errs := (&Config{}).Prepare(cfg) testConfigErr(t, warns, errs) // Bad - c = testConfig(t) - c["source_path"] = "/i/dont/exist" - _, warns, errs = NewConfig(c) + cfg = testConfig(t) + cfg["source_path"] = "/i/dont/exist" + warns, errs = (&Config{}).Prepare(cfg) testConfigErr(t, warns, errs) // Good @@ -53,8 +53,8 @@ func TestNewConfig_sourcePath(t *testing.T) { tf.Close() defer os.Remove(tf.Name()) - c = testConfig(t) - c["source_path"] = tf.Name() - _, warns, errs = NewConfig(c) + cfg = testConfig(t) + cfg["source_path"] = tf.Name() + warns, errs = (&Config{}).Prepare(cfg) testConfigOk(t, warns, errs) } diff --git a/builder/yandex/builder.go b/builder/yandex/builder.go index 69bb9469f..5f0b68102 100644 --- a/builder/yandex/builder.go +++ b/builder/yandex/builder.go @@ -5,6 +5,7 @@ import ( "fmt" "github.com/google/uuid" + "github.com/hashicorp/hcl/v2/hcldec" "github.com/hashicorp/packer/common" "github.com/hashicorp/packer/helper/communicator" "github.com/hashicorp/packer/helper/multistep" @@ -19,24 +20,24 @@ const BuilderID = "packer.yandex" // Builder represents a Packer Builder. type Builder struct { - config *Config + config Config runner multistep.Runner } -// Prepare processes the build configuration parameters. +func (b *Builder) ConfigSpec() hcldec.ObjectSpec { return b.config.FlatMapstructure().HCL2Spec() } + func (b *Builder) Prepare(raws ...interface{}) ([]string, error) { - c, warnings, errs := NewConfig(raws...) + warnings, errs := b.config.Prepare(raws...) if errs != nil { return warnings, errs } - b.config = c return warnings, nil } // Run executes a yandex Packer build and returns a packer.Artifact // representing a Yandex.Cloud compute image. func (b *Builder) Run(ctx context.Context, ui packer.Ui, hook packer.Hook) (packer.Artifact, error) { - driver, err := NewDriverYC(ui, b.config) + driver, err := NewDriverYC(ui, &b.config) ctx = requestid.ContextWithClientTraceID(ctx, uuid.New().String()) if err != nil { @@ -45,7 +46,7 @@ func (b *Builder) Run(ctx context.Context, ui packer.Ui, hook packer.Hook) (pack // Set up the state state := &multistep.BasicStateBag{} - state.Put("config", b.config) + state.Put("config", &b.config) state.Put("driver", driver) state.Put("sdk", driver.SDK()) state.Put("hook", hook) @@ -91,7 +92,7 @@ func (b *Builder) Run(ctx context.Context, ui packer.Ui, hook packer.Hook) (pack artifact := &Artifact{ image: image.(*compute.Image), - config: b.config, + config: &b.config, } return artifact, nil } diff --git a/builder/yandex/config.go b/builder/yandex/config.go index 2319203e1..5d3952a6a 100644 --- a/builder/yandex/config.go +++ b/builder/yandex/config.go @@ -122,15 +122,14 @@ type Config struct { StateTimeout time.Duration `mapstructure:"state_timeout" required:"false"` } -func NewConfig(raws ...interface{}) (*Config, []string, error) { - c := &Config{} +func (c *Config) Prepare(raws ...interface{}) ([]string, error) { c.ctx.Funcs = TemplateFuncs err := config.Decode(c, &config.DecodeOpts{ Interpolate: true, InterpolateContext: &c.ctx, }, raws...) if err != nil { - return nil, nil, err + return nil, err } var errs *packer.MultiError @@ -286,8 +285,8 @@ func NewConfig(raws ...interface{}) (*Config, []string, error) { // Check for any errors. if errs != nil && len(errs.Errors) > 0 { - return nil, nil, errs + return nil, errs } - return c, nil, nil + return nil, nil } diff --git a/builder/yandex/config.hcl2spec.go b/builder/yandex/config.hcl2spec.go index 69f6695d7..1fb0db29b 100644 --- a/builder/yandex/config.hcl2spec.go +++ b/builder/yandex/config.hcl2spec.go @@ -46,8 +46,8 @@ type FlatConfig struct { SSHReadWriteTimeout *string `mapstructure:"ssh_read_write_timeout" cty:"ssh_read_write_timeout"` SSHRemoteTunnels []string `mapstructure:"ssh_remote_tunnels" cty:"ssh_remote_tunnels"` SSHLocalTunnels []string `mapstructure:"ssh_local_tunnels" cty:"ssh_local_tunnels"` - SSHPublicKey []byte `cty:"ssh_public_key"` - SSHPrivateKey []byte `cty:"ssh_private_key"` + SSHPublicKey []byte `mapstructure:"ssh_public_key" cty:"ssh_public_key"` + SSHPrivateKey []byte `mapstructure:"ssh_private_key" cty:"ssh_private_key"` WinRMUser *string `mapstructure:"winrm_username" cty:"winrm_username"` WinRMPassword *string `mapstructure:"winrm_password" cty:"winrm_password"` WinRMHost *string `mapstructure:"winrm_host" cty:"winrm_host"` @@ -94,10 +94,13 @@ type FlatConfig struct { // FlatMapstructure returns a new FlatConfig. // FlatConfig is an auto-generated flat version of Config. // Where the contents a fields with a `mapstructure:,squash` tag are bubbled up. -func (*Config) FlatMapstructure() interface{} { return new(FlatConfig) } +func (*Config) FlatMapstructure() interface{ HCL2Spec() map[string]hcldec.Spec } { + return new(FlatConfig) +} -// HCL2Spec returns the hcldec.Spec of a FlatConfig. -// This spec is used by HCL to read the fields of FlatConfig. +// HCL2Spec returns the hcl spec of a Config. +// This spec is used by HCL to read the fields of Config. +// The decoded values from this spec will then be applied to a FlatConfig. func (*FlatConfig) HCL2Spec() map[string]hcldec.Spec { s := map[string]hcldec.Spec{ "packer_build_name": &hcldec.AttrSpec{Name: "packer_build_name", Type: cty.String, Required: false}, diff --git a/builder/yandex/config_test.go b/builder/yandex/config_test.go index 7e777d0e7..611474718 100644 --- a/builder/yandex/config_test.go +++ b/builder/yandex/config_test.go @@ -135,7 +135,8 @@ func TestConfigPrepare(t *testing.T) { delete(raw, "token") } - _, warns, errs := NewConfig(raw) + var c Config + warns, errs := c.Prepare(raw) if tc.Err { testConfigErr(t, warns, errs, tc.Key) @@ -156,7 +157,8 @@ func TestConfigPrepareStartupScriptFile(t *testing.T) { "key": "file_not_exist", } - _, _, errs := NewConfig(config) + var c Config + _, errs := c.Prepare(config) if errs == nil || !strings.Contains(errs.Error(), "cannot access file 'file_not_exist' with content "+ "for value of metadata key 'key':") { @@ -183,10 +185,11 @@ func TestConfigDefaults(t *testing.T) { for _, tc := range cases { raw := testConfig(t) - c, warns, errs := NewConfig(raw) + var c Config + warns, errs := c.Prepare(raw) testConfigOk(t, warns, errs) - actual := tc.Read(c) + actual := tc.Read(&c) if actual != tc.Value { t.Fatalf("bad: %#v", actual) } @@ -196,7 +199,8 @@ func TestConfigDefaults(t *testing.T) { func TestImageName(t *testing.T) { raw := testConfig(t) - c, _, _ := NewConfig(raw) + var c Config + c.Prepare(raw) if !strings.HasPrefix(c.ImageName, "packer-") { t.Fatalf("ImageName should have 'packer-' prefix, found %s", c.ImageName) } @@ -208,7 +212,8 @@ func TestImageName(t *testing.T) { func TestZone(t *testing.T) { raw := testConfig(t) - c, _, _ := NewConfig(raw) + var c Config + c.Prepare(raw) if c.Zone != "ru-central1-a" { t.Fatalf("Zone should be 'ru-central1-a' given, but is '%s'", c.Zone) } @@ -218,7 +223,8 @@ func TestGpuDefaultPlatformID(t *testing.T) { raw := testConfig(t) raw["instance_gpus"] = 1 - c, _, _ := NewConfig(raw) + var c Config + c.Prepare(raw) if c.PlatformID != "gpu-standard-v1" { t.Fatalf("expected 'gpu-standard-v1', but got '%s'", c.PlatformID) } @@ -229,7 +235,8 @@ func TestGpuWrongPlatformID(t *testing.T) { raw["instance_gpus"] = 1 raw["platform_id"] = "standard-v1" - _, warns, errs := NewConfig(raw) + var c Config + warns, errs := c.Prepare(raw) testConfigErr(t, warns, errs, "incompatible GPU platform_id") } @@ -254,12 +261,13 @@ func testConfig(t *testing.T) (config map[string]interface{}) { func testConfigStruct(t *testing.T) *Config { raw := testConfig(t) - c, warns, errs := NewConfig(raw) + var c Config + warns, errs := c.Prepare(raw) require.True(t, len(warns) == 0, "bad: %#v", warns) require.NoError(t, errs, "should not have error: %s", errs) - return c + return &c } func testConfigErr(t *testing.T, warns []string, err error, extra string) { diff --git a/cmd/hcl2-schema/hcl2-schema.go b/cmd/hcl2-schema/hcl2-schema.go deleted file mode 100644 index 7628e6185..000000000 --- a/cmd/hcl2-schema/hcl2-schema.go +++ /dev/null @@ -1,313 +0,0 @@ -package main - -import ( - "bytes" - "flag" - "fmt" - "go/ast" - "go/format" - "go/parser" - "go/token" - "io" - "io/ioutil" - "log" - "os" - "regexp" - "strings" - "text/template" - - "github.com/fatih/structtag" - "github.com/hashicorp/hcl/v2/hcldec" - "github.com/zclconf/go-cty/cty" -) - -var ( - typeNames = flag.String("type", "", "comma-separated list of type names; must be set") - output = flag.String("output", "", "output file name; default srcdir/_hcl2.go") - trimprefix = flag.String("trimprefix", "", "trim the `prefix` from the generated constant names") -) - -// Usage is a replacement usage function for the flags package. -func Usage() { - fmt.Fprintf(os.Stderr, "Usage of hcl2-schema:\n") - fmt.Fprintf(os.Stderr, "\thcl2-schema [flags] -type T [directory]\n") - fmt.Fprintf(os.Stderr, "\thcl2-schema [flags] -type T files... # Must be a single package\n") - fmt.Fprintf(os.Stderr, "Flags:\n") - flag.PrintDefaults() -} - -func main() { - log.SetFlags(0) - log.SetPrefix("hcl2-schema: ") - flag.Usage = Usage - flag.Parse() - if len(*typeNames) == 0 { - flag.Usage() - os.Exit(2) - } - types := strings.Split(*typeNames, ",") - - // We accept either one directory or a list of files. Which do we have? - args := flag.Args() - if len(args) == 0 { - // Default: process whole package in current directory. - args = []string{os.Getenv("GOFILE")} - } - fname := args[0] - outputPath := fname[:len(fname)-2] + "hcl2spec.go" - - b, err := ioutil.ReadFile(fname) - if err != nil { - fmt.Printf("ReadFile: %+v", err) - os.Exit(1) - } - - fset := token.NewFileSet() - f, err := parser.ParseFile(fset, fname, b, parser.ParseComments) - if err != nil { - fmt.Printf("ParseFile: %+v", err) - os.Exit(1) - } - output := Output{ - Args: strings.Join(os.Args[1:], " "), - Package: f.Name.String(), - } - - res := []StructDef{} - - for _, t := range types { - for _, decl := range f.Decls { - typeDecl, ok := decl.(*ast.GenDecl) - if !ok { - continue - } - typeSpec, ok := typeDecl.Specs[0].(*ast.TypeSpec) - if !ok { - continue - } - structDecl, ok := typeSpec.Type.(*ast.StructType) - if !ok { - continue - } - if typeSpec.Name.String() != t { - continue - } - sd := StructDef{StructName: t} - fields := structDecl.Fields.List - for _, field := range fields { - - fieldType := string(b[field.Type.Pos()-1 : field.Type.End()-1]) - fieldName := fieldType[strings.Index(fieldType, ".")+1:] - if len(field.Names) > 0 { - fieldName = field.Names[0].Name - } - - if ast.IsExported(fieldName) { - continue - } - if strings.Contains(fieldType, "func") { - continue - } - fd := FieldDef{Name: fieldName} - - squash := false - accessor := ToSnakeCase(fieldName) - if field.Tag != nil { - tag := field.Tag.Value[1:] - tag = tag[:len(tag)-1] - tags, err := structtag.Parse(tag) - if err != nil { - log.Fatalf("structtag.Parse(%s): err: %v", field.Tag.Value, err) - } - if hsg, err := tags.Get("hcl2-schema-generator"); err == nil { - if len(hsg.Options) > 0 && hsg.Options[0] == "direct" { - fieldType = "direct" - } - if hsg.Name != "" { - accessor = hsg.Name - } - } else if mstr, err := tags.Get("mapstructure"); err == nil { - if len(mstr.Options) > 0 && mstr.Options[0] == "squash" { - squash = true - } - if mstr.Name != "" { - accessor = mstr.Name - } - } - } - - switch fieldType { - case "direct": - fd.Spec = `(&` + sd.StructName + `{}).` + fieldName + `.HCL2Spec()` - case "[]string", "[]*string": - fd.Spec = fmt.Sprintf("%#v", &hcldec.AttrSpec{ - Name: accessor, - Type: cty.List(cty.String), - Required: false, - }) - case "[]int", "[]uint", "[]int32", "[]int64": - fd.Spec = fmt.Sprintf("%#v", &hcldec.AttrSpec{ - Name: accessor, - Type: cty.List(cty.Number), - Required: false, - }) - case "[]byte", "string", "*string", "time.Duration", "*url.URL": - fd.Spec = fmt.Sprintf("%#v", &hcldec.AttrSpec{ - Name: accessor, - Type: cty.String, - Required: false, - }) - case "uint", "*int", "int", "int32", "int64", "float", "float32", "float64": - fd.Spec = fmt.Sprintf("%#v", &hcldec.AttrSpec{ - Name: accessor, - Type: cty.Number, - Required: false, - }) - case "bool", "config.Trilean": - fd.Spec = fmt.Sprintf("%#v", &hcldec.AttrSpec{ - Name: accessor, - Type: cty.Bool, - Required: false, - }) - case "osccommon.TagMap", "awscommon.TagMap", "TagMap", - "map[string]*string", "map[*string]*string", "map[string]string": - fd.Spec = fmt.Sprintf("%#v", &hcldec.BlockAttrsSpec{ - TypeName: accessor, - ElementType: cty.String, - Required: false, - }) - case "[][]string": - // TODO(azr): implement those - continue - case "communicator.Config": - // this one is manually set - continue - case "map[string]interface{}", "map[string]map[string]interface{}": - // probably never going to be supported - continue - case "common.PackerConfig": - // this one is deprecated ? - continue - default: // nested structures - if squash { - sd.Squashed = append(sd.Squashed, fieldName) - } else if strings.HasPrefix(fieldType, "[]") { - sd.NestedList = append(sd.NestedList, NestedFieldDef{ - FieldName: fieldName, - TypeName: fieldType, - Accessor: accessor, - }) - } else { - sd.Nested = append(sd.Nested, NestedFieldDef{ - FieldName: fieldName, - TypeName: fieldType, - Accessor: accessor, - }) - } - continue - } - - output.ImportCty = true - sd.Fields = append(sd.Fields, fd) - } - res = append(res, sd) - } - } - - output.StructDefs = res - - result := bytes.NewBuffer(nil) - - err = structDocsTemplate.Execute(result, output) - if err != nil { - log.Fatalf("err templating: %v", err) - } - - formattedBytes, err := format.Source(result.Bytes()) - if err != nil { - log.Printf("formatting err: %v", err) - formattedBytes = result.Bytes() - } - - outputFile, err := os.Create(outputPath) - if err != nil { - log.Fatalf("err: %v", err) - } - defer outputFile.Close() - - _, err = io.Copy(outputFile, bytes.NewBuffer(formattedBytes)) - if err != nil { - log.Fatalf("err: %v", err) - } -} - -type Output struct { - Args string - Package string - StructDefs []StructDef - ImportCty bool -} - -type FieldDef struct { - Name string - Spec string -} -type NestedFieldDef struct { - TypeName string - FieldName string - Accessor string -} - -type StructDef struct { - StructName string - Fields []FieldDef - Nested []NestedFieldDef - NestedList []NestedFieldDef - Squashed []string -} - -var structDocsTemplate = template.Must(template.New("structDocsTemplate"). - Funcs(template.FuncMap{ - // "indent": indent, - }). - Parse(`// Code generated by "hcl2-schema {{ .Args }}"; DO NOT EDIT.\n - -package {{ .Package }} - -import ( - "github.com/hashicorp/hcl/v2/hcldec" -{{- if .ImportCty }} - "github.com/zclconf/go-cty/cty" -{{end -}} -) -{{ range .StructDefs }} -{{ $StructName := .StructName}} -func (*{{ .StructName }}) HCL2Spec() map[string]hcldec.Spec { - s := map[string]hcldec.Spec{ - {{- range .Fields}} - "{{ .Name }}": {{ .Spec }}, - {{- end }} - {{- range .Nested}} - "{{ .Accessor }}": &hcldec.BlockObjectSpec{TypeName: "{{ .TypeName }}", LabelNames: []string(nil), Nested: hcldec.ObjectSpec((&{{ $StructName }}{}).{{ .FieldName }}.HCL2Spec())}, - {{- end }} - {{- range .NestedList }} - "{{ .Accessor }}": &hcldec.BlockListSpec{TypeName: "{{ .TypeName }}", Nested: hcldec.ObjectSpec((&{{ $StructName }}{}).{{ .FieldName }}[0].HCL2Spec()) }, - {{- end}} - } - {{- range .Squashed }} - for k,v := range (&{{ $StructName }}{}).{{ . }}.HCL2Spec() { - s[k] = v - } - {{- end}} - return s -} -{{end}}`)) - -var matchFirstCap = regexp.MustCompile("(.)([A-Z][a-z]+)") -var matchAllCap = regexp.MustCompile("([a-z0-9])([A-Z])") - -func ToSnakeCase(str string) string { - snake := matchFirstCap.ReplaceAllString(str, "${1}_${2}") - snake = matchAllCap.ReplaceAllString(snake, "${1}_${2}") - return strings.ToLower(snake) -} diff --git a/cmd/mapstructure-to-hcl2/mapstructure-to-hcl2.go b/cmd/mapstructure-to-hcl2/mapstructure-to-hcl2.go index da3a507bf..58364cee9 100644 --- a/cmd/mapstructure-to-hcl2/mapstructure-to-hcl2.go +++ b/cmd/mapstructure-to-hcl2/mapstructure-to-hcl2.go @@ -125,7 +125,7 @@ func main() { newStructName := "Flat" + id.Name structs = append(structs, StructDef{ OriginalStructName: id.Name, - StructName: newStructName, + FlatStructName: newStructName, Struct: flatenedStruct, }) @@ -150,22 +150,23 @@ func main() { return structs[i].OriginalStructName < structs[j].OriginalStructName }) for _, flatenedStruct := range structs { - fmt.Fprintf(out, "\n// %s is an auto-generated flat version of %s.", flatenedStruct.StructName, flatenedStruct.OriginalStructName) + fmt.Fprintf(out, "\n// %s is an auto-generated flat version of %s.", flatenedStruct.FlatStructName, flatenedStruct.OriginalStructName) fmt.Fprintf(out, "\n// Where the contents of a field with a `mapstructure:,squash` tag are bubbled up.") - fmt.Fprintf(out, "\ntype %s struct {\n", flatenedStruct.StructName) + fmt.Fprintf(out, "\ntype %s struct {\n", flatenedStruct.FlatStructName) outputStructFields(out, flatenedStruct.Struct) fmt.Fprint(out, "}\n") - fmt.Fprintf(out, "\n// FlatMapstructure returns a new %s.", flatenedStruct.StructName) - fmt.Fprintf(out, "\n// %s is an auto-generated flat version of %s.", flatenedStruct.StructName, flatenedStruct.OriginalStructName) + fmt.Fprintf(out, "\n// FlatMapstructure returns a new %s.", flatenedStruct.FlatStructName) + fmt.Fprintf(out, "\n// %s is an auto-generated flat version of %s.", flatenedStruct.FlatStructName, flatenedStruct.OriginalStructName) fmt.Fprintf(out, "\n// Where the contents a fields with a `mapstructure:,squash` tag are bubbled up.") - fmt.Fprintf(out, "\nfunc (*%s) FlatMapstructure() interface{} {", flatenedStruct.OriginalStructName) - fmt.Fprintf(out, "return new(%s)", flatenedStruct.StructName) + fmt.Fprintf(out, "\nfunc (*%s) FlatMapstructure() interface{ HCL2Spec() map[string]hcldec.Spec } {", flatenedStruct.OriginalStructName) + fmt.Fprintf(out, "return new(%s)", flatenedStruct.FlatStructName) fmt.Fprint(out, "}\n") - fmt.Fprintf(out, "\n// HCL2Spec returns the hcldec.Spec of a %s.", flatenedStruct.StructName) - fmt.Fprintf(out, "\n// This spec is used by HCL to read the fields of %s.", flatenedStruct.StructName) - fmt.Fprintf(out, "\nfunc (*%s) HCL2Spec() map[string]hcldec.Spec {\n", flatenedStruct.StructName) + fmt.Fprintf(out, "\n// HCL2Spec returns the hcl spec of a %s.", flatenedStruct.OriginalStructName) + fmt.Fprintf(out, "\n// This spec is used by HCL to read the fields of %s.", flatenedStruct.OriginalStructName) + fmt.Fprintf(out, "\n// The decoded values from this spec will then be applied to a %s.", flatenedStruct.FlatStructName) + fmt.Fprintf(out, "\nfunc (*%s) HCL2Spec() map[string]hcldec.Spec {\n", flatenedStruct.FlatStructName) outputStructHCL2SpecBody(out, flatenedStruct.Struct) fmt.Fprint(out, "}\n") } @@ -196,7 +197,7 @@ func main() { type StructDef struct { OriginalStructName string - StructName string + FlatStructName string Struct *types.Struct } @@ -250,7 +251,13 @@ func outputHCL2SpecField(w io.Writer, accessor string, fieldType types.Type, tag }) case *types.Named: b := bytes.NewBuffer(nil) - outputHCL2SpecField(b, accessor, elem, tag) + underlyingType := elem.Underlying() + switch underlyingType.(type) { + case *types.Struct: + fmt.Fprintf(b, `hcldec.ObjectSpec((*%s)(nil).HCL2Spec())`, elem.String()) + default: + outputHCL2SpecField(b, accessor, elem, tag) + } fmt.Fprintf(w, `&hcldec.BlockListSpec{TypeName: "%s", Nested: %s}`, accessor, b.String()) case *types.Slice: b := bytes.NewBuffer(nil) @@ -268,9 +275,6 @@ func outputHCL2SpecField(w io.Writer, accessor string, fieldType types.Type, tag default: outputHCL2SpecField(w, f.String(), underlyingType, tag) } - case *types.Struct: - fmt.Fprintf(w, `&hcldec.BlockObjectSpec{TypeName: "%s",`+ - ` Nested: hcldec.ObjectSpec((*%s)(nil).HCL2Spec())}`, accessor, fieldType.String()) default: fmt.Fprintf(w, `%#v`, &hcldec.AttrSpec{ Name: accessor, @@ -303,7 +307,9 @@ func basicKindToCtyType(kind types.BasicKind) cty.Type { func outputStructFields(w io.Writer, s *types.Struct) { for i := 0; i < s.NumFields(); i++ { field, tag := s.Field(i), s.Tag(i) - fmt.Fprintf(w, " %s `%s`\n", strings.Replace(field.String(), "field ", "", 1), tag) + fieldNameStr := field.String() + fieldNameStr = strings.Replace(fieldNameStr, "field ", "", 1) + fmt.Fprintf(w, " %s `%s`\n", fieldNameStr, tag) } } @@ -350,6 +356,9 @@ func getUsedImports(s *types.Struct) map[NamePath]*types.Package { continue } pkg := namedType.Obj().Pkg() + if pkg == nil { + continue + } res[NamePath{pkg.Name(), pkg.Path()}] = pkg } return res diff --git a/command/build.go b/command/build.go index 8dd245458..d7bedb91e 100644 --- a/command/build.go +++ b/command/build.go @@ -13,6 +13,9 @@ import ( "sync" "syscall" + "github.com/hashicorp/hcl/v2" + "github.com/hashicorp/hcl/v2/hclparse" + "github.com/hashicorp/packer/hcl2template" "github.com/hashicorp/packer/helper/enumflag" "github.com/hashicorp/packer/packer" "github.com/hashicorp/packer/template" @@ -52,6 +55,7 @@ func (c *BuildCommand) Run(args []string) int { return c.RunContext(buildCtx, args) } +// Config is the command-configuration parsed from the command line. type Config struct { Color, Debug, Force, Timestamp bool ParallelBuilds int64 @@ -92,33 +96,67 @@ func (c *BuildCommand) ParseArgs(args []string) (Config, int) { return cfg, 0 } -func (c *BuildCommand) RunContext(buildCtx context.Context, args []string) int { - cfg, ret := c.ParseArgs(args) - if ret != 0 { - return ret +func (c *BuildCommand) GetBuildsFromHCL(path string) ([]packer.Build, int) { + parser := &hcl2template.Parser{ + Parser: hclparse.NewParser(), + BuilderSchemas: c.CoreConfig.Components.BuilderStore, + ProvisionersSchemas: c.CoreConfig.Components.ProvisionerStore, + PostProcessorsSchemas: c.CoreConfig.Components.PostProcessorStore, } + builds, diags := parser.Parse(path) + { + // write HCL errors/diagnostics if any. + b := bytes.NewBuffer(nil) + err := hcl.NewDiagnosticTextWriter(b, parser.Files(), 80, false).WriteDiagnostics(diags) + if err != nil { + c.Ui.Error("could not write diagnostic: " + err.Error()) + return nil, 1 + } + if b.Len() != 0 { + c.Ui.Message(b.String()) + } + } + ret := 0 + if diags.HasErrors() { + ret = 1 + } + + return builds, ret +} + +func (c *BuildCommand) GetBuilds(path string) ([]packer.Build, int) { + + isHCLLoaded, err := isHCLLoaded(path) + if path != "-" && err != nil { + c.Ui.Error(fmt.Sprintf("Could not tell wether %s is hcl enabled: %s", path, err)) + return nil, 1 + } + if isHCLLoaded { + return c.GetBuildsFromHCL(path) + } + + c.Ui.Say(`Legacy JSON Configuration Will Be Used. +The template will be parsed in the legacy configuration style. This style +Will continue to work but users are encouraged to move to the new style, +See: https://packer.io/guides/hcl2 .`) + // Parse the template var tpl *template.Template - var err error - tpl, err = template.ParseFile(cfg.Path) + tpl, err = template.ParseFile(path) if err != nil { c.Ui.Error(fmt.Sprintf("Failed to parse template: %s", err)) - return 1 + return nil, 1 } // Get the core core, err := c.Meta.Core(tpl) if err != nil { c.Ui.Error(err.Error()) - return 1 + return nil, 1 } - // Get the builds we care about - var errors = struct { - sync.RWMutex - m map[string]error - }{m: make(map[string]error)} + ret := 0 buildNames := c.Meta.BuildNames(core) builds := make([]packer.Build, 0, len(buildNames)) for _, n := range buildNames { @@ -127,12 +165,22 @@ func (c *BuildCommand) RunContext(buildCtx context.Context, args []string) int { c.Ui.Error(fmt.Sprintf( "Failed to initialize build '%s': %s", n, err)) - errors.m[n] = err + ret = 1 continue } builds = append(builds, b) } + return builds, ret +} + +func (c *BuildCommand) RunContext(buildCtx context.Context, args []string) int { + cfg, ret := c.ParseArgs(args) + if ret != 0 { + return ret + } + + builds, ret := c.GetBuilds(cfg.Path) if cfg.Debug { c.Ui.Say("Debug mode enabled. Builds will not be parallelized.") @@ -146,18 +194,17 @@ func (c *BuildCommand) RunContext(buildCtx context.Context, args []string) int { packer.UiColorYellow, packer.UiColorBlue, } - buildUis := make(map[string]packer.Ui) - for i, b := range buildNames { - var ui packer.Ui - ui = c.Ui + buildUis := make(map[packer.Build]packer.Ui) + for i := range builds { + ui := c.Ui if cfg.Color { ui = &packer.ColoredUi{ Color: colors[i%len(colors)], Ui: ui, } if _, ok := c.Ui.(*packer.MachineReadableUi); !ok { - ui.Say(fmt.Sprintf("%s output will be in this color.", b)) - if i+1 == len(buildNames) { + ui.Say(fmt.Sprintf("%s: output will be in this color.", builds[i].Name())) + if i+1 == len(builds) { // Add a newline between the color output and the actual output c.Ui.Say("") } @@ -170,7 +217,7 @@ func (c *BuildCommand) RunContext(buildCtx context.Context, args []string) int { } } - buildUis[b] = ui + buildUis[builds[i]] = ui } log.Printf("Build debug mode: %v", cfg.Debug) @@ -178,7 +225,8 @@ func (c *BuildCommand) RunContext(buildCtx context.Context, args []string) int { log.Printf("On error: %v", cfg.OnError) // Set the debug and force mode and prepare all the builds - for _, b := range builds { + for i := range builds { + b := builds[i] log.Printf("Preparing build: %s", b.Name()) b.SetDebug(cfg.Debug) b.SetForce(cfg.Force) @@ -190,7 +238,7 @@ func (c *BuildCommand) RunContext(buildCtx context.Context, args []string) int { return 1 } if len(warnings) > 0 { - ui := buildUis[b.Name()] + ui := buildUis[b] ui.Say(fmt.Sprintf("Warnings for build '%s':\n", b.Name())) for _, warning := range warnings { ui.Say(fmt.Sprintf("* %s", warning)) @@ -205,6 +253,11 @@ func (c *BuildCommand) RunContext(buildCtx context.Context, args []string) int { sync.RWMutex m map[string][]packer.Artifact }{m: make(map[string][]packer.Artifact)} + // Get the builds we care about + var errors = struct { + sync.RWMutex + m map[string]error + }{m: make(map[string]error)} limitParallel := semaphore.NewWeighted(cfg.ParallelBuilds) for i := range builds { if err := buildCtx.Err(); err != nil { @@ -214,7 +267,7 @@ func (c *BuildCommand) RunContext(buildCtx context.Context, args []string) int { b := builds[i] name := b.Name() - ui := buildUis[name] + ui := buildUis[b] if err := limitParallel.Acquire(buildCtx, 1); err != nil { ui.Error(fmt.Sprintf("Build '%s' failed to acquire semaphore: %s", name, err)) errors.Lock() @@ -338,10 +391,10 @@ func (c *BuildCommand) RunContext(buildCtx context.Context, args []string) int { if len(errors.m) > 0 { // If any errors occurred, exit with a non-zero exit status - return 1 + ret = 1 } - return 0 + return ret } func (*BuildCommand) Help() string { diff --git a/command/build_parallel_test.go b/command/build_parallel_test.go index a2dc240ae..4634f5191 100644 --- a/command/build_parallel_test.go +++ b/command/build_parallel_test.go @@ -4,6 +4,7 @@ import ( "bytes" "context" "fmt" + "github.com/hashicorp/hcl/v2/hcldec" "path/filepath" "sync" "testing" @@ -28,6 +29,7 @@ type ParallelTestBuilder struct { wg sync.WaitGroup } +func (b *ParallelTestBuilder) ConfigSpec() hcldec.ObjectSpec { return nil } func (b *ParallelTestBuilder) Prepare(raws ...interface{}) ([]string, error) { return nil, nil } func (b *ParallelTestBuilder) Run(ctx context.Context, ui packer.Ui, hook packer.Hook) (packer.Artifact, error) { @@ -39,6 +41,7 @@ func (b *ParallelTestBuilder) Run(ctx context.Context, ui packer.Ui, hook packer // LockedBuilder wont run until unlock is called type LockedBuilder struct{ unlock chan interface{} } +func (b *LockedBuilder) ConfigSpec() hcldec.ObjectSpec { return nil } func (b *LockedBuilder) Prepare(raws ...interface{}) ([]string, error) { return nil, nil } func (b *LockedBuilder) Run(ctx context.Context, ui packer.Ui, hook packer.Hook) (packer.Artifact, error) { @@ -57,25 +60,13 @@ func testMetaParallel(t *testing.T, builder *ParallelTestBuilder, locked *Locked return Meta{ CoreConfig: &packer.CoreConfig{ Components: packer.ComponentFinder{ - Builder: func(n string) (packer.Builder, error) { - switch n { - case "parallel-test": - return builder, nil - case "file": - return &file.Builder{}, nil - case "lock": - return locked, nil - default: - panic(n) - } + BuilderStore: packer.MapOfBuilder{ + "parallel-test": func() (packer.Builder, error) { return builder, nil }, + "file": func() (packer.Builder, error) { return &file.Builder{}, nil }, + "lock": func() (packer.Builder, error) { return locked, nil }, }, - Provisioner: func(n string) (packer.Provisioner, error) { - switch n { - case "sleep": - return &sleep.Provisioner{}, nil - default: - panic(n) - } + ProvisionerStore: packer.MapOfProvisioner{ + "sleep": func() (packer.Provisioner, error) { return &sleep.Provisioner{}, nil }, }, }, }, diff --git a/command/build_test.go b/command/build_test.go index b68ef068c..ca8252715 100644 --- a/command/build_test.go +++ b/command/build_test.go @@ -12,9 +12,9 @@ import ( "github.com/hashicorp/packer/builder/file" "github.com/hashicorp/packer/builder/null" "github.com/hashicorp/packer/packer" - shell_local "github.com/hashicorp/packer/post-processor/shell-local" + shell_local_pp "github.com/hashicorp/packer/post-processor/shell-local" "github.com/hashicorp/packer/provisioner/shell" - sl "github.com/hashicorp/packer/provisioner/shell-local" + shell_local "github.com/hashicorp/packer/provisioner/shell-local" ) func TestBuildOnlyFileCommaFlags(t *testing.T) { @@ -166,7 +166,7 @@ func TestBuildExceptFileCommaFlags(t *testing.T) { } } -func TestBuildExceptNonExistingBuilder(t *testing.T) { +func TestBuildWithNonExistingBuilder(t *testing.T) { c := &BuildCommand{ Meta: testMetaFile(t), } @@ -202,25 +202,16 @@ func fileExists(filename string) bool { // available. This allows us to test a builder that writes files to disk. func testCoreConfigBuilder(t *testing.T) *packer.CoreConfig { components := packer.ComponentFinder{ - Builder: func(n string) (packer.Builder, error) { - if n == "file" { - return &file.Builder{}, nil - } - if n == "non-existing" { - return nil, fmt.Errorf("builder type not found") - } - return &null.Builder{}, nil + BuilderStore: packer.MapOfBuilder{ + "file": func() (packer.Builder, error) { return &file.Builder{}, nil }, + "null": func() (packer.Builder, error) { return &null.Builder{}, nil }, }, - Provisioner: func(n string) (packer.Provisioner, error) { - if n == "shell" { - return &shell.Provisioner{}, nil - } else if n == "shell-local" { - return &sl.Provisioner{}, nil - } - return nil, fmt.Errorf("requested provisioner not implemented in this test") + ProvisionerStore: packer.MapOfProvisioner{ + "shell-local": func() (packer.Provisioner, error) { return &shell_local.Provisioner{}, nil }, + "shell": func() (packer.Provisioner, error) { return &shell.Provisioner{}, nil }, }, - PostProcessor: func(n string) (packer.PostProcessor, error) { - return &shell_local.PostProcessor{}, nil + PostProcessorStore: packer.MapOfPostProcessor{ + "shell-local": func() (packer.PostProcessor, error) { return &shell_local_pp.PostProcessor{}, nil }, }, } return &packer.CoreConfig{ diff --git a/command/build_timeout_test.go b/command/build_timeout_test.go index bba3bbca7..5657a37b0 100644 --- a/command/build_timeout_test.go +++ b/command/build_timeout_test.go @@ -15,23 +15,12 @@ import ( // available. This allows us to test a builder that writes files to disk. func testCoreConfigSleepBuilder(t *testing.T) *packer.CoreConfig { components := packer.ComponentFinder{ - Builder: func(n string) (packer.Builder, error) { - switch n { - case "file": - return &file.Builder{}, nil - default: - panic(n) - } + BuilderStore: packer.MapOfBuilder{ + "file": func() (packer.Builder, error) { return &file.Builder{}, nil }, }, - Provisioner: func(n string) (packer.Provisioner, error) { - switch n { - case "shell-local": - return &shell_local.Provisioner{}, nil - case "sleep": - return &sleep.Provisioner{}, nil - default: - panic(n) - } + ProvisionerStore: packer.MapOfProvisioner{ + "sleep": func() (packer.Provisioner, error) { return &sleep.Provisioner{}, nil }, + "shell-local": func() (packer.Provisioner, error) { return &shell_local.Provisioner{}, nil }, }, } return &packer.CoreConfig{ diff --git a/command/utils.go b/command/utils.go new file mode 100644 index 000000000..1123a2f0a --- /dev/null +++ b/command/utils.go @@ -0,0 +1,22 @@ +package command + +import ( + "os" + "strings" +) + +func isDir(name string) (bool, error) { + s, err := os.Stat(name) + if err != nil { + return false, err + } + return s.IsDir(), nil +} + +func isHCLLoaded(name string) (bool, error) { + if strings.HasSuffix(name, ".pkr.hcl") || + strings.HasSuffix(name, ".pkr.json") { + return true, nil + } + return isDir(name) +} diff --git a/common/shell-local/config.go b/common/shell-local/config.go index e4c808de9..2e6064258 100644 --- a/common/shell-local/config.go +++ b/common/shell-local/config.go @@ -50,7 +50,7 @@ func Decode(config *Config, raws ...interface{}) error { WinRMPassword: `{{.WinRMPassword}}`, } - err := configHelper.Decode(&config, &configHelper.DecodeOpts{ + err := configHelper.Decode(config, &configHelper.DecodeOpts{ Interpolate: true, InterpolateContext: &config.ctx, InterpolateFilter: &interpolate.RenderFilter{ diff --git a/common/shell-local/config.hcl2spec.go b/common/shell-local/config.hcl2spec.go index 257cd390d..e65a3a89d 100644 --- a/common/shell-local/config.hcl2spec.go +++ b/common/shell-local/config.hcl2spec.go @@ -33,10 +33,13 @@ type FlatConfig struct { // FlatMapstructure returns a new FlatConfig. // FlatConfig is an auto-generated flat version of Config. // Where the contents a fields with a `mapstructure:,squash` tag are bubbled up. -func (*Config) FlatMapstructure() interface{} { return new(FlatConfig) } +func (*Config) FlatMapstructure() interface{ HCL2Spec() map[string]hcldec.Spec } { + return new(FlatConfig) +} -// HCL2Spec returns the hcldec.Spec of a FlatConfig. -// This spec is used by HCL to read the fields of FlatConfig. +// HCL2Spec returns the hcl spec of a Config. +// This spec is used by HCL to read the fields of Config. +// The decoded values from this spec will then be applied to a FlatConfig. func (*FlatConfig) HCL2Spec() map[string]hcldec.Spec { s := map[string]hcldec.Spec{ "packer_build_name": &hcldec.AttrSpec{Name: "packer_build_name", Type: cty.String, Required: false}, diff --git a/config.go b/config.go index 173d4ece0..f99761979 100644 --- a/config.go +++ b/config.go @@ -9,6 +9,7 @@ import ( "os/exec" "path/filepath" "runtime" + "sort" "strings" "github.com/hashicorp/packer/command" @@ -27,9 +28,9 @@ type config struct { PluginMinPort int PluginMaxPort int - Builders map[string]string - PostProcessors map[string]string `json:"post-processors"` - Provisioners map[string]string + Builders packer.MapOfBuilder + Provisioners packer.MapOfProvisioner + PostProcessors packer.MapOfPostProcessor `json:"post-processors"` } // Decodes configuration in JSON format from the given io.Reader into @@ -58,7 +59,7 @@ func (c *config) Discover() error { if err != nil { log.Printf("[ERR] Error loading exe directory: %s", err) } else { - if err := c.discover(filepath.Dir(exePath)); err != nil { + if err := c.discoverExternalComponents(filepath.Dir(exePath)); err != nil { return err } } @@ -68,19 +69,19 @@ func (c *config) Discover() error { if err != nil { log.Printf("[ERR] Error loading config directory: %s", err) } else { - if err := c.discover(filepath.Join(dir, "plugins")); err != nil { + if err := c.discoverExternalComponents(filepath.Join(dir, "plugins")); err != nil { return err } } // Next, look in the CWD. - if err := c.discover("."); err != nil { + if err := c.discoverExternalComponents("."); err != nil { return err } // Finally, try to use an internal plugin. Note that this will not override // any previously-loaded plugins. - if err := c.discoverInternal(); err != nil { + if err := c.discoverInternalComponents(); err != nil { return err } @@ -89,51 +90,33 @@ func (c *config) Discover() error { // This is a proper packer.BuilderFunc that can be used to load packer.Builder // implementations from the defined plugins. -func (c *config) LoadBuilder(name string) (packer.Builder, error) { +func (c *config) StartBuilder(name string) (packer.Builder, error) { log.Printf("Loading builder: %s\n", name) - bin, ok := c.Builders[name] - if !ok { - log.Printf("Builder not found: %s\n", name) - return nil, nil - } - - return c.pluginClient(bin).Builder() + return c.Builders.Start(name) } // This is a proper implementation of packer.HookFunc that can be used // to load packer.Hook implementations from the defined plugins. -func (c *config) LoadHook(name string) (packer.Hook, error) { +func (c *config) StarHook(name string) (packer.Hook, error) { log.Printf("Loading hook: %s\n", name) return c.pluginClient(name).Hook() } // This is a proper packer.PostProcessorFunc that can be used to load // packer.PostProcessor implementations from defined plugins. -func (c *config) LoadPostProcessor(name string) (packer.PostProcessor, error) { +func (c *config) StartPostProcessor(name string) (packer.PostProcessor, error) { log.Printf("Loading post-processor: %s", name) - bin, ok := c.PostProcessors[name] - if !ok { - log.Printf("Post-processor not found: %s", name) - return nil, nil - } - - return c.pluginClient(bin).PostProcessor() + return c.PostProcessors.Start(name) } // This is a proper packer.ProvisionerFunc that can be used to load // packer.Provisioner implementations from defined plugins. -func (c *config) LoadProvisioner(name string) (packer.Provisioner, error) { +func (c *config) StartProvisioner(name string) (packer.Provisioner, error) { log.Printf("Loading provisioner: %s\n", name) - bin, ok := c.Provisioners[name] - if !ok { - log.Printf("Provisioner not found: %s\n", name) - return nil, nil - } - - return c.pluginClient(bin).Provisioner() + return c.Provisioners.Start(name) } -func (c *config) discover(path string) error { +func (c *config) discoverExternalComponents(path string) error { var err error if !filepath.IsAbs(path) { @@ -142,32 +125,69 @@ func (c *config) discover(path string) error { return err } } + externallyUsed := []string{} - err = c.discoverSingle( - filepath.Join(path, "packer-builder-*"), &c.Builders) + pluginPaths, err := c.discoverSingle(filepath.Join(path, "packer-builder-*")) if err != nil { return err } + for plugin := range pluginPaths { + plugin := plugin + c.Builders[plugin] = func() (packer.Builder, error) { + return c.pluginClient(pluginPaths[plugin]).Builder() + } + externallyUsed = append(externallyUsed, plugin) + } + if len(externallyUsed) > 0 { + sort.Strings(externallyUsed) + log.Printf("using external builders %v", externallyUsed) + externallyUsed = nil + } - err = c.discoverSingle( - filepath.Join(path, "packer-post-processor-*"), &c.PostProcessors) + pluginPaths, err = c.discoverSingle(filepath.Join(path, "packer-post-processor-*")) if err != nil { return err } + for plugin := range pluginPaths { + plugin := plugin + c.PostProcessors[plugin] = func() (packer.PostProcessor, error) { + return c.pluginClient(pluginPaths[plugin]).PostProcessor() + } + externallyUsed = append(externallyUsed, plugin) + } + if len(externallyUsed) > 0 { + sort.Strings(externallyUsed) + log.Printf("using external post-processors %v", externallyUsed) + externallyUsed = nil + } - return c.discoverSingle( - filepath.Join(path, "packer-provisioner-*"), &c.Provisioners) + pluginPaths, err = c.discoverSingle(filepath.Join(path, "packer-provisioner-*")) + if err != nil { + return err + } + for plugin := range pluginPaths { + plugin := plugin + c.Provisioners[plugin] = func() (packer.Provisioner, error) { + return c.pluginClient(pluginPaths[plugin]).Provisioner() + } + externallyUsed = append(externallyUsed, plugin) + } + if len(externallyUsed) > 0 { + sort.Strings(externallyUsed) + log.Printf("using external provisioners %v", externallyUsed) + externallyUsed = nil + } + + return nil } -func (c *config) discoverSingle(glob string, m *map[string]string) error { +func (c *config) discoverSingle(glob string) (map[string]string, error) { matches, err := filepath.Glob(glob) if err != nil { - return err + return nil, err } - if *m == nil { - *m = make(map[string]string) - } + res := make(map[string]string) prefix := filepath.Base(glob) prefix = prefix[:strings.Index(prefix, "*")] @@ -191,13 +211,13 @@ func (c *config) discoverSingle(glob string, m *map[string]string) error { // Look for foo-bar-baz. The plugin name is "baz" plugin := file[len(prefix):] log.Printf("[DEBUG] Discovered plugin: %s = %s", plugin, match) - (*m)[plugin] = match + res[plugin] = match } - return nil + return res, nil } -func (c *config) discoverInternal() error { +func (c *config) discoverInternalComponents() error { // Get the packer binary path packerPath, err := osext.Executable() if err != nil { @@ -206,34 +226,38 @@ func (c *config) discoverInternal() error { } for builder := range command.Builders { + builder := builder _, found := (c.Builders)[builder] if !found { - (c.Builders)[builder] = fmt.Sprintf("%s%splugin%spacker-builder-%s", - packerPath, PACKERSPACE, PACKERSPACE, builder) - } else { - log.Printf("Using external plugin for %s", builder) + c.Builders[builder] = func() (packer.Builder, error) { + bin := fmt.Sprintf("%s%splugin%spacker-builder-%s", + packerPath, PACKERSPACE, PACKERSPACE, builder) + return c.pluginClient(bin).Builder() + } } } for provisioner := range command.Provisioners { + provisioner := provisioner _, found := (c.Provisioners)[provisioner] if !found { - (c.Provisioners)[provisioner] = fmt.Sprintf( - "%s%splugin%spacker-provisioner-%s", - packerPath, PACKERSPACE, PACKERSPACE, provisioner) - } else { - log.Printf("Using external plugin for %s", provisioner) + c.Provisioners[provisioner] = func() (packer.Provisioner, error) { + bin := fmt.Sprintf("%s%splugin%spacker-provisioner-%s", + packerPath, PACKERSPACE, PACKERSPACE, provisioner) + return c.pluginClient(bin).Provisioner() + } } } for postProcessor := range command.PostProcessors { + postProcessor := postProcessor _, found := (c.PostProcessors)[postProcessor] if !found { - (c.PostProcessors)[postProcessor] = fmt.Sprintf( - "%s%splugin%spacker-post-processor-%s", - packerPath, PACKERSPACE, PACKERSPACE, postProcessor) - } else { - log.Printf("Using external plugin for %s", postProcessor) + c.PostProcessors[postProcessor] = func() (packer.PostProcessor, error) { + bin := fmt.Sprintf("%s%splugin%spacker-post-processor-%s", + packerPath, PACKERSPACE, PACKERSPACE, postProcessor) + return c.pluginClient(bin).PostProcessor() + } } } @@ -248,7 +272,7 @@ func (c *config) pluginClient(path string) *plugin.Client { if err != nil { // If that doesn't work, look for it in the same directory // as the `packer` executable (us). - log.Printf("Plugin could not be found. Checking same directory as executable.") + log.Printf("Plugin could not be found at %s (%v). Checking same directory as executable.", originalPath, err) exePath, err := osext.Executable() if err != nil { log.Printf("Couldn't get current exe path: %s", err) diff --git a/go.mod b/go.mod index 27db76320..d6f811f73 100644 --- a/go.mod +++ b/go.mod @@ -81,6 +81,7 @@ require ( github.com/hashicorp/golang-lru v0.5.3 // indirect github.com/hashicorp/hcl v1.0.0 // indirect github.com/hashicorp/hcl/v2 v2.0.0 + github.com/hashicorp/hcl2 v0.0.0-20191002203319-fb75b3253c80 github.com/hashicorp/serf v0.8.2 // indirect github.com/hashicorp/vault v1.1.0 github.com/hashicorp/yamux v0.0.0-20181012175058-2f1d1f20f75d @@ -124,8 +125,6 @@ require ( github.com/nbio/st v0.0.0-20140626010706-e9e8d9816f32 // indirect github.com/nu7hatch/gouuid v0.0.0-20131221200532-179d4d0c4d8d // indirect github.com/olekukonko/tablewriter v0.0.0-20180105111133-96aac992fc8b - github.com/onsi/ginkgo v1.7.0 // indirect - github.com/onsi/gomega v1.4.3 // indirect github.com/oracle/oci-go-sdk v1.8.0 github.com/outscale/osc-go v0.0.1 github.com/packer-community/winrmcp v0.0.0-20180921204643-0fd363d6159a @@ -157,7 +156,7 @@ require ( github.com/xanzy/go-cloudstack v0.0.0-20190526095453-42f262b63ed0 github.com/yandex-cloud/go-genproto v0.0.0-20190916101622-7617782d381e github.com/yandex-cloud/go-sdk v0.0.0-20190916101744-c781afa45829 - github.com/zclconf/go-cty v1.1.0 + github.com/zclconf/go-cty v1.1.2-0.20191126233707-f0f7fd24c4af go.opencensus.io v0.22.2 // indirect golang.org/x/crypto v0.0.0-20191202143827-86a70503ff7e golang.org/x/exp v0.0.0-20191129062945-2f5052295587 // indirect diff --git a/go.sum b/go.sum index 434ccc196..f6676f405 100644 --- a/go.sum +++ b/go.sum @@ -90,6 +90,7 @@ github.com/bgentry/speakeasy v0.1.0 h1:ByYyxL9InA1OWqxJqqp2A5pYHUrCiAL6K3J+LKSsQ github.com/bgentry/speakeasy v0.1.0/go.mod h1:+zsyZBPWlz7T6j88CTgSN5bM796AkVf0kBD4zp0CCIs= github.com/biogo/hts v0.0.0-20160420073057-50da7d4131a3 h1:3b+p838vN4sc37brz9W2HDphtSwZFcXZwFLyzm5Vk28= github.com/biogo/hts v0.0.0-20160420073057-50da7d4131a3/go.mod h1:YOY5xnRf7Jz2SZCLSKgVfyqNzbRgyTznM3HyDqQMxcU= +github.com/bsm/go-vlq v0.0.0-20150828105119-ec6e8d4f5f4e/go.mod h1:N+BjUcTjSxc2mtRGSCPsat1kze3CUtvJN3/jTXlp29k= github.com/c2h5oh/datasize v0.0.0-20171227191756-4eba002a5eae h1:2Zmk+8cNvAGuY8AyvZuWpUdpQUAXwfom4ReVMe/CTIo= github.com/c2h5oh/datasize v0.0.0-20171227191756-4eba002a5eae/go.mod h1:S/7n9copUssQ56c7aAgHqftWO4LTf4xY6CGWt8Bc+3M= github.com/census-instrumentation/opencensus-proto v0.2.0 h1:LzQXZOgg4CQfE6bFvXGM30YZL1WW/M337pXml+GrcZ4= @@ -222,6 +223,7 @@ github.com/grpc-ecosystem/grpc-gateway v1.8.5 h1:2+KSC78XiO6Qy0hIjfc1OD9H+hsaJdJ github.com/grpc-ecosystem/grpc-gateway v1.8.5/go.mod h1:vNeuVxBJEsws4ogUvrchl83t/GYV9WGTSLVdBhOQFDY= github.com/hashicorp/consul v1.4.0 h1:PQTW4xCuAExEiSbhrsFsikzbW5gVBoi74BjUvYFyKHw= github.com/hashicorp/consul v1.4.0/go.mod h1:mFrjN1mfidgJfYP1xrJCF+AfRhr6Eaqhb2+sfyn/OOI= +github.com/hashicorp/errwrap v0.0.0-20180715044906-d6c0cd880357/go.mod h1:YH+1FKiLXxHSkmPseP+kNlulaMuP3n2brvKWEqk/Jc4= github.com/hashicorp/errwrap v1.0.0 h1:hLrqtEDnRye3+sgx6z4qVLNuviH3MR5aQ0ykNJa/UYA= github.com/hashicorp/errwrap v1.0.0/go.mod h1:YH+1FKiLXxHSkmPseP+kNlulaMuP3n2brvKWEqk/Jc4= github.com/hashicorp/go-checkpoint v0.0.0-20171009173528-1545e56e46de h1:XDCSythtg8aWSRSO29uwhgh7b127fWr+m5SemqjSUL8= @@ -234,6 +236,7 @@ github.com/hashicorp/go-immutable-radix v1.0.0 h1:AKDB1HM5PWEA7i4nhcpwOrO2byshxB github.com/hashicorp/go-immutable-radix v1.0.0/go.mod h1:0y9vanUI8NX6FsYoO3zeMjhV/C5i9g4Q3DwcSNZ4P60= github.com/hashicorp/go-msgpack v0.5.3 h1:zKjpN5BK/P5lMYrLmBHdBULWbJ0XpYR+7NGzqkZzoD4= github.com/hashicorp/go-msgpack v0.5.3/go.mod h1:ahLV/dePpqEmjfWmKiqvPkv/twdG7iPBM1vqhUKIvfM= +github.com/hashicorp/go-multierror v0.0.0-20180717150148-3d5d8f294aa0/go.mod h1:JMRHfdO9jKNzS/+BTlxCjKNQHg/jZAft8U7LloJvN7I= github.com/hashicorp/go-multierror v1.0.0 h1:iVjPR7a6H0tWELX5NxNe7bYopibicUzc7uPribsnS6o= github.com/hashicorp/go-multierror v1.0.0/go.mod h1:dHtQlpGsu+cZNNAkkCN/P3hoUDHhCYQXV3UM06sGGrk= github.com/hashicorp/go-oracle-terraform v0.0.0-20181016190316-007121241b79 h1:RKu7yAXZTaQsxj1K9GDsh+QVw0+Wu1SWHxtbFN0n+hE= @@ -266,6 +269,8 @@ 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.0.0 h1:efQznTz+ydmQXq3BOnRa3AXzvCeTq1P4dKj/z5GLlY8= github.com/hashicorp/hcl/v2 v2.0.0/go.mod h1:oVVDG71tEinNGYCxinCYadcmKU9bglqW9pV3txagJ90= +github.com/hashicorp/hcl2 v0.0.0-20191002203319-fb75b3253c80 h1:PFfGModn55JA0oBsvFghhj0v93me+Ctr3uHC/UmFAls= +github.com/hashicorp/hcl2 v0.0.0-20191002203319-fb75b3253c80/go.mod h1:Cxv+IJLuBiEhQ7pBYGEuORa0nr4U994pE8mYLuFd7v0= 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/memberlist v0.1.3 h1:EmmoJme1matNzb+hMpDuR/0sbJSUisxyqBGG676r31M= @@ -290,6 +295,7 @@ github.com/jdcloud-api/jdcloud-sdk-go v1.9.1-0.20190605102154-3d81a50ca961 h1:a2 github.com/jdcloud-api/jdcloud-sdk-go v1.9.1-0.20190605102154-3d81a50ca961/go.mod h1:UrKjuULIWLjHFlG6aSPunArE5QX57LftMmStAZJBEX8= github.com/jehiah/go-strftime v0.0.0-20171201141054-1d33003b3869 h1:IPJ3dvxmJ4uczJe5YQdrYB16oTJlGSC/OyZDqUk9xX4= github.com/jehiah/go-strftime v0.0.0-20171201141054-1d33003b3869/go.mod h1:cJ6Cj7dQo+O6GJNiMx+Pa94qKj+TG8ONdKHgMNIyyag= +github.com/jessevdk/go-flags v1.4.0/go.mod h1:4FA24M0QyGHXBuZZK/XkWh8h0e1EYbRYJSGM75WSRxI= github.com/jmespath/go-jmespath v0.0.0-20160202185014-0b12d6b521d8/go.mod h1:Nht3zPeWKUH0NzdCt2Blrr5ys8VGpn0CEB0cQHVjt7k= github.com/jmespath/go-jmespath v0.0.0-20180206201540-c2b33e8439af h1:pmfjZENx5imkbgOkpRUYLnmbU7UEFbjtDA2hxJ1ichM= github.com/jmespath/go-jmespath v0.0.0-20180206201540-c2b33e8439af/go.mod h1:Nht3zPeWKUH0NzdCt2Blrr5ys8VGpn0CEB0cQHVjt7k= @@ -476,6 +482,7 @@ github.com/ugorji/go v0.0.0-20151218193438-646ae4a518c1 h1:U6ufy3mLDgg9RYupntOvA github.com/ugorji/go v0.0.0-20151218193438-646ae4a518c1/go.mod h1:hnLbHMwcvSihnDhEfx2/BzKp2xb0Y+ErdfYcrs9tkJQ= github.com/ulikunitz/xz v0.5.5 h1:pFrO0lVpTBXLpYw+pnLj6TbvHuyjXMfjGeCwSqCVwok= github.com/ulikunitz/xz v0.5.5/go.mod h1:2bypXElzHzzJZwzH67Y6wb67pO62Rzfn7BSiF4ABRW8= +github.com/vmihailenco/msgpack v3.3.3+incompatible h1:wapg9xDUZDzGCNFlwc5SqI1rvcciqcxEHac4CYj89xI= github.com/vmihailenco/msgpack v3.3.3+incompatible/go.mod h1:fy3FlTQTDXWkZ7Bh6AcGMlsjHatGryHQYUTf1ShIgkk= github.com/vmware/govmomi v0.0.0-20170707011325-c2105a174311 h1:s5pyxd5S6wRs2WpEE0xRfWUF46Wbz44h203KnbX0ecI= github.com/vmware/govmomi v0.0.0-20170707011325-c2105a174311/go.mod h1:URlwyTFZX72RmxtxuaFL2Uj3fD1JTvZdx59bHWk6aFU= @@ -485,8 +492,13 @@ github.com/yandex-cloud/go-genproto v0.0.0-20190916101622-7617782d381e h1:hzwq5G github.com/yandex-cloud/go-genproto v0.0.0-20190916101622-7617782d381e/go.mod h1:HEUYX/p8966tMUHHT+TsS0hF/Ca/NYwqprC5WXSDMfE= github.com/yandex-cloud/go-sdk v0.0.0-20190916101744-c781afa45829 h1:2FGwbx03GpP1Ulzg/L46tSoKh9t4yg8BhMKQl/Ff1x8= github.com/yandex-cloud/go-sdk v0.0.0-20190916101744-c781afa45829/go.mod h1:Eml0jFLU4VVHgIN8zPHMuNwZXVzUMILyO6lQZSfz854= +github.com/zclconf/go-cty v1.0.0/go.mod h1:xnAOWiHeOqg2nWS62VtQ7pbOu17FtxJNW8RLEih+O3s= github.com/zclconf/go-cty v1.1.0 h1:uJwc9HiBOCpoKIObTQaLR+tsEXx1HBHnOsOOpcdhZgw= github.com/zclconf/go-cty v1.1.0/go.mod h1:xnAOWiHeOqg2nWS62VtQ7pbOu17FtxJNW8RLEih+O3s= +github.com/zclconf/go-cty v1.1.1 h1:Shl2p9Dat0cqJfXu0DZa+cOTRPhXQjK8IYWD6GVfiqo= +github.com/zclconf/go-cty v1.1.1/go.mod h1:xnAOWiHeOqg2nWS62VtQ7pbOu17FtxJNW8RLEih+O3s= +github.com/zclconf/go-cty v1.1.2-0.20191126233707-f0f7fd24c4af h1:4arg31xOP/qIUV1YVbCWJtChPGzwGzgmlucVbddUq+Y= +github.com/zclconf/go-cty v1.1.2-0.20191126233707-f0f7fd24c4af/go.mod h1:xnAOWiHeOqg2nWS62VtQ7pbOu17FtxJNW8RLEih+O3s= go.opencensus.io v0.21.0 h1:mU6zScU4U1YAFPHEHYk+3JC4SY7JxgkqS10ZOSyksNg= go.opencensus.io v0.21.0/go.mod h1:mSImk1erAIZhrmZN+AvHh14ztQfjbGwt4TtuofqLduU= go.opencensus.io v0.22.0 h1:C9hSCOW830chIVkdja34wa6Ky+IzWllkUinR+BtRZd4= @@ -552,6 +564,7 @@ golang.org/x/net v0.0.0-20190311183353-d8887717615a h1:oWX7TPOiFAMXLq8o0ikBYfCJV golang.org/x/net v0.0.0-20190311183353-d8887717615a/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= golang.org/x/net v0.0.0-20190501004415-9ce7a6920f09/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= +golang.org/x/net v0.0.0-20190502183928-7f726cade0ab/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= golang.org/x/net v0.0.0-20190503192946-f4e77d36d62c/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= golang.org/x/net v0.0.0-20190603091049-60506f45cf65/go.mod h1:HSz+uSET+XFnRR8LxR5pz3Of3rY3CfYBVs4xY44aLks= golang.org/x/net v0.0.0-20190620200207-3b0461eec859 h1:R/3boaszxrf1GEUWTVDzSKVwLmSJpwZ1yqXm8j0v2QI= @@ -707,6 +720,7 @@ gopkg.in/yaml.v2 v2.2.7/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= honnef.co/go/tools v0.0.0-20190102054323-c2f93a96b099/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= honnef.co/go/tools v0.0.0-20190106161140-3f1c8253044a/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= honnef.co/go/tools v0.0.0-20190418001031-e561f6794a2a/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= +howett.net/plist v0.0.0-20181124034731-591f970eefbb/go.mod h1:vMygbs4qMhSZSc4lCUl2OEE+rDiIIJAIdR4m7MiMcm0= honnef.co/go/tools v0.0.0-20190523083050-ea95bdfd59fc/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= honnef.co/go/tools v0.0.1-2019.2.3 h1:3JgtbtFHMiCmsznwGVTUWbgGov+pVqnlf1dEJTNAXeM= honnef.co/go/tools v0.0.1-2019.2.3/go.mod h1:a3bituU0lyd329TUQxRnasdCoJDkEUEAqEt0JzvZhAg= diff --git a/hcl2template/common_test.go b/hcl2template/common_test.go new file mode 100644 index 000000000..3feae5347 --- /dev/null +++ b/hcl2template/common_test.go @@ -0,0 +1,236 @@ +package hcl2template + +import ( + "fmt" + "testing" + "time" + + "github.com/google/go-cmp/cmp" + "github.com/google/go-cmp/cmp/cmpopts" + + "github.com/hashicorp/hcl/v2" + "github.com/hashicorp/hcl/v2/hclparse" + "github.com/hashicorp/packer/helper/config" + "github.com/hashicorp/packer/packer" + "github.com/zclconf/go-cty/cty" +) + +func getBasicParser() *Parser { + return &Parser{ + Parser: hclparse.NewParser(), + BuilderSchemas: mapOfBuilder(map[string]packer.Builder{ + "amazon-ebs": &MockBuilder{}, + "virtualbox-iso": &MockBuilder{}, + }), + ProvisionersSchemas: mapOfProvisioner(map[string]packer.Provisioner{ + "shell": &MockProvisioner{}, + "file": &MockProvisioner{}, + }), + PostProcessorsSchemas: mapOfPostProcessor(map[string]packer.PostProcessor{ + "amazon-import": &MockPostProcessor{}, + }), + } +} + +type mapOfBuilder map[string]packer.Builder + +func (mob mapOfBuilder) Has(builder string) bool { + _, res := mob[builder] + return res +} + +func (mob mapOfBuilder) Start(builder string) (packer.Builder, error) { + d, found := mob[builder] + var err error + if !found { + err = fmt.Errorf("Unknown entry %s", builder) + } + return d, err +} + +func (mob mapOfBuilder) List() []string { + res := []string{} + for k := range mob { + res = append(res, k) + } + return res +} + +type mapOfCommunicator map[string]packer.ConfigurableCommunicator + +func (mob mapOfCommunicator) Start(communicator string) (packer.ConfigurableCommunicator, error) { + c, found := mob[communicator] + var err error + if !found { + err = fmt.Errorf("Unknown entry %s", communicator) + } + return c, err +} + +type mapOfProvisioner map[string]packer.Provisioner + +func (mop mapOfProvisioner) Has(provisioner string) bool { + _, res := mop[provisioner] + return res +} + +func (mop mapOfProvisioner) Start(provisioner string) (packer.Provisioner, error) { + p, found := mop[provisioner] + var err error + if !found { + err = fmt.Errorf("Unknown provisioner %s", provisioner) + } + return p, err +} + +func (mod mapOfProvisioner) List() []string { + res := []string{} + for k := range mod { + res = append(res, k) + } + return res +} + +type mapOfPostProcessor map[string]packer.PostProcessor + +func (mop mapOfPostProcessor) Has(provisioner string) bool { + _, res := mop[provisioner] + return res +} + +func (mop mapOfPostProcessor) Start(postProcessor string) (packer.PostProcessor, error) { + p, found := mop[postProcessor] + var err error + if !found { + err = fmt.Errorf("Unknown post-processor %s", postProcessor) + } + return p, err +} + +func (mod mapOfPostProcessor) List() []string { + res := []string{} + for k := range mod { + res = append(res, k) + } + return res +} + +type parseTestArgs struct { + filename string +} + +type parseTest struct { + name string + parser *Parser + args parseTestArgs + + parseWantCfg *PackerConfig + parseWantDiags bool + parseWantDiagHasErrors bool + + getBuildsWantBuilds []packer.Build + getBuildsWantDiags bool + // getBuildsWantDiagHasErrors bool +} + +func testParse(t *testing.T, tests []parseTest) { + + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + gotCfg, gotDiags := tt.parser.parse(tt.args.filename) + if tt.parseWantDiags == (gotDiags == nil) { + t.Fatalf("Parser.parse() unexpected diagnostics. %s", gotDiags) + } + if tt.parseWantDiagHasErrors != gotDiags.HasErrors() { + t.Fatalf("Parser.parse() unexpected diagnostics HasErrors. %s", gotDiags) + } + if diff := cmp.Diff(tt.parseWantCfg, gotCfg, + cmpopts.IgnoreUnexported(cty.Value{}, Source{}, ProvisionerBlock{}, PostProcessorBlock{}), + cmpopts.IgnoreTypes(HCL2Ref{}), + cmpopts.IgnoreTypes([]hcl.Range{}), + cmpopts.IgnoreTypes(hcl.Range{}), + cmpopts.IgnoreInterfaces(struct{ hcl.Expression }{}), + cmpopts.IgnoreInterfaces(struct{ hcl.Body }{}), + ); diff != "" { + t.Fatalf("Parser.parse() wrong packer config. %s", diff) + } + if gotDiags.HasErrors() { + return + } + + gotBuilds, gotDiags := tt.parser.getBuilds(gotCfg) + if tt.getBuildsWantDiags == (gotDiags == nil) { + t.Fatalf("Parser.getBuilds() unexpected diagnostics. %s", gotDiags) + } + if diff := cmp.Diff(tt.getBuildsWantBuilds, gotBuilds, + cmpopts.IgnoreUnexported(packer.CoreBuild{}, + packer.CoreBuildProvisioner{}, + packer.CoreBuildPostProcessor{}, + ), + ); diff != "" { + t.Fatalf("Parser.getBuilds() wrong packer builds. %s", diff) + } + }) + } +} + +var ( + // everything in the tests is a basicNestedMockConfig this allow to test + // each known type to packer ( and embedding ) in one go. + basicNestedMockConfig = NestedMockConfig{ + String: "string", + Int: 42, + Int64: 43, + Bool: true, + Trilean: config.TriTrue, + Duration: 10 * time.Second, + MapStringString: map[string]string{ + "a": "b", + "c": "d", + }, + SliceString: []string{ + "a", + "b", + "c", + }, + } + + basicMockBuilder = &MockBuilder{ + Config: MockConfig{ + NestedMockConfig: basicNestedMockConfig, + Nested: basicNestedMockConfig, + NestedSlice: []NestedMockConfig{ + basicNestedMockConfig, + basicNestedMockConfig, + }, + }, + } + + basicMockProvisioner = &MockProvisioner{ + Config: MockConfig{ + NestedMockConfig: basicNestedMockConfig, + Nested: basicNestedMockConfig, + NestedSlice: []NestedMockConfig{ + {}, + }, + }, + } + basicMockPostProcessor = &MockPostProcessor{ + Config: MockConfig{ + NestedMockConfig: basicNestedMockConfig, + Nested: basicNestedMockConfig, + NestedSlice: []NestedMockConfig{ + {}, + }, + }, + } + basicMockCommunicator = &MockCommunicator{ + Config: MockConfig{ + NestedMockConfig: basicNestedMockConfig, + Nested: basicNestedMockConfig, + NestedSlice: []NestedMockConfig{ + {}, + }, + }, + } +) diff --git a/hcl2template/config_load.go b/hcl2template/config_load.go deleted file mode 100644 index 61bd281aa..000000000 --- a/hcl2template/config_load.go +++ /dev/null @@ -1,101 +0,0 @@ -package hcl2template - -import ( - "fmt" - - "github.com/hashicorp/hcl/v2" - "github.com/zclconf/go-cty/cty" -) - -type Artifacts map[ArtifactRef]*Artifact - -type Artifact struct { - Type string - Name string - - DeclRange hcl.Range - - Config hcl.Body -} - -func (a *Artifact) Ref() ArtifactRef { - return ArtifactRef{ - Type: a.Type, - Name: a.Name, - } -} - -type ArtifactRef struct { - Type string - Name string -} - -// NoArtifact is the zero value of ArtifactRef, representing the absense of an -// artifact. -var NoArtifact ArtifactRef - -func artifactRefFromAbsTraversal(t hcl.Traversal) (ArtifactRef, hcl.Diagnostics) { - var diags hcl.Diagnostics - if len(t) != 3 { - diags = append(diags, &hcl.Diagnostic{ - Severity: hcl.DiagError, - Summary: "Invalid artifact reference", - Detail: "An artifact reference must have three parts separated by periods: the keyword \"artifact\", the builder type name, and the artifact name.", - Subject: t.SourceRange().Ptr(), - }) - return NoArtifact, diags - } - - if t.RootName() != "artifact" { - diags = append(diags, &hcl.Diagnostic{ - Severity: hcl.DiagError, - Summary: "Invalid artifact reference", - Detail: "The first part of an artifact reference must be the keyword \"artifact\".", - Subject: t[0].SourceRange().Ptr(), - }) - return NoArtifact, diags - } - btStep, ok := t[1].(hcl.TraverseAttr) - if !ok { - diags = append(diags, &hcl.Diagnostic{ - Severity: hcl.DiagError, - Summary: "Invalid artifact reference", - Detail: "The second part of an artifact reference must be an identifier giving the builder type of the artifact.", - Subject: t[1].SourceRange().Ptr(), - }) - return NoArtifact, diags - } - nameStep, ok := t[2].(hcl.TraverseAttr) - if !ok { - diags = append(diags, &hcl.Diagnostic{ - Severity: hcl.DiagError, - Summary: "Invalid artifact reference", - Detail: "The third part of an artifact reference must be an identifier giving the name of the artifact.", - Subject: t[2].SourceRange().Ptr(), - }) - return NoArtifact, diags - } - - return ArtifactRef{ - Type: btStep.Name, - Name: nameStep.Name, - }, diags -} - -func (r ArtifactRef) String() string { - return fmt.Sprintf("%s.%s", r.Type, r.Name) -} - -// decodeBodyWithoutSchema is a generic alternative to hcldec.Decode that -// just extracts whatever attributes are present and rejects any nested blocks, -// for compatibility with legacy builders that can't provide explicit schema. -func decodeBodyWithoutSchema(body hcl.Body, ctx *hcl.EvalContext) (cty.Value, hcl.Diagnostics) { - attrs, diags := body.JustAttributes() - vals := make(map[string]cty.Value) - for name, attr := range attrs { - val, moreDiags := attr.Expr.Value(ctx) - diags = append(diags, moreDiags...) - vals[name] = val - } - return cty.ObjectVal(vals), diags -} diff --git a/hcl2template/decode.go b/hcl2template/decode.go new file mode 100644 index 000000000..e7be2136e --- /dev/null +++ b/hcl2template/decode.go @@ -0,0 +1,15 @@ +package hcl2template + +import ( + "github.com/hashicorp/hcl/v2" + "github.com/hashicorp/hcl/v2/hcldec" + "github.com/zclconf/go-cty/cty" +) + +type Decodable interface { + ConfigSpec() hcldec.ObjectSpec +} + +func decodeHCL2Spec(block *hcl.Block, ctx *hcl.EvalContext, dec Decodable) (cty.Value, hcl.Diagnostics) { + return hcldec.Decode(block.Body, dec.ConfigSpec(), ctx) +} diff --git a/hcl2template/load_test.go b/hcl2template/load_test.go deleted file mode 100644 index 0f4d6f91d..000000000 --- a/hcl2template/load_test.go +++ /dev/null @@ -1,352 +0,0 @@ -package hcl2template - -import ( - "testing" - - awscommon "github.com/hashicorp/packer/builder/amazon/common" - - "github.com/hashicorp/hcl/v2" - "github.com/hashicorp/hcl/v2/hclparse" - "github.com/zclconf/go-cty/cty" - - "github.com/hashicorp/packer/helper/communicator" - - amazonebs "github.com/hashicorp/packer/builder/amazon/ebs" - "github.com/hashicorp/packer/builder/virtualbox/iso" - - "github.com/hashicorp/packer/provisioner/file" - "github.com/hashicorp/packer/provisioner/shell" - - amazon_import "github.com/hashicorp/packer/post-processor/amazon-import" - - "github.com/google/go-cmp/cmp" - "github.com/google/go-cmp/cmp/cmpopts" -) - -func getBasicParser() *Parser { - return &Parser{ - Parser: hclparse.NewParser(), - ProvisionersSchemas: map[string]Decodable{ - "shell": &shell.Config{}, - "file": &file.Config{}, - }, - PostProvisionersSchemas: map[string]Decodable{ - "amazon-import": &amazon_import.Config{}, - }, - CommunicatorSchemas: map[string]Decodable{ - "ssh": &communicator.SSH{}, - "winrm": &communicator.WinRM{}, - }, - SourceSchemas: map[string]Decodable{ - "amazon-ebs": &amazonebs.Config{}, - "virtualbox-iso": &iso.Config{}, - }, - } -} - -func TestParser_ParseFile(t *testing.T) { - defaultParser := getBasicParser() - - type fields struct { - Parser *hclparse.Parser - } - type args struct { - filename string - cfg *PackerConfig - } - tests := []struct { - name string - parser *Parser - args args - wantPackerConfig *PackerConfig - wantDiags bool - }{ - { - "valid " + sourceLabel + " load", - defaultParser, - args{"testdata/sources/basic.pkr.hcl", new(PackerConfig)}, - &PackerConfig{ - Sources: map[SourceRef]*Source{ - SourceRef{ - Type: "virtualbox-iso", - Name: "ubuntu-1204", - }: { - Type: "virtualbox-iso", - Name: "ubuntu-1204", - Cfg: &iso.FlatConfig{ - HTTPDir: strPtr("xxx"), - ISOChecksum: strPtr("769474248a3897f4865817446f9a4a53"), - ISOChecksumType: strPtr("md5"), - RawSingleISOUrl: strPtr("http://releases.ubuntu.com/12.04/ubuntu-12.04.5-server-amd64.iso"), - BootCommand: []string{"..."}, - ShutdownCommand: strPtr("echo 'vagrant' | sudo -S shutdown -P now"), - BootWait: strPtr("10s"), - VBoxManage: [][]string{}, - VBoxManagePost: [][]string{}, - }, - }, - SourceRef{ - Type: "amazon-ebs", - Name: "ubuntu-1604", - }: { - Type: "amazon-ebs", - Name: "ubuntu-1604", - Cfg: &amazonebs.FlatConfig{ - RawRegion: strPtr("eu-west-3"), - AMIEncryptBootVolume: boolPtr(true), - InstanceType: strPtr("t2.micro"), - SourceAmiFilter: &awscommon.FlatAmiFilterOptions{ - Filters: map[string]string{ - "name": "ubuntu/images/*ubuntu-xenial-{16.04}-amd64-server-*", - "root-device-type": "ebs", - "virtualization-type": "hvm", - }, - Owners: []string{"099720109477"}, - }, - AMIMappings: []awscommon.FlatBlockDevice{}, - LaunchMappings: []awscommon.FlatBlockDevice{}, - }, - }, - SourceRef{ - Type: "amazon-ebs", - Name: "that-ubuntu-1.0", - }: { - Type: "amazon-ebs", - Name: "that-ubuntu-1.0", - Cfg: &amazonebs.FlatConfig{ - RawRegion: strPtr("eu-west-3"), - AMIEncryptBootVolume: boolPtr(true), - InstanceType: strPtr("t2.micro"), - SourceAmiFilter: &awscommon.FlatAmiFilterOptions{ - MostRecent: boolPtr(true), - }, - AMIMappings: []awscommon.FlatBlockDevice{}, - LaunchMappings: []awscommon.FlatBlockDevice{}, - }, - }, - }, - }, - false, - }, - - { - "valid " + communicatorLabel + " load", - defaultParser, - args{"testdata/communicator/basic.pkr.hcl", new(PackerConfig)}, - &PackerConfig{ - Communicators: map[CommunicatorRef]*Communicator{ - {Type: "ssh", Name: "vagrant"}: { - Type: "ssh", Name: "vagrant", - Cfg: &communicator.FlatSSH{ - SSHUsername: strPtr("vagrant"), - SSHPassword: strPtr("s3cr4t"), - SSHClearAuthorizedKeys: boolPtr(true), - SSHHost: strPtr("sssssh.hashicorp.io"), - SSHHandshakeAttempts: intPtr(32), - SSHPort: intPtr(42), - SSHFileTransferMethod: strPtr("scp"), - SSHPrivateKeyFile: strPtr("file.pem"), - SSHPty: boolPtr(false), - SSHTimeout: strPtr("5m"), - SSHAgentAuth: boolPtr(false), - SSHDisableAgentForwarding: boolPtr(true), - SSHBastionHost: strPtr(""), - SSHBastionPort: intPtr(0), - SSHBastionAgentAuth: boolPtr(true), - SSHBastionUsername: strPtr(""), - SSHBastionPassword: strPtr(""), - SSHBastionPrivateKeyFile: strPtr(""), - SSHProxyHost: strPtr("ninja-potatoes.com"), - SSHProxyPort: intPtr(42), - SSHProxyUsername: strPtr("dark-father"), - SSHProxyPassword: strPtr("pickle-rick"), - SSHKeepAliveInterval: strPtr("10s"), - SSHReadWriteTimeout: strPtr("5m"), - }, - }, - }, - }, - false, - }, - - { - "duplicate " + sourceLabel, defaultParser, - args{"testdata/sources/basic.pkr.hcl", &PackerConfig{ - Sources: map[SourceRef]*Source{ - SourceRef{ - Type: "virtualbox-iso", - Name: "ubuntu-1204", - }: { - Type: "virtualbox-iso", - Name: "ubuntu-1204", - Cfg: &iso.FlatConfig{ - HTTPDir: strPtr("xxx"), - }, - }, - }, - }, - }, - &PackerConfig{ - Sources: map[SourceRef]*Source{ - SourceRef{ - Type: "virtualbox-iso", - Name: "ubuntu-1204", - }: { - Type: "virtualbox-iso", - Name: "ubuntu-1204", - Cfg: &iso.FlatConfig{ - HTTPDir: strPtr("xxx"), - }, - }, - SourceRef{ - Type: "amazon-ebs", - Name: "ubuntu-1604", - }: { - Type: "amazon-ebs", - Name: "ubuntu-1604", - Cfg: &amazonebs.FlatConfig{ - RawRegion: strPtr("eu-west-3"), - AMIEncryptBootVolume: boolPtr(true), - InstanceType: strPtr("t2.micro"), - SourceAmiFilter: &awscommon.FlatAmiFilterOptions{ - Filters: map[string]string{ - "name": "ubuntu/images/*ubuntu-xenial-{16.04}-amd64-server-*", - "root-device-type": "ebs", - "virtualization-type": "hvm", - }, - Owners: []string{"099720109477"}, - }, - AMIMappings: []awscommon.FlatBlockDevice{}, - LaunchMappings: []awscommon.FlatBlockDevice{}, - }, - }, - SourceRef{ - Type: "amazon-ebs", - Name: "that-ubuntu-1.0", - }: { - Type: "amazon-ebs", - Name: "that-ubuntu-1.0", - Cfg: &amazonebs.FlatConfig{ - RawRegion: strPtr("eu-west-3"), - AMIEncryptBootVolume: boolPtr(true), - InstanceType: strPtr("t2.micro"), - SourceAmiFilter: &awscommon.FlatAmiFilterOptions{ - MostRecent: boolPtr(true), - }, - AMIMappings: []awscommon.FlatBlockDevice{}, - LaunchMappings: []awscommon.FlatBlockDevice{}, - }, - }, - }, - }, - true, - }, - - {"valid variables load", defaultParser, - args{"testdata/variables/basic.pkr.hcl", new(PackerConfig)}, - &PackerConfig{ - Variables: PackerV1Variables{ - "image_name": "foo-image-{{user `my_secret`}}", - "key": "value", - "my_secret": "foo", - }, - }, - false, - }, - - {"valid " + buildLabel + " load", defaultParser, - args{"testdata/build/basic.pkr.hcl", new(PackerConfig)}, - &PackerConfig{ - Builds: Builds{ - { - Froms: BuildFromList{ - { - Src: SourceRef{"amazon-ebs", "ubuntu-1604"}, - }, - { - Src: SourceRef{"virtualbox-iso", "ubuntu-1204"}, - }, - }, - ProvisionerGroups: ProvisionerGroups{ - &ProvisionerGroup{ - CommunicatorRef: CommunicatorRef{"ssh", "vagrant"}, - Provisioners: []Provisioner{ - {Cfg: &shell.FlatConfig{ - Inline: []string{"echo '{{user `my_secret`}}' :D"}, - }}, - {Cfg: &shell.FlatConfig{ - Scripts: []string{"script-1.sh", "script-2.sh"}, - ValidExitCodes: []int{0, 42}, - }}, - {Cfg: &file.FlatConfig{ - Source: strPtr("app.tar.gz"), - Destination: strPtr("/tmp/app.tar.gz"), - }}, - }, - }, - }, - PostProvisionerGroups: ProvisionerGroups{ - &ProvisionerGroup{ - Provisioners: []Provisioner{ - {Cfg: &amazon_import.FlatConfig{ - Name: strPtr("that-ubuntu-1.0"), - }}, - }, - }, - }, - }, - &Build{ - Froms: BuildFromList{ - { - Src: SourceRef{"amazon", "that-ubuntu-1"}, - }, - }, - ProvisionerGroups: ProvisionerGroups{ - &ProvisionerGroup{ - Provisioners: []Provisioner{ - {Cfg: &shell.FlatConfig{ - Inline: []string{"echo HOLY GUACAMOLE !"}, - }}, - }, - }, - }, - }, - }, - }, - false, - }, - } - for _, tt := range tests { - t.Run(tt.name, func(t *testing.T) { - p := tt.parser - f, moreDiags := p.ParseHCLFile(tt.args.filename) - if moreDiags != nil { - t.Fatalf("diags: %s", moreDiags) - } - diags := p.ParseFile(f, tt.args.cfg) - if tt.wantDiags == (diags == nil) { - for _, diag := range diags { - t.Errorf("PackerConfig.Load() unexpected diagnostics. %v", diag) - } - t.Error("") - } - if diff := cmp.Diff(tt.wantPackerConfig, tt.args.cfg, - cmpopts.IgnoreUnexported(cty.Value{}), - cmpopts.IgnoreTypes(HCL2Ref{}), - cmpopts.IgnoreTypes([]hcl.Range{}), - cmpopts.IgnoreTypes(hcl.Range{}), - cmpopts.IgnoreInterfaces(struct{ hcl.Expression }{}), - cmpopts.IgnoreInterfaces(struct{ hcl.Body }{}), - ); diff != "" { - t.Errorf("PackerConfig.Load() wrong packer config. %s", diff) - } - if t.Failed() { - t.Fatal() - } - }) - } -} - -func strPtr(s string) *string { return &s } -func intPtr(i int) *int { return &i } -func boolPtr(b bool) *bool { return &b } diff --git a/hcl2template/mock.go b/hcl2template/mock.go new file mode 100644 index 000000000..09fbf0c19 --- /dev/null +++ b/hcl2template/mock.go @@ -0,0 +1,120 @@ +//go:generate mapstructure-to-hcl2 -type MockConfig,NestedMockConfig + +package hcl2template + +import ( + "context" + "time" + + "github.com/hashicorp/hcl/v2/hcldec" + "github.com/hashicorp/packer/helper/config" + "github.com/hashicorp/packer/packer" +) + +type NestedMockConfig struct { + String string `mapstructure:"string"` + Int int `mapstructure:"int"` + Int64 int64 `mapstructure:"int64"` + Bool bool `mapstructure:"bool"` + Trilean config.Trilean `mapstructure:"trilean"` + Duration time.Duration `mapstructure:"duration"` + MapStringString map[string]string `mapstructure:"map_string_string"` + SliceString []string `mapstructure:"slice_string"` +} + +type MockConfig struct { + NestedMockConfig `mapstructure:",squash"` + Nested NestedMockConfig `mapstructure:"nested"` + NestedSlice []NestedMockConfig `mapstructure:"nested_slice"` +} + +////// +// MockBuilder +////// + +type MockBuilder struct { + Config MockConfig +} + +var _ packer.Builder = new(MockBuilder) + +func (b *MockBuilder) ConfigSpec() hcldec.ObjectSpec { return b.Config.FlatMapstructure().HCL2Spec() } + +func (b *MockBuilder) Prepare(raws ...interface{}) ([]string, error) { + return nil, config.Decode(&b.Config, &config.DecodeOpts{ + Interpolate: true, + }, raws...) +} + +func (b *MockBuilder) Run(ctx context.Context, ui packer.Ui, hook packer.Hook) (packer.Artifact, error) { + return nil, nil +} + +////// +// MockProvisioner +////// + +type MockProvisioner struct { + Config MockConfig +} + +var _ packer.Provisioner = new(MockProvisioner) + +func (b *MockProvisioner) ConfigSpec() hcldec.ObjectSpec { + return b.Config.FlatMapstructure().HCL2Spec() +} + +func (b *MockProvisioner) Prepare(raws ...interface{}) error { + return config.Decode(&b.Config, &config.DecodeOpts{ + Interpolate: true, + }, raws...) +} + +func (b *MockProvisioner) Provision(ctx context.Context, ui packer.Ui, comm packer.Communicator) error { + return nil +} + +////// +// MockPostProcessor +////// + +type MockPostProcessor struct { + Config MockConfig +} + +var _ packer.PostProcessor = new(MockPostProcessor) + +func (b *MockPostProcessor) ConfigSpec() hcldec.ObjectSpec { + return b.Config.FlatMapstructure().HCL2Spec() +} + +func (b *MockPostProcessor) Configure(raws ...interface{}) error { + return config.Decode(&b.Config, &config.DecodeOpts{ + Interpolate: true, + }, raws...) +} + +func (b *MockPostProcessor) PostProcess(ctx context.Context, ui packer.Ui, a packer.Artifact) (packer.Artifact, bool, bool, error) { + return nil, false, false, nil +} + +////// +// MockCommunicator +////// + +type MockCommunicator struct { + Config MockConfig + packer.Communicator +} + +var _ packer.ConfigurableCommunicator = new(MockCommunicator) + +func (b *MockCommunicator) ConfigSpec() hcldec.ObjectSpec { + return b.Config.FlatMapstructure().HCL2Spec() +} + +func (b *MockCommunicator) Configure(raws ...interface{}) ([]string, error) { + return nil, config.Decode(&b.Config, &config.DecodeOpts{ + Interpolate: true, + }, raws...) +} diff --git a/hcl2template/mock.hcl2spec.go b/hcl2template/mock.hcl2spec.go new file mode 100644 index 000000000..99def008f --- /dev/null +++ b/hcl2template/mock.hcl2spec.go @@ -0,0 +1,85 @@ +// Code generated by "mapstructure-to-hcl2 -type MockConfig,NestedMockConfig"; DO NOT EDIT. +package hcl2template + +import ( + "github.com/hashicorp/hcl/v2/hcldec" + "github.com/zclconf/go-cty/cty" +) + +// FlatMockConfig is an auto-generated flat version of MockConfig. +// Where the contents of a field with a `mapstructure:,squash` tag are bubbled up. +type FlatMockConfig struct { + String *string `mapstructure:"string" cty:"string"` + Int *int `mapstructure:"int" cty:"int"` + Int64 *int64 `mapstructure:"int64" cty:"int64"` + Bool *bool `mapstructure:"bool" cty:"bool"` + Trilean *bool `mapstructure:"trilean" cty:"trilean"` + Duration *string `mapstructure:"duration" cty:"duration"` + MapStringString map[string]string `mapstructure:"map_string_string" cty:"map_string_string"` + SliceString []string `mapstructure:"slice_string" cty:"slice_string"` + Nested *FlatNestedMockConfig `mapstructure:"nested" cty:"nested"` + NestedSlice []FlatNestedMockConfig `mapstructure:"nested_slice" cty:"nested_slice"` +} + +// FlatMapstructure returns a new FlatMockConfig. +// FlatMockConfig is an auto-generated flat version of MockConfig. +// Where the contents a fields with a `mapstructure:,squash` tag are bubbled up. +func (*MockConfig) FlatMapstructure() interface{ HCL2Spec() map[string]hcldec.Spec } { + return new(FlatMockConfig) +} + +// HCL2Spec returns the hcl spec of a MockConfig. +// This spec is used by HCL to read the fields of MockConfig. +// The decoded values from this spec will then be applied to a FlatMockConfig. +func (*FlatMockConfig) HCL2Spec() map[string]hcldec.Spec { + s := map[string]hcldec.Spec{ + "string": &hcldec.AttrSpec{Name: "string", Type: cty.String, Required: false}, + "int": &hcldec.AttrSpec{Name: "int", Type: cty.Number, Required: false}, + "int64": &hcldec.AttrSpec{Name: "int64", Type: cty.Number, Required: false}, + "bool": &hcldec.AttrSpec{Name: "bool", Type: cty.Bool, Required: false}, + "trilean": &hcldec.AttrSpec{Name: "trilean", Type: cty.Bool, Required: false}, + "duration": &hcldec.AttrSpec{Name: "duration", Type: cty.String, Required: false}, + "map_string_string": &hcldec.BlockAttrsSpec{TypeName: "map_string_string", ElementType: cty.String, Required: false}, + "slice_string": &hcldec.AttrSpec{Name: "slice_string", Type: cty.List(cty.String), Required: false}, + "nested": &hcldec.BlockSpec{TypeName: "nested", Nested: hcldec.ObjectSpec((*FlatNestedMockConfig)(nil).HCL2Spec())}, + "nested_slice": &hcldec.BlockListSpec{TypeName: "nested_slice", Nested: hcldec.ObjectSpec((*FlatNestedMockConfig)(nil).HCL2Spec())}, + } + return s +} + +// FlatNestedMockConfig is an auto-generated flat version of NestedMockConfig. +// Where the contents of a field with a `mapstructure:,squash` tag are bubbled up. +type FlatNestedMockConfig struct { + String *string `mapstructure:"string" cty:"string"` + Int *int `mapstructure:"int" cty:"int"` + Int64 *int64 `mapstructure:"int64" cty:"int64"` + Bool *bool `mapstructure:"bool" cty:"bool"` + Trilean *bool `mapstructure:"trilean" cty:"trilean"` + Duration *string `mapstructure:"duration" cty:"duration"` + MapStringString map[string]string `mapstructure:"map_string_string" cty:"map_string_string"` + SliceString []string `mapstructure:"slice_string" cty:"slice_string"` +} + +// FlatMapstructure returns a new FlatNestedMockConfig. +// FlatNestedMockConfig is an auto-generated flat version of NestedMockConfig. +// Where the contents a fields with a `mapstructure:,squash` tag are bubbled up. +func (*NestedMockConfig) FlatMapstructure() interface{ HCL2Spec() map[string]hcldec.Spec } { + return new(FlatNestedMockConfig) +} + +// HCL2Spec returns the hcl spec of a NestedMockConfig. +// This spec is used by HCL to read the fields of NestedMockConfig. +// The decoded values from this spec will then be applied to a FlatNestedMockConfig. +func (*FlatNestedMockConfig) HCL2Spec() map[string]hcldec.Spec { + s := map[string]hcldec.Spec{ + "string": &hcldec.AttrSpec{Name: "string", Type: cty.String, Required: false}, + "int": &hcldec.AttrSpec{Name: "int", Type: cty.Number, Required: false}, + "int64": &hcldec.AttrSpec{Name: "int64", Type: cty.Number, Required: false}, + "bool": &hcldec.AttrSpec{Name: "bool", Type: cty.Bool, Required: false}, + "trilean": &hcldec.AttrSpec{Name: "trilean", Type: cty.Bool, Required: false}, + "duration": &hcldec.AttrSpec{Name: "duration", Type: cty.String, Required: false}, + "map_string_string": &hcldec.BlockAttrsSpec{TypeName: "map_string_string", ElementType: cty.String, Required: false}, + "slice_string": &hcldec.AttrSpec{Name: "slice_string", Type: cty.List(cty.String), Required: false}, + } + return s +} diff --git a/hcl2template/parser.go b/hcl2template/parser.go index 97bd1e8d6..770c7209a 100644 --- a/hcl2template/parser.go +++ b/hcl2template/parser.go @@ -8,6 +8,7 @@ import ( "github.com/hashicorp/hcl/v2" "github.com/hashicorp/hcl/v2/hclparse" + "github.com/hashicorp/packer/packer" ) const ( @@ -29,26 +30,24 @@ var configSchema = &hcl.BodySchema{ type Parser struct { *hclparse.Parser - ProvisionersSchemas map[string]Decodable + BuilderSchemas packer.BuilderStore - PostProvisionersSchemas map[string]Decodable + ProvisionersSchemas packer.ProvisionerStore - CommunicatorSchemas map[string]Decodable - - SourceSchemas map[string]Decodable + PostProcessorsSchemas packer.PostProcessorStore } const hcl2FileExt = ".pkr.hcl" -func (p *Parser) Parse(filename string) (*PackerConfig, hcl.Diagnostics) { +func (p *Parser) parse(filename string) (*PackerConfig, hcl.Diagnostics) { var diags hcl.Diagnostics hclFiles := []string{} jsonFiles := []string{} if strings.HasSuffix(filename, hcl2FileExt) { - hclFiles = append(hclFiles, hcl2FileExt) + hclFiles = append(hclFiles, filename) } else if strings.HasSuffix(filename, ".json") { - jsonFiles = append(jsonFiles, hcl2FileExt) + jsonFiles = append(jsonFiles, filename) } else { fileInfos, err := ioutil.ReadDir(filename) if err != nil { @@ -89,24 +88,21 @@ func (p *Parser) Parse(filename string) (*PackerConfig, hcl.Diagnostics) { cfg := &PackerConfig{} for _, file := range files { - moreDiags := p.ParseFile(file, cfg) + moreDiags := p.parseFile(file, cfg) diags = append(diags, moreDiags...) } - if diags.HasErrors() { - return cfg, diags - } - return cfg, nil + return cfg, diags } -// ParseFile filename content into cfg. +// parseFile filename content into cfg. // -// ParseFile may be called multiple times with the same cfg on a different file. +// parseFile may be called multiple times with the same cfg on a different file. // -// ParseFile returns as complete a config as we can manage, even if there are +// parseFile returns as complete a config as we can manage, even if there are // errors, since a partial result can be useful for careful analysis by // development tools such as text editor extensions. -func (p *Parser) ParseFile(f *hcl.File, cfg *PackerConfig) hcl.Diagnostics { +func (p *Parser) parseFile(f *hcl.File, cfg *PackerConfig) hcl.Diagnostics { var diags hcl.Diagnostics content, moreDiags := f.Body.Content(configSchema) @@ -115,12 +111,11 @@ func (p *Parser) ParseFile(f *hcl.File, cfg *PackerConfig) hcl.Diagnostics { for _, block := range content.Blocks { switch block.Type { case sourceLabel: - if cfg.Sources == nil { - cfg.Sources = map[SourceRef]*Source{} - } - - source, moreDiags := p.decodeSource(block, p.SourceSchemas) + source, moreDiags := p.decodeSource(block) diags = append(diags, moreDiags...) + if moreDiags.HasErrors() { + continue + } ref := source.Ref() if existing := cfg.Sources[ref]; existing != nil { @@ -130,11 +125,15 @@ func (p *Parser) ParseFile(f *hcl.File, cfg *PackerConfig) hcl.Diagnostics { Detail: fmt.Sprintf("This "+sourceLabel+" block has the "+ "same builder type and name as a previous block declared "+ "at %s. Each "+sourceLabel+" must have a unique name per builder type.", - existing.HCL2Ref.DeclRange), - Subject: &source.HCL2Ref.DeclRange, + existing.block.DefRange.Ptr()), + Subject: source.block.DefRange.Ptr(), }) continue } + + if cfg.Sources == nil { + cfg.Sources = map[SourceRef]*Source{} + } cfg.Sources[ref] = source case variablesLabel: @@ -143,35 +142,18 @@ func (p *Parser) ParseFile(f *hcl.File, cfg *PackerConfig) hcl.Diagnostics { } moreDiags := cfg.Variables.decodeConfig(block) + if moreDiags.HasErrors() { + continue + } diags = append(diags, moreDiags...) case buildLabel: build, moreDiags := p.decodeBuildConfig(block) diags = append(diags, moreDiags...) - cfg.Builds = append(cfg.Builds, build) - - case communicatorLabel: - if cfg.Communicators == nil { - cfg.Communicators = map[CommunicatorRef]*Communicator{} - } - communicator, moreDiags := p.decodeCommunicatorConfig(block) - diags = append(diags, moreDiags...) - - ref := communicator.Ref() - - if existing := cfg.Communicators[ref]; existing != nil { - diags = append(diags, &hcl.Diagnostic{ - Severity: hcl.DiagError, - Summary: "Duplicate " + communicatorLabel + " block", - Detail: fmt.Sprintf("This "+communicatorLabel+" block has the "+ - "same type and name as a previous block declared "+ - "at %s. Each "+communicatorLabel+" must have a unique name per type.", - existing.HCL2Ref.DeclRange), - Subject: &communicator.HCL2Ref.DeclRange, - }) + if moreDiags.HasErrors() { continue } - cfg.Communicators[ref] = communicator + cfg.Builds = append(cfg.Builds, build) default: panic(fmt.Sprintf("unexpected block type %q", block.Type)) // TODO(azr): err diff --git a/hcl2template/parser_test.go b/hcl2template/parser_test.go deleted file mode 100644 index 1056153d6..000000000 --- a/hcl2template/parser_test.go +++ /dev/null @@ -1,213 +0,0 @@ -package hcl2template - -import ( - "testing" - - "github.com/google/go-cmp/cmp" - "github.com/google/go-cmp/cmp/cmpopts" - - "github.com/hashicorp/hcl/v2" - "github.com/zclconf/go-cty/cty" - - awscommon "github.com/hashicorp/packer/builder/amazon/common" - amazonebs "github.com/hashicorp/packer/builder/amazon/ebs" - "github.com/hashicorp/packer/builder/virtualbox/iso" - - "github.com/hashicorp/packer/helper/communicator" - - amazon_import "github.com/hashicorp/packer/post-processor/amazon-import" - - "github.com/hashicorp/packer/provisioner/file" - "github.com/hashicorp/packer/provisioner/shell" -) - -func TestParser_Parse(t *testing.T) { - defaultParser := getBasicParser() - - type args struct { - filename string - } - tests := []struct { - name string - parser *Parser - args args - wantCfg *PackerConfig - wantDiags bool - }{ - {"complete", - defaultParser, - args{"testdata/complete"}, - &PackerConfig{ - Sources: map[SourceRef]*Source{ - SourceRef{ - Type: "virtualbox-iso", - Name: "ubuntu-1204", - }: { - Type: "virtualbox-iso", - Name: "ubuntu-1204", - Cfg: &iso.FlatConfig{ - HTTPDir: strPtr("xxx"), - ISOChecksum: strPtr("769474248a3897f4865817446f9a4a53"), - ISOChecksumType: strPtr("md5"), - RawSingleISOUrl: strPtr("http://releases.ubuntu.com/12.04/ubuntu-12.04.5-server-amd64.iso"), - BootCommand: []string{"..."}, - ShutdownCommand: strPtr("echo 'vagrant' | sudo -S shutdown -P now"), - BootWait: strPtr("10s"), - VBoxManage: [][]string{}, - VBoxManagePost: [][]string{}, - }, - }, - SourceRef{ - Type: "amazon-ebs", - Name: "ubuntu-1604", - }: { - Type: "amazon-ebs", - Name: "ubuntu-1604", - Cfg: &amazonebs.FlatConfig{ - RawRegion: strPtr("eu-west-3"), - AMIEncryptBootVolume: boolPtr(true), - InstanceType: strPtr("t2.micro"), - SourceAmiFilter: &awscommon.FlatAmiFilterOptions{ - Filters: map[string]string{ - "name": "ubuntu/images/*ubuntu-xenial-{16.04}-amd64-server-*", - "root-device-type": "ebs", - "virtualization-type": "hvm", - }, - Owners: []string{"099720109477"}, - }, - AMIMappings: []awscommon.FlatBlockDevice{}, - LaunchMappings: []awscommon.FlatBlockDevice{}, - }, - }, - SourceRef{ - Type: "amazon-ebs", - Name: "that-ubuntu-1.0", - }: { - Type: "amazon-ebs", - Name: "that-ubuntu-1.0", - Cfg: &amazonebs.FlatConfig{ - RawRegion: strPtr("eu-west-3"), - AMIEncryptBootVolume: boolPtr(true), - InstanceType: strPtr("t2.micro"), - SourceAmiFilter: &awscommon.FlatAmiFilterOptions{ - MostRecent: boolPtr(true), - }, - AMIMappings: []awscommon.FlatBlockDevice{}, - LaunchMappings: []awscommon.FlatBlockDevice{}, - }, - }, - }, - Communicators: map[CommunicatorRef]*Communicator{ - {Type: "ssh", Name: "vagrant"}: { - Type: "ssh", Name: "vagrant", - Cfg: &communicator.FlatSSH{ - SSHUsername: strPtr("vagrant"), - SSHPassword: strPtr("s3cr4t"), - SSHClearAuthorizedKeys: boolPtr(true), - SSHHost: strPtr("sssssh.hashicorp.io"), - SSHHandshakeAttempts: intPtr(32), - SSHPort: intPtr(42), - SSHFileTransferMethod: strPtr("scp"), - SSHPrivateKeyFile: strPtr("file.pem"), - SSHPty: boolPtr(false), - SSHTimeout: strPtr("5m"), - SSHAgentAuth: boolPtr(false), - SSHDisableAgentForwarding: boolPtr(true), - SSHBastionHost: strPtr(""), - SSHBastionPort: intPtr(0), - SSHBastionAgentAuth: boolPtr(true), - SSHBastionUsername: strPtr(""), - SSHBastionPassword: strPtr(""), - SSHBastionPrivateKeyFile: strPtr(""), - SSHProxyHost: strPtr("ninja-potatoes.com"), - SSHProxyPort: intPtr(42), - SSHProxyUsername: strPtr("dark-father"), - SSHProxyPassword: strPtr("pickle-rick"), - SSHKeepAliveInterval: strPtr("10s"), - SSHReadWriteTimeout: strPtr("5m"), - }, - }, - }, - Variables: PackerV1Variables{ - "image_name": "foo-image-{{user `my_secret`}}", - "key": "value", - "my_secret": "foo", - }, - Builds: Builds{ - { - Froms: BuildFromList{ - { - Src: SourceRef{"amazon-ebs", "ubuntu-1604"}, - }, - { - Src: SourceRef{"virtualbox-iso", "ubuntu-1204"}, - }, - }, - ProvisionerGroups: ProvisionerGroups{ - &ProvisionerGroup{ - CommunicatorRef: CommunicatorRef{"ssh", "vagrant"}, - Provisioners: []Provisioner{ - {Cfg: &shell.FlatConfig{ - Inline: []string{"echo '{{user `my_secret`}}' :D"}, - }}, - {Cfg: &shell.FlatConfig{ - Scripts: []string{"script-1.sh", "script-2.sh"}, - ValidExitCodes: []int{0, 42}, - }}, - {Cfg: &file.FlatConfig{ - Source: strPtr("app.tar.gz"), - Destination: strPtr("/tmp/app.tar.gz"), - }}, - }, - }, - }, - PostProvisionerGroups: ProvisionerGroups{ - &ProvisionerGroup{ - Provisioners: []Provisioner{ - {Cfg: &amazon_import.FlatConfig{ - Name: strPtr("that-ubuntu-1.0"), - }}, - }, - }, - }, - }, - &Build{ - Froms: BuildFromList{ - { - Src: SourceRef{"amazon", "that-ubuntu-1"}, - }, - }, - ProvisionerGroups: ProvisionerGroups{ - &ProvisionerGroup{ - Provisioners: []Provisioner{ - {Cfg: &shell.FlatConfig{ - Inline: []string{"echo HOLY GUACAMOLE !"}, - }}, - }, - }, - }, - }, - }, - }, false, - }, - } - for _, tt := range tests { - t.Run(tt.name, func(t *testing.T) { - gotCfg, gotDiags := tt.parser.Parse(tt.args.filename) - if tt.wantDiags == (gotDiags == nil) { - t.Errorf("Parser.Parse() unexpected diagnostics. %s", gotDiags) - } - if diff := cmp.Diff(tt.wantCfg, gotCfg, - cmpopts.IgnoreUnexported(cty.Value{}), - cmpopts.IgnoreTypes(HCL2Ref{}), - cmpopts.IgnoreTypes([]hcl.Range{}), - cmpopts.IgnoreTypes(hcl.Range{}), - cmpopts.IgnoreInterfaces(struct{ hcl.Expression }{}), - cmpopts.IgnoreInterfaces(struct{ hcl.Body }{}), - ); diff != "" { - t.Errorf("Parser.Parse() wrong packer config. %s", diff) - } - - }) - } -} diff --git a/hcl2template/testdata/build/basic.pkr.hcl b/hcl2template/testdata/build/basic.pkr.hcl index c48847c94..34d84edb9 100644 --- a/hcl2template/testdata/build/basic.pkr.hcl +++ b/hcl2template/testdata/build/basic.pkr.hcl @@ -1,66 +1,125 @@ // starts resources to provision them. build { - from "src.amazon-ebs.ubuntu-1604" { - ami_name = "that-ubuntu-1.0" - } + sources = [ + "source.amazon-ebs.ubuntu-1604", + "source.virtualbox-iso.ubuntu-1204", + ] - from "src.virtualbox-iso.ubuntu-1204" { - // build name is defaulted from the label "src.virtualbox-iso.ubuntu-1204" - outout_dir = "path/" - } + provisioner "shell" { + string = "string" + int = 42 + int64 = 43 + bool = true + trilean = true + duration = "10s" + map_string_string { + a = "b" + c = "d" + } + slice_string = [ + "a", + "b", + "c", + ] - provision { - communicator = "comm.ssh.vagrant" - - shell { - inline = [ - "echo '{{user `my_secret`}}' :D" + nested { + string = "string" + int = 42 + int64 = 43 + bool = true + trilean = true + duration = "10s" + map_string_string { + a = "b" + c = "d" + } + slice_string = [ + "a", + "b", + "c", ] } - shell { - valid_exit_codes = [ - 0, - 42, - ] - scripts = [ - "script-1.sh", - "script-2.sh", - ] - // override "vmware-iso" { // TODO(azr): handle common fields - // execute_command = "echo 'password' | sudo -S bash {{.Path}}" - // } + nested_slice { } - - file { - source = "app.tar.gz" - destination = "/tmp/app.tar.gz" - // timeout = "5s" // TODO(azr): handle common fields - } - } - post_provision { - amazon-import { - // only = ["src.virtualbox-iso.ubuntu-1204"] // TODO(azr): handle common fields - ami_name = "that-ubuntu-1.0" + provisioner "file" { + string = "string" + int = 42 + int64 = 43 + bool = true + trilean = true + duration = "10s" + map_string_string { + a = "b" + c = "d" + } + slice_string = [ + "a", + "b", + "c", + ] + + nested { + string = "string" + int = 42 + int64 = 43 + bool = true + trilean = true + duration = "10s" + map_string_string { + a = "b" + c = "d" + } + slice_string = [ + "a", + "b", + "c", + ] + } + + nested_slice { + } + } + + post-processor "amazon-import" { + string = "string" + int = 42 + int64 = 43 + bool = true + trilean = true + duration = "10s" + map_string_string { + a = "b" + c = "d" + } + slice_string = [ + "a", + "b", + "c", + ] + + nested { + string = "string" + int = 42 + int64 = 43 + bool = true + trilean = true + duration = "10s" + map_string_string { + a = "b" + c = "d" + } + slice_string = [ + "a", + "b", + "c", + ] + } + + nested_slice { } } } - -build { - // build an ami using the ami from the previous build block. - from "src.amazon.that-ubuntu-1.0" { - ami_name = "fooooobaaaar" - } - - provision { - - shell { - inline = [ - "echo HOLY GUACAMOLE !" - ] - } - } -} \ No newline at end of file diff --git a/hcl2template/testdata/build/post-processor_inexistent.pkr.hcl b/hcl2template/testdata/build/post-processor_inexistent.pkr.hcl new file mode 100644 index 000000000..179ff3475 --- /dev/null +++ b/hcl2template/testdata/build/post-processor_inexistent.pkr.hcl @@ -0,0 +1,6 @@ + +build { + post-processor "inexistant" { + foo = "bar" + } +} diff --git a/hcl2template/testdata/build/post-processor_untyped.pkr.hcl b/hcl2template/testdata/build/post-processor_untyped.pkr.hcl new file mode 100644 index 000000000..4b723360e --- /dev/null +++ b/hcl2template/testdata/build/post-processor_untyped.pkr.hcl @@ -0,0 +1,6 @@ + +build { + post-process { + foo = "bar" + } +} diff --git a/hcl2template/testdata/build/provisioner_inexistent.pkr.hcl b/hcl2template/testdata/build/provisioner_inexistent.pkr.hcl new file mode 100644 index 000000000..59b4dcbc5 --- /dev/null +++ b/hcl2template/testdata/build/provisioner_inexistent.pkr.hcl @@ -0,0 +1,6 @@ + +build { + provisioner "inexistant" { + foo = "bar" + } +} diff --git a/hcl2template/testdata/build/provisioner_untyped.pkr.hcl b/hcl2template/testdata/build/provisioner_untyped.pkr.hcl new file mode 100644 index 000000000..33a9fdb99 --- /dev/null +++ b/hcl2template/testdata/build/provisioner_untyped.pkr.hcl @@ -0,0 +1,6 @@ + +build { + provision { + foo = "bar" + } +} diff --git a/hcl2template/testdata/communicator/basic.pkr.hcl b/hcl2template/testdata/communicator/basic.pkr.hcl index b752333e9..4f5d8104e 100644 --- a/hcl2template/testdata/communicator/basic.pkr.hcl +++ b/hcl2template/testdata/communicator/basic.pkr.hcl @@ -1,27 +1,39 @@ communicator "ssh" "vagrant" { - ssh_password = "s3cr4t" - ssh_username = "vagrant" - ssh_agent_auth = false - ssh_bastion_agent_auth = true - ssh_bastion_host = "" - ssh_bastion_password = "" - ssh_bastion_port = 0 - ssh_bastion_private_key_file = "" - ssh_bastion_username = "" - ssh_clear_authorized_keys = true - ssh_disable_agent_forwarding = true - ssh_file_transfer_method = "scp" - ssh_handshake_attempts = 32 - ssh_host = "sssssh.hashicorp.io" - ssh_port = 42 - ssh_keep_alive_interval = "10s" - ssh_private_key_file = "file.pem" - ssh_proxy_host = "ninja-potatoes.com" - ssh_proxy_password = "pickle-rick" - ssh_proxy_port = "42" - ssh_proxy_username = "dark-father" - ssh_pty = false - ssh_read_write_timeout = "5m" - ssh_timeout = "5m" + string = "string" + int = 42 + int64 = 43 + bool = true + trilean = true + duration = "10s" + map_string_string { + a = "b" + c = "d" + } + slice_string = [ + "a", + "b", + "c", + ] + + nested { + string = "string" + int = 42 + int64 = 43 + bool = true + trilean = true + duration = "10s" + map_string_string { + a = "b" + c = "d" + } + slice_string = [ + "a", + "b", + "c", + ] + } + + nested_slice { + } } diff --git a/hcl2template/testdata/complete/build.pkr.hcl b/hcl2template/testdata/complete/build.pkr.hcl index c48847c94..de67e1c39 100644 --- a/hcl2template/testdata/complete/build.pkr.hcl +++ b/hcl2template/testdata/complete/build.pkr.hcl @@ -1,66 +1,124 @@ // starts resources to provision them. build { - from "src.amazon-ebs.ubuntu-1604" { - ami_name = "that-ubuntu-1.0" - } + sources = [ + "source.virtualbox-iso.ubuntu-1204", + ] - from "src.virtualbox-iso.ubuntu-1204" { - // build name is defaulted from the label "src.virtualbox-iso.ubuntu-1204" - outout_dir = "path/" - } + provisioner "shell" { + string = "string" + int = 42 + int64 = 43 + bool = true + trilean = true + duration = "10s" + map_string_string { + a = "b" + c = "d" + } + slice_string = [ + "a", + "b", + "c", + ] - provision { - communicator = "comm.ssh.vagrant" - - shell { - inline = [ - "echo '{{user `my_secret`}}' :D" + nested { + string = "string" + int = 42 + int64 = 43 + bool = true + trilean = true + duration = "10s" + map_string_string { + a = "b" + c = "d" + } + slice_string = [ + "a", + "b", + "c", ] } - shell { - valid_exit_codes = [ - 0, - 42, - ] - scripts = [ - "script-1.sh", - "script-2.sh", - ] - // override "vmware-iso" { // TODO(azr): handle common fields - // execute_command = "echo 'password' | sudo -S bash {{.Path}}" - // } + nested_slice { } - - file { - source = "app.tar.gz" - destination = "/tmp/app.tar.gz" - // timeout = "5s" // TODO(azr): handle common fields - } - } - post_provision { - amazon-import { - // only = ["src.virtualbox-iso.ubuntu-1204"] // TODO(azr): handle common fields - ami_name = "that-ubuntu-1.0" + provisioner "file" { + string = "string" + int = 42 + int64 = 43 + bool = true + trilean = true + duration = "10s" + map_string_string { + a = "b" + c = "d" + } + slice_string = [ + "a", + "b", + "c", + ] + + nested { + string = "string" + int = 42 + int64 = 43 + bool = true + trilean = true + duration = "10s" + map_string_string { + a = "b" + c = "d" + } + slice_string = [ + "a", + "b", + "c", + ] + } + + nested_slice { + } + } + + post-processor "amazon-import" { + string = "string" + int = 42 + int64 = 43 + bool = true + trilean = true + duration = "10s" + map_string_string { + a = "b" + c = "d" + } + slice_string = [ + "a", + "b", + "c", + ] + + nested { + string = "string" + int = 42 + int64 = 43 + bool = true + trilean = true + duration = "10s" + map_string_string { + a = "b" + c = "d" + } + slice_string = [ + "a", + "b", + "c", + ] + } + + nested_slice { } } } - -build { - // build an ami using the ami from the previous build block. - from "src.amazon.that-ubuntu-1.0" { - ami_name = "fooooobaaaar" - } - - provision { - - shell { - inline = [ - "echo HOLY GUACAMOLE !" - ] - } - } -} \ No newline at end of file diff --git a/hcl2template/testdata/complete/communicator.pkr.hcl b/hcl2template/testdata/complete/communicator.pkr.hcl deleted file mode 100644 index b752333e9..000000000 --- a/hcl2template/testdata/complete/communicator.pkr.hcl +++ /dev/null @@ -1,27 +0,0 @@ - -communicator "ssh" "vagrant" { - ssh_password = "s3cr4t" - ssh_username = "vagrant" - ssh_agent_auth = false - ssh_bastion_agent_auth = true - ssh_bastion_host = "" - ssh_bastion_password = "" - ssh_bastion_port = 0 - ssh_bastion_private_key_file = "" - ssh_bastion_username = "" - ssh_clear_authorized_keys = true - ssh_disable_agent_forwarding = true - ssh_file_transfer_method = "scp" - ssh_handshake_attempts = 32 - ssh_host = "sssssh.hashicorp.io" - ssh_port = 42 - ssh_keep_alive_interval = "10s" - ssh_private_key_file = "file.pem" - ssh_proxy_host = "ninja-potatoes.com" - ssh_proxy_password = "pickle-rick" - ssh_proxy_port = "42" - ssh_proxy_username = "dark-father" - ssh_pty = false - ssh_read_write_timeout = "5m" - ssh_timeout = "5m" -} diff --git a/hcl2template/testdata/complete/sources.pkr.hcl b/hcl2template/testdata/complete/sources.pkr.hcl index 43e3af082..b95308b5a 100644 --- a/hcl2template/testdata/complete/sources.pkr.hcl +++ b/hcl2template/testdata/complete/sources.pkr.hcl @@ -1,37 +1,71 @@ -// a source represents a reusable setting for a system boot/start. source "virtualbox-iso" "ubuntu-1204" { - iso_url = "http://releases.ubuntu.com/12.04/ubuntu-12.04.5-server-amd64.iso" - iso_checksum = "769474248a3897f4865817446f9a4a53" - iso_checksum_type = "md5" + string = "string" + int = 42 + int64 = 43 + bool = true + trilean = true + duration = "10s" + map_string_string { + a = "b" + c = "d" + } + slice_string = [ + "a", + "b", + "c", + ] - boot_wait = "10s" - http_directory = "xxx" - boot_command = ["..."] - - shutdown_command = "echo 'vagrant' | sudo -S shutdown -P now" -} - -source "amazon-ebs" "ubuntu-1604" { - instance_type = "t2.micro" - encrypt_boot = true - region = "eu-west-3" - source_ami_filter { - filters { - virtualization-type = "hvm" - name = "ubuntu/images/*ubuntu-xenial-{16.04}-amd64-server-*" - root-device-type = "ebs" + nested { + string = "string" + int = 42 + int64 = 43 + bool = true + trilean = true + duration = "10s" + map_string_string { + a = "b" + c = "d" } - owners = [ - "099720109477" + slice_string = [ + "a", + "b", + "c", ] } -} -source "amazon-ebs" "that-ubuntu-1.0" { - instance_type = "t2.micro" - encrypt_boot = true - region = "eu-west-3" - source_ami_filter { - most_recent = true + nested_slice { + string = "string" + int = 42 + int64 = 43 + bool = true + trilean = true + duration = "10s" + map_string_string { + a = "b" + c = "d" + } + slice_string = [ + "a", + "b", + "c", + ] } -} + + nested_slice { + string = "string" + int = 42 + int64 = 43 + bool = true + trilean = true + duration = "10s" + map_string_string { + a = "b" + c = "d" + } + slice_string = [ + "a", + "b", + "c", + ] + } +} \ No newline at end of file diff --git a/hcl2template/testdata/complete/subfolder/shouldnotload.pkr.hcl b/hcl2template/testdata/complete/subfolder/shouldnotload.pkr.hcl deleted file mode 100644 index 6f2d3076c..000000000 --- a/hcl2template/testdata/complete/subfolder/shouldnotload.pkr.hcl +++ /dev/null @@ -1,61 +0,0 @@ - -// starts resources to provision them. -build { - from "src.amazon-ebs.ubuntu-1604" { - ami_name = "that-ubuntu-1.0" - } - - from "src.virtualbox-iso.ubuntu-1204" { - // build name is defaulted from the label "src.virtualbox-iso.ubuntu-1204" - outout_dir = "path/" - } - - provision { - communicator = comm.ssh.vagrant - - shell { - inline = [ - "echo '{{user `my_secret`}}' :D" - ] - } - - shell { - script = [ - "script-1.sh", - "script-2.sh", - ] - override "vmware-iso" { - execute_command = "echo 'password' | sudo -S bash {{.Path}}" - } - } - - upload "log.go" "/tmp" { - timeout = "5s" - } - - } - - post_provision { - amazon-import { - only = ["src.virtualbox-iso.ubuntu-1204"] - ami_name = "that-ubuntu-1.0" - } - } -} - -build { - // build an ami using the ami from the previous build block. - from "src.amazon.that-ubuntu-1.0" { - ami_name = "fooooobaaaar" - } - - provision { - communicator = comm.ssh.vagrant - - shell { - inline = [ - "echo HOLY GUACAMOLE !" - ] - } - } -} \ No newline at end of file diff --git a/hcl2template/testdata/complete/variables.pkr.hcl b/hcl2template/testdata/complete/variables.pkr.hcl deleted file mode 100644 index d4e651247..000000000 --- a/hcl2template/testdata/complete/variables.pkr.hcl +++ /dev/null @@ -1,6 +0,0 @@ - -variables { - key = "value" - my_secret = "foo" - image_name = "foo-image-{{user `my_secret`}}" -} diff --git a/hcl2template/testdata/sources/basic.pkr.hcl b/hcl2template/testdata/sources/basic.pkr.hcl index 43e3af082..58d3164e5 100644 --- a/hcl2template/testdata/sources/basic.pkr.hcl +++ b/hcl2template/testdata/sources/basic.pkr.hcl @@ -1,37 +1,72 @@ // a source represents a reusable setting for a system boot/start. source "virtualbox-iso" "ubuntu-1204" { - iso_url = "http://releases.ubuntu.com/12.04/ubuntu-12.04.5-server-amd64.iso" - iso_checksum = "769474248a3897f4865817446f9a4a53" - iso_checksum_type = "md5" + string = "string" + int = 42 + int64 = 43 + bool = true + trilean = true + duration = "10s" + map_string_string { + a = "b" + c = "d" + } + slice_string = [ + "a", + "b", + "c", + ] - boot_wait = "10s" - http_directory = "xxx" - boot_command = ["..."] - - shutdown_command = "echo 'vagrant' | sudo -S shutdown -P now" -} - -source "amazon-ebs" "ubuntu-1604" { - instance_type = "t2.micro" - encrypt_boot = true - region = "eu-west-3" - source_ami_filter { - filters { - virtualization-type = "hvm" - name = "ubuntu/images/*ubuntu-xenial-{16.04}-amd64-server-*" - root-device-type = "ebs" + nested { + string = "string" + int = 42 + int64 = 43 + bool = true + trilean = true + duration = "10s" + map_string_string { + a = "b" + c = "d" } - owners = [ - "099720109477" + slice_string = [ + "a", + "b", + "c", ] } -} -source "amazon-ebs" "that-ubuntu-1.0" { - instance_type = "t2.micro" - encrypt_boot = true - region = "eu-west-3" - source_ami_filter { - most_recent = true + nested_slice { + string = "string" + int = 42 + int64 = 43 + bool = true + trilean = true + duration = "10s" + map_string_string { + a = "b" + c = "d" + } + slice_string = [ + "a", + "b", + "c", + ] } -} + + nested_slice { + string = "string" + int = 42 + int64 = 43 + bool = true + trilean = true + duration = "10s" + map_string_string { + a = "b" + c = "d" + } + slice_string = [ + "a", + "b", + "c", + ] + } +} \ No newline at end of file diff --git a/hcl2template/testdata/sources/duplicate.pkr.hcl b/hcl2template/testdata/sources/duplicate.pkr.hcl new file mode 100644 index 000000000..823d17e03 --- /dev/null +++ b/hcl2template/testdata/sources/duplicate.pkr.hcl @@ -0,0 +1,3 @@ + +source "virtualbox-iso" "ubuntu-1204" {} +source "virtualbox-iso" "ubuntu-1204" {} diff --git a/hcl2template/testdata/sources/inexistent.pkr.hcl b/hcl2template/testdata/sources/inexistent.pkr.hcl new file mode 100644 index 000000000..e9a112f17 --- /dev/null +++ b/hcl2template/testdata/sources/inexistent.pkr.hcl @@ -0,0 +1,4 @@ +// a source represents a reusable setting for a system boot/start. +source "inexistant" "ubuntu-1204" { + foo = "bar" +} \ No newline at end of file diff --git a/hcl2template/testdata/sources/unnamed.pkr.hcl b/hcl2template/testdata/sources/unnamed.pkr.hcl new file mode 100644 index 000000000..30adb0333 --- /dev/null +++ b/hcl2template/testdata/sources/unnamed.pkr.hcl @@ -0,0 +1,4 @@ +// a source represents a reusable setting for a system boot/start. +source "virtualbox-iso" { + foo = "bar" +} \ No newline at end of file diff --git a/hcl2template/testdata/sources/untyped.pkr.hcl b/hcl2template/testdata/sources/untyped.pkr.hcl new file mode 100644 index 000000000..28d4fad35 --- /dev/null +++ b/hcl2template/testdata/sources/untyped.pkr.hcl @@ -0,0 +1,4 @@ +// a source represents a reusable setting for a system boot/start. +source { + foo = "bar" +} \ No newline at end of file diff --git a/hcl2template/types.build.from.go b/hcl2template/types.build.from.go index 66d3b7645..b84b6ffc6 100644 --- a/hcl2template/types.build.from.go +++ b/hcl2template/types.build.from.go @@ -2,28 +2,15 @@ package hcl2template import ( "strings" - - "github.com/hashicorp/hcl/v2" - "github.com/hashicorp/hcl/v2/gohcl" - "github.com/hashicorp/hcl/v2/hclsyntax" ) -type BuildFromList []BuildFrom - -type BuildFrom struct { - // source to take config from - Src SourceRef `hcl:"-"` - - HCL2Ref HCL2Ref -} - func sourceRefFromString(in string) SourceRef { args := strings.Split(in, ".") if len(args) < 2 { return NoSource } if len(args) > 2 { - // src.type.name + // source.type.name args = args[1:] } return SourceRef{ @@ -31,38 +18,3 @@ func sourceRefFromString(in string) SourceRef { Name: args[1], } } - -func (bf *BuildFrom) decodeConfig(block *hcl.Block) hcl.Diagnostics { - - bf.Src = sourceRefFromString(block.Labels[0]) - bf.HCL2Ref.DeclRange = block.DefRange - - var b struct { - Config hcl.Body `hcl:",remain"` - } - diags := gohcl.DecodeBody(block.Body, nil, &b) - - if bf.Src == NoSource { - diags = append(diags, &hcl.Diagnostic{ - Severity: hcl.DiagError, - Summary: "Invalid " + sourceLabel + " reference", - Detail: "A " + sourceLabel + " type must start with a letter and " + - "may contain only letters, digits, underscores, and dashes." + - "A valid source reference looks like: `src.type.name`", - Subject: &block.LabelRanges[0], - }) - } - if !hclsyntax.ValidIdentifier(bf.Src.Type) || - !hclsyntax.ValidIdentifier(bf.Src.Name) { - diags = append(diags, &hcl.Diagnostic{ - Severity: hcl.DiagError, - Summary: "Invalid " + sourceLabel + " reference", - Detail: "A " + sourceLabel + " type must start with a letter and " + - "may contain only letters, digits, underscores, and dashes." + - "A valid source reference looks like: `src.type.name`", - Subject: &block.LabelRanges[0], - }) - } - - return diags -} diff --git a/hcl2template/types.build.go b/hcl2template/types.build.go index d758bfc5a..c6a2f68e8 100644 --- a/hcl2template/types.build.go +++ b/hcl2template/types.build.go @@ -2,58 +2,96 @@ package hcl2template import ( "github.com/hashicorp/hcl/v2" + "github.com/hashicorp/hcl/v2/gohcl" + "github.com/hashicorp/hcl/v2/hclsyntax" ) const ( buildFromLabel = "from" - buildProvisionnersLabel = "provision" + buildSourceLabel = "source" - buildPostProvisionnersLabel = "post_provision" + buildProvisionerLabel = "provisioner" + + buildPostProcessorLabel = "post-processor" ) var buildSchema = &hcl.BodySchema{ Blocks: []hcl.BlockHeaderSchema{ - {Type: buildFromLabel, LabelNames: []string{"src"}}, - {Type: buildProvisionnersLabel}, - {Type: buildPostProvisionnersLabel}, + {Type: buildFromLabel, LabelNames: []string{"type"}}, + {Type: buildProvisionerLabel, LabelNames: []string{"type"}}, + {Type: buildPostProcessorLabel, LabelNames: []string{"type"}}, }, } -type Build struct { - // Ordered list of provisioner groups - ProvisionerGroups ProvisionerGroups +type BuildBlock struct { + ProvisionerBlocks []*ProvisionerBlock - // Ordered list of post-provisioner groups - PostProvisionerGroups ProvisionerGroups + PostProcessors []*PostProcessorBlock - // Ordered list of output stanzas - Froms BuildFromList + Froms []SourceRef HCL2Ref HCL2Ref } -type Builds []*Build +type Builds []*BuildBlock -func (p *Parser) decodeBuildConfig(block *hcl.Block) (*Build, hcl.Diagnostics) { - build := &Build{} +func (p *Parser) decodeBuildConfig(block *hcl.Block) (*BuildBlock, hcl.Diagnostics) { + build := &BuildBlock{} - content, diags := block.Body.Content(buildSchema) + var b struct { + FromSources []string `hcl:"sources"` + Config hcl.Body `hcl:",remain"` + } + diags := gohcl.DecodeBody(block.Body, nil, &b) + + for _, buildFrom := range b.FromSources { + ref := sourceRefFromString(buildFrom) + + if ref == NoSource { + diags = append(diags, &hcl.Diagnostic{ + Severity: hcl.DiagError, + Summary: "Invalid " + sourceLabel + " reference", + Detail: "A " + sourceLabel + " type must start with a letter and " + + "may contain only letters, digits, underscores, and dashes." + + "A valid source reference looks like: `source.type.name`", + Subject: &block.LabelRanges[0], + }) + continue + } + if !hclsyntax.ValidIdentifier(ref.Type) || + !hclsyntax.ValidIdentifier(ref.Name) { + diags = append(diags, &hcl.Diagnostic{ + Severity: hcl.DiagError, + Summary: "Invalid " + sourceLabel + " reference", + Detail: "A " + sourceLabel + " type must start with a letter and " + + "may contain only letters, digits, underscores, and dashes." + + "A valid source reference looks like: `source.type.name`", + Subject: &block.LabelRanges[0], + }) + continue + } + + build.Froms = append(build.Froms, ref) + } + + content, diags := b.Config.Content(buildSchema) for _, block := range content.Blocks { switch block.Type { - case buildFromLabel: - bf := BuildFrom{} - moreDiags := bf.decodeConfig(block) + case buildProvisionerLabel: + p, moreDiags := p.decodeProvisioner(block) diags = append(diags, moreDiags...) - build.Froms = append(build.Froms, bf) - case buildProvisionnersLabel: - pg, moreDiags := p.decodeProvisionerGroup(block, p.ProvisionersSchemas) + if moreDiags.HasErrors() { + continue + } + build.ProvisionerBlocks = append(build.ProvisionerBlocks, p) + case buildPostProcessorLabel: + pp, moreDiags := p.decodePostProcessorGroup(block) diags = append(diags, moreDiags...) - build.ProvisionerGroups = append(build.ProvisionerGroups, pg) - case buildPostProvisionnersLabel: - pg, moreDiags := p.decodeProvisionerGroup(block, p.PostProvisionersSchemas) - diags = append(diags, moreDiags...) - build.PostProvisionerGroups = append(build.PostProvisionerGroups, pg) + if moreDiags.HasErrors() { + continue + } + build.PostProcessors = append(build.PostProcessors, pp) } } diff --git a/hcl2template/types.build.post-processor.go b/hcl2template/types.build.post-processor.go new file mode 100644 index 000000000..7575b17c7 --- /dev/null +++ b/hcl2template/types.build.post-processor.go @@ -0,0 +1,61 @@ +package hcl2template + +import ( + "fmt" + + "github.com/hashicorp/hcl/v2" + "github.com/hashicorp/packer/packer" +) + +// PostProcessorBlock represents a parsed PostProcessorBlock +type PostProcessorBlock struct { + PType string + block *hcl.Block +} + +func (p *Parser) decodePostProcessorGroup(block *hcl.Block) (*PostProcessorBlock, hcl.Diagnostics) { + postProcessor := &PostProcessorBlock{ + PType: block.Labels[0], + block: block, + } + var diags hcl.Diagnostics + + if !p.PostProcessorsSchemas.Has(postProcessor.PType) { + diags = append(diags, &hcl.Diagnostic{ + Summary: "Unknown " + buildPostProcessorLabel + " type " + postProcessor.PType, + Subject: block.LabelRanges[0].Ptr(), + Detail: fmt.Sprintf("known provisioners: %v", p.ProvisionersSchemas.List()), + Severity: hcl.DiagError, + }) + return nil, diags + } + + return postProcessor, diags +} + +func (p *Parser) StartPostProcessor(pp *PostProcessorBlock) (packer.PostProcessor, hcl.Diagnostics) { + var diags hcl.Diagnostics + + postProcessor, err := p.PostProcessorsSchemas.Start(pp.PType) + if err != nil { + diags = append(diags, &hcl.Diagnostic{ + Summary: "Failed loading " + pp.block.Type, + Subject: pp.block.LabelRanges[0].Ptr(), + Detail: err.Error(), + }) + return nil, diags + } + flatProvisinerCfg, moreDiags := decodeHCL2Spec(pp.block, nil, postProcessor) + diags = append(diags, moreDiags...) + err = postProcessor.Configure(flatProvisinerCfg) + if err != nil { + diags = append(diags, &hcl.Diagnostic{ + Severity: hcl.DiagError, + Summary: "Failed preparing " + pp.block.Type, + Detail: err.Error(), + Subject: pp.block.DefRange.Ptr(), + }) + return nil, diags + } + return postProcessor, diags +} diff --git a/hcl2template/types.build.provisioners.go b/hcl2template/types.build.provisioners.go index 77ec57c99..33d0fb324 100644 --- a/hcl2template/types.build.provisioners.go +++ b/hcl2template/types.build.provisioners.go @@ -1,70 +1,62 @@ package hcl2template import ( + "fmt" "github.com/hashicorp/hcl/v2" - "github.com/hashicorp/hcl/v2/gohcl" + "github.com/hashicorp/packer/packer" ) -// Provisioner represents a parsed provisioner -type Provisioner struct { - // Cfg is a parsed config - Cfg interface{} +// ProvisionerBlock represents a parsed provisioner +type ProvisionerBlock struct { + PType string + block *hcl.Block } -type ProvisionerGroup struct { - CommunicatorRef CommunicatorRef - - Provisioners []Provisioner - HCL2Ref HCL2Ref -} - -// ProvisionerGroups is a slice of provision blocks; which contains -// provisioners -type ProvisionerGroups []*ProvisionerGroup - -func (p *Parser) decodeProvisionerGroup(block *hcl.Block, provisionerSpecs map[string]Decodable) (*ProvisionerGroup, hcl.Diagnostics) { - var b struct { - Communicator string `hcl:"communicator,optional"` - Remain hcl.Body `hcl:",remain"` +func (p *Parser) decodeProvisioner(block *hcl.Block) (*ProvisionerBlock, hcl.Diagnostics) { + provisioner := &ProvisionerBlock{ + PType: block.Labels[0], + block: block, } + var diags hcl.Diagnostics - diags := gohcl.DecodeBody(block.Body, nil, &b) - - pg := &ProvisionerGroup{} - pg.CommunicatorRef = communicatorRefFromString(b.Communicator) - pg.HCL2Ref.DeclRange = block.DefRange - - buildSchema := &hcl.BodySchema{ - Blocks: []hcl.BlockHeaderSchema{}, - } - for k := range provisionerSpecs { - buildSchema.Blocks = append(buildSchema.Blocks, hcl.BlockHeaderSchema{ - Type: k, + if !p.ProvisionersSchemas.Has(provisioner.PType) { + diags = append(diags, &hcl.Diagnostic{ + Summary: "Unknown " + buildProvisionerLabel + " type " + provisioner.PType, + Subject: block.LabelRanges[0].Ptr(), + Detail: fmt.Sprintf("known provisioners: %v", p.ProvisionersSchemas.List()), + Severity: hcl.DiagError, }) + return nil, diags } + return provisioner, diags +} - content, moreDiags := b.Remain.Content(buildSchema) +func (p *Parser) StartProvisioner(pb *ProvisionerBlock) (packer.Provisioner, hcl.Diagnostics) { + var diags hcl.Diagnostics + + provisioner, err := p.ProvisionersSchemas.Start(pb.PType) + if err != nil { + diags = append(diags, &hcl.Diagnostic{ + Summary: "Failed loading " + pb.block.Type, + Subject: pb.block.LabelRanges[0].Ptr(), + Detail: err.Error(), + }) + return nil, diags + } + flatProvisinerCfg, moreDiags := decodeHCL2Spec(pb.block, nil, provisioner) diags = append(diags, moreDiags...) - for _, block := range content.Blocks { - provisioner, found := provisionerSpecs[block.Type] - if !found { - diags = append(diags, &hcl.Diagnostic{ - Summary: "Unknown " + buildProvisionnersLabel + " type", - Subject: &block.LabelRanges[0], - }) - continue - } - flatProvisinerCfg, moreDiags := decodeDecodable(block, nil, provisioner) - diags = append(diags, moreDiags...) - pg.Provisioners = append(pg.Provisioners, Provisioner{flatProvisinerCfg}) + if diags.HasErrors() { + return nil, diags } - - return pg, diags -} - -func (pgs ProvisionerGroups) FirstCommunicatorRef() CommunicatorRef { - if len(pgs) == 0 { - return NoCommunicator + err = provisioner.Prepare(flatProvisinerCfg) + if err != nil { + diags = append(diags, &hcl.Diagnostic{ + Severity: hcl.DiagError, + Summary: "Failed preparing " + pb.block.Type, + Detail: err.Error(), + Subject: pb.block.DefRange.Ptr(), + }) + return nil, diags } - return pgs[0].CommunicatorRef + return provisioner, diags } diff --git a/hcl2template/types.build_test.go b/hcl2template/types.build_test.go new file mode 100644 index 000000000..f5e1cea69 --- /dev/null +++ b/hcl2template/types.build_test.go @@ -0,0 +1,88 @@ +package hcl2template + +import ( + "testing" + + "github.com/hashicorp/packer/packer" +) + +func TestParse_build(t *testing.T) { + defaultParser := getBasicParser() + + tests := []parseTest{ + {"basic build no src", + defaultParser, + parseTestArgs{"testdata/build/basic.pkr.hcl"}, + &PackerConfig{ + Builds: Builds{ + &BuildBlock{ + Froms: []SourceRef{ + { + Type: "amazon-ebs", + Name: "ubuntu-1604", + }, + ref, + }, + ProvisionerBlocks: []*ProvisionerBlock{ + { + PType: "shell", + }, + { + PType: "file", + }, + }, + PostProcessors: []*PostProcessorBlock{ + { + PType: "amazon-import", + }, + }, + }, + }, + }, + false, false, + []packer.Build{}, + true, + }, + {"untyped provisioner", + defaultParser, + parseTestArgs{"testdata/build/provisioner_untyped.pkr.hcl"}, + &PackerConfig{ + Builds: nil, + }, + true, true, + nil, + false, + }, + {"inexistent provisioner", + defaultParser, + parseTestArgs{"testdata/build/provisioner_inexistent.pkr.hcl"}, + &PackerConfig{ + Builds: nil, + }, + true, true, + []packer.Build{&packer.CoreBuild{}}, + false, + }, + {"untyped post-processor", + defaultParser, + parseTestArgs{"testdata/build/post-processor_untyped.pkr.hcl"}, + &PackerConfig{ + Builds: nil, + }, + true, true, + []packer.Build{&packer.CoreBuild{}}, + false, + }, + {"inexistent post-processor", + defaultParser, + parseTestArgs{"testdata/build/post-processor_inexistent.pkr.hcl"}, + &PackerConfig{ + Builds: nil, + }, + true, true, + []packer.Build{}, + false, + }, + } + testParse(t, tests) +} diff --git a/hcl2template/types.communicator.go b/hcl2template/types.communicator.go deleted file mode 100644 index 0cc0841c6..000000000 --- a/hcl2template/types.communicator.go +++ /dev/null @@ -1,88 +0,0 @@ -package hcl2template - -import ( - "strings" - - "github.com/hashicorp/hcl/v2" - "github.com/hashicorp/hcl/v2/hclsyntax" -) - -type Communicator struct { - // Type of communicator; ex: ssh - Type string - // Given name - Name string - - Cfg interface{} - - HCL2Ref HCL2Ref -} - -func (communicator *Communicator) Ref() CommunicatorRef { - return CommunicatorRef{ - Type: communicator.Type, - Name: communicator.Name, - } -} - -func (p *Parser) decodeCommunicatorConfig(block *hcl.Block) (*Communicator, hcl.Diagnostics) { - - output := &Communicator{} - output.Type = block.Labels[0] - output.Name = block.Labels[1] - output.HCL2Ref.DeclRange = block.DefRange - - diags := hcl.Diagnostics{} - - communicator, found := p.CommunicatorSchemas[output.Type] - if !found { - diags = append(diags, &hcl.Diagnostic{ - Severity: hcl.DiagError, - Summary: "Unknown " + communicatorLabel + " type " + output.Type, - Detail: "A " + communicatorLabel + " type must start with a letter and " + - "may contain only letters, digits, underscores, and dashes.", - Subject: &block.DefRange, - }) - return output, diags - } - - flatCommunicator, moreDiags := decodeDecodable(block, nil, communicator) - diags = append(diags, moreDiags...) - output.Cfg = flatCommunicator - - if !hclsyntax.ValidIdentifier(output.Name) { - diags = append(diags, &hcl.Diagnostic{ - Severity: hcl.DiagError, - Summary: "Invalid " + communicatorLabel + " name", - Detail: "A " + communicatorLabel + " type must start with a letter and " + - "may contain only letters, digits, underscores, and dashes.", - Subject: &block.DefRange, - }) - } - - return output, diags -} - -type CommunicatorRef struct { - Type string - Name string -} - -// NoCommunicator is the zero value of CommunicatorRef, representing the -// absense of Communicator. -var NoCommunicator CommunicatorRef - -func communicatorRefFromString(in string) CommunicatorRef { - args := strings.Split(in, ".") - if len(args) < 2 { - return NoCommunicator - } - if len(args) > 2 { - // comm.type.name - args = args[1:] - } - return CommunicatorRef{ - Type: args[0], - Name: args[1], - } -} diff --git a/hcl2template/types.decodable.go b/hcl2template/types.decodable.go deleted file mode 100644 index 18c7dfe46..000000000 --- a/hcl2template/types.decodable.go +++ /dev/null @@ -1,56 +0,0 @@ -package hcl2template - -import ( - "fmt" - - "github.com/hashicorp/hcl/v2" - "github.com/hashicorp/hcl/v2/hcldec" - "github.com/zclconf/go-cty/cty" - "github.com/zclconf/go-cty/cty/gocty" -) - -type Decodable interface { - FlatMapstructure() interface{} -} - -type SelfSpecified interface { - HCL2Spec() map[string]hcldec.Spec -} - -func decodeDecodable(block *hcl.Block, ctx *hcl.EvalContext, dec Decodable) (interface{}, hcl.Diagnostics) { - var diags hcl.Diagnostics - - flatCfg := dec.FlatMapstructure() - var spec hcldec.ObjectSpec - if ss, selfSpecified := flatCfg.(SelfSpecified); selfSpecified { - spec = hcldec.ObjectSpec(ss.HCL2Spec()) - } else { - diags = append(diags, &hcl.Diagnostic{ - Summary: "Unknown type", - Subject: &block.DefRange, - Detail: fmt.Sprintf("Cannot get spec from a %T", flatCfg), - }) - return nil, diags - } - val, moreDiags := hcldec.Decode(block.Body, spec, ctx) - diags = append(diags, moreDiags...) - - err := gocty.FromCtyValue(val, flatCfg) - if err != nil { - switch err := err.(type) { - case cty.PathError: - diags = append(diags, &hcl.Diagnostic{ - Summary: "gocty.FromCtyValue: " + err.Error(), - Subject: &block.DefRange, - Detail: fmt.Sprintf("%v", err.Path), - }) - default: - diags = append(diags, &hcl.Diagnostic{ - Summary: "gocty.FromCtyValue: " + err.Error(), - Subject: &block.DefRange, - Detail: fmt.Sprintf("%v", err), - }) - } - } - return flatCfg, diags -} diff --git a/hcl2template/types.hcl_ref.go b/hcl2template/types.hcl_ref.go index 06568fb11..3c8060298 100644 --- a/hcl2template/types.hcl_ref.go +++ b/hcl2template/types.hcl_ref.go @@ -12,9 +12,3 @@ type HCL2Ref struct { // remainder of unparsed body Remain hcl.Body } - -// func (hr *HCL2Ref) Blah() { -// // hr.Remain. -// ctyjson.Marshal(nil, nil) -// hr.DeclRange. -// } diff --git a/hcl2template/types.packer_config.go b/hcl2template/types.packer_config.go index a1d3371e8..623360388 100644 --- a/hcl2template/types.packer_config.go +++ b/hcl2template/types.packer_config.go @@ -2,7 +2,7 @@ package hcl2template import ( "github.com/hashicorp/hcl/v2" - "github.com/hashicorp/packer/template" + "github.com/hashicorp/packer/packer" ) // PackerConfig represents a loaded packer config @@ -12,154 +12,91 @@ type PackerConfig struct { Variables PackerV1Variables Builds Builds - - Communicators map[CommunicatorRef]*Communicator } -type PackerV1Build struct { - Builders []*template.Builder - Provisioners []*template.Provisioner - PostProcessors []*template.PostProcessor -} - -func (pkrCfg *PackerConfig) ToV1Build() PackerV1Build { +func (p *Parser) CoreBuildProvisioners(blocks []*ProvisionerBlock) ([]packer.CoreBuildProvisioner, hcl.Diagnostics) { var diags hcl.Diagnostics - res := PackerV1Build{} + res := []packer.CoreBuildProvisioner{} + for _, pb := range blocks { + provisioner, moreDiags := p.StartProvisioner(pb) + diags = append(diags, moreDiags...) + if moreDiags.HasErrors() { + continue + } + res = append(res, packer.CoreBuildProvisioner{ + PType: pb.PType, + Provisioner: provisioner, + }) + } + return res, diags +} - for _, build := range pkrCfg.Builds { - communicator, _ := pkrCfg.Communicators[build.ProvisionerGroups.FirstCommunicatorRef()] +func (p *Parser) CoreBuildPostProcessors(blocks []*PostProcessorBlock) ([]packer.CoreBuildPostProcessor, hcl.Diagnostics) { + var diags hcl.Diagnostics + res := []packer.CoreBuildPostProcessor{} + for _, pp := range blocks { + postProcessor, moreDiags := p.StartPostProcessor(pp) + diags = append(diags, moreDiags...) + if moreDiags.HasErrors() { + continue + } + res = append(res, packer.CoreBuildPostProcessor{ + PostProcessor: postProcessor, + PType: pp.PType, + }) + } + return res, diags +} +func (p *Parser) getBuilds(cfg *PackerConfig) ([]packer.Build, hcl.Diagnostics) { + res := []packer.Build{} + var diags hcl.Diagnostics + + for _, build := range cfg.Builds { for _, from := range build.Froms { - source, found := pkrCfg.Sources[from.Src] + src, found := cfg.Sources[from] if !found { diags = append(diags, &hcl.Diagnostic{ + Summary: "Unknown " + sourceLabel + " " + from.String(), + Subject: build.HCL2Ref.DeclRange.Ptr(), Severity: hcl.DiagError, - Summary: "Unknown " + sourceLabel + " reference", - Detail: "", - Subject: &from.HCL2Ref.DeclRange, }) - + continue + } + builder, moreDiags := p.StartBuilder(src) + diags = append(diags, moreDiags...) + if moreDiags.HasErrors() { + continue + } + provisioners, moreDiags := p.CoreBuildProvisioners(build.ProvisionerBlocks) + diags = append(diags, moreDiags...) + if moreDiags.HasErrors() { + continue + } + postProcessors, moreDiags := p.CoreBuildPostProcessors(build.PostProcessors) + diags = append(diags, moreDiags...) + if moreDiags.HasErrors() { continue } - // provisioners := build.ProvisionerGroups.FlatProvisioners() - // postProcessors := build.PostProvisionerGroups.FlatProvisioners() - - _ = from - _ = source - _ = communicator - // _ = provisioners - // _ = postProcessors + pcb := &packer.CoreBuild{ + Type: src.Type, + Builder: builder, + Provisioners: provisioners, + PostProcessors: [][]packer.CoreBuildPostProcessor{postProcessors}, + Variables: cfg.Variables, + } + res = append(res, pcb) } } - return res + return res, diags } -func (pkrCfg *PackerConfig) ToTemplate() (*template.Template, error) { - var result template.Template - // var errs error +func (p *Parser) Parse(path string) ([]packer.Build, hcl.Diagnostics) { + cfg, diags := p.parse(path) + if diags.HasErrors() { + return nil, diags + } - result.Comments = nil - result.Variables = pkrCfg.Variables.Variables() - // TODO(azr): add sensitive variables - - builder := pkrCfg.ToV1Build() - _ = builder - - // // Gather all the post-processors - // if len(r.PostProcessors) > 0 { - // result.PostProcessors = make([][]*PostProcessor, 0, len(r.PostProcessors)) - // } - // for i, v := range r.PostProcessors { - // // Parse the configurations. We need to do this because post-processors - // // can take three different formats. - // configs, err := r.parsePostProcessor(i, v) - // if err != nil { - // errs = multierror.Append(errs, err) - // continue - // } - - // // Parse the PostProcessors out of the configs - // pps := make([]*PostProcessor, 0, len(configs)) - // for j, c := range configs { - // var pp PostProcessor - // if err := r.decoder(&pp, nil).Decode(c); err != nil { - // errs = multierror.Append(errs, fmt.Errorf( - // "post-processor %d.%d: %s", i+1, j+1, err)) - // continue - // } - - // // Type is required - // if pp.Type == "" { - // errs = multierror.Append(errs, fmt.Errorf( - // "post-processor %d.%d: type is required", i+1, j+1)) - // continue - // } - - // // Set the raw configuration and delete any special keys - // pp.Config = c - - // // The name defaults to the type if it isn't set - // if pp.Name == "" { - // pp.Name = pp.Type - // } - - // delete(pp.Config, "except") - // delete(pp.Config, "only") - // delete(pp.Config, "keep_input_artifact") - // delete(pp.Config, "type") - // delete(pp.Config, "name") - - // if len(pp.Config) == 0 { - // pp.Config = nil - // } - - // pps = append(pps, &pp) - // } - - // result.PostProcessors = append(result.PostProcessors, pps) - // } - - // // Gather all the provisioners - // if len(r.Provisioners) > 0 { - // result.Provisioners = make([]*Provisioner, 0, len(r.Provisioners)) - // } - // for i, v := range r.Provisioners { - // var p Provisioner - // if err := r.decoder(&p, nil).Decode(v); err != nil { - // errs = multierror.Append(errs, fmt.Errorf( - // "provisioner %d: %s", i+1, err)) - // continue - // } - - // // Type is required before any richer validation - // if p.Type == "" { - // errs = multierror.Append(errs, fmt.Errorf( - // "provisioner %d: missing 'type'", i+1)) - // continue - // } - - // // Set the raw configuration and delete any special keys - // p.Config = v.(map[string]interface{}) - - // delete(p.Config, "except") - // delete(p.Config, "only") - // delete(p.Config, "override") - // delete(p.Config, "pause_before") - // delete(p.Config, "type") - // delete(p.Config, "timeout") - - // if len(p.Config) == 0 { - // p.Config = nil - // } - - // result.Provisioners = append(result.Provisioners, &p) - // } - - // // If we have errors, return those with a nil result - // if errs != nil { - // return nil, errs - // } - - return &result, nil + return p.getBuilds(cfg) } diff --git a/hcl2template/types.packer_config_test.go b/hcl2template/types.packer_config_test.go new file mode 100644 index 000000000..b47ffba4a --- /dev/null +++ b/hcl2template/types.packer_config_test.go @@ -0,0 +1,55 @@ +package hcl2template + +import ( + "testing" + + "github.com/hashicorp/packer/packer" +) + +var ref = SourceRef{Type: "virtualbox-iso", Name: "ubuntu-1204"} + +func TestParser_complete(t *testing.T) { + defaultParser := getBasicParser() + + tests := []parseTest{ + {"working build", + defaultParser, + parseTestArgs{"testdata/complete"}, + &PackerConfig{ + Sources: map[SourceRef]*Source{ + ref: &Source{Type: "virtualbox-iso", Name: "ubuntu-1204"}, + }, + Builds: Builds{ + &BuildBlock{ + Froms: []SourceRef{ref}, + ProvisionerBlocks: []*ProvisionerBlock{ + {PType: "shell"}, + {PType: "file"}, + }, + PostProcessors: []*PostProcessorBlock{ + {PType: "amazon-import"}, + }, + }, + }, + }, + false, false, + []packer.Build{ + &packer.CoreBuild{ + Type: "virtualbox-iso", + Builder: basicMockBuilder, + Provisioners: []packer.CoreBuildProvisioner{ + {PType: "shell", Provisioner: basicMockProvisioner}, + {PType: "file", Provisioner: basicMockProvisioner}, + }, + PostProcessors: [][]packer.CoreBuildPostProcessor{ + { + {PType: "amazon-import", PostProcessor: basicMockPostProcessor}, + }, + }, + }, + }, + false, + }, + } + testParse(t, tests) +} diff --git a/hcl2template/types.source.go b/hcl2template/types.source.go index 17844b323..f86ab4b69 100644 --- a/hcl2template/types.source.go +++ b/hcl2template/types.source.go @@ -4,6 +4,7 @@ import ( "fmt" "github.com/hashicorp/hcl/v2" + "github.com/hashicorp/packer/packer" ) // A source field in an HCL file will load into the Source type. @@ -14,36 +15,58 @@ type Source struct { // Given name; if any Name string - Cfg interface{} - - HCL2Ref HCL2Ref + block *hcl.Block } -func (p *Parser) decodeSource(block *hcl.Block, sourceSpecs map[string]Decodable) (*Source, hcl.Diagnostics) { +func (p *Parser) decodeSource(block *hcl.Block) (*Source, hcl.Diagnostics) { source := &Source{ - Type: block.Labels[0], - Name: block.Labels[1], + Type: block.Labels[0], + Name: block.Labels[1], + block: block, } - source.HCL2Ref.DeclRange = block.DefRange - var diags hcl.Diagnostics - sourceSpec, found := sourceSpecs[source.Type] - if !found { + if !p.BuilderSchemas.Has(source.Type) { diags = append(diags, &hcl.Diagnostic{ - Summary: "Unknown " + sourceLabel + " type", - Subject: &block.LabelRanges[0], + Summary: "Unknown " + buildSourceLabel + " type " + source.Type, + Subject: block.LabelRanges[0].Ptr(), + Detail: fmt.Sprintf("known builders: %v", p.BuilderSchemas.List()), + Severity: hcl.DiagError, }) - return source, diags + return nil, diags } - flatSource, moreDiags := decodeDecodable(block, nil, sourceSpec) - diags = append(diags, moreDiags...) - source.Cfg = flatSource - return source, diags } +func (p *Parser) StartBuilder(source *Source) (packer.Builder, hcl.Diagnostics) { + var diags hcl.Diagnostics + + // calling BuilderSchemas will start a new builder plugin to ask about + // the schema of the builder; but we do not know yet if the builder is + // actually going to be used. This also allows to call the same builder + // more than once. + builder, err := p.BuilderSchemas.Start(source.Type) + if err != nil { + diags = append(diags, &hcl.Diagnostic{ + Summary: "Failed to load " + sourceLabel + " type", + Detail: err.Error(), + Subject: &source.block.LabelRanges[0], + }) + return builder, diags + } + + decoded, moreDiags := decodeHCL2Spec(source.block, nil, builder) + diags = append(diags, moreDiags...) + if moreDiags.HasErrors() { + return nil, diags + } + warning, err := builder.Prepare(decoded) + moreDiags = warningErrorsToDiags(source.block, warning, err) + diags = append(diags, moreDiags...) + return builder, diags +} + func (source *Source) Ref() SourceRef { return SourceRef{ Type: source.Type, diff --git a/hcl2template/types.source_test.go b/hcl2template/types.source_test.go new file mode 100644 index 000000000..66d38860a --- /dev/null +++ b/hcl2template/types.source_test.go @@ -0,0 +1,75 @@ +package hcl2template + +import ( + "testing" + + "github.com/hashicorp/packer/packer" +) + +func TestParse_source(t *testing.T) { + defaultParser := getBasicParser() + + tests := []parseTest{ + {"two basic sources", + defaultParser, + parseTestArgs{"testdata/sources/basic.pkr.hcl"}, + &PackerConfig{ + Sources: map[SourceRef]*Source{ + SourceRef{ + Type: "virtualbox-iso", + Name: "ubuntu-1204", + }: { + Type: "virtualbox-iso", + Name: "ubuntu-1204", + }, + }, + }, + false, false, + []packer.Build{}, + false, + }, + {"untyped source", + defaultParser, + parseTestArgs{"testdata/sources/untyped.pkr.hcl"}, + &PackerConfig{}, + true, true, + nil, + false, + }, + {"unnamed source", + defaultParser, + parseTestArgs{"testdata/sources/unnamed.pkr.hcl"}, + &PackerConfig{}, + true, true, + nil, + false, + }, + {"inexistent source", + defaultParser, + parseTestArgs{"testdata/sources/inexistent.pkr.hcl"}, + &PackerConfig{}, + true, true, + nil, + false, + }, + {"duplicate source", + defaultParser, + parseTestArgs{"testdata/sources/duplicate.pkr.hcl"}, + &PackerConfig{ + Sources: map[SourceRef]*Source{ + SourceRef{ + Type: "virtualbox-iso", + Name: "ubuntu-1204", + }: { + Type: "virtualbox-iso", + Name: "ubuntu-1204", + }, + }, + }, + true, true, + nil, + false, + }, + } + testParse(t, tests) +} diff --git a/hcl2template/types.variable.go b/hcl2template/types.variables.go similarity index 100% rename from hcl2template/types.variable.go rename to hcl2template/types.variables.go diff --git a/hcl2template/types.variables_test.go b/hcl2template/types.variables_test.go new file mode 100644 index 000000000..cf60f25ef --- /dev/null +++ b/hcl2template/types.variables_test.go @@ -0,0 +1,29 @@ +package hcl2template + +import ( + "testing" + + "github.com/hashicorp/packer/packer" +) + +func TestParse_variables(t *testing.T) { + defaultParser := getBasicParser() + + tests := []parseTest{ + {"basic variables", + defaultParser, + parseTestArgs{"testdata/variables/basic.pkr.hcl"}, + &PackerConfig{ + Variables: PackerV1Variables{ + "image_name": "foo-image-{{user `my_secret`}}", + "key": "value", + "my_secret": "foo", + }, + }, + false, false, + []packer.Build{}, + false, + }, + } + testParse(t, tests) +} diff --git a/hcl2template/utils.go b/hcl2template/utils.go new file mode 100644 index 000000000..6edb52447 --- /dev/null +++ b/hcl2template/utils.go @@ -0,0 +1,23 @@ +package hcl2template + +import "github.com/hashicorp/hcl/v2" + +func warningErrorsToDiags(block *hcl.Block, warnings []string, err error) hcl.Diagnostics { + var diags hcl.Diagnostics + + for _, warning := range warnings { + diags = append(diags, &hcl.Diagnostic{ + Summary: warning, + Subject: &block.DefRange, + Severity: hcl.DiagWarning, + }) + } + if err != nil { + diags = append(diags, &hcl.Diagnostic{ + Summary: err.Error(), + Subject: &block.DefRange, + Severity: hcl.DiagError, + }) + } + return diags +} diff --git a/helper/builder/testing/testing.go b/helper/builder/testing/testing.go index 223615fa3..df1bdad87 100644 --- a/helper/builder/testing/testing.go +++ b/helper/builder/testing/testing.go @@ -63,6 +63,13 @@ type TestT interface { Skip(args ...interface{}) } +type TestBuilderStore struct { + packer.BuilderStore + StartFn func(name string) (packer.Builder, error) +} + +func (tbs TestBuilderStore) Start(name string) (packer.Builder, error) { return tbs.StartFn(name) } + // Test performs an acceptance test on a backend with the given test case. // // Tests are not run unless an environmental variable "PACKER_ACC" is @@ -106,12 +113,14 @@ func Test(t TestT, c TestCase) { log.Printf("[DEBUG] Initializing core...") core, err := packer.NewCore(&packer.CoreConfig{ Components: packer.ComponentFinder{ - Builder: func(n string) (packer.Builder, error) { - if n == "test" { - return c.Builder, nil - } + BuilderStore: TestBuilderStore{ + StartFn: func(n string) (packer.Builder, error) { + if n == "test" { + return c.Builder, nil + } - return nil, nil + return nil, nil + }, }, }, Template: tpl, diff --git a/helper/communicator/config.go b/helper/communicator/config.go index c6c5d880e..a2e262445 100644 --- a/helper/communicator/config.go +++ b/helper/communicator/config.go @@ -11,7 +11,9 @@ import ( "os" "time" + "github.com/hashicorp/hcl/v2/hcldec" packerssh "github.com/hashicorp/packer/communicator/ssh" + "github.com/hashicorp/packer/helper/config" "github.com/hashicorp/packer/helper/multistep" helperssh "github.com/hashicorp/packer/helper/ssh" "github.com/hashicorp/packer/packer" @@ -160,10 +162,28 @@ type SSH struct { SSHLocalTunnels []string `mapstructure:"ssh_local_tunnels"` // SSH Internals - SSHPublicKey []byte - SSHPrivateKey []byte + SSHPublicKey []byte `mapstructure:"ssh_public_key"` + SSHPrivateKey []byte `mapstructure:"ssh_private_key"` } +func (c *SSH) ConfigSpec() hcldec.ObjectSpec { return c.FlatMapstructure().HCL2Spec() } +func (c *WinRM) ConfigSpec() hcldec.ObjectSpec { return c.FlatMapstructure().HCL2Spec() } + +func (c *SSH) Configure(raws ...interface{}) ([]string, error) { + err := config.Decode(c, nil, raws...) + return nil, err +} + +func (c *WinRM) Configure(raws ...interface{}) ([]string, error) { + err := config.Decode(c, nil, raws...) + return nil, err +} + +var ( + _ packer.ConfigurableCommunicator = new(SSH) + _ packer.ConfigurableCommunicator = new(WinRM) +) + type SSHInterface struct { // One of `public_ip`, `private_ip`, `public_dns`, or `private_dns`. If // set, either the public IP address, private IP address, public DNS name diff --git a/helper/communicator/config.hcl2spec.go b/helper/communicator/config.hcl2spec.go index bf726f3cd..977ac1d41 100644 --- a/helper/communicator/config.hcl2spec.go +++ b/helper/communicator/config.hcl2spec.go @@ -39,8 +39,8 @@ type FlatConfig struct { SSHReadWriteTimeout *string `mapstructure:"ssh_read_write_timeout" cty:"ssh_read_write_timeout"` SSHRemoteTunnels []string `mapstructure:"ssh_remote_tunnels" cty:"ssh_remote_tunnels"` SSHLocalTunnels []string `mapstructure:"ssh_local_tunnels" cty:"ssh_local_tunnels"` - SSHPublicKey []byte `cty:"ssh_public_key"` - SSHPrivateKey []byte `cty:"ssh_private_key"` + SSHPublicKey []byte `mapstructure:"ssh_public_key" cty:"ssh_public_key"` + SSHPrivateKey []byte `mapstructure:"ssh_private_key" cty:"ssh_private_key"` WinRMUser *string `mapstructure:"winrm_username" cty:"winrm_username"` WinRMPassword *string `mapstructure:"winrm_password" cty:"winrm_password"` WinRMHost *string `mapstructure:"winrm_host" cty:"winrm_host"` @@ -54,10 +54,13 @@ type FlatConfig struct { // FlatMapstructure returns a new FlatConfig. // FlatConfig is an auto-generated flat version of Config. // Where the contents a fields with a `mapstructure:,squash` tag are bubbled up. -func (*Config) FlatMapstructure() interface{} { return new(FlatConfig) } +func (*Config) FlatMapstructure() interface{ HCL2Spec() map[string]hcldec.Spec } { + return new(FlatConfig) +} -// HCL2Spec returns the hcldec.Spec of a FlatConfig. -// This spec is used by HCL to read the fields of FlatConfig. +// HCL2Spec returns the hcl spec of a Config. +// This spec is used by HCL to read the fields of Config. +// The decoded values from this spec will then be applied to a FlatConfig. func (*FlatConfig) HCL2Spec() map[string]hcldec.Spec { s := map[string]hcldec.Spec{ "communicator": &hcldec.AttrSpec{Name: "communicator", Type: cty.String, Required: false}, @@ -135,17 +138,18 @@ type FlatSSH struct { SSHReadWriteTimeout *string `mapstructure:"ssh_read_write_timeout" cty:"ssh_read_write_timeout"` SSHRemoteTunnels []string `mapstructure:"ssh_remote_tunnels" cty:"ssh_remote_tunnels"` SSHLocalTunnels []string `mapstructure:"ssh_local_tunnels" cty:"ssh_local_tunnels"` - SSHPublicKey []byte `cty:"ssh_public_key"` - SSHPrivateKey []byte `cty:"ssh_private_key"` + SSHPublicKey []byte `mapstructure:"ssh_public_key" cty:"ssh_public_key"` + SSHPrivateKey []byte `mapstructure:"ssh_private_key" cty:"ssh_private_key"` } // FlatMapstructure returns a new FlatSSH. // FlatSSH is an auto-generated flat version of SSH. // Where the contents a fields with a `mapstructure:,squash` tag are bubbled up. -func (*SSH) FlatMapstructure() interface{} { return new(FlatSSH) } +func (*SSH) FlatMapstructure() interface{ HCL2Spec() map[string]hcldec.Spec } { return new(FlatSSH) } -// HCL2Spec returns the hcldec.Spec of a FlatSSH. -// This spec is used by HCL to read the fields of FlatSSH. +// HCL2Spec returns the hcl spec of a SSH. +// This spec is used by HCL to read the fields of SSH. +// The decoded values from this spec will then be applied to a FlatSSH. func (*FlatSSH) HCL2Spec() map[string]hcldec.Spec { s := map[string]hcldec.Spec{ "ssh_host": &hcldec.AttrSpec{Name: "ssh_host", Type: cty.String, Required: false}, @@ -198,10 +202,11 @@ type FlatWinRM struct { // FlatMapstructure returns a new FlatWinRM. // FlatWinRM is an auto-generated flat version of WinRM. // Where the contents a fields with a `mapstructure:,squash` tag are bubbled up. -func (*WinRM) FlatMapstructure() interface{} { return new(FlatWinRM) } +func (*WinRM) FlatMapstructure() interface{ HCL2Spec() map[string]hcldec.Spec } { return new(FlatWinRM) } -// HCL2Spec returns the hcldec.Spec of a FlatWinRM. -// This spec is used by HCL to read the fields of FlatWinRM. +// HCL2Spec returns the hcl spec of a WinRM. +// This spec is used by HCL to read the fields of WinRM. +// The decoded values from this spec will then be applied to a FlatWinRM. func (*FlatWinRM) HCL2Spec() map[string]hcldec.Spec { s := map[string]hcldec.Spec{ "winrm_username": &hcldec.AttrSpec{Name: "winrm_username", Type: cty.String, Required: false}, diff --git a/helper/config/decode.go b/helper/config/decode.go index 43673e1dc..eec3bbdf7 100644 --- a/helper/config/decode.go +++ b/helper/config/decode.go @@ -1,14 +1,19 @@ package config import ( + "encoding/json" "fmt" "reflect" "sort" "strings" "github.com/hashicorp/go-multierror" + "github.com/hashicorp/hcl/v2/hcldec" "github.com/hashicorp/packer/template/interpolate" "github.com/mitchellh/mapstructure" + "github.com/zclconf/go-cty/cty" + "github.com/zclconf/go-cty/cty/gocty" + ctyjson "github.com/zclconf/go-cty/cty/json" ) // DecodeOpts are the options for decoding configuration. @@ -37,6 +42,36 @@ var DefaultDecodeHookFuncs = []mapstructure.DecodeHookFunc{ // Decode decodes the configuration into the target and optionally // automatically interpolates all the configuration as it goes. func Decode(target interface{}, config *DecodeOpts, raws ...interface{}) error { + for i, raw := range raws { + // check for cty values and transform them to json then to a + // map[string]interface{} so that mapstructure can do its thing. + cval, ok := raw.(cty.Value) + if !ok { + continue + } + type flatConfigurer interface { + FlatMapstructure() interface{ HCL2Spec() map[string]hcldec.Spec } + } + ctarget := target.(flatConfigurer) + flatCfg := ctarget.FlatMapstructure() + err := gocty.FromCtyValue(cval, flatCfg) + if err != nil { + switch err := err.(type) { + case cty.PathError: + return fmt.Errorf("%v: %v", err, err.Path) + } + return err + } + b, err := ctyjson.SimpleJSONValue{Value: cval}.MarshalJSON() + if err != nil { + return err + } + var raw map[string]interface{} + if err := json.Unmarshal(b, &raw); err != nil { + return err + } + raws[i] = raw + } if config == nil { config = &DecodeOpts{Interpolate: true} } diff --git a/main.go b/main.go index b24ee229b..bed90598c 100644 --- a/main.go +++ b/main.go @@ -141,16 +141,16 @@ func wrappedMain() int { log.SetFlags(0) } - log.Printf("[INFO] Packer version: %s", version.FormattedVersion()) - log.Printf("Packer Target OS/Arch: %s %s", runtime.GOOS, runtime.GOARCH) - log.Printf("Built with Go Version: %s", runtime.Version()) + log.Printf("[INFO] Packer version: %s [%s %s %s]", + version.FormattedVersion(), + runtime.Version(), + runtime.GOOS, runtime.GOARCH) config, err := loadConfig() if err != nil { fmt.Fprintf(os.Stderr, "Error loading configuration: \n\n%s\n", err) return 1 } - log.Printf("Packer config: %+v", config) // Fire off the checkpoint. go runCheckpoint(config) @@ -214,10 +214,11 @@ func wrappedMain() int { CommandMeta = &command.Meta{ CoreConfig: &packer.CoreConfig{ Components: packer.ComponentFinder{ - Builder: config.LoadBuilder, - Hook: config.LoadHook, - PostProcessor: config.LoadPostProcessor, - Provisioner: config.LoadProvisioner, + Hook: config.StarHook, + + BuilderStore: config.Builders, + ProvisionerStore: config.Provisioners, + PostProcessorStore: config.PostProcessors, }, Version: version.Version, }, @@ -291,6 +292,9 @@ func loadConfig() (*config, error) { var config config config.PluginMinPort = 10000 config.PluginMaxPort = 25000 + config.Builders = packer.MapOfBuilder{} + config.PostProcessors = packer.MapOfPostProcessor{} + config.Provisioners = packer.MapOfProvisioner{} if err := config.Discover(); err != nil { return nil, err } diff --git a/packer/build.go b/packer/build.go index badd67c1f..526dc05e4 100644 --- a/packer/build.go +++ b/packer/build.go @@ -79,21 +79,21 @@ type Build interface { SetOnError(string) } -// A build struct represents a single build job, the result of which should +// A CoreBuild struct represents a single build job, the result of which should // be a single machine image artifact. This artifact may be comprised of -// multiple files, of course, but it should be for only a single provider -// (such as VirtualBox, EC2, etc.). -type coreBuild struct { - name string - builder Builder - builderConfig interface{} - builderType string +// multiple files, of course, but it should be for only a single provider (such +// as VirtualBox, EC2, etc.). +type CoreBuild struct { + Type string + Builder Builder + BuilderConfig interface{} + BuilderType string hooks map[string][]Hook - postProcessors [][]coreBuildPostProcessor - provisioners []coreBuildProvisioner - cleanupProvisioner coreBuildProvisioner - templatePath string - variables map[string]string + Provisioners []CoreBuildProvisioner + PostProcessors [][]CoreBuildPostProcessor + CleanupProvisioner CoreBuildProvisioner + TemplatePath string + Variables map[string]string debug bool force bool @@ -102,32 +102,32 @@ type coreBuild struct { prepareCalled bool } -// Keeps track of the post-processor and the configuration of the -// post-processor used within a build. -type coreBuildPostProcessor struct { - processor PostProcessor - processorType string +// CoreBuildPostProcessor Keeps track of the post-processor and the +// configuration of the post-processor used within a build. +type CoreBuildPostProcessor struct { + PostProcessor PostProcessor + PType string config map[string]interface{} keepInputArtifact *bool } -// Keeps track of the provisioner and the configuration of the provisioner -// within the build. -type coreBuildProvisioner struct { - pType string - provisioner Provisioner +// CoreBuildProvisioner keeps track of the provisioner and the configuration of +// the provisioner within the build. +type CoreBuildProvisioner struct { + PType string + Provisioner Provisioner config []interface{} } // Returns the name of the build. -func (b *coreBuild) Name() string { - return b.name +func (b *CoreBuild) Name() string { + return b.Type } // Prepare prepares the build by doing some initialization for the builder // and any hooks. This _must_ be called prior to Run. The parameter is the // overrides for the variables within the template (if any). -func (b *coreBuild) Prepare() (warn []string, err error) { +func (b *CoreBuild) Prepare() (warn []string, err error) { b.l.Lock() defer b.l.Unlock() @@ -138,48 +138,48 @@ func (b *coreBuild) Prepare() (warn []string, err error) { b.prepareCalled = true packerConfig := map[string]interface{}{ - BuildNameConfigKey: b.name, - BuilderTypeConfigKey: b.builderType, + BuildNameConfigKey: b.Type, + BuilderTypeConfigKey: b.BuilderType, DebugConfigKey: b.debug, ForceConfigKey: b.force, OnErrorConfigKey: b.onError, - TemplatePathKey: b.templatePath, - UserVariablesConfigKey: b.variables, + TemplatePathKey: b.TemplatePath, + UserVariablesConfigKey: b.Variables, } // Prepare the builder - warn, err = b.builder.Prepare(b.builderConfig, packerConfig) + warn, err = b.Builder.Prepare(b.BuilderConfig, packerConfig) if err != nil { - log.Printf("Build '%s' prepare failure: %s\n", b.name, err) + log.Printf("Build '%s' prepare failure: %s\n", b.Type, err) return } // Prepare the provisioners - for _, coreProv := range b.provisioners { + for _, coreProv := range b.Provisioners { configs := make([]interface{}, len(coreProv.config), len(coreProv.config)+1) copy(configs, coreProv.config) configs = append(configs, packerConfig) - if err = coreProv.provisioner.Prepare(configs...); err != nil { + if err = coreProv.Provisioner.Prepare(configs...); err != nil { return } } // Prepare the on-error-cleanup provisioner - if b.cleanupProvisioner.pType != "" { - configs := make([]interface{}, len(b.cleanupProvisioner.config), len(b.cleanupProvisioner.config)+1) - copy(configs, b.cleanupProvisioner.config) + if b.CleanupProvisioner.PType != "" { + configs := make([]interface{}, len(b.CleanupProvisioner.config), len(b.CleanupProvisioner.config)+1) + copy(configs, b.CleanupProvisioner.config) configs = append(configs, packerConfig) - err = b.cleanupProvisioner.provisioner.Prepare(configs...) + err = b.CleanupProvisioner.Provisioner.Prepare(configs...) if err != nil { return } } // Prepare the post-processors - for _, ppSeq := range b.postProcessors { + for _, ppSeq := range b.PostProcessors { for _, corePP := range ppSeq { - err = corePP.processor.Configure(corePP.config, packerConfig) + err = corePP.PostProcessor.Configure(corePP.config, packerConfig) if err != nil { return } @@ -190,7 +190,7 @@ func (b *coreBuild) Prepare() (warn []string, err error) { } // Runs the actual build. Prepare must be called prior to running this. -func (b *coreBuild) Run(ctx context.Context, originalUi Ui) ([]Artifact, error) { +func (b *CoreBuild) Run(ctx context.Context, originalUi Ui) ([]Artifact, error) { if !b.prepareCalled { panic("Prepare must be called first") } @@ -203,24 +203,24 @@ func (b *coreBuild) Run(ctx context.Context, originalUi Ui) ([]Artifact, error) } // Add a hook for the provisioners if we have provisioners - if len(b.provisioners) > 0 { - hookedProvisioners := make([]*HookedProvisioner, len(b.provisioners)) - for i, p := range b.provisioners { + if len(b.Provisioners) > 0 { + hookedProvisioners := make([]*HookedProvisioner, len(b.Provisioners)) + for i, p := range b.Provisioners { var pConfig interface{} if len(p.config) > 0 { pConfig = p.config[0] } if b.debug { hookedProvisioners[i] = &HookedProvisioner{ - &DebuggedProvisioner{Provisioner: p.provisioner}, + &DebuggedProvisioner{Provisioner: p.Provisioner}, pConfig, - p.pType, + p.PType, } } else { hookedProvisioners[i] = &HookedProvisioner{ - p.provisioner, + p.Provisioner, pConfig, - p.pType, + p.PType, } } } @@ -234,11 +234,11 @@ func (b *coreBuild) Run(ctx context.Context, originalUi Ui) ([]Artifact, error) }) } - if b.cleanupProvisioner.pType != "" { + if b.CleanupProvisioner.PType != "" { hookedCleanupProvisioner := &HookedProvisioner{ - b.cleanupProvisioner.provisioner, - b.cleanupProvisioner.config, - b.cleanupProvisioner.pType, + b.CleanupProvisioner.Provisioner, + b.CleanupProvisioner.config, + b.CleanupProvisioner.PType, } hooks[HookCleanupProvision] = []Hook{&ProvisionHook{ Provisioners: []*HookedProvisioner{hookedCleanupProvisioner}, @@ -254,9 +254,9 @@ func (b *coreBuild) Run(ctx context.Context, originalUi Ui) ([]Artifact, error) Ui: originalUi, } - log.Printf("Running builder: %s", b.builderType) - ts := CheckpointReporter.AddSpan(b.builderType, "builder", b.builderConfig) - builderArtifact, err := b.builder.Run(ctx, builderUi, hook) + log.Printf("Running builder: %s", b.BuilderType) + ts := CheckpointReporter.AddSpan(b.BuilderType, "builder", b.BuilderConfig) + builderArtifact, err := b.Builder.Run(ctx, builderUi, hook) ts.End(err) if err != nil { return nil, err @@ -269,21 +269,21 @@ func (b *coreBuild) Run(ctx context.Context, originalUi Ui) ([]Artifact, error) } errors := make([]error, 0) - keepOriginalArtifact := len(b.postProcessors) == 0 + keepOriginalArtifact := len(b.PostProcessors) == 0 // Run the post-processors PostProcessorRunSeqLoop: - for _, ppSeq := range b.postProcessors { + for _, ppSeq := range b.PostProcessors { priorArtifact := builderArtifact for i, corePP := range ppSeq { ppUi := &TargetedUI{ - Target: fmt.Sprintf("%s (%s)", b.Name(), corePP.processorType), + Target: fmt.Sprintf("%s (%s)", b.Name(), corePP.PType), Ui: originalUi, } - builderUi.Say(fmt.Sprintf("Running post-processor: %s", corePP.processorType)) - ts := CheckpointReporter.AddSpan(corePP.processorType, "post-processor", corePP.config) - artifact, defaultKeep, forceOverride, err := corePP.processor.PostProcess(ctx, ppUi, priorArtifact) + builderUi.Say(fmt.Sprintf("Running post-processor: %s", corePP.PType)) + ts := CheckpointReporter.AddSpan(corePP.PType, "post-processor", corePP.config) + artifact, defaultKeep, forceOverride, err := corePP.PostProcessor.PostProcess(ctx, ppUi, priorArtifact) ts.End(err) if err != nil { errors = append(errors, fmt.Errorf("Post-processor failed: %s", err)) @@ -308,7 +308,7 @@ PostProcessorRunSeqLoop: log.Printf("The %s post-processor forces "+ "keep_input_artifact=true to preserve integrity of the"+ "build chain. User-set keep_input_artifact=false will be"+ - "ignored.", corePP.processorType) + "ignored.", corePP.PType) } else { // User overrides default. keep = *corePP.keepInputArtifact @@ -321,7 +321,7 @@ PostProcessorRunSeqLoop: if !keepOriginalArtifact && keep { log.Printf( "Flagging to keep original artifact from post-processor '%s'", - corePP.processorType) + corePP.PType) keepOriginalArtifact = true } } else { @@ -330,10 +330,10 @@ PostProcessorRunSeqLoop: if keep { artifacts = append(artifacts, priorArtifact) } else { - log.Printf("Deleting prior artifact from post-processor '%s'", corePP.processorType) + log.Printf("Deleting prior artifact from post-processor '%s'", corePP.PType) if err := priorArtifact.Destroy(); err != nil { log.Printf("Error is %#v", err) - errors = append(errors, fmt.Errorf("Failed cleaning up prior artifact: %s; pp is %s", err, corePP.processorType)) + errors = append(errors, fmt.Errorf("Failed cleaning up prior artifact: %s; pp is %s", err, corePP.PType)) } } } @@ -352,7 +352,7 @@ PostProcessorRunSeqLoop: copy(artifacts[1:], artifacts) artifacts[0] = builderArtifact } else { - log.Printf("Deleting original artifact for build '%s'", b.name) + log.Printf("Deleting original artifact for build '%s'", b.Type) if err := builderArtifact.Destroy(); err != nil { errors = append(errors, fmt.Errorf("Error destroying builder artifact: %s; bad artifact: %#v", err, builderArtifact.Files())) } @@ -365,7 +365,7 @@ PostProcessorRunSeqLoop: return artifacts, err } -func (b *coreBuild) SetDebug(val bool) { +func (b *CoreBuild) SetDebug(val bool) { if b.prepareCalled { panic("prepare has already been called") } @@ -373,7 +373,7 @@ func (b *coreBuild) SetDebug(val bool) { b.debug = val } -func (b *coreBuild) SetForce(val bool) { +func (b *CoreBuild) SetForce(val bool) { if b.prepareCalled { panic("prepare has already been called") } @@ -381,7 +381,7 @@ func (b *coreBuild) SetForce(val bool) { b.force = val } -func (b *coreBuild) SetOnError(val string) { +func (b *CoreBuild) SetOnError(val string) { if b.prepareCalled { panic("prepare has already been called") } diff --git a/packer/build_test.go b/packer/build_test.go index 0c58b405d..2168214f2 100644 --- a/packer/build_test.go +++ b/packer/build_test.go @@ -10,24 +10,24 @@ func boolPointer(tf bool) *bool { return &tf } -func testBuild() *coreBuild { - return &coreBuild{ - name: "test", - builder: &MockBuilder{ArtifactId: "b"}, - builderConfig: 42, - builderType: "foo", +func testBuild() *CoreBuild { + return &CoreBuild{ + Type: "test", + Builder: &MockBuilder{ArtifactId: "b"}, + BuilderConfig: 42, + BuilderType: "foo", hooks: map[string][]Hook{ "foo": {&MockHook{}}, }, - provisioners: []coreBuildProvisioner{ + Provisioners: []CoreBuildProvisioner{ {"mock-provisioner", &MockProvisioner{}, []interface{}{42}}, }, - postProcessors: [][]coreBuildPostProcessor{ + PostProcessors: [][]CoreBuildPostProcessor{ { {&MockPostProcessor{ArtifactId: "pp"}, "testPP", make(map[string]interface{}), boolPointer(true)}, }, }, - variables: make(map[string]string), + Variables: make(map[string]string), onError: "cleanup", } } @@ -54,7 +54,7 @@ func TestBuild_Prepare(t *testing.T) { packerConfig := testDefaultPackerConfig() build := testBuild() - builder := build.builder.(*MockBuilder) + builder := build.Builder.(*MockBuilder) build.Prepare() if !builder.PrepareCalled { @@ -64,8 +64,8 @@ func TestBuild_Prepare(t *testing.T) { t.Fatalf("bad: %#v", builder.PrepareConfig) } - coreProv := build.provisioners[0] - prov := coreProv.provisioner.(*MockProvisioner) + coreProv := build.Provisioners[0] + prov := coreProv.Provisioner.(*MockProvisioner) if !prov.PrepCalled { t.Fatal("prep should be called") } @@ -73,8 +73,8 @@ func TestBuild_Prepare(t *testing.T) { t.Fatalf("bad: %#v", prov.PrepConfigs) } - corePP := build.postProcessors[0][0] - pp := corePP.processor.(*MockPostProcessor) + corePP := build.PostProcessors[0][0] + pp := corePP.PostProcessor.(*MockPostProcessor) if !pp.ConfigureCalled { t.Fatal("should be called") } @@ -111,7 +111,7 @@ func TestBuildPrepare_BuilderWarnings(t *testing.T) { expected := []string{"foo"} build := testBuild() - builder := build.builder.(*MockBuilder) + builder := build.Builder.(*MockBuilder) builder.PrepareWarnings = expected warn, err := build.Prepare() @@ -128,7 +128,7 @@ func TestBuild_Prepare_Debug(t *testing.T) { packerConfig[DebugConfigKey] = true build := testBuild() - builder := build.builder.(*MockBuilder) + builder := build.Builder.(*MockBuilder) build.SetDebug(true) build.Prepare() @@ -139,8 +139,8 @@ func TestBuild_Prepare_Debug(t *testing.T) { t.Fatalf("bad: %#v", builder.PrepareConfig) } - coreProv := build.provisioners[0] - prov := coreProv.provisioner.(*MockProvisioner) + coreProv := build.Provisioners[0] + prov := coreProv.Provisioner.(*MockProvisioner) if !prov.PrepCalled { t.Fatal("prepare should be called") } @@ -156,8 +156,8 @@ func TestBuildPrepare_variables_default(t *testing.T) { } build := testBuild() - build.variables["foo"] = "bar" - builder := build.builder.(*MockBuilder) + build.Variables["foo"] = "bar" + builder := build.Builder.(*MockBuilder) warn, err := build.Prepare() if len(warn) > 0 { @@ -191,7 +191,7 @@ func TestBuild_Run(t *testing.T) { } // Verify builder was run - builder := build.builder.(*MockBuilder) + builder := build.Builder.(*MockBuilder) if !builder.RunCalled { t.Fatal("should be called") } @@ -210,13 +210,13 @@ func TestBuild_Run(t *testing.T) { // Verify provisioners run dispatchHook.Run(ctx, HookProvision, nil, new(MockCommunicator), 42) - prov := build.provisioners[0].provisioner.(*MockProvisioner) + prov := build.Provisioners[0].Provisioner.(*MockProvisioner) if !prov.ProvCalled { t.Fatal("should be called") } // Verify post-processor was run - pp := build.postProcessors[0][0].processor.(*MockPostProcessor) + pp := build.PostProcessors[0][0].PostProcessor.(*MockPostProcessor) if !pp.PostProcessCalled { t.Fatal("should be called") } @@ -228,7 +228,7 @@ func TestBuild_Run_Artifacts(t *testing.T) { // Test case: Test that with no post-processors, we only get the // main build. build := testBuild() - build.postProcessors = [][]coreBuildPostProcessor{} + build.PostProcessors = [][]CoreBuildPostProcessor{} build.Prepare() artifacts, err := build.Run(context.Background(), ui) @@ -249,7 +249,7 @@ func TestBuild_Run_Artifacts(t *testing.T) { // Test case: Test that with a single post-processor that doesn't keep // inputs, only that post-processors results are returned. build = testBuild() - build.postProcessors = [][]coreBuildPostProcessor{ + build.PostProcessors = [][]CoreBuildPostProcessor{ { {&MockPostProcessor{ArtifactId: "pp"}, "pp", make(map[string]interface{}), boolPointer(false)}, }, @@ -274,7 +274,7 @@ func TestBuild_Run_Artifacts(t *testing.T) { // Test case: Test that with multiple post-processors, as long as one // keeps the original, the original is kept. build = testBuild() - build.postProcessors = [][]coreBuildPostProcessor{ + build.PostProcessors = [][]CoreBuildPostProcessor{ { {&MockPostProcessor{ArtifactId: "pp1"}, "pp", make(map[string]interface{}), boolPointer(false)}, }, @@ -302,7 +302,7 @@ func TestBuild_Run_Artifacts(t *testing.T) { // Test case: Test that with sequences, intermediaries are kept if they // want to be. build = testBuild() - build.postProcessors = [][]coreBuildPostProcessor{ + build.PostProcessors = [][]CoreBuildPostProcessor{ { {&MockPostProcessor{ArtifactId: "pp1a"}, "pp", make(map[string]interface{}), boolPointer(false)}, {&MockPostProcessor{ArtifactId: "pp1b"}, "pp", make(map[string]interface{}), boolPointer(true)}, @@ -332,7 +332,7 @@ func TestBuild_Run_Artifacts(t *testing.T) { // Test case: Test that with a single post-processor that forcibly // keeps inputs, that the artifacts are kept. build = testBuild() - build.postProcessors = [][]coreBuildPostProcessor{ + build.PostProcessors = [][]CoreBuildPostProcessor{ { { &MockPostProcessor{ArtifactId: "pp", Keep: true, ForceOverride: true}, "pp", make(map[string]interface{}), boolPointer(false), @@ -360,7 +360,7 @@ func TestBuild_Run_Artifacts(t *testing.T) { // Test case: Test that with a single post-processor that non-forcibly // keeps inputs, that the artifacts are discarded if user overrides. build = testBuild() - build.postProcessors = [][]coreBuildPostProcessor{ + build.PostProcessors = [][]CoreBuildPostProcessor{ { { &MockPostProcessor{ArtifactId: "pp", Keep: true, ForceOverride: false}, "pp", make(map[string]interface{}), boolPointer(false), @@ -387,7 +387,7 @@ func TestBuild_Run_Artifacts(t *testing.T) { // Test case: Test that with a single post-processor that non-forcibly // keeps inputs, that the artifacts are kept if user does not have preference. build = testBuild() - build.postProcessors = [][]coreBuildPostProcessor{ + build.PostProcessors = [][]CoreBuildPostProcessor{ { { &MockPostProcessor{ArtifactId: "pp", Keep: true, ForceOverride: false}, "pp", make(map[string]interface{}), nil, @@ -434,7 +434,7 @@ func TestBuild_Cancel(t *testing.T) { topCtx, topCtxCancel := context.WithCancel(context.Background()) - builder := build.builder.(*MockBuilder) + builder := build.Builder.(*MockBuilder) builder.RunFn = func(ctx context.Context) { topCtxCancel() diff --git a/packer/builder.go b/packer/builder.go index e24073128..19bfc7673 100644 --- a/packer/builder.go +++ b/packer/builder.go @@ -1,6 +1,8 @@ package packer -import "context" +import ( + "context" +) // Implementers of Builder are responsible for actually building images // on some platform given some configuration. @@ -13,6 +15,8 @@ import "context" // parallelism is strictly disabled, so it is safe to request input from // stdin and so on. type Builder interface { + HCL2Speccer + // Prepare is responsible for configuring the builder and validating // that configuration. Any setup should be done in this method. Note that // NO side effects should take place in prepare, it is meant as a state diff --git a/packer/builder_mock.go b/packer/builder_mock.go index 5ebd2d993..2fee11c77 100644 --- a/packer/builder_mock.go +++ b/packer/builder_mock.go @@ -1,8 +1,12 @@ +//go:generate mapstructure-to-hcl2 -type MockBuilder,MockCommunicator,RemoteCmd,MockProvisioner,MockPostProcessor + package packer import ( "context" "errors" + + "github.com/hashicorp/hcl/v2/hcldec" ) // MockBuilder is an implementation of Builder that can be used for tests. @@ -23,6 +27,10 @@ type MockBuilder struct { RunFn func(ctx context.Context) } +func (tb *MockBuilder) ConfigSpec() hcldec.ObjectSpec { return tb.FlatMapstructure().HCL2Spec() } + +func (tb *MockBuilder) FlatConfig() interface{} { return tb.FlatMapstructure() } + func (tb *MockBuilder) Prepare(config ...interface{}) ([]string, error) { tb.PrepareCalled = true tb.PrepareConfig = config diff --git a/packer/builder_mock.hcl2spec.go b/packer/builder_mock.hcl2spec.go new file mode 100644 index 000000000..0baca99da --- /dev/null +++ b/packer/builder_mock.hcl2spec.go @@ -0,0 +1,207 @@ +// Code generated by "mapstructure-to-hcl2 -type MockBuilder,MockCommunicator,RemoteCmd,MockProvisioner,MockPostProcessor"; DO NOT EDIT. +package packer + +import ( + "github.com/hashicorp/hcl/v2/hcldec" + "github.com/zclconf/go-cty/cty" + "io" +) + +// FlatMockBuilder is an auto-generated flat version of MockBuilder. +// Where the contents of a field with a `mapstructure:,squash` tag are bubbled up. +type FlatMockBuilder struct { + ArtifactId *string `cty:"artifact_id"` + PrepareWarnings []string `cty:"prepare_warnings"` + RunErrResult *bool `cty:"run_err_result"` + RunNilResult *bool `cty:"run_nil_result"` + PrepareCalled *bool `cty:"prepare_called"` + PrepareConfig []interface{} `cty:"prepare_config"` + RunCalled *bool `cty:"run_called"` + RunHook Hook `cty:"run_hook"` + RunUi Ui `cty:"run_ui"` + CancelCalled *bool `cty:"cancel_called"` +} + +// FlatMapstructure returns a new FlatMockBuilder. +// FlatMockBuilder is an auto-generated flat version of MockBuilder. +// Where the contents a fields with a `mapstructure:,squash` tag are bubbled up. +func (*MockBuilder) FlatMapstructure() interface{ HCL2Spec() map[string]hcldec.Spec } { + return new(FlatMockBuilder) +} + +// HCL2Spec returns the hcl spec of a MockBuilder. +// This spec is used by HCL to read the fields of MockBuilder. +// The decoded values from this spec will then be applied to a FlatMockBuilder. +func (*FlatMockBuilder) HCL2Spec() map[string]hcldec.Spec { + s := map[string]hcldec.Spec{ + "artifact_id": &hcldec.AttrSpec{Name: "artifact_id", Type: cty.String, Required: false}, + "prepare_warnings": &hcldec.AttrSpec{Name: "prepare_warnings", Type: cty.List(cty.String), Required: false}, + "run_err_result": &hcldec.AttrSpec{Name: "run_err_result", Type: cty.Bool, Required: false}, + "run_nil_result": &hcldec.AttrSpec{Name: "run_nil_result", Type: cty.Bool, Required: false}, + "prepare_called": &hcldec.AttrSpec{Name: "prepare_called", Type: cty.Bool, Required: false}, + "prepare_config": &hcldec.AttrSpec{Name: "prepare_config", Type: cty.Bool, Required: false}, /* TODO(azr): could not find type */ + "run_called": &hcldec.AttrSpec{Name: "run_called", Type: cty.Bool, Required: false}, + "run_hook": &hcldec.AttrSpec{Name: "Hook", Type: cty.Bool, Required: false}, /* TODO(azr): could not find type */ + "run_ui": &hcldec.AttrSpec{Name: "Ui", Type: cty.Bool, Required: false}, /* TODO(azr): could not find type */ + "cancel_called": &hcldec.AttrSpec{Name: "cancel_called", Type: cty.Bool, Required: false}, + } + return s +} + +// FlatMockCommunicator is an auto-generated flat version of MockCommunicator. +// Where the contents of a field with a `mapstructure:,squash` tag are bubbled up. +type FlatMockCommunicator struct { + StartCalled *bool `cty:"start_called"` + StartCmd *FlatRemoteCmd `cty:"start_cmd"` + StartStderr *string `cty:"start_stderr"` + StartStdout *string `cty:"start_stdout"` + StartStdin *string `cty:"start_stdin"` + StartExitStatus *int `cty:"start_exit_status"` + UploadCalled *bool `cty:"upload_called"` + UploadPath *string `cty:"upload_path"` + UploadData *string `cty:"upload_data"` + UploadDirDst *string `cty:"upload_dir_dst"` + UploadDirSrc *string `cty:"upload_dir_src"` + UploadDirExclude []string `cty:"upload_dir_exclude"` + DownloadDirDst *string `cty:"download_dir_dst"` + DownloadDirSrc *string `cty:"download_dir_src"` + DownloadDirExclude []string `cty:"download_dir_exclude"` + DownloadCalled *bool `cty:"download_called"` + DownloadPath *string `cty:"download_path"` + DownloadData *string `cty:"download_data"` +} + +// FlatMapstructure returns a new FlatMockCommunicator. +// FlatMockCommunicator is an auto-generated flat version of MockCommunicator. +// Where the contents a fields with a `mapstructure:,squash` tag are bubbled up. +func (*MockCommunicator) FlatMapstructure() interface{ HCL2Spec() map[string]hcldec.Spec } { + return new(FlatMockCommunicator) +} + +// HCL2Spec returns the hcl spec of a MockCommunicator. +// This spec is used by HCL to read the fields of MockCommunicator. +// The decoded values from this spec will then be applied to a FlatMockCommunicator. +func (*FlatMockCommunicator) HCL2Spec() map[string]hcldec.Spec { + s := map[string]hcldec.Spec{ + "start_called": &hcldec.AttrSpec{Name: "start_called", Type: cty.Bool, Required: false}, + "start_cmd": &hcldec.BlockSpec{TypeName: "start_cmd", Nested: hcldec.ObjectSpec((*FlatRemoteCmd)(nil).HCL2Spec())}, + "start_stderr": &hcldec.AttrSpec{Name: "start_stderr", Type: cty.String, Required: false}, + "start_stdout": &hcldec.AttrSpec{Name: "start_stdout", Type: cty.String, Required: false}, + "start_stdin": &hcldec.AttrSpec{Name: "start_stdin", Type: cty.String, Required: false}, + "start_exit_status": &hcldec.AttrSpec{Name: "start_exit_status", Type: cty.Number, Required: false}, + "upload_called": &hcldec.AttrSpec{Name: "upload_called", Type: cty.Bool, Required: false}, + "upload_path": &hcldec.AttrSpec{Name: "upload_path", Type: cty.String, Required: false}, + "upload_data": &hcldec.AttrSpec{Name: "upload_data", Type: cty.String, Required: false}, + "upload_dir_dst": &hcldec.AttrSpec{Name: "upload_dir_dst", Type: cty.String, Required: false}, + "upload_dir_src": &hcldec.AttrSpec{Name: "upload_dir_src", Type: cty.String, Required: false}, + "upload_dir_exclude": &hcldec.AttrSpec{Name: "upload_dir_exclude", Type: cty.List(cty.String), Required: false}, + "download_dir_dst": &hcldec.AttrSpec{Name: "download_dir_dst", Type: cty.String, Required: false}, + "download_dir_src": &hcldec.AttrSpec{Name: "download_dir_src", Type: cty.String, Required: false}, + "download_dir_exclude": &hcldec.AttrSpec{Name: "download_dir_exclude", Type: cty.List(cty.String), Required: false}, + "download_called": &hcldec.AttrSpec{Name: "download_called", Type: cty.Bool, Required: false}, + "download_path": &hcldec.AttrSpec{Name: "download_path", Type: cty.String, Required: false}, + "download_data": &hcldec.AttrSpec{Name: "download_data", Type: cty.String, Required: false}, + } + return s +} + +// FlatMockPostProcessor is an auto-generated flat version of MockPostProcessor. +// Where the contents of a field with a `mapstructure:,squash` tag are bubbled up. +type FlatMockPostProcessor struct { + ArtifactId *string `cty:"artifact_id"` + Keep *bool `cty:"keep"` + ForceOverride *bool `cty:"force_override"` + Error error `cty:"error"` + ConfigureCalled *bool `cty:"configure_called"` + ConfigureConfigs []interface{} `cty:"configure_configs"` + ConfigureError error `cty:"configure_error"` + PostProcessCalled *bool `cty:"post_process_called"` + PostProcessArtifact Artifact `cty:"post_process_artifact"` + PostProcessUi Ui `cty:"post_process_ui"` +} + +// FlatMapstructure returns a new FlatMockPostProcessor. +// FlatMockPostProcessor is an auto-generated flat version of MockPostProcessor. +// Where the contents a fields with a `mapstructure:,squash` tag are bubbled up. +func (*MockPostProcessor) FlatMapstructure() interface{ HCL2Spec() map[string]hcldec.Spec } { + return new(FlatMockPostProcessor) +} + +// HCL2Spec returns the hcl spec of a MockPostProcessor. +// This spec is used by HCL to read the fields of MockPostProcessor. +// The decoded values from this spec will then be applied to a FlatMockPostProcessor. +func (*FlatMockPostProcessor) HCL2Spec() map[string]hcldec.Spec { + s := map[string]hcldec.Spec{ + "artifact_id": &hcldec.AttrSpec{Name: "artifact_id", Type: cty.String, Required: false}, + "keep": &hcldec.AttrSpec{Name: "keep", Type: cty.Bool, Required: false}, + "force_override": &hcldec.AttrSpec{Name: "force_override", Type: cty.Bool, Required: false}, + "error": &hcldec.AttrSpec{Name: "error", Type: cty.Bool, Required: false}, /* TODO(azr): could not find type */ + "configure_called": &hcldec.AttrSpec{Name: "configure_called", Type: cty.Bool, Required: false}, + "configure_configs": &hcldec.AttrSpec{Name: "configure_configs", Type: cty.Bool, Required: false}, /* TODO(azr): could not find type */ + "configure_error": &hcldec.AttrSpec{Name: "error", Type: cty.Bool, Required: false}, /* TODO(azr): could not find type */ + "post_process_called": &hcldec.AttrSpec{Name: "post_process_called", Type: cty.Bool, Required: false}, + "post_process_artifact": &hcldec.AttrSpec{Name: "Artifact", Type: cty.Bool, Required: false}, /* TODO(azr): could not find type */ + "post_process_ui": &hcldec.AttrSpec{Name: "Ui", Type: cty.Bool, Required: false}, /* TODO(azr): could not find type */ + } + return s +} + +// FlatMockProvisioner is an auto-generated flat version of MockProvisioner. +// Where the contents of a field with a `mapstructure:,squash` tag are bubbled up. +type FlatMockProvisioner struct { + PrepCalled *bool `cty:"prep_called"` + PrepConfigs []interface{} `cty:"prep_configs"` + ProvCalled *bool `cty:"prov_called"` + ProvCommunicator Communicator `cty:"prov_communicator"` + ProvUi Ui `cty:"prov_ui"` +} + +// FlatMapstructure returns a new FlatMockProvisioner. +// FlatMockProvisioner is an auto-generated flat version of MockProvisioner. +// Where the contents a fields with a `mapstructure:,squash` tag are bubbled up. +func (*MockProvisioner) FlatMapstructure() interface{ HCL2Spec() map[string]hcldec.Spec } { + return new(FlatMockProvisioner) +} + +// HCL2Spec returns the hcl spec of a MockProvisioner. +// This spec is used by HCL to read the fields of MockProvisioner. +// The decoded values from this spec will then be applied to a FlatMockProvisioner. +func (*FlatMockProvisioner) HCL2Spec() map[string]hcldec.Spec { + s := map[string]hcldec.Spec{ + "prep_called": &hcldec.AttrSpec{Name: "prep_called", Type: cty.Bool, Required: false}, + "prep_configs": &hcldec.AttrSpec{Name: "prep_configs", Type: cty.Bool, Required: false}, /* TODO(azr): could not find type */ + "prov_called": &hcldec.AttrSpec{Name: "prov_called", Type: cty.Bool, Required: false}, + "prov_communicator": &hcldec.AttrSpec{Name: "Communicator", Type: cty.Bool, Required: false}, /* TODO(azr): could not find type */ + "prov_ui": &hcldec.AttrSpec{Name: "Ui", Type: cty.Bool, Required: false}, /* TODO(azr): could not find type */ + } + return s +} + +// FlatRemoteCmd is an auto-generated flat version of RemoteCmd. +// Where the contents of a field with a `mapstructure:,squash` tag are bubbled up. +type FlatRemoteCmd struct { + Command *string `cty:"command"` + Stdin io.Reader `cty:"stdin"` + Stdout io.Writer `cty:"stdout"` + Stderr io.Writer `cty:"stderr"` +} + +// FlatMapstructure returns a new FlatRemoteCmd. +// FlatRemoteCmd is an auto-generated flat version of RemoteCmd. +// Where the contents a fields with a `mapstructure:,squash` tag are bubbled up. +func (*RemoteCmd) FlatMapstructure() interface{ HCL2Spec() map[string]hcldec.Spec } { + return new(FlatRemoteCmd) +} + +// HCL2Spec returns the hcl spec of a RemoteCmd. +// This spec is used by HCL to read the fields of RemoteCmd. +// The decoded values from this spec will then be applied to a FlatRemoteCmd. +func (*FlatRemoteCmd) HCL2Spec() map[string]hcldec.Spec { + s := map[string]hcldec.Spec{ + "command": &hcldec.AttrSpec{Name: "command", Type: cty.String, Required: false}, + "stdin": &hcldec.AttrSpec{Name: "io.Reader", Type: cty.Bool, Required: false}, /* TODO(azr): could not find type */ + "stdout": &hcldec.AttrSpec{Name: "io.Writer", Type: cty.Bool, Required: false}, /* TODO(azr): could not find type */ + "stderr": &hcldec.AttrSpec{Name: "io.Writer", Type: cty.Bool, Required: false}, /* TODO(azr): could not find type */ + } + return s +} diff --git a/packer/communicator.go b/packer/communicator.go index aef1775a0..026737d4b 100644 --- a/packer/communicator.go +++ b/packer/communicator.go @@ -37,7 +37,7 @@ type RemoteCmd struct { exitStatus int // This thing is a mutex, lock when making modifications concurrently - sync.Mutex + m sync.Mutex exitChInit sync.Once exitCh chan interface{} @@ -80,6 +80,11 @@ type Communicator interface { DownloadDir(src string, dst string, exclude []string) error } +type ConfigurableCommunicator interface { + HCL2Speccer + Configure(...interface{}) ([]string, error) +} + // RunWithUi runs the remote command and streams the output to any configured // Writers for stdout/stderr, while also writing each line as it comes to a Ui. // RunWithUi will not return until the command finishes or is cancelled. @@ -95,8 +100,8 @@ func (r *RemoteCmd) RunWithUi(ctx context.Context, c Communicator, ui Ui) error originalStdout := r.Stdout originalStderr := r.Stderr defer func() { - r.Lock() - defer r.Unlock() + r.m.Lock() + defer r.m.Unlock() r.Stdout = originalStdout r.Stderr = originalStderr @@ -169,9 +174,9 @@ OutputLoop: func (r *RemoteCmd) SetExited(status int) { r.initchan() - r.Lock() + r.m.Lock() r.exitStatus = status - r.Unlock() + r.m.Unlock() close(r.exitCh) } @@ -180,8 +185,8 @@ func (r *RemoteCmd) SetExited(status int) { func (r *RemoteCmd) Wait() int { r.initchan() <-r.exitCh - r.Lock() - defer r.Unlock() + r.m.Lock() + defer r.m.Unlock() return r.exitStatus } diff --git a/packer/config_file.go b/packer/config_file.go index 173766afa..bf6c8637c 100644 --- a/packer/config_file.go +++ b/packer/config_file.go @@ -24,12 +24,10 @@ func ConfigDir() (string, error) { func homeDir() (string, error) { // Prefer $HOME over user.Current due to glibc bug: golang.org/issue/13470 if home := os.Getenv("HOME"); home != "" { - log.Printf("Detected home directory from env var: %s", home) return home, nil } if home := os.Getenv("APPDATA"); home != "" { - log.Printf("Detected home directory from env var: %s", home) return home, nil } diff --git a/packer/core.go b/packer/core.go index 45912f070..46e6ad8eb 100644 --- a/packer/core.go +++ b/packer/core.go @@ -54,14 +54,36 @@ type PostProcessorFunc func(name string) (PostProcessor, error) // The function type used to lookup Provisioner implementations. type ProvisionerFunc func(name string) (Provisioner, error) +type BasicStore interface { + Has(name string) bool + List() (names []string) +} + +type BuilderStore interface { + BasicStore + Start(name string) (Builder, error) +} + +type ProvisionerStore interface { + BasicStore + Start(name string) (Provisioner, error) +} + +type PostProcessorStore interface { + BasicStore + Start(name string) (PostProcessor, error) +} + // ComponentFinder is a struct that contains the various function // pointers necessary to look up components of Packer such as builders, // commands, etc. type ComponentFinder struct { - Builder BuilderFunc - Hook HookFunc - PostProcessor PostProcessorFunc - Provisioner ProvisionerFunc + Hook HookFunc + + // For HCL2 + BuilderStore BuilderStore + ProvisionerStore ProvisionerStore + PostProcessorStore PostProcessorStore } // NewCore creates a new Core. @@ -112,10 +134,10 @@ func (c *Core) BuildNames() []string { return r } -func (c *Core) generateCoreBuildProvisioner(rawP *template.Provisioner, rawName string) (coreBuildProvisioner, error) { +func (c *Core) generateCoreBuildProvisioner(rawP *template.Provisioner, rawName string) (CoreBuildProvisioner, error) { // Get the provisioner - cbp := coreBuildProvisioner{} - provisioner, err := c.components.Provisioner(rawP.Type) + cbp := CoreBuildProvisioner{} + provisioner, err := c.components.ProvisionerStore.Start(rawP.Type) if err != nil { return cbp, fmt.Errorf( "error initializing provisioner '%s': %s", @@ -146,9 +168,9 @@ func (c *Core) generateCoreBuildProvisioner(rawP *template.Provisioner, rawName Provisioner: provisioner, } } - cbp = coreBuildProvisioner{ - pType: rawP.Type, - provisioner: provisioner, + cbp = CoreBuildProvisioner{ + PType: rawP.Type, + Provisioner: provisioner, config: config, } @@ -162,7 +184,7 @@ func (c *Core) Build(n string) (Build, error) { if !ok { return nil, fmt.Errorf("no such build found: %s", n) } - builder, err := c.components.Builder(configBuilder.Type) + builder, err := c.components.BuilderStore.Start(configBuilder.Type) if err != nil { return nil, fmt.Errorf( "error initializing builder '%s': %s", @@ -177,7 +199,7 @@ func (c *Core) Build(n string) (Build, error) { rawName := configBuilder.Name // Setup the provisioners for this build - provisioners := make([]coreBuildProvisioner, 0, len(c.Template.Provisioners)) + provisioners := make([]CoreBuildProvisioner, 0, len(c.Template.Provisioners)) for _, rawP := range c.Template.Provisioners { // If we're skipping this, then ignore it if rawP.OnlyExcept.Skip(rawName) { @@ -191,7 +213,7 @@ func (c *Core) Build(n string) (Build, error) { provisioners = append(provisioners, cbp) } - var cleanupProvisioner coreBuildProvisioner + var cleanupProvisioner CoreBuildProvisioner if c.Template.CleanupProvisioner != nil { // This is a special instantiation of the shell-local provisioner that // is only run on error at end of provisioning step before other step @@ -203,9 +225,9 @@ func (c *Core) Build(n string) (Build, error) { } // Setup the post-processors - postProcessors := make([][]coreBuildPostProcessor, 0, len(c.Template.PostProcessors)) + postProcessors := make([][]CoreBuildPostProcessor, 0, len(c.Template.PostProcessors)) for _, rawPs := range c.Template.PostProcessors { - current := make([]coreBuildPostProcessor, 0, len(rawPs)) + current := make([]CoreBuildPostProcessor, 0, len(rawPs)) for _, rawP := range rawPs { if rawP.Skip(rawName) { continue @@ -222,7 +244,7 @@ func (c *Core) Build(n string) (Build, error) { } // Get the post-processor - postProcessor, err := c.components.PostProcessor(rawP.Type) + postProcessor, err := c.components.PostProcessorStore.Start(rawP.Type) if err != nil { return nil, fmt.Errorf( "error initializing post-processor '%s': %s", @@ -233,9 +255,9 @@ func (c *Core) Build(n string) (Build, error) { "post-processor type not found: %s", rawP.Type) } - current = append(current, coreBuildPostProcessor{ - processor: postProcessor, - processorType: rawP.Type, + current = append(current, CoreBuildPostProcessor{ + PostProcessor: postProcessor, + PType: rawP.Type, config: rawP.Config, keepInputArtifact: rawP.KeepInputArtifact, }) @@ -251,16 +273,16 @@ func (c *Core) Build(n string) (Build, error) { // TODO hooks one day - return &coreBuild{ - name: n, - builder: builder, - builderConfig: configBuilder.Config, - builderType: configBuilder.Type, - postProcessors: postProcessors, - provisioners: provisioners, - cleanupProvisioner: cleanupProvisioner, - templatePath: c.Template.Path, - variables: c.variables, + return &CoreBuild{ + Type: n, + Builder: builder, + BuilderConfig: configBuilder.Config, + BuilderType: configBuilder.Type, + PostProcessors: postProcessors, + Provisioners: provisioners, + CleanupProvisioner: cleanupProvisioner, + TemplatePath: c.Template.Path, + Variables: c.variables, }, nil } diff --git a/packer/hcl2spec.go b/packer/hcl2spec.go new file mode 100644 index 000000000..bbba47ec3 --- /dev/null +++ b/packer/hcl2spec.go @@ -0,0 +1,12 @@ +package packer + +import "github.com/hashicorp/hcl/v2/hcldec" + +// a struct (or type) implementing HCL2Speccer is a type that can tell it's own +// hcl2 conf/layout. +type HCL2Speccer interface { + // ConfigSpec should return the hcl object spec used to configure the + // builder. It will be used to tell the HCL parsing library how to + // validate/configure a configuration. + ConfigSpec() hcldec.ObjectSpec +} diff --git a/packer/maps.go b/packer/maps.go new file mode 100644 index 000000000..7cad0a014 --- /dev/null +++ b/packer/maps.go @@ -0,0 +1,74 @@ +package packer + +import ( + "fmt" +) + +type MapOfProvisioner map[string]func() (Provisioner, error) + +func (mop MapOfProvisioner) Has(provisioner string) bool { + _, res := mop[provisioner] + return res +} + +func (mop MapOfProvisioner) Start(provisioner string) (Provisioner, error) { + p, found := mop[provisioner] + if !found { + return nil, fmt.Errorf("Unknown provisioner %s", provisioner) + } + return p() +} + +func (mop MapOfProvisioner) List() []string { + res := []string{} + for k := range mop { + res = append(res, k) + } + return res +} + +type MapOfPostProcessor map[string]func() (PostProcessor, error) + +func (mopp MapOfPostProcessor) Has(postProcessor string) bool { + _, res := mopp[postProcessor] + return res +} + +func (mopp MapOfPostProcessor) Start(postProcessor string) (PostProcessor, error) { + p, found := mopp[postProcessor] + if !found { + return nil, fmt.Errorf("Unknown post-processor %s", postProcessor) + } + return p() +} + +func (mopp MapOfPostProcessor) List() []string { + res := []string{} + for k := range mopp { + res = append(res, k) + } + return res +} + +type MapOfBuilder map[string]func() (Builder, error) + +func (mob MapOfBuilder) Has(builder string) bool { + _, res := mob[builder] + return res +} + +func (mob MapOfBuilder) Start(builder string) (Builder, error) { + d, found := mob[builder] + if !found { + return nil, fmt.Errorf("Unknown builder %s", builder) + } + return d() +} + +func (mob MapOfBuilder) List() []string { + res := []string{} + for k := range mob { + res = append(res, k) + } + return res +} diff --git a/packer/plugin/builder.go b/packer/plugin/builder.go index 44af7dd67..8eaa09f0b 100644 --- a/packer/plugin/builder.go +++ b/packer/plugin/builder.go @@ -4,6 +4,7 @@ import ( "context" "log" + "github.com/hashicorp/hcl/v2/hcldec" "github.com/hashicorp/packer/packer" ) @@ -12,6 +13,15 @@ type cmdBuilder struct { client *Client } +func (b *cmdBuilder) ConfigSpec() hcldec.ObjectSpec { + defer func() { + r := recover() + b.checkExit(r, nil) + }() + + return b.builder.ConfigSpec() +} + func (b *cmdBuilder) Prepare(config ...interface{}) ([]string, error) { defer func() { r := recover() diff --git a/packer/plugin/post_processor.go b/packer/plugin/post_processor.go index a08dc586a..98a956523 100644 --- a/packer/plugin/post_processor.go +++ b/packer/plugin/post_processor.go @@ -4,6 +4,7 @@ import ( "context" "log" + "github.com/hashicorp/hcl/v2/hcldec" "github.com/hashicorp/packer/packer" ) @@ -12,6 +13,15 @@ type cmdPostProcessor struct { client *Client } +func (b *cmdPostProcessor) ConfigSpec() hcldec.ObjectSpec { + defer func() { + r := recover() + b.checkExit(r, nil) + }() + + return b.p.ConfigSpec() +} + func (c *cmdPostProcessor) Configure(config ...interface{}) error { defer func() { r := recover() diff --git a/packer/plugin/post_processor_test.go b/packer/plugin/post_processor_test.go index ee012819c..b28340773 100644 --- a/packer/plugin/post_processor_test.go +++ b/packer/plugin/post_processor_test.go @@ -5,11 +5,14 @@ import ( "os/exec" "testing" + "github.com/hashicorp/hcl/v2/hcldec" "github.com/hashicorp/packer/packer" ) type helperPostProcessor byte +func (helperPostProcessor) ConfigSpec() hcldec.ObjectSpec { return nil } + func (helperPostProcessor) Configure(...interface{}) error { return nil } diff --git a/packer/plugin/provisioner.go b/packer/plugin/provisioner.go index 9c3b8fb42..fcc47aea4 100644 --- a/packer/plugin/provisioner.go +++ b/packer/plugin/provisioner.go @@ -4,6 +4,7 @@ import ( "context" "log" + "github.com/hashicorp/hcl/v2/hcldec" "github.com/hashicorp/packer/packer" ) @@ -12,6 +13,15 @@ type cmdProvisioner struct { client *Client } +func (p *cmdProvisioner) ConfigSpec() hcldec.ObjectSpec { + defer func() { + r := recover() + p.checkExit(r, nil) + }() + + return p.p.ConfigSpec() +} + func (c *cmdProvisioner) Prepare(configs ...interface{}) error { defer func() { r := recover() diff --git a/packer/plugin/server.go b/packer/plugin/server.go index e30de6fb0..839edc153 100644 --- a/packer/plugin/server.go +++ b/packer/plugin/server.go @@ -110,8 +110,7 @@ func serverListener_tcp() (net.Listener, error) { return nil, err } - log.Printf("Plugin minimum port: %d\n", minPort) - log.Printf("Plugin maximum port: %d\n", maxPort) + log.Printf("Plugin port range: [%d,%d]", minPort, maxPort) for port := minPort; port <= maxPort; port++ { address := fmt.Sprintf("127.0.0.1:%d", port) diff --git a/packer/post_processor.go b/packer/post_processor.go index c9b07e037..fd7df4e27 100644 --- a/packer/post_processor.go +++ b/packer/post_processor.go @@ -8,6 +8,8 @@ import "context" // the result of a build, compresses it, and returns a new artifact containing // a single file of the prior artifact compressed. type PostProcessor interface { + HCL2Speccer + // Configure is responsible for setting up configuration, storing // the state for later, and returning and errors, such as validation // errors. diff --git a/packer/post_processor_mock.go b/packer/post_processor_mock.go index d31fdec4b..b03abd4eb 100644 --- a/packer/post_processor_mock.go +++ b/packer/post_processor_mock.go @@ -1,6 +1,10 @@ package packer -import "context" +import ( + "context" + + "github.com/hashicorp/hcl/v2/hcldec" +) // MockPostProcessor is an implementation of PostProcessor that can be // used for tests. @@ -19,6 +23,8 @@ type MockPostProcessor struct { PostProcessUi Ui } +func (t *MockPostProcessor) ConfigSpec() hcldec.ObjectSpec { return t.FlatMapstructure().HCL2Spec() } + func (t *MockPostProcessor) Configure(configs ...interface{}) error { t.ConfigureCalled = true t.ConfigureConfigs = configs diff --git a/packer/provisioner.go b/packer/provisioner.go index ff89c0c42..98c91268f 100644 --- a/packer/provisioner.go +++ b/packer/provisioner.go @@ -6,11 +6,15 @@ import ( "log" "sync" "time" + + "github.com/hashicorp/hcl/v2/hcldec" ) // A provisioner is responsible for installing and configuring software // on a machine prior to building the actual image. type Provisioner interface { + HCL2Speccer + // Prepare is called with a set of configurations to setup the // internal state of the provisioner. The multiple configurations // should be merged in some sane way. @@ -71,6 +75,8 @@ type PausedProvisioner struct { Provisioner Provisioner } +func (p *PausedProvisioner) ConfigSpec() hcldec.ObjectSpec { return p.ConfigSpec() } +func (p *PausedProvisioner) FlatConfig() interface{} { return p.FlatConfig() } func (p *PausedProvisioner) Prepare(raws ...interface{}) error { return p.Provisioner.Prepare(raws...) } @@ -98,6 +104,8 @@ type DebuggedProvisioner struct { lock sync.Mutex } +func (p *DebuggedProvisioner) ConfigSpec() hcldec.ObjectSpec { return p.ConfigSpec() } +func (p *DebuggedProvisioner) FlatConfig() interface{} { return p.FlatConfig() } func (p *DebuggedProvisioner) Prepare(raws ...interface{}) error { return p.Provisioner.Prepare(raws...) } diff --git a/packer/provisioner_mock.go b/packer/provisioner_mock.go index 9555c5f57..2096a007d 100644 --- a/packer/provisioner_mock.go +++ b/packer/provisioner_mock.go @@ -2,6 +2,8 @@ package packer import ( "context" + + "github.com/hashicorp/hcl/v2/hcldec" ) // MockProvisioner is an implementation of Provisioner that can be @@ -16,6 +18,10 @@ type MockProvisioner struct { ProvUi Ui } +func (tp *MockProvisioner) ConfigSpec() hcldec.ObjectSpec { return tp.FlatMapstructure().HCL2Spec() } + +func (tp *MockProvisioner) FlatConfig() interface{} { return tp.FlatMapstructure() } + func (t *MockProvisioner) Prepare(configs ...interface{}) error { t.PrepCalled = true t.PrepConfigs = configs diff --git a/packer/rpc/artifact.go b/packer/rpc/artifact.go index b1166afa7..ff64920bc 100644 --- a/packer/rpc/artifact.go +++ b/packer/rpc/artifact.go @@ -1,16 +1,13 @@ package rpc import ( - "net/rpc" - "github.com/hashicorp/packer/packer" ) // An implementation of packer.Artifact where the artifact is actually // available over an RPC connection. type artifact struct { - client *rpc.Client - endpoint string + commonClient } // ArtifactServer wraps a packer.Artifact implementation and makes it diff --git a/packer/rpc/build.go b/packer/rpc/build.go index 39b31ed67..02ac0f86d 100644 --- a/packer/rpc/build.go +++ b/packer/rpc/build.go @@ -3,7 +3,6 @@ package rpc import ( "context" "log" - "net/rpc" "github.com/hashicorp/packer/packer" ) @@ -11,8 +10,7 @@ import ( // An implementation of packer.Build where the build is actually executed // over an RPC connection. type build struct { - client *rpc.Client - mux *muxBroker + commonClient } // BuildServer wraps a packer.Build implementation and makes it exportable diff --git a/packer/rpc/builder.go b/packer/rpc/builder.go index 4b95bea3e..f6f7265a5 100644 --- a/packer/rpc/builder.go +++ b/packer/rpc/builder.go @@ -3,7 +3,6 @@ package rpc import ( "context" "log" - "net/rpc" "github.com/hashicorp/packer/packer" ) @@ -11,8 +10,7 @@ import ( // An implementation of packer.Builder where the builder is actually executed // over an RPC connection. type builder struct { - client *rpc.Client - mux *muxBroker + commonClient } // BuilderServer wraps a packer.Builder implementation and makes it exportable @@ -21,8 +19,8 @@ type BuilderServer struct { context context.Context contextCancel func() + commonServer builder packer.Builder - mux *muxBroker } type BuilderPrepareArgs struct { @@ -35,12 +33,16 @@ type BuilderPrepareResponse struct { } func (b *builder) Prepare(config ...interface{}) ([]string, error) { + config, err := encodeCTYValues(config) + if err != nil { + return nil, err + } var resp BuilderPrepareResponse - cerr := b.client.Call("Builder.Prepare", &BuilderPrepareArgs{config}, &resp) + cerr := b.client.Call(b.endpoint+".Prepare", &BuilderPrepareArgs{config}, &resp) if cerr != nil { return nil, cerr } - var err error = nil + if resp.Error != nil { err = resp.Error } @@ -61,7 +63,7 @@ func (b *builder) Run(ctx context.Context, ui packer.Ui, hook packer.Hook) (pack select { case <-ctx.Done(): log.Printf("Cancelling builder after context cancellation %v", ctx.Err()) - if err := b.client.Call("Builder.Cancel", new(interface{}), new(interface{})); err != nil { + if err := b.client.Call(b.endpoint+".Cancel", new(interface{}), new(interface{})); err != nil { log.Printf("Error cancelling builder: %s", err) } case <-done: @@ -70,7 +72,7 @@ func (b *builder) Run(ctx context.Context, ui packer.Ui, hook packer.Hook) (pack var responseId uint32 - if err := b.client.Call("Builder.Run", nextId, &responseId); err != nil { + if err := b.client.Call(b.endpoint+".Run", nextId, &responseId); err != nil { return nil, err } @@ -87,7 +89,11 @@ func (b *builder) Run(ctx context.Context, ui packer.Ui, hook packer.Hook) (pack } func (b *BuilderServer) Prepare(args *BuilderPrepareArgs, reply *BuilderPrepareResponse) error { - warnings, err := b.builder.Prepare(args.Configs...) + config, err := decodeCTYValues(args.Configs) + if err != nil { + return err + } + warnings, err := b.builder.Prepare(config...) *reply = BuilderPrepareResponse{ Warnings: warnings, Error: NewBasicError(err), diff --git a/packer/rpc/client.go b/packer/rpc/client.go index 53f88d52f..e8eadfbf5 100644 --- a/packer/rpc/client.go +++ b/packer/rpc/client.go @@ -69,56 +69,79 @@ func (c *Client) Close() error { func (c *Client) Artifact() packer.Artifact { return &artifact{ - client: c.client, - endpoint: DefaultArtifactEndpoint, + commonClient: commonClient{ + endpoint: DefaultArtifactEndpoint, + client: c.client, + }, } } func (c *Client) Build() packer.Build { return &build{ - client: c.client, - mux: c.mux, + commonClient: commonClient{ + endpoint: DefaultBuildEndpoint, + client: c.client, + mux: c.mux, + }, } } func (c *Client) Builder() packer.Builder { return &builder{ - client: c.client, - mux: c.mux, + commonClient: commonClient{ + endpoint: DefaultBuilderEndpoint, + client: c.client, + mux: c.mux, + }, } } func (c *Client) Communicator() packer.Communicator { return &communicator{ - client: c.client, - mux: c.mux, + commonClient: commonClient{ + endpoint: DefaultCommunicatorEndpoint, + client: c.client, + mux: c.mux, + }, } } func (c *Client) Hook() packer.Hook { return &hook{ - client: c.client, - mux: c.mux, + commonClient: commonClient{ + endpoint: DefaultHookEndpoint, + client: c.client, + mux: c.mux, + }, } } func (c *Client) PostProcessor() packer.PostProcessor { return &postProcessor{ - client: c.client, - mux: c.mux, + commonClient: commonClient{ + endpoint: DefaultPostProcessorEndpoint, + client: c.client, + mux: c.mux, + }, } } func (c *Client) Provisioner() packer.Provisioner { return &provisioner{ - client: c.client, - mux: c.mux, + commonClient: commonClient{ + endpoint: DefaultProvisionerEndpoint, + client: c.client, + mux: c.mux, + }, } } func (c *Client) Ui() packer.Ui { return &Ui{ - client: c.client, + commonClient: commonClient{ + endpoint: DefaultUiEndpoint, + client: c.client, + }, endpoint: DefaultUiEndpoint, } } diff --git a/packer/rpc/client_test.go b/packer/rpc/client_test.go index 59a9389b9..ef91e6534 100644 --- a/packer/rpc/client_test.go +++ b/packer/rpc/client_test.go @@ -40,7 +40,10 @@ func testConn(t *testing.T) (net.Conn, net.Conn) { func testClientServer(t *testing.T) (*Client, *Server) { clientConn, serverConn := testConn(t) - server, _ := NewServer(serverConn) + server, err := NewServer(serverConn) + if err != nil { + t.Fatalf("err: %v", err) + } go server.Serve() client, err := NewClient(clientConn) diff --git a/packer/rpc/common.go b/packer/rpc/common.go new file mode 100644 index 000000000..7044ebcaf --- /dev/null +++ b/packer/rpc/common.go @@ -0,0 +1,71 @@ +package rpc + +import ( + "bytes" + "encoding/gob" + "fmt" + "net/rpc" + + "github.com/hashicorp/hcl/v2/hcldec" + "github.com/zclconf/go-cty/cty" +) + +// commonClient allows to rpc call funcs that can be defined on the different +// build blocks of packer +type commonClient struct { + // endpoint is usually the type of build block we are connecting to. + // + // eg: Provisioner / PostProcessor / Builder / Artifact / Communicator + endpoint string + client *rpc.Client + mux *muxBroker +} + +type commonServer struct { + mux *muxBroker + selfConfigurable interface { + ConfigSpec() hcldec.ObjectSpec + } +} + +type ConfigSpecResponse struct { + ConfigSpec []byte +} + +func (p *commonClient) ConfigSpec() hcldec.ObjectSpec { + // TODO(azr): the RPC Call can fail but the ConfigSpec signature doesn't + // return an error; should we simply panic ? Logging this for now; will + // decide later. The correct approach would probably be to return an error + // in ConfigSpec but that will break a lot of things. + resp := &ConfigSpecResponse{} + cerr := p.client.Call(p.endpoint+".ConfigSpec", new(interface{}), resp) + if cerr != nil { + err := fmt.Errorf("ConfigSpec failed: %v", cerr) + panic(err.Error()) + } + + res := hcldec.ObjectSpec{} + err := gob.NewDecoder(bytes.NewReader(resp.ConfigSpec)).Decode(&res) + if err != nil { + panic("ici:" + err.Error()) + } + return res +} + +func (s *commonServer) ConfigSpec(_ interface{}, reply *ConfigSpecResponse) error { + spec := s.selfConfigurable.ConfigSpec() + b := bytes.NewBuffer(nil) + err := gob.NewEncoder(b).Encode(spec) + reply.ConfigSpec = b.Bytes() + + return err +} + +func init() { + gob.Register(new(hcldec.AttrSpec)) + gob.Register(new(hcldec.BlockSpec)) + gob.Register(new(hcldec.BlockAttrsSpec)) + gob.Register(new(hcldec.BlockListSpec)) + gob.Register(new(hcldec.BlockObjectSpec)) + gob.Register(new(cty.Value)) +} diff --git a/packer/rpc/communicator.go b/packer/rpc/communicator.go index 814dc05ca..55da58f24 100644 --- a/packer/rpc/communicator.go +++ b/packer/rpc/communicator.go @@ -15,15 +15,14 @@ import ( // An implementation of packer.Communicator where the communicator is actually // executed over an RPC connection. type communicator struct { - client *rpc.Client - mux *muxBroker + commonClient } // CommunicatorServer wraps a packer.Communicator implementation and makes // it exportable as part of a Golang RPC server. type CommunicatorServer struct { - c packer.Communicator - mux *muxBroker + commonServer + c packer.Communicator } type CommandFinished struct { @@ -62,7 +61,12 @@ type CommunicatorDownloadDirArgs struct { } func Communicator(client *rpc.Client) *communicator { - return &communicator{client: client} + return &communicator{ + commonClient: commonClient{ + client: client, + endpoint: DefaultCommunicatorEndpoint, + }, + } } func (c *communicator) Start(ctx context.Context, cmd *packer.RemoteCmd) (err error) { @@ -123,7 +127,7 @@ func (c *communicator) Start(ctx context.Context, cmd *packer.RemoteCmd) (err er cmd.SetExited(finished.ExitStatus) }() - err = c.client.Call("Communicator.Start", &args, new(interface{})) + err = c.client.Call(c.endpoint+".Start", &args, new(interface{})) return } @@ -141,7 +145,7 @@ func (c *communicator) Upload(path string, r io.Reader, fi *os.FileInfo) (err er args.FileInfo = NewFileInfo(*fi) } - err = c.client.Call("Communicator.Upload", &args, new(interface{})) + err = c.client.Call(c.endpoint+".Upload", &args, new(interface{})) return } @@ -153,7 +157,7 @@ func (c *communicator) UploadDir(dst string, src string, exclude []string) error } var reply error - err := c.client.Call("Communicator.UploadDir", args, &reply) + err := c.client.Call(c.endpoint+".UploadDir", args, &reply) if err == nil { err = reply } @@ -169,7 +173,7 @@ func (c *communicator) DownloadDir(src string, dst string, exclude []string) err } var reply error - err := c.client.Call("Communicator.DownloadDir", args, &reply) + err := c.client.Call(c.endpoint+".DownloadDir", args, &reply) if err == nil { err = reply } @@ -193,7 +197,7 @@ func (c *communicator) Download(path string, w io.Writer) (err error) { } // Start sending data to the RPC server - err = c.client.Call("Communicator.Download", &args, new(interface{})) + err = c.client.Call(c.endpoint+".Download", &args, new(interface{})) // Wait for the RPC server to finish receiving the data before we return <-waitServer diff --git a/packer/rpc/cty_encode.go b/packer/rpc/cty_encode.go new file mode 100644 index 000000000..570e0ee13 --- /dev/null +++ b/packer/rpc/cty_encode.go @@ -0,0 +1,35 @@ +package rpc + +import ( + "github.com/zclconf/go-cty/cty" + "github.com/zclconf/go-cty/cty/json" +) + +// cty.Value is does not know how to encode itself through the wire so we +// transform it to bytes. +func encodeCTYValues(config []interface{}) ([]interface{}, error) { + for i := range config { + if v, ok := config[i].(cty.Value); ok { + b, err := json.Marshal(v, cty.DynamicPseudoType) + if err != nil { + return nil, err + } + config[i] = b + } + } + return config, nil +} + +// decodeCTYValues will try to decode a cty value when it finds a byte slice +func decodeCTYValues(config []interface{}) ([]interface{}, error) { + for i := range config { + if b, ok := config[i].([]byte); ok { + t, err := json.Unmarshal(b, cty.DynamicPseudoType) + if err != nil { + return nil, err + } + config[i] = t + } + } + return config, nil +} diff --git a/packer/rpc/hook.go b/packer/rpc/hook.go index c5ac329f6..12334b1d3 100644 --- a/packer/rpc/hook.go +++ b/packer/rpc/hook.go @@ -3,7 +3,6 @@ package rpc import ( "context" "log" - "net/rpc" "sync" "github.com/hashicorp/packer/packer" @@ -12,8 +11,7 @@ import ( // An implementation of packer.Hook where the hook is actually executed // over an RPC connection. type hook struct { - client *rpc.Client - mux *muxBroker + commonClient } // HookServer wraps a packer.Hook implementation and makes it exportable @@ -46,7 +44,7 @@ func (h *hook) Run(ctx context.Context, name string, ui packer.Ui, comm packer.C select { case <-ctx.Done(): log.Printf("Cancelling hook after context cancellation %v", ctx.Err()) - if err := h.client.Call("Hook.Cancel", new(interface{}), new(interface{})); err != nil { + if err := h.client.Call(h.endpoint+".Cancel", new(interface{}), new(interface{})); err != nil { log.Printf("Error cancelling builder: %s", err) } case <-done: @@ -59,7 +57,7 @@ func (h *hook) Run(ctx context.Context, name string, ui packer.Ui, comm packer.C StreamId: nextId, } - return h.client.Call("Hook.Run", &args, new(interface{})) + return h.client.Call(h.endpoint+".Run", &args, new(interface{})) } func (h *HookServer) Run(args *HookRunArgs, reply *interface{}) error { diff --git a/packer/rpc/post_processor.go b/packer/rpc/post_processor.go index 55e6363f3..4b36f2dd4 100644 --- a/packer/rpc/post_processor.go +++ b/packer/rpc/post_processor.go @@ -3,7 +3,6 @@ package rpc import ( "context" "log" - "net/rpc" "github.com/hashicorp/packer/packer" ) @@ -11,8 +10,7 @@ import ( // An implementation of packer.PostProcessor where the PostProcessor is actually // executed over an RPC connection. type postProcessor struct { - client *rpc.Client - mux *muxBroker + commonClient } // PostProcessorServer wraps a packer.PostProcessor implementation and makes it @@ -21,8 +19,8 @@ type PostProcessorServer struct { context context.Context contextCancel func() - mux *muxBroker - p packer.PostProcessor + commonServer + p packer.PostProcessor } type PostProcessorConfigureArgs struct { @@ -36,13 +34,13 @@ type PostProcessorProcessResponse struct { StreamId uint32 } -func (p *postProcessor) Configure(raw ...interface{}) (err error) { - args := &PostProcessorConfigureArgs{Configs: raw} - if cerr := p.client.Call("PostProcessor.Configure", args, new(interface{})); cerr != nil { - err = cerr +func (p *postProcessor) Configure(raw ...interface{}) error { + raw, err := encodeCTYValues(raw) + if err != nil { + return err } - - return + args := &PostProcessorConfigureArgs{Configs: raw} + return p.client.Call(p.endpoint+".Configure", args, new(interface{})) } func (p *postProcessor) PostProcess(ctx context.Context, ui packer.Ui, a packer.Artifact) (packer.Artifact, bool, bool, error) { @@ -59,7 +57,7 @@ func (p *postProcessor) PostProcess(ctx context.Context, ui packer.Ui, a packer. select { case <-ctx.Done(): log.Printf("Cancelling post-processor after context cancellation %v", ctx.Err()) - if err := p.client.Call("PostProcessor.Cancel", new(interface{}), new(interface{})); err != nil { + if err := p.client.Call(p.endpoint+".Cancel", new(interface{}), new(interface{})); err != nil { log.Printf("Error cancelling post-processor: %s", err) } case <-done: @@ -67,7 +65,7 @@ func (p *postProcessor) PostProcess(ctx context.Context, ui packer.Ui, a packer. }() var response PostProcessorProcessResponse - if err := p.client.Call("PostProcessor.PostProcess", nextId, &response); err != nil { + if err := p.client.Call(p.endpoint+".PostProcess", nextId, &response); err != nil { return nil, false, false, err } @@ -87,8 +85,12 @@ func (p *postProcessor) PostProcess(ctx context.Context, ui packer.Ui, a packer. return client.Artifact(), response.Keep, response.ForceOverride, nil } -func (p *PostProcessorServer) Configure(args *PostProcessorConfigureArgs, reply *interface{}) error { - err := p.p.Configure(args.Configs...) +func (p *PostProcessorServer) Configure(args *PostProcessorConfigureArgs, reply *interface{}) (err error) { + config, err := decodeCTYValues(args.Configs) + if err != nil { + return err + } + err = p.p.Configure(config...) return err } diff --git a/packer/rpc/post_processor_test.go b/packer/rpc/post_processor_test.go index 3338915d2..e6f7235a1 100644 --- a/packer/rpc/post_processor_test.go +++ b/packer/rpc/post_processor_test.go @@ -5,6 +5,7 @@ import ( "reflect" "testing" + "github.com/hashicorp/hcl/v2/hcldec" "github.com/hashicorp/packer/packer" ) @@ -21,6 +22,8 @@ type TestPostProcessor struct { postProcessFn func(context.Context) error } +func (*TestPostProcessor) ConfigSpec() hcldec.ObjectSpec { return nil } + func (pp *TestPostProcessor) Configure(v ...interface{}) error { pp.configCalled = true pp.configVal = v @@ -81,7 +84,7 @@ func TestPostProcessorRPC(t *testing.T) { } if p.ppArtifactId != "ppTestId" { - t.Fatalf("unknown artifact: %s", p.ppArtifact.Id()) + t.Fatalf("unknown artifact: '%s'", p.ppArtifact.Id()) } if artifact.Id() != "id" { diff --git a/packer/rpc/provisioner.go b/packer/rpc/provisioner.go index faad6f434..3ef1ea6b4 100644 --- a/packer/rpc/provisioner.go +++ b/packer/rpc/provisioner.go @@ -3,7 +3,6 @@ package rpc import ( "context" "log" - "net/rpc" "github.com/hashicorp/packer/packer" ) @@ -11,8 +10,7 @@ import ( // An implementation of packer.Provisioner where the provisioner is actually // executed over an RPC connection. type provisioner struct { - client *rpc.Client - mux *muxBroker + commonClient } // ProvisionerServer wraps a packer.Provisioner implementation and makes it @@ -21,21 +19,21 @@ type ProvisionerServer struct { context context.Context contextCancel func() - p packer.Provisioner - mux *muxBroker + commonServer + p packer.Provisioner } type ProvisionerPrepareArgs struct { Configs []interface{} } -func (p *provisioner) Prepare(configs ...interface{}) (err error) { - args := &ProvisionerPrepareArgs{configs} - if cerr := p.client.Call("Provisioner.Prepare", args, new(interface{})); cerr != nil { - err = cerr +func (p *provisioner) Prepare(configs ...interface{}) error { + configs, err := encodeCTYValues(configs) + if err != nil { + return err } - - return + args := &ProvisionerPrepareArgs{configs} + return p.client.Call(p.endpoint+".Prepare", args, new(interface{})) } func (p *provisioner) Provision(ctx context.Context, ui packer.Ui, comm packer.Communicator) error { @@ -52,18 +50,22 @@ func (p *provisioner) Provision(ctx context.Context, ui packer.Ui, comm packer.C select { case <-ctx.Done(): log.Printf("Cancelling provisioner after context cancellation %v", ctx.Err()) - if err := p.client.Call("Provisioner.Cancel", new(interface{}), new(interface{})); err != nil { + if err := p.client.Call(p.endpoint+".Cancel", new(interface{}), new(interface{})); err != nil { log.Printf("Error cancelling provisioner: %s", err) } case <-done: } }() - return p.client.Call("Provisioner.Provision", nextId, new(interface{})) + return p.client.Call(p.endpoint+".Provision", nextId, new(interface{})) } func (p *ProvisionerServer) Prepare(args *ProvisionerPrepareArgs, reply *interface{}) error { - return p.p.Prepare(args.Configs...) + config, err := decodeCTYValues(args.Configs) + if err != nil { + return err + } + return p.p.Prepare(config...) } func (p *ProvisionerServer) Provision(streamId uint32, reply *interface{}) error { diff --git a/packer/rpc/server.go b/packer/rpc/server.go index e0b90d261..b583e2945 100644 --- a/packer/rpc/server.go +++ b/packer/rpc/server.go @@ -76,15 +76,20 @@ func (s *Server) RegisterBuild(b packer.Build) error { func (s *Server) RegisterBuilder(b packer.Builder) error { return s.server.RegisterName(DefaultBuilderEndpoint, &BuilderServer{ + commonServer: commonServer{ + selfConfigurable: b, + mux: s.mux, + }, builder: b, - mux: s.mux, }) } func (s *Server) RegisterCommunicator(c packer.Communicator) error { return s.server.RegisterName(DefaultCommunicatorEndpoint, &CommunicatorServer{ - c: c, - mux: s.mux, + c: c, + commonServer: commonServer{ + mux: s.mux, + }, }) } @@ -97,15 +102,21 @@ func (s *Server) RegisterHook(h packer.Hook) error { func (s *Server) RegisterPostProcessor(p packer.PostProcessor) error { return s.server.RegisterName(DefaultPostProcessorEndpoint, &PostProcessorServer{ - mux: s.mux, - p: p, + commonServer: commonServer{ + selfConfigurable: p, + mux: s.mux, + }, + p: p, }) } func (s *Server) RegisterProvisioner(p packer.Provisioner) error { return s.server.RegisterName(DefaultProvisionerEndpoint, &ProvisionerServer{ - mux: s.mux, - p: p, + commonServer: commonServer{ + selfConfigurable: p, + mux: s.mux, + }, + p: p, }) } diff --git a/packer/rpc/ui.go b/packer/rpc/ui.go index d86301673..adcee28b5 100644 --- a/packer/rpc/ui.go +++ b/packer/rpc/ui.go @@ -2,7 +2,6 @@ package rpc import ( "log" - "net/rpc" "github.com/hashicorp/packer/packer" ) @@ -10,7 +9,7 @@ import ( // An implementation of packer.Ui where the Ui is actually executed // over an RPC connection. type Ui struct { - client *rpc.Client + commonClient endpoint string } diff --git a/packer/testing.go b/packer/testing.go index 7217a5083..000c57e70 100644 --- a/packer/testing.go +++ b/packer/testing.go @@ -9,12 +9,8 @@ import ( func TestCoreConfig(t *testing.T) *CoreConfig { // Create some test components components := ComponentFinder{ - Builder: func(n string) (Builder, error) { - if n != "test" { - return nil, nil - } - - return &MockBuilder{}, nil + BuilderStore: MapOfBuilder{ + "test": func() (Builder, error) { return &MockBuilder{}, nil }, }, } @@ -46,12 +42,8 @@ func TestUi(t *testing.T) Ui { func TestBuilder(t *testing.T, c *CoreConfig, n string) *MockBuilder { var b MockBuilder - c.Components.Builder = func(actual string) (Builder, error) { - if actual != n { - return nil, nil - } - - return &b, nil + c.Components.BuilderStore = MapOfBuilder{ + n: func() (Builder, error) { return &b, nil }, } return &b @@ -62,12 +54,8 @@ func TestBuilder(t *testing.T, c *CoreConfig, n string) *MockBuilder { func TestProvisioner(t *testing.T, c *CoreConfig, n string) *MockProvisioner { var b MockProvisioner - c.Components.Provisioner = func(actual string) (Provisioner, error) { - if actual != n { - return nil, nil - } - - return &b, nil + c.Components.ProvisionerStore = MapOfProvisioner{ + n: func() (Provisioner, error) { return &b, nil }, } return &b @@ -78,12 +66,8 @@ func TestProvisioner(t *testing.T, c *CoreConfig, n string) *MockProvisioner { func TestPostProcessor(t *testing.T, c *CoreConfig, n string) *MockPostProcessor { var b MockPostProcessor - c.Components.PostProcessor = func(actual string) (PostProcessor, error) { - if actual != n { - return nil, nil - } - - return &b, nil + c.Components.PostProcessorStore = MapOfPostProcessor{ + n: func() (PostProcessor, error) { return &b, nil }, } return &b diff --git a/post-processor/alicloud-import/post-processor.go b/post-processor/alicloud-import/post-processor.go index 42882c6df..a38ad7b13 100644 --- a/post-processor/alicloud-import/post-processor.go +++ b/post-processor/alicloud-import/post-processor.go @@ -16,6 +16,7 @@ import ( "github.com/aliyun/alibaba-cloud-sdk-go/services/ecs" "github.com/aliyun/alibaba-cloud-sdk-go/services/ram" "github.com/aliyun/aliyun-oss-go-sdk/oss" + "github.com/hashicorp/hcl/v2/hcldec" packerecs "github.com/hashicorp/packer/builder/alicloud/ecs" "github.com/hashicorp/packer/helper/config" "github.com/hashicorp/packer/packer" @@ -82,7 +83,8 @@ type PostProcessor struct { ramClient *ram.Client } -// Entry point for configuration parsing when we've defined +func (p *PostProcessor) ConfigSpec() hcldec.ObjectSpec { return p.config.FlatMapstructure().HCL2Spec() } + func (p *PostProcessor) Configure(raws ...interface{}) error { err := config.Decode(&p.config, &config.DecodeOpts{ Interpolate: true, diff --git a/post-processor/alicloud-import/post-processor.hcl2spec.go b/post-processor/alicloud-import/post-processor.hcl2spec.go index aeeed2435..d92036608 100644 --- a/post-processor/alicloud-import/post-processor.hcl2spec.go +++ b/post-processor/alicloud-import/post-processor.hcl2spec.go @@ -88,8 +88,8 @@ type FlatConfig struct { SSHReadWriteTimeout *string `mapstructure:"ssh_read_write_timeout" cty:"ssh_read_write_timeout"` SSHRemoteTunnels []string `mapstructure:"ssh_remote_tunnels" cty:"ssh_remote_tunnels"` SSHLocalTunnels []string `mapstructure:"ssh_local_tunnels" cty:"ssh_local_tunnels"` - SSHPublicKey []byte `cty:"ssh_public_key"` - SSHPrivateKey []byte `cty:"ssh_private_key"` + SSHPublicKey []byte `mapstructure:"ssh_public_key" cty:"ssh_public_key"` + SSHPrivateKey []byte `mapstructure:"ssh_private_key" cty:"ssh_private_key"` WinRMUser *string `mapstructure:"winrm_username" cty:"winrm_username"` WinRMPassword *string `mapstructure:"winrm_password" cty:"winrm_password"` WinRMHost *string `mapstructure:"winrm_host" cty:"winrm_host"` @@ -112,10 +112,13 @@ type FlatConfig struct { // FlatMapstructure returns a new FlatConfig. // FlatConfig is an auto-generated flat version of Config. // Where the contents a fields with a `mapstructure:,squash` tag are bubbled up. -func (*Config) FlatMapstructure() interface{} { return new(FlatConfig) } +func (*Config) FlatMapstructure() interface{ HCL2Spec() map[string]hcldec.Spec } { + return new(FlatConfig) +} -// HCL2Spec returns the hcldec.Spec of a FlatConfig. -// This spec is used by HCL to read the fields of FlatConfig. +// HCL2Spec returns the hcl spec of a Config. +// This spec is used by HCL to read the fields of Config. +// The decoded values from this spec will then be applied to a FlatConfig. func (*FlatConfig) HCL2Spec() map[string]hcldec.Spec { s := map[string]hcldec.Spec{ "packer_build_name": &hcldec.AttrSpec{Name: "packer_build_name", Type: cty.String, Required: false}, @@ -144,7 +147,7 @@ func (*FlatConfig) HCL2Spec() map[string]hcldec.Spec { "image_ignore_data_disks": &hcldec.AttrSpec{Name: "image_ignore_data_disks", Type: cty.Bool, Required: false}, "tags": &hcldec.BlockAttrsSpec{TypeName: "tags", ElementType: cty.String, Required: false}, "system_disk_mapping": &hcldec.BlockSpec{TypeName: "system_disk_mapping", Nested: hcldec.ObjectSpec((*ecs.FlatAlicloudDiskDevice)(nil).HCL2Spec())}, - "image_disk_mappings": &hcldec.BlockListSpec{TypeName: "image_disk_mappings", Nested: &hcldec.BlockSpec{TypeName: "image_disk_mappings", Nested: hcldec.ObjectSpec((*ecs.FlatAlicloudDiskDevice)(nil).HCL2Spec())}}, + "image_disk_mappings": &hcldec.BlockListSpec{TypeName: "image_disk_mappings", Nested: hcldec.ObjectSpec((*ecs.FlatAlicloudDiskDevice)(nil).HCL2Spec())}, "associate_public_ip_address": &hcldec.AttrSpec{Name: "associate_public_ip_address", Type: cty.Bool, Required: false}, "zone_id": &hcldec.AttrSpec{Name: "zone_id", Type: cty.String, Required: false}, "io_optimized": &hcldec.AttrSpec{Name: "io_optimized", Type: cty.Bool, Required: false}, diff --git a/post-processor/amazon-import/post-processor.go b/post-processor/amazon-import/post-processor.go index f8f055a27..f64decbe0 100644 --- a/post-processor/amazon-import/post-processor.go +++ b/post-processor/amazon-import/post-processor.go @@ -13,6 +13,7 @@ import ( "github.com/aws/aws-sdk-go/service/ec2" "github.com/aws/aws-sdk-go/service/s3" "github.com/aws/aws-sdk-go/service/s3/s3manager" + "github.com/hashicorp/hcl/v2/hcldec" awscommon "github.com/hashicorp/packer/builder/amazon/common" "github.com/hashicorp/packer/common" "github.com/hashicorp/packer/helper/config" @@ -51,7 +52,8 @@ type PostProcessor struct { config Config } -// Entry point for configuration parsing when we've defined +func (p *PostProcessor) ConfigSpec() hcldec.ObjectSpec { return p.config.FlatMapstructure().HCL2Spec() } + func (p *PostProcessor) Configure(raws ...interface{}) error { p.config.ctx.Funcs = awscommon.TemplateFuncs err := config.Decode(&p.config, &config.DecodeOpts{ diff --git a/post-processor/amazon-import/post-processor.hcl2spec.go b/post-processor/amazon-import/post-processor.hcl2spec.go index ef2134711..60ecb8f08 100644 --- a/post-processor/amazon-import/post-processor.hcl2spec.go +++ b/post-processor/amazon-import/post-processor.hcl2spec.go @@ -49,10 +49,13 @@ type FlatConfig struct { // FlatMapstructure returns a new FlatConfig. // FlatConfig is an auto-generated flat version of Config. // Where the contents a fields with a `mapstructure:,squash` tag are bubbled up. -func (*Config) FlatMapstructure() interface{} { return new(FlatConfig) } +func (*Config) FlatMapstructure() interface{ HCL2Spec() map[string]hcldec.Spec } { + return new(FlatConfig) +} -// HCL2Spec returns the hcldec.Spec of a FlatConfig. -// This spec is used by HCL to read the fields of FlatConfig. +// HCL2Spec returns the hcl spec of a Config. +// This spec is used by HCL to read the fields of Config. +// The decoded values from this spec will then be applied to a FlatConfig. func (*FlatConfig) HCL2Spec() map[string]hcldec.Spec { s := map[string]hcldec.Spec{ "packer_build_name": &hcldec.AttrSpec{Name: "packer_build_name", Type: cty.String, Required: false}, diff --git a/post-processor/artifice/post-processor.go b/post-processor/artifice/post-processor.go index fafa915e7..3ab566dea 100644 --- a/post-processor/artifice/post-processor.go +++ b/post-processor/artifice/post-processor.go @@ -7,6 +7,7 @@ import ( "fmt" "strings" + "github.com/hashicorp/hcl/v2/hcldec" "github.com/hashicorp/packer/common" "github.com/hashicorp/packer/helper/config" "github.com/hashicorp/packer/packer" @@ -32,6 +33,8 @@ type PostProcessor struct { config Config } +func (p *PostProcessor) ConfigSpec() hcldec.ObjectSpec { return p.config.FlatMapstructure().HCL2Spec() } + func (p *PostProcessor) Configure(raws ...interface{}) error { err := config.Decode(&p.config, &config.DecodeOpts{ Interpolate: true, diff --git a/post-processor/artifice/post-processor.hcl2spec.go b/post-processor/artifice/post-processor.hcl2spec.go index c81b3eefe..a2ae49b96 100644 --- a/post-processor/artifice/post-processor.hcl2spec.go +++ b/post-processor/artifice/post-processor.hcl2spec.go @@ -23,10 +23,13 @@ type FlatConfig struct { // FlatMapstructure returns a new FlatConfig. // FlatConfig is an auto-generated flat version of Config. // Where the contents a fields with a `mapstructure:,squash` tag are bubbled up. -func (*Config) FlatMapstructure() interface{} { return new(FlatConfig) } +func (*Config) FlatMapstructure() interface{ HCL2Spec() map[string]hcldec.Spec } { + return new(FlatConfig) +} -// HCL2Spec returns the hcldec.Spec of a FlatConfig. -// This spec is used by HCL to read the fields of FlatConfig. +// HCL2Spec returns the hcl spec of a Config. +// This spec is used by HCL to read the fields of Config. +// The decoded values from this spec will then be applied to a FlatConfig. func (*FlatConfig) HCL2Spec() map[string]hcldec.Spec { s := map[string]hcldec.Spec{ "packer_build_name": &hcldec.AttrSpec{Name: "packer_build_name", Type: cty.String, Required: false}, diff --git a/post-processor/checksum/post-processor.go b/post-processor/checksum/post-processor.go index 9b3cec76f..3495a633f 100644 --- a/post-processor/checksum/post-processor.go +++ b/post-processor/checksum/post-processor.go @@ -14,6 +14,7 @@ import ( "os" "path/filepath" + "github.com/hashicorp/hcl/v2/hcldec" "github.com/hashicorp/packer/common" "github.com/hashicorp/packer/helper/config" "github.com/hashicorp/packer/packer" @@ -58,6 +59,8 @@ func getHash(t string) hash.Hash { return h } +func (p *PostProcessor) ConfigSpec() hcldec.ObjectSpec { return p.config.FlatMapstructure().HCL2Spec() } + func (p *PostProcessor) Configure(raws ...interface{}) error { err := config.Decode(&p.config, &config.DecodeOpts{ Interpolate: true, diff --git a/post-processor/checksum/post-processor.hcl2spec.go b/post-processor/checksum/post-processor.hcl2spec.go index 1efec6c80..b136faac0 100644 --- a/post-processor/checksum/post-processor.hcl2spec.go +++ b/post-processor/checksum/post-processor.hcl2spec.go @@ -24,10 +24,13 @@ type FlatConfig struct { // FlatMapstructure returns a new FlatConfig. // FlatConfig is an auto-generated flat version of Config. // Where the contents a fields with a `mapstructure:,squash` tag are bubbled up. -func (*Config) FlatMapstructure() interface{} { return new(FlatConfig) } +func (*Config) FlatMapstructure() interface{ HCL2Spec() map[string]hcldec.Spec } { + return new(FlatConfig) +} -// HCL2Spec returns the hcldec.Spec of a FlatConfig. -// This spec is used by HCL to read the fields of FlatConfig. +// HCL2Spec returns the hcl spec of a Config. +// This spec is used by HCL to read the fields of Config. +// The decoded values from this spec will then be applied to a FlatConfig. func (*FlatConfig) HCL2Spec() map[string]hcldec.Spec { s := map[string]hcldec.Spec{ "packer_build_name": &hcldec.AttrSpec{Name: "packer_build_name", Type: cty.String, Required: false}, diff --git a/post-processor/compress/post-processor.go b/post-processor/compress/post-processor.go index 0bc89b9a3..8777cab49 100644 --- a/post-processor/compress/post-processor.go +++ b/post-processor/compress/post-processor.go @@ -14,6 +14,7 @@ import ( "runtime" "github.com/biogo/hts/bgzf" + "github.com/hashicorp/hcl/v2/hcldec" "github.com/hashicorp/packer/common" "github.com/hashicorp/packer/helper/config" "github.com/hashicorp/packer/packer" @@ -54,6 +55,8 @@ type PostProcessor struct { config Config } +func (p *PostProcessor) ConfigSpec() hcldec.ObjectSpec { return p.config.FlatMapstructure().HCL2Spec() } + func (p *PostProcessor) Configure(raws ...interface{}) error { err := config.Decode(&p.config, &config.DecodeOpts{ Interpolate: true, diff --git a/post-processor/compress/post-processor.hcl2spec.go b/post-processor/compress/post-processor.hcl2spec.go index 4e8db0497..e1e9e55ac 100644 --- a/post-processor/compress/post-processor.hcl2spec.go +++ b/post-processor/compress/post-processor.hcl2spec.go @@ -26,10 +26,13 @@ type FlatConfig struct { // FlatMapstructure returns a new FlatConfig. // FlatConfig is an auto-generated flat version of Config. // Where the contents a fields with a `mapstructure:,squash` tag are bubbled up. -func (*Config) FlatMapstructure() interface{} { return new(FlatConfig) } +func (*Config) FlatMapstructure() interface{ HCL2Spec() map[string]hcldec.Spec } { + return new(FlatConfig) +} -// HCL2Spec returns the hcldec.Spec of a FlatConfig. -// This spec is used by HCL to read the fields of FlatConfig. +// HCL2Spec returns the hcl spec of a Config. +// This spec is used by HCL to read the fields of Config. +// The decoded values from this spec will then be applied to a FlatConfig. func (*FlatConfig) HCL2Spec() map[string]hcldec.Spec { s := map[string]hcldec.Spec{ "packer_build_name": &hcldec.AttrSpec{Name: "packer_build_name", Type: cty.String, Required: false}, diff --git a/post-processor/digitalocean-import/post-processor.go b/post-processor/digitalocean-import/post-processor.go index 57e82f668..50f70013a 100644 --- a/post-processor/digitalocean-import/post-processor.go +++ b/post-processor/digitalocean-import/post-processor.go @@ -19,6 +19,7 @@ import ( "github.com/aws/aws-sdk-go/service/s3/s3manager" "github.com/digitalocean/godo" + "github.com/hashicorp/hcl/v2/hcldec" "github.com/hashicorp/packer/builder/digitalocean" "github.com/hashicorp/packer/common" "github.com/hashicorp/packer/helper/config" @@ -72,6 +73,8 @@ func (l logger) Log(args ...interface{}) { l.logger.Println(args...) } +func (p *PostProcessor) ConfigSpec() hcldec.ObjectSpec { return p.config.FlatMapstructure().HCL2Spec() } + func (p *PostProcessor) Configure(raws ...interface{}) error { err := config.Decode(&p.config, &config.DecodeOpts{ Interpolate: true, diff --git a/post-processor/digitalocean-import/post-processor.hcl2spec.go b/post-processor/digitalocean-import/post-processor.hcl2spec.go index 00e764e83..fb2a4d8ac 100644 --- a/post-processor/digitalocean-import/post-processor.hcl2spec.go +++ b/post-processor/digitalocean-import/post-processor.hcl2spec.go @@ -34,10 +34,13 @@ type FlatConfig struct { // FlatMapstructure returns a new FlatConfig. // FlatConfig is an auto-generated flat version of Config. // Where the contents a fields with a `mapstructure:,squash` tag are bubbled up. -func (*Config) FlatMapstructure() interface{} { return new(FlatConfig) } +func (*Config) FlatMapstructure() interface{ HCL2Spec() map[string]hcldec.Spec } { + return new(FlatConfig) +} -// HCL2Spec returns the hcldec.Spec of a FlatConfig. -// This spec is used by HCL to read the fields of FlatConfig. +// HCL2Spec returns the hcl spec of a Config. +// This spec is used by HCL to read the fields of Config. +// The decoded values from this spec will then be applied to a FlatConfig. func (*FlatConfig) HCL2Spec() map[string]hcldec.Spec { s := map[string]hcldec.Spec{ "packer_build_name": &hcldec.AttrSpec{Name: "packer_build_name", Type: cty.String, Required: false}, diff --git a/post-processor/docker-import/post-processor.go b/post-processor/docker-import/post-processor.go index fc0309a3c..089aed122 100644 --- a/post-processor/docker-import/post-processor.go +++ b/post-processor/docker-import/post-processor.go @@ -6,6 +6,7 @@ import ( "context" "fmt" + "github.com/hashicorp/hcl/v2/hcldec" "github.com/hashicorp/packer/builder/docker" "github.com/hashicorp/packer/common" "github.com/hashicorp/packer/helper/config" @@ -30,6 +31,8 @@ type PostProcessor struct { config Config } +func (p *PostProcessor) ConfigSpec() hcldec.ObjectSpec { return p.config.FlatMapstructure().HCL2Spec() } + func (p *PostProcessor) Configure(raws ...interface{}) error { err := config.Decode(&p.config, &config.DecodeOpts{ Interpolate: true, diff --git a/post-processor/docker-import/post-processor.hcl2spec.go b/post-processor/docker-import/post-processor.hcl2spec.go index 80a0ff36c..fc90b5adf 100644 --- a/post-processor/docker-import/post-processor.hcl2spec.go +++ b/post-processor/docker-import/post-processor.hcl2spec.go @@ -24,10 +24,13 @@ type FlatConfig struct { // FlatMapstructure returns a new FlatConfig. // FlatConfig is an auto-generated flat version of Config. // Where the contents a fields with a `mapstructure:,squash` tag are bubbled up. -func (*Config) FlatMapstructure() interface{} { return new(FlatConfig) } +func (*Config) FlatMapstructure() interface{ HCL2Spec() map[string]hcldec.Spec } { + return new(FlatConfig) +} -// HCL2Spec returns the hcldec.Spec of a FlatConfig. -// This spec is used by HCL to read the fields of FlatConfig. +// HCL2Spec returns the hcl spec of a Config. +// This spec is used by HCL to read the fields of Config. +// The decoded values from this spec will then be applied to a FlatConfig. func (*FlatConfig) HCL2Spec() map[string]hcldec.Spec { s := map[string]hcldec.Spec{ "packer_build_name": &hcldec.AttrSpec{Name: "packer_build_name", Type: cty.String, Required: false}, diff --git a/post-processor/docker-push/post-processor.go b/post-processor/docker-push/post-processor.go index cd0b950e1..a276b0fca 100644 --- a/post-processor/docker-push/post-processor.go +++ b/post-processor/docker-push/post-processor.go @@ -6,6 +6,7 @@ import ( "context" "fmt" + "github.com/hashicorp/hcl/v2/hcldec" "github.com/hashicorp/packer/builder/docker" "github.com/hashicorp/packer/common" "github.com/hashicorp/packer/helper/config" @@ -36,6 +37,8 @@ type PostProcessor struct { config Config } +func (p *PostProcessor) ConfigSpec() hcldec.ObjectSpec { return p.config.FlatMapstructure().HCL2Spec() } + func (p *PostProcessor) Configure(raws ...interface{}) error { err := config.Decode(&p.config, &config.DecodeOpts{ Interpolate: true, diff --git a/post-processor/docker-push/post-processor.hcl2spec.go b/post-processor/docker-push/post-processor.hcl2spec.go index 096aebdda..cbd5cf726 100644 --- a/post-processor/docker-push/post-processor.hcl2spec.go +++ b/post-processor/docker-push/post-processor.hcl2spec.go @@ -30,10 +30,13 @@ type FlatConfig struct { // FlatMapstructure returns a new FlatConfig. // FlatConfig is an auto-generated flat version of Config. // Where the contents a fields with a `mapstructure:,squash` tag are bubbled up. -func (*Config) FlatMapstructure() interface{} { return new(FlatConfig) } +func (*Config) FlatMapstructure() interface{ HCL2Spec() map[string]hcldec.Spec } { + return new(FlatConfig) +} -// HCL2Spec returns the hcldec.Spec of a FlatConfig. -// This spec is used by HCL to read the fields of FlatConfig. +// HCL2Spec returns the hcl spec of a Config. +// This spec is used by HCL to read the fields of Config. +// The decoded values from this spec will then be applied to a FlatConfig. func (*FlatConfig) HCL2Spec() map[string]hcldec.Spec { s := map[string]hcldec.Spec{ "packer_build_name": &hcldec.AttrSpec{Name: "packer_build_name", Type: cty.String, Required: false}, diff --git a/post-processor/docker-save/post-processor.go b/post-processor/docker-save/post-processor.go index 26198142c..8cf3306d7 100644 --- a/post-processor/docker-save/post-processor.go +++ b/post-processor/docker-save/post-processor.go @@ -7,6 +7,7 @@ import ( "fmt" "os" + "github.com/hashicorp/hcl/v2/hcldec" "github.com/hashicorp/packer/builder/docker" "github.com/hashicorp/packer/common" "github.com/hashicorp/packer/helper/config" @@ -32,6 +33,8 @@ type PostProcessor struct { config Config } +func (p *PostProcessor) ConfigSpec() hcldec.ObjectSpec { return p.config.FlatMapstructure().HCL2Spec() } + func (p *PostProcessor) Configure(raws ...interface{}) error { err := config.Decode(&p.config, &config.DecodeOpts{ Interpolate: true, diff --git a/post-processor/docker-save/post-processor.hcl2spec.go b/post-processor/docker-save/post-processor.hcl2spec.go index e7c71ed51..59bee1e9b 100644 --- a/post-processor/docker-save/post-processor.hcl2spec.go +++ b/post-processor/docker-save/post-processor.hcl2spec.go @@ -22,10 +22,13 @@ type FlatConfig struct { // FlatMapstructure returns a new FlatConfig. // FlatConfig is an auto-generated flat version of Config. // Where the contents a fields with a `mapstructure:,squash` tag are bubbled up. -func (*Config) FlatMapstructure() interface{} { return new(FlatConfig) } +func (*Config) FlatMapstructure() interface{ HCL2Spec() map[string]hcldec.Spec } { + return new(FlatConfig) +} -// HCL2Spec returns the hcldec.Spec of a FlatConfig. -// This spec is used by HCL to read the fields of FlatConfig. +// HCL2Spec returns the hcl spec of a Config. +// This spec is used by HCL to read the fields of Config. +// The decoded values from this spec will then be applied to a FlatConfig. func (*FlatConfig) HCL2Spec() map[string]hcldec.Spec { s := map[string]hcldec.Spec{ "packer_build_name": &hcldec.AttrSpec{Name: "packer_build_name", Type: cty.String, Required: false}, diff --git a/post-processor/docker-tag/post-processor.go b/post-processor/docker-tag/post-processor.go index f2ba1beb3..4c1f03054 100644 --- a/post-processor/docker-tag/post-processor.go +++ b/post-processor/docker-tag/post-processor.go @@ -6,6 +6,7 @@ import ( "context" "fmt" + "github.com/hashicorp/hcl/v2/hcldec" "github.com/hashicorp/packer/builder/docker" "github.com/hashicorp/packer/common" "github.com/hashicorp/packer/helper/config" @@ -32,6 +33,8 @@ type PostProcessor struct { config Config } +func (p *PostProcessor) ConfigSpec() hcldec.ObjectSpec { return p.config.FlatMapstructure().HCL2Spec() } + func (p *PostProcessor) Configure(raws ...interface{}) error { err := config.Decode(&p.config, &config.DecodeOpts{ Interpolate: true, diff --git a/post-processor/docker-tag/post-processor.hcl2spec.go b/post-processor/docker-tag/post-processor.hcl2spec.go index 22c20a420..2ec63f2b5 100644 --- a/post-processor/docker-tag/post-processor.hcl2spec.go +++ b/post-processor/docker-tag/post-processor.hcl2spec.go @@ -24,10 +24,13 @@ type FlatConfig struct { // FlatMapstructure returns a new FlatConfig. // FlatConfig is an auto-generated flat version of Config. // Where the contents a fields with a `mapstructure:,squash` tag are bubbled up. -func (*Config) FlatMapstructure() interface{} { return new(FlatConfig) } +func (*Config) FlatMapstructure() interface{ HCL2Spec() map[string]hcldec.Spec } { + return new(FlatConfig) +} -// HCL2Spec returns the hcldec.Spec of a FlatConfig. -// This spec is used by HCL to read the fields of FlatConfig. +// HCL2Spec returns the hcl spec of a Config. +// This spec is used by HCL to read the fields of Config. +// The decoded values from this spec will then be applied to a FlatConfig. func (*FlatConfig) HCL2Spec() map[string]hcldec.Spec { s := map[string]hcldec.Spec{ "packer_build_name": &hcldec.AttrSpec{Name: "packer_build_name", Type: cty.String, Required: false}, diff --git a/post-processor/exoscale-import/post-processor.go b/post-processor/exoscale-import/post-processor.go index 3e8cc261a..ac190191e 100644 --- a/post-processor/exoscale-import/post-processor.go +++ b/post-processor/exoscale-import/post-processor.go @@ -17,6 +17,7 @@ import ( "github.com/aws/aws-sdk-go/service/s3" "github.com/aws/aws-sdk-go/service/s3/s3manager" "github.com/exoscale/egoscale" + "github.com/hashicorp/hcl/v2/hcldec" "github.com/hashicorp/packer/builder/file" "github.com/hashicorp/packer/builder/qemu" "github.com/hashicorp/packer/common" @@ -57,6 +58,8 @@ type PostProcessor struct { config Config } +func (p *PostProcessor) ConfigSpec() hcldec.ObjectSpec { return p.config.FlatMapstructure().HCL2Spec() } + func (p *PostProcessor) Configure(raws ...interface{}) error { p.config.TemplateZone = defaultTemplateZone p.config.APIEndpoint = defaultAPIEndpoint diff --git a/post-processor/exoscale-import/post-processor.hcl2spec.go b/post-processor/exoscale-import/post-processor.hcl2spec.go index 3dbd4740d..243b54166 100644 --- a/post-processor/exoscale-import/post-processor.hcl2spec.go +++ b/post-processor/exoscale-import/post-processor.hcl2spec.go @@ -33,10 +33,13 @@ type FlatConfig struct { // FlatMapstructure returns a new FlatConfig. // FlatConfig is an auto-generated flat version of Config. // Where the contents a fields with a `mapstructure:,squash` tag are bubbled up. -func (*Config) FlatMapstructure() interface{} { return new(FlatConfig) } +func (*Config) FlatMapstructure() interface{ HCL2Spec() map[string]hcldec.Spec } { + return new(FlatConfig) +} -// HCL2Spec returns the hcldec.Spec of a FlatConfig. -// This spec is used by HCL to read the fields of FlatConfig. +// HCL2Spec returns the hcl spec of a Config. +// This spec is used by HCL to read the fields of Config. +// The decoded values from this spec will then be applied to a FlatConfig. func (*FlatConfig) HCL2Spec() map[string]hcldec.Spec { s := map[string]hcldec.Spec{ "packer_build_name": &hcldec.AttrSpec{Name: "packer_build_name", Type: cty.String, Required: false}, diff --git a/post-processor/googlecompute-export/post-processor.go b/post-processor/googlecompute-export/post-processor.go index 133e47dc8..16453742a 100644 --- a/post-processor/googlecompute-export/post-processor.go +++ b/post-processor/googlecompute-export/post-processor.go @@ -8,6 +8,7 @@ import ( "strings" "time" + "github.com/hashicorp/hcl/v2/hcldec" "github.com/hashicorp/packer/builder/googlecompute" "github.com/hashicorp/packer/common" "github.com/hashicorp/packer/helper/config" @@ -41,6 +42,8 @@ type PostProcessor struct { runner multistep.Runner } +func (p *PostProcessor) ConfigSpec() hcldec.ObjectSpec { return p.config.FlatMapstructure().HCL2Spec() } + func (p *PostProcessor) Configure(raws ...interface{}) error { err := config.Decode(&p.config, &config.DecodeOpts{ Interpolate: true, diff --git a/post-processor/googlecompute-export/post-processor.hcl2spec.go b/post-processor/googlecompute-export/post-processor.hcl2spec.go index 1fce9eab7..5df63b9eb 100644 --- a/post-processor/googlecompute-export/post-processor.hcl2spec.go +++ b/post-processor/googlecompute-export/post-processor.hcl2spec.go @@ -31,10 +31,13 @@ type FlatConfig struct { // FlatMapstructure returns a new FlatConfig. // FlatConfig is an auto-generated flat version of Config. // Where the contents a fields with a `mapstructure:,squash` tag are bubbled up. -func (*Config) FlatMapstructure() interface{} { return new(FlatConfig) } +func (*Config) FlatMapstructure() interface{ HCL2Spec() map[string]hcldec.Spec } { + return new(FlatConfig) +} -// HCL2Spec returns the hcldec.Spec of a FlatConfig. -// This spec is used by HCL to read the fields of FlatConfig. +// HCL2Spec returns the hcl spec of a Config. +// This spec is used by HCL to read the fields of Config. +// The decoded values from this spec will then be applied to a FlatConfig. func (*FlatConfig) HCL2Spec() map[string]hcldec.Spec { s := map[string]hcldec.Spec{ "packer_build_name": &hcldec.AttrSpec{Name: "packer_build_name", Type: cty.String, Required: false}, diff --git a/post-processor/googlecompute-import/post-processor.go b/post-processor/googlecompute-import/post-processor.go index fd949fef3..1eaf9f954 100644 --- a/post-processor/googlecompute-import/post-processor.go +++ b/post-processor/googlecompute-import/post-processor.go @@ -14,6 +14,7 @@ import ( "google.golang.org/api/compute/v1" "google.golang.org/api/storage/v1" + "github.com/hashicorp/hcl/v2/hcldec" "github.com/hashicorp/packer/builder/googlecompute" "github.com/hashicorp/packer/common" "github.com/hashicorp/packer/helper/config" @@ -46,6 +47,8 @@ type PostProcessor struct { config Config } +func (p *PostProcessor) ConfigSpec() hcldec.ObjectSpec { return p.config.FlatMapstructure().HCL2Spec() } + func (p *PostProcessor) Configure(raws ...interface{}) error { err := config.Decode(&p.config, &config.DecodeOpts{ Interpolate: true, diff --git a/post-processor/googlecompute-import/post-processor.hcl2spec.go b/post-processor/googlecompute-import/post-processor.hcl2spec.go index 39d10924a..cc9e3a5e6 100644 --- a/post-processor/googlecompute-import/post-processor.hcl2spec.go +++ b/post-processor/googlecompute-import/post-processor.hcl2spec.go @@ -32,10 +32,13 @@ type FlatConfig struct { // FlatMapstructure returns a new FlatConfig. // FlatConfig is an auto-generated flat version of Config. // Where the contents a fields with a `mapstructure:,squash` tag are bubbled up. -func (*Config) FlatMapstructure() interface{} { return new(FlatConfig) } +func (*Config) FlatMapstructure() interface{ HCL2Spec() map[string]hcldec.Spec } { + return new(FlatConfig) +} -// HCL2Spec returns the hcldec.Spec of a FlatConfig. -// This spec is used by HCL to read the fields of FlatConfig. +// HCL2Spec returns the hcl spec of a Config. +// This spec is used by HCL to read the fields of Config. +// The decoded values from this spec will then be applied to a FlatConfig. func (*FlatConfig) HCL2Spec() map[string]hcldec.Spec { s := map[string]hcldec.Spec{ "packer_build_name": &hcldec.AttrSpec{Name: "packer_build_name", Type: cty.String, Required: false}, diff --git a/post-processor/manifest/post-processor.go b/post-processor/manifest/post-processor.go index d10919d28..698c12b01 100644 --- a/post-processor/manifest/post-processor.go +++ b/post-processor/manifest/post-processor.go @@ -12,6 +12,7 @@ import ( "path/filepath" "time" + "github.com/hashicorp/hcl/v2/hcldec" "github.com/hashicorp/packer/common" "github.com/hashicorp/packer/helper/config" "github.com/hashicorp/packer/packer" @@ -36,6 +37,8 @@ type ManifestFile struct { LastRunUUID string `json:"last_run_uuid"` } +func (p *PostProcessor) ConfigSpec() hcldec.ObjectSpec { return p.config.FlatMapstructure().HCL2Spec() } + func (p *PostProcessor) Configure(raws ...interface{}) error { err := config.Decode(&p.config, &config.DecodeOpts{ Interpolate: true, diff --git a/post-processor/manifest/post-processor.hcl2spec.go b/post-processor/manifest/post-processor.hcl2spec.go index a7e831e30..08be1e1e9 100644 --- a/post-processor/manifest/post-processor.hcl2spec.go +++ b/post-processor/manifest/post-processor.hcl2spec.go @@ -24,10 +24,13 @@ type FlatConfig struct { // FlatMapstructure returns a new FlatConfig. // FlatConfig is an auto-generated flat version of Config. // Where the contents a fields with a `mapstructure:,squash` tag are bubbled up. -func (*Config) FlatMapstructure() interface{} { return new(FlatConfig) } +func (*Config) FlatMapstructure() interface{ HCL2Spec() map[string]hcldec.Spec } { + return new(FlatConfig) +} -// HCL2Spec returns the hcldec.Spec of a FlatConfig. -// This spec is used by HCL to read the fields of FlatConfig. +// HCL2Spec returns the hcl spec of a Config. +// This spec is used by HCL to read the fields of Config. +// The decoded values from this spec will then be applied to a FlatConfig. func (*FlatConfig) HCL2Spec() map[string]hcldec.Spec { s := map[string]hcldec.Spec{ "packer_build_name": &hcldec.AttrSpec{Name: "packer_build_name", Type: cty.String, Required: false}, diff --git a/post-processor/shell-local/post-processor.go b/post-processor/shell-local/post-processor.go index e23d31648..0924314ff 100644 --- a/post-processor/shell-local/post-processor.go +++ b/post-processor/shell-local/post-processor.go @@ -3,6 +3,7 @@ package shell_local import ( "context" + "github.com/hashicorp/hcl/v2/hcldec" sl "github.com/hashicorp/packer/common/shell-local" "github.com/hashicorp/packer/packer" ) @@ -16,6 +17,8 @@ type ExecuteCommandTemplate struct { Script string } +func (p *PostProcessor) ConfigSpec() hcldec.ObjectSpec { return p.config.FlatMapstructure().HCL2Spec() } + func (p *PostProcessor) Configure(raws ...interface{}) error { err := sl.Decode(&p.config, raws...) if err != nil { diff --git a/post-processor/ucloud-import/post-processor.go b/post-processor/ucloud-import/post-processor.go index 69dded174..a0ec219ed 100644 --- a/post-processor/ucloud-import/post-processor.go +++ b/post-processor/ucloud-import/post-processor.go @@ -1,8 +1,16 @@ +//go:generate mapstructure-to-hcl2 -type Config + package ucloudimport import ( "context" "fmt" + "log" + "net/url" + "strings" + "time" + + "github.com/hashicorp/hcl/v2/hcldec" ucloudcommon "github.com/hashicorp/packer/builder/ucloud/common" "github.com/hashicorp/packer/common" "github.com/hashicorp/packer/common/retry" @@ -13,10 +21,6 @@ import ( "github.com/ucloud/ucloud-sdk-go/services/uhost" "github.com/ucloud/ucloud-sdk-go/ucloud" ufsdk "github.com/ufilesdk-dev/ufile-gosdk" - "log" - "net/url" - "strings" - "time" ) const ( @@ -57,7 +61,8 @@ type PostProcessor struct { config Config } -// Entry point for configuration parsing when we've defined +func (p *PostProcessor) ConfigSpec() hcldec.ObjectSpec { return p.config.FlatMapstructure().HCL2Spec() } + func (p *PostProcessor) Configure(raws ...interface{}) error { err := config.Decode(&p.config, &config.DecodeOpts{ Interpolate: true, diff --git a/post-processor/ucloud-import/post-processor.hcl2spec.go b/post-processor/ucloud-import/post-processor.hcl2spec.go new file mode 100644 index 000000000..ec78de713 --- /dev/null +++ b/post-processor/ucloud-import/post-processor.hcl2spec.go @@ -0,0 +1,70 @@ +// Code generated by "mapstructure-to-hcl2 -type Config"; DO NOT EDIT. +package ucloudimport + +import ( + "github.com/hashicorp/hcl/v2/hcldec" + "github.com/zclconf/go-cty/cty" +) + +// FlatConfig is an auto-generated flat version of Config. +// Where the contents of a field with a `mapstructure:,squash` tag are bubbled up. +type FlatConfig struct { + PackerBuildName *string `mapstructure:"packer_build_name" cty:"packer_build_name"` + PackerBuilderType *string `mapstructure:"packer_builder_type" cty:"packer_builder_type"` + PackerDebug *bool `mapstructure:"packer_debug" cty:"packer_debug"` + PackerForce *bool `mapstructure:"packer_force" cty:"packer_force"` + PackerOnError *string `mapstructure:"packer_on_error" cty:"packer_on_error"` + PackerUserVars map[string]string `mapstructure:"packer_user_variables" cty:"packer_user_variables"` + PackerSensitiveVars []string `mapstructure:"packer_sensitive_variables" cty:"packer_sensitive_variables"` + PublicKey *string `mapstructure:"public_key" cty:"public_key"` + PrivateKey *string `mapstructure:"private_key" cty:"private_key"` + Region *string `mapstructure:"region" cty:"region"` + ProjectId *string `mapstructure:"project_id" cty:"project_id"` + BaseUrl *string `mapstructure:"base_url" cty:"base_url"` + UFileBucket *string `mapstructure:"ufile_bucket_name" cty:"ufile_bucket_name"` + UFileKey *string `mapstructure:"ufile_key_name" cty:"ufile_key_name"` + SkipClean *bool `mapstructure:"skip_clean" cty:"skip_clean"` + ImageName *string `mapstructure:"image_name" cty:"image_name"` + ImageDescription *string `mapstructure:"image_description" cty:"image_description"` + OSType *string `mapstructure:"image_os_type" cty:"image_os_type"` + OSName *string `mapstructure:"image_os_name" cty:"image_os_name"` + Format *string `mapstructure:"format" cty:"format"` + WaitImageReadyTimeout *int `mapstructure:"wait_image_ready_timeout" cty:"wait_image_ready_timeout"` +} + +// FlatMapstructure returns a new FlatConfig. +// FlatConfig is an auto-generated flat version of Config. +// Where the contents a fields with a `mapstructure:,squash` tag are bubbled up. +func (*Config) FlatMapstructure() interface{ HCL2Spec() map[string]hcldec.Spec } { + return new(FlatConfig) +} + +// HCL2Spec returns the hcl spec of a Config. +// This spec is used by HCL to read the fields of Config. +// The decoded values from this spec will then be applied to a FlatConfig. +func (*FlatConfig) HCL2Spec() map[string]hcldec.Spec { + s := map[string]hcldec.Spec{ + "packer_build_name": &hcldec.AttrSpec{Name: "packer_build_name", Type: cty.String, Required: false}, + "packer_builder_type": &hcldec.AttrSpec{Name: "packer_builder_type", Type: cty.String, Required: false}, + "packer_debug": &hcldec.AttrSpec{Name: "packer_debug", Type: cty.Bool, Required: false}, + "packer_force": &hcldec.AttrSpec{Name: "packer_force", Type: cty.Bool, Required: false}, + "packer_on_error": &hcldec.AttrSpec{Name: "packer_on_error", Type: cty.String, Required: false}, + "packer_user_variables": &hcldec.BlockAttrsSpec{TypeName: "packer_user_variables", ElementType: cty.String, Required: false}, + "packer_sensitive_variables": &hcldec.AttrSpec{Name: "packer_sensitive_variables", Type: cty.List(cty.String), Required: false}, + "public_key": &hcldec.AttrSpec{Name: "public_key", Type: cty.String, Required: false}, + "private_key": &hcldec.AttrSpec{Name: "private_key", Type: cty.String, Required: false}, + "region": &hcldec.AttrSpec{Name: "region", Type: cty.String, Required: false}, + "project_id": &hcldec.AttrSpec{Name: "project_id", Type: cty.String, Required: false}, + "base_url": &hcldec.AttrSpec{Name: "base_url", Type: cty.String, Required: false}, + "ufile_bucket_name": &hcldec.AttrSpec{Name: "ufile_bucket_name", Type: cty.String, Required: false}, + "ufile_key_name": &hcldec.AttrSpec{Name: "ufile_key_name", Type: cty.String, Required: false}, + "skip_clean": &hcldec.AttrSpec{Name: "skip_clean", Type: cty.Bool, Required: false}, + "image_name": &hcldec.AttrSpec{Name: "image_name", Type: cty.String, Required: false}, + "image_description": &hcldec.AttrSpec{Name: "image_description", Type: cty.String, Required: false}, + "image_os_type": &hcldec.AttrSpec{Name: "image_os_type", Type: cty.String, Required: false}, + "image_os_name": &hcldec.AttrSpec{Name: "image_os_name", Type: cty.String, Required: false}, + "format": &hcldec.AttrSpec{Name: "format", Type: cty.String, Required: false}, + "wait_image_ready_timeout": &hcldec.AttrSpec{Name: "wait_image_ready_timeout", Type: cty.Number, Required: false}, + } + return s +} diff --git a/post-processor/vagrant-cloud/post-processor.go b/post-processor/vagrant-cloud/post-processor.go index 9401b8374..74a72d074 100644 --- a/post-processor/vagrant-cloud/post-processor.go +++ b/post-processor/vagrant-cloud/post-processor.go @@ -18,6 +18,7 @@ import ( "os" "strings" + "github.com/hashicorp/hcl/v2/hcldec" "github.com/hashicorp/packer/common" "github.com/hashicorp/packer/helper/config" "github.com/hashicorp/packer/helper/multistep" @@ -63,6 +64,8 @@ type PostProcessor struct { insecureSkipTLSVerify bool } +func (p *PostProcessor) ConfigSpec() hcldec.ObjectSpec { return p.config.FlatMapstructure().HCL2Spec() } + func (p *PostProcessor) Configure(raws ...interface{}) error { err := config.Decode(&p.config, &config.DecodeOpts{ Interpolate: true, diff --git a/post-processor/vagrant-cloud/post-processor.hcl2spec.go b/post-processor/vagrant-cloud/post-processor.hcl2spec.go index a8216aeff..515a06914 100644 --- a/post-processor/vagrant-cloud/post-processor.hcl2spec.go +++ b/post-processor/vagrant-cloud/post-processor.hcl2spec.go @@ -29,10 +29,13 @@ type FlatConfig struct { // FlatMapstructure returns a new FlatConfig. // FlatConfig is an auto-generated flat version of Config. // Where the contents a fields with a `mapstructure:,squash` tag are bubbled up. -func (*Config) FlatMapstructure() interface{} { return new(FlatConfig) } +func (*Config) FlatMapstructure() interface{ HCL2Spec() map[string]hcldec.Spec } { + return new(FlatConfig) +} -// HCL2Spec returns the hcldec.Spec of a FlatConfig. -// This spec is used by HCL to read the fields of FlatConfig. +// HCL2Spec returns the hcl spec of a Config. +// This spec is used by HCL to read the fields of Config. +// The decoded values from this spec will then be applied to a FlatConfig. func (*FlatConfig) HCL2Spec() map[string]hcldec.Spec { s := map[string]hcldec.Spec{ "packer_build_name": &hcldec.AttrSpec{Name: "packer_build_name", Type: cty.String, Required: false}, diff --git a/post-processor/vagrant/post-processor.go b/post-processor/vagrant/post-processor.go index 0a4527026..0e57829f2 100644 --- a/post-processor/vagrant/post-processor.go +++ b/post-processor/vagrant/post-processor.go @@ -14,6 +14,7 @@ import ( "path/filepath" "text/template" + "github.com/hashicorp/hcl/v2/hcldec" "github.com/hashicorp/packer/common" "github.com/hashicorp/packer/helper/config" "github.com/hashicorp/packer/packer" @@ -58,6 +59,11 @@ type PostProcessor struct { configs map[string]*Config } +func (p *PostProcessor) ConfigSpec() hcldec.ObjectSpec { + panic("not implemented yet") + // return p.config.FlatMapstructure().HCL2Spec() +} + func (p *PostProcessor) Configure(raws ...interface{}) error { p.configs = make(map[string]*Config) p.configs[""] = new(Config) diff --git a/post-processor/vagrant/post-processor.hcl2spec.go b/post-processor/vagrant/post-processor.hcl2spec.go index 541c4d493..ea0b1a5af 100644 --- a/post-processor/vagrant/post-processor.hcl2spec.go +++ b/post-processor/vagrant/post-processor.hcl2spec.go @@ -27,10 +27,13 @@ type FlatConfig struct { // FlatMapstructure returns a new FlatConfig. // FlatConfig is an auto-generated flat version of Config. // Where the contents a fields with a `mapstructure:,squash` tag are bubbled up. -func (*Config) FlatMapstructure() interface{} { return new(FlatConfig) } +func (*Config) FlatMapstructure() interface{ HCL2Spec() map[string]hcldec.Spec } { + return new(FlatConfig) +} -// HCL2Spec returns the hcldec.Spec of a FlatConfig. -// This spec is used by HCL to read the fields of FlatConfig. +// HCL2Spec returns the hcl spec of a Config. +// This spec is used by HCL to read the fields of Config. +// The decoded values from this spec will then be applied to a FlatConfig. func (*FlatConfig) HCL2Spec() map[string]hcldec.Spec { s := map[string]hcldec.Spec{ "packer_build_name": &hcldec.AttrSpec{Name: "packer_build_name", Type: cty.String, Required: false}, diff --git a/post-processor/vsphere-template/post-processor.go b/post-processor/vsphere-template/post-processor.go index ca4b8393c..83f9c8865 100644 --- a/post-processor/vsphere-template/post-processor.go +++ b/post-processor/vsphere-template/post-processor.go @@ -10,6 +10,7 @@ import ( "strings" "time" + "github.com/hashicorp/hcl/v2/hcldec" vmwcommon "github.com/hashicorp/packer/builder/vmware/common" "github.com/hashicorp/packer/common" "github.com/hashicorp/packer/helper/config" @@ -45,6 +46,8 @@ type PostProcessor struct { url *url.URL } +func (p *PostProcessor) ConfigSpec() hcldec.ObjectSpec { return p.config.FlatMapstructure().HCL2Spec() } + func (p *PostProcessor) Configure(raws ...interface{}) error { err := config.Decode(&p.config, &config.DecodeOpts{ Interpolate: true, diff --git a/post-processor/vsphere-template/post-processor.hcl2spec.go b/post-processor/vsphere-template/post-processor.hcl2spec.go index 0ec10a50b..c45780821 100644 --- a/post-processor/vsphere-template/post-processor.hcl2spec.go +++ b/post-processor/vsphere-template/post-processor.hcl2spec.go @@ -30,10 +30,13 @@ type FlatConfig struct { // FlatMapstructure returns a new FlatConfig. // FlatConfig is an auto-generated flat version of Config. // Where the contents a fields with a `mapstructure:,squash` tag are bubbled up. -func (*Config) FlatMapstructure() interface{} { return new(FlatConfig) } +func (*Config) FlatMapstructure() interface{ HCL2Spec() map[string]hcldec.Spec } { + return new(FlatConfig) +} -// HCL2Spec returns the hcldec.Spec of a FlatConfig. -// This spec is used by HCL to read the fields of FlatConfig. +// HCL2Spec returns the hcl spec of a Config. +// This spec is used by HCL to read the fields of Config. +// The decoded values from this spec will then be applied to a FlatConfig. func (*FlatConfig) HCL2Spec() map[string]hcldec.Spec { s := map[string]hcldec.Spec{ "packer_build_name": &hcldec.AttrSpec{Name: "packer_build_name", Type: cty.String, Required: false}, diff --git a/post-processor/vsphere/post-processor.go b/post-processor/vsphere/post-processor.go index c4da09a8e..6bfdda5b7 100644 --- a/post-processor/vsphere/post-processor.go +++ b/post-processor/vsphere/post-processor.go @@ -15,6 +15,7 @@ import ( "runtime" "strings" + "github.com/hashicorp/hcl/v2/hcldec" "github.com/hashicorp/packer/common" "github.com/hashicorp/packer/helper/config" "github.com/hashicorp/packer/packer" @@ -59,6 +60,8 @@ type PostProcessor struct { config Config } +func (p *PostProcessor) ConfigSpec() hcldec.ObjectSpec { return p.config.FlatMapstructure().HCL2Spec() } + func (p *PostProcessor) Configure(raws ...interface{}) error { err := config.Decode(&p.config, &config.DecodeOpts{ Interpolate: true, diff --git a/post-processor/vsphere/post-processor.hcl2spec.go b/post-processor/vsphere/post-processor.hcl2spec.go index ecf70776a..09373442b 100644 --- a/post-processor/vsphere/post-processor.hcl2spec.go +++ b/post-processor/vsphere/post-processor.hcl2spec.go @@ -36,10 +36,13 @@ type FlatConfig struct { // FlatMapstructure returns a new FlatConfig. // FlatConfig is an auto-generated flat version of Config. // Where the contents a fields with a `mapstructure:,squash` tag are bubbled up. -func (*Config) FlatMapstructure() interface{} { return new(FlatConfig) } +func (*Config) FlatMapstructure() interface{ HCL2Spec() map[string]hcldec.Spec } { + return new(FlatConfig) +} -// HCL2Spec returns the hcldec.Spec of a FlatConfig. -// This spec is used by HCL to read the fields of FlatConfig. +// HCL2Spec returns the hcl spec of a Config. +// This spec is used by HCL to read the fields of Config. +// The decoded values from this spec will then be applied to a FlatConfig. func (*FlatConfig) HCL2Spec() map[string]hcldec.Spec { s := map[string]hcldec.Spec{ "packer_build_name": &hcldec.AttrSpec{Name: "packer_build_name", Type: cty.String, Required: false}, diff --git a/provisioner/ansible-local/provisioner.go b/provisioner/ansible-local/provisioner.go index 4575115ac..59fec5eac 100644 --- a/provisioner/ansible-local/provisioner.go +++ b/provisioner/ansible-local/provisioner.go @@ -9,6 +9,7 @@ import ( "path/filepath" "strings" + "github.com/hashicorp/hcl/v2/hcldec" "github.com/hashicorp/packer/common" "github.com/hashicorp/packer/common/uuid" "github.com/hashicorp/packer/helper/config" @@ -76,6 +77,8 @@ type Provisioner struct { playbookFiles []string } +func (p *Provisioner) ConfigSpec() hcldec.ObjectSpec { return p.config.FlatMapstructure().HCL2Spec() } + func (p *Provisioner) Prepare(raws ...interface{}) error { err := config.Decode(&p.config, &config.DecodeOpts{ Interpolate: true, diff --git a/provisioner/ansible-local/provisioner.hcl2spec.go b/provisioner/ansible-local/provisioner.hcl2spec.go index 7424c095f..9c162d6ba 100644 --- a/provisioner/ansible-local/provisioner.hcl2spec.go +++ b/provisioner/ansible-local/provisioner.hcl2spec.go @@ -36,10 +36,13 @@ type FlatConfig struct { // FlatMapstructure returns a new FlatConfig. // FlatConfig is an auto-generated flat version of Config. // Where the contents a fields with a `mapstructure:,squash` tag are bubbled up. -func (*Config) FlatMapstructure() interface{} { return new(FlatConfig) } +func (*Config) FlatMapstructure() interface{ HCL2Spec() map[string]hcldec.Spec } { + return new(FlatConfig) +} -// HCL2Spec returns the hcldec.Spec of a FlatConfig. -// This spec is used by HCL to read the fields of FlatConfig. +// HCL2Spec returns the hcl spec of a Config. +// This spec is used by HCL to read the fields of Config. +// The decoded values from this spec will then be applied to a FlatConfig. func (*FlatConfig) HCL2Spec() map[string]hcldec.Spec { s := map[string]hcldec.Spec{ "packer_build_name": &hcldec.AttrSpec{Name: "packer_build_name", Type: cty.String, Required: false}, diff --git a/provisioner/ansible/provisioner.go b/provisioner/ansible/provisioner.go index 6de5bba5f..729cc61e0 100644 --- a/provisioner/ansible/provisioner.go +++ b/provisioner/ansible/provisioner.go @@ -28,6 +28,7 @@ import ( "golang.org/x/crypto/ssh" + "github.com/hashicorp/hcl/v2/hcldec" "github.com/hashicorp/packer/common" "github.com/hashicorp/packer/common/adapter" commonhelper "github.com/hashicorp/packer/helper/common" @@ -81,6 +82,8 @@ type PassthroughTemplate struct { WinRMPassword string } +func (p *Provisioner) ConfigSpec() hcldec.ObjectSpec { return p.config.FlatMapstructure().HCL2Spec() } + func (p *Provisioner) Prepare(raws ...interface{}) error { p.done = make(chan struct{}) diff --git a/provisioner/ansible/provisioner.hcl2spec.go b/provisioner/ansible/provisioner.hcl2spec.go index 1e659cece..dbee849a1 100644 --- a/provisioner/ansible/provisioner.hcl2spec.go +++ b/provisioner/ansible/provisioner.hcl2spec.go @@ -41,10 +41,13 @@ type FlatConfig struct { // FlatMapstructure returns a new FlatConfig. // FlatConfig is an auto-generated flat version of Config. // Where the contents a fields with a `mapstructure:,squash` tag are bubbled up. -func (*Config) FlatMapstructure() interface{} { return new(FlatConfig) } +func (*Config) FlatMapstructure() interface{ HCL2Spec() map[string]hcldec.Spec } { + return new(FlatConfig) +} -// HCL2Spec returns the hcldec.Spec of a FlatConfig. -// This spec is used by HCL to read the fields of FlatConfig. +// HCL2Spec returns the hcl spec of a Config. +// This spec is used by HCL to read the fields of Config. +// The decoded values from this spec will then be applied to a FlatConfig. func (*FlatConfig) HCL2Spec() map[string]hcldec.Spec { s := map[string]hcldec.Spec{ "packer_build_name": &hcldec.AttrSpec{Name: "packer_build_name", Type: cty.String, Required: false}, diff --git a/provisioner/breakpoint/provisioner.go b/provisioner/breakpoint/provisioner.go index 8ff752065..4cb138cba 100644 --- a/provisioner/breakpoint/provisioner.go +++ b/provisioner/breakpoint/provisioner.go @@ -8,6 +8,7 @@ import ( "golang.org/x/sync/errgroup" + "github.com/hashicorp/hcl/v2/hcldec" "github.com/hashicorp/packer/common" "github.com/hashicorp/packer/helper/config" "github.com/hashicorp/packer/packer" @@ -27,6 +28,8 @@ type Provisioner struct { config Config } +func (p *Provisioner) ConfigSpec() hcldec.ObjectSpec { return p.config.FlatMapstructure().HCL2Spec() } + func (p *Provisioner) Prepare(raws ...interface{}) error { err := config.Decode(&p.config, &config.DecodeOpts{ Interpolate: true, diff --git a/provisioner/breakpoint/provisioner.hcl2spec.go b/provisioner/breakpoint/provisioner.hcl2spec.go index d22e893ae..d56a14362 100644 --- a/provisioner/breakpoint/provisioner.hcl2spec.go +++ b/provisioner/breakpoint/provisioner.hcl2spec.go @@ -23,10 +23,13 @@ type FlatConfig struct { // FlatMapstructure returns a new FlatConfig. // FlatConfig is an auto-generated flat version of Config. // Where the contents a fields with a `mapstructure:,squash` tag are bubbled up. -func (*Config) FlatMapstructure() interface{} { return new(FlatConfig) } +func (*Config) FlatMapstructure() interface{ HCL2Spec() map[string]hcldec.Spec } { + return new(FlatConfig) +} -// HCL2Spec returns the hcldec.Spec of a FlatConfig. -// This spec is used by HCL to read the fields of FlatConfig. +// HCL2Spec returns the hcl spec of a Config. +// This spec is used by HCL to read the fields of Config. +// The decoded values from this spec will then be applied to a FlatConfig. func (*FlatConfig) HCL2Spec() map[string]hcldec.Spec { s := map[string]hcldec.Spec{ "packer_build_name": &hcldec.AttrSpec{Name: "packer_build_name", Type: cty.String, Required: false}, diff --git a/provisioner/chef-client/provisioner.go b/provisioner/chef-client/provisioner.go index 59e433c3f..c8d54968e 100644 --- a/provisioner/chef-client/provisioner.go +++ b/provisioner/chef-client/provisioner.go @@ -15,6 +15,7 @@ import ( "path/filepath" "strings" + "github.com/hashicorp/hcl/v2/hcldec" "github.com/hashicorp/packer/common" "github.com/hashicorp/packer/common/uuid" commonhelper "github.com/hashicorp/packer/helper/common" @@ -125,6 +126,8 @@ type KnifeTemplate struct { Args string } +func (p *Provisioner) ConfigSpec() hcldec.ObjectSpec { return p.config.FlatMapstructure().HCL2Spec() } + func (p *Provisioner) Prepare(raws ...interface{}) error { // Create passthrough for winrm password so we can fill it in once we know // it diff --git a/provisioner/chef-client/provisioner.hcl2spec.go b/provisioner/chef-client/provisioner.hcl2spec.go index 59378b356..5fb6a4716 100644 --- a/provisioner/chef-client/provisioner.hcl2spec.go +++ b/provisioner/chef-client/provisioner.hcl2spec.go @@ -49,10 +49,13 @@ type FlatConfig struct { // FlatMapstructure returns a new FlatConfig. // FlatConfig is an auto-generated flat version of Config. // Where the contents a fields with a `mapstructure:,squash` tag are bubbled up. -func (*Config) FlatMapstructure() interface{} { return new(FlatConfig) } +func (*Config) FlatMapstructure() interface{ HCL2Spec() map[string]hcldec.Spec } { + return new(FlatConfig) +} -// HCL2Spec returns the hcldec.Spec of a FlatConfig. -// This spec is used by HCL to read the fields of FlatConfig. +// HCL2Spec returns the hcl spec of a Config. +// This spec is used by HCL to read the fields of Config. +// The decoded values from this spec will then be applied to a FlatConfig. func (*FlatConfig) HCL2Spec() map[string]hcldec.Spec { s := map[string]hcldec.Spec{ "packer_build_name": &hcldec.AttrSpec{Name: "packer_build_name", Type: cty.String, Required: false}, diff --git a/provisioner/chef-solo/provisioner.go b/provisioner/chef-solo/provisioner.go index 39d59b36b..cab586dc4 100644 --- a/provisioner/chef-solo/provisioner.go +++ b/provisioner/chef-solo/provisioner.go @@ -15,6 +15,7 @@ import ( "path/filepath" "strings" + "github.com/hashicorp/hcl/v2/hcldec" "github.com/hashicorp/packer/common" "github.com/hashicorp/packer/helper/config" "github.com/hashicorp/packer/packer" @@ -101,6 +102,8 @@ type InstallChefTemplate struct { Version string } +func (p *Provisioner) ConfigSpec() hcldec.ObjectSpec { return p.config.FlatMapstructure().HCL2Spec() } + func (p *Provisioner) Prepare(raws ...interface{}) error { err := config.Decode(&p.config, &config.DecodeOpts{ Interpolate: true, diff --git a/provisioner/chef-solo/provisioner.hcl2spec.go b/provisioner/chef-solo/provisioner.hcl2spec.go index df801aa29..86f602b64 100644 --- a/provisioner/chef-solo/provisioner.hcl2spec.go +++ b/provisioner/chef-solo/provisioner.hcl2spec.go @@ -39,10 +39,13 @@ type FlatConfig struct { // FlatMapstructure returns a new FlatConfig. // FlatConfig is an auto-generated flat version of Config. // Where the contents a fields with a `mapstructure:,squash` tag are bubbled up. -func (*Config) FlatMapstructure() interface{} { return new(FlatConfig) } +func (*Config) FlatMapstructure() interface{ HCL2Spec() map[string]hcldec.Spec } { + return new(FlatConfig) +} -// HCL2Spec returns the hcldec.Spec of a FlatConfig. -// This spec is used by HCL to read the fields of FlatConfig. +// HCL2Spec returns the hcl spec of a Config. +// This spec is used by HCL to read the fields of Config. +// The decoded values from this spec will then be applied to a FlatConfig. func (*FlatConfig) HCL2Spec() map[string]hcldec.Spec { s := map[string]hcldec.Spec{ "packer_build_name": &hcldec.AttrSpec{Name: "packer_build_name", Type: cty.String, Required: false}, diff --git a/provisioner/converge/provisioner.go b/provisioner/converge/provisioner.go index c0c5cbeb4..99033a3c5 100644 --- a/provisioner/converge/provisioner.go +++ b/provisioner/converge/provisioner.go @@ -15,6 +15,7 @@ import ( "encoding/json" + "github.com/hashicorp/hcl/v2/hcldec" "github.com/hashicorp/packer/common" "github.com/hashicorp/packer/helper/config" "github.com/hashicorp/packer/packer" @@ -56,7 +57,8 @@ type Provisioner struct { config Config } -// Prepare provisioner somehow. TODO: actual docs +func (p *Provisioner) ConfigSpec() hcldec.ObjectSpec { return p.config.FlatMapstructure().HCL2Spec() } + func (p *Provisioner) Prepare(raws ...interface{}) error { err := config.Decode( &p.config, diff --git a/provisioner/converge/provisioner.hcl2spec.go b/provisioner/converge/provisioner.hcl2spec.go index 1594f8f14..1d35a429f 100644 --- a/provisioner/converge/provisioner.hcl2spec.go +++ b/provisioner/converge/provisioner.hcl2spec.go @@ -31,10 +31,13 @@ type FlatConfig struct { // FlatMapstructure returns a new FlatConfig. // FlatConfig is an auto-generated flat version of Config. // Where the contents a fields with a `mapstructure:,squash` tag are bubbled up. -func (*Config) FlatMapstructure() interface{} { return new(FlatConfig) } +func (*Config) FlatMapstructure() interface{ HCL2Spec() map[string]hcldec.Spec } { + return new(FlatConfig) +} -// HCL2Spec returns the hcldec.Spec of a FlatConfig. -// This spec is used by HCL to read the fields of FlatConfig. +// HCL2Spec returns the hcl spec of a Config. +// This spec is used by HCL to read the fields of Config. +// The decoded values from this spec will then be applied to a FlatConfig. func (*FlatConfig) HCL2Spec() map[string]hcldec.Spec { s := map[string]hcldec.Spec{ "packer_build_name": &hcldec.AttrSpec{Name: "packer_build_name", Type: cty.String, Required: false}, @@ -48,7 +51,7 @@ func (*FlatConfig) HCL2Spec() map[string]hcldec.Spec { "version": &hcldec.AttrSpec{Name: "version", Type: cty.String, Required: false}, "bootstrap_command": &hcldec.AttrSpec{Name: "bootstrap_command", Type: cty.String, Required: false}, "prevent_bootstrap_sudo": &hcldec.AttrSpec{Name: "prevent_bootstrap_sudo", Type: cty.Bool, Required: false}, - "module_dirs": &hcldec.BlockListSpec{TypeName: "module_dirs", Nested: &hcldec.BlockSpec{TypeName: "module_dirs", Nested: hcldec.ObjectSpec((*FlatModuleDir)(nil).HCL2Spec())}}, + "module_dirs": &hcldec.BlockListSpec{TypeName: "module_dirs", Nested: hcldec.ObjectSpec((*FlatModuleDir)(nil).HCL2Spec())}, "module": &hcldec.AttrSpec{Name: "module", Type: cty.String, Required: false}, "working_directory": &hcldec.AttrSpec{Name: "working_directory", Type: cty.String, Required: false}, "params": &hcldec.BlockAttrsSpec{TypeName: "params", ElementType: cty.String, Required: false}, @@ -69,10 +72,13 @@ type FlatModuleDir struct { // FlatMapstructure returns a new FlatModuleDir. // FlatModuleDir is an auto-generated flat version of ModuleDir. // Where the contents a fields with a `mapstructure:,squash` tag are bubbled up. -func (*ModuleDir) FlatMapstructure() interface{} { return new(FlatModuleDir) } +func (*ModuleDir) FlatMapstructure() interface{ HCL2Spec() map[string]hcldec.Spec } { + return new(FlatModuleDir) +} -// HCL2Spec returns the hcldec.Spec of a FlatModuleDir. -// This spec is used by HCL to read the fields of FlatModuleDir. +// HCL2Spec returns the hcl spec of a ModuleDir. +// This spec is used by HCL to read the fields of ModuleDir. +// The decoded values from this spec will then be applied to a FlatModuleDir. func (*FlatModuleDir) HCL2Spec() map[string]hcldec.Spec { s := map[string]hcldec.Spec{ "source": &hcldec.AttrSpec{Name: "source", Type: cty.String, Required: false}, diff --git a/provisioner/file/provisioner.go b/provisioner/file/provisioner.go index 9fc73b950..e7dc1c15a 100644 --- a/provisioner/file/provisioner.go +++ b/provisioner/file/provisioner.go @@ -11,6 +11,7 @@ import ( "path/filepath" "strings" + "github.com/hashicorp/hcl/v2/hcldec" "github.com/hashicorp/packer/common" "github.com/hashicorp/packer/helper/config" "github.com/hashicorp/packer/packer" @@ -40,6 +41,8 @@ type Provisioner struct { config Config } +func (p *Provisioner) ConfigSpec() hcldec.ObjectSpec { return p.config.FlatMapstructure().HCL2Spec() } + func (p *Provisioner) Prepare(raws ...interface{}) error { err := config.Decode(&p.config, &config.DecodeOpts{ Interpolate: true, diff --git a/provisioner/file/provisioner.hcl2spec.go b/provisioner/file/provisioner.hcl2spec.go index 7a7c90d78..85a536403 100644 --- a/provisioner/file/provisioner.hcl2spec.go +++ b/provisioner/file/provisioner.hcl2spec.go @@ -26,10 +26,13 @@ type FlatConfig struct { // FlatMapstructure returns a new FlatConfig. // FlatConfig is an auto-generated flat version of Config. // Where the contents a fields with a `mapstructure:,squash` tag are bubbled up. -func (*Config) FlatMapstructure() interface{} { return new(FlatConfig) } +func (*Config) FlatMapstructure() interface{ HCL2Spec() map[string]hcldec.Spec } { + return new(FlatConfig) +} -// HCL2Spec returns the hcldec.Spec of a FlatConfig. -// This spec is used by HCL to read the fields of FlatConfig. +// HCL2Spec returns the hcl spec of a Config. +// This spec is used by HCL to read the fields of Config. +// The decoded values from this spec will then be applied to a FlatConfig. func (*FlatConfig) HCL2Spec() map[string]hcldec.Spec { s := map[string]hcldec.Spec{ "packer_build_name": &hcldec.AttrSpec{Name: "packer_build_name", Type: cty.String, Required: false}, diff --git a/provisioner/inspec/provisioner.go b/provisioner/inspec/provisioner.go index 0b7d047aa..099ca2e87 100644 --- a/provisioner/inspec/provisioner.go +++ b/provisioner/inspec/provisioner.go @@ -27,6 +27,7 @@ import ( "golang.org/x/crypto/ssh" + "github.com/hashicorp/hcl/v2/hcldec" "github.com/hashicorp/packer/common" "github.com/hashicorp/packer/common/adapter" "github.com/hashicorp/packer/helper/config" @@ -68,6 +69,8 @@ type Provisioner struct { inspecMajVersion uint } +func (p *Provisioner) ConfigSpec() hcldec.ObjectSpec { return p.config.FlatMapstructure().HCL2Spec() } + func (p *Provisioner) Prepare(raws ...interface{}) error { p.done = make(chan struct{}) diff --git a/provisioner/inspec/provisioner.hcl2spec.go b/provisioner/inspec/provisioner.hcl2spec.go index cc6411db5..8afc07f0f 100644 --- a/provisioner/inspec/provisioner.hcl2spec.go +++ b/provisioner/inspec/provisioner.hcl2spec.go @@ -34,10 +34,13 @@ type FlatConfig struct { // FlatMapstructure returns a new FlatConfig. // FlatConfig is an auto-generated flat version of Config. // Where the contents a fields with a `mapstructure:,squash` tag are bubbled up. -func (*Config) FlatMapstructure() interface{} { return new(FlatConfig) } +func (*Config) FlatMapstructure() interface{ HCL2Spec() map[string]hcldec.Spec } { + return new(FlatConfig) +} -// HCL2Spec returns the hcldec.Spec of a FlatConfig. -// This spec is used by HCL to read the fields of FlatConfig. +// HCL2Spec returns the hcl spec of a Config. +// This spec is used by HCL to read the fields of Config. +// The decoded values from this spec will then be applied to a FlatConfig. func (*FlatConfig) HCL2Spec() map[string]hcldec.Spec { s := map[string]hcldec.Spec{ "packer_build_name": &hcldec.AttrSpec{Name: "packer_build_name", Type: cty.String, Required: false}, diff --git a/provisioner/powershell/provisioner.go b/provisioner/powershell/provisioner.go index 5ff6cbe93..d07190179 100644 --- a/provisioner/powershell/provisioner.go +++ b/provisioner/powershell/provisioner.go @@ -16,6 +16,7 @@ import ( "strings" "time" + "github.com/hashicorp/hcl/v2/hcldec" "github.com/hashicorp/packer/common" "github.com/hashicorp/packer/common/retry" "github.com/hashicorp/packer/common/shell" @@ -97,6 +98,7 @@ func (p *Provisioner) defaultExecuteCommand() string { return fmt.Sprintf(`powershell -executionpolicy %s "%s"`, p.config.ExecutionPolicy, baseCmd) } } +func (p *Provisioner) ConfigSpec() hcldec.ObjectSpec { return p.config.FlatMapstructure().HCL2Spec() } func (p *Provisioner) Prepare(raws ...interface{}) error { // Create passthrough for winrm password so we can fill it in once we know diff --git a/provisioner/powershell/provisioner.hcl2spec.go b/provisioner/powershell/provisioner.hcl2spec.go index e1c543f62..81db6dc4f 100644 --- a/provisioner/powershell/provisioner.hcl2spec.go +++ b/provisioner/powershell/provisioner.hcl2spec.go @@ -37,10 +37,13 @@ type FlatConfig struct { // FlatMapstructure returns a new FlatConfig. // FlatConfig is an auto-generated flat version of Config. // Where the contents a fields with a `mapstructure:,squash` tag are bubbled up. -func (*Config) FlatMapstructure() interface{} { return new(FlatConfig) } +func (*Config) FlatMapstructure() interface{ HCL2Spec() map[string]hcldec.Spec } { + return new(FlatConfig) +} -// HCL2Spec returns the hcldec.Spec of a FlatConfig. -// This spec is used by HCL to read the fields of FlatConfig. +// HCL2Spec returns the hcl spec of a Config. +// This spec is used by HCL to read the fields of Config. +// The decoded values from this spec will then be applied to a FlatConfig. func (*FlatConfig) HCL2Spec() map[string]hcldec.Spec { s := map[string]hcldec.Spec{ "packer_build_name": &hcldec.AttrSpec{Name: "packer_build_name", Type: cty.String, Required: false}, diff --git a/provisioner/puppet-masterless/provisioner.go b/provisioner/puppet-masterless/provisioner.go index d66e54baa..ed43bc427 100644 --- a/provisioner/puppet-masterless/provisioner.go +++ b/provisioner/puppet-masterless/provisioner.go @@ -12,6 +12,7 @@ import ( "path/filepath" "strings" + "github.com/hashicorp/hcl/v2/hcldec" "github.com/hashicorp/packer/common" commonhelper "github.com/hashicorp/packer/helper/common" "github.com/hashicorp/packer/helper/config" @@ -150,6 +151,8 @@ type EnvVarsTemplate struct { WinRMPassword string } +func (p *Provisioner) ConfigSpec() hcldec.ObjectSpec { return p.config.FlatMapstructure().HCL2Spec() } + func (p *Provisioner) Prepare(raws ...interface{}) error { // Create passthrough for winrm password so we can fill it in once we know // it diff --git a/provisioner/puppet-masterless/provisioner.hcl2spec.go b/provisioner/puppet-masterless/provisioner.hcl2spec.go index d0deb5920..07ce05c12 100644 --- a/provisioner/puppet-masterless/provisioner.hcl2spec.go +++ b/provisioner/puppet-masterless/provisioner.hcl2spec.go @@ -37,10 +37,13 @@ type FlatConfig struct { // FlatMapstructure returns a new FlatConfig. // FlatConfig is an auto-generated flat version of Config. // Where the contents a fields with a `mapstructure:,squash` tag are bubbled up. -func (*Config) FlatMapstructure() interface{} { return new(FlatConfig) } +func (*Config) FlatMapstructure() interface{ HCL2Spec() map[string]hcldec.Spec } { + return new(FlatConfig) +} -// HCL2Spec returns the hcldec.Spec of a FlatConfig. -// This spec is used by HCL to read the fields of FlatConfig. +// HCL2Spec returns the hcl spec of a Config. +// This spec is used by HCL to read the fields of Config. +// The decoded values from this spec will then be applied to a FlatConfig. func (*FlatConfig) HCL2Spec() map[string]hcldec.Spec { s := map[string]hcldec.Spec{ "packer_build_name": &hcldec.AttrSpec{Name: "packer_build_name", Type: cty.String, Required: false}, diff --git a/provisioner/puppet-server/provisioner.go b/provisioner/puppet-server/provisioner.go index e4fc2adff..91b0f4ca6 100644 --- a/provisioner/puppet-server/provisioner.go +++ b/provisioner/puppet-server/provisioner.go @@ -11,6 +11,7 @@ import ( "path/filepath" "strings" + "github.com/hashicorp/hcl/v2/hcldec" "github.com/hashicorp/packer/common" commonhelper "github.com/hashicorp/packer/helper/common" "github.com/hashicorp/packer/helper/config" @@ -144,6 +145,8 @@ type EnvVarsTemplate struct { WinRMPassword string } +func (p *Provisioner) ConfigSpec() hcldec.ObjectSpec { return p.config.FlatMapstructure().HCL2Spec() } + func (p *Provisioner) Prepare(raws ...interface{}) error { // Create passthrough for winrm password so we can fill it in once we know // it diff --git a/provisioner/puppet-server/provisioner.hcl2spec.go b/provisioner/puppet-server/provisioner.hcl2spec.go index 3c2eb2cde..5535856c9 100644 --- a/provisioner/puppet-server/provisioner.hcl2spec.go +++ b/provisioner/puppet-server/provisioner.hcl2spec.go @@ -37,10 +37,13 @@ type FlatConfig struct { // FlatMapstructure returns a new FlatConfig. // FlatConfig is an auto-generated flat version of Config. // Where the contents a fields with a `mapstructure:,squash` tag are bubbled up. -func (*Config) FlatMapstructure() interface{} { return new(FlatConfig) } +func (*Config) FlatMapstructure() interface{ HCL2Spec() map[string]hcldec.Spec } { + return new(FlatConfig) +} -// HCL2Spec returns the hcldec.Spec of a FlatConfig. -// This spec is used by HCL to read the fields of FlatConfig. +// HCL2Spec returns the hcl spec of a Config. +// This spec is used by HCL to read the fields of Config. +// The decoded values from this spec will then be applied to a FlatConfig. func (*FlatConfig) HCL2Spec() map[string]hcldec.Spec { s := map[string]hcldec.Spec{ "packer_build_name": &hcldec.AttrSpec{Name: "packer_build_name", Type: cty.String, Required: false}, diff --git a/provisioner/salt-masterless/provisioner.go b/provisioner/salt-masterless/provisioner.go index 5531d76b9..2fddb034f 100644 --- a/provisioner/salt-masterless/provisioner.go +++ b/provisioner/salt-masterless/provisioner.go @@ -13,6 +13,7 @@ import ( "path/filepath" "strings" + "github.com/hashicorp/hcl/v2/hcldec" "github.com/hashicorp/packer/common" "github.com/hashicorp/packer/helper/config" "github.com/hashicorp/packer/packer" @@ -108,6 +109,8 @@ var guestOSTypeConfigs = map[string]guestOSTypeConfig{ }, } +func (p *Provisioner) ConfigSpec() hcldec.ObjectSpec { return p.config.FlatMapstructure().HCL2Spec() } + func (p *Provisioner) Prepare(raws ...interface{}) error { err := config.Decode(&p.config, &config.DecodeOpts{ Interpolate: true, diff --git a/provisioner/salt-masterless/provisioner.hcl2spec.go b/provisioner/salt-masterless/provisioner.hcl2spec.go index 9d918990b..4bd24fd11 100644 --- a/provisioner/salt-masterless/provisioner.hcl2spec.go +++ b/provisioner/salt-masterless/provisioner.hcl2spec.go @@ -38,10 +38,13 @@ type FlatConfig struct { // FlatMapstructure returns a new FlatConfig. // FlatConfig is an auto-generated flat version of Config. // Where the contents a fields with a `mapstructure:,squash` tag are bubbled up. -func (*Config) FlatMapstructure() interface{} { return new(FlatConfig) } +func (*Config) FlatMapstructure() interface{ HCL2Spec() map[string]hcldec.Spec } { + return new(FlatConfig) +} -// HCL2Spec returns the hcldec.Spec of a FlatConfig. -// This spec is used by HCL to read the fields of FlatConfig. +// HCL2Spec returns the hcl spec of a Config. +// This spec is used by HCL to read the fields of Config. +// The decoded values from this spec will then be applied to a FlatConfig. func (*FlatConfig) HCL2Spec() map[string]hcldec.Spec { s := map[string]hcldec.Spec{ "packer_build_name": &hcldec.AttrSpec{Name: "packer_build_name", Type: cty.String, Required: false}, diff --git a/provisioner/shell-local/provisioner.go b/provisioner/shell-local/provisioner.go index f92f93c70..186644a42 100644 --- a/provisioner/shell-local/provisioner.go +++ b/provisioner/shell-local/provisioner.go @@ -3,6 +3,7 @@ package shell import ( "context" + "github.com/hashicorp/hcl/v2/hcldec" sl "github.com/hashicorp/packer/common/shell-local" "github.com/hashicorp/packer/packer" ) @@ -11,6 +12,8 @@ type Provisioner struct { config sl.Config } +func (p *Provisioner) ConfigSpec() hcldec.ObjectSpec { return p.config.FlatMapstructure().HCL2Spec() } + func (p *Provisioner) Prepare(raws ...interface{}) error { err := sl.Decode(&p.config, raws...) if err != nil { diff --git a/provisioner/shell/provisioner.go b/provisioner/shell/provisioner.go index 824225aee..f3a36c11c 100644 --- a/provisioner/shell/provisioner.go +++ b/provisioner/shell/provisioner.go @@ -17,6 +17,7 @@ import ( "strings" "time" + "github.com/hashicorp/hcl/v2/hcldec" "github.com/hashicorp/packer/common" "github.com/hashicorp/packer/common/retry" "github.com/hashicorp/packer/common/shell" @@ -75,6 +76,8 @@ type ExecuteCommandTemplate struct { Path string } +func (p *Provisioner) ConfigSpec() hcldec.ObjectSpec { return p.config.FlatMapstructure().HCL2Spec() } + func (p *Provisioner) Prepare(raws ...interface{}) error { err := config.Decode(&p.config, &config.DecodeOpts{ Interpolate: true, diff --git a/provisioner/shell/provisioner.hcl2spec.go b/provisioner/shell/provisioner.hcl2spec.go index 9b48d3c3f..6e6d88a85 100644 --- a/provisioner/shell/provisioner.hcl2spec.go +++ b/provisioner/shell/provisioner.hcl2spec.go @@ -38,10 +38,13 @@ type FlatConfig struct { // FlatMapstructure returns a new FlatConfig. // FlatConfig is an auto-generated flat version of Config. // Where the contents a fields with a `mapstructure:,squash` tag are bubbled up. -func (*Config) FlatMapstructure() interface{} { return new(FlatConfig) } +func (*Config) FlatMapstructure() interface{ HCL2Spec() map[string]hcldec.Spec } { + return new(FlatConfig) +} -// HCL2Spec returns the hcldec.Spec of a FlatConfig. -// This spec is used by HCL to read the fields of FlatConfig. +// HCL2Spec returns the hcl spec of a Config. +// This spec is used by HCL to read the fields of Config. +// The decoded values from this spec will then be applied to a FlatConfig. func (*FlatConfig) HCL2Spec() map[string]hcldec.Spec { s := map[string]hcldec.Spec{ "packer_build_name": &hcldec.AttrSpec{Name: "packer_build_name", Type: cty.String, Required: false}, diff --git a/provisioner/sleep/provisioner.go b/provisioner/sleep/provisioner.go index 646f1c4dd..d87152149 100644 --- a/provisioner/sleep/provisioner.go +++ b/provisioner/sleep/provisioner.go @@ -6,6 +6,7 @@ import ( "context" "time" + "github.com/hashicorp/hcl/v2/hcldec" "github.com/hashicorp/packer/helper/config" "github.com/hashicorp/packer/packer" ) @@ -16,6 +17,10 @@ type Provisioner struct { var _ packer.Provisioner = new(Provisioner) +func (p *Provisioner) ConfigSpec() hcldec.ObjectSpec { return p.FlatMapstructure().HCL2Spec() } + +func (p *Provisioner) FlatConfig() interface{} { return p.FlatMapstructure() } + func (p *Provisioner) Prepare(raws ...interface{}) error { return config.Decode(&p, &config.DecodeOpts{}, raws...) } diff --git a/provisioner/sleep/provisioner.hcl2spec.go b/provisioner/sleep/provisioner.hcl2spec.go index 60f3ed89d..fbdec1b02 100644 --- a/provisioner/sleep/provisioner.hcl2spec.go +++ b/provisioner/sleep/provisioner.hcl2spec.go @@ -15,10 +15,13 @@ type FlatProvisioner struct { // FlatMapstructure returns a new FlatProvisioner. // FlatProvisioner is an auto-generated flat version of Provisioner. // Where the contents a fields with a `mapstructure:,squash` tag are bubbled up. -func (*Provisioner) FlatMapstructure() interface{} { return new(FlatProvisioner) } +func (*Provisioner) FlatMapstructure() interface{ HCL2Spec() map[string]hcldec.Spec } { + return new(FlatProvisioner) +} -// HCL2Spec returns the hcldec.Spec of a FlatProvisioner. -// This spec is used by HCL to read the fields of FlatProvisioner. +// HCL2Spec returns the hcl spec of a Provisioner. +// This spec is used by HCL to read the fields of Provisioner. +// The decoded values from this spec will then be applied to a FlatProvisioner. func (*FlatProvisioner) HCL2Spec() map[string]hcldec.Spec { s := map[string]hcldec.Spec{ "duration": &hcldec.AttrSpec{Name: "duration", Type: cty.String, Required: false}, diff --git a/provisioner/windows-restart/provisioner.go b/provisioner/windows-restart/provisioner.go index f2bc01d30..69f073690 100644 --- a/provisioner/windows-restart/provisioner.go +++ b/provisioner/windows-restart/provisioner.go @@ -13,6 +13,7 @@ import ( "sync" "time" + "github.com/hashicorp/hcl/v2/hcldec" "github.com/hashicorp/packer/common" "github.com/hashicorp/packer/common/retry" "github.com/hashicorp/packer/helper/config" @@ -63,6 +64,8 @@ type Provisioner struct { cancelLock sync.Mutex } +func (p *Provisioner) ConfigSpec() hcldec.ObjectSpec { return p.config.FlatMapstructure().HCL2Spec() } + func (p *Provisioner) Prepare(raws ...interface{}) error { err := config.Decode(&p.config, &config.DecodeOpts{ Interpolate: true, diff --git a/provisioner/windows-restart/provisioner.hcl2spec.go b/provisioner/windows-restart/provisioner.hcl2spec.go index eda8f486e..e2de58e75 100644 --- a/provisioner/windows-restart/provisioner.hcl2spec.go +++ b/provisioner/windows-restart/provisioner.hcl2spec.go @@ -26,10 +26,13 @@ type FlatConfig struct { // FlatMapstructure returns a new FlatConfig. // FlatConfig is an auto-generated flat version of Config. // Where the contents a fields with a `mapstructure:,squash` tag are bubbled up. -func (*Config) FlatMapstructure() interface{} { return new(FlatConfig) } +func (*Config) FlatMapstructure() interface{ HCL2Spec() map[string]hcldec.Spec } { + return new(FlatConfig) +} -// HCL2Spec returns the hcldec.Spec of a FlatConfig. -// This spec is used by HCL to read the fields of FlatConfig. +// HCL2Spec returns the hcl spec of a Config. +// This spec is used by HCL to read the fields of Config. +// The decoded values from this spec will then be applied to a FlatConfig. func (*FlatConfig) HCL2Spec() map[string]hcldec.Spec { s := map[string]hcldec.Spec{ "packer_build_name": &hcldec.AttrSpec{Name: "packer_build_name", Type: cty.String, Required: false}, diff --git a/provisioner/windows-shell/provisioner.go b/provisioner/windows-shell/provisioner.go index 2f42b145d..c5a1289e0 100644 --- a/provisioner/windows-shell/provisioner.go +++ b/provisioner/windows-shell/provisioner.go @@ -15,6 +15,7 @@ import ( "strings" "time" + "github.com/hashicorp/hcl/v2/hcldec" "github.com/hashicorp/packer/common" "github.com/hashicorp/packer/common/retry" "github.com/hashicorp/packer/common/shell" @@ -51,6 +52,8 @@ type ExecuteCommandTemplate struct { Path string } +func (p *Provisioner) ConfigSpec() hcldec.ObjectSpec { return p.config.FlatMapstructure().HCL2Spec() } + func (p *Provisioner) Prepare(raws ...interface{}) error { err := config.Decode(&p.config, &config.DecodeOpts{ Interpolate: true, diff --git a/provisioner/windows-shell/provisioner.hcl2spec.go b/provisioner/windows-shell/provisioner.hcl2spec.go index ee62b6bf2..af5d7c5ec 100644 --- a/provisioner/windows-shell/provisioner.hcl2spec.go +++ b/provisioner/windows-shell/provisioner.hcl2spec.go @@ -31,10 +31,13 @@ type FlatConfig struct { // FlatMapstructure returns a new FlatConfig. // FlatConfig is an auto-generated flat version of Config. // Where the contents a fields with a `mapstructure:,squash` tag are bubbled up. -func (*Config) FlatMapstructure() interface{} { return new(FlatConfig) } +func (*Config) FlatMapstructure() interface{ HCL2Spec() map[string]hcldec.Spec } { + return new(FlatConfig) +} -// HCL2Spec returns the hcldec.Spec of a FlatConfig. -// This spec is used by HCL to read the fields of FlatConfig. +// HCL2Spec returns the hcl spec of a Config. +// This spec is used by HCL to read the fields of Config. +// The decoded values from this spec will then be applied to a FlatConfig. func (*FlatConfig) HCL2Spec() map[string]hcldec.Spec { s := map[string]hcldec.Spec{ "packer_build_name": &hcldec.AttrSpec{Name: "packer_build_name", Type: cty.String, Required: false}, diff --git a/template/template.hcl2spec.go b/template/template.hcl2spec.go index 988b6fc79..c5b7d11a5 100644 --- a/template/template.hcl2spec.go +++ b/template/template.hcl2spec.go @@ -21,10 +21,13 @@ type FlatProvisioner struct { // FlatMapstructure returns a new FlatProvisioner. // FlatProvisioner is an auto-generated flat version of Provisioner. // Where the contents a fields with a `mapstructure:,squash` tag are bubbled up. -func (*Provisioner) FlatMapstructure() interface{} { return new(FlatProvisioner) } +func (*Provisioner) FlatMapstructure() interface{ HCL2Spec() map[string]hcldec.Spec } { + return new(FlatProvisioner) +} -// HCL2Spec returns the hcldec.Spec of a FlatProvisioner. -// This spec is used by HCL to read the fields of FlatProvisioner. +// HCL2Spec returns the hcl spec of a Provisioner. +// This spec is used by HCL to read the fields of Provisioner. +// The decoded values from this spec will then be applied to a FlatProvisioner. func (*FlatProvisioner) HCL2Spec() map[string]hcldec.Spec { s := map[string]hcldec.Spec{ "only": &hcldec.AttrSpec{Name: "only", Type: cty.List(cty.String), Required: false}, diff --git a/vendor/github.com/zclconf/go-cty/cty/convert/conversion_primitive.go b/vendor/github.com/zclconf/go-cty/cty/convert/conversion_primitive.go index e0dbf491e..0d6fae964 100644 --- a/vendor/github.com/zclconf/go-cty/cty/convert/conversion_primitive.go +++ b/vendor/github.com/zclconf/go-cty/cty/convert/conversion_primitive.go @@ -1,6 +1,8 @@ package convert import ( + "strings" + "github.com/zclconf/go-cty/cty" ) @@ -41,7 +43,14 @@ var primitiveConversionsUnsafe = map[cty.Type]map[cty.Type]conversion{ case "false", "0": return cty.False, nil default: - return cty.NilVal, path.NewErrorf("a bool is required") + switch strings.ToLower(val.AsString()) { + case "true": + return cty.NilVal, path.NewErrorf("a bool is required; to convert from string, use lowercase \"true\"") + case "false": + return cty.NilVal, path.NewErrorf("a bool is required; to convert from string, use lowercase \"false\"") + default: + return cty.NilVal, path.NewErrorf("a bool is required") + } } }, }, diff --git a/vendor/github.com/zclconf/go-cty/cty/function/stdlib/format.go b/vendor/github.com/zclconf/go-cty/cty/function/stdlib/format.go index 664790b46..834e9b6fc 100644 --- a/vendor/github.com/zclconf/go-cty/cty/function/stdlib/format.go +++ b/vendor/github.com/zclconf/go-cty/cty/function/stdlib/format.go @@ -80,6 +80,7 @@ var FormatListFunc = function.New(&function.Spec{ lenChooser := -1 iterators := make([]cty.ElementIterator, len(args)) singleVals := make([]cty.Value, len(args)) + unknowns := make([]bool, len(args)) for i, arg := range args { argTy := arg.Type() switch { @@ -87,7 +88,8 @@ var FormatListFunc = function.New(&function.Spec{ if !argTy.IsTupleType() && !arg.IsKnown() { // We can't iterate this one at all yet then, so we can't // yet produce a result. - return cty.UnknownVal(retType), nil + unknowns[i] = true + continue } thisLen := arg.LengthInt() if iterLen == -1 { @@ -103,12 +105,26 @@ var FormatListFunc = function.New(&function.Spec{ ) } } + if !arg.IsKnown() { + // We allowed an unknown tuple value to fall through in + // our initial check above so that we'd be able to run + // the above error checks against it, but we still can't + // iterate it if the checks pass. + unknowns[i] = true + continue + } iterators[i] = arg.ElementIterator() default: singleVals[i] = arg } } + for _, isUnk := range unknowns { + if isUnk { + return cty.UnknownVal(retType), nil + } + } + if iterLen == 0 { // If our sequences are all empty then our result must be empty. return cty.ListValEmpty(cty.String), nil diff --git a/vendor/github.com/zclconf/go-cty/cty/gob.go b/vendor/github.com/zclconf/go-cty/cty/gob.go index a77dace27..6c972d7de 100644 --- a/vendor/github.com/zclconf/go-cty/cty/gob.go +++ b/vendor/github.com/zclconf/go-cty/cty/gob.go @@ -5,6 +5,8 @@ import ( "encoding/gob" "fmt" "math/big" + + "github.com/zclconf/go-cty/cty/set" ) // GobEncode is an implementation of the gob.GobEncoder interface, which @@ -46,11 +48,12 @@ func (val *Value) GobDecode(buf []byte) error { return fmt.Errorf("unsupported cty.Value encoding version %d; only 0 is supported", gv.Version) } - // big.Float seems to, for some reason, lose its "pointerness" when we - // round-trip it, so we'll fix that here. - if bf, ok := gv.V.(big.Float); ok { - gv.V = &bf - } + // Because big.Float.GobEncode is implemented with a pointer reciever, + // gob encoding of an interface{} containing a *big.Float value does not + // round-trip correctly, emerging instead as a non-pointer big.Float. + // The rest of cty expects all number values to be represented by + // *big.Float, so we'll fix that up here. + gv.V = gobDecodeFixNumberPtr(gv.V, gv.Ty) val.ty = gv.Ty val.v = gv.V @@ -123,3 +126,74 @@ type gobType struct { type gobCapsuleTypeImpl struct { } + +// goDecodeFixNumberPtr fixes an unfortunate quirk of round-tripping cty.Number +// values through gob: the big.Float.GobEncode method is implemented on a +// pointer receiver, and so it loses the "pointer-ness" of the value on +// encode, causing the values to emerge the other end as big.Float rather than +// *big.Float as we expect elsewhere in cty. +// +// The implementation of gobDecodeFixNumberPtr mutates the given raw value +// during its work, and may either return the same value mutated or a new +// value. Callers must no longer use whatever value they pass as "raw" after +// this function is called. +func gobDecodeFixNumberPtr(raw interface{}, ty Type) interface{} { + // Unfortunately we need to work recursively here because number values + // might be embedded in structural or collection type values. + + switch { + case ty.Equals(Number): + if bf, ok := raw.(big.Float); ok { + return &bf // wrap in pointer + } + case ty.IsMapType() && ty.ElementType().Equals(Number): + if m, ok := raw.(map[string]interface{}); ok { + for k, v := range m { + m[k] = gobDecodeFixNumberPtr(v, ty.ElementType()) + } + } + case ty.IsListType() && ty.ElementType().Equals(Number): + if s, ok := raw.([]interface{}); ok { + for i, v := range s { + s[i] = gobDecodeFixNumberPtr(v, ty.ElementType()) + } + } + case ty.IsSetType() && ty.ElementType().Equals(Number): + if s, ok := raw.(set.Set); ok { + newS := set.NewSet(s.Rules()) + for it := s.Iterator(); it.Next(); { + newV := gobDecodeFixNumberPtr(it.Value(), ty.ElementType()) + newS.Add(newV) + } + return newS + } + case ty.IsObjectType(): + if m, ok := raw.(map[string]interface{}); ok { + for k, v := range m { + aty := ty.AttributeType(k) + m[k] = gobDecodeFixNumberPtr(v, aty) + } + } + case ty.IsTupleType(): + if s, ok := raw.([]interface{}); ok { + for i, v := range s { + ety := ty.TupleElementType(i) + s[i] = gobDecodeFixNumberPtr(v, ety) + } + } + } + + return raw +} + +// gobDecodeFixNumberPtrVal is a helper wrapper around gobDecodeFixNumberPtr +// that works with already-constructed values. This is primarily for testing, +// to fix up intentionally-invalid number values for the parts of the test +// code that need them to be valid, such as calling GoString on them. +func gobDecodeFixNumberPtrVal(v Value) Value { + raw := gobDecodeFixNumberPtr(v.v, v.ty) + return Value{ + v: raw, + ty: v.ty, + } +} diff --git a/vendor/github.com/zclconf/go-cty/cty/set_internals.go b/vendor/github.com/zclconf/go-cty/cty/set_internals.go index 3fd4fb2df..f1ec98556 100644 --- a/vendor/github.com/zclconf/go-cty/cty/set_internals.go +++ b/vendor/github.com/zclconf/go-cty/cty/set_internals.go @@ -147,6 +147,17 @@ func appendSetHashBytes(val Value, buf *bytes.Buffer) { switch val.ty { case Number: + // Due to an unfortunate quirk of gob encoding for big.Float, we end up + // with non-pointer values immediately after a gob round-trip, and + // we end up in here before we've had a chance to run + // gobDecodeFixNumberPtr on the inner values of a gob-encoded set, + // and so sadly we must make a special effort to handle that situation + // here just so that we can get far enough along to fix it up for + // everything else in this package. + if bf, ok := val.v.(big.Float); ok { + buf.WriteString(bf.String()) + return + } buf.WriteString(val.v.(*big.Float).String()) return case Bool: diff --git a/vendor/modules.txt b/vendor/modules.txt index 632ccc2ba..fdb69eaed 100644 --- a/vendor/modules.txt +++ b/vendor/modules.txt @@ -618,7 +618,7 @@ github.com/yandex-cloud/go-sdk/pkg/retry github.com/yandex-cloud/go-sdk/pkg/sdkerrors github.com/yandex-cloud/go-sdk/pkg/singleflight github.com/yandex-cloud/go-sdk/sdkresolvers -# github.com/zclconf/go-cty v1.1.0 +# github.com/zclconf/go-cty v1.1.2-0.20191126233707-f0f7fd24c4af github.com/zclconf/go-cty/cty github.com/zclconf/go-cty/cty/convert github.com/zclconf/go-cty/cty/function diff --git a/website/source/guides/hcl/from-json-v1/index.html.md.erb b/website/source/guides/hcl/from-json-v1/index.html.md.erb new file mode 100644 index 000000000..01a8e2278 --- /dev/null +++ b/website/source/guides/hcl/from-json-v1/index.html.md.erb @@ -0,0 +1,179 @@ +--- +layout: guides +page_title: Transforming Packer v1 files for Packer v1.5.0 +sidebar_current: hcl +description: |- + Learn how to manually move from a Packer v1 working JSON build file to a + working v1.5.0 HCL file. +--- + +# Transforming Packer v1 files for Packer v1.5 + +-> **Note:** Starting from version **1.5.0** Packer can read HCL2 files. + +We will soon provide a programatical a way to transpose a v1 buildfile to a v1.5 +HCL file. In the meantime we will show how to manually do it. + +The following file : + +```json +{ + "builders": [ + { + "ami_name": "packer-test", + "region": "us-east-1" + "instance_type": "t2.micro" + + "source_ami_filter": { + "filters": { + "virtualization-type": "hvm", + "name": "ubuntu/images/*ubuntu-xenial-16.04-amd64-server-*", + "root-device-type": "ebs" + }, + "owners": ["amazon"], + "most_recent": true + }, + + "ssh_username": "ubuntu", + "type": "amazon-ebs" + } + ], + "provisioners": [ + { + "type": "shell", + "inline": [ + "sleep 5" + ] + } + ] +} +``` + +Becomes: + +```hcl +# the source block is what was defined in the builders section and represents a +# reusable way to start a machine. You build your images from that source. All +# sources have a 1:1 correspondance to what currently is a builder. The +# argument name (ie: ami_name) must be unquoted and can be set using the equal +# sign operator (=). +source "amazon-ebs" "example" { + ami_name = "packer-test" + region = "us-east-1" + instance_type = "t2.micro" + + source_ami_filter { + filters { + virtualization-type = "hvm" + name = "ubuntu/images/*ubuntu-xenial-16.04-amd64-server-*" + root-device-type = "ebs" + } + owners = ["amazon"] + most_recent = true + } + + communicator = "ssh" + ssh_username = "ec2-user" +} + +# A build starts sources and runs provisioning steps on those sources. +build { + sources = [ + # there can be multiple sources per build + "source.amazon-ebs.example" + ] + + # All provisioners and post-processors have a 1:1 correspondence to their + # current layout. The argument name (ie: inline) must to be unquoted + # and can be set using the equal sign operator (=). + provisioner "shell" { + inline = ["sleep 5"] + } + + # post-processors work too, example: `post-processor "shell-local" {}`. +} + +``` + +### 1:1 correspondence of components ... except : + +All fields of builders, provisioners and post-processors have a 1:1 +correspondance except for the following: + +* builders: + * aws ami_block_device_mappings + * aws launch_block_device_mappings + * aws run_volume_tags + * alicloud image_disk_mappings + * osc omi_block_device_mappings + * osc launch_block_device_mappings + * proxmox network_adapters + * proxmox disks + * tencentcloud data_disks + * ucloud image_copy_to_mappings + + +* provisioner: + * converge module_dirs + +* post-processor: + * alicloud-import image_disk_mappings + +One could think that these are defined as "arrays of blocks" - they are in fact +repeatable blocks with the same identifier. For example: + +```json +"builders": [ + { + "type": "amazon-ebs", + "launch_block_device_mappings": [ + { + "device_name": "/dev/xvda", + "volume_size": "20", + "volume_type": "gp2", + "delete_on_termination": "true" + }, + { + "device_name": "/dev/xvdf", + "volume_size": "500", + "volume_type": "gp2", + "delete_on_termination": "true", + "encrypted": true + } + ], + } +``` + +Becomes: + +```hcl +source "amazon-ebs" "example" { + launch_block_device_mappings { + device_name = "/dev/xvda" + volume_size = 20 + volume_type = "gp2" + delete_on_termination = true + } + launch_block_device_mappings { + device_name = "/dev/xvdf" + volume_size = 500 + volume_type = "gp2" + delete_on_termination = true + encrypted = true + } +``` + +There is soon going to be a PR to drop the `s` at the end of these fields. + +### Deprecation + +The current layout of buildfiles will be supported until we and the community +love the new format. Only then the v1 format will be carefully deprecated. + +-> **Note:** The HCL parsing library can read JSON and if it is your +configuration format of predilection, you will still be able to do it. You will +have to tweak a few things in order to use future versions of Packer that have +deprecated the current format. Sorry about that! Because the HCL reading code +is generated from the JSON parsing settings; every builder, provisioner and +post-processor setting should look and work the same. A config file transposer +is currently in the making. diff --git a/website/source/guides/hcl/index.html.md.erb b/website/source/guides/hcl/index.html.md.erb new file mode 100644 index 000000000..5d66496bf --- /dev/null +++ b/website/source/guides/hcl/index.html.md.erb @@ -0,0 +1,52 @@ +--- +layout: guides +sidebar_current: hcl +page_title: Getting started configuring Packer with HCL2 files +--- + +# Introduction to Packer HCL2 + +-> **Note:** Starting from version **1.5.0** Packer can read HCL2 files. + +It is not necessary to know all of the details of the HCL syntax in order to +use Packer, and so this page summarizes the most important details to get you +started. If you are interested, you can find a [full definition of HCL +syntax](https://github.com/hashicorp/hcl2/blob/master/hcl/hclsyntax/spec.md) in +the HCL native syntax specification. + +## Arguments and Blocks + +The HCL syntax is built around two key syntax constructs: arguments and blocks. + +```hcl +# block +source "amazon-ebs" "example" { + + # argument + ami_name = "abc123" +} +``` + +## Comments + +The HCL language supports three different syntaxes for comments: + +* ```#``` begins a single-line comment, ending at the end of the line. +* ```//``` also begins a single-line comment, as an alternative to ```#```. +* ```/*``` and ```*/``` are start and end delimiters for a comment that might + span over multiple lines. + +## Multi-line strings + +A multi-line string value can be provided using heredoc syntax. + +```hcl +variable "long_key" { + type = "string" + default = < <% content_for :sidebar do %>