From a418a22caea93e98d24bcfbb1f8b5c35321ca41f Mon Sep 17 00:00:00 2001
From: DanHam
Date: Thu, 29 Aug 2019 15:40:37 +0100
Subject: [PATCH 01/16] Docs: Fix missing required option `ami_name` for
ebssurrogate builder
---
.../source/docs/builders/amazon-ebssurrogate.html.md.erb | 7 ++++++-
1 file changed, 6 insertions(+), 1 deletion(-)
diff --git a/website/source/docs/builders/amazon-ebssurrogate.html.md.erb b/website/source/docs/builders/amazon-ebssurrogate.html.md.erb
index e34e9ed1b..37148385f 100644
--- a/website/source/docs/builders/amazon-ebssurrogate.html.md.erb
+++ b/website/source/docs/builders/amazon-ebssurrogate.html.md.erb
@@ -38,6 +38,11 @@ builder.
- `access_key` (string) - The access key used to communicate with AWS. [Learn
how to set this](/docs/builders/amazon.html#specifying-amazon-credentials)
+- `ami_name` (string) - 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](../templates/engine.html) for more info).
+
- `instance_type` (string) - The EC2 instance type to use while building the
AMI, such as `m1.small`.
@@ -591,4 +596,4 @@ volumes created by this builder, any volumes inn the source AMI which are not
marked for deletion on termination will remain in your account.
-<%= partial "partials/builders/aws-ssh-differentiation-table" %>
\ No newline at end of file
+<%= partial "partials/builders/aws-ssh-differentiation-table" %>
From 06b049987babc4edc5e11ea6b6dd1aeb375b133b Mon Sep 17 00:00:00 2001
From: DanHam
Date: Thu, 29 Aug 2019 16:44:36 +0100
Subject: [PATCH 02/16] Docs: Formatting, fixes, nits, and improvements to the
ebsvolume builder doc
* Fix or remove references to AMI creation copied in from other docs
* Remove unsupported `snapshot_users` and `snapshot_groups` options
---
.../builders/amazon-ebsvolume.html.md.erb | 64 ++++++++-----------
1 file changed, 28 insertions(+), 36 deletions(-)
diff --git a/website/source/docs/builders/amazon-ebsvolume.html.md.erb b/website/source/docs/builders/amazon-ebsvolume.html.md.erb
index c9e5d885c..e11321e4d 100644
--- a/website/source/docs/builders/amazon-ebsvolume.html.md.erb
+++ b/website/source/docs/builders/amazon-ebsvolume.html.md.erb
@@ -1,7 +1,7 @@
---
description: |
- The amazon-ebsvolume Packer builder is like the EBS builder, but is intended to
- create EBS volumes rather than a machine image.
+ The amazon-ebsvolume Packer builder is like the EBS builder, but is
+ intended to create EBS volumes rather than a machine image.
layout: docs
page_title: 'Amazon EBS Volume - Builders'
sidebar_current: 'docs-builders-amazon-ebsvolume'
@@ -14,9 +14,11 @@ Type: `amazon-ebsvolume`
The `amazon-ebsvolume` Packer builder is able to create Amazon Elastic Block
Store volumes which are prepopulated with filesystems or data.
-This builder builds EBS volumes by launching an EC2 instance from a source AMI,
-provisioning that running machine, and then destroying the source machine,
-keeping the volumes intact.
+This builder creates EBS volumes by launching an EC2 instance from a source
+AMI. One or more EBS volumes are attached to the running instance, allowing
+them to be provisioned into from the running machine. Once provisioning is
+complete the source machine is destroyed. The provisioned volumes are kept
+intact.
This is all done in your own AWS account. The builder will create temporary key
pairs, security group rules, etc. that provide it temporary access to the
@@ -44,11 +46,11 @@ builder.
- `access_key` (string) - The access key used to communicate with AWS. [Learn
how to set this.](/docs/builders/amazon.html#specifying-amazon-credentials)
-- `instance_type` (string) - The EC2 instance type to use while building the
- AMI, such as `m1.small`.
+- `instance_type` (string) - The EC2 instance type to use while creating
+ the EBS volumes, such as `m1.small`.
- `region` (string) - The name of the region, such as `us-east-1`, in which
- to launch the EC2 instance to create the AMI.
+ to launch the EC2 instance to create the new EBS volumes.
- `secret_key` (string) - The secret key used to communicate with AWS. [Learn
how to set this.](/docs/builders/amazon.html#specifying-amazon-credentials)
@@ -60,7 +62,7 @@ builder.
### Optional:
- `ebs_volumes` (array of block device mappings) - Add the block device
- mappings to the AMI. The block device mappings allow for keys:
+ mappings to the running instance. The block device mappings allow for keys:
- `device_name` (string) - The device name exposed to the instance (for
example, `/dev/sdh` or `xvdh`). Required for every device in the block
@@ -82,24 +84,24 @@ builder.
- `iops` (number) - The number of I/O operations per second (IOPS) that
the volume supports. See the documentation on
[IOPs](https://docs.aws.amazon.com/AWSEC2/latest/APIReference/API_EbsBlockDevice.html)
- for more information
+ for more information.
- `no_device` (boolean) - Suppresses the specified device included in the
- block device mapping of the AMI
+ block device mapping of the AMI.
- - `snapshot_id` (string) - The ID of the snapshot
+ - `snapshot_id` (string) - The ID of the snapshot.
- `virtual_name` (string) - The virtual device name. See the
documentation on [Block Device
Mapping](https://docs.aws.amazon.com/AWSEC2/latest/APIReference/API_BlockDeviceMapping.html)
- for more information
+ for more information.
- `volume_size` (number) - The size of the volume, in GiB. Required if
- not specifying a `snapshot_id`
+ not specifying a `snapshot_id`.
- `volume_type` (string) - The volume type. `gp2` for General Purpose
(SSD) volumes, `io1` for Provisioned IOPS (SSD) volumes, and `standard`
- for Magnetic volumes
+ for Magnetic volumes.
- `tags` (map) - Tags to apply to the volume. These are retained after
the builder completes. This is a [template
@@ -108,7 +110,7 @@ builder.
- `associate_public_ip_address` (boolean) - If using a non-default VPC,
public IP addresses are not provided by default. If this is `true`, your
- new instance will get a Public IP. default: `false`
+ new instance will get a Public IP. Default: `false`.
- `availability_zone` (string) - Destination availability zone to launch
instance in. Leave this empty to allow Amazon to auto-assign.
@@ -158,11 +160,9 @@ builder.
- `ena_support` (boolean) - Enable enhanced networking (ENA but not
SriovNetSupport) on HVM-compatible AMIs. If set, add
- `ec2:ModifyInstanceAttribute` to your AWS IAM policy. If false, this will
- disable enhanced networking in the final AMI as opposed to passing the
- setting through unchanged from the source. Note: you must make sure
- enhanced networking is enabled on your instance. See [Amazon's
- documentation on enabling enhanced
+ `ec2:ModifyInstanceAttribute` to your AWS IAM policy. Note: you must
+ make sure enhanced networking is enabled on your instance. See
+ [Amazon's documentation on enabling enhanced
networking](https://docs.aws.amazon.com/AWSEC2/latest/UserGuide/enhanced-networking.html#enabling_enhanced_networking).
- `enable_t2_unlimited` (boolean) - Enabling T2 Unlimited allows the source
@@ -206,8 +206,7 @@ builder.
for more details.
- `run_tags` (object of key/value strings) - Tags to apply to the instance
- that is *launched* to create the AMI. These tags are *not* applied to the
- resulting AMI unless they're duplicated in `tags`. This is a [template
+ that is *launched* to create the EBS volumes. This is a [template
engine](/docs/templates/engine.html), see [Build template
data](#build-template-data) for more information.
@@ -250,16 +249,6 @@ builder.
- `skip_region_validation` (boolean) - Set to `true` if you want to skip
validation of the region configuration option. Defaults to `false`.
-- `snapshot_groups` (array of strings) - 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.
-
-- `snapshot_users` (array of strings) - 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).
-
- `source_ami_filter` (object) - Filters used to populate the `source_ami`
field. Example:
@@ -376,9 +365,12 @@ builder.
`packer_`, where <UUID> is a 36 character unique identifier.
- `temporary_security_group_source_cidrs` (list of string) - A list of IPv4
- CIDR blocks to be authorized access to the instance, when packer is creating a temporary security group.
+ CIDR blocks to be authorized access to the instance, when packer is
+ creating a temporary security group.
- The default is [`0.0.0.0/0`] (i.e., allow any IPv4 source). This is only used when `security_group_id` or `security_group_ids` is not specified.
+ The default is [`0.0.0.0/0`] (i.e., allow any IPv4 source). This is
+ only used when `security_group_id` or `security_group_ids` is not
+ specified.
- `token` (string) - The access token to use. This is different from the
access key and secret key. If you're not sure what this is, then you
@@ -551,4 +543,4 @@ volumes created by this builder, any volumes inn the source AMI which are not
marked for deletion on termination will remain in your account.
-<%= partial "partials/builders/aws-ssh-differentiation-table" %>
\ No newline at end of file
+<%= partial "partials/builders/aws-ssh-differentiation-table" %>
From 30070ffb30ee5e118abf3e401fd5164da9cc06fc Mon Sep 17 00:00:00 2001
From: DanHam
Date: Wed, 28 Aug 2019 09:31:34 +0100
Subject: [PATCH 03/16] Sort fields and options alphabetically
---
builder/amazon/ebsvolume/builder.go | 6 +++---
1 file changed, 3 insertions(+), 3 deletions(-)
diff --git a/builder/amazon/ebsvolume/builder.go b/builder/amazon/ebsvolume/builder.go
index 7047c1734..941b2f958 100644
--- a/builder/amazon/ebsvolume/builder.go
+++ b/builder/amazon/ebsvolume/builder.go
@@ -23,9 +23,9 @@ type Config struct {
awscommon.AccessConfig `mapstructure:",squash"`
awscommon.RunConfig `mapstructure:",squash"`
- VolumeMappings []BlockDevice `mapstructure:"ebs_volumes"`
AMIENASupport *bool `mapstructure:"ena_support"`
AMISriovNetSupport bool `mapstructure:"sriov_support"`
+ VolumeMappings []BlockDevice `mapstructure:"ebs_volumes"`
launchBlockDevices awscommon.BlockDevices
ctx interpolate.Context
@@ -120,8 +120,8 @@ func (b *Builder) Run(ctx context.Context, ui packer.Ui, hook packer.Hook) (pack
AssociatePublicIpAddress: b.config.AssociatePublicIpAddress,
BlockDevices: b.config.launchBlockDevices,
BlockDurationMinutes: b.config.BlockDurationMinutes,
- Ctx: b.config.ctx,
Comm: &b.config.RunConfig.Comm,
+ Ctx: b.config.ctx,
Debug: b.config.PackerDebug,
EbsOptimized: b.config.EbsOptimized,
ExpectedRootDevice: "ebs",
@@ -129,8 +129,8 @@ func (b *Builder) Run(ctx context.Context, ui packer.Ui, hook packer.Hook) (pack
InstanceInitiatedShutdownBehavior: b.config.InstanceInitiatedShutdownBehavior,
InstanceType: b.config.InstanceType,
SourceAMI: b.config.SourceAmi,
- SpotPrice: b.config.SpotPrice,
SpotInstanceTypes: b.config.SpotInstanceTypes,
+ SpotPrice: b.config.SpotPrice,
SpotTags: b.config.SpotTags,
Tags: b.config.RunTags,
UserData: b.config.UserData,
From 430d41fbf9f5b8deeb976abcdf31b394af70b7c4 Mon Sep 17 00:00:00 2001
From: DanHam
Date: Wed, 28 Aug 2019 09:34:16 +0100
Subject: [PATCH 04/16] Add option to enable tagging of the running instances
volumes
* Currently this results in *all* volumes attached to the instance
being tagged with the `run_volume_tags`. This includes any `ebs_volumes`
for which the user may have configured other tags.
* This issue will be addressed in a later commit
---
builder/amazon/ebsvolume/builder.go | 9 ++++++---
1 file changed, 6 insertions(+), 3 deletions(-)
diff --git a/builder/amazon/ebsvolume/builder.go b/builder/amazon/ebsvolume/builder.go
index 941b2f958..8a5b840ae 100644
--- a/builder/amazon/ebsvolume/builder.go
+++ b/builder/amazon/ebsvolume/builder.go
@@ -23,9 +23,10 @@ type Config struct {
awscommon.AccessConfig `mapstructure:",squash"`
awscommon.RunConfig `mapstructure:",squash"`
- AMIENASupport *bool `mapstructure:"ena_support"`
- AMISriovNetSupport bool `mapstructure:"sriov_support"`
- VolumeMappings []BlockDevice `mapstructure:"ebs_volumes"`
+ AMIENASupport *bool `mapstructure:"ena_support"`
+ AMISriovNetSupport bool `mapstructure:"sriov_support"`
+ VolumeMappings []BlockDevice `mapstructure:"ebs_volumes"`
+ VolumeRunTags awscommon.TagMap `mapstructure:"run_volume_tags"`
launchBlockDevices awscommon.BlockDevices
ctx interpolate.Context
@@ -135,6 +136,7 @@ func (b *Builder) Run(ctx context.Context, ui packer.Ui, hook packer.Hook) (pack
Tags: b.config.RunTags,
UserData: b.config.UserData,
UserDataFile: b.config.UserDataFile,
+ VolumeTags: b.config.VolumeRunTags,
}
} else {
instanceStep = &awscommon.StepRunSourceInstance{
@@ -154,6 +156,7 @@ func (b *Builder) Run(ctx context.Context, ui packer.Ui, hook packer.Hook) (pack
Tags: b.config.RunTags,
UserData: b.config.UserData,
UserDataFile: b.config.UserDataFile,
+ VolumeTags: b.config.VolumeRunTags,
}
}
From 61b1605a85839d8757e7f1734234863250463509 Mon Sep 17 00:00:00 2001
From: DanHam
Date: Wed, 28 Aug 2019 17:20:36 +0100
Subject: [PATCH 05/16] More accurate error description; Add comments; Add
output
---
builder/amazon/ebsvolume/step_tag_ebs_volumes.go | 12 +++++++++---
1 file changed, 9 insertions(+), 3 deletions(-)
diff --git a/builder/amazon/ebsvolume/step_tag_ebs_volumes.go b/builder/amazon/ebsvolume/step_tag_ebs_volumes.go
index e58cb98de..29320ea2e 100644
--- a/builder/amazon/ebsvolume/step_tag_ebs_volumes.go
+++ b/builder/amazon/ebsvolume/step_tag_ebs_volumes.go
@@ -4,6 +4,7 @@ import (
"context"
"fmt"
+ "github.com/aws/aws-sdk-go/aws"
"github.com/aws/aws-sdk-go/service/ec2"
"github.com/hashicorp/packer/helper/multistep"
"github.com/hashicorp/packer/packer"
@@ -45,15 +46,19 @@ func (s *stepTagEBSVolumes) Run(ctx context.Context, state multistep.StateBag) m
continue
}
+ ui.Message(fmt.Sprintf("Compiling list of tags to apply to volume on %s...", mapping.DeviceName))
tags, err := mapping.Tags.EC2Tags(s.Ctx, *ec2conn.Config.Region, state)
if err != nil {
- err := fmt.Errorf("Error tagging device %s with %s", mapping.DeviceName, err)
+ err := fmt.Errorf("Error generating tags for device %s: %s", mapping.DeviceName, err)
state.Put("error", err)
ui.Error(err.Error())
return multistep.ActionHalt
}
tags.Report(ui)
+ // Generate the map of volumes and associated tags to apply.
+ // Looping over the instance block device mappings allows us to
+ // obtain the volumeId
for _, v := range instance.BlockDeviceMappings {
if *v.DeviceName == mapping.DeviceName {
toTag[*v.Ebs.VolumeId] = tags
@@ -61,9 +66,11 @@ func (s *stepTagEBSVolumes) Run(ctx context.Context, state multistep.StateBag) m
}
}
+ // Apply the tags
for volumeId, tags := range toTag {
+ ui.Message(fmt.Sprintf("Applying tags to EBS Volume: %s", volumeId))
_, err := ec2conn.CreateTags(&ec2.CreateTagsInput{
- Resources: []*string{&volumeId},
+ Resources: aws.StringSlice([]string{volumeId}),
Tags: tags,
})
if err != nil {
@@ -72,7 +79,6 @@ func (s *stepTagEBSVolumes) Run(ctx context.Context, state multistep.StateBag) m
ui.Error(err.Error())
return multistep.ActionHalt
}
-
}
}
From bbeb1a3528538159dca808162be64ce80169bcd6 Mon Sep 17 00:00:00 2001
From: DanHam
Date: Thu, 29 Aug 2019 13:01:20 +0100
Subject: [PATCH 06/16] Delete any `run_volume_tags` applied to EBS volumes at
instance creation
* Allows `run_volume_tags` to be set without requiring a major rewrite
of the (common) amazon builder code used to start an instance.
The common start up code tags the instance and *all attached volumes
at creation*. If `run_volume_tags` are set this means that any volumes
specified in `ebs_volumes` will *initially* be tagged with the
`run_volume_tags` rather than the tags set in the `ebs_volumes`
section
* Once the instance is reported to be 'ready' the step to tag the EBS
volumes is run. Once complete all volumes should have the tags
requested by the user:
* Volumes associated with the source instance should be tagged with
the tags set in `run_volume_tags` (if any)
* Each EBS volumes specified in the `ebs_volumes` section of the
template should only be tagged with its associated tags (if any)
---
.../amazon/ebsvolume/step_tag_ebs_volumes.go | 46 ++++++++++++++++++-
1 file changed, 45 insertions(+), 1 deletion(-)
diff --git a/builder/amazon/ebsvolume/step_tag_ebs_volumes.go b/builder/amazon/ebsvolume/step_tag_ebs_volumes.go
index 29320ea2e..d98848d70 100644
--- a/builder/amazon/ebsvolume/step_tag_ebs_volumes.go
+++ b/builder/amazon/ebsvolume/step_tag_ebs_volumes.go
@@ -3,6 +3,7 @@ package ebsvolume
import (
"context"
"fmt"
+ "strings"
"github.com/aws/aws-sdk-go/aws"
"github.com/aws/aws-sdk-go/service/ec2"
@@ -20,6 +21,7 @@ func (s *stepTagEBSVolumes) Run(ctx context.Context, state multistep.StateBag) m
ec2conn := state.Get("ec2").(*ec2.EC2)
instance := state.Get("instance").(*ec2.Instance)
ui := state.Get("ui").(packer.Ui)
+ config := state.Get("config").(*Config)
volumes := make(EbsVolumes)
for _, instanceBlockDevices := range instance.BlockDeviceMappings {
@@ -37,8 +39,50 @@ func (s *stepTagEBSVolumes) Run(ctx context.Context, state multistep.StateBag) m
state.Put("ebsvolumes", volumes)
if len(s.VolumeMapping) > 0 {
- ui.Say("Tagging EBS volumes...")
+ // If run_volume_tags were set in the template any attached EBS
+ // volume will have had these tags applied when the instance was
+ // created. We now need to remove these tags to ensure only the EBS
+ // volume tags are applied (if any)
+ if config.VolumeRunTags.IsSet() {
+ ui.Say("Removing any tags applied to EBS volumes when the source instance was created...")
+ ui.Message("Compiling list of existing tags to remove...")
+ existingTags, err := config.VolumeRunTags.EC2Tags(s.Ctx, *ec2conn.Config.Region, state)
+ if err != nil {
+ err := fmt.Errorf("Error generating list of tags to remove: %s", err)
+ state.Put("error", err)
+ ui.Error(err.Error())
+ return multistep.ActionHalt
+ }
+ existingTags.Report(ui)
+
+ // Generate the list of volumes with tags to delete.
+ // Looping over the instance block device mappings allows us to
+ // obtain the volumeId
+ volumeIds := []string{}
+ for _, mapping := range s.VolumeMapping {
+ for _, v := range instance.BlockDeviceMappings {
+ if *v.DeviceName == mapping.DeviceName {
+ volumeIds = append(volumeIds, *v.Ebs.VolumeId)
+ }
+ }
+ }
+
+ // Delete the tags
+ ui.Message(fmt.Sprintf("Deleting 'run_volume_tags' on EBS Volumes: %s", strings.Join(volumeIds, ", ")))
+ _, err = ec2conn.DeleteTags(&ec2.DeleteTagsInput{
+ Resources: aws.StringSlice(volumeIds),
+ Tags: existingTags,
+ })
+ if err != nil {
+ err := fmt.Errorf("Error deleting tags on EBS Volumes %s: %s", strings.Join(volumeIds, ", "), err)
+ state.Put("error", err)
+ ui.Error(err.Error())
+ return multistep.ActionHalt
+ }
+ }
+
+ ui.Say("Tagging EBS volumes...")
toTag := map[string][]*ec2.Tag{}
for _, mapping := range s.VolumeMapping {
if len(mapping.Tags) == 0 {
From 6b80075db0a2846572264623d7d85cc35b42abf4 Mon Sep 17 00:00:00 2001
From: DanHam
Date: Thu, 29 Aug 2019 17:53:06 +0100
Subject: [PATCH 07/16] Docs: Add `run_volume_tags` option to the ebsvolume
docs
* Add note explaining temp override of `ebs_volumes` tags when the
instance is initially created
---
.../builders/amazon-ebsvolume.html.md.erb | 19 +++++++++++++++++++
1 file changed, 19 insertions(+)
diff --git a/website/source/docs/builders/amazon-ebsvolume.html.md.erb b/website/source/docs/builders/amazon-ebsvolume.html.md.erb
index e11321e4d..30e52cfab 100644
--- a/website/source/docs/builders/amazon-ebsvolume.html.md.erb
+++ b/website/source/docs/builders/amazon-ebsvolume.html.md.erb
@@ -108,6 +108,12 @@ builder.
engine](/docs/templates/engine.html), see [Build template
data](#build-template-data) for more information.
+ Note: The tags specified here may be *temporarily* overridden by
+ those specified in `run_volume_tags` - but only while the instance
+ is being created. Packer will replace all tags on the volume with
+ those configured here as soon as the instance is reported as
+ 'ready'.
+
- `associate_public_ip_address` (boolean) - If using a non-default VPC,
public IP addresses are not provided by default. If this is `true`, your
new instance will get a Public IP. Default: `false`.
@@ -210,6 +216,19 @@ builder.
engine](/docs/templates/engine.html), see [Build template
data](#build-template-data) for more information.
+- `run_volume_tags` (object of key/value strings) - Tags to apply to the
+ volumes of the instance that is *launched* to create EBS Volumes. These
+ tags will *not* appear in the tags of the resulting EBS volumes unless
+ they're duplicated under `tags` in the `ebs_volumes` setting. This is a
+ [template engine](/docs/templates/engine.html), see [Build template
+ data](#build-template-data) for more information.
+
+ Note: The tags specified here will be *temporarily* applied to volumes
+ specified in `ebs_volumes` - but only while the instance is being
+ created. Packer will replace all tags on the volume with the tags
+ configured in the `ebs_volumes` section as soon as the instance is
+ reported as 'ready'.
+
- `security_group_id` (string) - The ID (*not* the name) of the security
group to assign to the instance. By default this is not set and Packer will
automatically create a new temporary security group to allow SSH access.
From 8048b200e3fbab35c5eeabf81959ae75cbb9dc83 Mon Sep 17 00:00:00 2001
From: Megan Marsh
Date: Thu, 5 Sep 2019 16:00:54 -0700
Subject: [PATCH 08/16] update changelog
---
CHANGELOG.md | 1 +
1 file changed, 1 insertion(+)
diff --git a/CHANGELOG.md b/CHANGELOG.md
index 858034ce5..c77b1c246 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -23,6 +23,7 @@
* core: Fix bug where sensitive variables contianing commas were not being
properly sanitized in UI calls. [GH-7997]
* provisioner/ansible: Fix provisioner dropped errors [GH-8045]
+* builder/proxmox: Fix panic caused by cancelling build [GH-8067] [GH-8072]
## 1.4.3 (August 14, 2019)
From 675260a8c1c418def0f60e62d831315f1d5204fd Mon Sep 17 00:00:00 2001
From: Arjun Dandagi
Date: Fri, 6 Sep 2019 12:32:30 +0530
Subject: [PATCH 09/16] added version compatibility to console command
let the user know that console is available from version 1.4.2 and above, I was using 1.3.5 and didn't see the console command. thought of updating the docs.
---
website/source/docs/commands/console.html.md | 2 ++
1 file changed, 2 insertions(+)
diff --git a/website/source/docs/commands/console.html.md b/website/source/docs/commands/console.html.md
index fc05e0e3c..31ee4e0b1 100644
--- a/website/source/docs/commands/console.html.md
+++ b/website/source/docs/commands/console.html.md
@@ -14,6 +14,8 @@ interpolations. You may access variables in the Packer config you called the
console with, or provide variables when you call console using the -var or
-var-file command line options.
+~> **Note:** `console` is available from version 1.4.2 and above.
+
Type in the interpolation to test and hit \ to see the result.
To exit the console, type "exit" and hit \, or use Control-C.
From 4a319272e8c3094d6ec05c5929e1bf37aa68e282 Mon Sep 17 00:00:00 2001
From: Megan Marsh
Date: Fri, 6 Sep 2019 09:26:01 -0700
Subject: [PATCH 10/16] borrow some of the copy from the terraform providers
page and link from this list to the page on installing plugins
---
website/source/community-tools.html.md | 25 +++++++++++++++++++------
1 file changed, 19 insertions(+), 6 deletions(-)
diff --git a/website/source/community-tools.html.md b/website/source/community-tools.html.md
index 1dd5aff67..1832281a2 100644
--- a/website/source/community-tools.html.md
+++ b/website/source/community-tools.html.md
@@ -16,16 +16,22 @@ power of Packer templates.
## Third-Party plugins
-This is an incomplete list. If you have written your own plugin, please make a
-pull request to the website so that we can document your contribution here!
+The plugins listed below have been built by the community of Packer users and
+vendors. These plugins are not officially tested nor officially maintained by
+HashiCorp, and are listed here in order to help users find them easily.
+
+To learn more about how to use community plugins, or how to build your own,
+check out the docs on [extending Packer](/docs/extending/plugins.html)
+
+If you have built a plugin and would like to add it to this community list,
+please make a pull request to the website so that we can document your
+contribution here!
+
+### Community Builders
- [ARM builder](https://github.com/solo-io/packer-builder-arm-image) - A builder
for creating ARM images
-- [Comment Provisioner](https://github.com/SwampDragons/packer-provisioner-comment) -
- Example provisioner that allows you to annotate your build with bubble-text
- comments.
-
- [vSphere builder](https://github.com/jetbrains-infra/packer-builder-vsphere) -
A builder for interacting directly with the vSphere API rather than the esx
host directly.
@@ -33,6 +39,13 @@ pull request to the website so that we can document your contribution here!
- [Vultr builder](https://github.com/vultr/packer-builder-vultr) - A builder
for creating [Vultr](https://www.vultr.com/) snapshots.
+
+### Community Provisioners
+
+- [Comment Provisioner](https://github.com/SwampDragons/packer-provisioner-comment) -
+ Example provisioner that allows you to annotate your build with bubble-text
+ comments.
+
- [Windows Update provisioner](https://github.com/rgl/packer-provisioner-windows-update) -
A provisioner for gracefully handling windows updates and the reboots they
cause.
From 8c2c1a82cbe002ebc5d6d28006c18fdce6c0cac3 Mon Sep 17 00:00:00 2001
From: Calle Pettersson
Date: Sat, 7 Sep 2019 21:07:09 +0200
Subject: [PATCH 11/16] Bump proxmox-api-go dependency
---
builder/proxmox/builder.go | 2 +-
builder/proxmox/step_start_vm.go | 4 +-
go.mod | 2 +-
go.sum | 2 +
.../Telmate/proxmox-api-go/proxmox/client.go | 90 +++-
.../proxmox-api-go/proxmox/config_lxc.go | 433 ++++++++++++++++++
.../proxmox-api-go/proxmox/config_qemu.go | 151 +++++-
.../Telmate/proxmox-api-go/proxmox/session.go | 12 +-
vendor/modules.txt | 2 +-
9 files changed, 680 insertions(+), 18 deletions(-)
create mode 100644 vendor/github.com/Telmate/proxmox-api-go/proxmox/config_lxc.go
diff --git a/builder/proxmox/builder.go b/builder/proxmox/builder.go
index 131dfbb48..2b12308e1 100644
--- a/builder/proxmox/builder.go
+++ b/builder/proxmox/builder.go
@@ -46,7 +46,7 @@ func (b *Builder) Run(ctx context.Context, ui packer.Ui, hook packer.Hook) (pack
return nil, err
}
- err = b.proxmoxClient.Login(b.config.Username, b.config.Password)
+ err = b.proxmoxClient.Login(b.config.Username, b.config.Password, "")
if err != nil {
return nil, err
}
diff --git a/builder/proxmox/step_start_vm.go b/builder/proxmox/step_start_vm.go
index 551174ea1..82358efb2 100644
--- a/builder/proxmox/step_start_vm.go
+++ b/builder/proxmox/step_start_vm.go
@@ -30,6 +30,8 @@ func (s *stepStartVM) Run(ctx context.Context, state multistep.StateBag) multist
config := proxmox.ConfigQemu{
Name: c.VMName,
Agent: agent,
+ Boot: "cdn", // Boot priority, c:CDROM -> d:Disk -> n:Network
+ QemuCpu: "host",
Description: "Packer ephemeral build VM",
Memory: c.Memory,
QemuCores: c.Cores,
@@ -142,7 +144,7 @@ func (s *stepStartVM) Cleanup(state multistep.StateBag) {
ui.Say("Stopping VM")
_, err := client.StopVm(vmRef)
if err != nil {
- ui.Error(fmt.Sprintf("Error stop VM. Please stop and delete it manually: %s", err))
+ ui.Error(fmt.Sprintf("Error stopping VM. Please stop and delete it manually: %s", err))
return
}
diff --git a/go.mod b/go.mod
index a132d598c..32556cece 100644
--- a/go.mod
+++ b/go.mod
@@ -8,7 +8,7 @@ require (
github.com/Azure/go-ntlmssp v0.0.0-20180810175552-4a21cbd618b4 // indirect
github.com/ChrisTrenkamp/goxpath v0.0.0-20170625215350-4fe035839290
github.com/NaverCloudPlatform/ncloud-sdk-go v0.0.0-20180110055012-c2e73f942591
- github.com/Telmate/proxmox-api-go v0.0.0-20190614181158-26cd147831a4
+ github.com/Telmate/proxmox-api-go v0.0.0-20190815172943-ef9222844e60
github.com/abdullin/seq v0.0.0-20160510034733-d5467c17e7af // indirect
github.com/aliyun/alibaba-cloud-sdk-go v0.0.0-20190418113227-25233c783f4e
github.com/aliyun/aliyun-oss-go-sdk v0.0.0-20170113022742-e6dbea820a9f
diff --git a/go.sum b/go.sum
index 4fc7f942b..0f660370d 100644
--- a/go.sum
+++ b/go.sum
@@ -25,6 +25,8 @@ github.com/NaverCloudPlatform/ncloud-sdk-go v0.0.0-20180110055012-c2e73f942591 h
github.com/NaverCloudPlatform/ncloud-sdk-go v0.0.0-20180110055012-c2e73f942591/go.mod h1:EHGzQGbwozJBj/4qj3WGrTJ0FqjgOTOxLQ0VNWvPn08=
github.com/Telmate/proxmox-api-go v0.0.0-20190614181158-26cd147831a4 h1:o//09WenT9BNcQypCYfOBfRe5gtLUvUfTPq0xQqPMEI=
github.com/Telmate/proxmox-api-go v0.0.0-20190614181158-26cd147831a4/go.mod h1:OGWyIMJ87/k/GCz8CGiWB2HOXsOVDM6Lpe/nFPkC4IQ=
+github.com/Telmate/proxmox-api-go v0.0.0-20190815172943-ef9222844e60 h1:iEmbIRk4brAP3wevhCr5MGAqxHUbbIDHvE+6D1/7pRA=
+github.com/Telmate/proxmox-api-go v0.0.0-20190815172943-ef9222844e60/go.mod h1:OGWyIMJ87/k/GCz8CGiWB2HOXsOVDM6Lpe/nFPkC4IQ=
github.com/abdullin/seq v0.0.0-20160510034733-d5467c17e7af h1:DBNMBMuMiWYu0b+8KMJuWmfCkcxl09JwdlqwDZZ6U14=
github.com/abdullin/seq v0.0.0-20160510034733-d5467c17e7af/go.mod h1:5Jv4cbFiHJMsVxt52+i0Ha45fjshj6wxYr1r19tB9bw=
github.com/aliyun/alibaba-cloud-sdk-go v0.0.0-20190418113227-25233c783f4e h1:/8wOj52pewmIX/8d5eVO3t7Rr3astkBI/ruyg4WNqRo=
diff --git a/vendor/github.com/Telmate/proxmox-api-go/proxmox/client.go b/vendor/github.com/Telmate/proxmox-api-go/proxmox/client.go
index 75ebf1a8a..d8acfa33f 100644
--- a/vendor/github.com/Telmate/proxmox-api-go/proxmox/client.go
+++ b/vendor/github.com/Telmate/proxmox-api-go/proxmox/client.go
@@ -31,6 +31,7 @@ type Client struct {
ApiUrl string
Username string
Password string
+ Otp string
}
// VmRef - virtual machine ref parts
@@ -38,6 +39,7 @@ type Client struct {
type VmRef struct {
vmId int
node string
+ pool string
vmType string
}
@@ -46,11 +48,19 @@ func (vmr *VmRef) SetNode(node string) {
return
}
+func (vmr *VmRef) SetPool(pool string) {
+ vmr.pool = pool
+}
+
func (vmr *VmRef) SetVmType(vmType string) {
vmr.vmType = vmType
return
}
+func (vmr *VmRef) GetVmType() (string) {
+ return vmr.vmType
+}
+
func (vmr *VmRef) VmId() int {
return vmr.vmId
}
@@ -59,6 +69,10 @@ func (vmr *VmRef) Node() string {
return vmr.node
}
+func (vmr *VmRef) Pool() string {
+ return vmr.pool
+}
+
func NewVmRef(vmId int) (vmr *VmRef) {
vmr = &VmRef{vmId: vmId, node: "", vmType: ""}
return
@@ -73,10 +87,11 @@ func NewClient(apiUrl string, hclient *http.Client, tls *tls.Config) (client *Cl
return client, err
}
-func (c *Client) Login(username string, password string) (err error) {
+func (c *Client) Login(username string, password string, otp string) (err error) {
c.Username = username
c.Password = password
- return c.session.Login(username, password)
+ c.Otp = otp
+ return c.session.Login(username, password, otp)
}
func (c *Client) GetJsonRetryable(url string, data *map[string]interface{}, tries int) error {
@@ -275,10 +290,26 @@ func (c *Client) MonitorCmd(vmr *VmRef, command string) (monitorRes map[string]i
reqbody := ParamsToBody(map[string]interface{}{"command": command})
url := fmt.Sprintf("/nodes/%s/%s/%d/monitor", vmr.node, vmr.vmType, vmr.vmId)
resp, err := c.session.Post(url, nil, nil, &reqbody)
+ if err != nil {
+ return nil, err
+ }
monitorRes, err = ResponseJSON(resp)
return
}
+func (c *Client) Sendkey(vmr *VmRef, qmKey string) error {
+ err := c.CheckVmRef(vmr)
+ if err != nil {
+ return err
+ }
+ reqbody := ParamsToBody(map[string]interface{}{"key": qmKey})
+ url := fmt.Sprintf("/nodes/%s/%s/%d/sendkey", vmr.node, vmr.vmType, vmr.vmId)
+ // No return, even for errors: https://bugzilla.proxmox.com/show_bug.cgi?id=2275
+ _, err = c.session.Put(url, nil, nil, &reqbody)
+
+ return err
+}
+
// WaitForCompletion - poll the API for task completion
func (c *Client) WaitForCompletion(taskResponse map[string]interface{}) (waitExitStatus string, err error) {
if taskResponse["errors"] != nil {
@@ -416,6 +447,29 @@ func (c *Client) CreateQemuVm(node string, vmParams map[string]interface{}) (exi
return
}
+func (c *Client) CreateLxcContainer(node string, vmParams map[string]interface{}) (exitStatus string, err error) {
+ reqbody := ParamsToBody(vmParams)
+ url := fmt.Sprintf("/nodes/%s/lxc", node)
+ var resp *http.Response
+ resp, err = c.session.Post(url, nil, nil, &reqbody)
+ defer resp.Body.Close()
+ if err != nil {
+ // This might not work if we never got a body. We'll ignore errors in trying to read,
+ // but extract the body if possible to give any error information back in the exitStatus
+ b, _ := ioutil.ReadAll(resp.Body)
+ exitStatus = string(b)
+ return exitStatus, err
+ }
+
+ taskResponse, err := ResponseJSON(resp)
+ if err != nil {
+ return "", err
+ }
+ exitStatus, err = c.WaitForCompletion(taskResponse)
+
+ return
+}
+
func (c *Client) CloneQemuVm(vmr *VmRef, vmParams map[string]interface{}) (exitStatus string, err error) {
reqbody := ParamsToBody(vmParams)
url := fmt.Sprintf("/nodes/%s/qemu/%d/clone", vmr.node, vmr.vmId)
@@ -457,6 +511,21 @@ func (c *Client) SetVmConfig(vmr *VmRef, vmParams map[string]interface{}) (exitS
return
}
+// SetLxcConfig - send config options
+func (c *Client) SetLxcConfig(vmr *VmRef, vmParams map[string]interface{}) (exitStatus interface{}, err error) {
+ reqbody := ParamsToBody(vmParams)
+ url := fmt.Sprintf("/nodes/%s/%s/%d/config", vmr.node, vmr.vmType, vmr.vmId)
+ resp, err := c.session.Put(url, nil, nil, &reqbody)
+ if err == nil {
+ taskResponse, err := ResponseJSON(resp)
+ if err != nil {
+ return nil, err
+ }
+ exitStatus, err = c.WaitForCompletion(taskResponse)
+ }
+ return
+}
+
func (c *Client) ResizeQemuDisk(vmr *VmRef, disk string, moreSizeGB int) (exitStatus interface{}, err error) {
// PUT
//disk:virtio0
@@ -478,6 +547,23 @@ func (c *Client) ResizeQemuDisk(vmr *VmRef, disk string, moreSizeGB int) (exitSt
return
}
+func (c *Client) MoveQemuDisk(vmr *VmRef, disk string, storage string) (exitStatus interface{}, err error) {
+ if disk == "" {
+ disk = "virtio0"
+ }
+ reqbody := ParamsToBody(map[string]interface{}{"disk": disk, "storage": storage, "delete": true})
+ url := fmt.Sprintf("/nodes/%s/%s/%d/move_disk", vmr.node, vmr.vmType, vmr.vmId)
+ resp, err := c.session.Post(url, nil, nil, &reqbody)
+ if err == nil {
+ taskResponse, err := ResponseJSON(resp)
+ if err != nil {
+ return nil, err
+ }
+ exitStatus, err = c.WaitForCompletion(taskResponse)
+ }
+ return
+}
+
// GetNextID - Get next free VMID
func (c *Client) GetNextID(currentID int) (nextID int, err error) {
var data map[string]interface{}
diff --git a/vendor/github.com/Telmate/proxmox-api-go/proxmox/config_lxc.go b/vendor/github.com/Telmate/proxmox-api-go/proxmox/config_lxc.go
new file mode 100644
index 000000000..58393f0e3
--- /dev/null
+++ b/vendor/github.com/Telmate/proxmox-api-go/proxmox/config_lxc.go
@@ -0,0 +1,433 @@
+package proxmox
+
+import (
+ "encoding/json"
+ "fmt"
+ "io"
+ "log"
+ "strings"
+ "strconv"
+)
+
+// LXC options for the Proxmox API
+type configLxc struct {
+ Ostemplate string `json:"ostemplate"`
+ Arch string `json:"arch"`
+ BWLimit int `json:"bwlimit,omitempty"`
+ CMode string `json:"cmode"`
+ Console bool `json:"console"`
+ Cores int `json:"cores,omitempty"`
+ CPULimit int `json:"cpulimit"`
+ CPUUnits int `json:"cpuunits"`
+ Description string `json:"description,omitempty"`
+ Features QemuDevice `json:"features,omitempty"`
+ Force bool `json:"force,omitempty"`
+ Hookscript string `json:"hookscript,omitempty"`
+ Hostname string `json:"hostname,omitempty"`
+ IgnoreUnpackErrors bool `json:"ignore-unpack-errors,omitempty"`
+ Lock string `json:"lock,omitempty"`
+ Memory int `json:"memory"`
+ Mountpoints QemuDevices `json:"mountpoints,omitempty"`
+ Nameserver string `json:"nameserver,omitempty"`
+ Networks QemuDevices `json:"networks,omitempty"`
+ OnBoot bool `json:"onboot"`
+ OsType string `json:"ostype,omitempty"`
+ Password string `json:"password,omitempty"`
+ Pool string `json:"pool,omitempty"`
+ Protection bool `json:"protection"`
+ Restore bool `json:"restore,omitempty"`
+ RootFs string `json:"rootfs,omitempty"`
+ SearchDomain string `json:"searchdomain,omitempty"`
+ SSHPublicKeys string `json:"ssh-public-keys,omitempty"`
+ Start bool `json:"start"`
+ Startup string `json:"startup,omitempty"`
+ Storage string `json:"storage"`
+ Swap int `json:"swap"`
+ Template bool `json:"template,omitempty"`
+ Tty int `json:"tty"`
+ Unique bool `json:"unique,omitempty"`
+ Unprivileged bool `json:"unprivileged"`
+ Unused []string `json:"unused,omitempty"`
+}
+
+func NewConfigLxc() (configLxc) {
+ return configLxc{
+ Arch: "amd64",
+ CMode: "tty",
+ Console: true,
+ CPULimit: 0,
+ CPUUnits: 1024,
+ Memory: 512,
+ OnBoot: false,
+ Protection: false,
+ Start: false,
+ Storage: "local",
+ Swap: 512,
+ Template: false,
+ Tty: 2,
+ Unprivileged: false,
+ }
+}
+
+func NewConfigLxcFromJson(io io.Reader) (config configLxc, err error) {
+ config = NewConfigLxc()
+ err = json.NewDecoder(io).Decode(config)
+ if err != nil {
+ log.Fatal(err)
+ return config, err
+ }
+ log.Println(config)
+ return
+}
+
+func NewConfigLxcFromApi(vmr *VmRef, client *Client) (config *configLxc, err error) {
+ // prepare json map to receive the information from the api
+ var lxcConfig map[string]interface{}
+ lxcConfig, err = client.GetVmConfig(vmr)
+ if err != nil {
+ log.Fatal(err)
+ return nil, err
+ }
+
+ // prepare a new lxc config to store and return\
+ // the information from api
+ newConfig := NewConfigLxc()
+ config = &newConfig
+
+ arch := ""
+ if _, isSet := lxcConfig["arch"]; isSet {
+ arch = lxcConfig["arch"].(string)
+ }
+ cmode := ""
+ if _, isSet := lxcConfig["cmode"]; isSet {
+ cmode = lxcConfig["cmode"].(string)
+ }
+ console := true
+ if _, isSet := lxcConfig["console"]; isSet {
+ console = Itob(int(lxcConfig["console"].(float64)))
+ }
+ cores := 1
+ if _, isSet := lxcConfig["cores"]; isSet {
+ cores = int(lxcConfig["cores"].(float64))
+ }
+ cpulimit := 0
+ if _, isSet := lxcConfig["cpulimit"]; isSet {
+ cpulimit, _ = strconv.Atoi(lxcConfig["cpulimit"].(string))
+ }
+ cpuunits := 1024
+ if _, isSet := lxcConfig["cpuunits"]; isSet {
+ cpuunits = int(lxcConfig["cpuunits"].(float64))
+ }
+ description := ""
+ if _, isSet := lxcConfig["description"]; isSet {
+ description = lxcConfig["description"].(string)
+ }
+
+ // add features, if any
+ if features, isSet := lxcConfig["features"]; isSet {
+ featureList := strings.Split(features.(string), ",")
+
+ // create new device map to store features
+ featureMap := QemuDevice{}
+ // add all features to device map
+ featureMap.readDeviceConfig(featureList)
+ // prepare empty feature map
+ if config.Features == nil {
+ config.Features = QemuDevice{}
+ }
+ // and device config to networks
+ if len(featureMap) > 0 {
+ config.Features = featureMap
+ }
+ }
+
+ hookscript := ""
+ if _, isSet := lxcConfig["hookscript"]; isSet {
+ hookscript = lxcConfig["hookscript"].(string)
+ }
+ hostname := ""
+ if _, isSet := lxcConfig["hostname"]; isSet {
+ hostname = lxcConfig["hostname"].(string)
+ }
+ lock := ""
+ if _, isSet := lxcConfig["lock"]; isSet {
+ lock = lxcConfig["lock"].(string)
+ }
+ memory := 512
+ if _, isSet := lxcConfig["memory"]; isSet {
+ memory = int(lxcConfig["memory"].(float64))
+ }
+
+ // add mountpoints
+ mpNames := []string{}
+
+ for k, _ := range lxcConfig {
+ if mpName:= rxMpName.FindStringSubmatch(k); len(mpName) > 0 {
+ mpNames = append(mpNames, mpName[0])
+ }
+ }
+
+ for _, mpName := range mpNames {
+ mpConfStr := lxcConfig[mpName]
+ mpConfList := strings.Split(mpConfStr.(string), ",")
+
+ id := rxDeviceID.FindStringSubmatch(mpName)
+ mpID, _ := strconv.Atoi(id[0])
+ // add mp id
+ mpConfMap := QemuDevice{
+ "id": mpID,
+ }
+ // add rest of device config
+ mpConfMap.readDeviceConfig(mpConfList)
+ // prepare empty mountpoint map
+ if config.Mountpoints == nil {
+ config.Mountpoints = QemuDevices{}
+ }
+ // and device config to mountpoints
+ if len(mpConfMap) > 0 {
+ config.Mountpoints[mpID] = mpConfMap
+ }
+ }
+
+ nameserver := ""
+ if _, isSet := lxcConfig["nameserver"]; isSet {
+ nameserver = lxcConfig["nameserver"].(string)
+ }
+
+ // add networks
+ nicNames := []string{}
+
+ for k, _ := range lxcConfig {
+ if nicName := rxNicName.FindStringSubmatch(k); len(nicName) > 0 {
+ nicNames = append(nicNames, nicName[0])
+ }
+ }
+
+ for _, nicName := range nicNames {
+ nicConfStr := lxcConfig[nicName]
+ nicConfList := strings.Split(nicConfStr.(string), ",")
+
+ id := rxDeviceID.FindStringSubmatch(nicName)
+ nicID, _ := strconv.Atoi(id[0])
+ // add nic id
+ nicConfMap := QemuDevice{
+ "id": nicID,
+ }
+ // add rest of device config
+ nicConfMap.readDeviceConfig(nicConfList)
+ // prepare empty network map
+ if config.Networks == nil {
+ config.Networks = QemuDevices{}
+ }
+ // and device config to networks
+ if len(nicConfMap) > 0 {
+ config.Networks[nicID] = nicConfMap
+ }
+ }
+
+ onboot := false
+ if _, isSet := lxcConfig["onboot"]; isSet {
+ onboot = Itob(int(lxcConfig["onboot"].(float64)))
+ }
+ ostype := ""
+ if _, isSet := lxcConfig["ostype"]; isSet {
+ ostype = lxcConfig["ostype"].(string)
+ }
+ protection := false
+ if _, isSet := lxcConfig["protection"]; isSet {
+ protection = Itob(int(lxcConfig["protection"].(float64)))
+ }
+ rootfs := ""
+ if _, isSet := lxcConfig["rootfs"]; isSet {
+ rootfs = lxcConfig["rootfs"].(string)
+ }
+ searchdomain := ""
+ if _, isSet := lxcConfig["searchdomain"]; isSet {
+ searchdomain = lxcConfig["searchdomain"].(string)
+ }
+ startup := ""
+ if _, isSet := lxcConfig["startup"]; isSet {
+ startup = lxcConfig["startup"].(string)
+ }
+ swap := 512
+ if _, isSet := lxcConfig["swap"]; isSet {
+ swap = int(lxcConfig["swap"].(float64))
+ }
+ template := false
+ if _, isSet := lxcConfig["template"]; isSet {
+ template = Itob(int(lxcConfig["template"].(float64)))
+ }
+ tty := 2
+ if _, isSet := lxcConfig["tty"]; isSet {
+ tty = int(lxcConfig["tty"].(float64))
+ }
+ unprivileged := false
+ if _, isset := lxcConfig["unprivileged"]; isset {
+ unprivileged = Itob(int(lxcConfig["unprivileged"].(float64)))
+ }
+ var unused []string
+ if _, isset := lxcConfig["unused"]; isset {
+ unused = lxcConfig["unused"].([]string)
+ }
+
+ config.Arch = arch
+ config.CMode = cmode
+ config.Console = console
+ config.Cores = cores
+ config.CPULimit = cpulimit
+ config.CPUUnits = cpuunits
+ config.Description = description
+ config.OnBoot = onboot
+ config.Hookscript = hookscript
+ config.Hostname = hostname
+ config.Lock = lock
+ config.Memory = memory
+ config.Nameserver = nameserver
+ config.OnBoot = onboot
+ config.OsType = ostype
+ config.Protection = protection
+ config.RootFs = rootfs
+ config.SearchDomain = searchdomain
+ config.Startup = startup
+ config.Swap = swap
+ config.Template = template
+ config.Tty = tty
+ config.Unprivileged = unprivileged
+ config.Unused = unused
+
+ return
+}
+
+// create LXC container using the Proxmox API
+func (config configLxc) CreateLxc(vmr *VmRef, client *Client) (err error) {
+ vmr.SetVmType("lxc")
+
+ // convert config to map
+ params, _ := json.Marshal(&config)
+ var paramMap map[string]interface{}
+ json.Unmarshal(params, ¶mMap)
+
+ // build list of features
+ // add features as parameter list to lxc parameters
+ // this overwrites the orginal formatting with a
+ // comma separated list of "key=value" pairs
+ featuresParam := QemuDeviceParam{}
+ featuresParam = featuresParam.createDeviceParam(config.Features, nil)
+ paramMap["features"] = strings.Join(featuresParam, ",")
+
+ // build list of mountpoints
+ // this does the same as for the feature list
+ // except that there can be multiple of these mountpoint sets
+ // and each mountpoint set comes with a new id
+ for mpID, mpConfMap := range config.Mountpoints {
+ mpConfParam := QemuDeviceParam{}
+ mpConfParam = mpConfParam.createDeviceParam(mpConfMap, nil)
+
+ // add mp to lxc parameters
+ mpName := fmt.Sprintf("mp%v", mpID)
+ paramMap[mpName] = strings.Join(mpConfParam, ",")
+ }
+
+ // build list of network parameters
+ for nicID, nicConfMap := range config.Networks {
+ nicConfParam := QemuDeviceParam{}
+ nicConfParam = nicConfParam.createDeviceParam(nicConfMap, nil)
+
+ // add nic to lxc parameters
+ nicName := fmt.Sprintf("net%v", nicID)
+ paramMap[nicName] = strings.Join(nicConfParam, ",")
+ }
+
+ // build list of unused volumes for sake of completenes,
+ // even if it is not recommended to change these volumes manually
+ for volID, vol := range config.Unused {
+ // add volume to lxc parameters
+ volName := fmt.Sprintf("unused%v", volID)
+ paramMap[volName] = vol
+ }
+
+ // now that we concatenated the key value parameter
+ // list for the networks, mountpoints and unused volumes,
+ // remove the original keys, since the Proxmox API does
+ // not know how to handle this key
+ delete(paramMap, "networks")
+ delete(paramMap, "mountpoints")
+ delete(paramMap, "unused")
+
+ // amend vmid
+ paramMap["vmid"] = vmr.vmId
+
+ exitStatus, err := client.CreateLxcContainer(vmr.node, paramMap)
+ if err != nil {
+ return fmt.Errorf("Error creating LXC container: %v, error status: %s (params: %v)", err, exitStatus, params)
+ }
+ return
+}
+
+func (config configLxc) UpdateConfig(vmr *VmRef, client *Client) (err error) {
+ // convert config to map
+ params, _ := json.Marshal(&config)
+ var paramMap map[string]interface{}
+ json.Unmarshal(params, ¶mMap)
+
+ // build list of features
+ // add features as parameter list to lxc parameters
+ // this overwrites the orginal formatting with a
+ // comma separated list of "key=value" pairs
+ featuresParam := QemuDeviceParam{}
+ featuresParam = featuresParam.createDeviceParam(config.Features, nil)
+ paramMap["features"] = strings.Join(featuresParam, ",")
+
+ // build list of mountpoints
+ // this does the same as for the feature list
+ // except that there can be multiple of these mountpoint sets
+ // and each mountpoint set comes with a new id
+ for mpID, mpConfMap := range config.Mountpoints {
+ mpConfParam := QemuDeviceParam{}
+ mpConfParam = mpConfParam.createDeviceParam(mpConfMap, nil)
+
+ // add mp to lxc parameters
+ mpName := fmt.Sprintf("mp%v", mpID)
+ paramMap[mpName] = strings.Join(mpConfParam, ",")
+ }
+
+ // build list of network parameters
+ for nicID, nicConfMap := range config.Networks {
+ nicConfParam := QemuDeviceParam{}
+ nicConfParam = nicConfParam.createDeviceParam(nicConfMap, nil)
+
+ // add nic to lxc parameters
+ nicName := fmt.Sprintf("net%v", nicID)
+ paramMap[nicName] = strings.Join(nicConfParam, ",")
+ }
+
+ // build list of unused volumes for sake of completenes,
+ // even if it is not recommended to change these volumes manually
+ for volID, vol := range config.Unused {
+ // add volume to lxc parameters
+ volName := fmt.Sprintf("unused%v", volID)
+ paramMap[volName] = vol
+ }
+
+ // now that we concatenated the key value parameter
+ // list for the networks, mountpoints and unused volumes,
+ // remove the original keys, since the Proxmox API does
+ // not know how to handle this key
+ delete(paramMap, "networks")
+ delete(paramMap, "mountpoints")
+ delete(paramMap, "unused")
+
+ // delete parameters wich are not supported in updated operations
+ delete(paramMap, "pool")
+ delete(paramMap, "storage")
+ delete(paramMap, "password")
+ delete(paramMap, "ostemplate")
+ delete(paramMap, "start")
+ // even though it is listed as a PUT option in the API documentation
+ // we remove it here because "it should not be modified manually";
+ // also, error "500 unable to modify read-only option: 'unprivileged'"
+ delete(paramMap, "unprivileged")
+
+ _, err = client.SetLxcConfig(vmr, paramMap)
+ return err
+}
diff --git a/vendor/github.com/Telmate/proxmox-api-go/proxmox/config_qemu.go b/vendor/github.com/Telmate/proxmox-api-go/proxmox/config_qemu.go
index f0ee911b5..eaffb1801 100644
--- a/vendor/github.com/Telmate/proxmox-api-go/proxmox/config_qemu.go
+++ b/vendor/github.com/Telmate/proxmox-api-go/proxmox/config_qemu.go
@@ -31,10 +31,17 @@ type ConfigQemu struct {
QemuOs string `json:"os"`
QemuCores int `json:"cores"`
QemuSockets int `json:"sockets"`
+ QemuCpu string `json:"cpu"`
+ QemuNuma bool `json:"numa"`
+ Hotplug string `json:"hotplug"`
QemuIso string `json:"iso"`
FullClone *int `json:"fullclone"`
+ Boot string `json:"boot"`
+ BootDisk string `json:"bootdisk,omitempty"`
+ Scsihw string `json:"scsihw,omitempty"`
QemuDisks QemuDevices `json:"disk"`
QemuNetworks QemuDevices `json:"network"`
+ QemuSerials QemuDevices `json:"serial,omitempty"`
// Deprecated single disk.
DiskSize float64 `json:"diskGB"`
@@ -50,6 +57,7 @@ type ConfigQemu struct {
// cloud-init options
CIuser string `json:"ciuser"`
CIpassword string `json:"cipassword"`
+ CIcustom string `json:"cicustom"`
Searchdomain string `json:"searchdomain"`
Nameserver string `json:"nameserver"`
@@ -76,10 +84,24 @@ func (config ConfigQemu) CreateVm(vmr *VmRef, client *Client) (err error) {
"ostype": config.QemuOs,
"sockets": config.QemuSockets,
"cores": config.QemuCores,
- "cpu": "host",
+ "cpu": config.QemuCpu,
+ "numa": config.QemuNuma,
+ "hotplug": config.Hotplug,
"memory": config.Memory,
+ "boot": config.Boot,
"description": config.Description,
}
+ if vmr.pool != "" {
+ params["pool"] = vmr.pool
+ }
+
+ if config.BootDisk != "" {
+ params["bootdisk"] = config.BootDisk
+ }
+
+ if config.Scsihw != "" {
+ params["scsihw"] = config.Scsihw
+ }
// Create disks config.
config.CreateQemuDisksParams(vmr.vmId, params, false)
@@ -87,6 +109,9 @@ func (config ConfigQemu) CreateVm(vmr *VmRef, client *Client) (err error) {
// Create networks config.
config.CreateQemuNetworksParams(vmr.vmId, params)
+ // Create serial interfaces
+ config.CreateQemuSerialsParams(vmr.vmId, params)
+
exitStatus, err := client.CreateQemuVm(vmr.node, params)
if err != nil {
return fmt.Errorf("Error creating VM: %v, error status: %s (params: %v)", err, exitStatus, params)
@@ -102,7 +127,8 @@ func (config ConfigQemu) HasCloudInit() bool {
config.Nameserver != "" ||
config.Sshkeys != "" ||
config.Ipconfig0 != "" ||
- config.Ipconfig1 != ""
+ config.Ipconfig1 != "" ||
+ config.CIcustom != ""
}
/*
@@ -136,6 +162,10 @@ func (config ConfigQemu) CloneVm(sourceVmr *VmRef, vmr *VmRef, client *Client) (
"storage": storage,
"full": fullclone,
}
+ if vmr.pool != "" {
+ params["pool"] = vmr.pool
+ }
+
_, err = client.CloneQemuVm(sourceVmr, params)
if err != nil {
return
@@ -151,7 +181,19 @@ func (config ConfigQemu) UpdateConfig(vmr *VmRef, client *Client) (err error) {
"agent": config.Agent,
"sockets": config.QemuSockets,
"cores": config.QemuCores,
+ "cpu": config.QemuCpu,
+ "numa": config.QemuNuma,
+ "hotplug": config.Hotplug,
"memory": config.Memory,
+ "boot": config.Boot,
+ }
+
+ if config.BootDisk != "" {
+ configParams["bootdisk"] = config.BootDisk
+ }
+
+ if config.Scsihw != "" {
+ configParams["scsihw"] = config.Scsihw
}
// Create disks config.
@@ -160,6 +202,9 @@ func (config ConfigQemu) UpdateConfig(vmr *VmRef, client *Client) (err error) {
// Create networks config.
config.CreateQemuNetworksParams(vmr.vmId, configParams)
+ // Create serial interfaces
+ config.CreateQemuSerialsParams(vmr.vmId, configParams)
+
// cloud-init options
if config.CIuser != "" {
configParams["ciuser"] = config.CIuser
@@ -167,6 +212,9 @@ func (config ConfigQemu) UpdateConfig(vmr *VmRef, client *Client) (err error) {
if config.CIpassword != "" {
configParams["cipassword"] = config.CIpassword
}
+ if config.CIcustom != "" {
+ configParams["cicustom"] = config.CIcustom
+ }
if config.Searchdomain != "" {
configParams["searchdomain"] = config.Searchdomain
}
@@ -202,11 +250,13 @@ func NewConfigQemuFromJson(io io.Reader) (config *ConfigQemu, err error) {
}
var (
- rxIso = regexp.MustCompile(`(.*?),media`)
- rxDeviceID = regexp.MustCompile(`\d+`)
- rxDiskName = regexp.MustCompile(`(virtio|scsi)\d+`)
- rxDiskType = regexp.MustCompile(`\D+`)
- rxNicName = regexp.MustCompile(`net\d+`)
+ rxIso = regexp.MustCompile(`(.*?),media`)
+ rxDeviceID = regexp.MustCompile(`\d+`)
+ rxDiskName = regexp.MustCompile(`(virtio|scsi)\d+`)
+ rxDiskType = regexp.MustCompile(`\D+`)
+ rxNicName = regexp.MustCompile(`net\d+`)
+ rxMpName = regexp.MustCompile(`mp\d+`)
+ rxSerialName = regexp.MustCompile(`serial\d+`)
)
func NewConfigQemuFromApi(vmr *VmRef, client *Client) (config *ConfigQemu, err error) {
@@ -278,6 +328,32 @@ func NewConfigQemuFromApi(vmr *VmRef, client *Client) (config *ConfigQemu, err e
if _, isSet := vmConfig["sockets"]; isSet {
sockets = vmConfig["sockets"].(float64)
}
+ cpu := "host"
+ if _, isSet := vmConfig["cpu"]; isSet {
+ cpu = vmConfig["cpu"].(string)
+ }
+ numa := false
+ if _, isSet := vmConfig["numa"]; isSet {
+ numa = Itob(int(vmConfig["numa"].(float64)))
+ }
+ //Can be network,disk,cpu,memory,usb
+ hotplug := "network,disk,usb"
+ if _, isSet := vmConfig["hotplug"]; isSet {
+ hotplug = vmConfig["hotplug"].(string)
+ }
+ //boot by default from hard disk (c), CD-ROM (d), network (n).
+ boot := "cdn"
+ if _, isSet := vmConfig["boot"]; isSet {
+ boot = vmConfig["boot"].(string)
+ }
+ bootdisk := ""
+ if _, isSet := vmConfig["bootdisk"]; isSet {
+ bootdisk = vmConfig["bootdisk"].(string)
+ }
+ scsihw := "lsi"
+ if _, isSet := vmConfig["scsihw"]; isSet {
+ scsihw = vmConfig["scsihw"].(string)
+ }
config = &ConfigQemu{
Name: name,
Description: strings.TrimSpace(description),
@@ -287,9 +363,16 @@ func NewConfigQemuFromApi(vmr *VmRef, client *Client) (config *ConfigQemu, err e
Memory: int(memory),
QemuCores: int(cores),
QemuSockets: int(sockets),
+ QemuCpu: cpu,
+ QemuNuma: numa,
+ Hotplug: hotplug,
QemuVlanTag: -1,
+ Boot: boot,
+ BootDisk: bootdisk,
+ Scsihw: scsihw,
QemuDisks: QemuDevices{},
QemuNetworks: QemuDevices{},
+ QemuSerials: QemuDevices{},
}
if vmConfig["ide2"] != nil {
@@ -303,6 +386,9 @@ func NewConfigQemuFromApi(vmr *VmRef, client *Client) (config *ConfigQemu, err e
if _, isSet := vmConfig["cipassword"]; isSet {
config.CIpassword = vmConfig["cipassword"].(string)
}
+ if _, isSet := vmConfig["cicustom"]; isSet {
+ config.CIcustom = vmConfig["cicustom"].(string)
+ }
if _, isSet := vmConfig["searchdomain"]; isSet {
config.Searchdomain = vmConfig["searchdomain"].(string)
}
@@ -388,6 +474,30 @@ func NewConfigQemuFromApi(vmr *VmRef, client *Client) (config *ConfigQemu, err e
}
}
+ // Add serials
+ serialNames := []string{}
+
+ for k, _ := range vmConfig {
+ if serialName := rxSerialName.FindStringSubmatch(k); len(serialName) > 0 {
+ serialNames = append(serialNames, serialName[0])
+ }
+ }
+
+ for _, serialName := range serialNames {
+ id := rxDeviceID.FindStringSubmatch(serialName)
+ serialID, _ := strconv.Atoi(id[0])
+
+ serialConfMap := QemuDevice{
+ "id": serialID,
+ "type": vmConfig[serialName],
+ }
+
+ // And device config to serials map.
+ if len(serialConfMap) > 0 {
+ config.QemuSerials[serialID] = serialConfMap
+ }
+ }
+
return
}
@@ -620,7 +730,9 @@ func (c ConfigQemu) CreateQemuDisksParams(
"storage_type": "lvm", // default old style
"cache": "none", // default old value
}
-
+ if c.QemuDisks == nil {
+ c.QemuDisks = make(QemuDevices)
+ }
c.QemuDisks[0] = deprecatedStyleMap
}
@@ -646,9 +758,9 @@ func (c ConfigQemu) CreateQemuDisksParams(
// Disk name.
var diskFile string
- // Currently ZFS local, LVM, and Directory are considered.
+ // Currently ZFS local, LVM, Ceph RBD, and Directory are considered.
// Other formats are not verified, but could be added if they're needed.
- rxStorageTypes := `(zfspool|lvm)`
+ rxStorageTypes := `(zfspool|lvm|rbd)`
storageType := diskConfMap["storage_type"].(string)
if matched, _ := regexp.MatchString(rxStorageTypes, storageType); matched {
diskFile = fmt.Sprintf("file=%v:vm-%v-disk-%v", diskConfMap["storage"], vmID, diskID)
@@ -716,3 +828,22 @@ func (c ConfigQemu) String() string {
jsConf, _ := json.Marshal(c)
return string(jsConf)
}
+
+// Create parameters for serial interface
+func (c ConfigQemu) CreateQemuSerialsParams(
+ vmID int,
+ params map[string]interface{},
+) error {
+
+ // For new style with multi disk device.
+ for serialID, serialConfMap := range c.QemuSerials {
+ // Device name.
+ deviceType := serialConfMap["type"].(string)
+ qemuSerialName := "serial" + strconv.Itoa(serialID)
+
+ // Add back to Qemu prams.
+ params[qemuSerialName] = deviceType
+ }
+
+ return nil
+}
diff --git a/vendor/github.com/Telmate/proxmox-api-go/proxmox/session.go b/vendor/github.com/Telmate/proxmox-api-go/proxmox/session.go
index 72f72eb6a..759d20e75 100644
--- a/vendor/github.com/Telmate/proxmox-api-go/proxmox/session.go
+++ b/vendor/github.com/Telmate/proxmox-api-go/proxmox/session.go
@@ -106,8 +106,12 @@ func TypedResponse(resp *http.Response, v interface{}) error {
return nil
}
-func (s *Session) Login(username string, password string) (err error) {
- reqbody := ParamsToBody(map[string]interface{}{"username": username, "password": password})
+func (s *Session) Login(username string, password string, otp string) (err error) {
+ reqUser := map[string]interface{}{"username": username, "password": password}
+ if otp != "" {
+ reqUser["otp"] = otp
+ }
+ reqbody := ParamsToBody(reqUser)
olddebug := *Debug
*Debug = false // don't share passwords in debug log
resp, err := s.Post("/access/ticket", nil, nil, &reqbody)
@@ -127,6 +131,10 @@ func (s *Session) Login(username string, password string) (err error) {
return fmt.Errorf("Invalid login response:\n-----\n%s\n-----", dr)
}
dat := jbody["data"].(map[string]interface{})
+ //Check if the 2FA was required
+ if dat["NeedTFA"] == 1.0 {
+ return errors.New("Missing TFA code")
+ }
s.AuthTicket = dat["ticket"].(string)
s.CsrfToken = dat["CSRFPreventionToken"].(string)
return nil
diff --git a/vendor/modules.txt b/vendor/modules.txt
index a15d8dbfa..8fce12087 100644
--- a/vendor/modules.txt
+++ b/vendor/modules.txt
@@ -50,7 +50,7 @@ github.com/NaverCloudPlatform/ncloud-sdk-go/sdk
github.com/NaverCloudPlatform/ncloud-sdk-go/common
github.com/NaverCloudPlatform/ncloud-sdk-go/request
github.com/NaverCloudPlatform/ncloud-sdk-go/oauth
-# github.com/Telmate/proxmox-api-go v0.0.0-20190614181158-26cd147831a4
+# github.com/Telmate/proxmox-api-go v0.0.0-20190815172943-ef9222844e60
github.com/Telmate/proxmox-api-go/proxmox
# github.com/aliyun/alibaba-cloud-sdk-go v0.0.0-20190418113227-25233c783f4e
github.com/aliyun/alibaba-cloud-sdk-go/sdk/errors
From df41b56d9addd2c0669c1822ab140e1ce3f2531d Mon Sep 17 00:00:00 2001
From: Calle Pettersson
Date: Sat, 7 Sep 2019 23:09:16 +0200
Subject: [PATCH 12/16] Change MonitorCmd/sendkeys to Sendkey
---
builder/proxmox/bootcommand_driver.go | 5 +-
builder/proxmox/step_type_boot_command.go | 2 +-
.../proxmox/step_type_boot_command_test.go | 102 +++++++++---------
3 files changed, 51 insertions(+), 58 deletions(-)
diff --git a/builder/proxmox/bootcommand_driver.go b/builder/proxmox/bootcommand_driver.go
index 1570845fe..372e34829 100644
--- a/builder/proxmox/bootcommand_driver.go
+++ b/builder/proxmox/bootcommand_driver.go
@@ -102,13 +102,10 @@ func (p *proxmoxDriver) SendSpecial(special string, action bootcommand.KeyAction
}
func (p *proxmoxDriver) send(keys string) error {
- res, err := p.client.MonitorCmd(p.vmRef, "sendkey "+keys)
+ err := p.client.Sendkey(p.vmRef, keys)
if err != nil {
return err
}
- if data, ok := res["data"].(string); ok && len(data) > 0 {
- return fmt.Errorf("failed to send keys: %s", data)
- }
time.Sleep(p.interval)
return nil
diff --git a/builder/proxmox/step_type_boot_command.go b/builder/proxmox/step_type_boot_command.go
index 04361ee21..97cb70c54 100644
--- a/builder/proxmox/step_type_boot_command.go
+++ b/builder/proxmox/step_type_boot_command.go
@@ -30,7 +30,7 @@ type bootCommandTemplateData struct {
}
type commandTyper interface {
- MonitorCmd(*proxmox.VmRef, string) (map[string]interface{}, error)
+ Sendkey(*proxmox.VmRef, string) error
}
var _ commandTyper = &proxmox.Client{}
diff --git a/builder/proxmox/step_type_boot_command_test.go b/builder/proxmox/step_type_boot_command_test.go
index 9514ee0c4..c1581a3f8 100644
--- a/builder/proxmox/step_type_boot_command_test.go
+++ b/builder/proxmox/step_type_boot_command_test.go
@@ -13,75 +13,74 @@ import (
)
type commandTyperMock struct {
- monitorCmd func(*proxmox.VmRef, string) (map[string]interface{}, error)
+ sendkey func(*proxmox.VmRef, string) error
}
-func (m commandTyperMock) MonitorCmd(ref *proxmox.VmRef, cmd string) (map[string]interface{}, error) {
- return m.monitorCmd(ref, cmd)
+func (m commandTyperMock) Sendkey(ref *proxmox.VmRef, cmd string) error {
+ return m.sendkey(ref, cmd)
}
var _ commandTyper = commandTyperMock{}
func TestTypeBootCommand(t *testing.T) {
cs := []struct {
- name string
- builderConfig *Config
- expectCallMonitorCmd bool
- monitorCmdErr error
- monitorCmdRet map[string]interface{}
- expectedKeysSent string
- expectedAction multistep.StepAction
+ name string
+ builderConfig *Config
+ expectCallSendkey bool
+ sendkeyErr error
+ expectedKeysSent string
+ expectedAction multistep.StepAction
}{
{
- name: "simple boot command is typed",
- builderConfig: &Config{BootConfig: bootcommand.BootConfig{BootCommand: []string{"hello"}}},
- expectCallMonitorCmd: true,
- expectedKeysSent: "hello",
- expectedAction: multistep.ActionContinue,
+ name: "simple boot command is typed",
+ builderConfig: &Config{BootConfig: bootcommand.BootConfig{BootCommand: []string{"hello"}}},
+ expectCallSendkey: true,
+ expectedKeysSent: "hello",
+ expectedAction: multistep.ActionContinue,
},
{
- name: "interpolated boot command",
- builderConfig: &Config{BootConfig: bootcommand.BootConfig{BootCommand: []string{"helloworld"}}},
- expectCallMonitorCmd: true,
- expectedKeysSent: "helloretworld",
- expectedAction: multistep.ActionContinue,
+ name: "interpolated boot command",
+ builderConfig: &Config{BootConfig: bootcommand.BootConfig{BootCommand: []string{"helloworld"}}},
+ expectCallSendkey: true,
+ expectedKeysSent: "helloretworld",
+ expectedAction: multistep.ActionContinue,
},
{
- name: "merge multiple interpolated boot command",
- builderConfig: &Config{BootConfig: bootcommand.BootConfig{BootCommand: []string{"Hello World 2.0", "foo!bar@baz"}}},
- expectCallMonitorCmd: true,
- expectedKeysSent: "shift-hellospcshift-worldspc2dot0fooshift-1barshift-2baz",
- expectedAction: multistep.ActionContinue,
+ name: "merge multiple interpolated boot command",
+ builderConfig: &Config{BootConfig: bootcommand.BootConfig{BootCommand: []string{"Hello World 2.0", "foo!bar@baz"}}},
+ expectCallSendkey: true,
+ expectedKeysSent: "shift-hellospcshift-worldspc2dot0fooshift-1barshift-2baz",
+ expectedAction: multistep.ActionContinue,
},
{
- name: "without boot command monitorcmd should not be called",
- builderConfig: &Config{BootConfig: bootcommand.BootConfig{BootCommand: []string{}}},
- expectCallMonitorCmd: false,
- expectedAction: multistep.ActionContinue,
+ name: "without boot command sendkey should not be called",
+ builderConfig: &Config{BootConfig: bootcommand.BootConfig{BootCommand: []string{}}},
+ expectCallSendkey: false,
+ expectedAction: multistep.ActionContinue,
},
{
- name: "invalid boot command template function",
- builderConfig: &Config{BootConfig: bootcommand.BootConfig{BootCommand: []string{"{{ foo }}"}}},
- expectCallMonitorCmd: false,
- expectedAction: multistep.ActionHalt,
+ name: "invalid boot command template function",
+ builderConfig: &Config{BootConfig: bootcommand.BootConfig{BootCommand: []string{"{{ foo }}"}}},
+ expectCallSendkey: false,
+ expectedAction: multistep.ActionHalt,
},
{
// When proxmox (or Qemu, really) doesn't recognize the keycode we send, we get no error back, but
// a map {"data": "invalid parameter: X"}, where X is the keycode.
- name: "invalid keys sent to proxmox",
- builderConfig: &Config{BootConfig: bootcommand.BootConfig{BootCommand: []string{"x"}}},
- expectCallMonitorCmd: true,
- monitorCmdRet: map[string]interface{}{"data": "invalid parameter: x"},
- expectedKeysSent: "x",
- expectedAction: multistep.ActionHalt,
+ name: "invalid keys sent to proxmox",
+ builderConfig: &Config{BootConfig: bootcommand.BootConfig{BootCommand: []string{"x"}}},
+ expectCallSendkey: true,
+ sendkeyErr: fmt.Errorf("invalid parameter: x"),
+ expectedKeysSent: "x",
+ expectedAction: multistep.ActionHalt,
},
{
- name: "error in typing should return halt",
- builderConfig: &Config{BootConfig: bootcommand.BootConfig{BootCommand: []string{"hello"}}},
- expectCallMonitorCmd: true,
- monitorCmdErr: fmt.Errorf("some error"),
- expectedKeysSent: "h",
- expectedAction: multistep.ActionHalt,
+ name: "error in typing should return halt",
+ builderConfig: &Config{BootConfig: bootcommand.BootConfig{BootCommand: []string{"hello"}}},
+ expectCallSendkey: true,
+ sendkeyErr: fmt.Errorf("some error"),
+ expectedKeysSent: "h",
+ expectedAction: multistep.ActionHalt,
},
}
@@ -89,17 +88,14 @@ func TestTypeBootCommand(t *testing.T) {
t.Run(c.name, func(t *testing.T) {
accumulator := strings.Builder{}
typer := commandTyperMock{
- monitorCmd: func(ref *proxmox.VmRef, cmd string) (map[string]interface{}, error) {
- if !c.expectCallMonitorCmd {
- t.Error("Did not expect MonitorCmd to be called")
- }
- if !strings.HasPrefix(cmd, "sendkey ") {
- t.Errorf("Expected all commands to be sendkey, got %s", cmd)
+ sendkey: func(ref *proxmox.VmRef, cmd string) error {
+ if !c.expectCallSendkey {
+ t.Error("Did not expect sendkey to be called")
}
- accumulator.WriteString(strings.TrimPrefix(cmd, "sendkey "))
+ accumulator.WriteString(cmd)
- return c.monitorCmdRet, c.monitorCmdErr
+ return c.sendkeyErr
},
}
From 10ad38fcbd1ac4eaec8bc32296318bf4c3a3629c Mon Sep 17 00:00:00 2001
From: Calle Pettersson
Date: Sun, 8 Sep 2019 18:40:29 +0200
Subject: [PATCH 13/16] Check that disk format is set when pool type requires
it
---
builder/proxmox/config.go | 15 +++++++++++++++
1 file changed, 15 insertions(+)
diff --git a/builder/proxmox/config.go b/builder/proxmox/config.go
index a45ad5a9c..b5cf759fe 100644
--- a/builder/proxmox/config.go
+++ b/builder/proxmox/config.go
@@ -151,6 +151,12 @@ func NewConfig(raws ...interface{}) (*Config, []string, error) {
log.Printf("Disk %d cache mode not set, using default 'none'", idx)
c.Disks[idx].CacheMode = "none"
}
+ // For any storage pool types which aren't in rxStorageTypes in proxmox-api/proxmox/config_qemu.go:651
+ // (currently zfspool and lvm), the format parameter is mandatory. Make sure this is still up to date
+ // when updating the vendored code!
+ if !contains([]string{"zfspool", "lvm"}, c.Disks[idx].StoragePoolType) && c.Disks[idx].DiskFormat == "" {
+ errs = packer.MultiErrorAppend(errs, errors.New(fmt.Sprintf("disk format must be specified for pool type %q", c.Disks[idx].StoragePoolType)))
+ }
}
errs = packer.MultiErrorAppend(errs, c.Comm.Prepare(&c.ctx)...)
@@ -197,3 +203,12 @@ func NewConfig(raws ...interface{}) (*Config, []string, error) {
packer.LogSecretFilter.Set(c.Password)
return c, nil, nil
}
+
+func contains(haystack []string, needle string) bool {
+ for _, candidate := range haystack {
+ if candidate == needle {
+ return true
+ }
+ }
+ return false
+}
From 4981782130b6afd700bf99d8a4cc4a0e3f5142ee Mon Sep 17 00:00:00 2001
From: Adrien Delorme
Date: Mon, 9 Sep 2019 18:27:33 +0200
Subject: [PATCH 14/16] Add community forum link to community mediums list
---
website/source/community.html.erb | 4 ++++
1 file changed, 4 insertions(+)
diff --git a/website/source/community.html.erb b/website/source/community.html.erb
index ef6130d6c..87043ff38 100644
--- a/website/source/community.html.erb
+++ b/website/source/community.html.erb
@@ -22,6 +22,10 @@ description: |-
Discussion list:
Packer Google Group
+
+ Community Forum:
+ Packer Community Forum
+
Bug Tracker:
Issue tracker
From b467bb22a3a696ea038cc852a6f1cb286e029d97 Mon Sep 17 00:00:00 2001
From: Calle Pettersson
Date: Mon, 9 Sep 2019 22:33:48 +0200
Subject: [PATCH 15/16] Shorten default boot_key_interval to 5ms from 100ms
---
builder/proxmox/config.go | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/builder/proxmox/config.go b/builder/proxmox/config.go
index b5cf759fe..e84d81d2c 100644
--- a/builder/proxmox/config.go
+++ b/builder/proxmox/config.go
@@ -103,7 +103,7 @@ func NewConfig(raws ...interface{}) (*Config, []string, error) {
c.RawBootKeyInterval = os.Getenv(common.PackerKeyEnv)
}
if c.RawBootKeyInterval == "" {
- c.BootKeyInterval = common.PackerKeyDefault
+ c.BootKeyInterval = 5 * time.Millisecond
} else {
if interval, err := time.ParseDuration(c.RawBootKeyInterval); err == nil {
c.BootKeyInterval = interval
From 2b6286c31c4bb6d5460400c7a7d46a5724877c60 Mon Sep 17 00:00:00 2001
From: DanHam
Date: Mon, 9 Sep 2019 23:05:40 +0100
Subject: [PATCH 16/16] Fix powershell formatting. Match style for conditionals
---
common/powershell/hyperv/hyperv.go | 22 ++++++++++------------
1 file changed, 10 insertions(+), 12 deletions(-)
diff --git a/common/powershell/hyperv/hyperv.go b/common/powershell/hyperv/hyperv.go
index e93a885eb..15d8d92b1 100644
--- a/common/powershell/hyperv/hyperv.go
+++ b/common/powershell/hyperv/hyperv.go
@@ -35,21 +35,19 @@ param([string]$switchName, [int]$addressIndex)
$HostVMAdapter = Hyper-V\Get-VMNetworkAdapter -ManagementOS -SwitchName $switchName
if ($HostVMAdapter){
$HostNetAdapter = Get-NetAdapter | Where-Object { $_.DeviceId -eq $HostVMAdapter.DeviceId }
- if ($HostNetAdapter){
- $HostNetAdapterIfIndex = @()
- $HostNetAdapterIfIndex += $HostNetAdapter.ifIndex
- $HostNetAdapterConfiguration = @(get-wmiobject win32_networkadapterconfiguration -filter "IPEnabled = 'TRUE'") | Where-Object { $HostNetAdapterIfIndex.Contains($_.InterfaceIndex) }
- if ($HostNetAdapterConfiguration){
- return @($HostNetAdapterConfiguration.IpAddress)[$addressIndex]
- }
- }
-}
-else {
+ if ($HostNetAdapter){
+ $HostNetAdapterIfIndex = @()
+ $HostNetAdapterIfIndex += $HostNetAdapter.ifIndex
+ $HostNetAdapterConfiguration = @(get-wmiobject win32_networkadapterconfiguration -filter "IPEnabled = 'TRUE'") | Where-Object { $HostNetAdapterIfIndex.Contains($_.InterfaceIndex)
+ if ($HostNetAdapterConfiguration){
+ return @($HostNetAdapterConfiguration.IpAddress)[$addressIndex]
+ }
+ }
+} else {
$HostNetAdapterConfiguration=@(Get-NetIPAddress -CimSession $env:computername -AddressFamily IPv4 | Where-Object { ( $_.InterfaceAlias -notmatch 'Loopback' ) -and ( $_.SuffixOrigin -notmatch "Link" )})
if ($HostNetAdapterConfiguration) {
return @($HostNetAdapterConfiguration.IpAddress)[$addressIndex]
- }
- else {
+ } else {
return $false
}
}