Compare commits
170 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| a416158371 | |||
| fbe6ebebf1 | |||
| e441733cbd | |||
| b4b0df44b4 | |||
| 25920b2bd3 | |||
| 74a6c1987c | |||
| 160be7e773 | |||
| fc23eb9241 | |||
| ff5b55b560 | |||
| 95fb13209a | |||
| 5515d7293f | |||
| 6107aa51ef | |||
| d398487afd | |||
| 0f86c66f8b | |||
| 822dcf93af | |||
| 11a7b4f74e | |||
| ae1069d3df | |||
| a1a5cf0113 | |||
| 60017822e0 | |||
| 2016d6baec | |||
| 2967fccfd7 | |||
| 95e8263280 | |||
| 1f4971f5ac | |||
| 44d19f160a | |||
| 557bffc94a | |||
| 08fd0a7e33 | |||
| 079786f8d0 | |||
| 9a11fd4136 | |||
| c7545c37dd | |||
| 2b35873dd9 | |||
| 8226bc9d9f | |||
| ffa8b7de8a | |||
| e9936cf0da | |||
| eb4a6f30a0 | |||
| fa844543ec | |||
| e8ccd5304b | |||
| fca92b1953 | |||
| a29075d6c1 | |||
| 7ad99c2c85 | |||
| 5385275836 | |||
| 591b1c2637 | |||
| f48d7e3990 | |||
| c9c65383e9 | |||
| 1ddbe4d9ce | |||
| dd167925d7 | |||
| 317e2da000 | |||
| c4a22e9a56 | |||
| aef74cd059 | |||
| b3b4559434 | |||
| ac2ce0097c | |||
| ca0b11028e | |||
| b199ce9a22 | |||
| 0f83ba6ee6 | |||
| 3e754c9f3f | |||
| 7f0b41bb0e | |||
| 482a2c8232 | |||
| 3d55fa51d8 | |||
| 37d05c85ff | |||
| ab89df9a88 | |||
| 00e503388e | |||
| bf5b4d63da | |||
| 21c6811334 | |||
| 7ea4a779af | |||
| a939671abb | |||
| c6c047905d | |||
| c6c8c88b74 | |||
| 3b40127cb0 | |||
| 993ae765e5 | |||
| 201869d627 | |||
| 6ea7edf4e9 | |||
| 99087d71bc | |||
| 1710590418 | |||
| 10d32bda6d | |||
| 0d14df3020 | |||
| 2065de0b4f | |||
| 728c5a217d | |||
| f48583c57e | |||
| 6cc3933c77 | |||
| 4409991887 | |||
| 6e195f92e8 | |||
| 715fe51517 | |||
| d2a8408577 | |||
| 192e85951f | |||
| e2a337e0ea | |||
| c29b9f957d | |||
| 514be931be | |||
| 1049db4f98 | |||
| 9ce9f49f4d | |||
| 47adccabf2 | |||
| 1a20146993 | |||
| 496e3ed07b | |||
| 9718af4982 | |||
| e0557f84e9 | |||
| f7ee3f8ead | |||
| 8208f425c8 | |||
| 516e919c5e | |||
| 13b34e2c73 | |||
| ba87656273 | |||
| 429262030f | |||
| 4487152d1e | |||
| 72e4dc4cb5 | |||
| 7809242f41 | |||
| 632e918c52 | |||
| 4cb94a67b0 | |||
| 029729225d | |||
| aeecfcd422 | |||
| 9f545c28fe | |||
| fe12d53e77 | |||
| 00fce3c46f | |||
| bd7b31853e | |||
| cac6b04546 | |||
| 80ed988ffe | |||
| b20dea6aec | |||
| 4f5af3b919 | |||
| e69410b633 | |||
| 855ba9775a | |||
| 76e3d57cae | |||
| 7089e0854a | |||
| 40c2b2a153 | |||
| 3677069010 | |||
| b321c64c1e | |||
| ad0ce49cec | |||
| 021044e963 | |||
| ba8484ee6b | |||
| 3052e3c5d5 | |||
| c5fca1f876 | |||
| 915372c73d | |||
| 37769c2b95 | |||
| 2c08479825 | |||
| 774c5903f6 | |||
| fd8e76636a | |||
| 50a6732859 | |||
| 4e2d2961b6 | |||
| 926e287025 | |||
| d28e6fe009 | |||
| 1e889078fd | |||
| 6c77d28537 | |||
| 92f384b52f | |||
| 29beb051c5 | |||
| bb03a321f2 | |||
| 25b2ab9082 | |||
| 9986834a62 | |||
| 368ccfe7c9 | |||
| ffbb110167 | |||
| 9afaa5a21f | |||
| bcd60c3895 | |||
| f5006d0842 | |||
| 88c516b2d5 | |||
| 6b947edd04 | |||
| d7bb60ea86 | |||
| 08e67f8990 | |||
| 8c3b3ca00f | |||
| 4a686ef66d | |||
| f616955ebc | |||
| a1b2e71005 | |||
| d53488db68 | |||
| b6cfe16444 | |||
| 4c02123142 | |||
| 13320650f0 | |||
| 2324f433f7 | |||
| ece5e94c3d | |||
| ad239bd2b9 | |||
| 39d550054d | |||
| ef4d35097b | |||
| a63ad19b0c | |||
| ca123721a6 | |||
| 540effbbc0 | |||
| 30b0229fb4 | |||
| 34b86c0228 | |||
| 9b1d31cee7 |
@@ -19,7 +19,7 @@ can quickly merge or address your contributions.
|
||||
already fixed the bug you're experiencing.
|
||||
|
||||
- Run the command with debug output with the environment variable `PACKER_LOG`.
|
||||
For example: `PACKER_LOG=1 packer build template.json`. Take the _entire_
|
||||
For example: `PACKER_LOG=1 packer build template.pkr.hcl`. Take the _entire_
|
||||
output and create a [gist](https://gist.github.com) for linking to in your
|
||||
issue. Packer should strip sensitive keys from the output, but take a look
|
||||
through just in case.
|
||||
@@ -91,7 +91,7 @@ The instructions below are for go 1.7. or later.
|
||||
4. After running building Packer successfully, use
|
||||
`$GOPATH/src/github.com/hashicorp/packer/bin/packer` to build a machine and
|
||||
verify your changes work. For instance:
|
||||
`$GOPATH/src/github.com/hashicorp/packer/bin/packer build template.json`.
|
||||
`$GOPATH/src/github.com/hashicorp/packer/bin/packer build template.pkr.hcl`.
|
||||
|
||||
5. If everything works well and the tests pass, run `go fmt` on your code before
|
||||
submitting a pull-request.
|
||||
|
||||
+87
-3
@@ -1,15 +1,99 @@
|
||||
## 1.7.0 (Upcoming)
|
||||
## 1.7.1 (Upcoming)
|
||||
|
||||
### FEATURES
|
||||
### IMPROVEMENTS
|
||||
* core: Change template parsing error to include warning about file extensions.
|
||||
[GH-10652]
|
||||
|
||||
### BUG FIXES
|
||||
* builder/azure: Don't overwrite subscription id if unset. [GH-10659]
|
||||
* builder/oracle-oci: Update Oracle Go SDK to fix issue with reading key file.
|
||||
[GH-10560]
|
||||
* core/hcl2_upgrade: Make hcl2_upgrade command correctly translate
|
||||
pause_before. [GH-10654]
|
||||
* core: Templates previously could not interpolate the environment variable
|
||||
PACKER_LOG_PATH. [GH-10660]
|
||||
|
||||
## 1.7.0 (February 17, 2021)
|
||||
|
||||
### FEATURES
|
||||
* **New Command** (HCL only) `packer init` command will download plugins defined
|
||||
in a new `required_plugins` block [GH-10304] [GH-10633].
|
||||
* **New Plugin Type** Data sources can be implemented (blog post forthcoming).
|
||||
[GH-10440]
|
||||
* **New Plugin** Aws Secrets Manager data source [GH-10505] [GH-10467]
|
||||
|
||||
### BACKWARDS INCOMPATIBILITIES
|
||||
* core: The API that the Packer core uses to communicate with community plugins
|
||||
has changed; maintainers of community plugins will need to upgrade their
|
||||
plugins in order to make them compatible with v1.7.0. An upgrade guide will
|
||||
be available on our guides page https://www.packer.io/guides.
|
||||
|
||||
### IMPROVEMENTS
|
||||
* builder/amazon: Add `skip_create_ami` option for testing and situations where
|
||||
artifact is not the ami. [GH-10531]
|
||||
* builder/amazon: Add IMDSv2 support for AWS EBS builder. [GH-10546]
|
||||
* builder/amazon: Add resource tags in the launch template used to request spot
|
||||
instances. [GH-10456]
|
||||
* builder/openstack: Add `skip_create_image` option for testing and situations
|
||||
where artifact is not the image. [GH-10496]
|
||||
* builder/oracle-oci: Add retry strategies to oci calls [GH-10591]
|
||||
* core/fmt: The `packer fmt` can now read from stdin. [GH-10500]
|
||||
* core/hcl: Add regex and regexall hcl2 template functions. [GH-10601]
|
||||
* core/hcl: Templates now support "sensitive" locals. [GH-10509]
|
||||
* core/hcl: Templates now support error-cleanup-provisioner. [GH-10604]
|
||||
* hcl2_upgrade: Command now comes with a flag so you can control whether output
|
||||
templates are annotated with helpful comments. [GH-10619]
|
||||
* hcl2_upgrade: Command now gracefully handles options with template engine
|
||||
interpolations. [GH-10625]
|
||||
* hcl2_upgrade: Command will convert amazon filters to use the ami data source.
|
||||
[GH-10491]
|
||||
|
||||
### BUG FIXES
|
||||
* amazon/ebssurrogate: Apply snapshot tags at same time as when taking
|
||||
snapshot. [GH-10150]
|
||||
* builder/amazon: Fix bug where validation fails if optional iops value is
|
||||
unset. [GH-10518]
|
||||
* builder/amazon: Wrap API call to get filtered image in a retry. [GH-10610]
|
||||
* builder/bsusurrogate: override bsu when omi root device is set. [GH-10490]
|
||||
* builder/google: Fix bug where Packer would fail when run by users who do not
|
||||
have permission to access the metadata, even though the metadata is not
|
||||
necessary to the run. [GH-10458]
|
||||
* builder/profitbricks: Profitbricks builder could not connect using SSH
|
||||
communicator. [GH-10549]
|
||||
* builder/proxmox: Ensure ISOs in additional_iso_files are mounted during VM
|
||||
creation. [GH-10586]
|
||||
* builder/proxmox: Improve cloud init error logging for proxmox builder.
|
||||
[GH-10499]
|
||||
* builder/qemu: Fix bug where vnc_min_port set to value greater then 5900 could
|
||||
prevent Packer from connecting to QEMU. [GH-10450] [GH-10451]
|
||||
* builder/qemu: Fix regression with cd indexing when disk_interface is `ide`.
|
||||
[GH-10519]
|
||||
* builder/vmware-esx: Skip credential validation, which requires ovftool to be
|
||||
installed, if we are not exporting an image. [GH-10520]
|
||||
* builder/yandex: Fix cloud-init config for ubuntu 20.04. [GH-10522]
|
||||
* builder/yandex: Fix incorrect access to `instance_id`. [GH-10522]
|
||||
* core/hcl: Fix bug where []uint8 types could not be passed to plugins.
|
||||
* core/hcl: fix bug where HCL core could not handle passing []uint8 to plugins.
|
||||
[GH-10516]
|
||||
* core/hcl: Fix force flag for hcl2 provisioners and post-processors.
|
||||
[GH-10571]
|
||||
* post-processor/vsphere: Fix regression where Packer would not check the exit
|
||||
status after streaming UI from the ovftool command. [GH-10468]
|
||||
* post-processor/yandex-export: Changed dhclient command and supported
|
||||
configuring disk for exportupdate-dump-method. Also added support for
|
||||
`file` builder. [GH-10488]
|
||||
|
||||
## 1.6.6 (December 16, 2020)
|
||||
|
||||
### FEATURES:
|
||||
### FEATURES
|
||||
* **New command** `fmt` allows users to format existing HCL2 configuration
|
||||
files into a canonical style. Please see [fmt command
|
||||
docs](https://packer.io/docs/commands/fmt) for more details. [GH-10225]
|
||||
[GH-10377]
|
||||
* **New function** `env` allows users to set the default value of a variable to
|
||||
the value of an environment variable. Please see [env function
|
||||
docs](https://www.packer.io/docs/templates/hcl_templates/functions/contextual/env") for
|
||||
docs](https://www.packer.io/docs/templates/hcl_templates/functions/contextual/env) for
|
||||
more details. [GH-10240]
|
||||
* **Future Scaffolding** This release contains a large number of no-op
|
||||
refactoring changes. The Packer team at HashiCorp is preparing to split the
|
||||
|
||||
+2
-2
@@ -37,8 +37,8 @@
|
||||
/builder/oracle/ @prydie @owainlewis
|
||||
/website/pages/docs/builders/oracle* @prydie @owainlewis
|
||||
|
||||
/builder/profitbricks/ @jasmingacic
|
||||
/website/pages/docs/builders/profitbricks* @jasmingacic
|
||||
/builder/profitbricks/ @LiviusP @mflorin
|
||||
/website/pages/docs/builders/profitbricks* @LiviusP @mflorin
|
||||
|
||||
/builder/triton/ @sean-
|
||||
/website/pages/docs/builders/triton* @sean-
|
||||
|
||||
@@ -174,6 +174,16 @@ type AccessConfig struct {
|
||||
// credential types) and GetFederationToken (for federation\_token
|
||||
// credential types) for more details.
|
||||
//
|
||||
// HCL2 example:
|
||||
//
|
||||
// ```hcl
|
||||
// vault_aws_engine {
|
||||
// name = "myrole"
|
||||
// role_arn = "myarn"
|
||||
// ttl = "3600s"
|
||||
// }
|
||||
// ```
|
||||
//
|
||||
// JSON example:
|
||||
//
|
||||
// ```json
|
||||
@@ -185,16 +195,6 @@ type AccessConfig struct {
|
||||
// }
|
||||
// }
|
||||
// ```
|
||||
//
|
||||
// HCL2 example:
|
||||
//
|
||||
// ```hcl
|
||||
// vault_aws_engine {
|
||||
// name = "myrole"
|
||||
// role_arn = "myarn"
|
||||
// ttl = "3600s"
|
||||
// }
|
||||
// ```
|
||||
VaultAWSEngine VaultAWSEngineOptions `mapstructure:"vault_aws_engine" required:"false"`
|
||||
// [Polling configuration](#polling-configuration) for the AWS waiter. Configures the waiter that checks
|
||||
// resource state.
|
||||
|
||||
@@ -5,20 +5,10 @@ import (
|
||||
|
||||
"github.com/aws/aws-sdk-go/aws"
|
||||
"github.com/aws/aws-sdk-go/aws/session"
|
||||
"github.com/aws/aws-sdk-go/service/ec2/ec2iface"
|
||||
)
|
||||
|
||||
func testAccessConfig() *AccessConfig {
|
||||
return &AccessConfig{
|
||||
getEC2Connection: func() ec2iface.EC2API {
|
||||
return &mockEC2Client{}
|
||||
},
|
||||
PollingConfig: new(AWSPollingConfig),
|
||||
}
|
||||
}
|
||||
|
||||
func TestAccessConfigPrepare_Region(t *testing.T) {
|
||||
c := testAccessConfig()
|
||||
c := FakeAccessConfig()
|
||||
|
||||
c.RawRegion = "us-east-12"
|
||||
err := c.ValidateRegion(c.RawRegion)
|
||||
@@ -40,7 +30,7 @@ func TestAccessConfigPrepare_Region(t *testing.T) {
|
||||
}
|
||||
|
||||
func TestAccessConfigPrepare_RegionRestricted(t *testing.T) {
|
||||
c := testAccessConfig()
|
||||
c := FakeAccessConfig()
|
||||
|
||||
// Create a Session with a custom region
|
||||
c.session = session.Must(session.NewSession(&aws.Config{
|
||||
|
||||
@@ -135,26 +135,8 @@ type AMIConfig struct {
|
||||
// the intermediary AMI into any regions provided in `ami_regions`, then
|
||||
// delete the intermediary AMI. Default `false`.
|
||||
AMISkipBuildRegion bool `mapstructure:"skip_save_build_region"`
|
||||
// Key/value pair tags to apply to snapshot. They will override AMI tags if
|
||||
// already applied to snapshot. This is a [template
|
||||
// engine](/docs/templates/legacy_json_templates/engine), see [Build template
|
||||
// data](#build-template-data) for more information.
|
||||
SnapshotTags map[string]string `mapstructure:"snapshot_tags" required:"false"`
|
||||
// Same as [`snapshot_tags`](#snapshot_tags) but defined as a singular
|
||||
// repeatable block containing a `key` and a `value` field. In HCL2 mode the
|
||||
// [`dynamic_block`](/docs/templates/hcl_templates/expressions#dynamic-blocks)
|
||||
// will allow you to create those programatically.
|
||||
SnapshotTag config.KeyValues `mapstructure:"snapshot_tag" required:"false"`
|
||||
// A list of account IDs that have
|
||||
// access to create volumes from the snapshot(s). By default no additional
|
||||
// users other than the user creating the AMI has permissions to create
|
||||
// volumes from the backing snapshot(s).
|
||||
SnapshotUsers []string `mapstructure:"snapshot_users" required:"false"`
|
||||
// A list of groups that have access to
|
||||
// create volumes from the snapshot(s). By default no groups have permission
|
||||
// to create volumes from the snapshot(s). all will make the snapshot
|
||||
// publicly accessible.
|
||||
SnapshotGroups []string `mapstructure:"snapshot_groups" required:"false"`
|
||||
|
||||
SnapshotConfig `mapstructure:",squash"`
|
||||
}
|
||||
|
||||
func stringInSlice(s []string, searchstr string) bool {
|
||||
|
||||
@@ -7,7 +7,6 @@ import (
|
||||
|
||||
"github.com/aws/aws-sdk-go/aws"
|
||||
"github.com/aws/aws-sdk-go/service/ec2"
|
||||
"github.com/aws/aws-sdk-go/service/ec2/ec2iface"
|
||||
"github.com/hashicorp/packer-plugin-sdk/template/config"
|
||||
)
|
||||
|
||||
@@ -18,14 +17,14 @@ func testAMIConfig() *AMIConfig {
|
||||
}
|
||||
|
||||
func getFakeAccessConfig(region string) *AccessConfig {
|
||||
c := testAccessConfig()
|
||||
c := FakeAccessConfig()
|
||||
c.RawRegion = region
|
||||
return c
|
||||
}
|
||||
|
||||
func TestAMIConfigPrepare_name(t *testing.T) {
|
||||
c := testAMIConfig()
|
||||
accessConf := testAccessConfig()
|
||||
accessConf := FakeAccessConfig()
|
||||
if err := c.Prepare(accessConf, nil); err != nil {
|
||||
t.Fatalf("shouldn't have err: %s", err)
|
||||
}
|
||||
@@ -36,10 +35,6 @@ func TestAMIConfigPrepare_name(t *testing.T) {
|
||||
}
|
||||
}
|
||||
|
||||
type mockEC2Client struct {
|
||||
ec2iface.EC2API
|
||||
}
|
||||
|
||||
func (m *mockEC2Client) DescribeRegions(*ec2.DescribeRegionsInput) (*ec2.DescribeRegionsOutput, error) {
|
||||
return &ec2.DescribeRegionsOutput{
|
||||
Regions: []*ec2.Region{
|
||||
@@ -56,7 +51,7 @@ func TestAMIConfigPrepare_regions(t *testing.T) {
|
||||
|
||||
var errs []error
|
||||
var err error
|
||||
accessConf := testAccessConfig()
|
||||
accessConf := FakeAccessConfig()
|
||||
mockConn := &mockEC2Client{}
|
||||
if errs = c.prepareRegions(accessConf); len(errs) > 0 {
|
||||
t.Fatalf("shouldn't have err: %#v", errs)
|
||||
@@ -163,7 +158,7 @@ func TestAMIConfigPrepare_Share_EncryptedBoot(t *testing.T) {
|
||||
c.AMIUsers = []string{"testAccountID"}
|
||||
c.AMIEncryptBootVolume = config.TriTrue
|
||||
|
||||
accessConf := testAccessConfig()
|
||||
accessConf := FakeAccessConfig()
|
||||
|
||||
c.AMIKmsKeyId = ""
|
||||
if err := c.Prepare(accessConf, nil); err == nil {
|
||||
@@ -179,7 +174,7 @@ func TestAMIConfigPrepare_ValidateKmsKey(t *testing.T) {
|
||||
c := testAMIConfig()
|
||||
c.AMIEncryptBootVolume = config.TriTrue
|
||||
|
||||
accessConf := testAccessConfig()
|
||||
accessConf := FakeAccessConfig()
|
||||
|
||||
validCases := []string{
|
||||
"abcd1234-e567-890f-a12b-a123b4cd56ef",
|
||||
@@ -215,7 +210,7 @@ func TestAMIConfigPrepare_ValidateKmsKey(t *testing.T) {
|
||||
func TestAMINameValidation(t *testing.T) {
|
||||
c := testAMIConfig()
|
||||
|
||||
accessConf := testAccessConfig()
|
||||
accessConf := FakeAccessConfig()
|
||||
|
||||
c.AMIName = "aa"
|
||||
if err := c.Prepare(accessConf, nil); err == nil {
|
||||
|
||||
@@ -51,7 +51,10 @@ func (d *AmiFilterOptions) GetFilteredImage(params *ec2.DescribeImagesInput, ec2
|
||||
}
|
||||
|
||||
log.Printf("Using AMI Filters %v", params)
|
||||
imageResp, err := ec2conn.DescribeImages(params)
|
||||
req, imageResp := ec2conn.DescribeImagesRequest(params)
|
||||
req.RetryCount = 11
|
||||
|
||||
err := req.Send()
|
||||
if err != nil {
|
||||
err := fmt.Errorf("Error querying AMI: %s", err)
|
||||
return nil, err
|
||||
|
||||
@@ -30,18 +30,6 @@ const (
|
||||
// The following mapping will tell Packer to encrypt the root volume of the
|
||||
// build instance at launch using a specific non-default kms key:
|
||||
//
|
||||
// JSON example:
|
||||
//
|
||||
// ```json
|
||||
// launch_block_device_mappings: [
|
||||
// {
|
||||
// "device_name": "/dev/sda1",
|
||||
// "encrypted": true,
|
||||
// "kms_key_id": "1a2b3c4d-5e6f-1a2b-3c4d-5e6f1a2b3c4d"
|
||||
// }
|
||||
// ]
|
||||
// ```
|
||||
//
|
||||
// HCL2 example:
|
||||
//
|
||||
// ```hcl
|
||||
@@ -52,6 +40,17 @@ const (
|
||||
// }
|
||||
// ```
|
||||
//
|
||||
// JSON example:
|
||||
// ```json
|
||||
// launch_block_device_mappings: [
|
||||
// {
|
||||
// "device_name": "/dev/sda1",
|
||||
// "encrypted": true,
|
||||
// "kms_key_id": "1a2b3c4d-5e6f-1a2b-3c4d-5e6f1a2b3c4d"
|
||||
// }
|
||||
// ]
|
||||
// ```
|
||||
//
|
||||
// Please note that the kms_key_id option in this example exists for
|
||||
// launch_block_device_mappings but not ami_block_device_mappings.
|
||||
//
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
//go:generate struct-markdown
|
||||
//go:generate mapstructure-to-hcl2 -type AmiFilterOptions,SecurityGroupFilterOptions,SubnetFilterOptions,VpcFilterOptions,PolicyDocument,Statement
|
||||
//go:generate mapstructure-to-hcl2 -type AmiFilterOptions,SecurityGroupFilterOptions,SubnetFilterOptions,VpcFilterOptions,PolicyDocument,Statement,MetadataOptions
|
||||
|
||||
package common
|
||||
|
||||
@@ -44,6 +44,20 @@ type SecurityGroupFilterOptions struct {
|
||||
config.NameValueFilter `mapstructure:",squash"`
|
||||
}
|
||||
|
||||
// Configures the metadata options.
|
||||
// See [Configure IMDS](https://docs.aws.amazon.com/AWSEC2/latest/UserGuide/configuring-instance-metadata-service.html) for details.
|
||||
type MetadataOptions struct {
|
||||
// A string to enable or disble the IMDS endpoint for an instance. Defaults to enabled.
|
||||
// Accepts either "enabled" or "disabled"
|
||||
HttpEndpoint string `mapstructure:"http_endpoint" required:"false"`
|
||||
// A string to either set the use of IMDSv2 for the instance to optional or required. Defaults to "optional".
|
||||
// Accepts either "optional" or "required"
|
||||
HttpTokens string `mapstructure:"http_tokens" required:"false"`
|
||||
// A numerical value to set an upper limit for the amount of hops allowed when communicating with IMDS endpoints.
|
||||
// Defaults to 1.
|
||||
HttpPutResponseHopLimit int64 `mapstructure:"http_put_response_hop_limit" required:"false"`
|
||||
}
|
||||
|
||||
// RunConfig contains configuration for running an instance from a source
|
||||
// AMI and details on how to access that launched image.
|
||||
type RunConfig struct {
|
||||
@@ -118,8 +132,21 @@ type RunConfig struct {
|
||||
// Whether or not to check if the IAM instance profile exists. Defaults to false
|
||||
SkipProfileValidation bool `mapstructure:"skip_profile_validation" required:"false"`
|
||||
// Temporary IAM instance profile policy document
|
||||
// If IamInstanceProfile is specified it will be used instead. Example:
|
||||
// If IamInstanceProfile is specified it will be used instead.
|
||||
//
|
||||
// HCL2 example:
|
||||
// ```hcl
|
||||
//temporary_iam_instance_profile_policy_document {
|
||||
// Statement {
|
||||
// Action = ["logs:*"]
|
||||
// Effect = "Allow"
|
||||
// Resource = "*"
|
||||
// }
|
||||
// Version = "2012-10-17"
|
||||
//}
|
||||
// ```
|
||||
//
|
||||
// JSON example:
|
||||
// ```json
|
||||
//{
|
||||
// "Version": "2012-10-17",
|
||||
@@ -143,17 +170,7 @@ type RunConfig struct {
|
||||
// The EC2 instance type to use while building the
|
||||
// AMI, such as t2.small.
|
||||
InstanceType string `mapstructure:"instance_type" required:"true"`
|
||||
// Filters used to populate the `security_group_ids` field. JSON Example:
|
||||
//
|
||||
// ```json
|
||||
// {
|
||||
// "security_group_filter": {
|
||||
// "filters": {
|
||||
// "tag:Class": "packer"
|
||||
// }
|
||||
// }
|
||||
// }
|
||||
// ```
|
||||
// Filters used to populate the `security_group_ids` field.
|
||||
//
|
||||
// HCL2 Example:
|
||||
//
|
||||
@@ -165,6 +182,17 @@ type RunConfig struct {
|
||||
// }
|
||||
// ```
|
||||
//
|
||||
// JSON Example:
|
||||
// ```json
|
||||
// {
|
||||
// "security_group_filter": {
|
||||
// "filters": {
|
||||
// "tag:Class": "packer"
|
||||
// }
|
||||
// }
|
||||
// }
|
||||
// ```
|
||||
//
|
||||
// This selects the SG's with tag `Class` with the value `packer`.
|
||||
//
|
||||
// - `filters` (map of strings) - filters used to select a
|
||||
@@ -199,8 +227,24 @@ type RunConfig struct {
|
||||
// AMI with a root volume snapshot that you have access to.
|
||||
SourceAmi string `mapstructure:"source_ami" required:"true"`
|
||||
// Filters used to populate the `source_ami`
|
||||
// field. JSON Example:
|
||||
// field.
|
||||
//
|
||||
// HCL2 example:
|
||||
// ```hcl
|
||||
// source "amazon-ebs" "basic-example" {
|
||||
// source_ami_filter {
|
||||
// filters = {
|
||||
// virtualization-type = "hvm"
|
||||
// name = "ubuntu/images/\*ubuntu-xenial-16.04-amd64-server-\*"
|
||||
// root-device-type = "ebs"
|
||||
// }
|
||||
// owners = ["099720109477"]
|
||||
// most_recent = true
|
||||
// }
|
||||
// }
|
||||
// ```
|
||||
//
|
||||
// JSON Example:
|
||||
// ```json
|
||||
// "builders" [
|
||||
// {
|
||||
@@ -217,21 +261,6 @@ type RunConfig struct {
|
||||
// }
|
||||
// ]
|
||||
// ```
|
||||
// HCL2 example:
|
||||
//
|
||||
// ```hcl
|
||||
// source "amazon-ebs" "basic-example" {
|
||||
// source_ami_filter {
|
||||
// filters = {
|
||||
// virtualization-type = "hvm"
|
||||
// name = "ubuntu/images/\*ubuntu-xenial-16.04-amd64-server-\*"
|
||||
// root-device-type = "ebs"
|
||||
// }
|
||||
// owners = ["099720109477"]
|
||||
// most_recent = true
|
||||
// }
|
||||
// }
|
||||
// ```
|
||||
//
|
||||
// This selects the most recent Ubuntu 16.04 HVM EBS AMI from Canonical. NOTE:
|
||||
// This will fail unless *exactly* one AMI is returned. In the above example,
|
||||
@@ -299,8 +328,22 @@ type RunConfig struct {
|
||||
// will allow you to create those programatically.
|
||||
SpotTag config.KeyValues `mapstructure:"spot_tag" required:"false"`
|
||||
// Filters used to populate the `subnet_id` field.
|
||||
// JSON Example:
|
||||
//
|
||||
// HCL2 example:
|
||||
//
|
||||
// ```hcl
|
||||
// source "amazon-ebs" "basic-example" {
|
||||
// subnet_filter {
|
||||
// filters = {
|
||||
// "tag:Class": "build"
|
||||
// }
|
||||
// most_free = true
|
||||
// random = false
|
||||
// }
|
||||
// }
|
||||
// ```
|
||||
//
|
||||
// JSON Example:
|
||||
// ```json
|
||||
// "builders" [
|
||||
// {
|
||||
@@ -315,19 +358,6 @@ type RunConfig struct {
|
||||
// }
|
||||
// ]
|
||||
// ```
|
||||
// HCL2 example:
|
||||
//
|
||||
// ```hcl
|
||||
// source "amazon-ebs" "basic-example" {
|
||||
// subnet_filter {
|
||||
// filters = {
|
||||
// "tag:Class": "build"
|
||||
// }
|
||||
// most_free = true
|
||||
// random = false
|
||||
// }
|
||||
// }
|
||||
// ```
|
||||
//
|
||||
// This selects the Subnet with tag `Class` with the value `build`, which has
|
||||
// the most free IP addresses. NOTE: This will fail unless *exactly* one
|
||||
@@ -374,8 +404,21 @@ type RunConfig struct {
|
||||
// data when launching the instance.
|
||||
UserDataFile string `mapstructure:"user_data_file" required:"false"`
|
||||
// Filters used to populate the `vpc_id` field.
|
||||
// JSON Example:
|
||||
//
|
||||
// HCL2 example:
|
||||
// ```hcl
|
||||
// source "amazon-ebs" "basic-example" {
|
||||
// vpc_filter {
|
||||
// filters = {
|
||||
// "tag:Class": "build",
|
||||
// "isDefault": "false",
|
||||
// "cidr": "/24"
|
||||
// }
|
||||
// }
|
||||
// }
|
||||
// ```
|
||||
//
|
||||
// JSON Example:
|
||||
// ```json
|
||||
// "builders" [
|
||||
// {
|
||||
@@ -390,19 +433,6 @@ type RunConfig struct {
|
||||
// }
|
||||
// ]
|
||||
// ```
|
||||
// HCL2 example:
|
||||
//
|
||||
// ```hcl
|
||||
// source "amazon-ebs" "basic-example" {
|
||||
// vpc_filter {
|
||||
// filters = {
|
||||
// "tag:Class": "build",
|
||||
// "isDefault": "false",
|
||||
// "cidr": "/24"
|
||||
// }
|
||||
// }
|
||||
// }
|
||||
// ```
|
||||
//
|
||||
// This selects the VPC with tag `Class` with the value `build`, which is not
|
||||
// the default VPC, and have a IPv4 CIDR block of `/24`. NOTE: This will fail
|
||||
@@ -426,6 +456,9 @@ type RunConfig struct {
|
||||
// 10m
|
||||
WindowsPasswordTimeout time.Duration `mapstructure:"windows_password_timeout" required:"false"`
|
||||
|
||||
// [Metadata Settings](#metadata-settings)
|
||||
Metadata MetadataOptions `mapstructure:"metadata_options" required:"false"`
|
||||
|
||||
// Communicator settings
|
||||
Comm communicator.Config `mapstructure:",squash"`
|
||||
|
||||
@@ -486,6 +519,33 @@ func (c *RunConfig) Prepare(ctx *interpolate.Context) []error {
|
||||
// Validation
|
||||
errs := c.Comm.Prepare(ctx)
|
||||
|
||||
if c.Metadata.HttpEndpoint == "" {
|
||||
c.Metadata.HttpEndpoint = "enabled"
|
||||
}
|
||||
|
||||
if c.Metadata.HttpTokens == "" {
|
||||
c.Metadata.HttpTokens = "optional"
|
||||
}
|
||||
|
||||
if c.Metadata.HttpPutResponseHopLimit == 0 {
|
||||
c.Metadata.HttpPutResponseHopLimit = 1
|
||||
}
|
||||
|
||||
if c.Metadata.HttpEndpoint != "enabled" && c.Metadata.HttpEndpoint != "disabled" {
|
||||
msg := fmt.Errorf("http_endpoint requires either disabled or enabled as its value")
|
||||
errs = append(errs, msg)
|
||||
}
|
||||
|
||||
if c.Metadata.HttpTokens != "optional" && c.Metadata.HttpTokens != "required" {
|
||||
msg := fmt.Errorf("http_tokens requires either optional or required as its value")
|
||||
errs = append(errs, msg)
|
||||
}
|
||||
|
||||
if c.Metadata.HttpPutResponseHopLimit < 1 || c.Metadata.HttpPutResponseHopLimit > 64 {
|
||||
msg := fmt.Errorf("http_put_response_hop_limit requires a number between 1 and 64")
|
||||
errs = append(errs, msg)
|
||||
}
|
||||
|
||||
// Copy singular tag maps
|
||||
errs = append(errs, c.RunTag.CopyOn(&c.RunTags)...)
|
||||
errs = append(errs, c.SpotTag.CopyOn(&c.SpotTags)...)
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
// Code generated by "mapstructure-to-hcl2 -type AmiFilterOptions,SecurityGroupFilterOptions,SubnetFilterOptions,VpcFilterOptions,PolicyDocument,Statement"; DO NOT EDIT.
|
||||
// Code generated by "mapstructure-to-hcl2 -type AmiFilterOptions,SecurityGroupFilterOptions,SubnetFilterOptions,VpcFilterOptions,PolicyDocument,Statement,MetadataOptions"; DO NOT EDIT.
|
||||
|
||||
package common
|
||||
|
||||
@@ -35,6 +35,33 @@ func (*FlatAmiFilterOptions) HCL2Spec() map[string]hcldec.Spec {
|
||||
return s
|
||||
}
|
||||
|
||||
// FlatMetadataOptions is an auto-generated flat version of MetadataOptions.
|
||||
// Where the contents of a field with a `mapstructure:,squash` tag are bubbled up.
|
||||
type FlatMetadataOptions struct {
|
||||
HttpEndpoint *string `mapstructure:"http_endpoint" required:"false" cty:"http_endpoint" hcl:"http_endpoint"`
|
||||
HttpTokens *string `mapstructure:"http_tokens" required:"false" cty:"http_tokens" hcl:"http_tokens"`
|
||||
HttpPutResponseHopLimit *int64 `mapstructure:"http_put_response_hop_limit" required:"false" cty:"http_put_response_hop_limit" hcl:"http_put_response_hop_limit"`
|
||||
}
|
||||
|
||||
// FlatMapstructure returns a new FlatMetadataOptions.
|
||||
// FlatMetadataOptions is an auto-generated flat version of MetadataOptions.
|
||||
// Where the contents a fields with a `mapstructure:,squash` tag are bubbled up.
|
||||
func (*MetadataOptions) FlatMapstructure() interface{ HCL2Spec() map[string]hcldec.Spec } {
|
||||
return new(FlatMetadataOptions)
|
||||
}
|
||||
|
||||
// HCL2Spec returns the hcl spec of a MetadataOptions.
|
||||
// This spec is used by HCL to read the fields of MetadataOptions.
|
||||
// The decoded values from this spec will then be applied to a FlatMetadataOptions.
|
||||
func (*FlatMetadataOptions) HCL2Spec() map[string]hcldec.Spec {
|
||||
s := map[string]hcldec.Spec{
|
||||
"http_endpoint": &hcldec.AttrSpec{Name: "http_endpoint", Type: cty.String, Required: false},
|
||||
"http_tokens": &hcldec.AttrSpec{Name: "http_tokens", Type: cty.String, Required: false},
|
||||
"http_put_response_hop_limit": &hcldec.AttrSpec{Name: "http_put_response_hop_limit", Type: cty.Number, Required: false},
|
||||
}
|
||||
return s
|
||||
}
|
||||
|
||||
// FlatPolicyDocument is an auto-generated flat version of PolicyDocument.
|
||||
// Where the contents of a field with a `mapstructure:,squash` tag are bubbled up.
|
||||
type FlatPolicyDocument struct {
|
||||
|
||||
@@ -0,0 +1,29 @@
|
||||
//go:generate struct-markdown
|
||||
|
||||
package common
|
||||
|
||||
import "github.com/hashicorp/packer-plugin-sdk/template/config"
|
||||
|
||||
// SnapshotConfig is for common configuration related to creating AMIs.
|
||||
type SnapshotConfig struct {
|
||||
// Key/value pair tags to apply to snapshot. They will override AMI tags if
|
||||
// already applied to snapshot. This is a [template
|
||||
// engine](/docs/templates/legacy_json_templates/engine), see [Build template
|
||||
// data](#build-template-data) for more information.
|
||||
SnapshotTags map[string]string `mapstructure:"snapshot_tags" required:"false"`
|
||||
// Same as [`snapshot_tags`](#snapshot_tags) but defined as a singular
|
||||
// repeatable block containing a `key` and a `value` field. In HCL2 mode the
|
||||
// [`dynamic_block`](/docs/templates/hcl_templates/expressions#dynamic-blocks)
|
||||
// will allow you to create those programatically.
|
||||
SnapshotTag config.KeyValues `mapstructure:"snapshot_tag" required:"false"`
|
||||
// A list of account IDs that have
|
||||
// access to create volumes from the snapshot(s). By default no additional
|
||||
// users other than the user creating the AMI has permissions to create
|
||||
// volumes from the backing snapshot(s).
|
||||
SnapshotUsers []string `mapstructure:"snapshot_users" required:"false"`
|
||||
// A list of groups that have access to
|
||||
// create volumes from the snapshot(s). By default no groups have permission
|
||||
// to create volumes from the snapshot(s). all will make the snapshot
|
||||
// publicly accessible.
|
||||
SnapshotGroups []string `mapstructure:"snapshot_groups" required:"false"`
|
||||
}
|
||||
@@ -42,17 +42,8 @@ type StateChangeConf struct {
|
||||
|
||||
// Polling configuration for the AWS waiter. Configures the waiter for resources creation or actions like attaching
|
||||
// volumes or importing image.
|
||||
// Usage example:
|
||||
//
|
||||
// In JSON:
|
||||
// ```json
|
||||
// "aws_polling" : {
|
||||
// "delay_seconds": 30,
|
||||
// "max_attempts": 50
|
||||
// }
|
||||
// ```
|
||||
//
|
||||
// In HCL2:
|
||||
// HCL2 example:
|
||||
// ```hcl
|
||||
// aws_polling {
|
||||
// delay_seconds = 30
|
||||
@@ -60,6 +51,13 @@ type StateChangeConf struct {
|
||||
// }
|
||||
// ```
|
||||
//
|
||||
// JSON example:
|
||||
// ```json
|
||||
// "aws_polling" : {
|
||||
// "delay_seconds": 30,
|
||||
// "max_attempts": 50
|
||||
// }
|
||||
// ```
|
||||
type AWSPollingConfig struct {
|
||||
// Specifies the maximum number of attempts the waiter will check for resource state.
|
||||
// This value can also be set via the AWS_MAX_ATTEMPTS.
|
||||
@@ -151,15 +149,22 @@ func (w *AWSPollingConfig) WaitUntilVolumeAvailable(ctx aws.Context, conn *ec2.E
|
||||
return err
|
||||
}
|
||||
|
||||
func (w *AWSPollingConfig) WaitUntilSnapshotDone(ctx aws.Context, conn *ec2.EC2, snapshotID string) error {
|
||||
func (w *AWSPollingConfig) WaitUntilSnapshotDone(ctx aws.Context, conn ec2iface.EC2API, snapshotID string) error {
|
||||
snapInput := ec2.DescribeSnapshotsInput{
|
||||
SnapshotIds: []*string{&snapshotID},
|
||||
}
|
||||
|
||||
waitOpts := w.getWaiterOptions()
|
||||
if len(waitOpts) == 0 {
|
||||
// Bump this default to 30 minutes.
|
||||
// Large snapshots can take a long time for the copy to s3
|
||||
waitOpts = append(waitOpts, request.WithWaiterMaxAttempts(120))
|
||||
}
|
||||
|
||||
err := conn.WaitUntilSnapshotCompletedWithContext(
|
||||
ctx,
|
||||
&snapInput,
|
||||
w.getWaiterOptions()...)
|
||||
waitOpts...)
|
||||
return err
|
||||
}
|
||||
|
||||
|
||||
@@ -105,7 +105,7 @@ func TestStepAMIRegionCopy_duplicates(t *testing.T) {
|
||||
// ------------------------------------------------------------------------
|
||||
|
||||
stepAMIRegionCopy := StepAMIRegionCopy{
|
||||
AccessConfig: testAccessConfig(),
|
||||
AccessConfig: FakeAccessConfig(),
|
||||
Regions: []string{"us-east-1"},
|
||||
AMIKmsKeyId: "12345",
|
||||
// Original region key in regionkeyids is different than in amikmskeyid
|
||||
@@ -131,7 +131,7 @@ func TestStepAMIRegionCopy_duplicates(t *testing.T) {
|
||||
|
||||
// the ami is only copied once.
|
||||
stepAMIRegionCopy = StepAMIRegionCopy{
|
||||
AccessConfig: testAccessConfig(),
|
||||
AccessConfig: FakeAccessConfig(),
|
||||
Regions: []string{"us-east-1"},
|
||||
Name: "fake-ami-name",
|
||||
OriginalRegion: "us-east-1",
|
||||
@@ -152,7 +152,7 @@ func TestStepAMIRegionCopy_duplicates(t *testing.T) {
|
||||
|
||||
// the ami is only copied once.
|
||||
stepAMIRegionCopy = StepAMIRegionCopy{
|
||||
AccessConfig: testAccessConfig(),
|
||||
AccessConfig: FakeAccessConfig(),
|
||||
Regions: []string{"us-east-1"},
|
||||
EncryptBootVolume: config.TriFalse,
|
||||
Name: "fake-ami-name",
|
||||
@@ -174,7 +174,7 @@ func TestStepAMIRegionCopy_duplicates(t *testing.T) {
|
||||
// ------------------------------------------------------------------------
|
||||
|
||||
stepAMIRegionCopy = StepAMIRegionCopy{
|
||||
AccessConfig: testAccessConfig(),
|
||||
AccessConfig: FakeAccessConfig(),
|
||||
// Many duplicates for only 3 actual values
|
||||
Regions: []string{"us-east-1", "us-west-2", "us-west-2", "ap-east-1", "ap-east-1", "ap-east-1"},
|
||||
AMIKmsKeyId: "IlikePancakes",
|
||||
@@ -203,7 +203,7 @@ func TestStepAMIRegionCopy_duplicates(t *testing.T) {
|
||||
// ------------------------------------------------------------------------
|
||||
|
||||
stepAMIRegionCopy = StepAMIRegionCopy{
|
||||
AccessConfig: testAccessConfig(),
|
||||
AccessConfig: FakeAccessConfig(),
|
||||
// Many duplicates for only 3 actual values
|
||||
Regions: []string{"us-east-1", "us-west-2", "us-west-2", "ap-east-1", "ap-east-1", "ap-east-1"},
|
||||
Name: "fake-ami-name",
|
||||
@@ -223,7 +223,7 @@ func TestStepAMIRegionCopy_duplicates(t *testing.T) {
|
||||
func TestStepAmiRegionCopy_nil_encryption(t *testing.T) {
|
||||
// create step
|
||||
stepAMIRegionCopy := StepAMIRegionCopy{
|
||||
AccessConfig: testAccessConfig(),
|
||||
AccessConfig: FakeAccessConfig(),
|
||||
Regions: make([]string, 0),
|
||||
AMIKmsKeyId: "",
|
||||
RegionKeyIds: make(map[string]string),
|
||||
@@ -249,7 +249,7 @@ func TestStepAmiRegionCopy_nil_encryption(t *testing.T) {
|
||||
func TestStepAmiRegionCopy_true_encryption(t *testing.T) {
|
||||
// create step
|
||||
stepAMIRegionCopy := StepAMIRegionCopy{
|
||||
AccessConfig: testAccessConfig(),
|
||||
AccessConfig: FakeAccessConfig(),
|
||||
Regions: make([]string, 0),
|
||||
AMIKmsKeyId: "",
|
||||
RegionKeyIds: make(map[string]string),
|
||||
@@ -275,7 +275,7 @@ func TestStepAmiRegionCopy_true_encryption(t *testing.T) {
|
||||
func TestStepAmiRegionCopy_nil_intermediary(t *testing.T) {
|
||||
// create step
|
||||
stepAMIRegionCopy := StepAMIRegionCopy{
|
||||
AccessConfig: testAccessConfig(),
|
||||
AccessConfig: FakeAccessConfig(),
|
||||
Regions: make([]string, 0),
|
||||
AMIKmsKeyId: "",
|
||||
RegionKeyIds: make(map[string]string),
|
||||
@@ -303,7 +303,7 @@ func TestStepAmiRegionCopy_AMISkipBuildRegion(t *testing.T) {
|
||||
// ------------------------------------------------------------------------
|
||||
|
||||
stepAMIRegionCopy := StepAMIRegionCopy{
|
||||
AccessConfig: testAccessConfig(),
|
||||
AccessConfig: FakeAccessConfig(),
|
||||
Regions: []string{"us-west-1"},
|
||||
AMIKmsKeyId: "",
|
||||
RegionKeyIds: map[string]string{"us-west-1": "abcde"},
|
||||
@@ -329,7 +329,7 @@ func TestStepAmiRegionCopy_AMISkipBuildRegion(t *testing.T) {
|
||||
// skip build region is false.
|
||||
// ------------------------------------------------------------------------
|
||||
stepAMIRegionCopy = StepAMIRegionCopy{
|
||||
AccessConfig: testAccessConfig(),
|
||||
AccessConfig: FakeAccessConfig(),
|
||||
Regions: []string{"us-west-1"},
|
||||
AMIKmsKeyId: "",
|
||||
RegionKeyIds: make(map[string]string),
|
||||
@@ -354,7 +354,7 @@ func TestStepAmiRegionCopy_AMISkipBuildRegion(t *testing.T) {
|
||||
// skip build region is false, but encrypt is true
|
||||
// ------------------------------------------------------------------------
|
||||
stepAMIRegionCopy = StepAMIRegionCopy{
|
||||
AccessConfig: testAccessConfig(),
|
||||
AccessConfig: FakeAccessConfig(),
|
||||
Regions: []string{"us-west-1"},
|
||||
AMIKmsKeyId: "",
|
||||
RegionKeyIds: map[string]string{"us-west-1": "abcde"},
|
||||
@@ -380,7 +380,7 @@ func TestStepAmiRegionCopy_AMISkipBuildRegion(t *testing.T) {
|
||||
// skip build region is true, and encrypt is true
|
||||
// ------------------------------------------------------------------------
|
||||
stepAMIRegionCopy = StepAMIRegionCopy{
|
||||
AccessConfig: testAccessConfig(),
|
||||
AccessConfig: FakeAccessConfig(),
|
||||
Regions: []string{"us-west-1"},
|
||||
AMIKmsKeyId: "",
|
||||
RegionKeyIds: map[string]string{"us-west-1": "abcde"},
|
||||
|
||||
@@ -29,6 +29,9 @@ type StepRunSourceInstance struct {
|
||||
EbsOptimized bool
|
||||
EnableT2Unlimited bool
|
||||
ExpectedRootDevice string
|
||||
HttpEndpoint string
|
||||
HttpTokens string
|
||||
HttpPutResponseHopLimit int64
|
||||
InstanceInitiatedShutdownBehavior string
|
||||
InstanceType string
|
||||
IsRestricted bool
|
||||
@@ -144,6 +147,10 @@ func (s *StepRunSourceInstance) Run(ctx context.Context, state multistep.StateBa
|
||||
runOpts.CreditSpecification = &ec2.CreditSpecificationRequest{CpuCredits: &creditOption}
|
||||
}
|
||||
|
||||
if s.HttpEndpoint == "enabled" {
|
||||
runOpts.MetadataOptions = &ec2.InstanceMetadataOptionsRequest{HttpEndpoint: &s.HttpEndpoint, HttpTokens: &s.HttpTokens, HttpPutResponseHopLimit: &s.HttpPutResponseHopLimit}
|
||||
}
|
||||
|
||||
// Collect tags for tagging on resource creation
|
||||
var tagSpecs []*ec2.TagSpecification
|
||||
|
||||
@@ -251,7 +258,7 @@ func (s *StepRunSourceInstance) Run(ctx context.Context, state multistep.StateBa
|
||||
if resp, e := ec2conn.DescribeInstances(describeInstance); e == nil {
|
||||
if len(resp.Reservations) > 0 && len(resp.Reservations[0].Instances) > 0 {
|
||||
instance := resp.Reservations[0].Instances[0]
|
||||
if instance.StateTransitionReason != nil && instance.StateReason.Message != nil {
|
||||
if instance.StateTransitionReason != nil && instance.StateReason != nil && instance.StateReason.Message != nil {
|
||||
ui.Error(fmt.Sprintf("Instance state change details: %s: %s",
|
||||
*instance.StateTransitionReason, *instance.StateReason.Message))
|
||||
}
|
||||
|
||||
@@ -34,6 +34,9 @@ type StepRunSpotInstance struct {
|
||||
Comm *communicator.Config
|
||||
EbsOptimized bool
|
||||
ExpectedRootDevice string
|
||||
HttpEndpoint string
|
||||
HttpTokens string
|
||||
HttpPutResponseHopLimit int64
|
||||
InstanceInitiatedShutdownBehavior string
|
||||
InstanceType string
|
||||
Region string
|
||||
@@ -51,6 +54,24 @@ type StepRunSpotInstance struct {
|
||||
instanceId string
|
||||
}
|
||||
|
||||
// The EbsBlockDevice and LaunchTemplateEbsBlockDeviceRequest structs are
|
||||
// nearly identical except for the struct's name and one extra field in
|
||||
// EbsBlockDeviceResuest, which unfortunately means you can't just cast one
|
||||
// into the other. THANKS AMAZON.
|
||||
func castBlockDeviceToRequest(bd *ec2.EbsBlockDevice) *ec2.LaunchTemplateEbsBlockDeviceRequest {
|
||||
out := &ec2.LaunchTemplateEbsBlockDeviceRequest{
|
||||
DeleteOnTermination: bd.DeleteOnTermination,
|
||||
Encrypted: bd.Encrypted,
|
||||
Iops: bd.Iops,
|
||||
KmsKeyId: bd.KmsKeyId,
|
||||
SnapshotId: bd.SnapshotId,
|
||||
Throughput: bd.Throughput,
|
||||
VolumeSize: bd.VolumeSize,
|
||||
VolumeType: bd.VolumeType,
|
||||
}
|
||||
return out
|
||||
}
|
||||
|
||||
func (s *StepRunSpotInstance) CreateTemplateData(userData *string, az string,
|
||||
state multistep.StateBag, marketOptions *ec2.LaunchTemplateInstanceMarketOptionsRequest) *ec2.RequestLaunchTemplateData {
|
||||
blockDeviceMappings := s.LaunchMappings.BuildEC2BlockDeviceMappings()
|
||||
@@ -58,15 +79,12 @@ func (s *StepRunSpotInstance) CreateTemplateData(userData *string, az string,
|
||||
// LaunchTemplateBlockDeviceMappingRequest. These structs are identical,
|
||||
// except for the EBS field -- on one, that field contains a
|
||||
// LaunchTemplateEbsBlockDeviceRequest, and on the other, it contains an
|
||||
// EbsBlockDevice. The EbsBlockDevice and
|
||||
// LaunchTemplateEbsBlockDeviceRequest structs are themselves
|
||||
// identical except for the struct's name, so you can cast one directly
|
||||
// into the other.
|
||||
// EbsBlockDevice.
|
||||
var launchMappingRequests []*ec2.LaunchTemplateBlockDeviceMappingRequest
|
||||
for _, mapping := range blockDeviceMappings {
|
||||
launchRequest := &ec2.LaunchTemplateBlockDeviceMappingRequest{
|
||||
DeviceName: mapping.DeviceName,
|
||||
Ebs: (*ec2.LaunchTemplateEbsBlockDeviceRequest)(mapping.Ebs),
|
||||
Ebs: castBlockDeviceToRequest(mapping.Ebs),
|
||||
VirtualName: mapping.VirtualName,
|
||||
}
|
||||
launchMappingRequests = append(launchMappingRequests, launchRequest)
|
||||
@@ -127,6 +145,10 @@ func (s *StepRunSpotInstance) CreateTemplateData(userData *string, az string,
|
||||
|
||||
}
|
||||
|
||||
if s.HttpEndpoint == "enabled" {
|
||||
templateData.MetadataOptions = &ec2.LaunchTemplateInstanceMetadataOptionsRequest{HttpEndpoint: &s.HttpEndpoint, HttpTokens: &s.HttpTokens, HttpPutResponseHopLimit: &s.HttpPutResponseHopLimit}
|
||||
}
|
||||
|
||||
// If instance type is not set, we'll just pick the lowest priced instance
|
||||
// available.
|
||||
if s.InstanceType != "" {
|
||||
|
||||
@@ -0,0 +1,25 @@
|
||||
package common
|
||||
|
||||
import (
|
||||
"github.com/aws/aws-sdk-go/aws"
|
||||
"github.com/aws/aws-sdk-go/aws/session"
|
||||
"github.com/aws/aws-sdk-go/service/ec2/ec2iface"
|
||||
)
|
||||
|
||||
type mockEC2Client struct {
|
||||
ec2iface.EC2API
|
||||
}
|
||||
|
||||
func FakeAccessConfig() *AccessConfig {
|
||||
accessConfig := AccessConfig{
|
||||
getEC2Connection: func() ec2iface.EC2API {
|
||||
return &mockEC2Client{}
|
||||
},
|
||||
PollingConfig: new(AWSPollingConfig),
|
||||
}
|
||||
accessConfig.session = session.Must(session.NewSession(&aws.Config{
|
||||
Region: aws.String("us-west-1"),
|
||||
}))
|
||||
|
||||
return &accessConfig
|
||||
}
|
||||
@@ -187,6 +187,9 @@ func (b *Builder) Run(ctx context.Context, ui packersdk.Ui, hook packersdk.Hook)
|
||||
Debug: b.config.PackerDebug,
|
||||
EbsOptimized: b.config.EbsOptimized,
|
||||
ExpectedRootDevice: "ebs",
|
||||
HttpEndpoint: b.config.Metadata.HttpEndpoint,
|
||||
HttpTokens: b.config.Metadata.HttpTokens,
|
||||
HttpPutResponseHopLimit: b.config.Metadata.HttpPutResponseHopLimit,
|
||||
InstanceInitiatedShutdownBehavior: b.config.InstanceInitiatedShutdownBehavior,
|
||||
InstanceType: b.config.InstanceType,
|
||||
Region: *ec2conn.Config.Region,
|
||||
@@ -211,6 +214,9 @@ func (b *Builder) Run(ctx context.Context, ui packersdk.Ui, hook packersdk.Hook)
|
||||
EbsOptimized: b.config.EbsOptimized,
|
||||
EnableT2Unlimited: b.config.EnableT2Unlimited,
|
||||
ExpectedRootDevice: "ebs",
|
||||
HttpEndpoint: b.config.Metadata.HttpEndpoint,
|
||||
HttpTokens: b.config.Metadata.HttpTokens,
|
||||
HttpPutResponseHopLimit: b.config.Metadata.HttpPutResponseHopLimit,
|
||||
InstanceInitiatedShutdownBehavior: b.config.InstanceInitiatedShutdownBehavior,
|
||||
InstanceType: b.config.InstanceType,
|
||||
IsRestricted: b.config.IsChinaCloud() || b.config.IsGovCloud(),
|
||||
|
||||
@@ -90,6 +90,7 @@ type FlatConfig struct {
|
||||
VpcFilter *common.FlatVpcFilterOptions `mapstructure:"vpc_filter" required:"false" cty:"vpc_filter" hcl:"vpc_filter"`
|
||||
VpcId *string `mapstructure:"vpc_id" required:"false" cty:"vpc_id" hcl:"vpc_id"`
|
||||
WindowsPasswordTimeout *string `mapstructure:"windows_password_timeout" required:"false" cty:"windows_password_timeout" hcl:"windows_password_timeout"`
|
||||
Metadata *common.FlatMetadataOptions `mapstructure:"metadata_options" required:"false" cty:"metadata_options" hcl:"metadata_options"`
|
||||
Type *string `mapstructure:"communicator" cty:"communicator" hcl:"communicator"`
|
||||
PauseBeforeConnect *string `mapstructure:"pause_before_connecting" cty:"pause_before_connecting" hcl:"pause_before_connecting"`
|
||||
SSHHost *string `mapstructure:"ssh_host" cty:"ssh_host" hcl:"ssh_host"`
|
||||
@@ -240,6 +241,7 @@ func (*FlatConfig) HCL2Spec() map[string]hcldec.Spec {
|
||||
"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},
|
||||
"metadata_options": &hcldec.BlockSpec{TypeName: "metadata_options", Nested: hcldec.ObjectSpec((*common.FlatMetadataOptions)(nil).HCL2Spec())},
|
||||
"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},
|
||||
|
||||
@@ -207,6 +207,9 @@ func (b *Builder) Run(ctx context.Context, ui packersdk.Ui, hook packersdk.Hook)
|
||||
Debug: b.config.PackerDebug,
|
||||
EbsOptimized: b.config.EbsOptimized,
|
||||
ExpectedRootDevice: "ebs",
|
||||
HttpEndpoint: b.config.Metadata.HttpEndpoint,
|
||||
HttpTokens: b.config.Metadata.HttpTokens,
|
||||
HttpPutResponseHopLimit: b.config.Metadata.HttpPutResponseHopLimit,
|
||||
InstanceInitiatedShutdownBehavior: b.config.InstanceInitiatedShutdownBehavior,
|
||||
InstanceType: b.config.InstanceType,
|
||||
Region: *ec2conn.Config.Region,
|
||||
@@ -230,6 +233,9 @@ func (b *Builder) Run(ctx context.Context, ui packersdk.Ui, hook packersdk.Hook)
|
||||
EbsOptimized: b.config.EbsOptimized,
|
||||
EnableT2Unlimited: b.config.EnableT2Unlimited,
|
||||
ExpectedRootDevice: "ebs",
|
||||
HttpEndpoint: b.config.Metadata.HttpEndpoint,
|
||||
HttpTokens: b.config.Metadata.HttpTokens,
|
||||
HttpPutResponseHopLimit: b.config.Metadata.HttpPutResponseHopLimit,
|
||||
InstanceInitiatedShutdownBehavior: b.config.InstanceInitiatedShutdownBehavior,
|
||||
InstanceType: b.config.InstanceType,
|
||||
IsRestricted: b.config.IsChinaCloud() || b.config.IsGovCloud(),
|
||||
|
||||
@@ -113,6 +113,7 @@ type FlatConfig struct {
|
||||
VpcFilter *common.FlatVpcFilterOptions `mapstructure:"vpc_filter" required:"false" cty:"vpc_filter" hcl:"vpc_filter"`
|
||||
VpcId *string `mapstructure:"vpc_id" required:"false" cty:"vpc_id" hcl:"vpc_id"`
|
||||
WindowsPasswordTimeout *string `mapstructure:"windows_password_timeout" required:"false" cty:"windows_password_timeout" hcl:"windows_password_timeout"`
|
||||
Metadata *common.FlatMetadataOptions `mapstructure:"metadata_options" required:"false" cty:"metadata_options" hcl:"metadata_options"`
|
||||
Type *string `mapstructure:"communicator" cty:"communicator" hcl:"communicator"`
|
||||
PauseBeforeConnect *string `mapstructure:"pause_before_connecting" cty:"pause_before_connecting" hcl:"pause_before_connecting"`
|
||||
SSHHost *string `mapstructure:"ssh_host" cty:"ssh_host" hcl:"ssh_host"`
|
||||
@@ -263,6 +264,7 @@ func (*FlatConfig) HCL2Spec() map[string]hcldec.Spec {
|
||||
"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},
|
||||
"metadata_options": &hcldec.BlockSpec{TypeName: "metadata_options", Nested: hcldec.ObjectSpec((*common.FlatMetadataOptions)(nil).HCL2Spec())},
|
||||
"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},
|
||||
|
||||
@@ -13,11 +13,15 @@ import (
|
||||
// map of region to list of volume IDs
|
||||
type EbsVolumes map[string][]string
|
||||
|
||||
// map of region to list of snapshot IDs
|
||||
type EbsSnapshots map[string][]string
|
||||
|
||||
// Artifact is an artifact implementation that contains built AMIs.
|
||||
type Artifact struct {
|
||||
// A map of regions to EBS Volume IDs.
|
||||
Volumes EbsVolumes
|
||||
|
||||
// A map of regions to EBS Snapshot IDs.
|
||||
Snapshots EbsSnapshots
|
||||
// BuilderId is the unique ID for the builder that created this AMI
|
||||
BuilderIdValue string
|
||||
|
||||
@@ -40,13 +44,21 @@ func (*Artifact) Files() []string {
|
||||
|
||||
// returns a sorted list of region:ID pairs
|
||||
func (a *Artifact) idList() []string {
|
||||
parts := make([]string, 0, len(a.Volumes))
|
||||
|
||||
parts := make([]string, 0, len(a.Volumes)+len(a.Snapshots))
|
||||
|
||||
for region, volumeIDs := range a.Volumes {
|
||||
for _, volumeID := range volumeIDs {
|
||||
parts = append(parts, fmt.Sprintf("%s:%s", region, volumeID))
|
||||
}
|
||||
}
|
||||
|
||||
for region, snapshotIDs := range a.Snapshots {
|
||||
for _, snapshotID := range snapshotIDs {
|
||||
parts = append(parts, fmt.Sprintf("%s:%s", region, snapshotID))
|
||||
}
|
||||
}
|
||||
|
||||
sort.Strings(parts)
|
||||
return parts
|
||||
}
|
||||
|
||||
@@ -20,6 +20,11 @@ type BlockDevice struct {
|
||||
// [`dynamic_block`](/docs/templates/hcl_templates/expressions#dynamic-blocks)
|
||||
// will allow you to create those programatically.
|
||||
Tag config.KeyValues `mapstructure:"tag" required:"false"`
|
||||
|
||||
// Create a Snapshot of this Volume.
|
||||
SnapshotVolume bool `mapstructure:"snapshot_volume" required:"false"`
|
||||
|
||||
awscommon.SnapshotConfig `mapstructure:",squash"`
|
||||
}
|
||||
|
||||
type BlockDevices []BlockDevice
|
||||
@@ -38,6 +43,7 @@ func (bds BlockDevices) Prepare(ctx *interpolate.Context) (errs []error) {
|
||||
for _, block := range bds {
|
||||
|
||||
errs = append(errs, block.Tag.CopyOn(&block.Tags)...)
|
||||
errs = append(errs, block.SnapshotTag.CopyOn(&block.SnapshotTags)...)
|
||||
|
||||
if err := block.Prepare(ctx); err != nil {
|
||||
errs = append(errs, err)
|
||||
|
||||
@@ -123,16 +123,12 @@ func (b *Builder) Prepare(raws ...interface{}) ([]string, []string, error) {
|
||||
// Accumulate any errors
|
||||
var errs *packersdk.MultiError
|
||||
var warns []string
|
||||
|
||||
errs = packersdk.MultiErrorAppend(errs, b.config.VolumeRunTag.CopyOn(&b.config.VolumeRunTags)...)
|
||||
errs = packersdk.MultiErrorAppend(errs, b.config.AccessConfig.Prepare()...)
|
||||
errs = packersdk.MultiErrorAppend(errs, b.config.RunConfig.Prepare(&b.config.ctx)...)
|
||||
errs = packersdk.MultiErrorAppend(errs, b.config.launchBlockDevices.Prepare(&b.config.ctx)...)
|
||||
|
||||
for _, d := range b.config.VolumeMappings {
|
||||
if err := d.Prepare(&b.config.ctx); err != nil {
|
||||
errs = packersdk.MultiErrorAppend(errs, fmt.Errorf("AMIMapping: %s", err.Error()))
|
||||
}
|
||||
}
|
||||
errs = packersdk.MultiErrorAppend(errs, b.config.VolumeMappings.Prepare(&b.config.ctx)...)
|
||||
|
||||
b.config.launchBlockDevices = b.config.VolumeMappings
|
||||
if err != nil {
|
||||
@@ -195,6 +191,9 @@ func (b *Builder) Run(ctx context.Context, ui packersdk.Ui, hook packersdk.Hook)
|
||||
Debug: b.config.PackerDebug,
|
||||
EbsOptimized: b.config.EbsOptimized,
|
||||
ExpectedRootDevice: "ebs",
|
||||
HttpEndpoint: b.config.Metadata.HttpEndpoint,
|
||||
HttpTokens: b.config.Metadata.HttpTokens,
|
||||
HttpPutResponseHopLimit: b.config.Metadata.HttpPutResponseHopLimit,
|
||||
InstanceInitiatedShutdownBehavior: b.config.InstanceInitiatedShutdownBehavior,
|
||||
InstanceType: b.config.InstanceType,
|
||||
Region: *ec2conn.Config.Region,
|
||||
@@ -218,6 +217,9 @@ func (b *Builder) Run(ctx context.Context, ui packersdk.Ui, hook packersdk.Hook)
|
||||
EbsOptimized: b.config.EbsOptimized,
|
||||
EnableT2Unlimited: b.config.EnableT2Unlimited,
|
||||
ExpectedRootDevice: "ebs",
|
||||
HttpEndpoint: b.config.Metadata.HttpEndpoint,
|
||||
HttpTokens: b.config.Metadata.HttpTokens,
|
||||
HttpPutResponseHopLimit: b.config.Metadata.HttpPutResponseHopLimit,
|
||||
InstanceInitiatedShutdownBehavior: b.config.InstanceInitiatedShutdownBehavior,
|
||||
InstanceType: b.config.InstanceType,
|
||||
IsRestricted: b.config.IsChinaCloud() || b.config.IsGovCloud(),
|
||||
@@ -312,6 +314,12 @@ func (b *Builder) Run(ctx context.Context, ui packersdk.Ui, hook packersdk.Hook)
|
||||
EnableAMISriovNetSupport: b.config.AMISriovNetSupport,
|
||||
EnableAMIENASupport: b.config.AMIENASupport,
|
||||
},
|
||||
&stepSnapshotEBSVolumes{
|
||||
PollingConfig: b.config.PollingConfig,
|
||||
VolumeMapping: b.config.VolumeMappings,
|
||||
AccessConfig: &b.config.AccessConfig,
|
||||
Ctx: b.config.ctx,
|
||||
},
|
||||
}
|
||||
|
||||
// Run!
|
||||
@@ -326,6 +334,7 @@ func (b *Builder) Run(ctx context.Context, ui packersdk.Ui, hook packersdk.Hook)
|
||||
// Build the artifact and return it
|
||||
artifact := &Artifact{
|
||||
Volumes: state.Get("ebsvolumes").(EbsVolumes),
|
||||
Snapshots: state.Get("ebssnapshots").(EbsSnapshots),
|
||||
BuilderIdValue: BuilderId,
|
||||
Conn: ec2conn,
|
||||
StateData: map[string]interface{}{"generated_data": state.Get("generated_data")},
|
||||
|
||||
@@ -25,6 +25,11 @@ type FlatBlockDevice struct {
|
||||
KmsKeyId *string `mapstructure:"kms_key_id" required:"false" cty:"kms_key_id" hcl:"kms_key_id"`
|
||||
Tags map[string]string `mapstructure:"tags" required:"false" cty:"tags" hcl:"tags"`
|
||||
Tag []config.FlatKeyValue `mapstructure:"tag" required:"false" cty:"tag" hcl:"tag"`
|
||||
SnapshotVolume *bool `mapstructure:"snapshot_volume" required:"false" cty:"snapshot_volume" hcl:"snapshot_volume"`
|
||||
SnapshotTags map[string]string `mapstructure:"snapshot_tags" required:"false" cty:"snapshot_tags" hcl:"snapshot_tags"`
|
||||
SnapshotTag []config.FlatKeyValue `mapstructure:"snapshot_tag" required:"false" cty:"snapshot_tag" hcl:"snapshot_tag"`
|
||||
SnapshotUsers []string `mapstructure:"snapshot_users" required:"false" cty:"snapshot_users" hcl:"snapshot_users"`
|
||||
SnapshotGroups []string `mapstructure:"snapshot_groups" required:"false" cty:"snapshot_groups" hcl:"snapshot_groups"`
|
||||
}
|
||||
|
||||
// FlatMapstructure returns a new FlatBlockDevice.
|
||||
@@ -52,6 +57,11 @@ func (*FlatBlockDevice) HCL2Spec() map[string]hcldec.Spec {
|
||||
"kms_key_id": &hcldec.AttrSpec{Name: "kms_key_id", Type: cty.String, Required: false},
|
||||
"tags": &hcldec.AttrSpec{Name: "tags", Type: cty.Map(cty.String), Required: false},
|
||||
"tag": &hcldec.BlockListSpec{TypeName: "tag", Nested: hcldec.ObjectSpec((*config.FlatKeyValue)(nil).HCL2Spec())},
|
||||
"snapshot_volume": &hcldec.AttrSpec{Name: "snapshot_volume", Type: cty.Bool, Required: false},
|
||||
"snapshot_tags": &hcldec.AttrSpec{Name: "snapshot_tags", Type: cty.Map(cty.String), Required: false},
|
||||
"snapshot_tag": &hcldec.BlockListSpec{TypeName: "snapshot_tag", Nested: hcldec.ObjectSpec((*config.FlatKeyValue)(nil).HCL2Spec())},
|
||||
"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},
|
||||
}
|
||||
return s
|
||||
}
|
||||
@@ -115,6 +125,7 @@ type FlatConfig struct {
|
||||
VpcFilter *common.FlatVpcFilterOptions `mapstructure:"vpc_filter" required:"false" cty:"vpc_filter" hcl:"vpc_filter"`
|
||||
VpcId *string `mapstructure:"vpc_id" required:"false" cty:"vpc_id" hcl:"vpc_id"`
|
||||
WindowsPasswordTimeout *string `mapstructure:"windows_password_timeout" required:"false" cty:"windows_password_timeout" hcl:"windows_password_timeout"`
|
||||
Metadata *common.FlatMetadataOptions `mapstructure:"metadata_options" required:"false" cty:"metadata_options" hcl:"metadata_options"`
|
||||
Type *string `mapstructure:"communicator" cty:"communicator" hcl:"communicator"`
|
||||
PauseBeforeConnect *string `mapstructure:"pause_before_connecting" cty:"pause_before_connecting" hcl:"pause_before_connecting"`
|
||||
SSHHost *string `mapstructure:"ssh_host" cty:"ssh_host" hcl:"ssh_host"`
|
||||
@@ -242,6 +253,7 @@ func (*FlatConfig) HCL2Spec() map[string]hcldec.Spec {
|
||||
"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},
|
||||
"metadata_options": &hcldec.BlockSpec{TypeName: "metadata_options", Nested: hcldec.ObjectSpec((*common.FlatMetadataOptions)(nil).HCL2Spec())},
|
||||
"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},
|
||||
|
||||
@@ -104,9 +104,6 @@ func TestBuilderPrepare_ReturnGeneratedData(t *testing.T) {
|
||||
if len(generatedData) == 0 {
|
||||
t.Fatalf("Generated data should not be empty")
|
||||
}
|
||||
if len(generatedData) == 0 {
|
||||
t.Fatalf("Generated data should not be empty")
|
||||
}
|
||||
if generatedData[0] != "SourceAMIName" {
|
||||
t.Fatalf("Generated data should contain SourceAMIName")
|
||||
}
|
||||
@@ -126,3 +123,44 @@ func TestBuilderPrepare_ReturnGeneratedData(t *testing.T) {
|
||||
t.Fatalf("Generated data should contain SourceAMIOwnerName")
|
||||
}
|
||||
}
|
||||
|
||||
func TestBuidler_ConfigBlockdevicemapping(t *testing.T) {
|
||||
var b Builder
|
||||
config := testConfig()
|
||||
|
||||
//Set some snapshot settings
|
||||
config["ebs_volumes"] = []map[string]interface{}{
|
||||
{
|
||||
"device_name": "/dev/xvdb",
|
||||
"volume_size": "32",
|
||||
"delete_on_termination": true,
|
||||
},
|
||||
{
|
||||
"device_name": "/dev/xvdc",
|
||||
"volume_size": "32",
|
||||
"delete_on_termination": true,
|
||||
"snapshot_tags": map[string]string{
|
||||
"Test_Tag": "tag_value",
|
||||
"another tag": "another value",
|
||||
},
|
||||
"snapshot_users": []string{
|
||||
"123", "456",
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
generatedData, warnings, err := b.Prepare(config)
|
||||
|
||||
if len(warnings) > 0 {
|
||||
t.Fatalf("bad: %#v", warnings)
|
||||
}
|
||||
if err != nil {
|
||||
t.Fatalf("should not have error: %s", err)
|
||||
}
|
||||
if len(generatedData) == 0 {
|
||||
t.Fatalf("Generated data should not be empty")
|
||||
}
|
||||
|
||||
t.Logf("Test gen %+v", b.config.VolumeMappings)
|
||||
|
||||
}
|
||||
|
||||
@@ -0,0 +1,167 @@
|
||||
package ebsvolume
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
|
||||
"github.com/aws/aws-sdk-go/aws"
|
||||
"github.com/aws/aws-sdk-go/service/ec2"
|
||||
"github.com/aws/aws-sdk-go/service/ec2/ec2iface"
|
||||
"github.com/hashicorp/packer-plugin-sdk/multistep"
|
||||
"github.com/hashicorp/packer-plugin-sdk/packer"
|
||||
"github.com/hashicorp/packer-plugin-sdk/template/interpolate"
|
||||
awscommon "github.com/hashicorp/packer/builder/amazon/common"
|
||||
)
|
||||
|
||||
type stepSnapshotEBSVolumes struct {
|
||||
PollingConfig *awscommon.AWSPollingConfig
|
||||
AccessConfig *awscommon.AccessConfig
|
||||
VolumeMapping []BlockDevice
|
||||
//Map of SnapshotID: BlockDevice, Where *BlockDevice is in VolumeMapping
|
||||
snapshotMap map[string]*BlockDevice
|
||||
Ctx interpolate.Context
|
||||
}
|
||||
|
||||
func (s *stepSnapshotEBSVolumes) Run(ctx context.Context, state multistep.StateBag) multistep.StepAction {
|
||||
ec2conn := state.Get("ec2").(ec2iface.EC2API)
|
||||
instance := state.Get("instance").(*ec2.Instance)
|
||||
ui := state.Get("ui").(packer.Ui)
|
||||
|
||||
s.snapshotMap = make(map[string]*BlockDevice)
|
||||
|
||||
for _, instanceBlockDevice := range instance.BlockDeviceMappings {
|
||||
for _, configVolumeMapping := range s.VolumeMapping {
|
||||
//Find the config entry for the instance blockDevice
|
||||
if configVolumeMapping.DeviceName == *instanceBlockDevice.DeviceName {
|
||||
//Skip Volumes that are not set to create snapshot
|
||||
if configVolumeMapping.SnapshotVolume != true {
|
||||
continue
|
||||
}
|
||||
|
||||
ui.Message(fmt.Sprintf("Compiling list of tags to apply to snapshot from Volume %s...", *instanceBlockDevice.DeviceName))
|
||||
tags, err := awscommon.TagMap(configVolumeMapping.SnapshotTags).EC2Tags(s.Ctx, s.AccessConfig.SessionRegion(), state)
|
||||
if err != nil {
|
||||
err := fmt.Errorf("Error generating tags for snapshot %s: %s", *instanceBlockDevice.DeviceName, err)
|
||||
state.Put("error", err)
|
||||
ui.Error(err.Error())
|
||||
return multistep.ActionHalt
|
||||
}
|
||||
tags.Report(ui)
|
||||
|
||||
tagSpec := &ec2.TagSpecification{
|
||||
ResourceType: aws.String("snapshot"),
|
||||
Tags: tags,
|
||||
}
|
||||
|
||||
input := &ec2.CreateSnapshotInput{
|
||||
VolumeId: aws.String(*instanceBlockDevice.Ebs.VolumeId),
|
||||
TagSpecifications: []*ec2.TagSpecification{tagSpec},
|
||||
}
|
||||
|
||||
//Dont try to set an empty tag spec
|
||||
if len(tags) == 0 {
|
||||
input.TagSpecifications = nil
|
||||
}
|
||||
|
||||
ui.Message(fmt.Sprintf("Requesting snapshot of volume: %s...", *instanceBlockDevice.Ebs.VolumeId))
|
||||
snapshot, err := ec2conn.CreateSnapshot(input)
|
||||
if err != nil || snapshot == nil {
|
||||
err := fmt.Errorf("Error generating snapsot for volume %s: %s", *instanceBlockDevice.Ebs.VolumeId, err)
|
||||
state.Put("error", err)
|
||||
ui.Error(err.Error())
|
||||
return multistep.ActionHalt
|
||||
}
|
||||
ui.Message(fmt.Sprintf("Requested Snapshot of Volume %s: %s", *instanceBlockDevice.Ebs.VolumeId, *snapshot.SnapshotId))
|
||||
s.snapshotMap[*snapshot.SnapshotId] = &configVolumeMapping
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
ui.Say("Waiting for Snapshots to become ready...")
|
||||
for snapID := range s.snapshotMap {
|
||||
ui.Message(fmt.Sprintf("Waiting for %s to be ready.", snapID))
|
||||
err := s.PollingConfig.WaitUntilSnapshotDone(ctx, ec2conn, snapID)
|
||||
if err != nil {
|
||||
err = fmt.Errorf("Error waiting for snapsot to become ready %s", err)
|
||||
state.Put("error", err)
|
||||
ui.Error(err.Error())
|
||||
ui.Message("Failed to wait")
|
||||
return multistep.ActionHalt
|
||||
}
|
||||
ui.Message(fmt.Sprintf("Snapshot Ready: %s", snapID))
|
||||
}
|
||||
|
||||
//Attach User and Group permissions to snapshots
|
||||
ui.Say("Setting User/Group Permissions for Snapshots...")
|
||||
for snapID, bd := range s.snapshotMap {
|
||||
snapshotOptions := make(map[string]*ec2.ModifySnapshotAttributeInput)
|
||||
|
||||
if len(bd.SnapshotGroups) > 0 {
|
||||
groups := make([]*string, len(bd.SnapshotGroups))
|
||||
addsSnapshot := make([]*ec2.CreateVolumePermission, len(bd.SnapshotGroups))
|
||||
|
||||
addSnapshotGroups := &ec2.ModifySnapshotAttributeInput{
|
||||
CreateVolumePermission: &ec2.CreateVolumePermissionModifications{},
|
||||
}
|
||||
|
||||
for i, g := range bd.SnapshotGroups {
|
||||
groups[i] = aws.String(g)
|
||||
addsSnapshot[i] = &ec2.CreateVolumePermission{
|
||||
Group: aws.String(g),
|
||||
}
|
||||
}
|
||||
|
||||
addSnapshotGroups.GroupNames = groups
|
||||
addSnapshotGroups.CreateVolumePermission.Add = addsSnapshot
|
||||
snapshotOptions["groups"] = addSnapshotGroups
|
||||
|
||||
}
|
||||
|
||||
if len(bd.SnapshotUsers) > 0 {
|
||||
users := make([]*string, len(bd.SnapshotUsers))
|
||||
addsSnapshot := make([]*ec2.CreateVolumePermission, len(bd.SnapshotUsers))
|
||||
for i, u := range bd.SnapshotUsers {
|
||||
users[i] = aws.String(u)
|
||||
addsSnapshot[i] = &ec2.CreateVolumePermission{UserId: aws.String(u)}
|
||||
}
|
||||
|
||||
snapshotOptions["users"] = &ec2.ModifySnapshotAttributeInput{
|
||||
UserIds: users,
|
||||
CreateVolumePermission: &ec2.CreateVolumePermissionModifications{
|
||||
Add: addsSnapshot,
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
//Todo: Copy to other regions and repeat this block in all regions.
|
||||
for name, input := range snapshotOptions {
|
||||
ui.Message(fmt.Sprintf("Modifying: %s", name))
|
||||
input.SnapshotId = &snapID
|
||||
_, err := ec2conn.ModifySnapshotAttribute(input)
|
||||
if err != nil {
|
||||
err := fmt.Errorf("Error modify snapshot attributes: %s", err)
|
||||
state.Put("error", err)
|
||||
ui.Error(err.Error())
|
||||
return multistep.ActionHalt
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
//Record all snapshots in current Region.
|
||||
snapshots := make(EbsSnapshots)
|
||||
currentregion := s.AccessConfig.SessionRegion()
|
||||
|
||||
for snapID := range s.snapshotMap {
|
||||
snapshots[currentregion] = append(
|
||||
snapshots[currentregion],
|
||||
snapID)
|
||||
}
|
||||
//Records artifacts
|
||||
state.Put("ebssnapshots", snapshots)
|
||||
|
||||
return multistep.ActionContinue
|
||||
}
|
||||
|
||||
func (s *stepSnapshotEBSVolumes) Cleanup(state multistep.StateBag) {
|
||||
// No cleanup...
|
||||
}
|
||||
@@ -0,0 +1,170 @@
|
||||
package ebsvolume
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"context"
|
||||
"fmt"
|
||||
"testing"
|
||||
|
||||
"github.com/aws/aws-sdk-go/aws"
|
||||
"github.com/aws/aws-sdk-go/aws/request"
|
||||
|
||||
//"github.com/aws/aws-sdk-go/service/ec2"
|
||||
"github.com/aws/aws-sdk-go/service/ec2"
|
||||
"github.com/aws/aws-sdk-go/service/ec2/ec2iface"
|
||||
"github.com/hashicorp/packer-plugin-sdk/multistep"
|
||||
"github.com/hashicorp/packer-plugin-sdk/packer"
|
||||
"github.com/hashicorp/packer/builder/amazon/common"
|
||||
)
|
||||
|
||||
// Define a mock struct to be used in unit tests for common aws steps.
|
||||
type mockEC2Conn struct {
|
||||
ec2iface.EC2API
|
||||
Config *aws.Config
|
||||
}
|
||||
|
||||
func (m *mockEC2Conn) CreateSnapshot(input *ec2.CreateSnapshotInput) (*ec2.Snapshot, error) {
|
||||
snap := &ec2.Snapshot{
|
||||
// This isn't typical amazon format, but injecting the volume id into
|
||||
// this field lets us verify that the right volume was snapshotted with
|
||||
// a simple string comparison
|
||||
SnapshotId: aws.String(fmt.Sprintf("snap-of-%s", *input.VolumeId)),
|
||||
}
|
||||
|
||||
return snap, nil
|
||||
}
|
||||
|
||||
func (m *mockEC2Conn) WaitUntilSnapshotCompletedWithContext(aws.Context, *ec2.DescribeSnapshotsInput, ...request.WaiterOption) error {
|
||||
return nil
|
||||
}
|
||||
|
||||
func getMockConn(config *common.AccessConfig, target string) (ec2iface.EC2API, error) {
|
||||
mockConn := &mockEC2Conn{
|
||||
Config: aws.NewConfig(),
|
||||
}
|
||||
return mockConn, nil
|
||||
}
|
||||
|
||||
// Create statebag for running test
|
||||
func tState(t *testing.T) multistep.StateBag {
|
||||
state := new(multistep.BasicStateBag)
|
||||
state.Put("ui", &packer.BasicUi{
|
||||
Reader: new(bytes.Buffer),
|
||||
Writer: new(bytes.Buffer),
|
||||
})
|
||||
// state.Put("amis", map[string]string{"us-east-1": "ami-12345"})
|
||||
// state.Put("snapshots", map[string][]string{"us-east-1": {"snap-0012345"}})
|
||||
conn, _ := getMockConn(&common.AccessConfig{}, "us-east-2")
|
||||
|
||||
state.Put("ec2", conn)
|
||||
// Store a fake instance that contains a block device that matches the
|
||||
// volumes defined in the config above
|
||||
state.Put("instance", &ec2.Instance{
|
||||
InstanceId: aws.String("instance-id"),
|
||||
BlockDeviceMappings: []*ec2.InstanceBlockDeviceMapping{
|
||||
{
|
||||
DeviceName: aws.String("/dev/xvda"),
|
||||
Ebs: &ec2.EbsInstanceBlockDevice{
|
||||
VolumeId: aws.String("vol-1234"),
|
||||
},
|
||||
},
|
||||
{
|
||||
DeviceName: aws.String("/dev/xvdb"),
|
||||
Ebs: &ec2.EbsInstanceBlockDevice{
|
||||
VolumeId: aws.String("vol-5678"),
|
||||
},
|
||||
},
|
||||
},
|
||||
})
|
||||
return state
|
||||
}
|
||||
|
||||
func TestStepSnapshot_run_simple(t *testing.T) {
|
||||
var b Builder
|
||||
config := testConfig() //from builder_test
|
||||
|
||||
//Set some snapshot settings
|
||||
config["ebs_volumes"] = []map[string]interface{}{
|
||||
{
|
||||
"device_name": "/dev/xvdb",
|
||||
"volume_size": "32",
|
||||
"delete_on_termination": true,
|
||||
"snapshot_volume": true,
|
||||
},
|
||||
}
|
||||
|
||||
generatedData, warnings, err := b.Prepare(config)
|
||||
if len(warnings) > 0 {
|
||||
t.Fatalf("bad: %#v", warnings)
|
||||
}
|
||||
if err != nil {
|
||||
t.Fatalf("should not have error: %s", err)
|
||||
}
|
||||
if len(generatedData) == 0 {
|
||||
t.Fatalf("Generated data should not be empty")
|
||||
}
|
||||
|
||||
state := tState(t)
|
||||
|
||||
accessConfig := common.FakeAccessConfig()
|
||||
|
||||
step := stepSnapshotEBSVolumes{
|
||||
PollingConfig: new(common.AWSPollingConfig),
|
||||
AccessConfig: accessConfig,
|
||||
VolumeMapping: b.config.VolumeMappings,
|
||||
Ctx: b.config.ctx,
|
||||
}
|
||||
|
||||
step.Run(context.Background(), state)
|
||||
|
||||
if len(step.snapshotMap) != 1 {
|
||||
t.Fatalf("Missing Snapshot from step")
|
||||
}
|
||||
|
||||
if volmapping := step.snapshotMap["snap-of-vol-5678"]; volmapping == nil {
|
||||
t.Fatalf("Didn't snapshot correct volume: Map is %#v", step.snapshotMap)
|
||||
}
|
||||
}
|
||||
|
||||
func TestStepSnapshot_run_no_snaps(t *testing.T) {
|
||||
var b Builder
|
||||
config := testConfig() //from builder_test
|
||||
|
||||
//Set some snapshot settings
|
||||
config["ebs_volumes"] = []map[string]interface{}{
|
||||
{
|
||||
"device_name": "/dev/xvdb",
|
||||
"volume_size": "32",
|
||||
"delete_on_termination": true,
|
||||
"snapshot_volume": false,
|
||||
},
|
||||
}
|
||||
|
||||
generatedData, warnings, err := b.Prepare(config)
|
||||
if len(warnings) > 0 {
|
||||
t.Fatalf("bad: %#v", warnings)
|
||||
}
|
||||
if err != nil {
|
||||
t.Fatalf("should not have error: %s", err)
|
||||
}
|
||||
if len(generatedData) == 0 {
|
||||
t.Fatalf("Generated data should not be empty")
|
||||
}
|
||||
|
||||
state := tState(t)
|
||||
|
||||
accessConfig := common.FakeAccessConfig()
|
||||
|
||||
step := stepSnapshotEBSVolumes{
|
||||
PollingConfig: new(common.AWSPollingConfig),
|
||||
AccessConfig: accessConfig,
|
||||
VolumeMapping: b.config.VolumeMappings,
|
||||
Ctx: b.config.ctx,
|
||||
}
|
||||
|
||||
step.Run(context.Background(), state)
|
||||
|
||||
if len(step.snapshotMap) != 0 {
|
||||
t.Fatalf("Shouldn't have snapshotted any volumes")
|
||||
}
|
||||
}
|
||||
@@ -90,6 +90,7 @@ type FlatConfig struct {
|
||||
VpcFilter *common.FlatVpcFilterOptions `mapstructure:"vpc_filter" required:"false" cty:"vpc_filter" hcl:"vpc_filter"`
|
||||
VpcId *string `mapstructure:"vpc_id" required:"false" cty:"vpc_id" hcl:"vpc_id"`
|
||||
WindowsPasswordTimeout *string `mapstructure:"windows_password_timeout" required:"false" cty:"windows_password_timeout" hcl:"windows_password_timeout"`
|
||||
Metadata *common.FlatMetadataOptions `mapstructure:"metadata_options" required:"false" cty:"metadata_options" hcl:"metadata_options"`
|
||||
Type *string `mapstructure:"communicator" cty:"communicator" hcl:"communicator"`
|
||||
PauseBeforeConnect *string `mapstructure:"pause_before_connecting" cty:"pause_before_connecting" hcl:"pause_before_connecting"`
|
||||
SSHHost *string `mapstructure:"ssh_host" cty:"ssh_host" hcl:"ssh_host"`
|
||||
@@ -245,6 +246,7 @@ func (*FlatConfig) HCL2Spec() map[string]hcldec.Spec {
|
||||
"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},
|
||||
"metadata_options": &hcldec.BlockSpec{TypeName: "metadata_options", Nested: hcldec.ObjectSpec((*common.FlatMetadataOptions)(nil).HCL2Spec())},
|
||||
"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},
|
||||
|
||||
@@ -233,7 +233,9 @@ func NewAzureClient(subscriptionID, sigSubscriptionID, resourceGroupName, storag
|
||||
azureClient.GalleryImageVersionsClient.ResponseInspector = byConcatDecorators(byInspecting(maxlen), errorCapture(azureClient))
|
||||
azureClient.GalleryImageVersionsClient.UserAgent = fmt.Sprintf("%s %s", useragent.String(version.AzurePluginVersion.FormattedVersion()), azureClient.GalleryImageVersionsClient.UserAgent)
|
||||
azureClient.GalleryImageVersionsClient.Client.PollingDuration = sharedGalleryTimeout
|
||||
azureClient.GalleryImageVersionsClient.SubscriptionID = sigSubscriptionID
|
||||
if sigSubscriptionID != "" {
|
||||
azureClient.GalleryImageVersionsClient.SubscriptionID = sigSubscriptionID
|
||||
}
|
||||
|
||||
azureClient.GalleryImagesClient = newCompute.NewGalleryImagesClientWithBaseURI(cloud.ResourceManagerEndpoint, subscriptionID)
|
||||
azureClient.GalleryImagesClient.Authorizer = autorest.NewBearerAuthorizer(servicePrincipalToken)
|
||||
@@ -241,7 +243,9 @@ func NewAzureClient(subscriptionID, sigSubscriptionID, resourceGroupName, storag
|
||||
azureClient.GalleryImagesClient.ResponseInspector = byConcatDecorators(byInspecting(maxlen), errorCapture(azureClient))
|
||||
azureClient.GalleryImagesClient.UserAgent = fmt.Sprintf("%s %s", useragent.String(version.AzurePluginVersion.FormattedVersion()), azureClient.GalleryImagesClient.UserAgent)
|
||||
azureClient.GalleryImagesClient.Client.PollingDuration = pollingDuration
|
||||
azureClient.GalleryImagesClient.SubscriptionID = sigSubscriptionID
|
||||
if sigSubscriptionID != "" {
|
||||
azureClient.GalleryImagesClient.SubscriptionID = sigSubscriptionID
|
||||
}
|
||||
|
||||
keyVaultURL, err := url.Parse(cloud.KeyVaultEndpoint)
|
||||
if err != nil {
|
||||
|
||||
@@ -4,9 +4,14 @@ import (
|
||||
"context"
|
||||
"errors"
|
||||
"fmt"
|
||||
"math"
|
||||
"math/rand"
|
||||
"net/http"
|
||||
"regexp"
|
||||
"sync/atomic"
|
||||
"time"
|
||||
|
||||
"github.com/oracle/oci-go-sdk/common"
|
||||
core "github.com/oracle/oci-go-sdk/core"
|
||||
)
|
||||
|
||||
@@ -19,6 +24,31 @@ type driverOCI struct {
|
||||
context context.Context
|
||||
}
|
||||
|
||||
var retryPolicy = &common.RetryPolicy{
|
||||
MaximumNumberAttempts: 10,
|
||||
ShouldRetryOperation: func(res common.OCIOperationResponse) bool {
|
||||
var e common.ServiceError
|
||||
if errors.As(res.Error, &e) {
|
||||
switch e.GetHTTPStatusCode() {
|
||||
case http.StatusTooManyRequests, http.StatusInternalServerError, http.StatusServiceUnavailable:
|
||||
return true
|
||||
}
|
||||
}
|
||||
return false
|
||||
},
|
||||
NextDuration: func(res common.OCIOperationResponse) time.Duration {
|
||||
x := uint64(res.AttemptNumber)
|
||||
d := time.Duration(math.Pow(2, float64(atomic.LoadUint64(&x)))) * time.Second
|
||||
j := time.Duration(rand.Float64()*(2000)) * time.Millisecond
|
||||
w := d + j
|
||||
return w
|
||||
},
|
||||
}
|
||||
|
||||
var requestMetadata = common.RequestMetadata{
|
||||
RetryPolicy: retryPolicy,
|
||||
}
|
||||
|
||||
// NewDriverOCI Creates a new driverOCI with a connected compute client and a connected vcn client.
|
||||
func NewDriverOCI(cfg *Config) (Driver, error) {
|
||||
coreClient, err := core.NewComputeClientWithConfigurationProvider(cfg.configProvider)
|
||||
@@ -80,6 +110,7 @@ func (d *driverOCI) CreateInstance(ctx context.Context, publicKey string) (strin
|
||||
LifecycleState: "AVAILABLE",
|
||||
SortBy: "TIMECREATED",
|
||||
SortOrder: "DESC",
|
||||
RequestMetadata: requestMetadata,
|
||||
})
|
||||
if err != nil {
|
||||
return "", err
|
||||
@@ -127,7 +158,10 @@ func (d *driverOCI) CreateInstance(ctx context.Context, publicKey string) (strin
|
||||
Metadata: metadata,
|
||||
}
|
||||
|
||||
instance, err := d.computeClient.LaunchInstance(context.TODO(), core.LaunchInstanceRequest{LaunchInstanceDetails: instanceDetails})
|
||||
instance, err := d.computeClient.LaunchInstance(context.TODO(), core.LaunchInstanceRequest{
|
||||
LaunchInstanceDetails: instanceDetails,
|
||||
RequestMetadata: requestMetadata,
|
||||
})
|
||||
|
||||
if err != nil {
|
||||
return "", err
|
||||
@@ -145,7 +179,9 @@ func (d *driverOCI) CreateImage(ctx context.Context, id string) (core.Image, err
|
||||
FreeformTags: d.cfg.Tags,
|
||||
DefinedTags: d.cfg.DefinedTags,
|
||||
LaunchMode: core.CreateImageDetailsLaunchModeEnum(d.cfg.LaunchMode),
|
||||
}})
|
||||
},
|
||||
RequestMetadata: requestMetadata,
|
||||
})
|
||||
|
||||
if err != nil {
|
||||
return core.Image{}, err
|
||||
@@ -156,15 +192,19 @@ func (d *driverOCI) CreateImage(ctx context.Context, id string) (core.Image, err
|
||||
|
||||
// DeleteImage deletes a custom image.
|
||||
func (d *driverOCI) DeleteImage(ctx context.Context, id string) error {
|
||||
_, err := d.computeClient.DeleteImage(ctx, core.DeleteImageRequest{ImageId: &id})
|
||||
_, err := d.computeClient.DeleteImage(ctx, core.DeleteImageRequest{
|
||||
ImageId: &id,
|
||||
RequestMetadata: requestMetadata,
|
||||
})
|
||||
return err
|
||||
}
|
||||
|
||||
// GetInstanceIP returns the public or private IP corresponding to the given instance id.
|
||||
func (d *driverOCI) GetInstanceIP(ctx context.Context, id string) (string, error) {
|
||||
vnics, err := d.computeClient.ListVnicAttachments(ctx, core.ListVnicAttachmentsRequest{
|
||||
InstanceId: &id,
|
||||
CompartmentId: &d.cfg.CompartmentID,
|
||||
InstanceId: &id,
|
||||
CompartmentId: &d.cfg.CompartmentID,
|
||||
RequestMetadata: requestMetadata,
|
||||
})
|
||||
if err != nil {
|
||||
return "", err
|
||||
@@ -174,7 +214,10 @@ func (d *driverOCI) GetInstanceIP(ctx context.Context, id string) (string, error
|
||||
return "", errors.New("instance has zero VNICs")
|
||||
}
|
||||
|
||||
vnic, err := d.vcnClient.GetVnic(ctx, core.GetVnicRequest{VnicId: vnics.Items[0].VnicId})
|
||||
vnic, err := d.vcnClient.GetVnic(ctx, core.GetVnicRequest{
|
||||
VnicId: vnics.Items[0].VnicId,
|
||||
RequestMetadata: requestMetadata,
|
||||
})
|
||||
if err != nil {
|
||||
return "", fmt.Errorf("Error getting VNIC details: %s", err)
|
||||
}
|
||||
@@ -192,7 +235,8 @@ func (d *driverOCI) GetInstanceIP(ctx context.Context, id string) (string, error
|
||||
|
||||
func (d *driverOCI) GetInstanceInitialCredentials(ctx context.Context, id string) (string, string, error) {
|
||||
credentials, err := d.computeClient.GetWindowsInstanceInitialCredentials(ctx, core.GetWindowsInstanceInitialCredentialsRequest{
|
||||
InstanceId: &id,
|
||||
InstanceId: &id,
|
||||
RequestMetadata: requestMetadata,
|
||||
})
|
||||
if err != nil {
|
||||
return "", "", err
|
||||
@@ -204,7 +248,8 @@ func (d *driverOCI) GetInstanceInitialCredentials(ctx context.Context, id string
|
||||
// TerminateInstance terminates a compute instance.
|
||||
func (d *driverOCI) TerminateInstance(ctx context.Context, id string) error {
|
||||
_, err := d.computeClient.TerminateInstance(ctx, core.TerminateInstanceRequest{
|
||||
InstanceId: &id,
|
||||
InstanceId: &id,
|
||||
RequestMetadata: requestMetadata,
|
||||
})
|
||||
return err
|
||||
}
|
||||
@@ -214,7 +259,10 @@ func (d *driverOCI) TerminateInstance(ctx context.Context, id string) error {
|
||||
func (d *driverOCI) WaitForImageCreation(ctx context.Context, id string) error {
|
||||
return waitForResourceToReachState(
|
||||
func(string) (string, error) {
|
||||
image, err := d.computeClient.GetImage(ctx, core.GetImageRequest{ImageId: &id})
|
||||
image, err := d.computeClient.GetImage(ctx, core.GetImageRequest{
|
||||
ImageId: &id,
|
||||
RequestMetadata: requestMetadata,
|
||||
})
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
@@ -233,7 +281,10 @@ func (d *driverOCI) WaitForImageCreation(ctx context.Context, id string) error {
|
||||
func (d *driverOCI) WaitForInstanceState(ctx context.Context, id string, waitStates []string, terminalState string) error {
|
||||
return waitForResourceToReachState(
|
||||
func(string) (string, error) {
|
||||
instance, err := d.computeClient.GetInstance(ctx, core.GetInstanceRequest{InstanceId: &id})
|
||||
instance, err := d.computeClient.GetInstance(ctx, core.GetInstanceRequest{
|
||||
InstanceId: &id,
|
||||
RequestMetadata: requestMetadata,
|
||||
})
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
|
||||
@@ -22,8 +22,11 @@ func (s *stepCreateServer) Run(ctx context.Context, state multistep.StateBag) mu
|
||||
|
||||
profitbricks.SetAuth(c.PBUsername, c.PBPassword)
|
||||
profitbricks.SetDepth("5")
|
||||
if sshkey, ok := state.GetOk("publicKey"); ok {
|
||||
c.SSHKey = sshkey.(string)
|
||||
if c.Comm.SSHPublicKey != nil {
|
||||
c.SSHKey = string(c.Comm.SSHPublicKey)
|
||||
} else {
|
||||
ui.Error("No ssh private key set; ssh authentication won't be possible. Please specify your private key in the ssh_private_key_file configuration key.")
|
||||
return multistep.ActionHalt
|
||||
}
|
||||
ui.Say("Creating Virtual Data Center...")
|
||||
img := s.getImageId(c.Image, c)
|
||||
@@ -204,7 +207,7 @@ func (d *stepCreateServer) setPB(username string, password string, url string) {
|
||||
|
||||
func (d *stepCreateServer) checkForErrors(instance profitbricks.Resp) error {
|
||||
if instance.StatusCode > 299 {
|
||||
return errors.New(fmt.Sprintf("Error occurred %s", string(instance.Body)))
|
||||
return fmt.Errorf("Error occurred %s", string(instance.Body))
|
||||
}
|
||||
return nil
|
||||
}
|
||||
@@ -261,7 +264,9 @@ func (d *stepCreateServer) getImageAlias(imageAlias string, location string, ui
|
||||
|
||||
func parseErrorMessage(raw string) (toreturn string) {
|
||||
var tmp map[string]interface{}
|
||||
json.Unmarshal([]byte(raw), &tmp)
|
||||
if json.Unmarshal([]byte(raw), &tmp) != nil {
|
||||
return ""
|
||||
}
|
||||
|
||||
for _, v := range tmp["messages"].([]interface{}) {
|
||||
for index, i := range v.(map[string]interface{}) {
|
||||
|
||||
@@ -3,6 +3,9 @@ package profitbricks
|
||||
import (
|
||||
"context"
|
||||
"encoding/json"
|
||||
"errors"
|
||||
"fmt"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
"github.com/hashicorp/packer-plugin-sdk/multistep"
|
||||
@@ -22,9 +25,32 @@ func (s *stepTakeSnapshot) Run(ctx context.Context, state multistep.StateBag) mu
|
||||
|
||||
dcId := state.Get("datacenter_id").(string)
|
||||
volumeId := state.Get("volume_id").(string)
|
||||
serverId := state.Get("instance_id").(string)
|
||||
|
||||
comm, _ := state.Get("communicator").(packersdk.Communicator)
|
||||
if comm == nil {
|
||||
ui.Error("no communicator found")
|
||||
return multistep.ActionHalt
|
||||
}
|
||||
|
||||
/* sync fs changes from the provisioning step */
|
||||
os, err := s.getOs(dcId, serverId)
|
||||
if err != nil {
|
||||
ui.Error(fmt.Sprintf("an error occurred while getting the server os: %s", err.Error()))
|
||||
return multistep.ActionHalt
|
||||
}
|
||||
ui.Say(fmt.Sprintf("Server OS is %s", os))
|
||||
|
||||
switch strings.ToLower(os) {
|
||||
case "linux":
|
||||
ui.Say("syncing file system changes")
|
||||
if err := s.syncFs(ctx, comm); err != nil {
|
||||
ui.Error(fmt.Sprintf("error syncing fs changes: %s", err.Error()))
|
||||
return multistep.ActionHalt
|
||||
}
|
||||
}
|
||||
|
||||
snapshot := profitbricks.CreateSnapshot(dcId, volumeId, c.SnapshotName, "")
|
||||
|
||||
state.Put("snapshotname", c.SnapshotName)
|
||||
|
||||
if snapshot.StatusCode > 299 {
|
||||
@@ -42,31 +68,123 @@ func (s *stepTakeSnapshot) Run(ctx context.Context, state multistep.StateBag) mu
|
||||
return multistep.ActionHalt
|
||||
}
|
||||
|
||||
s.waitTillProvisioned(snapshot.Headers.Get("Location"), *c)
|
||||
ui.Say(fmt.Sprintf("Creating a snapshot for %s/volumes/%s", dcId, volumeId))
|
||||
|
||||
err = s.waitForRequest(snapshot.Headers.Get("Location"), *c, ui)
|
||||
if err != nil {
|
||||
ui.Error(fmt.Sprintf("An error occurred while waiting for the request to be done: %s", err.Error()))
|
||||
return multistep.ActionHalt
|
||||
}
|
||||
|
||||
err = s.waitTillSnapshotAvailable(snapshot.Id, *c, ui)
|
||||
if err != nil {
|
||||
ui.Error(fmt.Sprintf("An error occurred while waiting for the snapshot to be created: %s", err.Error()))
|
||||
return multistep.ActionHalt
|
||||
}
|
||||
|
||||
return multistep.ActionContinue
|
||||
}
|
||||
|
||||
func (s *stepTakeSnapshot) Cleanup(state multistep.StateBag) {
|
||||
func (s *stepTakeSnapshot) Cleanup(_ multistep.StateBag) {
|
||||
}
|
||||
|
||||
func (d *stepTakeSnapshot) waitTillProvisioned(path string, config Config) {
|
||||
d.setPB(config.PBUsername, config.PBPassword, config.PBUrl)
|
||||
func (s *stepTakeSnapshot) waitForRequest(path string, config Config, ui packersdk.Ui) error {
|
||||
|
||||
ui.Say(fmt.Sprintf("Watching request %s", path))
|
||||
s.setPB(config.PBUsername, config.PBPassword, config.PBUrl)
|
||||
waitCount := 50
|
||||
var waitInterval = 10 * time.Second
|
||||
if config.Retries > 0 {
|
||||
waitCount = config.Retries
|
||||
}
|
||||
done := false
|
||||
for i := 0; i < waitCount; i++ {
|
||||
request := profitbricks.GetRequestStatus(path)
|
||||
ui.Say(fmt.Sprintf("request status = %s", request.Metadata.Status))
|
||||
if request.Metadata.Status == "DONE" {
|
||||
done = true
|
||||
break
|
||||
}
|
||||
time.Sleep(10 * time.Second)
|
||||
if request.Metadata.Status == "FAILED" {
|
||||
return fmt.Errorf("Request failed: %s", request.Response)
|
||||
}
|
||||
time.Sleep(waitInterval)
|
||||
i++
|
||||
}
|
||||
|
||||
if done == false {
|
||||
return fmt.Errorf("request not fulfilled after waiting %d seconds",
|
||||
int64(waitCount)*int64(waitInterval)/int64(time.Second))
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (d *stepTakeSnapshot) setPB(username string, password string, url string) {
|
||||
func (s *stepTakeSnapshot) waitTillSnapshotAvailable(id string, config Config, ui packersdk.Ui) error {
|
||||
s.setPB(config.PBUsername, config.PBPassword, config.PBUrl)
|
||||
waitCount := 50
|
||||
var waitInterval = 10 * time.Second
|
||||
if config.Retries > 0 {
|
||||
waitCount = config.Retries
|
||||
}
|
||||
done := false
|
||||
ui.Say(fmt.Sprintf("waiting for snapshot %s to become available", id))
|
||||
for i := 0; i < waitCount; i++ {
|
||||
snap := profitbricks.GetSnapshot(id)
|
||||
ui.Say(fmt.Sprintf("snapshot status = %s", snap.Metadata.State))
|
||||
if snap.StatusCode != 200 {
|
||||
return fmt.Errorf("%s", snap.Response)
|
||||
}
|
||||
if snap.Metadata.State == "AVAILABLE" {
|
||||
done = true
|
||||
break
|
||||
}
|
||||
time.Sleep(waitInterval)
|
||||
i++
|
||||
ui.Say(fmt.Sprintf("... still waiting, %d seconds have passed", int64(waitInterval)*int64(i)))
|
||||
}
|
||||
|
||||
if done == false {
|
||||
return fmt.Errorf("snapshot not created after waiting %d seconds",
|
||||
int64(waitCount)*int64(waitInterval)/int64(time.Second))
|
||||
}
|
||||
|
||||
ui.Say("snapshot created")
|
||||
return nil
|
||||
}
|
||||
|
||||
func (s *stepTakeSnapshot) syncFs(ctx context.Context, comm packersdk.Communicator) error {
|
||||
cmd := &packersdk.RemoteCmd{
|
||||
Command: "sync",
|
||||
}
|
||||
if err := comm.Start(ctx, cmd); err != nil {
|
||||
return err
|
||||
}
|
||||
if cmd.Wait() != 0 {
|
||||
return fmt.Errorf("sync command exited with code %d", cmd.ExitStatus())
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (s *stepTakeSnapshot) getOs(dcId string, serverId string) (string, error) {
|
||||
server := profitbricks.GetServer(dcId, serverId)
|
||||
if server.StatusCode != 200 {
|
||||
return "", errors.New(server.Response)
|
||||
}
|
||||
|
||||
if server.Properties.BootVolume == nil {
|
||||
return "", errors.New("no boot volume found on server")
|
||||
}
|
||||
|
||||
volumeId := server.Properties.BootVolume.Id
|
||||
volume := profitbricks.GetVolume(dcId, volumeId)
|
||||
if volume.StatusCode != 200 {
|
||||
return "", errors.New(volume.Response)
|
||||
}
|
||||
|
||||
return volume.Properties.LicenceType, nil
|
||||
}
|
||||
|
||||
func (s *stepTakeSnapshot) setPB(username string, password string, url string) {
|
||||
profitbricks.SetAuth(username, password)
|
||||
profitbricks.SetEndpoint(url)
|
||||
}
|
||||
|
||||
@@ -11,101 +11,101 @@ import (
|
||||
// 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" hcl:"packer_build_name"`
|
||||
PackerBuilderType *string `mapstructure:"packer_builder_type" cty:"packer_builder_type" hcl:"packer_builder_type"`
|
||||
PackerCoreVersion *string `mapstructure:"packer_core_version" cty:"packer_core_version" hcl:"packer_core_version"`
|
||||
PackerDebug *bool `mapstructure:"packer_debug" cty:"packer_debug" hcl:"packer_debug"`
|
||||
PackerForce *bool `mapstructure:"packer_force" cty:"packer_force" hcl:"packer_force"`
|
||||
PackerOnError *string `mapstructure:"packer_on_error" cty:"packer_on_error" hcl:"packer_on_error"`
|
||||
PackerUserVars map[string]string `mapstructure:"packer_user_variables" cty:"packer_user_variables" hcl:"packer_user_variables"`
|
||||
PackerSensitiveVars []string `mapstructure:"packer_sensitive_variables" cty:"packer_sensitive_variables" hcl:"packer_sensitive_variables"`
|
||||
HTTPDir *string `mapstructure:"http_directory" cty:"http_directory" hcl:"http_directory"`
|
||||
HTTPPortMin *int `mapstructure:"http_port_min" cty:"http_port_min" hcl:"http_port_min"`
|
||||
HTTPPortMax *int `mapstructure:"http_port_max" cty:"http_port_max" hcl:"http_port_max"`
|
||||
HTTPAddress *string `mapstructure:"http_bind_address" cty:"http_bind_address" hcl:"http_bind_address"`
|
||||
HTTPInterface *string `mapstructure:"http_interface" undocumented:"true" cty:"http_interface" hcl:"http_interface"`
|
||||
BootGroupInterval *string `mapstructure:"boot_keygroup_interval" cty:"boot_keygroup_interval" hcl:"boot_keygroup_interval"`
|
||||
BootWait *string `mapstructure:"boot_wait" cty:"boot_wait" hcl:"boot_wait"`
|
||||
BootCommand []string `mapstructure:"boot_command" cty:"boot_command" hcl:"boot_command"`
|
||||
BootKeyInterval *string `mapstructure:"boot_key_interval" cty:"boot_key_interval" hcl:"boot_key_interval"`
|
||||
Type *string `mapstructure:"communicator" cty:"communicator" hcl:"communicator"`
|
||||
PauseBeforeConnect *string `mapstructure:"pause_before_connecting" cty:"pause_before_connecting" hcl:"pause_before_connecting"`
|
||||
SSHHost *string `mapstructure:"ssh_host" cty:"ssh_host" hcl:"ssh_host"`
|
||||
SSHPort *int `mapstructure:"ssh_port" cty:"ssh_port" hcl:"ssh_port"`
|
||||
SSHUsername *string `mapstructure:"ssh_username" cty:"ssh_username" hcl:"ssh_username"`
|
||||
SSHPassword *string `mapstructure:"ssh_password" cty:"ssh_password" hcl:"ssh_password"`
|
||||
SSHKeyPairName *string `mapstructure:"ssh_keypair_name" undocumented:"true" cty:"ssh_keypair_name" hcl:"ssh_keypair_name"`
|
||||
SSHTemporaryKeyPairName *string `mapstructure:"temporary_key_pair_name" undocumented:"true" cty:"temporary_key_pair_name" hcl:"temporary_key_pair_name"`
|
||||
SSHTemporaryKeyPairType *string `mapstructure:"temporary_key_pair_type" cty:"temporary_key_pair_type" hcl:"temporary_key_pair_type"`
|
||||
SSHTemporaryKeyPairBits *int `mapstructure:"temporary_key_pair_bits" cty:"temporary_key_pair_bits" hcl:"temporary_key_pair_bits"`
|
||||
SSHCiphers []string `mapstructure:"ssh_ciphers" cty:"ssh_ciphers" hcl:"ssh_ciphers"`
|
||||
SSHClearAuthorizedKeys *bool `mapstructure:"ssh_clear_authorized_keys" cty:"ssh_clear_authorized_keys" hcl:"ssh_clear_authorized_keys"`
|
||||
SSHKEXAlgos []string `mapstructure:"ssh_key_exchange_algorithms" cty:"ssh_key_exchange_algorithms" hcl:"ssh_key_exchange_algorithms"`
|
||||
SSHPrivateKeyFile *string `mapstructure:"ssh_private_key_file" undocumented:"true" cty:"ssh_private_key_file" hcl:"ssh_private_key_file"`
|
||||
SSHCertificateFile *string `mapstructure:"ssh_certificate_file" cty:"ssh_certificate_file" hcl:"ssh_certificate_file"`
|
||||
SSHPty *bool `mapstructure:"ssh_pty" cty:"ssh_pty" hcl:"ssh_pty"`
|
||||
SSHTimeout *string `mapstructure:"ssh_timeout" cty:"ssh_timeout" hcl:"ssh_timeout"`
|
||||
SSHWaitTimeout *string `mapstructure:"ssh_wait_timeout" undocumented:"true" cty:"ssh_wait_timeout" hcl:"ssh_wait_timeout"`
|
||||
SSHAgentAuth *bool `mapstructure:"ssh_agent_auth" undocumented:"true" cty:"ssh_agent_auth" hcl:"ssh_agent_auth"`
|
||||
SSHDisableAgentForwarding *bool `mapstructure:"ssh_disable_agent_forwarding" cty:"ssh_disable_agent_forwarding" hcl:"ssh_disable_agent_forwarding"`
|
||||
SSHHandshakeAttempts *int `mapstructure:"ssh_handshake_attempts" cty:"ssh_handshake_attempts" hcl:"ssh_handshake_attempts"`
|
||||
SSHBastionHost *string `mapstructure:"ssh_bastion_host" cty:"ssh_bastion_host" hcl:"ssh_bastion_host"`
|
||||
SSHBastionPort *int `mapstructure:"ssh_bastion_port" cty:"ssh_bastion_port" hcl:"ssh_bastion_port"`
|
||||
SSHBastionAgentAuth *bool `mapstructure:"ssh_bastion_agent_auth" cty:"ssh_bastion_agent_auth" hcl:"ssh_bastion_agent_auth"`
|
||||
SSHBastionUsername *string `mapstructure:"ssh_bastion_username" cty:"ssh_bastion_username" hcl:"ssh_bastion_username"`
|
||||
SSHBastionPassword *string `mapstructure:"ssh_bastion_password" cty:"ssh_bastion_password" hcl:"ssh_bastion_password"`
|
||||
SSHBastionInteractive *bool `mapstructure:"ssh_bastion_interactive" cty:"ssh_bastion_interactive" hcl:"ssh_bastion_interactive"`
|
||||
SSHBastionPrivateKeyFile *string `mapstructure:"ssh_bastion_private_key_file" cty:"ssh_bastion_private_key_file" hcl:"ssh_bastion_private_key_file"`
|
||||
SSHBastionCertificateFile *string `mapstructure:"ssh_bastion_certificate_file" cty:"ssh_bastion_certificate_file" hcl:"ssh_bastion_certificate_file"`
|
||||
SSHFileTransferMethod *string `mapstructure:"ssh_file_transfer_method" cty:"ssh_file_transfer_method" hcl:"ssh_file_transfer_method"`
|
||||
SSHProxyHost *string `mapstructure:"ssh_proxy_host" cty:"ssh_proxy_host" hcl:"ssh_proxy_host"`
|
||||
SSHProxyPort *int `mapstructure:"ssh_proxy_port" cty:"ssh_proxy_port" hcl:"ssh_proxy_port"`
|
||||
SSHProxyUsername *string `mapstructure:"ssh_proxy_username" cty:"ssh_proxy_username" hcl:"ssh_proxy_username"`
|
||||
SSHProxyPassword *string `mapstructure:"ssh_proxy_password" cty:"ssh_proxy_password" hcl:"ssh_proxy_password"`
|
||||
SSHKeepAliveInterval *string `mapstructure:"ssh_keep_alive_interval" cty:"ssh_keep_alive_interval" hcl:"ssh_keep_alive_interval"`
|
||||
SSHReadWriteTimeout *string `mapstructure:"ssh_read_write_timeout" cty:"ssh_read_write_timeout" hcl:"ssh_read_write_timeout"`
|
||||
SSHRemoteTunnels []string `mapstructure:"ssh_remote_tunnels" cty:"ssh_remote_tunnels" hcl:"ssh_remote_tunnels"`
|
||||
SSHLocalTunnels []string `mapstructure:"ssh_local_tunnels" cty:"ssh_local_tunnels" hcl:"ssh_local_tunnels"`
|
||||
SSHPublicKey []byte `mapstructure:"ssh_public_key" undocumented:"true" cty:"ssh_public_key" hcl:"ssh_public_key"`
|
||||
SSHPrivateKey []byte `mapstructure:"ssh_private_key" undocumented:"true" cty:"ssh_private_key" hcl:"ssh_private_key"`
|
||||
WinRMUser *string `mapstructure:"winrm_username" cty:"winrm_username" hcl:"winrm_username"`
|
||||
WinRMPassword *string `mapstructure:"winrm_password" cty:"winrm_password" hcl:"winrm_password"`
|
||||
WinRMHost *string `mapstructure:"winrm_host" cty:"winrm_host" hcl:"winrm_host"`
|
||||
WinRMNoProxy *bool `mapstructure:"winrm_no_proxy" cty:"winrm_no_proxy" hcl:"winrm_no_proxy"`
|
||||
WinRMPort *int `mapstructure:"winrm_port" cty:"winrm_port" hcl:"winrm_port"`
|
||||
WinRMTimeout *string `mapstructure:"winrm_timeout" cty:"winrm_timeout" hcl:"winrm_timeout"`
|
||||
WinRMUseSSL *bool `mapstructure:"winrm_use_ssl" cty:"winrm_use_ssl" hcl:"winrm_use_ssl"`
|
||||
WinRMInsecure *bool `mapstructure:"winrm_insecure" cty:"winrm_insecure" hcl:"winrm_insecure"`
|
||||
WinRMUseNTLM *bool `mapstructure:"winrm_use_ntlm" cty:"winrm_use_ntlm" hcl:"winrm_use_ntlm"`
|
||||
ProxmoxURLRaw *string `mapstructure:"proxmox_url" cty:"proxmox_url" hcl:"proxmox_url"`
|
||||
SkipCertValidation *bool `mapstructure:"insecure_skip_tls_verify" cty:"insecure_skip_tls_verify" hcl:"insecure_skip_tls_verify"`
|
||||
Username *string `mapstructure:"username" cty:"username" hcl:"username"`
|
||||
Password *string `mapstructure:"password" cty:"password" hcl:"password"`
|
||||
Node *string `mapstructure:"node" cty:"node" hcl:"node"`
|
||||
Pool *string `mapstructure:"pool" cty:"pool" hcl:"pool"`
|
||||
VMName *string `mapstructure:"vm_name" cty:"vm_name" hcl:"vm_name"`
|
||||
VMID *int `mapstructure:"vm_id" cty:"vm_id" hcl:"vm_id"`
|
||||
Boot *string `mapstructure:"boot" cty:"boot" hcl:"boot"`
|
||||
Memory *int `mapstructure:"memory" cty:"memory" hcl:"memory"`
|
||||
Cores *int `mapstructure:"cores" cty:"cores" hcl:"cores"`
|
||||
CPUType *string `mapstructure:"cpu_type" cty:"cpu_type" hcl:"cpu_type"`
|
||||
Sockets *int `mapstructure:"sockets" cty:"sockets" hcl:"sockets"`
|
||||
OS *string `mapstructure:"os" cty:"os" hcl:"os"`
|
||||
VGA *proxmox.FlatvgaConfig `mapstructure:"vga" cty:"vga" hcl:"vga"`
|
||||
NICs []proxmox.FlatnicConfig `mapstructure:"network_adapters" cty:"network_adapters" hcl:"network_adapters"`
|
||||
Disks []proxmox.FlatdiskConfig `mapstructure:"disks" cty:"disks" hcl:"disks"`
|
||||
Agent *bool `mapstructure:"qemu_agent" cty:"qemu_agent" hcl:"qemu_agent"`
|
||||
SCSIController *string `mapstructure:"scsi_controller" cty:"scsi_controller" hcl:"scsi_controller"`
|
||||
Onboot *bool `mapstructure:"onboot" cty:"onboot" hcl:"onboot"`
|
||||
DisableKVM *bool `mapstructure:"disable_kvm" cty:"disable_kvm" hcl:"disable_kvm"`
|
||||
TemplateName *string `mapstructure:"template_name" cty:"template_name" hcl:"template_name"`
|
||||
TemplateDescription *string `mapstructure:"template_description" cty:"template_description" hcl:"template_description"`
|
||||
CloudInit *bool `mapstructure:"cloud_init" cty:"cloud_init" hcl:"cloud_init"`
|
||||
CloudInitStoragePool *string `mapstructure:"cloud_init_storage_pool" cty:"cloud_init_storage_pool" hcl:"cloud_init_storage_pool"`
|
||||
AdditionalISOFiles []proxmox.FlatstorageConfig `mapstructure:"additional_iso_files" cty:"additional_iso_files" hcl:"additional_iso_files"`
|
||||
VMInterface *string `mapstructure:"vm_interface" cty:"vm_interface" hcl:"vm_interface"`
|
||||
CloneVM *string `mapstructure:"clone_vm" cty:"clone_vm" hcl:"clone_vm"`
|
||||
FullClone *bool `mapstructure:"full_clone" required:"false" cty:"full_clone" hcl:"full_clone"`
|
||||
PackerBuildName *string `mapstructure:"packer_build_name" cty:"packer_build_name" hcl:"packer_build_name"`
|
||||
PackerBuilderType *string `mapstructure:"packer_builder_type" cty:"packer_builder_type" hcl:"packer_builder_type"`
|
||||
PackerCoreVersion *string `mapstructure:"packer_core_version" cty:"packer_core_version" hcl:"packer_core_version"`
|
||||
PackerDebug *bool `mapstructure:"packer_debug" cty:"packer_debug" hcl:"packer_debug"`
|
||||
PackerForce *bool `mapstructure:"packer_force" cty:"packer_force" hcl:"packer_force"`
|
||||
PackerOnError *string `mapstructure:"packer_on_error" cty:"packer_on_error" hcl:"packer_on_error"`
|
||||
PackerUserVars map[string]string `mapstructure:"packer_user_variables" cty:"packer_user_variables" hcl:"packer_user_variables"`
|
||||
PackerSensitiveVars []string `mapstructure:"packer_sensitive_variables" cty:"packer_sensitive_variables" hcl:"packer_sensitive_variables"`
|
||||
HTTPDir *string `mapstructure:"http_directory" cty:"http_directory" hcl:"http_directory"`
|
||||
HTTPPortMin *int `mapstructure:"http_port_min" cty:"http_port_min" hcl:"http_port_min"`
|
||||
HTTPPortMax *int `mapstructure:"http_port_max" cty:"http_port_max" hcl:"http_port_max"`
|
||||
HTTPAddress *string `mapstructure:"http_bind_address" cty:"http_bind_address" hcl:"http_bind_address"`
|
||||
HTTPInterface *string `mapstructure:"http_interface" undocumented:"true" cty:"http_interface" hcl:"http_interface"`
|
||||
BootGroupInterval *string `mapstructure:"boot_keygroup_interval" cty:"boot_keygroup_interval" hcl:"boot_keygroup_interval"`
|
||||
BootWait *string `mapstructure:"boot_wait" cty:"boot_wait" hcl:"boot_wait"`
|
||||
BootCommand []string `mapstructure:"boot_command" cty:"boot_command" hcl:"boot_command"`
|
||||
BootKeyInterval *string `mapstructure:"boot_key_interval" cty:"boot_key_interval" hcl:"boot_key_interval"`
|
||||
Type *string `mapstructure:"communicator" cty:"communicator" hcl:"communicator"`
|
||||
PauseBeforeConnect *string `mapstructure:"pause_before_connecting" cty:"pause_before_connecting" hcl:"pause_before_connecting"`
|
||||
SSHHost *string `mapstructure:"ssh_host" cty:"ssh_host" hcl:"ssh_host"`
|
||||
SSHPort *int `mapstructure:"ssh_port" cty:"ssh_port" hcl:"ssh_port"`
|
||||
SSHUsername *string `mapstructure:"ssh_username" cty:"ssh_username" hcl:"ssh_username"`
|
||||
SSHPassword *string `mapstructure:"ssh_password" cty:"ssh_password" hcl:"ssh_password"`
|
||||
SSHKeyPairName *string `mapstructure:"ssh_keypair_name" undocumented:"true" cty:"ssh_keypair_name" hcl:"ssh_keypair_name"`
|
||||
SSHTemporaryKeyPairName *string `mapstructure:"temporary_key_pair_name" undocumented:"true" cty:"temporary_key_pair_name" hcl:"temporary_key_pair_name"`
|
||||
SSHTemporaryKeyPairType *string `mapstructure:"temporary_key_pair_type" cty:"temporary_key_pair_type" hcl:"temporary_key_pair_type"`
|
||||
SSHTemporaryKeyPairBits *int `mapstructure:"temporary_key_pair_bits" cty:"temporary_key_pair_bits" hcl:"temporary_key_pair_bits"`
|
||||
SSHCiphers []string `mapstructure:"ssh_ciphers" cty:"ssh_ciphers" hcl:"ssh_ciphers"`
|
||||
SSHClearAuthorizedKeys *bool `mapstructure:"ssh_clear_authorized_keys" cty:"ssh_clear_authorized_keys" hcl:"ssh_clear_authorized_keys"`
|
||||
SSHKEXAlgos []string `mapstructure:"ssh_key_exchange_algorithms" cty:"ssh_key_exchange_algorithms" hcl:"ssh_key_exchange_algorithms"`
|
||||
SSHPrivateKeyFile *string `mapstructure:"ssh_private_key_file" undocumented:"true" cty:"ssh_private_key_file" hcl:"ssh_private_key_file"`
|
||||
SSHCertificateFile *string `mapstructure:"ssh_certificate_file" cty:"ssh_certificate_file" hcl:"ssh_certificate_file"`
|
||||
SSHPty *bool `mapstructure:"ssh_pty" cty:"ssh_pty" hcl:"ssh_pty"`
|
||||
SSHTimeout *string `mapstructure:"ssh_timeout" cty:"ssh_timeout" hcl:"ssh_timeout"`
|
||||
SSHWaitTimeout *string `mapstructure:"ssh_wait_timeout" undocumented:"true" cty:"ssh_wait_timeout" hcl:"ssh_wait_timeout"`
|
||||
SSHAgentAuth *bool `mapstructure:"ssh_agent_auth" undocumented:"true" cty:"ssh_agent_auth" hcl:"ssh_agent_auth"`
|
||||
SSHDisableAgentForwarding *bool `mapstructure:"ssh_disable_agent_forwarding" cty:"ssh_disable_agent_forwarding" hcl:"ssh_disable_agent_forwarding"`
|
||||
SSHHandshakeAttempts *int `mapstructure:"ssh_handshake_attempts" cty:"ssh_handshake_attempts" hcl:"ssh_handshake_attempts"`
|
||||
SSHBastionHost *string `mapstructure:"ssh_bastion_host" cty:"ssh_bastion_host" hcl:"ssh_bastion_host"`
|
||||
SSHBastionPort *int `mapstructure:"ssh_bastion_port" cty:"ssh_bastion_port" hcl:"ssh_bastion_port"`
|
||||
SSHBastionAgentAuth *bool `mapstructure:"ssh_bastion_agent_auth" cty:"ssh_bastion_agent_auth" hcl:"ssh_bastion_agent_auth"`
|
||||
SSHBastionUsername *string `mapstructure:"ssh_bastion_username" cty:"ssh_bastion_username" hcl:"ssh_bastion_username"`
|
||||
SSHBastionPassword *string `mapstructure:"ssh_bastion_password" cty:"ssh_bastion_password" hcl:"ssh_bastion_password"`
|
||||
SSHBastionInteractive *bool `mapstructure:"ssh_bastion_interactive" cty:"ssh_bastion_interactive" hcl:"ssh_bastion_interactive"`
|
||||
SSHBastionPrivateKeyFile *string `mapstructure:"ssh_bastion_private_key_file" cty:"ssh_bastion_private_key_file" hcl:"ssh_bastion_private_key_file"`
|
||||
SSHBastionCertificateFile *string `mapstructure:"ssh_bastion_certificate_file" cty:"ssh_bastion_certificate_file" hcl:"ssh_bastion_certificate_file"`
|
||||
SSHFileTransferMethod *string `mapstructure:"ssh_file_transfer_method" cty:"ssh_file_transfer_method" hcl:"ssh_file_transfer_method"`
|
||||
SSHProxyHost *string `mapstructure:"ssh_proxy_host" cty:"ssh_proxy_host" hcl:"ssh_proxy_host"`
|
||||
SSHProxyPort *int `mapstructure:"ssh_proxy_port" cty:"ssh_proxy_port" hcl:"ssh_proxy_port"`
|
||||
SSHProxyUsername *string `mapstructure:"ssh_proxy_username" cty:"ssh_proxy_username" hcl:"ssh_proxy_username"`
|
||||
SSHProxyPassword *string `mapstructure:"ssh_proxy_password" cty:"ssh_proxy_password" hcl:"ssh_proxy_password"`
|
||||
SSHKeepAliveInterval *string `mapstructure:"ssh_keep_alive_interval" cty:"ssh_keep_alive_interval" hcl:"ssh_keep_alive_interval"`
|
||||
SSHReadWriteTimeout *string `mapstructure:"ssh_read_write_timeout" cty:"ssh_read_write_timeout" hcl:"ssh_read_write_timeout"`
|
||||
SSHRemoteTunnels []string `mapstructure:"ssh_remote_tunnels" cty:"ssh_remote_tunnels" hcl:"ssh_remote_tunnels"`
|
||||
SSHLocalTunnels []string `mapstructure:"ssh_local_tunnels" cty:"ssh_local_tunnels" hcl:"ssh_local_tunnels"`
|
||||
SSHPublicKey []byte `mapstructure:"ssh_public_key" undocumented:"true" cty:"ssh_public_key" hcl:"ssh_public_key"`
|
||||
SSHPrivateKey []byte `mapstructure:"ssh_private_key" undocumented:"true" cty:"ssh_private_key" hcl:"ssh_private_key"`
|
||||
WinRMUser *string `mapstructure:"winrm_username" cty:"winrm_username" hcl:"winrm_username"`
|
||||
WinRMPassword *string `mapstructure:"winrm_password" cty:"winrm_password" hcl:"winrm_password"`
|
||||
WinRMHost *string `mapstructure:"winrm_host" cty:"winrm_host" hcl:"winrm_host"`
|
||||
WinRMNoProxy *bool `mapstructure:"winrm_no_proxy" cty:"winrm_no_proxy" hcl:"winrm_no_proxy"`
|
||||
WinRMPort *int `mapstructure:"winrm_port" cty:"winrm_port" hcl:"winrm_port"`
|
||||
WinRMTimeout *string `mapstructure:"winrm_timeout" cty:"winrm_timeout" hcl:"winrm_timeout"`
|
||||
WinRMUseSSL *bool `mapstructure:"winrm_use_ssl" cty:"winrm_use_ssl" hcl:"winrm_use_ssl"`
|
||||
WinRMInsecure *bool `mapstructure:"winrm_insecure" cty:"winrm_insecure" hcl:"winrm_insecure"`
|
||||
WinRMUseNTLM *bool `mapstructure:"winrm_use_ntlm" cty:"winrm_use_ntlm" hcl:"winrm_use_ntlm"`
|
||||
ProxmoxURLRaw *string `mapstructure:"proxmox_url" cty:"proxmox_url" hcl:"proxmox_url"`
|
||||
SkipCertValidation *bool `mapstructure:"insecure_skip_tls_verify" cty:"insecure_skip_tls_verify" hcl:"insecure_skip_tls_verify"`
|
||||
Username *string `mapstructure:"username" cty:"username" hcl:"username"`
|
||||
Password *string `mapstructure:"password" cty:"password" hcl:"password"`
|
||||
Node *string `mapstructure:"node" cty:"node" hcl:"node"`
|
||||
Pool *string `mapstructure:"pool" cty:"pool" hcl:"pool"`
|
||||
VMName *string `mapstructure:"vm_name" cty:"vm_name" hcl:"vm_name"`
|
||||
VMID *int `mapstructure:"vm_id" cty:"vm_id" hcl:"vm_id"`
|
||||
Boot *string `mapstructure:"boot" cty:"boot" hcl:"boot"`
|
||||
Memory *int `mapstructure:"memory" cty:"memory" hcl:"memory"`
|
||||
Cores *int `mapstructure:"cores" cty:"cores" hcl:"cores"`
|
||||
CPUType *string `mapstructure:"cpu_type" cty:"cpu_type" hcl:"cpu_type"`
|
||||
Sockets *int `mapstructure:"sockets" cty:"sockets" hcl:"sockets"`
|
||||
OS *string `mapstructure:"os" cty:"os" hcl:"os"`
|
||||
VGA *proxmox.FlatvgaConfig `mapstructure:"vga" cty:"vga" hcl:"vga"`
|
||||
NICs []proxmox.FlatnicConfig `mapstructure:"network_adapters" cty:"network_adapters" hcl:"network_adapters"`
|
||||
Disks []proxmox.FlatdiskConfig `mapstructure:"disks" cty:"disks" hcl:"disks"`
|
||||
Agent *bool `mapstructure:"qemu_agent" cty:"qemu_agent" hcl:"qemu_agent"`
|
||||
SCSIController *string `mapstructure:"scsi_controller" cty:"scsi_controller" hcl:"scsi_controller"`
|
||||
Onboot *bool `mapstructure:"onboot" cty:"onboot" hcl:"onboot"`
|
||||
DisableKVM *bool `mapstructure:"disable_kvm" cty:"disable_kvm" hcl:"disable_kvm"`
|
||||
TemplateName *string `mapstructure:"template_name" cty:"template_name" hcl:"template_name"`
|
||||
TemplateDescription *string `mapstructure:"template_description" cty:"template_description" hcl:"template_description"`
|
||||
CloudInit *bool `mapstructure:"cloud_init" cty:"cloud_init" hcl:"cloud_init"`
|
||||
CloudInitStoragePool *string `mapstructure:"cloud_init_storage_pool" cty:"cloud_init_storage_pool" hcl:"cloud_init_storage_pool"`
|
||||
AdditionalISOFiles []proxmox.FlatadditionalISOsConfig `mapstructure:"additional_iso_files" cty:"additional_iso_files" hcl:"additional_iso_files"`
|
||||
VMInterface *string `mapstructure:"vm_interface" cty:"vm_interface" hcl:"vm_interface"`
|
||||
CloneVM *string `mapstructure:"clone_vm" cty:"clone_vm" hcl:"clone_vm"`
|
||||
FullClone *bool `mapstructure:"full_clone" required:"false" cty:"full_clone" hcl:"full_clone"`
|
||||
}
|
||||
|
||||
// FlatMapstructure returns a new FlatConfig.
|
||||
@@ -211,7 +211,7 @@ func (*FlatConfig) HCL2Spec() map[string]hcldec.Spec {
|
||||
"template_description": &hcldec.AttrSpec{Name: "template_description", Type: cty.String, Required: false},
|
||||
"cloud_init": &hcldec.AttrSpec{Name: "cloud_init", Type: cty.Bool, Required: false},
|
||||
"cloud_init_storage_pool": &hcldec.AttrSpec{Name: "cloud_init_storage_pool", Type: cty.String, Required: false},
|
||||
"additional_iso_files": &hcldec.BlockListSpec{TypeName: "additional_iso_files", Nested: hcldec.ObjectSpec((*proxmox.FlatstorageConfig)(nil).HCL2Spec())},
|
||||
"additional_iso_files": &hcldec.BlockListSpec{TypeName: "additional_iso_files", Nested: hcldec.ObjectSpec((*proxmox.FlatadditionalISOsConfig)(nil).HCL2Spec())},
|
||||
"vm_interface": &hcldec.AttrSpec{Name: "vm_interface", Type: cty.String, Required: false},
|
||||
"clone_vm": &hcldec.AttrSpec{Name: "clone_vm", Type: cty.String, Required: false},
|
||||
"full_clone": &hcldec.AttrSpec{Name: "full_clone", Type: cty.Bool, Required: false},
|
||||
|
||||
@@ -84,7 +84,20 @@ func (b *Builder) Run(ctx context.Context, ui packersdk.Ui, hook packersdk.Hook,
|
||||
&stepFinalizeTemplateConfig{},
|
||||
&stepSuccess{},
|
||||
}
|
||||
steps := append(b.preSteps, coreSteps...)
|
||||
preSteps := b.preSteps
|
||||
for idx := range b.config.AdditionalISOFiles {
|
||||
preSteps = append(preSteps, &commonsteps.StepDownload{
|
||||
Checksum: b.config.AdditionalISOFiles[idx].ISOChecksum,
|
||||
Description: "additional ISO",
|
||||
Extension: b.config.AdditionalISOFiles[idx].TargetExtension,
|
||||
ResultKey: b.config.AdditionalISOFiles[idx].DownloadPathKey,
|
||||
TargetPath: b.config.AdditionalISOFiles[idx].DownloadPathKey,
|
||||
Url: b.config.AdditionalISOFiles[idx].ISOUrls,
|
||||
})
|
||||
}
|
||||
preSteps = append(preSteps, &stepUploadAdditionalISOs{})
|
||||
|
||||
steps := append(preSteps, coreSteps...)
|
||||
steps = append(steps, b.postSteps...)
|
||||
// Run the steps
|
||||
b.runner = commonsteps.NewRunner(steps, b.config.PackerConfig, ui)
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
//go:generate mapstructure-to-hcl2 -type Config,nicConfig,diskConfig,vgaConfig,storageConfig
|
||||
//go:generate mapstructure-to-hcl2 -type Config,nicConfig,diskConfig,vgaConfig,additionalISOsConfig
|
||||
|
||||
package proxmox
|
||||
|
||||
@@ -8,6 +8,7 @@ import (
|
||||
"log"
|
||||
"net/url"
|
||||
"os"
|
||||
"strconv"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
@@ -60,13 +61,13 @@ type Config struct {
|
||||
CloudInit bool `mapstructure:"cloud_init"`
|
||||
CloudInitStoragePool string `mapstructure:"cloud_init_storage_pool"`
|
||||
|
||||
AdditionalISOFiles []storageConfig `mapstructure:"additional_iso_files"`
|
||||
VMInterface string `mapstructure:"vm_interface"`
|
||||
AdditionalISOFiles []additionalISOsConfig `mapstructure:"additional_iso_files"`
|
||||
VMInterface string `mapstructure:"vm_interface"`
|
||||
|
||||
Ctx interpolate.Context `mapstructure-to-hcl2:",skip"`
|
||||
}
|
||||
|
||||
type storageConfig struct {
|
||||
type additionalISOsConfig struct {
|
||||
commonsteps.ISOConfig `mapstructure:",squash"`
|
||||
Device string `mapstructure:"device"`
|
||||
ISOFile string `mapstructure:"iso_file"`
|
||||
@@ -250,6 +251,58 @@ func (c *Config) Prepare(upper interface{}, raws ...interface{}) ([]string, []st
|
||||
errs = packersdk.MultiErrorAppend(errs, fmt.Errorf("disks[%d].storage_pool_type must be specified", idx))
|
||||
}
|
||||
}
|
||||
for idx := range c.AdditionalISOFiles {
|
||||
// Check AdditionalISO config
|
||||
// Either a pre-uploaded ISO should be referenced in iso_file, OR a URL
|
||||
// (possibly to a local file) to an ISO file that will be downloaded and
|
||||
// then uploaded to Proxmox.
|
||||
if c.AdditionalISOFiles[idx].ISOFile != "" {
|
||||
c.AdditionalISOFiles[idx].ShouldUploadISO = false
|
||||
} else {
|
||||
c.AdditionalISOFiles[idx].DownloadPathKey = "downloaded_additional_iso_path_" + strconv.Itoa(idx)
|
||||
isoWarnings, isoErrors := c.AdditionalISOFiles[idx].ISOConfig.Prepare(&c.Ctx)
|
||||
errs = packersdk.MultiErrorAppend(errs, isoErrors...)
|
||||
warnings = append(warnings, isoWarnings...)
|
||||
c.AdditionalISOFiles[idx].ShouldUploadISO = true
|
||||
}
|
||||
if c.AdditionalISOFiles[idx].Device == "" {
|
||||
log.Printf("AdditionalISOFile %d Device not set, using default 'ide3'", idx)
|
||||
c.AdditionalISOFiles[idx].Device = "ide3"
|
||||
}
|
||||
if strings.HasPrefix(c.AdditionalISOFiles[idx].Device, "ide") {
|
||||
busnumber, err := strconv.Atoi(c.AdditionalISOFiles[idx].Device[3:])
|
||||
if err != nil {
|
||||
errs = packersdk.MultiErrorAppend(errs, fmt.Errorf("%s is not a valid bus index", c.AdditionalISOFiles[idx].Device[3:]))
|
||||
}
|
||||
if busnumber == 2 {
|
||||
errs = packersdk.MultiErrorAppend(errs, fmt.Errorf("IDE bus 2 is used by boot ISO"))
|
||||
}
|
||||
if busnumber > 3 {
|
||||
errs = packersdk.MultiErrorAppend(errs, fmt.Errorf("IDE bus index can't be higher than 3"))
|
||||
}
|
||||
}
|
||||
if strings.HasPrefix(c.AdditionalISOFiles[idx].Device, "sata") {
|
||||
busnumber, err := strconv.Atoi(c.AdditionalISOFiles[idx].Device[4:])
|
||||
if err != nil {
|
||||
errs = packersdk.MultiErrorAppend(errs, fmt.Errorf("%s is not a valid bus index", c.AdditionalISOFiles[idx].Device[4:]))
|
||||
}
|
||||
if busnumber > 5 {
|
||||
errs = packersdk.MultiErrorAppend(errs, fmt.Errorf("SATA bus index can't be higher than 5"))
|
||||
}
|
||||
}
|
||||
if strings.HasPrefix(c.AdditionalISOFiles[idx].Device, "scsi") {
|
||||
busnumber, err := strconv.Atoi(c.AdditionalISOFiles[idx].Device[4:])
|
||||
if err != nil {
|
||||
errs = packersdk.MultiErrorAppend(errs, fmt.Errorf("%s is not a valid bus index", c.AdditionalISOFiles[idx].Device[4:]))
|
||||
}
|
||||
if busnumber > 30 {
|
||||
errs = packersdk.MultiErrorAppend(errs, fmt.Errorf("SCSI bus index can't be higher than 30"))
|
||||
}
|
||||
}
|
||||
if (c.AdditionalISOFiles[idx].ISOFile == "" && len(c.AdditionalISOFiles[idx].ISOConfig.ISOUrls) == 0) || (c.AdditionalISOFiles[idx].ISOFile != "" && len(c.AdditionalISOFiles[idx].ISOConfig.ISOUrls) != 0) {
|
||||
errs = packersdk.MultiErrorAppend(errs, fmt.Errorf("either iso_file or iso_url, but not both, must be specified for AdditionalISO file %s", c.AdditionalISOFiles[idx].Device))
|
||||
}
|
||||
}
|
||||
|
||||
if errs != nil && len(errs.Errors) > 0 {
|
||||
return nil, warnings, errs
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
// Code generated by "mapstructure-to-hcl2 -type Config,nicConfig,diskConfig,vgaConfig,storageConfig"; DO NOT EDIT.
|
||||
// Code generated by "mapstructure-to-hcl2 -type Config,nicConfig,diskConfig,vgaConfig,additionalISOsConfig"; DO NOT EDIT.
|
||||
|
||||
package proxmox
|
||||
|
||||
@@ -10,99 +10,99 @@ import (
|
||||
// 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" hcl:"packer_build_name"`
|
||||
PackerBuilderType *string `mapstructure:"packer_builder_type" cty:"packer_builder_type" hcl:"packer_builder_type"`
|
||||
PackerCoreVersion *string `mapstructure:"packer_core_version" cty:"packer_core_version" hcl:"packer_core_version"`
|
||||
PackerDebug *bool `mapstructure:"packer_debug" cty:"packer_debug" hcl:"packer_debug"`
|
||||
PackerForce *bool `mapstructure:"packer_force" cty:"packer_force" hcl:"packer_force"`
|
||||
PackerOnError *string `mapstructure:"packer_on_error" cty:"packer_on_error" hcl:"packer_on_error"`
|
||||
PackerUserVars map[string]string `mapstructure:"packer_user_variables" cty:"packer_user_variables" hcl:"packer_user_variables"`
|
||||
PackerSensitiveVars []string `mapstructure:"packer_sensitive_variables" cty:"packer_sensitive_variables" hcl:"packer_sensitive_variables"`
|
||||
HTTPDir *string `mapstructure:"http_directory" cty:"http_directory" hcl:"http_directory"`
|
||||
HTTPPortMin *int `mapstructure:"http_port_min" cty:"http_port_min" hcl:"http_port_min"`
|
||||
HTTPPortMax *int `mapstructure:"http_port_max" cty:"http_port_max" hcl:"http_port_max"`
|
||||
HTTPAddress *string `mapstructure:"http_bind_address" cty:"http_bind_address" hcl:"http_bind_address"`
|
||||
HTTPInterface *string `mapstructure:"http_interface" undocumented:"true" cty:"http_interface" hcl:"http_interface"`
|
||||
BootGroupInterval *string `mapstructure:"boot_keygroup_interval" cty:"boot_keygroup_interval" hcl:"boot_keygroup_interval"`
|
||||
BootWait *string `mapstructure:"boot_wait" cty:"boot_wait" hcl:"boot_wait"`
|
||||
BootCommand []string `mapstructure:"boot_command" cty:"boot_command" hcl:"boot_command"`
|
||||
BootKeyInterval *string `mapstructure:"boot_key_interval" cty:"boot_key_interval" hcl:"boot_key_interval"`
|
||||
Type *string `mapstructure:"communicator" cty:"communicator" hcl:"communicator"`
|
||||
PauseBeforeConnect *string `mapstructure:"pause_before_connecting" cty:"pause_before_connecting" hcl:"pause_before_connecting"`
|
||||
SSHHost *string `mapstructure:"ssh_host" cty:"ssh_host" hcl:"ssh_host"`
|
||||
SSHPort *int `mapstructure:"ssh_port" cty:"ssh_port" hcl:"ssh_port"`
|
||||
SSHUsername *string `mapstructure:"ssh_username" cty:"ssh_username" hcl:"ssh_username"`
|
||||
SSHPassword *string `mapstructure:"ssh_password" cty:"ssh_password" hcl:"ssh_password"`
|
||||
SSHKeyPairName *string `mapstructure:"ssh_keypair_name" undocumented:"true" cty:"ssh_keypair_name" hcl:"ssh_keypair_name"`
|
||||
SSHTemporaryKeyPairName *string `mapstructure:"temporary_key_pair_name" undocumented:"true" cty:"temporary_key_pair_name" hcl:"temporary_key_pair_name"`
|
||||
SSHTemporaryKeyPairType *string `mapstructure:"temporary_key_pair_type" cty:"temporary_key_pair_type" hcl:"temporary_key_pair_type"`
|
||||
SSHTemporaryKeyPairBits *int `mapstructure:"temporary_key_pair_bits" cty:"temporary_key_pair_bits" hcl:"temporary_key_pair_bits"`
|
||||
SSHCiphers []string `mapstructure:"ssh_ciphers" cty:"ssh_ciphers" hcl:"ssh_ciphers"`
|
||||
SSHClearAuthorizedKeys *bool `mapstructure:"ssh_clear_authorized_keys" cty:"ssh_clear_authorized_keys" hcl:"ssh_clear_authorized_keys"`
|
||||
SSHKEXAlgos []string `mapstructure:"ssh_key_exchange_algorithms" cty:"ssh_key_exchange_algorithms" hcl:"ssh_key_exchange_algorithms"`
|
||||
SSHPrivateKeyFile *string `mapstructure:"ssh_private_key_file" undocumented:"true" cty:"ssh_private_key_file" hcl:"ssh_private_key_file"`
|
||||
SSHCertificateFile *string `mapstructure:"ssh_certificate_file" cty:"ssh_certificate_file" hcl:"ssh_certificate_file"`
|
||||
SSHPty *bool `mapstructure:"ssh_pty" cty:"ssh_pty" hcl:"ssh_pty"`
|
||||
SSHTimeout *string `mapstructure:"ssh_timeout" cty:"ssh_timeout" hcl:"ssh_timeout"`
|
||||
SSHWaitTimeout *string `mapstructure:"ssh_wait_timeout" undocumented:"true" cty:"ssh_wait_timeout" hcl:"ssh_wait_timeout"`
|
||||
SSHAgentAuth *bool `mapstructure:"ssh_agent_auth" undocumented:"true" cty:"ssh_agent_auth" hcl:"ssh_agent_auth"`
|
||||
SSHDisableAgentForwarding *bool `mapstructure:"ssh_disable_agent_forwarding" cty:"ssh_disable_agent_forwarding" hcl:"ssh_disable_agent_forwarding"`
|
||||
SSHHandshakeAttempts *int `mapstructure:"ssh_handshake_attempts" cty:"ssh_handshake_attempts" hcl:"ssh_handshake_attempts"`
|
||||
SSHBastionHost *string `mapstructure:"ssh_bastion_host" cty:"ssh_bastion_host" hcl:"ssh_bastion_host"`
|
||||
SSHBastionPort *int `mapstructure:"ssh_bastion_port" cty:"ssh_bastion_port" hcl:"ssh_bastion_port"`
|
||||
SSHBastionAgentAuth *bool `mapstructure:"ssh_bastion_agent_auth" cty:"ssh_bastion_agent_auth" hcl:"ssh_bastion_agent_auth"`
|
||||
SSHBastionUsername *string `mapstructure:"ssh_bastion_username" cty:"ssh_bastion_username" hcl:"ssh_bastion_username"`
|
||||
SSHBastionPassword *string `mapstructure:"ssh_bastion_password" cty:"ssh_bastion_password" hcl:"ssh_bastion_password"`
|
||||
SSHBastionInteractive *bool `mapstructure:"ssh_bastion_interactive" cty:"ssh_bastion_interactive" hcl:"ssh_bastion_interactive"`
|
||||
SSHBastionPrivateKeyFile *string `mapstructure:"ssh_bastion_private_key_file" cty:"ssh_bastion_private_key_file" hcl:"ssh_bastion_private_key_file"`
|
||||
SSHBastionCertificateFile *string `mapstructure:"ssh_bastion_certificate_file" cty:"ssh_bastion_certificate_file" hcl:"ssh_bastion_certificate_file"`
|
||||
SSHFileTransferMethod *string `mapstructure:"ssh_file_transfer_method" cty:"ssh_file_transfer_method" hcl:"ssh_file_transfer_method"`
|
||||
SSHProxyHost *string `mapstructure:"ssh_proxy_host" cty:"ssh_proxy_host" hcl:"ssh_proxy_host"`
|
||||
SSHProxyPort *int `mapstructure:"ssh_proxy_port" cty:"ssh_proxy_port" hcl:"ssh_proxy_port"`
|
||||
SSHProxyUsername *string `mapstructure:"ssh_proxy_username" cty:"ssh_proxy_username" hcl:"ssh_proxy_username"`
|
||||
SSHProxyPassword *string `mapstructure:"ssh_proxy_password" cty:"ssh_proxy_password" hcl:"ssh_proxy_password"`
|
||||
SSHKeepAliveInterval *string `mapstructure:"ssh_keep_alive_interval" cty:"ssh_keep_alive_interval" hcl:"ssh_keep_alive_interval"`
|
||||
SSHReadWriteTimeout *string `mapstructure:"ssh_read_write_timeout" cty:"ssh_read_write_timeout" hcl:"ssh_read_write_timeout"`
|
||||
SSHRemoteTunnels []string `mapstructure:"ssh_remote_tunnels" cty:"ssh_remote_tunnels" hcl:"ssh_remote_tunnels"`
|
||||
SSHLocalTunnels []string `mapstructure:"ssh_local_tunnels" cty:"ssh_local_tunnels" hcl:"ssh_local_tunnels"`
|
||||
SSHPublicKey []byte `mapstructure:"ssh_public_key" undocumented:"true" cty:"ssh_public_key" hcl:"ssh_public_key"`
|
||||
SSHPrivateKey []byte `mapstructure:"ssh_private_key" undocumented:"true" cty:"ssh_private_key" hcl:"ssh_private_key"`
|
||||
WinRMUser *string `mapstructure:"winrm_username" cty:"winrm_username" hcl:"winrm_username"`
|
||||
WinRMPassword *string `mapstructure:"winrm_password" cty:"winrm_password" hcl:"winrm_password"`
|
||||
WinRMHost *string `mapstructure:"winrm_host" cty:"winrm_host" hcl:"winrm_host"`
|
||||
WinRMNoProxy *bool `mapstructure:"winrm_no_proxy" cty:"winrm_no_proxy" hcl:"winrm_no_proxy"`
|
||||
WinRMPort *int `mapstructure:"winrm_port" cty:"winrm_port" hcl:"winrm_port"`
|
||||
WinRMTimeout *string `mapstructure:"winrm_timeout" cty:"winrm_timeout" hcl:"winrm_timeout"`
|
||||
WinRMUseSSL *bool `mapstructure:"winrm_use_ssl" cty:"winrm_use_ssl" hcl:"winrm_use_ssl"`
|
||||
WinRMInsecure *bool `mapstructure:"winrm_insecure" cty:"winrm_insecure" hcl:"winrm_insecure"`
|
||||
WinRMUseNTLM *bool `mapstructure:"winrm_use_ntlm" cty:"winrm_use_ntlm" hcl:"winrm_use_ntlm"`
|
||||
ProxmoxURLRaw *string `mapstructure:"proxmox_url" cty:"proxmox_url" hcl:"proxmox_url"`
|
||||
SkipCertValidation *bool `mapstructure:"insecure_skip_tls_verify" cty:"insecure_skip_tls_verify" hcl:"insecure_skip_tls_verify"`
|
||||
Username *string `mapstructure:"username" cty:"username" hcl:"username"`
|
||||
Password *string `mapstructure:"password" cty:"password" hcl:"password"`
|
||||
Node *string `mapstructure:"node" cty:"node" hcl:"node"`
|
||||
Pool *string `mapstructure:"pool" cty:"pool" hcl:"pool"`
|
||||
VMName *string `mapstructure:"vm_name" cty:"vm_name" hcl:"vm_name"`
|
||||
VMID *int `mapstructure:"vm_id" cty:"vm_id" hcl:"vm_id"`
|
||||
Boot *string `mapstructure:"boot" cty:"boot" hcl:"boot"`
|
||||
Memory *int `mapstructure:"memory" cty:"memory" hcl:"memory"`
|
||||
Cores *int `mapstructure:"cores" cty:"cores" hcl:"cores"`
|
||||
CPUType *string `mapstructure:"cpu_type" cty:"cpu_type" hcl:"cpu_type"`
|
||||
Sockets *int `mapstructure:"sockets" cty:"sockets" hcl:"sockets"`
|
||||
OS *string `mapstructure:"os" cty:"os" hcl:"os"`
|
||||
VGA *FlatvgaConfig `mapstructure:"vga" cty:"vga" hcl:"vga"`
|
||||
NICs []FlatnicConfig `mapstructure:"network_adapters" cty:"network_adapters" hcl:"network_adapters"`
|
||||
Disks []FlatdiskConfig `mapstructure:"disks" cty:"disks" hcl:"disks"`
|
||||
Agent *bool `mapstructure:"qemu_agent" cty:"qemu_agent" hcl:"qemu_agent"`
|
||||
SCSIController *string `mapstructure:"scsi_controller" cty:"scsi_controller" hcl:"scsi_controller"`
|
||||
Onboot *bool `mapstructure:"onboot" cty:"onboot" hcl:"onboot"`
|
||||
DisableKVM *bool `mapstructure:"disable_kvm" cty:"disable_kvm" hcl:"disable_kvm"`
|
||||
TemplateName *string `mapstructure:"template_name" cty:"template_name" hcl:"template_name"`
|
||||
TemplateDescription *string `mapstructure:"template_description" cty:"template_description" hcl:"template_description"`
|
||||
CloudInit *bool `mapstructure:"cloud_init" cty:"cloud_init" hcl:"cloud_init"`
|
||||
CloudInitStoragePool *string `mapstructure:"cloud_init_storage_pool" cty:"cloud_init_storage_pool" hcl:"cloud_init_storage_pool"`
|
||||
AdditionalISOFiles []FlatstorageConfig `mapstructure:"additional_iso_files" cty:"additional_iso_files" hcl:"additional_iso_files"`
|
||||
VMInterface *string `mapstructure:"vm_interface" cty:"vm_interface" hcl:"vm_interface"`
|
||||
PackerBuildName *string `mapstructure:"packer_build_name" cty:"packer_build_name" hcl:"packer_build_name"`
|
||||
PackerBuilderType *string `mapstructure:"packer_builder_type" cty:"packer_builder_type" hcl:"packer_builder_type"`
|
||||
PackerCoreVersion *string `mapstructure:"packer_core_version" cty:"packer_core_version" hcl:"packer_core_version"`
|
||||
PackerDebug *bool `mapstructure:"packer_debug" cty:"packer_debug" hcl:"packer_debug"`
|
||||
PackerForce *bool `mapstructure:"packer_force" cty:"packer_force" hcl:"packer_force"`
|
||||
PackerOnError *string `mapstructure:"packer_on_error" cty:"packer_on_error" hcl:"packer_on_error"`
|
||||
PackerUserVars map[string]string `mapstructure:"packer_user_variables" cty:"packer_user_variables" hcl:"packer_user_variables"`
|
||||
PackerSensitiveVars []string `mapstructure:"packer_sensitive_variables" cty:"packer_sensitive_variables" hcl:"packer_sensitive_variables"`
|
||||
HTTPDir *string `mapstructure:"http_directory" cty:"http_directory" hcl:"http_directory"`
|
||||
HTTPPortMin *int `mapstructure:"http_port_min" cty:"http_port_min" hcl:"http_port_min"`
|
||||
HTTPPortMax *int `mapstructure:"http_port_max" cty:"http_port_max" hcl:"http_port_max"`
|
||||
HTTPAddress *string `mapstructure:"http_bind_address" cty:"http_bind_address" hcl:"http_bind_address"`
|
||||
HTTPInterface *string `mapstructure:"http_interface" undocumented:"true" cty:"http_interface" hcl:"http_interface"`
|
||||
BootGroupInterval *string `mapstructure:"boot_keygroup_interval" cty:"boot_keygroup_interval" hcl:"boot_keygroup_interval"`
|
||||
BootWait *string `mapstructure:"boot_wait" cty:"boot_wait" hcl:"boot_wait"`
|
||||
BootCommand []string `mapstructure:"boot_command" cty:"boot_command" hcl:"boot_command"`
|
||||
BootKeyInterval *string `mapstructure:"boot_key_interval" cty:"boot_key_interval" hcl:"boot_key_interval"`
|
||||
Type *string `mapstructure:"communicator" cty:"communicator" hcl:"communicator"`
|
||||
PauseBeforeConnect *string `mapstructure:"pause_before_connecting" cty:"pause_before_connecting" hcl:"pause_before_connecting"`
|
||||
SSHHost *string `mapstructure:"ssh_host" cty:"ssh_host" hcl:"ssh_host"`
|
||||
SSHPort *int `mapstructure:"ssh_port" cty:"ssh_port" hcl:"ssh_port"`
|
||||
SSHUsername *string `mapstructure:"ssh_username" cty:"ssh_username" hcl:"ssh_username"`
|
||||
SSHPassword *string `mapstructure:"ssh_password" cty:"ssh_password" hcl:"ssh_password"`
|
||||
SSHKeyPairName *string `mapstructure:"ssh_keypair_name" undocumented:"true" cty:"ssh_keypair_name" hcl:"ssh_keypair_name"`
|
||||
SSHTemporaryKeyPairName *string `mapstructure:"temporary_key_pair_name" undocumented:"true" cty:"temporary_key_pair_name" hcl:"temporary_key_pair_name"`
|
||||
SSHTemporaryKeyPairType *string `mapstructure:"temporary_key_pair_type" cty:"temporary_key_pair_type" hcl:"temporary_key_pair_type"`
|
||||
SSHTemporaryKeyPairBits *int `mapstructure:"temporary_key_pair_bits" cty:"temporary_key_pair_bits" hcl:"temporary_key_pair_bits"`
|
||||
SSHCiphers []string `mapstructure:"ssh_ciphers" cty:"ssh_ciphers" hcl:"ssh_ciphers"`
|
||||
SSHClearAuthorizedKeys *bool `mapstructure:"ssh_clear_authorized_keys" cty:"ssh_clear_authorized_keys" hcl:"ssh_clear_authorized_keys"`
|
||||
SSHKEXAlgos []string `mapstructure:"ssh_key_exchange_algorithms" cty:"ssh_key_exchange_algorithms" hcl:"ssh_key_exchange_algorithms"`
|
||||
SSHPrivateKeyFile *string `mapstructure:"ssh_private_key_file" undocumented:"true" cty:"ssh_private_key_file" hcl:"ssh_private_key_file"`
|
||||
SSHCertificateFile *string `mapstructure:"ssh_certificate_file" cty:"ssh_certificate_file" hcl:"ssh_certificate_file"`
|
||||
SSHPty *bool `mapstructure:"ssh_pty" cty:"ssh_pty" hcl:"ssh_pty"`
|
||||
SSHTimeout *string `mapstructure:"ssh_timeout" cty:"ssh_timeout" hcl:"ssh_timeout"`
|
||||
SSHWaitTimeout *string `mapstructure:"ssh_wait_timeout" undocumented:"true" cty:"ssh_wait_timeout" hcl:"ssh_wait_timeout"`
|
||||
SSHAgentAuth *bool `mapstructure:"ssh_agent_auth" undocumented:"true" cty:"ssh_agent_auth" hcl:"ssh_agent_auth"`
|
||||
SSHDisableAgentForwarding *bool `mapstructure:"ssh_disable_agent_forwarding" cty:"ssh_disable_agent_forwarding" hcl:"ssh_disable_agent_forwarding"`
|
||||
SSHHandshakeAttempts *int `mapstructure:"ssh_handshake_attempts" cty:"ssh_handshake_attempts" hcl:"ssh_handshake_attempts"`
|
||||
SSHBastionHost *string `mapstructure:"ssh_bastion_host" cty:"ssh_bastion_host" hcl:"ssh_bastion_host"`
|
||||
SSHBastionPort *int `mapstructure:"ssh_bastion_port" cty:"ssh_bastion_port" hcl:"ssh_bastion_port"`
|
||||
SSHBastionAgentAuth *bool `mapstructure:"ssh_bastion_agent_auth" cty:"ssh_bastion_agent_auth" hcl:"ssh_bastion_agent_auth"`
|
||||
SSHBastionUsername *string `mapstructure:"ssh_bastion_username" cty:"ssh_bastion_username" hcl:"ssh_bastion_username"`
|
||||
SSHBastionPassword *string `mapstructure:"ssh_bastion_password" cty:"ssh_bastion_password" hcl:"ssh_bastion_password"`
|
||||
SSHBastionInteractive *bool `mapstructure:"ssh_bastion_interactive" cty:"ssh_bastion_interactive" hcl:"ssh_bastion_interactive"`
|
||||
SSHBastionPrivateKeyFile *string `mapstructure:"ssh_bastion_private_key_file" cty:"ssh_bastion_private_key_file" hcl:"ssh_bastion_private_key_file"`
|
||||
SSHBastionCertificateFile *string `mapstructure:"ssh_bastion_certificate_file" cty:"ssh_bastion_certificate_file" hcl:"ssh_bastion_certificate_file"`
|
||||
SSHFileTransferMethod *string `mapstructure:"ssh_file_transfer_method" cty:"ssh_file_transfer_method" hcl:"ssh_file_transfer_method"`
|
||||
SSHProxyHost *string `mapstructure:"ssh_proxy_host" cty:"ssh_proxy_host" hcl:"ssh_proxy_host"`
|
||||
SSHProxyPort *int `mapstructure:"ssh_proxy_port" cty:"ssh_proxy_port" hcl:"ssh_proxy_port"`
|
||||
SSHProxyUsername *string `mapstructure:"ssh_proxy_username" cty:"ssh_proxy_username" hcl:"ssh_proxy_username"`
|
||||
SSHProxyPassword *string `mapstructure:"ssh_proxy_password" cty:"ssh_proxy_password" hcl:"ssh_proxy_password"`
|
||||
SSHKeepAliveInterval *string `mapstructure:"ssh_keep_alive_interval" cty:"ssh_keep_alive_interval" hcl:"ssh_keep_alive_interval"`
|
||||
SSHReadWriteTimeout *string `mapstructure:"ssh_read_write_timeout" cty:"ssh_read_write_timeout" hcl:"ssh_read_write_timeout"`
|
||||
SSHRemoteTunnels []string `mapstructure:"ssh_remote_tunnels" cty:"ssh_remote_tunnels" hcl:"ssh_remote_tunnels"`
|
||||
SSHLocalTunnels []string `mapstructure:"ssh_local_tunnels" cty:"ssh_local_tunnels" hcl:"ssh_local_tunnels"`
|
||||
SSHPublicKey []byte `mapstructure:"ssh_public_key" undocumented:"true" cty:"ssh_public_key" hcl:"ssh_public_key"`
|
||||
SSHPrivateKey []byte `mapstructure:"ssh_private_key" undocumented:"true" cty:"ssh_private_key" hcl:"ssh_private_key"`
|
||||
WinRMUser *string `mapstructure:"winrm_username" cty:"winrm_username" hcl:"winrm_username"`
|
||||
WinRMPassword *string `mapstructure:"winrm_password" cty:"winrm_password" hcl:"winrm_password"`
|
||||
WinRMHost *string `mapstructure:"winrm_host" cty:"winrm_host" hcl:"winrm_host"`
|
||||
WinRMNoProxy *bool `mapstructure:"winrm_no_proxy" cty:"winrm_no_proxy" hcl:"winrm_no_proxy"`
|
||||
WinRMPort *int `mapstructure:"winrm_port" cty:"winrm_port" hcl:"winrm_port"`
|
||||
WinRMTimeout *string `mapstructure:"winrm_timeout" cty:"winrm_timeout" hcl:"winrm_timeout"`
|
||||
WinRMUseSSL *bool `mapstructure:"winrm_use_ssl" cty:"winrm_use_ssl" hcl:"winrm_use_ssl"`
|
||||
WinRMInsecure *bool `mapstructure:"winrm_insecure" cty:"winrm_insecure" hcl:"winrm_insecure"`
|
||||
WinRMUseNTLM *bool `mapstructure:"winrm_use_ntlm" cty:"winrm_use_ntlm" hcl:"winrm_use_ntlm"`
|
||||
ProxmoxURLRaw *string `mapstructure:"proxmox_url" cty:"proxmox_url" hcl:"proxmox_url"`
|
||||
SkipCertValidation *bool `mapstructure:"insecure_skip_tls_verify" cty:"insecure_skip_tls_verify" hcl:"insecure_skip_tls_verify"`
|
||||
Username *string `mapstructure:"username" cty:"username" hcl:"username"`
|
||||
Password *string `mapstructure:"password" cty:"password" hcl:"password"`
|
||||
Node *string `mapstructure:"node" cty:"node" hcl:"node"`
|
||||
Pool *string `mapstructure:"pool" cty:"pool" hcl:"pool"`
|
||||
VMName *string `mapstructure:"vm_name" cty:"vm_name" hcl:"vm_name"`
|
||||
VMID *int `mapstructure:"vm_id" cty:"vm_id" hcl:"vm_id"`
|
||||
Boot *string `mapstructure:"boot" cty:"boot" hcl:"boot"`
|
||||
Memory *int `mapstructure:"memory" cty:"memory" hcl:"memory"`
|
||||
Cores *int `mapstructure:"cores" cty:"cores" hcl:"cores"`
|
||||
CPUType *string `mapstructure:"cpu_type" cty:"cpu_type" hcl:"cpu_type"`
|
||||
Sockets *int `mapstructure:"sockets" cty:"sockets" hcl:"sockets"`
|
||||
OS *string `mapstructure:"os" cty:"os" hcl:"os"`
|
||||
VGA *FlatvgaConfig `mapstructure:"vga" cty:"vga" hcl:"vga"`
|
||||
NICs []FlatnicConfig `mapstructure:"network_adapters" cty:"network_adapters" hcl:"network_adapters"`
|
||||
Disks []FlatdiskConfig `mapstructure:"disks" cty:"disks" hcl:"disks"`
|
||||
Agent *bool `mapstructure:"qemu_agent" cty:"qemu_agent" hcl:"qemu_agent"`
|
||||
SCSIController *string `mapstructure:"scsi_controller" cty:"scsi_controller" hcl:"scsi_controller"`
|
||||
Onboot *bool `mapstructure:"onboot" cty:"onboot" hcl:"onboot"`
|
||||
DisableKVM *bool `mapstructure:"disable_kvm" cty:"disable_kvm" hcl:"disable_kvm"`
|
||||
TemplateName *string `mapstructure:"template_name" cty:"template_name" hcl:"template_name"`
|
||||
TemplateDescription *string `mapstructure:"template_description" cty:"template_description" hcl:"template_description"`
|
||||
CloudInit *bool `mapstructure:"cloud_init" cty:"cloud_init" hcl:"cloud_init"`
|
||||
CloudInitStoragePool *string `mapstructure:"cloud_init_storage_pool" cty:"cloud_init_storage_pool" hcl:"cloud_init_storage_pool"`
|
||||
AdditionalISOFiles []FlatadditionalISOsConfig `mapstructure:"additional_iso_files" cty:"additional_iso_files" hcl:"additional_iso_files"`
|
||||
VMInterface *string `mapstructure:"vm_interface" cty:"vm_interface" hcl:"vm_interface"`
|
||||
}
|
||||
|
||||
// FlatMapstructure returns a new FlatConfig.
|
||||
@@ -208,12 +208,55 @@ func (*FlatConfig) HCL2Spec() map[string]hcldec.Spec {
|
||||
"template_description": &hcldec.AttrSpec{Name: "template_description", Type: cty.String, Required: false},
|
||||
"cloud_init": &hcldec.AttrSpec{Name: "cloud_init", Type: cty.Bool, Required: false},
|
||||
"cloud_init_storage_pool": &hcldec.AttrSpec{Name: "cloud_init_storage_pool", Type: cty.String, Required: false},
|
||||
"additional_iso_files": &hcldec.BlockListSpec{TypeName: "additional_iso_files", Nested: hcldec.ObjectSpec((*FlatstorageConfig)(nil).HCL2Spec())},
|
||||
"additional_iso_files": &hcldec.BlockListSpec{TypeName: "additional_iso_files", Nested: hcldec.ObjectSpec((*FlatadditionalISOsConfig)(nil).HCL2Spec())},
|
||||
"vm_interface": &hcldec.AttrSpec{Name: "vm_interface", Type: cty.String, Required: false},
|
||||
}
|
||||
return s
|
||||
}
|
||||
|
||||
// FlatadditionalISOsConfig is an auto-generated flat version of additionalISOsConfig.
|
||||
// Where the contents of a field with a `mapstructure:,squash` tag are bubbled up.
|
||||
type FlatadditionalISOsConfig struct {
|
||||
ISOChecksum *string `mapstructure:"iso_checksum" required:"true" cty:"iso_checksum" hcl:"iso_checksum"`
|
||||
RawSingleISOUrl *string `mapstructure:"iso_url" required:"true" cty:"iso_url" hcl:"iso_url"`
|
||||
ISOUrls []string `mapstructure:"iso_urls" cty:"iso_urls" hcl:"iso_urls"`
|
||||
TargetPath *string `mapstructure:"iso_target_path" cty:"iso_target_path" hcl:"iso_target_path"`
|
||||
TargetExtension *string `mapstructure:"iso_target_extension" cty:"iso_target_extension" hcl:"iso_target_extension"`
|
||||
Device *string `mapstructure:"device" cty:"device" hcl:"device"`
|
||||
ISOFile *string `mapstructure:"iso_file" cty:"iso_file" hcl:"iso_file"`
|
||||
ISOStoragePool *string `mapstructure:"iso_storage_pool" cty:"iso_storage_pool" hcl:"iso_storage_pool"`
|
||||
Unmount *bool `mapstructure:"unmount" cty:"unmount" hcl:"unmount"`
|
||||
ShouldUploadISO *bool `cty:"should_upload_iso" hcl:"should_upload_iso"`
|
||||
DownloadPathKey *string `cty:"download_path_key" hcl:"download_path_key"`
|
||||
}
|
||||
|
||||
// FlatMapstructure returns a new FlatadditionalISOsConfig.
|
||||
// FlatadditionalISOsConfig is an auto-generated flat version of additionalISOsConfig.
|
||||
// Where the contents a fields with a `mapstructure:,squash` tag are bubbled up.
|
||||
func (*additionalISOsConfig) FlatMapstructure() interface{ HCL2Spec() map[string]hcldec.Spec } {
|
||||
return new(FlatadditionalISOsConfig)
|
||||
}
|
||||
|
||||
// HCL2Spec returns the hcl spec of a additionalISOsConfig.
|
||||
// This spec is used by HCL to read the fields of additionalISOsConfig.
|
||||
// The decoded values from this spec will then be applied to a FlatadditionalISOsConfig.
|
||||
func (*FlatadditionalISOsConfig) HCL2Spec() map[string]hcldec.Spec {
|
||||
s := map[string]hcldec.Spec{
|
||||
"iso_checksum": &hcldec.AttrSpec{Name: "iso_checksum", Type: cty.String, Required: false},
|
||||
"iso_url": &hcldec.AttrSpec{Name: "iso_url", Type: cty.String, Required: false},
|
||||
"iso_urls": &hcldec.AttrSpec{Name: "iso_urls", Type: cty.List(cty.String), Required: false},
|
||||
"iso_target_path": &hcldec.AttrSpec{Name: "iso_target_path", Type: cty.String, Required: false},
|
||||
"iso_target_extension": &hcldec.AttrSpec{Name: "iso_target_extension", Type: cty.String, Required: false},
|
||||
"device": &hcldec.AttrSpec{Name: "device", Type: cty.String, Required: false},
|
||||
"iso_file": &hcldec.AttrSpec{Name: "iso_file", Type: cty.String, Required: false},
|
||||
"iso_storage_pool": &hcldec.AttrSpec{Name: "iso_storage_pool", Type: cty.String, Required: false},
|
||||
"unmount": &hcldec.AttrSpec{Name: "unmount", Type: cty.Bool, Required: false},
|
||||
"should_upload_iso": &hcldec.AttrSpec{Name: "should_upload_iso", Type: cty.Bool, Required: false},
|
||||
"download_path_key": &hcldec.AttrSpec{Name: "download_path_key", Type: cty.String, Required: false},
|
||||
}
|
||||
return s
|
||||
}
|
||||
|
||||
// FlatdiskConfig is an auto-generated flat version of diskConfig.
|
||||
// Where the contents of a field with a `mapstructure:,squash` tag are bubbled up.
|
||||
type FlatdiskConfig struct {
|
||||
@@ -282,49 +325,6 @@ func (*FlatnicConfig) HCL2Spec() map[string]hcldec.Spec {
|
||||
return s
|
||||
}
|
||||
|
||||
// FlatstorageConfig is an auto-generated flat version of storageConfig.
|
||||
// Where the contents of a field with a `mapstructure:,squash` tag are bubbled up.
|
||||
type FlatstorageConfig struct {
|
||||
ISOChecksum *string `mapstructure:"iso_checksum" required:"true" cty:"iso_checksum" hcl:"iso_checksum"`
|
||||
RawSingleISOUrl *string `mapstructure:"iso_url" required:"true" cty:"iso_url" hcl:"iso_url"`
|
||||
ISOUrls []string `mapstructure:"iso_urls" cty:"iso_urls" hcl:"iso_urls"`
|
||||
TargetPath *string `mapstructure:"iso_target_path" cty:"iso_target_path" hcl:"iso_target_path"`
|
||||
TargetExtension *string `mapstructure:"iso_target_extension" cty:"iso_target_extension" hcl:"iso_target_extension"`
|
||||
Device *string `mapstructure:"device" cty:"device" hcl:"device"`
|
||||
ISOFile *string `mapstructure:"iso_file" cty:"iso_file" hcl:"iso_file"`
|
||||
ISOStoragePool *string `mapstructure:"iso_storage_pool" cty:"iso_storage_pool" hcl:"iso_storage_pool"`
|
||||
Unmount *bool `mapstructure:"unmount" cty:"unmount" hcl:"unmount"`
|
||||
ShouldUploadISO *bool `cty:"should_upload_iso" hcl:"should_upload_iso"`
|
||||
DownloadPathKey *string `cty:"download_path_key" hcl:"download_path_key"`
|
||||
}
|
||||
|
||||
// FlatMapstructure returns a new FlatstorageConfig.
|
||||
// FlatstorageConfig is an auto-generated flat version of storageConfig.
|
||||
// Where the contents a fields with a `mapstructure:,squash` tag are bubbled up.
|
||||
func (*storageConfig) FlatMapstructure() interface{ HCL2Spec() map[string]hcldec.Spec } {
|
||||
return new(FlatstorageConfig)
|
||||
}
|
||||
|
||||
// HCL2Spec returns the hcl spec of a storageConfig.
|
||||
// This spec is used by HCL to read the fields of storageConfig.
|
||||
// The decoded values from this spec will then be applied to a FlatstorageConfig.
|
||||
func (*FlatstorageConfig) HCL2Spec() map[string]hcldec.Spec {
|
||||
s := map[string]hcldec.Spec{
|
||||
"iso_checksum": &hcldec.AttrSpec{Name: "iso_checksum", Type: cty.String, Required: false},
|
||||
"iso_url": &hcldec.AttrSpec{Name: "iso_url", Type: cty.String, Required: false},
|
||||
"iso_urls": &hcldec.AttrSpec{Name: "iso_urls", Type: cty.List(cty.String), Required: false},
|
||||
"iso_target_path": &hcldec.AttrSpec{Name: "iso_target_path", Type: cty.String, Required: false},
|
||||
"iso_target_extension": &hcldec.AttrSpec{Name: "iso_target_extension", Type: cty.String, Required: false},
|
||||
"device": &hcldec.AttrSpec{Name: "device", Type: cty.String, Required: false},
|
||||
"iso_file": &hcldec.AttrSpec{Name: "iso_file", Type: cty.String, Required: false},
|
||||
"iso_storage_pool": &hcldec.AttrSpec{Name: "iso_storage_pool", Type: cty.String, Required: false},
|
||||
"unmount": &hcldec.AttrSpec{Name: "unmount", Type: cty.Bool, Required: false},
|
||||
"should_upload_iso": &hcldec.AttrSpec{Name: "should_upload_iso", Type: cty.Bool, Required: false},
|
||||
"download_path_key": &hcldec.AttrSpec{Name: "download_path_key", Type: cty.String, Required: false},
|
||||
}
|
||||
return s
|
||||
}
|
||||
|
||||
// FlatvgaConfig is an auto-generated flat version of vgaConfig.
|
||||
// Where the contents of a field with a `mapstructure:,squash` tag are bubbled up.
|
||||
type FlatvgaConfig struct {
|
||||
|
||||
@@ -80,6 +80,30 @@ func (s *stepFinalizeTemplateConfig) Run(ctx context.Context, state multistep.St
|
||||
}
|
||||
}
|
||||
|
||||
if len(c.AdditionalISOFiles) > 0 {
|
||||
vmParams, err := client.GetVmConfig(vmRef)
|
||||
if err != nil {
|
||||
err := fmt.Errorf("Error fetching template config: %s", err)
|
||||
state.Put("error", err)
|
||||
ui.Error(err.Error())
|
||||
return multistep.ActionHalt
|
||||
}
|
||||
for idx := range c.AdditionalISOFiles {
|
||||
cdrom := c.AdditionalISOFiles[idx].Device
|
||||
if c.AdditionalISOFiles[idx].Unmount {
|
||||
if vmParams[cdrom] == nil || !strings.Contains(vmParams[cdrom].(string), "media=cdrom") {
|
||||
err := fmt.Errorf("Cannot eject ISO from cdrom drive, %s is not present or not a cdrom media", cdrom)
|
||||
state.Put("error", err)
|
||||
ui.Error(err.Error())
|
||||
return multistep.ActionHalt
|
||||
}
|
||||
changes[cdrom] = "none,media=cdrom"
|
||||
} else {
|
||||
changes[cdrom] = c.AdditionalISOFiles[idx].ISOFile + ",media=cdrom"
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if len(changes) > 0 {
|
||||
_, err := client.SetVmConfig(vmRef, changes)
|
||||
if err != nil {
|
||||
|
||||
@@ -3,7 +3,6 @@ package proxmox
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"log"
|
||||
"strconv"
|
||||
|
||||
"github.com/Telmate/proxmox-api-go/proxmox"
|
||||
@@ -22,10 +21,15 @@ type stepStartVM struct {
|
||||
type ProxmoxVMCreator interface {
|
||||
Create(*proxmox.VmRef, proxmox.ConfigQemu, multistep.StateBag) error
|
||||
}
|
||||
type vmStarter interface {
|
||||
GetNextID(int) (int, error)
|
||||
StartVm(*proxmox.VmRef) (string, error)
|
||||
SetVmConfig(*proxmox.VmRef, map[string]interface{}) (interface{}, error)
|
||||
}
|
||||
|
||||
func (s *stepStartVM) Run(ctx context.Context, state multistep.StateBag) multistep.StepAction {
|
||||
ui := state.Get("ui").(packersdk.Ui)
|
||||
client := state.Get("proxmoxClient").(*proxmox.Client)
|
||||
client := state.Get("proxmoxClient").(vmStarter)
|
||||
c := state.Get("config").(*Config)
|
||||
|
||||
agent := 1
|
||||
@@ -59,21 +63,14 @@ func (s *stepStartVM) Run(ctx context.Context, state multistep.StateBag) multist
|
||||
|
||||
if c.VMID == 0 {
|
||||
ui.Say("No VM ID given, getting next free from Proxmox")
|
||||
for n := 0; n < 5; n++ {
|
||||
id, err := proxmox.MaxVmId(client)
|
||||
if err != nil {
|
||||
log.Printf("Error getting max used VM ID: %v (attempt %d/5)", err, n+1)
|
||||
continue
|
||||
}
|
||||
c.VMID = id + 1
|
||||
break
|
||||
}
|
||||
if c.VMID == 0 {
|
||||
err := fmt.Errorf("Failed to get free VM ID")
|
||||
id, err := client.GetNextID(0)
|
||||
if err != nil {
|
||||
state.Put("error", err)
|
||||
ui.Error(err.Error())
|
||||
return multistep.ActionHalt
|
||||
}
|
||||
c.VMID = id
|
||||
config.VmID = id
|
||||
}
|
||||
vmRef := proxmox.NewVmRef(c.VMID)
|
||||
vmRef.SetNode(c.Node)
|
||||
@@ -89,6 +86,22 @@ func (s *stepStartVM) Run(ctx context.Context, state multistep.StateBag) multist
|
||||
return multistep.ActionHalt
|
||||
}
|
||||
|
||||
// proxmox-api-go assumes all QemuDisks are actually hard disks, not cd
|
||||
// drives, so we need to add them via a config update
|
||||
if len(c.AdditionalISOFiles) > 0 {
|
||||
addISOConfig := make(map[string]interface{})
|
||||
for _, iso := range c.AdditionalISOFiles {
|
||||
addISOConfig[iso.Device] = fmt.Sprintf("%s,media=cdrom", iso.ISOFile)
|
||||
}
|
||||
_, err := client.SetVmConfig(vmRef, addISOConfig)
|
||||
if err != nil {
|
||||
err := fmt.Errorf("Error updating template: %s", err)
|
||||
state.Put("error", err)
|
||||
ui.Error(err.Error())
|
||||
return multistep.ActionHalt
|
||||
}
|
||||
}
|
||||
|
||||
// Store the vm id for later
|
||||
state.Put("vmRef", vmRef)
|
||||
// instance_id is the generic term used so that users can have access to the
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
package proxmox
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"testing"
|
||||
|
||||
@@ -106,3 +107,80 @@ func TestCleanupStartVM(t *testing.T) {
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
type startVMMock struct {
|
||||
create func(*proxmox.VmRef, proxmox.ConfigQemu, multistep.StateBag) error
|
||||
startVm func(*proxmox.VmRef) (string, error)
|
||||
setVmConfig func(*proxmox.VmRef, map[string]interface{}) (interface{}, error)
|
||||
}
|
||||
|
||||
func (m *startVMMock) Create(vmRef *proxmox.VmRef, config proxmox.ConfigQemu, state multistep.StateBag) error {
|
||||
return m.create(vmRef, config, state)
|
||||
}
|
||||
func (m *startVMMock) StartVm(vmRef *proxmox.VmRef) (string, error) {
|
||||
return m.startVm(vmRef)
|
||||
}
|
||||
func (m *startVMMock) SetVmConfig(vmRef *proxmox.VmRef, config map[string]interface{}) (interface{}, error) {
|
||||
return m.setVmConfig(vmRef, config)
|
||||
}
|
||||
func (m *startVMMock) GetNextID(int) (int, error) {
|
||||
return 1, nil
|
||||
}
|
||||
|
||||
func TestStartVM(t *testing.T) {
|
||||
// TODO: proxmox-api-go does a lot of manipulation on the input and does not
|
||||
// give any way to access the actual data it sends to the Proxmox server,
|
||||
// which means writing good tests here is quite hard. This test is mainly a
|
||||
// stub to revisit when we can write better tests.
|
||||
cs := []struct {
|
||||
name string
|
||||
config *Config
|
||||
expectedAction multistep.StepAction
|
||||
}{
|
||||
{
|
||||
name: "Example config from documentation works",
|
||||
config: &Config{
|
||||
Disks: []diskConfig{
|
||||
{
|
||||
Type: "sata",
|
||||
Size: "10G",
|
||||
StoragePool: "local",
|
||||
StoragePoolType: "lvm",
|
||||
},
|
||||
},
|
||||
NICs: []nicConfig{
|
||||
{
|
||||
Bridge: "vmbr0",
|
||||
},
|
||||
},
|
||||
},
|
||||
expectedAction: multistep.ActionContinue,
|
||||
},
|
||||
}
|
||||
|
||||
for _, c := range cs {
|
||||
t.Run(c.name, func(t *testing.T) {
|
||||
mock := &startVMMock{
|
||||
create: func(vmRef *proxmox.VmRef, config proxmox.ConfigQemu, state multistep.StateBag) error {
|
||||
return nil
|
||||
},
|
||||
startVm: func(*proxmox.VmRef) (string, error) {
|
||||
return "", nil
|
||||
},
|
||||
setVmConfig: func(*proxmox.VmRef, map[string]interface{}) (interface{}, error) {
|
||||
return nil, nil
|
||||
},
|
||||
}
|
||||
state := new(multistep.BasicStateBag)
|
||||
state.Put("ui", packersdk.TestUi(t))
|
||||
state.Put("config", c.config)
|
||||
state.Put("proxmoxClient", mock)
|
||||
s := stepStartVM{vmCreator: mock}
|
||||
|
||||
action := s.Run(context.TODO(), state)
|
||||
if action != c.expectedAction {
|
||||
t.Errorf("Expected action %s, got %s", c.expectedAction, action)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
+7
-2
@@ -1,8 +1,9 @@
|
||||
package proxmoxiso
|
||||
package proxmox
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"io"
|
||||
"os"
|
||||
"path/filepath"
|
||||
|
||||
@@ -15,12 +16,16 @@ import (
|
||||
// to the VM
|
||||
type stepUploadAdditionalISOs struct{}
|
||||
|
||||
type uploader interface {
|
||||
Upload(node string, storage string, contentType string, filename string, file io.Reader) error
|
||||
}
|
||||
|
||||
var _ uploader = &proxmox.Client{}
|
||||
|
||||
func (s *stepUploadAdditionalISOs) Run(ctx context.Context, state multistep.StateBag) multistep.StepAction {
|
||||
ui := state.Get("ui").(packersdk.Ui)
|
||||
client := state.Get("proxmoxClient").(uploader)
|
||||
c := state.Get("iso-config").(*Config)
|
||||
c := state.Get("config").(*Config)
|
||||
|
||||
for idx := range c.AdditionalISOFiles {
|
||||
if !c.AdditionalISOFiles[idx].ShouldUploadISO {
|
||||
@@ -43,19 +43,8 @@ func (b *Builder) Run(ctx context.Context, ui packersdk.Ui, hook packersdk.Hook)
|
||||
Url: b.config.ISOUrls,
|
||||
},
|
||||
}
|
||||
for idx := range b.config.AdditionalISOFiles {
|
||||
preSteps = append(preSteps, &commonsteps.StepDownload{
|
||||
Checksum: b.config.AdditionalISOFiles[idx].ISOChecksum,
|
||||
Description: "additional ISO",
|
||||
Extension: b.config.AdditionalISOFiles[idx].TargetExtension,
|
||||
ResultKey: b.config.AdditionalISOFiles[idx].DownloadPathKey,
|
||||
TargetPath: b.config.AdditionalISOFiles[idx].DownloadPathKey,
|
||||
Url: b.config.AdditionalISOFiles[idx].ISOUrls,
|
||||
})
|
||||
}
|
||||
preSteps = append(preSteps,
|
||||
&stepUploadISO{},
|
||||
&stepUploadAdditionalISOs{},
|
||||
)
|
||||
postSteps := []multistep.Step{
|
||||
&stepFinalizeISOTemplate{},
|
||||
|
||||
@@ -1,13 +1,9 @@
|
||||
//go:generate mapstructure-to-hcl2 -type Config,nicConfig,diskConfig,vgaConfig
|
||||
//go:generate mapstructure-to-hcl2 -type Config,nicConfig,diskConfig,vgaConfig,additionalISOsConfig
|
||||
|
||||
package proxmoxiso
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"fmt"
|
||||
"log"
|
||||
"strconv"
|
||||
"strings"
|
||||
|
||||
"github.com/hashicorp/packer-plugin-sdk/multistep/commonsteps"
|
||||
packersdk "github.com/hashicorp/packer-plugin-sdk/packer"
|
||||
@@ -51,62 +47,6 @@ func (c *Config) Prepare(raws ...interface{}) ([]string, []string, error) {
|
||||
errs = packersdk.MultiErrorAppend(errs, errors.New("when specifying iso_url, iso_storage_pool must also be specified"))
|
||||
}
|
||||
|
||||
for idx := range c.AdditionalISOFiles {
|
||||
// Check AdditionalISO config
|
||||
// Either a pre-uploaded ISO should be referenced in iso_file, OR a URL
|
||||
// (possibly to a local file) to an ISO file that will be downloaded and
|
||||
// then uploaded to Proxmox.
|
||||
if c.AdditionalISOFiles[idx].ISOFile != "" {
|
||||
c.AdditionalISOFiles[idx].ShouldUploadISO = false
|
||||
} else {
|
||||
c.AdditionalISOFiles[idx].DownloadPathKey = "downloaded_additional_iso_path_" + strconv.Itoa(idx)
|
||||
isoWarnings, isoErrors := c.AdditionalISOFiles[idx].ISOConfig.Prepare(&c.Ctx)
|
||||
errs = packersdk.MultiErrorAppend(errs, isoErrors...)
|
||||
warnings = append(warnings, isoWarnings...)
|
||||
c.AdditionalISOFiles[idx].ShouldUploadISO = true
|
||||
}
|
||||
if c.AdditionalISOFiles[idx].Device == "" {
|
||||
log.Printf("AdditionalISOFile %d Device not set, using default 'ide3'", idx)
|
||||
c.AdditionalISOFiles[idx].Device = "ide3"
|
||||
}
|
||||
if strings.HasPrefix(c.AdditionalISOFiles[idx].Device, "ide") {
|
||||
busnumber, err := strconv.Atoi(c.AdditionalISOFiles[idx].Device[3:])
|
||||
if err != nil {
|
||||
errs = packersdk.MultiErrorAppend(errs, fmt.Errorf("%s is not a valid bus index", c.AdditionalISOFiles[idx].Device[3:]))
|
||||
}
|
||||
if busnumber == 2 {
|
||||
errs = packersdk.MultiErrorAppend(errs, fmt.Errorf("IDE bus 2 is used by boot ISO"))
|
||||
}
|
||||
if busnumber > 3 {
|
||||
errs = packersdk.MultiErrorAppend(errs, fmt.Errorf("IDE bus index can't be higher than 3"))
|
||||
}
|
||||
}
|
||||
if strings.HasPrefix(c.AdditionalISOFiles[idx].Device, "sata") {
|
||||
busnumber, err := strconv.Atoi(c.AdditionalISOFiles[idx].Device[4:])
|
||||
if err != nil {
|
||||
errs = packersdk.MultiErrorAppend(errs, fmt.Errorf("%s is not a valid bus index", c.AdditionalISOFiles[idx].Device[4:]))
|
||||
}
|
||||
if busnumber > 5 {
|
||||
errs = packersdk.MultiErrorAppend(errs, fmt.Errorf("SATA bus index can't be higher than 5"))
|
||||
}
|
||||
}
|
||||
if strings.HasPrefix(c.AdditionalISOFiles[idx].Device, "scsi") {
|
||||
busnumber, err := strconv.Atoi(c.AdditionalISOFiles[idx].Device[4:])
|
||||
if err != nil {
|
||||
errs = packersdk.MultiErrorAppend(errs, fmt.Errorf("%s is not a valid bus index", c.AdditionalISOFiles[idx].Device[4:]))
|
||||
}
|
||||
if busnumber > 30 {
|
||||
errs = packersdk.MultiErrorAppend(errs, fmt.Errorf("SCSI bus index can't be higher than 30"))
|
||||
}
|
||||
}
|
||||
if (c.AdditionalISOFiles[idx].ISOFile == "" && len(c.AdditionalISOFiles[idx].ISOConfig.ISOUrls) == 0) || (c.AdditionalISOFiles[idx].ISOFile != "" && len(c.AdditionalISOFiles[idx].ISOConfig.ISOUrls) != 0) {
|
||||
errs = packersdk.MultiErrorAppend(errs, fmt.Errorf("either iso_file or iso_url, but not both, must be specified for AdditionalISO file %s", c.AdditionalISOFiles[idx].Device))
|
||||
}
|
||||
if len(c.ISOConfig.ISOUrls) != 0 && c.ISOStoragePool == "" {
|
||||
errs = packersdk.MultiErrorAppend(errs, errors.New("when specifying iso_url, iso_storage_pool must also be specified"))
|
||||
}
|
||||
}
|
||||
|
||||
if errs != nil && len(errs.Errors) > 0 {
|
||||
return nil, warnings, errs
|
||||
}
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
// Code generated by "mapstructure-to-hcl2 -type Config,nicConfig,diskConfig,vgaConfig"; DO NOT EDIT.
|
||||
// Code generated by "mapstructure-to-hcl2 -type Config,nicConfig,diskConfig,vgaConfig,additionalISOsConfig"; DO NOT EDIT.
|
||||
|
||||
package proxmoxiso
|
||||
|
||||
@@ -11,107 +11,107 @@ import (
|
||||
// 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" hcl:"packer_build_name"`
|
||||
PackerBuilderType *string `mapstructure:"packer_builder_type" cty:"packer_builder_type" hcl:"packer_builder_type"`
|
||||
PackerCoreVersion *string `mapstructure:"packer_core_version" cty:"packer_core_version" hcl:"packer_core_version"`
|
||||
PackerDebug *bool `mapstructure:"packer_debug" cty:"packer_debug" hcl:"packer_debug"`
|
||||
PackerForce *bool `mapstructure:"packer_force" cty:"packer_force" hcl:"packer_force"`
|
||||
PackerOnError *string `mapstructure:"packer_on_error" cty:"packer_on_error" hcl:"packer_on_error"`
|
||||
PackerUserVars map[string]string `mapstructure:"packer_user_variables" cty:"packer_user_variables" hcl:"packer_user_variables"`
|
||||
PackerSensitiveVars []string `mapstructure:"packer_sensitive_variables" cty:"packer_sensitive_variables" hcl:"packer_sensitive_variables"`
|
||||
HTTPDir *string `mapstructure:"http_directory" cty:"http_directory" hcl:"http_directory"`
|
||||
HTTPPortMin *int `mapstructure:"http_port_min" cty:"http_port_min" hcl:"http_port_min"`
|
||||
HTTPPortMax *int `mapstructure:"http_port_max" cty:"http_port_max" hcl:"http_port_max"`
|
||||
HTTPAddress *string `mapstructure:"http_bind_address" cty:"http_bind_address" hcl:"http_bind_address"`
|
||||
HTTPInterface *string `mapstructure:"http_interface" undocumented:"true" cty:"http_interface" hcl:"http_interface"`
|
||||
BootGroupInterval *string `mapstructure:"boot_keygroup_interval" cty:"boot_keygroup_interval" hcl:"boot_keygroup_interval"`
|
||||
BootWait *string `mapstructure:"boot_wait" cty:"boot_wait" hcl:"boot_wait"`
|
||||
BootCommand []string `mapstructure:"boot_command" cty:"boot_command" hcl:"boot_command"`
|
||||
BootKeyInterval *string `mapstructure:"boot_key_interval" cty:"boot_key_interval" hcl:"boot_key_interval"`
|
||||
Type *string `mapstructure:"communicator" cty:"communicator" hcl:"communicator"`
|
||||
PauseBeforeConnect *string `mapstructure:"pause_before_connecting" cty:"pause_before_connecting" hcl:"pause_before_connecting"`
|
||||
SSHHost *string `mapstructure:"ssh_host" cty:"ssh_host" hcl:"ssh_host"`
|
||||
SSHPort *int `mapstructure:"ssh_port" cty:"ssh_port" hcl:"ssh_port"`
|
||||
SSHUsername *string `mapstructure:"ssh_username" cty:"ssh_username" hcl:"ssh_username"`
|
||||
SSHPassword *string `mapstructure:"ssh_password" cty:"ssh_password" hcl:"ssh_password"`
|
||||
SSHKeyPairName *string `mapstructure:"ssh_keypair_name" undocumented:"true" cty:"ssh_keypair_name" hcl:"ssh_keypair_name"`
|
||||
SSHTemporaryKeyPairName *string `mapstructure:"temporary_key_pair_name" undocumented:"true" cty:"temporary_key_pair_name" hcl:"temporary_key_pair_name"`
|
||||
SSHTemporaryKeyPairType *string `mapstructure:"temporary_key_pair_type" cty:"temporary_key_pair_type" hcl:"temporary_key_pair_type"`
|
||||
SSHTemporaryKeyPairBits *int `mapstructure:"temporary_key_pair_bits" cty:"temporary_key_pair_bits" hcl:"temporary_key_pair_bits"`
|
||||
SSHCiphers []string `mapstructure:"ssh_ciphers" cty:"ssh_ciphers" hcl:"ssh_ciphers"`
|
||||
SSHClearAuthorizedKeys *bool `mapstructure:"ssh_clear_authorized_keys" cty:"ssh_clear_authorized_keys" hcl:"ssh_clear_authorized_keys"`
|
||||
SSHKEXAlgos []string `mapstructure:"ssh_key_exchange_algorithms" cty:"ssh_key_exchange_algorithms" hcl:"ssh_key_exchange_algorithms"`
|
||||
SSHPrivateKeyFile *string `mapstructure:"ssh_private_key_file" undocumented:"true" cty:"ssh_private_key_file" hcl:"ssh_private_key_file"`
|
||||
SSHCertificateFile *string `mapstructure:"ssh_certificate_file" cty:"ssh_certificate_file" hcl:"ssh_certificate_file"`
|
||||
SSHPty *bool `mapstructure:"ssh_pty" cty:"ssh_pty" hcl:"ssh_pty"`
|
||||
SSHTimeout *string `mapstructure:"ssh_timeout" cty:"ssh_timeout" hcl:"ssh_timeout"`
|
||||
SSHWaitTimeout *string `mapstructure:"ssh_wait_timeout" undocumented:"true" cty:"ssh_wait_timeout" hcl:"ssh_wait_timeout"`
|
||||
SSHAgentAuth *bool `mapstructure:"ssh_agent_auth" undocumented:"true" cty:"ssh_agent_auth" hcl:"ssh_agent_auth"`
|
||||
SSHDisableAgentForwarding *bool `mapstructure:"ssh_disable_agent_forwarding" cty:"ssh_disable_agent_forwarding" hcl:"ssh_disable_agent_forwarding"`
|
||||
SSHHandshakeAttempts *int `mapstructure:"ssh_handshake_attempts" cty:"ssh_handshake_attempts" hcl:"ssh_handshake_attempts"`
|
||||
SSHBastionHost *string `mapstructure:"ssh_bastion_host" cty:"ssh_bastion_host" hcl:"ssh_bastion_host"`
|
||||
SSHBastionPort *int `mapstructure:"ssh_bastion_port" cty:"ssh_bastion_port" hcl:"ssh_bastion_port"`
|
||||
SSHBastionAgentAuth *bool `mapstructure:"ssh_bastion_agent_auth" cty:"ssh_bastion_agent_auth" hcl:"ssh_bastion_agent_auth"`
|
||||
SSHBastionUsername *string `mapstructure:"ssh_bastion_username" cty:"ssh_bastion_username" hcl:"ssh_bastion_username"`
|
||||
SSHBastionPassword *string `mapstructure:"ssh_bastion_password" cty:"ssh_bastion_password" hcl:"ssh_bastion_password"`
|
||||
SSHBastionInteractive *bool `mapstructure:"ssh_bastion_interactive" cty:"ssh_bastion_interactive" hcl:"ssh_bastion_interactive"`
|
||||
SSHBastionPrivateKeyFile *string `mapstructure:"ssh_bastion_private_key_file" cty:"ssh_bastion_private_key_file" hcl:"ssh_bastion_private_key_file"`
|
||||
SSHBastionCertificateFile *string `mapstructure:"ssh_bastion_certificate_file" cty:"ssh_bastion_certificate_file" hcl:"ssh_bastion_certificate_file"`
|
||||
SSHFileTransferMethod *string `mapstructure:"ssh_file_transfer_method" cty:"ssh_file_transfer_method" hcl:"ssh_file_transfer_method"`
|
||||
SSHProxyHost *string `mapstructure:"ssh_proxy_host" cty:"ssh_proxy_host" hcl:"ssh_proxy_host"`
|
||||
SSHProxyPort *int `mapstructure:"ssh_proxy_port" cty:"ssh_proxy_port" hcl:"ssh_proxy_port"`
|
||||
SSHProxyUsername *string `mapstructure:"ssh_proxy_username" cty:"ssh_proxy_username" hcl:"ssh_proxy_username"`
|
||||
SSHProxyPassword *string `mapstructure:"ssh_proxy_password" cty:"ssh_proxy_password" hcl:"ssh_proxy_password"`
|
||||
SSHKeepAliveInterval *string `mapstructure:"ssh_keep_alive_interval" cty:"ssh_keep_alive_interval" hcl:"ssh_keep_alive_interval"`
|
||||
SSHReadWriteTimeout *string `mapstructure:"ssh_read_write_timeout" cty:"ssh_read_write_timeout" hcl:"ssh_read_write_timeout"`
|
||||
SSHRemoteTunnels []string `mapstructure:"ssh_remote_tunnels" cty:"ssh_remote_tunnels" hcl:"ssh_remote_tunnels"`
|
||||
SSHLocalTunnels []string `mapstructure:"ssh_local_tunnels" cty:"ssh_local_tunnels" hcl:"ssh_local_tunnels"`
|
||||
SSHPublicKey []byte `mapstructure:"ssh_public_key" undocumented:"true" cty:"ssh_public_key" hcl:"ssh_public_key"`
|
||||
SSHPrivateKey []byte `mapstructure:"ssh_private_key" undocumented:"true" cty:"ssh_private_key" hcl:"ssh_private_key"`
|
||||
WinRMUser *string `mapstructure:"winrm_username" cty:"winrm_username" hcl:"winrm_username"`
|
||||
WinRMPassword *string `mapstructure:"winrm_password" cty:"winrm_password" hcl:"winrm_password"`
|
||||
WinRMHost *string `mapstructure:"winrm_host" cty:"winrm_host" hcl:"winrm_host"`
|
||||
WinRMNoProxy *bool `mapstructure:"winrm_no_proxy" cty:"winrm_no_proxy" hcl:"winrm_no_proxy"`
|
||||
WinRMPort *int `mapstructure:"winrm_port" cty:"winrm_port" hcl:"winrm_port"`
|
||||
WinRMTimeout *string `mapstructure:"winrm_timeout" cty:"winrm_timeout" hcl:"winrm_timeout"`
|
||||
WinRMUseSSL *bool `mapstructure:"winrm_use_ssl" cty:"winrm_use_ssl" hcl:"winrm_use_ssl"`
|
||||
WinRMInsecure *bool `mapstructure:"winrm_insecure" cty:"winrm_insecure" hcl:"winrm_insecure"`
|
||||
WinRMUseNTLM *bool `mapstructure:"winrm_use_ntlm" cty:"winrm_use_ntlm" hcl:"winrm_use_ntlm"`
|
||||
ProxmoxURLRaw *string `mapstructure:"proxmox_url" cty:"proxmox_url" hcl:"proxmox_url"`
|
||||
SkipCertValidation *bool `mapstructure:"insecure_skip_tls_verify" cty:"insecure_skip_tls_verify" hcl:"insecure_skip_tls_verify"`
|
||||
Username *string `mapstructure:"username" cty:"username" hcl:"username"`
|
||||
Password *string `mapstructure:"password" cty:"password" hcl:"password"`
|
||||
Node *string `mapstructure:"node" cty:"node" hcl:"node"`
|
||||
Pool *string `mapstructure:"pool" cty:"pool" hcl:"pool"`
|
||||
VMName *string `mapstructure:"vm_name" cty:"vm_name" hcl:"vm_name"`
|
||||
VMID *int `mapstructure:"vm_id" cty:"vm_id" hcl:"vm_id"`
|
||||
Boot *string `mapstructure:"boot" cty:"boot" hcl:"boot"`
|
||||
Memory *int `mapstructure:"memory" cty:"memory" hcl:"memory"`
|
||||
Cores *int `mapstructure:"cores" cty:"cores" hcl:"cores"`
|
||||
CPUType *string `mapstructure:"cpu_type" cty:"cpu_type" hcl:"cpu_type"`
|
||||
Sockets *int `mapstructure:"sockets" cty:"sockets" hcl:"sockets"`
|
||||
OS *string `mapstructure:"os" cty:"os" hcl:"os"`
|
||||
VGA *proxmox.FlatvgaConfig `mapstructure:"vga" cty:"vga" hcl:"vga"`
|
||||
NICs []proxmox.FlatnicConfig `mapstructure:"network_adapters" cty:"network_adapters" hcl:"network_adapters"`
|
||||
Disks []proxmox.FlatdiskConfig `mapstructure:"disks" cty:"disks" hcl:"disks"`
|
||||
Agent *bool `mapstructure:"qemu_agent" cty:"qemu_agent" hcl:"qemu_agent"`
|
||||
SCSIController *string `mapstructure:"scsi_controller" cty:"scsi_controller" hcl:"scsi_controller"`
|
||||
Onboot *bool `mapstructure:"onboot" cty:"onboot" hcl:"onboot"`
|
||||
DisableKVM *bool `mapstructure:"disable_kvm" cty:"disable_kvm" hcl:"disable_kvm"`
|
||||
TemplateName *string `mapstructure:"template_name" cty:"template_name" hcl:"template_name"`
|
||||
TemplateDescription *string `mapstructure:"template_description" cty:"template_description" hcl:"template_description"`
|
||||
CloudInit *bool `mapstructure:"cloud_init" cty:"cloud_init" hcl:"cloud_init"`
|
||||
CloudInitStoragePool *string `mapstructure:"cloud_init_storage_pool" cty:"cloud_init_storage_pool" hcl:"cloud_init_storage_pool"`
|
||||
AdditionalISOFiles []proxmox.FlatstorageConfig `mapstructure:"additional_iso_files" cty:"additional_iso_files" hcl:"additional_iso_files"`
|
||||
VMInterface *string `mapstructure:"vm_interface" cty:"vm_interface" hcl:"vm_interface"`
|
||||
ISOChecksum *string `mapstructure:"iso_checksum" required:"true" cty:"iso_checksum" hcl:"iso_checksum"`
|
||||
RawSingleISOUrl *string `mapstructure:"iso_url" required:"true" cty:"iso_url" hcl:"iso_url"`
|
||||
ISOUrls []string `mapstructure:"iso_urls" cty:"iso_urls" hcl:"iso_urls"`
|
||||
TargetPath *string `mapstructure:"iso_target_path" cty:"iso_target_path" hcl:"iso_target_path"`
|
||||
TargetExtension *string `mapstructure:"iso_target_extension" cty:"iso_target_extension" hcl:"iso_target_extension"`
|
||||
ISOFile *string `mapstructure:"iso_file" cty:"iso_file" hcl:"iso_file"`
|
||||
ISOStoragePool *string `mapstructure:"iso_storage_pool" cty:"iso_storage_pool" hcl:"iso_storage_pool"`
|
||||
UnmountISO *bool `mapstructure:"unmount_iso" cty:"unmount_iso" hcl:"unmount_iso"`
|
||||
PackerBuildName *string `mapstructure:"packer_build_name" cty:"packer_build_name" hcl:"packer_build_name"`
|
||||
PackerBuilderType *string `mapstructure:"packer_builder_type" cty:"packer_builder_type" hcl:"packer_builder_type"`
|
||||
PackerCoreVersion *string `mapstructure:"packer_core_version" cty:"packer_core_version" hcl:"packer_core_version"`
|
||||
PackerDebug *bool `mapstructure:"packer_debug" cty:"packer_debug" hcl:"packer_debug"`
|
||||
PackerForce *bool `mapstructure:"packer_force" cty:"packer_force" hcl:"packer_force"`
|
||||
PackerOnError *string `mapstructure:"packer_on_error" cty:"packer_on_error" hcl:"packer_on_error"`
|
||||
PackerUserVars map[string]string `mapstructure:"packer_user_variables" cty:"packer_user_variables" hcl:"packer_user_variables"`
|
||||
PackerSensitiveVars []string `mapstructure:"packer_sensitive_variables" cty:"packer_sensitive_variables" hcl:"packer_sensitive_variables"`
|
||||
HTTPDir *string `mapstructure:"http_directory" cty:"http_directory" hcl:"http_directory"`
|
||||
HTTPPortMin *int `mapstructure:"http_port_min" cty:"http_port_min" hcl:"http_port_min"`
|
||||
HTTPPortMax *int `mapstructure:"http_port_max" cty:"http_port_max" hcl:"http_port_max"`
|
||||
HTTPAddress *string `mapstructure:"http_bind_address" cty:"http_bind_address" hcl:"http_bind_address"`
|
||||
HTTPInterface *string `mapstructure:"http_interface" undocumented:"true" cty:"http_interface" hcl:"http_interface"`
|
||||
BootGroupInterval *string `mapstructure:"boot_keygroup_interval" cty:"boot_keygroup_interval" hcl:"boot_keygroup_interval"`
|
||||
BootWait *string `mapstructure:"boot_wait" cty:"boot_wait" hcl:"boot_wait"`
|
||||
BootCommand []string `mapstructure:"boot_command" cty:"boot_command" hcl:"boot_command"`
|
||||
BootKeyInterval *string `mapstructure:"boot_key_interval" cty:"boot_key_interval" hcl:"boot_key_interval"`
|
||||
Type *string `mapstructure:"communicator" cty:"communicator" hcl:"communicator"`
|
||||
PauseBeforeConnect *string `mapstructure:"pause_before_connecting" cty:"pause_before_connecting" hcl:"pause_before_connecting"`
|
||||
SSHHost *string `mapstructure:"ssh_host" cty:"ssh_host" hcl:"ssh_host"`
|
||||
SSHPort *int `mapstructure:"ssh_port" cty:"ssh_port" hcl:"ssh_port"`
|
||||
SSHUsername *string `mapstructure:"ssh_username" cty:"ssh_username" hcl:"ssh_username"`
|
||||
SSHPassword *string `mapstructure:"ssh_password" cty:"ssh_password" hcl:"ssh_password"`
|
||||
SSHKeyPairName *string `mapstructure:"ssh_keypair_name" undocumented:"true" cty:"ssh_keypair_name" hcl:"ssh_keypair_name"`
|
||||
SSHTemporaryKeyPairName *string `mapstructure:"temporary_key_pair_name" undocumented:"true" cty:"temporary_key_pair_name" hcl:"temporary_key_pair_name"`
|
||||
SSHTemporaryKeyPairType *string `mapstructure:"temporary_key_pair_type" cty:"temporary_key_pair_type" hcl:"temporary_key_pair_type"`
|
||||
SSHTemporaryKeyPairBits *int `mapstructure:"temporary_key_pair_bits" cty:"temporary_key_pair_bits" hcl:"temporary_key_pair_bits"`
|
||||
SSHCiphers []string `mapstructure:"ssh_ciphers" cty:"ssh_ciphers" hcl:"ssh_ciphers"`
|
||||
SSHClearAuthorizedKeys *bool `mapstructure:"ssh_clear_authorized_keys" cty:"ssh_clear_authorized_keys" hcl:"ssh_clear_authorized_keys"`
|
||||
SSHKEXAlgos []string `mapstructure:"ssh_key_exchange_algorithms" cty:"ssh_key_exchange_algorithms" hcl:"ssh_key_exchange_algorithms"`
|
||||
SSHPrivateKeyFile *string `mapstructure:"ssh_private_key_file" undocumented:"true" cty:"ssh_private_key_file" hcl:"ssh_private_key_file"`
|
||||
SSHCertificateFile *string `mapstructure:"ssh_certificate_file" cty:"ssh_certificate_file" hcl:"ssh_certificate_file"`
|
||||
SSHPty *bool `mapstructure:"ssh_pty" cty:"ssh_pty" hcl:"ssh_pty"`
|
||||
SSHTimeout *string `mapstructure:"ssh_timeout" cty:"ssh_timeout" hcl:"ssh_timeout"`
|
||||
SSHWaitTimeout *string `mapstructure:"ssh_wait_timeout" undocumented:"true" cty:"ssh_wait_timeout" hcl:"ssh_wait_timeout"`
|
||||
SSHAgentAuth *bool `mapstructure:"ssh_agent_auth" undocumented:"true" cty:"ssh_agent_auth" hcl:"ssh_agent_auth"`
|
||||
SSHDisableAgentForwarding *bool `mapstructure:"ssh_disable_agent_forwarding" cty:"ssh_disable_agent_forwarding" hcl:"ssh_disable_agent_forwarding"`
|
||||
SSHHandshakeAttempts *int `mapstructure:"ssh_handshake_attempts" cty:"ssh_handshake_attempts" hcl:"ssh_handshake_attempts"`
|
||||
SSHBastionHost *string `mapstructure:"ssh_bastion_host" cty:"ssh_bastion_host" hcl:"ssh_bastion_host"`
|
||||
SSHBastionPort *int `mapstructure:"ssh_bastion_port" cty:"ssh_bastion_port" hcl:"ssh_bastion_port"`
|
||||
SSHBastionAgentAuth *bool `mapstructure:"ssh_bastion_agent_auth" cty:"ssh_bastion_agent_auth" hcl:"ssh_bastion_agent_auth"`
|
||||
SSHBastionUsername *string `mapstructure:"ssh_bastion_username" cty:"ssh_bastion_username" hcl:"ssh_bastion_username"`
|
||||
SSHBastionPassword *string `mapstructure:"ssh_bastion_password" cty:"ssh_bastion_password" hcl:"ssh_bastion_password"`
|
||||
SSHBastionInteractive *bool `mapstructure:"ssh_bastion_interactive" cty:"ssh_bastion_interactive" hcl:"ssh_bastion_interactive"`
|
||||
SSHBastionPrivateKeyFile *string `mapstructure:"ssh_bastion_private_key_file" cty:"ssh_bastion_private_key_file" hcl:"ssh_bastion_private_key_file"`
|
||||
SSHBastionCertificateFile *string `mapstructure:"ssh_bastion_certificate_file" cty:"ssh_bastion_certificate_file" hcl:"ssh_bastion_certificate_file"`
|
||||
SSHFileTransferMethod *string `mapstructure:"ssh_file_transfer_method" cty:"ssh_file_transfer_method" hcl:"ssh_file_transfer_method"`
|
||||
SSHProxyHost *string `mapstructure:"ssh_proxy_host" cty:"ssh_proxy_host" hcl:"ssh_proxy_host"`
|
||||
SSHProxyPort *int `mapstructure:"ssh_proxy_port" cty:"ssh_proxy_port" hcl:"ssh_proxy_port"`
|
||||
SSHProxyUsername *string `mapstructure:"ssh_proxy_username" cty:"ssh_proxy_username" hcl:"ssh_proxy_username"`
|
||||
SSHProxyPassword *string `mapstructure:"ssh_proxy_password" cty:"ssh_proxy_password" hcl:"ssh_proxy_password"`
|
||||
SSHKeepAliveInterval *string `mapstructure:"ssh_keep_alive_interval" cty:"ssh_keep_alive_interval" hcl:"ssh_keep_alive_interval"`
|
||||
SSHReadWriteTimeout *string `mapstructure:"ssh_read_write_timeout" cty:"ssh_read_write_timeout" hcl:"ssh_read_write_timeout"`
|
||||
SSHRemoteTunnels []string `mapstructure:"ssh_remote_tunnels" cty:"ssh_remote_tunnels" hcl:"ssh_remote_tunnels"`
|
||||
SSHLocalTunnels []string `mapstructure:"ssh_local_tunnels" cty:"ssh_local_tunnels" hcl:"ssh_local_tunnels"`
|
||||
SSHPublicKey []byte `mapstructure:"ssh_public_key" undocumented:"true" cty:"ssh_public_key" hcl:"ssh_public_key"`
|
||||
SSHPrivateKey []byte `mapstructure:"ssh_private_key" undocumented:"true" cty:"ssh_private_key" hcl:"ssh_private_key"`
|
||||
WinRMUser *string `mapstructure:"winrm_username" cty:"winrm_username" hcl:"winrm_username"`
|
||||
WinRMPassword *string `mapstructure:"winrm_password" cty:"winrm_password" hcl:"winrm_password"`
|
||||
WinRMHost *string `mapstructure:"winrm_host" cty:"winrm_host" hcl:"winrm_host"`
|
||||
WinRMNoProxy *bool `mapstructure:"winrm_no_proxy" cty:"winrm_no_proxy" hcl:"winrm_no_proxy"`
|
||||
WinRMPort *int `mapstructure:"winrm_port" cty:"winrm_port" hcl:"winrm_port"`
|
||||
WinRMTimeout *string `mapstructure:"winrm_timeout" cty:"winrm_timeout" hcl:"winrm_timeout"`
|
||||
WinRMUseSSL *bool `mapstructure:"winrm_use_ssl" cty:"winrm_use_ssl" hcl:"winrm_use_ssl"`
|
||||
WinRMInsecure *bool `mapstructure:"winrm_insecure" cty:"winrm_insecure" hcl:"winrm_insecure"`
|
||||
WinRMUseNTLM *bool `mapstructure:"winrm_use_ntlm" cty:"winrm_use_ntlm" hcl:"winrm_use_ntlm"`
|
||||
ProxmoxURLRaw *string `mapstructure:"proxmox_url" cty:"proxmox_url" hcl:"proxmox_url"`
|
||||
SkipCertValidation *bool `mapstructure:"insecure_skip_tls_verify" cty:"insecure_skip_tls_verify" hcl:"insecure_skip_tls_verify"`
|
||||
Username *string `mapstructure:"username" cty:"username" hcl:"username"`
|
||||
Password *string `mapstructure:"password" cty:"password" hcl:"password"`
|
||||
Node *string `mapstructure:"node" cty:"node" hcl:"node"`
|
||||
Pool *string `mapstructure:"pool" cty:"pool" hcl:"pool"`
|
||||
VMName *string `mapstructure:"vm_name" cty:"vm_name" hcl:"vm_name"`
|
||||
VMID *int `mapstructure:"vm_id" cty:"vm_id" hcl:"vm_id"`
|
||||
Boot *string `mapstructure:"boot" cty:"boot" hcl:"boot"`
|
||||
Memory *int `mapstructure:"memory" cty:"memory" hcl:"memory"`
|
||||
Cores *int `mapstructure:"cores" cty:"cores" hcl:"cores"`
|
||||
CPUType *string `mapstructure:"cpu_type" cty:"cpu_type" hcl:"cpu_type"`
|
||||
Sockets *int `mapstructure:"sockets" cty:"sockets" hcl:"sockets"`
|
||||
OS *string `mapstructure:"os" cty:"os" hcl:"os"`
|
||||
VGA *proxmox.FlatvgaConfig `mapstructure:"vga" cty:"vga" hcl:"vga"`
|
||||
NICs []proxmox.FlatnicConfig `mapstructure:"network_adapters" cty:"network_adapters" hcl:"network_adapters"`
|
||||
Disks []proxmox.FlatdiskConfig `mapstructure:"disks" cty:"disks" hcl:"disks"`
|
||||
Agent *bool `mapstructure:"qemu_agent" cty:"qemu_agent" hcl:"qemu_agent"`
|
||||
SCSIController *string `mapstructure:"scsi_controller" cty:"scsi_controller" hcl:"scsi_controller"`
|
||||
Onboot *bool `mapstructure:"onboot" cty:"onboot" hcl:"onboot"`
|
||||
DisableKVM *bool `mapstructure:"disable_kvm" cty:"disable_kvm" hcl:"disable_kvm"`
|
||||
TemplateName *string `mapstructure:"template_name" cty:"template_name" hcl:"template_name"`
|
||||
TemplateDescription *string `mapstructure:"template_description" cty:"template_description" hcl:"template_description"`
|
||||
CloudInit *bool `mapstructure:"cloud_init" cty:"cloud_init" hcl:"cloud_init"`
|
||||
CloudInitStoragePool *string `mapstructure:"cloud_init_storage_pool" cty:"cloud_init_storage_pool" hcl:"cloud_init_storage_pool"`
|
||||
AdditionalISOFiles []proxmox.FlatadditionalISOsConfig `mapstructure:"additional_iso_files" cty:"additional_iso_files" hcl:"additional_iso_files"`
|
||||
VMInterface *string `mapstructure:"vm_interface" cty:"vm_interface" hcl:"vm_interface"`
|
||||
ISOChecksum *string `mapstructure:"iso_checksum" required:"true" cty:"iso_checksum" hcl:"iso_checksum"`
|
||||
RawSingleISOUrl *string `mapstructure:"iso_url" required:"true" cty:"iso_url" hcl:"iso_url"`
|
||||
ISOUrls []string `mapstructure:"iso_urls" cty:"iso_urls" hcl:"iso_urls"`
|
||||
TargetPath *string `mapstructure:"iso_target_path" cty:"iso_target_path" hcl:"iso_target_path"`
|
||||
TargetExtension *string `mapstructure:"iso_target_extension" cty:"iso_target_extension" hcl:"iso_target_extension"`
|
||||
ISOFile *string `mapstructure:"iso_file" cty:"iso_file" hcl:"iso_file"`
|
||||
ISOStoragePool *string `mapstructure:"iso_storage_pool" cty:"iso_storage_pool" hcl:"iso_storage_pool"`
|
||||
UnmountISO *bool `mapstructure:"unmount_iso" cty:"unmount_iso" hcl:"unmount_iso"`
|
||||
}
|
||||
|
||||
// FlatMapstructure returns a new FlatConfig.
|
||||
@@ -217,7 +217,7 @@ func (*FlatConfig) HCL2Spec() map[string]hcldec.Spec {
|
||||
"template_description": &hcldec.AttrSpec{Name: "template_description", Type: cty.String, Required: false},
|
||||
"cloud_init": &hcldec.AttrSpec{Name: "cloud_init", Type: cty.Bool, Required: false},
|
||||
"cloud_init_storage_pool": &hcldec.AttrSpec{Name: "cloud_init_storage_pool", Type: cty.String, Required: false},
|
||||
"additional_iso_files": &hcldec.BlockListSpec{TypeName: "additional_iso_files", Nested: hcldec.ObjectSpec((*proxmox.FlatstorageConfig)(nil).HCL2Spec())},
|
||||
"additional_iso_files": &hcldec.BlockListSpec{TypeName: "additional_iso_files", Nested: hcldec.ObjectSpec((*proxmox.FlatadditionalISOsConfig)(nil).HCL2Spec())},
|
||||
"vm_interface": &hcldec.AttrSpec{Name: "vm_interface", Type: cty.String, Required: false},
|
||||
"iso_checksum": &hcldec.AttrSpec{Name: "iso_checksum", Type: cty.String, Required: false},
|
||||
"iso_url": &hcldec.AttrSpec{Name: "iso_url", Type: cty.String, Required: false},
|
||||
|
||||
@@ -46,29 +46,6 @@ func (s *stepFinalizeISOTemplate) Run(ctx context.Context, state multistep.State
|
||||
}
|
||||
changes["ide2"] = "none,media=cdrom"
|
||||
}
|
||||
if len(c.AdditionalISOFiles) > 0 {
|
||||
vmParams, err := client.GetVmConfig(vmRef)
|
||||
if err != nil {
|
||||
err := fmt.Errorf("Error fetching template config: %s", err)
|
||||
state.Put("error", err)
|
||||
ui.Error(err.Error())
|
||||
return multistep.ActionHalt
|
||||
}
|
||||
for idx := range c.AdditionalISOFiles {
|
||||
cdrom := c.AdditionalISOFiles[idx].Device
|
||||
if c.AdditionalISOFiles[idx].Unmount {
|
||||
if vmParams[cdrom] == nil || !strings.Contains(vmParams[cdrom].(string), "media=cdrom") {
|
||||
err := fmt.Errorf("Cannot eject ISO from cdrom drive, %s is not present or not a cdrom media", cdrom)
|
||||
state.Put("error", err)
|
||||
ui.Error(err.Error())
|
||||
return multistep.ActionHalt
|
||||
}
|
||||
changes[cdrom] = "none,media=cdrom"
|
||||
} else {
|
||||
changes[cdrom] = c.AdditionalISOFiles[idx].ISOFile + ",media=cdrom"
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if len(changes) > 0 {
|
||||
_, err := client.SetVmConfig(vmRef, changes)
|
||||
|
||||
@@ -74,8 +74,10 @@ type Config struct {
|
||||
|
||||
RemoveVolume bool `mapstructure:"remove_volume"`
|
||||
|
||||
UserAgent string `mapstructure-to-hcl2:",skip"`
|
||||
ctx interpolate.Context
|
||||
// Shutdown timeout. Default to 5m
|
||||
ShutdownTimeout string `mapstructure:"shutdown_timeout" required:"false"`
|
||||
UserAgent string `mapstructure-to-hcl2:",skip"`
|
||||
ctx interpolate.Context
|
||||
|
||||
// Deprecated configs
|
||||
|
||||
@@ -255,6 +257,10 @@ func (c *Config) Prepare(raws ...interface{}) ([]string, error) {
|
||||
errs, errors.New("image is required"))
|
||||
}
|
||||
|
||||
if c.ShutdownTimeout == "" {
|
||||
c.ShutdownTimeout = "5m"
|
||||
}
|
||||
|
||||
if errs != nil && len(errs.Errors) > 0 {
|
||||
return warnings, errs
|
||||
}
|
||||
|
||||
@@ -80,6 +80,7 @@ type FlatConfig struct {
|
||||
Bootscript *string `mapstructure:"bootscript" required:"false" cty:"bootscript" hcl:"bootscript"`
|
||||
BootType *string `mapstructure:"boottype" required:"false" cty:"boottype" hcl:"boottype"`
|
||||
RemoveVolume *bool `mapstructure:"remove_volume" cty:"remove_volume" hcl:"remove_volume"`
|
||||
ShutdownTimeout *string `mapstructure:"shutdown_timeout" required:"false" cty:"shutdown_timeout" hcl:"shutdown_timeout"`
|
||||
Token *string `mapstructure:"api_token" required:"false" cty:"api_token" hcl:"api_token"`
|
||||
Organization *string `mapstructure:"organization_id" required:"false" cty:"organization_id" hcl:"organization_id"`
|
||||
Region *string `mapstructure:"region" required:"false" cty:"region" hcl:"region"`
|
||||
@@ -167,6 +168,7 @@ func (*FlatConfig) HCL2Spec() map[string]hcldec.Spec {
|
||||
"bootscript": &hcldec.AttrSpec{Name: "bootscript", Type: cty.String, Required: false},
|
||||
"boottype": &hcldec.AttrSpec{Name: "boottype", Type: cty.String, Required: false},
|
||||
"remove_volume": &hcldec.AttrSpec{Name: "remove_volume", Type: cty.Bool, Required: false},
|
||||
"shutdown_timeout": &hcldec.AttrSpec{Name: "shutdown_timeout", Type: cty.String, Required: false},
|
||||
"api_token": &hcldec.AttrSpec{Name: "api_token", Type: cty.String, Required: false},
|
||||
"organization_id": &hcldec.AttrSpec{Name: "organization_id", Type: cty.String, Required: false},
|
||||
"region": &hcldec.AttrSpec{Name: "region", Type: cty.String, Required: false},
|
||||
|
||||
@@ -3,6 +3,7 @@ package scaleway
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"time"
|
||||
|
||||
"github.com/hashicorp/packer-plugin-sdk/multistep"
|
||||
packersdk "github.com/hashicorp/packer-plugin-sdk/packer"
|
||||
@@ -30,9 +31,23 @@ func (s *stepShutdown) Run(ctx context.Context, state multistep.StateBag) multis
|
||||
return multistep.ActionHalt
|
||||
}
|
||||
|
||||
instanceResp, err := instanceAPI.WaitForServer(&instance.WaitForServerRequest{
|
||||
waitRequest := &instance.WaitForServerRequest{
|
||||
ServerID: serverID,
|
||||
})
|
||||
}
|
||||
c := state.Get("config").(*Config)
|
||||
timeout := c.ShutdownTimeout
|
||||
duration, err := time.ParseDuration(timeout)
|
||||
if err != nil {
|
||||
err := fmt.Errorf("error: %s could not parse string %s as a duration", err, timeout)
|
||||
state.Put("error", err)
|
||||
ui.Error(err.Error())
|
||||
return multistep.ActionHalt
|
||||
}
|
||||
if timeout != "" {
|
||||
waitRequest.Timeout = scw.TimeDurationPtr(duration)
|
||||
}
|
||||
|
||||
instanceResp, err := instanceAPI.WaitForServer(waitRequest)
|
||||
if err != nil {
|
||||
err := fmt.Errorf("Error shutting down server: %s", err)
|
||||
state.Put("error", err)
|
||||
|
||||
@@ -22,6 +22,9 @@ type Driver interface {
|
||||
// Create a SCSI controller.
|
||||
CreateSCSIController(vm string, controller string) error
|
||||
|
||||
// Create a VirtIO controller.
|
||||
CreateVirtIOController(vm string, controller string) error
|
||||
|
||||
// Create an NVME controller
|
||||
CreateNVMeController(vm string, controller string, portcount int) error
|
||||
|
||||
|
||||
@@ -64,7 +64,6 @@ func (d *VBox42Driver) CreateNVMeController(vmName string, name string, portcoun
|
||||
}
|
||||
|
||||
func (d *VBox42Driver) CreateSCSIController(vmName string, name string) error {
|
||||
|
||||
command := []string{
|
||||
"storagectl", vmName,
|
||||
"--name", name,
|
||||
@@ -75,6 +74,17 @@ func (d *VBox42Driver) CreateSCSIController(vmName string, name string) error {
|
||||
return d.VBoxManage(command...)
|
||||
}
|
||||
|
||||
func (d *VBox42Driver) CreateVirtIOController(vmName string, name string) error {
|
||||
command := []string{
|
||||
"storagectl", vmName,
|
||||
"--name", name,
|
||||
"--add", "VirtIO",
|
||||
"--controller", "VirtIO",
|
||||
}
|
||||
|
||||
return d.VBoxManage(command...)
|
||||
}
|
||||
|
||||
func (d *VBox42Driver) RemoveFloppyControllers(vmName string) error {
|
||||
var stdout bytes.Buffer
|
||||
|
||||
|
||||
@@ -13,6 +13,10 @@ type DriverMock struct {
|
||||
CreateSCSIControllerController string
|
||||
CreateSCSIControllerErr error
|
||||
|
||||
CreateVirtIOControllerVM string
|
||||
CreateVirtIOControllerController string
|
||||
CreateVirtIOControllerErr error
|
||||
|
||||
CreateNVMeControllerVM string
|
||||
CreateNVMeControllerController string
|
||||
CreateNVMeControllerErr error
|
||||
@@ -78,6 +82,12 @@ func (d *DriverMock) CreateSCSIController(vm string, controller string) error {
|
||||
return d.CreateSCSIControllerErr
|
||||
}
|
||||
|
||||
func (d *DriverMock) CreateVirtIOController(vm string, controller string) error {
|
||||
d.CreateVirtIOControllerVM = vm
|
||||
d.CreateVirtIOControllerController = vm
|
||||
return d.CreateVirtIOControllerErr
|
||||
}
|
||||
|
||||
func (d *DriverMock) CreateNVMeController(vm string, controller string, portcount int) error {
|
||||
d.CreateNVMeControllerVM = vm
|
||||
d.CreateNVMeControllerController = vm
|
||||
|
||||
@@ -80,6 +80,10 @@ func (s *StepAttachISOs) Run(ctx context.Context, state multistep.StateBag) mult
|
||||
controllerName = "SATA Controller"
|
||||
port = "1"
|
||||
device = "0"
|
||||
} else if s.ISOInterface == "virtio" {
|
||||
controllerName = "VirtIO Controller"
|
||||
port = "1"
|
||||
device = "0"
|
||||
}
|
||||
ui.Message("Mounting boot ISO...")
|
||||
case "guest_additions":
|
||||
@@ -90,6 +94,10 @@ func (s *StepAttachISOs) Run(ctx context.Context, state multistep.StateBag) mult
|
||||
controllerName = "SATA Controller"
|
||||
port = "2"
|
||||
device = "0"
|
||||
} else if s.GuestAdditionsInterface == "virtio" {
|
||||
controllerName = "VirtIO Controller"
|
||||
port = "2"
|
||||
device = "0"
|
||||
}
|
||||
ui.Message("Mounting guest additions ISO...")
|
||||
case "cd_files":
|
||||
@@ -100,6 +108,10 @@ func (s *StepAttachISOs) Run(ctx context.Context, state multistep.StateBag) mult
|
||||
controllerName = "SATA Controller"
|
||||
port = "3"
|
||||
device = "0"
|
||||
} else if s.ISOInterface == "virtio" {
|
||||
controllerName = "VirtIO Controller"
|
||||
port = "3"
|
||||
device = "0"
|
||||
}
|
||||
ui.Message("Mounting cd_files ISO...")
|
||||
}
|
||||
|
||||
@@ -44,9 +44,37 @@ type Config struct {
|
||||
vboxcommon.VBoxVersionConfig `mapstructure:",squash"`
|
||||
vboxcommon.VBoxBundleConfig `mapstructure:",squash"`
|
||||
vboxcommon.GuestAdditionsConfig `mapstructure:",squash"`
|
||||
// The chipset to be used: PIIX3 or ICH9.
|
||||
// When set to piix3, the firmare is PIIX3. This is the default.
|
||||
// When set to ich9, the firmare is ICH9.
|
||||
Chipset string `mapstructure:"chipset" required:"false"`
|
||||
// The firmware to be used: BIOS or EFI.
|
||||
// When set to bios, the firmare is BIOS. This is the default.
|
||||
// When set to efi, the firmare is EFI.
|
||||
Firmware string `mapstructure:"firmware" required:"false"`
|
||||
// The size, in megabytes, of the hard disk to create for the VM. By
|
||||
// default, this is 40000 (about 40 GB).
|
||||
DiskSize uint `mapstructure:"disk_size" required:"false"`
|
||||
// The NIC type to be used for the network interfaces.
|
||||
// When set to 82540EM, the NICs are Intel PRO/1000 MT Desktop (82540EM). This is the default.
|
||||
// When set to 82543GC, the NICs are Intel PRO/1000 T Server (82543GC).
|
||||
// When set to 82545EM, the NICs are Intel PRO/1000 MT Server (82545EM).
|
||||
// When set to Am79C970A, the NICs are AMD PCNet-PCI II network card (Am79C970A).
|
||||
// When set to Am79C973, the NICs are AMD PCNet-FAST III network card (Am79C973).
|
||||
// When set to Am79C960, the NICs are AMD PCnet-ISA/NE2100 (Am79C960).
|
||||
// When set to virtio, the NICs are VirtIO.
|
||||
NICType string `mapstructure:"nic_type" required:"false"`
|
||||
// The audio controller type to be used.
|
||||
// When set to ac97, the audio controller is ICH AC97. This is the default.
|
||||
// When set to hda, the audio controller is Intel HD Audio.
|
||||
// When set to sb16, the audio controller is SoundBlaster 16.
|
||||
AudioController string `mapstructure:"audio_controller" required:"false"`
|
||||
// The graphics controller type to be used.
|
||||
// When set to vboxvga, the graphics controller is VirtualBox VGA. This is the default.
|
||||
// When set to vboxsvga, the graphics controller is VirtualBox SVGA.
|
||||
// When set to vmsvga, the graphics controller is VMware SVGA.
|
||||
// When set to none, the graphics controller is disabled.
|
||||
GfxController string `mapstructure:"gfx_controller" required:"false"`
|
||||
// The guest OS type being installed. By default this is other, but you can
|
||||
// get dramatic performance improvements by setting this to the proper
|
||||
// value. To view all available values for this run VBoxManage list
|
||||
@@ -62,6 +90,7 @@ type Config struct {
|
||||
// defaults to ide. When set to sata, the drive is attached to an AHCI SATA
|
||||
// controller. When set to scsi, the drive is attached to an LsiLogic SCSI
|
||||
// controller. When set to pcie, the drive is attached to an NVMe
|
||||
// controller. When set to virtio, the drive is attached to a VirtIO
|
||||
// controller. Please note that when you use "pcie", you'll need to have
|
||||
// Virtualbox 6, install an [extension
|
||||
// pack](https://www.virtualbox.org/wiki/Downloads#VirtualBox6.0.14OracleVMVirtualBoxExtensionPack)
|
||||
@@ -98,6 +127,7 @@ type Config struct {
|
||||
HardDriveNonrotational bool `mapstructure:"hard_drive_nonrotational" required:"false"`
|
||||
// The type of controller that the ISO is attached to, defaults to ide.
|
||||
// When set to sata, the drive is attached to an AHCI SATA controller.
|
||||
// When set to virtio, the drive is attached to a VirtIO controller.
|
||||
ISOInterface string `mapstructure:"iso_interface" required:"false"`
|
||||
// Set this to true if you would like to keep the VM registered with
|
||||
// virtualbox. Defaults to false.
|
||||
@@ -160,6 +190,28 @@ func (b *Builder) Prepare(raws ...interface{}) ([]string, []string, error) {
|
||||
errs = packersdk.MultiErrorAppend(errs, b.config.BootConfig.Prepare(&b.config.ctx)...)
|
||||
errs = packersdk.MultiErrorAppend(errs, b.config.GuestAdditionsConfig.Prepare(b.config.CommConfig.Comm.Type)...)
|
||||
|
||||
if b.config.Chipset == "" {
|
||||
b.config.Chipset = "piix3"
|
||||
}
|
||||
switch b.config.Chipset {
|
||||
case "piix3", "ich9":
|
||||
// do nothing
|
||||
default:
|
||||
errs = packersdk.MultiErrorAppend(
|
||||
errs, errors.New("chipset can only be piix3 or ich9"))
|
||||
}
|
||||
|
||||
if b.config.Firmware == "" {
|
||||
b.config.Firmware = "bios"
|
||||
}
|
||||
switch b.config.Firmware {
|
||||
case "bios", "efi":
|
||||
// do nothing
|
||||
default:
|
||||
errs = packersdk.MultiErrorAppend(
|
||||
errs, errors.New("firmware can only be bios or efi"))
|
||||
}
|
||||
|
||||
if b.config.DiskSize == 0 {
|
||||
b.config.DiskSize = 40000
|
||||
}
|
||||
@@ -168,6 +220,39 @@ func (b *Builder) Prepare(raws ...interface{}) ([]string, []string, error) {
|
||||
b.config.HardDriveInterface = "ide"
|
||||
}
|
||||
|
||||
if b.config.NICType == "" {
|
||||
b.config.NICType = "82540EM"
|
||||
}
|
||||
switch b.config.NICType {
|
||||
case "82540EM", "82543GC", "82545EM", "Am79C970A", "Am79C973", "Am79C960", "virtio":
|
||||
// do nothing
|
||||
default:
|
||||
errs = packersdk.MultiErrorAppend(
|
||||
errs, errors.New("NIC type can only be 82540EM, 82543GC, 82545EM, Am79C970A, Am79C973, Am79C960 or virtio"))
|
||||
}
|
||||
|
||||
if b.config.GfxController == "" {
|
||||
b.config.GfxController = "vboxvga"
|
||||
}
|
||||
switch b.config.GfxController {
|
||||
case "vboxvga", "vboxsvga", "vmsvga", "none":
|
||||
// do nothing
|
||||
default:
|
||||
errs = packersdk.MultiErrorAppend(
|
||||
errs, errors.New("Graphics controller type can only be vboxvga, vboxsvga, vmsvga, none"))
|
||||
}
|
||||
|
||||
if b.config.AudioController == "" {
|
||||
b.config.AudioController = "ac97"
|
||||
}
|
||||
switch b.config.AudioController {
|
||||
case "ac97", "hda", "sb16":
|
||||
// do nothing
|
||||
default:
|
||||
errs = packersdk.MultiErrorAppend(
|
||||
errs, errors.New("Audio controller type can only be ac97, hda or sb16"))
|
||||
}
|
||||
|
||||
if b.config.GuestOSType == "" {
|
||||
b.config.GuestOSType = "Other"
|
||||
}
|
||||
@@ -186,11 +271,11 @@ func (b *Builder) Prepare(raws ...interface{}) ([]string, []string, error) {
|
||||
}
|
||||
|
||||
switch b.config.HardDriveInterface {
|
||||
case "ide", "sata", "scsi", "pcie":
|
||||
case "ide", "sata", "scsi", "pcie", "virtio":
|
||||
// do nothing
|
||||
default:
|
||||
errs = packersdk.MultiErrorAppend(
|
||||
errs, errors.New("hard_drive_interface can only be ide, sata, pcie or scsi"))
|
||||
errs, errors.New("hard_drive_interface can only be ide, sata, pcie, scsi or virtio"))
|
||||
}
|
||||
|
||||
if b.config.SATAPortCount == 0 {
|
||||
@@ -211,9 +296,9 @@ func (b *Builder) Prepare(raws ...interface{}) ([]string, []string, error) {
|
||||
errs, errors.New("nvme_port_count cannot be greater than 255"))
|
||||
}
|
||||
|
||||
if b.config.ISOInterface != "ide" && b.config.ISOInterface != "sata" {
|
||||
if b.config.ISOInterface != "ide" && b.config.ISOInterface != "sata" && b.config.ISOInterface != "virtio" {
|
||||
errs = packersdk.MultiErrorAppend(
|
||||
errs, errors.New("iso_interface can only be ide or sata"))
|
||||
errs, errors.New("iso_interface can only be ide, sata or virtio"))
|
||||
}
|
||||
|
||||
// Warnings
|
||||
|
||||
@@ -117,7 +117,12 @@ type FlatConfig struct {
|
||||
GuestAdditionsPath *string `mapstructure:"guest_additions_path" cty:"guest_additions_path" hcl:"guest_additions_path"`
|
||||
GuestAdditionsSHA256 *string `mapstructure:"guest_additions_sha256" cty:"guest_additions_sha256" hcl:"guest_additions_sha256"`
|
||||
GuestAdditionsURL *string `mapstructure:"guest_additions_url" required:"false" cty:"guest_additions_url" hcl:"guest_additions_url"`
|
||||
Chipset *string `mapstructure:"chipset" required:"false" cty:"chipset" hcl:"chipset"`
|
||||
Firmware *string `mapstructure:"firmware" required:"false" cty:"firmware" hcl:"firmware"`
|
||||
DiskSize *uint `mapstructure:"disk_size" required:"false" cty:"disk_size" hcl:"disk_size"`
|
||||
NICType *string `mapstructure:"nic_type" required:"false" cty:"nic_type" hcl:"nic_type"`
|
||||
AudioController *string `mapstructure:"audio_controller" required:"false" cty:"audio_controller" hcl:"audio_controller"`
|
||||
GfxController *string `mapstructure:"gfx_controller" required:"false" cty:"gfx_controller" hcl:"gfx_controller"`
|
||||
GuestOSType *string `mapstructure:"guest_os_type" required:"false" cty:"guest_os_type" hcl:"guest_os_type"`
|
||||
HardDriveDiscard *bool `mapstructure:"hard_drive_discard" required:"false" cty:"hard_drive_discard" hcl:"hard_drive_discard"`
|
||||
HardDriveInterface *string `mapstructure:"hard_drive_interface" required:"false" cty:"hard_drive_interface" hcl:"hard_drive_interface"`
|
||||
@@ -249,7 +254,12 @@ func (*FlatConfig) HCL2Spec() map[string]hcldec.Spec {
|
||||
"guest_additions_path": &hcldec.AttrSpec{Name: "guest_additions_path", Type: cty.String, Required: false},
|
||||
"guest_additions_sha256": &hcldec.AttrSpec{Name: "guest_additions_sha256", Type: cty.String, Required: false},
|
||||
"guest_additions_url": &hcldec.AttrSpec{Name: "guest_additions_url", Type: cty.String, Required: false},
|
||||
"chipset": &hcldec.AttrSpec{Name: "chipset", Type: cty.String, Required: false},
|
||||
"firmware": &hcldec.AttrSpec{Name: "firmware", Type: cty.String, Required: false},
|
||||
"disk_size": &hcldec.AttrSpec{Name: "disk_size", Type: cty.Number, Required: false},
|
||||
"nic_type": &hcldec.AttrSpec{Name: "nic_type", Type: cty.String, Required: false},
|
||||
"audio_controller": &hcldec.AttrSpec{Name: "audio_controller", Type: cty.String, Required: false},
|
||||
"gfx_controller": &hcldec.AttrSpec{Name: "gfx_controller", Type: cty.String, Required: false},
|
||||
"guest_os_type": &hcldec.AttrSpec{Name: "guest_os_type", Type: cty.String, Required: false},
|
||||
"hard_drive_discard": &hcldec.AttrSpec{Name: "hard_drive_discard", Type: cty.Bool, Required: false},
|
||||
"hard_drive_interface": &hcldec.AttrSpec{Name: "hard_drive_interface", Type: cty.String, Required: false},
|
||||
|
||||
@@ -66,6 +66,18 @@ func (s *stepCreateDisk) Run(ctx context.Context, state multistep.StateBag) mult
|
||||
}
|
||||
}
|
||||
|
||||
// Add a VirtIO controller if we were asked to use VirtIO. We still attach
|
||||
// the VirtIO controller above because some other things (disks) require
|
||||
// that.
|
||||
if config.HardDriveInterface == "virtio" || config.ISOInterface == "virtio" {
|
||||
if err := driver.CreateVirtIOController(vmName, "VirtIO Controller"); err != nil {
|
||||
err := fmt.Errorf("Error creating disk controller: %s", err)
|
||||
state.Put("error", err)
|
||||
ui.Error(err.Error())
|
||||
return multistep.ActionHalt
|
||||
}
|
||||
}
|
||||
|
||||
if config.HardDriveInterface == "scsi" {
|
||||
if err := driver.CreateSCSIController(vmName, "SCSI Controller"); err != nil {
|
||||
err := fmt.Errorf("Error creating disk controller: %s", err)
|
||||
@@ -86,13 +98,11 @@ func (s *stepCreateDisk) Run(ctx context.Context, state multistep.StateBag) mult
|
||||
controllerName := "IDE Controller"
|
||||
if config.HardDriveInterface == "sata" {
|
||||
controllerName = "SATA Controller"
|
||||
}
|
||||
|
||||
if config.HardDriveInterface == "scsi" {
|
||||
} else if config.HardDriveInterface == "scsi" {
|
||||
controllerName = "SCSI Controller"
|
||||
}
|
||||
|
||||
if config.HardDriveInterface == "pcie" {
|
||||
} else if config.HardDriveInterface == "virtio" {
|
||||
controllerName = "VirtIO Controller"
|
||||
} else if config.HardDriveInterface == "pcie" {
|
||||
controllerName = "NVMe Controller"
|
||||
}
|
||||
|
||||
|
||||
@@ -26,7 +26,7 @@ func (s *stepCreateVM) Run(ctx context.Context, state multistep.StateBag) multis
|
||||
|
||||
name := config.VMName
|
||||
|
||||
commands := make([][]string, 6)
|
||||
commands := make([][]string, 10)
|
||||
commands[0] = []string{
|
||||
"createvm", "--name", name,
|
||||
"--ostype", config.GuestOSType, "--register",
|
||||
@@ -40,11 +40,27 @@ func (s *stepCreateVM) Run(ctx context.Context, state multistep.StateBag) multis
|
||||
commands[4] = []string{"modifyvm", name, "--usb", map[bool]string{true: "on", false: "off"}[config.HWConfig.USB]}
|
||||
|
||||
if strings.ToLower(config.HWConfig.Sound) == "none" {
|
||||
commands[5] = []string{"modifyvm", name, "--audio", config.HWConfig.Sound}
|
||||
commands[5] = []string{"modifyvm", name, "--audio", config.HWConfig.Sound,
|
||||
"--audiocontroller", config.AudioController}
|
||||
} else {
|
||||
commands[5] = []string{"modifyvm", name, "--audio", config.HWConfig.Sound, "--audioin", "on", "--audioout", "on"}
|
||||
commands[5] = []string{"modifyvm", name, "--audio", config.HWConfig.Sound, "--audioin", "on", "--audioout", "on",
|
||||
"--audiocontroller", config.AudioController}
|
||||
}
|
||||
|
||||
commands[6] = []string{"modifyvm", name, "--chipset", config.Chipset}
|
||||
commands[7] = []string{"modifyvm", name, "--firmware", config.Firmware}
|
||||
// Set the configured NIC type for all 8 possible NICs
|
||||
commands[8] = []string{"modifyvm", name,
|
||||
"--nictype1", config.NICType,
|
||||
"--nictype2", config.NICType,
|
||||
"--nictype3", config.NICType,
|
||||
"--nictype4", config.NICType,
|
||||
"--nictype5", config.NICType,
|
||||
"--nictype6", config.NICType,
|
||||
"--nictype7", config.NICType,
|
||||
"--nictype8", config.NICType}
|
||||
commands[9] = []string{"modifyvm", name, "--graphicscontroller", config.GfxController}
|
||||
|
||||
ui.Say("Creating virtual machine...")
|
||||
for _, command := range commands {
|
||||
err := driver.VBoxManage(command...)
|
||||
|
||||
@@ -23,7 +23,7 @@ type Driver interface {
|
||||
// Clone clones the VMX and the disk to the destination path. The
|
||||
// destination is a path to the VMX file. The disk will be copied
|
||||
// to that same directory.
|
||||
Clone(dst string, src string, cloneType bool) error
|
||||
Clone(dst string, src string, cloneType bool, snapshot string) error
|
||||
|
||||
// CompactDisk compacts a virtual disk.
|
||||
CompactDisk(string) error
|
||||
|
||||
@@ -112,7 +112,7 @@ func NewESX5Driver(dconfig *DriverConfig, config *SSHConfig, vmName string) (Dri
|
||||
}, nil
|
||||
}
|
||||
|
||||
func (d *ESX5Driver) Clone(dst, src string, linked bool) error {
|
||||
func (d *ESX5Driver) Clone(dst, src string, linked bool, snapshot string) error {
|
||||
|
||||
linesToArray := func(lines string) []string { return strings.Split(strings.Trim(lines, "\r\n"), "\n") }
|
||||
|
||||
|
||||
@@ -31,7 +31,7 @@ func NewFusion5Driver(dconfig *DriverConfig, config *SSHConfig) Driver {
|
||||
}
|
||||
}
|
||||
|
||||
func (d *Fusion5Driver) Clone(dst, src string, linked bool) error {
|
||||
func (d *Fusion5Driver) Clone(dst, src string, linked bool, snapshot string) error {
|
||||
return errors.New("Cloning is not supported with Fusion 5. Please use Fusion 6+.")
|
||||
}
|
||||
|
||||
|
||||
@@ -27,7 +27,7 @@ func NewFusion6Driver(dconfig *DriverConfig, config *SSHConfig) Driver {
|
||||
}
|
||||
}
|
||||
|
||||
func (d *Fusion6Driver) Clone(dst, src string, linked bool) error {
|
||||
func (d *Fusion6Driver) Clone(dst, src string, linked bool, snapshot string) error {
|
||||
|
||||
var cloneType string
|
||||
if linked {
|
||||
@@ -36,10 +36,11 @@ func (d *Fusion6Driver) Clone(dst, src string, linked bool) error {
|
||||
cloneType = "full"
|
||||
}
|
||||
|
||||
cmd := exec.Command(d.vmrunPath(),
|
||||
"-T", "fusion",
|
||||
"clone", src, dst,
|
||||
cloneType)
|
||||
args := []string{"-T", "fusion", "clone", src, dst, cloneType}
|
||||
if snapshot != "" {
|
||||
args = append(args, "-snapshot", snapshot)
|
||||
}
|
||||
cmd := exec.Command(d.vmrunPath(), args...)
|
||||
if _, _, err := runAndLog(cmd); err != nil {
|
||||
if strings.Contains(err.Error(), "parameters was invalid") {
|
||||
return fmt.Errorf(
|
||||
|
||||
@@ -14,6 +14,7 @@ type DriverMock struct {
|
||||
CloneDst string
|
||||
CloneSrc string
|
||||
Linked bool
|
||||
Snapshot string
|
||||
CloneErr error
|
||||
|
||||
CompactDiskCalled bool
|
||||
@@ -113,11 +114,12 @@ func (m NetworkMapperMock) DeviceIntoName(device string) (string, error) {
|
||||
return "", nil
|
||||
}
|
||||
|
||||
func (d *DriverMock) Clone(dst string, src string, linked bool) error {
|
||||
func (d *DriverMock) Clone(dst string, src string, linked bool, snapshot string) error {
|
||||
d.CloneCalled = true
|
||||
d.CloneDst = dst
|
||||
d.CloneSrc = src
|
||||
d.Linked = linked
|
||||
d.Snapshot = snapshot
|
||||
return d.CloneErr
|
||||
}
|
||||
|
||||
|
||||
@@ -31,7 +31,7 @@ func NewPlayer5Driver(config *SSHConfig) Driver {
|
||||
}
|
||||
}
|
||||
|
||||
func (d *Player5Driver) Clone(dst, src string, linked bool) error {
|
||||
func (d *Player5Driver) Clone(dst, src string, linked bool, snapshot string) error {
|
||||
return errors.New("Cloning is not supported with VMWare Player version 5. Please use VMWare Player version 6, or greater.")
|
||||
}
|
||||
|
||||
@@ -204,12 +204,32 @@ func (d *Player5Driver) Verify() error {
|
||||
|
||||
d.VmwareDriver.NetworkMapper = func() (NetworkNameMapper, error) {
|
||||
pathNetmap := playerNetmapConfPath()
|
||||
if _, err := os.Stat(pathNetmap); err != nil {
|
||||
return nil, fmt.Errorf("Could not find netmap conf file: %s", pathNetmap)
|
||||
}
|
||||
log.Printf("Located networkmapper configuration file using Player: %s", pathNetmap)
|
||||
|
||||
return ReadNetmapConfig(pathNetmap)
|
||||
// If we were able to find the file (no error), then we can proceed with reading
|
||||
// the networkmapper configuration.
|
||||
if _, err := os.Stat(pathNetmap); err == nil {
|
||||
log.Printf("Located networkmapper configuration file using Player: %s", pathNetmap)
|
||||
return ReadNetmapConfig(pathNetmap)
|
||||
}
|
||||
|
||||
// If we weren't able to find the networkmapper configuration file, then fall back
|
||||
// to the networking file which might also be in the configuration directory.
|
||||
libpath, _ := playerVMwareRoot()
|
||||
pathNetworking := filepath.Join(libpath, "networking")
|
||||
if _, err := os.Stat(pathNetworking); err != nil {
|
||||
return nil, fmt.Errorf("Could not determine network mappings from files in path: %s", libpath)
|
||||
}
|
||||
|
||||
// We were able to successfully stat the file.. So, now we can open a handle to it.
|
||||
log.Printf("Located networking configuration file using Player: %s", pathNetworking)
|
||||
fd, err := os.Open(pathNetworking)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
defer fd.Close()
|
||||
|
||||
// Then we pass the handle to the networking configuration parser.
|
||||
return ReadNetworkingConfig(fd)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
@@ -21,7 +21,7 @@ func NewPlayer6Driver(config *SSHConfig) Driver {
|
||||
}
|
||||
}
|
||||
|
||||
func (d *Player6Driver) Clone(dst, src string, linked bool) error {
|
||||
func (d *Player6Driver) Clone(dst, src string, linked bool, snapshot string) error {
|
||||
// TODO(rasa) check if running player+, not just player
|
||||
|
||||
var cloneType string
|
||||
@@ -31,11 +31,11 @@ func (d *Player6Driver) Clone(dst, src string, linked bool) error {
|
||||
cloneType = "full"
|
||||
}
|
||||
|
||||
cmd := exec.Command(d.Player5Driver.VmrunPath,
|
||||
"-T", "ws",
|
||||
"clone", src, dst,
|
||||
cloneType)
|
||||
|
||||
args := []string{"-T", "ws", "clone", src, dst, cloneType}
|
||||
if snapshot != "" {
|
||||
args = append(args, "-snapshot", snapshot)
|
||||
}
|
||||
cmd := exec.Command(d.Player5Driver.VmrunPath, args...)
|
||||
if _, _, err := runAndLog(cmd); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
@@ -21,7 +21,7 @@ func NewWorkstation10Driver(config *SSHConfig) Driver {
|
||||
}
|
||||
}
|
||||
|
||||
func (d *Workstation10Driver) Clone(dst, src string, linked bool) error {
|
||||
func (d *Workstation10Driver) Clone(dst, src string, linked bool, snapshot string) error {
|
||||
|
||||
var cloneType string
|
||||
if linked {
|
||||
@@ -30,11 +30,11 @@ func (d *Workstation10Driver) Clone(dst, src string, linked bool) error {
|
||||
cloneType = "full"
|
||||
}
|
||||
|
||||
cmd := exec.Command(d.Workstation9Driver.VmrunPath,
|
||||
"-T", "ws",
|
||||
"clone", src, dst,
|
||||
cloneType)
|
||||
|
||||
args := []string{"-T", "ws", "clone", src, dst, cloneType}
|
||||
if snapshot != "" {
|
||||
args = append(args, "-snapshot", snapshot)
|
||||
}
|
||||
cmd := exec.Command(d.Workstation9Driver.VmrunPath, args...)
|
||||
if _, _, err := runAndLog(cmd); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
@@ -30,7 +30,7 @@ func NewWorkstation9Driver(config *SSHConfig) Driver {
|
||||
}
|
||||
}
|
||||
|
||||
func (d *Workstation9Driver) Clone(dst, src string, linked bool) error {
|
||||
func (d *Workstation9Driver) Clone(dst, src string, linked bool, snapshot string) error {
|
||||
return errors.New("Cloning is not supported with VMware WS version 9. Please use VMware WS version 10, or greater.")
|
||||
}
|
||||
|
||||
@@ -165,12 +165,32 @@ func (d *Workstation9Driver) Verify() error {
|
||||
|
||||
d.VmwareDriver.NetworkMapper = func() (NetworkNameMapper, error) {
|
||||
pathNetmap := workstationNetmapConfPath()
|
||||
if _, err := os.Stat(pathNetmap); err != nil {
|
||||
return nil, fmt.Errorf("Could not find netmap conf file: %s", pathNetmap)
|
||||
}
|
||||
log.Printf("Located networkmapper configuration file using Workstation: %s", pathNetmap)
|
||||
|
||||
return ReadNetmapConfig(pathNetmap)
|
||||
// Check that the file for the networkmapper configuration exists. If there's no
|
||||
// error, then the file exists and we can proceed to read the configuration out of it.
|
||||
if _, err := os.Stat(pathNetmap); err == nil {
|
||||
log.Printf("Located networkmapper configuration file using Workstation: %s", pathNetmap)
|
||||
return ReadNetmapConfig(pathNetmap)
|
||||
}
|
||||
|
||||
// If we weren't able to find the networkmapper configuration file, then fall back
|
||||
// to the networking file which might also be in the configuration directory.
|
||||
libpath, _ := workstationVMwareRoot()
|
||||
pathNetworking := filepath.Join(libpath, "networking")
|
||||
if _, err := os.Stat(pathNetworking); err != nil {
|
||||
return nil, fmt.Errorf("Could not determine network mappings from files in path: %s", libpath)
|
||||
}
|
||||
|
||||
// We were able to successfully stat the file.. So, now we can open a handle to it.
|
||||
log.Printf("Located networking configuration file using Workstation: %s", pathNetworking)
|
||||
fd, err := os.Open(pathNetworking)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
defer fd.Close()
|
||||
|
||||
// Then we pass the handle to the networking configuration parser.
|
||||
return ReadNetworkingConfig(fd)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
@@ -97,6 +97,7 @@ func (b *Builder) Run(ctx context.Context, ui packersdk.Ui, hook packersdk.Hook)
|
||||
OutputDir: &b.config.OutputDir,
|
||||
VMName: b.config.VMName,
|
||||
Linked: b.config.Linked,
|
||||
Snapshot: b.config.AttachSnapshot,
|
||||
},
|
||||
&vmwcommon.StepConfigureVMX{
|
||||
CustomData: b.config.VMXData,
|
||||
|
||||
@@ -47,6 +47,11 @@ type Config struct {
|
||||
// scenarios. Most users will wish to create a full clone instead. Defaults
|
||||
// to false.
|
||||
Linked bool `mapstructure:"linked" required:"false"`
|
||||
// Default to `null/empty`. The name of an
|
||||
// **existing** snapshot to which the builder shall attach the VM before
|
||||
// starting it. If no snapshot is specified the builder will simply start the
|
||||
// VM from it's current state i.e. snapshot.
|
||||
AttachSnapshot string `mapstructure:"attach_snapshot" required:"false"`
|
||||
// Path to the source VMX file to clone. If
|
||||
// remote_type is enabled then this specifies a path on the remote_host.
|
||||
SourcePath string `mapstructure:"source_path" required:"true"`
|
||||
|
||||
@@ -123,6 +123,7 @@ type FlatConfig struct {
|
||||
DiskName *string `mapstructure:"vmdk_name" required:"false" cty:"vmdk_name" hcl:"vmdk_name"`
|
||||
DiskTypeId *string `mapstructure:"disk_type_id" required:"false" cty:"disk_type_id" hcl:"disk_type_id"`
|
||||
Linked *bool `mapstructure:"linked" required:"false" cty:"linked" hcl:"linked"`
|
||||
AttachSnapshot *string `mapstructure:"attach_snapshot" required:"false" cty:"attach_snapshot" hcl:"attach_snapshot"`
|
||||
SourcePath *string `mapstructure:"source_path" required:"true" cty:"source_path" hcl:"source_path"`
|
||||
VMName *string `mapstructure:"vm_name" required:"false" cty:"vm_name" hcl:"vm_name"`
|
||||
}
|
||||
@@ -252,6 +253,7 @@ func (*FlatConfig) HCL2Spec() map[string]hcldec.Spec {
|
||||
"vmdk_name": &hcldec.AttrSpec{Name: "vmdk_name", Type: cty.String, Required: false},
|
||||
"disk_type_id": &hcldec.AttrSpec{Name: "disk_type_id", Type: cty.String, Required: false},
|
||||
"linked": &hcldec.AttrSpec{Name: "linked", Type: cty.Bool, Required: false},
|
||||
"attach_snapshot": &hcldec.AttrSpec{Name: "attach_snapshot", Type: cty.String, Required: false},
|
||||
"source_path": &hcldec.AttrSpec{Name: "source_path", Type: cty.String, Required: false},
|
||||
"vm_name": &hcldec.AttrSpec{Name: "vm_name", Type: cty.String, Required: false},
|
||||
}
|
||||
|
||||
@@ -20,6 +20,7 @@ type StepCloneVMX struct {
|
||||
Path string
|
||||
VMName string
|
||||
Linked bool
|
||||
Snapshot string
|
||||
tempDir string
|
||||
}
|
||||
|
||||
@@ -38,7 +39,7 @@ func (s *StepCloneVMX) Run(ctx context.Context, state multistep.StateBag) multis
|
||||
log.Printf("Cloning from: %s", s.Path)
|
||||
log.Printf("Cloning to: %s", vmxPath)
|
||||
|
||||
if err := driver.Clone(vmxPath, s.Path, s.Linked); err != nil {
|
||||
if err := driver.Clone(vmxPath, s.Path, s.Linked, s.Snapshot); err != nil {
|
||||
return halt(err)
|
||||
}
|
||||
|
||||
|
||||
@@ -5,7 +5,7 @@ package common
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"os"
|
||||
"io/fs"
|
||||
"path/filepath"
|
||||
|
||||
"github.com/hashicorp/packer-plugin-sdk/common"
|
||||
@@ -27,7 +27,7 @@ type OutputConfig struct {
|
||||
// leading zero such as "0755" in JSON file, because JSON does not support
|
||||
// octal value. In Unix-like OS, the actual permission may differ from
|
||||
// this value because of umask.
|
||||
DirPerm os.FileMode `mapstructure:"directory_permission" required:"false"`
|
||||
DirPerm fs.FileMode `mapstructure:"directory_permission" required:"false"`
|
||||
}
|
||||
|
||||
func (c *OutputConfig) Prepare(ctx *interpolate.Context, pc *common.PackerConfig) []error {
|
||||
@@ -45,7 +45,7 @@ func (c *OutputConfig) Prepare(ctx *interpolate.Context, pc *common.PackerConfig
|
||||
func (c *OutputConfig) ListFiles() ([]string, error) {
|
||||
files := make([]string, 0, 10)
|
||||
|
||||
visit := func(path string, info os.FileInfo, err error) error {
|
||||
visit := func(path string, info fs.FileInfo, err error) error {
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
@@ -3,7 +3,7 @@
|
||||
package common
|
||||
|
||||
import (
|
||||
"os"
|
||||
"io/fs"
|
||||
|
||||
"github.com/hashicorp/hcl/v2/hcldec"
|
||||
"github.com/zclconf/go-cty/cty"
|
||||
@@ -13,7 +13,7 @@ import (
|
||||
// Where the contents of a field with a `mapstructure:,squash` tag are bubbled up.
|
||||
type FlatOutputConfig struct {
|
||||
OutputDir *string `mapstructure:"output_directory" required:"false" cty:"output_directory" hcl:"output_directory"`
|
||||
DirPerm *os.FileMode `mapstructure:"directory_permission" required:"false" cty:"directory_permission" hcl:"directory_permission"`
|
||||
DirPerm *fs.FileMode `mapstructure:"directory_permission" required:"false" cty:"directory_permission" hcl:"directory_permission"`
|
||||
}
|
||||
|
||||
// FlatMapstructure returns a new FlatOutputConfig.
|
||||
|
||||
@@ -3,7 +3,7 @@
|
||||
package common
|
||||
|
||||
import (
|
||||
"os"
|
||||
"io/fs"
|
||||
|
||||
"github.com/hashicorp/hcl/v2/hcldec"
|
||||
"github.com/zclconf/go-cty/cty"
|
||||
@@ -17,7 +17,7 @@ type FlatExportConfig struct {
|
||||
Images *bool `mapstructure:"images" cty:"images" hcl:"images"`
|
||||
Manifest *string `mapstructure:"manifest" cty:"manifest" hcl:"manifest"`
|
||||
OutputDir *string `mapstructure:"output_directory" required:"false" cty:"output_directory" hcl:"output_directory"`
|
||||
DirPerm *os.FileMode `mapstructure:"directory_permission" required:"false" cty:"directory_permission" hcl:"directory_permission"`
|
||||
DirPerm *fs.FileMode `mapstructure:"directory_permission" required:"false" cty:"directory_permission" hcl:"directory_permission"`
|
||||
Options []string `mapstructure:"options" cty:"options" hcl:"options"`
|
||||
}
|
||||
|
||||
|
||||
@@ -109,7 +109,7 @@ func checkPluginName(name string) error {
|
||||
strings.HasPrefix(name, "packer-post-processor-") {
|
||||
fmt.Printf("\n[WARNING] Plugin is named with old prefix `packer-[builder|provisioner|post-processor]-{name})`. " +
|
||||
"These will be detected but Packer cannot install them automatically. " +
|
||||
"The plugin must be a multi-plugin named packer-plugin-{name} to be installable through the `packer init` command.\n")
|
||||
"The plugin must be a multi-component plugin named packer-plugin-{name} to be installable through the `packer init` command.\n")
|
||||
return nil
|
||||
}
|
||||
return fmt.Errorf("plugin's name is not valid")
|
||||
|
||||
+5
-1
@@ -126,7 +126,11 @@ func (m *Meta) GetConfigFromJSON(cla *MetaArgs) (packer.Handler, int) {
|
||||
}
|
||||
|
||||
if err != nil {
|
||||
m.Ui.Error(fmt.Sprintf("Failed to parse template: %s", err))
|
||||
m.Ui.Error(fmt.Sprintf("Failed to parse file as legacy JSON template: "+
|
||||
"if you are using an HCL template, check your file extensions; they "+
|
||||
"should be either *.pkr.hcl or *.pkr.json; see the docs for more "+
|
||||
"details: https://www.packer.io/docs/templates/hcl_templates. \n"+
|
||||
"Original error: %s", err))
|
||||
return nil, 1
|
||||
}
|
||||
|
||||
|
||||
+3
-1
@@ -145,6 +145,7 @@ type InspectArgs struct {
|
||||
|
||||
func (va *HCL2UpgradeArgs) AddFlagSets(flags *flag.FlagSet) {
|
||||
flags.StringVar(&va.OutputFile, "output-file", "", "File where to put the hcl2 generated config. Defaults to JSON_TEMPLATE.pkr.hcl")
|
||||
flags.BoolVar(&va.WithAnnotations, "with-annotations", false, "Adds helper annotations with information about the generated HCL2 blocks.")
|
||||
|
||||
va.MetaArgs.AddFlagSets(flags)
|
||||
}
|
||||
@@ -152,7 +153,8 @@ func (va *HCL2UpgradeArgs) AddFlagSets(flags *flag.FlagSet) {
|
||||
// HCL2UpgradeArgs represents a parsed cli line for a `packer hcl2_upgrade`
|
||||
type HCL2UpgradeArgs struct {
|
||||
MetaArgs
|
||||
OutputFile string
|
||||
OutputFile string
|
||||
WithAnnotations bool
|
||||
}
|
||||
|
||||
func (va *FormatArgs) AddFlagSets(flags *flag.FlagSet) {
|
||||
|
||||
@@ -0,0 +1,7 @@
|
||||
// +build darwin freebsd linux netbsd openbsd solaris
|
||||
|
||||
package command
|
||||
|
||||
const (
|
||||
defaultConfigDir = ".packer.d"
|
||||
)
|
||||
@@ -0,0 +1,7 @@
|
||||
// +build windows
|
||||
|
||||
package command
|
||||
|
||||
const (
|
||||
defaultConfigDir = "packer.d"
|
||||
)
|
||||
+772
-216
File diff suppressed because it is too large
Load Diff
@@ -18,16 +18,29 @@ func Test_hcl2_upgrade(t *testing.T) {
|
||||
|
||||
tc := []struct {
|
||||
folder string
|
||||
flags []string
|
||||
}{
|
||||
{"hcl2_upgrade_basic"},
|
||||
{folder: "complete", flags: []string{"-with-annotations"}},
|
||||
{folder: "without-annotations", flags: []string{}},
|
||||
{folder: "minimal", flags: []string{"-with-annotations"}},
|
||||
{folder: "source-name", flags: []string{"-with-annotations"}},
|
||||
{folder: "error-cleanup-provisioner", flags: []string{"-with-annotations"}},
|
||||
{folder: "aws-access-config", flags: []string{}},
|
||||
{folder: "variables-only", flags: []string{}},
|
||||
{folder: "variables-with-variables", flags: []string{}},
|
||||
}
|
||||
|
||||
for _, tc := range tc {
|
||||
t.Run(tc.folder, func(t *testing.T) {
|
||||
inputPath := filepath.Join(testFixture(tc.folder, "input.json"))
|
||||
inputPath := filepath.Join(testFixture("hcl2_upgrade", tc.folder, "input.json"))
|
||||
outputPath := inputPath + ".pkr.hcl"
|
||||
expectedPath := filepath.Join(testFixture(tc.folder, "expected.pkr.hcl"))
|
||||
p := helperCommand(t, "hcl2_upgrade", inputPath)
|
||||
expectedPath := filepath.Join(testFixture("hcl2_upgrade", tc.folder, "expected.pkr.hcl"))
|
||||
args := []string{"hcl2_upgrade"}
|
||||
if len(tc.flags) > 0 {
|
||||
args = append(args, tc.flags...)
|
||||
}
|
||||
args = append(args, inputPath)
|
||||
p := helperCommand(t, args...)
|
||||
bs, err := p.CombinedOutput()
|
||||
if err != nil {
|
||||
t.Fatalf("%v %s", err, bs)
|
||||
|
||||
+2
-2
@@ -109,7 +109,7 @@ func (c *InitCommand) RunContext(buildCtx context.Context, cla *InitArgs) int {
|
||||
return 1
|
||||
}
|
||||
|
||||
log.Printf("[TRACE] for plugin %s found %d matching installation(s)", pluginRequirement.Identifier.ForDisplay(), len(installs))
|
||||
log.Printf("[TRACE] for plugin %s found %d matching installation(s)", pluginRequirement.Identifier, len(installs))
|
||||
|
||||
if len(installs) > 0 && cla.Upgrade == false {
|
||||
continue
|
||||
@@ -125,7 +125,7 @@ func (c *InitCommand) RunContext(buildCtx context.Context, cla *InitArgs) int {
|
||||
ret = 1
|
||||
}
|
||||
if newInstall != nil {
|
||||
msg := fmt.Sprintf("Installed plugin %s %s in %q", pluginRequirement.Identifier.ForDisplay(), newInstall.Version, newInstall.BinaryPath)
|
||||
msg := fmt.Sprintf("Installed plugin %s %s in %q", pluginRequirement.Identifier, newInstall.Version, newInstall.BinaryPath)
|
||||
ui.Say(msg)
|
||||
}
|
||||
}
|
||||
|
||||
+272
-73
@@ -1,7 +1,8 @@
|
||||
package command
|
||||
|
||||
import (
|
||||
"io/ioutil"
|
||||
"context"
|
||||
"fmt"
|
||||
"log"
|
||||
"os"
|
||||
"path/filepath"
|
||||
@@ -10,39 +11,60 @@ import (
|
||||
"testing"
|
||||
|
||||
"github.com/google/go-cmp/cmp"
|
||||
"github.com/hashicorp/go-getter/v2"
|
||||
"github.com/hashicorp/packer-plugin-sdk/acctest"
|
||||
"golang.org/x/mod/sumdb/dirhash"
|
||||
)
|
||||
|
||||
type testCaseInit struct {
|
||||
name string
|
||||
setup []func(*testing.T, testCaseInit)
|
||||
Meta Meta
|
||||
inPluginFolder map[string]string
|
||||
expectedPackerConfigDirHashBeforeInit string
|
||||
inConfigFolder map[string]string
|
||||
packerConfigDir string
|
||||
packerUserFolder string
|
||||
want int
|
||||
dirFiles []string
|
||||
expectedPackerConfigDirHashAfterInit string
|
||||
moreTests []func(*testing.T, testCaseInit)
|
||||
}
|
||||
|
||||
type testBuild struct {
|
||||
want int
|
||||
}
|
||||
|
||||
func (tb testBuild) fn(t *testing.T, tc testCaseInit) {
|
||||
bc := BuildCommand{
|
||||
Meta: tc.Meta,
|
||||
}
|
||||
|
||||
args := []string{tc.packerUserFolder}
|
||||
want := tb.want
|
||||
if got := bc.Run(args); got != want {
|
||||
t.Errorf("BuildCommand.Run() = %v, want %v", got, want)
|
||||
}
|
||||
}
|
||||
|
||||
func TestInitCommand_Run(t *testing.T) {
|
||||
// These tests will try to optimise for doing the least amount of github api
|
||||
// requests whilst testing the max amount of things at once. Hopefully they
|
||||
// don't require a GH token just yet. Acc tests are run on linux, darwin and
|
||||
// windows, so requests are done 3 times.
|
||||
|
||||
type testCase struct {
|
||||
checkSkip func(*testing.T)
|
||||
name string
|
||||
inPluginFolder map[string]string
|
||||
expectedPackerConfigDirHashBeforeInit string
|
||||
hclFile string
|
||||
packerConfigDir string
|
||||
want int
|
||||
dirFiles []string
|
||||
expectedPackerConfigDirHashAfterInit string
|
||||
}
|
||||
|
||||
cfg := &configDirSingleton{map[string]string{}}
|
||||
|
||||
tests := []testCase{
|
||||
tests := []testCaseInit{
|
||||
{
|
||||
nil,
|
||||
// here we pre-write plugins with valid checksums, Packer will
|
||||
// see those as valid installations it did.
|
||||
// the directory hash before and after init should be the same,
|
||||
// that's a no-op. This also should do no GH query, so it is best
|
||||
// to always run it.
|
||||
"already-installed-no-op",
|
||||
nil,
|
||||
testMetaFile(t),
|
||||
map[string]string{
|
||||
"github.com/sylviamoss/comment/packer-plugin-comment_v0.2.18_x5.0_darwin_amd64": "1",
|
||||
"github.com/sylviamoss/comment/packer-plugin-comment_v0.2.18_x5.0_darwin_amd64_SHA256SUM": "6b86b273ff34fce19d6b804eff5a3f5747ada4eaa22f1d49c01e52ddb7875b4b",
|
||||
@@ -52,30 +74,37 @@ func TestInitCommand_Run(t *testing.T) {
|
||||
"github.com/sylviamoss/comment/packer-plugin-comment_v0.2.18_x5.0_linux_amd64_SHA256SUM": "59031c50e0dfeedfde2b4e9445754804dce3f29e4efa737eead0ca9b4f5b85a5",
|
||||
},
|
||||
"h1:Q5qyAOdD43hL3CquQdVfaHpOYGf0UsZ/+wVA9Ry6cbA=",
|
||||
`# cfg.pkr.hcl
|
||||
packer {
|
||||
required_plugins {
|
||||
comment = {
|
||||
source = "github.com/sylviamoss/comment"
|
||||
version = "v0.2.018"
|
||||
}
|
||||
}
|
||||
}`,
|
||||
cfg.dir("1"),
|
||||
map[string]string{
|
||||
`cfg.pkr.hcl`: `
|
||||
packer {
|
||||
required_plugins {
|
||||
comment = {
|
||||
source = "github.com/sylviamoss/comment"
|
||||
version = "v0.2.018"
|
||||
}
|
||||
}
|
||||
}`,
|
||||
},
|
||||
cfg.dir("1_pkr_config"),
|
||||
cfg.dir("1_pkr_user_folder"),
|
||||
0,
|
||||
nil,
|
||||
"h1:Q5qyAOdD43hL3CquQdVfaHpOYGf0UsZ/+wVA9Ry6cbA=",
|
||||
[]func(t *testing.T, tc testCaseInit){
|
||||
// test that a build will not work since plugins are broken for
|
||||
// this tests (they are not binaries).
|
||||
testBuild{want: 1}.fn,
|
||||
},
|
||||
},
|
||||
{
|
||||
func(t *testing.T) {
|
||||
if os.Getenv(acctest.TestEnvVar) == "" {
|
||||
t.Skipf("Acceptance test skipped unless env '%s' set", acctest.TestEnvVar)
|
||||
}
|
||||
},
|
||||
// here we pre-write plugins with valid checksums, Packer will
|
||||
// see those as valid installations it did.
|
||||
// But because we require version 0.2.19, we will upgrade.
|
||||
"already-installed-upgrade",
|
||||
[]func(t *testing.T, tc testCaseInit){
|
||||
skipInitTestUnlessEnVar(acctest.TestEnvVar).fn,
|
||||
},
|
||||
testMetaFile(t),
|
||||
map[string]string{
|
||||
"github.com/sylviamoss/comment/packer-plugin-comment_v0.2.18_x5.0_darwin_amd64": "1",
|
||||
"github.com/sylviamoss/comment/packer-plugin-comment_v0.2.18_x5.0_darwin_amd64_SHA256SUM": "6b86b273ff34fce19d6b804eff5a3f5747ada4eaa22f1d49c01e52ddb7875b4b",
|
||||
@@ -85,16 +114,34 @@ func TestInitCommand_Run(t *testing.T) {
|
||||
"github.com/sylviamoss/comment/packer-plugin-comment_v0.2.18_x5.0_linux_amd64_SHA256SUM": "59031c50e0dfeedfde2b4e9445754804dce3f29e4efa737eead0ca9b4f5b85a5",
|
||||
},
|
||||
"h1:Q5qyAOdD43hL3CquQdVfaHpOYGf0UsZ/+wVA9Ry6cbA=",
|
||||
`# cfg.pkr.hcl
|
||||
packer {
|
||||
required_plugins {
|
||||
comment = {
|
||||
source = "github.com/sylviamoss/comment"
|
||||
version = "v0.2.019"
|
||||
map[string]string{
|
||||
`cfg.pkr.hcl`: `
|
||||
packer {
|
||||
required_plugins {
|
||||
comment = {
|
||||
source = "github.com/sylviamoss/comment"
|
||||
version = "v0.2.019"
|
||||
}
|
||||
}
|
||||
}`,
|
||||
`source.pkr.hcl`: `
|
||||
source "null" "test" {
|
||||
communicator = "none"
|
||||
}
|
||||
`,
|
||||
`build.pkr.hcl`: `
|
||||
build {
|
||||
sources = ["source.null.test"]
|
||||
provisioner "comment" {
|
||||
comment = "Begin ¡"
|
||||
ui = true
|
||||
bubble_text = true
|
||||
}
|
||||
}
|
||||
}`,
|
||||
cfg.dir("2"),
|
||||
`,
|
||||
},
|
||||
cfg.dir("2_pkr_config"),
|
||||
cfg.dir("2_pkr_user_folder"),
|
||||
0,
|
||||
[]string{
|
||||
"github.com/sylviamoss/comment/packer-plugin-comment_v0.2.18_x5.0_darwin_amd64",
|
||||
@@ -119,68 +166,194 @@ func TestInitCommand_Run(t *testing.T) {
|
||||
"linux": "h1:CGym0+Nd0LEANgzqL0wx/LDjRL8bYwlpZ0HajPJo/hs=",
|
||||
"windows": "h1:ag0/C1YjP7KoEDYOiJHE0K+lhFgs0tVgjriWCXVT1fg=",
|
||||
}[runtime.GOOS],
|
||||
[]func(t *testing.T, tc testCaseInit){
|
||||
// test that a build will work as the plugin was just installed
|
||||
testBuild{want: 0}.fn,
|
||||
},
|
||||
},
|
||||
{
|
||||
func(t *testing.T) {
|
||||
if os.Getenv(acctest.TestEnvVar) == "" {
|
||||
t.Skipf("Acceptance test skipped unless env '%s' set", acctest.TestEnvVar)
|
||||
}
|
||||
},
|
||||
"release-with-no-binary",
|
||||
[]func(t *testing.T, tc testCaseInit){
|
||||
skipInitTestUnlessEnVar(acctest.TestEnvVar).fn,
|
||||
},
|
||||
testMetaFile(t),
|
||||
nil,
|
||||
"h1:47DEQpj8HBSa+/TImW+5JCeuQeRkm5NMpJWZG3hSuFU=",
|
||||
`# cfg.pkr.hcl
|
||||
packer {
|
||||
required_plugins {
|
||||
comment = {
|
||||
source = "github.com/sylviamoss/comment"
|
||||
version = "v0.2.20"
|
||||
}
|
||||
}
|
||||
}`,
|
||||
cfg.dir("3"),
|
||||
map[string]string{
|
||||
`cfg.pkr.hcl`: `
|
||||
packer {
|
||||
required_plugins {
|
||||
comment = {
|
||||
source = "github.com/sylviamoss/comment"
|
||||
version = "v0.2.20"
|
||||
}
|
||||
}
|
||||
}`,
|
||||
},
|
||||
cfg.dir("3_pkr_config"),
|
||||
cfg.dir("3_pkr_user_folder"),
|
||||
1,
|
||||
nil,
|
||||
"h1:47DEQpj8HBSa+/TImW+5JCeuQeRkm5NMpJWZG3hSuFU=",
|
||||
nil,
|
||||
},
|
||||
{
|
||||
"manually-installed-single-component-plugin-works",
|
||||
[]func(t *testing.T, tc testCaseInit){
|
||||
skipInitTestUnlessEnVar(acctest.TestEnvVar).fn,
|
||||
initTestGoGetPlugin{
|
||||
Src: "https://github.com/azr/packer-provisioner-comment/releases/download/v1.0.0/" +
|
||||
"packer-provisioner-comment_v1.0.0_" + runtime.GOOS + "_" + runtime.GOARCH + ".zip",
|
||||
Dst: filepath.Join(cfg.dir("4_pkr_config"), defaultConfigDir, "plugins"),
|
||||
}.fn,
|
||||
},
|
||||
testMetaFile(t),
|
||||
nil,
|
||||
map[string]string{
|
||||
"darwin": "h1:nVebbXToeehPUASRbvV9M4qaA9+UgoR5AMp7LjTrSBk=",
|
||||
"linux": "h1:/U5vdeMtOpRKNu0ld8+qf4t6WC+BsfCQ6JRo9Dh/khI=",
|
||||
"windows": "h1:0nkdNCjtTHTgBNkzVKG++/VYmWAvq/o236GGTxrIf/Q=",
|
||||
}[runtime.GOOS],
|
||||
map[string]string{
|
||||
`source.pkr.hcl`: `
|
||||
source "null" "test" {
|
||||
communicator = "none"
|
||||
}
|
||||
`,
|
||||
`build.pkr.hcl`: `
|
||||
build {
|
||||
sources = ["source.null.test"]
|
||||
provisioner "comment" {
|
||||
comment = "Begin ¡"
|
||||
ui = true
|
||||
bubble_text = true
|
||||
}
|
||||
}
|
||||
`,
|
||||
},
|
||||
cfg.dir("4_pkr_config"),
|
||||
cfg.dir("4_pkr_user_folder"),
|
||||
0,
|
||||
nil,
|
||||
map[string]string{
|
||||
"darwin": "h1:nVebbXToeehPUASRbvV9M4qaA9+UgoR5AMp7LjTrSBk=",
|
||||
"linux": "h1:/U5vdeMtOpRKNu0ld8+qf4t6WC+BsfCQ6JRo9Dh/khI=",
|
||||
"windows": "h1:0nkdNCjtTHTgBNkzVKG++/VYmWAvq/o236GGTxrIf/Q=",
|
||||
}[runtime.GOOS],
|
||||
[]func(*testing.T, testCaseInit){
|
||||
testBuild{want: 0}.fn,
|
||||
},
|
||||
},
|
||||
{
|
||||
"manually-installed-single-component-plugin-old-api-fails",
|
||||
[]func(t *testing.T, tc testCaseInit){
|
||||
skipInitTestUnlessEnVar(acctest.TestEnvVar).fn,
|
||||
initTestGoGetPlugin{
|
||||
Src: "https://github.com/azr/packer-provisioner-comment/releases/download/v0.0.0/" +
|
||||
"packer-provisioner-comment_v0.0.0_" + runtime.GOOS + "_" + runtime.GOARCH + ".zip",
|
||||
Dst: filepath.Join(cfg.dir("5_pkr_config"), defaultConfigDir, "plugins"),
|
||||
}.fn,
|
||||
},
|
||||
testMetaFile(t),
|
||||
nil,
|
||||
map[string]string{
|
||||
"darwin": "h1:gW4gzpDXeu3cDrXgHJj9iWAN7Pyak626Gq8Bu2LG1kY=",
|
||||
"linux": "h1:wQ2H5+J7VXwQzqR9DgpWtjhw9OVEFbcKQL6dgm/+zwo=",
|
||||
"windows": "h1:BqRdW3c5H1PZ2Q4DOaKWja21v3nDlY5Nn8kqahhHGSw=",
|
||||
}[runtime.GOOS],
|
||||
map[string]string{
|
||||
`source.pkr.hcl`: `
|
||||
source "null" "test" {
|
||||
communicator = "none"
|
||||
}
|
||||
`,
|
||||
`build.pkr.hcl`: `
|
||||
build {
|
||||
sources = ["source.null.test"]
|
||||
provisioner "comment" {
|
||||
comment = "Begin ¡"
|
||||
ui = true
|
||||
bubble_text = true
|
||||
}
|
||||
}
|
||||
`,
|
||||
},
|
||||
cfg.dir("5_pkr_config"),
|
||||
cfg.dir("5_pkr_user_folder"),
|
||||
0,
|
||||
nil,
|
||||
map[string]string{
|
||||
"darwin": "h1:gW4gzpDXeu3cDrXgHJj9iWAN7Pyak626Gq8Bu2LG1kY=",
|
||||
"linux": "h1:wQ2H5+J7VXwQzqR9DgpWtjhw9OVEFbcKQL6dgm/+zwo=",
|
||||
"windows": "h1:BqRdW3c5H1PZ2Q4DOaKWja21v3nDlY5Nn8kqahhHGSw=",
|
||||
}[runtime.GOOS],
|
||||
[]func(*testing.T, testCaseInit){
|
||||
testBuild{want: 1}.fn,
|
||||
},
|
||||
},
|
||||
{
|
||||
"unsupported-non-github-source-address",
|
||||
[]func(t *testing.T, tc testCaseInit){
|
||||
skipInitTestUnlessEnVar(acctest.TestEnvVar).fn,
|
||||
},
|
||||
testMetaFile(t),
|
||||
nil,
|
||||
"h1:47DEQpj8HBSa+/TImW+5JCeuQeRkm5NMpJWZG3hSuFU=",
|
||||
map[string]string{
|
||||
`cfg.pkr.hcl`: `
|
||||
packer {
|
||||
required_plugins {
|
||||
comment = {
|
||||
source = "example.com/sylviamoss/comment"
|
||||
version = "v0.2.19"
|
||||
}
|
||||
}
|
||||
}`,
|
||||
},
|
||||
cfg.dir("6_pkr_config"),
|
||||
cfg.dir("6_pkr_user_folder"),
|
||||
1,
|
||||
nil,
|
||||
"h1:47DEQpj8HBSa+/TImW+5JCeuQeRkm5NMpJWZG3hSuFU=",
|
||||
nil,
|
||||
},
|
||||
}
|
||||
for _, tt := range tests {
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
if tt.checkSkip != nil {
|
||||
tt.checkSkip(t)
|
||||
log.Printf("starting %s", tt.name)
|
||||
log.Printf("%#v", tt)
|
||||
t.Cleanup(func() {
|
||||
_ = os.RemoveAll(tt.packerConfigDir)
|
||||
})
|
||||
t.Cleanup(func() {
|
||||
_ = os.RemoveAll(tt.packerUserFolder)
|
||||
})
|
||||
os.Setenv("PACKER_CONFIG_DIR", tt.packerConfigDir)
|
||||
for _, init := range tt.setup {
|
||||
init(t, tt)
|
||||
if t.Skipped() {
|
||||
return
|
||||
}
|
||||
}
|
||||
log.Printf("starting %s", tt.name)
|
||||
createFiles(tt.packerConfigDir, tt.inPluginFolder)
|
||||
t.Cleanup(func() {
|
||||
_ = os.RemoveAll(tt.packerConfigDir)
|
||||
})
|
||||
createFiles(tt.packerUserFolder, tt.inConfigFolder)
|
||||
|
||||
hash, err := dirhash.HashDir(tt.packerConfigDir, "", dirhash.DefaultHash)
|
||||
if err != nil {
|
||||
t.Fatalf("HashDir: %v", err)
|
||||
}
|
||||
if diff := cmp.Diff(tt.expectedPackerConfigDirHashBeforeInit, hash); diff != "" {
|
||||
t.Errorf("unexpected dir hash before init: %s", diff)
|
||||
t.Errorf("unexpected dir hash before init: +found -expected %s", diff)
|
||||
}
|
||||
|
||||
cfgDir, err := ioutil.TempDir("", "pkr-test-init-file-folder")
|
||||
if err != nil {
|
||||
t.Fatalf("TempDir: %v", err)
|
||||
}
|
||||
if err := ioutil.WriteFile(filepath.Join(cfgDir, "cfg.pkr.hcl"), []byte(tt.hclFile), 0666); err != nil {
|
||||
t.Fatalf("WriteFile: %v", err)
|
||||
}
|
||||
t.Cleanup(func() {
|
||||
_ = os.RemoveAll(cfgDir)
|
||||
})
|
||||
|
||||
args := []string{cfgDir}
|
||||
args := []string{tt.packerUserFolder}
|
||||
|
||||
c := &InitCommand{
|
||||
Meta: testMetaFile(t),
|
||||
Meta: tt.Meta,
|
||||
}
|
||||
|
||||
if err := c.CoreConfig.Components.PluginConfig.Discover(); err != nil {
|
||||
t.Fatalf("Failed to discover plugins: %s", err)
|
||||
}
|
||||
|
||||
c.CoreConfig.Components.PluginConfig.KnownPluginFolders = []string{tt.packerConfigDir}
|
||||
@@ -207,6 +380,32 @@ func TestInitCommand_Run(t *testing.T) {
|
||||
if diff := cmp.Diff(tt.expectedPackerConfigDirHashAfterInit, hash); diff != "" {
|
||||
t.Errorf("unexpected dir hash after init: %s", diff)
|
||||
}
|
||||
|
||||
for i, subTest := range tt.moreTests {
|
||||
t.Run(fmt.Sprintf("-subtest-%d", i), func(t *testing.T) {
|
||||
subTest(t, tt)
|
||||
})
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
type skipInitTestUnlessEnVar string
|
||||
|
||||
func (key skipInitTestUnlessEnVar) fn(t *testing.T, tc testCaseInit) {
|
||||
// always run acc tests for now
|
||||
// if os.Getenv(string(key)) == "" {
|
||||
// t.Skipf("Acceptance test skipped unless env '%s' set", key)
|
||||
// }
|
||||
}
|
||||
|
||||
type initTestGoGetPlugin struct {
|
||||
Src string
|
||||
Dst string
|
||||
}
|
||||
|
||||
func (opts initTestGoGetPlugin) fn(t *testing.T, _ testCaseInit) {
|
||||
if _, err := getter.Get(context.Background(), opts.Dst, opts.Src); err != nil {
|
||||
t.Fatalf("get: %v", err)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,72 @@
|
||||
packer {
|
||||
required_version = ">= 1.6.0"
|
||||
}
|
||||
|
||||
variable "aws_access_key" {
|
||||
type = string
|
||||
default = ""
|
||||
sensitive = true
|
||||
}
|
||||
|
||||
variable "aws_region" {
|
||||
type = string
|
||||
}
|
||||
|
||||
variable "aws_secret_key" {
|
||||
type = string
|
||||
default = ""
|
||||
sensitive = true
|
||||
}
|
||||
|
||||
data "amazon-ami" "autogenerated_1" {
|
||||
access_key = "NJDBFASJDbsajhbda5487"
|
||||
filters = {
|
||||
name = "ubuntu/images/*/ubuntu-xenial-16.04-amd64-server-*"
|
||||
root-device-type = "ebs"
|
||||
virtualization-type = "hvm"
|
||||
}
|
||||
most_recent = true
|
||||
owners = ["099720109477"]
|
||||
region = "us-west-2"
|
||||
secret_key = "ASEfewdsfAWASTT51874"
|
||||
}
|
||||
|
||||
data "amazon-ami" "autogenerated_2" {
|
||||
access_key = "${var.aws_access_key}"
|
||||
filters = {
|
||||
name = "ubuntu/images/*/ubuntu-xenial-16.04-amd64-server-*"
|
||||
root-device-type = "ebs"
|
||||
virtualization-type = "hvm"
|
||||
}
|
||||
most_recent = true
|
||||
owners = ["099720109477"]
|
||||
region = "${var.aws_region}"
|
||||
secret_key = "${var.aws_secret_key}"
|
||||
}
|
||||
|
||||
locals { timestamp = regex_replace(timestamp(), "[- TZ:]", "") }
|
||||
|
||||
source "amazon-ebs" "autogenerated_1" {
|
||||
access_key = "NJDBFASJDbsajhbda5487"
|
||||
ami_name = "ubuntu-16-04-test-${local.timestamp}"
|
||||
region = "us-west-2"
|
||||
secret_key = "ASEfewdsfAWASTT51874"
|
||||
source_ami = "${data.amazon-ami.autogenerated_1.id}"
|
||||
ssh_interface = "session_manager"
|
||||
ssh_username = "ubuntu"
|
||||
}
|
||||
|
||||
source "amazon-ebs" "named_builder" {
|
||||
access_key = "${var.aws_access_key}"
|
||||
ami_name = "ubuntu-16-04-test-${local.timestamp}"
|
||||
region = "${var.aws_region}"
|
||||
secret_key = "${var.aws_secret_key}"
|
||||
source_ami = "${data.amazon-ami.autogenerated_2.id}"
|
||||
ssh_interface = "session_manager"
|
||||
ssh_username = "ubuntu"
|
||||
}
|
||||
|
||||
build {
|
||||
sources = ["source.amazon-ebs.autogenerated_1", "source.amazon-ebs.named_builder"]
|
||||
|
||||
}
|
||||
@@ -0,0 +1,55 @@
|
||||
{
|
||||
"min_packer_version": "1.6.0",
|
||||
"variables": {
|
||||
"aws_region": null,
|
||||
"aws_secret_key": "",
|
||||
"aws_access_key": ""
|
||||
},
|
||||
"sensitive-variables": [
|
||||
"aws_secret_key",
|
||||
"aws_access_key"
|
||||
],
|
||||
"builders": [
|
||||
{
|
||||
"type": "amazon-ebs",
|
||||
"region": "us-west-2",
|
||||
"secret_key": "ASEfewdsfAWASTT51874",
|
||||
"access_key": "NJDBFASJDbsajhbda5487",
|
||||
"ami_name": "ubuntu-16-04-test-{{ timestamp }}",
|
||||
"source_ami_filter": {
|
||||
"filters": {
|
||||
"virtualization-type": "hvm",
|
||||
"name": "ubuntu/images/*/ubuntu-xenial-16.04-amd64-server-*",
|
||||
"root-device-type": "ebs"
|
||||
},
|
||||
"owners": [
|
||||
"099720109477"
|
||||
],
|
||||
"most_recent": true
|
||||
},
|
||||
"ssh_username": "ubuntu",
|
||||
"ssh_interface": "session_manager"
|
||||
},
|
||||
{
|
||||
"type": "amazon-ebs",
|
||||
"name": "named_builder",
|
||||
"region": "{{ user `aws_region` }}",
|
||||
"secret_key": "{{ user `aws_secret_key` }}",
|
||||
"access_key": "{{ user `aws_access_key` }}",
|
||||
"ami_name": "ubuntu-16-04-test-{{ timestamp }}",
|
||||
"source_ami_filter": {
|
||||
"filters": {
|
||||
"virtualization-type": "hvm",
|
||||
"name": "ubuntu/images/*/ubuntu-xenial-16.04-amd64-server-*",
|
||||
"root-device-type": "ebs"
|
||||
},
|
||||
"owners": [
|
||||
"099720109477"
|
||||
],
|
||||
"most_recent": true
|
||||
},
|
||||
"ssh_username": "ubuntu",
|
||||
"ssh_interface": "session_manager"
|
||||
}
|
||||
]
|
||||
}
|
||||
+64
-5
@@ -47,14 +47,38 @@ variable "secret_account" {
|
||||
sensitive = true
|
||||
}
|
||||
|
||||
# "timestamp" template function replacement
|
||||
locals { timestamp = regex_replace(timestamp(), "[- TZ:]", "") }
|
||||
# The amazon-secretsmanager data block is generated from your aws_secretsmanager template function; a data
|
||||
# from this block can be referenced in source and locals blocks.
|
||||
# Read the documentation for data blocks here:
|
||||
# https://www.packer.io/docs/templates/hcl_templates/blocks/data
|
||||
# Read the documentation for the Amazon Secrets Manager Data Source here:
|
||||
# https://www.packer.io/docs/datasources/amazon/secretsmanager
|
||||
data "amazon-secretsmanager" "autogenerated_1" {
|
||||
name = "sample/app/password"
|
||||
}
|
||||
|
||||
data "amazon-secretsmanager" "autogenerated_2" {
|
||||
key = "api_key"
|
||||
name = "sample/app/passwords"
|
||||
}
|
||||
|
||||
data "amazon-secretsmanager" "autogenerated_3" {
|
||||
name = "some_secret"
|
||||
}
|
||||
|
||||
data "amazon-secretsmanager" "autogenerated_4" {
|
||||
key = "with_key"
|
||||
name = "some_secret"
|
||||
}
|
||||
|
||||
# The amazon-ami data block is generated from your amazon builder source_ami_filter; a data
|
||||
# from this block can be referenced in source and locals blocks.
|
||||
# Read the documentation for data blocks here:
|
||||
# https://www.packer.io/docs/templates/hcl_templates/blocks/data
|
||||
# Read the documentation for the Amazon AMI Data Source here:
|
||||
# https://www.packer.io/docs/datasources/amazon/ami
|
||||
data "amazon-ami" "autogenerated_1" {
|
||||
access_key = "${var.aws_access_key}"
|
||||
filters = {
|
||||
name = "ubuntu/images/*/ubuntu-xenial-16.04-amd64-server-*"
|
||||
root-device-type = "ebs"
|
||||
@@ -62,6 +86,24 @@ data "amazon-ami" "autogenerated_1" {
|
||||
}
|
||||
most_recent = true
|
||||
owners = ["099720109477"]
|
||||
region = "${var.aws_region}"
|
||||
secret_key = "${var.aws_secret_key}"
|
||||
}
|
||||
|
||||
# "timestamp" template function replacement
|
||||
locals { timestamp = regex_replace(timestamp(), "[- TZ:]", "") }
|
||||
|
||||
# All locals variables are generated from variables that uses expressions
|
||||
# that are not allowed in HCL2 variables.
|
||||
# Read the documentation for locals blocks here:
|
||||
# https://www.packer.io/docs/templates/hcl_templates/blocks/locals
|
||||
local "password" {
|
||||
sensitive = true
|
||||
expression = "${data.amazon-secretsmanager.autogenerated_1.value}"
|
||||
}
|
||||
|
||||
locals {
|
||||
password_key = "MY_KEY_${data.amazon-secretsmanager.autogenerated_2.value}"
|
||||
}
|
||||
|
||||
# source blocks are generated from your builders; a source can be referenced in
|
||||
@@ -136,6 +178,15 @@ build {
|
||||
max_retries = "5"
|
||||
}
|
||||
|
||||
provisioner "shell" {
|
||||
inline = ["echo ${local.password}", "echo ${data.amazon-secretsmanager.autogenerated_1.value}", "echo ${local.password_key}", "echo ${data.amazon-secretsmanager.autogenerated_2.value}"]
|
||||
}
|
||||
|
||||
provisioner "shell" {
|
||||
inline = ["echo ${data.amazon-secretsmanager.autogenerated_3.value}", "echo ${data.amazon-secretsmanager.autogenerated_4.value}"]
|
||||
}
|
||||
|
||||
|
||||
# template: hcl2_upgrade:2:38: executing "hcl2_upgrade" at <clean_resource_name>: error calling clean_resource_name: unhandled "clean_resource_name" call:
|
||||
# there is no way to automatically upgrade the "clean_resource_name" call.
|
||||
# Please manually upgrade to use custom validation rules, `replace(string, substring, replacement)` or `regex_replace(string, substring, replacement)`
|
||||
@@ -144,6 +195,7 @@ build {
|
||||
inline = ["echo mybuild-{{isotime | clean_resource_name}}"]
|
||||
}
|
||||
|
||||
|
||||
# template: hcl2_upgrade:2:35: executing "hcl2_upgrade" at <lower>: error calling lower: unhandled "lower" call:
|
||||
# there is no way to automatically upgrade the "lower" call.
|
||||
# Please manually upgrade to `lower(var.example)`
|
||||
@@ -152,6 +204,7 @@ build {
|
||||
inline = ["echo {{ `SOMETHING` | lower }}"]
|
||||
}
|
||||
|
||||
|
||||
# template: hcl2_upgrade:2:35: executing "hcl2_upgrade" at <upper>: error calling upper: unhandled "upper" call:
|
||||
# there is no way to automatically upgrade the "upper" call.
|
||||
# Please manually upgrade to `upper(var.example)`
|
||||
@@ -160,6 +213,7 @@ build {
|
||||
inline = ["echo {{ `something` | upper }}"]
|
||||
}
|
||||
|
||||
|
||||
# template: hcl2_upgrade:2:21: executing "hcl2_upgrade" at <split `some-string` `-` 0>: error calling split: unhandled "split" call:
|
||||
# there is no way to automatically upgrade the "split" call.
|
||||
# Please manually upgrade to `split(separator, string)`
|
||||
@@ -168,6 +222,7 @@ build {
|
||||
inline = ["echo {{ split `some-string` `-` 0 }}"]
|
||||
}
|
||||
|
||||
|
||||
# template: hcl2_upgrade:2:21: executing "hcl2_upgrade" at <replace_all `-` `/` build_name>: error calling replace_all: unhandled "replace_all" call:
|
||||
# there is no way to automatically upgrade the "replace_all" call.
|
||||
# Please manually upgrade to `replace(string, substring, replacement)` or `regex_replace(string, substring, replacement)`
|
||||
@@ -176,6 +231,7 @@ build {
|
||||
inline = ["echo {{ replace_all `-` `/` build_name }}"]
|
||||
}
|
||||
|
||||
|
||||
# template: hcl2_upgrade:2:21: executing "hcl2_upgrade" at <replace `some-string` `-` `/` 1>: error calling replace: unhandled "replace" call:
|
||||
# there is no way to automatically upgrade the "replace" call.
|
||||
# Please manually upgrade to `replace(string, substring, replacement)` or `regex_replace(string, substring, replacement)`
|
||||
@@ -183,11 +239,14 @@ build {
|
||||
provisioner "shell" {
|
||||
inline = ["echo {{ replace `some-string` `-` `/` 1 }}"]
|
||||
}
|
||||
|
||||
provisioner "shell-local" {
|
||||
inline = ["sleep 100000"]
|
||||
only = ["amazon-ebs"]
|
||||
timeout = "5s"
|
||||
inline = ["sleep 100000"]
|
||||
only = ["amazon-ebs"]
|
||||
pause_before = "5s"
|
||||
timeout = "5s"
|
||||
}
|
||||
|
||||
post-processor "amazon-import" {
|
||||
format = "vmdk"
|
||||
license_type = "BYOL"
|
||||
@@ -0,0 +1,236 @@
|
||||
{
|
||||
"min_packer_version": "1.6.0",
|
||||
"variables": {
|
||||
"secret_account": "🤷",
|
||||
"aws_region": null,
|
||||
"aws_secondary_region": "{{ env `AWS_DEFAULT_REGION` }}",
|
||||
"aws_secret_key": "",
|
||||
"aws_access_key": "",
|
||||
"password": "{{ aws_secretsmanager `sample/app/password` }}",
|
||||
"password_key": "MY_KEY_{{ aws_secretsmanager `sample/app/passwords` `api_key` }}"
|
||||
},
|
||||
"sensitive-variables": [
|
||||
"aws_secret_key",
|
||||
"aws_access_key",
|
||||
"secret_account",
|
||||
"potato",
|
||||
"password"
|
||||
],
|
||||
"builders": [
|
||||
{
|
||||
"type": "amazon-ebs",
|
||||
"region": "{{ user `aws_region` }}",
|
||||
"secret_key": "{{ user `aws_secret_key` }}",
|
||||
"access_key": "{{ user `aws_access_key` }}",
|
||||
"ami_name": "ubuntu-16-04-test-{{ timestamp }}",
|
||||
"ami_description": "Ubuntu 16.04 LTS - expand root partition",
|
||||
"source_ami_filter": {
|
||||
"filters": {
|
||||
"virtualization-type": "hvm",
|
||||
"name": "ubuntu/images/*/ubuntu-xenial-16.04-amd64-server-*",
|
||||
"root-device-type": "ebs"
|
||||
},
|
||||
"owners": [
|
||||
"099720109477"
|
||||
],
|
||||
"most_recent": true
|
||||
},
|
||||
"launch_block_device_mappings": [
|
||||
{
|
||||
"delete_on_termination": true,
|
||||
"device_name": "/dev/sda1",
|
||||
"volume_type": "gp2",
|
||||
"volume_size": 48
|
||||
}
|
||||
],
|
||||
"spot_price": "0.0075",
|
||||
"spot_instance_types": [
|
||||
"t2.small",
|
||||
"t2.medium",
|
||||
"t2.large"
|
||||
],
|
||||
"encrypt_boot": true,
|
||||
"ssh_username": "ubuntu",
|
||||
"temporary_iam_instance_profile_policy_document": {
|
||||
"Version": "2012-10-17",
|
||||
"Statement": [
|
||||
{
|
||||
"Effect": "Allow",
|
||||
"Action": [
|
||||
"*"
|
||||
],
|
||||
"Resource": ["*"]
|
||||
}
|
||||
]
|
||||
},
|
||||
"ssh_interface": "session_manager"
|
||||
},
|
||||
{
|
||||
"type": "amazon-ebs",
|
||||
"name": "named_builder",
|
||||
"region": "{{ user `aws_region` }}",
|
||||
"secret_key": "{{ user `aws_secret_key` }}",
|
||||
"access_key": "{{ user `aws_access_key` }}",
|
||||
"ami_name": "ubuntu-16-04-test-{{ timestamp }}",
|
||||
"ami_description": "Ubuntu 16.04 LTS - expand root partition",
|
||||
"source_ami_filter": {
|
||||
"filters": {
|
||||
"virtualization-type": "hvm",
|
||||
"name": "ubuntu/images/*/ubuntu-xenial-16.04-amd64-server-*",
|
||||
"root-device-type": "ebs"
|
||||
},
|
||||
"owners": [
|
||||
"099720109477"
|
||||
],
|
||||
"most_recent": true
|
||||
},
|
||||
"launch_block_device_mappings": [
|
||||
{
|
||||
"delete_on_termination": true,
|
||||
"device_name": "/dev/sda1",
|
||||
"volume_type": "gp2",
|
||||
"volume_size": 48
|
||||
}
|
||||
],
|
||||
"spot_price": "0.0075",
|
||||
"spot_instance_types": [
|
||||
"t2.small",
|
||||
"t2.medium",
|
||||
"t2.large"
|
||||
],
|
||||
"encrypt_boot": true,
|
||||
"ssh_username": "ubuntu",
|
||||
"temporary_iam_instance_profile_policy_document": {
|
||||
"Version": "2012-10-17",
|
||||
"Statement": [
|
||||
{
|
||||
"Effect": "Allow",
|
||||
"Action": [
|
||||
"*"
|
||||
],
|
||||
"Resource": ["*"]
|
||||
}
|
||||
]
|
||||
},
|
||||
"ssh_interface": "session_manager"
|
||||
}
|
||||
],
|
||||
"provisioners": [
|
||||
{
|
||||
"type": "shell",
|
||||
"except": [
|
||||
"amazon-ebs"
|
||||
],
|
||||
"max_retries": 5,
|
||||
"inline": [
|
||||
"echo {{ user `secret_account` }}",
|
||||
"echo {{ build `ID` }}",
|
||||
"echo {{ build `SSHPublicKey` }} | head -c 14",
|
||||
"echo {{ template_dir }} is not {{ pwd }}",
|
||||
"echo {{ packer_version }}",
|
||||
"echo {{ uuid }}"
|
||||
]
|
||||
},
|
||||
{
|
||||
"type": "shell",
|
||||
"inline": [
|
||||
"echo {{ user `password` }}",
|
||||
"echo {{ aws_secretsmanager `sample/app/password` }}",
|
||||
"echo {{ user `password_key` }}",
|
||||
"echo {{ aws_secretsmanager `sample/app/passwords` `api_key` }}"
|
||||
]
|
||||
},
|
||||
{
|
||||
"type": "shell",
|
||||
"inline": [
|
||||
"echo {{ aws_secretsmanager `some_secret` }}",
|
||||
"echo {{ aws_secretsmanager `some_secret` `with_key` }}"
|
||||
]
|
||||
},
|
||||
{
|
||||
"type": "shell",
|
||||
"inline": [
|
||||
"echo mybuild-{{isotime | clean_resource_name}}"
|
||||
]
|
||||
},
|
||||
{
|
||||
"type": "shell",
|
||||
"inline": [
|
||||
"echo {{ `SOMETHING` | lower }}"
|
||||
]
|
||||
},
|
||||
{
|
||||
"type": "shell",
|
||||
"inline": [
|
||||
"echo {{ `something` | upper }}"
|
||||
]
|
||||
},
|
||||
{
|
||||
"type": "shell",
|
||||
"inline": [
|
||||
"echo {{ split `some-string` `-` 0 }}"
|
||||
]
|
||||
},
|
||||
{
|
||||
"type": "shell",
|
||||
"inline": [
|
||||
"echo {{ replace_all `-` `/` build_name }}"
|
||||
]
|
||||
},
|
||||
{
|
||||
"type": "shell",
|
||||
"inline": [
|
||||
"echo {{ replace `some-string` `-` `/` 1 }}"
|
||||
]
|
||||
},
|
||||
{
|
||||
"type": "shell-local",
|
||||
"only": [
|
||||
"amazon-ebs"
|
||||
],
|
||||
"timeout": "5s",
|
||||
"pause_before": "5s",
|
||||
"inline": [
|
||||
"sleep 100000"
|
||||
]
|
||||
}
|
||||
],
|
||||
"post-processors": [
|
||||
[
|
||||
{
|
||||
"type": "amazon-import",
|
||||
"region": "eu-west-3",
|
||||
"s3_bucket_name": "hashicorp.adrien",
|
||||
"license_type": "BYOL",
|
||||
"format": "vmdk",
|
||||
"tags": {
|
||||
"Description": "packer amazon-import {{timestamp}}"
|
||||
}
|
||||
}
|
||||
],
|
||||
[
|
||||
{
|
||||
"only": [
|
||||
"amazon-ebs"
|
||||
],
|
||||
"files": [
|
||||
"path/something.ova"
|
||||
],
|
||||
"keep_input_artifact": true,
|
||||
"name": "very_special_artifice_post-processor",
|
||||
"type": "artifice"
|
||||
},
|
||||
{
|
||||
"except": [
|
||||
"amazon-ebs"
|
||||
],
|
||||
"type": "amazon-import",
|
||||
"s3_bucket_name": "hashicorp.adrien",
|
||||
"license_type": "BYOL",
|
||||
"tags": {
|
||||
"Description": "packer amazon-import {{timestamp}}"
|
||||
}
|
||||
}
|
||||
]
|
||||
]
|
||||
}
|
||||
@@ -0,0 +1,36 @@
|
||||
# This file was autogenerated by the 'packer hcl2_upgrade' command. We
|
||||
# recommend double checking that everything is correct before going forward. We
|
||||
# also recommend treating this file as disposable. The HCL2 blocks in this
|
||||
# file can be moved to other files. For example, the variable blocks could be
|
||||
# moved to their own 'variables.pkr.hcl' file, etc. Those files need to be
|
||||
# suffixed with '.pkr.hcl' to be visible to Packer. To use multiple files at
|
||||
# once they also need to be in the same folder. 'packer inspect folder/'
|
||||
# will describe to you what is in that folder.
|
||||
|
||||
# Avoid mixing go templating calls ( for example ```{{ upper(`string`) }}``` )
|
||||
# and HCL2 calls (for example '${ var.string_value_example }' ). They won't be
|
||||
# executed together and the outcome will be unknown.
|
||||
|
||||
# source blocks are generated from your builders; a source can be referenced in
|
||||
# build blocks. A build block runs provisioner and post-processors on a
|
||||
# source. Read the documentation for source blocks here:
|
||||
# https://www.packer.io/docs/templates/hcl_templates/blocks/source
|
||||
source "null" "autogenerated_1" {
|
||||
communicator = "none"
|
||||
}
|
||||
|
||||
# a build block invokes sources and runs provisioning steps on them. The
|
||||
# documentation for build blocks can be found here:
|
||||
# https://www.packer.io/docs/templates/hcl_templates/blocks/build
|
||||
build {
|
||||
sources = ["source.null.autogenerated_1"]
|
||||
|
||||
provisioner "shell-local" {
|
||||
inline = ["exit 2"]
|
||||
}
|
||||
|
||||
error-cleanup-provisioner "shell-local" {
|
||||
inline = ["echo 'rubber ducky'> ducky.txt"]
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,18 @@
|
||||
{
|
||||
"builders": [
|
||||
{
|
||||
"type": "null",
|
||||
"communicator": "none"
|
||||
}
|
||||
],
|
||||
"provisioners": [
|
||||
{
|
||||
"type": "shell-local",
|
||||
"inline": ["exit 2"]
|
||||
}
|
||||
],
|
||||
"error-cleanup-provisioner": {
|
||||
"type": "shell-local",
|
||||
"inline": ["echo 'rubber ducky'> ducky.txt"]
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,75 @@
|
||||
# This file was autogenerated by the 'packer hcl2_upgrade' command. We
|
||||
# recommend double checking that everything is correct before going forward. We
|
||||
# also recommend treating this file as disposable. The HCL2 blocks in this
|
||||
# file can be moved to other files. For example, the variable blocks could be
|
||||
# moved to their own 'variables.pkr.hcl' file, etc. Those files need to be
|
||||
# suffixed with '.pkr.hcl' to be visible to Packer. To use multiple files at
|
||||
# once they also need to be in the same folder. 'packer inspect folder/'
|
||||
# will describe to you what is in that folder.
|
||||
|
||||
# Avoid mixing go templating calls ( for example ```{{ upper(`string`) }}``` )
|
||||
# and HCL2 calls (for example '${ var.string_value_example }' ). They won't be
|
||||
# executed together and the outcome will be unknown.
|
||||
|
||||
# See https://www.packer.io/docs/templates/hcl_templates/blocks/packer for more info
|
||||
packer {
|
||||
required_version = ">= 1.6.0"
|
||||
}
|
||||
|
||||
# All generated input variables will be of 'string' type as this is how Packer JSON
|
||||
# views them; you can change their type later on. Read the variables type
|
||||
# constraints documentation
|
||||
# https://www.packer.io/docs/templates/hcl_templates/variables#type-constraints for more info.
|
||||
variable "aws_access_key" {
|
||||
type = string
|
||||
default = ""
|
||||
}
|
||||
|
||||
variable "aws_region" {
|
||||
type = string
|
||||
}
|
||||
|
||||
variable "aws_secret_key" {
|
||||
type = string
|
||||
default = ""
|
||||
}
|
||||
|
||||
# source blocks are generated from your builders; a source can be referenced in
|
||||
# build blocks. A build block runs provisioner and post-processors on a
|
||||
# source. Read the documentation for source blocks here:
|
||||
# https://www.packer.io/docs/templates/hcl_templates/blocks/source
|
||||
source "amazon-ebs" "autogenerated_1" {
|
||||
access_key = "${var.aws_access_key}"
|
||||
ami_description = "Ubuntu 16.04 LTS - expand root partition"
|
||||
ami_name = "ubuntu-16-04-test"
|
||||
encrypt_boot = true
|
||||
launch_block_device_mappings {
|
||||
delete_on_termination = true
|
||||
device_name = "/dev/sda1"
|
||||
volume_size = 48
|
||||
volume_type = "gp2"
|
||||
}
|
||||
region = "${var.aws_region}"
|
||||
secret_key = "${var.aws_secret_key}"
|
||||
source_ami = "ami1234567"
|
||||
spot_instance_types = ["t2.small", "t2.medium", "t2.large"]
|
||||
spot_price = "0.0075"
|
||||
ssh_interface = "session_manager"
|
||||
ssh_username = "ubuntu"
|
||||
temporary_iam_instance_profile_policy_document {
|
||||
Statement {
|
||||
Action = ["*"]
|
||||
Effect = "Allow"
|
||||
Resource = ["*"]
|
||||
}
|
||||
Version = "2012-10-17"
|
||||
}
|
||||
}
|
||||
|
||||
# a build block invokes sources and runs provisioning steps on them. The
|
||||
# documentation for build blocks can be found here:
|
||||
# https://www.packer.io/docs/templates/hcl_templates/blocks/build
|
||||
build {
|
||||
sources = ["source.amazon-ebs.autogenerated_1"]
|
||||
|
||||
}
|
||||
@@ -0,0 +1,48 @@
|
||||
{
|
||||
"min_packer_version": "1.6.0",
|
||||
"variables": {
|
||||
"aws_region": null,
|
||||
"aws_secret_key": "",
|
||||
"aws_access_key": ""
|
||||
},
|
||||
"builders": [
|
||||
{
|
||||
"type": "amazon-ebs",
|
||||
"region": "{{ user `aws_region` }}",
|
||||
"secret_key": "{{ user `aws_secret_key` }}",
|
||||
"access_key": "{{ user `aws_access_key` }}",
|
||||
"ami_name": "ubuntu-16-04-test",
|
||||
"ami_description": "Ubuntu 16.04 LTS - expand root partition",
|
||||
"source_ami": "ami1234567",
|
||||
"launch_block_device_mappings": [
|
||||
{
|
||||
"delete_on_termination": true,
|
||||
"device_name": "/dev/sda1",
|
||||
"volume_type": "gp2",
|
||||
"volume_size": 48
|
||||
}
|
||||
],
|
||||
"spot_price": "0.0075",
|
||||
"spot_instance_types": [
|
||||
"t2.small",
|
||||
"t2.medium",
|
||||
"t2.large"
|
||||
],
|
||||
"encrypt_boot": true,
|
||||
"ssh_username": "ubuntu",
|
||||
"temporary_iam_instance_profile_policy_document": {
|
||||
"Version": "2012-10-17",
|
||||
"Statement": [
|
||||
{
|
||||
"Effect": "Allow",
|
||||
"Action": [
|
||||
"*"
|
||||
],
|
||||
"Resource": ["*"]
|
||||
}
|
||||
]
|
||||
},
|
||||
"ssh_interface": "session_manager"
|
||||
}
|
||||
]
|
||||
}
|
||||
@@ -0,0 +1,78 @@
|
||||
# This file was autogenerated by the 'packer hcl2_upgrade' command. We
|
||||
# recommend double checking that everything is correct before going forward. We
|
||||
# also recommend treating this file as disposable. The HCL2 blocks in this
|
||||
# file can be moved to other files. For example, the variable blocks could be
|
||||
# moved to their own 'variables.pkr.hcl' file, etc. Those files need to be
|
||||
# suffixed with '.pkr.hcl' to be visible to Packer. To use multiple files at
|
||||
# once they also need to be in the same folder. 'packer inspect folder/'
|
||||
# will describe to you what is in that folder.
|
||||
|
||||
# Avoid mixing go templating calls ( for example ```{{ upper(`string`) }}``` )
|
||||
# and HCL2 calls (for example '${ var.string_value_example }' ). They won't be
|
||||
# executed together and the outcome will be unknown.
|
||||
|
||||
# See https://www.packer.io/docs/templates/hcl_templates/blocks/packer for more info
|
||||
packer {
|
||||
required_version = ">= 1.6.0"
|
||||
}
|
||||
|
||||
# All generated input variables will be of 'string' type as this is how Packer JSON
|
||||
# views them; you can change their type later on. Read the variables type
|
||||
# constraints documentation
|
||||
# https://www.packer.io/docs/templates/hcl_templates/variables#type-constraints for more info.
|
||||
variable "aws_access_key" {
|
||||
type = string
|
||||
default = ""
|
||||
}
|
||||
|
||||
variable "aws_region" {
|
||||
type = string
|
||||
}
|
||||
|
||||
variable "aws_secret_key" {
|
||||
type = string
|
||||
default = ""
|
||||
}
|
||||
|
||||
# "timestamp" template function replacement
|
||||
locals { timestamp = regex_replace(timestamp(), "[- TZ:]", "") }
|
||||
|
||||
# source blocks are generated from your builders; a source can be referenced in
|
||||
# build blocks. A build block runs provisioner and post-processors on a
|
||||
# source. Read the documentation for source blocks here:
|
||||
# https://www.packer.io/docs/templates/hcl_templates/blocks/source
|
||||
source "amazon-ebs" "party_parrot" {
|
||||
access_key = "${var.aws_access_key}"
|
||||
ami_description = "Ubuntu 16.04 LTS - expand root partition"
|
||||
ami_name = "ubuntu-16-04-test-${local.timestamp}"
|
||||
encrypt_boot = true
|
||||
launch_block_device_mappings {
|
||||
delete_on_termination = true
|
||||
device_name = "/dev/sda1"
|
||||
volume_size = 48
|
||||
volume_type = "gp2"
|
||||
}
|
||||
region = "${var.aws_region}"
|
||||
secret_key = "${var.aws_secret_key}"
|
||||
source_ami = "ami1234567"
|
||||
spot_instance_types = ["t2.small", "t2.medium", "t2.large"]
|
||||
spot_price = "0.0075"
|
||||
ssh_interface = "session_manager"
|
||||
ssh_username = "ubuntu"
|
||||
temporary_iam_instance_profile_policy_document {
|
||||
Statement {
|
||||
Action = ["*"]
|
||||
Effect = "Allow"
|
||||
Resource = ["*"]
|
||||
}
|
||||
Version = "2012-10-17"
|
||||
}
|
||||
}
|
||||
|
||||
# a build block invokes sources and runs provisioning steps on them. The
|
||||
# documentation for build blocks can be found here:
|
||||
# https://www.packer.io/docs/templates/hcl_templates/blocks/build
|
||||
build {
|
||||
sources = ["source.amazon-ebs.party_parrot"]
|
||||
|
||||
}
|
||||
@@ -0,0 +1,49 @@
|
||||
{
|
||||
"min_packer_version": "1.6.0",
|
||||
"variables": {
|
||||
"aws_region": null,
|
||||
"aws_secret_key": "",
|
||||
"aws_access_key": ""
|
||||
},
|
||||
"builders": [
|
||||
{
|
||||
"type": "amazon-ebs",
|
||||
"name": " party parrot ",
|
||||
"region": "{{ user `aws_region` }}",
|
||||
"secret_key": "{{ user `aws_secret_key` }}",
|
||||
"access_key": "{{ user `aws_access_key` }}",
|
||||
"ami_name": "ubuntu-16-04-test-{{ timestamp }}",
|
||||
"ami_description": "Ubuntu 16.04 LTS - expand root partition",
|
||||
"source_ami": "ami1234567",
|
||||
"launch_block_device_mappings": [
|
||||
{
|
||||
"delete_on_termination": true,
|
||||
"device_name": "/dev/sda1",
|
||||
"volume_type": "gp2",
|
||||
"volume_size": 48
|
||||
}
|
||||
],
|
||||
"spot_price": "0.0075",
|
||||
"spot_instance_types": [
|
||||
"t2.small",
|
||||
"t2.medium",
|
||||
"t2.large"
|
||||
],
|
||||
"encrypt_boot": true,
|
||||
"ssh_username": "ubuntu",
|
||||
"temporary_iam_instance_profile_policy_document": {
|
||||
"Version": "2012-10-17",
|
||||
"Statement": [
|
||||
{
|
||||
"Effect": "Allow",
|
||||
"Action": [
|
||||
"*"
|
||||
],
|
||||
"Resource": ["*"]
|
||||
}
|
||||
]
|
||||
},
|
||||
"ssh_interface": "session_manager"
|
||||
}
|
||||
]
|
||||
}
|
||||
@@ -0,0 +1,45 @@
|
||||
|
||||
variable "aws_access_key" {
|
||||
type = string
|
||||
default = ""
|
||||
sensitive = true
|
||||
}
|
||||
|
||||
variable "aws_region" {
|
||||
type = string
|
||||
}
|
||||
|
||||
variable "aws_secondary_region" {
|
||||
type = string
|
||||
default = "${env("AWS_DEFAULT_REGION")}"
|
||||
}
|
||||
|
||||
variable "aws_secret_key" {
|
||||
type = string
|
||||
default = ""
|
||||
sensitive = true
|
||||
}
|
||||
|
||||
variable "secret_account" {
|
||||
type = string
|
||||
default = "🤷"
|
||||
sensitive = true
|
||||
}
|
||||
|
||||
data "amazon-secretsmanager" "autogenerated_1" {
|
||||
name = "sample/app/password"
|
||||
}
|
||||
|
||||
data "amazon-secretsmanager" "autogenerated_2" {
|
||||
key = "api_key"
|
||||
name = "sample/app/passwords"
|
||||
}
|
||||
|
||||
local "password" {
|
||||
sensitive = true
|
||||
expression = "${data.amazon-secretsmanager.autogenerated_1.value}"
|
||||
}
|
||||
|
||||
locals {
|
||||
password_key = "MY_KEY_${data.amazon-secretsmanager.autogenerated_2.value}"
|
||||
}
|
||||
@@ -0,0 +1,18 @@
|
||||
{
|
||||
"variables": {
|
||||
"secret_account": "🤷",
|
||||
"aws_region": null,
|
||||
"aws_secondary_region": "{{ env `AWS_DEFAULT_REGION` }}",
|
||||
"aws_secret_key": "",
|
||||
"aws_access_key": "",
|
||||
"password": "{{ aws_secretsmanager `sample/app/password` }}",
|
||||
"password_key": "MY_KEY_{{ aws_secretsmanager `sample/app/passwords` `api_key` }}"
|
||||
},
|
||||
"sensitive-variables": [
|
||||
"aws_secret_key",
|
||||
"aws_access_key",
|
||||
"secret_account",
|
||||
"potato",
|
||||
"password"
|
||||
]
|
||||
}
|
||||
@@ -0,0 +1,25 @@
|
||||
|
||||
variable "aws_access_key" {
|
||||
type = string
|
||||
default = ""
|
||||
sensitive = true
|
||||
}
|
||||
|
||||
variable "aws_region" {
|
||||
type = string
|
||||
}
|
||||
|
||||
variable "aws_secret_key" {
|
||||
type = string
|
||||
default = ""
|
||||
sensitive = true
|
||||
}
|
||||
|
||||
local "password" {
|
||||
sensitive = true
|
||||
expression = "${var.aws_secret_key}-${var.aws_access_key}"
|
||||
}
|
||||
|
||||
locals {
|
||||
aws_secondary_region = "${var.aws_region}"
|
||||
}
|
||||
@@ -0,0 +1,14 @@
|
||||
{
|
||||
"variables": {
|
||||
"aws_region": null,
|
||||
"aws_secondary_region": "{{ user `aws_region` }}",
|
||||
"aws_secret_key": "",
|
||||
"aws_access_key": "",
|
||||
"password": "{{ user `aws_secret_key` }}-{{ user `aws_access_key` }}"
|
||||
},
|
||||
"sensitive-variables": [
|
||||
"aws_secret_key",
|
||||
"aws_access_key",
|
||||
"password"
|
||||
]
|
||||
}
|
||||
@@ -0,0 +1,232 @@
|
||||
packer {
|
||||
required_version = ">= 1.6.0"
|
||||
}
|
||||
|
||||
variable "aws_access_key" {
|
||||
type = string
|
||||
default = ""
|
||||
sensitive = true
|
||||
}
|
||||
|
||||
variable "aws_region" {
|
||||
type = string
|
||||
}
|
||||
|
||||
variable "aws_secondary_region" {
|
||||
type = string
|
||||
default = "${env("AWS_DEFAULT_REGION")}"
|
||||
}
|
||||
|
||||
variable "aws_secret_key" {
|
||||
type = string
|
||||
default = ""
|
||||
sensitive = true
|
||||
}
|
||||
|
||||
variable "secret_account" {
|
||||
type = string
|
||||
default = "🤷"
|
||||
sensitive = true
|
||||
}
|
||||
|
||||
data "amazon-secretsmanager" "autogenerated_1" {
|
||||
name = "sample/app/password"
|
||||
}
|
||||
|
||||
data "amazon-secretsmanager" "autogenerated_2" {
|
||||
key = "api_key"
|
||||
name = "sample/app/passwords"
|
||||
}
|
||||
|
||||
data "amazon-secretsmanager" "autogenerated_3" {
|
||||
name = "some_secret"
|
||||
}
|
||||
|
||||
data "amazon-secretsmanager" "autogenerated_4" {
|
||||
key = "with_key"
|
||||
name = "some_secret"
|
||||
}
|
||||
|
||||
data "amazon-ami" "autogenerated_1" {
|
||||
access_key = "${var.aws_access_key}"
|
||||
filters = {
|
||||
name = "ubuntu/images/*/ubuntu-xenial-16.04-amd64-server-*"
|
||||
root-device-type = "ebs"
|
||||
virtualization-type = "hvm"
|
||||
}
|
||||
most_recent = true
|
||||
owners = ["099720109477"]
|
||||
region = "${var.aws_region}"
|
||||
secret_key = "${var.aws_secret_key}"
|
||||
}
|
||||
|
||||
locals { timestamp = regex_replace(timestamp(), "[- TZ:]", "") }
|
||||
|
||||
local "password" {
|
||||
sensitive = true
|
||||
expression = "${data.amazon-secretsmanager.autogenerated_1.value}"
|
||||
}
|
||||
|
||||
locals {
|
||||
password_key = "MY_KEY_${data.amazon-secretsmanager.autogenerated_2.value}"
|
||||
}
|
||||
|
||||
source "amazon-ebs" "autogenerated_1" {
|
||||
access_key = "${var.aws_access_key}"
|
||||
ami_description = "Ubuntu 16.04 LTS - expand root partition"
|
||||
ami_name = "ubuntu-16-04-test-${local.timestamp}"
|
||||
encrypt_boot = true
|
||||
launch_block_device_mappings {
|
||||
delete_on_termination = true
|
||||
device_name = "/dev/sda1"
|
||||
volume_size = 48
|
||||
volume_type = "gp2"
|
||||
}
|
||||
region = "${var.aws_region}"
|
||||
secret_key = "${var.aws_secret_key}"
|
||||
source_ami = "${data.amazon-ami.autogenerated_1.id}"
|
||||
spot_instance_types = ["t2.small", "t2.medium", "t2.large"]
|
||||
spot_price = "0.0075"
|
||||
ssh_interface = "session_manager"
|
||||
ssh_username = "ubuntu"
|
||||
temporary_iam_instance_profile_policy_document {
|
||||
Statement {
|
||||
Action = ["*"]
|
||||
Effect = "Allow"
|
||||
Resource = ["*"]
|
||||
}
|
||||
Version = "2012-10-17"
|
||||
}
|
||||
}
|
||||
|
||||
source "amazon-ebs" "named_builder" {
|
||||
access_key = "${var.aws_access_key}"
|
||||
ami_description = "Ubuntu 16.04 LTS - expand root partition"
|
||||
ami_name = "ubuntu-16-04-test-${local.timestamp}"
|
||||
encrypt_boot = true
|
||||
launch_block_device_mappings {
|
||||
delete_on_termination = true
|
||||
device_name = "/dev/sda1"
|
||||
volume_size = 48
|
||||
volume_type = "gp2"
|
||||
}
|
||||
region = "${var.aws_region}"
|
||||
secret_key = "${var.aws_secret_key}"
|
||||
source_ami = "${data.amazon-ami.autogenerated_1.id}"
|
||||
spot_instance_types = ["t2.small", "t2.medium", "t2.large"]
|
||||
spot_price = "0.0075"
|
||||
ssh_interface = "session_manager"
|
||||
ssh_username = "ubuntu"
|
||||
temporary_iam_instance_profile_policy_document {
|
||||
Statement {
|
||||
Action = ["*"]
|
||||
Effect = "Allow"
|
||||
Resource = ["*"]
|
||||
}
|
||||
Version = "2012-10-17"
|
||||
}
|
||||
}
|
||||
|
||||
build {
|
||||
sources = ["source.amazon-ebs.autogenerated_1", "source.amazon-ebs.named_builder"]
|
||||
|
||||
provisioner "shell" {
|
||||
except = ["amazon-ebs"]
|
||||
inline = ["echo ${var.secret_account}", "echo ${build.ID}", "echo ${build.SSHPublicKey} | head -c 14", "echo ${path.root} is not ${path.cwd}", "echo ${packer.version}", "echo ${uuidv4()}"]
|
||||
max_retries = "5"
|
||||
}
|
||||
|
||||
provisioner "shell" {
|
||||
inline = ["echo ${local.password}", "echo ${data.amazon-secretsmanager.autogenerated_1.value}", "echo ${local.password_key}", "echo ${data.amazon-secretsmanager.autogenerated_2.value}"]
|
||||
}
|
||||
|
||||
provisioner "shell" {
|
||||
inline = ["echo ${data.amazon-secretsmanager.autogenerated_3.value}", "echo ${data.amazon-secretsmanager.autogenerated_4.value}"]
|
||||
}
|
||||
|
||||
|
||||
# template: hcl2_upgrade:2:38: executing "hcl2_upgrade" at <clean_resource_name>: error calling clean_resource_name: unhandled "clean_resource_name" call:
|
||||
# there is no way to automatically upgrade the "clean_resource_name" call.
|
||||
# Please manually upgrade to use custom validation rules, `replace(string, substring, replacement)` or `regex_replace(string, substring, replacement)`
|
||||
# Visit https://packer.io/docs/templates/hcl_templates/variables#custom-validation-rules , https://www.packer.io/docs/templates/hcl_templates/functions/string/replace or https://www.packer.io/docs/templates/hcl_templates/functions/string/regex_replace for more infos.
|
||||
provisioner "shell" {
|
||||
inline = ["echo mybuild-{{isotime | clean_resource_name}}"]
|
||||
}
|
||||
|
||||
|
||||
# template: hcl2_upgrade:2:35: executing "hcl2_upgrade" at <lower>: error calling lower: unhandled "lower" call:
|
||||
# there is no way to automatically upgrade the "lower" call.
|
||||
# Please manually upgrade to `lower(var.example)`
|
||||
# Visit https://www.packer.io/docs/templates/hcl_templates/functions/string/lower for more infos.
|
||||
provisioner "shell" {
|
||||
inline = ["echo {{ `SOMETHING` | lower }}"]
|
||||
}
|
||||
|
||||
|
||||
# template: hcl2_upgrade:2:35: executing "hcl2_upgrade" at <upper>: error calling upper: unhandled "upper" call:
|
||||
# there is no way to automatically upgrade the "upper" call.
|
||||
# Please manually upgrade to `upper(var.example)`
|
||||
# Visit https://www.packer.io/docs/templates/hcl_templates/functions/string/upper for more infos.
|
||||
provisioner "shell" {
|
||||
inline = ["echo {{ `something` | upper }}"]
|
||||
}
|
||||
|
||||
|
||||
# template: hcl2_upgrade:2:21: executing "hcl2_upgrade" at <split `some-string` `-` 0>: error calling split: unhandled "split" call:
|
||||
# there is no way to automatically upgrade the "split" call.
|
||||
# Please manually upgrade to `split(separator, string)`
|
||||
# Visit https://www.packer.io/docs/templates/hcl_templates/functions/string/split for more infos.
|
||||
provisioner "shell" {
|
||||
inline = ["echo {{ split `some-string` `-` 0 }}"]
|
||||
}
|
||||
|
||||
|
||||
# template: hcl2_upgrade:2:21: executing "hcl2_upgrade" at <replace_all `-` `/` build_name>: error calling replace_all: unhandled "replace_all" call:
|
||||
# there is no way to automatically upgrade the "replace_all" call.
|
||||
# Please manually upgrade to `replace(string, substring, replacement)` or `regex_replace(string, substring, replacement)`
|
||||
# Visit https://www.packer.io/docs/templates/hcl_templates/functions/string/replace or https://www.packer.io/docs/templates/hcl_templates/functions/string/regex_replace for more infos.
|
||||
provisioner "shell" {
|
||||
inline = ["echo {{ replace_all `-` `/` build_name }}"]
|
||||
}
|
||||
|
||||
|
||||
# template: hcl2_upgrade:2:21: executing "hcl2_upgrade" at <replace `some-string` `-` `/` 1>: error calling replace: unhandled "replace" call:
|
||||
# there is no way to automatically upgrade the "replace" call.
|
||||
# Please manually upgrade to `replace(string, substring, replacement)` or `regex_replace(string, substring, replacement)`
|
||||
# Visit https://www.packer.io/docs/templates/hcl_templates/functions/string/replace or https://www.packer.io/docs/templates/hcl_templates/functions/string/regex_replace for more infos.
|
||||
provisioner "shell" {
|
||||
inline = ["echo {{ replace `some-string` `-` `/` 1 }}"]
|
||||
}
|
||||
|
||||
provisioner "shell-local" {
|
||||
inline = ["sleep 100000"]
|
||||
only = ["amazon-ebs"]
|
||||
timeout = "5s"
|
||||
}
|
||||
|
||||
post-processor "amazon-import" {
|
||||
format = "vmdk"
|
||||
license_type = "BYOL"
|
||||
region = "eu-west-3"
|
||||
s3_bucket_name = "hashicorp.adrien"
|
||||
tags = {
|
||||
Description = "packer amazon-import ${local.timestamp}"
|
||||
}
|
||||
}
|
||||
post-processors {
|
||||
post-processor "artifice" {
|
||||
keep_input_artifact = true
|
||||
files = ["path/something.ova"]
|
||||
name = "very_special_artifice_post-processor"
|
||||
only = ["amazon-ebs"]
|
||||
}
|
||||
post-processor "amazon-import" {
|
||||
except = ["amazon-ebs"]
|
||||
license_type = "BYOL"
|
||||
s3_bucket_name = "hashicorp.adrien"
|
||||
tags = {
|
||||
Description = "packer amazon-import ${local.timestamp}"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
+21
-2
@@ -5,13 +5,16 @@
|
||||
"aws_region": null,
|
||||
"aws_secondary_region": "{{ env `AWS_DEFAULT_REGION` }}",
|
||||
"aws_secret_key": "",
|
||||
"aws_access_key": ""
|
||||
"aws_access_key": "",
|
||||
"password": "{{ aws_secretsmanager `sample/app/password` }}",
|
||||
"password_key": "MY_KEY_{{ aws_secretsmanager `sample/app/passwords` `api_key` }}"
|
||||
},
|
||||
"sensitive-variables": [
|
||||
"aws_secret_key",
|
||||
"aws_access_key",
|
||||
"secret_account",
|
||||
"potato"
|
||||
"potato",
|
||||
"password"
|
||||
],
|
||||
"builders": [
|
||||
{
|
||||
@@ -128,6 +131,22 @@
|
||||
"echo {{ uuid }}"
|
||||
]
|
||||
},
|
||||
{
|
||||
"type": "shell",
|
||||
"inline": [
|
||||
"echo {{ user `password` }}",
|
||||
"echo {{ aws_secretsmanager `sample/app/password` }}",
|
||||
"echo {{ user `password_key` }}",
|
||||
"echo {{ aws_secretsmanager `sample/app/passwords` `api_key` }}"
|
||||
]
|
||||
},
|
||||
{
|
||||
"type": "shell",
|
||||
"inline": [
|
||||
"echo {{ aws_secretsmanager `some_secret` }}",
|
||||
"echo {{ aws_secretsmanager `some_secret` `with_key` }}"
|
||||
]
|
||||
},
|
||||
{
|
||||
"type": "shell",
|
||||
"inline": [
|
||||
+7
-5
@@ -92,6 +92,7 @@ Options:
|
||||
|
||||
-syntax-only Only check syntax. Do not verify config of the template.
|
||||
-except=foo,bar,baz Validate all builds other than these.
|
||||
-machine-readable Produce machine-readable output.
|
||||
-only=foo,bar,baz Validate only these builds.
|
||||
-var 'key=value' Variable for templates, can be used multiple times.
|
||||
-var-file=path JSON or HCL2 file containing user variables.
|
||||
@@ -110,10 +111,11 @@ func (*ValidateCommand) AutocompleteArgs() complete.Predictor {
|
||||
|
||||
func (*ValidateCommand) AutocompleteFlags() complete.Flags {
|
||||
return complete.Flags{
|
||||
"-syntax-only": complete.PredictNothing,
|
||||
"-except": complete.PredictNothing,
|
||||
"-only": complete.PredictNothing,
|
||||
"-var": complete.PredictNothing,
|
||||
"-var-file": complete.PredictNothing,
|
||||
"-syntax-only": complete.PredictNothing,
|
||||
"-except": complete.PredictNothing,
|
||||
"-only": complete.PredictNothing,
|
||||
"-var": complete.PredictNothing,
|
||||
"-machine-readable": complete.PredictNothing,
|
||||
"-var-file": complete.PredictNothing,
|
||||
}
|
||||
}
|
||||
|
||||
@@ -17,7 +17,7 @@ require (
|
||||
github.com/aliyun/aliyun-oss-go-sdk v0.0.0-20170113022742-e6dbea820a9f
|
||||
github.com/antihax/optional v1.0.0
|
||||
github.com/approvals/go-approval-tests v0.0.0-20160714161514-ad96e53bea43
|
||||
github.com/aws/aws-sdk-go v1.36.5
|
||||
github.com/aws/aws-sdk-go v1.37.15
|
||||
github.com/biogo/hts v0.0.0-20160420073057-50da7d4131a3
|
||||
github.com/c2h5oh/datasize v0.0.0-20200112174442-28bbd4740fee
|
||||
github.com/cheggaaa/pb v1.0.27
|
||||
@@ -50,7 +50,7 @@ require (
|
||||
github.com/hashicorp/go-uuid v1.0.2
|
||||
github.com/hashicorp/go-version v1.2.0
|
||||
github.com/hashicorp/hcl/v2 v2.8.0
|
||||
github.com/hashicorp/packer-plugin-sdk v0.0.11
|
||||
github.com/hashicorp/packer-plugin-sdk v0.0.14
|
||||
github.com/hashicorp/vault/api v1.0.4
|
||||
github.com/hetznercloud/hcloud-go v1.15.1
|
||||
github.com/hyperonecom/h1-client-go v0.0.0-20191203060043-b46280e4c4a4
|
||||
@@ -68,7 +68,7 @@ require (
|
||||
github.com/mitchellh/prefixedio v0.0.0-20151214002211-6e6954073784
|
||||
github.com/mitchellh/reflectwalk v1.0.0
|
||||
github.com/olekukonko/tablewriter v0.0.0-20180105111133-96aac992fc8b
|
||||
github.com/oracle/oci-go-sdk v18.0.0+incompatible
|
||||
github.com/oracle/oci-go-sdk v24.3.0+incompatible
|
||||
github.com/outscale/osc-sdk-go/osc v0.0.0-20200722135656-d654809d0699
|
||||
github.com/pierrec/lz4 v2.0.5+incompatible
|
||||
github.com/pkg/errors v0.9.1
|
||||
@@ -76,7 +76,6 @@ require (
|
||||
github.com/profitbricks/profitbricks-sdk-go v4.0.2+incompatible
|
||||
github.com/scaleway/scaleway-sdk-go v1.0.0-beta.7
|
||||
github.com/shirou/gopsutil v2.18.12+incompatible
|
||||
github.com/smartystreets/goconvey v1.6.4 // indirect
|
||||
github.com/stretchr/testify v1.6.1
|
||||
github.com/tencentcloud/tencentcloud-sdk-go v3.0.222+incompatible
|
||||
github.com/ucloud/ucloud-sdk-go v0.16.3
|
||||
@@ -100,6 +99,4 @@ require (
|
||||
google.golang.org/grpc v1.32.0
|
||||
)
|
||||
|
||||
// replace github.com/hashicorp/packer-plugin-sdk => /Users/azr/go/src/github.com/hashicorp/packer-plugin-sdk
|
||||
|
||||
go 1.13
|
||||
|
||||
@@ -129,6 +129,8 @@ github.com/aws/aws-sdk-go v1.31.9/go.mod h1:5zCpMtNQVjRREroY7sYe8lOMRSxkhG6MZveU
|
||||
github.com/aws/aws-sdk-go v1.36.0/go.mod h1:hcU610XS61/+aQV88ixoOzUoG7v3b31pl2zKMmprdro=
|
||||
github.com/aws/aws-sdk-go v1.36.5 h1:SouM2ar1A8f+3DYWW622sDdqkkZAO3ha4j8GQjiPLFg=
|
||||
github.com/aws/aws-sdk-go v1.36.5/go.mod h1:hcU610XS61/+aQV88ixoOzUoG7v3b31pl2zKMmprdro=
|
||||
github.com/aws/aws-sdk-go v1.37.15 h1:W7l7gLLMcYRlg6a+uvf3Zz4jYwdqYzhe5ymqwWoOhp4=
|
||||
github.com/aws/aws-sdk-go v1.37.15/go.mod h1:hcU610XS61/+aQV88ixoOzUoG7v3b31pl2zKMmprdro=
|
||||
github.com/beorn7/perks v0.0.0-20180321164747-3a771d992973/go.mod h1:Dwedo/Wpr24TaqPxmxbtue+5NUziq4I4S80YR8gNf3Q=
|
||||
github.com/bgentry/go-netrc v0.0.0-20140422174119-9fd32a8b3d3d h1:xDfNPAt8lFiC1UJrqV3uuy861HCTo708pDMbjHHdCas=
|
||||
github.com/bgentry/go-netrc v0.0.0-20140422174119-9fd32a8b3d3d/go.mod h1:6QX/PXZ00z/TKoufEY6K/a0k6AhaJrQKdFe6OfVXsa4=
|
||||
@@ -393,6 +395,8 @@ github.com/hashicorp/packer v1.6.7-0.20210112155033-1bc89e34714f/go.mod h1:sKsux
|
||||
github.com/hashicorp/packer v1.6.7-0.20210120105538-140685fb4f8b/go.mod h1:Yaw0q8brThFN2PKMWMRcuhQBuwPVDJRHHioNuMJTS7Q=
|
||||
github.com/hashicorp/packer v1.6.7-0.20210125170305-539638b0f951/go.mod h1:Z3eunaxVQ3XgQ+rW7TEH0T/PRQzCUSyCBUTkm/VL7io=
|
||||
github.com/hashicorp/packer v1.6.7-0.20210126105722-aef4ced967ec/go.mod h1:2+Vo/c/fA+TD9yFc/h9jQMFm4yG+IymQIr0OdJJOPiE=
|
||||
github.com/hashicorp/packer v1.6.7-0.20210208125835-f616955ebcb6/go.mod h1:7f5ZpTTRG53rQ58BcTADuTnpiBcB3wapuxl4sF2sGMM=
|
||||
github.com/hashicorp/packer v1.6.7-0.20210217093213-201869d627bf/go.mod h1:+EWPPcqee4h8S/y913Dnta1eJkgiqsGXBQgB75A2qV0=
|
||||
github.com/hashicorp/packer-plugin-sdk v0.0.6 h1:BN2G4APXSMvDURFdnk+6DspwsU83pZeMsbEur7NmGsA=
|
||||
github.com/hashicorp/packer-plugin-sdk v0.0.6/go.mod h1:Nvh28f+Jmpp2rcaN79bULTouNkGNDRfHckhHKTAXtyU=
|
||||
github.com/hashicorp/packer-plugin-sdk v0.0.6/go.mod h1:Nvh28f+Jmpp2rcaN79bULTouNkGNDRfHckhHKTAXtyU=
|
||||
@@ -405,6 +409,10 @@ github.com/hashicorp/packer-plugin-sdk v0.0.10-0.20210126105622-8e1648006d93 h1:
|
||||
github.com/hashicorp/packer-plugin-sdk v0.0.10-0.20210126105622-8e1648006d93/go.mod h1:AtWQLNfpn7cgH2SmZ1PTedwqNOhiPvzcuKfH5sDvIQ0=
|
||||
github.com/hashicorp/packer-plugin-sdk v0.0.11 h1:nUurEGaJtpVDyg94bKC2xOXWf1TqQ+++GovFUnP5REI=
|
||||
github.com/hashicorp/packer-plugin-sdk v0.0.11/go.mod h1:GNb0WNs7zibb8vzUZce1As64z2AW0FEMwhe2J7/NW5I=
|
||||
github.com/hashicorp/packer-plugin-sdk v0.0.12 h1:eagxsrborfhc0E8zvDVCiRE9pQkjBw/6jm23jp0D8RU=
|
||||
github.com/hashicorp/packer-plugin-sdk v0.0.12/go.mod h1:hs82OYeufirGG6KRENMpjBWomnIlte99X6wXAPThJ5I=
|
||||
github.com/hashicorp/packer-plugin-sdk v0.0.14 h1:42WOZLmIbAYYC1WXxtlrQZN+fFdysVvTmj3jtoI6gOU=
|
||||
github.com/hashicorp/packer-plugin-sdk v0.0.14/go.mod h1:tNb3XzJPnjMl3QuUdKmF47B5ImerdTakalHzUAvW0aw=
|
||||
github.com/hashicorp/serf v0.8.2 h1:YZ7UKsJv+hKjqGVUUbtE3HNj79Eln2oQ75tniF6iPt0=
|
||||
github.com/hashicorp/serf v0.8.2/go.mod h1:6hOLApaqBFA1NXqRQAsxw9QxuDEvNxSQRwA/JwenrHc=
|
||||
github.com/hashicorp/serf v0.9.2 h1:yJoyfZXo4Pk2p/M/viW+YLibBFiIbKoP79gu7kDAFP0=
|
||||
@@ -530,6 +538,7 @@ github.com/mitchellh/go-wordwrap v0.0.0-20150314170334-ad45545899c7/go.mod h1:ZX
|
||||
github.com/mitchellh/go-wordwrap v1.0.0 h1:6GlHJ/LTGMrIJbwgdqdl2eEH8o+Exx/0m8ir9Gns0u4=
|
||||
github.com/mitchellh/go-wordwrap v1.0.0/go.mod h1:ZXFpozHsX6DPmq2I0TCekCxypsnAUbP2oI0UX1GXzOo=
|
||||
github.com/mitchellh/gox v0.4.0/go.mod h1:Sd9lOJ0+aimLBi73mGofS1ycjY8lL3uZM3JPS42BGNg=
|
||||
github.com/mitchellh/gox v1.0.1 h1:x0jD3dcHk9a9xPSDN6YEL4xL6Qz0dvNYm8yZqui5chI=
|
||||
github.com/mitchellh/gox v1.0.1/go.mod h1:ED6BioOGXMswlXa2zxfh/xdd5QhwYliBFn9V18Ap4z4=
|
||||
github.com/mitchellh/iochan v1.0.0 h1:C+X3KsSTLFVBr/tK1eYN/vs4rJcvsiLU338UhYPJWeY=
|
||||
github.com/mitchellh/iochan v1.0.0/go.mod h1:JwYml1nuB7xOzsp52dPpHFffvOCDupsG0QubkSMEySY=
|
||||
@@ -560,6 +569,8 @@ github.com/olekukonko/tablewriter v0.0.0-20180105111133-96aac992fc8b/go.mod h1:v
|
||||
github.com/opentracing/opentracing-go v1.1.0/go.mod h1:UkNAQd3GIcIGf0SeVgPpRdFStlNbqXla1AfSYxPUl2o=
|
||||
github.com/oracle/oci-go-sdk v18.0.0+incompatible h1:FLV4KixsVfF3rwyVTMI6Ryp/Q+OSb9sR5TawbfjFLN4=
|
||||
github.com/oracle/oci-go-sdk v18.0.0+incompatible/go.mod h1:VQb79nF8Z2cwLkLS35ukwStZIg5F66tcBccjip/j888=
|
||||
github.com/oracle/oci-go-sdk v24.3.0+incompatible h1:x4mcfb4agelf1O4/1/auGlZ1lr97jXRSSN5MxTgG/zU=
|
||||
github.com/oracle/oci-go-sdk v24.3.0+incompatible/go.mod h1:VQb79nF8Z2cwLkLS35ukwStZIg5F66tcBccjip/j888=
|
||||
github.com/outscale/osc-sdk-go/osc v0.0.0-20200722135656-d654809d0699 h1:SHe9i7h5cHe+cB77fQ6lsEgIwKg3ckNU90P03CjGMnI=
|
||||
github.com/outscale/osc-sdk-go/osc v0.0.0-20200722135656-d654809d0699/go.mod h1:5AqqNH1X8zCHescKVlpSHRzrat1KCKDXqZoQPe8fY3A=
|
||||
github.com/packer-community/winrmcp v0.0.0-20180921204643-0fd363d6159a h1:A3QMuteviunoaY/8ex+RKFqwhcZJ/Cf3fCW3IwL2wx4=
|
||||
@@ -637,6 +648,10 @@ github.com/ufilesdk-dev/ufile-gosdk v0.0.0-20190830075812-b4dbc4ef43a6 h1:FAWNiq
|
||||
github.com/ufilesdk-dev/ufile-gosdk v0.0.0-20190830075812-b4dbc4ef43a6/go.mod h1:R5FMQxkQ+QK/9Vz+jfnJP4rZIktYrRcWmuAnbOSkROI=
|
||||
github.com/ugorji/go v0.0.0-20151218193438-646ae4a518c1 h1:U6ufy3mLDgg9RYupntOvAF7xCmNNquyKaYaaVHo1Nnk=
|
||||
github.com/ugorji/go v0.0.0-20151218193438-646ae4a518c1/go.mod h1:hnLbHMwcvSihnDhEfx2/BzKp2xb0Y+ErdfYcrs9tkJQ=
|
||||
github.com/ugorji/go v1.2.4 h1:cTciPbZ/VSOzCLKclmssnfQ/jyoVyOcJ3aoJyUV1Urc=
|
||||
github.com/ugorji/go v1.2.4/go.mod h1:EuaSCk8iZMdIspsu6HXH7X2UGKw1ezO4wCfGszGmmo4=
|
||||
github.com/ugorji/go/codec v1.2.4 h1:C5VurWRRCKjuENsbM6GYVw8W++WVW9rSxoACKIvxzz8=
|
||||
github.com/ugorji/go/codec v1.2.4/go.mod h1:bWBu1+kIRWcF8uMklKaJrR6fTWQOwAlrIzX22pHwryA=
|
||||
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/go.mod h1:fy3FlTQTDXWkZ7Bh6AcGMlsjHatGryHQYUTf1ShIgkk=
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user