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 } }