Compare commits
54 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| acb194c4a6 | |||
| ffbb110167 | |||
| 9afaa5a21f | |||
| f5006d0842 | |||
| 88c516b2d5 | |||
| d7bb60ea86 | |||
| 08e67f8990 | |||
| 8c3b3ca00f | |||
| 4a686ef66d | |||
| f616955ebc | |||
| a1b2e71005 | |||
| d53488db68 | |||
| b6cfe16444 | |||
| 4c02123142 | |||
| 13320650f0 | |||
| 2324f433f7 | |||
| ece5e94c3d | |||
| ad239bd2b9 | |||
| 39d550054d | |||
| ef4d35097b | |||
| a63ad19b0c | |||
| ca123721a6 | |||
| 540effbbc0 | |||
| 692433721d | |||
| 0f34592daa | |||
| ba1ad9ea15 | |||
| ef4afafde9 | |||
| f588c46fb6 | |||
| 1075120109 | |||
| 3ddb17ad86 | |||
| 92ccd5fa1d | |||
| 8ccd164f25 | |||
| ed091163be | |||
| ac014fc1c3 | |||
| 61f03cc137 | |||
| 18970bfbe4 | |||
| 300604675b | |||
| f36554fa0e | |||
| 782cf058b5 | |||
| a5a1344948 | |||
| 90fb09e52a | |||
| 21df997061 | |||
| fbbda0f9d9 | |||
| e3b14d888b | |||
| c8d5d56f61 | |||
| a8c8d247bb | |||
| ea7fef699f | |||
| e588029d6a | |||
| c3266cc3b0 | |||
| 37dcf6183c | |||
| 3242b7ee10 | |||
| 48a31d1b6a | |||
| 85bb4e7a1e | |||
| 3aab42b770 |
@@ -1,14 +1,37 @@
|
||||
on: [pull_request]
|
||||
name: Check Markdown links for modified files
|
||||
on:
|
||||
pull_request:
|
||||
paths:
|
||||
- 'website/**'
|
||||
|
||||
name: Check markdown links on modified website files
|
||||
jobs:
|
||||
vercel-deployment-poll:
|
||||
runs-on: ubuntu-latest
|
||||
timeout-minutes: 3 #cancel job if no deployment is found within x minutes
|
||||
outputs:
|
||||
url: ${{ steps.waitForVercelPreviewDeployment.outputs.url }}
|
||||
steps:
|
||||
- name: Wait for Vercel preview deployment to be ready
|
||||
uses: nywilken/wait-for-vercel-preview@master
|
||||
id: waitForVercelPreviewDeployment
|
||||
with:
|
||||
token: ${{ secrets.GITHUB_TOKEN }}
|
||||
max_timeout: 600 # in seconds, set really high to leverage job timeout-minutes values
|
||||
allow_inactive: true # needed to ensure we get a URL for a previously released deployment
|
||||
markdown-link-check:
|
||||
needs: vercel-deployment-poll
|
||||
if: ${{ needs.vercel-deployment-poll.outputs.url != '' }}
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/checkout@master
|
||||
- uses: gaurav-nelson/github-action-markdown-link-check@v1
|
||||
- name: Get Deployment URL
|
||||
run:
|
||||
echo "DEPLOYMENT_URL=${{ needs.vercel-deployment-poll.outputs.url }}" >> $GITHUB_ENV
|
||||
- name: Checkout source branch
|
||||
uses: actions/checkout@master
|
||||
- name: Check links
|
||||
uses: gaurav-nelson/github-action-markdown-link-check@v1
|
||||
with:
|
||||
use-quiet-mode: 'yes'
|
||||
file-extension: 'mdx'
|
||||
check-modified-files-only: 'yes'
|
||||
folder-path: 'website/content'
|
||||
|
||||
|
||||
@@ -6,6 +6,9 @@ jobs:
|
||||
markdown-link-check:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- name: Set deployment URL env
|
||||
run:
|
||||
echo "DEPLOYMENT_URL=https://packer-git-master.hashicorp.vercel.app" >> $GITHUB_ENV
|
||||
- uses: actions/checkout@master
|
||||
- uses: gaurav-nelson/github-action-markdown-link-check@v1
|
||||
with:
|
||||
|
||||
@@ -162,7 +162,7 @@ type Config struct {
|
||||
//filter, but will cause Packer to fail if the `source_ami` does not exist.
|
||||
SourceAmiFilter awscommon.AmiFilterOptions `mapstructure:"source_ami_filter" required:"false"`
|
||||
// Key/value pair tags to apply to the volumes that are *launched*. This is
|
||||
// a [template engine](/docs/templates/engine), see [Build template
|
||||
// a [template engine](/docs/templates/legacy_json_templates/engine), see [Build template
|
||||
// data](#build-template-data) for more information.
|
||||
RootVolumeTags map[string]string `mapstructure:"root_volume_tags" required:"false"`
|
||||
// Same as [`root_volume_tags`](#root_volume_tags) but defined as a
|
||||
|
||||
@@ -16,11 +16,11 @@ type AMIConfig struct {
|
||||
// The name of the resulting AMI that will appear when managing AMIs in the
|
||||
// AWS console or via APIs. This must be unique. To help make this unique,
|
||||
// use a function like timestamp (see [template
|
||||
// engine](/docs/templates/engine) for more info).
|
||||
// engine](/docs/templates/legacy_json_templates/engine) for more info).
|
||||
AMIName string `mapstructure:"ami_name" required:"true"`
|
||||
// The description to set for the resulting
|
||||
// AMI(s). By default this description is empty. This is a
|
||||
// [template engine](/docs/templates/engine), see [Build template
|
||||
// [template engine](/docs/templates/legacy_json_templates/engine), see [Build template
|
||||
// data](#build-template-data) for more information.
|
||||
AMIDescription string `mapstructure:"ami_description" required:"false"`
|
||||
// The type of virtualization for the AMI
|
||||
@@ -47,7 +47,7 @@ type AMIConfig struct {
|
||||
// validation of the ami_regions configuration option. Default false.
|
||||
AMISkipRegionValidation bool `mapstructure:"skip_region_validation" required:"false"`
|
||||
// Key/value pair tags applied to the AMI. This is a [template
|
||||
// engine](/docs/templates/engine), see [Build template
|
||||
// engine](/docs/templates/legacy_json_templates/engine), see [Build template
|
||||
// data](#build-template-data) for more information.
|
||||
AMITags map[string]string `mapstructure:"tags" required:"false"`
|
||||
// Same as [`tags`](#tags) but defined as a singular repeatable block
|
||||
@@ -137,7 +137,7 @@ type AMIConfig struct {
|
||||
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/engine), see [Build 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
|
||||
|
||||
@@ -76,7 +76,7 @@ type BlockDevice struct {
|
||||
// See the documentation on
|
||||
// [IOPs](https://docs.aws.amazon.com/AWSEC2/latest/APIReference/API_EbsBlockDevice.html)
|
||||
// for more information
|
||||
IOPS int64 `mapstructure:"iops" required:"false"`
|
||||
IOPS *int64 `mapstructure:"iops" required:"false"`
|
||||
// Suppresses the specified device included in the block device mapping of
|
||||
// the AMI.
|
||||
NoDevice bool `mapstructure:"no_device" required:"false"`
|
||||
@@ -86,7 +86,7 @@ type BlockDevice struct {
|
||||
// See the documentation on
|
||||
// [Throughput](https://docs.aws.amazon.com/AWSEC2/latest/APIReference/API_EbsBlockDevice.html)
|
||||
// for more information
|
||||
Throughput int64 `mapstructure:"throughput" required:"false"`
|
||||
Throughput *int64 `mapstructure:"throughput" required:"false"`
|
||||
// The virtual device name. See the documentation on Block Device Mapping
|
||||
// for more information.
|
||||
VirtualName string `mapstructure:"virtual_name" required:"false"`
|
||||
@@ -150,12 +150,12 @@ func (blockDevice BlockDevice) BuildEC2BlockDeviceMapping() *ec2.BlockDeviceMapp
|
||||
|
||||
switch blockDevice.VolumeType {
|
||||
case "io1", "io2", "gp3":
|
||||
ebsBlockDevice.Iops = aws.Int64(blockDevice.IOPS)
|
||||
ebsBlockDevice.Iops = blockDevice.IOPS
|
||||
}
|
||||
|
||||
// Throughput is only valid for gp3 types
|
||||
if blockDevice.VolumeType == "gp3" {
|
||||
ebsBlockDevice.Throughput = aws.Int64(blockDevice.Throughput)
|
||||
ebsBlockDevice.Throughput = blockDevice.Throughput
|
||||
}
|
||||
|
||||
// You cannot specify Encrypted if you specify a Snapshot ID
|
||||
@@ -191,28 +191,28 @@ func (b *BlockDevice) Prepare(ctx *interpolate.Context) error {
|
||||
}
|
||||
|
||||
if ratio, ok := iopsRatios[b.VolumeType]; b.VolumeSize != 0 && ok {
|
||||
if b.IOPS/b.VolumeSize > ratio {
|
||||
if b.IOPS != nil && (*b.IOPS/b.VolumeSize > ratio) {
|
||||
return fmt.Errorf("%s: the maximum ratio of provisioned IOPS to requested volume size "+
|
||||
"(in GiB) is %v:1 for %s volumes", b.DeviceName, ratio, b.VolumeType)
|
||||
}
|
||||
|
||||
if b.IOPS < minIops || b.IOPS > maxIops {
|
||||
if b.IOPS != nil && (*b.IOPS < minIops || *b.IOPS > maxIops) {
|
||||
return fmt.Errorf("IOPS must be between %d and %d for device %s",
|
||||
minIops, maxIops, b.DeviceName)
|
||||
}
|
||||
}
|
||||
|
||||
if b.VolumeType == "gp3" {
|
||||
if b.Throughput < minThroughput || b.Throughput > maxThroughput {
|
||||
if b.Throughput != nil && (*b.Throughput < minThroughput || *b.Throughput > maxThroughput) {
|
||||
return fmt.Errorf("Throughput must be between %d and %d for device %s",
|
||||
minThroughput, maxThroughput, b.DeviceName)
|
||||
}
|
||||
|
||||
if b.IOPS < minIopsGp3 || b.IOPS > maxIopsGp3 {
|
||||
if b.IOPS != nil && (*b.IOPS < minIopsGp3 || *b.IOPS > maxIopsGp3) {
|
||||
return fmt.Errorf("IOPS must be between %d and %d for device %s",
|
||||
minIopsGp3, maxIopsGp3, b.DeviceName)
|
||||
}
|
||||
} else if b.Throughput > 0 {
|
||||
} else if b.Throughput != nil {
|
||||
return fmt.Errorf("Throughput is not available for device %s",
|
||||
b.DeviceName)
|
||||
}
|
||||
|
||||
@@ -54,7 +54,7 @@ func TestBlockDevice(t *testing.T) {
|
||||
VolumeType: "io1",
|
||||
VolumeSize: 8,
|
||||
DeleteOnTermination: true,
|
||||
IOPS: 1000,
|
||||
IOPS: aws.Int64(1000),
|
||||
},
|
||||
|
||||
Result: &ec2.BlockDeviceMapping{
|
||||
@@ -73,7 +73,7 @@ func TestBlockDevice(t *testing.T) {
|
||||
VolumeType: "io2",
|
||||
VolumeSize: 8,
|
||||
DeleteOnTermination: true,
|
||||
IOPS: 1000,
|
||||
IOPS: aws.Int64(1000),
|
||||
},
|
||||
|
||||
Result: &ec2.BlockDeviceMapping{
|
||||
@@ -168,8 +168,8 @@ func TestBlockDevice(t *testing.T) {
|
||||
DeviceName: "/dev/sdb",
|
||||
VolumeType: "gp3",
|
||||
VolumeSize: 8,
|
||||
Throughput: 125,
|
||||
IOPS: 3000,
|
||||
Throughput: aws.Int64(125),
|
||||
IOPS: aws.Int64(3000),
|
||||
DeleteOnTermination: true,
|
||||
Encrypted: config.TriTrue,
|
||||
},
|
||||
@@ -219,7 +219,7 @@ func TestIOPSValidation(t *testing.T) {
|
||||
device: BlockDevice{
|
||||
DeviceName: "/dev/sdb",
|
||||
VolumeType: "io1",
|
||||
IOPS: 1000,
|
||||
IOPS: aws.Int64(1000),
|
||||
},
|
||||
ok: true,
|
||||
},
|
||||
@@ -227,7 +227,7 @@ func TestIOPSValidation(t *testing.T) {
|
||||
device: BlockDevice{
|
||||
DeviceName: "/dev/sdb",
|
||||
VolumeType: "io2",
|
||||
IOPS: 1000,
|
||||
IOPS: aws.Int64(1000),
|
||||
},
|
||||
ok: true,
|
||||
},
|
||||
@@ -237,7 +237,7 @@ func TestIOPSValidation(t *testing.T) {
|
||||
DeviceName: "/dev/sdb",
|
||||
VolumeType: "io1",
|
||||
VolumeSize: 50,
|
||||
IOPS: 1000,
|
||||
IOPS: aws.Int64(1000),
|
||||
},
|
||||
ok: true,
|
||||
},
|
||||
@@ -246,7 +246,7 @@ func TestIOPSValidation(t *testing.T) {
|
||||
DeviceName: "/dev/sdb",
|
||||
VolumeType: "io2",
|
||||
VolumeSize: 100,
|
||||
IOPS: 1000,
|
||||
IOPS: aws.Int64(1000),
|
||||
},
|
||||
ok: true,
|
||||
},
|
||||
@@ -256,7 +256,7 @@ func TestIOPSValidation(t *testing.T) {
|
||||
DeviceName: "/dev/sdb",
|
||||
VolumeType: "io1",
|
||||
VolumeSize: 10,
|
||||
IOPS: 2000,
|
||||
IOPS: aws.Int64(2000),
|
||||
},
|
||||
ok: false,
|
||||
msg: "/dev/sdb: the maximum ratio of provisioned IOPS to requested volume size (in GiB) is 50:1 for io1 volumes",
|
||||
@@ -266,7 +266,7 @@ func TestIOPSValidation(t *testing.T) {
|
||||
DeviceName: "/dev/sdb",
|
||||
VolumeType: "io2",
|
||||
VolumeSize: 50,
|
||||
IOPS: 30000,
|
||||
IOPS: aws.Int64(30000),
|
||||
},
|
||||
ok: false,
|
||||
msg: "/dev/sdb: the maximum ratio of provisioned IOPS to requested volume size (in GiB) is 500:1 for io2 volumes",
|
||||
@@ -277,7 +277,7 @@ func TestIOPSValidation(t *testing.T) {
|
||||
DeviceName: "/dev/sdb",
|
||||
VolumeType: "io2",
|
||||
VolumeSize: 500,
|
||||
IOPS: 99999,
|
||||
IOPS: aws.Int64(99999),
|
||||
},
|
||||
ok: false,
|
||||
msg: "IOPS must be between 100 and 64000 for device /dev/sdb",
|
||||
@@ -288,7 +288,7 @@ func TestIOPSValidation(t *testing.T) {
|
||||
DeviceName: "/dev/sdb",
|
||||
VolumeType: "io2",
|
||||
VolumeSize: 50,
|
||||
IOPS: 10,
|
||||
IOPS: aws.Int64(10),
|
||||
},
|
||||
ok: false,
|
||||
msg: "IOPS must be between 100 and 64000 for device /dev/sdb",
|
||||
@@ -299,8 +299,8 @@ func TestIOPSValidation(t *testing.T) {
|
||||
DeviceName: "/dev/sdb",
|
||||
VolumeType: "gp3",
|
||||
VolumeSize: 50,
|
||||
Throughput: 125,
|
||||
IOPS: 99999,
|
||||
Throughput: aws.Int64(125),
|
||||
IOPS: aws.Int64(99999),
|
||||
},
|
||||
ok: false,
|
||||
msg: "IOPS must be between 3000 and 16000 for device /dev/sdb",
|
||||
@@ -311,8 +311,8 @@ func TestIOPSValidation(t *testing.T) {
|
||||
DeviceName: "/dev/sdb",
|
||||
VolumeType: "gp3",
|
||||
VolumeSize: 50,
|
||||
Throughput: 125,
|
||||
IOPS: 10,
|
||||
Throughput: aws.Int64(125),
|
||||
IOPS: aws.Int64(10),
|
||||
},
|
||||
ok: false,
|
||||
msg: "IOPS must be between 3000 and 16000 for device /dev/sdb",
|
||||
@@ -346,8 +346,8 @@ func TestThroughputValidation(t *testing.T) {
|
||||
device: BlockDevice{
|
||||
DeviceName: "/dev/sdb",
|
||||
VolumeType: "gp3",
|
||||
Throughput: 125,
|
||||
IOPS: 3000,
|
||||
Throughput: aws.Int64(125),
|
||||
IOPS: aws.Int64(3000),
|
||||
},
|
||||
ok: true,
|
||||
},
|
||||
@@ -355,8 +355,8 @@ func TestThroughputValidation(t *testing.T) {
|
||||
device: BlockDevice{
|
||||
DeviceName: "/dev/sdb",
|
||||
VolumeType: "gp3",
|
||||
Throughput: 1000,
|
||||
IOPS: 3000,
|
||||
Throughput: aws.Int64(1000),
|
||||
IOPS: aws.Int64(3000),
|
||||
},
|
||||
ok: true,
|
||||
},
|
||||
@@ -365,8 +365,8 @@ func TestThroughputValidation(t *testing.T) {
|
||||
device: BlockDevice{
|
||||
DeviceName: "/dev/sdb",
|
||||
VolumeType: "gp3",
|
||||
Throughput: 1001,
|
||||
IOPS: 3000,
|
||||
Throughput: aws.Int64(1001),
|
||||
IOPS: aws.Int64(3000),
|
||||
},
|
||||
ok: false,
|
||||
msg: "Throughput must be between 125 and 1000 for device /dev/sdb",
|
||||
@@ -376,8 +376,8 @@ func TestThroughputValidation(t *testing.T) {
|
||||
device: BlockDevice{
|
||||
DeviceName: "/dev/sdb",
|
||||
VolumeType: "gp3",
|
||||
Throughput: 124,
|
||||
IOPS: 3000,
|
||||
Throughput: aws.Int64(124),
|
||||
IOPS: aws.Int64(3000),
|
||||
},
|
||||
ok: false,
|
||||
msg: "Throughput must be between 125 and 1000 for device /dev/sdb",
|
||||
|
||||
@@ -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 {
|
||||
@@ -176,7 +190,7 @@ type RunConfig struct {
|
||||
SecurityGroupFilter SecurityGroupFilterOptions `mapstructure:"security_group_filter" required:"false"`
|
||||
// Key/value pair tags to apply to the instance that is that is *launched*
|
||||
// to create the EBS volumes. This is a [template
|
||||
// engine](/docs/templates/engine), see [Build template
|
||||
// engine](/docs/templates/legacy_json_templates/engine), see [Build template
|
||||
// data](#build-template-data) for more information.
|
||||
RunTags map[string]string `mapstructure:"run_tags" required:"false"`
|
||||
// Same as [`run_tags`](#run_tags) but defined as a singular repeatable
|
||||
@@ -196,8 +210,7 @@ type RunConfig struct {
|
||||
SecurityGroupIds []string `mapstructure:"security_group_ids" required:"false"`
|
||||
// The source AMI whose root volume will be copied and
|
||||
// provisioned on the currently running instance. This must be an EBS-backed
|
||||
// AMI with a root volume snapshot that you have access to. Note: this is not
|
||||
// used when from_scratch is set to true.
|
||||
// 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:
|
||||
@@ -427,6 +440,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"`
|
||||
|
||||
@@ -487,6 +503,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 {
|
||||
|
||||
@@ -24,6 +24,7 @@ type StepAMIRegionCopy struct {
|
||||
|
||||
toDelete string
|
||||
getRegionConn func(*AccessConfig, string) (ec2iface.EC2API, error)
|
||||
AMISkipCreateImage bool
|
||||
AMISkipBuildRegion bool
|
||||
}
|
||||
|
||||
@@ -63,6 +64,12 @@ func (s *StepAMIRegionCopy) DeduplicateRegions(intermediary bool) {
|
||||
|
||||
func (s *StepAMIRegionCopy) Run(ctx context.Context, state multistep.StateBag) multistep.StepAction {
|
||||
ui := state.Get("ui").(packersdk.Ui)
|
||||
|
||||
if s.AMISkipCreateImage {
|
||||
ui.Say("Skipping AMI region copy...")
|
||||
return multistep.ActionContinue
|
||||
}
|
||||
|
||||
amis := state.Get("amis").(map[string]string)
|
||||
snapshots := state.Get("snapshots").(map[string][]string)
|
||||
intermediary, _ := state.Get("intermediary_image").(bool)
|
||||
|
||||
@@ -16,6 +16,8 @@ import (
|
||||
)
|
||||
|
||||
type StepCreateTags struct {
|
||||
AMISkipCreateImage bool
|
||||
|
||||
Tags map[string]string
|
||||
SnapshotTags map[string]string
|
||||
Ctx interpolate.Context
|
||||
@@ -25,6 +27,12 @@ func (s *StepCreateTags) Run(ctx context.Context, state multistep.StateBag) mult
|
||||
ec2conn := state.Get("ec2").(*ec2.EC2)
|
||||
session := state.Get("awsSession").(*session.Session)
|
||||
ui := state.Get("ui").(packersdk.Ui)
|
||||
|
||||
if s.AMISkipCreateImage {
|
||||
ui.Say("Skipping AMI create tags...")
|
||||
return multistep.ActionContinue
|
||||
}
|
||||
|
||||
amis := state.Get("amis").(map[string]string)
|
||||
|
||||
if len(s.Tags) == 0 && len(s.SnapshotTags) == 0 {
|
||||
|
||||
@@ -14,6 +14,8 @@ import (
|
||||
)
|
||||
|
||||
type StepModifyAMIAttributes struct {
|
||||
AMISkipCreateImage bool
|
||||
|
||||
Users []string
|
||||
Groups []string
|
||||
SnapshotUsers []string
|
||||
@@ -29,6 +31,12 @@ func (s *StepModifyAMIAttributes) Run(ctx context.Context, state multistep.State
|
||||
ec2conn := state.Get("ec2").(*ec2.EC2)
|
||||
session := state.Get("awsSession").(*session.Session)
|
||||
ui := state.Get("ui").(packersdk.Ui)
|
||||
|
||||
if s.AMISkipCreateImage {
|
||||
ui.Say("Skipping AMI modify attributes...")
|
||||
return multistep.ActionContinue
|
||||
}
|
||||
|
||||
amis := state.Get("amis").(map[string]string)
|
||||
snapshots := state.Get("snapshots").(map[string][]string)
|
||||
|
||||
|
||||
@@ -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
|
||||
|
||||
|
||||
@@ -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
|
||||
@@ -127,6 +130,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 != "" {
|
||||
|
||||
@@ -34,6 +34,9 @@ type Config struct {
|
||||
awscommon.AccessConfig `mapstructure:",squash"`
|
||||
awscommon.AMIConfig `mapstructure:",squash"`
|
||||
awscommon.RunConfig `mapstructure:",squash"`
|
||||
// If true, Packer will not create the AMI. Useful for setting to `true`
|
||||
// during a build test stage. Default `false`.
|
||||
AMISkipCreateImage bool `mapstructure:"skip_create_ami" required:"false"`
|
||||
// Add one or more block device mappings to the AMI. These will be attached
|
||||
// when booting a new instance from your AMI. To add a block device during
|
||||
// the Packer build see `launch_block_device_mappings` below. Your options
|
||||
@@ -53,7 +56,7 @@ type Config struct {
|
||||
// Tags to apply to the volumes that are *launched* to create the AMI.
|
||||
// These tags are *not* applied to the resulting AMI unless they're
|
||||
// duplicated in `tags`. This is a [template
|
||||
// engine](/docs/templates/engine), see [Build template
|
||||
// engine](/docs/templates/legacy_json_templates/engine), see [Build template
|
||||
// data](#build-template-data) for more information.
|
||||
VolumeRunTags map[string]string `mapstructure:"run_volume_tags"`
|
||||
// Same as [`run_volume_tags`](#run_volume_tags) but defined as a singular
|
||||
@@ -184,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,
|
||||
@@ -208,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(),
|
||||
@@ -319,6 +328,7 @@ func (b *Builder) Run(ctx context.Context, ui packersdk.Ui, hook packersdk.Hook)
|
||||
Regions: b.config.AMIRegions,
|
||||
},
|
||||
&stepCreateAMI{
|
||||
AMISkipCreateImage: b.config.AMISkipCreateImage,
|
||||
AMISkipBuildRegion: b.config.AMISkipBuildRegion,
|
||||
PollingConfig: b.config.PollingConfig,
|
||||
},
|
||||
@@ -330,22 +340,25 @@ func (b *Builder) Run(ctx context.Context, ui packersdk.Ui, hook packersdk.Hook)
|
||||
EncryptBootVolume: b.config.AMIEncryptBootVolume,
|
||||
Name: b.config.AMIName,
|
||||
OriginalRegion: *ec2conn.Config.Region,
|
||||
AMISkipCreateImage: b.config.AMISkipCreateImage,
|
||||
AMISkipBuildRegion: b.config.AMISkipBuildRegion,
|
||||
},
|
||||
&awscommon.StepModifyAMIAttributes{
|
||||
Description: b.config.AMIDescription,
|
||||
Users: b.config.AMIUsers,
|
||||
Groups: b.config.AMIGroups,
|
||||
ProductCodes: b.config.AMIProductCodes,
|
||||
SnapshotUsers: b.config.SnapshotUsers,
|
||||
SnapshotGroups: b.config.SnapshotGroups,
|
||||
Ctx: b.config.ctx,
|
||||
GeneratedData: generatedData,
|
||||
AMISkipCreateImage: b.config.AMISkipCreateImage,
|
||||
Description: b.config.AMIDescription,
|
||||
Users: b.config.AMIUsers,
|
||||
Groups: b.config.AMIGroups,
|
||||
ProductCodes: b.config.AMIProductCodes,
|
||||
SnapshotUsers: b.config.SnapshotUsers,
|
||||
SnapshotGroups: b.config.SnapshotGroups,
|
||||
Ctx: b.config.ctx,
|
||||
GeneratedData: generatedData,
|
||||
},
|
||||
&awscommon.StepCreateTags{
|
||||
Tags: b.config.AMITags,
|
||||
SnapshotTags: b.config.SnapshotTags,
|
||||
Ctx: b.config.ctx,
|
||||
AMISkipCreateImage: b.config.AMISkipCreateImage,
|
||||
Tags: b.config.AMITags,
|
||||
SnapshotTags: b.config.SnapshotTags,
|
||||
Ctx: b.config.ctx,
|
||||
},
|
||||
}
|
||||
|
||||
|
||||
@@ -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"`
|
||||
@@ -142,6 +143,7 @@ type FlatConfig struct {
|
||||
SSHInterface *string `mapstructure:"ssh_interface" cty:"ssh_interface" hcl:"ssh_interface"`
|
||||
PauseBeforeSSM *string `mapstructure:"pause_before_ssm" cty:"pause_before_ssm" hcl:"pause_before_ssm"`
|
||||
SessionManagerPort *int `mapstructure:"session_manager_port" cty:"session_manager_port" hcl:"session_manager_port"`
|
||||
AMISkipCreateImage *bool `mapstructure:"skip_create_ami" required:"false" cty:"skip_create_ami" hcl:"skip_create_ami"`
|
||||
AMIMappings []common.FlatBlockDevice `mapstructure:"ami_block_device_mappings" required:"false" cty:"ami_block_device_mappings" hcl:"ami_block_device_mappings"`
|
||||
LaunchMappings []common.FlatBlockDevice `mapstructure:"launch_block_device_mappings" required:"false" cty:"launch_block_device_mappings" hcl:"launch_block_device_mappings"`
|
||||
VolumeRunTags map[string]string `mapstructure:"run_volume_tags" cty:"run_volume_tags" hcl:"run_volume_tags"`
|
||||
@@ -239,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},
|
||||
@@ -291,6 +294,7 @@ func (*FlatConfig) HCL2Spec() map[string]hcldec.Spec {
|
||||
"ssh_interface": &hcldec.AttrSpec{Name: "ssh_interface", Type: cty.String, Required: false},
|
||||
"pause_before_ssm": &hcldec.AttrSpec{Name: "pause_before_ssm", Type: cty.String, Required: false},
|
||||
"session_manager_port": &hcldec.AttrSpec{Name: "session_manager_port", Type: cty.Number, Required: false},
|
||||
"skip_create_ami": &hcldec.AttrSpec{Name: "skip_create_ami", Type: cty.Bool, Required: false},
|
||||
"ami_block_device_mappings": &hcldec.BlockListSpec{TypeName: "ami_block_device_mappings", Nested: hcldec.ObjectSpec((*common.FlatBlockDevice)(nil).HCL2Spec())},
|
||||
"launch_block_device_mappings": &hcldec.BlockListSpec{TypeName: "launch_block_device_mappings", Nested: hcldec.ObjectSpec((*common.FlatBlockDevice)(nil).HCL2Spec())},
|
||||
"run_volume_tags": &hcldec.AttrSpec{Name: "run_volume_tags", Type: cty.Map(cty.String), Required: false},
|
||||
|
||||
@@ -19,6 +19,7 @@ import (
|
||||
type stepCreateAMI struct {
|
||||
PollingConfig *awscommon.AWSPollingConfig
|
||||
image *ec2.Image
|
||||
AMISkipCreateImage bool
|
||||
AMISkipBuildRegion bool
|
||||
}
|
||||
|
||||
@@ -28,6 +29,11 @@ func (s *stepCreateAMI) Run(ctx context.Context, state multistep.StateBag) multi
|
||||
instance := state.Get("instance").(*ec2.Instance)
|
||||
ui := state.Get("ui").(packersdk.Ui)
|
||||
|
||||
if s.AMISkipCreateImage {
|
||||
ui.Say("Skipping AMI creation...")
|
||||
return multistep.ActionContinue
|
||||
}
|
||||
|
||||
// Create the image
|
||||
amiName := config.AMIName
|
||||
state.Put("intermediary_image", false)
|
||||
|
||||
@@ -59,7 +59,7 @@ type Config struct {
|
||||
// Tags to apply to the volumes that are *launched* to create the AMI.
|
||||
// These tags are *not* applied to the resulting AMI unless they're
|
||||
// duplicated in `tags`. This is a [template
|
||||
// engine](/docs/templates/engine), see [Build template
|
||||
// engine](/docs/templates/legacy_json_templates/engine), see [Build template
|
||||
// data](#build-template-data) for more information.
|
||||
VolumeRunTags map[string]string `mapstructure:"run_volume_tags"`
|
||||
// Same as [`run_volume_tags`](#run_volume_tags) but defined as a singular
|
||||
@@ -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},
|
||||
|
||||
@@ -12,7 +12,7 @@ import (
|
||||
type BlockDevice struct {
|
||||
awscommon.BlockDevice `mapstructure:",squash"`
|
||||
// Key/value pair tags to apply to the volume. These are retained after the builder
|
||||
// completes. This is a [template engine](/docs/templates/engine), see
|
||||
// completes. This is a [template engine](/docs/templates/legacy_json_templates/engine), see
|
||||
// [Build template data](#build-template-data) for more information.
|
||||
Tags map[string]string `mapstructure:"tags" required:"false"`
|
||||
// Same as [`tags`](#tags) but defined as a singular repeatable block
|
||||
|
||||
@@ -58,7 +58,7 @@ type Config struct {
|
||||
// *launched* to create EBS Volumes. These tags will *not* appear in the
|
||||
// tags of the resulting EBS volumes unless they're duplicated under `tags`
|
||||
// in the `ebs_volumes` setting. This is a [template
|
||||
// engine](/docs/templates/engine), see [Build template
|
||||
// engine](/docs/templates/legacy_json_templates/engine), see [Build template
|
||||
// data](#build-template-data) for more information.
|
||||
//
|
||||
// Note: The tags specified here will be *temporarily* applied to volumes
|
||||
@@ -195,6 +195,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 +221,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(),
|
||||
|
||||
@@ -115,6 +115,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 +243,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},
|
||||
|
||||
@@ -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},
|
||||
|
||||
@@ -121,14 +121,14 @@ type Config struct {
|
||||
// provisioners should connect to the local IP address of the instance.
|
||||
UseLocalIPAddress bool `mapstructure:"use_local_ip_address" required:"false"`
|
||||
// User data to launch with the instance. This is a
|
||||
// template engine; see "User Data" bellow for
|
||||
// template engine; see "User Data" below for
|
||||
// more details. Packer will not automatically wait for a user script to
|
||||
// finish before shutting down the instance this must be handled in a
|
||||
// provisioner.
|
||||
UserData string `mapstructure:"user_data" required:"false"`
|
||||
// Path to a file that will be used for the user
|
||||
// data when launching the instance. This file will be parsed as a template
|
||||
// engine see User Data bellow for more
|
||||
// engine see User Data below for more
|
||||
// details.
|
||||
UserDataFile string `mapstructure:"user_data_file" required:"false"`
|
||||
// The name or ID of the zone where the instance will be
|
||||
|
||||
@@ -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)
|
||||
}
|
||||
|
||||
@@ -260,7 +260,7 @@ func (s *stepRun) getDeviceAndDriveArgs(config *Config, state multistep.StateBag
|
||||
}
|
||||
for i, cdPath := range cdPaths {
|
||||
if config.CDROMInterface == "" {
|
||||
driveArgs = append(driveArgs, fmt.Sprintf("file=%s,index=%d,media=cdrom", cdPath, i))
|
||||
driveArgs = append(driveArgs, fmt.Sprintf("file=%s,media=cdrom", cdPath))
|
||||
} else if config.CDROMInterface == "virtio-scsi" {
|
||||
driveArgs = append(driveArgs, fmt.Sprintf("file=%s,if=none,index=%d,id=cdrom%d,media=cdrom", cdPath, i, i))
|
||||
deviceArgs = append(deviceArgs, "virtio-scsi-device", fmt.Sprintf("scsi-cd,drive=cdrom%d", i))
|
||||
|
||||
@@ -52,7 +52,7 @@ func Test_UserOverrides(t *testing.T) {
|
||||
},
|
||||
[]string{
|
||||
"-display", "gtk",
|
||||
"-drive", "file=/path/to/test.iso,index=0,media=cdrom",
|
||||
"-drive", "file=/path/to/test.iso,media=cdrom",
|
||||
"-randomflag1", "127.0.0.1-1234-http/directory",
|
||||
"-randomflag2", "output/directory-myvm",
|
||||
"-device", ",netdev=user.0",
|
||||
@@ -66,7 +66,7 @@ func Test_UserOverrides(t *testing.T) {
|
||||
},
|
||||
[]string{
|
||||
"-display", "partydisplay",
|
||||
"-drive", "file=/path/to/test.iso,index=0,media=cdrom",
|
||||
"-drive", "file=/path/to/test.iso,media=cdrom",
|
||||
"-device", ",netdev=user.0",
|
||||
},
|
||||
"User input overrides default, rest is populated as normal",
|
||||
@@ -81,7 +81,7 @@ func Test_UserOverrides(t *testing.T) {
|
||||
"-display", "gtk",
|
||||
"-device", "somerandomdevice",
|
||||
"-device", "mynetdevice,netdev=user.0",
|
||||
"-drive", "file=/path/to/test.iso,index=0,media=cdrom",
|
||||
"-drive", "file=/path/to/test.iso,media=cdrom",
|
||||
},
|
||||
"Net device gets added",
|
||||
},
|
||||
@@ -135,7 +135,7 @@ func Test_DriveAndDeviceArgs(t *testing.T) {
|
||||
[]string{
|
||||
"-display", "gtk",
|
||||
"-boot", "once=d",
|
||||
"-drive", "file=/path/to/test.iso,index=0,media=cdrom",
|
||||
"-drive", "file=/path/to/test.iso,media=cdrom",
|
||||
},
|
||||
"Boot value should default to once=d when diskImage isn't set",
|
||||
},
|
||||
@@ -164,7 +164,7 @@ func Test_DriveAndDeviceArgs(t *testing.T) {
|
||||
"-device", "virtio-scsi-pci,id=scsi0",
|
||||
"-device", "scsi-hd,bus=scsi0.0,drive=drive0",
|
||||
"-drive", "if=none,file=path_to_output,id=drive0,cache=writeback,discard=,format=qcow2",
|
||||
"-drive", "file=fake_cd_path.iso,index=0,media=cdrom",
|
||||
"-drive", "file=fake_cd_path.iso,media=cdrom",
|
||||
},
|
||||
"virtio-scsi interface, DiskImage true, extra cdrom, detectZeroes off",
|
||||
},
|
||||
@@ -193,7 +193,7 @@ func Test_DriveAndDeviceArgs(t *testing.T) {
|
||||
"-device", "virtio-scsi-pci,id=scsi0",
|
||||
"-device", "scsi-hd,bus=scsi0.0,drive=drive0",
|
||||
"-drive", "if=none,file=path_to_output,id=drive0,cache=writeback,discard=,format=qcow2,detect-zeroes=on",
|
||||
"-drive", "file=fake_cd_path.iso,index=0,media=cdrom",
|
||||
"-drive", "file=fake_cd_path.iso,media=cdrom",
|
||||
},
|
||||
"virtio-scsi interface, DiskImage true, extra cdrom, detectZeroes on",
|
||||
},
|
||||
@@ -224,8 +224,8 @@ func Test_DriveAndDeviceArgs(t *testing.T) {
|
||||
"-device", "scsi-hd,bus=scsi0.0,drive=drive1",
|
||||
"-drive", "if=none,file=qemupath1,id=drive0,cache=writeback,discard=,format=qcow2",
|
||||
"-drive", "if=none,file=qemupath2,id=drive1,cache=writeback,discard=,format=qcow2",
|
||||
"-drive", "file=/path/to/test.iso,index=0,media=cdrom",
|
||||
"-drive", "file=fake_cd_path.iso,index=1,media=cdrom",
|
||||
"-drive", "file=/path/to/test.iso,media=cdrom",
|
||||
"-drive", "file=fake_cd_path.iso,media=cdrom",
|
||||
},
|
||||
"virtio-scsi interface, bootable iso, cdrom",
|
||||
},
|
||||
@@ -256,8 +256,8 @@ func Test_DriveAndDeviceArgs(t *testing.T) {
|
||||
"-device", "scsi-hd,bus=scsi0.0,drive=drive1",
|
||||
"-drive", "if=none,file=qemupath1,id=drive0,cache=writeback,discard=,format=qcow2,detect-zeroes=on",
|
||||
"-drive", "if=none,file=qemupath2,id=drive1,cache=writeback,discard=,format=qcow2,detect-zeroes=on",
|
||||
"-drive", "file=/path/to/test.iso,index=0,media=cdrom",
|
||||
"-drive", "file=fake_cd_path.iso,index=1,media=cdrom",
|
||||
"-drive", "file=/path/to/test.iso,media=cdrom",
|
||||
"-drive", "file=fake_cd_path.iso,media=cdrom",
|
||||
},
|
||||
"virtio-scsi interface, DiskImage false, extra cdrom, detect zeroes on",
|
||||
},
|
||||
@@ -284,7 +284,7 @@ func Test_DriveAndDeviceArgs(t *testing.T) {
|
||||
"-device", "virtio-scsi-pci,id=scsi0",
|
||||
"-device", "scsi-hd,bus=scsi0.0,drive=drive0",
|
||||
"-drive", "if=none,file=output/dir/path/mydisk.qcow2,id=drive0,cache=writeback,discard=,format=qcow2,detect-zeroes=",
|
||||
"-drive", "file=/path/to/test.iso,index=0,media=cdrom",
|
||||
"-drive", "file=/path/to/test.iso,media=cdrom",
|
||||
},
|
||||
"virtio-scsi interface, DiskImage false, no extra disks or cds",
|
||||
},
|
||||
@@ -302,8 +302,8 @@ func Test_DriveAndDeviceArgs(t *testing.T) {
|
||||
"-display", "gtk",
|
||||
"-boot", "once=d",
|
||||
"-drive", "file=output/dir/path/mydisk.qcow2,if=,cache=,discard=,format=,detect-zeroes=",
|
||||
"-drive", "file=/path/to/test.iso,index=0,media=cdrom",
|
||||
"-drive", "file=fake_cd_path.iso,index=1,media=cdrom",
|
||||
"-drive", "file=/path/to/test.iso,media=cdrom",
|
||||
"-drive", "file=fake_cd_path.iso,media=cdrom",
|
||||
},
|
||||
"cd_path is set and DiskImage is false",
|
||||
},
|
||||
@@ -322,7 +322,7 @@ func Test_DriveAndDeviceArgs(t *testing.T) {
|
||||
"-display", "gtk",
|
||||
"-boot", "once=d",
|
||||
"-drive", "file=output/dir/path/mydisk.qcow2,if=,cache=,discard=,format=,detect-zeroes=",
|
||||
"-drive", "file=/path/to/test.iso,index=0,media=cdrom",
|
||||
"-drive", "file=/path/to/test.iso,media=cdrom",
|
||||
},
|
||||
"empty config",
|
||||
},
|
||||
@@ -345,7 +345,7 @@ func Test_DriveAndDeviceArgs(t *testing.T) {
|
||||
[]string{
|
||||
"-boot", "once=d",
|
||||
"-drive", "file=path_to_output,if=virtio,cache=writeback,format=qcow2",
|
||||
"-drive", "file=/path/to/test.iso,index=0,media=cdrom",
|
||||
"-drive", "file=/path/to/test.iso,media=cdrom",
|
||||
},
|
||||
"version less than 2",
|
||||
},
|
||||
@@ -369,8 +369,8 @@ func Test_DriveAndDeviceArgs(t *testing.T) {
|
||||
"-boot", "once=d",
|
||||
"-drive", "file=qemupath1,if=virtio,cache=writeback,discard=,format=qcow2,detect-zeroes=",
|
||||
"-drive", "file=qemupath2,if=virtio,cache=writeback,discard=,format=qcow2,detect-zeroes=",
|
||||
"-drive", "file=fake_cd_path.iso,index=1,media=cdrom",
|
||||
"-drive", "file=/path/to/test.iso,index=0,media=cdrom",
|
||||
"-drive", "file=fake_cd_path.iso,media=cdrom",
|
||||
"-drive", "file=/path/to/test.iso,media=cdrom",
|
||||
},
|
||||
"virtio interface with extra disks",
|
||||
},
|
||||
@@ -395,7 +395,7 @@ func Test_DriveAndDeviceArgs(t *testing.T) {
|
||||
"-display", "gtk",
|
||||
"-boot", "c",
|
||||
"-drive", "file=path_to_output,if=virtio,cache=writeback,discard=,format=qcow2,detect-zeroes=",
|
||||
"-drive", "file=fake_cd_path.iso,index=0,media=cdrom",
|
||||
"-drive", "file=fake_cd_path.iso,media=cdrom",
|
||||
},
|
||||
"virtio interface with disk image",
|
||||
},
|
||||
@@ -456,7 +456,7 @@ func Test_OptionalConfigOptionsGetSet(t *testing.T) {
|
||||
"-vnc", ":5,password",
|
||||
"-machine", "type=pc,accel=hvf",
|
||||
"-device", ",netdev=user.0",
|
||||
"-drive", "file=/path/to/test.iso,index=0,media=cdrom",
|
||||
"-drive", "file=/path/to/test.iso,media=cdrom",
|
||||
"-qmp", "unix:qmp_path,server,nowait",
|
||||
}
|
||||
|
||||
|
||||
@@ -33,7 +33,7 @@ type GuestAdditionsConfig struct {
|
||||
// where the VirtualBox guest additions ISO will be uploaded. By default this
|
||||
// is `VBoxGuestAdditions.iso` which should upload into the login directory of
|
||||
// the user. This is a [configuration
|
||||
// template](/docs/templates/engine) where the `Version`
|
||||
// template](/docs/templates/legacy_json_templates/engine) where the `Version`
|
||||
// variable is replaced with the VirtualBox version.
|
||||
GuestAdditionsPath string `mapstructure:"guest_additions_path"`
|
||||
// The SHA256 checksum of the guest
|
||||
|
||||
@@ -37,7 +37,7 @@ type VBoxManageConfig struct {
|
||||
// followed by the CPUs.
|
||||
// Each command itself is an array of strings, where each string is an argument to
|
||||
// `VBoxManage`. Each argument is treated as a [configuration
|
||||
// template](/docs/templates/engine). The only available
|
||||
// template](/docs/templates/legacy_json_templates/engine). The only available
|
||||
// variable is `Name` which is replaced with the unique name of the VM, which is
|
||||
// required for many VBoxManage calls.
|
||||
VBoxManage [][]string `mapstructure:"vboxmanage" required:"false"`
|
||||
|
||||
@@ -328,21 +328,27 @@ func (d *ESX5Driver) Verify() error {
|
||||
}
|
||||
|
||||
func (d *ESX5Driver) VerifyOvfTool(SkipExport, skipValidateCredentials bool) error {
|
||||
// We don't use ovftool if we aren't exporting a VM; return without error
|
||||
// if ovftool isn't on path.
|
||||
if SkipExport {
|
||||
return nil
|
||||
}
|
||||
|
||||
err := d.base.VerifyOvfTool(SkipExport, skipValidateCredentials)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if skipValidateCredentials {
|
||||
return nil
|
||||
}
|
||||
|
||||
log.Printf("Verifying that ovftool credentials are valid...")
|
||||
// check that password is valid by sending a dummy ovftool command
|
||||
// now, so that we don't fail for a simple mistake after a long
|
||||
// build
|
||||
ovftool := GetOVFTool()
|
||||
|
||||
if skipValidateCredentials {
|
||||
return nil
|
||||
}
|
||||
|
||||
if d.Password == "" {
|
||||
return fmt.Errorf("exporting the vm from esxi with ovftool requires " +
|
||||
"that you set a value for remote_password")
|
||||
|
||||
@@ -92,3 +92,13 @@ func TestESX5Driver_CommHost(t *testing.T) {
|
||||
t.Errorf("bad vm_address: %s", address.(string))
|
||||
}
|
||||
}
|
||||
|
||||
func TestESX5Driver_VerifyOvfTool(t *testing.T) {
|
||||
driver := ESX5Driver{}
|
||||
// should always skip validation if export is skipped, so this should always
|
||||
// pass even when ovftool is not installed.
|
||||
err := driver.VerifyOvfTool(true, false)
|
||||
if err != nil {
|
||||
t.Fatalf("shouldn't fail ever because should always skip check")
|
||||
}
|
||||
}
|
||||
|
||||
@@ -15,7 +15,7 @@ type ToolsConfig struct {
|
||||
ToolsUploadFlavor string `mapstructure:"tools_upload_flavor" required:"false"`
|
||||
// The path in the VM to upload the VMware tools. This only takes effect if
|
||||
// `tools_upload_flavor` is non-empty. This is a [configuration
|
||||
// template](/docs/templates/engine) that has a single valid variable:
|
||||
// template](/docs/templates/legacy_json_templates/engine) that has a single valid variable:
|
||||
// `Flavor`, which will be the value of `tools_upload_flavor`. By default
|
||||
// the upload path is set to `{{.Flavor}}.iso`. This setting is not used
|
||||
// when `remote_type` is `esx5`.
|
||||
|
||||
@@ -66,7 +66,7 @@ type Config struct {
|
||||
VMName string `mapstructure:"vm_name" required:"false"`
|
||||
|
||||
VMXDiskTemplatePath string `mapstructure:"vmx_disk_template_path"`
|
||||
// Path to a [configuration template](/docs/templates/engine) that
|
||||
// Path to a [configuration template](/docs/templates/legacy_json_templates/engine) that
|
||||
// defines the contents of the virtual machine VMX file for VMware. The
|
||||
// engine has access to the template variables `{{ .DiskNumber }}` and
|
||||
// `{{ .DiskName }}`.
|
||||
|
||||
@@ -2,34 +2,9 @@
|
||||
"builders": [
|
||||
{
|
||||
"type": "vsphere-iso",
|
||||
|
||||
"vcenter_server": "vcenter.vsphere65.test",
|
||||
"username": "root",
|
||||
"password": "jetbrains",
|
||||
"insecure_connection": true,
|
||||
|
||||
"vm_name": "alpine-{{timestamp}}",
|
||||
"host": "esxi-1.vsphere65.test",
|
||||
|
||||
"CPUs": 1,
|
||||
"RAM": 512,
|
||||
"CPUs": 1,
|
||||
"RAM": 512,
|
||||
"RAM_reserve_all": true,
|
||||
"disk_controller_type": "pvscsi",
|
||||
"disk_size": 1024,
|
||||
"disk_thin_provisioned": true,
|
||||
"network_card": "vmxnet3",
|
||||
|
||||
"guest_os_type": "other3xLinux64Guest",
|
||||
|
||||
"iso_paths": [
|
||||
"[datastore1] ISO/alpine-standard-3.8.2-x86_64.iso"
|
||||
],
|
||||
"floppy_files": [
|
||||
"{{template_dir}}/answerfile",
|
||||
"{{template_dir}}/setup.sh"
|
||||
],
|
||||
|
||||
"boot_wait": "15s",
|
||||
"boot_command": [
|
||||
"root<enter><wait>",
|
||||
"mount -t vfat /dev/fd0 /media/floppy<enter><wait>",
|
||||
@@ -47,16 +22,43 @@
|
||||
"mount -t vfat /dev/fd0 /media/floppy<enter><wait>",
|
||||
"/media/floppy/SETUP.SH<enter>"
|
||||
],
|
||||
|
||||
"boot_wait": "15s",
|
||||
"disk_controller_type": "pvscsi",
|
||||
"floppy_files": [
|
||||
"{{template_dir}}/answerfile",
|
||||
"{{template_dir}}/setup.sh"
|
||||
],
|
||||
"guest_os_type": "other3xLinux64Guest",
|
||||
"host": "esxi-1.vsphere65.test",
|
||||
"insecure_connection": true,
|
||||
"iso_paths": [
|
||||
"[datastore1] ISO/alpine-standard-3.8.2-x86_64.iso"
|
||||
],
|
||||
"network_adapters": [
|
||||
{
|
||||
"network_card": "vmxnet3"
|
||||
}
|
||||
],
|
||||
"password": "jetbrains",
|
||||
"ssh_password": "jetbrains",
|
||||
"ssh_username": "root",
|
||||
"ssh_password": "jetbrains"
|
||||
"storage": [
|
||||
{
|
||||
"disk_size": 1024,
|
||||
"disk_thin_provisioned": true
|
||||
}
|
||||
],
|
||||
"username": "root",
|
||||
"vcenter_server": "vcenter.vsphere65.test",
|
||||
"vm_name": "alpine-{{timestamp}}"
|
||||
}
|
||||
],
|
||||
|
||||
"provisioners": [
|
||||
{
|
||||
"type": "shell",
|
||||
"inline": ["ls /"]
|
||||
"inline": [
|
||||
"ls /"
|
||||
],
|
||||
"type": "shell"
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,44 @@
|
||||
# "timestamp" template function replacement
|
||||
locals { timestamp = regex_replace(timestamp(), "[- TZ:]", "") }
|
||||
|
||||
# source blocks are analogous to the "builders" in json templates. They are used
|
||||
# in build blocks. A build block runs provisioners and post-processors on a
|
||||
# source. Read the documentation for source blocks here:
|
||||
# https://www.packer.io/docs/templates/hcl_templates/blocks/source
|
||||
source "vsphere-iso" "autogenerated_1" {
|
||||
CPUs = 1
|
||||
RAM = 512
|
||||
RAM_reserve_all = true
|
||||
boot_command = ["root<enter><wait>", "mount -t vfat /dev/fd0 /media/floppy<enter><wait>", "setup-alpine -f /media/floppy/answerfile<enter>", "<wait5>", "jetbrains<enter>", "jetbrains<enter>", "<wait5>", "y<enter>", "<wait10><wait10><wait10><wait10>", "reboot<enter>", "<wait10><wait10>", "root<enter>", "jetbrains<enter><wait>", "mount -t vfat /dev/fd0 /media/floppy<enter><wait>", "/media/floppy/SETUP.SH<enter>"]
|
||||
boot_wait = "15s"
|
||||
disk_controller_type = ["pvscsi"]
|
||||
floppy_files = ["${path.root}/answerfile", "${path.root}/setup.sh"]
|
||||
guest_os_type = "other3xLinux64Guest"
|
||||
host = "esxi-1.vsphere65.test"
|
||||
insecure_connection = true
|
||||
iso_paths = ["[datastore1] ISO/alpine-standard-3.8.2-x86_64.iso"]
|
||||
network_adapters {
|
||||
network_card = "vmxnet3"
|
||||
}
|
||||
password = "jetbrains"
|
||||
ssh_password = "jetbrains"
|
||||
ssh_username = "root"
|
||||
storage {
|
||||
disk_size = 1024
|
||||
disk_thin_provisioned = true
|
||||
}
|
||||
username = "root"
|
||||
vcenter_server = "vcenter.vsphere65.test"
|
||||
vm_name = "alpine-${local.timestamp}"
|
||||
}
|
||||
|
||||
# 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.vsphere-iso.autogenerated_1"]
|
||||
|
||||
provisioner "shell" {
|
||||
inline = ["ls /"]
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,25 @@
|
||||
# "timestamp" template function replacement
|
||||
locals { timestamp = regex_replace(timestamp(), "[- TZ:]", "") }
|
||||
|
||||
# source blocks are analogous to the "builders" in json templates. They are used
|
||||
# in build blocks. A build block runs provisioners and post-processors on a
|
||||
# source. Read the documentation for source blocks here:
|
||||
# https://www.packer.io/docs/templates/hcl_templates/blocks/source
|
||||
source "vsphere-clone" "example_clone" {
|
||||
communicator = "none"
|
||||
host = "esxi-1.vsphere65.test"
|
||||
insecure_connection = "true"
|
||||
password = "jetbrains"
|
||||
template = "alpine"
|
||||
username = "root"
|
||||
vcenter_server = "vcenter.vsphere65.test"
|
||||
vm_name = "alpine-clone-${local.timestamp}"
|
||||
}
|
||||
|
||||
# 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.vsphere-clone.example_clone"]
|
||||
|
||||
}
|
||||
@@ -2,49 +2,48 @@
|
||||
"builders": [
|
||||
{
|
||||
"type": "vsphere-iso",
|
||||
|
||||
"vcenter_server": "vcenter.vsphere65.test",
|
||||
"username": "root",
|
||||
"password": "jetbrains",
|
||||
"insecure_connection": "true",
|
||||
|
||||
"vm_name": "macos-packer",
|
||||
"host": "esxi-mac.vsphere65.test",
|
||||
|
||||
"guest_os_type": "darwin16_64Guest",
|
||||
|
||||
"CPUs": 1,
|
||||
"RAM": 4096,
|
||||
|
||||
"disk_size": 32768,
|
||||
"disk_thin_provisioned": true,
|
||||
|
||||
"network_card": "e1000e",
|
||||
"usb_controller": true,
|
||||
|
||||
"configuration_parameters": {
|
||||
"ich7m.present": "TRUE",
|
||||
"smc.present": "TRUE"
|
||||
},
|
||||
"cdrom_type": "sata",
|
||||
|
||||
"iso_paths": [
|
||||
"[datastore-mac] ISO/macOS 10.13.3.iso",
|
||||
"[datastore-mac] ISO/VMware Tools/10.2.0/darwin.iso"
|
||||
],
|
||||
|
||||
"iso_urls": ["{{template_dir}}/setup/out/setup.iso"],
|
||||
"iso_checksum": "file:///{{template_dir}}/setup/out/sha256sums",
|
||||
|
||||
"boot_wait": "4m",
|
||||
"CPUs": 1,
|
||||
"RAM": 4096,
|
||||
"boot_command": [
|
||||
"<enter><wait5>",
|
||||
"<leftCtrlOn><f2><leftCtrlOff>u<enter>t<enter><wait5>",
|
||||
"/Volumes/setup/setup.sh<enter>"
|
||||
],
|
||||
|
||||
"boot_wait": "4m",
|
||||
"cdrom_type": "sata",
|
||||
"configuration_parameters": {
|
||||
"ich7m.present": "TRUE",
|
||||
"smc.present": "TRUE"
|
||||
},
|
||||
"guest_os_type": "darwin16_64Guest",
|
||||
"host": "esxi-mac.vsphere65.test",
|
||||
"insecure_connection": "true",
|
||||
"iso_checksum": "file:///{{template_dir}}/setup/out/sha256sums",
|
||||
"iso_paths": [
|
||||
"[datastore-mac] ISO/macOS 10.13.3.iso",
|
||||
"[datastore-mac] ISO/VMware Tools/10.2.0/darwin.iso"
|
||||
],
|
||||
"iso_urls": [
|
||||
"{{template_dir}}/setup/out/setup.iso"
|
||||
],
|
||||
"network_adapters": [
|
||||
{
|
||||
"network_card": "e1000e"
|
||||
}
|
||||
],
|
||||
"password": "jetbrains",
|
||||
"ssh_password": "jetbrains",
|
||||
"ssh_username": "jetbrains",
|
||||
"ssh_password": "jetbrains"
|
||||
"storage": [
|
||||
{
|
||||
"disk_size": 32768,
|
||||
"disk_thin_provisioned": true
|
||||
}
|
||||
],
|
||||
"usb_controller": true,
|
||||
"username": "root",
|
||||
"vcenter_server": "vcenter.vsphere65.test",
|
||||
"vm_name": "macos-packer"
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,43 @@
|
||||
# source blocks are analogous to the "builders" in json templates. They are used
|
||||
# in build blocks. A build block runs provisioners and post-processors on a
|
||||
# source. Read the documentation for source blocks here:
|
||||
# https://www.packer.io/docs/templates/hcl_templates/blocks/source
|
||||
source "vsphere-iso" "example_osx" {
|
||||
CPUs = 1
|
||||
RAM = 4096
|
||||
boot_command = ["<enter><wait5>", "<leftCtrlOn><f2><leftCtrlOff>u<enter>t<enter><wait5>", "/Volumes/setup/setup.sh<enter>"]
|
||||
boot_wait = "4m"
|
||||
cdrom_type = "sata"
|
||||
configuration_parameters = {
|
||||
"ich7m.present" = "TRUE"
|
||||
"smc.present" = "TRUE"
|
||||
}
|
||||
guest_os_type = "darwin16_64Guest"
|
||||
host = "esxi-mac.vsphere65.test"
|
||||
insecure_connection = "true"
|
||||
iso_checksum = "file:///${path.root}/setup/out/sha256sums"
|
||||
iso_paths = ["[datastore-mac] ISO/macOS 10.13.3.iso", "[datastore-mac] ISO/VMware Tools/10.2.0/darwin.iso"]
|
||||
iso_urls = ["${path.root}/setup/out/setup.iso"]
|
||||
network_adapters {
|
||||
network_card = "e1000e"
|
||||
}
|
||||
password = "jetbrains"
|
||||
ssh_password = "jetbrains"
|
||||
ssh_username = "jetbrains"
|
||||
storage {
|
||||
disk_size = 32768
|
||||
disk_thin_provisioned = true
|
||||
}
|
||||
usb_controller = ["usb"]
|
||||
username = "root"
|
||||
vcenter_server = "vcenter.vsphere65.test"
|
||||
vm_name = "macos-packer"
|
||||
}
|
||||
|
||||
# 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.vsphere-iso.example_osx"]
|
||||
|
||||
}
|
||||
@@ -2,36 +2,9 @@
|
||||
"builders": [
|
||||
{
|
||||
"type": "vsphere-iso",
|
||||
|
||||
"vcenter_server": "vcenter.vsphere65.test",
|
||||
"username": "root",
|
||||
"password": "jetbrains",
|
||||
"insecure_connection": "true",
|
||||
|
||||
"vm_name": "example-ubuntu",
|
||||
"host": "esxi-1.vsphere65.test",
|
||||
|
||||
"guest_os_type": "ubuntu64Guest",
|
||||
|
||||
"ssh_username": "jetbrains",
|
||||
"ssh_password": "jetbrains",
|
||||
|
||||
"CPUs": 1,
|
||||
"RAM": 1024,
|
||||
"CPUs": 1,
|
||||
"RAM": 1024,
|
||||
"RAM_reserve_all": true,
|
||||
|
||||
"disk_controller_type": "pvscsi",
|
||||
"disk_size": 32768,
|
||||
"disk_thin_provisioned": true,
|
||||
|
||||
"network_card": "vmxnet3",
|
||||
|
||||
"iso_paths": [
|
||||
"[datastore1] ISO/ubuntu-16.04.3-server-amd64.iso"
|
||||
],
|
||||
"floppy_files": [
|
||||
"{{template_dir}}/preseed.cfg"
|
||||
],
|
||||
"boot_command": [
|
||||
"<enter><wait><f6><wait><esc><wait>",
|
||||
"<bs><bs><bs><bs><bs><bs><bs><bs><bs><bs>",
|
||||
@@ -49,14 +22,42 @@
|
||||
" locale=en_US",
|
||||
" file=/media/preseed.cfg",
|
||||
"<enter>"
|
||||
]
|
||||
],
|
||||
"disk_controller_type": "pvscsi",
|
||||
"floppy_files": [
|
||||
"{{template_dir}}/preseed.cfg"
|
||||
],
|
||||
"guest_os_type": "ubuntu64Guest",
|
||||
"host": "esxi-1.vsphere65.test",
|
||||
"insecure_connection": true,
|
||||
"iso_paths": [
|
||||
"[datastore1] ISO/ubuntu-16.04.3-server-amd64.iso"
|
||||
],
|
||||
"network_adapters": [
|
||||
{
|
||||
"network_card": "vmxnet3"
|
||||
}
|
||||
],
|
||||
"password": "jetbrains",
|
||||
"ssh_password": "jetbrains",
|
||||
"ssh_username": "jetbrains",
|
||||
"storage": [
|
||||
{
|
||||
"disk_size": 32768,
|
||||
"disk_thin_provisioned": true
|
||||
}
|
||||
],
|
||||
"username": "root",
|
||||
"vcenter_server": "vcenter.vsphere65.test",
|
||||
"vm_name": "example-ubuntu"
|
||||
}
|
||||
],
|
||||
|
||||
"provisioners": [
|
||||
{
|
||||
"type": "shell",
|
||||
"inline": ["ls /"]
|
||||
"inline": [
|
||||
"ls /"
|
||||
],
|
||||
"type": "shell"
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,52 @@
|
||||
# source blocks are analogous to the "builders" in json templates. They are used
|
||||
# in build blocks. A build block runs provisioners and post-processors on a
|
||||
# source. Read the documentation for source blocks here:
|
||||
# https://www.packer.io/docs/templates/hcl_templates/blocks/source
|
||||
source "vsphere-iso" "example" {
|
||||
CPUs = 1
|
||||
RAM = 1024
|
||||
RAM_reserve_all = true
|
||||
boot_command = ["<enter><wait><f6><wait><esc><wait>",
|
||||
"<bs><bs><bs><bs><bs><bs><bs><bs><bs><bs>",
|
||||
"<bs><bs><bs><bs><bs><bs><bs><bs><bs><bs>",
|
||||
"<bs><bs><bs><bs><bs><bs><bs><bs><bs><bs>",
|
||||
"<bs><bs><bs><bs><bs><bs><bs><bs><bs><bs>",
|
||||
"<bs><bs><bs><bs><bs><bs><bs><bs><bs><bs>",
|
||||
"<bs><bs><bs><bs><bs><bs><bs><bs><bs><bs>",
|
||||
"<bs><bs><bs><bs><bs><bs><bs><bs><bs><bs>",
|
||||
"<bs><bs><bs><bs><bs><bs><bs><bs><bs><bs>",
|
||||
"<bs><bs><bs>", "/install/vmlinuz",
|
||||
" initrd=/install/initrd.gz", " priority=critical",
|
||||
" locale=en_US", " file=/media/preseed.cfg",
|
||||
"<enter>"]
|
||||
disk_controller_type = ["pvscsi"]
|
||||
floppy_files = ["${path.root}/preseed.cfg"]
|
||||
guest_os_type = "ubuntu64Guest"
|
||||
host = "esxi-1.vsphere65.test"
|
||||
insecure_connection = true
|
||||
iso_paths = ["[datastore1] ISO/ubuntu-16.04.3-server-amd64.iso"]
|
||||
network_adapters {
|
||||
network_card = "vmxnet3"
|
||||
}
|
||||
password = "jetbrains"
|
||||
ssh_password = "jetbrains"
|
||||
ssh_username = "jetbrains"
|
||||
storage {
|
||||
disk_size = 32768
|
||||
disk_thin_provisioned = true
|
||||
}
|
||||
username = "root"
|
||||
vcenter_server = "vcenter.vsphere65.test"
|
||||
vm_name = "example-ubuntu"
|
||||
}
|
||||
|
||||
# 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.vsphere-iso.example"]
|
||||
|
||||
provisioner "shell" {
|
||||
inline = ["ls /"]
|
||||
}
|
||||
}
|
||||
@@ -2,47 +2,47 @@
|
||||
"builders": [
|
||||
{
|
||||
"type": "vsphere-iso",
|
||||
|
||||
"vcenter_server": "vcenter.vsphere65.test",
|
||||
"username": "root",
|
||||
"password": "jetbrains",
|
||||
"insecure_connection": "true",
|
||||
|
||||
"vm_name": "example-windows",
|
||||
"host": "esxi-1.vsphere65.test",
|
||||
|
||||
"guest_os_type": "windows9_64Guest",
|
||||
|
||||
"communicator": "winrm",
|
||||
"winrm_username": "jetbrains",
|
||||
"winrm_password": "jetbrains",
|
||||
|
||||
"CPUs": 1,
|
||||
"RAM": 4096,
|
||||
"CPUs": 1,
|
||||
"RAM": 4096,
|
||||
"RAM_reserve_all": true,
|
||||
|
||||
"disk_controller_type": "pvscsi",
|
||||
"disk_size": 32768,
|
||||
"disk_thin_provisioned": true,
|
||||
|
||||
"network_card": "vmxnet3",
|
||||
|
||||
"communicator": "winrm",
|
||||
"disk_controller_type": "pvscsi",
|
||||
"floppy_files": [
|
||||
"{{template_dir}}/setup/"
|
||||
],
|
||||
"floppy_img_path": "[datastore1] ISO/VMware Tools/10.2.0/pvscsi-Windows8.flp",
|
||||
"guest_os_type": "windows9_64Guest",
|
||||
"host": "esxi-1.vsphere65.test",
|
||||
"insecure_connection": "true",
|
||||
"iso_paths": [
|
||||
"[datastore1] ISO/en_windows_10_multi-edition_vl_version_1709_updated_dec_2017_x64_dvd_100406172.iso",
|
||||
"[datastore1] ISO/VMware Tools/10.2.0/windows.iso"
|
||||
],
|
||||
|
||||
"floppy_files": [
|
||||
"{{template_dir}}/setup/"
|
||||
"network_adapters": [
|
||||
{
|
||||
"network_card": "vmxnet3"
|
||||
}
|
||||
],
|
||||
"floppy_img_path": "[datastore1] ISO/VMware Tools/10.2.0/pvscsi-Windows8.flp"
|
||||
"password": "jetbrains",
|
||||
"storage": [
|
||||
{
|
||||
"disk_size": 32768,
|
||||
"disk_thin_provisioned": true
|
||||
}
|
||||
],
|
||||
"username": "root",
|
||||
"vcenter_server": "vcenter.vsphere65.test",
|
||||
"vm_name": "example-windows",
|
||||
"winrm_password": "jetbrains",
|
||||
"winrm_username": "jetbrains"
|
||||
}
|
||||
],
|
||||
|
||||
"provisioners": [
|
||||
{
|
||||
"type": "windows-shell",
|
||||
"inline": ["dir c:\\"]
|
||||
"inline": [
|
||||
"dir c:\\"
|
||||
],
|
||||
"type": "windows-shell"
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,41 @@
|
||||
# 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 "vsphere-iso" "example_windows" {
|
||||
CPUs = 1
|
||||
RAM = 4096
|
||||
RAM_reserve_all = true
|
||||
communicator = "winrm"
|
||||
disk_controller_type = ["pvscsi"]
|
||||
floppy_files = ["${path.root}/setup/"]
|
||||
floppy_img_path = "[datastore1] ISO/VMware Tools/10.2.0/pvscsi-Windows8.flp"
|
||||
guest_os_type = "windows9_64Guest"
|
||||
host = "esxi-1.vsphere65.test"
|
||||
insecure_connection = "true"
|
||||
iso_paths = ["[datastore1] ISO/en_windows_10_multi-edition_vl_version_1709_updated_dec_2017_x64_dvd_100406172.iso", "[datastore1] ISO/VMware Tools/10.2.0/windows.iso"]
|
||||
network_adapters {
|
||||
network_card = "vmxnet3"
|
||||
}
|
||||
password = "jetbrains"
|
||||
storage {
|
||||
disk_size = 32768
|
||||
disk_thin_provisioned = true
|
||||
}
|
||||
username = "root"
|
||||
vcenter_server = "vcenter.vsphere65.test"
|
||||
vm_name = "example-windows"
|
||||
winrm_password = "jetbrains"
|
||||
winrm_username = "jetbrains"
|
||||
}
|
||||
|
||||
# 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.vsphere-iso.example_windows"]
|
||||
|
||||
provisioner "windows-shell" {
|
||||
inline = ["dir c:\\"]
|
||||
}
|
||||
}
|
||||
@@ -34,16 +34,18 @@ func MergeCloudUserMetaData(usersData ...string) (string, error) {
|
||||
}
|
||||
|
||||
for i, userData := range usersData {
|
||||
w, err := data.CreatePart(textproto.MIMEHeader{
|
||||
"Content-Disposition": {fmt.Sprintf("attachment; filename=\"user-data-%d\"", i)},
|
||||
"Content-Type": {detectContentType(userData)},
|
||||
})
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
_, err = w.Write([]byte(userData))
|
||||
if err != nil {
|
||||
return "", err
|
||||
if len(userData) != 0 {
|
||||
w, err := data.CreatePart(textproto.MIMEHeader{
|
||||
"Content-Disposition": {fmt.Sprintf("attachment; filename=\"user-data-%d\"", i)},
|
||||
"Content-Type": {detectContentType(userData)},
|
||||
})
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
_, err = w.Write([]byte(userData))
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
}
|
||||
}
|
||||
return buff.String(), nil
|
||||
|
||||
@@ -53,6 +53,7 @@ func (s *stepCreateImage) Run(ctx context.Context, state multistep.StateBag) mul
|
||||
if err != nil {
|
||||
return StepHaltWithError(state, err)
|
||||
}
|
||||
ui.Say("Success image create...")
|
||||
|
||||
image, ok := resp.(*compute.Image)
|
||||
if !ok {
|
||||
|
||||
@@ -31,13 +31,14 @@ func writeSerialLogFile(ctx context.Context, state multistep.StateBag, serialLog
|
||||
sdk := state.Get("sdk").(*ycsdk.SDK)
|
||||
ui := state.Get("ui").(packersdk.Ui)
|
||||
|
||||
instanceID := state.Get("instance_id").(string)
|
||||
if instanceID == "" {
|
||||
instanceID, ok := state.GetOk("instance_id")
|
||||
|
||||
if !ok || instanceID.(string) == "" {
|
||||
return nil
|
||||
}
|
||||
ui.Say("Try get instance's serial port output and write to file " + serialLogFile)
|
||||
serialOutput, err := sdk.Compute().Instance().GetSerialPortOutput(ctx, &compute.GetInstanceSerialPortOutputRequest{
|
||||
InstanceId: instanceID,
|
||||
InstanceId: instanceID.(string),
|
||||
})
|
||||
if err != nil {
|
||||
return fmt.Errorf("Failed to get serial port output for instance (id: %s): %s", instanceID, err)
|
||||
|
||||
@@ -12,7 +12,7 @@ import (
|
||||
"strings"
|
||||
|
||||
packersdk "github.com/hashicorp/packer-plugin-sdk/packer"
|
||||
"github.com/hashicorp/packer/packer/plugin"
|
||||
"github.com/hashicorp/packer/packer"
|
||||
)
|
||||
|
||||
const packerPluginsCheck = "packer-plugins-check"
|
||||
@@ -119,7 +119,7 @@ func checkPluginName(name string) error {
|
||||
// in the plugin configuration. At least one builder, provisioner, or post-processor should be found to validate the plugin's
|
||||
// compatibility with Packer.
|
||||
func discoverAndLoad() error {
|
||||
config := plugin.Config{
|
||||
config := packer.PluginConfig{
|
||||
PluginMinPort: 10000,
|
||||
PluginMaxPort: 25000,
|
||||
}
|
||||
@@ -129,20 +129,18 @@ func discoverAndLoad() error {
|
||||
}
|
||||
|
||||
// TODO: validate correctness of plugins loaded by checking them against the output of the `describe` command.
|
||||
plugins := config.GetPlugins()
|
||||
if len(plugins.Builders) == 0 &&
|
||||
len(plugins.Provisioners) == 0 &&
|
||||
len(plugins.PostProcessors) == 0 &&
|
||||
len(plugins.DataSources) == 0 {
|
||||
return fmt.Errorf("couldn't load any Builder/Provisioner/Post-Processor/Datasource from the plugin binary")
|
||||
if len(config.Builders.List()) == 0 &&
|
||||
len(config.Provisioners.List()) == 0 &&
|
||||
len(config.PostProcessors.List()) == 0 {
|
||||
return fmt.Errorf("couldn't load any Builder/Provisioner/Post-Processor from the plugin binary")
|
||||
}
|
||||
|
||||
return checkHCL2ConfigSpec(plugins)
|
||||
return checkHCL2ConfigSpec(config)
|
||||
}
|
||||
|
||||
// checkHCL2ConfigSpec checks if the hcl2spec config is present for the given plugins by validating that ConfigSpec() does not
|
||||
// return an empty map of specs.
|
||||
func checkHCL2ConfigSpec(plugins plugin.Plugins) error {
|
||||
func checkHCL2ConfigSpec(plugins packer.PluginConfig) error {
|
||||
var errs *packersdk.MultiError
|
||||
for _, b := range plugins.Builders.List() {
|
||||
builder, err := plugins.Builders.Start(b)
|
||||
|
||||
+1
-4
@@ -67,10 +67,7 @@ func (m *Meta) GetConfigFromHCL(cla *MetaArgs) (*hcl2template.PackerConfig, int)
|
||||
CorePackerVersion: version.SemVer,
|
||||
CorePackerVersionString: version.FormattedVersion(),
|
||||
Parser: hclparse.NewParser(),
|
||||
BuilderSchemas: m.CoreConfig.Components.BuilderStore,
|
||||
ProvisionersSchemas: m.CoreConfig.Components.ProvisionerStore,
|
||||
PostProcessorsSchemas: m.CoreConfig.Components.PostProcessorStore,
|
||||
DatasourceSchemas: m.CoreConfig.Components.DatasourceStore,
|
||||
PluginConfig: m.CoreConfig.Components.PluginConfig,
|
||||
}
|
||||
cfg, diags := parser.Parse(cla.Path, cla.VarFiles, cla.Vars)
|
||||
return cfg, writeDiags(m.Ui, parser.Files(), diags)
|
||||
|
||||
@@ -68,13 +68,15 @@ func testMetaParallel(t *testing.T, builder *ParallelTestBuilder, locked *Locked
|
||||
return Meta{
|
||||
CoreConfig: &packer.CoreConfig{
|
||||
Components: packer.ComponentFinder{
|
||||
BuilderStore: packer.MapOfBuilder{
|
||||
"parallel-test": func() (packersdk.Builder, error) { return builder, nil },
|
||||
"file": func() (packersdk.Builder, error) { return &file.Builder{}, nil },
|
||||
"lock": func() (packersdk.Builder, error) { return locked, nil },
|
||||
},
|
||||
ProvisionerStore: packer.MapOfProvisioner{
|
||||
"sleep": func() (packersdk.Provisioner, error) { return &sleep.Provisioner{}, nil },
|
||||
PluginConfig: &packer.PluginConfig{
|
||||
Builders: packer.MapOfBuilder{
|
||||
"parallel-test": func() (packersdk.Builder, error) { return builder, nil },
|
||||
"file": func() (packersdk.Builder, error) { return &file.Builder{}, nil },
|
||||
"lock": func() (packersdk.Builder, error) { return locked, nil },
|
||||
},
|
||||
Provisioners: packer.MapOfProvisioner{
|
||||
"sleep": func() (packersdk.Provisioner, error) { return &sleep.Provisioner{}, nil },
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
|
||||
+91
-15
@@ -12,6 +12,7 @@ import (
|
||||
"testing"
|
||||
|
||||
"github.com/google/go-cmp/cmp"
|
||||
"github.com/hashicorp/go-uuid"
|
||||
packersdk "github.com/hashicorp/packer-plugin-sdk/packer"
|
||||
"github.com/hashicorp/packer/builder/file"
|
||||
"github.com/hashicorp/packer/builder/null"
|
||||
@@ -695,6 +696,79 @@ func testHCLOnlyExceptFlags(t *testing.T, args, present, notPresent []string) {
|
||||
}
|
||||
}
|
||||
|
||||
func TestHCL2PostProcessorForceFlag(t *testing.T) {
|
||||
t.Helper()
|
||||
|
||||
UUID, _ := uuid.GenerateUUID()
|
||||
// Manifest will only clean with force if the build's PACKER_RUN_UUID are different
|
||||
os.Setenv("PACKER_RUN_UUID", UUID)
|
||||
defer os.Unsetenv("PACKER_RUN_UUID")
|
||||
|
||||
args := []string{
|
||||
filepath.Join(testFixture("hcl"), "force.pkr.hcl"),
|
||||
}
|
||||
fCheck := fileCheck{
|
||||
expectedContent: map[string]string{
|
||||
"manifest.json": fmt.Sprintf(`{
|
||||
"builds": [
|
||||
{
|
||||
"name": "potato",
|
||||
"builder_type": "null",
|
||||
"files": null,
|
||||
"artifact_id": "Null",
|
||||
"packer_run_uuid": %q,
|
||||
"custom_data": null
|
||||
}
|
||||
],
|
||||
"last_run_uuid": %q
|
||||
}`, UUID, UUID),
|
||||
},
|
||||
}
|
||||
defer fCheck.cleanup(t)
|
||||
|
||||
c := &BuildCommand{
|
||||
Meta: testMetaFile(t),
|
||||
}
|
||||
if code := c.Run(args); code != 0 {
|
||||
fatalCommand(t, c.Meta)
|
||||
}
|
||||
fCheck.verify(t)
|
||||
|
||||
// Second build should override previous manifest
|
||||
UUID, _ = uuid.GenerateUUID()
|
||||
os.Setenv("PACKER_RUN_UUID", UUID)
|
||||
|
||||
args = []string{
|
||||
"-force",
|
||||
filepath.Join(testFixture("hcl"), "force.pkr.hcl"),
|
||||
}
|
||||
fCheck = fileCheck{
|
||||
expectedContent: map[string]string{
|
||||
"manifest.json": fmt.Sprintf(`{
|
||||
"builds": [
|
||||
{
|
||||
"name": "potato",
|
||||
"builder_type": "null",
|
||||
"files": null,
|
||||
"artifact_id": "Null",
|
||||
"packer_run_uuid": %q,
|
||||
"custom_data": null
|
||||
}
|
||||
],
|
||||
"last_run_uuid": %q
|
||||
}`, UUID, UUID),
|
||||
},
|
||||
}
|
||||
|
||||
c = &BuildCommand{
|
||||
Meta: testMetaFile(t),
|
||||
}
|
||||
if code := c.Run(args); code != 0 {
|
||||
fatalCommand(t, c.Meta)
|
||||
}
|
||||
fCheck.verify(t)
|
||||
}
|
||||
|
||||
func TestBuildCommand_HCLOnlyExceptOptions(t *testing.T) {
|
||||
tests := []struct {
|
||||
args []string
|
||||
@@ -847,21 +921,23 @@ func fileExists(filename string) bool {
|
||||
// available. This allows us to test a builder that writes files to disk.
|
||||
func testCoreConfigBuilder(t *testing.T) *packer.CoreConfig {
|
||||
components := packer.ComponentFinder{
|
||||
BuilderStore: packer.MapOfBuilder{
|
||||
"file": func() (packersdk.Builder, error) { return &file.Builder{}, nil },
|
||||
"null": func() (packersdk.Builder, error) { return &null.Builder{}, nil },
|
||||
},
|
||||
ProvisionerStore: packer.MapOfProvisioner{
|
||||
"shell-local": func() (packersdk.Provisioner, error) { return &shell_local.Provisioner{}, nil },
|
||||
"shell": func() (packersdk.Provisioner, error) { return &shell.Provisioner{}, nil },
|
||||
"file": func() (packersdk.Provisioner, error) { return &filep.Provisioner{}, nil },
|
||||
},
|
||||
PostProcessorStore: packer.MapOfPostProcessor{
|
||||
"shell-local": func() (packersdk.PostProcessor, error) { return &shell_local_pp.PostProcessor{}, nil },
|
||||
"manifest": func() (packersdk.PostProcessor, error) { return &manifest.PostProcessor{}, nil },
|
||||
},
|
||||
DatasourceStore: packersdk.MapOfDatasource{
|
||||
"mock": func() (packersdk.Datasource, error) { return &packersdk.MockDatasource{}, nil },
|
||||
PluginConfig: &packer.PluginConfig{
|
||||
Builders: packer.MapOfBuilder{
|
||||
"file": func() (packersdk.Builder, error) { return &file.Builder{}, nil },
|
||||
"null": func() (packersdk.Builder, error) { return &null.Builder{}, nil },
|
||||
},
|
||||
Provisioners: packer.MapOfProvisioner{
|
||||
"shell-local": func() (packersdk.Provisioner, error) { return &shell_local.Provisioner{}, nil },
|
||||
"shell": func() (packersdk.Provisioner, error) { return &shell.Provisioner{}, nil },
|
||||
"file": func() (packersdk.Provisioner, error) { return &filep.Provisioner{}, nil },
|
||||
},
|
||||
PostProcessors: packer.MapOfPostProcessor{
|
||||
"shell-local": func() (packersdk.PostProcessor, error) { return &shell_local_pp.PostProcessor{}, nil },
|
||||
"manifest": func() (packersdk.PostProcessor, error) { return &manifest.PostProcessor{}, nil },
|
||||
},
|
||||
DataSources: packer.MapOfDatasource{
|
||||
"mock": func() (packersdk.Datasource, error) { return &packersdk.MockDatasource{}, nil },
|
||||
},
|
||||
},
|
||||
}
|
||||
return &packer.CoreConfig{
|
||||
|
||||
@@ -16,12 +16,14 @@ import (
|
||||
// available. This allows us to test a builder that writes files to disk.
|
||||
func testCoreConfigSleepBuilder(t *testing.T) *packer.CoreConfig {
|
||||
components := packer.ComponentFinder{
|
||||
BuilderStore: packer.MapOfBuilder{
|
||||
"file": func() (packersdk.Builder, error) { return &file.Builder{}, nil },
|
||||
},
|
||||
ProvisionerStore: packer.MapOfProvisioner{
|
||||
"sleep": func() (packersdk.Provisioner, error) { return &sleep.Provisioner{}, nil },
|
||||
"shell-local": func() (packersdk.Provisioner, error) { return &shell_local.Provisioner{}, nil },
|
||||
PluginConfig: &packer.PluginConfig{
|
||||
Builders: packer.MapOfBuilder{
|
||||
"file": func() (packersdk.Builder, error) { return &file.Builder{}, nil },
|
||||
},
|
||||
Provisioners: packer.MapOfProvisioner{
|
||||
"sleep": func() (packersdk.Provisioner, error) { return &sleep.Provisioner{}, nil },
|
||||
"shell-local": func() (packersdk.Provisioner, error) { return &shell_local.Provisioner{}, nil },
|
||||
},
|
||||
},
|
||||
}
|
||||
return &packer.CoreConfig{
|
||||
|
||||
@@ -93,6 +93,18 @@ type BuildArgs struct {
|
||||
OnError string
|
||||
}
|
||||
|
||||
func (ia *InitArgs) AddFlagSets(flags *flag.FlagSet) {
|
||||
flags.BoolVar(&ia.Upgrade, "upgrade", false, "upgrade any present plugin to the highest allowed version.")
|
||||
|
||||
ia.MetaArgs.AddFlagSets(flags)
|
||||
}
|
||||
|
||||
// InitArgs represents a parsed cli line for a `packer build`
|
||||
type InitArgs struct {
|
||||
MetaArgs
|
||||
Upgrade bool
|
||||
}
|
||||
|
||||
// ConsoleArgs represents a parsed cli line for a `packer console`
|
||||
type ConsoleArgs struct {
|
||||
MetaArgs
|
||||
|
||||
@@ -3,6 +3,7 @@ package command
|
||||
import (
|
||||
"github.com/hashicorp/hcl/v2"
|
||||
"github.com/hashicorp/packer/packer"
|
||||
plugingetter "github.com/hashicorp/packer/packer/plugin-getter"
|
||||
)
|
||||
|
||||
// CoreWrapper wraps a packer.Core in order to have it's Initialize func return
|
||||
@@ -23,3 +24,13 @@ func (c *CoreWrapper) Initialize(_ packer.InitializeOptions) hcl.Diagnostics {
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (c *CoreWrapper) PluginRequirements() (plugingetter.Requirements, hcl.Diagnostics) {
|
||||
return nil, hcl.Diagnostics{
|
||||
&hcl.Diagnostic{
|
||||
Summary: "Packer init is supported for HCL2 configuration templates only",
|
||||
Detail: "Please manually install plugins or use a HCL2 configuration that will do that for you.",
|
||||
Severity: hcl.DiagError,
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
+15
-13
@@ -118,19 +118,21 @@ func commandMeta() Meta {
|
||||
|
||||
func getBareComponentFinder() packer.ComponentFinder {
|
||||
return packer.ComponentFinder{
|
||||
BuilderStore: packer.MapOfBuilder{
|
||||
"file": func() (packersdk.Builder, error) { return &file.Builder{}, nil },
|
||||
"null": func() (packersdk.Builder, error) { return &null.Builder{}, nil },
|
||||
"amazon-ebs": func() (packersdk.Builder, error) { return &ebs.Builder{}, nil },
|
||||
},
|
||||
ProvisionerStore: packer.MapOfProvisioner{
|
||||
"shell-local": func() (packersdk.Provisioner, error) { return &shell_local.Provisioner{}, nil },
|
||||
"shell": func() (packersdk.Provisioner, error) { return &shell.Provisioner{}, nil },
|
||||
"file": func() (packersdk.Provisioner, error) { return &filep.Provisioner{}, nil },
|
||||
},
|
||||
PostProcessorStore: packer.MapOfPostProcessor{
|
||||
"shell-local": func() (packersdk.PostProcessor, error) { return &shell_local_pp.PostProcessor{}, nil },
|
||||
"manifest": func() (packersdk.PostProcessor, error) { return &manifest.PostProcessor{}, nil },
|
||||
PluginConfig: &packer.PluginConfig{
|
||||
Builders: packer.MapOfBuilder{
|
||||
"file": func() (packersdk.Builder, error) { return &file.Builder{}, nil },
|
||||
"null": func() (packersdk.Builder, error) { return &null.Builder{}, nil },
|
||||
"amazon-ebs": func() (packersdk.Builder, error) { return &ebs.Builder{}, nil },
|
||||
},
|
||||
Provisioners: packer.MapOfProvisioner{
|
||||
"shell-local": func() (packersdk.Provisioner, error) { return &shell_local.Provisioner{}, nil },
|
||||
"shell": func() (packersdk.Provisioner, error) { return &shell.Provisioner{}, nil },
|
||||
"file": func() (packersdk.Provisioner, error) { return &filep.Provisioner{}, nil },
|
||||
},
|
||||
PostProcessors: packer.MapOfPostProcessor{
|
||||
"shell-local": func() (packersdk.PostProcessor, error) { return &shell_local_pp.PostProcessor{}, nil },
|
||||
"manifest": func() (packersdk.PostProcessor, error) { return &manifest.PostProcessor{}, nil },
|
||||
},
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
+220
-47
@@ -74,9 +74,12 @@ const (
|
||||
# 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.
|
||||
`
|
||||
|
||||
# https://www.packer.io/docs/templates/hcl_templates/variables#type-constraints for more info.`
|
||||
localsVarHeader = `
|
||||
# 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`
|
||||
packerBlockHeader = `
|
||||
# See https://www.packer.io/docs/templates/hcl_templates/blocks/packer for more info
|
||||
`
|
||||
@@ -93,15 +96,28 @@ const (
|
||||
# https://www.packer.io/docs/templates/hcl_templates/blocks/build
|
||||
build {
|
||||
`
|
||||
|
||||
amazonAmiDataHeader = `
|
||||
# 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`
|
||||
# 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`
|
||||
|
||||
amazonSecretsManagerDataHeader = `
|
||||
# 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`
|
||||
)
|
||||
|
||||
var amazonSecretsManagerMap = map[string]map[string]interface{}{}
|
||||
var localsVariableMap = map[string]string{}
|
||||
|
||||
func (c *HCL2UpgradeCommand) RunContext(buildCtx context.Context, cla *HCL2UpgradeArgs) int {
|
||||
out := &bytes.Buffer{}
|
||||
var output io.Writer
|
||||
if err := os.MkdirAll(filepath.Dir(cla.OutputFile), 0); err != nil {
|
||||
c.Ui.Error(fmt.Sprintf("Failed to create output directory: %v", err))
|
||||
@@ -131,20 +147,16 @@ func (c *HCL2UpgradeCommand) RunContext(buildCtx context.Context, cla *HCL2Upgra
|
||||
}
|
||||
tpl := core.Template
|
||||
|
||||
// Packer section
|
||||
if tpl.MinVersion != "" {
|
||||
out.Write([]byte(packerBlockHeader))
|
||||
fileContent := hclwrite.NewEmptyFile()
|
||||
body := fileContent.Body()
|
||||
packerBody := body.AppendNewBlock("packer", nil).Body()
|
||||
packerBody.SetAttributeValue("required_version", cty.StringVal(fmt.Sprintf(">= %s", tpl.MinVersion)))
|
||||
out.Write(fileContent.Bytes())
|
||||
}
|
||||
// OutPut Locals and Local blocks
|
||||
localsContent := hclwrite.NewEmptyFile()
|
||||
localsBody := localsContent.Body()
|
||||
localsBody.AppendNewline()
|
||||
localBody := localsBody.AppendNewBlock("locals", nil).Body()
|
||||
|
||||
out.Write([]byte(inputVarHeader))
|
||||
localsOut := []byte{}
|
||||
|
||||
// Output variables section
|
||||
|
||||
variablesOut := []byte{}
|
||||
variables := []*template.Variable{}
|
||||
{
|
||||
// sort variables to avoid map's randomness
|
||||
@@ -157,27 +169,47 @@ func (c *HCL2UpgradeCommand) RunContext(buildCtx context.Context, cla *HCL2Upgra
|
||||
})
|
||||
}
|
||||
|
||||
hasLocals := false
|
||||
for _, variable := range variables {
|
||||
variablesContent := hclwrite.NewEmptyFile()
|
||||
variablesBody := variablesContent.Body()
|
||||
|
||||
variablesBody.AppendNewline()
|
||||
variableBody := variablesBody.AppendNewBlock("variable", []string{variable.Key}).Body()
|
||||
variableBody.SetAttributeRaw("type", hclwrite.Tokens{&hclwrite.Token{Bytes: []byte("string")}})
|
||||
|
||||
if variable.Default != "" || !variable.Required {
|
||||
variableBody.SetAttributeValue("default", hcl2shim.HCL2ValueFromConfigValue(variable.Default))
|
||||
}
|
||||
sensitive := false
|
||||
if isSensitiveVariable(variable.Key, tpl.SensitiveVariables) {
|
||||
sensitive = true
|
||||
variableBody.SetAttributeValue("sensitive", cty.BoolVal(true))
|
||||
}
|
||||
variablesBody.AppendNewline()
|
||||
out.Write(transposeTemplatingCalls(variablesContent.Bytes()))
|
||||
isLocal, out := variableTransposeTemplatingCalls(variablesContent.Bytes())
|
||||
if isLocal {
|
||||
if sensitive {
|
||||
// Create Local block because this is sensitive
|
||||
localContent := hclwrite.NewEmptyFile()
|
||||
body := localContent.Body()
|
||||
body.AppendNewline()
|
||||
localBody := body.AppendNewBlock("local", []string{variable.Key}).Body()
|
||||
localBody.SetAttributeValue("sensitive", cty.BoolVal(true))
|
||||
localBody.SetAttributeValue("expression", hcl2shim.HCL2ValueFromConfigValue(variable.Default))
|
||||
localsOut = append(localsOut, transposeTemplatingCalls(localContent.Bytes())...)
|
||||
localsVariableMap[variable.Key] = "local"
|
||||
continue
|
||||
}
|
||||
localBody.SetAttributeValue(variable.Key, hcl2shim.HCL2ValueFromConfigValue(variable.Default))
|
||||
localsVariableMap[variable.Key] = "locals"
|
||||
hasLocals = true
|
||||
continue
|
||||
}
|
||||
variablesOut = append(variablesOut, out...)
|
||||
}
|
||||
|
||||
fmt.Fprintln(out, `# "timestamp" template function replacement`)
|
||||
fmt.Fprintln(out, `locals { timestamp = regex_replace(timestamp(), "[- TZ:]", "") }`)
|
||||
|
||||
// Output sources section
|
||||
if hasLocals {
|
||||
localsOut = append(localsOut, transposeTemplatingCalls(localsContent.Bytes())...)
|
||||
}
|
||||
|
||||
builders := []*template.Builder{}
|
||||
{
|
||||
@@ -187,7 +219,9 @@ func (c *HCL2UpgradeCommand) RunContext(buildCtx context.Context, cla *HCL2Upgra
|
||||
}
|
||||
}
|
||||
|
||||
if err := c.writeAmazonAmiDatasource(builders, out); err != nil {
|
||||
// Output amazon-ami data source section
|
||||
amazonAmiOut, err := c.writeAmazonAmiDatasource(builders)
|
||||
if err != nil {
|
||||
return 1
|
||||
}
|
||||
|
||||
@@ -195,30 +229,30 @@ func (c *HCL2UpgradeCommand) RunContext(buildCtx context.Context, cla *HCL2Upgra
|
||||
return builders[i].Type+builders[i].Name < builders[j].Type+builders[j].Name
|
||||
})
|
||||
|
||||
out.Write([]byte(sourcesHeader))
|
||||
|
||||
// Output sources section
|
||||
sourcesOut := []byte{}
|
||||
for i, builderCfg := range builders {
|
||||
sourcesContent := hclwrite.NewEmptyFile()
|
||||
body := sourcesContent.Body()
|
||||
|
||||
body.AppendNewline()
|
||||
if !c.Meta.CoreConfig.Components.BuilderStore.Has(builderCfg.Type) {
|
||||
if !c.Meta.CoreConfig.Components.PluginConfig.Builders.Has(builderCfg.Type) {
|
||||
c.Ui.Error(fmt.Sprintf("unknown builder type: %q\n", builderCfg.Type))
|
||||
return 1
|
||||
}
|
||||
if builderCfg.Name == "" || builderCfg.Name == builderCfg.Type {
|
||||
builderCfg.Name = fmt.Sprintf("autogenerated_%d", i+1)
|
||||
}
|
||||
builderCfg.Name = strings.ReplaceAll(strings.TrimSpace(builderCfg.Name), " ", "_")
|
||||
|
||||
sourceBody := body.AppendNewBlock("source", []string{builderCfg.Type, builderCfg.Name}).Body()
|
||||
|
||||
jsonBodyToHCL2Body(sourceBody, builderCfg.Config)
|
||||
|
||||
_, _ = out.Write(transposeTemplatingCalls(sourcesContent.Bytes()))
|
||||
sourcesOut = append(sourcesOut, transposeTemplatingCalls(sourcesContent.Bytes())...)
|
||||
}
|
||||
|
||||
// Output build section
|
||||
out.Write([]byte(buildHeader))
|
||||
|
||||
buildContent := hclwrite.NewEmptyFile()
|
||||
buildBody := buildContent.Body()
|
||||
if tpl.Description != "" {
|
||||
@@ -232,8 +266,10 @@ func (c *HCL2UpgradeCommand) RunContext(buildCtx context.Context, cla *HCL2Upgra
|
||||
}
|
||||
buildBody.SetAttributeValue("sources", hcl2shim.HCL2ValueFromConfigValue(sourceNames))
|
||||
buildBody.AppendNewline()
|
||||
_, _ = buildContent.WriteTo(out)
|
||||
buildOut := buildContent.Bytes()
|
||||
|
||||
// Output provisioners section
|
||||
provisionersOut := []byte{}
|
||||
for _, provisioner := range tpl.Provisioners {
|
||||
provisionerContent := hclwrite.NewEmptyFile()
|
||||
body := provisionerContent.Body()
|
||||
@@ -255,8 +291,11 @@ func (c *HCL2UpgradeCommand) RunContext(buildCtx context.Context, cla *HCL2Upgra
|
||||
}
|
||||
jsonBodyToHCL2Body(block.Body(), cfg)
|
||||
|
||||
out.Write(transposeTemplatingCalls(provisionerContent.Bytes()))
|
||||
provisionersOut = append(provisionersOut, transposeTemplatingCalls(provisionerContent.Bytes())...)
|
||||
}
|
||||
|
||||
// Output post-processors section
|
||||
postProcessorsOut := []byte{}
|
||||
for _, pps := range tpl.PostProcessors {
|
||||
postProcessorContent := hclwrite.NewEmptyFile()
|
||||
body := postProcessorContent.Body()
|
||||
@@ -286,9 +325,69 @@ func (c *HCL2UpgradeCommand) RunContext(buildCtx context.Context, cla *HCL2Upgra
|
||||
jsonBodyToHCL2Body(ppBody, cfg)
|
||||
}
|
||||
|
||||
_, _ = out.Write(transposeTemplatingCalls(postProcessorContent.Bytes()))
|
||||
postProcessorsOut = append(postProcessorsOut, transposeTemplatingCalls(postProcessorContent.Bytes())...)
|
||||
}
|
||||
|
||||
// Output amazon-secretsmanager data source section
|
||||
keys := make([]string, 0, len(amazonSecretsManagerMap))
|
||||
for k := range amazonSecretsManagerMap {
|
||||
keys = append(keys, k)
|
||||
}
|
||||
sort.Strings(keys)
|
||||
|
||||
amazonSecretsDataOut := []byte{}
|
||||
for _, dataSourceName := range keys {
|
||||
datasourceContent := hclwrite.NewEmptyFile()
|
||||
body := datasourceContent.Body()
|
||||
body.AppendNewline()
|
||||
datasourceBody := body.AppendNewBlock("data", []string{"amazon-secretsmanager", dataSourceName}).Body()
|
||||
jsonBodyToHCL2Body(datasourceBody, amazonSecretsManagerMap[dataSourceName])
|
||||
amazonSecretsDataOut = append(amazonSecretsDataOut, datasourceContent.Bytes()...)
|
||||
}
|
||||
|
||||
// Write file
|
||||
out := &bytes.Buffer{}
|
||||
|
||||
// Packer section
|
||||
if tpl.MinVersion != "" {
|
||||
out.Write([]byte(packerBlockHeader))
|
||||
fileContent := hclwrite.NewEmptyFile()
|
||||
body := fileContent.Body()
|
||||
packerBody := body.AppendNewBlock("packer", nil).Body()
|
||||
packerBody.SetAttributeValue("required_version", cty.StringVal(fmt.Sprintf(">= %s", tpl.MinVersion)))
|
||||
out.Write(fileContent.Bytes())
|
||||
}
|
||||
|
||||
out.Write([]byte(inputVarHeader))
|
||||
out.Write(variablesOut)
|
||||
|
||||
if len(amazonSecretsManagerMap) > 0 {
|
||||
out.Write([]byte(amazonSecretsManagerDataHeader))
|
||||
out.Write(amazonSecretsDataOut)
|
||||
}
|
||||
|
||||
if len(amazonAmiOut) > 0 {
|
||||
out.Write([]byte(amazonAmiDataHeader))
|
||||
out.Write(amazonAmiOut)
|
||||
}
|
||||
|
||||
_, _ = out.Write([]byte("\n"))
|
||||
fmt.Fprintln(out, `# "timestamp" template function replacement`)
|
||||
fmt.Fprintln(out, `locals { timestamp = regex_replace(timestamp(), "[- TZ:]", "") }`)
|
||||
|
||||
if len(localsOut) > 0 {
|
||||
out.Write([]byte(localsVarHeader))
|
||||
out.Write(localsOut)
|
||||
}
|
||||
|
||||
out.Write([]byte(sourcesHeader))
|
||||
out.Write(sourcesOut)
|
||||
|
||||
out.Write([]byte(buildHeader))
|
||||
out.Write(buildOut)
|
||||
out.Write(provisionersOut)
|
||||
out.Write(postProcessorsOut)
|
||||
|
||||
_, _ = out.Write([]byte("}\n"))
|
||||
|
||||
_, _ = output.Write(hclwrite.Format(out.Bytes()))
|
||||
@@ -298,9 +397,9 @@ func (c *HCL2UpgradeCommand) RunContext(buildCtx context.Context, cla *HCL2Upgra
|
||||
return 0
|
||||
}
|
||||
|
||||
func (c *HCL2UpgradeCommand) writeAmazonAmiDatasource(builders []*template.Builder, out *bytes.Buffer) error {
|
||||
func (c *HCL2UpgradeCommand) writeAmazonAmiDatasource(builders []*template.Builder) ([]byte, error) {
|
||||
amazonAmiOut := []byte{}
|
||||
amazonAmiFilters := []map[string]interface{}{}
|
||||
first := true
|
||||
i := 1
|
||||
for _, builder := range builders {
|
||||
if strings.HasPrefix(builder.Type, "amazon-") {
|
||||
@@ -308,7 +407,7 @@ func (c *HCL2UpgradeCommand) writeAmazonAmiDatasource(builders []*template.Build
|
||||
sourceAmiFilterCfg := map[string]interface{}{}
|
||||
if err := mapstructure.Decode(sourceAmiFilter, &sourceAmiFilterCfg); err != nil {
|
||||
c.Ui.Error(fmt.Sprintf("Failed to write amazon-ami data source: %v", err))
|
||||
return err
|
||||
return nil, err
|
||||
}
|
||||
|
||||
duplicate := false
|
||||
@@ -336,21 +435,17 @@ func (c *HCL2UpgradeCommand) writeAmazonAmiDatasource(builders []*template.Build
|
||||
builder.Config["source_ami"] = sourceAmiDataRef
|
||||
i++
|
||||
|
||||
if first {
|
||||
out.Write([]byte(amazonAmiDataHeader))
|
||||
first = false
|
||||
}
|
||||
datasourceContent := hclwrite.NewEmptyFile()
|
||||
body := datasourceContent.Body()
|
||||
body.AppendNewline()
|
||||
sourceBody := body.AppendNewBlock("data", []string{"amazon-ami", dataSourceName}).Body()
|
||||
jsonBodyToHCL2Body(sourceBody, sourceAmiFilterCfg)
|
||||
_, _ = out.Write(transposeTemplatingCalls(datasourceContent.Bytes()))
|
||||
amazonAmiOut = append(amazonAmiOut, transposeTemplatingCalls(datasourceContent.Bytes())...)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return nil
|
||||
return amazonAmiOut, nil
|
||||
}
|
||||
|
||||
type UnhandleableArgumentError struct {
|
||||
@@ -377,14 +472,71 @@ func transposeTemplatingCalls(s []byte) []byte {
|
||||
|
||||
return append([]byte(fmt.Sprintf("\n# could not parse template for following block: %q\n", err)), s...)
|
||||
}
|
||||
funcMap := texttemplate.FuncMap{
|
||||
"timestamp": func() string {
|
||||
funcMap := templateCommonFunctionMap()
|
||||
|
||||
tpl, err := texttemplate.New("hcl2_upgrade").
|
||||
Funcs(funcMap).
|
||||
Parse(string(s))
|
||||
|
||||
if err != nil {
|
||||
return fallbackReturn(err)
|
||||
}
|
||||
|
||||
str := &bytes.Buffer{}
|
||||
v := struct {
|
||||
HTTPIP string
|
||||
HTTPPort string
|
||||
}{
|
||||
HTTPIP: "{{ .HTTPIP }}",
|
||||
HTTPPort: "{{ .HTTPPort }}",
|
||||
}
|
||||
if err := tpl.Execute(str, v); err != nil {
|
||||
return fallbackReturn(err)
|
||||
}
|
||||
|
||||
return str.Bytes()
|
||||
}
|
||||
|
||||
func templateCommonFunctionMap() texttemplate.FuncMap {
|
||||
return texttemplate.FuncMap{
|
||||
"aws_secretsmanager": func(a ...string) string {
|
||||
if len(a) == 2 {
|
||||
for key, config := range amazonSecretsManagerMap {
|
||||
nameOk := config["name"] == a[0]
|
||||
keyOk := config["key"] == a[1]
|
||||
if nameOk && keyOk {
|
||||
return fmt.Sprintf("${data.amazon-secretsmanager.%s.value}", key)
|
||||
}
|
||||
}
|
||||
id := fmt.Sprintf("autogenerated_%d", len(amazonSecretsManagerMap)+1)
|
||||
amazonSecretsManagerMap[id] = map[string]interface{}{
|
||||
"name": a[0],
|
||||
"key": a[1],
|
||||
}
|
||||
return fmt.Sprintf("${data.amazon-secretsmanager.%s.value}", id)
|
||||
}
|
||||
for key, config := range amazonSecretsManagerMap {
|
||||
nameOk := config["name"] == a[0]
|
||||
if nameOk {
|
||||
return fmt.Sprintf("${data.amazon-secretsmanager.%s.value}", key)
|
||||
}
|
||||
}
|
||||
id := fmt.Sprintf("autogenerated_%d", len(amazonSecretsManagerMap)+1)
|
||||
amazonSecretsManagerMap[id] = map[string]interface{}{
|
||||
"name": a[0],
|
||||
}
|
||||
return fmt.Sprintf("${data.amazon-secretsmanager.%s.value}", id)
|
||||
}, "timestamp": func() string {
|
||||
return "${local.timestamp}"
|
||||
},
|
||||
"isotime": func() string {
|
||||
return "${local.timestamp}"
|
||||
},
|
||||
"user": func(in string) string {
|
||||
if _, ok := localsVariableMap[in]; ok {
|
||||
// variable is now a local
|
||||
return fmt.Sprintf("${local.%s}", in)
|
||||
}
|
||||
return fmt.Sprintf("${var.%s}", in)
|
||||
},
|
||||
"env": func(in string) string {
|
||||
@@ -459,13 +611,34 @@ func transposeTemplatingCalls(s []byte) []byte {
|
||||
return fmt.Sprintf("${build.type}")
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
// variableTransposeTemplatingCalls executes parts of blocks as go template files and replaces
|
||||
// their result with their hcl2 variant for variables block only. If something goes wrong the template
|
||||
// containing the go template string is returned.
|
||||
// In variableTransposeTemplatingCalls the definition of aws_secretsmanager function will create a data source
|
||||
// with the same name as the variable.
|
||||
func variableTransposeTemplatingCalls(s []byte) (isLocal bool, body []byte) {
|
||||
fallbackReturn := func(err error) []byte {
|
||||
if strings.Contains(err.Error(), "unhandled") {
|
||||
return append([]byte(fmt.Sprintf("\n# %s\n", err)), s...)
|
||||
}
|
||||
|
||||
return append([]byte(fmt.Sprintf("\n# could not parse template for following block: %q\n", err)), s...)
|
||||
}
|
||||
|
||||
funcMap := templateCommonFunctionMap()
|
||||
funcMap["aws_secretsmanager"] = func(a ...string) string {
|
||||
isLocal = true
|
||||
return ""
|
||||
}
|
||||
|
||||
tpl, err := texttemplate.New("hcl2_upgrade").
|
||||
Funcs(funcMap).
|
||||
Parse(string(s))
|
||||
|
||||
if err != nil {
|
||||
return fallbackReturn(err)
|
||||
return isLocal, fallbackReturn(err)
|
||||
}
|
||||
|
||||
str := &bytes.Buffer{}
|
||||
@@ -477,10 +650,10 @@ func transposeTemplatingCalls(s []byte) []byte {
|
||||
HTTPPort: "{{ .HTTPPort }}",
|
||||
}
|
||||
if err := tpl.Execute(str, v); err != nil {
|
||||
return fallbackReturn(err)
|
||||
return isLocal, fallbackReturn(err)
|
||||
}
|
||||
|
||||
return str.Bytes()
|
||||
return isLocal, str.Bytes()
|
||||
}
|
||||
|
||||
func jsonBodyToHCL2Body(out *hclwrite.Body, kvs map[string]interface{}) {
|
||||
|
||||
@@ -19,14 +19,16 @@ func Test_hcl2_upgrade(t *testing.T) {
|
||||
tc := []struct {
|
||||
folder string
|
||||
}{
|
||||
{"hcl2_upgrade_basic"},
|
||||
{"basic"},
|
||||
{"minimal"},
|
||||
{"source-name"},
|
||||
}
|
||||
|
||||
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"))
|
||||
expectedPath := filepath.Join(testFixture("hcl2_upgrade", tc.folder, "expected.pkr.hcl"))
|
||||
p := helperCommand(t, "hcl2_upgrade", inputPath)
|
||||
bs, err := p.CombinedOutput()
|
||||
if err != nil {
|
||||
|
||||
+171
@@ -0,0 +1,171 @@
|
||||
package command
|
||||
|
||||
import (
|
||||
"context"
|
||||
"crypto/sha256"
|
||||
"fmt"
|
||||
"log"
|
||||
"runtime"
|
||||
"strings"
|
||||
|
||||
pluginsdk "github.com/hashicorp/packer-plugin-sdk/plugin"
|
||||
"github.com/hashicorp/packer/packer"
|
||||
plugingetter "github.com/hashicorp/packer/packer/plugin-getter"
|
||||
"github.com/hashicorp/packer/packer/plugin-getter/github"
|
||||
"github.com/hashicorp/packer/version"
|
||||
"github.com/posener/complete"
|
||||
)
|
||||
|
||||
type InitCommand struct {
|
||||
Meta
|
||||
}
|
||||
|
||||
func (c *InitCommand) Run(args []string) int {
|
||||
ctx, cleanup := handleTermInterrupt(c.Ui)
|
||||
defer cleanup()
|
||||
|
||||
cfg, ret := c.ParseArgs(args)
|
||||
if ret != 0 {
|
||||
return ret
|
||||
}
|
||||
|
||||
return c.RunContext(ctx, cfg)
|
||||
}
|
||||
|
||||
func (c *InitCommand) ParseArgs(args []string) (*InitArgs, int) {
|
||||
var cfg InitArgs
|
||||
flags := c.Meta.FlagSet("init", 0)
|
||||
flags.Usage = func() { c.Ui.Say(c.Help()) }
|
||||
cfg.AddFlagSets(flags)
|
||||
if err := flags.Parse(args); err != nil {
|
||||
return &cfg, 1
|
||||
}
|
||||
|
||||
args = flags.Args()
|
||||
if len(args) != 1 {
|
||||
flags.Usage()
|
||||
return &cfg, 1
|
||||
}
|
||||
cfg.Path = args[0]
|
||||
return &cfg, 0
|
||||
}
|
||||
|
||||
func (c *InitCommand) RunContext(buildCtx context.Context, cla *InitArgs) int {
|
||||
packerStarter, ret := c.GetConfig(&cla.MetaArgs)
|
||||
if ret != 0 {
|
||||
return ret
|
||||
}
|
||||
|
||||
// Get plugins requirements
|
||||
reqs, diags := packerStarter.PluginRequirements()
|
||||
ret = writeDiags(c.Ui, nil, diags)
|
||||
if ret != 0 {
|
||||
return ret
|
||||
}
|
||||
|
||||
opts := plugingetter.ListInstallationsOptions{
|
||||
FromFolders: c.Meta.CoreConfig.Components.PluginConfig.KnownPluginFolders,
|
||||
BinaryInstallationOptions: plugingetter.BinaryInstallationOptions{
|
||||
OS: runtime.GOOS,
|
||||
ARCH: runtime.GOARCH,
|
||||
APIVersionMajor: pluginsdk.APIVersionMajor,
|
||||
APIVersionMinor: pluginsdk.APIVersionMinor,
|
||||
Checksummers: []plugingetter.Checksummer{
|
||||
{Type: "sha256", Hash: sha256.New()},
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
if runtime.GOOS == "windows" && opts.Ext == "" {
|
||||
opts.BinaryInstallationOptions.Ext = ".exe"
|
||||
}
|
||||
|
||||
log.Printf("[TRACE] init: %#v", opts)
|
||||
|
||||
getters := []plugingetter.Getter{
|
||||
&github.Getter{
|
||||
// In the past some terraform plugins downloads were blocked from a
|
||||
// specific aws region by s3. Changing the user agent unblocked the
|
||||
// downloads so having one user agent per version will help mitigate
|
||||
// that a little more. Especially in the case someone forks this
|
||||
// code to make it more aggressive or something.
|
||||
// TODO: allow to set this from the config file or an environment
|
||||
// variable.
|
||||
UserAgent: "packer-getter-github-" + version.String(),
|
||||
},
|
||||
}
|
||||
|
||||
ui := &packer.ColoredUi{
|
||||
Color: packer.UiColorCyan,
|
||||
Ui: c.Ui,
|
||||
}
|
||||
|
||||
for _, pluginRequirement := range reqs {
|
||||
// Get installed plugins that match requirement
|
||||
|
||||
installs, err := pluginRequirement.ListInstallations(opts)
|
||||
if err != nil {
|
||||
c.Ui.Error(err.Error())
|
||||
return 1
|
||||
}
|
||||
|
||||
log.Printf("[TRACE] for plugin %s found %d matching installation(s)", pluginRequirement.Identifier.ForDisplay(), len(installs))
|
||||
|
||||
if len(installs) > 0 && cla.Upgrade == false {
|
||||
continue
|
||||
}
|
||||
|
||||
newInstall, err := pluginRequirement.InstallLatest(plugingetter.InstallOptions{
|
||||
InFolders: opts.FromFolders,
|
||||
BinaryInstallationOptions: opts.BinaryInstallationOptions,
|
||||
Getters: getters,
|
||||
})
|
||||
if err != nil {
|
||||
c.Ui.Error(err.Error())
|
||||
ret = 1
|
||||
}
|
||||
if newInstall != nil {
|
||||
msg := fmt.Sprintf("Installed plugin %s %s in %q", pluginRequirement.Identifier.ForDisplay(), newInstall.Version, newInstall.BinaryPath)
|
||||
ui.Say(msg)
|
||||
}
|
||||
}
|
||||
return ret
|
||||
}
|
||||
|
||||
func (*InitCommand) Help() string {
|
||||
helpText := `
|
||||
Usage: packer init [options] [config.pkr.hcl|folder/]
|
||||
|
||||
Install all the missing plugins required in a Packer config. Note that Packer
|
||||
does not have a state.
|
||||
|
||||
This is the first command that should be executed when working with a new
|
||||
or existing template.
|
||||
|
||||
This command is always safe to run multiple times. Though subsequent runs may
|
||||
give errors, this command will never delete anything.
|
||||
|
||||
Options:
|
||||
-upgrade On top of installing missing plugins, update
|
||||
installed plugins to the latest available
|
||||
version, if there is a new higher one. Note that
|
||||
this still takes into consideration the version
|
||||
constraint of the config.
|
||||
`
|
||||
|
||||
return strings.TrimSpace(helpText)
|
||||
}
|
||||
|
||||
func (*InitCommand) Synopsis() string {
|
||||
return "Install missing plugins or upgrade plugins"
|
||||
}
|
||||
|
||||
func (*InitCommand) AutocompleteArgs() complete.Predictor {
|
||||
return complete.PredictNothing
|
||||
}
|
||||
|
||||
func (*InitCommand) AutocompleteFlags() complete.Flags {
|
||||
return complete.Flags{
|
||||
"-upgrade": complete.PredictNothing,
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,301 @@
|
||||
package command
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"log"
|
||||
"os"
|
||||
"runtime"
|
||||
"sort"
|
||||
"testing"
|
||||
|
||||
"github.com/google/go-cmp/cmp"
|
||||
"github.com/hashicorp/packer-plugin-sdk/acctest"
|
||||
"golang.org/x/mod/sumdb/dirhash"
|
||||
)
|
||||
|
||||
type testCaseInit struct {
|
||||
checkSkip func(*testing.T)
|
||||
name string
|
||||
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.
|
||||
|
||||
cfg := &configDirSingleton{map[string]string{}}
|
||||
|
||||
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",
|
||||
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",
|
||||
"github.com/sylviamoss/comment/packer-plugin-comment_v0.2.18_x5.0_windows_amd64.exe": "1.exe",
|
||||
"github.com/sylviamoss/comment/packer-plugin-comment_v0.2.18_x5.0_windows_amd64.exe_SHA256SUM": "07d8453027192ee0c4120242e6e84e2ca2328b8e0f506e2f818a1a5b82790a0b",
|
||||
"github.com/sylviamoss/comment/packer-plugin-comment_v0.2.18_x5.0_linux_amd64": "1.out",
|
||||
"github.com/sylviamoss/comment/packer-plugin-comment_v0.2.18_x5.0_linux_amd64_SHA256SUM": "59031c50e0dfeedfde2b4e9445754804dce3f29e4efa737eead0ca9b4f5b85a5",
|
||||
},
|
||||
"h1:Q5qyAOdD43hL3CquQdVfaHpOYGf0UsZ/+wVA9Ry6cbA=",
|
||||
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",
|
||||
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",
|
||||
"github.com/sylviamoss/comment/packer-plugin-comment_v0.2.18_x5.0_windows_amd64.exe": "1.exe",
|
||||
"github.com/sylviamoss/comment/packer-plugin-comment_v0.2.18_x5.0_windows_amd64.exe_SHA256SUM": "07d8453027192ee0c4120242e6e84e2ca2328b8e0f506e2f818a1a5b82790a0b",
|
||||
"github.com/sylviamoss/comment/packer-plugin-comment_v0.2.18_x5.0_linux_amd64": "1.out",
|
||||
"github.com/sylviamoss/comment/packer-plugin-comment_v0.2.18_x5.0_linux_amd64_SHA256SUM": "59031c50e0dfeedfde2b4e9445754804dce3f29e4efa737eead0ca9b4f5b85a5",
|
||||
},
|
||||
"h1:Q5qyAOdD43hL3CquQdVfaHpOYGf0UsZ/+wVA9Ry6cbA=",
|
||||
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_pkr_config"),
|
||||
cfg.dir("2_pkr_user_folder"),
|
||||
0,
|
||||
[]string{
|
||||
"github.com/sylviamoss/comment/packer-plugin-comment_v0.2.18_x5.0_darwin_amd64",
|
||||
"github.com/sylviamoss/comment/packer-plugin-comment_v0.2.18_x5.0_darwin_amd64_SHA256SUM",
|
||||
"github.com/sylviamoss/comment/packer-plugin-comment_v0.2.18_x5.0_linux_amd64",
|
||||
"github.com/sylviamoss/comment/packer-plugin-comment_v0.2.18_x5.0_linux_amd64_SHA256SUM",
|
||||
"github.com/sylviamoss/comment/packer-plugin-comment_v0.2.18_x5.0_windows_amd64.exe",
|
||||
"github.com/sylviamoss/comment/packer-plugin-comment_v0.2.18_x5.0_windows_amd64.exe_SHA256SUM",
|
||||
map[string]string{
|
||||
"darwin": "github.com/sylviamoss/comment/packer-plugin-comment_v0.2.19_x5.0_darwin_amd64_SHA256SUM",
|
||||
"linux": "github.com/sylviamoss/comment/packer-plugin-comment_v0.2.19_x5.0_linux_amd64_SHA256SUM",
|
||||
"windows": "github.com/sylviamoss/comment/packer-plugin-comment_v0.2.19_x5.0_windows_amd64.exe_SHA256SUM",
|
||||
}[runtime.GOOS],
|
||||
map[string]string{
|
||||
"darwin": "github.com/sylviamoss/comment/packer-plugin-comment_v0.2.19_x5.0_darwin_amd64",
|
||||
"linux": "github.com/sylviamoss/comment/packer-plugin-comment_v0.2.19_x5.0_linux_amd64",
|
||||
"windows": "github.com/sylviamoss/comment/packer-plugin-comment_v0.2.19_x5.0_windows_amd64.exe",
|
||||
}[runtime.GOOS],
|
||||
},
|
||||
map[string]string{
|
||||
"darwin": "h1:ORwcCYUx8z/5n/QvuTJo2vrgKpfJA4AxlNg1G9/BCDI=",
|
||||
"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",
|
||||
testMetaFile(t),
|
||||
nil,
|
||||
"h1:47DEQpj8HBSa+/TImW+5JCeuQeRkm5NMpJWZG3hSuFU=",
|
||||
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,
|
||||
},
|
||||
{
|
||||
func(t *testing.T) {
|
||||
if os.Getenv(acctest.TestEnvVar) == "" {
|
||||
t.Skipf("Acceptance test skipped unless env '%s' set", acctest.TestEnvVar)
|
||||
}
|
||||
tc := testCaseInit{}
|
||||
if err := getBinary(getBinaryOptions{
|
||||
GetZip: "https://github.com/azr/packer-provisioner-comment/releases/download/v1.0.0/packer-provisioner-comment_v1.0.0_" + runtime.GOOS + "_" + runtime.GOARCH + ".tar.gz",
|
||||
UnzipIn: tc.inPluginFolder,
|
||||
}); err != nil {
|
||||
t.Fatal("getBinary: %v", err)
|
||||
}
|
||||
},
|
||||
"release-with-no-binary",
|
||||
testMetaFile(t),
|
||||
nil,
|
||||
"h1:47DEQpj8HBSa+/TImW+5JCeuQeRkm5NMpJWZG3hSuFU=",
|
||||
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,
|
||||
},
|
||||
}
|
||||
for _, tt := range tests {
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
if tt.checkSkip != nil {
|
||||
tt.checkSkip(t)
|
||||
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)
|
||||
t.Cleanup(func() {
|
||||
_ = os.RemoveAll(tt.packerUserFolder)
|
||||
})
|
||||
|
||||
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)
|
||||
}
|
||||
|
||||
args := []string{tt.packerUserFolder}
|
||||
|
||||
c := &InitCommand{
|
||||
Meta: tt.Meta,
|
||||
}
|
||||
|
||||
c.CoreConfig.Components.PluginConfig.KnownPluginFolders = []string{tt.packerConfigDir}
|
||||
if got := c.Run(args); got != tt.want {
|
||||
t.Errorf("InitCommand.Run() = %v, want %v", got, tt.want)
|
||||
}
|
||||
|
||||
if tt.dirFiles != nil {
|
||||
dirFiles, err := dirhash.DirFiles(tt.packerConfigDir, "")
|
||||
if err != nil {
|
||||
t.Fatalf("DirFiles: %v", err)
|
||||
}
|
||||
sort.Strings(tt.dirFiles)
|
||||
sort.Strings(dirFiles)
|
||||
if diff := cmp.Diff(tt.dirFiles, dirFiles); diff != "" {
|
||||
t.Errorf("found files differ: %v", diff)
|
||||
}
|
||||
}
|
||||
|
||||
hash, err = dirhash.HashDir(tt.packerConfigDir, "", dirhash.DefaultHash)
|
||||
if err != nil {
|
||||
t.Fatalf("HashDir: %v", err)
|
||||
}
|
||||
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)
|
||||
})
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
@@ -42,13 +42,13 @@ build {
|
||||
}
|
||||
|
||||
post-processor "shell-local" {
|
||||
only = ["sources.file.vanilla"]
|
||||
only = ["file.vanilla"]
|
||||
name = "tomato"
|
||||
inline = [ "echo apple > tomato.txt" ]
|
||||
}
|
||||
|
||||
post-processor "shell-local" {
|
||||
only = ["sources.file.chocolate"]
|
||||
only = ["file.chocolate"]
|
||||
inline = [ "echo apple > unnamed.txt" ]
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,12 @@
|
||||
source "null" "potato" {
|
||||
communicator = "none"
|
||||
}
|
||||
|
||||
build {
|
||||
sources = ["sources.null.potato"]
|
||||
|
||||
post-processor "manifest" {
|
||||
output = "manifest.json"
|
||||
strip_time = true
|
||||
}
|
||||
}
|
||||
+47
-2
@@ -47,13 +47,36 @@ 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" {
|
||||
filters = {
|
||||
name = "ubuntu/images/*/ubuntu-xenial-16.04-amd64-server-*"
|
||||
@@ -64,6 +87,22 @@ data "amazon-ami" "autogenerated_1" {
|
||||
owners = ["099720109477"]
|
||||
}
|
||||
|
||||
# "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
|
||||
# build blocks. A build block runs provisioner and post-processors on a
|
||||
# source. Read the documentation for source blocks here:
|
||||
@@ -135,6 +174,12 @@ build {
|
||||
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.
|
||||
+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": [
|
||||
@@ -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" "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 = "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-{{ 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,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,41 @@
|
||||
package command
|
||||
|
||||
import (
|
||||
"io/ioutil"
|
||||
"log"
|
||||
"os"
|
||||
"path/filepath"
|
||||
)
|
||||
|
||||
func mustString(s string, e error) string {
|
||||
if e != nil {
|
||||
panic(e)
|
||||
}
|
||||
return s
|
||||
}
|
||||
|
||||
func createFiles(dir string, content map[string]string) {
|
||||
for relPath, content := range content {
|
||||
contentPath := filepath.Join(dir, relPath)
|
||||
if err := os.MkdirAll(filepath.Dir(contentPath), 0777); err != nil {
|
||||
panic(err)
|
||||
}
|
||||
if err := ioutil.WriteFile(contentPath, []byte(content), 0666); err != nil {
|
||||
panic(err)
|
||||
}
|
||||
log.Printf("created tmp file: %s", contentPath)
|
||||
}
|
||||
}
|
||||
|
||||
type configDirSingleton struct {
|
||||
dirs map[string]string
|
||||
}
|
||||
|
||||
// when you call dir twice with the same key, the result should be the same
|
||||
func (c *configDirSingleton) dir(key string) string {
|
||||
if v, exists := c.dirs[key]; exists {
|
||||
return v
|
||||
}
|
||||
c.dirs[key] = mustString(ioutil.TempDir("", "pkr-test-cfg-dir-"+key))
|
||||
return c.dirs[key]
|
||||
}
|
||||
@@ -6,6 +6,7 @@ import (
|
||||
|
||||
"github.com/google/go-cmp/cmp"
|
||||
packersdk "github.com/hashicorp/packer-plugin-sdk/packer"
|
||||
"github.com/hashicorp/packer/packer"
|
||||
)
|
||||
|
||||
func TestValidateCommand(t *testing.T) {
|
||||
@@ -49,7 +50,7 @@ func TestValidateCommand(t *testing.T) {
|
||||
func TestValidateCommand_SkipDatasourceExecution(t *testing.T) {
|
||||
datasourceMock := &packersdk.MockDatasource{}
|
||||
meta := testMetaFile(t)
|
||||
meta.CoreConfig.Components.DatasourceStore = packersdk.MapOfDatasource{
|
||||
meta.CoreConfig.Components.PluginConfig.DataSources = packer.MapOfDatasource{
|
||||
"mock": func() (packersdk.Datasource, error) {
|
||||
return datasourceMock, nil
|
||||
},
|
||||
|
||||
@@ -44,6 +44,12 @@ func init() {
|
||||
}, nil
|
||||
},
|
||||
|
||||
"init": func() (cli.Command, error) {
|
||||
return &command.InitCommand{
|
||||
Meta: *CommandMeta,
|
||||
}, nil
|
||||
},
|
||||
|
||||
"inspect": func() (cli.Command, error) {
|
||||
return &command.InspectCommand{
|
||||
Meta: *CommandMeta,
|
||||
|
||||
@@ -14,7 +14,6 @@ import (
|
||||
packersdk "github.com/hashicorp/packer-plugin-sdk/packer"
|
||||
"github.com/hashicorp/packer/command"
|
||||
"github.com/hashicorp/packer/packer"
|
||||
"github.com/hashicorp/packer/packer/plugin"
|
||||
)
|
||||
|
||||
// PACKERSPACE is used to represent the spaces that separate args for a command
|
||||
@@ -22,16 +21,13 @@ import (
|
||||
const PACKERSPACE = "-PACKERSPACE-"
|
||||
|
||||
type config struct {
|
||||
DisableCheckpoint bool `json:"disable_checkpoint"`
|
||||
DisableCheckpointSignature bool `json:"disable_checkpoint_signature"`
|
||||
RawBuilders map[string]string `json:"builders"`
|
||||
RawProvisioners map[string]string `json:"provisioners"`
|
||||
RawPostProcessors map[string]string `json:"post-processors"`
|
||||
Builders packer.MapOfBuilder `json:"-"`
|
||||
Provisioners packer.MapOfProvisioner `json:"-"`
|
||||
PostProcessors packer.MapOfPostProcessor `json:"-"`
|
||||
Datasources packer.MapOfDatasource `json:"-"`
|
||||
Plugins plugin.Config
|
||||
DisableCheckpoint bool `json:"disable_checkpoint"`
|
||||
DisableCheckpointSignature bool `json:"disable_checkpoint_signature"`
|
||||
RawBuilders map[string]string `json:"builders"`
|
||||
RawProvisioners map[string]string `json:"provisioners"`
|
||||
RawPostProcessors map[string]string `json:"post-processors"`
|
||||
|
||||
Plugins *packer.PluginConfig
|
||||
}
|
||||
|
||||
// decodeConfig decodes configuration in JSON format from the given io.Reader into
|
||||
@@ -97,19 +93,19 @@ func (c *config) loadSingleComponent(path string) (string, error) {
|
||||
switch {
|
||||
case strings.HasPrefix(pluginName, "packer-builder-"):
|
||||
pluginName = pluginName[len("packer-builder-"):]
|
||||
c.Builders[pluginName] = func() (packersdk.Builder, error) {
|
||||
c.Plugins.Builders.Set(pluginName, func() (packersdk.Builder, error) {
|
||||
return c.Plugins.Client(path).Builder()
|
||||
}
|
||||
})
|
||||
case strings.HasPrefix(pluginName, "packer-post-processor-"):
|
||||
pluginName = pluginName[len("packer-post-processor-"):]
|
||||
c.PostProcessors[pluginName] = func() (packersdk.PostProcessor, error) {
|
||||
c.Plugins.PostProcessors.Set(pluginName, func() (packersdk.PostProcessor, error) {
|
||||
return c.Plugins.Client(path).PostProcessor()
|
||||
}
|
||||
})
|
||||
case strings.HasPrefix(pluginName, "packer-provisioner-"):
|
||||
pluginName = pluginName[len("packer-provisioner-"):]
|
||||
c.Provisioners[pluginName] = func() (packersdk.Provisioner, error) {
|
||||
c.Plugins.Provisioners.Set(pluginName, func() (packersdk.Provisioner, error) {
|
||||
return c.Plugins.Client(path).Provisioner()
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
return pluginName, nil
|
||||
@@ -119,7 +115,7 @@ func (c *config) loadSingleComponent(path string) (string, error) {
|
||||
// implementations from the defined plugins.
|
||||
func (c *config) StartBuilder(name string) (packersdk.Builder, error) {
|
||||
log.Printf("Loading builder: %s\n", name)
|
||||
return c.Builders.Start(name)
|
||||
return c.Plugins.Builders.Start(name)
|
||||
}
|
||||
|
||||
// This is a proper implementation of packer.HookFunc that can be used
|
||||
@@ -133,14 +129,14 @@ func (c *config) StarHook(name string) (packersdk.Hook, error) {
|
||||
// packersdk.PostProcessor implementations from defined plugins.
|
||||
func (c *config) StartPostProcessor(name string) (packersdk.PostProcessor, error) {
|
||||
log.Printf("Loading post-processor: %s", name)
|
||||
return c.PostProcessors.Start(name)
|
||||
return c.Plugins.PostProcessors.Start(name)
|
||||
}
|
||||
|
||||
// This is a proper packer.ProvisionerFunc that can be used to load
|
||||
// packer.Provisioner implementations from defined plugins.
|
||||
func (c *config) StartProvisioner(name string) (packersdk.Provisioner, error) {
|
||||
log.Printf("Loading provisioner: %s\n", name)
|
||||
return c.Provisioners.Start(name)
|
||||
return c.Plugins.Provisioners.Start(name)
|
||||
}
|
||||
|
||||
func (c *config) discoverInternalComponents() error {
|
||||
@@ -153,49 +149,45 @@ func (c *config) discoverInternalComponents() error {
|
||||
|
||||
for builder := range command.Builders {
|
||||
builder := builder
|
||||
_, found := (c.Builders)[builder]
|
||||
if !found {
|
||||
c.Builders[builder] = func() (packersdk.Builder, error) {
|
||||
bin := fmt.Sprintf("%s%splugin%spacker-builder-%s",
|
||||
packerPath, PACKERSPACE, PACKERSPACE, builder)
|
||||
if !c.Plugins.Builders.Has(builder) {
|
||||
bin := fmt.Sprintf("%s%splugin%spacker-builder-%s",
|
||||
packerPath, PACKERSPACE, PACKERSPACE, builder)
|
||||
c.Plugins.Builders.Set(builder, func() (packersdk.Builder, error) {
|
||||
return c.Plugins.Client(bin).Builder()
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
for provisioner := range command.Provisioners {
|
||||
provisioner := provisioner
|
||||
_, found := (c.Provisioners)[provisioner]
|
||||
if !found {
|
||||
c.Provisioners[provisioner] = func() (packersdk.Provisioner, error) {
|
||||
bin := fmt.Sprintf("%s%splugin%spacker-provisioner-%s",
|
||||
packerPath, PACKERSPACE, PACKERSPACE, provisioner)
|
||||
if !c.Plugins.Provisioners.Has(provisioner) {
|
||||
bin := fmt.Sprintf("%s%splugin%spacker-provisioner-%s",
|
||||
packerPath, PACKERSPACE, PACKERSPACE, provisioner)
|
||||
c.Plugins.Provisioners.Set(provisioner, func() (packersdk.Provisioner, error) {
|
||||
return c.Plugins.Client(bin).Provisioner()
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
for postProcessor := range command.PostProcessors {
|
||||
postProcessor := postProcessor
|
||||
_, found := (c.PostProcessors)[postProcessor]
|
||||
if !found {
|
||||
c.PostProcessors[postProcessor] = func() (packersdk.PostProcessor, error) {
|
||||
bin := fmt.Sprintf("%s%splugin%spacker-post-processor-%s",
|
||||
packerPath, PACKERSPACE, PACKERSPACE, postProcessor)
|
||||
if !c.Plugins.PostProcessors.Has(postProcessor) {
|
||||
bin := fmt.Sprintf("%s%splugin%spacker-post-processor-%s",
|
||||
packerPath, PACKERSPACE, PACKERSPACE, postProcessor)
|
||||
c.Plugins.PostProcessors.Set(postProcessor, func() (packersdk.PostProcessor, error) {
|
||||
return c.Plugins.Client(bin).PostProcessor()
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
for dataSource := range command.Datasources {
|
||||
dataSource := dataSource
|
||||
_, found := (c.Datasources)[dataSource]
|
||||
if !found {
|
||||
c.Datasources[dataSource] = func() (packersdk.Datasource, error) {
|
||||
bin := fmt.Sprintf("%s%splugin%spacker-datasource-%s",
|
||||
packerPath, PACKERSPACE, PACKERSPACE, dataSource)
|
||||
if !c.Plugins.DataSources.Has(dataSource) {
|
||||
bin := fmt.Sprintf("%s%splugin%spacker-datasource-%s",
|
||||
packerPath, PACKERSPACE, PACKERSPACE, dataSource)
|
||||
c.Plugins.DataSources.Set(dataSource, func() (packersdk.Datasource, error) {
|
||||
return c.Plugins.Client(bin).Datasource()
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
+33
-22
@@ -48,10 +48,13 @@ func TestLoadExternalComponentsFromConfig(t *testing.T) {
|
||||
}
|
||||
defer cleanUpFunc()
|
||||
|
||||
var cfg config
|
||||
cfg.Builders = packer.MapOfBuilder{}
|
||||
cfg.PostProcessors = packer.MapOfPostProcessor{}
|
||||
cfg.Provisioners = packer.MapOfProvisioner{}
|
||||
cfg := config{
|
||||
Plugins: &packer.PluginConfig{
|
||||
Builders: packer.MapOfBuilder{},
|
||||
PostProcessors: packer.MapOfPostProcessor{},
|
||||
Provisioners: packer.MapOfProvisioner{},
|
||||
},
|
||||
}
|
||||
|
||||
if err := decodeConfig(strings.NewReader(packerConfigData), &cfg); err != nil {
|
||||
t.Fatalf("error encountered decoding configuration: %v", err)
|
||||
@@ -59,16 +62,16 @@ func TestLoadExternalComponentsFromConfig(t *testing.T) {
|
||||
|
||||
cfg.LoadExternalComponentsFromConfig()
|
||||
|
||||
if len(cfg.Builders) != 1 || !cfg.Builders.Has("cloud-xyz") {
|
||||
t.Errorf("failed to load external builders; got %v as the resulting config", cfg.Builders)
|
||||
if len(cfg.Plugins.Builders.List()) != 1 || !cfg.Plugins.Builders.Has("cloud-xyz") {
|
||||
t.Errorf("failed to load external builders; got %v as the resulting config", cfg.Plugins.Builders)
|
||||
}
|
||||
|
||||
if len(cfg.PostProcessors) != 1 || !cfg.PostProcessors.Has("noop") {
|
||||
t.Errorf("failed to load external post-processors; got %v as the resulting config", cfg.PostProcessors)
|
||||
if len(cfg.Plugins.PostProcessors.List()) != 1 || !cfg.Plugins.PostProcessors.Has("noop") {
|
||||
t.Errorf("failed to load external post-processors; got %v as the resulting config", cfg.Plugins.PostProcessors)
|
||||
}
|
||||
|
||||
if len(cfg.Provisioners) != 1 || !cfg.Provisioners.Has("super-shell") {
|
||||
t.Errorf("failed to load external provisioners; got %v as the resulting config", cfg.Provisioners)
|
||||
if len(cfg.Plugins.Provisioners.List()) != 1 || !cfg.Plugins.Provisioners.Has("super-shell") {
|
||||
t.Errorf("failed to load external provisioners; got %v as the resulting config", cfg.Plugins.Provisioners)
|
||||
}
|
||||
|
||||
}
|
||||
@@ -80,8 +83,13 @@ func TestLoadExternalComponentsFromConfig_onlyProvisioner(t *testing.T) {
|
||||
}
|
||||
defer cleanUpFunc()
|
||||
|
||||
var cfg config
|
||||
cfg.Provisioners = packer.MapOfProvisioner{}
|
||||
cfg := config{
|
||||
Plugins: &packer.PluginConfig{
|
||||
Builders: packer.MapOfBuilder{},
|
||||
PostProcessors: packer.MapOfPostProcessor{},
|
||||
Provisioners: packer.MapOfProvisioner{},
|
||||
},
|
||||
}
|
||||
|
||||
if err := decodeConfig(strings.NewReader(packerConfigData), &cfg); err != nil {
|
||||
t.Fatalf("error encountered decoding configuration: %v", err)
|
||||
@@ -95,16 +103,16 @@ func TestLoadExternalComponentsFromConfig_onlyProvisioner(t *testing.T) {
|
||||
|
||||
cfg.LoadExternalComponentsFromConfig()
|
||||
|
||||
if len(cfg.Builders) != 0 {
|
||||
t.Errorf("loaded external builders when it wasn't supposed to; got %v as the resulting config", cfg.Builders)
|
||||
if len(cfg.Plugins.Builders.List()) != 0 {
|
||||
t.Errorf("loaded external builders when it wasn't supposed to; got %v as the resulting config", cfg.Plugins.Builders)
|
||||
}
|
||||
|
||||
if len(cfg.PostProcessors) != 0 {
|
||||
t.Errorf("loaded external post-processors when it wasn't supposed to; got %v as the resulting config", cfg.PostProcessors)
|
||||
if len(cfg.Plugins.PostProcessors.List()) != 0 {
|
||||
t.Errorf("loaded external post-processors when it wasn't supposed to; got %v as the resulting config", cfg.Plugins.PostProcessors)
|
||||
}
|
||||
|
||||
if len(cfg.Provisioners) != 1 || !cfg.Provisioners.Has("super-shell") {
|
||||
t.Errorf("failed to load external provisioners; got %v as the resulting config", cfg.Provisioners)
|
||||
if len(cfg.Plugins.Provisioners.List()) != 1 || !cfg.Plugins.Provisioners.Has("super-shell") {
|
||||
t.Errorf("failed to load external provisioners; got %v as the resulting config", cfg.Plugins.Provisioners)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -125,10 +133,13 @@ func TestLoadSingleComponent(t *testing.T) {
|
||||
{pluginPath: "./non-existing-file", errorExpected: true},
|
||||
}
|
||||
|
||||
var cfg config
|
||||
cfg.Builders = packer.MapOfBuilder{}
|
||||
cfg.PostProcessors = packer.MapOfPostProcessor{}
|
||||
cfg.Provisioners = packer.MapOfProvisioner{}
|
||||
cfg := config{
|
||||
Plugins: &packer.PluginConfig{
|
||||
Builders: packer.MapOfBuilder{},
|
||||
PostProcessors: packer.MapOfPostProcessor{},
|
||||
Provisioners: packer.MapOfProvisioner{},
|
||||
},
|
||||
}
|
||||
|
||||
for _, tc := range tt {
|
||||
tc := tc
|
||||
|
||||
+34
-2
@@ -1,7 +1,39 @@
|
||||
source "null" "example" {
|
||||
source "amazon-ebs" "example" {
|
||||
communicator = "none"
|
||||
source_ami = "potato"
|
||||
ami_name = "potato"
|
||||
instance_type = "potato"
|
||||
}
|
||||
|
||||
build {
|
||||
sources = ["source.null.example"]
|
||||
name = "my-provisioners-are-cooler"
|
||||
sources = ["source.amazon-ebs.example"]
|
||||
|
||||
provisioner "comment-that-works" {
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
packer {
|
||||
required_plugins {
|
||||
comment = {
|
||||
source = "sylviamoss/comment"
|
||||
version = "v0.2.15"
|
||||
}
|
||||
comment-that-works = {
|
||||
source = "sylviamoss/comment"
|
||||
version = "v0.2.19"
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
build {
|
||||
sources = ["source.amazon-ebs.example"]
|
||||
|
||||
provisioner "comment-my-provisioner" {
|
||||
|
||||
}
|
||||
provisioner "shell-local" {
|
||||
inline = ["yo"]
|
||||
}
|
||||
}
|
||||
|
||||
@@ -23,7 +23,7 @@ require (
|
||||
github.com/cheggaaa/pb v1.0.27
|
||||
github.com/chzyer/readline v0.0.0-20180603132655-2972be24d48e
|
||||
github.com/dgrijalva/jwt-go v3.2.0+incompatible
|
||||
github.com/digitalocean/go-qemu v0.0.0-20181112162955-dd7bb9c771b8
|
||||
github.com/digitalocean/go-qemu v0.0.0-20201211181942-d361e7b4965f
|
||||
github.com/digitalocean/godo v1.11.1
|
||||
github.com/exoscale/egoscale v0.18.1
|
||||
github.com/fatih/camelcase v1.0.0
|
||||
@@ -33,6 +33,7 @@ require (
|
||||
github.com/gobwas/glob v0.2.3
|
||||
github.com/golang-collections/collections v0.0.0-20130729185459-604e922904d3
|
||||
github.com/google/go-cmp v0.5.2
|
||||
github.com/google/go-github/v33 v33.0.1-0.20210113204525-9318e629ec69
|
||||
github.com/google/uuid v1.1.2
|
||||
github.com/gophercloud/gophercloud v0.12.0
|
||||
github.com/gophercloud/utils v0.0.0-20200508015959-b0167b94122c
|
||||
@@ -49,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.9
|
||||
github.com/hashicorp/packer-plugin-sdk v0.0.11
|
||||
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
|
||||
@@ -75,6 +76,7 @@ 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
|
||||
@@ -88,6 +90,7 @@ require (
|
||||
github.com/zclconf/go-cty-yaml v1.0.1
|
||||
golang.org/x/crypto v0.0.0-20201208171446-5f87f3452ae9
|
||||
golang.org/x/mobile v0.0.0-20201208152944-da85bec010a2
|
||||
golang.org/x/mod v0.3.0
|
||||
golang.org/x/net v0.0.0-20201209123823-ac852fbbde11
|
||||
golang.org/x/oauth2 v0.0.0-20200902213428-5d25da1a8d43
|
||||
golang.org/x/sync v0.0.0-20201207232520-09787c993a3a
|
||||
|
||||
@@ -38,6 +38,8 @@ github.com/1and1/oneandone-cloudserver-sdk-go v1.0.1 h1:RMTyvS5bjvSWiUcfqfr/E2px
|
||||
github.com/1and1/oneandone-cloudserver-sdk-go v1.0.1/go.mod h1:61apmbkVJH4kg+38ftT+/l0XxdUCVnHggqcOTqZRSEE=
|
||||
github.com/Azure/azure-sdk-for-go v40.5.0+incompatible h1:CVQNKuUepSFBo6BW6gM1J9slPHLRcjn6vaw+j+causw=
|
||||
github.com/Azure/azure-sdk-for-go v40.5.0+incompatible/go.mod h1:9XXNKU+eRnpl9moKnB4QOLf1HestfXbmab5FXxiDBjc=
|
||||
github.com/Azure/go-autorest v14.2.0+incompatible h1:V5VMDjClD3GiElqLWO7mz2MxNAK/vTfRHdAubSIPRgs=
|
||||
github.com/Azure/go-autorest v14.2.0+incompatible/go.mod h1:r+4oMnoxhatjLLJ6zxSWATqVooLgysK6ZNox3g/xq24=
|
||||
github.com/Azure/go-autorest/autorest v0.9.0/go.mod h1:xyHB1BMZT0cuDHU7I0+g046+BFDTQ8rEZB0s4Yfa6bI=
|
||||
github.com/Azure/go-autorest/autorest v0.9.3/go.mod h1:GsRuLYvwzLjjjRoWEIyMUaYq8GNUx2nRB378IPt/1p0=
|
||||
github.com/Azure/go-autorest/autorest v0.10.0 h1:mvdtztBqcL8se7MdrUweNieTNi4kfNG6GOJuurQJpuY=
|
||||
@@ -62,10 +64,13 @@ github.com/Azure/go-autorest/autorest/to v0.3.0 h1:zebkZaadz7+wIQYgC7GXaz3Wb28yK
|
||||
github.com/Azure/go-autorest/autorest/to v0.3.0/go.mod h1:MgwOyqaIuKdG4TL/2ywSsIWKAfJfgHDo8ObuUk3t5sA=
|
||||
github.com/Azure/go-autorest/autorest/validation v0.2.0 h1:15vMO4y76dehZSq7pAaOLQxC6dZYsSrj2GQpflyM/L4=
|
||||
github.com/Azure/go-autorest/autorest/validation v0.2.0/go.mod h1:3EEqHnBxQGHXRYq3HT1WyXAvT7LLY3tl70hw6tQIbjI=
|
||||
github.com/Azure/go-autorest/autorest/validation v0.3.1 h1:AgyqjAd94fwNAoTjl/WQXg4VvFeRFpO+UhNyRXqF1ac=
|
||||
github.com/Azure/go-autorest/autorest/validation v0.3.1/go.mod h1:yhLgjC0Wda5DYXl6JAsWyUe4KVNffhoDhG0zVzUMo3E=
|
||||
github.com/Azure/go-autorest/logger v0.1.0 h1:ruG4BSDXONFRrZZJ2GUXDiUyVpayPmb1GnWeHDdaNKY=
|
||||
github.com/Azure/go-autorest/logger v0.1.0/go.mod h1:oExouG+K6PryycPJfVSxi/koC6LSNgds39diKLz7Vrc=
|
||||
github.com/Azure/go-autorest/tracing v0.5.0 h1:TRn4WjSnkcSy5AEG3pnbtFSwNtwzjr4VYyQflFE619k=
|
||||
github.com/Azure/go-autorest/tracing v0.5.0/go.mod h1:r/s2XiOKccPW3HrqB+W0TQzfbtp2fGCgRFtBroKn4Dk=
|
||||
github.com/Azure/go-ntlmssp v0.0.0-20180810175552-4a21cbd618b4 h1:pSm8mp0T2OH2CPmPDPtwHPr3VAQaOwVF/JbllOPP4xA=
|
||||
github.com/Azure/go-ntlmssp v0.0.0-20180810175552-4a21cbd618b4/go.mod h1:chxPXzSsl7ZWRAuOIE23GDNzjWuZquvFlgA8xmpunjU=
|
||||
github.com/Azure/go-ntlmssp v0.0.0-20191115201650-bad6df29494a h1:3FwiePtHk5YJrooV799oo5jIfsgRdES25VdngJM03dU=
|
||||
github.com/Azure/go-ntlmssp v0.0.0-20191115201650-bad6df29494a/go.mod h1:chxPXzSsl7ZWRAuOIE23GDNzjWuZquvFlgA8xmpunjU=
|
||||
@@ -90,8 +95,12 @@ github.com/aliyun/aliyun-oss-go-sdk v0.0.0-20170113022742-e6dbea820a9f h1:jI4DIE
|
||||
github.com/aliyun/aliyun-oss-go-sdk v0.0.0-20170113022742-e6dbea820a9f/go.mod h1:T/Aws4fEfogEE9v+HPhhw+CntffsBHJ8nXQCwKr0/g8=
|
||||
github.com/antchfx/xpath v0.0.0-20170728053731-b5c552e1acbd h1:S3Fr6QnkpW9VRjiEY4psQHhhbbahASuNVj52YIce7lI=
|
||||
github.com/antchfx/xpath v0.0.0-20170728053731-b5c552e1acbd/go.mod h1:Yee4kTMuNiPYJ7nSNorELQMr1J33uOpXDMByNYhvtNk=
|
||||
github.com/antchfx/xpath v1.1.11 h1:WOFtK8TVAjLm3lbgqeP0arlHpvCEeTANeWZ/csPpJkQ=
|
||||
github.com/antchfx/xpath v1.1.11/go.mod h1:i54GszH55fYfBmoZXapTHN8T8tkcHfRgLyVwwqzXNcs=
|
||||
github.com/antchfx/xquery v0.0.0-20170730121040-eb8c3c172607 h1:BFFG6KP8ASFBg2ptWsJn8p8RDufBjBDKIxLU7BTYGOM=
|
||||
github.com/antchfx/xquery v0.0.0-20170730121040-eb8c3c172607/go.mod h1:LzD22aAzDP8/dyiCKFp31He4m2GPjl0AFyzDtZzUu9M=
|
||||
github.com/antchfx/xquery v0.0.0-20180515051857-ad5b8c7a47b0 h1:JaCC8jz0zdMLk2m+qCCVLLLM/PL93p84w4pK3aJWj60=
|
||||
github.com/antchfx/xquery v0.0.0-20180515051857-ad5b8c7a47b0/go.mod h1:LzD22aAzDP8/dyiCKFp31He4m2GPjl0AFyzDtZzUu9M=
|
||||
github.com/antihax/optional v1.0.0 h1:xK2lYat7ZLaVVcIuj82J8kIro4V6kDe0AUDFboUCwcg=
|
||||
github.com/antihax/optional v1.0.0/go.mod h1:uupD/76wgC+ih3iEmQUL+0Ugr19nfwCT1kdvxnR2qWY=
|
||||
github.com/apparentlymart/go-cidr v1.0.1 h1:NmIwLZ/KdsjIUlhf+/Np40atNXm/+lZ5txfTJ/SpF+U=
|
||||
@@ -105,9 +114,11 @@ github.com/apparentlymart/go-textseg/v12 v12.0.0/go.mod h1:S/4uRK2UtaQttw1GenVJE
|
||||
github.com/approvals/go-approval-tests v0.0.0-20160714161514-ad96e53bea43 h1:ePCAQPf5tUc5IMcUvu6euhSGna7jzs7eiXtJXHig6Zc=
|
||||
github.com/approvals/go-approval-tests v0.0.0-20160714161514-ad96e53bea43/go.mod h1:S6puKjZ9ZeqUPBv2hEBnMZGcM2J6mOsDRQcmxkMAND0=
|
||||
github.com/armon/circbuf v0.0.0-20150827004946-bbbad097214e/go.mod h1:3U/XgcO3hCbHZ8TKRvWD2dDTCfh9M9ya+I9JpbB7O8o=
|
||||
github.com/armon/go-metrics v0.0.0-20180917152333-f0300d1749da h1:8GUt8eRujhVEGZFFEjBj46YV4rDjvGrNxb0KMWYkL2I=
|
||||
github.com/armon/go-metrics v0.0.0-20180917152333-f0300d1749da/go.mod h1:Q73ZrmVTwzkszR9V5SSuryQ31EELlFMUz1kKyl939pY=
|
||||
github.com/armon/go-metrics v0.0.0-20190430140413-ec5e00d3c878 h1:EFSB7Zo9Eg91v7MJPVsifUysc/wPdN+NOnVe6bWbdBM=
|
||||
github.com/armon/go-metrics v0.0.0-20190430140413-ec5e00d3c878/go.mod h1:3AMJUQhVx52RsWOnlkpikZr01T/yAVN2gn0861vByNg=
|
||||
github.com/armon/go-radix v0.0.0-20180808171621-7fddfc383310 h1:BUAU3CGlLvorLI26FmByPp2eC2qla6E1Tw+scpcg/to=
|
||||
github.com/armon/go-radix v0.0.0-20180808171621-7fddfc383310/go.mod h1:ufUuZ+zHj4x4TnLV4JWEpy2hxWSpsRywHrMgIH9cCH8=
|
||||
github.com/armon/go-radix v1.0.0 h1:F4z6KzEeeQIMeLFa97iZU6vupzoecKdU5TX24SNppXI=
|
||||
github.com/armon/go-radix v1.0.0/go.mod h1:ufUuZ+zHj4x4TnLV4JWEpy2hxWSpsRywHrMgIH9cCH8=
|
||||
@@ -150,8 +161,16 @@ github.com/dgrijalva/jwt-go v3.2.0+incompatible h1:7qlOGliEKZXTDg6OTjfoBKDXWrumC
|
||||
github.com/dgrijalva/jwt-go v3.2.0+incompatible/go.mod h1:E3ru+11k8xSBh+hMPgOLZmtrrCbhqsmaPHjLKYnJCaQ=
|
||||
github.com/digitalocean/go-libvirt v0.0.0-20190626172931-4d226dd6c437 h1:phR13shVFOIpa1pnLBmewI9p16NEladLPvVylLPeexo=
|
||||
github.com/digitalocean/go-libvirt v0.0.0-20190626172931-4d226dd6c437/go.mod h1:PRcPVAAma6zcLpFd4GZrjR/MRpood3TamjKI2m/z/Uw=
|
||||
github.com/digitalocean/go-libvirt v0.0.0-20201209184759-e2a69bcd5bd1 h1:j6vGflaQ2T7yOWqVgPdiRF73j/U2Zmpbbzab8nyDCRQ=
|
||||
github.com/digitalocean/go-libvirt v0.0.0-20201209184759-e2a69bcd5bd1/go.mod h1:QS1XzqZLcDniNYrN7EZefq3wIyb/M2WmJbql4ZKoc1Q=
|
||||
github.com/digitalocean/go-libvirt v0.0.0-20210108193637-3a8ae49ba8cd h1:+96Lbk3f8glkOcsRdy3Nubga8pE40kor2OgxDzVGNZM=
|
||||
github.com/digitalocean/go-libvirt v0.0.0-20210108193637-3a8ae49ba8cd/go.mod h1:gtar3MgGsIO64GgphCHw1cbyxSI6qEuTIm9+izMmlfk=
|
||||
github.com/digitalocean/go-libvirt v0.0.0-20210112203132-25518eb2c840 h1:F3RVNV8SLLNhkNFcbDTgD3wAPMcrMJW6xjjI0JXy9z8=
|
||||
github.com/digitalocean/go-libvirt v0.0.0-20210112203132-25518eb2c840/go.mod h1:gtar3MgGsIO64GgphCHw1cbyxSI6qEuTIm9+izMmlfk=
|
||||
github.com/digitalocean/go-qemu v0.0.0-20181112162955-dd7bb9c771b8 h1:N7nH2py78LcMqYY3rZjjrsX6N7uCN7sjvaosgpXN9Ow=
|
||||
github.com/digitalocean/go-qemu v0.0.0-20181112162955-dd7bb9c771b8/go.mod h1:/YnlngP1PARC0SKAZx6kaAEMOp8bNTQGqS+Ka3MctNI=
|
||||
github.com/digitalocean/go-qemu v0.0.0-20201211181942-d361e7b4965f h1:BYkBJhHxUJJn27mhqfqWycWaEOWv9JQqLgQ2pOFJMqE=
|
||||
github.com/digitalocean/go-qemu v0.0.0-20201211181942-d361e7b4965f/go.mod h1:y4Eq3ZfZQFWQwVyW0qvgo5seXUIq2C7BlHsdE+xtXL4=
|
||||
github.com/digitalocean/godo v1.11.1 h1:OsTh37YFKk+g6DnAOrkXJ9oDArTkRx5UTkBJ2EWAO38=
|
||||
github.com/digitalocean/godo v1.11.1/go.mod h1:h6faOIcZ8lWIwNQ+DN7b3CgX4Kwby5T+nbpNqkUIozU=
|
||||
github.com/dimchansky/utfbom v1.1.0 h1:FcM3g+nofKgUteL8dm/UpdRXNC9KmADgTpLKsu0TRo4=
|
||||
@@ -170,6 +189,7 @@ github.com/exoscale/egoscale v0.18.1 h1:1FNZVk8jHUx0AvWhOZxLEDNlacTU0chMXUUNkm9E
|
||||
github.com/exoscale/egoscale v0.18.1/go.mod h1:Z7OOdzzTOz1Q1PjQXumlz9Wn/CddH0zSYdCF3rnBKXE=
|
||||
github.com/fatih/camelcase v1.0.0 h1:hxNvNX/xYBp0ovncs8WyWZrOrpBNub/JfaMvbURyft8=
|
||||
github.com/fatih/camelcase v1.0.0/go.mod h1:yN2Sb0lFhZJUdVvtELVWefmrXpuZESvPmqwoZc+/fpc=
|
||||
github.com/fatih/color v1.7.0 h1:DkWD4oS2D8LGGgTQ6IvwJJXSL5Vp2ffcQg58nFV38Ys=
|
||||
github.com/fatih/color v1.7.0/go.mod h1:Zm6kSWBoL9eyXnKyktHP6abPY2pDugNf5KwzbycvMj4=
|
||||
github.com/fatih/color v1.9.0 h1:8xPHl4/q1VyqGIPif1F+1V3Y3lSmrq01EabUW3CoW5s=
|
||||
github.com/fatih/color v1.9.0/go.mod h1:eQcE1qtQxscV5RaZvpXrrb8Drkc3/DdQ+uUYCNjL+zU=
|
||||
@@ -186,6 +206,8 @@ github.com/go-ini/ini v1.25.4/go.mod h1:ByCAeIL28uOIIG0E3PJtZPDL8WnHpFKFOtgjp+3I
|
||||
github.com/go-ldap/ldap v3.0.2+incompatible/go.mod h1:qfd9rJvER9Q0/D/Sqn1DfHRoBp40uXYvFoEVrNEPqRc=
|
||||
github.com/go-ole/go-ole v1.2.4 h1:nNBDSCOigTSiarFpYE9J/KtEA1IOW4CNeqT9TQDqCxI=
|
||||
github.com/go-ole/go-ole v1.2.4/go.mod h1:XCwSNxSkXRo4vlyPy93sltvi/qJq0jqQhjqQNIwKuxM=
|
||||
github.com/go-ole/go-ole v1.2.5 h1:t4MGB5xEDZvXI+0rMjjsfBsD7yAgp/s9ZDkL1JndXwY=
|
||||
github.com/go-ole/go-ole v1.2.5/go.mod h1:pprOEPIfldk/42T2oK7lQ4v4JSDwmV0As9GaiUsvbm0=
|
||||
github.com/go-resty/resty/v2 v2.1.1-0.20191201195748-d7b97669fe48/go.mod h1:dZGr0i9PLlaaTD4H/hoZIDjQ+r6xq8mgbRzHZf7f2J8=
|
||||
github.com/go-resty/resty/v2 v2.3.0 h1:JOOeAvjSlapTT92p8xiS19Zxev1neGikoHsXJeOq8So=
|
||||
github.com/go-resty/resty/v2 v2.3.0/go.mod h1:UpN9CgLZNsv4e9XG50UU8xdI0F43UQ4HmxLBDwaroHU=
|
||||
@@ -243,8 +265,12 @@ github.com/google/go-cmp v0.5.0/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/
|
||||
github.com/google/go-cmp v0.5.1/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
|
||||
github.com/google/go-cmp v0.5.2 h1:X2ev0eStA3AbceY54o37/0PQ/UWqKEiiO2dKL5OPaFM=
|
||||
github.com/google/go-cmp v0.5.2/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
|
||||
github.com/google/go-github/v33 v33.0.0/go.mod h1:GMdDnVZY/2TsWgp/lkYnpSAh6TrzhANBBwm6k6TTEXg=
|
||||
github.com/google/go-github/v33 v33.0.1-0.20210113204525-9318e629ec69 h1:zL0/Ug5CMhV0XRb3A6vnK1SQ9kJM3VIyRxPQ5t9w8Bg=
|
||||
github.com/google/go-github/v33 v33.0.1-0.20210113204525-9318e629ec69/go.mod h1:GMdDnVZY/2TsWgp/lkYnpSAh6TrzhANBBwm6k6TTEXg=
|
||||
github.com/google/go-querystring v1.0.0 h1:Xkwi/a1rcvNg1PPYe5vI8GbeBY/jrVuDX5ASuANWTrk=
|
||||
github.com/google/go-querystring v1.0.0/go.mod h1:odCYkC5MyYFN7vkCjXpyrEuKhc/BUO6wN/zVPAxq5ck=
|
||||
github.com/google/gofuzz v1.0.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg=
|
||||
github.com/google/martian v2.1.0+incompatible h1:/CP5g8u/VJHijgedC/Legn3BAbAaWPgecwXBIDzw5no=
|
||||
github.com/google/martian v2.1.0+incompatible/go.mod h1:9I4somxYTbIHy5NJKHRl3wXiIaQGbYVAs8BPL6v8lEs=
|
||||
github.com/google/martian/v3 v3.0.0 h1:pMen7vLs8nvgEYhywH3KDWJIJTeEr2ULsVWHWYHQyBs=
|
||||
@@ -272,6 +298,8 @@ github.com/gophercloud/gophercloud v0.12.0 h1:mZrie07npp6ODiwHZolTicr5jV8Ogn43Av
|
||||
github.com/gophercloud/gophercloud v0.12.0/go.mod h1:gmC5oQqMDOMO1t1gq5DquX/yAU808e/4mzjjDA76+Ss=
|
||||
github.com/gophercloud/utils v0.0.0-20200508015959-b0167b94122c h1:iawx2ojEQA7c+GmkaVO5sN+k8YONibXyDO8RlsC+1bs=
|
||||
github.com/gophercloud/utils v0.0.0-20200508015959-b0167b94122c/go.mod h1:ehWUbLQJPqS0Ep+CxeD559hsm9pthPXadJNKwZkp43w=
|
||||
github.com/gopherjs/gopherjs v0.0.0-20181017120253-0766667cb4d1 h1:EGx4pi6eqNxGaHF6qqu48+N2wcFQ5qg5FXgOdqsJ5d8=
|
||||
github.com/gopherjs/gopherjs v0.0.0-20181017120253-0766667cb4d1/go.mod h1:wJfORRmW1u3UXTncJ5qlYoELFm8eSnnEO6hX4iZ3EWY=
|
||||
github.com/gopherjs/gopherjs v0.0.0-20181103185306-d547d1d9531e h1:JKmoR8x90Iww1ks85zJ1lfDGgIiMDuIptTOhJq+zKyg=
|
||||
github.com/gopherjs/gopherjs v0.0.0-20181103185306-d547d1d9531e/go.mod h1:wJfORRmW1u3UXTncJ5qlYoELFm8eSnnEO6hX4iZ3EWY=
|
||||
github.com/grpc-ecosystem/go-grpc-middleware v1.1.0 h1:THDBEeQ9xZ8JEaCLyLQqXMMdRqNr0QAUJTIkQAUtFjg=
|
||||
@@ -306,9 +334,11 @@ github.com/hashicorp/go-hclog v0.0.0-20180709165350-ff2cf002a8dd/go.mod h1:9bjs9
|
||||
github.com/hashicorp/go-hclog v0.8.0/go.mod h1:5CU+agLiy3J7N7QjHK5d05KxGsuXiQLrjA0H7acj2lQ=
|
||||
github.com/hashicorp/go-hclog v0.12.0 h1:d4QkX8FRTYaKaCZBoXYY8zJX2BXjWxurN/GA2tkrmZM=
|
||||
github.com/hashicorp/go-hclog v0.12.0/go.mod h1:whpDNt7SSdeAju8AWKIWsul05p54N/39EeqMAyrmvFQ=
|
||||
github.com/hashicorp/go-immutable-radix v1.0.0 h1:AKDB1HM5PWEA7i4nhcpwOrO2byshxBjXVn/J/3+z5/0=
|
||||
github.com/hashicorp/go-immutable-radix v1.0.0/go.mod h1:0y9vanUI8NX6FsYoO3zeMjhV/C5i9g4Q3DwcSNZ4P60=
|
||||
github.com/hashicorp/go-immutable-radix v1.1.0 h1:vN9wG1D6KG6YHRTWr8512cxGOVgTMEfgEdSj/hr8MPc=
|
||||
github.com/hashicorp/go-immutable-radix v1.1.0/go.mod h1:0y9vanUI8NX6FsYoO3zeMjhV/C5i9g4Q3DwcSNZ4P60=
|
||||
github.com/hashicorp/go-msgpack v0.5.3 h1:zKjpN5BK/P5lMYrLmBHdBULWbJ0XpYR+7NGzqkZzoD4=
|
||||
github.com/hashicorp/go-msgpack v0.5.3/go.mod h1:ahLV/dePpqEmjfWmKiqvPkv/twdG7iPBM1vqhUKIvfM=
|
||||
github.com/hashicorp/go-msgpack v0.5.5 h1:i9R9JSrqIz0QVLz3sz+i3YJdT7TTSLcfLLzJi9aZTuI=
|
||||
github.com/hashicorp/go-msgpack v0.5.5/go.mod h1:ahLV/dePpqEmjfWmKiqvPkv/twdG7iPBM1vqhUKIvfM=
|
||||
@@ -321,6 +351,7 @@ github.com/hashicorp/go-plugin v1.0.1/go.mod h1:++UyYGoz3o5w9ZzAdZxtQKrWWP+iqPBn
|
||||
github.com/hashicorp/go-retryablehttp v0.5.3/go.mod h1:9B5zBasrRhHXnJnui7y6sL7es7NDiJgTc6Er0maI1Xs=
|
||||
github.com/hashicorp/go-retryablehttp v0.5.4 h1:1BZvpawXoJCWX6pNtow9+rpEj+3itIlutiqnntI6jOE=
|
||||
github.com/hashicorp/go-retryablehttp v0.5.4/go.mod h1:9B5zBasrRhHXnJnui7y6sL7es7NDiJgTc6Er0maI1Xs=
|
||||
github.com/hashicorp/go-rootcerts v1.0.1 h1:DMo4fmknnz0E0evoNYnV48RjWndOsmd6OW+09R3cEP8=
|
||||
github.com/hashicorp/go-rootcerts v1.0.1/go.mod h1:pqUvnprVnM5bf7AOirdbb01K4ccR319Vf4pU3K5EGc8=
|
||||
github.com/hashicorp/go-rootcerts v1.0.2 h1:jzhAVGtqPKbwpyCPELlgNWhE1znq+qwJtW5Oi2viEzc=
|
||||
github.com/hashicorp/go-rootcerts v1.0.2/go.mod h1:pqUvnprVnM5bf7AOirdbb01K4ccR319Vf4pU3K5EGc8=
|
||||
@@ -340,6 +371,7 @@ github.com/hashicorp/go-version v1.2.0 h1:3vNe/fWF5CBgRIguda1meWhsZHy3m8gCJ5wx+d
|
||||
github.com/hashicorp/go-version v1.2.0/go.mod h1:fltr4n8CU8Ke44wwGCBoEymUuxUHl09ZGVZPK5anwXA=
|
||||
github.com/hashicorp/go.net v0.0.1/go.mod h1:hjKkEWcCURg++eb33jQU7oqQcI9XDCnUzHA0oac0k90=
|
||||
github.com/hashicorp/golang-lru v0.5.0/go.mod h1:/m3WP610KZHVQ1SGc6re/UDhFvYD7pJ4Ao+sR/qLZy8=
|
||||
github.com/hashicorp/golang-lru v0.5.1 h1:0hERBMJE1eitiLkihrMvRVBYAkpHzc/J3QdDN+dAcgU=
|
||||
github.com/hashicorp/golang-lru v0.5.1/go.mod h1:/m3WP610KZHVQ1SGc6re/UDhFvYD7pJ4Ao+sR/qLZy8=
|
||||
github.com/hashicorp/golang-lru v0.5.3 h1:YPkqC67at8FYaadspW/6uE0COsBxS2656RLEr8Bppgk=
|
||||
github.com/hashicorp/golang-lru v0.5.3/go.mod h1:iADmTwqILo4mZ8BN3D2Q6+9jd8WM5uGBxy+E8yxSoD4=
|
||||
@@ -351,24 +383,29 @@ github.com/hashicorp/hcl/v2 v2.8.0/go.mod h1:bQTN5mpo+jewjJgh8jr0JUguIi7qPHUF6yI
|
||||
github.com/hashicorp/logutils v1.0.0/go.mod h1:QIAnNjmIWmVIIkWDTG1z5v++HQmx9WQRO+LraFDTW64=
|
||||
github.com/hashicorp/mdns v1.0.0/go.mod h1:tL+uN++7HEJ6SQLQ2/p+z2pH24WQKWjBPkE0mNTz8vQ=
|
||||
github.com/hashicorp/mdns v1.0.1/go.mod h1:4gW7WsVCke5TE7EPeYliwHlRUyBtfCwuFwuMg2DmyNY=
|
||||
github.com/hashicorp/memberlist v0.1.3 h1:EmmoJme1matNzb+hMpDuR/0sbJSUisxyqBGG676r31M=
|
||||
github.com/hashicorp/memberlist v0.1.3/go.mod h1:ajVTdAv/9Im8oMAAj5G31PhhMCZJV2pPBoIllUwCN7I=
|
||||
github.com/hashicorp/memberlist v0.2.2 h1:5+RffWKwqJ71YPu9mWsF7ZOscZmwfasdA8kbdC7AO2g=
|
||||
github.com/hashicorp/memberlist v0.2.2/go.mod h1:MS2lj3INKhZjWNqd3N0m3J+Jxf3DAOnAH9VT3Sh9MUE=
|
||||
github.com/hashicorp/packer v1.6.6/go.mod h1:meJucaEeJro8UP1jw+KaOCpaiC4VE+itPLXY8lbIU2A=
|
||||
github.com/hashicorp/packer v1.6.7-0.20210107234516-6564ee76e807/go.mod h1:fBz288Z4of8zkpDWwL/ngG1txC36jGSXS7dnUmUaLUs=
|
||||
github.com/hashicorp/packer v1.6.7-0.20210112155033-1bc89e34714f/go.mod h1:sKsuxExP+0/WCnIwwCRFFEHKy9TuQrZcwqbidE2oSq8=
|
||||
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-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.7-0.20210113192617-8a28198491f7 h1:2N1NAfBCmG1vIkbdlIOb/YbaYXCW40YOllWqMZDjnHM=
|
||||
github.com/hashicorp/packer-plugin-sdk v0.0.7-0.20210113192617-8a28198491f7/go.mod h1:YdWTt5w6cYfaQG7IOi5iorL+3SXnz8hI0gJCi8Db/LI=
|
||||
github.com/hashicorp/packer-plugin-sdk v0.0.7-0.20210120130732-6167b5e5b2e8 h1:50/m5nP40RaXnXyd0GHHUd+CfkmcYeTNGAY5eXQlBeY=
|
||||
github.com/hashicorp/packer-plugin-sdk v0.0.7-0.20210120130732-6167b5e5b2e8/go.mod h1:YdWTt5w6cYfaQG7IOi5iorL+3SXnz8hI0gJCi8Db/LI=
|
||||
github.com/hashicorp/packer-plugin-sdk v0.0.7-0.20210121103409-4b079ce99178 h1:AVT2ugu3+UzTDEViAxMFbUzzxgUpSVMMpbuaOEd97HY=
|
||||
github.com/hashicorp/packer-plugin-sdk v0.0.7-0.20210121103409-4b079ce99178/go.mod h1:YdWTt5w6cYfaQG7IOi5iorL+3SXnz8hI0gJCi8Db/LI=
|
||||
github.com/hashicorp/packer-plugin-sdk v0.0.7 h1:adELlId/KOGWXmQ79L+NwYSgKES6811RVXiRCj4FE0s=
|
||||
github.com/hashicorp/packer-plugin-sdk v0.0.7/go.mod h1:YdWTt5w6cYfaQG7IOi5iorL+3SXnz8hI0gJCi8Db/LI=
|
||||
github.com/hashicorp/packer-plugin-sdk v0.0.8 h1:/qyCO9YqALnaHSE++y+//tNy68++4SThZctqTwqikrU=
|
||||
github.com/hashicorp/packer-plugin-sdk v0.0.8/go.mod h1:YdWTt5w6cYfaQG7IOi5iorL+3SXnz8hI0gJCi8Db/LI=
|
||||
github.com/hashicorp/packer-plugin-sdk v0.0.9 h1:PWX6g0TeAbev5zhiRR91k3Z0wVCqsivs6xyBTRmPMkQ=
|
||||
github.com/hashicorp/packer-plugin-sdk v0.0.9/go.mod h1:YdWTt5w6cYfaQG7IOi5iorL+3SXnz8hI0gJCi8Db/LI=
|
||||
github.com/hashicorp/packer-plugin-sdk v0.0.6/go.mod h1:Nvh28f+Jmpp2rcaN79bULTouNkGNDRfHckhHKTAXtyU=
|
||||
github.com/hashicorp/packer-plugin-sdk v0.0.7-0.20210111224258-fd30ebb797f0/go.mod h1:YdWTt5w6cYfaQG7IOi5iorL+3SXnz8hI0gJCi8Db/LI=
|
||||
github.com/hashicorp/packer-plugin-sdk v0.0.7-0.20210120105339-f6fd68d2570a h1:QbS+UBmK9DZuEDPodi1pCiS66dLYI3rmUX/cowNopsk=
|
||||
github.com/hashicorp/packer-plugin-sdk v0.0.7-0.20210120105339-f6fd68d2570a/go.mod h1:exN0C+Pe+3zu18l4nxueNjX5cfmslxUX/m/xk4IVmZQ=
|
||||
github.com/hashicorp/packer-plugin-sdk v0.0.7-0.20210122130548-45a6ca0a9365 h1:u7DeYY9ukhSZpLE11qCFU8pxnO+YM2/85wwVXHJZdRE=
|
||||
github.com/hashicorp/packer-plugin-sdk v0.0.7-0.20210122130548-45a6ca0a9365/go.mod h1:K7VsU0lfJBDyiUrSNnS/j+zMxSRwwH9WC9QvHv32KsU=
|
||||
github.com/hashicorp/packer-plugin-sdk v0.0.10-0.20210126105622-8e1648006d93 h1:3wFACjFiBkF5sZrai0zvcWv2fHIgLa4g6ZXxbqngBhs=
|
||||
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/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=
|
||||
github.com/hashicorp/serf v0.9.2/go.mod h1:UWDWwZeL5cuWDJdl0C6wrvrUwEqtQ4ZKBKKENpqIUyk=
|
||||
@@ -399,25 +436,38 @@ github.com/joyent/triton-go v0.0.0-20180628001255-830d2b111e62 h1:JHCT6xuyPUrbbg
|
||||
github.com/joyent/triton-go v0.0.0-20180628001255-830d2b111e62/go.mod h1:U+RSyWxWd04xTqnuOQxnai7XGS2PrPY2cfGoDKtMHjA=
|
||||
github.com/json-iterator/go v1.1.6 h1:MrUvLMLTMxbqFJ9kzlvat/rYZqZnW3u4wkLzWTaFwKs=
|
||||
github.com/json-iterator/go v1.1.6/go.mod h1:+SdeFBvtyEkXs7REEP0seUULqWtbJapLOCVDaaPEHmU=
|
||||
github.com/json-iterator/go v1.1.10 h1:Kz6Cvnvv2wGdaG/V8yMvfkmNiXq9Ya2KUv4rouJJr68=
|
||||
github.com/json-iterator/go v1.1.10/go.mod h1:KdQUCv79m/52Kvf8AW2vK1V8akMuk1QjK/uOdHXbAo4=
|
||||
github.com/jstemmer/go-junit-report v0.0.0-20190106144839-af01ea7f8024/go.mod h1:6v2b51hI/fHJwM22ozAgKL4VKDeJcHhJFhtBdhmNjmU=
|
||||
github.com/jstemmer/go-junit-report v0.9.1 h1:6QPYqodiu3GuPL+7mfx+NwDdp2eTkp9IfEUpgAwUN0o=
|
||||
github.com/jstemmer/go-junit-report v0.9.1/go.mod h1:Brl9GWCQeLvo8nXZwPNNblvFj/XSXhF0NWZEnDohbsk=
|
||||
github.com/jtolds/gls v4.2.1+incompatible h1:fSuqC+Gmlu6l/ZYAoZzx2pyucC8Xza35fpRVWLVmUEE=
|
||||
github.com/jtolds/gls v4.2.1+incompatible/go.mod h1:QJZ7F/aHp+rZTRtaJ1ow/lLfFfVYBRgL+9YlvaHOwJU=
|
||||
github.com/jtolds/gls v4.20.0+incompatible h1:xdiiI2gbIgH/gLH7ADydsJ1uDOEzR8yvV7C0MuV77Wo=
|
||||
github.com/jtolds/gls v4.20.0+incompatible/go.mod h1:QJZ7F/aHp+rZTRtaJ1ow/lLfFfVYBRgL+9YlvaHOwJU=
|
||||
github.com/kisielk/errcheck v1.1.0/go.mod h1:EZBBE59ingxPouuu3KfxchcWSUPOHkagtvWXihfKN4Q=
|
||||
github.com/kisielk/gotool v1.0.0/go.mod h1:XhKaO+MFFWcvkIS/tQcRk01m1F5IRFswLeQ+oQHNcck=
|
||||
github.com/klauspost/compress v0.0.0-20160131094358-f86d2e6d8a77 h1:rJnR80lkojFgjdg/oQPhbZoY8t8uM51XMz8DrJrjabk=
|
||||
github.com/klauspost/compress v0.0.0-20160131094358-f86d2e6d8a77/go.mod h1:RyIbtBH6LamlWaDj8nUwkbUhJ87Yi3uG0guNDohfE1A=
|
||||
github.com/klauspost/compress v1.11.6 h1:EgWPCW6O3n1D5n99Zq3xXBt9uCwRGvpwGOusOLNBRSQ=
|
||||
github.com/klauspost/compress v1.11.6/go.mod h1:aoV0uJVorq1K+umq18yTdKaF57EivdYsUV+/s2qKfXs=
|
||||
github.com/klauspost/compress v1.11.7 h1:0hzRabrMN4tSTvMfnL3SCv1ZGeAP23ynzodBgaHeMeg=
|
||||
github.com/klauspost/compress v1.11.7/go.mod h1:aoV0uJVorq1K+umq18yTdKaF57EivdYsUV+/s2qKfXs=
|
||||
github.com/klauspost/cpuid v0.0.0-20160106104451-349c67577817 h1:/7pPahIC+GoCm/euDCi2Pm29bAj9tc6TcK4Zcc8D3WI=
|
||||
github.com/klauspost/cpuid v0.0.0-20160106104451-349c67577817/go.mod h1:Pj4uuM528wm8OyEC2QMXAi2YiTZ96dNQPGgoMS4s3ek=
|
||||
github.com/klauspost/crc32 v0.0.0-20160114101742-999f3125931f h1:UD9YLTi2aBhdOOThzatodQ/pGd9nd5255swS+UzHZj4=
|
||||
github.com/klauspost/crc32 v0.0.0-20160114101742-999f3125931f/go.mod h1:+ZoRqAPRLkC4NPOvfYeR5KNOrY6TD+/sAC3HXPZgDYg=
|
||||
github.com/klauspost/crc32 v1.2.0 h1:0VuyqOCruD33/lJ/ojXNvzVyl8Zr5zdTmj9l9qLZ86I=
|
||||
github.com/klauspost/crc32 v1.2.0/go.mod h1:+ZoRqAPRLkC4NPOvfYeR5KNOrY6TD+/sAC3HXPZgDYg=
|
||||
github.com/klauspost/pgzip v0.0.0-20151221113845-47f36e165cec h1:PYqF3Tiz2W2Ag0ezyDhAobWDWlrFv7U+qct4spLeDBM=
|
||||
github.com/klauspost/pgzip v0.0.0-20151221113845-47f36e165cec/go.mod h1:Ch1tH69qFZu15pkjo5kYi6mth2Zzwzt50oCQKQE9RUs=
|
||||
github.com/konsorten/go-windows-terminal-sequences v1.0.1 h1:mweAR1A6xJ3oS2pRaGiHgQ4OO8tzTaLawm8vnODuwDk=
|
||||
github.com/konsorten/go-windows-terminal-sequences v1.0.1/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ=
|
||||
github.com/kr/fs v0.0.0-20131111012553-2788f0dbd169 h1:YUrU1/jxRqnt0PSrKj1Uj/wEjk/fjnE80QFfi2Zlj7Q=
|
||||
github.com/kr/fs v0.0.0-20131111012553-2788f0dbd169/go.mod h1:glhvuHOU9Hy7/8PwwdtnarXqLagOX0b/TbZx2zLMqEg=
|
||||
github.com/kr/fs v0.1.0 h1:Jskdu9ieNAYnjxsi0LbQp1ulIKZV1LAFgK1tWhpZgl8=
|
||||
github.com/kr/fs v0.1.0/go.mod h1:FFnZGqtBN9Gxj7eW1uZ42v5BccTP0vu6NEaFoC2HwRg=
|
||||
github.com/kr/pretty v0.1.0 h1:L/CwN0zerZDmRFUapSPitk6f+Q3+0za1rQkzVuMiMFI=
|
||||
github.com/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORNo=
|
||||
github.com/kr/pretty v0.2.0 h1:s5hAObm+yFO5uHYt5dYjxi2rXrsnmRpJx4OYvIWUaQs=
|
||||
github.com/kr/pretty v0.2.0/go.mod h1:ipq/a2n7PKx3OHsz4KJII5eveXtPO4qwEXGdVfWzfnI=
|
||||
@@ -428,16 +478,20 @@ github.com/kylelemons/godebug v0.0.0-20170820004349-d65d576e9348 h1:MtvEpTB6LX3v
|
||||
github.com/kylelemons/godebug v0.0.0-20170820004349-d65d576e9348/go.mod h1:B69LEHPfb2qLo0BaaOLcbitczOKLWTsrBG9LczfCD4k=
|
||||
github.com/linode/linodego v0.14.0 h1:0APKMjiVGyry2TTUVDiok72H6cWpFNMMrFWBFn14aFU=
|
||||
github.com/linode/linodego v0.14.0/go.mod h1:2ce3S00NrDqJfp4i55ZuSlT0U3cKNELNYACWBPI8Tnw=
|
||||
github.com/masterzen/simplexml v0.0.0-20160608183007-4572e39b1ab9 h1:SmVbOZFWAlyQshuMfOkiAx1f5oUTsOGG5IXplAEYeeM=
|
||||
github.com/masterzen/simplexml v0.0.0-20160608183007-4572e39b1ab9/go.mod h1:kCEbxUJlNDEBNbdQMkPSp6yaKcRXVI6f4ddk8Riv4bc=
|
||||
github.com/masterzen/simplexml v0.0.0-20190410153822-31eea3082786 h1:2ZKn+w/BJeL43sCxI2jhPLRv73oVVOjEKZjKkflyqxg=
|
||||
github.com/masterzen/simplexml v0.0.0-20190410153822-31eea3082786/go.mod h1:kCEbxUJlNDEBNbdQMkPSp6yaKcRXVI6f4ddk8Riv4bc=
|
||||
github.com/masterzen/winrm v0.0.0-20200615185753-c42b5136ff88/go.mod h1:a2HXwefeat3evJHxFXSayvRHpYEPJYtErl4uIzfaUqY=
|
||||
github.com/masterzen/winrm v0.0.0-20201030141608-56ca5c5f2380 h1:uKhPH5dYpx3Z8ZAnaTGfGZUiHOWa5p5mdG8wZlh+tLo=
|
||||
github.com/masterzen/winrm v0.0.0-20201030141608-56ca5c5f2380/go.mod h1:a2HXwefeat3evJHxFXSayvRHpYEPJYtErl4uIzfaUqY=
|
||||
github.com/mattn/go-colorable v0.0.9 h1:UVL0vNpWh04HeJXV0KLcaT7r06gOH2l4OW6ddYRUIY4=
|
||||
github.com/mattn/go-colorable v0.0.9/go.mod h1:9vuHe8Xs5qXnSaW/c/ABM9alt+Vo+STaOChaDxuIBZU=
|
||||
github.com/mattn/go-colorable v0.1.4 h1:snbPLB8fVfU9iwbbo30TPtbLRzwWu6aJS6Xh4eaaviA=
|
||||
github.com/mattn/go-colorable v0.1.4/go.mod h1:U0ppj6V5qS13XJ6of8GYAs25YV2eR4EVcfRqFIhoBtE=
|
||||
github.com/mattn/go-colorable v0.1.6 h1:6Su7aK7lXmJ/U79bYtBjLNaha4Fs1Rg9plHpcH+vvnE=
|
||||
github.com/mattn/go-colorable v0.1.6/go.mod h1:u6P/XSegPjTcexA+o6vUJrdnUu04hMope9wVRipJSqc=
|
||||
github.com/mattn/go-isatty v0.0.3 h1:ns/ykhmWi7G9O+8a448SecJU3nSMBXJfqQkl0upE1jI=
|
||||
github.com/mattn/go-isatty v0.0.3/go.mod h1:M+lRXTBqGeGNdLjl/ufCoiOlB5xdOkqRJdNxMWT7Zi4=
|
||||
github.com/mattn/go-isatty v0.0.4/go.mod h1:M+lRXTBqGeGNdLjl/ufCoiOlB5xdOkqRJdNxMWT7Zi4=
|
||||
github.com/mattn/go-isatty v0.0.8/go.mod h1:Iq45c/XA43vh69/j3iqttzPXn0bhXyGjM0Hdxcsrc5s=
|
||||
@@ -447,9 +501,12 @@ github.com/mattn/go-isatty v0.0.12 h1:wuysRhFDzyxgEmMf5xjvJ2M9dZoWAXNNr5LSBS7uHX
|
||||
github.com/mattn/go-isatty v0.0.12/go.mod h1:cbi8OIDigv2wuxKPP5vlRcQ1OAZbq2CE4Kysco4FUpU=
|
||||
github.com/mattn/go-runewidth v0.0.4 h1:2BvfKmzob6Bmd4YsL0zygOqfdFnK7GR4QL06Do4/p7Y=
|
||||
github.com/mattn/go-runewidth v0.0.4/go.mod h1:LwmH8dsx7+W8Uxz3IHJYH5QSwggIsqBzpuz5H//U1FU=
|
||||
github.com/mattn/go-runewidth v0.0.10 h1:CoZ3S2P7pvtP45xOtBw+/mDL2z0RKI576gSkzRRpdGg=
|
||||
github.com/mattn/go-runewidth v0.0.10/go.mod h1:RAqKPSqVFrSLVXbA8x7dzmKdmGzieGRCM46jaSJTDAk=
|
||||
github.com/mattn/go-tty v0.0.0-20191112051231-74040eebce08 h1:8YAWbq7rJqfbc6IaAvA2eCQuOQvf6Bs4vHKcOyWw//E=
|
||||
github.com/mattn/go-tty v0.0.0-20191112051231-74040eebce08/go.mod h1:XPvLUNfbS4fJH25nqRHfWLMa1ONC8Amw+mIA639KxkE=
|
||||
github.com/matttproud/golang_protobuf_extensions v1.0.1/go.mod h1:D8He9yQNgCq6Z5Ld7szi9bcBfOoFv/3dc6xSMkL2PC0=
|
||||
github.com/miekg/dns v1.0.14 h1:9jZdLNd/P4+SfEJ0TNyxYpsK8N4GtfylBLqtbYN1sbA=
|
||||
github.com/miekg/dns v1.0.14/go.mod h1:W1PPwlIAgtquWBMBEV9nkV9Cazfe8ScdGz/Lj7v3Nrg=
|
||||
github.com/miekg/dns v1.1.26 h1:gPxPSwALAeHJSjarOs00QjVdV9QoBvc1D2ujQUr5BzU=
|
||||
github.com/miekg/dns v1.1.26/go.mod h1:bPDLeHnStXmXAq1m/Ch/hvfNHr14JKNPMBo3VZKjuso=
|
||||
@@ -463,6 +520,7 @@ github.com/mitchellh/go-homedir v1.0.0/go.mod h1:SfyaCUpYCn1Vlf4IUYiD9fPX4A5wJrk
|
||||
github.com/mitchellh/go-homedir v1.1.0 h1:lukF9ziXFxDFPkA1vsr5zpc1XuPDn/wFntq5mG+4E0Y=
|
||||
github.com/mitchellh/go-homedir v1.1.0/go.mod h1:SfyaCUpYCn1Vlf4IUYiD9fPX4A5wJrkLzIz1N1q0pr0=
|
||||
github.com/mitchellh/go-testing-interface v0.0.0-20171004221916-a61a99592b77/go.mod h1:kRemZodwjscx+RGhAo8eIhFbs2+BFgRtFPeD/KE+zxI=
|
||||
github.com/mitchellh/go-testing-interface v1.0.0 h1:fzU/JVNcaqHQEcVFAKeR41fkiLdIPrefOvVG1VZ96U0=
|
||||
github.com/mitchellh/go-testing-interface v1.0.0/go.mod h1:kRemZodwjscx+RGhAo8eIhFbs2+BFgRtFPeD/KE+zxI=
|
||||
github.com/mitchellh/go-testing-interface v1.0.3 h1:gqwbsGvc0jbhAPW/26WfEoSiPANAVlR49AAVdvaTjI4=
|
||||
github.com/mitchellh/go-testing-interface v1.0.3/go.mod h1:kRemZodwjscx+RGhAo8eIhFbs2+BFgRtFPeD/KE+zxI=
|
||||
@@ -472,6 +530,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=
|
||||
@@ -486,8 +545,12 @@ github.com/mitchellh/prefixedio v0.0.0-20151214002211-6e6954073784 h1:+DAetXqxv/
|
||||
github.com/mitchellh/prefixedio v0.0.0-20151214002211-6e6954073784/go.mod h1:kB1naBgV9ORnkiTVeyJOI1DavaJkG4oNIq0Af6ZVKUo=
|
||||
github.com/mitchellh/reflectwalk v1.0.0 h1:9D+8oIskB4VJBN5SFlmc27fSlIBZaov1Wpk/IfikLNY=
|
||||
github.com/mitchellh/reflectwalk v1.0.0/go.mod h1:mSTlrgnPZtwu0c4WaC2kGObEpuNDbx0jmZXqmk4esnw=
|
||||
github.com/modern-go/concurrent v0.0.0-20180228061459-e0a39a4cb421 h1:ZqeYNhU3OHLH3mGKHDcjJRFFRrJa6eAM5H+CtDdOsPc=
|
||||
github.com/modern-go/concurrent v0.0.0-20180228061459-e0a39a4cb421/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q=
|
||||
github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd h1:TRLaZ9cD/w8PVh93nsPXa1VrQ6jlwL5oN8l14QlcNfg=
|
||||
github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q=
|
||||
github.com/modern-go/reflect2 v0.0.0-20180701023420-4b7aa43c6742 h1:Esafd1046DLDQ0W1YjYsBW+p8U2u7vzgW2SQVmlNazg=
|
||||
github.com/modern-go/reflect2 v0.0.0-20180701023420-4b7aa43c6742/go.mod h1:bx2lNnkwVCuqBIxFjflWJWanXIb3RllmbCylyMrvgv0=
|
||||
github.com/modern-go/reflect2 v1.0.1 h1:9f412s+6RmYXLWZSEzVVgPGK7C2PphHj5RJrvfx9AWI=
|
||||
github.com/modern-go/reflect2 v1.0.1/go.mod h1:bx2lNnkwVCuqBIxFjflWJWanXIb3RllmbCylyMrvgv0=
|
||||
github.com/nu7hatch/gouuid v0.0.0-20131221200532-179d4d0c4d8d h1:VhgPp6v9qf9Agr/56bj7Y/xa04UccTW04VP0Qed4vnQ=
|
||||
@@ -525,6 +588,8 @@ github.com/prometheus/client_model v0.0.0-20180712105110-5c3871d89910/go.mod h1:
|
||||
github.com/prometheus/client_model v0.0.0-20190812154241-14fe0d1b01d4/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA=
|
||||
github.com/prometheus/common v0.0.0-20181126121408-4724e9255275/go.mod h1:daVV7qP5qjZbuso7PdcryaAu0sAZbrN9i7WWcTMWvro=
|
||||
github.com/prometheus/procfs v0.0.0-20181204211112-1dc9a6cbc91a/go.mod h1:c3At6R/oaqEKCNdg8wHV1ftS6bRYblBhIjjI8uT2IGk=
|
||||
github.com/rivo/uniseg v0.1.0 h1:+2KBaVoUmb9XzDsrx/Ct0W/EYOSFf/nWTauy++DprtY=
|
||||
github.com/rivo/uniseg v0.1.0/go.mod h1:J6wj4VEh+S6ZtnVlnTBMWIodfgj8LQOQFoIToxlJtxc=
|
||||
github.com/rogpeppe/go-internal v1.3.0/go.mod h1:M8bDsm7K2OlrFYOpmOWEs/qY81heoFRclV5y23lUDJ4=
|
||||
github.com/ryanuber/columnize v0.0.0-20160712163229-9b3edd62028f/go.mod h1:sm1tb6uqfes/u+d4ooFouqFdy9/2g9QGwK3SQygK0Ts=
|
||||
github.com/ryanuber/columnize v2.1.0+incompatible/go.mod h1:sm1tb6uqfes/u+d4ooFouqFdy9/2g9QGwK3SQygK0Ts=
|
||||
@@ -549,9 +614,12 @@ github.com/smartystreets/assertions v0.0.0-20180927180507-b2de0cb4f26d h1:zE9ykE
|
||||
github.com/smartystreets/assertions v0.0.0-20180927180507-b2de0cb4f26d/go.mod h1:OnSkiWE9lh6wB0YB77sQom3nweQdgAjqCqsofrRNTgc=
|
||||
github.com/smartystreets/goconvey v0.0.0-20181108003508-044398e4856c h1:Ho+uVpkel/udgjbwB5Lktg9BtvJSh2DT0Hi6LPSyI2w=
|
||||
github.com/smartystreets/goconvey v0.0.0-20181108003508-044398e4856c/go.mod h1:XDJAKZRPZ1CvBcN2aX5YOUTYGHki24fSF0Iv48Ibg0s=
|
||||
github.com/smartystreets/goconvey v1.6.4 h1:fv0U8FUIMPNf1L9lnHLvLhgicrIVChEkdzIKYqbNC9s=
|
||||
github.com/smartystreets/goconvey v1.6.4/go.mod h1:syvi0/a8iFYH4r/RixwvyeAJjdLS9QV7WQ/tjFTllLA=
|
||||
github.com/spf13/pflag v1.0.2/go.mod h1:DYY7MBk1bdzusC3SYhjObp+wFpr4gzcvqqNjLnInEg4=
|
||||
github.com/spf13/pflag v1.0.5/go.mod h1:McXfInJRrz4CZXVZOBLb0bTZqETkiAhM9Iw0y3An2Bg=
|
||||
github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
|
||||
github.com/stretchr/objx v0.1.1 h1:2vfRuCMp5sSVIDSqO8oNnWJq7mPa6KVP3iPIwFBuy8A=
|
||||
github.com/stretchr/objx v0.1.1/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
|
||||
github.com/stretchr/objx v0.2.0 h1:Hbg2NidpLE8veEBkEZTL3CvlkUIVzuU9jDplZO54c48=
|
||||
github.com/stretchr/objx v0.2.0/go.mod h1:qt09Ya8vawLte6SNmTgCsAVtYtaKzEcn8ATUoHMkEqE=
|
||||
@@ -728,6 +796,7 @@ golang.org/x/sys v0.0.0-20190507160741-ecd444e8653b/go.mod h1:h1NjWce9XRLGQEsW7w
|
||||
golang.org/x/sys v0.0.0-20190606165138-5da285871e9c/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20190624142023-c5567b49c5d0/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20190726091711-fc99dfbffb4e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20190916202348-b4ddaad3f8a3/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20190922100055-0a153f010e69/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20190924154521-2837fb4f24fe/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20191001151750-bb3f8db39f24/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
@@ -777,6 +846,7 @@ golang.org/x/tools v0.0.0-20190226205152-f727befe758c/go.mod h1:9Yl7xja0Znq3iFh3
|
||||
golang.org/x/tools v0.0.0-20190311212946-11955173bddd/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs=
|
||||
golang.org/x/tools v0.0.0-20190312151545-0bb0c0a6e846/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs=
|
||||
golang.org/x/tools v0.0.0-20190312170243-e65039ee4138/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs=
|
||||
golang.org/x/tools v0.0.0-20190328211700-ab21143f2384/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs=
|
||||
golang.org/x/tools v0.0.0-20190425150028-36563e24a262/go.mod h1:RgjU9mgBXZiqYHBnxXauZ1Gv1EHHAz9KjViQ78xBX0Q=
|
||||
golang.org/x/tools v0.0.0-20190506145303-2d16b83fe98c/go.mod h1:RgjU9mgBXZiqYHBnxXauZ1Gv1EHHAz9KjViQ78xBX0Q=
|
||||
golang.org/x/tools v0.0.0-20190524140312-2c0ae7006135/go.mod h1:RgjU9mgBXZiqYHBnxXauZ1Gv1EHHAz9KjViQ78xBX0Q=
|
||||
@@ -812,6 +882,7 @@ golang.org/x/tools v0.0.0-20200501065659-ab2804fb9c9d/go.mod h1:EkVYQZoAsY45+roY
|
||||
golang.org/x/tools v0.0.0-20200512131952-2bc93b1c0c88/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE=
|
||||
golang.org/x/tools v0.0.0-20200515010526-7d3b6ebf133d/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE=
|
||||
golang.org/x/tools v0.0.0-20200618134242-20370b0cb4b2/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE=
|
||||
golang.org/x/tools v0.0.0-20200711155855-7342f9734a7d/go.mod h1:njjCfa9FT2d7l9Bc6FUM5FLjQPp3cFF28FI3qnDFljA=
|
||||
golang.org/x/tools v0.0.0-20200729194436-6467de6f59a7/go.mod h1:njjCfa9FT2d7l9Bc6FUM5FLjQPp3cFF28FI3qnDFljA=
|
||||
golang.org/x/tools v0.0.0-20200804011535-6c149bb5ef0d/go.mod h1:njjCfa9FT2d7l9Bc6FUM5FLjQPp3cFF28FI3qnDFljA=
|
||||
golang.org/x/tools v0.0.0-20200825202427-b303f430e36d/go.mod h1:njjCfa9FT2d7l9Bc6FUM5FLjQPp3cFF28FI3qnDFljA=
|
||||
@@ -885,6 +956,7 @@ google.golang.org/genproto v0.0.0-20200804131852-c06518451d9c/go.mod h1:FWY/as6D
|
||||
google.golang.org/genproto v0.0.0-20200825200019-8632dd797987/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no=
|
||||
google.golang.org/genproto v0.0.0-20200831141814-d751682dd103/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no=
|
||||
google.golang.org/genproto v0.0.0-20200904004341-0bd0a958aa1d/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no=
|
||||
google.golang.org/genproto v0.0.0-20200914193844-75d14daec038 h1:SnvTpXhVDJGFxzZiHbMUZTh3VjU2Vx2feJ7Zfl5+OIY=
|
||||
google.golang.org/genproto v0.0.0-20200914193844-75d14daec038/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no=
|
||||
google.golang.org/genproto v0.0.0-20200918140846-d0d605568037 h1:ujwz1DPMeHwCvo36rK5shXhAzc4GMRecrqQFaMZJBKQ=
|
||||
google.golang.org/genproto v0.0.0-20200918140846-d0d605568037/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no=
|
||||
@@ -926,6 +998,8 @@ gopkg.in/cheggaaa/pb.v1 v1.0.27/go.mod h1:V/YB90LKu/1FcN3WVnfiiE5oMCibMjukxqG/qS
|
||||
gopkg.in/errgo.v2 v2.1.0/go.mod h1:hNsd1EY+bozCKY1Ytp96fpM3vjJbqLJn88ws8XvfDNI=
|
||||
gopkg.in/ini.v1 v1.42.0 h1:7N3gPTt50s8GuLortA00n8AqRTk75qOP98+mTPpgzRk=
|
||||
gopkg.in/ini.v1 v1.42.0/go.mod h1:pNLf8WUiyNEtQjuu5G5vTm06TEv9tsIgeAvK8hOrP4k=
|
||||
gopkg.in/ini.v1 v1.62.0 h1:duBzk771uxoUuOlyRLkHsygud9+5lrlGjdFBb4mSKDU=
|
||||
gopkg.in/ini.v1 v1.62.0/go.mod h1:pNLf8WUiyNEtQjuu5G5vTm06TEv9tsIgeAvK8hOrP4k=
|
||||
gopkg.in/jarcoal/httpmock.v1 v1.0.0-20181117152235-275e9df93516 h1:H6trpavCIuipdInWrab8l34Mf+GGVfphniHostMdMaQ=
|
||||
gopkg.in/jarcoal/httpmock.v1 v1.0.0-20181117152235-275e9df93516/go.mod h1:d3R+NllX3X5e0zlG1Rful3uLvsGC/Q3OHut5464DEQw=
|
||||
gopkg.in/square/go-jose.v2 v2.3.1 h1:SK5KegNXmKmqE342YYN2qPHEnUYeoMiXXl1poUlI+o4=
|
||||
@@ -934,6 +1008,7 @@ gopkg.in/yaml.v2 v2.2.1/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
|
||||
gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
|
||||
gopkg.in/yaml.v2 v2.2.4/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
|
||||
gopkg.in/yaml.v2 v2.2.7/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
|
||||
gopkg.in/yaml.v2 v2.2.8 h1:obN1ZagJSUGI0Ek/LBmuj4SNLPfIny3KsKFopxRdj10=
|
||||
gopkg.in/yaml.v2 v2.2.8/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
|
||||
gopkg.in/yaml.v2 v2.3.0 h1:clyUAQHOM3G0M3f5vQj7LuJrETvjVot3Z5el9nffUtU=
|
||||
gopkg.in/yaml.v2 v2.3.0/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
|
||||
|
||||
@@ -0,0 +1,238 @@
|
||||
package addrs
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"strings"
|
||||
|
||||
"github.com/hashicorp/hcl/v2"
|
||||
"golang.org/x/net/idna"
|
||||
)
|
||||
|
||||
// Plugin encapsulates a single plugin type.
|
||||
type Plugin struct {
|
||||
Type string
|
||||
Namespace string
|
||||
Hostname string
|
||||
}
|
||||
|
||||
func (p Plugin) RealRelativePath() string {
|
||||
ns := DefaultPluginNamespace
|
||||
if p.Namespace != "" {
|
||||
ns = p.Namespace
|
||||
}
|
||||
return ns + "/packer-plugin-" + p.Type
|
||||
}
|
||||
|
||||
func (p Plugin) Parts() []string {
|
||||
return []string{p.Hostname, p.Namespace, p.Type}
|
||||
}
|
||||
|
||||
func (p Plugin) String() string {
|
||||
return strings.Join(p.Parts(), "/")
|
||||
}
|
||||
|
||||
// ForDisplay returns a user-friendly FQN string, simplified for readability. If
|
||||
// the plugin is using the default hostname, the hostname is omitted.
|
||||
func (p *Plugin) ForDisplay() string {
|
||||
parts := []string{}
|
||||
if p.Hostname != DefaultPluginHost {
|
||||
parts = append(parts, p.Hostname)
|
||||
}
|
||||
if p.Namespace != DefaultPluginNamespace {
|
||||
parts = append(parts, p.Namespace)
|
||||
}
|
||||
parts = append(parts, p.Type)
|
||||
return strings.Join(parts, "/")
|
||||
}
|
||||
|
||||
const DefaultPluginHost = "github.com"
|
||||
const DefaultPluginNamespace = "hashicorp"
|
||||
|
||||
// ParsePluginPart processes an addrs.Plugin namespace or type string
|
||||
// provided by an end-user, producing a normalized version if possible or
|
||||
// an error if the string contains invalid characters.
|
||||
//
|
||||
// A plugin part is processed in the same way as an individual label in a DNS
|
||||
// domain name: it is transformed to lowercase per the usual DNS case mapping
|
||||
// and normalization rules and may contain only letters, digits, and dashes.
|
||||
// Additionally, dashes may not appear at the start or end of the string.
|
||||
//
|
||||
// These restrictions are intended to allow these names to appear in fussy
|
||||
// contexts such as directory/file names on case-insensitive filesystems,
|
||||
// repository names on GitHub, etc. We're using the DNS rules in particular,
|
||||
// rather than some similar rules defined locally, because the hostname part
|
||||
// of an addrs.Plugin is already a hostname and it's ideal to use exactly
|
||||
// the same case folding and normalization rules for all of the parts.
|
||||
//
|
||||
// It's valid to pass the result of this function as the argument to a
|
||||
// subsequent call, in which case the result will be identical.
|
||||
func ParsePluginPart(given string) (string, error) {
|
||||
if len(given) == 0 {
|
||||
return "", fmt.Errorf("must have at least one character")
|
||||
}
|
||||
|
||||
// We're going to process the given name using the same "IDNA" library we
|
||||
// use for the hostname portion, since it already implements the case
|
||||
// folding rules we want.
|
||||
//
|
||||
// The idna library doesn't expose individual label parsing directly, but
|
||||
// once we've verified it doesn't contain any dots we can just treat it
|
||||
// like a top-level domain for this library's purposes.
|
||||
if strings.ContainsRune(given, '.') {
|
||||
return "", fmt.Errorf("dots are not allowed")
|
||||
}
|
||||
|
||||
// We don't allow names containing multiple consecutive dashes, just as
|
||||
// a matter of preference: they look confusing, or incorrect.
|
||||
// This also, as a side-effect, prevents the use of the "punycode"
|
||||
// indicator prefix "xn--" that would cause the IDNA library to interpret
|
||||
// the given name as punycode, because that would be weird and unexpected.
|
||||
if strings.Contains(given, "--") {
|
||||
return "", fmt.Errorf("cannot use multiple consecutive dashes")
|
||||
}
|
||||
|
||||
result, err := idna.Lookup.ToUnicode(given)
|
||||
if err != nil {
|
||||
return "", fmt.Errorf("must contain only letters, digits, and dashes, and may not use leading or trailing dashes: %w", err)
|
||||
}
|
||||
|
||||
return result, nil
|
||||
}
|
||||
|
||||
// IsPluginPartNormalized compares a given string to the result of ParsePluginPart(string)
|
||||
func IsPluginPartNormalized(str string) (bool, error) {
|
||||
normalized, err := ParsePluginPart(str)
|
||||
if err != nil {
|
||||
return false, err
|
||||
}
|
||||
if str == normalized {
|
||||
return true, nil
|
||||
}
|
||||
return false, nil
|
||||
}
|
||||
|
||||
// ParsePluginSourceString parses the source attribute and returns a plugin.
|
||||
// This is intended primarily to parse the FQN-like strings
|
||||
//
|
||||
// The following are valid source string formats:
|
||||
// name
|
||||
// namespace/name
|
||||
// hostname/namespace/name
|
||||
func ParsePluginSourceString(str string) (*Plugin, hcl.Diagnostics) {
|
||||
ret := &Plugin{
|
||||
Hostname: DefaultPluginHost,
|
||||
Namespace: DefaultPluginNamespace,
|
||||
}
|
||||
var diags hcl.Diagnostics
|
||||
|
||||
// split the source string into individual components
|
||||
parts := strings.Split(str, "/")
|
||||
if len(parts) == 0 || len(parts) > 3 {
|
||||
diags = diags.Append(&hcl.Diagnostic{
|
||||
Severity: hcl.DiagError,
|
||||
Summary: "Invalid plugin source string",
|
||||
Detail: `The "source" attribute must be in the format "[hostname/][namespace/]name"`,
|
||||
})
|
||||
return nil, diags
|
||||
}
|
||||
|
||||
// check for an invalid empty string in any part
|
||||
for i := range parts {
|
||||
if parts[i] == "" {
|
||||
diags = diags.Append(&hcl.Diagnostic{
|
||||
Severity: hcl.DiagError,
|
||||
Summary: "Invalid plugin source string",
|
||||
Detail: `The "source" attribute must be in the format "[hostname/][namespace/]name"`,
|
||||
})
|
||||
return nil, diags
|
||||
}
|
||||
}
|
||||
|
||||
// check the 'name' portion, which is always the last part
|
||||
givenName := parts[len(parts)-1]
|
||||
name, err := ParsePluginPart(givenName)
|
||||
if err != nil {
|
||||
diags = diags.Append(&hcl.Diagnostic{
|
||||
Severity: hcl.DiagError,
|
||||
Summary: "Invalid plugin type",
|
||||
Detail: fmt.Sprintf(`Invalid plugin type %q in source %q: %s"`, givenName, str, err),
|
||||
})
|
||||
return nil, diags
|
||||
}
|
||||
ret.Type = name
|
||||
|
||||
if len(parts) == 1 {
|
||||
return ret, diags
|
||||
}
|
||||
|
||||
if len(parts) >= 2 {
|
||||
// the namespace is always the second-to-last part
|
||||
givenNamespace := parts[len(parts)-2]
|
||||
namespace, err := ParsePluginPart(givenNamespace)
|
||||
if err != nil {
|
||||
diags = diags.Append(&hcl.Diagnostic{
|
||||
Severity: hcl.DiagError,
|
||||
Summary: "Invalid plugin namespace",
|
||||
Detail: fmt.Sprintf(`Invalid plugin namespace %q in source %q: %s"`, namespace, str, err),
|
||||
})
|
||||
return nil, diags
|
||||
}
|
||||
ret.Namespace = namespace
|
||||
}
|
||||
|
||||
// Final Case: 3 parts
|
||||
if len(parts) == 3 {
|
||||
// the hostname is always the first part in a three-part source string
|
||||
hostname := parts[0]
|
||||
// TODO(azr): validate host ? Can this be something else than a
|
||||
// github.com host for now?
|
||||
ret.Hostname = hostname
|
||||
}
|
||||
|
||||
// Due to how plugin executables are named and plugin git repositories
|
||||
// are conventionally named, it's a reasonable and
|
||||
// apparently-somewhat-common user error to incorrectly use the
|
||||
// "packer-plugin-" prefix in a plugin source address. There is
|
||||
// no good reason for a plugin to have the prefix "packer-" anyway,
|
||||
// so we've made that invalid from the start both so we can give feedback
|
||||
// to plugin developers about the packer- prefix being redundant
|
||||
// and give specialized feedback to folks who incorrectly use the full
|
||||
// packer-plugin- prefix to help them self-correct.
|
||||
const redundantPrefix = "packer-"
|
||||
const userErrorPrefix = "packer-plugin-"
|
||||
if strings.HasPrefix(ret.Type, redundantPrefix) {
|
||||
if strings.HasPrefix(ret.Type, userErrorPrefix) {
|
||||
// Likely user error. We only return this specialized error if
|
||||
// whatever is after the prefix would otherwise be a
|
||||
// syntactically-valid plugin type, so we don't end up advising
|
||||
// the user to try something that would be invalid for another
|
||||
// reason anyway.
|
||||
// (This is mainly just for robustness, because the validation
|
||||
// we already did above should've rejected most/all ways for
|
||||
// the suggestedType to end up invalid here.)
|
||||
suggestedType := ret.Type[len(userErrorPrefix):]
|
||||
if _, err := ParsePluginPart(suggestedType); err == nil {
|
||||
suggestedAddr := ret
|
||||
suggestedAddr.Type = suggestedType
|
||||
diags = diags.Append(&hcl.Diagnostic{
|
||||
Severity: hcl.DiagError,
|
||||
Summary: "Invalid plugin type",
|
||||
Detail: fmt.Sprintf("Plugin source %q has a type with the prefix %q, which isn't valid. Although that prefix is often used in the names of version control repositories for Packer plugins, plugin source strings should not include it.\n\nDid you mean %q?", ret.ForDisplay(), userErrorPrefix, suggestedAddr.ForDisplay()),
|
||||
})
|
||||
return nil, diags
|
||||
}
|
||||
}
|
||||
// Otherwise, probably instead an incorrectly-named plugin, perhaps
|
||||
// arising from a similar instinct to what causes there to be
|
||||
// thousands of Python packages on PyPI with "python-"-prefixed
|
||||
// names.
|
||||
diags = diags.Append(&hcl.Diagnostic{
|
||||
Severity: hcl.DiagError,
|
||||
Summary: "Invalid plugin type",
|
||||
Detail: fmt.Sprintf("Plugin source %q has a type with the prefix %q, which isn't allowed because it would be redundant to name a Packer plugin with that prefix. If you are the author of this plugin, rename it to not include the prefix.", ret, redundantPrefix),
|
||||
})
|
||||
return nil, diags
|
||||
}
|
||||
|
||||
return ret, diags
|
||||
}
|
||||
+99
-16
@@ -7,6 +7,7 @@ import (
|
||||
"github.com/google/go-cmp/cmp"
|
||||
"github.com/google/go-cmp/cmp/cmpopts"
|
||||
|
||||
"github.com/hashicorp/go-version"
|
||||
"github.com/hashicorp/hcl/v2"
|
||||
"github.com/hashicorp/hcl/v2/hclparse"
|
||||
packersdk "github.com/hashicorp/packer-plugin-sdk/packer"
|
||||
@@ -17,24 +18,30 @@ import (
|
||||
"github.com/zclconf/go-cty/cty"
|
||||
)
|
||||
|
||||
const lockedVersion = "v1.5.0"
|
||||
|
||||
func getBasicParser() *Parser {
|
||||
return &Parser{
|
||||
Parser: hclparse.NewParser(),
|
||||
BuilderSchemas: packer.MapOfBuilder{
|
||||
"amazon-ebs": func() (packersdk.Builder, error) { return &MockBuilder{}, nil },
|
||||
"virtualbox-iso": func() (packersdk.Builder, error) { return &MockBuilder{}, nil },
|
||||
"null": func() (packersdk.Builder, error) { return &null.Builder{}, nil },
|
||||
},
|
||||
ProvisionersSchemas: packer.MapOfProvisioner{
|
||||
"shell": func() (packersdk.Provisioner, error) { return &MockProvisioner{}, nil },
|
||||
"file": func() (packersdk.Provisioner, error) { return &MockProvisioner{}, nil },
|
||||
},
|
||||
PostProcessorsSchemas: packer.MapOfPostProcessor{
|
||||
"amazon-import": func() (packersdk.PostProcessor, error) { return &MockPostProcessor{}, nil },
|
||||
"manifest": func() (packersdk.PostProcessor, error) { return &MockPostProcessor{}, nil },
|
||||
},
|
||||
DatasourceSchemas: packersdk.MapOfDatasource{
|
||||
"amazon-ami": func() (packersdk.Datasource, error) { return &MockDatasource{}, nil },
|
||||
CorePackerVersion: version.Must(version.NewSemver(lockedVersion)),
|
||||
CorePackerVersionString: lockedVersion,
|
||||
Parser: hclparse.NewParser(),
|
||||
PluginConfig: &packer.PluginConfig{
|
||||
Builders: packer.MapOfBuilder{
|
||||
"amazon-ebs": func() (packersdk.Builder, error) { return &MockBuilder{}, nil },
|
||||
"virtualbox-iso": func() (packersdk.Builder, error) { return &MockBuilder{}, nil },
|
||||
"null": func() (packersdk.Builder, error) { return &null.Builder{}, nil },
|
||||
},
|
||||
Provisioners: packer.MapOfProvisioner{
|
||||
"shell": func() (packersdk.Provisioner, error) { return &MockProvisioner{}, nil },
|
||||
"file": func() (packersdk.Provisioner, error) { return &MockProvisioner{}, nil },
|
||||
},
|
||||
PostProcessors: packer.MapOfPostProcessor{
|
||||
"amazon-import": func() (packersdk.PostProcessor, error) { return &MockPostProcessor{}, nil },
|
||||
"manifest": func() (packersdk.PostProcessor, error) { return &MockPostProcessor{}, nil },
|
||||
},
|
||||
DataSources: packer.MapOfDatasource{
|
||||
"amazon-ami": func() (packersdk.Datasource, error) { return &MockDatasource{}, nil },
|
||||
},
|
||||
},
|
||||
}
|
||||
}
|
||||
@@ -102,6 +109,39 @@ func testParse(t *testing.T, tests []parseTest) {
|
||||
}
|
||||
}
|
||||
|
||||
func testParse_only_Parse(t *testing.T, tests []parseTest) {
|
||||
t.Helper()
|
||||
|
||||
for _, tt := range tests {
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
gotCfg, gotDiags := tt.parser.Parse(tt.args.filename, tt.args.varFiles, tt.args.vars)
|
||||
if tt.parseWantDiags == (gotDiags == nil) {
|
||||
t.Fatalf("Parser.parse() unexpected %q diagnostics.", gotDiags)
|
||||
}
|
||||
if tt.parseWantDiagHasErrors != gotDiags.HasErrors() {
|
||||
t.Fatalf("Parser.parse() unexpected diagnostics HasErrors. %s", gotDiags)
|
||||
}
|
||||
if diff := cmp.Diff(tt.parseWantCfg, gotCfg, cmpOpts...); diff != "" {
|
||||
t.Fatalf("Parser.parse() wrong packer config. %s", diff)
|
||||
}
|
||||
|
||||
if gotCfg != nil && !tt.parseWantDiagHasErrors {
|
||||
if diff := cmp.Diff(tt.parseWantCfg.InputVariables, gotCfg.InputVariables, cmpOpts...); diff != "" {
|
||||
t.Fatalf("Parser.parse() unexpected input vars. %s", diff)
|
||||
}
|
||||
|
||||
if diff := cmp.Diff(tt.parseWantCfg.LocalVariables, gotCfg.LocalVariables, cmpOpts...); diff != "" {
|
||||
t.Fatalf("Parser.parse() unexpected local vars. %s", diff)
|
||||
}
|
||||
}
|
||||
|
||||
if gotDiags.HasErrors() {
|
||||
return
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
var (
|
||||
// everything in the tests is a basicNestedMockConfig this allow to test
|
||||
// each known type to packer ( and embedding ) in one go.
|
||||
@@ -189,6 +229,39 @@ var (
|
||||
},
|
||||
},
|
||||
}
|
||||
basicMockPostProcessorDynamicTags = &MockPostProcessor{
|
||||
Config: MockConfig{
|
||||
NotSquashed: "value <UNKNOWN>",
|
||||
NestedMockConfig: NestedMockConfig{
|
||||
String: "string",
|
||||
Int: 42,
|
||||
Int64: 43,
|
||||
Bool: true,
|
||||
Trilean: config.TriTrue,
|
||||
Duration: 10 * time.Second,
|
||||
MapStringString: map[string]string{
|
||||
"a": "b",
|
||||
"c": "d",
|
||||
},
|
||||
SliceString: []string{
|
||||
"a",
|
||||
"b",
|
||||
"c",
|
||||
},
|
||||
SliceSliceString: [][]string{
|
||||
{"a", "b"},
|
||||
{"c", "d"},
|
||||
},
|
||||
Tags: []MockTag{
|
||||
{Key: "first_tag_key", Value: "first_tag_value"},
|
||||
{Key: "Component", Value: "user-service"},
|
||||
{Key: "Environment", Value: "production"},
|
||||
},
|
||||
},
|
||||
Nested: basicNestedMockConfig,
|
||||
NestedSlice: []NestedMockConfig{},
|
||||
},
|
||||
}
|
||||
basicMockCommunicator = &MockCommunicator{
|
||||
Config: MockConfig{
|
||||
NestedMockConfig: basicNestedMockConfig,
|
||||
@@ -248,9 +321,19 @@ var ctyTypeComparer = cmp.Comparer(func(x, y cty.Type) bool {
|
||||
return x.Equals(y)
|
||||
})
|
||||
|
||||
var versionComparer = cmp.Comparer(func(x, y *version.Version) bool {
|
||||
return x.Equal(y)
|
||||
})
|
||||
|
||||
var versionConstraintComparer = cmp.Comparer(func(x, y *version.Constraint) bool {
|
||||
return x.String() == y.String()
|
||||
})
|
||||
|
||||
var cmpOpts = []cmp.Option{
|
||||
ctyValueComparer,
|
||||
ctyTypeComparer,
|
||||
versionComparer,
|
||||
versionConstraintComparer,
|
||||
cmpopts.IgnoreUnexported(
|
||||
PackerConfig{},
|
||||
Variable{},
|
||||
|
||||
+63
-33
@@ -20,6 +20,7 @@ const (
|
||||
variablesLabel = "variables"
|
||||
variableLabel = "variable"
|
||||
localsLabel = "locals"
|
||||
localLabel = "local"
|
||||
dataSourceLabel = "data"
|
||||
buildLabel = "build"
|
||||
communicatorLabel = "communicator"
|
||||
@@ -32,6 +33,7 @@ var configSchema = &hcl.BodySchema{
|
||||
{Type: variablesLabel},
|
||||
{Type: variableLabel, LabelNames: []string{"name"}},
|
||||
{Type: localsLabel},
|
||||
{Type: localLabel, LabelNames: []string{"name"}},
|
||||
{Type: dataSourceLabel, LabelNames: []string{"type", "name"}},
|
||||
{Type: buildLabel},
|
||||
{Type: communicatorLabel, LabelNames: []string{"type", "name"}},
|
||||
@@ -44,6 +46,9 @@ var packerBlockSchema = &hcl.BodySchema{
|
||||
Attributes: []hcl.AttributeSchema{
|
||||
{Name: "required_version"},
|
||||
},
|
||||
Blocks: []hcl.BlockHeaderSchema{
|
||||
{Type: "required_plugins"},
|
||||
},
|
||||
}
|
||||
|
||||
// Parser helps you parse HCL folders. It will parse an hcl file or directory
|
||||
@@ -57,13 +62,7 @@ type Parser struct {
|
||||
|
||||
*hclparse.Parser
|
||||
|
||||
BuilderSchemas packer.BuilderStore
|
||||
|
||||
ProvisionersSchemas packer.ProvisionerStore
|
||||
|
||||
PostProcessorsSchemas packer.PostProcessorStore
|
||||
|
||||
DatasourceSchemas packer.DatasourceStore
|
||||
PluginConfig *packer.PluginConfig
|
||||
}
|
||||
|
||||
const (
|
||||
@@ -130,10 +129,6 @@ func (p *Parser) Parse(filename string, varFiles []string, argVars map[string]st
|
||||
Basedir: basedir,
|
||||
Cwd: wd,
|
||||
CorePackerVersionString: p.CorePackerVersionString,
|
||||
builderSchemas: p.BuilderSchemas,
|
||||
provisionersSchemas: p.ProvisionersSchemas,
|
||||
postProcessorsSchemas: p.PostProcessorsSchemas,
|
||||
datasourceSchemas: p.DatasourceSchemas,
|
||||
parser: p,
|
||||
files: files,
|
||||
}
|
||||
@@ -145,7 +140,7 @@ func (p *Parser) Parse(filename string, varFiles []string, argVars map[string]st
|
||||
}
|
||||
|
||||
// Before we go further, we'll check to make sure this version can read
|
||||
// that file, so we can produce a version-related error message rather than
|
||||
// all files, so we can produce a version-related error message rather than
|
||||
// potentially-confusing downstream errors.
|
||||
versionDiags := cfg.CheckCoreVersionRequirements(p.CorePackerVersion)
|
||||
diags = append(diags, versionDiags...)
|
||||
@@ -153,8 +148,30 @@ func (p *Parser) Parse(filename string, varFiles []string, argVars map[string]st
|
||||
return cfg, diags
|
||||
}
|
||||
|
||||
// Decode required_plugins blocks and create implicit required_plugins
|
||||
// blocks. Implicit required_plugins blocks happen when a builder or another
|
||||
// plugin cannot be found, for example if one uses :
|
||||
// source "amazon-ebs" "example" { ... }
|
||||
// And no `amazon-ebs` builder can be found. This will then be the
|
||||
// equivalent of having :
|
||||
// packer {
|
||||
// required_plugins {
|
||||
// amazon = "latest"
|
||||
// }
|
||||
// }
|
||||
// Note: using `latest` ( or actually an empty string ) in a config file
|
||||
// does not work and packer will ask you to pick a version
|
||||
{
|
||||
for _, file := range files {
|
||||
diags = append(diags, cfg.decodeRequiredPluginsBlock(file)...)
|
||||
}
|
||||
for _, file := range files {
|
||||
diags = append(diags, cfg.decodeImplicitRequiredPluginsBlocks(file)...)
|
||||
}
|
||||
}
|
||||
|
||||
// Decode variable blocks so that they are available later on. Here locals
|
||||
// can use input variables so we decode them firsthand.
|
||||
// can use input variables so we decode input variables first.
|
||||
{
|
||||
for _, file := range files {
|
||||
diags = append(diags, cfg.decodeInputVariables(file)...)
|
||||
@@ -210,6 +227,11 @@ func (p *Parser) Parse(filename string, varFiles []string, argVars map[string]st
|
||||
|
||||
diags = append(diags, cfg.collectInputVariableValues(os.Environ(), varFiles, argVars)...)
|
||||
}
|
||||
|
||||
// parse the actual content // rest
|
||||
for _, file := range cfg.files {
|
||||
diags = append(diags, cfg.parser.parseConfig(file, cfg)...)
|
||||
}
|
||||
return cfg, diags
|
||||
}
|
||||
|
||||
@@ -257,17 +279,8 @@ func sniffCoreVersionRequirements(body hcl.Body) ([]VersionConstraint, hcl.Diagn
|
||||
return constraints, diags
|
||||
}
|
||||
|
||||
func (cfg *PackerConfig) Initialize(opts packer.InitializeOptions) hcl.Diagnostics {
|
||||
var diags hcl.Diagnostics
|
||||
|
||||
_, moreDiags := cfg.InputVariables.Values()
|
||||
diags = append(diags, moreDiags...)
|
||||
_, moreDiags = cfg.LocalVariables.Values()
|
||||
diags = append(diags, moreDiags...)
|
||||
diags = append(diags, cfg.evaluateDatasources(opts.SkipDatasourcesExecution)...)
|
||||
diags = append(diags, cfg.evaluateLocalVariables(cfg.LocalBlocks)...)
|
||||
|
||||
for _, variable := range cfg.InputVariables {
|
||||
func filterVarsFromLogs(inputOrLocal Variables) {
|
||||
for _, variable := range inputOrLocal {
|
||||
if !variable.Sensitive {
|
||||
continue
|
||||
}
|
||||
@@ -279,22 +292,39 @@ func (cfg *PackerConfig) Initialize(opts packer.InitializeOptions) hcl.Diagnosti
|
||||
return true, nil
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
// decode the actual content
|
||||
for _, file := range cfg.files {
|
||||
diags = append(diags, cfg.parser.decodeConfig(file, cfg)...)
|
||||
func (cfg *PackerConfig) Initialize(opts packer.InitializeOptions) hcl.Diagnostics {
|
||||
var diags hcl.Diagnostics
|
||||
|
||||
// enable packer to start plugins requested in required_plugins.
|
||||
moreDiags := cfg.detectPluginBinaries()
|
||||
diags = append(diags, moreDiags...)
|
||||
if moreDiags.HasErrors() {
|
||||
return diags
|
||||
}
|
||||
|
||||
_, moreDiags = cfg.InputVariables.Values()
|
||||
diags = append(diags, moreDiags...)
|
||||
_, moreDiags = cfg.LocalVariables.Values()
|
||||
diags = append(diags, moreDiags...)
|
||||
diags = append(diags, cfg.evaluateDatasources(opts.SkipDatasourcesExecution)...)
|
||||
diags = append(diags, cfg.evaluateLocalVariables(cfg.LocalBlocks)...)
|
||||
|
||||
filterVarsFromLogs(cfg.InputVariables)
|
||||
filterVarsFromLogs(cfg.LocalVariables)
|
||||
|
||||
diags = append(diags, cfg.initializeBlocks()...)
|
||||
|
||||
return diags
|
||||
}
|
||||
|
||||
// decodeConfig looks in the found blocks for everything that is not a variable
|
||||
// block. It should be called after parsing input variables and locals so that
|
||||
// they can be referenced.
|
||||
func (p *Parser) decodeConfig(f *hcl.File, cfg *PackerConfig) hcl.Diagnostics {
|
||||
// parseConfig looks in the found blocks for everything that is not a variable
|
||||
// block.
|
||||
func (p *Parser) parseConfig(f *hcl.File, cfg *PackerConfig) hcl.Diagnostics {
|
||||
var diags hcl.Diagnostics
|
||||
|
||||
body := dynblock.Expand(f.Body, cfg.EvalContext(nil))
|
||||
body := f.Body
|
||||
content, moreDiags := body.Content(configSchema)
|
||||
diags = append(diags, moreDiags...)
|
||||
|
||||
@@ -343,7 +373,7 @@ func (p *Parser) decodeConfig(f *hcl.File, cfg *PackerConfig) hcl.Diagnostics {
|
||||
func (p *Parser) decodeDatasources(file *hcl.File, cfg *PackerConfig) hcl.Diagnostics {
|
||||
var diags hcl.Diagnostics
|
||||
|
||||
body := dynblock.Expand(file.Body, cfg.EvalContext(nil))
|
||||
body := dynblock.Expand(file.Body, cfg.EvalContext(DatasourceContext, nil))
|
||||
content, moreDiags := body.Content(configSchema)
|
||||
diags = append(diags, moreDiags...)
|
||||
|
||||
|
||||
@@ -0,0 +1,183 @@
|
||||
package hcl2template
|
||||
|
||||
import (
|
||||
"crypto/sha256"
|
||||
"fmt"
|
||||
"log"
|
||||
"runtime"
|
||||
|
||||
"github.com/hashicorp/hcl/v2"
|
||||
"github.com/hashicorp/hcl/v2/ext/dynblock"
|
||||
pluginsdk "github.com/hashicorp/packer-plugin-sdk/plugin"
|
||||
plugingetter "github.com/hashicorp/packer/packer/plugin-getter"
|
||||
)
|
||||
|
||||
// PluginRequirements returns a sorted list of plugin requirements.
|
||||
func (cfg *PackerConfig) PluginRequirements() (plugingetter.Requirements, hcl.Diagnostics) {
|
||||
|
||||
var diags hcl.Diagnostics
|
||||
var reqs plugingetter.Requirements
|
||||
reqPluginsBlocks := cfg.Packer.RequiredPlugins
|
||||
|
||||
// Take all required plugins, make sure there are no conflicting blocks
|
||||
// and append them to the list.
|
||||
uniq := map[string]*RequiredPlugin{}
|
||||
for _, requiredPluginsBlock := range reqPluginsBlocks {
|
||||
for name, block := range requiredPluginsBlock.RequiredPlugins {
|
||||
|
||||
if previouslySeenBlock, found := uniq[name]; found {
|
||||
diags = append(diags, &hcl.Diagnostic{
|
||||
Severity: hcl.DiagError,
|
||||
Summary: fmt.Sprintf("Duplicate required_plugin.%q block", name),
|
||||
Detail: fmt.Sprintf("Block previously seen at %s is already named %q.\n", previouslySeenBlock.DeclRange, name) +
|
||||
"Names at the left hand side of required_plugins are made available to use in your HCL2 configurations.\n" +
|
||||
"To allow to calling to their features correctly two plugins have to have different accessors.",
|
||||
Context: &block.DeclRange,
|
||||
})
|
||||
continue
|
||||
}
|
||||
|
||||
reqs = append(reqs, &plugingetter.Requirement{
|
||||
Accessor: name,
|
||||
Identifier: block.Type,
|
||||
VersionConstraints: block.Requirement.Required,
|
||||
})
|
||||
uniq[name] = block
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
return reqs, diags
|
||||
}
|
||||
|
||||
func (cfg *PackerConfig) detectPluginBinaries() hcl.Diagnostics {
|
||||
opts := plugingetter.ListInstallationsOptions{
|
||||
FromFolders: cfg.parser.PluginConfig.KnownPluginFolders,
|
||||
BinaryInstallationOptions: plugingetter.BinaryInstallationOptions{
|
||||
OS: runtime.GOOS,
|
||||
ARCH: runtime.GOARCH,
|
||||
APIVersionMajor: pluginsdk.APIVersionMajor,
|
||||
APIVersionMinor: pluginsdk.APIVersionMinor,
|
||||
Checksummers: []plugingetter.Checksummer{
|
||||
{Type: "sha256", Hash: sha256.New()},
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
if runtime.GOOS == "windows" && opts.Ext == "" {
|
||||
opts.BinaryInstallationOptions.Ext = ".exe"
|
||||
}
|
||||
|
||||
pluginReqs, diags := cfg.PluginRequirements()
|
||||
if diags.HasErrors() {
|
||||
return diags
|
||||
}
|
||||
|
||||
for _, pluginRequirement := range pluginReqs {
|
||||
sortedInstalls, err := pluginRequirement.ListInstallations(opts)
|
||||
if err != nil {
|
||||
diags = append(diags, &hcl.Diagnostic{
|
||||
Severity: hcl.DiagError,
|
||||
Summary: fmt.Sprintf("Failed to list installation for %s", pluginRequirement.Identifier.ForDisplay()),
|
||||
Detail: err.Error(),
|
||||
})
|
||||
continue
|
||||
}
|
||||
if len(sortedInstalls) == 0 {
|
||||
diags = append(diags, &hcl.Diagnostic{
|
||||
Severity: hcl.DiagError,
|
||||
Summary: fmt.Sprintf("no plugin installed for %s %v", pluginRequirement.Identifier.ForDisplay(), pluginRequirement.VersionConstraints.String()),
|
||||
Detail: "Did you run packer init for this project ?",
|
||||
})
|
||||
continue
|
||||
}
|
||||
log.Printf("[TRACE] Found the following %q installations: %v", pluginRequirement.Identifier.ForDisplay(), sortedInstalls)
|
||||
install := sortedInstalls[len(sortedInstalls)-1]
|
||||
err = cfg.parser.PluginConfig.DiscoverMultiPlugin(pluginRequirement.Accessor, install.BinaryPath)
|
||||
if err != nil {
|
||||
diags = append(diags, &hcl.Diagnostic{
|
||||
Severity: hcl.DiagError,
|
||||
Summary: fmt.Sprintf("Error discovering plugin %s", pluginRequirement.Identifier.ForDisplay()),
|
||||
Detail: err.Error(),
|
||||
})
|
||||
continue
|
||||
}
|
||||
}
|
||||
|
||||
return diags
|
||||
}
|
||||
|
||||
func (cfg *PackerConfig) initializeBlocks() hcl.Diagnostics {
|
||||
// verify that all used plugins do exist and expand dynamic bodies
|
||||
var diags hcl.Diagnostics
|
||||
|
||||
for _, build := range cfg.Builds {
|
||||
for i := range build.Sources {
|
||||
// here we grab a pointer to the source usage because we will set
|
||||
// its body.
|
||||
srcUsage := &(build.Sources[i])
|
||||
if !cfg.parser.PluginConfig.Builders.Has(srcUsage.Type) {
|
||||
diags = append(diags, &hcl.Diagnostic{
|
||||
Summary: "Unknown " + buildSourceLabel + " type " + srcUsage.Type,
|
||||
Subject: &build.HCL2Ref.DefRange,
|
||||
Detail: fmt.Sprintf("known builders: %v", cfg.parser.PluginConfig.Builders.List()),
|
||||
Severity: hcl.DiagError,
|
||||
})
|
||||
continue
|
||||
}
|
||||
|
||||
sourceDefinition, found := cfg.Sources[srcUsage.SourceRef]
|
||||
if !found {
|
||||
diags = append(diags, &hcl.Diagnostic{
|
||||
Summary: "Unknown " + sourceLabel + " " + srcUsage.String(),
|
||||
Subject: build.HCL2Ref.DefRange.Ptr(),
|
||||
Severity: hcl.DiagError,
|
||||
Detail: fmt.Sprintf("Known: %v", cfg.Sources),
|
||||
// TODO: show known sources as a string slice here ^.
|
||||
})
|
||||
continue
|
||||
}
|
||||
|
||||
body := sourceDefinition.block.Body
|
||||
if srcUsage.Body != nil {
|
||||
// merge additions into source definition to get a new body.
|
||||
body = hcl.MergeBodies([]hcl.Body{body, srcUsage.Body})
|
||||
}
|
||||
// expand any dynamic block.
|
||||
body = dynblock.Expand(body, cfg.EvalContext(BuildContext, nil))
|
||||
|
||||
srcUsage.Body = body
|
||||
}
|
||||
|
||||
for _, provBlock := range build.ProvisionerBlocks {
|
||||
if !cfg.parser.PluginConfig.Provisioners.Has(provBlock.PType) {
|
||||
diags = append(diags, &hcl.Diagnostic{
|
||||
Summary: fmt.Sprintf("Unknown "+buildProvisionerLabel+" type %q", provBlock.PType),
|
||||
Subject: provBlock.HCL2Ref.TypeRange.Ptr(),
|
||||
Detail: fmt.Sprintf("known "+buildProvisionerLabel+"s: %v", cfg.parser.PluginConfig.Provisioners.List()),
|
||||
Severity: hcl.DiagError,
|
||||
})
|
||||
}
|
||||
// Allow rest of the body to have dynamic blocks
|
||||
provBlock.HCL2Ref.Rest = dynblock.Expand(provBlock.HCL2Ref.Rest, cfg.EvalContext(BuildContext, nil))
|
||||
}
|
||||
|
||||
for _, ppList := range build.PostProcessorsLists {
|
||||
for _, ppBlock := range ppList {
|
||||
if !cfg.parser.PluginConfig.PostProcessors.Has(ppBlock.PType) {
|
||||
diags = append(diags, &hcl.Diagnostic{
|
||||
Summary: fmt.Sprintf("Unknown "+buildPostProcessorLabel+" type %q", ppBlock.PType),
|
||||
Subject: ppBlock.HCL2Ref.TypeRange.Ptr(),
|
||||
Detail: fmt.Sprintf("known "+buildPostProcessorLabel+"s: %v", cfg.parser.PluginConfig.PostProcessors.List()),
|
||||
Severity: hcl.DiagError,
|
||||
})
|
||||
}
|
||||
// Allow the rest of the body to have dynamic blocks
|
||||
ppBlock.HCL2Ref.Rest = dynblock.Expand(ppBlock.HCL2Ref.Rest, cfg.EvalContext(BuildContext, nil))
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
return diags
|
||||
}
|
||||
+23
-1
@@ -1,4 +1,8 @@
|
||||
|
||||
packer {
|
||||
required_version = ">= v1"
|
||||
}
|
||||
|
||||
// starts resources to provision them.
|
||||
build {
|
||||
sources = [
|
||||
@@ -7,6 +11,15 @@ build {
|
||||
|
||||
source "source.amazon-ebs.ubuntu-1604" {
|
||||
string = "setting from build section"
|
||||
nested {
|
||||
dynamic "tag" {
|
||||
for_each = local.standard_tags
|
||||
content {
|
||||
key = tag.key
|
||||
value = tag.value
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
provisioner "shell" {
|
||||
@@ -162,7 +175,16 @@ build {
|
||||
]
|
||||
}
|
||||
|
||||
nested_slice {
|
||||
tag {
|
||||
key = "first_tag_key"
|
||||
value = "first_tag_value"
|
||||
}
|
||||
dynamic "tag" {
|
||||
for_each = local.standard_tags
|
||||
content {
|
||||
key = tag.key
|
||||
value = tag.value
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
+9
-1
@@ -1,9 +1,17 @@
|
||||
|
||||
source "amazon-ebs" "ubuntu-1604" {
|
||||
int = 42
|
||||
nested_slice {
|
||||
dynamic "tag" {
|
||||
for_each = local.standard_tags
|
||||
content {
|
||||
key = tag.key
|
||||
value = tag.value
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
source "virtualbox-iso" "ubuntu-1204" {
|
||||
string = "string"
|
||||
int = 42
|
||||
|
||||
@@ -38,3 +38,8 @@ locals {
|
||||
{id = "c"},
|
||||
]
|
||||
}
|
||||
|
||||
local "supersecret" {
|
||||
expression = "${var.image_id}-password"
|
||||
sensitive = true
|
||||
}
|
||||
|
||||
@@ -0,0 +1,7 @@
|
||||
data "amazon-ami" "test_0" {
|
||||
string = "string"
|
||||
}
|
||||
|
||||
data "amazon-ami" "test_1" {
|
||||
string = data.amazon-ami.test_0.string
|
||||
}
|
||||
@@ -0,0 +1,7 @@
|
||||
|
||||
packer {
|
||||
required_plugins {
|
||||
amazon = ">= v0"
|
||||
amazon = ">= v4"
|
||||
}
|
||||
}
|
||||
+465
@@ -0,0 +1,465 @@
|
||||
|
||||
packer {
|
||||
required_version = ">= v1"
|
||||
|
||||
required_plugins {
|
||||
amazon = ">= v0"
|
||||
|
||||
amazon-v1 = {
|
||||
source = "amazon"
|
||||
version = ">= v1"
|
||||
}
|
||||
|
||||
amazon-v2 = {
|
||||
source = "amazon"
|
||||
version = ">= v2"
|
||||
}
|
||||
|
||||
|
||||
amazon-v3 = {
|
||||
source = "hashicorp/amazon"
|
||||
version = ">= v3"
|
||||
}
|
||||
|
||||
amazon-v3-azr = {
|
||||
source = "azr/amazon"
|
||||
version = ">= v3"
|
||||
}
|
||||
|
||||
amazon-v4 = {
|
||||
source = "github.com/hashicorp/amazon"
|
||||
version = ">= v4"
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// starts resources to provision them.
|
||||
build {
|
||||
sources = [
|
||||
"source.virtualbox-iso.ubuntu-1204",
|
||||
]
|
||||
|
||||
source "source.amazon-v3-ebs.my-image" {
|
||||
string = "setting from build section"
|
||||
}
|
||||
|
||||
provisioner "shell" {
|
||||
name = "provisioner that does something"
|
||||
not_squashed = "${var.foo} ${upper(build.ID)}"
|
||||
string = "string"
|
||||
int = "${41 + 1}"
|
||||
int64 = "${42 + 1}"
|
||||
bool = "true"
|
||||
trilean = true
|
||||
duration = "${9 + 1}s"
|
||||
map_string_string = {
|
||||
a = "b"
|
||||
c = "d"
|
||||
}
|
||||
slice_string = [for s in var.availability_zone_names : lower(s)]
|
||||
slice_slice_string = [
|
||||
["a","b"],
|
||||
["c","d"]
|
||||
]
|
||||
|
||||
nested {
|
||||
string = "string"
|
||||
int = 42
|
||||
int64 = 43
|
||||
bool = true
|
||||
trilean = true
|
||||
duration = "10s"
|
||||
map_string_string = {
|
||||
a = "b"
|
||||
c = "d"
|
||||
}
|
||||
slice_string = [for s in var.availability_zone_names : lower(s)]
|
||||
slice_slice_string = [
|
||||
["a","b"],
|
||||
["c","d"]
|
||||
]
|
||||
}
|
||||
|
||||
nested_slice {
|
||||
tag {
|
||||
key = "first_tag_key"
|
||||
value = "first_tag_value"
|
||||
}
|
||||
dynamic "tag" {
|
||||
for_each = local.standard_tags
|
||||
content {
|
||||
key = tag.key
|
||||
value = tag.value
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
provisioner "file" {
|
||||
not_squashed = "${var.foo} ${upper(build.ID)}"
|
||||
string = "string"
|
||||
int = 42
|
||||
int64 = 43
|
||||
bool = true
|
||||
trilean = true
|
||||
duration = "10s"
|
||||
map_string_string = {
|
||||
a = "b"
|
||||
c = "d"
|
||||
}
|
||||
slice_string = local.abc_map[*].id
|
||||
slice_slice_string = [
|
||||
["a","b"],
|
||||
["c","d"]
|
||||
]
|
||||
|
||||
nested {
|
||||
string = "string"
|
||||
int = 42
|
||||
int64 = 43
|
||||
bool = true
|
||||
trilean = true
|
||||
duration = "10s"
|
||||
map_string_string = {
|
||||
a = "b"
|
||||
c = "d"
|
||||
}
|
||||
slice_string = [
|
||||
"a",
|
||||
"b",
|
||||
"c",
|
||||
]
|
||||
slice_slice_string = [
|
||||
["a","b"],
|
||||
["c","d"]
|
||||
]
|
||||
}
|
||||
|
||||
nested_slice {
|
||||
tag {
|
||||
key = "first_tag_key"
|
||||
value = "first_tag_value"
|
||||
}
|
||||
dynamic "tag" {
|
||||
for_each = local.standard_tags
|
||||
content {
|
||||
key = tag.key
|
||||
value = tag.value
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
post-processor "amazon-import" {
|
||||
name = "something"
|
||||
string = "string"
|
||||
int = 42
|
||||
int64 = 43
|
||||
bool = true
|
||||
trilean = true
|
||||
duration = "10s"
|
||||
keep_input_artifact = true
|
||||
map_string_string = {
|
||||
a = "b"
|
||||
c = "d"
|
||||
}
|
||||
slice_string = [
|
||||
"a",
|
||||
"b",
|
||||
"c",
|
||||
]
|
||||
slice_slice_string = [
|
||||
["a","b"],
|
||||
["c","d"]
|
||||
]
|
||||
not_squashed = "${var.foo} ${upper(build.ID)}"
|
||||
|
||||
nested {
|
||||
string = "string"
|
||||
int = 42
|
||||
int64 = 43
|
||||
bool = true
|
||||
trilean = true
|
||||
duration = "10s"
|
||||
map_string_string = {
|
||||
a = "b"
|
||||
c = "d"
|
||||
}
|
||||
slice_string = [
|
||||
"a",
|
||||
"b",
|
||||
"c",
|
||||
]
|
||||
slice_slice_string = [
|
||||
["a","b"],
|
||||
["c","d"]
|
||||
]
|
||||
}
|
||||
|
||||
nested_slice {
|
||||
tag {
|
||||
key = "first_tag_key"
|
||||
value = "first_tag_value"
|
||||
}
|
||||
dynamic "tag" {
|
||||
for_each = local.standard_tags
|
||||
content {
|
||||
key = tag.key
|
||||
value = tag.value
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
post-processor "amazon-import" {
|
||||
string = "string"
|
||||
int = 42
|
||||
int64 = 43
|
||||
bool = true
|
||||
trilean = true
|
||||
duration = "10s"
|
||||
map_string_string = {
|
||||
a = "b"
|
||||
c = "d"
|
||||
}
|
||||
slice_string = [
|
||||
"a",
|
||||
"b",
|
||||
"c",
|
||||
]
|
||||
slice_slice_string = [
|
||||
["a","b"],
|
||||
["c","d"]
|
||||
]
|
||||
not_squashed = "${var.foo} ${upper(build.ID)}"
|
||||
|
||||
nested {
|
||||
string = "string"
|
||||
int = 42
|
||||
int64 = 43
|
||||
bool = true
|
||||
trilean = true
|
||||
duration = "10s"
|
||||
map_string_string = {
|
||||
a = "b"
|
||||
c = "d"
|
||||
}
|
||||
slice_string = [
|
||||
"a",
|
||||
"b",
|
||||
"c",
|
||||
]
|
||||
slice_slice_string = [
|
||||
["a","b"],
|
||||
["c","d"]
|
||||
]
|
||||
}
|
||||
|
||||
nested_slice {
|
||||
}
|
||||
}
|
||||
|
||||
post-processors {
|
||||
|
||||
post-processor "amazon-import" {
|
||||
name = "first-nested-post-processor"
|
||||
string = "string"
|
||||
int = 42
|
||||
int64 = 43
|
||||
bool = true
|
||||
trilean = true
|
||||
duration = "10s"
|
||||
map_string_string = {
|
||||
a = "b"
|
||||
c = "d"
|
||||
}
|
||||
slice_string = [
|
||||
"a",
|
||||
"b",
|
||||
"c",
|
||||
]
|
||||
slice_slice_string = [
|
||||
["a","b"],
|
||||
["c","d"]
|
||||
]
|
||||
not_squashed = "${var.foo} ${upper(build.ID)}"
|
||||
|
||||
nested {
|
||||
string = "string"
|
||||
int = 42
|
||||
int64 = 43
|
||||
bool = true
|
||||
trilean = true
|
||||
duration = "10s"
|
||||
map_string_string = {
|
||||
a = "b"
|
||||
c = "d"
|
||||
}
|
||||
slice_string = [
|
||||
"a",
|
||||
"b",
|
||||
"c",
|
||||
]
|
||||
slice_slice_string = [
|
||||
["a","b"],
|
||||
["c","d"]
|
||||
]
|
||||
}
|
||||
|
||||
nested_slice {
|
||||
}
|
||||
}
|
||||
|
||||
post-processor "amazon-import" {
|
||||
name = "second-nested-post-processor"
|
||||
string = "string"
|
||||
int = 42
|
||||
int64 = 43
|
||||
bool = true
|
||||
trilean = true
|
||||
duration = "10s"
|
||||
map_string_string = {
|
||||
a = "b"
|
||||
c = "d"
|
||||
}
|
||||
slice_string = [
|
||||
"a",
|
||||
"b",
|
||||
"c",
|
||||
]
|
||||
slice_slice_string = [
|
||||
["a","b"],
|
||||
["c","d"]
|
||||
]
|
||||
not_squashed = "${var.foo} ${upper(build.ID)}"
|
||||
|
||||
nested {
|
||||
string = "string"
|
||||
int = 42
|
||||
int64 = 43
|
||||
bool = true
|
||||
trilean = true
|
||||
duration = "10s"
|
||||
map_string_string = {
|
||||
a = "b"
|
||||
c = "d"
|
||||
}
|
||||
slice_string = [
|
||||
"a",
|
||||
"b",
|
||||
"c",
|
||||
]
|
||||
slice_slice_string = [
|
||||
["a","b"],
|
||||
["c","d"]
|
||||
]
|
||||
}
|
||||
|
||||
nested_slice {
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
post-processors {
|
||||
|
||||
post-processor "amazon-import" {
|
||||
name = "third-nested-post-processor"
|
||||
string = "string"
|
||||
int = 42
|
||||
int64 = 43
|
||||
bool = true
|
||||
trilean = true
|
||||
duration = "10s"
|
||||
map_string_string = {
|
||||
a = "b"
|
||||
c = "d"
|
||||
}
|
||||
slice_string = [
|
||||
"a",
|
||||
"b",
|
||||
"c",
|
||||
]
|
||||
slice_slice_string = [
|
||||
["a","b"],
|
||||
["c","d"]
|
||||
]
|
||||
not_squashed = "${var.foo} ${upper(build.ID)}"
|
||||
|
||||
nested {
|
||||
string = "string"
|
||||
int = 42
|
||||
int64 = 43
|
||||
bool = true
|
||||
trilean = true
|
||||
duration = "10s"
|
||||
map_string_string = {
|
||||
a = "b"
|
||||
c = "d"
|
||||
}
|
||||
slice_string = [
|
||||
"a",
|
||||
"b",
|
||||
"c",
|
||||
]
|
||||
slice_slice_string = [
|
||||
["a","b"],
|
||||
["c","d"]
|
||||
]
|
||||
}
|
||||
|
||||
nested_slice {
|
||||
}
|
||||
}
|
||||
|
||||
post-processor "amazon-import" {
|
||||
name = "fourth-nested-post-processor"
|
||||
string = "string"
|
||||
int = 42
|
||||
int64 = 43
|
||||
bool = true
|
||||
trilean = true
|
||||
duration = "10s"
|
||||
map_string_string = {
|
||||
a = "b"
|
||||
c = "d"
|
||||
}
|
||||
slice_string = [
|
||||
"a",
|
||||
"b",
|
||||
"c",
|
||||
]
|
||||
slice_slice_string = [
|
||||
["a","b"],
|
||||
["c","d"]
|
||||
]
|
||||
not_squashed = "${var.foo} ${upper(build.ID)}"
|
||||
|
||||
nested {
|
||||
string = "string"
|
||||
int = 42
|
||||
int64 = 43
|
||||
bool = true
|
||||
trilean = true
|
||||
duration = "10s"
|
||||
map_string_string = {
|
||||
a = "b"
|
||||
c = "d"
|
||||
}
|
||||
slice_string = [
|
||||
"a",
|
||||
"b",
|
||||
"c",
|
||||
]
|
||||
slice_slice_string = [
|
||||
["a","b"],
|
||||
["c","d"]
|
||||
]
|
||||
}
|
||||
|
||||
nested_slice {
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,94 @@
|
||||
|
||||
source "amazon-v3-ebs" "my-image" {
|
||||
int = 42
|
||||
}
|
||||
|
||||
|
||||
source "virtualbox-iso" "ubuntu-1204" {
|
||||
string = "string"
|
||||
int = 42
|
||||
int64 = 43
|
||||
bool = true
|
||||
trilean = true
|
||||
duration = "10s"
|
||||
|
||||
map_string_string = {
|
||||
a = "b"
|
||||
c = "d"
|
||||
}
|
||||
slice_string = [
|
||||
"a",
|
||||
"b",
|
||||
"c",
|
||||
]
|
||||
slice_slice_string = [
|
||||
["a","b"],
|
||||
["c","d"]
|
||||
]
|
||||
|
||||
nested {
|
||||
string = "string"
|
||||
int = 42
|
||||
int64 = 43
|
||||
bool = true
|
||||
trilean = true
|
||||
duration = "10s"
|
||||
map_string_string = {
|
||||
a = "b"
|
||||
c = "d"
|
||||
}
|
||||
slice_string = [
|
||||
"a",
|
||||
"b",
|
||||
"c",
|
||||
]
|
||||
slice_slice_string = [
|
||||
["a","b"],
|
||||
["c","d"]
|
||||
]
|
||||
}
|
||||
|
||||
nested_slice {
|
||||
string = "string"
|
||||
int = 42
|
||||
int64 = 43
|
||||
bool = true
|
||||
trilean = true
|
||||
duration = "10s"
|
||||
map_string_string = {
|
||||
a = "b"
|
||||
c = "d"
|
||||
}
|
||||
slice_string = [
|
||||
"a",
|
||||
"b",
|
||||
"c",
|
||||
]
|
||||
slice_slice_string = [
|
||||
["a","b"],
|
||||
["c","d"]
|
||||
]
|
||||
}
|
||||
|
||||
nested_slice {
|
||||
string = "string"
|
||||
int = 42
|
||||
int64 = 43
|
||||
bool = true
|
||||
trilean = true
|
||||
duration = "10s"
|
||||
map_string_string = {
|
||||
a = "b"
|
||||
c = "d"
|
||||
}
|
||||
slice_string = [
|
||||
"a",
|
||||
"b",
|
||||
"c",
|
||||
]
|
||||
slice_slice_string = [
|
||||
["a","b"],
|
||||
["c","d"]
|
||||
]
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,39 @@
|
||||
|
||||
variables {
|
||||
foo = "value"
|
||||
// my_secret = "foo"
|
||||
// image_name = "foo-image-{{user `my_secret`}}"
|
||||
}
|
||||
|
||||
variable "image_id" {
|
||||
type = string
|
||||
default = "image-id-default"
|
||||
}
|
||||
|
||||
variable "port" {
|
||||
type = number
|
||||
default = 42
|
||||
}
|
||||
|
||||
variable "availability_zone_names" {
|
||||
type = list(string)
|
||||
default = ["A", "B", "C"]
|
||||
}
|
||||
|
||||
locals {
|
||||
feefoo = "${var.foo}_${var.image_id}"
|
||||
}
|
||||
|
||||
|
||||
locals {
|
||||
standard_tags = {
|
||||
Component = "user-service"
|
||||
Environment = "production"
|
||||
}
|
||||
|
||||
abc_map = [
|
||||
{id = "a"},
|
||||
{id = "b"},
|
||||
{id = "c"},
|
||||
]
|
||||
}
|
||||
@@ -0,0 +1,8 @@
|
||||
// a source represents a reusable setting for a system boot/start.
|
||||
source "inexistant" "ubuntu-1204" {
|
||||
foo = "bar"
|
||||
}
|
||||
|
||||
build {
|
||||
sources = ["inexistant.ubuntu-1204"]
|
||||
}
|
||||
@@ -36,3 +36,8 @@ locals {
|
||||
service_name = "forum"
|
||||
owner = "Community Team"
|
||||
}
|
||||
|
||||
local "supersecret" {
|
||||
sensitive = true
|
||||
expression = "secretvar"
|
||||
}
|
||||
|
||||
@@ -0,0 +1,4 @@
|
||||
|
||||
local "sensible" {
|
||||
expression = "something"
|
||||
}
|
||||
@@ -0,0 +1,4 @@
|
||||
|
||||
local "sensible" {
|
||||
expression = "something"
|
||||
}
|
||||
@@ -52,7 +52,7 @@ type BuildBlock struct {
|
||||
Description string
|
||||
|
||||
// Sources is the list of sources that we want to start in this build block.
|
||||
Sources []SourceRef
|
||||
Sources []SourceUseBlock
|
||||
|
||||
// ProvisionerBlocks references a list of HCL provisioner block that will
|
||||
// will be ran against the sources.
|
||||
@@ -105,7 +105,8 @@ func (p *Parser) decodeBuildConfig(block *hcl.Block, cfg *PackerConfig) (*BuildB
|
||||
continue
|
||||
}
|
||||
|
||||
build.Sources = append(build.Sources, ref)
|
||||
// source with no body
|
||||
build.Sources = append(build.Sources, SourceUseBlock{SourceRef: ref})
|
||||
}
|
||||
|
||||
content, moreDiags := b.Config.Content(buildSchema)
|
||||
|
||||
@@ -2,6 +2,7 @@ package hcl2template
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"strconv"
|
||||
|
||||
"github.com/hashicorp/hcl/v2"
|
||||
"github.com/hashicorp/hcl/v2/gohcl"
|
||||
@@ -48,37 +49,34 @@ func (p *Parser) decodePostProcessor(block *hcl.Block) (*PostProcessorBlock, hcl
|
||||
return nil, diags
|
||||
}
|
||||
|
||||
if !p.PostProcessorsSchemas.Has(postProcessor.PType) {
|
||||
diags = append(diags, &hcl.Diagnostic{
|
||||
Summary: fmt.Sprintf("Unknown "+buildPostProcessorLabel+" type %q", postProcessor.PType),
|
||||
Subject: block.LabelRanges[0].Ptr(),
|
||||
Detail: fmt.Sprintf("known "+buildPostProcessorLabel+"s: %v", p.PostProcessorsSchemas.List()),
|
||||
Severity: hcl.DiagError,
|
||||
})
|
||||
return nil, diags
|
||||
}
|
||||
|
||||
return postProcessor, diags
|
||||
}
|
||||
|
||||
func (cfg *PackerConfig) startPostProcessor(source SourceBlock, pp *PostProcessorBlock, ectx *hcl.EvalContext) (packersdk.PostProcessor, hcl.Diagnostics) {
|
||||
func (cfg *PackerConfig) startPostProcessor(source SourceUseBlock, pp *PostProcessorBlock, ectx *hcl.EvalContext) (packersdk.PostProcessor, hcl.Diagnostics) {
|
||||
// ProvisionerBlock represents a detected but unparsed provisioner
|
||||
var diags hcl.Diagnostics
|
||||
|
||||
postProcessor, err := cfg.postProcessorsSchemas.Start(pp.PType)
|
||||
postProcessor, err := cfg.parser.PluginConfig.PostProcessors.Start(pp.PType)
|
||||
if err != nil {
|
||||
diags = append(diags, &hcl.Diagnostic{
|
||||
Summary: fmt.Sprintf("Failed loading %s", pp.PType),
|
||||
Subject: pp.DefRange.Ptr(),
|
||||
Detail: err.Error(),
|
||||
Severity: hcl.DiagError,
|
||||
Summary: fmt.Sprintf("Failed loading %s", pp.PType),
|
||||
Subject: pp.DefRange.Ptr(),
|
||||
Detail: err.Error(),
|
||||
})
|
||||
return nil, diags
|
||||
}
|
||||
|
||||
builderVars := source.builderVariables()
|
||||
builderVars["packer_debug"] = strconv.FormatBool(cfg.debug)
|
||||
builderVars["packer_force"] = strconv.FormatBool(cfg.force)
|
||||
builderVars["packer_on_error"] = cfg.onError
|
||||
|
||||
hclPostProcessor := &HCL2PostProcessor{
|
||||
PostProcessor: postProcessor,
|
||||
postProcessorBlock: pp,
|
||||
evalContext: ectx,
|
||||
builderVariables: source.builderVariables(),
|
||||
builderVariables: builderVars,
|
||||
}
|
||||
err = hclPostProcessor.HCL2Prepare(nil)
|
||||
if err != nil {
|
||||
|
||||
@@ -2,6 +2,7 @@ package hcl2template
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"strconv"
|
||||
"time"
|
||||
|
||||
"github.com/hashicorp/hcl/v2"
|
||||
@@ -84,7 +85,7 @@ func (p *Parser) decodeProvisioner(block *hcl.Block, cfg *PackerConfig) (*Provis
|
||||
Override cty.Value `hcl:"override,optional"`
|
||||
Rest hcl.Body `hcl:",remain"`
|
||||
}
|
||||
diags := gohcl.DecodeBody(block.Body, cfg.EvalContext(nil), &b)
|
||||
diags := gohcl.DecodeBody(block.Body, cfg.EvalContext(BuildContext, nil), &b)
|
||||
if diags.HasErrors() {
|
||||
return nil, diags
|
||||
}
|
||||
@@ -136,36 +137,33 @@ func (p *Parser) decodeProvisioner(block *hcl.Block, cfg *PackerConfig) (*Provis
|
||||
provisioner.Timeout = timeout
|
||||
}
|
||||
|
||||
if !p.ProvisionersSchemas.Has(provisioner.PType) {
|
||||
diags = append(diags, &hcl.Diagnostic{
|
||||
Summary: fmt.Sprintf("Unknown "+buildProvisionerLabel+" type %q", provisioner.PType),
|
||||
Subject: block.LabelRanges[0].Ptr(),
|
||||
Detail: fmt.Sprintf("known "+buildProvisionerLabel+"s: %v", p.ProvisionersSchemas.List()),
|
||||
Severity: hcl.DiagError,
|
||||
})
|
||||
return nil, diags
|
||||
}
|
||||
return provisioner, diags
|
||||
}
|
||||
|
||||
func (cfg *PackerConfig) startProvisioner(source SourceBlock, pb *ProvisionerBlock, ectx *hcl.EvalContext) (packersdk.Provisioner, hcl.Diagnostics) {
|
||||
func (cfg *PackerConfig) startProvisioner(source SourceUseBlock, pb *ProvisionerBlock, ectx *hcl.EvalContext) (packersdk.Provisioner, hcl.Diagnostics) {
|
||||
var diags hcl.Diagnostics
|
||||
|
||||
provisioner, err := cfg.provisionersSchemas.Start(pb.PType)
|
||||
provisioner, err := cfg.parser.PluginConfig.Provisioners.Start(pb.PType)
|
||||
if err != nil {
|
||||
diags = append(diags, &hcl.Diagnostic{
|
||||
Summary: fmt.Sprintf("failed loading %s", pb.PType),
|
||||
Subject: pb.HCL2Ref.LabelsRanges[0].Ptr(),
|
||||
Detail: err.Error(),
|
||||
Severity: hcl.DiagError,
|
||||
Summary: fmt.Sprintf("failed loading %s", pb.PType),
|
||||
Subject: pb.HCL2Ref.LabelsRanges[0].Ptr(),
|
||||
Detail: err.Error(),
|
||||
})
|
||||
return nil, diags
|
||||
}
|
||||
|
||||
builderVars := source.builderVariables()
|
||||
builderVars["packer_debug"] = strconv.FormatBool(cfg.debug)
|
||||
builderVars["packer_force"] = strconv.FormatBool(cfg.force)
|
||||
builderVars["packer_on_error"] = cfg.onError
|
||||
|
||||
hclProvisioner := &HCL2Provisioner{
|
||||
Provisioner: provisioner,
|
||||
provisionerBlock: pb,
|
||||
evalContext: ectx,
|
||||
builderVariables: source.builderVariables(),
|
||||
builderVariables: builderVars,
|
||||
}
|
||||
|
||||
if pb.Override != nil {
|
||||
|
||||
@@ -17,15 +17,20 @@ func TestParse_build(t *testing.T) {
|
||||
defaultParser,
|
||||
parseTestArgs{"testdata/build/basic.pkr.hcl", nil, nil},
|
||||
&PackerConfig{
|
||||
Basedir: filepath.Join("testdata", "build"),
|
||||
CorePackerVersionString: lockedVersion,
|
||||
Basedir: filepath.Join("testdata", "build"),
|
||||
Builds: Builds{
|
||||
&BuildBlock{
|
||||
Sources: []SourceRef{
|
||||
Sources: []SourceUseBlock{
|
||||
{
|
||||
Type: "amazon-ebs",
|
||||
Name: "ubuntu-1604",
|
||||
SourceRef: SourceRef{
|
||||
Type: "amazon-ebs",
|
||||
Name: "ubuntu-1604",
|
||||
},
|
||||
},
|
||||
{
|
||||
SourceRef: refVBIsoUbuntu1204,
|
||||
},
|
||||
refVBIsoUbuntu1204,
|
||||
},
|
||||
ProvisionerBlocks: []*ProvisionerBlock{
|
||||
{
|
||||
@@ -45,7 +50,7 @@ func TestParse_build(t *testing.T) {
|
||||
},
|
||||
},
|
||||
},
|
||||
false, false,
|
||||
true, true,
|
||||
[]packersdk.Build{},
|
||||
true,
|
||||
},
|
||||
@@ -53,8 +58,9 @@ func TestParse_build(t *testing.T) {
|
||||
defaultParser,
|
||||
parseTestArgs{"testdata/build/provisioner_untyped.pkr.hcl", nil, nil},
|
||||
&PackerConfig{
|
||||
Basedir: filepath.Join("testdata", "build"),
|
||||
Builds: nil,
|
||||
CorePackerVersionString: lockedVersion,
|
||||
Basedir: filepath.Join("testdata", "build"),
|
||||
Builds: nil,
|
||||
},
|
||||
true, true,
|
||||
nil,
|
||||
@@ -64,19 +70,31 @@ func TestParse_build(t *testing.T) {
|
||||
defaultParser,
|
||||
parseTestArgs{"testdata/build/provisioner_inexistent.pkr.hcl", nil, nil},
|
||||
&PackerConfig{
|
||||
Basedir: filepath.Join("testdata", "build"),
|
||||
Builds: nil,
|
||||
CorePackerVersionString: lockedVersion,
|
||||
Basedir: filepath.Join("testdata", "build"),
|
||||
Builds: Builds{
|
||||
&BuildBlock{
|
||||
ProvisionerBlocks: []*ProvisionerBlock{
|
||||
{
|
||||
PType: "inexistant",
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
true, true,
|
||||
[]packersdk.Build{&packer.CoreBuild{}},
|
||||
[]packersdk.Build{&packer.CoreBuild{
|
||||
Provisioners: []packer.CoreBuildProvisioner{},
|
||||
}},
|
||||
false,
|
||||
},
|
||||
{"untyped post-processor",
|
||||
defaultParser,
|
||||
parseTestArgs{"testdata/build/post-processor_untyped.pkr.hcl", nil, nil},
|
||||
&PackerConfig{
|
||||
Basedir: filepath.Join("testdata", "build"),
|
||||
Builds: nil,
|
||||
CorePackerVersionString: lockedVersion,
|
||||
Basedir: filepath.Join("testdata", "build"),
|
||||
Builds: nil,
|
||||
},
|
||||
true, true,
|
||||
[]packersdk.Build{&packer.CoreBuild{}},
|
||||
@@ -86,19 +104,33 @@ func TestParse_build(t *testing.T) {
|
||||
defaultParser,
|
||||
parseTestArgs{"testdata/build/post-processor_inexistent.pkr.hcl", nil, nil},
|
||||
&PackerConfig{
|
||||
Basedir: filepath.Join("testdata", "build"),
|
||||
Builds: nil,
|
||||
CorePackerVersionString: lockedVersion,
|
||||
Basedir: filepath.Join("testdata", "build"),
|
||||
Builds: Builds{
|
||||
&BuildBlock{
|
||||
PostProcessorsLists: [][]*PostProcessorBlock{
|
||||
{
|
||||
{
|
||||
PType: "inexistant",
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
true, true,
|
||||
[]packersdk.Build{},
|
||||
false,
|
||||
[]packersdk.Build{&packer.CoreBuild{
|
||||
PostProcessors: [][]packer.CoreBuildPostProcessor{},
|
||||
}},
|
||||
true,
|
||||
},
|
||||
{"invalid source",
|
||||
defaultParser,
|
||||
parseTestArgs{"testdata/build/invalid_source_reference.pkr.hcl", nil, nil},
|
||||
&PackerConfig{
|
||||
Basedir: filepath.Join("testdata", "build"),
|
||||
Builds: nil,
|
||||
CorePackerVersionString: lockedVersion,
|
||||
Basedir: filepath.Join("testdata", "build"),
|
||||
Builds: nil,
|
||||
},
|
||||
true, true,
|
||||
[]packersdk.Build{},
|
||||
@@ -108,21 +140,26 @@ func TestParse_build(t *testing.T) {
|
||||
defaultParser,
|
||||
parseTestArgs{"testdata/build/named.pkr.hcl", nil, nil},
|
||||
&PackerConfig{
|
||||
Basedir: filepath.Join("testdata", "build"),
|
||||
CorePackerVersionString: lockedVersion,
|
||||
Basedir: filepath.Join("testdata", "build"),
|
||||
Builds: Builds{
|
||||
&BuildBlock{
|
||||
Name: "somebuild",
|
||||
Sources: []SourceRef{
|
||||
Sources: []SourceUseBlock{
|
||||
{
|
||||
Type: "amazon-ebs",
|
||||
Name: "ubuntu-1604",
|
||||
SourceRef: SourceRef{
|
||||
Type: "amazon-ebs",
|
||||
Name: "ubuntu-1604",
|
||||
},
|
||||
},
|
||||
{
|
||||
SourceRef: refVBIsoUbuntu1204,
|
||||
},
|
||||
refVBIsoUbuntu1204,
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
false, false,
|
||||
true, true,
|
||||
[]packersdk.Build{},
|
||||
true,
|
||||
},
|
||||
@@ -130,16 +167,22 @@ func TestParse_build(t *testing.T) {
|
||||
defaultParser,
|
||||
parseTestArgs{"testdata/build/post-processor_onlyexcept.pkr.hcl", nil, nil},
|
||||
&PackerConfig{
|
||||
Basedir: filepath.Join("testdata", "build"),
|
||||
CorePackerVersionString: lockedVersion,
|
||||
Basedir: filepath.Join("testdata", "build"),
|
||||
Sources: map[SourceRef]SourceBlock{
|
||||
refVBIsoUbuntu1204: {Type: "virtualbox-iso", Name: "ubuntu-1204"},
|
||||
refAWSEBSUbuntu1604: {Type: "amazon-ebs", Name: "ubuntu-1604"},
|
||||
},
|
||||
Builds: Builds{
|
||||
&BuildBlock{
|
||||
Sources: []SourceRef{
|
||||
refVBIsoUbuntu1204,
|
||||
SourceRef{Type: "amazon-ebs", Name: "ubuntu-1604", LocalName: "aws-ubuntu-16.04"},
|
||||
Sources: []SourceUseBlock{
|
||||
{
|
||||
SourceRef: refVBIsoUbuntu1204,
|
||||
},
|
||||
{
|
||||
SourceRef: SourceRef{Type: "amazon-ebs", Name: "ubuntu-1604"},
|
||||
LocalName: "aws-ubuntu-16.04",
|
||||
},
|
||||
},
|
||||
ProvisionerBlocks: nil,
|
||||
PostProcessorsLists: [][]*PostProcessorBlock{
|
||||
@@ -248,16 +291,22 @@ func TestParse_build(t *testing.T) {
|
||||
defaultParser,
|
||||
parseTestArgs{"testdata/build/provisioner_onlyexcept.pkr.hcl", nil, nil},
|
||||
&PackerConfig{
|
||||
Basedir: filepath.Join("testdata", "build"),
|
||||
CorePackerVersionString: lockedVersion,
|
||||
Basedir: filepath.Join("testdata", "build"),
|
||||
Sources: map[SourceRef]SourceBlock{
|
||||
refVBIsoUbuntu1204: {Type: "virtualbox-iso", Name: "ubuntu-1204"},
|
||||
refAWSEBSUbuntu1604: {Type: "amazon-ebs", Name: "ubuntu-1604"},
|
||||
},
|
||||
Builds: Builds{
|
||||
&BuildBlock{
|
||||
Sources: []SourceRef{
|
||||
refVBIsoUbuntu1204,
|
||||
SourceRef{Type: "amazon-ebs", Name: "ubuntu-1604", LocalName: "aws-ubuntu-16.04"},
|
||||
Sources: []SourceUseBlock{
|
||||
{
|
||||
SourceRef: refVBIsoUbuntu1204,
|
||||
},
|
||||
{
|
||||
SourceRef: SourceRef{Type: "amazon-ebs", Name: "ubuntu-1604"},
|
||||
LocalName: "aws-ubuntu-16.04",
|
||||
},
|
||||
},
|
||||
ProvisionerBlocks: []*ProvisionerBlock{
|
||||
{
|
||||
|
||||
@@ -102,7 +102,7 @@ func (cfg *PackerConfig) startDatasource(dataSourceStore packer.DatasourceStore,
|
||||
})
|
||||
}
|
||||
body := block.Body
|
||||
decoded, moreDiags := decodeHCL2Spec(body, cfg.EvalContext(nil), datasource)
|
||||
decoded, moreDiags := decodeHCL2Spec(body, cfg.EvalContext(DatasourceContext, nil), datasource)
|
||||
diags = append(diags, moreDiags...)
|
||||
if moreDiags.HasErrors() {
|
||||
return nil, diags
|
||||
|
||||
@@ -15,7 +15,8 @@ func TestParse_datasource(t *testing.T) {
|
||||
defaultParser,
|
||||
parseTestArgs{"testdata/datasources/basic.pkr.hcl", nil, nil},
|
||||
&PackerConfig{
|
||||
Basedir: filepath.Join("testdata", "datasources"),
|
||||
CorePackerVersionString: lockedVersion,
|
||||
Basedir: filepath.Join("testdata", "datasources"),
|
||||
Datasources: Datasources{
|
||||
{
|
||||
Type: "amazon-ami",
|
||||
@@ -34,7 +35,8 @@ func TestParse_datasource(t *testing.T) {
|
||||
defaultParser,
|
||||
parseTestArgs{"testdata/datasources/untyped.pkr.hcl", nil, nil},
|
||||
&PackerConfig{
|
||||
Basedir: filepath.Join("testdata", "datasources"),
|
||||
CorePackerVersionString: lockedVersion,
|
||||
Basedir: filepath.Join("testdata", "datasources"),
|
||||
},
|
||||
true, true,
|
||||
nil,
|
||||
@@ -44,7 +46,35 @@ func TestParse_datasource(t *testing.T) {
|
||||
defaultParser,
|
||||
parseTestArgs{"testdata/datasources/unnamed.pkr.hcl", nil, nil},
|
||||
&PackerConfig{
|
||||
Basedir: filepath.Join("testdata", "datasources"),
|
||||
CorePackerVersionString: lockedVersion,
|
||||
Basedir: filepath.Join("testdata", "datasources"),
|
||||
},
|
||||
true, true,
|
||||
nil,
|
||||
false,
|
||||
},
|
||||
{"not allowed usage of data source within another data source",
|
||||
defaultParser,
|
||||
parseTestArgs{"testdata/datasources/not-allowed.pkr.hcl", nil, nil},
|
||||
&PackerConfig{
|
||||
CorePackerVersionString: lockedVersion,
|
||||
Basedir: filepath.Join("testdata", "datasources"),
|
||||
Datasources: Datasources{
|
||||
{
|
||||
Type: "amazon-ami",
|
||||
Name: "test_0",
|
||||
}: {
|
||||
Type: "amazon-ami",
|
||||
Name: "test_0",
|
||||
},
|
||||
{
|
||||
Type: "amazon-ami",
|
||||
Name: "test_1",
|
||||
}: {
|
||||
Type: "amazon-ami",
|
||||
Name: "test_1",
|
||||
},
|
||||
},
|
||||
},
|
||||
true, true,
|
||||
nil,
|
||||
@@ -54,7 +84,8 @@ func TestParse_datasource(t *testing.T) {
|
||||
defaultParser,
|
||||
parseTestArgs{"testdata/datasources/inexistent.pkr.hcl", nil, nil},
|
||||
&PackerConfig{
|
||||
Basedir: filepath.Join("testdata", "datasources"),
|
||||
CorePackerVersionString: lockedVersion,
|
||||
Basedir: filepath.Join("testdata", "datasources"),
|
||||
Datasources: Datasources{
|
||||
{
|
||||
Type: "inexistant",
|
||||
@@ -73,7 +104,8 @@ func TestParse_datasource(t *testing.T) {
|
||||
defaultParser,
|
||||
parseTestArgs{"testdata/datasources/duplicate.pkr.hcl", nil, nil},
|
||||
&PackerConfig{
|
||||
Basedir: filepath.Join("testdata", "datasources"),
|
||||
CorePackerVersionString: lockedVersion,
|
||||
Basedir: filepath.Join("testdata", "datasources"),
|
||||
Datasources: Datasources{
|
||||
{
|
||||
Type: "amazon-ami",
|
||||
|
||||
@@ -2,7 +2,6 @@ package hcl2template
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
|
||||
"github.com/hashicorp/hcl/v2"
|
||||
"github.com/hashicorp/hcl/v2/hcldec"
|
||||
@@ -33,18 +32,12 @@ func (p *HCL2PostProcessor) HCL2Prepare(buildVars map[string]interface{}) error
|
||||
ectx = p.evalContext.NewChild()
|
||||
buildValues := map[string]cty.Value{}
|
||||
for k, v := range buildVars {
|
||||
switch v := v.(type) {
|
||||
case string:
|
||||
buildValues[k] = cty.StringVal(v)
|
||||
case int64:
|
||||
buildValues[k] = cty.NumberIntVal(v)
|
||||
case uint64:
|
||||
buildValues[k] = cty.NumberUIntVal(v)
|
||||
case bool:
|
||||
buildValues[k] = cty.BoolVal(v)
|
||||
default:
|
||||
return fmt.Errorf("unhandled buildvar type: %T", v)
|
||||
val, err := ConvertPluginConfigValueToHCLValue(v)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
buildValues[k] = val
|
||||
}
|
||||
ectx.Variables = map[string]cty.Value{
|
||||
buildAccessor: cty.ObjectVal(buildValues),
|
||||
|
||||
@@ -2,7 +2,6 @@ package hcl2template
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
|
||||
"github.com/hashicorp/hcl/v2"
|
||||
"github.com/hashicorp/hcl/v2/hcldec"
|
||||
@@ -37,18 +36,12 @@ func (p *HCL2Provisioner) HCL2Prepare(buildVars map[string]interface{}) error {
|
||||
buildValues = p.evalContext.Variables[buildAccessor].AsValueMap()
|
||||
}
|
||||
for k, v := range buildVars {
|
||||
switch v := v.(type) {
|
||||
case string:
|
||||
buildValues[k] = cty.StringVal(v)
|
||||
case int64:
|
||||
buildValues[k] = cty.NumberIntVal(v)
|
||||
case uint64:
|
||||
buildValues[k] = cty.NumberUIntVal(v)
|
||||
case bool:
|
||||
buildValues[k] = cty.BoolVal(v)
|
||||
default:
|
||||
return fmt.Errorf("unhandled buildvar type: %T", v)
|
||||
val, err := ConvertPluginConfigValueToHCLValue(v)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
buildValues[k] = val
|
||||
}
|
||||
ectx.Variables = map[string]cty.Value{
|
||||
buildAccessor: cty.ObjectVal(buildValues),
|
||||
|
||||
@@ -21,7 +21,9 @@ import (
|
||||
type PackerConfig struct {
|
||||
Packer struct {
|
||||
VersionConstraints []VersionConstraint
|
||||
RequiredPlugins []*RequiredPlugins
|
||||
}
|
||||
|
||||
// Directory where the config files are defined
|
||||
Basedir string
|
||||
|
||||
@@ -50,16 +52,15 @@ type PackerConfig struct {
|
||||
// Builds is the list of Build blocks defined in the config files.
|
||||
Builds Builds
|
||||
|
||||
builderSchemas packer.BuilderStore
|
||||
provisionersSchemas packer.ProvisionerStore
|
||||
postProcessorsSchemas packer.PostProcessorStore
|
||||
datasourceSchemas packer.DatasourceStore
|
||||
|
||||
except []glob.Glob
|
||||
only []glob.Glob
|
||||
|
||||
parser *Parser
|
||||
files []*hcl.File
|
||||
|
||||
// Fields passed as command line flags
|
||||
except []glob.Glob
|
||||
only []glob.Glob
|
||||
force bool
|
||||
debug bool
|
||||
onError string
|
||||
}
|
||||
|
||||
type ValidationOptions struct {
|
||||
@@ -76,13 +77,22 @@ const (
|
||||
dataAccessor = "data"
|
||||
)
|
||||
|
||||
type BlockContext int
|
||||
|
||||
const (
|
||||
InputVariableContext BlockContext = iota
|
||||
LocalContext
|
||||
BuildContext
|
||||
DatasourceContext
|
||||
NilContext
|
||||
)
|
||||
|
||||
// EvalContext returns the *hcl.EvalContext that will be passed to an hcl
|
||||
// decoder in order to tell what is the actual value of a var or a local and
|
||||
// the list of defined functions.
|
||||
func (cfg *PackerConfig) EvalContext(variables map[string]cty.Value) *hcl.EvalContext {
|
||||
func (cfg *PackerConfig) EvalContext(ctx BlockContext, variables map[string]cty.Value) *hcl.EvalContext {
|
||||
inputVariables, _ := cfg.InputVariables.Values()
|
||||
localVariables, _ := cfg.LocalVariables.Values()
|
||||
datasourceVariables, _ := cfg.Datasources.Values()
|
||||
ectx := &hcl.EvalContext{
|
||||
Functions: Functions(cfg.Basedir),
|
||||
Variables: map[string]cty.Value{
|
||||
@@ -100,9 +110,25 @@ func (cfg *PackerConfig) EvalContext(variables map[string]cty.Value) *hcl.EvalCo
|
||||
"cwd": cty.StringVal(strings.ReplaceAll(cfg.Cwd, `\`, `/`)),
|
||||
"root": cty.StringVal(strings.ReplaceAll(cfg.Basedir, `\`, `/`)),
|
||||
}),
|
||||
dataAccessor: cty.ObjectVal(datasourceVariables),
|
||||
},
|
||||
}
|
||||
|
||||
// Currently the places where you can make references to other blocks
|
||||
// from one is very 'procedural', and in this specific case, we could make
|
||||
// the data sources available to other datasources, but this would be
|
||||
// order dependant, meaning that if you define two datasources in two
|
||||
// different blocks, the second one can use the first one, but not the
|
||||
// other way around; which would be totally confusing; so - for now -
|
||||
// datasources can't use other datasources.
|
||||
// In the future we'd like to load and execute HCL blocks using a graph
|
||||
// dependency tree, so that any block can use any block whatever the
|
||||
// order.
|
||||
switch ctx {
|
||||
case LocalContext, BuildContext:
|
||||
datasourceVariables, _ := cfg.Datasources.Values()
|
||||
ectx.Variables[dataAccessor] = cty.ObjectVal(datasourceVariables)
|
||||
}
|
||||
|
||||
for k, v := range variables {
|
||||
ectx.Variables[k] = v
|
||||
}
|
||||
@@ -150,10 +176,20 @@ func (c *PackerConfig) parseLocalVariables(f *hcl.File) ([]*LocalBlock, hcl.Diag
|
||||
|
||||
content, moreDiags := f.Body.Content(configSchema)
|
||||
diags = append(diags, moreDiags...)
|
||||
var locals []*LocalBlock
|
||||
|
||||
locals := c.LocalBlocks
|
||||
|
||||
for _, block := range content.Blocks {
|
||||
switch block.Type {
|
||||
case localLabel:
|
||||
l, moreDiags := decodeLocalBlock(block, locals)
|
||||
diags = append(diags, moreDiags...)
|
||||
if l != nil {
|
||||
locals = append(locals, l)
|
||||
}
|
||||
if moreDiags.HasErrors() {
|
||||
return locals, diags
|
||||
}
|
||||
case localsLabel:
|
||||
attrs, moreDiags := block.Body.JustAttributes()
|
||||
diags = append(diags, moreDiags...)
|
||||
@@ -166,7 +202,7 @@ func (c *PackerConfig) parseLocalVariables(f *hcl.File) ([]*LocalBlock, hcl.Diag
|
||||
Subject: attr.NameRange.Ptr(),
|
||||
Context: block.DefRange.Ptr(),
|
||||
})
|
||||
return nil, diags
|
||||
return locals, diags
|
||||
}
|
||||
locals = append(locals, &LocalBlock{
|
||||
Name: name,
|
||||
@@ -176,6 +212,7 @@ func (c *PackerConfig) parseLocalVariables(f *hcl.File) ([]*LocalBlock, hcl.Diag
|
||||
}
|
||||
}
|
||||
|
||||
c.LocalBlocks = locals
|
||||
return locals, diags
|
||||
}
|
||||
|
||||
@@ -221,14 +258,14 @@ func (c *PackerConfig) evaluateLocalVariables(locals []*LocalBlock) hcl.Diagnost
|
||||
|
||||
func (c *PackerConfig) evaluateLocalVariable(local *LocalBlock) hcl.Diagnostics {
|
||||
var diags hcl.Diagnostics
|
||||
|
||||
value, moreDiags := local.Expr.Value(c.EvalContext(nil))
|
||||
value, moreDiags := local.Expr.Value(c.EvalContext(LocalContext, nil))
|
||||
diags = append(diags, moreDiags...)
|
||||
if moreDiags.HasErrors() {
|
||||
return diags
|
||||
}
|
||||
c.LocalVariables[local.Name] = &Variable{
|
||||
Name: local.Name,
|
||||
Name: local.Name,
|
||||
Sensitive: local.Sensitive,
|
||||
Values: []VariableAssignment{{
|
||||
Value: value,
|
||||
Expr: local.Expr,
|
||||
@@ -248,7 +285,7 @@ func (cfg *PackerConfig) evaluateDatasources(skipExecution bool) hcl.Diagnostics
|
||||
continue
|
||||
}
|
||||
|
||||
datasource, startDiags := cfg.startDatasource(cfg.datasourceSchemas, ref)
|
||||
datasource, startDiags := cfg.startDatasource(cfg.parser.PluginConfig.DataSources, ref)
|
||||
diags = append(diags, startDiags...)
|
||||
if diags.HasErrors() {
|
||||
continue
|
||||
@@ -279,7 +316,7 @@ func (cfg *PackerConfig) evaluateDatasources(skipExecution bool) hcl.Diagnostics
|
||||
|
||||
// getCoreBuildProvisioners takes a list of provisioner block, starts according
|
||||
// provisioners and sends parsed HCL2 over to it.
|
||||
func (cfg *PackerConfig) getCoreBuildProvisioners(source SourceBlock, blocks []*ProvisionerBlock, ectx *hcl.EvalContext) ([]packer.CoreBuildProvisioner, hcl.Diagnostics) {
|
||||
func (cfg *PackerConfig) getCoreBuildProvisioners(source SourceUseBlock, blocks []*ProvisionerBlock, ectx *hcl.EvalContext) ([]packer.CoreBuildProvisioner, hcl.Diagnostics) {
|
||||
var diags hcl.Diagnostics
|
||||
res := []packer.CoreBuildProvisioner{}
|
||||
for _, pb := range blocks {
|
||||
@@ -322,7 +359,7 @@ func (cfg *PackerConfig) getCoreBuildProvisioners(source SourceBlock, blocks []*
|
||||
|
||||
// getCoreBuildProvisioners takes a list of post processor block, starts
|
||||
// according provisioners and sends parsed HCL2 over to it.
|
||||
func (cfg *PackerConfig) getCoreBuildPostProcessors(source SourceBlock, blocksList [][]*PostProcessorBlock, ectx *hcl.EvalContext) ([][]packer.CoreBuildPostProcessor, hcl.Diagnostics) {
|
||||
func (cfg *PackerConfig) getCoreBuildPostProcessors(source SourceUseBlock, blocksList [][]*PostProcessorBlock, ectx *hcl.EvalContext) ([][]packer.CoreBuildPostProcessor, hcl.Diagnostics) {
|
||||
var diags hcl.Diagnostics
|
||||
res := [][]packer.CoreBuildPostProcessor{}
|
||||
for _, blocks := range blocksList {
|
||||
@@ -375,24 +412,26 @@ func (cfg *PackerConfig) GetBuilds(opts packer.GetBuildsOptions) ([]packersdk.Bu
|
||||
res := []packersdk.Build{}
|
||||
var diags hcl.Diagnostics
|
||||
|
||||
cfg.debug = opts.Debug
|
||||
cfg.force = opts.Force
|
||||
cfg.onError = opts.OnError
|
||||
|
||||
for _, build := range cfg.Builds {
|
||||
for _, from := range build.Sources {
|
||||
src, found := cfg.Sources[from.Ref()]
|
||||
for _, srcUsage := range build.Sources {
|
||||
src, found := cfg.Sources[srcUsage.SourceRef]
|
||||
if !found {
|
||||
diags = append(diags, &hcl.Diagnostic{
|
||||
Summary: "Unknown " + sourceLabel + " " + from.String(),
|
||||
Summary: "Unknown " + sourceLabel + " " + srcUsage.String(),
|
||||
Subject: build.HCL2Ref.DefRange.Ptr(),
|
||||
Severity: hcl.DiagError,
|
||||
Detail: fmt.Sprintf("Known: %v", cfg.Sources),
|
||||
})
|
||||
continue
|
||||
}
|
||||
src.addition = from.addition
|
||||
src.LocalName = from.LocalName
|
||||
|
||||
pcb := &packer.CoreBuild{
|
||||
BuildName: build.Name,
|
||||
Type: src.String(),
|
||||
Type: srcUsage.String(),
|
||||
}
|
||||
|
||||
// Apply the -only and -except command-line options to exclude matching builds.
|
||||
@@ -435,7 +474,7 @@ func (cfg *PackerConfig) GetBuilds(opts packer.GetBuildsOptions) ([]packersdk.Bu
|
||||
}
|
||||
}
|
||||
|
||||
builder, moreDiags, generatedVars := cfg.startBuilder(src, cfg.EvalContext(nil), opts)
|
||||
builder, moreDiags, generatedVars := cfg.startBuilder(srcUsage, cfg.EvalContext(BuildContext, nil))
|
||||
diags = append(diags, moreDiags...)
|
||||
if moreDiags.HasErrors() {
|
||||
continue
|
||||
@@ -453,16 +492,16 @@ func (cfg *PackerConfig) GetBuilds(opts packer.GetBuildsOptions) ([]packersdk.Bu
|
||||
unknownBuildValues["name"] = cty.StringVal(build.Name)
|
||||
|
||||
variables := map[string]cty.Value{
|
||||
sourcesAccessor: cty.ObjectVal(src.ctyValues()),
|
||||
sourcesAccessor: cty.ObjectVal(srcUsage.ctyValues()),
|
||||
buildAccessor: cty.ObjectVal(unknownBuildValues),
|
||||
}
|
||||
|
||||
provisioners, moreDiags := cfg.getCoreBuildProvisioners(src, build.ProvisionerBlocks, cfg.EvalContext(variables))
|
||||
provisioners, moreDiags := cfg.getCoreBuildProvisioners(srcUsage, build.ProvisionerBlocks, cfg.EvalContext(BuildContext, variables))
|
||||
diags = append(diags, moreDiags...)
|
||||
if moreDiags.HasErrors() {
|
||||
continue
|
||||
}
|
||||
pps, moreDiags := cfg.getCoreBuildPostProcessors(src, build.PostProcessorsLists, cfg.EvalContext(variables))
|
||||
pps, moreDiags := cfg.getCoreBuildPostProcessors(srcUsage, build.PostProcessorsLists, cfg.EvalContext(BuildContext, variables))
|
||||
diags = append(diags, moreDiags...)
|
||||
if moreDiags.HasErrors() {
|
||||
continue
|
||||
@@ -564,7 +603,7 @@ func (p *PackerConfig) printBuilds() string {
|
||||
fmt.Fprintf(out, "\n <no source>\n")
|
||||
}
|
||||
for _, source := range build.Sources {
|
||||
fmt.Fprintf(out, "\n %s\n", source)
|
||||
fmt.Fprintf(out, "\n %s\n", source.String())
|
||||
}
|
||||
fmt.Fprintf(out, "\n provisioners:\n\n")
|
||||
if len(build.ProvisionerBlocks) == 0 {
|
||||
@@ -604,7 +643,7 @@ func (p *PackerConfig) handleEval(line string) (out string, exit bool, diags hcl
|
||||
return "", false, diags
|
||||
}
|
||||
|
||||
val, valueDiags := expr.Value(p.EvalContext(nil))
|
||||
val, valueDiags := expr.Value(p.EvalContext(NilContext, nil))
|
||||
diags = append(diags, valueDiags...)
|
||||
if valueDiags.HasErrors() {
|
||||
return "", false, diags
|
||||
|
||||
@@ -3,8 +3,11 @@ package hcl2template
|
||||
import (
|
||||
"testing"
|
||||
|
||||
"github.com/hashicorp/go-version"
|
||||
packersdk "github.com/hashicorp/packer-plugin-sdk/packer"
|
||||
"github.com/hashicorp/packer/hcl2template/addrs"
|
||||
. "github.com/hashicorp/packer/hcl2template/internal"
|
||||
hcl2template "github.com/hashicorp/packer/hcl2template/internal"
|
||||
"github.com/hashicorp/packer/packer"
|
||||
"github.com/zclconf/go-cty/cty"
|
||||
)
|
||||
@@ -12,6 +15,7 @@ import (
|
||||
var (
|
||||
refVBIsoUbuntu1204 = SourceRef{Type: "virtualbox-iso", Name: "ubuntu-1204"}
|
||||
refAWSEBSUbuntu1604 = SourceRef{Type: "amazon-ebs", Name: "ubuntu-1604"}
|
||||
refAWSV3MyImage = SourceRef{Type: "amazon-v3-ebs", Name: "my-image"}
|
||||
pTrue = pointerToBool(true)
|
||||
)
|
||||
|
||||
@@ -23,7 +27,20 @@ func TestParser_complete(t *testing.T) {
|
||||
defaultParser,
|
||||
parseTestArgs{"testdata/complete", nil, nil},
|
||||
&PackerConfig{
|
||||
Basedir: "testdata/complete",
|
||||
Packer: struct {
|
||||
VersionConstraints []VersionConstraint
|
||||
RequiredPlugins []*RequiredPlugins
|
||||
}{
|
||||
VersionConstraints: []VersionConstraint{
|
||||
{
|
||||
Required: mustVersionConstraints(version.NewConstraint(">= v1")),
|
||||
},
|
||||
},
|
||||
RequiredPlugins: nil,
|
||||
},
|
||||
CorePackerVersionString: lockedVersion,
|
||||
Basedir: "testdata/complete",
|
||||
|
||||
InputVariables: Variables{
|
||||
"foo": &Variable{
|
||||
Name: "foo",
|
||||
@@ -104,6 +121,13 @@ func TestParser_complete(t *testing.T) {
|
||||
}),
|
||||
}),
|
||||
},
|
||||
"supersecret": &Variable{
|
||||
Name: "supersecret",
|
||||
Values: []VariableAssignment{{From: "default",
|
||||
Value: cty.StringVal("image-id-default-password")}},
|
||||
Type: cty.String,
|
||||
Sensitive: true,
|
||||
},
|
||||
},
|
||||
Datasources: Datasources{
|
||||
DatasourceRef{Type: "amazon-ami", Name: "test"}: Datasource{
|
||||
@@ -118,9 +142,13 @@ func TestParser_complete(t *testing.T) {
|
||||
},
|
||||
Builds: Builds{
|
||||
&BuildBlock{
|
||||
Sources: []SourceRef{
|
||||
refVBIsoUbuntu1204,
|
||||
refAWSEBSUbuntu1604,
|
||||
Sources: []SourceUseBlock{
|
||||
{
|
||||
SourceRef: refVBIsoUbuntu1204,
|
||||
},
|
||||
{
|
||||
SourceRef: refAWSEBSUbuntu1604,
|
||||
},
|
||||
},
|
||||
ProvisionerBlocks: []*ProvisionerBlock{
|
||||
{
|
||||
@@ -193,7 +221,7 @@ func TestParser_complete(t *testing.T) {
|
||||
PType: "amazon-import",
|
||||
PName: "something",
|
||||
PostProcessor: &HCL2PostProcessor{
|
||||
PostProcessor: basicMockPostProcessor,
|
||||
PostProcessor: basicMockPostProcessorDynamicTags,
|
||||
},
|
||||
KeepInputArtifact: pTrue,
|
||||
},
|
||||
@@ -250,7 +278,20 @@ func TestParser_complete(t *testing.T) {
|
||||
Int: 42,
|
||||
Tags: []MockTag{},
|
||||
},
|
||||
NestedSlice: []NestedMockConfig{},
|
||||
Nested: hcl2template.NestedMockConfig{
|
||||
Tags: []hcl2template.MockTag{
|
||||
{Key: "Component", Value: "user-service"},
|
||||
{Key: "Environment", Value: "production"},
|
||||
},
|
||||
},
|
||||
NestedSlice: []NestedMockConfig{
|
||||
hcl2template.NestedMockConfig{
|
||||
Tags: []hcl2template.MockTag{
|
||||
{Key: "Component", Value: "user-service"},
|
||||
{Key: "Environment", Value: "production"},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
Provisioners: []packer.CoreBuildProvisioner{
|
||||
@@ -274,7 +315,7 @@ func TestParser_complete(t *testing.T) {
|
||||
PType: "amazon-import",
|
||||
PName: "something",
|
||||
PostProcessor: &HCL2PostProcessor{
|
||||
PostProcessor: basicMockPostProcessor,
|
||||
PostProcessor: basicMockPostProcessorDynamicTags,
|
||||
},
|
||||
KeepInputArtifact: pTrue,
|
||||
},
|
||||
@@ -350,6 +391,218 @@ func TestParser_ValidateFilterOption(t *testing.T) {
|
||||
}
|
||||
}
|
||||
|
||||
func TestParser_no_init(t *testing.T) {
|
||||
defaultParser := getBasicParser()
|
||||
|
||||
tests := []parseTest{
|
||||
{"working build with imports",
|
||||
defaultParser,
|
||||
parseTestArgs{"testdata/init/imports", nil, nil},
|
||||
&PackerConfig{
|
||||
Packer: struct {
|
||||
VersionConstraints []VersionConstraint
|
||||
RequiredPlugins []*RequiredPlugins
|
||||
}{
|
||||
VersionConstraints: []VersionConstraint{
|
||||
{
|
||||
Required: mustVersionConstraints(version.NewConstraint(">= v1")),
|
||||
},
|
||||
},
|
||||
RequiredPlugins: []*RequiredPlugins{
|
||||
{
|
||||
RequiredPlugins: map[string]*RequiredPlugin{
|
||||
"amazon": {
|
||||
Name: "amazon",
|
||||
Source: "",
|
||||
Type: &addrs.Plugin{
|
||||
Type: "amazon",
|
||||
Namespace: "hashicorp",
|
||||
Hostname: "github.com",
|
||||
},
|
||||
Requirement: VersionConstraint{
|
||||
Required: mustVersionConstraints(version.NewConstraint(">= v0")),
|
||||
},
|
||||
},
|
||||
"amazon-v1": {
|
||||
Name: "amazon-v1",
|
||||
Source: "amazon",
|
||||
Type: &addrs.Plugin{
|
||||
Type: "amazon",
|
||||
Namespace: "hashicorp",
|
||||
Hostname: "github.com",
|
||||
},
|
||||
Requirement: VersionConstraint{
|
||||
Required: mustVersionConstraints(version.NewConstraint(">= v1")),
|
||||
},
|
||||
},
|
||||
"amazon-v2": {
|
||||
Name: "amazon-v2",
|
||||
Source: "amazon",
|
||||
Type: &addrs.Plugin{
|
||||
Type: "amazon",
|
||||
Namespace: "hashicorp",
|
||||
Hostname: "github.com",
|
||||
},
|
||||
Requirement: VersionConstraint{
|
||||
Required: mustVersionConstraints(version.NewConstraint(">= v2")),
|
||||
},
|
||||
},
|
||||
"amazon-v3": {
|
||||
Name: "amazon-v3",
|
||||
Source: "hashicorp/amazon",
|
||||
Type: &addrs.Plugin{
|
||||
Type: "amazon",
|
||||
Namespace: "hashicorp",
|
||||
Hostname: "github.com",
|
||||
},
|
||||
Requirement: VersionConstraint{
|
||||
Required: mustVersionConstraints(version.NewConstraint(">= v3")),
|
||||
},
|
||||
},
|
||||
"amazon-v3-azr": {
|
||||
Name: "amazon-v3-azr",
|
||||
Source: "azr/amazon",
|
||||
Type: &addrs.Plugin{
|
||||
Type: "amazon",
|
||||
Namespace: "azr",
|
||||
Hostname: "github.com",
|
||||
},
|
||||
Requirement: VersionConstraint{
|
||||
Required: mustVersionConstraints(version.NewConstraint(">= v3")),
|
||||
},
|
||||
},
|
||||
"amazon-v4": {
|
||||
Name: "amazon-v4",
|
||||
Source: "github.com/hashicorp/amazon",
|
||||
Type: &addrs.Plugin{
|
||||
Type: "amazon",
|
||||
Namespace: "hashicorp",
|
||||
Hostname: "github.com",
|
||||
},
|
||||
Requirement: VersionConstraint{
|
||||
Required: mustVersionConstraints(version.NewConstraint(">= v4")),
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
CorePackerVersionString: lockedVersion,
|
||||
Basedir: "testdata/init/imports",
|
||||
|
||||
InputVariables: Variables{
|
||||
"foo": &Variable{
|
||||
Name: "foo",
|
||||
Values: []VariableAssignment{{From: "default", Value: cty.StringVal("value")}},
|
||||
Type: cty.String,
|
||||
},
|
||||
"image_id": &Variable{
|
||||
Name: "image_id",
|
||||
Values: []VariableAssignment{{From: "default", Value: cty.StringVal("image-id-default")}},
|
||||
Type: cty.String,
|
||||
},
|
||||
"port": &Variable{
|
||||
Name: "port",
|
||||
Values: []VariableAssignment{{From: "default", Value: cty.NumberIntVal(42)}},
|
||||
Type: cty.Number,
|
||||
},
|
||||
"availability_zone_names": &Variable{
|
||||
Name: "availability_zone_names",
|
||||
Values: []VariableAssignment{{
|
||||
From: "default",
|
||||
Value: cty.ListVal([]cty.Value{
|
||||
cty.StringVal("A"),
|
||||
cty.StringVal("B"),
|
||||
cty.StringVal("C"),
|
||||
}),
|
||||
}},
|
||||
Type: cty.List(cty.String),
|
||||
},
|
||||
},
|
||||
Sources: map[SourceRef]SourceBlock{
|
||||
refVBIsoUbuntu1204: {Type: "virtualbox-iso", Name: "ubuntu-1204"},
|
||||
refAWSV3MyImage: {Type: "amazon-v3-ebs", Name: "my-image"},
|
||||
},
|
||||
Builds: Builds{
|
||||
&BuildBlock{
|
||||
Sources: []SourceUseBlock{
|
||||
{
|
||||
SourceRef: refVBIsoUbuntu1204,
|
||||
},
|
||||
{
|
||||
SourceRef: refAWSV3MyImage,
|
||||
},
|
||||
},
|
||||
ProvisionerBlocks: []*ProvisionerBlock{
|
||||
{
|
||||
PType: "shell",
|
||||
PName: "provisioner that does something",
|
||||
},
|
||||
{
|
||||
PType: "file",
|
||||
},
|
||||
},
|
||||
PostProcessorsLists: [][]*PostProcessorBlock{
|
||||
{
|
||||
{
|
||||
PType: "amazon-import",
|
||||
PName: "something",
|
||||
KeepInputArtifact: pTrue,
|
||||
},
|
||||
},
|
||||
{
|
||||
{
|
||||
PType: "amazon-import",
|
||||
},
|
||||
},
|
||||
{
|
||||
{
|
||||
PType: "amazon-import",
|
||||
PName: "first-nested-post-processor",
|
||||
},
|
||||
{
|
||||
PType: "amazon-import",
|
||||
PName: "second-nested-post-processor",
|
||||
},
|
||||
},
|
||||
{
|
||||
{
|
||||
PType: "amazon-import",
|
||||
PName: "third-nested-post-processor",
|
||||
},
|
||||
{
|
||||
PType: "amazon-import",
|
||||
PName: "fourth-nested-post-processor",
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
false, false,
|
||||
[]packersdk.Build{},
|
||||
false,
|
||||
},
|
||||
|
||||
{"duplicate required plugin accessor fails",
|
||||
defaultParser,
|
||||
parseTestArgs{"testdata/init/duplicate_required_plugins", nil, nil},
|
||||
nil,
|
||||
true, true,
|
||||
[]packersdk.Build{},
|
||||
false,
|
||||
},
|
||||
}
|
||||
testParse_only_Parse(t, tests)
|
||||
}
|
||||
|
||||
func pointerToBool(b bool) *bool {
|
||||
return &b
|
||||
}
|
||||
|
||||
func mustVersionConstraints(vs version.Constraints, err error) version.Constraints {
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
return vs
|
||||
}
|
||||
|
||||
@@ -0,0 +1,242 @@
|
||||
package hcl2template
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
|
||||
"github.com/hashicorp/go-version"
|
||||
"github.com/hashicorp/hcl/v2"
|
||||
"github.com/hashicorp/packer/hcl2template/addrs"
|
||||
"github.com/zclconf/go-cty/cty"
|
||||
)
|
||||
|
||||
func (cfg *PackerConfig) decodeRequiredPluginsBlock(f *hcl.File) hcl.Diagnostics {
|
||||
var diags hcl.Diagnostics
|
||||
|
||||
content, moreDiags := f.Body.Content(configSchema)
|
||||
diags = append(diags, moreDiags...)
|
||||
|
||||
for _, block := range content.Blocks {
|
||||
switch block.Type {
|
||||
case packerLabel:
|
||||
content, contentDiags := block.Body.Content(packerBlockSchema)
|
||||
diags = append(diags, contentDiags...)
|
||||
|
||||
// We ignore "packer_version"" here because
|
||||
// sniffCoreVersionRequirements already dealt with that
|
||||
|
||||
for _, innerBlock := range content.Blocks {
|
||||
switch innerBlock.Type {
|
||||
case "required_plugins":
|
||||
reqs, reqsDiags := decodeRequiredPluginsBlock(innerBlock)
|
||||
diags = append(diags, reqsDiags...)
|
||||
cfg.Packer.RequiredPlugins = append(cfg.Packer.RequiredPlugins, reqs)
|
||||
default:
|
||||
continue
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
return diags
|
||||
}
|
||||
|
||||
func (cfg *PackerConfig) decodeImplicitRequiredPluginsBlocks(f *hcl.File) hcl.Diagnostics {
|
||||
// when a plugin is used but not defined in the required plugin blocks, it
|
||||
// is 'implicitly used'. Here we read common configuration blocks to try to
|
||||
// guess plugins.
|
||||
|
||||
var diags hcl.Diagnostics
|
||||
|
||||
content, moreDiags := f.Body.Content(configSchema)
|
||||
diags = append(diags, moreDiags...)
|
||||
|
||||
for _, block := range content.Blocks {
|
||||
switch block.Type {
|
||||
case sourceLabel:
|
||||
// TODO
|
||||
}
|
||||
}
|
||||
return diags
|
||||
}
|
||||
|
||||
// RequiredPlugin represents a declaration of a dependency on a particular
|
||||
// Plugin version or source.
|
||||
type RequiredPlugin struct {
|
||||
Name string
|
||||
Source string
|
||||
Type *addrs.Plugin
|
||||
Requirement VersionConstraint
|
||||
DeclRange hcl.Range
|
||||
}
|
||||
|
||||
type RequiredPlugins struct {
|
||||
RequiredPlugins map[string]*RequiredPlugin
|
||||
DeclRange hcl.Range
|
||||
}
|
||||
|
||||
func decodeRequiredPluginsBlock(block *hcl.Block) (*RequiredPlugins, hcl.Diagnostics) {
|
||||
attrs, diags := block.Body.JustAttributes()
|
||||
ret := &RequiredPlugins{
|
||||
RequiredPlugins: make(map[string]*RequiredPlugin),
|
||||
DeclRange: block.DefRange,
|
||||
}
|
||||
for name, attr := range attrs {
|
||||
expr, err := attr.Expr.Value(nil)
|
||||
if err != nil {
|
||||
diags = append(diags, err...)
|
||||
}
|
||||
|
||||
nameDiags := checkPluginNameNormalized(name, attr.Expr.Range())
|
||||
diags = append(diags, nameDiags...)
|
||||
|
||||
rp := &RequiredPlugin{
|
||||
Name: name,
|
||||
DeclRange: attr.Expr.Range(),
|
||||
}
|
||||
|
||||
switch {
|
||||
case expr.Type().IsPrimitiveType():
|
||||
vc, reqDiags := decodeVersionConstraint(attr)
|
||||
diags = append(diags, reqDiags...)
|
||||
rp.Requirement = vc
|
||||
rp.Type, err = addrs.ParsePluginSourceString(name)
|
||||
if err != nil {
|
||||
diags = diags.Append(&hcl.Diagnostic{
|
||||
Severity: hcl.DiagError,
|
||||
Summary: "Invalid plugin type",
|
||||
Detail: fmt.Sprintf(`Invalid plugin type %q: %s"`, name, err),
|
||||
})
|
||||
}
|
||||
|
||||
case expr.Type().IsObjectType():
|
||||
if !expr.Type().HasAttribute("version") {
|
||||
diags = append(diags, &hcl.Diagnostic{
|
||||
Severity: hcl.DiagError,
|
||||
Summary: "No version constraint was set",
|
||||
Detail: "The version field must be specified as a string. Ex: `version = \">= 1.2.0, < 2.0.0\". See https://www.packer.io/docs/templates/hcl_templates/blocks/packer#version-constraints for docs",
|
||||
Subject: attr.Expr.Range().Ptr(),
|
||||
})
|
||||
continue
|
||||
}
|
||||
|
||||
vc := VersionConstraint{
|
||||
DeclRange: attr.Range,
|
||||
}
|
||||
constraint := expr.GetAttr("version")
|
||||
if !constraint.Type().Equals(cty.String) || constraint.IsNull() {
|
||||
diags = append(diags, &hcl.Diagnostic{
|
||||
Severity: hcl.DiagError,
|
||||
Summary: "Invalid version constraint",
|
||||
Detail: "Version must be specified as a string. See https://www.packer.io/docs/templates/hcl_templates/blocks/packer#version-constraint-syntax for docs.",
|
||||
Subject: attr.Expr.Range().Ptr(),
|
||||
})
|
||||
continue
|
||||
}
|
||||
constraintStr := constraint.AsString()
|
||||
constraints, err := version.NewConstraint(constraintStr)
|
||||
if err != nil {
|
||||
// NewConstraint doesn't return user-friendly errors, so we'll just
|
||||
// ignore the provided error and produce our own generic one.
|
||||
diags = append(diags, &hcl.Diagnostic{
|
||||
Severity: hcl.DiagError,
|
||||
Summary: "Invalid version constraint",
|
||||
Detail: "This string does not use correct version constraint syntax. See https://www.packer.io/docs/templates/hcl_templates/blocks/packer#version-constraint-syntax for docs.",
|
||||
Subject: attr.Expr.Range().Ptr(),
|
||||
})
|
||||
continue
|
||||
}
|
||||
vc.Required = constraints
|
||||
rp.Requirement = vc
|
||||
|
||||
if !expr.Type().HasAttribute("source") {
|
||||
diags = append(diags, &hcl.Diagnostic{
|
||||
Severity: hcl.DiagError,
|
||||
Summary: "No source was set",
|
||||
Detail: "The source field must be specified as a string. Ex: `source = \"coolcloud\". See https://www.packer.io/docs/templates/hcl_templates/blocks/packer#specifying-plugin-requirements for docs",
|
||||
Subject: attr.Expr.Range().Ptr(),
|
||||
})
|
||||
continue
|
||||
}
|
||||
source := expr.GetAttr("source")
|
||||
|
||||
if !source.Type().Equals(cty.String) || source.IsNull() {
|
||||
diags = append(diags, &hcl.Diagnostic{
|
||||
Severity: hcl.DiagError,
|
||||
Summary: "Invalid source",
|
||||
Detail: "Source must be specified as a string. For example: " + `source = "coolcloud"`,
|
||||
Subject: attr.Expr.Range().Ptr(),
|
||||
})
|
||||
continue
|
||||
}
|
||||
|
||||
rp.Source = source.AsString()
|
||||
p, sourceDiags := addrs.ParsePluginSourceString(rp.Source)
|
||||
|
||||
if sourceDiags.HasErrors() {
|
||||
for _, diag := range sourceDiags {
|
||||
if diag.Subject == nil {
|
||||
diag.Subject = attr.Expr.Range().Ptr()
|
||||
}
|
||||
}
|
||||
diags = append(diags, sourceDiags...)
|
||||
} else {
|
||||
rp.Type = p
|
||||
}
|
||||
|
||||
attrTypes := expr.Type().AttributeTypes()
|
||||
for name := range attrTypes {
|
||||
if name == "version" || name == "source" {
|
||||
continue
|
||||
}
|
||||
diags = append(diags, &hcl.Diagnostic{
|
||||
Severity: hcl.DiagError,
|
||||
Summary: "Invalid required_plugins object",
|
||||
Detail: `required_plugins objects can only contain "version" and "source" attributes.`,
|
||||
Subject: attr.Expr.Range().Ptr(),
|
||||
})
|
||||
break
|
||||
}
|
||||
|
||||
default:
|
||||
// should not happen
|
||||
diags = append(diags, &hcl.Diagnostic{
|
||||
Severity: hcl.DiagError,
|
||||
Summary: "Invalid required_plugins syntax",
|
||||
Detail: "required_plugins entries must be objects.",
|
||||
Subject: attr.Expr.Range().Ptr(),
|
||||
})
|
||||
}
|
||||
|
||||
ret.RequiredPlugins[rp.Name] = rp
|
||||
}
|
||||
|
||||
return ret, diags
|
||||
}
|
||||
|
||||
// checkPluginNameNormalized verifies that the given string is already
|
||||
// normalized and returns an error if not.
|
||||
func checkPluginNameNormalized(name string, declrange hcl.Range) hcl.Diagnostics {
|
||||
var diags hcl.Diagnostics
|
||||
// verify that the plugin local name is normalized
|
||||
normalized, err := addrs.IsPluginPartNormalized(name)
|
||||
if err != nil {
|
||||
diags = append(diags, &hcl.Diagnostic{
|
||||
Severity: hcl.DiagError,
|
||||
Summary: "Invalid plugin local name",
|
||||
Detail: fmt.Sprintf("%s is an invalid plugin local name: %s", name, err),
|
||||
Subject: &declrange,
|
||||
})
|
||||
return diags
|
||||
}
|
||||
if !normalized {
|
||||
// we would have returned this error already
|
||||
normalizedPlugin, _ := addrs.ParsePluginPart(name)
|
||||
diags = append(diags, &hcl.Diagnostic{
|
||||
Severity: hcl.DiagError,
|
||||
Summary: "Invalid plugin local name",
|
||||
Detail: fmt.Sprintf("Plugin names must be normalized. Replace %q with %q to fix this error.", name, normalizedPlugin),
|
||||
Subject: &declrange,
|
||||
})
|
||||
}
|
||||
return diags
|
||||
}
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user