Compare commits
3 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| 77137ef541 | |||
| 703b17d47e | |||
| 32cc0fb222 |
@@ -19,7 +19,7 @@ can quickly merge or address your contributions.
|
||||
already fixed the bug you're experiencing.
|
||||
|
||||
- Run the command with debug output with the environment variable `PACKER_LOG`.
|
||||
For example: `PACKER_LOG=1 packer build template.pkr.hcl`. Take the _entire_
|
||||
For example: `PACKER_LOG=1 packer build template.json`. Take the _entire_
|
||||
output and create a [gist](https://gist.github.com) for linking to in your
|
||||
issue. Packer should strip sensitive keys from the output, but take a look
|
||||
through just in case.
|
||||
@@ -91,7 +91,7 @@ The instructions below are for go 1.7. or later.
|
||||
4. After running building Packer successfully, use
|
||||
`$GOPATH/src/github.com/hashicorp/packer/bin/packer` to build a machine and
|
||||
verify your changes work. For instance:
|
||||
`$GOPATH/src/github.com/hashicorp/packer/bin/packer build template.pkr.hcl`.
|
||||
`$GOPATH/src/github.com/hashicorp/packer/bin/packer build template.json`.
|
||||
|
||||
5. If everything works well and the tests pass, run `go fmt` on your code before
|
||||
submitting a pull-request.
|
||||
|
||||
@@ -1,37 +1,14 @@
|
||||
on:
|
||||
pull_request:
|
||||
paths:
|
||||
- 'website/**'
|
||||
|
||||
name: Check markdown links on modified website files
|
||||
on: [pull_request]
|
||||
name: Check Markdown links for modified 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:
|
||||
- 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
|
||||
- uses: actions/checkout@master
|
||||
- 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,9 +6,6 @@ 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:
|
||||
|
||||
+3
-87
@@ -1,99 +1,15 @@
|
||||
## 1.7.1 (Upcoming)
|
||||
|
||||
### FEATURES
|
||||
### IMPROVEMENTS
|
||||
* core: Change template parsing error to include warning about file extensions.
|
||||
[GH-10652]
|
||||
|
||||
### BUG FIXES
|
||||
* builder/azure: Don't overwrite subscription id if unset. [GH-10659]
|
||||
* builder/oracle-oci: Update Oracle Go SDK to fix issue with reading key file.
|
||||
[GH-10560]
|
||||
* core/hcl2_upgrade: Make hcl2_upgrade command correctly translate
|
||||
pause_before. [GH-10654]
|
||||
* core: Templates previously could not interpolate the environment variable
|
||||
PACKER_LOG_PATH. [GH-10660]
|
||||
|
||||
## 1.7.0 (February 17, 2021)
|
||||
|
||||
### FEATURES
|
||||
* **New Command** (HCL only) `packer init` command will download plugins defined
|
||||
in a new `required_plugins` block [GH-10304] [GH-10633].
|
||||
* **New Plugin Type** Data sources can be implemented (blog post forthcoming).
|
||||
[GH-10440]
|
||||
* **New Plugin** Aws Secrets Manager data source [GH-10505] [GH-10467]
|
||||
|
||||
### BACKWARDS INCOMPATIBILITIES
|
||||
* core: The API that the Packer core uses to communicate with community plugins
|
||||
has changed; maintainers of community plugins will need to upgrade their
|
||||
plugins in order to make them compatible with v1.7.0. An upgrade guide will
|
||||
be available on our guides page https://www.packer.io/guides.
|
||||
|
||||
### IMPROVEMENTS
|
||||
* builder/amazon: Add `skip_create_ami` option for testing and situations where
|
||||
artifact is not the ami. [GH-10531]
|
||||
* builder/amazon: Add IMDSv2 support for AWS EBS builder. [GH-10546]
|
||||
* builder/amazon: Add resource tags in the launch template used to request spot
|
||||
instances. [GH-10456]
|
||||
* builder/openstack: Add `skip_create_image` option for testing and situations
|
||||
where artifact is not the image. [GH-10496]
|
||||
* builder/oracle-oci: Add retry strategies to oci calls [GH-10591]
|
||||
* core/fmt: The `packer fmt` can now read from stdin. [GH-10500]
|
||||
* core/hcl: Add regex and regexall hcl2 template functions. [GH-10601]
|
||||
* core/hcl: Templates now support "sensitive" locals. [GH-10509]
|
||||
* core/hcl: Templates now support error-cleanup-provisioner. [GH-10604]
|
||||
* hcl2_upgrade: Command now comes with a flag so you can control whether output
|
||||
templates are annotated with helpful comments. [GH-10619]
|
||||
* hcl2_upgrade: Command now gracefully handles options with template engine
|
||||
interpolations. [GH-10625]
|
||||
* hcl2_upgrade: Command will convert amazon filters to use the ami data source.
|
||||
[GH-10491]
|
||||
|
||||
### BUG FIXES
|
||||
* amazon/ebssurrogate: Apply snapshot tags at same time as when taking
|
||||
snapshot. [GH-10150]
|
||||
* builder/amazon: Fix bug where validation fails if optional iops value is
|
||||
unset. [GH-10518]
|
||||
* builder/amazon: Wrap API call to get filtered image in a retry. [GH-10610]
|
||||
* builder/bsusurrogate: override bsu when omi root device is set. [GH-10490]
|
||||
* builder/google: Fix bug where Packer would fail when run by users who do not
|
||||
have permission to access the metadata, even though the metadata is not
|
||||
necessary to the run. [GH-10458]
|
||||
* builder/profitbricks: Profitbricks builder could not connect using SSH
|
||||
communicator. [GH-10549]
|
||||
* builder/proxmox: Ensure ISOs in additional_iso_files are mounted during VM
|
||||
creation. [GH-10586]
|
||||
* builder/proxmox: Improve cloud init error logging for proxmox builder.
|
||||
[GH-10499]
|
||||
* builder/qemu: Fix bug where vnc_min_port set to value greater then 5900 could
|
||||
prevent Packer from connecting to QEMU. [GH-10450] [GH-10451]
|
||||
* builder/qemu: Fix regression with cd indexing when disk_interface is `ide`.
|
||||
[GH-10519]
|
||||
* builder/vmware-esx: Skip credential validation, which requires ovftool to be
|
||||
installed, if we are not exporting an image. [GH-10520]
|
||||
* builder/yandex: Fix cloud-init config for ubuntu 20.04. [GH-10522]
|
||||
* builder/yandex: Fix incorrect access to `instance_id`. [GH-10522]
|
||||
* core/hcl: Fix bug where []uint8 types could not be passed to plugins.
|
||||
* core/hcl: fix bug where HCL core could not handle passing []uint8 to plugins.
|
||||
[GH-10516]
|
||||
* core/hcl: Fix force flag for hcl2 provisioners and post-processors.
|
||||
[GH-10571]
|
||||
* post-processor/vsphere: Fix regression where Packer would not check the exit
|
||||
status after streaming UI from the ovftool command. [GH-10468]
|
||||
* post-processor/yandex-export: Changed dhclient command and supported
|
||||
configuring disk for exportupdate-dump-method. Also added support for
|
||||
`file` builder. [GH-10488]
|
||||
## 1.7.0 (Upcoming)
|
||||
|
||||
## 1.6.6 (December 16, 2020)
|
||||
|
||||
### FEATURES
|
||||
### FEATURES:
|
||||
* **New command** `fmt` allows users to format existing HCL2 configuration
|
||||
files into a canonical style. Please see [fmt command
|
||||
docs](https://packer.io/docs/commands/fmt) for more details. [GH-10225]
|
||||
[GH-10377]
|
||||
* **New function** `env` allows users to set the default value of a variable to
|
||||
the value of an environment variable. Please see [env function
|
||||
docs](https://www.packer.io/docs/templates/hcl_templates/functions/contextual/env) for
|
||||
docs](https://www.packer.io/docs/templates/hcl_templates/functions/contextual/env") for
|
||||
more details. [GH-10240]
|
||||
* **Future Scaffolding** This release contains a large number of no-op
|
||||
refactoring changes. The Packer team at HashiCorp is preparing to split the
|
||||
|
||||
+2
-2
@@ -37,8 +37,8 @@
|
||||
/builder/oracle/ @prydie @owainlewis
|
||||
/website/pages/docs/builders/oracle* @prydie @owainlewis
|
||||
|
||||
/builder/profitbricks/ @LiviusP @mflorin
|
||||
/website/pages/docs/builders/profitbricks* @LiviusP @mflorin
|
||||
/builder/profitbricks/ @jasmingacic
|
||||
/website/pages/docs/builders/profitbricks* @jasmingacic
|
||||
|
||||
/builder/triton/ @sean-
|
||||
/website/pages/docs/builders/triton* @sean-
|
||||
|
||||
@@ -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/legacy_json_templates/engine), see [Build template
|
||||
// a [template engine](/docs/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
|
||||
|
||||
@@ -174,16 +174,6 @@ type AccessConfig struct {
|
||||
// credential types) and GetFederationToken (for federation\_token
|
||||
// credential types) for more details.
|
||||
//
|
||||
// HCL2 example:
|
||||
//
|
||||
// ```hcl
|
||||
// vault_aws_engine {
|
||||
// name = "myrole"
|
||||
// role_arn = "myarn"
|
||||
// ttl = "3600s"
|
||||
// }
|
||||
// ```
|
||||
//
|
||||
// JSON example:
|
||||
//
|
||||
// ```json
|
||||
@@ -195,6 +185,16 @@ type AccessConfig struct {
|
||||
// }
|
||||
// }
|
||||
// ```
|
||||
//
|
||||
// HCL2 example:
|
||||
//
|
||||
// ```hcl
|
||||
// vault_aws_engine {
|
||||
// name = "myrole"
|
||||
// role_arn = "myarn"
|
||||
// ttl = "3600s"
|
||||
// }
|
||||
// ```
|
||||
VaultAWSEngine VaultAWSEngineOptions `mapstructure:"vault_aws_engine" required:"false"`
|
||||
// [Polling configuration](#polling-configuration) for the AWS waiter. Configures the waiter that checks
|
||||
// resource state.
|
||||
|
||||
@@ -5,10 +5,20 @@ import (
|
||||
|
||||
"github.com/aws/aws-sdk-go/aws"
|
||||
"github.com/aws/aws-sdk-go/aws/session"
|
||||
"github.com/aws/aws-sdk-go/service/ec2/ec2iface"
|
||||
)
|
||||
|
||||
func testAccessConfig() *AccessConfig {
|
||||
return &AccessConfig{
|
||||
getEC2Connection: func() ec2iface.EC2API {
|
||||
return &mockEC2Client{}
|
||||
},
|
||||
PollingConfig: new(AWSPollingConfig),
|
||||
}
|
||||
}
|
||||
|
||||
func TestAccessConfigPrepare_Region(t *testing.T) {
|
||||
c := FakeAccessConfig()
|
||||
c := testAccessConfig()
|
||||
|
||||
c.RawRegion = "us-east-12"
|
||||
err := c.ValidateRegion(c.RawRegion)
|
||||
@@ -30,7 +40,7 @@ func TestAccessConfigPrepare_Region(t *testing.T) {
|
||||
}
|
||||
|
||||
func TestAccessConfigPrepare_RegionRestricted(t *testing.T) {
|
||||
c := FakeAccessConfig()
|
||||
c := testAccessConfig()
|
||||
|
||||
// Create a Session with a custom region
|
||||
c.session = session.Must(session.NewSession(&aws.Config{
|
||||
|
||||
@@ -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/legacy_json_templates/engine) for more info).
|
||||
// engine](/docs/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/legacy_json_templates/engine), see [Build template
|
||||
// [template engine](/docs/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/legacy_json_templates/engine), see [Build template
|
||||
// engine](/docs/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
|
||||
@@ -135,8 +135,26 @@ type AMIConfig struct {
|
||||
// the intermediary AMI into any regions provided in `ami_regions`, then
|
||||
// delete the intermediary AMI. Default `false`.
|
||||
AMISkipBuildRegion bool `mapstructure:"skip_save_build_region"`
|
||||
|
||||
SnapshotConfig `mapstructure:",squash"`
|
||||
// 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
|
||||
// data](#build-template-data) for more information.
|
||||
SnapshotTags map[string]string `mapstructure:"snapshot_tags" required:"false"`
|
||||
// Same as [`snapshot_tags`](#snapshot_tags) but defined as a singular
|
||||
// repeatable block containing a `key` and a `value` field. In HCL2 mode the
|
||||
// [`dynamic_block`](/docs/templates/hcl_templates/expressions#dynamic-blocks)
|
||||
// will allow you to create those programatically.
|
||||
SnapshotTag config.KeyValues `mapstructure:"snapshot_tag" required:"false"`
|
||||
// A list of account IDs that have
|
||||
// access to create volumes from the snapshot(s). By default no additional
|
||||
// users other than the user creating the AMI has permissions to create
|
||||
// volumes from the backing snapshot(s).
|
||||
SnapshotUsers []string `mapstructure:"snapshot_users" required:"false"`
|
||||
// A list of groups that have access to
|
||||
// create volumes from the snapshot(s). By default no groups have permission
|
||||
// to create volumes from the snapshot(s). all will make the snapshot
|
||||
// publicly accessible.
|
||||
SnapshotGroups []string `mapstructure:"snapshot_groups" required:"false"`
|
||||
}
|
||||
|
||||
func stringInSlice(s []string, searchstr string) bool {
|
||||
|
||||
@@ -7,6 +7,7 @@ import (
|
||||
|
||||
"github.com/aws/aws-sdk-go/aws"
|
||||
"github.com/aws/aws-sdk-go/service/ec2"
|
||||
"github.com/aws/aws-sdk-go/service/ec2/ec2iface"
|
||||
"github.com/hashicorp/packer-plugin-sdk/template/config"
|
||||
)
|
||||
|
||||
@@ -17,14 +18,14 @@ func testAMIConfig() *AMIConfig {
|
||||
}
|
||||
|
||||
func getFakeAccessConfig(region string) *AccessConfig {
|
||||
c := FakeAccessConfig()
|
||||
c := testAccessConfig()
|
||||
c.RawRegion = region
|
||||
return c
|
||||
}
|
||||
|
||||
func TestAMIConfigPrepare_name(t *testing.T) {
|
||||
c := testAMIConfig()
|
||||
accessConf := FakeAccessConfig()
|
||||
accessConf := testAccessConfig()
|
||||
if err := c.Prepare(accessConf, nil); err != nil {
|
||||
t.Fatalf("shouldn't have err: %s", err)
|
||||
}
|
||||
@@ -35,6 +36,10 @@ func TestAMIConfigPrepare_name(t *testing.T) {
|
||||
}
|
||||
}
|
||||
|
||||
type mockEC2Client struct {
|
||||
ec2iface.EC2API
|
||||
}
|
||||
|
||||
func (m *mockEC2Client) DescribeRegions(*ec2.DescribeRegionsInput) (*ec2.DescribeRegionsOutput, error) {
|
||||
return &ec2.DescribeRegionsOutput{
|
||||
Regions: []*ec2.Region{
|
||||
@@ -51,7 +56,7 @@ func TestAMIConfigPrepare_regions(t *testing.T) {
|
||||
|
||||
var errs []error
|
||||
var err error
|
||||
accessConf := FakeAccessConfig()
|
||||
accessConf := testAccessConfig()
|
||||
mockConn := &mockEC2Client{}
|
||||
if errs = c.prepareRegions(accessConf); len(errs) > 0 {
|
||||
t.Fatalf("shouldn't have err: %#v", errs)
|
||||
@@ -158,7 +163,7 @@ func TestAMIConfigPrepare_Share_EncryptedBoot(t *testing.T) {
|
||||
c.AMIUsers = []string{"testAccountID"}
|
||||
c.AMIEncryptBootVolume = config.TriTrue
|
||||
|
||||
accessConf := FakeAccessConfig()
|
||||
accessConf := testAccessConfig()
|
||||
|
||||
c.AMIKmsKeyId = ""
|
||||
if err := c.Prepare(accessConf, nil); err == nil {
|
||||
@@ -174,7 +179,7 @@ func TestAMIConfigPrepare_ValidateKmsKey(t *testing.T) {
|
||||
c := testAMIConfig()
|
||||
c.AMIEncryptBootVolume = config.TriTrue
|
||||
|
||||
accessConf := FakeAccessConfig()
|
||||
accessConf := testAccessConfig()
|
||||
|
||||
validCases := []string{
|
||||
"abcd1234-e567-890f-a12b-a123b4cd56ef",
|
||||
@@ -210,7 +215,7 @@ func TestAMIConfigPrepare_ValidateKmsKey(t *testing.T) {
|
||||
func TestAMINameValidation(t *testing.T) {
|
||||
c := testAMIConfig()
|
||||
|
||||
accessConf := FakeAccessConfig()
|
||||
accessConf := testAccessConfig()
|
||||
|
||||
c.AMIName = "aa"
|
||||
if err := c.Prepare(accessConf, nil); err == nil {
|
||||
|
||||
@@ -51,10 +51,7 @@ func (d *AmiFilterOptions) GetFilteredImage(params *ec2.DescribeImagesInput, ec2
|
||||
}
|
||||
|
||||
log.Printf("Using AMI Filters %v", params)
|
||||
req, imageResp := ec2conn.DescribeImagesRequest(params)
|
||||
req.RetryCount = 11
|
||||
|
||||
err := req.Send()
|
||||
imageResp, err := ec2conn.DescribeImages(params)
|
||||
if err != nil {
|
||||
err := fmt.Errorf("Error querying AMI: %s", err)
|
||||
return nil, err
|
||||
|
||||
@@ -30,17 +30,8 @@ const (
|
||||
// The following mapping will tell Packer to encrypt the root volume of the
|
||||
// build instance at launch using a specific non-default kms key:
|
||||
//
|
||||
// HCL2 example:
|
||||
//
|
||||
// ```hcl
|
||||
// launch_block_device_mappings {
|
||||
// device_name = "/dev/sda1"
|
||||
// encrypted = true
|
||||
// kms_key_id = "1a2b3c4d-5e6f-1a2b-3c4d-5e6f1a2b3c4d"
|
||||
// }
|
||||
// ```
|
||||
//
|
||||
// JSON example:
|
||||
//
|
||||
// ```json
|
||||
// launch_block_device_mappings: [
|
||||
// {
|
||||
@@ -51,6 +42,16 @@ const (
|
||||
// ]
|
||||
// ```
|
||||
//
|
||||
// HCL2 example:
|
||||
//
|
||||
// ```hcl
|
||||
// launch_block_device_mappings {
|
||||
// device_name = "/dev/sda1"
|
||||
// encrypted = true
|
||||
// kms_key_id = "1a2b3c4d-5e6f-1a2b-3c4d-5e6f1a2b3c4d"
|
||||
// }
|
||||
// ```
|
||||
//
|
||||
// Please note that the kms_key_id option in this example exists for
|
||||
// launch_block_device_mappings but not ami_block_device_mappings.
|
||||
//
|
||||
@@ -75,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"`
|
||||
@@ -85,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"`
|
||||
@@ -149,12 +150,12 @@ func (blockDevice BlockDevice) BuildEC2BlockDeviceMapping() *ec2.BlockDeviceMapp
|
||||
|
||||
switch blockDevice.VolumeType {
|
||||
case "io1", "io2", "gp3":
|
||||
ebsBlockDevice.Iops = blockDevice.IOPS
|
||||
ebsBlockDevice.Iops = aws.Int64(blockDevice.IOPS)
|
||||
}
|
||||
|
||||
// Throughput is only valid for gp3 types
|
||||
if blockDevice.VolumeType == "gp3" {
|
||||
ebsBlockDevice.Throughput = blockDevice.Throughput
|
||||
ebsBlockDevice.Throughput = aws.Int64(blockDevice.Throughput)
|
||||
}
|
||||
|
||||
// You cannot specify Encrypted if you specify a Snapshot ID
|
||||
@@ -190,28 +191,28 @@ func (b *BlockDevice) Prepare(ctx *interpolate.Context) error {
|
||||
}
|
||||
|
||||
if ratio, ok := iopsRatios[b.VolumeType]; b.VolumeSize != 0 && ok {
|
||||
if b.IOPS != nil && (*b.IOPS/b.VolumeSize > ratio) {
|
||||
if 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 != nil && (*b.IOPS < minIops || *b.IOPS > maxIops) {
|
||||
if 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 != nil && (*b.Throughput < minThroughput || *b.Throughput > maxThroughput) {
|
||||
if 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 != nil && (*b.IOPS < minIopsGp3 || *b.IOPS > maxIopsGp3) {
|
||||
if 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 != nil {
|
||||
} else if b.Throughput > 0 {
|
||||
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: aws.Int64(1000),
|
||||
IOPS: 1000,
|
||||
},
|
||||
|
||||
Result: &ec2.BlockDeviceMapping{
|
||||
@@ -73,7 +73,7 @@ func TestBlockDevice(t *testing.T) {
|
||||
VolumeType: "io2",
|
||||
VolumeSize: 8,
|
||||
DeleteOnTermination: true,
|
||||
IOPS: aws.Int64(1000),
|
||||
IOPS: 1000,
|
||||
},
|
||||
|
||||
Result: &ec2.BlockDeviceMapping{
|
||||
@@ -168,8 +168,8 @@ func TestBlockDevice(t *testing.T) {
|
||||
DeviceName: "/dev/sdb",
|
||||
VolumeType: "gp3",
|
||||
VolumeSize: 8,
|
||||
Throughput: aws.Int64(125),
|
||||
IOPS: aws.Int64(3000),
|
||||
Throughput: 125,
|
||||
IOPS: 3000,
|
||||
DeleteOnTermination: true,
|
||||
Encrypted: config.TriTrue,
|
||||
},
|
||||
@@ -219,7 +219,7 @@ func TestIOPSValidation(t *testing.T) {
|
||||
device: BlockDevice{
|
||||
DeviceName: "/dev/sdb",
|
||||
VolumeType: "io1",
|
||||
IOPS: aws.Int64(1000),
|
||||
IOPS: 1000,
|
||||
},
|
||||
ok: true,
|
||||
},
|
||||
@@ -227,7 +227,7 @@ func TestIOPSValidation(t *testing.T) {
|
||||
device: BlockDevice{
|
||||
DeviceName: "/dev/sdb",
|
||||
VolumeType: "io2",
|
||||
IOPS: aws.Int64(1000),
|
||||
IOPS: 1000,
|
||||
},
|
||||
ok: true,
|
||||
},
|
||||
@@ -237,7 +237,7 @@ func TestIOPSValidation(t *testing.T) {
|
||||
DeviceName: "/dev/sdb",
|
||||
VolumeType: "io1",
|
||||
VolumeSize: 50,
|
||||
IOPS: aws.Int64(1000),
|
||||
IOPS: 1000,
|
||||
},
|
||||
ok: true,
|
||||
},
|
||||
@@ -246,7 +246,7 @@ func TestIOPSValidation(t *testing.T) {
|
||||
DeviceName: "/dev/sdb",
|
||||
VolumeType: "io2",
|
||||
VolumeSize: 100,
|
||||
IOPS: aws.Int64(1000),
|
||||
IOPS: 1000,
|
||||
},
|
||||
ok: true,
|
||||
},
|
||||
@@ -256,7 +256,7 @@ func TestIOPSValidation(t *testing.T) {
|
||||
DeviceName: "/dev/sdb",
|
||||
VolumeType: "io1",
|
||||
VolumeSize: 10,
|
||||
IOPS: aws.Int64(2000),
|
||||
IOPS: 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: aws.Int64(30000),
|
||||
IOPS: 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: aws.Int64(99999),
|
||||
IOPS: 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: aws.Int64(10),
|
||||
IOPS: 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: aws.Int64(125),
|
||||
IOPS: aws.Int64(99999),
|
||||
Throughput: 125,
|
||||
IOPS: 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: aws.Int64(125),
|
||||
IOPS: aws.Int64(10),
|
||||
Throughput: 125,
|
||||
IOPS: 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: aws.Int64(125),
|
||||
IOPS: aws.Int64(3000),
|
||||
Throughput: 125,
|
||||
IOPS: 3000,
|
||||
},
|
||||
ok: true,
|
||||
},
|
||||
@@ -355,8 +355,8 @@ func TestThroughputValidation(t *testing.T) {
|
||||
device: BlockDevice{
|
||||
DeviceName: "/dev/sdb",
|
||||
VolumeType: "gp3",
|
||||
Throughput: aws.Int64(1000),
|
||||
IOPS: aws.Int64(3000),
|
||||
Throughput: 1000,
|
||||
IOPS: 3000,
|
||||
},
|
||||
ok: true,
|
||||
},
|
||||
@@ -365,8 +365,8 @@ func TestThroughputValidation(t *testing.T) {
|
||||
device: BlockDevice{
|
||||
DeviceName: "/dev/sdb",
|
||||
VolumeType: "gp3",
|
||||
Throughput: aws.Int64(1001),
|
||||
IOPS: aws.Int64(3000),
|
||||
Throughput: 1001,
|
||||
IOPS: 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: aws.Int64(124),
|
||||
IOPS: aws.Int64(3000),
|
||||
Throughput: 124,
|
||||
IOPS: 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,MetadataOptions
|
||||
//go:generate mapstructure-to-hcl2 -type AmiFilterOptions,SecurityGroupFilterOptions,SubnetFilterOptions,VpcFilterOptions,PolicyDocument,Statement
|
||||
|
||||
package common
|
||||
|
||||
@@ -44,20 +44,6 @@ 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 {
|
||||
@@ -132,21 +118,8 @@ type RunConfig struct {
|
||||
// Whether or not to check if the IAM instance profile exists. Defaults to false
|
||||
SkipProfileValidation bool `mapstructure:"skip_profile_validation" required:"false"`
|
||||
// Temporary IAM instance profile policy document
|
||||
// If IamInstanceProfile is specified it will be used instead.
|
||||
// If IamInstanceProfile is specified it will be used instead. Example:
|
||||
//
|
||||
// HCL2 example:
|
||||
// ```hcl
|
||||
//temporary_iam_instance_profile_policy_document {
|
||||
// Statement {
|
||||
// Action = ["logs:*"]
|
||||
// Effect = "Allow"
|
||||
// Resource = "*"
|
||||
// }
|
||||
// Version = "2012-10-17"
|
||||
//}
|
||||
// ```
|
||||
//
|
||||
// JSON example:
|
||||
// ```json
|
||||
//{
|
||||
// "Version": "2012-10-17",
|
||||
@@ -170,7 +143,17 @@ type RunConfig struct {
|
||||
// The EC2 instance type to use while building the
|
||||
// AMI, such as t2.small.
|
||||
InstanceType string `mapstructure:"instance_type" required:"true"`
|
||||
// Filters used to populate the `security_group_ids` field.
|
||||
// Filters used to populate the `security_group_ids` field. JSON Example:
|
||||
//
|
||||
// ```json
|
||||
// {
|
||||
// "security_group_filter": {
|
||||
// "filters": {
|
||||
// "tag:Class": "packer"
|
||||
// }
|
||||
// }
|
||||
// }
|
||||
// ```
|
||||
//
|
||||
// HCL2 Example:
|
||||
//
|
||||
@@ -182,17 +165,6 @@ type RunConfig struct {
|
||||
// }
|
||||
// ```
|
||||
//
|
||||
// JSON Example:
|
||||
// ```json
|
||||
// {
|
||||
// "security_group_filter": {
|
||||
// "filters": {
|
||||
// "tag:Class": "packer"
|
||||
// }
|
||||
// }
|
||||
// }
|
||||
// ```
|
||||
//
|
||||
// This selects the SG's with tag `Class` with the value `packer`.
|
||||
//
|
||||
// - `filters` (map of strings) - filters used to select a
|
||||
@@ -204,7 +176,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/legacy_json_templates/engine), see [Build template
|
||||
// engine](/docs/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
|
||||
@@ -224,27 +196,12 @@ 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.
|
||||
// AMI with a root volume snapshot that you have access to. Note: this is not
|
||||
// used when from_scratch is set to true.
|
||||
SourceAmi string `mapstructure:"source_ami" required:"true"`
|
||||
// Filters used to populate the `source_ami`
|
||||
// field.
|
||||
// field. JSON Example:
|
||||
//
|
||||
// HCL2 example:
|
||||
// ```hcl
|
||||
// source "amazon-ebs" "basic-example" {
|
||||
// source_ami_filter {
|
||||
// filters = {
|
||||
// virtualization-type = "hvm"
|
||||
// name = "ubuntu/images/\*ubuntu-xenial-16.04-amd64-server-\*"
|
||||
// root-device-type = "ebs"
|
||||
// }
|
||||
// owners = ["099720109477"]
|
||||
// most_recent = true
|
||||
// }
|
||||
// }
|
||||
// ```
|
||||
//
|
||||
// JSON Example:
|
||||
// ```json
|
||||
// "builders" [
|
||||
// {
|
||||
@@ -261,6 +218,21 @@ type RunConfig struct {
|
||||
// }
|
||||
// ]
|
||||
// ```
|
||||
// HCL2 example:
|
||||
//
|
||||
// ```hcl
|
||||
// source "amazon-ebs" "basic-example" {
|
||||
// source_ami_filter {
|
||||
// filters = {
|
||||
// virtualization-type = "hvm"
|
||||
// name = "ubuntu/images/\*ubuntu-xenial-16.04-amd64-server-\*"
|
||||
// root-device-type = "ebs"
|
||||
// }
|
||||
// owners = ["099720109477"]
|
||||
// most_recent = true
|
||||
// }
|
||||
// }
|
||||
// ```
|
||||
//
|
||||
// This selects the most recent Ubuntu 16.04 HVM EBS AMI from Canonical. NOTE:
|
||||
// This will fail unless *exactly* one AMI is returned. In the above example,
|
||||
@@ -328,22 +300,8 @@ type RunConfig struct {
|
||||
// will allow you to create those programatically.
|
||||
SpotTag config.KeyValues `mapstructure:"spot_tag" required:"false"`
|
||||
// Filters used to populate the `subnet_id` field.
|
||||
//
|
||||
// HCL2 example:
|
||||
//
|
||||
// ```hcl
|
||||
// source "amazon-ebs" "basic-example" {
|
||||
// subnet_filter {
|
||||
// filters = {
|
||||
// "tag:Class": "build"
|
||||
// }
|
||||
// most_free = true
|
||||
// random = false
|
||||
// }
|
||||
// }
|
||||
// ```
|
||||
//
|
||||
// JSON Example:
|
||||
//
|
||||
// ```json
|
||||
// "builders" [
|
||||
// {
|
||||
@@ -358,6 +316,19 @@ type RunConfig struct {
|
||||
// }
|
||||
// ]
|
||||
// ```
|
||||
// HCL2 example:
|
||||
//
|
||||
// ```hcl
|
||||
// source "amazon-ebs" "basic-example" {
|
||||
// subnet_filter {
|
||||
// filters = {
|
||||
// "tag:Class": "build"
|
||||
// }
|
||||
// most_free = true
|
||||
// random = false
|
||||
// }
|
||||
// }
|
||||
// ```
|
||||
//
|
||||
// This selects the Subnet with tag `Class` with the value `build`, which has
|
||||
// the most free IP addresses. NOTE: This will fail unless *exactly* one
|
||||
@@ -404,21 +375,8 @@ type RunConfig struct {
|
||||
// data when launching the instance.
|
||||
UserDataFile string `mapstructure:"user_data_file" required:"false"`
|
||||
// Filters used to populate the `vpc_id` field.
|
||||
//
|
||||
// HCL2 example:
|
||||
// ```hcl
|
||||
// source "amazon-ebs" "basic-example" {
|
||||
// vpc_filter {
|
||||
// filters = {
|
||||
// "tag:Class": "build",
|
||||
// "isDefault": "false",
|
||||
// "cidr": "/24"
|
||||
// }
|
||||
// }
|
||||
// }
|
||||
// ```
|
||||
//
|
||||
// JSON Example:
|
||||
//
|
||||
// ```json
|
||||
// "builders" [
|
||||
// {
|
||||
@@ -433,6 +391,19 @@ type RunConfig struct {
|
||||
// }
|
||||
// ]
|
||||
// ```
|
||||
// HCL2 example:
|
||||
//
|
||||
// ```hcl
|
||||
// source "amazon-ebs" "basic-example" {
|
||||
// vpc_filter {
|
||||
// filters = {
|
||||
// "tag:Class": "build",
|
||||
// "isDefault": "false",
|
||||
// "cidr": "/24"
|
||||
// }
|
||||
// }
|
||||
// }
|
||||
// ```
|
||||
//
|
||||
// This selects the VPC with tag `Class` with the value `build`, which is not
|
||||
// the default VPC, and have a IPv4 CIDR block of `/24`. NOTE: This will fail
|
||||
@@ -456,9 +427,6 @@ 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"`
|
||||
|
||||
@@ -519,33 +487,6 @@ 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,MetadataOptions"; DO NOT EDIT.
|
||||
// Code generated by "mapstructure-to-hcl2 -type AmiFilterOptions,SecurityGroupFilterOptions,SubnetFilterOptions,VpcFilterOptions,PolicyDocument,Statement"; DO NOT EDIT.
|
||||
|
||||
package common
|
||||
|
||||
@@ -35,33 +35,6 @@ 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 {
|
||||
|
||||
@@ -1,29 +0,0 @@
|
||||
//go:generate struct-markdown
|
||||
|
||||
package common
|
||||
|
||||
import "github.com/hashicorp/packer-plugin-sdk/template/config"
|
||||
|
||||
// SnapshotConfig is for common configuration related to creating AMIs.
|
||||
type SnapshotConfig struct {
|
||||
// Key/value pair tags to apply to snapshot. They will override AMI tags if
|
||||
// already applied to snapshot. This is a [template
|
||||
// engine](/docs/templates/legacy_json_templates/engine), see [Build template
|
||||
// data](#build-template-data) for more information.
|
||||
SnapshotTags map[string]string `mapstructure:"snapshot_tags" required:"false"`
|
||||
// Same as [`snapshot_tags`](#snapshot_tags) but defined as a singular
|
||||
// repeatable block containing a `key` and a `value` field. In HCL2 mode the
|
||||
// [`dynamic_block`](/docs/templates/hcl_templates/expressions#dynamic-blocks)
|
||||
// will allow you to create those programatically.
|
||||
SnapshotTag config.KeyValues `mapstructure:"snapshot_tag" required:"false"`
|
||||
// A list of account IDs that have
|
||||
// access to create volumes from the snapshot(s). By default no additional
|
||||
// users other than the user creating the AMI has permissions to create
|
||||
// volumes from the backing snapshot(s).
|
||||
SnapshotUsers []string `mapstructure:"snapshot_users" required:"false"`
|
||||
// A list of groups that have access to
|
||||
// create volumes from the snapshot(s). By default no groups have permission
|
||||
// to create volumes from the snapshot(s). all will make the snapshot
|
||||
// publicly accessible.
|
||||
SnapshotGroups []string `mapstructure:"snapshot_groups" required:"false"`
|
||||
}
|
||||
@@ -42,8 +42,17 @@ type StateChangeConf struct {
|
||||
|
||||
// Polling configuration for the AWS waiter. Configures the waiter for resources creation or actions like attaching
|
||||
// volumes or importing image.
|
||||
// Usage example:
|
||||
//
|
||||
// HCL2 example:
|
||||
// In JSON:
|
||||
// ```json
|
||||
// "aws_polling" : {
|
||||
// "delay_seconds": 30,
|
||||
// "max_attempts": 50
|
||||
// }
|
||||
// ```
|
||||
//
|
||||
// In HCL2:
|
||||
// ```hcl
|
||||
// aws_polling {
|
||||
// delay_seconds = 30
|
||||
@@ -51,13 +60,6 @@ type StateChangeConf struct {
|
||||
// }
|
||||
// ```
|
||||
//
|
||||
// JSON example:
|
||||
// ```json
|
||||
// "aws_polling" : {
|
||||
// "delay_seconds": 30,
|
||||
// "max_attempts": 50
|
||||
// }
|
||||
// ```
|
||||
type AWSPollingConfig struct {
|
||||
// Specifies the maximum number of attempts the waiter will check for resource state.
|
||||
// This value can also be set via the AWS_MAX_ATTEMPTS.
|
||||
@@ -149,22 +151,15 @@ func (w *AWSPollingConfig) WaitUntilVolumeAvailable(ctx aws.Context, conn *ec2.E
|
||||
return err
|
||||
}
|
||||
|
||||
func (w *AWSPollingConfig) WaitUntilSnapshotDone(ctx aws.Context, conn ec2iface.EC2API, snapshotID string) error {
|
||||
func (w *AWSPollingConfig) WaitUntilSnapshotDone(ctx aws.Context, conn *ec2.EC2, snapshotID string) error {
|
||||
snapInput := ec2.DescribeSnapshotsInput{
|
||||
SnapshotIds: []*string{&snapshotID},
|
||||
}
|
||||
|
||||
waitOpts := w.getWaiterOptions()
|
||||
if len(waitOpts) == 0 {
|
||||
// Bump this default to 30 minutes.
|
||||
// Large snapshots can take a long time for the copy to s3
|
||||
waitOpts = append(waitOpts, request.WithWaiterMaxAttempts(120))
|
||||
}
|
||||
|
||||
err := conn.WaitUntilSnapshotCompletedWithContext(
|
||||
ctx,
|
||||
&snapInput,
|
||||
waitOpts...)
|
||||
w.getWaiterOptions()...)
|
||||
return err
|
||||
}
|
||||
|
||||
|
||||
@@ -24,7 +24,6 @@ type StepAMIRegionCopy struct {
|
||||
|
||||
toDelete string
|
||||
getRegionConn func(*AccessConfig, string) (ec2iface.EC2API, error)
|
||||
AMISkipCreateImage bool
|
||||
AMISkipBuildRegion bool
|
||||
}
|
||||
|
||||
@@ -64,12 +63,6 @@ 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)
|
||||
|
||||
@@ -105,7 +105,7 @@ func TestStepAMIRegionCopy_duplicates(t *testing.T) {
|
||||
// ------------------------------------------------------------------------
|
||||
|
||||
stepAMIRegionCopy := StepAMIRegionCopy{
|
||||
AccessConfig: FakeAccessConfig(),
|
||||
AccessConfig: testAccessConfig(),
|
||||
Regions: []string{"us-east-1"},
|
||||
AMIKmsKeyId: "12345",
|
||||
// Original region key in regionkeyids is different than in amikmskeyid
|
||||
@@ -131,7 +131,7 @@ func TestStepAMIRegionCopy_duplicates(t *testing.T) {
|
||||
|
||||
// the ami is only copied once.
|
||||
stepAMIRegionCopy = StepAMIRegionCopy{
|
||||
AccessConfig: FakeAccessConfig(),
|
||||
AccessConfig: testAccessConfig(),
|
||||
Regions: []string{"us-east-1"},
|
||||
Name: "fake-ami-name",
|
||||
OriginalRegion: "us-east-1",
|
||||
@@ -152,7 +152,7 @@ func TestStepAMIRegionCopy_duplicates(t *testing.T) {
|
||||
|
||||
// the ami is only copied once.
|
||||
stepAMIRegionCopy = StepAMIRegionCopy{
|
||||
AccessConfig: FakeAccessConfig(),
|
||||
AccessConfig: testAccessConfig(),
|
||||
Regions: []string{"us-east-1"},
|
||||
EncryptBootVolume: config.TriFalse,
|
||||
Name: "fake-ami-name",
|
||||
@@ -174,7 +174,7 @@ func TestStepAMIRegionCopy_duplicates(t *testing.T) {
|
||||
// ------------------------------------------------------------------------
|
||||
|
||||
stepAMIRegionCopy = StepAMIRegionCopy{
|
||||
AccessConfig: FakeAccessConfig(),
|
||||
AccessConfig: testAccessConfig(),
|
||||
// Many duplicates for only 3 actual values
|
||||
Regions: []string{"us-east-1", "us-west-2", "us-west-2", "ap-east-1", "ap-east-1", "ap-east-1"},
|
||||
AMIKmsKeyId: "IlikePancakes",
|
||||
@@ -203,7 +203,7 @@ func TestStepAMIRegionCopy_duplicates(t *testing.T) {
|
||||
// ------------------------------------------------------------------------
|
||||
|
||||
stepAMIRegionCopy = StepAMIRegionCopy{
|
||||
AccessConfig: FakeAccessConfig(),
|
||||
AccessConfig: testAccessConfig(),
|
||||
// Many duplicates for only 3 actual values
|
||||
Regions: []string{"us-east-1", "us-west-2", "us-west-2", "ap-east-1", "ap-east-1", "ap-east-1"},
|
||||
Name: "fake-ami-name",
|
||||
@@ -223,7 +223,7 @@ func TestStepAMIRegionCopy_duplicates(t *testing.T) {
|
||||
func TestStepAmiRegionCopy_nil_encryption(t *testing.T) {
|
||||
// create step
|
||||
stepAMIRegionCopy := StepAMIRegionCopy{
|
||||
AccessConfig: FakeAccessConfig(),
|
||||
AccessConfig: testAccessConfig(),
|
||||
Regions: make([]string, 0),
|
||||
AMIKmsKeyId: "",
|
||||
RegionKeyIds: make(map[string]string),
|
||||
@@ -249,7 +249,7 @@ func TestStepAmiRegionCopy_nil_encryption(t *testing.T) {
|
||||
func TestStepAmiRegionCopy_true_encryption(t *testing.T) {
|
||||
// create step
|
||||
stepAMIRegionCopy := StepAMIRegionCopy{
|
||||
AccessConfig: FakeAccessConfig(),
|
||||
AccessConfig: testAccessConfig(),
|
||||
Regions: make([]string, 0),
|
||||
AMIKmsKeyId: "",
|
||||
RegionKeyIds: make(map[string]string),
|
||||
@@ -275,7 +275,7 @@ func TestStepAmiRegionCopy_true_encryption(t *testing.T) {
|
||||
func TestStepAmiRegionCopy_nil_intermediary(t *testing.T) {
|
||||
// create step
|
||||
stepAMIRegionCopy := StepAMIRegionCopy{
|
||||
AccessConfig: FakeAccessConfig(),
|
||||
AccessConfig: testAccessConfig(),
|
||||
Regions: make([]string, 0),
|
||||
AMIKmsKeyId: "",
|
||||
RegionKeyIds: make(map[string]string),
|
||||
@@ -303,7 +303,7 @@ func TestStepAmiRegionCopy_AMISkipBuildRegion(t *testing.T) {
|
||||
// ------------------------------------------------------------------------
|
||||
|
||||
stepAMIRegionCopy := StepAMIRegionCopy{
|
||||
AccessConfig: FakeAccessConfig(),
|
||||
AccessConfig: testAccessConfig(),
|
||||
Regions: []string{"us-west-1"},
|
||||
AMIKmsKeyId: "",
|
||||
RegionKeyIds: map[string]string{"us-west-1": "abcde"},
|
||||
@@ -329,7 +329,7 @@ func TestStepAmiRegionCopy_AMISkipBuildRegion(t *testing.T) {
|
||||
// skip build region is false.
|
||||
// ------------------------------------------------------------------------
|
||||
stepAMIRegionCopy = StepAMIRegionCopy{
|
||||
AccessConfig: FakeAccessConfig(),
|
||||
AccessConfig: testAccessConfig(),
|
||||
Regions: []string{"us-west-1"},
|
||||
AMIKmsKeyId: "",
|
||||
RegionKeyIds: make(map[string]string),
|
||||
@@ -354,7 +354,7 @@ func TestStepAmiRegionCopy_AMISkipBuildRegion(t *testing.T) {
|
||||
// skip build region is false, but encrypt is true
|
||||
// ------------------------------------------------------------------------
|
||||
stepAMIRegionCopy = StepAMIRegionCopy{
|
||||
AccessConfig: FakeAccessConfig(),
|
||||
AccessConfig: testAccessConfig(),
|
||||
Regions: []string{"us-west-1"},
|
||||
AMIKmsKeyId: "",
|
||||
RegionKeyIds: map[string]string{"us-west-1": "abcde"},
|
||||
@@ -380,7 +380,7 @@ func TestStepAmiRegionCopy_AMISkipBuildRegion(t *testing.T) {
|
||||
// skip build region is true, and encrypt is true
|
||||
// ------------------------------------------------------------------------
|
||||
stepAMIRegionCopy = StepAMIRegionCopy{
|
||||
AccessConfig: FakeAccessConfig(),
|
||||
AccessConfig: testAccessConfig(),
|
||||
Regions: []string{"us-west-1"},
|
||||
AMIKmsKeyId: "",
|
||||
RegionKeyIds: map[string]string{"us-west-1": "abcde"},
|
||||
|
||||
@@ -16,8 +16,6 @@ import (
|
||||
)
|
||||
|
||||
type StepCreateTags struct {
|
||||
AMISkipCreateImage bool
|
||||
|
||||
Tags map[string]string
|
||||
SnapshotTags map[string]string
|
||||
Ctx interpolate.Context
|
||||
@@ -27,12 +25,6 @@ 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,8 +14,6 @@ import (
|
||||
)
|
||||
|
||||
type StepModifyAMIAttributes struct {
|
||||
AMISkipCreateImage bool
|
||||
|
||||
Users []string
|
||||
Groups []string
|
||||
SnapshotUsers []string
|
||||
@@ -31,12 +29,6 @@ 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,9 +29,6 @@ type StepRunSourceInstance struct {
|
||||
EbsOptimized bool
|
||||
EnableT2Unlimited bool
|
||||
ExpectedRootDevice string
|
||||
HttpEndpoint string
|
||||
HttpTokens string
|
||||
HttpPutResponseHopLimit int64
|
||||
InstanceInitiatedShutdownBehavior string
|
||||
InstanceType string
|
||||
IsRestricted bool
|
||||
@@ -147,10 +144,6 @@ 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
|
||||
|
||||
@@ -258,7 +251,7 @@ func (s *StepRunSourceInstance) Run(ctx context.Context, state multistep.StateBa
|
||||
if resp, e := ec2conn.DescribeInstances(describeInstance); e == nil {
|
||||
if len(resp.Reservations) > 0 && len(resp.Reservations[0].Instances) > 0 {
|
||||
instance := resp.Reservations[0].Instances[0]
|
||||
if instance.StateTransitionReason != nil && instance.StateReason != nil && instance.StateReason.Message != nil {
|
||||
if instance.StateTransitionReason != nil && instance.StateReason.Message != nil {
|
||||
ui.Error(fmt.Sprintf("Instance state change details: %s: %s",
|
||||
*instance.StateTransitionReason, *instance.StateReason.Message))
|
||||
}
|
||||
|
||||
@@ -34,9 +34,6 @@ type StepRunSpotInstance struct {
|
||||
Comm *communicator.Config
|
||||
EbsOptimized bool
|
||||
ExpectedRootDevice string
|
||||
HttpEndpoint string
|
||||
HttpTokens string
|
||||
HttpPutResponseHopLimit int64
|
||||
InstanceInitiatedShutdownBehavior string
|
||||
InstanceType string
|
||||
Region string
|
||||
@@ -54,24 +51,6 @@ type StepRunSpotInstance struct {
|
||||
instanceId string
|
||||
}
|
||||
|
||||
// The EbsBlockDevice and LaunchTemplateEbsBlockDeviceRequest structs are
|
||||
// nearly identical except for the struct's name and one extra field in
|
||||
// EbsBlockDeviceResuest, which unfortunately means you can't just cast one
|
||||
// into the other. THANKS AMAZON.
|
||||
func castBlockDeviceToRequest(bd *ec2.EbsBlockDevice) *ec2.LaunchTemplateEbsBlockDeviceRequest {
|
||||
out := &ec2.LaunchTemplateEbsBlockDeviceRequest{
|
||||
DeleteOnTermination: bd.DeleteOnTermination,
|
||||
Encrypted: bd.Encrypted,
|
||||
Iops: bd.Iops,
|
||||
KmsKeyId: bd.KmsKeyId,
|
||||
SnapshotId: bd.SnapshotId,
|
||||
Throughput: bd.Throughput,
|
||||
VolumeSize: bd.VolumeSize,
|
||||
VolumeType: bd.VolumeType,
|
||||
}
|
||||
return out
|
||||
}
|
||||
|
||||
func (s *StepRunSpotInstance) CreateTemplateData(userData *string, az string,
|
||||
state multistep.StateBag, marketOptions *ec2.LaunchTemplateInstanceMarketOptionsRequest) *ec2.RequestLaunchTemplateData {
|
||||
blockDeviceMappings := s.LaunchMappings.BuildEC2BlockDeviceMappings()
|
||||
@@ -79,12 +58,15 @@ func (s *StepRunSpotInstance) CreateTemplateData(userData *string, az string,
|
||||
// LaunchTemplateBlockDeviceMappingRequest. These structs are identical,
|
||||
// except for the EBS field -- on one, that field contains a
|
||||
// LaunchTemplateEbsBlockDeviceRequest, and on the other, it contains an
|
||||
// EbsBlockDevice.
|
||||
// EbsBlockDevice. The EbsBlockDevice and
|
||||
// LaunchTemplateEbsBlockDeviceRequest structs are themselves
|
||||
// identical except for the struct's name, so you can cast one directly
|
||||
// into the other.
|
||||
var launchMappingRequests []*ec2.LaunchTemplateBlockDeviceMappingRequest
|
||||
for _, mapping := range blockDeviceMappings {
|
||||
launchRequest := &ec2.LaunchTemplateBlockDeviceMappingRequest{
|
||||
DeviceName: mapping.DeviceName,
|
||||
Ebs: castBlockDeviceToRequest(mapping.Ebs),
|
||||
Ebs: (*ec2.LaunchTemplateEbsBlockDeviceRequest)(mapping.Ebs),
|
||||
VirtualName: mapping.VirtualName,
|
||||
}
|
||||
launchMappingRequests = append(launchMappingRequests, launchRequest)
|
||||
@@ -145,10 +127,6 @@ 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 != "" {
|
||||
|
||||
@@ -1,25 +0,0 @@
|
||||
package common
|
||||
|
||||
import (
|
||||
"github.com/aws/aws-sdk-go/aws"
|
||||
"github.com/aws/aws-sdk-go/aws/session"
|
||||
"github.com/aws/aws-sdk-go/service/ec2/ec2iface"
|
||||
)
|
||||
|
||||
type mockEC2Client struct {
|
||||
ec2iface.EC2API
|
||||
}
|
||||
|
||||
func FakeAccessConfig() *AccessConfig {
|
||||
accessConfig := AccessConfig{
|
||||
getEC2Connection: func() ec2iface.EC2API {
|
||||
return &mockEC2Client{}
|
||||
},
|
||||
PollingConfig: new(AWSPollingConfig),
|
||||
}
|
||||
accessConfig.session = session.Must(session.NewSession(&aws.Config{
|
||||
Region: aws.String("us-west-1"),
|
||||
}))
|
||||
|
||||
return &accessConfig
|
||||
}
|
||||
@@ -34,9 +34,6 @@ 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
|
||||
@@ -56,7 +53,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/legacy_json_templates/engine), see [Build template
|
||||
// engine](/docs/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
|
||||
@@ -187,9 +184,6 @@ 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,
|
||||
@@ -214,9 +208,6 @@ 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(),
|
||||
@@ -328,7 +319,6 @@ 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,
|
||||
},
|
||||
@@ -340,25 +330,22 @@ 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{
|
||||
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,
|
||||
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{
|
||||
AMISkipCreateImage: b.config.AMISkipCreateImage,
|
||||
Tags: b.config.AMITags,
|
||||
SnapshotTags: b.config.SnapshotTags,
|
||||
Ctx: b.config.ctx,
|
||||
Tags: b.config.AMITags,
|
||||
SnapshotTags: b.config.SnapshotTags,
|
||||
Ctx: b.config.ctx,
|
||||
},
|
||||
}
|
||||
|
||||
|
||||
@@ -90,7 +90,6 @@ 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"`
|
||||
@@ -143,7 +142,6 @@ 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"`
|
||||
@@ -241,7 +239,6 @@ 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},
|
||||
@@ -294,7 +291,6 @@ 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,7 +19,6 @@ import (
|
||||
type stepCreateAMI struct {
|
||||
PollingConfig *awscommon.AWSPollingConfig
|
||||
image *ec2.Image
|
||||
AMISkipCreateImage bool
|
||||
AMISkipBuildRegion bool
|
||||
}
|
||||
|
||||
@@ -29,11 +28,6 @@ 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/legacy_json_templates/engine), see [Build template
|
||||
// engine](/docs/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,9 +207,6 @@ 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,
|
||||
@@ -233,9 +230,6 @@ 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,7 +113,6 @@ 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"`
|
||||
@@ -264,7 +263,6 @@ func (*FlatConfig) HCL2Spec() map[string]hcldec.Spec {
|
||||
"vpc_filter": &hcldec.BlockSpec{TypeName: "vpc_filter", Nested: hcldec.ObjectSpec((*common.FlatVpcFilterOptions)(nil).HCL2Spec())},
|
||||
"vpc_id": &hcldec.AttrSpec{Name: "vpc_id", Type: cty.String, Required: false},
|
||||
"windows_password_timeout": &hcldec.AttrSpec{Name: "windows_password_timeout", Type: cty.String, Required: false},
|
||||
"metadata_options": &hcldec.BlockSpec{TypeName: "metadata_options", Nested: hcldec.ObjectSpec((*common.FlatMetadataOptions)(nil).HCL2Spec())},
|
||||
"communicator": &hcldec.AttrSpec{Name: "communicator", Type: cty.String, Required: false},
|
||||
"pause_before_connecting": &hcldec.AttrSpec{Name: "pause_before_connecting", Type: cty.String, Required: false},
|
||||
"ssh_host": &hcldec.AttrSpec{Name: "ssh_host", Type: cty.String, Required: false},
|
||||
|
||||
@@ -13,15 +13,11 @@ import (
|
||||
// map of region to list of volume IDs
|
||||
type EbsVolumes map[string][]string
|
||||
|
||||
// map of region to list of snapshot IDs
|
||||
type EbsSnapshots map[string][]string
|
||||
|
||||
// Artifact is an artifact implementation that contains built AMIs.
|
||||
type Artifact struct {
|
||||
// A map of regions to EBS Volume IDs.
|
||||
Volumes EbsVolumes
|
||||
// A map of regions to EBS Snapshot IDs.
|
||||
Snapshots EbsSnapshots
|
||||
|
||||
// BuilderId is the unique ID for the builder that created this AMI
|
||||
BuilderIdValue string
|
||||
|
||||
@@ -44,21 +40,13 @@ func (*Artifact) Files() []string {
|
||||
|
||||
// returns a sorted list of region:ID pairs
|
||||
func (a *Artifact) idList() []string {
|
||||
|
||||
parts := make([]string, 0, len(a.Volumes)+len(a.Snapshots))
|
||||
|
||||
parts := make([]string, 0, len(a.Volumes))
|
||||
for region, volumeIDs := range a.Volumes {
|
||||
for _, volumeID := range volumeIDs {
|
||||
parts = append(parts, fmt.Sprintf("%s:%s", region, volumeID))
|
||||
}
|
||||
}
|
||||
|
||||
for region, snapshotIDs := range a.Snapshots {
|
||||
for _, snapshotID := range snapshotIDs {
|
||||
parts = append(parts, fmt.Sprintf("%s:%s", region, snapshotID))
|
||||
}
|
||||
}
|
||||
|
||||
sort.Strings(parts)
|
||||
return parts
|
||||
}
|
||||
|
||||
@@ -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/legacy_json_templates/engine), see
|
||||
// completes. This is a [template engine](/docs/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
|
||||
@@ -20,11 +20,6 @@ type BlockDevice struct {
|
||||
// [`dynamic_block`](/docs/templates/hcl_templates/expressions#dynamic-blocks)
|
||||
// will allow you to create those programatically.
|
||||
Tag config.KeyValues `mapstructure:"tag" required:"false"`
|
||||
|
||||
// Create a Snapshot of this Volume.
|
||||
SnapshotVolume bool `mapstructure:"snapshot_volume" required:"false"`
|
||||
|
||||
awscommon.SnapshotConfig `mapstructure:",squash"`
|
||||
}
|
||||
|
||||
type BlockDevices []BlockDevice
|
||||
@@ -43,7 +38,6 @@ func (bds BlockDevices) Prepare(ctx *interpolate.Context) (errs []error) {
|
||||
for _, block := range bds {
|
||||
|
||||
errs = append(errs, block.Tag.CopyOn(&block.Tags)...)
|
||||
errs = append(errs, block.SnapshotTag.CopyOn(&block.SnapshotTags)...)
|
||||
|
||||
if err := block.Prepare(ctx); err != nil {
|
||||
errs = append(errs, err)
|
||||
|
||||
@@ -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/legacy_json_templates/engine), see [Build template
|
||||
// engine](/docs/templates/engine), see [Build template
|
||||
// data](#build-template-data) for more information.
|
||||
//
|
||||
// Note: The tags specified here will be *temporarily* applied to volumes
|
||||
@@ -123,12 +123,16 @@ func (b *Builder) Prepare(raws ...interface{}) ([]string, []string, error) {
|
||||
// Accumulate any errors
|
||||
var errs *packersdk.MultiError
|
||||
var warns []string
|
||||
|
||||
errs = packersdk.MultiErrorAppend(errs, b.config.VolumeRunTag.CopyOn(&b.config.VolumeRunTags)...)
|
||||
errs = packersdk.MultiErrorAppend(errs, b.config.AccessConfig.Prepare()...)
|
||||
errs = packersdk.MultiErrorAppend(errs, b.config.RunConfig.Prepare(&b.config.ctx)...)
|
||||
errs = packersdk.MultiErrorAppend(errs, b.config.launchBlockDevices.Prepare(&b.config.ctx)...)
|
||||
errs = packersdk.MultiErrorAppend(errs, b.config.VolumeMappings.Prepare(&b.config.ctx)...)
|
||||
|
||||
for _, d := range b.config.VolumeMappings {
|
||||
if err := d.Prepare(&b.config.ctx); err != nil {
|
||||
errs = packersdk.MultiErrorAppend(errs, fmt.Errorf("AMIMapping: %s", err.Error()))
|
||||
}
|
||||
}
|
||||
|
||||
b.config.launchBlockDevices = b.config.VolumeMappings
|
||||
if err != nil {
|
||||
@@ -191,9 +195,6 @@ 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,
|
||||
@@ -217,9 +218,6 @@ 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(),
|
||||
@@ -314,12 +312,6 @@ func (b *Builder) Run(ctx context.Context, ui packersdk.Ui, hook packersdk.Hook)
|
||||
EnableAMISriovNetSupport: b.config.AMISriovNetSupport,
|
||||
EnableAMIENASupport: b.config.AMIENASupport,
|
||||
},
|
||||
&stepSnapshotEBSVolumes{
|
||||
PollingConfig: b.config.PollingConfig,
|
||||
VolumeMapping: b.config.VolumeMappings,
|
||||
AccessConfig: &b.config.AccessConfig,
|
||||
Ctx: b.config.ctx,
|
||||
},
|
||||
}
|
||||
|
||||
// Run!
|
||||
@@ -334,7 +326,6 @@ func (b *Builder) Run(ctx context.Context, ui packersdk.Ui, hook packersdk.Hook)
|
||||
// Build the artifact and return it
|
||||
artifact := &Artifact{
|
||||
Volumes: state.Get("ebsvolumes").(EbsVolumes),
|
||||
Snapshots: state.Get("ebssnapshots").(EbsSnapshots),
|
||||
BuilderIdValue: BuilderId,
|
||||
Conn: ec2conn,
|
||||
StateData: map[string]interface{}{"generated_data": state.Get("generated_data")},
|
||||
|
||||
@@ -25,11 +25,6 @@ type FlatBlockDevice struct {
|
||||
KmsKeyId *string `mapstructure:"kms_key_id" required:"false" cty:"kms_key_id" hcl:"kms_key_id"`
|
||||
Tags map[string]string `mapstructure:"tags" required:"false" cty:"tags" hcl:"tags"`
|
||||
Tag []config.FlatKeyValue `mapstructure:"tag" required:"false" cty:"tag" hcl:"tag"`
|
||||
SnapshotVolume *bool `mapstructure:"snapshot_volume" required:"false" cty:"snapshot_volume" hcl:"snapshot_volume"`
|
||||
SnapshotTags map[string]string `mapstructure:"snapshot_tags" required:"false" cty:"snapshot_tags" hcl:"snapshot_tags"`
|
||||
SnapshotTag []config.FlatKeyValue `mapstructure:"snapshot_tag" required:"false" cty:"snapshot_tag" hcl:"snapshot_tag"`
|
||||
SnapshotUsers []string `mapstructure:"snapshot_users" required:"false" cty:"snapshot_users" hcl:"snapshot_users"`
|
||||
SnapshotGroups []string `mapstructure:"snapshot_groups" required:"false" cty:"snapshot_groups" hcl:"snapshot_groups"`
|
||||
}
|
||||
|
||||
// FlatMapstructure returns a new FlatBlockDevice.
|
||||
@@ -57,11 +52,6 @@ func (*FlatBlockDevice) HCL2Spec() map[string]hcldec.Spec {
|
||||
"kms_key_id": &hcldec.AttrSpec{Name: "kms_key_id", Type: cty.String, Required: false},
|
||||
"tags": &hcldec.AttrSpec{Name: "tags", Type: cty.Map(cty.String), Required: false},
|
||||
"tag": &hcldec.BlockListSpec{TypeName: "tag", Nested: hcldec.ObjectSpec((*config.FlatKeyValue)(nil).HCL2Spec())},
|
||||
"snapshot_volume": &hcldec.AttrSpec{Name: "snapshot_volume", Type: cty.Bool, Required: false},
|
||||
"snapshot_tags": &hcldec.AttrSpec{Name: "snapshot_tags", Type: cty.Map(cty.String), Required: false},
|
||||
"snapshot_tag": &hcldec.BlockListSpec{TypeName: "snapshot_tag", Nested: hcldec.ObjectSpec((*config.FlatKeyValue)(nil).HCL2Spec())},
|
||||
"snapshot_users": &hcldec.AttrSpec{Name: "snapshot_users", Type: cty.List(cty.String), Required: false},
|
||||
"snapshot_groups": &hcldec.AttrSpec{Name: "snapshot_groups", Type: cty.List(cty.String), Required: false},
|
||||
}
|
||||
return s
|
||||
}
|
||||
@@ -125,7 +115,6 @@ 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"`
|
||||
@@ -253,7 +242,6 @@ func (*FlatConfig) HCL2Spec() map[string]hcldec.Spec {
|
||||
"vpc_filter": &hcldec.BlockSpec{TypeName: "vpc_filter", Nested: hcldec.ObjectSpec((*common.FlatVpcFilterOptions)(nil).HCL2Spec())},
|
||||
"vpc_id": &hcldec.AttrSpec{Name: "vpc_id", Type: cty.String, Required: false},
|
||||
"windows_password_timeout": &hcldec.AttrSpec{Name: "windows_password_timeout", Type: cty.String, Required: false},
|
||||
"metadata_options": &hcldec.BlockSpec{TypeName: "metadata_options", Nested: hcldec.ObjectSpec((*common.FlatMetadataOptions)(nil).HCL2Spec())},
|
||||
"communicator": &hcldec.AttrSpec{Name: "communicator", Type: cty.String, Required: false},
|
||||
"pause_before_connecting": &hcldec.AttrSpec{Name: "pause_before_connecting", Type: cty.String, Required: false},
|
||||
"ssh_host": &hcldec.AttrSpec{Name: "ssh_host", Type: cty.String, Required: false},
|
||||
|
||||
@@ -104,6 +104,9 @@ func TestBuilderPrepare_ReturnGeneratedData(t *testing.T) {
|
||||
if len(generatedData) == 0 {
|
||||
t.Fatalf("Generated data should not be empty")
|
||||
}
|
||||
if len(generatedData) == 0 {
|
||||
t.Fatalf("Generated data should not be empty")
|
||||
}
|
||||
if generatedData[0] != "SourceAMIName" {
|
||||
t.Fatalf("Generated data should contain SourceAMIName")
|
||||
}
|
||||
@@ -123,44 +126,3 @@ func TestBuilderPrepare_ReturnGeneratedData(t *testing.T) {
|
||||
t.Fatalf("Generated data should contain SourceAMIOwnerName")
|
||||
}
|
||||
}
|
||||
|
||||
func TestBuidler_ConfigBlockdevicemapping(t *testing.T) {
|
||||
var b Builder
|
||||
config := testConfig()
|
||||
|
||||
//Set some snapshot settings
|
||||
config["ebs_volumes"] = []map[string]interface{}{
|
||||
{
|
||||
"device_name": "/dev/xvdb",
|
||||
"volume_size": "32",
|
||||
"delete_on_termination": true,
|
||||
},
|
||||
{
|
||||
"device_name": "/dev/xvdc",
|
||||
"volume_size": "32",
|
||||
"delete_on_termination": true,
|
||||
"snapshot_tags": map[string]string{
|
||||
"Test_Tag": "tag_value",
|
||||
"another tag": "another value",
|
||||
},
|
||||
"snapshot_users": []string{
|
||||
"123", "456",
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
generatedData, warnings, err := b.Prepare(config)
|
||||
|
||||
if len(warnings) > 0 {
|
||||
t.Fatalf("bad: %#v", warnings)
|
||||
}
|
||||
if err != nil {
|
||||
t.Fatalf("should not have error: %s", err)
|
||||
}
|
||||
if len(generatedData) == 0 {
|
||||
t.Fatalf("Generated data should not be empty")
|
||||
}
|
||||
|
||||
t.Logf("Test gen %+v", b.config.VolumeMappings)
|
||||
|
||||
}
|
||||
|
||||
@@ -1,167 +0,0 @@
|
||||
package ebsvolume
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
|
||||
"github.com/aws/aws-sdk-go/aws"
|
||||
"github.com/aws/aws-sdk-go/service/ec2"
|
||||
"github.com/aws/aws-sdk-go/service/ec2/ec2iface"
|
||||
"github.com/hashicorp/packer-plugin-sdk/multistep"
|
||||
"github.com/hashicorp/packer-plugin-sdk/packer"
|
||||
"github.com/hashicorp/packer-plugin-sdk/template/interpolate"
|
||||
awscommon "github.com/hashicorp/packer/builder/amazon/common"
|
||||
)
|
||||
|
||||
type stepSnapshotEBSVolumes struct {
|
||||
PollingConfig *awscommon.AWSPollingConfig
|
||||
AccessConfig *awscommon.AccessConfig
|
||||
VolumeMapping []BlockDevice
|
||||
//Map of SnapshotID: BlockDevice, Where *BlockDevice is in VolumeMapping
|
||||
snapshotMap map[string]*BlockDevice
|
||||
Ctx interpolate.Context
|
||||
}
|
||||
|
||||
func (s *stepSnapshotEBSVolumes) Run(ctx context.Context, state multistep.StateBag) multistep.StepAction {
|
||||
ec2conn := state.Get("ec2").(ec2iface.EC2API)
|
||||
instance := state.Get("instance").(*ec2.Instance)
|
||||
ui := state.Get("ui").(packer.Ui)
|
||||
|
||||
s.snapshotMap = make(map[string]*BlockDevice)
|
||||
|
||||
for _, instanceBlockDevice := range instance.BlockDeviceMappings {
|
||||
for _, configVolumeMapping := range s.VolumeMapping {
|
||||
//Find the config entry for the instance blockDevice
|
||||
if configVolumeMapping.DeviceName == *instanceBlockDevice.DeviceName {
|
||||
//Skip Volumes that are not set to create snapshot
|
||||
if configVolumeMapping.SnapshotVolume != true {
|
||||
continue
|
||||
}
|
||||
|
||||
ui.Message(fmt.Sprintf("Compiling list of tags to apply to snapshot from Volume %s...", *instanceBlockDevice.DeviceName))
|
||||
tags, err := awscommon.TagMap(configVolumeMapping.SnapshotTags).EC2Tags(s.Ctx, s.AccessConfig.SessionRegion(), state)
|
||||
if err != nil {
|
||||
err := fmt.Errorf("Error generating tags for snapshot %s: %s", *instanceBlockDevice.DeviceName, err)
|
||||
state.Put("error", err)
|
||||
ui.Error(err.Error())
|
||||
return multistep.ActionHalt
|
||||
}
|
||||
tags.Report(ui)
|
||||
|
||||
tagSpec := &ec2.TagSpecification{
|
||||
ResourceType: aws.String("snapshot"),
|
||||
Tags: tags,
|
||||
}
|
||||
|
||||
input := &ec2.CreateSnapshotInput{
|
||||
VolumeId: aws.String(*instanceBlockDevice.Ebs.VolumeId),
|
||||
TagSpecifications: []*ec2.TagSpecification{tagSpec},
|
||||
}
|
||||
|
||||
//Dont try to set an empty tag spec
|
||||
if len(tags) == 0 {
|
||||
input.TagSpecifications = nil
|
||||
}
|
||||
|
||||
ui.Message(fmt.Sprintf("Requesting snapshot of volume: %s...", *instanceBlockDevice.Ebs.VolumeId))
|
||||
snapshot, err := ec2conn.CreateSnapshot(input)
|
||||
if err != nil || snapshot == nil {
|
||||
err := fmt.Errorf("Error generating snapsot for volume %s: %s", *instanceBlockDevice.Ebs.VolumeId, err)
|
||||
state.Put("error", err)
|
||||
ui.Error(err.Error())
|
||||
return multistep.ActionHalt
|
||||
}
|
||||
ui.Message(fmt.Sprintf("Requested Snapshot of Volume %s: %s", *instanceBlockDevice.Ebs.VolumeId, *snapshot.SnapshotId))
|
||||
s.snapshotMap[*snapshot.SnapshotId] = &configVolumeMapping
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
ui.Say("Waiting for Snapshots to become ready...")
|
||||
for snapID := range s.snapshotMap {
|
||||
ui.Message(fmt.Sprintf("Waiting for %s to be ready.", snapID))
|
||||
err := s.PollingConfig.WaitUntilSnapshotDone(ctx, ec2conn, snapID)
|
||||
if err != nil {
|
||||
err = fmt.Errorf("Error waiting for snapsot to become ready %s", err)
|
||||
state.Put("error", err)
|
||||
ui.Error(err.Error())
|
||||
ui.Message("Failed to wait")
|
||||
return multistep.ActionHalt
|
||||
}
|
||||
ui.Message(fmt.Sprintf("Snapshot Ready: %s", snapID))
|
||||
}
|
||||
|
||||
//Attach User and Group permissions to snapshots
|
||||
ui.Say("Setting User/Group Permissions for Snapshots...")
|
||||
for snapID, bd := range s.snapshotMap {
|
||||
snapshotOptions := make(map[string]*ec2.ModifySnapshotAttributeInput)
|
||||
|
||||
if len(bd.SnapshotGroups) > 0 {
|
||||
groups := make([]*string, len(bd.SnapshotGroups))
|
||||
addsSnapshot := make([]*ec2.CreateVolumePermission, len(bd.SnapshotGroups))
|
||||
|
||||
addSnapshotGroups := &ec2.ModifySnapshotAttributeInput{
|
||||
CreateVolumePermission: &ec2.CreateVolumePermissionModifications{},
|
||||
}
|
||||
|
||||
for i, g := range bd.SnapshotGroups {
|
||||
groups[i] = aws.String(g)
|
||||
addsSnapshot[i] = &ec2.CreateVolumePermission{
|
||||
Group: aws.String(g),
|
||||
}
|
||||
}
|
||||
|
||||
addSnapshotGroups.GroupNames = groups
|
||||
addSnapshotGroups.CreateVolumePermission.Add = addsSnapshot
|
||||
snapshotOptions["groups"] = addSnapshotGroups
|
||||
|
||||
}
|
||||
|
||||
if len(bd.SnapshotUsers) > 0 {
|
||||
users := make([]*string, len(bd.SnapshotUsers))
|
||||
addsSnapshot := make([]*ec2.CreateVolumePermission, len(bd.SnapshotUsers))
|
||||
for i, u := range bd.SnapshotUsers {
|
||||
users[i] = aws.String(u)
|
||||
addsSnapshot[i] = &ec2.CreateVolumePermission{UserId: aws.String(u)}
|
||||
}
|
||||
|
||||
snapshotOptions["users"] = &ec2.ModifySnapshotAttributeInput{
|
||||
UserIds: users,
|
||||
CreateVolumePermission: &ec2.CreateVolumePermissionModifications{
|
||||
Add: addsSnapshot,
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
//Todo: Copy to other regions and repeat this block in all regions.
|
||||
for name, input := range snapshotOptions {
|
||||
ui.Message(fmt.Sprintf("Modifying: %s", name))
|
||||
input.SnapshotId = &snapID
|
||||
_, err := ec2conn.ModifySnapshotAttribute(input)
|
||||
if err != nil {
|
||||
err := fmt.Errorf("Error modify snapshot attributes: %s", err)
|
||||
state.Put("error", err)
|
||||
ui.Error(err.Error())
|
||||
return multistep.ActionHalt
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
//Record all snapshots in current Region.
|
||||
snapshots := make(EbsSnapshots)
|
||||
currentregion := s.AccessConfig.SessionRegion()
|
||||
|
||||
for snapID := range s.snapshotMap {
|
||||
snapshots[currentregion] = append(
|
||||
snapshots[currentregion],
|
||||
snapID)
|
||||
}
|
||||
//Records artifacts
|
||||
state.Put("ebssnapshots", snapshots)
|
||||
|
||||
return multistep.ActionContinue
|
||||
}
|
||||
|
||||
func (s *stepSnapshotEBSVolumes) Cleanup(state multistep.StateBag) {
|
||||
// No cleanup...
|
||||
}
|
||||
@@ -1,170 +0,0 @@
|
||||
package ebsvolume
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"context"
|
||||
"fmt"
|
||||
"testing"
|
||||
|
||||
"github.com/aws/aws-sdk-go/aws"
|
||||
"github.com/aws/aws-sdk-go/aws/request"
|
||||
|
||||
//"github.com/aws/aws-sdk-go/service/ec2"
|
||||
"github.com/aws/aws-sdk-go/service/ec2"
|
||||
"github.com/aws/aws-sdk-go/service/ec2/ec2iface"
|
||||
"github.com/hashicorp/packer-plugin-sdk/multistep"
|
||||
"github.com/hashicorp/packer-plugin-sdk/packer"
|
||||
"github.com/hashicorp/packer/builder/amazon/common"
|
||||
)
|
||||
|
||||
// Define a mock struct to be used in unit tests for common aws steps.
|
||||
type mockEC2Conn struct {
|
||||
ec2iface.EC2API
|
||||
Config *aws.Config
|
||||
}
|
||||
|
||||
func (m *mockEC2Conn) CreateSnapshot(input *ec2.CreateSnapshotInput) (*ec2.Snapshot, error) {
|
||||
snap := &ec2.Snapshot{
|
||||
// This isn't typical amazon format, but injecting the volume id into
|
||||
// this field lets us verify that the right volume was snapshotted with
|
||||
// a simple string comparison
|
||||
SnapshotId: aws.String(fmt.Sprintf("snap-of-%s", *input.VolumeId)),
|
||||
}
|
||||
|
||||
return snap, nil
|
||||
}
|
||||
|
||||
func (m *mockEC2Conn) WaitUntilSnapshotCompletedWithContext(aws.Context, *ec2.DescribeSnapshotsInput, ...request.WaiterOption) error {
|
||||
return nil
|
||||
}
|
||||
|
||||
func getMockConn(config *common.AccessConfig, target string) (ec2iface.EC2API, error) {
|
||||
mockConn := &mockEC2Conn{
|
||||
Config: aws.NewConfig(),
|
||||
}
|
||||
return mockConn, nil
|
||||
}
|
||||
|
||||
// Create statebag for running test
|
||||
func tState(t *testing.T) multistep.StateBag {
|
||||
state := new(multistep.BasicStateBag)
|
||||
state.Put("ui", &packer.BasicUi{
|
||||
Reader: new(bytes.Buffer),
|
||||
Writer: new(bytes.Buffer),
|
||||
})
|
||||
// state.Put("amis", map[string]string{"us-east-1": "ami-12345"})
|
||||
// state.Put("snapshots", map[string][]string{"us-east-1": {"snap-0012345"}})
|
||||
conn, _ := getMockConn(&common.AccessConfig{}, "us-east-2")
|
||||
|
||||
state.Put("ec2", conn)
|
||||
// Store a fake instance that contains a block device that matches the
|
||||
// volumes defined in the config above
|
||||
state.Put("instance", &ec2.Instance{
|
||||
InstanceId: aws.String("instance-id"),
|
||||
BlockDeviceMappings: []*ec2.InstanceBlockDeviceMapping{
|
||||
{
|
||||
DeviceName: aws.String("/dev/xvda"),
|
||||
Ebs: &ec2.EbsInstanceBlockDevice{
|
||||
VolumeId: aws.String("vol-1234"),
|
||||
},
|
||||
},
|
||||
{
|
||||
DeviceName: aws.String("/dev/xvdb"),
|
||||
Ebs: &ec2.EbsInstanceBlockDevice{
|
||||
VolumeId: aws.String("vol-5678"),
|
||||
},
|
||||
},
|
||||
},
|
||||
})
|
||||
return state
|
||||
}
|
||||
|
||||
func TestStepSnapshot_run_simple(t *testing.T) {
|
||||
var b Builder
|
||||
config := testConfig() //from builder_test
|
||||
|
||||
//Set some snapshot settings
|
||||
config["ebs_volumes"] = []map[string]interface{}{
|
||||
{
|
||||
"device_name": "/dev/xvdb",
|
||||
"volume_size": "32",
|
||||
"delete_on_termination": true,
|
||||
"snapshot_volume": true,
|
||||
},
|
||||
}
|
||||
|
||||
generatedData, warnings, err := b.Prepare(config)
|
||||
if len(warnings) > 0 {
|
||||
t.Fatalf("bad: %#v", warnings)
|
||||
}
|
||||
if err != nil {
|
||||
t.Fatalf("should not have error: %s", err)
|
||||
}
|
||||
if len(generatedData) == 0 {
|
||||
t.Fatalf("Generated data should not be empty")
|
||||
}
|
||||
|
||||
state := tState(t)
|
||||
|
||||
accessConfig := common.FakeAccessConfig()
|
||||
|
||||
step := stepSnapshotEBSVolumes{
|
||||
PollingConfig: new(common.AWSPollingConfig),
|
||||
AccessConfig: accessConfig,
|
||||
VolumeMapping: b.config.VolumeMappings,
|
||||
Ctx: b.config.ctx,
|
||||
}
|
||||
|
||||
step.Run(context.Background(), state)
|
||||
|
||||
if len(step.snapshotMap) != 1 {
|
||||
t.Fatalf("Missing Snapshot from step")
|
||||
}
|
||||
|
||||
if volmapping := step.snapshotMap["snap-of-vol-5678"]; volmapping == nil {
|
||||
t.Fatalf("Didn't snapshot correct volume: Map is %#v", step.snapshotMap)
|
||||
}
|
||||
}
|
||||
|
||||
func TestStepSnapshot_run_no_snaps(t *testing.T) {
|
||||
var b Builder
|
||||
config := testConfig() //from builder_test
|
||||
|
||||
//Set some snapshot settings
|
||||
config["ebs_volumes"] = []map[string]interface{}{
|
||||
{
|
||||
"device_name": "/dev/xvdb",
|
||||
"volume_size": "32",
|
||||
"delete_on_termination": true,
|
||||
"snapshot_volume": false,
|
||||
},
|
||||
}
|
||||
|
||||
generatedData, warnings, err := b.Prepare(config)
|
||||
if len(warnings) > 0 {
|
||||
t.Fatalf("bad: %#v", warnings)
|
||||
}
|
||||
if err != nil {
|
||||
t.Fatalf("should not have error: %s", err)
|
||||
}
|
||||
if len(generatedData) == 0 {
|
||||
t.Fatalf("Generated data should not be empty")
|
||||
}
|
||||
|
||||
state := tState(t)
|
||||
|
||||
accessConfig := common.FakeAccessConfig()
|
||||
|
||||
step := stepSnapshotEBSVolumes{
|
||||
PollingConfig: new(common.AWSPollingConfig),
|
||||
AccessConfig: accessConfig,
|
||||
VolumeMapping: b.config.VolumeMappings,
|
||||
Ctx: b.config.ctx,
|
||||
}
|
||||
|
||||
step.Run(context.Background(), state)
|
||||
|
||||
if len(step.snapshotMap) != 0 {
|
||||
t.Fatalf("Shouldn't have snapshotted any volumes")
|
||||
}
|
||||
}
|
||||
@@ -90,7 +90,6 @@ 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"`
|
||||
@@ -246,7 +245,6 @@ func (*FlatConfig) HCL2Spec() map[string]hcldec.Spec {
|
||||
"vpc_filter": &hcldec.BlockSpec{TypeName: "vpc_filter", Nested: hcldec.ObjectSpec((*common.FlatVpcFilterOptions)(nil).HCL2Spec())},
|
||||
"vpc_id": &hcldec.AttrSpec{Name: "vpc_id", Type: cty.String, Required: false},
|
||||
"windows_password_timeout": &hcldec.AttrSpec{Name: "windows_password_timeout", Type: cty.String, Required: false},
|
||||
"metadata_options": &hcldec.BlockSpec{TypeName: "metadata_options", Nested: hcldec.ObjectSpec((*common.FlatMetadataOptions)(nil).HCL2Spec())},
|
||||
"communicator": &hcldec.AttrSpec{Name: "communicator", Type: cty.String, Required: false},
|
||||
"pause_before_connecting": &hcldec.AttrSpec{Name: "pause_before_connecting", Type: cty.String, Required: false},
|
||||
"ssh_host": &hcldec.AttrSpec{Name: "ssh_host", Type: cty.String, Required: false},
|
||||
|
||||
@@ -233,9 +233,7 @@ func NewAzureClient(subscriptionID, sigSubscriptionID, resourceGroupName, storag
|
||||
azureClient.GalleryImageVersionsClient.ResponseInspector = byConcatDecorators(byInspecting(maxlen), errorCapture(azureClient))
|
||||
azureClient.GalleryImageVersionsClient.UserAgent = fmt.Sprintf("%s %s", useragent.String(version.AzurePluginVersion.FormattedVersion()), azureClient.GalleryImageVersionsClient.UserAgent)
|
||||
azureClient.GalleryImageVersionsClient.Client.PollingDuration = sharedGalleryTimeout
|
||||
if sigSubscriptionID != "" {
|
||||
azureClient.GalleryImageVersionsClient.SubscriptionID = sigSubscriptionID
|
||||
}
|
||||
azureClient.GalleryImageVersionsClient.SubscriptionID = sigSubscriptionID
|
||||
|
||||
azureClient.GalleryImagesClient = newCompute.NewGalleryImagesClientWithBaseURI(cloud.ResourceManagerEndpoint, subscriptionID)
|
||||
azureClient.GalleryImagesClient.Authorizer = autorest.NewBearerAuthorizer(servicePrincipalToken)
|
||||
@@ -243,9 +241,7 @@ func NewAzureClient(subscriptionID, sigSubscriptionID, resourceGroupName, storag
|
||||
azureClient.GalleryImagesClient.ResponseInspector = byConcatDecorators(byInspecting(maxlen), errorCapture(azureClient))
|
||||
azureClient.GalleryImagesClient.UserAgent = fmt.Sprintf("%s %s", useragent.String(version.AzurePluginVersion.FormattedVersion()), azureClient.GalleryImagesClient.UserAgent)
|
||||
azureClient.GalleryImagesClient.Client.PollingDuration = pollingDuration
|
||||
if sigSubscriptionID != "" {
|
||||
azureClient.GalleryImagesClient.SubscriptionID = sigSubscriptionID
|
||||
}
|
||||
azureClient.GalleryImagesClient.SubscriptionID = sigSubscriptionID
|
||||
|
||||
keyVaultURL, err := url.Parse(cloud.KeyVaultEndpoint)
|
||||
if err != nil {
|
||||
|
||||
@@ -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" below for
|
||||
// template engine; see "User Data" bellow 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 below for more
|
||||
// engine see User Data bellow for more
|
||||
// details.
|
||||
UserDataFile string `mapstructure:"user_data_file" required:"false"`
|
||||
// The name or ID of the zone where the instance will be
|
||||
|
||||
@@ -4,14 +4,9 @@ import (
|
||||
"context"
|
||||
"errors"
|
||||
"fmt"
|
||||
"math"
|
||||
"math/rand"
|
||||
"net/http"
|
||||
"regexp"
|
||||
"sync/atomic"
|
||||
"time"
|
||||
|
||||
"github.com/oracle/oci-go-sdk/common"
|
||||
core "github.com/oracle/oci-go-sdk/core"
|
||||
)
|
||||
|
||||
@@ -24,31 +19,6 @@ type driverOCI struct {
|
||||
context context.Context
|
||||
}
|
||||
|
||||
var retryPolicy = &common.RetryPolicy{
|
||||
MaximumNumberAttempts: 10,
|
||||
ShouldRetryOperation: func(res common.OCIOperationResponse) bool {
|
||||
var e common.ServiceError
|
||||
if errors.As(res.Error, &e) {
|
||||
switch e.GetHTTPStatusCode() {
|
||||
case http.StatusTooManyRequests, http.StatusInternalServerError, http.StatusServiceUnavailable:
|
||||
return true
|
||||
}
|
||||
}
|
||||
return false
|
||||
},
|
||||
NextDuration: func(res common.OCIOperationResponse) time.Duration {
|
||||
x := uint64(res.AttemptNumber)
|
||||
d := time.Duration(math.Pow(2, float64(atomic.LoadUint64(&x)))) * time.Second
|
||||
j := time.Duration(rand.Float64()*(2000)) * time.Millisecond
|
||||
w := d + j
|
||||
return w
|
||||
},
|
||||
}
|
||||
|
||||
var requestMetadata = common.RequestMetadata{
|
||||
RetryPolicy: retryPolicy,
|
||||
}
|
||||
|
||||
// NewDriverOCI Creates a new driverOCI with a connected compute client and a connected vcn client.
|
||||
func NewDriverOCI(cfg *Config) (Driver, error) {
|
||||
coreClient, err := core.NewComputeClientWithConfigurationProvider(cfg.configProvider)
|
||||
@@ -110,7 +80,6 @@ func (d *driverOCI) CreateInstance(ctx context.Context, publicKey string) (strin
|
||||
LifecycleState: "AVAILABLE",
|
||||
SortBy: "TIMECREATED",
|
||||
SortOrder: "DESC",
|
||||
RequestMetadata: requestMetadata,
|
||||
})
|
||||
if err != nil {
|
||||
return "", err
|
||||
@@ -158,10 +127,7 @@ func (d *driverOCI) CreateInstance(ctx context.Context, publicKey string) (strin
|
||||
Metadata: metadata,
|
||||
}
|
||||
|
||||
instance, err := d.computeClient.LaunchInstance(context.TODO(), core.LaunchInstanceRequest{
|
||||
LaunchInstanceDetails: instanceDetails,
|
||||
RequestMetadata: requestMetadata,
|
||||
})
|
||||
instance, err := d.computeClient.LaunchInstance(context.TODO(), core.LaunchInstanceRequest{LaunchInstanceDetails: instanceDetails})
|
||||
|
||||
if err != nil {
|
||||
return "", err
|
||||
@@ -179,9 +145,7 @@ func (d *driverOCI) CreateImage(ctx context.Context, id string) (core.Image, err
|
||||
FreeformTags: d.cfg.Tags,
|
||||
DefinedTags: d.cfg.DefinedTags,
|
||||
LaunchMode: core.CreateImageDetailsLaunchModeEnum(d.cfg.LaunchMode),
|
||||
},
|
||||
RequestMetadata: requestMetadata,
|
||||
})
|
||||
}})
|
||||
|
||||
if err != nil {
|
||||
return core.Image{}, err
|
||||
@@ -192,19 +156,15 @@ func (d *driverOCI) CreateImage(ctx context.Context, id string) (core.Image, err
|
||||
|
||||
// DeleteImage deletes a custom image.
|
||||
func (d *driverOCI) DeleteImage(ctx context.Context, id string) error {
|
||||
_, err := d.computeClient.DeleteImage(ctx, core.DeleteImageRequest{
|
||||
ImageId: &id,
|
||||
RequestMetadata: requestMetadata,
|
||||
})
|
||||
_, err := d.computeClient.DeleteImage(ctx, core.DeleteImageRequest{ImageId: &id})
|
||||
return err
|
||||
}
|
||||
|
||||
// GetInstanceIP returns the public or private IP corresponding to the given instance id.
|
||||
func (d *driverOCI) GetInstanceIP(ctx context.Context, id string) (string, error) {
|
||||
vnics, err := d.computeClient.ListVnicAttachments(ctx, core.ListVnicAttachmentsRequest{
|
||||
InstanceId: &id,
|
||||
CompartmentId: &d.cfg.CompartmentID,
|
||||
RequestMetadata: requestMetadata,
|
||||
InstanceId: &id,
|
||||
CompartmentId: &d.cfg.CompartmentID,
|
||||
})
|
||||
if err != nil {
|
||||
return "", err
|
||||
@@ -214,10 +174,7 @@ func (d *driverOCI) GetInstanceIP(ctx context.Context, id string) (string, error
|
||||
return "", errors.New("instance has zero VNICs")
|
||||
}
|
||||
|
||||
vnic, err := d.vcnClient.GetVnic(ctx, core.GetVnicRequest{
|
||||
VnicId: vnics.Items[0].VnicId,
|
||||
RequestMetadata: requestMetadata,
|
||||
})
|
||||
vnic, err := d.vcnClient.GetVnic(ctx, core.GetVnicRequest{VnicId: vnics.Items[0].VnicId})
|
||||
if err != nil {
|
||||
return "", fmt.Errorf("Error getting VNIC details: %s", err)
|
||||
}
|
||||
@@ -235,8 +192,7 @@ func (d *driverOCI) GetInstanceIP(ctx context.Context, id string) (string, error
|
||||
|
||||
func (d *driverOCI) GetInstanceInitialCredentials(ctx context.Context, id string) (string, string, error) {
|
||||
credentials, err := d.computeClient.GetWindowsInstanceInitialCredentials(ctx, core.GetWindowsInstanceInitialCredentialsRequest{
|
||||
InstanceId: &id,
|
||||
RequestMetadata: requestMetadata,
|
||||
InstanceId: &id,
|
||||
})
|
||||
if err != nil {
|
||||
return "", "", err
|
||||
@@ -248,8 +204,7 @@ func (d *driverOCI) GetInstanceInitialCredentials(ctx context.Context, id string
|
||||
// TerminateInstance terminates a compute instance.
|
||||
func (d *driverOCI) TerminateInstance(ctx context.Context, id string) error {
|
||||
_, err := d.computeClient.TerminateInstance(ctx, core.TerminateInstanceRequest{
|
||||
InstanceId: &id,
|
||||
RequestMetadata: requestMetadata,
|
||||
InstanceId: &id,
|
||||
})
|
||||
return err
|
||||
}
|
||||
@@ -259,10 +214,7 @@ func (d *driverOCI) TerminateInstance(ctx context.Context, id string) error {
|
||||
func (d *driverOCI) WaitForImageCreation(ctx context.Context, id string) error {
|
||||
return waitForResourceToReachState(
|
||||
func(string) (string, error) {
|
||||
image, err := d.computeClient.GetImage(ctx, core.GetImageRequest{
|
||||
ImageId: &id,
|
||||
RequestMetadata: requestMetadata,
|
||||
})
|
||||
image, err := d.computeClient.GetImage(ctx, core.GetImageRequest{ImageId: &id})
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
@@ -281,10 +233,7 @@ func (d *driverOCI) WaitForImageCreation(ctx context.Context, id string) error {
|
||||
func (d *driverOCI) WaitForInstanceState(ctx context.Context, id string, waitStates []string, terminalState string) error {
|
||||
return waitForResourceToReachState(
|
||||
func(string) (string, error) {
|
||||
instance, err := d.computeClient.GetInstance(ctx, core.GetInstanceRequest{
|
||||
InstanceId: &id,
|
||||
RequestMetadata: requestMetadata,
|
||||
})
|
||||
instance, err := d.computeClient.GetInstance(ctx, core.GetInstanceRequest{InstanceId: &id})
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
|
||||
@@ -22,11 +22,8 @@ func (s *stepCreateServer) Run(ctx context.Context, state multistep.StateBag) mu
|
||||
|
||||
profitbricks.SetAuth(c.PBUsername, c.PBPassword)
|
||||
profitbricks.SetDepth("5")
|
||||
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
|
||||
if sshkey, ok := state.GetOk("publicKey"); ok {
|
||||
c.SSHKey = sshkey.(string)
|
||||
}
|
||||
ui.Say("Creating Virtual Data Center...")
|
||||
img := s.getImageId(c.Image, c)
|
||||
@@ -207,7 +204,7 @@ func (d *stepCreateServer) setPB(username string, password string, url string) {
|
||||
|
||||
func (d *stepCreateServer) checkForErrors(instance profitbricks.Resp) error {
|
||||
if instance.StatusCode > 299 {
|
||||
return fmt.Errorf("Error occurred %s", string(instance.Body))
|
||||
return errors.New(fmt.Sprintf("Error occurred %s", string(instance.Body)))
|
||||
}
|
||||
return nil
|
||||
}
|
||||
@@ -264,9 +261,7 @@ func (d *stepCreateServer) getImageAlias(imageAlias string, location string, ui
|
||||
|
||||
func parseErrorMessage(raw string) (toreturn string) {
|
||||
var tmp map[string]interface{}
|
||||
if json.Unmarshal([]byte(raw), &tmp) != nil {
|
||||
return ""
|
||||
}
|
||||
json.Unmarshal([]byte(raw), &tmp)
|
||||
|
||||
for _, v := range tmp["messages"].([]interface{}) {
|
||||
for index, i := range v.(map[string]interface{}) {
|
||||
|
||||
@@ -3,9 +3,6 @@ package profitbricks
|
||||
import (
|
||||
"context"
|
||||
"encoding/json"
|
||||
"errors"
|
||||
"fmt"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
"github.com/hashicorp/packer-plugin-sdk/multistep"
|
||||
@@ -25,32 +22,9 @@ 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 {
|
||||
@@ -68,123 +42,31 @@ func (s *stepTakeSnapshot) Run(ctx context.Context, state multistep.StateBag) mu
|
||||
return multistep.ActionHalt
|
||||
}
|
||||
|
||||
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
|
||||
}
|
||||
s.waitTillProvisioned(snapshot.Headers.Get("Location"), *c)
|
||||
|
||||
return multistep.ActionContinue
|
||||
}
|
||||
|
||||
func (s *stepTakeSnapshot) Cleanup(_ multistep.StateBag) {
|
||||
func (s *stepTakeSnapshot) Cleanup(state multistep.StateBag) {
|
||||
}
|
||||
|
||||
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)
|
||||
func (d *stepTakeSnapshot) waitTillProvisioned(path string, config Config) {
|
||||
d.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
|
||||
}
|
||||
if request.Metadata.Status == "FAILED" {
|
||||
return fmt.Errorf("Request failed: %s", request.Response)
|
||||
}
|
||||
time.Sleep(waitInterval)
|
||||
time.Sleep(10 * time.Second)
|
||||
i++
|
||||
}
|
||||
|
||||
if done == false {
|
||||
return fmt.Errorf("request not fulfilled after waiting %d seconds",
|
||||
int64(waitCount)*int64(waitInterval)/int64(time.Second))
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
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) {
|
||||
func (d *stepTakeSnapshot) setPB(username string, password string, url string) {
|
||||
profitbricks.SetAuth(username, password)
|
||||
profitbricks.SetEndpoint(url)
|
||||
}
|
||||
|
||||
@@ -11,101 +11,101 @@ import (
|
||||
// FlatConfig is an auto-generated flat version of Config.
|
||||
// Where the contents of a field with a `mapstructure:,squash` tag are bubbled up.
|
||||
type FlatConfig struct {
|
||||
PackerBuildName *string `mapstructure:"packer_build_name" cty:"packer_build_name" hcl:"packer_build_name"`
|
||||
PackerBuilderType *string `mapstructure:"packer_builder_type" cty:"packer_builder_type" hcl:"packer_builder_type"`
|
||||
PackerCoreVersion *string `mapstructure:"packer_core_version" cty:"packer_core_version" hcl:"packer_core_version"`
|
||||
PackerDebug *bool `mapstructure:"packer_debug" cty:"packer_debug" hcl:"packer_debug"`
|
||||
PackerForce *bool `mapstructure:"packer_force" cty:"packer_force" hcl:"packer_force"`
|
||||
PackerOnError *string `mapstructure:"packer_on_error" cty:"packer_on_error" hcl:"packer_on_error"`
|
||||
PackerUserVars map[string]string `mapstructure:"packer_user_variables" cty:"packer_user_variables" hcl:"packer_user_variables"`
|
||||
PackerSensitiveVars []string `mapstructure:"packer_sensitive_variables" cty:"packer_sensitive_variables" hcl:"packer_sensitive_variables"`
|
||||
HTTPDir *string `mapstructure:"http_directory" cty:"http_directory" hcl:"http_directory"`
|
||||
HTTPPortMin *int `mapstructure:"http_port_min" cty:"http_port_min" hcl:"http_port_min"`
|
||||
HTTPPortMax *int `mapstructure:"http_port_max" cty:"http_port_max" hcl:"http_port_max"`
|
||||
HTTPAddress *string `mapstructure:"http_bind_address" cty:"http_bind_address" hcl:"http_bind_address"`
|
||||
HTTPInterface *string `mapstructure:"http_interface" undocumented:"true" cty:"http_interface" hcl:"http_interface"`
|
||||
BootGroupInterval *string `mapstructure:"boot_keygroup_interval" cty:"boot_keygroup_interval" hcl:"boot_keygroup_interval"`
|
||||
BootWait *string `mapstructure:"boot_wait" cty:"boot_wait" hcl:"boot_wait"`
|
||||
BootCommand []string `mapstructure:"boot_command" cty:"boot_command" hcl:"boot_command"`
|
||||
BootKeyInterval *string `mapstructure:"boot_key_interval" cty:"boot_key_interval" hcl:"boot_key_interval"`
|
||||
Type *string `mapstructure:"communicator" cty:"communicator" hcl:"communicator"`
|
||||
PauseBeforeConnect *string `mapstructure:"pause_before_connecting" cty:"pause_before_connecting" hcl:"pause_before_connecting"`
|
||||
SSHHost *string `mapstructure:"ssh_host" cty:"ssh_host" hcl:"ssh_host"`
|
||||
SSHPort *int `mapstructure:"ssh_port" cty:"ssh_port" hcl:"ssh_port"`
|
||||
SSHUsername *string `mapstructure:"ssh_username" cty:"ssh_username" hcl:"ssh_username"`
|
||||
SSHPassword *string `mapstructure:"ssh_password" cty:"ssh_password" hcl:"ssh_password"`
|
||||
SSHKeyPairName *string `mapstructure:"ssh_keypair_name" undocumented:"true" cty:"ssh_keypair_name" hcl:"ssh_keypair_name"`
|
||||
SSHTemporaryKeyPairName *string `mapstructure:"temporary_key_pair_name" undocumented:"true" cty:"temporary_key_pair_name" hcl:"temporary_key_pair_name"`
|
||||
SSHTemporaryKeyPairType *string `mapstructure:"temporary_key_pair_type" cty:"temporary_key_pair_type" hcl:"temporary_key_pair_type"`
|
||||
SSHTemporaryKeyPairBits *int `mapstructure:"temporary_key_pair_bits" cty:"temporary_key_pair_bits" hcl:"temporary_key_pair_bits"`
|
||||
SSHCiphers []string `mapstructure:"ssh_ciphers" cty:"ssh_ciphers" hcl:"ssh_ciphers"`
|
||||
SSHClearAuthorizedKeys *bool `mapstructure:"ssh_clear_authorized_keys" cty:"ssh_clear_authorized_keys" hcl:"ssh_clear_authorized_keys"`
|
||||
SSHKEXAlgos []string `mapstructure:"ssh_key_exchange_algorithms" cty:"ssh_key_exchange_algorithms" hcl:"ssh_key_exchange_algorithms"`
|
||||
SSHPrivateKeyFile *string `mapstructure:"ssh_private_key_file" undocumented:"true" cty:"ssh_private_key_file" hcl:"ssh_private_key_file"`
|
||||
SSHCertificateFile *string `mapstructure:"ssh_certificate_file" cty:"ssh_certificate_file" hcl:"ssh_certificate_file"`
|
||||
SSHPty *bool `mapstructure:"ssh_pty" cty:"ssh_pty" hcl:"ssh_pty"`
|
||||
SSHTimeout *string `mapstructure:"ssh_timeout" cty:"ssh_timeout" hcl:"ssh_timeout"`
|
||||
SSHWaitTimeout *string `mapstructure:"ssh_wait_timeout" undocumented:"true" cty:"ssh_wait_timeout" hcl:"ssh_wait_timeout"`
|
||||
SSHAgentAuth *bool `mapstructure:"ssh_agent_auth" undocumented:"true" cty:"ssh_agent_auth" hcl:"ssh_agent_auth"`
|
||||
SSHDisableAgentForwarding *bool `mapstructure:"ssh_disable_agent_forwarding" cty:"ssh_disable_agent_forwarding" hcl:"ssh_disable_agent_forwarding"`
|
||||
SSHHandshakeAttempts *int `mapstructure:"ssh_handshake_attempts" cty:"ssh_handshake_attempts" hcl:"ssh_handshake_attempts"`
|
||||
SSHBastionHost *string `mapstructure:"ssh_bastion_host" cty:"ssh_bastion_host" hcl:"ssh_bastion_host"`
|
||||
SSHBastionPort *int `mapstructure:"ssh_bastion_port" cty:"ssh_bastion_port" hcl:"ssh_bastion_port"`
|
||||
SSHBastionAgentAuth *bool `mapstructure:"ssh_bastion_agent_auth" cty:"ssh_bastion_agent_auth" hcl:"ssh_bastion_agent_auth"`
|
||||
SSHBastionUsername *string `mapstructure:"ssh_bastion_username" cty:"ssh_bastion_username" hcl:"ssh_bastion_username"`
|
||||
SSHBastionPassword *string `mapstructure:"ssh_bastion_password" cty:"ssh_bastion_password" hcl:"ssh_bastion_password"`
|
||||
SSHBastionInteractive *bool `mapstructure:"ssh_bastion_interactive" cty:"ssh_bastion_interactive" hcl:"ssh_bastion_interactive"`
|
||||
SSHBastionPrivateKeyFile *string `mapstructure:"ssh_bastion_private_key_file" cty:"ssh_bastion_private_key_file" hcl:"ssh_bastion_private_key_file"`
|
||||
SSHBastionCertificateFile *string `mapstructure:"ssh_bastion_certificate_file" cty:"ssh_bastion_certificate_file" hcl:"ssh_bastion_certificate_file"`
|
||||
SSHFileTransferMethod *string `mapstructure:"ssh_file_transfer_method" cty:"ssh_file_transfer_method" hcl:"ssh_file_transfer_method"`
|
||||
SSHProxyHost *string `mapstructure:"ssh_proxy_host" cty:"ssh_proxy_host" hcl:"ssh_proxy_host"`
|
||||
SSHProxyPort *int `mapstructure:"ssh_proxy_port" cty:"ssh_proxy_port" hcl:"ssh_proxy_port"`
|
||||
SSHProxyUsername *string `mapstructure:"ssh_proxy_username" cty:"ssh_proxy_username" hcl:"ssh_proxy_username"`
|
||||
SSHProxyPassword *string `mapstructure:"ssh_proxy_password" cty:"ssh_proxy_password" hcl:"ssh_proxy_password"`
|
||||
SSHKeepAliveInterval *string `mapstructure:"ssh_keep_alive_interval" cty:"ssh_keep_alive_interval" hcl:"ssh_keep_alive_interval"`
|
||||
SSHReadWriteTimeout *string `mapstructure:"ssh_read_write_timeout" cty:"ssh_read_write_timeout" hcl:"ssh_read_write_timeout"`
|
||||
SSHRemoteTunnels []string `mapstructure:"ssh_remote_tunnels" cty:"ssh_remote_tunnels" hcl:"ssh_remote_tunnels"`
|
||||
SSHLocalTunnels []string `mapstructure:"ssh_local_tunnels" cty:"ssh_local_tunnels" hcl:"ssh_local_tunnels"`
|
||||
SSHPublicKey []byte `mapstructure:"ssh_public_key" undocumented:"true" cty:"ssh_public_key" hcl:"ssh_public_key"`
|
||||
SSHPrivateKey []byte `mapstructure:"ssh_private_key" undocumented:"true" cty:"ssh_private_key" hcl:"ssh_private_key"`
|
||||
WinRMUser *string `mapstructure:"winrm_username" cty:"winrm_username" hcl:"winrm_username"`
|
||||
WinRMPassword *string `mapstructure:"winrm_password" cty:"winrm_password" hcl:"winrm_password"`
|
||||
WinRMHost *string `mapstructure:"winrm_host" cty:"winrm_host" hcl:"winrm_host"`
|
||||
WinRMNoProxy *bool `mapstructure:"winrm_no_proxy" cty:"winrm_no_proxy" hcl:"winrm_no_proxy"`
|
||||
WinRMPort *int `mapstructure:"winrm_port" cty:"winrm_port" hcl:"winrm_port"`
|
||||
WinRMTimeout *string `mapstructure:"winrm_timeout" cty:"winrm_timeout" hcl:"winrm_timeout"`
|
||||
WinRMUseSSL *bool `mapstructure:"winrm_use_ssl" cty:"winrm_use_ssl" hcl:"winrm_use_ssl"`
|
||||
WinRMInsecure *bool `mapstructure:"winrm_insecure" cty:"winrm_insecure" hcl:"winrm_insecure"`
|
||||
WinRMUseNTLM *bool `mapstructure:"winrm_use_ntlm" cty:"winrm_use_ntlm" hcl:"winrm_use_ntlm"`
|
||||
ProxmoxURLRaw *string `mapstructure:"proxmox_url" cty:"proxmox_url" hcl:"proxmox_url"`
|
||||
SkipCertValidation *bool `mapstructure:"insecure_skip_tls_verify" cty:"insecure_skip_tls_verify" hcl:"insecure_skip_tls_verify"`
|
||||
Username *string `mapstructure:"username" cty:"username" hcl:"username"`
|
||||
Password *string `mapstructure:"password" cty:"password" hcl:"password"`
|
||||
Node *string `mapstructure:"node" cty:"node" hcl:"node"`
|
||||
Pool *string `mapstructure:"pool" cty:"pool" hcl:"pool"`
|
||||
VMName *string `mapstructure:"vm_name" cty:"vm_name" hcl:"vm_name"`
|
||||
VMID *int `mapstructure:"vm_id" cty:"vm_id" hcl:"vm_id"`
|
||||
Boot *string `mapstructure:"boot" cty:"boot" hcl:"boot"`
|
||||
Memory *int `mapstructure:"memory" cty:"memory" hcl:"memory"`
|
||||
Cores *int `mapstructure:"cores" cty:"cores" hcl:"cores"`
|
||||
CPUType *string `mapstructure:"cpu_type" cty:"cpu_type" hcl:"cpu_type"`
|
||||
Sockets *int `mapstructure:"sockets" cty:"sockets" hcl:"sockets"`
|
||||
OS *string `mapstructure:"os" cty:"os" hcl:"os"`
|
||||
VGA *proxmox.FlatvgaConfig `mapstructure:"vga" cty:"vga" hcl:"vga"`
|
||||
NICs []proxmox.FlatnicConfig `mapstructure:"network_adapters" cty:"network_adapters" hcl:"network_adapters"`
|
||||
Disks []proxmox.FlatdiskConfig `mapstructure:"disks" cty:"disks" hcl:"disks"`
|
||||
Agent *bool `mapstructure:"qemu_agent" cty:"qemu_agent" hcl:"qemu_agent"`
|
||||
SCSIController *string `mapstructure:"scsi_controller" cty:"scsi_controller" hcl:"scsi_controller"`
|
||||
Onboot *bool `mapstructure:"onboot" cty:"onboot" hcl:"onboot"`
|
||||
DisableKVM *bool `mapstructure:"disable_kvm" cty:"disable_kvm" hcl:"disable_kvm"`
|
||||
TemplateName *string `mapstructure:"template_name" cty:"template_name" hcl:"template_name"`
|
||||
TemplateDescription *string `mapstructure:"template_description" cty:"template_description" hcl:"template_description"`
|
||||
CloudInit *bool `mapstructure:"cloud_init" cty:"cloud_init" hcl:"cloud_init"`
|
||||
CloudInitStoragePool *string `mapstructure:"cloud_init_storage_pool" cty:"cloud_init_storage_pool" hcl:"cloud_init_storage_pool"`
|
||||
AdditionalISOFiles []proxmox.FlatadditionalISOsConfig `mapstructure:"additional_iso_files" cty:"additional_iso_files" hcl:"additional_iso_files"`
|
||||
VMInterface *string `mapstructure:"vm_interface" cty:"vm_interface" hcl:"vm_interface"`
|
||||
CloneVM *string `mapstructure:"clone_vm" cty:"clone_vm" hcl:"clone_vm"`
|
||||
FullClone *bool `mapstructure:"full_clone" required:"false" cty:"full_clone" hcl:"full_clone"`
|
||||
PackerBuildName *string `mapstructure:"packer_build_name" cty:"packer_build_name" hcl:"packer_build_name"`
|
||||
PackerBuilderType *string `mapstructure:"packer_builder_type" cty:"packer_builder_type" hcl:"packer_builder_type"`
|
||||
PackerCoreVersion *string `mapstructure:"packer_core_version" cty:"packer_core_version" hcl:"packer_core_version"`
|
||||
PackerDebug *bool `mapstructure:"packer_debug" cty:"packer_debug" hcl:"packer_debug"`
|
||||
PackerForce *bool `mapstructure:"packer_force" cty:"packer_force" hcl:"packer_force"`
|
||||
PackerOnError *string `mapstructure:"packer_on_error" cty:"packer_on_error" hcl:"packer_on_error"`
|
||||
PackerUserVars map[string]string `mapstructure:"packer_user_variables" cty:"packer_user_variables" hcl:"packer_user_variables"`
|
||||
PackerSensitiveVars []string `mapstructure:"packer_sensitive_variables" cty:"packer_sensitive_variables" hcl:"packer_sensitive_variables"`
|
||||
HTTPDir *string `mapstructure:"http_directory" cty:"http_directory" hcl:"http_directory"`
|
||||
HTTPPortMin *int `mapstructure:"http_port_min" cty:"http_port_min" hcl:"http_port_min"`
|
||||
HTTPPortMax *int `mapstructure:"http_port_max" cty:"http_port_max" hcl:"http_port_max"`
|
||||
HTTPAddress *string `mapstructure:"http_bind_address" cty:"http_bind_address" hcl:"http_bind_address"`
|
||||
HTTPInterface *string `mapstructure:"http_interface" undocumented:"true" cty:"http_interface" hcl:"http_interface"`
|
||||
BootGroupInterval *string `mapstructure:"boot_keygroup_interval" cty:"boot_keygroup_interval" hcl:"boot_keygroup_interval"`
|
||||
BootWait *string `mapstructure:"boot_wait" cty:"boot_wait" hcl:"boot_wait"`
|
||||
BootCommand []string `mapstructure:"boot_command" cty:"boot_command" hcl:"boot_command"`
|
||||
BootKeyInterval *string `mapstructure:"boot_key_interval" cty:"boot_key_interval" hcl:"boot_key_interval"`
|
||||
Type *string `mapstructure:"communicator" cty:"communicator" hcl:"communicator"`
|
||||
PauseBeforeConnect *string `mapstructure:"pause_before_connecting" cty:"pause_before_connecting" hcl:"pause_before_connecting"`
|
||||
SSHHost *string `mapstructure:"ssh_host" cty:"ssh_host" hcl:"ssh_host"`
|
||||
SSHPort *int `mapstructure:"ssh_port" cty:"ssh_port" hcl:"ssh_port"`
|
||||
SSHUsername *string `mapstructure:"ssh_username" cty:"ssh_username" hcl:"ssh_username"`
|
||||
SSHPassword *string `mapstructure:"ssh_password" cty:"ssh_password" hcl:"ssh_password"`
|
||||
SSHKeyPairName *string `mapstructure:"ssh_keypair_name" undocumented:"true" cty:"ssh_keypair_name" hcl:"ssh_keypair_name"`
|
||||
SSHTemporaryKeyPairName *string `mapstructure:"temporary_key_pair_name" undocumented:"true" cty:"temporary_key_pair_name" hcl:"temporary_key_pair_name"`
|
||||
SSHTemporaryKeyPairType *string `mapstructure:"temporary_key_pair_type" cty:"temporary_key_pair_type" hcl:"temporary_key_pair_type"`
|
||||
SSHTemporaryKeyPairBits *int `mapstructure:"temporary_key_pair_bits" cty:"temporary_key_pair_bits" hcl:"temporary_key_pair_bits"`
|
||||
SSHCiphers []string `mapstructure:"ssh_ciphers" cty:"ssh_ciphers" hcl:"ssh_ciphers"`
|
||||
SSHClearAuthorizedKeys *bool `mapstructure:"ssh_clear_authorized_keys" cty:"ssh_clear_authorized_keys" hcl:"ssh_clear_authorized_keys"`
|
||||
SSHKEXAlgos []string `mapstructure:"ssh_key_exchange_algorithms" cty:"ssh_key_exchange_algorithms" hcl:"ssh_key_exchange_algorithms"`
|
||||
SSHPrivateKeyFile *string `mapstructure:"ssh_private_key_file" undocumented:"true" cty:"ssh_private_key_file" hcl:"ssh_private_key_file"`
|
||||
SSHCertificateFile *string `mapstructure:"ssh_certificate_file" cty:"ssh_certificate_file" hcl:"ssh_certificate_file"`
|
||||
SSHPty *bool `mapstructure:"ssh_pty" cty:"ssh_pty" hcl:"ssh_pty"`
|
||||
SSHTimeout *string `mapstructure:"ssh_timeout" cty:"ssh_timeout" hcl:"ssh_timeout"`
|
||||
SSHWaitTimeout *string `mapstructure:"ssh_wait_timeout" undocumented:"true" cty:"ssh_wait_timeout" hcl:"ssh_wait_timeout"`
|
||||
SSHAgentAuth *bool `mapstructure:"ssh_agent_auth" undocumented:"true" cty:"ssh_agent_auth" hcl:"ssh_agent_auth"`
|
||||
SSHDisableAgentForwarding *bool `mapstructure:"ssh_disable_agent_forwarding" cty:"ssh_disable_agent_forwarding" hcl:"ssh_disable_agent_forwarding"`
|
||||
SSHHandshakeAttempts *int `mapstructure:"ssh_handshake_attempts" cty:"ssh_handshake_attempts" hcl:"ssh_handshake_attempts"`
|
||||
SSHBastionHost *string `mapstructure:"ssh_bastion_host" cty:"ssh_bastion_host" hcl:"ssh_bastion_host"`
|
||||
SSHBastionPort *int `mapstructure:"ssh_bastion_port" cty:"ssh_bastion_port" hcl:"ssh_bastion_port"`
|
||||
SSHBastionAgentAuth *bool `mapstructure:"ssh_bastion_agent_auth" cty:"ssh_bastion_agent_auth" hcl:"ssh_bastion_agent_auth"`
|
||||
SSHBastionUsername *string `mapstructure:"ssh_bastion_username" cty:"ssh_bastion_username" hcl:"ssh_bastion_username"`
|
||||
SSHBastionPassword *string `mapstructure:"ssh_bastion_password" cty:"ssh_bastion_password" hcl:"ssh_bastion_password"`
|
||||
SSHBastionInteractive *bool `mapstructure:"ssh_bastion_interactive" cty:"ssh_bastion_interactive" hcl:"ssh_bastion_interactive"`
|
||||
SSHBastionPrivateKeyFile *string `mapstructure:"ssh_bastion_private_key_file" cty:"ssh_bastion_private_key_file" hcl:"ssh_bastion_private_key_file"`
|
||||
SSHBastionCertificateFile *string `mapstructure:"ssh_bastion_certificate_file" cty:"ssh_bastion_certificate_file" hcl:"ssh_bastion_certificate_file"`
|
||||
SSHFileTransferMethod *string `mapstructure:"ssh_file_transfer_method" cty:"ssh_file_transfer_method" hcl:"ssh_file_transfer_method"`
|
||||
SSHProxyHost *string `mapstructure:"ssh_proxy_host" cty:"ssh_proxy_host" hcl:"ssh_proxy_host"`
|
||||
SSHProxyPort *int `mapstructure:"ssh_proxy_port" cty:"ssh_proxy_port" hcl:"ssh_proxy_port"`
|
||||
SSHProxyUsername *string `mapstructure:"ssh_proxy_username" cty:"ssh_proxy_username" hcl:"ssh_proxy_username"`
|
||||
SSHProxyPassword *string `mapstructure:"ssh_proxy_password" cty:"ssh_proxy_password" hcl:"ssh_proxy_password"`
|
||||
SSHKeepAliveInterval *string `mapstructure:"ssh_keep_alive_interval" cty:"ssh_keep_alive_interval" hcl:"ssh_keep_alive_interval"`
|
||||
SSHReadWriteTimeout *string `mapstructure:"ssh_read_write_timeout" cty:"ssh_read_write_timeout" hcl:"ssh_read_write_timeout"`
|
||||
SSHRemoteTunnels []string `mapstructure:"ssh_remote_tunnels" cty:"ssh_remote_tunnels" hcl:"ssh_remote_tunnels"`
|
||||
SSHLocalTunnels []string `mapstructure:"ssh_local_tunnels" cty:"ssh_local_tunnels" hcl:"ssh_local_tunnels"`
|
||||
SSHPublicKey []byte `mapstructure:"ssh_public_key" undocumented:"true" cty:"ssh_public_key" hcl:"ssh_public_key"`
|
||||
SSHPrivateKey []byte `mapstructure:"ssh_private_key" undocumented:"true" cty:"ssh_private_key" hcl:"ssh_private_key"`
|
||||
WinRMUser *string `mapstructure:"winrm_username" cty:"winrm_username" hcl:"winrm_username"`
|
||||
WinRMPassword *string `mapstructure:"winrm_password" cty:"winrm_password" hcl:"winrm_password"`
|
||||
WinRMHost *string `mapstructure:"winrm_host" cty:"winrm_host" hcl:"winrm_host"`
|
||||
WinRMNoProxy *bool `mapstructure:"winrm_no_proxy" cty:"winrm_no_proxy" hcl:"winrm_no_proxy"`
|
||||
WinRMPort *int `mapstructure:"winrm_port" cty:"winrm_port" hcl:"winrm_port"`
|
||||
WinRMTimeout *string `mapstructure:"winrm_timeout" cty:"winrm_timeout" hcl:"winrm_timeout"`
|
||||
WinRMUseSSL *bool `mapstructure:"winrm_use_ssl" cty:"winrm_use_ssl" hcl:"winrm_use_ssl"`
|
||||
WinRMInsecure *bool `mapstructure:"winrm_insecure" cty:"winrm_insecure" hcl:"winrm_insecure"`
|
||||
WinRMUseNTLM *bool `mapstructure:"winrm_use_ntlm" cty:"winrm_use_ntlm" hcl:"winrm_use_ntlm"`
|
||||
ProxmoxURLRaw *string `mapstructure:"proxmox_url" cty:"proxmox_url" hcl:"proxmox_url"`
|
||||
SkipCertValidation *bool `mapstructure:"insecure_skip_tls_verify" cty:"insecure_skip_tls_verify" hcl:"insecure_skip_tls_verify"`
|
||||
Username *string `mapstructure:"username" cty:"username" hcl:"username"`
|
||||
Password *string `mapstructure:"password" cty:"password" hcl:"password"`
|
||||
Node *string `mapstructure:"node" cty:"node" hcl:"node"`
|
||||
Pool *string `mapstructure:"pool" cty:"pool" hcl:"pool"`
|
||||
VMName *string `mapstructure:"vm_name" cty:"vm_name" hcl:"vm_name"`
|
||||
VMID *int `mapstructure:"vm_id" cty:"vm_id" hcl:"vm_id"`
|
||||
Boot *string `mapstructure:"boot" cty:"boot" hcl:"boot"`
|
||||
Memory *int `mapstructure:"memory" cty:"memory" hcl:"memory"`
|
||||
Cores *int `mapstructure:"cores" cty:"cores" hcl:"cores"`
|
||||
CPUType *string `mapstructure:"cpu_type" cty:"cpu_type" hcl:"cpu_type"`
|
||||
Sockets *int `mapstructure:"sockets" cty:"sockets" hcl:"sockets"`
|
||||
OS *string `mapstructure:"os" cty:"os" hcl:"os"`
|
||||
VGA *proxmox.FlatvgaConfig `mapstructure:"vga" cty:"vga" hcl:"vga"`
|
||||
NICs []proxmox.FlatnicConfig `mapstructure:"network_adapters" cty:"network_adapters" hcl:"network_adapters"`
|
||||
Disks []proxmox.FlatdiskConfig `mapstructure:"disks" cty:"disks" hcl:"disks"`
|
||||
Agent *bool `mapstructure:"qemu_agent" cty:"qemu_agent" hcl:"qemu_agent"`
|
||||
SCSIController *string `mapstructure:"scsi_controller" cty:"scsi_controller" hcl:"scsi_controller"`
|
||||
Onboot *bool `mapstructure:"onboot" cty:"onboot" hcl:"onboot"`
|
||||
DisableKVM *bool `mapstructure:"disable_kvm" cty:"disable_kvm" hcl:"disable_kvm"`
|
||||
TemplateName *string `mapstructure:"template_name" cty:"template_name" hcl:"template_name"`
|
||||
TemplateDescription *string `mapstructure:"template_description" cty:"template_description" hcl:"template_description"`
|
||||
CloudInit *bool `mapstructure:"cloud_init" cty:"cloud_init" hcl:"cloud_init"`
|
||||
CloudInitStoragePool *string `mapstructure:"cloud_init_storage_pool" cty:"cloud_init_storage_pool" hcl:"cloud_init_storage_pool"`
|
||||
AdditionalISOFiles []proxmox.FlatstorageConfig `mapstructure:"additional_iso_files" cty:"additional_iso_files" hcl:"additional_iso_files"`
|
||||
VMInterface *string `mapstructure:"vm_interface" cty:"vm_interface" hcl:"vm_interface"`
|
||||
CloneVM *string `mapstructure:"clone_vm" cty:"clone_vm" hcl:"clone_vm"`
|
||||
FullClone *bool `mapstructure:"full_clone" required:"false" cty:"full_clone" hcl:"full_clone"`
|
||||
}
|
||||
|
||||
// FlatMapstructure returns a new FlatConfig.
|
||||
@@ -211,7 +211,7 @@ func (*FlatConfig) HCL2Spec() map[string]hcldec.Spec {
|
||||
"template_description": &hcldec.AttrSpec{Name: "template_description", Type: cty.String, Required: false},
|
||||
"cloud_init": &hcldec.AttrSpec{Name: "cloud_init", Type: cty.Bool, Required: false},
|
||||
"cloud_init_storage_pool": &hcldec.AttrSpec{Name: "cloud_init_storage_pool", Type: cty.String, Required: false},
|
||||
"additional_iso_files": &hcldec.BlockListSpec{TypeName: "additional_iso_files", Nested: hcldec.ObjectSpec((*proxmox.FlatadditionalISOsConfig)(nil).HCL2Spec())},
|
||||
"additional_iso_files": &hcldec.BlockListSpec{TypeName: "additional_iso_files", Nested: hcldec.ObjectSpec((*proxmox.FlatstorageConfig)(nil).HCL2Spec())},
|
||||
"vm_interface": &hcldec.AttrSpec{Name: "vm_interface", Type: cty.String, Required: false},
|
||||
"clone_vm": &hcldec.AttrSpec{Name: "clone_vm", Type: cty.String, Required: false},
|
||||
"full_clone": &hcldec.AttrSpec{Name: "full_clone", Type: cty.Bool, Required: false},
|
||||
|
||||
@@ -84,20 +84,7 @@ func (b *Builder) Run(ctx context.Context, ui packersdk.Ui, hook packersdk.Hook,
|
||||
&stepFinalizeTemplateConfig{},
|
||||
&stepSuccess{},
|
||||
}
|
||||
preSteps := b.preSteps
|
||||
for idx := range b.config.AdditionalISOFiles {
|
||||
preSteps = append(preSteps, &commonsteps.StepDownload{
|
||||
Checksum: b.config.AdditionalISOFiles[idx].ISOChecksum,
|
||||
Description: "additional ISO",
|
||||
Extension: b.config.AdditionalISOFiles[idx].TargetExtension,
|
||||
ResultKey: b.config.AdditionalISOFiles[idx].DownloadPathKey,
|
||||
TargetPath: b.config.AdditionalISOFiles[idx].DownloadPathKey,
|
||||
Url: b.config.AdditionalISOFiles[idx].ISOUrls,
|
||||
})
|
||||
}
|
||||
preSteps = append(preSteps, &stepUploadAdditionalISOs{})
|
||||
|
||||
steps := append(preSteps, coreSteps...)
|
||||
steps := append(b.preSteps, coreSteps...)
|
||||
steps = append(steps, b.postSteps...)
|
||||
// Run the steps
|
||||
b.runner = commonsteps.NewRunner(steps, b.config.PackerConfig, ui)
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
//go:generate mapstructure-to-hcl2 -type Config,nicConfig,diskConfig,vgaConfig,additionalISOsConfig
|
||||
//go:generate mapstructure-to-hcl2 -type Config,nicConfig,diskConfig,vgaConfig,storageConfig
|
||||
|
||||
package proxmox
|
||||
|
||||
@@ -8,7 +8,6 @@ import (
|
||||
"log"
|
||||
"net/url"
|
||||
"os"
|
||||
"strconv"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
@@ -61,13 +60,13 @@ type Config struct {
|
||||
CloudInit bool `mapstructure:"cloud_init"`
|
||||
CloudInitStoragePool string `mapstructure:"cloud_init_storage_pool"`
|
||||
|
||||
AdditionalISOFiles []additionalISOsConfig `mapstructure:"additional_iso_files"`
|
||||
VMInterface string `mapstructure:"vm_interface"`
|
||||
AdditionalISOFiles []storageConfig `mapstructure:"additional_iso_files"`
|
||||
VMInterface string `mapstructure:"vm_interface"`
|
||||
|
||||
Ctx interpolate.Context `mapstructure-to-hcl2:",skip"`
|
||||
}
|
||||
|
||||
type additionalISOsConfig struct {
|
||||
type storageConfig struct {
|
||||
commonsteps.ISOConfig `mapstructure:",squash"`
|
||||
Device string `mapstructure:"device"`
|
||||
ISOFile string `mapstructure:"iso_file"`
|
||||
@@ -251,58 +250,6 @@ func (c *Config) Prepare(upper interface{}, raws ...interface{}) ([]string, []st
|
||||
errs = packersdk.MultiErrorAppend(errs, fmt.Errorf("disks[%d].storage_pool_type must be specified", idx))
|
||||
}
|
||||
}
|
||||
for idx := range c.AdditionalISOFiles {
|
||||
// Check AdditionalISO config
|
||||
// Either a pre-uploaded ISO should be referenced in iso_file, OR a URL
|
||||
// (possibly to a local file) to an ISO file that will be downloaded and
|
||||
// then uploaded to Proxmox.
|
||||
if c.AdditionalISOFiles[idx].ISOFile != "" {
|
||||
c.AdditionalISOFiles[idx].ShouldUploadISO = false
|
||||
} else {
|
||||
c.AdditionalISOFiles[idx].DownloadPathKey = "downloaded_additional_iso_path_" + strconv.Itoa(idx)
|
||||
isoWarnings, isoErrors := c.AdditionalISOFiles[idx].ISOConfig.Prepare(&c.Ctx)
|
||||
errs = packersdk.MultiErrorAppend(errs, isoErrors...)
|
||||
warnings = append(warnings, isoWarnings...)
|
||||
c.AdditionalISOFiles[idx].ShouldUploadISO = true
|
||||
}
|
||||
if c.AdditionalISOFiles[idx].Device == "" {
|
||||
log.Printf("AdditionalISOFile %d Device not set, using default 'ide3'", idx)
|
||||
c.AdditionalISOFiles[idx].Device = "ide3"
|
||||
}
|
||||
if strings.HasPrefix(c.AdditionalISOFiles[idx].Device, "ide") {
|
||||
busnumber, err := strconv.Atoi(c.AdditionalISOFiles[idx].Device[3:])
|
||||
if err != nil {
|
||||
errs = packersdk.MultiErrorAppend(errs, fmt.Errorf("%s is not a valid bus index", c.AdditionalISOFiles[idx].Device[3:]))
|
||||
}
|
||||
if busnumber == 2 {
|
||||
errs = packersdk.MultiErrorAppend(errs, fmt.Errorf("IDE bus 2 is used by boot ISO"))
|
||||
}
|
||||
if busnumber > 3 {
|
||||
errs = packersdk.MultiErrorAppend(errs, fmt.Errorf("IDE bus index can't be higher than 3"))
|
||||
}
|
||||
}
|
||||
if strings.HasPrefix(c.AdditionalISOFiles[idx].Device, "sata") {
|
||||
busnumber, err := strconv.Atoi(c.AdditionalISOFiles[idx].Device[4:])
|
||||
if err != nil {
|
||||
errs = packersdk.MultiErrorAppend(errs, fmt.Errorf("%s is not a valid bus index", c.AdditionalISOFiles[idx].Device[4:]))
|
||||
}
|
||||
if busnumber > 5 {
|
||||
errs = packersdk.MultiErrorAppend(errs, fmt.Errorf("SATA bus index can't be higher than 5"))
|
||||
}
|
||||
}
|
||||
if strings.HasPrefix(c.AdditionalISOFiles[idx].Device, "scsi") {
|
||||
busnumber, err := strconv.Atoi(c.AdditionalISOFiles[idx].Device[4:])
|
||||
if err != nil {
|
||||
errs = packersdk.MultiErrorAppend(errs, fmt.Errorf("%s is not a valid bus index", c.AdditionalISOFiles[idx].Device[4:]))
|
||||
}
|
||||
if busnumber > 30 {
|
||||
errs = packersdk.MultiErrorAppend(errs, fmt.Errorf("SCSI bus index can't be higher than 30"))
|
||||
}
|
||||
}
|
||||
if (c.AdditionalISOFiles[idx].ISOFile == "" && len(c.AdditionalISOFiles[idx].ISOConfig.ISOUrls) == 0) || (c.AdditionalISOFiles[idx].ISOFile != "" && len(c.AdditionalISOFiles[idx].ISOConfig.ISOUrls) != 0) {
|
||||
errs = packersdk.MultiErrorAppend(errs, fmt.Errorf("either iso_file or iso_url, but not both, must be specified for AdditionalISO file %s", c.AdditionalISOFiles[idx].Device))
|
||||
}
|
||||
}
|
||||
|
||||
if errs != nil && len(errs.Errors) > 0 {
|
||||
return nil, warnings, errs
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
// Code generated by "mapstructure-to-hcl2 -type Config,nicConfig,diskConfig,vgaConfig,additionalISOsConfig"; DO NOT EDIT.
|
||||
// Code generated by "mapstructure-to-hcl2 -type Config,nicConfig,diskConfig,vgaConfig,storageConfig"; DO NOT EDIT.
|
||||
|
||||
package proxmox
|
||||
|
||||
@@ -10,99 +10,99 @@ import (
|
||||
// FlatConfig is an auto-generated flat version of Config.
|
||||
// Where the contents of a field with a `mapstructure:,squash` tag are bubbled up.
|
||||
type FlatConfig struct {
|
||||
PackerBuildName *string `mapstructure:"packer_build_name" cty:"packer_build_name" hcl:"packer_build_name"`
|
||||
PackerBuilderType *string `mapstructure:"packer_builder_type" cty:"packer_builder_type" hcl:"packer_builder_type"`
|
||||
PackerCoreVersion *string `mapstructure:"packer_core_version" cty:"packer_core_version" hcl:"packer_core_version"`
|
||||
PackerDebug *bool `mapstructure:"packer_debug" cty:"packer_debug" hcl:"packer_debug"`
|
||||
PackerForce *bool `mapstructure:"packer_force" cty:"packer_force" hcl:"packer_force"`
|
||||
PackerOnError *string `mapstructure:"packer_on_error" cty:"packer_on_error" hcl:"packer_on_error"`
|
||||
PackerUserVars map[string]string `mapstructure:"packer_user_variables" cty:"packer_user_variables" hcl:"packer_user_variables"`
|
||||
PackerSensitiveVars []string `mapstructure:"packer_sensitive_variables" cty:"packer_sensitive_variables" hcl:"packer_sensitive_variables"`
|
||||
HTTPDir *string `mapstructure:"http_directory" cty:"http_directory" hcl:"http_directory"`
|
||||
HTTPPortMin *int `mapstructure:"http_port_min" cty:"http_port_min" hcl:"http_port_min"`
|
||||
HTTPPortMax *int `mapstructure:"http_port_max" cty:"http_port_max" hcl:"http_port_max"`
|
||||
HTTPAddress *string `mapstructure:"http_bind_address" cty:"http_bind_address" hcl:"http_bind_address"`
|
||||
HTTPInterface *string `mapstructure:"http_interface" undocumented:"true" cty:"http_interface" hcl:"http_interface"`
|
||||
BootGroupInterval *string `mapstructure:"boot_keygroup_interval" cty:"boot_keygroup_interval" hcl:"boot_keygroup_interval"`
|
||||
BootWait *string `mapstructure:"boot_wait" cty:"boot_wait" hcl:"boot_wait"`
|
||||
BootCommand []string `mapstructure:"boot_command" cty:"boot_command" hcl:"boot_command"`
|
||||
BootKeyInterval *string `mapstructure:"boot_key_interval" cty:"boot_key_interval" hcl:"boot_key_interval"`
|
||||
Type *string `mapstructure:"communicator" cty:"communicator" hcl:"communicator"`
|
||||
PauseBeforeConnect *string `mapstructure:"pause_before_connecting" cty:"pause_before_connecting" hcl:"pause_before_connecting"`
|
||||
SSHHost *string `mapstructure:"ssh_host" cty:"ssh_host" hcl:"ssh_host"`
|
||||
SSHPort *int `mapstructure:"ssh_port" cty:"ssh_port" hcl:"ssh_port"`
|
||||
SSHUsername *string `mapstructure:"ssh_username" cty:"ssh_username" hcl:"ssh_username"`
|
||||
SSHPassword *string `mapstructure:"ssh_password" cty:"ssh_password" hcl:"ssh_password"`
|
||||
SSHKeyPairName *string `mapstructure:"ssh_keypair_name" undocumented:"true" cty:"ssh_keypair_name" hcl:"ssh_keypair_name"`
|
||||
SSHTemporaryKeyPairName *string `mapstructure:"temporary_key_pair_name" undocumented:"true" cty:"temporary_key_pair_name" hcl:"temporary_key_pair_name"`
|
||||
SSHTemporaryKeyPairType *string `mapstructure:"temporary_key_pair_type" cty:"temporary_key_pair_type" hcl:"temporary_key_pair_type"`
|
||||
SSHTemporaryKeyPairBits *int `mapstructure:"temporary_key_pair_bits" cty:"temporary_key_pair_bits" hcl:"temporary_key_pair_bits"`
|
||||
SSHCiphers []string `mapstructure:"ssh_ciphers" cty:"ssh_ciphers" hcl:"ssh_ciphers"`
|
||||
SSHClearAuthorizedKeys *bool `mapstructure:"ssh_clear_authorized_keys" cty:"ssh_clear_authorized_keys" hcl:"ssh_clear_authorized_keys"`
|
||||
SSHKEXAlgos []string `mapstructure:"ssh_key_exchange_algorithms" cty:"ssh_key_exchange_algorithms" hcl:"ssh_key_exchange_algorithms"`
|
||||
SSHPrivateKeyFile *string `mapstructure:"ssh_private_key_file" undocumented:"true" cty:"ssh_private_key_file" hcl:"ssh_private_key_file"`
|
||||
SSHCertificateFile *string `mapstructure:"ssh_certificate_file" cty:"ssh_certificate_file" hcl:"ssh_certificate_file"`
|
||||
SSHPty *bool `mapstructure:"ssh_pty" cty:"ssh_pty" hcl:"ssh_pty"`
|
||||
SSHTimeout *string `mapstructure:"ssh_timeout" cty:"ssh_timeout" hcl:"ssh_timeout"`
|
||||
SSHWaitTimeout *string `mapstructure:"ssh_wait_timeout" undocumented:"true" cty:"ssh_wait_timeout" hcl:"ssh_wait_timeout"`
|
||||
SSHAgentAuth *bool `mapstructure:"ssh_agent_auth" undocumented:"true" cty:"ssh_agent_auth" hcl:"ssh_agent_auth"`
|
||||
SSHDisableAgentForwarding *bool `mapstructure:"ssh_disable_agent_forwarding" cty:"ssh_disable_agent_forwarding" hcl:"ssh_disable_agent_forwarding"`
|
||||
SSHHandshakeAttempts *int `mapstructure:"ssh_handshake_attempts" cty:"ssh_handshake_attempts" hcl:"ssh_handshake_attempts"`
|
||||
SSHBastionHost *string `mapstructure:"ssh_bastion_host" cty:"ssh_bastion_host" hcl:"ssh_bastion_host"`
|
||||
SSHBastionPort *int `mapstructure:"ssh_bastion_port" cty:"ssh_bastion_port" hcl:"ssh_bastion_port"`
|
||||
SSHBastionAgentAuth *bool `mapstructure:"ssh_bastion_agent_auth" cty:"ssh_bastion_agent_auth" hcl:"ssh_bastion_agent_auth"`
|
||||
SSHBastionUsername *string `mapstructure:"ssh_bastion_username" cty:"ssh_bastion_username" hcl:"ssh_bastion_username"`
|
||||
SSHBastionPassword *string `mapstructure:"ssh_bastion_password" cty:"ssh_bastion_password" hcl:"ssh_bastion_password"`
|
||||
SSHBastionInteractive *bool `mapstructure:"ssh_bastion_interactive" cty:"ssh_bastion_interactive" hcl:"ssh_bastion_interactive"`
|
||||
SSHBastionPrivateKeyFile *string `mapstructure:"ssh_bastion_private_key_file" cty:"ssh_bastion_private_key_file" hcl:"ssh_bastion_private_key_file"`
|
||||
SSHBastionCertificateFile *string `mapstructure:"ssh_bastion_certificate_file" cty:"ssh_bastion_certificate_file" hcl:"ssh_bastion_certificate_file"`
|
||||
SSHFileTransferMethod *string `mapstructure:"ssh_file_transfer_method" cty:"ssh_file_transfer_method" hcl:"ssh_file_transfer_method"`
|
||||
SSHProxyHost *string `mapstructure:"ssh_proxy_host" cty:"ssh_proxy_host" hcl:"ssh_proxy_host"`
|
||||
SSHProxyPort *int `mapstructure:"ssh_proxy_port" cty:"ssh_proxy_port" hcl:"ssh_proxy_port"`
|
||||
SSHProxyUsername *string `mapstructure:"ssh_proxy_username" cty:"ssh_proxy_username" hcl:"ssh_proxy_username"`
|
||||
SSHProxyPassword *string `mapstructure:"ssh_proxy_password" cty:"ssh_proxy_password" hcl:"ssh_proxy_password"`
|
||||
SSHKeepAliveInterval *string `mapstructure:"ssh_keep_alive_interval" cty:"ssh_keep_alive_interval" hcl:"ssh_keep_alive_interval"`
|
||||
SSHReadWriteTimeout *string `mapstructure:"ssh_read_write_timeout" cty:"ssh_read_write_timeout" hcl:"ssh_read_write_timeout"`
|
||||
SSHRemoteTunnels []string `mapstructure:"ssh_remote_tunnels" cty:"ssh_remote_tunnels" hcl:"ssh_remote_tunnels"`
|
||||
SSHLocalTunnels []string `mapstructure:"ssh_local_tunnels" cty:"ssh_local_tunnels" hcl:"ssh_local_tunnels"`
|
||||
SSHPublicKey []byte `mapstructure:"ssh_public_key" undocumented:"true" cty:"ssh_public_key" hcl:"ssh_public_key"`
|
||||
SSHPrivateKey []byte `mapstructure:"ssh_private_key" undocumented:"true" cty:"ssh_private_key" hcl:"ssh_private_key"`
|
||||
WinRMUser *string `mapstructure:"winrm_username" cty:"winrm_username" hcl:"winrm_username"`
|
||||
WinRMPassword *string `mapstructure:"winrm_password" cty:"winrm_password" hcl:"winrm_password"`
|
||||
WinRMHost *string `mapstructure:"winrm_host" cty:"winrm_host" hcl:"winrm_host"`
|
||||
WinRMNoProxy *bool `mapstructure:"winrm_no_proxy" cty:"winrm_no_proxy" hcl:"winrm_no_proxy"`
|
||||
WinRMPort *int `mapstructure:"winrm_port" cty:"winrm_port" hcl:"winrm_port"`
|
||||
WinRMTimeout *string `mapstructure:"winrm_timeout" cty:"winrm_timeout" hcl:"winrm_timeout"`
|
||||
WinRMUseSSL *bool `mapstructure:"winrm_use_ssl" cty:"winrm_use_ssl" hcl:"winrm_use_ssl"`
|
||||
WinRMInsecure *bool `mapstructure:"winrm_insecure" cty:"winrm_insecure" hcl:"winrm_insecure"`
|
||||
WinRMUseNTLM *bool `mapstructure:"winrm_use_ntlm" cty:"winrm_use_ntlm" hcl:"winrm_use_ntlm"`
|
||||
ProxmoxURLRaw *string `mapstructure:"proxmox_url" cty:"proxmox_url" hcl:"proxmox_url"`
|
||||
SkipCertValidation *bool `mapstructure:"insecure_skip_tls_verify" cty:"insecure_skip_tls_verify" hcl:"insecure_skip_tls_verify"`
|
||||
Username *string `mapstructure:"username" cty:"username" hcl:"username"`
|
||||
Password *string `mapstructure:"password" cty:"password" hcl:"password"`
|
||||
Node *string `mapstructure:"node" cty:"node" hcl:"node"`
|
||||
Pool *string `mapstructure:"pool" cty:"pool" hcl:"pool"`
|
||||
VMName *string `mapstructure:"vm_name" cty:"vm_name" hcl:"vm_name"`
|
||||
VMID *int `mapstructure:"vm_id" cty:"vm_id" hcl:"vm_id"`
|
||||
Boot *string `mapstructure:"boot" cty:"boot" hcl:"boot"`
|
||||
Memory *int `mapstructure:"memory" cty:"memory" hcl:"memory"`
|
||||
Cores *int `mapstructure:"cores" cty:"cores" hcl:"cores"`
|
||||
CPUType *string `mapstructure:"cpu_type" cty:"cpu_type" hcl:"cpu_type"`
|
||||
Sockets *int `mapstructure:"sockets" cty:"sockets" hcl:"sockets"`
|
||||
OS *string `mapstructure:"os" cty:"os" hcl:"os"`
|
||||
VGA *FlatvgaConfig `mapstructure:"vga" cty:"vga" hcl:"vga"`
|
||||
NICs []FlatnicConfig `mapstructure:"network_adapters" cty:"network_adapters" hcl:"network_adapters"`
|
||||
Disks []FlatdiskConfig `mapstructure:"disks" cty:"disks" hcl:"disks"`
|
||||
Agent *bool `mapstructure:"qemu_agent" cty:"qemu_agent" hcl:"qemu_agent"`
|
||||
SCSIController *string `mapstructure:"scsi_controller" cty:"scsi_controller" hcl:"scsi_controller"`
|
||||
Onboot *bool `mapstructure:"onboot" cty:"onboot" hcl:"onboot"`
|
||||
DisableKVM *bool `mapstructure:"disable_kvm" cty:"disable_kvm" hcl:"disable_kvm"`
|
||||
TemplateName *string `mapstructure:"template_name" cty:"template_name" hcl:"template_name"`
|
||||
TemplateDescription *string `mapstructure:"template_description" cty:"template_description" hcl:"template_description"`
|
||||
CloudInit *bool `mapstructure:"cloud_init" cty:"cloud_init" hcl:"cloud_init"`
|
||||
CloudInitStoragePool *string `mapstructure:"cloud_init_storage_pool" cty:"cloud_init_storage_pool" hcl:"cloud_init_storage_pool"`
|
||||
AdditionalISOFiles []FlatadditionalISOsConfig `mapstructure:"additional_iso_files" cty:"additional_iso_files" hcl:"additional_iso_files"`
|
||||
VMInterface *string `mapstructure:"vm_interface" cty:"vm_interface" hcl:"vm_interface"`
|
||||
PackerBuildName *string `mapstructure:"packer_build_name" cty:"packer_build_name" hcl:"packer_build_name"`
|
||||
PackerBuilderType *string `mapstructure:"packer_builder_type" cty:"packer_builder_type" hcl:"packer_builder_type"`
|
||||
PackerCoreVersion *string `mapstructure:"packer_core_version" cty:"packer_core_version" hcl:"packer_core_version"`
|
||||
PackerDebug *bool `mapstructure:"packer_debug" cty:"packer_debug" hcl:"packer_debug"`
|
||||
PackerForce *bool `mapstructure:"packer_force" cty:"packer_force" hcl:"packer_force"`
|
||||
PackerOnError *string `mapstructure:"packer_on_error" cty:"packer_on_error" hcl:"packer_on_error"`
|
||||
PackerUserVars map[string]string `mapstructure:"packer_user_variables" cty:"packer_user_variables" hcl:"packer_user_variables"`
|
||||
PackerSensitiveVars []string `mapstructure:"packer_sensitive_variables" cty:"packer_sensitive_variables" hcl:"packer_sensitive_variables"`
|
||||
HTTPDir *string `mapstructure:"http_directory" cty:"http_directory" hcl:"http_directory"`
|
||||
HTTPPortMin *int `mapstructure:"http_port_min" cty:"http_port_min" hcl:"http_port_min"`
|
||||
HTTPPortMax *int `mapstructure:"http_port_max" cty:"http_port_max" hcl:"http_port_max"`
|
||||
HTTPAddress *string `mapstructure:"http_bind_address" cty:"http_bind_address" hcl:"http_bind_address"`
|
||||
HTTPInterface *string `mapstructure:"http_interface" undocumented:"true" cty:"http_interface" hcl:"http_interface"`
|
||||
BootGroupInterval *string `mapstructure:"boot_keygroup_interval" cty:"boot_keygroup_interval" hcl:"boot_keygroup_interval"`
|
||||
BootWait *string `mapstructure:"boot_wait" cty:"boot_wait" hcl:"boot_wait"`
|
||||
BootCommand []string `mapstructure:"boot_command" cty:"boot_command" hcl:"boot_command"`
|
||||
BootKeyInterval *string `mapstructure:"boot_key_interval" cty:"boot_key_interval" hcl:"boot_key_interval"`
|
||||
Type *string `mapstructure:"communicator" cty:"communicator" hcl:"communicator"`
|
||||
PauseBeforeConnect *string `mapstructure:"pause_before_connecting" cty:"pause_before_connecting" hcl:"pause_before_connecting"`
|
||||
SSHHost *string `mapstructure:"ssh_host" cty:"ssh_host" hcl:"ssh_host"`
|
||||
SSHPort *int `mapstructure:"ssh_port" cty:"ssh_port" hcl:"ssh_port"`
|
||||
SSHUsername *string `mapstructure:"ssh_username" cty:"ssh_username" hcl:"ssh_username"`
|
||||
SSHPassword *string `mapstructure:"ssh_password" cty:"ssh_password" hcl:"ssh_password"`
|
||||
SSHKeyPairName *string `mapstructure:"ssh_keypair_name" undocumented:"true" cty:"ssh_keypair_name" hcl:"ssh_keypair_name"`
|
||||
SSHTemporaryKeyPairName *string `mapstructure:"temporary_key_pair_name" undocumented:"true" cty:"temporary_key_pair_name" hcl:"temporary_key_pair_name"`
|
||||
SSHTemporaryKeyPairType *string `mapstructure:"temporary_key_pair_type" cty:"temporary_key_pair_type" hcl:"temporary_key_pair_type"`
|
||||
SSHTemporaryKeyPairBits *int `mapstructure:"temporary_key_pair_bits" cty:"temporary_key_pair_bits" hcl:"temporary_key_pair_bits"`
|
||||
SSHCiphers []string `mapstructure:"ssh_ciphers" cty:"ssh_ciphers" hcl:"ssh_ciphers"`
|
||||
SSHClearAuthorizedKeys *bool `mapstructure:"ssh_clear_authorized_keys" cty:"ssh_clear_authorized_keys" hcl:"ssh_clear_authorized_keys"`
|
||||
SSHKEXAlgos []string `mapstructure:"ssh_key_exchange_algorithms" cty:"ssh_key_exchange_algorithms" hcl:"ssh_key_exchange_algorithms"`
|
||||
SSHPrivateKeyFile *string `mapstructure:"ssh_private_key_file" undocumented:"true" cty:"ssh_private_key_file" hcl:"ssh_private_key_file"`
|
||||
SSHCertificateFile *string `mapstructure:"ssh_certificate_file" cty:"ssh_certificate_file" hcl:"ssh_certificate_file"`
|
||||
SSHPty *bool `mapstructure:"ssh_pty" cty:"ssh_pty" hcl:"ssh_pty"`
|
||||
SSHTimeout *string `mapstructure:"ssh_timeout" cty:"ssh_timeout" hcl:"ssh_timeout"`
|
||||
SSHWaitTimeout *string `mapstructure:"ssh_wait_timeout" undocumented:"true" cty:"ssh_wait_timeout" hcl:"ssh_wait_timeout"`
|
||||
SSHAgentAuth *bool `mapstructure:"ssh_agent_auth" undocumented:"true" cty:"ssh_agent_auth" hcl:"ssh_agent_auth"`
|
||||
SSHDisableAgentForwarding *bool `mapstructure:"ssh_disable_agent_forwarding" cty:"ssh_disable_agent_forwarding" hcl:"ssh_disable_agent_forwarding"`
|
||||
SSHHandshakeAttempts *int `mapstructure:"ssh_handshake_attempts" cty:"ssh_handshake_attempts" hcl:"ssh_handshake_attempts"`
|
||||
SSHBastionHost *string `mapstructure:"ssh_bastion_host" cty:"ssh_bastion_host" hcl:"ssh_bastion_host"`
|
||||
SSHBastionPort *int `mapstructure:"ssh_bastion_port" cty:"ssh_bastion_port" hcl:"ssh_bastion_port"`
|
||||
SSHBastionAgentAuth *bool `mapstructure:"ssh_bastion_agent_auth" cty:"ssh_bastion_agent_auth" hcl:"ssh_bastion_agent_auth"`
|
||||
SSHBastionUsername *string `mapstructure:"ssh_bastion_username" cty:"ssh_bastion_username" hcl:"ssh_bastion_username"`
|
||||
SSHBastionPassword *string `mapstructure:"ssh_bastion_password" cty:"ssh_bastion_password" hcl:"ssh_bastion_password"`
|
||||
SSHBastionInteractive *bool `mapstructure:"ssh_bastion_interactive" cty:"ssh_bastion_interactive" hcl:"ssh_bastion_interactive"`
|
||||
SSHBastionPrivateKeyFile *string `mapstructure:"ssh_bastion_private_key_file" cty:"ssh_bastion_private_key_file" hcl:"ssh_bastion_private_key_file"`
|
||||
SSHBastionCertificateFile *string `mapstructure:"ssh_bastion_certificate_file" cty:"ssh_bastion_certificate_file" hcl:"ssh_bastion_certificate_file"`
|
||||
SSHFileTransferMethod *string `mapstructure:"ssh_file_transfer_method" cty:"ssh_file_transfer_method" hcl:"ssh_file_transfer_method"`
|
||||
SSHProxyHost *string `mapstructure:"ssh_proxy_host" cty:"ssh_proxy_host" hcl:"ssh_proxy_host"`
|
||||
SSHProxyPort *int `mapstructure:"ssh_proxy_port" cty:"ssh_proxy_port" hcl:"ssh_proxy_port"`
|
||||
SSHProxyUsername *string `mapstructure:"ssh_proxy_username" cty:"ssh_proxy_username" hcl:"ssh_proxy_username"`
|
||||
SSHProxyPassword *string `mapstructure:"ssh_proxy_password" cty:"ssh_proxy_password" hcl:"ssh_proxy_password"`
|
||||
SSHKeepAliveInterval *string `mapstructure:"ssh_keep_alive_interval" cty:"ssh_keep_alive_interval" hcl:"ssh_keep_alive_interval"`
|
||||
SSHReadWriteTimeout *string `mapstructure:"ssh_read_write_timeout" cty:"ssh_read_write_timeout" hcl:"ssh_read_write_timeout"`
|
||||
SSHRemoteTunnels []string `mapstructure:"ssh_remote_tunnels" cty:"ssh_remote_tunnels" hcl:"ssh_remote_tunnels"`
|
||||
SSHLocalTunnels []string `mapstructure:"ssh_local_tunnels" cty:"ssh_local_tunnels" hcl:"ssh_local_tunnels"`
|
||||
SSHPublicKey []byte `mapstructure:"ssh_public_key" undocumented:"true" cty:"ssh_public_key" hcl:"ssh_public_key"`
|
||||
SSHPrivateKey []byte `mapstructure:"ssh_private_key" undocumented:"true" cty:"ssh_private_key" hcl:"ssh_private_key"`
|
||||
WinRMUser *string `mapstructure:"winrm_username" cty:"winrm_username" hcl:"winrm_username"`
|
||||
WinRMPassword *string `mapstructure:"winrm_password" cty:"winrm_password" hcl:"winrm_password"`
|
||||
WinRMHost *string `mapstructure:"winrm_host" cty:"winrm_host" hcl:"winrm_host"`
|
||||
WinRMNoProxy *bool `mapstructure:"winrm_no_proxy" cty:"winrm_no_proxy" hcl:"winrm_no_proxy"`
|
||||
WinRMPort *int `mapstructure:"winrm_port" cty:"winrm_port" hcl:"winrm_port"`
|
||||
WinRMTimeout *string `mapstructure:"winrm_timeout" cty:"winrm_timeout" hcl:"winrm_timeout"`
|
||||
WinRMUseSSL *bool `mapstructure:"winrm_use_ssl" cty:"winrm_use_ssl" hcl:"winrm_use_ssl"`
|
||||
WinRMInsecure *bool `mapstructure:"winrm_insecure" cty:"winrm_insecure" hcl:"winrm_insecure"`
|
||||
WinRMUseNTLM *bool `mapstructure:"winrm_use_ntlm" cty:"winrm_use_ntlm" hcl:"winrm_use_ntlm"`
|
||||
ProxmoxURLRaw *string `mapstructure:"proxmox_url" cty:"proxmox_url" hcl:"proxmox_url"`
|
||||
SkipCertValidation *bool `mapstructure:"insecure_skip_tls_verify" cty:"insecure_skip_tls_verify" hcl:"insecure_skip_tls_verify"`
|
||||
Username *string `mapstructure:"username" cty:"username" hcl:"username"`
|
||||
Password *string `mapstructure:"password" cty:"password" hcl:"password"`
|
||||
Node *string `mapstructure:"node" cty:"node" hcl:"node"`
|
||||
Pool *string `mapstructure:"pool" cty:"pool" hcl:"pool"`
|
||||
VMName *string `mapstructure:"vm_name" cty:"vm_name" hcl:"vm_name"`
|
||||
VMID *int `mapstructure:"vm_id" cty:"vm_id" hcl:"vm_id"`
|
||||
Boot *string `mapstructure:"boot" cty:"boot" hcl:"boot"`
|
||||
Memory *int `mapstructure:"memory" cty:"memory" hcl:"memory"`
|
||||
Cores *int `mapstructure:"cores" cty:"cores" hcl:"cores"`
|
||||
CPUType *string `mapstructure:"cpu_type" cty:"cpu_type" hcl:"cpu_type"`
|
||||
Sockets *int `mapstructure:"sockets" cty:"sockets" hcl:"sockets"`
|
||||
OS *string `mapstructure:"os" cty:"os" hcl:"os"`
|
||||
VGA *FlatvgaConfig `mapstructure:"vga" cty:"vga" hcl:"vga"`
|
||||
NICs []FlatnicConfig `mapstructure:"network_adapters" cty:"network_adapters" hcl:"network_adapters"`
|
||||
Disks []FlatdiskConfig `mapstructure:"disks" cty:"disks" hcl:"disks"`
|
||||
Agent *bool `mapstructure:"qemu_agent" cty:"qemu_agent" hcl:"qemu_agent"`
|
||||
SCSIController *string `mapstructure:"scsi_controller" cty:"scsi_controller" hcl:"scsi_controller"`
|
||||
Onboot *bool `mapstructure:"onboot" cty:"onboot" hcl:"onboot"`
|
||||
DisableKVM *bool `mapstructure:"disable_kvm" cty:"disable_kvm" hcl:"disable_kvm"`
|
||||
TemplateName *string `mapstructure:"template_name" cty:"template_name" hcl:"template_name"`
|
||||
TemplateDescription *string `mapstructure:"template_description" cty:"template_description" hcl:"template_description"`
|
||||
CloudInit *bool `mapstructure:"cloud_init" cty:"cloud_init" hcl:"cloud_init"`
|
||||
CloudInitStoragePool *string `mapstructure:"cloud_init_storage_pool" cty:"cloud_init_storage_pool" hcl:"cloud_init_storage_pool"`
|
||||
AdditionalISOFiles []FlatstorageConfig `mapstructure:"additional_iso_files" cty:"additional_iso_files" hcl:"additional_iso_files"`
|
||||
VMInterface *string `mapstructure:"vm_interface" cty:"vm_interface" hcl:"vm_interface"`
|
||||
}
|
||||
|
||||
// FlatMapstructure returns a new FlatConfig.
|
||||
@@ -208,55 +208,12 @@ func (*FlatConfig) HCL2Spec() map[string]hcldec.Spec {
|
||||
"template_description": &hcldec.AttrSpec{Name: "template_description", Type: cty.String, Required: false},
|
||||
"cloud_init": &hcldec.AttrSpec{Name: "cloud_init", Type: cty.Bool, Required: false},
|
||||
"cloud_init_storage_pool": &hcldec.AttrSpec{Name: "cloud_init_storage_pool", Type: cty.String, Required: false},
|
||||
"additional_iso_files": &hcldec.BlockListSpec{TypeName: "additional_iso_files", Nested: hcldec.ObjectSpec((*FlatadditionalISOsConfig)(nil).HCL2Spec())},
|
||||
"additional_iso_files": &hcldec.BlockListSpec{TypeName: "additional_iso_files", Nested: hcldec.ObjectSpec((*FlatstorageConfig)(nil).HCL2Spec())},
|
||||
"vm_interface": &hcldec.AttrSpec{Name: "vm_interface", Type: cty.String, Required: false},
|
||||
}
|
||||
return s
|
||||
}
|
||||
|
||||
// FlatadditionalISOsConfig is an auto-generated flat version of additionalISOsConfig.
|
||||
// Where the contents of a field with a `mapstructure:,squash` tag are bubbled up.
|
||||
type FlatadditionalISOsConfig struct {
|
||||
ISOChecksum *string `mapstructure:"iso_checksum" required:"true" cty:"iso_checksum" hcl:"iso_checksum"`
|
||||
RawSingleISOUrl *string `mapstructure:"iso_url" required:"true" cty:"iso_url" hcl:"iso_url"`
|
||||
ISOUrls []string `mapstructure:"iso_urls" cty:"iso_urls" hcl:"iso_urls"`
|
||||
TargetPath *string `mapstructure:"iso_target_path" cty:"iso_target_path" hcl:"iso_target_path"`
|
||||
TargetExtension *string `mapstructure:"iso_target_extension" cty:"iso_target_extension" hcl:"iso_target_extension"`
|
||||
Device *string `mapstructure:"device" cty:"device" hcl:"device"`
|
||||
ISOFile *string `mapstructure:"iso_file" cty:"iso_file" hcl:"iso_file"`
|
||||
ISOStoragePool *string `mapstructure:"iso_storage_pool" cty:"iso_storage_pool" hcl:"iso_storage_pool"`
|
||||
Unmount *bool `mapstructure:"unmount" cty:"unmount" hcl:"unmount"`
|
||||
ShouldUploadISO *bool `cty:"should_upload_iso" hcl:"should_upload_iso"`
|
||||
DownloadPathKey *string `cty:"download_path_key" hcl:"download_path_key"`
|
||||
}
|
||||
|
||||
// FlatMapstructure returns a new FlatadditionalISOsConfig.
|
||||
// FlatadditionalISOsConfig is an auto-generated flat version of additionalISOsConfig.
|
||||
// Where the contents a fields with a `mapstructure:,squash` tag are bubbled up.
|
||||
func (*additionalISOsConfig) FlatMapstructure() interface{ HCL2Spec() map[string]hcldec.Spec } {
|
||||
return new(FlatadditionalISOsConfig)
|
||||
}
|
||||
|
||||
// HCL2Spec returns the hcl spec of a additionalISOsConfig.
|
||||
// This spec is used by HCL to read the fields of additionalISOsConfig.
|
||||
// The decoded values from this spec will then be applied to a FlatadditionalISOsConfig.
|
||||
func (*FlatadditionalISOsConfig) HCL2Spec() map[string]hcldec.Spec {
|
||||
s := map[string]hcldec.Spec{
|
||||
"iso_checksum": &hcldec.AttrSpec{Name: "iso_checksum", Type: cty.String, Required: false},
|
||||
"iso_url": &hcldec.AttrSpec{Name: "iso_url", Type: cty.String, Required: false},
|
||||
"iso_urls": &hcldec.AttrSpec{Name: "iso_urls", Type: cty.List(cty.String), Required: false},
|
||||
"iso_target_path": &hcldec.AttrSpec{Name: "iso_target_path", Type: cty.String, Required: false},
|
||||
"iso_target_extension": &hcldec.AttrSpec{Name: "iso_target_extension", Type: cty.String, Required: false},
|
||||
"device": &hcldec.AttrSpec{Name: "device", Type: cty.String, Required: false},
|
||||
"iso_file": &hcldec.AttrSpec{Name: "iso_file", Type: cty.String, Required: false},
|
||||
"iso_storage_pool": &hcldec.AttrSpec{Name: "iso_storage_pool", Type: cty.String, Required: false},
|
||||
"unmount": &hcldec.AttrSpec{Name: "unmount", Type: cty.Bool, Required: false},
|
||||
"should_upload_iso": &hcldec.AttrSpec{Name: "should_upload_iso", Type: cty.Bool, Required: false},
|
||||
"download_path_key": &hcldec.AttrSpec{Name: "download_path_key", Type: cty.String, Required: false},
|
||||
}
|
||||
return s
|
||||
}
|
||||
|
||||
// FlatdiskConfig is an auto-generated flat version of diskConfig.
|
||||
// Where the contents of a field with a `mapstructure:,squash` tag are bubbled up.
|
||||
type FlatdiskConfig struct {
|
||||
@@ -325,6 +282,49 @@ func (*FlatnicConfig) HCL2Spec() map[string]hcldec.Spec {
|
||||
return s
|
||||
}
|
||||
|
||||
// FlatstorageConfig is an auto-generated flat version of storageConfig.
|
||||
// Where the contents of a field with a `mapstructure:,squash` tag are bubbled up.
|
||||
type FlatstorageConfig struct {
|
||||
ISOChecksum *string `mapstructure:"iso_checksum" required:"true" cty:"iso_checksum" hcl:"iso_checksum"`
|
||||
RawSingleISOUrl *string `mapstructure:"iso_url" required:"true" cty:"iso_url" hcl:"iso_url"`
|
||||
ISOUrls []string `mapstructure:"iso_urls" cty:"iso_urls" hcl:"iso_urls"`
|
||||
TargetPath *string `mapstructure:"iso_target_path" cty:"iso_target_path" hcl:"iso_target_path"`
|
||||
TargetExtension *string `mapstructure:"iso_target_extension" cty:"iso_target_extension" hcl:"iso_target_extension"`
|
||||
Device *string `mapstructure:"device" cty:"device" hcl:"device"`
|
||||
ISOFile *string `mapstructure:"iso_file" cty:"iso_file" hcl:"iso_file"`
|
||||
ISOStoragePool *string `mapstructure:"iso_storage_pool" cty:"iso_storage_pool" hcl:"iso_storage_pool"`
|
||||
Unmount *bool `mapstructure:"unmount" cty:"unmount" hcl:"unmount"`
|
||||
ShouldUploadISO *bool `cty:"should_upload_iso" hcl:"should_upload_iso"`
|
||||
DownloadPathKey *string `cty:"download_path_key" hcl:"download_path_key"`
|
||||
}
|
||||
|
||||
// FlatMapstructure returns a new FlatstorageConfig.
|
||||
// FlatstorageConfig is an auto-generated flat version of storageConfig.
|
||||
// Where the contents a fields with a `mapstructure:,squash` tag are bubbled up.
|
||||
func (*storageConfig) FlatMapstructure() interface{ HCL2Spec() map[string]hcldec.Spec } {
|
||||
return new(FlatstorageConfig)
|
||||
}
|
||||
|
||||
// HCL2Spec returns the hcl spec of a storageConfig.
|
||||
// This spec is used by HCL to read the fields of storageConfig.
|
||||
// The decoded values from this spec will then be applied to a FlatstorageConfig.
|
||||
func (*FlatstorageConfig) HCL2Spec() map[string]hcldec.Spec {
|
||||
s := map[string]hcldec.Spec{
|
||||
"iso_checksum": &hcldec.AttrSpec{Name: "iso_checksum", Type: cty.String, Required: false},
|
||||
"iso_url": &hcldec.AttrSpec{Name: "iso_url", Type: cty.String, Required: false},
|
||||
"iso_urls": &hcldec.AttrSpec{Name: "iso_urls", Type: cty.List(cty.String), Required: false},
|
||||
"iso_target_path": &hcldec.AttrSpec{Name: "iso_target_path", Type: cty.String, Required: false},
|
||||
"iso_target_extension": &hcldec.AttrSpec{Name: "iso_target_extension", Type: cty.String, Required: false},
|
||||
"device": &hcldec.AttrSpec{Name: "device", Type: cty.String, Required: false},
|
||||
"iso_file": &hcldec.AttrSpec{Name: "iso_file", Type: cty.String, Required: false},
|
||||
"iso_storage_pool": &hcldec.AttrSpec{Name: "iso_storage_pool", Type: cty.String, Required: false},
|
||||
"unmount": &hcldec.AttrSpec{Name: "unmount", Type: cty.Bool, Required: false},
|
||||
"should_upload_iso": &hcldec.AttrSpec{Name: "should_upload_iso", Type: cty.Bool, Required: false},
|
||||
"download_path_key": &hcldec.AttrSpec{Name: "download_path_key", Type: cty.String, Required: false},
|
||||
}
|
||||
return s
|
||||
}
|
||||
|
||||
// FlatvgaConfig is an auto-generated flat version of vgaConfig.
|
||||
// Where the contents of a field with a `mapstructure:,squash` tag are bubbled up.
|
||||
type FlatvgaConfig struct {
|
||||
|
||||
@@ -80,30 +80,6 @@ func (s *stepFinalizeTemplateConfig) Run(ctx context.Context, state multistep.St
|
||||
}
|
||||
}
|
||||
|
||||
if len(c.AdditionalISOFiles) > 0 {
|
||||
vmParams, err := client.GetVmConfig(vmRef)
|
||||
if err != nil {
|
||||
err := fmt.Errorf("Error fetching template config: %s", err)
|
||||
state.Put("error", err)
|
||||
ui.Error(err.Error())
|
||||
return multistep.ActionHalt
|
||||
}
|
||||
for idx := range c.AdditionalISOFiles {
|
||||
cdrom := c.AdditionalISOFiles[idx].Device
|
||||
if c.AdditionalISOFiles[idx].Unmount {
|
||||
if vmParams[cdrom] == nil || !strings.Contains(vmParams[cdrom].(string), "media=cdrom") {
|
||||
err := fmt.Errorf("Cannot eject ISO from cdrom drive, %s is not present or not a cdrom media", cdrom)
|
||||
state.Put("error", err)
|
||||
ui.Error(err.Error())
|
||||
return multistep.ActionHalt
|
||||
}
|
||||
changes[cdrom] = "none,media=cdrom"
|
||||
} else {
|
||||
changes[cdrom] = c.AdditionalISOFiles[idx].ISOFile + ",media=cdrom"
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if len(changes) > 0 {
|
||||
_, err := client.SetVmConfig(vmRef, changes)
|
||||
if err != nil {
|
||||
|
||||
@@ -3,6 +3,7 @@ package proxmox
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"log"
|
||||
"strconv"
|
||||
|
||||
"github.com/Telmate/proxmox-api-go/proxmox"
|
||||
@@ -21,15 +22,10 @@ type stepStartVM struct {
|
||||
type ProxmoxVMCreator interface {
|
||||
Create(*proxmox.VmRef, proxmox.ConfigQemu, multistep.StateBag) error
|
||||
}
|
||||
type vmStarter interface {
|
||||
GetNextID(int) (int, error)
|
||||
StartVm(*proxmox.VmRef) (string, error)
|
||||
SetVmConfig(*proxmox.VmRef, map[string]interface{}) (interface{}, error)
|
||||
}
|
||||
|
||||
func (s *stepStartVM) Run(ctx context.Context, state multistep.StateBag) multistep.StepAction {
|
||||
ui := state.Get("ui").(packersdk.Ui)
|
||||
client := state.Get("proxmoxClient").(vmStarter)
|
||||
client := state.Get("proxmoxClient").(*proxmox.Client)
|
||||
c := state.Get("config").(*Config)
|
||||
|
||||
agent := 1
|
||||
@@ -63,14 +59,21 @@ func (s *stepStartVM) Run(ctx context.Context, state multistep.StateBag) multist
|
||||
|
||||
if c.VMID == 0 {
|
||||
ui.Say("No VM ID given, getting next free from Proxmox")
|
||||
id, err := client.GetNextID(0)
|
||||
if err != nil {
|
||||
for n := 0; n < 5; n++ {
|
||||
id, err := proxmox.MaxVmId(client)
|
||||
if err != nil {
|
||||
log.Printf("Error getting max used VM ID: %v (attempt %d/5)", err, n+1)
|
||||
continue
|
||||
}
|
||||
c.VMID = id + 1
|
||||
break
|
||||
}
|
||||
if c.VMID == 0 {
|
||||
err := fmt.Errorf("Failed to get free VM ID")
|
||||
state.Put("error", err)
|
||||
ui.Error(err.Error())
|
||||
return multistep.ActionHalt
|
||||
}
|
||||
c.VMID = id
|
||||
config.VmID = id
|
||||
}
|
||||
vmRef := proxmox.NewVmRef(c.VMID)
|
||||
vmRef.SetNode(c.Node)
|
||||
@@ -86,22 +89,6 @@ func (s *stepStartVM) Run(ctx context.Context, state multistep.StateBag) multist
|
||||
return multistep.ActionHalt
|
||||
}
|
||||
|
||||
// proxmox-api-go assumes all QemuDisks are actually hard disks, not cd
|
||||
// drives, so we need to add them via a config update
|
||||
if len(c.AdditionalISOFiles) > 0 {
|
||||
addISOConfig := make(map[string]interface{})
|
||||
for _, iso := range c.AdditionalISOFiles {
|
||||
addISOConfig[iso.Device] = fmt.Sprintf("%s,media=cdrom", iso.ISOFile)
|
||||
}
|
||||
_, err := client.SetVmConfig(vmRef, addISOConfig)
|
||||
if err != nil {
|
||||
err := fmt.Errorf("Error updating template: %s", err)
|
||||
state.Put("error", err)
|
||||
ui.Error(err.Error())
|
||||
return multistep.ActionHalt
|
||||
}
|
||||
}
|
||||
|
||||
// Store the vm id for later
|
||||
state.Put("vmRef", vmRef)
|
||||
// instance_id is the generic term used so that users can have access to the
|
||||
|
||||
@@ -1,7 +1,6 @@
|
||||
package proxmox
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"testing"
|
||||
|
||||
@@ -107,80 +106,3 @@ func TestCleanupStartVM(t *testing.T) {
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
type startVMMock struct {
|
||||
create func(*proxmox.VmRef, proxmox.ConfigQemu, multistep.StateBag) error
|
||||
startVm func(*proxmox.VmRef) (string, error)
|
||||
setVmConfig func(*proxmox.VmRef, map[string]interface{}) (interface{}, error)
|
||||
}
|
||||
|
||||
func (m *startVMMock) Create(vmRef *proxmox.VmRef, config proxmox.ConfigQemu, state multistep.StateBag) error {
|
||||
return m.create(vmRef, config, state)
|
||||
}
|
||||
func (m *startVMMock) StartVm(vmRef *proxmox.VmRef) (string, error) {
|
||||
return m.startVm(vmRef)
|
||||
}
|
||||
func (m *startVMMock) SetVmConfig(vmRef *proxmox.VmRef, config map[string]interface{}) (interface{}, error) {
|
||||
return m.setVmConfig(vmRef, config)
|
||||
}
|
||||
func (m *startVMMock) GetNextID(int) (int, error) {
|
||||
return 1, nil
|
||||
}
|
||||
|
||||
func TestStartVM(t *testing.T) {
|
||||
// TODO: proxmox-api-go does a lot of manipulation on the input and does not
|
||||
// give any way to access the actual data it sends to the Proxmox server,
|
||||
// which means writing good tests here is quite hard. This test is mainly a
|
||||
// stub to revisit when we can write better tests.
|
||||
cs := []struct {
|
||||
name string
|
||||
config *Config
|
||||
expectedAction multistep.StepAction
|
||||
}{
|
||||
{
|
||||
name: "Example config from documentation works",
|
||||
config: &Config{
|
||||
Disks: []diskConfig{
|
||||
{
|
||||
Type: "sata",
|
||||
Size: "10G",
|
||||
StoragePool: "local",
|
||||
StoragePoolType: "lvm",
|
||||
},
|
||||
},
|
||||
NICs: []nicConfig{
|
||||
{
|
||||
Bridge: "vmbr0",
|
||||
},
|
||||
},
|
||||
},
|
||||
expectedAction: multistep.ActionContinue,
|
||||
},
|
||||
}
|
||||
|
||||
for _, c := range cs {
|
||||
t.Run(c.name, func(t *testing.T) {
|
||||
mock := &startVMMock{
|
||||
create: func(vmRef *proxmox.VmRef, config proxmox.ConfigQemu, state multistep.StateBag) error {
|
||||
return nil
|
||||
},
|
||||
startVm: func(*proxmox.VmRef) (string, error) {
|
||||
return "", nil
|
||||
},
|
||||
setVmConfig: func(*proxmox.VmRef, map[string]interface{}) (interface{}, error) {
|
||||
return nil, nil
|
||||
},
|
||||
}
|
||||
state := new(multistep.BasicStateBag)
|
||||
state.Put("ui", packersdk.TestUi(t))
|
||||
state.Put("config", c.config)
|
||||
state.Put("proxmoxClient", mock)
|
||||
s := stepStartVM{vmCreator: mock}
|
||||
|
||||
action := s.Run(context.TODO(), state)
|
||||
if action != c.expectedAction {
|
||||
t.Errorf("Expected action %s, got %s", c.expectedAction, action)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
@@ -43,8 +43,19 @@ func (b *Builder) Run(ctx context.Context, ui packersdk.Ui, hook packersdk.Hook)
|
||||
Url: b.config.ISOUrls,
|
||||
},
|
||||
}
|
||||
for idx := range b.config.AdditionalISOFiles {
|
||||
preSteps = append(preSteps, &commonsteps.StepDownload{
|
||||
Checksum: b.config.AdditionalISOFiles[idx].ISOChecksum,
|
||||
Description: "additional ISO",
|
||||
Extension: b.config.AdditionalISOFiles[idx].TargetExtension,
|
||||
ResultKey: b.config.AdditionalISOFiles[idx].DownloadPathKey,
|
||||
TargetPath: b.config.AdditionalISOFiles[idx].DownloadPathKey,
|
||||
Url: b.config.AdditionalISOFiles[idx].ISOUrls,
|
||||
})
|
||||
}
|
||||
preSteps = append(preSteps,
|
||||
&stepUploadISO{},
|
||||
&stepUploadAdditionalISOs{},
|
||||
)
|
||||
postSteps := []multistep.Step{
|
||||
&stepFinalizeISOTemplate{},
|
||||
|
||||
@@ -1,9 +1,13 @@
|
||||
//go:generate mapstructure-to-hcl2 -type Config,nicConfig,diskConfig,vgaConfig,additionalISOsConfig
|
||||
//go:generate mapstructure-to-hcl2 -type Config,nicConfig,diskConfig,vgaConfig
|
||||
|
||||
package proxmoxiso
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"fmt"
|
||||
"log"
|
||||
"strconv"
|
||||
"strings"
|
||||
|
||||
"github.com/hashicorp/packer-plugin-sdk/multistep/commonsteps"
|
||||
packersdk "github.com/hashicorp/packer-plugin-sdk/packer"
|
||||
@@ -47,6 +51,62 @@ func (c *Config) Prepare(raws ...interface{}) ([]string, []string, error) {
|
||||
errs = packersdk.MultiErrorAppend(errs, errors.New("when specifying iso_url, iso_storage_pool must also be specified"))
|
||||
}
|
||||
|
||||
for idx := range c.AdditionalISOFiles {
|
||||
// Check AdditionalISO config
|
||||
// Either a pre-uploaded ISO should be referenced in iso_file, OR a URL
|
||||
// (possibly to a local file) to an ISO file that will be downloaded and
|
||||
// then uploaded to Proxmox.
|
||||
if c.AdditionalISOFiles[idx].ISOFile != "" {
|
||||
c.AdditionalISOFiles[idx].ShouldUploadISO = false
|
||||
} else {
|
||||
c.AdditionalISOFiles[idx].DownloadPathKey = "downloaded_additional_iso_path_" + strconv.Itoa(idx)
|
||||
isoWarnings, isoErrors := c.AdditionalISOFiles[idx].ISOConfig.Prepare(&c.Ctx)
|
||||
errs = packersdk.MultiErrorAppend(errs, isoErrors...)
|
||||
warnings = append(warnings, isoWarnings...)
|
||||
c.AdditionalISOFiles[idx].ShouldUploadISO = true
|
||||
}
|
||||
if c.AdditionalISOFiles[idx].Device == "" {
|
||||
log.Printf("AdditionalISOFile %d Device not set, using default 'ide3'", idx)
|
||||
c.AdditionalISOFiles[idx].Device = "ide3"
|
||||
}
|
||||
if strings.HasPrefix(c.AdditionalISOFiles[idx].Device, "ide") {
|
||||
busnumber, err := strconv.Atoi(c.AdditionalISOFiles[idx].Device[3:])
|
||||
if err != nil {
|
||||
errs = packersdk.MultiErrorAppend(errs, fmt.Errorf("%s is not a valid bus index", c.AdditionalISOFiles[idx].Device[3:]))
|
||||
}
|
||||
if busnumber == 2 {
|
||||
errs = packersdk.MultiErrorAppend(errs, fmt.Errorf("IDE bus 2 is used by boot ISO"))
|
||||
}
|
||||
if busnumber > 3 {
|
||||
errs = packersdk.MultiErrorAppend(errs, fmt.Errorf("IDE bus index can't be higher than 3"))
|
||||
}
|
||||
}
|
||||
if strings.HasPrefix(c.AdditionalISOFiles[idx].Device, "sata") {
|
||||
busnumber, err := strconv.Atoi(c.AdditionalISOFiles[idx].Device[4:])
|
||||
if err != nil {
|
||||
errs = packersdk.MultiErrorAppend(errs, fmt.Errorf("%s is not a valid bus index", c.AdditionalISOFiles[idx].Device[4:]))
|
||||
}
|
||||
if busnumber > 5 {
|
||||
errs = packersdk.MultiErrorAppend(errs, fmt.Errorf("SATA bus index can't be higher than 5"))
|
||||
}
|
||||
}
|
||||
if strings.HasPrefix(c.AdditionalISOFiles[idx].Device, "scsi") {
|
||||
busnumber, err := strconv.Atoi(c.AdditionalISOFiles[idx].Device[4:])
|
||||
if err != nil {
|
||||
errs = packersdk.MultiErrorAppend(errs, fmt.Errorf("%s is not a valid bus index", c.AdditionalISOFiles[idx].Device[4:]))
|
||||
}
|
||||
if busnumber > 30 {
|
||||
errs = packersdk.MultiErrorAppend(errs, fmt.Errorf("SCSI bus index can't be higher than 30"))
|
||||
}
|
||||
}
|
||||
if (c.AdditionalISOFiles[idx].ISOFile == "" && len(c.AdditionalISOFiles[idx].ISOConfig.ISOUrls) == 0) || (c.AdditionalISOFiles[idx].ISOFile != "" && len(c.AdditionalISOFiles[idx].ISOConfig.ISOUrls) != 0) {
|
||||
errs = packersdk.MultiErrorAppend(errs, fmt.Errorf("either iso_file or iso_url, but not both, must be specified for AdditionalISO file %s", c.AdditionalISOFiles[idx].Device))
|
||||
}
|
||||
if len(c.ISOConfig.ISOUrls) != 0 && c.ISOStoragePool == "" {
|
||||
errs = packersdk.MultiErrorAppend(errs, errors.New("when specifying iso_url, iso_storage_pool must also be specified"))
|
||||
}
|
||||
}
|
||||
|
||||
if errs != nil && len(errs.Errors) > 0 {
|
||||
return nil, warnings, errs
|
||||
}
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
// Code generated by "mapstructure-to-hcl2 -type Config,nicConfig,diskConfig,vgaConfig,additionalISOsConfig"; DO NOT EDIT.
|
||||
// Code generated by "mapstructure-to-hcl2 -type Config,nicConfig,diskConfig,vgaConfig"; DO NOT EDIT.
|
||||
|
||||
package proxmoxiso
|
||||
|
||||
@@ -11,107 +11,107 @@ import (
|
||||
// FlatConfig is an auto-generated flat version of Config.
|
||||
// Where the contents of a field with a `mapstructure:,squash` tag are bubbled up.
|
||||
type FlatConfig struct {
|
||||
PackerBuildName *string `mapstructure:"packer_build_name" cty:"packer_build_name" hcl:"packer_build_name"`
|
||||
PackerBuilderType *string `mapstructure:"packer_builder_type" cty:"packer_builder_type" hcl:"packer_builder_type"`
|
||||
PackerCoreVersion *string `mapstructure:"packer_core_version" cty:"packer_core_version" hcl:"packer_core_version"`
|
||||
PackerDebug *bool `mapstructure:"packer_debug" cty:"packer_debug" hcl:"packer_debug"`
|
||||
PackerForce *bool `mapstructure:"packer_force" cty:"packer_force" hcl:"packer_force"`
|
||||
PackerOnError *string `mapstructure:"packer_on_error" cty:"packer_on_error" hcl:"packer_on_error"`
|
||||
PackerUserVars map[string]string `mapstructure:"packer_user_variables" cty:"packer_user_variables" hcl:"packer_user_variables"`
|
||||
PackerSensitiveVars []string `mapstructure:"packer_sensitive_variables" cty:"packer_sensitive_variables" hcl:"packer_sensitive_variables"`
|
||||
HTTPDir *string `mapstructure:"http_directory" cty:"http_directory" hcl:"http_directory"`
|
||||
HTTPPortMin *int `mapstructure:"http_port_min" cty:"http_port_min" hcl:"http_port_min"`
|
||||
HTTPPortMax *int `mapstructure:"http_port_max" cty:"http_port_max" hcl:"http_port_max"`
|
||||
HTTPAddress *string `mapstructure:"http_bind_address" cty:"http_bind_address" hcl:"http_bind_address"`
|
||||
HTTPInterface *string `mapstructure:"http_interface" undocumented:"true" cty:"http_interface" hcl:"http_interface"`
|
||||
BootGroupInterval *string `mapstructure:"boot_keygroup_interval" cty:"boot_keygroup_interval" hcl:"boot_keygroup_interval"`
|
||||
BootWait *string `mapstructure:"boot_wait" cty:"boot_wait" hcl:"boot_wait"`
|
||||
BootCommand []string `mapstructure:"boot_command" cty:"boot_command" hcl:"boot_command"`
|
||||
BootKeyInterval *string `mapstructure:"boot_key_interval" cty:"boot_key_interval" hcl:"boot_key_interval"`
|
||||
Type *string `mapstructure:"communicator" cty:"communicator" hcl:"communicator"`
|
||||
PauseBeforeConnect *string `mapstructure:"pause_before_connecting" cty:"pause_before_connecting" hcl:"pause_before_connecting"`
|
||||
SSHHost *string `mapstructure:"ssh_host" cty:"ssh_host" hcl:"ssh_host"`
|
||||
SSHPort *int `mapstructure:"ssh_port" cty:"ssh_port" hcl:"ssh_port"`
|
||||
SSHUsername *string `mapstructure:"ssh_username" cty:"ssh_username" hcl:"ssh_username"`
|
||||
SSHPassword *string `mapstructure:"ssh_password" cty:"ssh_password" hcl:"ssh_password"`
|
||||
SSHKeyPairName *string `mapstructure:"ssh_keypair_name" undocumented:"true" cty:"ssh_keypair_name" hcl:"ssh_keypair_name"`
|
||||
SSHTemporaryKeyPairName *string `mapstructure:"temporary_key_pair_name" undocumented:"true" cty:"temporary_key_pair_name" hcl:"temporary_key_pair_name"`
|
||||
SSHTemporaryKeyPairType *string `mapstructure:"temporary_key_pair_type" cty:"temporary_key_pair_type" hcl:"temporary_key_pair_type"`
|
||||
SSHTemporaryKeyPairBits *int `mapstructure:"temporary_key_pair_bits" cty:"temporary_key_pair_bits" hcl:"temporary_key_pair_bits"`
|
||||
SSHCiphers []string `mapstructure:"ssh_ciphers" cty:"ssh_ciphers" hcl:"ssh_ciphers"`
|
||||
SSHClearAuthorizedKeys *bool `mapstructure:"ssh_clear_authorized_keys" cty:"ssh_clear_authorized_keys" hcl:"ssh_clear_authorized_keys"`
|
||||
SSHKEXAlgos []string `mapstructure:"ssh_key_exchange_algorithms" cty:"ssh_key_exchange_algorithms" hcl:"ssh_key_exchange_algorithms"`
|
||||
SSHPrivateKeyFile *string `mapstructure:"ssh_private_key_file" undocumented:"true" cty:"ssh_private_key_file" hcl:"ssh_private_key_file"`
|
||||
SSHCertificateFile *string `mapstructure:"ssh_certificate_file" cty:"ssh_certificate_file" hcl:"ssh_certificate_file"`
|
||||
SSHPty *bool `mapstructure:"ssh_pty" cty:"ssh_pty" hcl:"ssh_pty"`
|
||||
SSHTimeout *string `mapstructure:"ssh_timeout" cty:"ssh_timeout" hcl:"ssh_timeout"`
|
||||
SSHWaitTimeout *string `mapstructure:"ssh_wait_timeout" undocumented:"true" cty:"ssh_wait_timeout" hcl:"ssh_wait_timeout"`
|
||||
SSHAgentAuth *bool `mapstructure:"ssh_agent_auth" undocumented:"true" cty:"ssh_agent_auth" hcl:"ssh_agent_auth"`
|
||||
SSHDisableAgentForwarding *bool `mapstructure:"ssh_disable_agent_forwarding" cty:"ssh_disable_agent_forwarding" hcl:"ssh_disable_agent_forwarding"`
|
||||
SSHHandshakeAttempts *int `mapstructure:"ssh_handshake_attempts" cty:"ssh_handshake_attempts" hcl:"ssh_handshake_attempts"`
|
||||
SSHBastionHost *string `mapstructure:"ssh_bastion_host" cty:"ssh_bastion_host" hcl:"ssh_bastion_host"`
|
||||
SSHBastionPort *int `mapstructure:"ssh_bastion_port" cty:"ssh_bastion_port" hcl:"ssh_bastion_port"`
|
||||
SSHBastionAgentAuth *bool `mapstructure:"ssh_bastion_agent_auth" cty:"ssh_bastion_agent_auth" hcl:"ssh_bastion_agent_auth"`
|
||||
SSHBastionUsername *string `mapstructure:"ssh_bastion_username" cty:"ssh_bastion_username" hcl:"ssh_bastion_username"`
|
||||
SSHBastionPassword *string `mapstructure:"ssh_bastion_password" cty:"ssh_bastion_password" hcl:"ssh_bastion_password"`
|
||||
SSHBastionInteractive *bool `mapstructure:"ssh_bastion_interactive" cty:"ssh_bastion_interactive" hcl:"ssh_bastion_interactive"`
|
||||
SSHBastionPrivateKeyFile *string `mapstructure:"ssh_bastion_private_key_file" cty:"ssh_bastion_private_key_file" hcl:"ssh_bastion_private_key_file"`
|
||||
SSHBastionCertificateFile *string `mapstructure:"ssh_bastion_certificate_file" cty:"ssh_bastion_certificate_file" hcl:"ssh_bastion_certificate_file"`
|
||||
SSHFileTransferMethod *string `mapstructure:"ssh_file_transfer_method" cty:"ssh_file_transfer_method" hcl:"ssh_file_transfer_method"`
|
||||
SSHProxyHost *string `mapstructure:"ssh_proxy_host" cty:"ssh_proxy_host" hcl:"ssh_proxy_host"`
|
||||
SSHProxyPort *int `mapstructure:"ssh_proxy_port" cty:"ssh_proxy_port" hcl:"ssh_proxy_port"`
|
||||
SSHProxyUsername *string `mapstructure:"ssh_proxy_username" cty:"ssh_proxy_username" hcl:"ssh_proxy_username"`
|
||||
SSHProxyPassword *string `mapstructure:"ssh_proxy_password" cty:"ssh_proxy_password" hcl:"ssh_proxy_password"`
|
||||
SSHKeepAliveInterval *string `mapstructure:"ssh_keep_alive_interval" cty:"ssh_keep_alive_interval" hcl:"ssh_keep_alive_interval"`
|
||||
SSHReadWriteTimeout *string `mapstructure:"ssh_read_write_timeout" cty:"ssh_read_write_timeout" hcl:"ssh_read_write_timeout"`
|
||||
SSHRemoteTunnels []string `mapstructure:"ssh_remote_tunnels" cty:"ssh_remote_tunnels" hcl:"ssh_remote_tunnels"`
|
||||
SSHLocalTunnels []string `mapstructure:"ssh_local_tunnels" cty:"ssh_local_tunnels" hcl:"ssh_local_tunnels"`
|
||||
SSHPublicKey []byte `mapstructure:"ssh_public_key" undocumented:"true" cty:"ssh_public_key" hcl:"ssh_public_key"`
|
||||
SSHPrivateKey []byte `mapstructure:"ssh_private_key" undocumented:"true" cty:"ssh_private_key" hcl:"ssh_private_key"`
|
||||
WinRMUser *string `mapstructure:"winrm_username" cty:"winrm_username" hcl:"winrm_username"`
|
||||
WinRMPassword *string `mapstructure:"winrm_password" cty:"winrm_password" hcl:"winrm_password"`
|
||||
WinRMHost *string `mapstructure:"winrm_host" cty:"winrm_host" hcl:"winrm_host"`
|
||||
WinRMNoProxy *bool `mapstructure:"winrm_no_proxy" cty:"winrm_no_proxy" hcl:"winrm_no_proxy"`
|
||||
WinRMPort *int `mapstructure:"winrm_port" cty:"winrm_port" hcl:"winrm_port"`
|
||||
WinRMTimeout *string `mapstructure:"winrm_timeout" cty:"winrm_timeout" hcl:"winrm_timeout"`
|
||||
WinRMUseSSL *bool `mapstructure:"winrm_use_ssl" cty:"winrm_use_ssl" hcl:"winrm_use_ssl"`
|
||||
WinRMInsecure *bool `mapstructure:"winrm_insecure" cty:"winrm_insecure" hcl:"winrm_insecure"`
|
||||
WinRMUseNTLM *bool `mapstructure:"winrm_use_ntlm" cty:"winrm_use_ntlm" hcl:"winrm_use_ntlm"`
|
||||
ProxmoxURLRaw *string `mapstructure:"proxmox_url" cty:"proxmox_url" hcl:"proxmox_url"`
|
||||
SkipCertValidation *bool `mapstructure:"insecure_skip_tls_verify" cty:"insecure_skip_tls_verify" hcl:"insecure_skip_tls_verify"`
|
||||
Username *string `mapstructure:"username" cty:"username" hcl:"username"`
|
||||
Password *string `mapstructure:"password" cty:"password" hcl:"password"`
|
||||
Node *string `mapstructure:"node" cty:"node" hcl:"node"`
|
||||
Pool *string `mapstructure:"pool" cty:"pool" hcl:"pool"`
|
||||
VMName *string `mapstructure:"vm_name" cty:"vm_name" hcl:"vm_name"`
|
||||
VMID *int `mapstructure:"vm_id" cty:"vm_id" hcl:"vm_id"`
|
||||
Boot *string `mapstructure:"boot" cty:"boot" hcl:"boot"`
|
||||
Memory *int `mapstructure:"memory" cty:"memory" hcl:"memory"`
|
||||
Cores *int `mapstructure:"cores" cty:"cores" hcl:"cores"`
|
||||
CPUType *string `mapstructure:"cpu_type" cty:"cpu_type" hcl:"cpu_type"`
|
||||
Sockets *int `mapstructure:"sockets" cty:"sockets" hcl:"sockets"`
|
||||
OS *string `mapstructure:"os" cty:"os" hcl:"os"`
|
||||
VGA *proxmox.FlatvgaConfig `mapstructure:"vga" cty:"vga" hcl:"vga"`
|
||||
NICs []proxmox.FlatnicConfig `mapstructure:"network_adapters" cty:"network_adapters" hcl:"network_adapters"`
|
||||
Disks []proxmox.FlatdiskConfig `mapstructure:"disks" cty:"disks" hcl:"disks"`
|
||||
Agent *bool `mapstructure:"qemu_agent" cty:"qemu_agent" hcl:"qemu_agent"`
|
||||
SCSIController *string `mapstructure:"scsi_controller" cty:"scsi_controller" hcl:"scsi_controller"`
|
||||
Onboot *bool `mapstructure:"onboot" cty:"onboot" hcl:"onboot"`
|
||||
DisableKVM *bool `mapstructure:"disable_kvm" cty:"disable_kvm" hcl:"disable_kvm"`
|
||||
TemplateName *string `mapstructure:"template_name" cty:"template_name" hcl:"template_name"`
|
||||
TemplateDescription *string `mapstructure:"template_description" cty:"template_description" hcl:"template_description"`
|
||||
CloudInit *bool `mapstructure:"cloud_init" cty:"cloud_init" hcl:"cloud_init"`
|
||||
CloudInitStoragePool *string `mapstructure:"cloud_init_storage_pool" cty:"cloud_init_storage_pool" hcl:"cloud_init_storage_pool"`
|
||||
AdditionalISOFiles []proxmox.FlatadditionalISOsConfig `mapstructure:"additional_iso_files" cty:"additional_iso_files" hcl:"additional_iso_files"`
|
||||
VMInterface *string `mapstructure:"vm_interface" cty:"vm_interface" hcl:"vm_interface"`
|
||||
ISOChecksum *string `mapstructure:"iso_checksum" required:"true" cty:"iso_checksum" hcl:"iso_checksum"`
|
||||
RawSingleISOUrl *string `mapstructure:"iso_url" required:"true" cty:"iso_url" hcl:"iso_url"`
|
||||
ISOUrls []string `mapstructure:"iso_urls" cty:"iso_urls" hcl:"iso_urls"`
|
||||
TargetPath *string `mapstructure:"iso_target_path" cty:"iso_target_path" hcl:"iso_target_path"`
|
||||
TargetExtension *string `mapstructure:"iso_target_extension" cty:"iso_target_extension" hcl:"iso_target_extension"`
|
||||
ISOFile *string `mapstructure:"iso_file" cty:"iso_file" hcl:"iso_file"`
|
||||
ISOStoragePool *string `mapstructure:"iso_storage_pool" cty:"iso_storage_pool" hcl:"iso_storage_pool"`
|
||||
UnmountISO *bool `mapstructure:"unmount_iso" cty:"unmount_iso" hcl:"unmount_iso"`
|
||||
PackerBuildName *string `mapstructure:"packer_build_name" cty:"packer_build_name" hcl:"packer_build_name"`
|
||||
PackerBuilderType *string `mapstructure:"packer_builder_type" cty:"packer_builder_type" hcl:"packer_builder_type"`
|
||||
PackerCoreVersion *string `mapstructure:"packer_core_version" cty:"packer_core_version" hcl:"packer_core_version"`
|
||||
PackerDebug *bool `mapstructure:"packer_debug" cty:"packer_debug" hcl:"packer_debug"`
|
||||
PackerForce *bool `mapstructure:"packer_force" cty:"packer_force" hcl:"packer_force"`
|
||||
PackerOnError *string `mapstructure:"packer_on_error" cty:"packer_on_error" hcl:"packer_on_error"`
|
||||
PackerUserVars map[string]string `mapstructure:"packer_user_variables" cty:"packer_user_variables" hcl:"packer_user_variables"`
|
||||
PackerSensitiveVars []string `mapstructure:"packer_sensitive_variables" cty:"packer_sensitive_variables" hcl:"packer_sensitive_variables"`
|
||||
HTTPDir *string `mapstructure:"http_directory" cty:"http_directory" hcl:"http_directory"`
|
||||
HTTPPortMin *int `mapstructure:"http_port_min" cty:"http_port_min" hcl:"http_port_min"`
|
||||
HTTPPortMax *int `mapstructure:"http_port_max" cty:"http_port_max" hcl:"http_port_max"`
|
||||
HTTPAddress *string `mapstructure:"http_bind_address" cty:"http_bind_address" hcl:"http_bind_address"`
|
||||
HTTPInterface *string `mapstructure:"http_interface" undocumented:"true" cty:"http_interface" hcl:"http_interface"`
|
||||
BootGroupInterval *string `mapstructure:"boot_keygroup_interval" cty:"boot_keygroup_interval" hcl:"boot_keygroup_interval"`
|
||||
BootWait *string `mapstructure:"boot_wait" cty:"boot_wait" hcl:"boot_wait"`
|
||||
BootCommand []string `mapstructure:"boot_command" cty:"boot_command" hcl:"boot_command"`
|
||||
BootKeyInterval *string `mapstructure:"boot_key_interval" cty:"boot_key_interval" hcl:"boot_key_interval"`
|
||||
Type *string `mapstructure:"communicator" cty:"communicator" hcl:"communicator"`
|
||||
PauseBeforeConnect *string `mapstructure:"pause_before_connecting" cty:"pause_before_connecting" hcl:"pause_before_connecting"`
|
||||
SSHHost *string `mapstructure:"ssh_host" cty:"ssh_host" hcl:"ssh_host"`
|
||||
SSHPort *int `mapstructure:"ssh_port" cty:"ssh_port" hcl:"ssh_port"`
|
||||
SSHUsername *string `mapstructure:"ssh_username" cty:"ssh_username" hcl:"ssh_username"`
|
||||
SSHPassword *string `mapstructure:"ssh_password" cty:"ssh_password" hcl:"ssh_password"`
|
||||
SSHKeyPairName *string `mapstructure:"ssh_keypair_name" undocumented:"true" cty:"ssh_keypair_name" hcl:"ssh_keypair_name"`
|
||||
SSHTemporaryKeyPairName *string `mapstructure:"temporary_key_pair_name" undocumented:"true" cty:"temporary_key_pair_name" hcl:"temporary_key_pair_name"`
|
||||
SSHTemporaryKeyPairType *string `mapstructure:"temporary_key_pair_type" cty:"temporary_key_pair_type" hcl:"temporary_key_pair_type"`
|
||||
SSHTemporaryKeyPairBits *int `mapstructure:"temporary_key_pair_bits" cty:"temporary_key_pair_bits" hcl:"temporary_key_pair_bits"`
|
||||
SSHCiphers []string `mapstructure:"ssh_ciphers" cty:"ssh_ciphers" hcl:"ssh_ciphers"`
|
||||
SSHClearAuthorizedKeys *bool `mapstructure:"ssh_clear_authorized_keys" cty:"ssh_clear_authorized_keys" hcl:"ssh_clear_authorized_keys"`
|
||||
SSHKEXAlgos []string `mapstructure:"ssh_key_exchange_algorithms" cty:"ssh_key_exchange_algorithms" hcl:"ssh_key_exchange_algorithms"`
|
||||
SSHPrivateKeyFile *string `mapstructure:"ssh_private_key_file" undocumented:"true" cty:"ssh_private_key_file" hcl:"ssh_private_key_file"`
|
||||
SSHCertificateFile *string `mapstructure:"ssh_certificate_file" cty:"ssh_certificate_file" hcl:"ssh_certificate_file"`
|
||||
SSHPty *bool `mapstructure:"ssh_pty" cty:"ssh_pty" hcl:"ssh_pty"`
|
||||
SSHTimeout *string `mapstructure:"ssh_timeout" cty:"ssh_timeout" hcl:"ssh_timeout"`
|
||||
SSHWaitTimeout *string `mapstructure:"ssh_wait_timeout" undocumented:"true" cty:"ssh_wait_timeout" hcl:"ssh_wait_timeout"`
|
||||
SSHAgentAuth *bool `mapstructure:"ssh_agent_auth" undocumented:"true" cty:"ssh_agent_auth" hcl:"ssh_agent_auth"`
|
||||
SSHDisableAgentForwarding *bool `mapstructure:"ssh_disable_agent_forwarding" cty:"ssh_disable_agent_forwarding" hcl:"ssh_disable_agent_forwarding"`
|
||||
SSHHandshakeAttempts *int `mapstructure:"ssh_handshake_attempts" cty:"ssh_handshake_attempts" hcl:"ssh_handshake_attempts"`
|
||||
SSHBastionHost *string `mapstructure:"ssh_bastion_host" cty:"ssh_bastion_host" hcl:"ssh_bastion_host"`
|
||||
SSHBastionPort *int `mapstructure:"ssh_bastion_port" cty:"ssh_bastion_port" hcl:"ssh_bastion_port"`
|
||||
SSHBastionAgentAuth *bool `mapstructure:"ssh_bastion_agent_auth" cty:"ssh_bastion_agent_auth" hcl:"ssh_bastion_agent_auth"`
|
||||
SSHBastionUsername *string `mapstructure:"ssh_bastion_username" cty:"ssh_bastion_username" hcl:"ssh_bastion_username"`
|
||||
SSHBastionPassword *string `mapstructure:"ssh_bastion_password" cty:"ssh_bastion_password" hcl:"ssh_bastion_password"`
|
||||
SSHBastionInteractive *bool `mapstructure:"ssh_bastion_interactive" cty:"ssh_bastion_interactive" hcl:"ssh_bastion_interactive"`
|
||||
SSHBastionPrivateKeyFile *string `mapstructure:"ssh_bastion_private_key_file" cty:"ssh_bastion_private_key_file" hcl:"ssh_bastion_private_key_file"`
|
||||
SSHBastionCertificateFile *string `mapstructure:"ssh_bastion_certificate_file" cty:"ssh_bastion_certificate_file" hcl:"ssh_bastion_certificate_file"`
|
||||
SSHFileTransferMethod *string `mapstructure:"ssh_file_transfer_method" cty:"ssh_file_transfer_method" hcl:"ssh_file_transfer_method"`
|
||||
SSHProxyHost *string `mapstructure:"ssh_proxy_host" cty:"ssh_proxy_host" hcl:"ssh_proxy_host"`
|
||||
SSHProxyPort *int `mapstructure:"ssh_proxy_port" cty:"ssh_proxy_port" hcl:"ssh_proxy_port"`
|
||||
SSHProxyUsername *string `mapstructure:"ssh_proxy_username" cty:"ssh_proxy_username" hcl:"ssh_proxy_username"`
|
||||
SSHProxyPassword *string `mapstructure:"ssh_proxy_password" cty:"ssh_proxy_password" hcl:"ssh_proxy_password"`
|
||||
SSHKeepAliveInterval *string `mapstructure:"ssh_keep_alive_interval" cty:"ssh_keep_alive_interval" hcl:"ssh_keep_alive_interval"`
|
||||
SSHReadWriteTimeout *string `mapstructure:"ssh_read_write_timeout" cty:"ssh_read_write_timeout" hcl:"ssh_read_write_timeout"`
|
||||
SSHRemoteTunnels []string `mapstructure:"ssh_remote_tunnels" cty:"ssh_remote_tunnels" hcl:"ssh_remote_tunnels"`
|
||||
SSHLocalTunnels []string `mapstructure:"ssh_local_tunnels" cty:"ssh_local_tunnels" hcl:"ssh_local_tunnels"`
|
||||
SSHPublicKey []byte `mapstructure:"ssh_public_key" undocumented:"true" cty:"ssh_public_key" hcl:"ssh_public_key"`
|
||||
SSHPrivateKey []byte `mapstructure:"ssh_private_key" undocumented:"true" cty:"ssh_private_key" hcl:"ssh_private_key"`
|
||||
WinRMUser *string `mapstructure:"winrm_username" cty:"winrm_username" hcl:"winrm_username"`
|
||||
WinRMPassword *string `mapstructure:"winrm_password" cty:"winrm_password" hcl:"winrm_password"`
|
||||
WinRMHost *string `mapstructure:"winrm_host" cty:"winrm_host" hcl:"winrm_host"`
|
||||
WinRMNoProxy *bool `mapstructure:"winrm_no_proxy" cty:"winrm_no_proxy" hcl:"winrm_no_proxy"`
|
||||
WinRMPort *int `mapstructure:"winrm_port" cty:"winrm_port" hcl:"winrm_port"`
|
||||
WinRMTimeout *string `mapstructure:"winrm_timeout" cty:"winrm_timeout" hcl:"winrm_timeout"`
|
||||
WinRMUseSSL *bool `mapstructure:"winrm_use_ssl" cty:"winrm_use_ssl" hcl:"winrm_use_ssl"`
|
||||
WinRMInsecure *bool `mapstructure:"winrm_insecure" cty:"winrm_insecure" hcl:"winrm_insecure"`
|
||||
WinRMUseNTLM *bool `mapstructure:"winrm_use_ntlm" cty:"winrm_use_ntlm" hcl:"winrm_use_ntlm"`
|
||||
ProxmoxURLRaw *string `mapstructure:"proxmox_url" cty:"proxmox_url" hcl:"proxmox_url"`
|
||||
SkipCertValidation *bool `mapstructure:"insecure_skip_tls_verify" cty:"insecure_skip_tls_verify" hcl:"insecure_skip_tls_verify"`
|
||||
Username *string `mapstructure:"username" cty:"username" hcl:"username"`
|
||||
Password *string `mapstructure:"password" cty:"password" hcl:"password"`
|
||||
Node *string `mapstructure:"node" cty:"node" hcl:"node"`
|
||||
Pool *string `mapstructure:"pool" cty:"pool" hcl:"pool"`
|
||||
VMName *string `mapstructure:"vm_name" cty:"vm_name" hcl:"vm_name"`
|
||||
VMID *int `mapstructure:"vm_id" cty:"vm_id" hcl:"vm_id"`
|
||||
Boot *string `mapstructure:"boot" cty:"boot" hcl:"boot"`
|
||||
Memory *int `mapstructure:"memory" cty:"memory" hcl:"memory"`
|
||||
Cores *int `mapstructure:"cores" cty:"cores" hcl:"cores"`
|
||||
CPUType *string `mapstructure:"cpu_type" cty:"cpu_type" hcl:"cpu_type"`
|
||||
Sockets *int `mapstructure:"sockets" cty:"sockets" hcl:"sockets"`
|
||||
OS *string `mapstructure:"os" cty:"os" hcl:"os"`
|
||||
VGA *proxmox.FlatvgaConfig `mapstructure:"vga" cty:"vga" hcl:"vga"`
|
||||
NICs []proxmox.FlatnicConfig `mapstructure:"network_adapters" cty:"network_adapters" hcl:"network_adapters"`
|
||||
Disks []proxmox.FlatdiskConfig `mapstructure:"disks" cty:"disks" hcl:"disks"`
|
||||
Agent *bool `mapstructure:"qemu_agent" cty:"qemu_agent" hcl:"qemu_agent"`
|
||||
SCSIController *string `mapstructure:"scsi_controller" cty:"scsi_controller" hcl:"scsi_controller"`
|
||||
Onboot *bool `mapstructure:"onboot" cty:"onboot" hcl:"onboot"`
|
||||
DisableKVM *bool `mapstructure:"disable_kvm" cty:"disable_kvm" hcl:"disable_kvm"`
|
||||
TemplateName *string `mapstructure:"template_name" cty:"template_name" hcl:"template_name"`
|
||||
TemplateDescription *string `mapstructure:"template_description" cty:"template_description" hcl:"template_description"`
|
||||
CloudInit *bool `mapstructure:"cloud_init" cty:"cloud_init" hcl:"cloud_init"`
|
||||
CloudInitStoragePool *string `mapstructure:"cloud_init_storage_pool" cty:"cloud_init_storage_pool" hcl:"cloud_init_storage_pool"`
|
||||
AdditionalISOFiles []proxmox.FlatstorageConfig `mapstructure:"additional_iso_files" cty:"additional_iso_files" hcl:"additional_iso_files"`
|
||||
VMInterface *string `mapstructure:"vm_interface" cty:"vm_interface" hcl:"vm_interface"`
|
||||
ISOChecksum *string `mapstructure:"iso_checksum" required:"true" cty:"iso_checksum" hcl:"iso_checksum"`
|
||||
RawSingleISOUrl *string `mapstructure:"iso_url" required:"true" cty:"iso_url" hcl:"iso_url"`
|
||||
ISOUrls []string `mapstructure:"iso_urls" cty:"iso_urls" hcl:"iso_urls"`
|
||||
TargetPath *string `mapstructure:"iso_target_path" cty:"iso_target_path" hcl:"iso_target_path"`
|
||||
TargetExtension *string `mapstructure:"iso_target_extension" cty:"iso_target_extension" hcl:"iso_target_extension"`
|
||||
ISOFile *string `mapstructure:"iso_file" cty:"iso_file" hcl:"iso_file"`
|
||||
ISOStoragePool *string `mapstructure:"iso_storage_pool" cty:"iso_storage_pool" hcl:"iso_storage_pool"`
|
||||
UnmountISO *bool `mapstructure:"unmount_iso" cty:"unmount_iso" hcl:"unmount_iso"`
|
||||
}
|
||||
|
||||
// FlatMapstructure returns a new FlatConfig.
|
||||
@@ -217,7 +217,7 @@ func (*FlatConfig) HCL2Spec() map[string]hcldec.Spec {
|
||||
"template_description": &hcldec.AttrSpec{Name: "template_description", Type: cty.String, Required: false},
|
||||
"cloud_init": &hcldec.AttrSpec{Name: "cloud_init", Type: cty.Bool, Required: false},
|
||||
"cloud_init_storage_pool": &hcldec.AttrSpec{Name: "cloud_init_storage_pool", Type: cty.String, Required: false},
|
||||
"additional_iso_files": &hcldec.BlockListSpec{TypeName: "additional_iso_files", Nested: hcldec.ObjectSpec((*proxmox.FlatadditionalISOsConfig)(nil).HCL2Spec())},
|
||||
"additional_iso_files": &hcldec.BlockListSpec{TypeName: "additional_iso_files", Nested: hcldec.ObjectSpec((*proxmox.FlatstorageConfig)(nil).HCL2Spec())},
|
||||
"vm_interface": &hcldec.AttrSpec{Name: "vm_interface", Type: cty.String, Required: false},
|
||||
"iso_checksum": &hcldec.AttrSpec{Name: "iso_checksum", Type: cty.String, Required: false},
|
||||
"iso_url": &hcldec.AttrSpec{Name: "iso_url", Type: cty.String, Required: false},
|
||||
|
||||
@@ -46,6 +46,29 @@ func (s *stepFinalizeISOTemplate) Run(ctx context.Context, state multistep.State
|
||||
}
|
||||
changes["ide2"] = "none,media=cdrom"
|
||||
}
|
||||
if len(c.AdditionalISOFiles) > 0 {
|
||||
vmParams, err := client.GetVmConfig(vmRef)
|
||||
if err != nil {
|
||||
err := fmt.Errorf("Error fetching template config: %s", err)
|
||||
state.Put("error", err)
|
||||
ui.Error(err.Error())
|
||||
return multistep.ActionHalt
|
||||
}
|
||||
for idx := range c.AdditionalISOFiles {
|
||||
cdrom := c.AdditionalISOFiles[idx].Device
|
||||
if c.AdditionalISOFiles[idx].Unmount {
|
||||
if vmParams[cdrom] == nil || !strings.Contains(vmParams[cdrom].(string), "media=cdrom") {
|
||||
err := fmt.Errorf("Cannot eject ISO from cdrom drive, %s is not present or not a cdrom media", cdrom)
|
||||
state.Put("error", err)
|
||||
ui.Error(err.Error())
|
||||
return multistep.ActionHalt
|
||||
}
|
||||
changes[cdrom] = "none,media=cdrom"
|
||||
} else {
|
||||
changes[cdrom] = c.AdditionalISOFiles[idx].ISOFile + ",media=cdrom"
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if len(changes) > 0 {
|
||||
_, err := client.SetVmConfig(vmRef, changes)
|
||||
|
||||
+2
-7
@@ -1,9 +1,8 @@
|
||||
package proxmox
|
||||
package proxmoxiso
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"io"
|
||||
"os"
|
||||
"path/filepath"
|
||||
|
||||
@@ -16,16 +15,12 @@ import (
|
||||
// to the VM
|
||||
type stepUploadAdditionalISOs struct{}
|
||||
|
||||
type uploader interface {
|
||||
Upload(node string, storage string, contentType string, filename string, file io.Reader) error
|
||||
}
|
||||
|
||||
var _ uploader = &proxmox.Client{}
|
||||
|
||||
func (s *stepUploadAdditionalISOs) Run(ctx context.Context, state multistep.StateBag) multistep.StepAction {
|
||||
ui := state.Get("ui").(packersdk.Ui)
|
||||
client := state.Get("proxmoxClient").(uploader)
|
||||
c := state.Get("config").(*Config)
|
||||
c := state.Get("iso-config").(*Config)
|
||||
|
||||
for idx := range c.AdditionalISOFiles {
|
||||
if !c.AdditionalISOFiles[idx].ShouldUploadISO {
|
||||
@@ -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,media=cdrom", cdPath))
|
||||
driveArgs = append(driveArgs, fmt.Sprintf("file=%s,index=%d,media=cdrom", cdPath, i))
|
||||
} 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,media=cdrom",
|
||||
"-drive", "file=/path/to/test.iso,index=0,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,media=cdrom",
|
||||
"-drive", "file=/path/to/test.iso,index=0,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,media=cdrom",
|
||||
"-drive", "file=/path/to/test.iso,index=0,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,media=cdrom",
|
||||
"-drive", "file=/path/to/test.iso,index=0,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,media=cdrom",
|
||||
"-drive", "file=fake_cd_path.iso,index=0,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,media=cdrom",
|
||||
"-drive", "file=fake_cd_path.iso,index=0,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,media=cdrom",
|
||||
"-drive", "file=fake_cd_path.iso,media=cdrom",
|
||||
"-drive", "file=/path/to/test.iso,index=0,media=cdrom",
|
||||
"-drive", "file=fake_cd_path.iso,index=1,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,media=cdrom",
|
||||
"-drive", "file=fake_cd_path.iso,media=cdrom",
|
||||
"-drive", "file=/path/to/test.iso,index=0,media=cdrom",
|
||||
"-drive", "file=fake_cd_path.iso,index=1,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,media=cdrom",
|
||||
"-drive", "file=/path/to/test.iso,index=0,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,media=cdrom",
|
||||
"-drive", "file=fake_cd_path.iso,media=cdrom",
|
||||
"-drive", "file=/path/to/test.iso,index=0,media=cdrom",
|
||||
"-drive", "file=fake_cd_path.iso,index=1,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,media=cdrom",
|
||||
"-drive", "file=/path/to/test.iso,index=0,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,media=cdrom",
|
||||
"-drive", "file=/path/to/test.iso,index=0,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,media=cdrom",
|
||||
"-drive", "file=/path/to/test.iso,media=cdrom",
|
||||
"-drive", "file=fake_cd_path.iso,index=1,media=cdrom",
|
||||
"-drive", "file=/path/to/test.iso,index=0,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,media=cdrom",
|
||||
"-drive", "file=fake_cd_path.iso,index=0,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,media=cdrom",
|
||||
"-drive", "file=/path/to/test.iso,index=0,media=cdrom",
|
||||
"-qmp", "unix:qmp_path,server,nowait",
|
||||
}
|
||||
|
||||
|
||||
@@ -74,10 +74,8 @@ type Config struct {
|
||||
|
||||
RemoveVolume bool `mapstructure:"remove_volume"`
|
||||
|
||||
// Shutdown timeout. Default to 5m
|
||||
ShutdownTimeout string `mapstructure:"shutdown_timeout" required:"false"`
|
||||
UserAgent string `mapstructure-to-hcl2:",skip"`
|
||||
ctx interpolate.Context
|
||||
UserAgent string `mapstructure-to-hcl2:",skip"`
|
||||
ctx interpolate.Context
|
||||
|
||||
// Deprecated configs
|
||||
|
||||
@@ -257,10 +255,6 @@ func (c *Config) Prepare(raws ...interface{}) ([]string, error) {
|
||||
errs, errors.New("image is required"))
|
||||
}
|
||||
|
||||
if c.ShutdownTimeout == "" {
|
||||
c.ShutdownTimeout = "5m"
|
||||
}
|
||||
|
||||
if errs != nil && len(errs.Errors) > 0 {
|
||||
return warnings, errs
|
||||
}
|
||||
|
||||
@@ -80,7 +80,6 @@ type FlatConfig struct {
|
||||
Bootscript *string `mapstructure:"bootscript" required:"false" cty:"bootscript" hcl:"bootscript"`
|
||||
BootType *string `mapstructure:"boottype" required:"false" cty:"boottype" hcl:"boottype"`
|
||||
RemoveVolume *bool `mapstructure:"remove_volume" cty:"remove_volume" hcl:"remove_volume"`
|
||||
ShutdownTimeout *string `mapstructure:"shutdown_timeout" required:"false" cty:"shutdown_timeout" hcl:"shutdown_timeout"`
|
||||
Token *string `mapstructure:"api_token" required:"false" cty:"api_token" hcl:"api_token"`
|
||||
Organization *string `mapstructure:"organization_id" required:"false" cty:"organization_id" hcl:"organization_id"`
|
||||
Region *string `mapstructure:"region" required:"false" cty:"region" hcl:"region"`
|
||||
@@ -168,7 +167,6 @@ func (*FlatConfig) HCL2Spec() map[string]hcldec.Spec {
|
||||
"bootscript": &hcldec.AttrSpec{Name: "bootscript", Type: cty.String, Required: false},
|
||||
"boottype": &hcldec.AttrSpec{Name: "boottype", Type: cty.String, Required: false},
|
||||
"remove_volume": &hcldec.AttrSpec{Name: "remove_volume", Type: cty.Bool, Required: false},
|
||||
"shutdown_timeout": &hcldec.AttrSpec{Name: "shutdown_timeout", Type: cty.String, Required: false},
|
||||
"api_token": &hcldec.AttrSpec{Name: "api_token", Type: cty.String, Required: false},
|
||||
"organization_id": &hcldec.AttrSpec{Name: "organization_id", Type: cty.String, Required: false},
|
||||
"region": &hcldec.AttrSpec{Name: "region", Type: cty.String, Required: false},
|
||||
|
||||
@@ -3,7 +3,6 @@ package scaleway
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"time"
|
||||
|
||||
"github.com/hashicorp/packer-plugin-sdk/multistep"
|
||||
packersdk "github.com/hashicorp/packer-plugin-sdk/packer"
|
||||
@@ -31,23 +30,9 @@ func (s *stepShutdown) Run(ctx context.Context, state multistep.StateBag) multis
|
||||
return multistep.ActionHalt
|
||||
}
|
||||
|
||||
waitRequest := &instance.WaitForServerRequest{
|
||||
instanceResp, err := instanceAPI.WaitForServer(&instance.WaitForServerRequest{
|
||||
ServerID: serverID,
|
||||
}
|
||||
c := state.Get("config").(*Config)
|
||||
timeout := c.ShutdownTimeout
|
||||
duration, err := time.ParseDuration(timeout)
|
||||
if err != nil {
|
||||
err := fmt.Errorf("error: %s could not parse string %s as a duration", err, timeout)
|
||||
state.Put("error", err)
|
||||
ui.Error(err.Error())
|
||||
return multistep.ActionHalt
|
||||
}
|
||||
if timeout != "" {
|
||||
waitRequest.Timeout = scw.TimeDurationPtr(duration)
|
||||
}
|
||||
|
||||
instanceResp, err := instanceAPI.WaitForServer(waitRequest)
|
||||
})
|
||||
if err != nil {
|
||||
err := fmt.Errorf("Error shutting down server: %s", err)
|
||||
state.Put("error", err)
|
||||
|
||||
@@ -22,9 +22,6 @@ type Driver interface {
|
||||
// Create a SCSI controller.
|
||||
CreateSCSIController(vm string, controller string) error
|
||||
|
||||
// Create a VirtIO controller.
|
||||
CreateVirtIOController(vm string, controller string) error
|
||||
|
||||
// Create an NVME controller
|
||||
CreateNVMeController(vm string, controller string, portcount int) error
|
||||
|
||||
|
||||
@@ -64,6 +64,7 @@ func (d *VBox42Driver) CreateNVMeController(vmName string, name string, portcoun
|
||||
}
|
||||
|
||||
func (d *VBox42Driver) CreateSCSIController(vmName string, name string) error {
|
||||
|
||||
command := []string{
|
||||
"storagectl", vmName,
|
||||
"--name", name,
|
||||
@@ -74,17 +75,6 @@ func (d *VBox42Driver) CreateSCSIController(vmName string, name string) error {
|
||||
return d.VBoxManage(command...)
|
||||
}
|
||||
|
||||
func (d *VBox42Driver) CreateVirtIOController(vmName string, name string) error {
|
||||
command := []string{
|
||||
"storagectl", vmName,
|
||||
"--name", name,
|
||||
"--add", "VirtIO",
|
||||
"--controller", "VirtIO",
|
||||
}
|
||||
|
||||
return d.VBoxManage(command...)
|
||||
}
|
||||
|
||||
func (d *VBox42Driver) RemoveFloppyControllers(vmName string) error {
|
||||
var stdout bytes.Buffer
|
||||
|
||||
|
||||
@@ -13,10 +13,6 @@ type DriverMock struct {
|
||||
CreateSCSIControllerController string
|
||||
CreateSCSIControllerErr error
|
||||
|
||||
CreateVirtIOControllerVM string
|
||||
CreateVirtIOControllerController string
|
||||
CreateVirtIOControllerErr error
|
||||
|
||||
CreateNVMeControllerVM string
|
||||
CreateNVMeControllerController string
|
||||
CreateNVMeControllerErr error
|
||||
@@ -82,12 +78,6 @@ func (d *DriverMock) CreateSCSIController(vm string, controller string) error {
|
||||
return d.CreateSCSIControllerErr
|
||||
}
|
||||
|
||||
func (d *DriverMock) CreateVirtIOController(vm string, controller string) error {
|
||||
d.CreateVirtIOControllerVM = vm
|
||||
d.CreateVirtIOControllerController = vm
|
||||
return d.CreateVirtIOControllerErr
|
||||
}
|
||||
|
||||
func (d *DriverMock) CreateNVMeController(vm string, controller string, portcount int) error {
|
||||
d.CreateNVMeControllerVM = vm
|
||||
d.CreateNVMeControllerController = vm
|
||||
|
||||
@@ -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/legacy_json_templates/engine) where the `Version`
|
||||
// template](/docs/templates/engine) where the `Version`
|
||||
// variable is replaced with the VirtualBox version.
|
||||
GuestAdditionsPath string `mapstructure:"guest_additions_path"`
|
||||
// The SHA256 checksum of the guest
|
||||
|
||||
@@ -80,10 +80,6 @@ func (s *StepAttachISOs) Run(ctx context.Context, state multistep.StateBag) mult
|
||||
controllerName = "SATA Controller"
|
||||
port = "1"
|
||||
device = "0"
|
||||
} else if s.ISOInterface == "virtio" {
|
||||
controllerName = "VirtIO Controller"
|
||||
port = "1"
|
||||
device = "0"
|
||||
}
|
||||
ui.Message("Mounting boot ISO...")
|
||||
case "guest_additions":
|
||||
@@ -94,10 +90,6 @@ func (s *StepAttachISOs) Run(ctx context.Context, state multistep.StateBag) mult
|
||||
controllerName = "SATA Controller"
|
||||
port = "2"
|
||||
device = "0"
|
||||
} else if s.GuestAdditionsInterface == "virtio" {
|
||||
controllerName = "VirtIO Controller"
|
||||
port = "2"
|
||||
device = "0"
|
||||
}
|
||||
ui.Message("Mounting guest additions ISO...")
|
||||
case "cd_files":
|
||||
@@ -108,10 +100,6 @@ func (s *StepAttachISOs) Run(ctx context.Context, state multistep.StateBag) mult
|
||||
controllerName = "SATA Controller"
|
||||
port = "3"
|
||||
device = "0"
|
||||
} else if s.ISOInterface == "virtio" {
|
||||
controllerName = "VirtIO Controller"
|
||||
port = "3"
|
||||
device = "0"
|
||||
}
|
||||
ui.Message("Mounting cd_files ISO...")
|
||||
}
|
||||
|
||||
@@ -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/legacy_json_templates/engine). The only available
|
||||
// template](/docs/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"`
|
||||
|
||||
@@ -44,37 +44,9 @@ type Config struct {
|
||||
vboxcommon.VBoxVersionConfig `mapstructure:",squash"`
|
||||
vboxcommon.VBoxBundleConfig `mapstructure:",squash"`
|
||||
vboxcommon.GuestAdditionsConfig `mapstructure:",squash"`
|
||||
// The chipset to be used: PIIX3 or ICH9.
|
||||
// When set to piix3, the firmare is PIIX3. This is the default.
|
||||
// When set to ich9, the firmare is ICH9.
|
||||
Chipset string `mapstructure:"chipset" required:"false"`
|
||||
// The firmware to be used: BIOS or EFI.
|
||||
// When set to bios, the firmare is BIOS. This is the default.
|
||||
// When set to efi, the firmare is EFI.
|
||||
Firmware string `mapstructure:"firmware" required:"false"`
|
||||
// The size, in megabytes, of the hard disk to create for the VM. By
|
||||
// default, this is 40000 (about 40 GB).
|
||||
DiskSize uint `mapstructure:"disk_size" required:"false"`
|
||||
// The NIC type to be used for the network interfaces.
|
||||
// When set to 82540EM, the NICs are Intel PRO/1000 MT Desktop (82540EM). This is the default.
|
||||
// When set to 82543GC, the NICs are Intel PRO/1000 T Server (82543GC).
|
||||
// When set to 82545EM, the NICs are Intel PRO/1000 MT Server (82545EM).
|
||||
// When set to Am79C970A, the NICs are AMD PCNet-PCI II network card (Am79C970A).
|
||||
// When set to Am79C973, the NICs are AMD PCNet-FAST III network card (Am79C973).
|
||||
// When set to Am79C960, the NICs are AMD PCnet-ISA/NE2100 (Am79C960).
|
||||
// When set to virtio, the NICs are VirtIO.
|
||||
NICType string `mapstructure:"nic_type" required:"false"`
|
||||
// The audio controller type to be used.
|
||||
// When set to ac97, the audio controller is ICH AC97. This is the default.
|
||||
// When set to hda, the audio controller is Intel HD Audio.
|
||||
// When set to sb16, the audio controller is SoundBlaster 16.
|
||||
AudioController string `mapstructure:"audio_controller" required:"false"`
|
||||
// The graphics controller type to be used.
|
||||
// When set to vboxvga, the graphics controller is VirtualBox VGA. This is the default.
|
||||
// When set to vboxsvga, the graphics controller is VirtualBox SVGA.
|
||||
// When set to vmsvga, the graphics controller is VMware SVGA.
|
||||
// When set to none, the graphics controller is disabled.
|
||||
GfxController string `mapstructure:"gfx_controller" required:"false"`
|
||||
// The guest OS type being installed. By default this is other, but you can
|
||||
// get dramatic performance improvements by setting this to the proper
|
||||
// value. To view all available values for this run VBoxManage list
|
||||
@@ -90,7 +62,6 @@ type Config struct {
|
||||
// defaults to ide. When set to sata, the drive is attached to an AHCI SATA
|
||||
// controller. When set to scsi, the drive is attached to an LsiLogic SCSI
|
||||
// controller. When set to pcie, the drive is attached to an NVMe
|
||||
// controller. When set to virtio, the drive is attached to a VirtIO
|
||||
// controller. Please note that when you use "pcie", you'll need to have
|
||||
// Virtualbox 6, install an [extension
|
||||
// pack](https://www.virtualbox.org/wiki/Downloads#VirtualBox6.0.14OracleVMVirtualBoxExtensionPack)
|
||||
@@ -127,7 +98,6 @@ type Config struct {
|
||||
HardDriveNonrotational bool `mapstructure:"hard_drive_nonrotational" required:"false"`
|
||||
// The type of controller that the ISO is attached to, defaults to ide.
|
||||
// When set to sata, the drive is attached to an AHCI SATA controller.
|
||||
// When set to virtio, the drive is attached to a VirtIO controller.
|
||||
ISOInterface string `mapstructure:"iso_interface" required:"false"`
|
||||
// Set this to true if you would like to keep the VM registered with
|
||||
// virtualbox. Defaults to false.
|
||||
@@ -190,28 +160,6 @@ func (b *Builder) Prepare(raws ...interface{}) ([]string, []string, error) {
|
||||
errs = packersdk.MultiErrorAppend(errs, b.config.BootConfig.Prepare(&b.config.ctx)...)
|
||||
errs = packersdk.MultiErrorAppend(errs, b.config.GuestAdditionsConfig.Prepare(b.config.CommConfig.Comm.Type)...)
|
||||
|
||||
if b.config.Chipset == "" {
|
||||
b.config.Chipset = "piix3"
|
||||
}
|
||||
switch b.config.Chipset {
|
||||
case "piix3", "ich9":
|
||||
// do nothing
|
||||
default:
|
||||
errs = packersdk.MultiErrorAppend(
|
||||
errs, errors.New("chipset can only be piix3 or ich9"))
|
||||
}
|
||||
|
||||
if b.config.Firmware == "" {
|
||||
b.config.Firmware = "bios"
|
||||
}
|
||||
switch b.config.Firmware {
|
||||
case "bios", "efi":
|
||||
// do nothing
|
||||
default:
|
||||
errs = packersdk.MultiErrorAppend(
|
||||
errs, errors.New("firmware can only be bios or efi"))
|
||||
}
|
||||
|
||||
if b.config.DiskSize == 0 {
|
||||
b.config.DiskSize = 40000
|
||||
}
|
||||
@@ -220,39 +168,6 @@ func (b *Builder) Prepare(raws ...interface{}) ([]string, []string, error) {
|
||||
b.config.HardDriveInterface = "ide"
|
||||
}
|
||||
|
||||
if b.config.NICType == "" {
|
||||
b.config.NICType = "82540EM"
|
||||
}
|
||||
switch b.config.NICType {
|
||||
case "82540EM", "82543GC", "82545EM", "Am79C970A", "Am79C973", "Am79C960", "virtio":
|
||||
// do nothing
|
||||
default:
|
||||
errs = packersdk.MultiErrorAppend(
|
||||
errs, errors.New("NIC type can only be 82540EM, 82543GC, 82545EM, Am79C970A, Am79C973, Am79C960 or virtio"))
|
||||
}
|
||||
|
||||
if b.config.GfxController == "" {
|
||||
b.config.GfxController = "vboxvga"
|
||||
}
|
||||
switch b.config.GfxController {
|
||||
case "vboxvga", "vboxsvga", "vmsvga", "none":
|
||||
// do nothing
|
||||
default:
|
||||
errs = packersdk.MultiErrorAppend(
|
||||
errs, errors.New("Graphics controller type can only be vboxvga, vboxsvga, vmsvga, none"))
|
||||
}
|
||||
|
||||
if b.config.AudioController == "" {
|
||||
b.config.AudioController = "ac97"
|
||||
}
|
||||
switch b.config.AudioController {
|
||||
case "ac97", "hda", "sb16":
|
||||
// do nothing
|
||||
default:
|
||||
errs = packersdk.MultiErrorAppend(
|
||||
errs, errors.New("Audio controller type can only be ac97, hda or sb16"))
|
||||
}
|
||||
|
||||
if b.config.GuestOSType == "" {
|
||||
b.config.GuestOSType = "Other"
|
||||
}
|
||||
@@ -271,11 +186,11 @@ func (b *Builder) Prepare(raws ...interface{}) ([]string, []string, error) {
|
||||
}
|
||||
|
||||
switch b.config.HardDriveInterface {
|
||||
case "ide", "sata", "scsi", "pcie", "virtio":
|
||||
case "ide", "sata", "scsi", "pcie":
|
||||
// do nothing
|
||||
default:
|
||||
errs = packersdk.MultiErrorAppend(
|
||||
errs, errors.New("hard_drive_interface can only be ide, sata, pcie, scsi or virtio"))
|
||||
errs, errors.New("hard_drive_interface can only be ide, sata, pcie or scsi"))
|
||||
}
|
||||
|
||||
if b.config.SATAPortCount == 0 {
|
||||
@@ -296,9 +211,9 @@ func (b *Builder) Prepare(raws ...interface{}) ([]string, []string, error) {
|
||||
errs, errors.New("nvme_port_count cannot be greater than 255"))
|
||||
}
|
||||
|
||||
if b.config.ISOInterface != "ide" && b.config.ISOInterface != "sata" && b.config.ISOInterface != "virtio" {
|
||||
if b.config.ISOInterface != "ide" && b.config.ISOInterface != "sata" {
|
||||
errs = packersdk.MultiErrorAppend(
|
||||
errs, errors.New("iso_interface can only be ide, sata or virtio"))
|
||||
errs, errors.New("iso_interface can only be ide or sata"))
|
||||
}
|
||||
|
||||
// Warnings
|
||||
|
||||
@@ -117,12 +117,7 @@ type FlatConfig struct {
|
||||
GuestAdditionsPath *string `mapstructure:"guest_additions_path" cty:"guest_additions_path" hcl:"guest_additions_path"`
|
||||
GuestAdditionsSHA256 *string `mapstructure:"guest_additions_sha256" cty:"guest_additions_sha256" hcl:"guest_additions_sha256"`
|
||||
GuestAdditionsURL *string `mapstructure:"guest_additions_url" required:"false" cty:"guest_additions_url" hcl:"guest_additions_url"`
|
||||
Chipset *string `mapstructure:"chipset" required:"false" cty:"chipset" hcl:"chipset"`
|
||||
Firmware *string `mapstructure:"firmware" required:"false" cty:"firmware" hcl:"firmware"`
|
||||
DiskSize *uint `mapstructure:"disk_size" required:"false" cty:"disk_size" hcl:"disk_size"`
|
||||
NICType *string `mapstructure:"nic_type" required:"false" cty:"nic_type" hcl:"nic_type"`
|
||||
AudioController *string `mapstructure:"audio_controller" required:"false" cty:"audio_controller" hcl:"audio_controller"`
|
||||
GfxController *string `mapstructure:"gfx_controller" required:"false" cty:"gfx_controller" hcl:"gfx_controller"`
|
||||
GuestOSType *string `mapstructure:"guest_os_type" required:"false" cty:"guest_os_type" hcl:"guest_os_type"`
|
||||
HardDriveDiscard *bool `mapstructure:"hard_drive_discard" required:"false" cty:"hard_drive_discard" hcl:"hard_drive_discard"`
|
||||
HardDriveInterface *string `mapstructure:"hard_drive_interface" required:"false" cty:"hard_drive_interface" hcl:"hard_drive_interface"`
|
||||
@@ -254,12 +249,7 @@ func (*FlatConfig) HCL2Spec() map[string]hcldec.Spec {
|
||||
"guest_additions_path": &hcldec.AttrSpec{Name: "guest_additions_path", Type: cty.String, Required: false},
|
||||
"guest_additions_sha256": &hcldec.AttrSpec{Name: "guest_additions_sha256", Type: cty.String, Required: false},
|
||||
"guest_additions_url": &hcldec.AttrSpec{Name: "guest_additions_url", Type: cty.String, Required: false},
|
||||
"chipset": &hcldec.AttrSpec{Name: "chipset", Type: cty.String, Required: false},
|
||||
"firmware": &hcldec.AttrSpec{Name: "firmware", Type: cty.String, Required: false},
|
||||
"disk_size": &hcldec.AttrSpec{Name: "disk_size", Type: cty.Number, Required: false},
|
||||
"nic_type": &hcldec.AttrSpec{Name: "nic_type", Type: cty.String, Required: false},
|
||||
"audio_controller": &hcldec.AttrSpec{Name: "audio_controller", Type: cty.String, Required: false},
|
||||
"gfx_controller": &hcldec.AttrSpec{Name: "gfx_controller", Type: cty.String, Required: false},
|
||||
"guest_os_type": &hcldec.AttrSpec{Name: "guest_os_type", Type: cty.String, Required: false},
|
||||
"hard_drive_discard": &hcldec.AttrSpec{Name: "hard_drive_discard", Type: cty.Bool, Required: false},
|
||||
"hard_drive_interface": &hcldec.AttrSpec{Name: "hard_drive_interface", Type: cty.String, Required: false},
|
||||
|
||||
@@ -66,18 +66,6 @@ func (s *stepCreateDisk) Run(ctx context.Context, state multistep.StateBag) mult
|
||||
}
|
||||
}
|
||||
|
||||
// Add a VirtIO controller if we were asked to use VirtIO. We still attach
|
||||
// the VirtIO controller above because some other things (disks) require
|
||||
// that.
|
||||
if config.HardDriveInterface == "virtio" || config.ISOInterface == "virtio" {
|
||||
if err := driver.CreateVirtIOController(vmName, "VirtIO Controller"); err != nil {
|
||||
err := fmt.Errorf("Error creating disk controller: %s", err)
|
||||
state.Put("error", err)
|
||||
ui.Error(err.Error())
|
||||
return multistep.ActionHalt
|
||||
}
|
||||
}
|
||||
|
||||
if config.HardDriveInterface == "scsi" {
|
||||
if err := driver.CreateSCSIController(vmName, "SCSI Controller"); err != nil {
|
||||
err := fmt.Errorf("Error creating disk controller: %s", err)
|
||||
@@ -98,11 +86,13 @@ func (s *stepCreateDisk) Run(ctx context.Context, state multistep.StateBag) mult
|
||||
controllerName := "IDE Controller"
|
||||
if config.HardDriveInterface == "sata" {
|
||||
controllerName = "SATA Controller"
|
||||
} else if config.HardDriveInterface == "scsi" {
|
||||
}
|
||||
|
||||
if config.HardDriveInterface == "scsi" {
|
||||
controllerName = "SCSI Controller"
|
||||
} else if config.HardDriveInterface == "virtio" {
|
||||
controllerName = "VirtIO Controller"
|
||||
} else if config.HardDriveInterface == "pcie" {
|
||||
}
|
||||
|
||||
if config.HardDriveInterface == "pcie" {
|
||||
controllerName = "NVMe Controller"
|
||||
}
|
||||
|
||||
|
||||
@@ -26,7 +26,7 @@ func (s *stepCreateVM) Run(ctx context.Context, state multistep.StateBag) multis
|
||||
|
||||
name := config.VMName
|
||||
|
||||
commands := make([][]string, 10)
|
||||
commands := make([][]string, 6)
|
||||
commands[0] = []string{
|
||||
"createvm", "--name", name,
|
||||
"--ostype", config.GuestOSType, "--register",
|
||||
@@ -40,27 +40,11 @@ func (s *stepCreateVM) Run(ctx context.Context, state multistep.StateBag) multis
|
||||
commands[4] = []string{"modifyvm", name, "--usb", map[bool]string{true: "on", false: "off"}[config.HWConfig.USB]}
|
||||
|
||||
if strings.ToLower(config.HWConfig.Sound) == "none" {
|
||||
commands[5] = []string{"modifyvm", name, "--audio", config.HWConfig.Sound,
|
||||
"--audiocontroller", config.AudioController}
|
||||
commands[5] = []string{"modifyvm", name, "--audio", config.HWConfig.Sound}
|
||||
} else {
|
||||
commands[5] = []string{"modifyvm", name, "--audio", config.HWConfig.Sound, "--audioin", "on", "--audioout", "on",
|
||||
"--audiocontroller", config.AudioController}
|
||||
commands[5] = []string{"modifyvm", name, "--audio", config.HWConfig.Sound, "--audioin", "on", "--audioout", "on"}
|
||||
}
|
||||
|
||||
commands[6] = []string{"modifyvm", name, "--chipset", config.Chipset}
|
||||
commands[7] = []string{"modifyvm", name, "--firmware", config.Firmware}
|
||||
// Set the configured NIC type for all 8 possible NICs
|
||||
commands[8] = []string{"modifyvm", name,
|
||||
"--nictype1", config.NICType,
|
||||
"--nictype2", config.NICType,
|
||||
"--nictype3", config.NICType,
|
||||
"--nictype4", config.NICType,
|
||||
"--nictype5", config.NICType,
|
||||
"--nictype6", config.NICType,
|
||||
"--nictype7", config.NICType,
|
||||
"--nictype8", config.NICType}
|
||||
commands[9] = []string{"modifyvm", name, "--graphicscontroller", config.GfxController}
|
||||
|
||||
ui.Say("Creating virtual machine...")
|
||||
for _, command := range commands {
|
||||
err := driver.VBoxManage(command...)
|
||||
|
||||
@@ -23,7 +23,7 @@ type Driver interface {
|
||||
// Clone clones the VMX and the disk to the destination path. The
|
||||
// destination is a path to the VMX file. The disk will be copied
|
||||
// to that same directory.
|
||||
Clone(dst string, src string, cloneType bool, snapshot string) error
|
||||
Clone(dst string, src string, cloneType bool) error
|
||||
|
||||
// CompactDisk compacts a virtual disk.
|
||||
CompactDisk(string) error
|
||||
|
||||
@@ -112,7 +112,7 @@ func NewESX5Driver(dconfig *DriverConfig, config *SSHConfig, vmName string) (Dri
|
||||
}, nil
|
||||
}
|
||||
|
||||
func (d *ESX5Driver) Clone(dst, src string, linked bool, snapshot string) error {
|
||||
func (d *ESX5Driver) Clone(dst, src string, linked bool) error {
|
||||
|
||||
linesToArray := func(lines string) []string { return strings.Split(strings.Trim(lines, "\r\n"), "\n") }
|
||||
|
||||
@@ -328,27 +328,21 @@ 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,13 +92,3 @@ 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")
|
||||
}
|
||||
}
|
||||
|
||||
@@ -31,7 +31,7 @@ func NewFusion5Driver(dconfig *DriverConfig, config *SSHConfig) Driver {
|
||||
}
|
||||
}
|
||||
|
||||
func (d *Fusion5Driver) Clone(dst, src string, linked bool, snapshot string) error {
|
||||
func (d *Fusion5Driver) Clone(dst, src string, linked bool) error {
|
||||
return errors.New("Cloning is not supported with Fusion 5. Please use Fusion 6+.")
|
||||
}
|
||||
|
||||
|
||||
@@ -27,7 +27,7 @@ func NewFusion6Driver(dconfig *DriverConfig, config *SSHConfig) Driver {
|
||||
}
|
||||
}
|
||||
|
||||
func (d *Fusion6Driver) Clone(dst, src string, linked bool, snapshot string) error {
|
||||
func (d *Fusion6Driver) Clone(dst, src string, linked bool) error {
|
||||
|
||||
var cloneType string
|
||||
if linked {
|
||||
@@ -36,11 +36,10 @@ func (d *Fusion6Driver) Clone(dst, src string, linked bool, snapshot string) err
|
||||
cloneType = "full"
|
||||
}
|
||||
|
||||
args := []string{"-T", "fusion", "clone", src, dst, cloneType}
|
||||
if snapshot != "" {
|
||||
args = append(args, "-snapshot", snapshot)
|
||||
}
|
||||
cmd := exec.Command(d.vmrunPath(), args...)
|
||||
cmd := exec.Command(d.vmrunPath(),
|
||||
"-T", "fusion",
|
||||
"clone", src, dst,
|
||||
cloneType)
|
||||
if _, _, err := runAndLog(cmd); err != nil {
|
||||
if strings.Contains(err.Error(), "parameters was invalid") {
|
||||
return fmt.Errorf(
|
||||
|
||||
@@ -14,7 +14,6 @@ type DriverMock struct {
|
||||
CloneDst string
|
||||
CloneSrc string
|
||||
Linked bool
|
||||
Snapshot string
|
||||
CloneErr error
|
||||
|
||||
CompactDiskCalled bool
|
||||
@@ -114,12 +113,11 @@ func (m NetworkMapperMock) DeviceIntoName(device string) (string, error) {
|
||||
return "", nil
|
||||
}
|
||||
|
||||
func (d *DriverMock) Clone(dst string, src string, linked bool, snapshot string) error {
|
||||
func (d *DriverMock) Clone(dst string, src string, linked bool) error {
|
||||
d.CloneCalled = true
|
||||
d.CloneDst = dst
|
||||
d.CloneSrc = src
|
||||
d.Linked = linked
|
||||
d.Snapshot = snapshot
|
||||
return d.CloneErr
|
||||
}
|
||||
|
||||
|
||||
@@ -31,7 +31,7 @@ func NewPlayer5Driver(config *SSHConfig) Driver {
|
||||
}
|
||||
}
|
||||
|
||||
func (d *Player5Driver) Clone(dst, src string, linked bool, snapshot string) error {
|
||||
func (d *Player5Driver) Clone(dst, src string, linked bool) error {
|
||||
return errors.New("Cloning is not supported with VMWare Player version 5. Please use VMWare Player version 6, or greater.")
|
||||
}
|
||||
|
||||
@@ -204,32 +204,12 @@ func (d *Player5Driver) Verify() error {
|
||||
|
||||
d.VmwareDriver.NetworkMapper = func() (NetworkNameMapper, error) {
|
||||
pathNetmap := playerNetmapConfPath()
|
||||
|
||||
// If we were able to find the file (no error), then we can proceed with reading
|
||||
// the networkmapper configuration.
|
||||
if _, err := os.Stat(pathNetmap); err == nil {
|
||||
log.Printf("Located networkmapper configuration file using Player: %s", pathNetmap)
|
||||
return ReadNetmapConfig(pathNetmap)
|
||||
if _, err := os.Stat(pathNetmap); err != nil {
|
||||
return nil, fmt.Errorf("Could not find netmap conf file: %s", pathNetmap)
|
||||
}
|
||||
log.Printf("Located networkmapper configuration file using Player: %s", pathNetmap)
|
||||
|
||||
// If we weren't able to find the networkmapper configuration file, then fall back
|
||||
// to the networking file which might also be in the configuration directory.
|
||||
libpath, _ := playerVMwareRoot()
|
||||
pathNetworking := filepath.Join(libpath, "networking")
|
||||
if _, err := os.Stat(pathNetworking); err != nil {
|
||||
return nil, fmt.Errorf("Could not determine network mappings from files in path: %s", libpath)
|
||||
}
|
||||
|
||||
// We were able to successfully stat the file.. So, now we can open a handle to it.
|
||||
log.Printf("Located networking configuration file using Player: %s", pathNetworking)
|
||||
fd, err := os.Open(pathNetworking)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
defer fd.Close()
|
||||
|
||||
// Then we pass the handle to the networking configuration parser.
|
||||
return ReadNetworkingConfig(fd)
|
||||
return ReadNetmapConfig(pathNetmap)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
@@ -21,7 +21,7 @@ func NewPlayer6Driver(config *SSHConfig) Driver {
|
||||
}
|
||||
}
|
||||
|
||||
func (d *Player6Driver) Clone(dst, src string, linked bool, snapshot string) error {
|
||||
func (d *Player6Driver) Clone(dst, src string, linked bool) error {
|
||||
// TODO(rasa) check if running player+, not just player
|
||||
|
||||
var cloneType string
|
||||
@@ -31,11 +31,11 @@ func (d *Player6Driver) Clone(dst, src string, linked bool, snapshot string) err
|
||||
cloneType = "full"
|
||||
}
|
||||
|
||||
args := []string{"-T", "ws", "clone", src, dst, cloneType}
|
||||
if snapshot != "" {
|
||||
args = append(args, "-snapshot", snapshot)
|
||||
}
|
||||
cmd := exec.Command(d.Player5Driver.VmrunPath, args...)
|
||||
cmd := exec.Command(d.Player5Driver.VmrunPath,
|
||||
"-T", "ws",
|
||||
"clone", src, dst,
|
||||
cloneType)
|
||||
|
||||
if _, _, err := runAndLog(cmd); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
@@ -21,7 +21,7 @@ func NewWorkstation10Driver(config *SSHConfig) Driver {
|
||||
}
|
||||
}
|
||||
|
||||
func (d *Workstation10Driver) Clone(dst, src string, linked bool, snapshot string) error {
|
||||
func (d *Workstation10Driver) Clone(dst, src string, linked bool) error {
|
||||
|
||||
var cloneType string
|
||||
if linked {
|
||||
@@ -30,11 +30,11 @@ func (d *Workstation10Driver) Clone(dst, src string, linked bool, snapshot strin
|
||||
cloneType = "full"
|
||||
}
|
||||
|
||||
args := []string{"-T", "ws", "clone", src, dst, cloneType}
|
||||
if snapshot != "" {
|
||||
args = append(args, "-snapshot", snapshot)
|
||||
}
|
||||
cmd := exec.Command(d.Workstation9Driver.VmrunPath, args...)
|
||||
cmd := exec.Command(d.Workstation9Driver.VmrunPath,
|
||||
"-T", "ws",
|
||||
"clone", src, dst,
|
||||
cloneType)
|
||||
|
||||
if _, _, err := runAndLog(cmd); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
@@ -30,7 +30,7 @@ func NewWorkstation9Driver(config *SSHConfig) Driver {
|
||||
}
|
||||
}
|
||||
|
||||
func (d *Workstation9Driver) Clone(dst, src string, linked bool, snapshot string) error {
|
||||
func (d *Workstation9Driver) Clone(dst, src string, linked bool) error {
|
||||
return errors.New("Cloning is not supported with VMware WS version 9. Please use VMware WS version 10, or greater.")
|
||||
}
|
||||
|
||||
@@ -165,32 +165,12 @@ func (d *Workstation9Driver) Verify() error {
|
||||
|
||||
d.VmwareDriver.NetworkMapper = func() (NetworkNameMapper, error) {
|
||||
pathNetmap := workstationNetmapConfPath()
|
||||
|
||||
// Check that the file for the networkmapper configuration exists. If there's no
|
||||
// error, then the file exists and we can proceed to read the configuration out of it.
|
||||
if _, err := os.Stat(pathNetmap); err == nil {
|
||||
log.Printf("Located networkmapper configuration file using Workstation: %s", pathNetmap)
|
||||
return ReadNetmapConfig(pathNetmap)
|
||||
if _, err := os.Stat(pathNetmap); err != nil {
|
||||
return nil, fmt.Errorf("Could not find netmap conf file: %s", pathNetmap)
|
||||
}
|
||||
log.Printf("Located networkmapper configuration file using Workstation: %s", pathNetmap)
|
||||
|
||||
// If we weren't able to find the networkmapper configuration file, then fall back
|
||||
// to the networking file which might also be in the configuration directory.
|
||||
libpath, _ := workstationVMwareRoot()
|
||||
pathNetworking := filepath.Join(libpath, "networking")
|
||||
if _, err := os.Stat(pathNetworking); err != nil {
|
||||
return nil, fmt.Errorf("Could not determine network mappings from files in path: %s", libpath)
|
||||
}
|
||||
|
||||
// We were able to successfully stat the file.. So, now we can open a handle to it.
|
||||
log.Printf("Located networking configuration file using Workstation: %s", pathNetworking)
|
||||
fd, err := os.Open(pathNetworking)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
defer fd.Close()
|
||||
|
||||
// Then we pass the handle to the networking configuration parser.
|
||||
return ReadNetworkingConfig(fd)
|
||||
return ReadNetmapConfig(pathNetmap)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
@@ -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/legacy_json_templates/engine) that has a single valid variable:
|
||||
// template](/docs/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/legacy_json_templates/engine) that
|
||||
// Path to a [configuration template](/docs/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 }}`.
|
||||
|
||||
@@ -97,7 +97,6 @@ func (b *Builder) Run(ctx context.Context, ui packersdk.Ui, hook packersdk.Hook)
|
||||
OutputDir: &b.config.OutputDir,
|
||||
VMName: b.config.VMName,
|
||||
Linked: b.config.Linked,
|
||||
Snapshot: b.config.AttachSnapshot,
|
||||
},
|
||||
&vmwcommon.StepConfigureVMX{
|
||||
CustomData: b.config.VMXData,
|
||||
|
||||
@@ -47,11 +47,6 @@ type Config struct {
|
||||
// scenarios. Most users will wish to create a full clone instead. Defaults
|
||||
// to false.
|
||||
Linked bool `mapstructure:"linked" required:"false"`
|
||||
// Default to `null/empty`. The name of an
|
||||
// **existing** snapshot to which the builder shall attach the VM before
|
||||
// starting it. If no snapshot is specified the builder will simply start the
|
||||
// VM from it's current state i.e. snapshot.
|
||||
AttachSnapshot string `mapstructure:"attach_snapshot" required:"false"`
|
||||
// Path to the source VMX file to clone. If
|
||||
// remote_type is enabled then this specifies a path on the remote_host.
|
||||
SourcePath string `mapstructure:"source_path" required:"true"`
|
||||
|
||||
@@ -123,7 +123,6 @@ type FlatConfig struct {
|
||||
DiskName *string `mapstructure:"vmdk_name" required:"false" cty:"vmdk_name" hcl:"vmdk_name"`
|
||||
DiskTypeId *string `mapstructure:"disk_type_id" required:"false" cty:"disk_type_id" hcl:"disk_type_id"`
|
||||
Linked *bool `mapstructure:"linked" required:"false" cty:"linked" hcl:"linked"`
|
||||
AttachSnapshot *string `mapstructure:"attach_snapshot" required:"false" cty:"attach_snapshot" hcl:"attach_snapshot"`
|
||||
SourcePath *string `mapstructure:"source_path" required:"true" cty:"source_path" hcl:"source_path"`
|
||||
VMName *string `mapstructure:"vm_name" required:"false" cty:"vm_name" hcl:"vm_name"`
|
||||
}
|
||||
@@ -253,7 +252,6 @@ func (*FlatConfig) HCL2Spec() map[string]hcldec.Spec {
|
||||
"vmdk_name": &hcldec.AttrSpec{Name: "vmdk_name", Type: cty.String, Required: false},
|
||||
"disk_type_id": &hcldec.AttrSpec{Name: "disk_type_id", Type: cty.String, Required: false},
|
||||
"linked": &hcldec.AttrSpec{Name: "linked", Type: cty.Bool, Required: false},
|
||||
"attach_snapshot": &hcldec.AttrSpec{Name: "attach_snapshot", Type: cty.String, Required: false},
|
||||
"source_path": &hcldec.AttrSpec{Name: "source_path", Type: cty.String, Required: false},
|
||||
"vm_name": &hcldec.AttrSpec{Name: "vm_name", Type: cty.String, Required: false},
|
||||
}
|
||||
|
||||
@@ -20,7 +20,6 @@ type StepCloneVMX struct {
|
||||
Path string
|
||||
VMName string
|
||||
Linked bool
|
||||
Snapshot string
|
||||
tempDir string
|
||||
}
|
||||
|
||||
@@ -39,7 +38,7 @@ func (s *StepCloneVMX) Run(ctx context.Context, state multistep.StateBag) multis
|
||||
log.Printf("Cloning from: %s", s.Path)
|
||||
log.Printf("Cloning to: %s", vmxPath)
|
||||
|
||||
if err := driver.Clone(vmxPath, s.Path, s.Linked, s.Snapshot); err != nil {
|
||||
if err := driver.Clone(vmxPath, s.Path, s.Linked); err != nil {
|
||||
return halt(err)
|
||||
}
|
||||
|
||||
|
||||
@@ -5,7 +5,7 @@ package common
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"io/fs"
|
||||
"os"
|
||||
"path/filepath"
|
||||
|
||||
"github.com/hashicorp/packer-plugin-sdk/common"
|
||||
@@ -27,7 +27,7 @@ type OutputConfig struct {
|
||||
// leading zero such as "0755" in JSON file, because JSON does not support
|
||||
// octal value. In Unix-like OS, the actual permission may differ from
|
||||
// this value because of umask.
|
||||
DirPerm fs.FileMode `mapstructure:"directory_permission" required:"false"`
|
||||
DirPerm os.FileMode `mapstructure:"directory_permission" required:"false"`
|
||||
}
|
||||
|
||||
func (c *OutputConfig) Prepare(ctx *interpolate.Context, pc *common.PackerConfig) []error {
|
||||
@@ -45,7 +45,7 @@ func (c *OutputConfig) Prepare(ctx *interpolate.Context, pc *common.PackerConfig
|
||||
func (c *OutputConfig) ListFiles() ([]string, error) {
|
||||
files := make([]string, 0, 10)
|
||||
|
||||
visit := func(path string, info fs.FileInfo, err error) error {
|
||||
visit := func(path string, info os.FileInfo, err error) error {
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
@@ -3,7 +3,7 @@
|
||||
package common
|
||||
|
||||
import (
|
||||
"io/fs"
|
||||
"os"
|
||||
|
||||
"github.com/hashicorp/hcl/v2/hcldec"
|
||||
"github.com/zclconf/go-cty/cty"
|
||||
@@ -13,7 +13,7 @@ import (
|
||||
// Where the contents of a field with a `mapstructure:,squash` tag are bubbled up.
|
||||
type FlatOutputConfig struct {
|
||||
OutputDir *string `mapstructure:"output_directory" required:"false" cty:"output_directory" hcl:"output_directory"`
|
||||
DirPerm *fs.FileMode `mapstructure:"directory_permission" required:"false" cty:"directory_permission" hcl:"directory_permission"`
|
||||
DirPerm *os.FileMode `mapstructure:"directory_permission" required:"false" cty:"directory_permission" hcl:"directory_permission"`
|
||||
}
|
||||
|
||||
// FlatMapstructure returns a new FlatOutputConfig.
|
||||
|
||||
@@ -3,7 +3,7 @@
|
||||
package common
|
||||
|
||||
import (
|
||||
"io/fs"
|
||||
"os"
|
||||
|
||||
"github.com/hashicorp/hcl/v2/hcldec"
|
||||
"github.com/zclconf/go-cty/cty"
|
||||
@@ -17,7 +17,7 @@ type FlatExportConfig struct {
|
||||
Images *bool `mapstructure:"images" cty:"images" hcl:"images"`
|
||||
Manifest *string `mapstructure:"manifest" cty:"manifest" hcl:"manifest"`
|
||||
OutputDir *string `mapstructure:"output_directory" required:"false" cty:"output_directory" hcl:"output_directory"`
|
||||
DirPerm *fs.FileMode `mapstructure:"directory_permission" required:"false" cty:"directory_permission" hcl:"directory_permission"`
|
||||
DirPerm *os.FileMode `mapstructure:"directory_permission" required:"false" cty:"directory_permission" hcl:"directory_permission"`
|
||||
Options []string `mapstructure:"options" cty:"options" hcl:"options"`
|
||||
}
|
||||
|
||||
|
||||
@@ -2,9 +2,34 @@
|
||||
"builders": [
|
||||
{
|
||||
"type": "vsphere-iso",
|
||||
"CPUs": 1,
|
||||
"RAM": 512,
|
||||
|
||||
"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,
|
||||
"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>",
|
||||
@@ -22,43 +47,16 @@
|
||||
"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",
|
||||
"storage": [
|
||||
{
|
||||
"disk_size": 1024,
|
||||
"disk_thin_provisioned": true
|
||||
}
|
||||
],
|
||||
"username": "root",
|
||||
"vcenter_server": "vcenter.vsphere65.test",
|
||||
"vm_name": "alpine-{{timestamp}}"
|
||||
"ssh_password": "jetbrains"
|
||||
}
|
||||
],
|
||||
|
||||
"provisioners": [
|
||||
{
|
||||
"inline": [
|
||||
"ls /"
|
||||
],
|
||||
"type": "shell"
|
||||
"type": "shell",
|
||||
"inline": ["ls /"]
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,44 +0,0 @@
|
||||
# "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 /"]
|
||||
}
|
||||
}
|
||||
@@ -1,25 +0,0 @@
|
||||
# "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,48 +2,49 @@
|
||||
"builders": [
|
||||
{
|
||||
"type": "vsphere-iso",
|
||||
"CPUs": 1,
|
||||
"RAM": 4096,
|
||||
|
||||
"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",
|
||||
"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",
|
||||
"storage": [
|
||||
{
|
||||
"disk_size": 32768,
|
||||
"disk_thin_provisioned": true
|
||||
}
|
||||
],
|
||||
"usb_controller": true,
|
||||
"username": "root",
|
||||
"vcenter_server": "vcenter.vsphere65.test",
|
||||
"vm_name": "macos-packer"
|
||||
"ssh_password": "jetbrains"
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,43 +0,0 @@
|
||||
# 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,9 +2,36 @@
|
||||
"builders": [
|
||||
{
|
||||
"type": "vsphere-iso",
|
||||
"CPUs": 1,
|
||||
"RAM": 1024,
|
||||
|
||||
"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,
|
||||
"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>",
|
||||
@@ -22,42 +49,14 @@
|
||||
" 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": [
|
||||
{
|
||||
"inline": [
|
||||
"ls /"
|
||||
],
|
||||
"type": "shell"
|
||||
"type": "shell",
|
||||
"inline": ["ls /"]
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,52 +0,0 @@
|
||||
# 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",
|
||||
"CPUs": 1,
|
||||
"RAM": 4096,
|
||||
"RAM_reserve_all": true,
|
||||
"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",
|
||||
|
||||
"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,
|
||||
"RAM_reserve_all": true,
|
||||
|
||||
"disk_controller_type": "pvscsi",
|
||||
"disk_size": 32768,
|
||||
"disk_thin_provisioned": true,
|
||||
|
||||
"network_card": "vmxnet3",
|
||||
|
||||
"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"
|
||||
}
|
||||
|
||||
"floppy_files": [
|
||||
"{{template_dir}}/setup/"
|
||||
],
|
||||
"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"
|
||||
"floppy_img_path": "[datastore1] ISO/VMware Tools/10.2.0/pvscsi-Windows8.flp"
|
||||
}
|
||||
],
|
||||
|
||||
"provisioners": [
|
||||
{
|
||||
"inline": [
|
||||
"dir c:\\"
|
||||
],
|
||||
"type": "windows-shell"
|
||||
"type": "windows-shell",
|
||||
"inline": ["dir c:\\"]
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,41 +0,0 @@
|
||||
# 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,18 +34,16 @@ func MergeCloudUserMetaData(usersData ...string) (string, error) {
|
||||
}
|
||||
|
||||
for i, userData := range usersData {
|
||||
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
|
||||
}
|
||||
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,7 +53,6 @@ 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,14 +31,13 @@ func writeSerialLogFile(ctx context.Context, state multistep.StateBag, serialLog
|
||||
sdk := state.Get("sdk").(*ycsdk.SDK)
|
||||
ui := state.Get("ui").(packersdk.Ui)
|
||||
|
||||
instanceID, ok := state.GetOk("instance_id")
|
||||
|
||||
if !ok || instanceID.(string) == "" {
|
||||
instanceID := state.Get("instance_id").(string)
|
||||
if instanceID == "" {
|
||||
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.(string),
|
||||
InstanceId: instanceID,
|
||||
})
|
||||
if err != nil {
|
||||
return fmt.Errorf("Failed to get serial port output for instance (id: %s): %s", instanceID, err)
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user