Compare commits

..

9 Commits

Author SHA1 Message Date
Adrien Delorme 429e5149d3 tidy go modules 2021-02-05 14:55:43 +01:00
Adrien Delorme 0c00ef62a5 remove amazon refs from tests 2021-02-05 14:55:33 +01:00
Adrien Delorme 38254bca28 remove aws plugin core implementations 2021-02-05 14:55:21 +01:00
Adrien Delorme fa034ed424 delete cmd/packer-plugin-amazon/main.go 2021-02-05 14:54:44 +01:00
Adrien Delorme 56b4a512ed remove aws datasources 2021-02-05 14:54:16 +01:00
Adrien Delorme 3f7550cad1 remove docker push ecr reference 2021-02-05 14:54:03 +01:00
Adrien Delorme 559c5c040b remove post-processor/amazon-import 2021-02-05 14:53:53 +01:00
Adrien Delorme 26b952e66d docker builder remove aws ref 2021-02-05 14:53:38 +01:00
Adrien Delorme 453e56e554 remove aws builders 2021-02-05 14:52:55 +01:00
2118 changed files with 113124 additions and 381492 deletions
+9 -20
View File
@@ -7,7 +7,7 @@ version: 2.1
executors: executors:
golang: golang:
docker: docker:
- image: docker.mirror.hashicorp.services/circleci/golang:1.16 - image: docker.mirror.hashicorp.services/circleci/golang:1.15
resource_class: medium+ resource_class: medium+
darwin: darwin:
macos: macos:
@@ -37,19 +37,15 @@ commands:
parameters: parameters:
GOOS: GOOS:
type: string type: string
GOARCH:
default: "amd64"
type: string
steps: steps:
- checkout - checkout
- run: GOOS=<< parameters.GOOS >> GOARCH=<<parameters.GOARCH>> go build -ldflags="-s -w -X github.com/hashicorp/packer/version.GitCommit=${CIRCLE_SHA1}" -o ./pkg/packer_<< parameters.GOOS >>_<< parameters.GOARCH >> . - run: GOOS=<< parameters.GOOS >> go build -ldflags="-s -w -X github.com/hashicorp/packer/version.GitCommit=${CIRCLE_SHA1}" -o ./pkg/packer_<< parameters.GOOS >>_$(go env GOARCH) .
- run: zip ./pkg/packer_<< parameters.GOOS >>_<< parameters.GOARCH >>.zip ./pkg/packer_<< parameters.GOOS >>_<< parameters.GOARCH >> - run: zip ./pkg/packer_<< parameters.GOOS >>_$(go env GOARCH).zip ./pkg/packer_<< parameters.GOOS >>_$(go env GOARCH)
- run: rm ./pkg/packer_<< parameters.GOOS >>_<< parameters.GOARCH >> - run: rm ./pkg/packer_<< parameters.GOOS >>_$(go env GOARCH)
- persist_to_workspace: - persist_to_workspace:
root: . root: .
paths: paths:
- ./pkg/ - ./pkg/
# Golang CircleCI 2.0 configuration file # Golang CircleCI 2.0 configuration file
# #
# Check https://circleci.com/docs/2.0/language-go/ for more details # Check https://circleci.com/docs/2.0/language-go/ for more details
@@ -65,11 +61,13 @@ jobs:
file: coverage.txt file: coverage.txt
test-darwin: test-darwin:
executor: darwin executor: darwin
working_directory: ~/go/github.com/hashicorp/packer working_directory: ~/go/src/github.com/hashicorp/packer
environment:
GO111MODULE: "off"
steps: steps:
- install-go-run-tests-unix: - install-go-run-tests-unix:
GOOS: darwin GOOS: darwin
GOVERSION: "1.16" GOVERSION: "1.15"
- codecov/upload: - codecov/upload:
file: coverage.txt file: coverage.txt
test-windows: test-windows:
@@ -78,7 +76,7 @@ jobs:
shell: bash.exe shell: bash.exe
steps: steps:
- install-go-run-tests-windows: - install-go-run-tests-windows:
GOVERSION: "1.16" GOVERSION: "1.15"
- codecov/upload: - codecov/upload:
file: coverage.txt file: coverage.txt
check-lint: check-lint:
@@ -127,13 +125,6 @@ jobs:
steps: steps:
- build-and-persist-packer-binary: - build-and-persist-packer-binary:
GOOS: darwin GOOS: darwin
build_darwin_arm64:
executor: golang
working_directory: /go/src/github.com/hashicorp/packer
steps:
- build-and-persist-packer-binary:
GOOS: darwin
GOARCH: arm64
build_freebsd: build_freebsd:
executor: golang executor: golang
working_directory: /go/src/github.com/hashicorp/packer working_directory: /go/src/github.com/hashicorp/packer
@@ -214,7 +205,6 @@ workflows:
jobs: jobs:
- build_linux - build_linux
- build_darwin - build_darwin
- build_darwin_arm64
- build_windows - build_windows
- build_freebsd - build_freebsd
- build_openbsd - build_openbsd
@@ -223,7 +213,6 @@ workflows:
requires: requires:
- build_linux - build_linux
- build_darwin - build_darwin
- build_darwin_arm64
- build_windows - build_windows
- build_freebsd - build_freebsd
- build_openbsd - build_openbsd
-1
View File
@@ -6,7 +6,6 @@
*.mdx text eol=lf *.mdx text eol=lf
*.ps1 text eol=lf *.ps1 text eol=lf
*.hcl text eol=lf *.hcl text eol=lf
*.tmpl text eol=lf
*.txt text eol=lf *.txt text eol=lf
go.mod text eol=lf go.mod text eol=lf
go.sum text eol=lf go.sum text eol=lf
+4 -11
View File
@@ -11,12 +11,6 @@ contribute to the project, read on. This document will cover what we're looking
for. By addressing all the points we're looking for, it raises the chances we for. By addressing all the points we're looking for, it raises the chances we
can quickly merge or address your contributions. can quickly merge or address your contributions.
When contributing in any way to the Packer project (new issue, PR, etc), please
be aware that our team identifies with many gender pronouns. Please remember to
use nonbinary pronouns (they/them) and gender neutral language ("Hello folks")
when addressing our team. For more reading on our code of conduct, please see the
[HashiCorp community guidelines](https://www.hashicorp.com/community-guidelines).
## Issues ## Issues
### Reporting an Issue ### Reporting an Issue
@@ -25,7 +19,7 @@ when addressing our team. For more reading on our code of conduct, please see th
already fixed the bug you're experiencing. already fixed the bug you're experiencing.
- Run the command with debug output with the environment variable `PACKER_LOG`. - Run the command with debug output with the environment variable `PACKER_LOG`.
For example: `PACKER_LOG=1 packer build template.pkr.hcl`. Take the _entire_ For example: `PACKER_LOG=1 packer build template.json`. Take the _entire_
output and create a [gist](https://gist.github.com) for linking to in your output and create a [gist](https://gist.github.com) for linking to in your
issue. Packer should strip sensitive keys from the output, but take a look issue. Packer should strip sensitive keys from the output, but take a look
through just in case. through just in case.
@@ -70,9 +64,7 @@ when addressing our team. For more reading on our code of conduct, please see th
If you have never worked with Go before, you will have to install its If you have never worked with Go before, you will have to install its
runtime in order to build packer. runtime in order to build packer.
1. This project always releases from the latest version of golang. 1. This project always releases from the latest version of golang. [Install go](https://golang.org/doc/install#install)
[Install go](https://golang.org/doc/install#install) To properly build from
source, you need to have golang >= v1.16
## Setting up Packer for dev ## Setting up Packer for dev
@@ -80,6 +72,7 @@ If/when you have go installed you can already `go get` packer and `make` in
order to compile and test Packer. These instructions target order to compile and test Packer. These instructions target
POSIX-like environments (macOS, Linux, Cygwin, etc.) so you may need to POSIX-like environments (macOS, Linux, Cygwin, etc.) so you may need to
adjust them for Windows or other shells. adjust them for Windows or other shells.
The instructions below are for go 1.7. or later.
1. Download the Packer source (and its dependencies) by running 1. Download the Packer source (and its dependencies) by running
`go get github.com/hashicorp/packer`. This will download the Packer source to `go get github.com/hashicorp/packer`. This will download the Packer source to
@@ -98,7 +91,7 @@ adjust them for Windows or other shells.
4. After running building Packer successfully, use 4. After running building Packer successfully, use
`$GOPATH/src/github.com/hashicorp/packer/bin/packer` to build a machine and `$GOPATH/src/github.com/hashicorp/packer/bin/packer` to build a machine and
verify your changes work. For instance: verify your changes work. For instance:
`$GOPATH/src/github.com/hashicorp/packer/bin/packer build template.pkr.hcl`. `$GOPATH/src/github.com/hashicorp/packer/bin/packer build template.json`.
5. If everything works well and the tests pass, run `go fmt` on your code before 5. If everything works well and the tests pass, run `go fmt` on your code before
submitting a pull-request. submitting a pull-request.
+1 -1
View File
@@ -10,7 +10,7 @@ Describe the change you are making here!
Please include tests. Check out these examples: Please include tests. Check out these examples:
- https://github.com/hashicorp/packer/blob/master/builder/parallels/common/ssh_config_test.go#L34 - https://github.com/hashicorp/packer/blob/master/builder/virtualbox/common/ssh_config_test.go#L19-L37
- https://github.com/hashicorp/packer/blob/master/post-processor/compress/post-processor_test.go#L153-L182 - https://github.com/hashicorp/packer/blob/master/post-processor/compress/post-processor_test.go#L153-L182
If your PR resolves any open issue(s), please indicate them like this so they will be closed when your PR is merged: If your PR resolves any open issue(s), please indicate them like this so they will be closed when your PR is merged:
-77
View File
@@ -1,77 +0,0 @@
const fs = require("fs");
const path = require("path");
const fetchPluginDocs = require("../../website/components/remote-plugin-docs/utils/fetch-plugin-docs");
const COLOR_RESET = "\x1b[0m";
const COLOR_GREEN = "\x1b[32m";
const COLOR_BLUE = "\x1b[34m";
const COLOR_RED = "\x1b[31m";
async function checkPluginDocs() {
const failureMessages = [];
const pluginsPath = "website/data/docs-remote-plugins.json";
const pluginsFile = fs.readFileSync(path.join(process.cwd(), pluginsPath));
const pluginEntries = JSON.parse(pluginsFile);
const entriesCount = pluginEntries.length;
console.log(`\nResolving plugin docs from ${entriesCount} repositories …`);
for (var i = 0; i < entriesCount; i++) {
const pluginEntry = pluginEntries[i];
const { title, repo, version } = pluginEntry;
console.log(`\n${COLOR_BLUE}${repo}${COLOR_RESET} | ${title}`);
console.log(`Fetching docs from release "${version}" …`);
try {
const undefinedProps = ["title", "repo", "version", "path"].filter(
(key) => typeof pluginEntry[key] == "undefined"
);
if (undefinedProps.length > 0) {
throw new Error(
`Failed to validate plugin docs config. Undefined configuration properties ${JSON.stringify(
undefinedProps
)} found for "${
title || pluginEntry.path || repo
}". In "website/data/docs-remote-plugins.json", please ensure the missing properties ${JSON.stringify(
undefinedProps
)} are defined. Additional information on this configuration can be found in "website/README.md".`
);
}
const docsMdxFiles = await fetchPluginDocs({ repo, tag: version });
const mdxFilesByComponent = docsMdxFiles.reduce((acc, mdxFile) => {
const componentType = mdxFile.filePath.split("/")[1];
if (!acc[componentType]) acc[componentType] = [];
acc[componentType].push(mdxFile);
return acc;
}, {});
console.log(`${COLOR_GREEN}Found valid docs:${COLOR_RESET}`);
Object.keys(mdxFilesByComponent).forEach((component) => {
const componentFiles = mdxFilesByComponent[component];
console.log(` ${component}`);
componentFiles.forEach(({ filePath }) => {
const pathFromComponent = filePath.split("/").slice(2).join("/");
console.log(` ├── ${pathFromComponent}`);
});
});
} catch (err) {
console.log(`${COLOR_RED}${err}${COLOR_RESET}`);
failureMessages.push(`\n${COLOR_RED}× ${repo}: ${COLOR_RESET}${err}`);
}
}
if (failureMessages.length === 0) {
console.log(
`\n---\n\n${COLOR_GREEN}Summary: Successfully resolved all plugin docs.`
);
pluginEntries.forEach((e) =>
console.log(`${COLOR_GREEN}${e.repo}${COLOR_RESET}`)
);
console.log("");
} else {
console.log(
`\n---\n\n${COLOR_RED}Summary: Failed to fetch docs for ${failureMessages.length} plugin(s):`
);
failureMessages.forEach((err) => console.log(err));
console.log("");
process.exit(1);
}
}
checkPluginDocs();
-29
View File
@@ -1,29 +0,0 @@
#
# This GitHub action checks plugin repositories for valid docs.
#
# This provides a quick assessment on PRs of whether
# there might be issues with docs in plugin repositories.
#
# This is intended to help debug Vercel build issues, which
# may or may not be related to docs in plugin repositories.
name: "website: Check plugin docs"
on:
pull_request:
paths:
- "website/**"
schedule:
- cron: "45 0 * * *"
jobs:
check-plugin-docs:
runs-on: ubuntu-latest
steps:
- name: Checkout
uses: actions/checkout@v2
- name: Setup Node
uses: actions/setup-node@v1
- name: Install Dependencies
run: npm i isomorphic-unfetch adm-zip gray-matter
- name: Fetch and validate plugin docs
run: node .github/workflows/check-plugin-docs.js
+1 -1
View File
@@ -7,7 +7,7 @@ name: Check markdown links on modified website files
jobs: jobs:
vercel-deployment-poll: vercel-deployment-poll:
runs-on: ubuntu-latest runs-on: ubuntu-latest
timeout-minutes: 5 #cancel job if no deployment is found within x minutes timeout-minutes: 3 #cancel job if no deployment is found within x minutes
outputs: outputs:
url: ${{ steps.waitForVercelPreviewDeployment.outputs.url }} url: ${{ steps.waitForVercelPreviewDeployment.outputs.url }}
steps: steps:
-18
View File
@@ -22,21 +22,3 @@ poll "closed_issue_locker" "locker" {
If you have found a problem that seems similar to this, please open a new issue and complete the issue template so we can capture all the details necessary to investigate further. If you have found a problem that seems similar to this, please open a new issue and complete the issue template so we can capture all the details necessary to investigate further.
EOF EOF
} }
poll "label_issue_migrater" "remote_plugin_migrater" {
schedule = "0 20 * * * *"
new_owner = "hashicorp"
repo_prefix = "packer-plugin-"
label_prefix = "remote-plugin/"
excluded_label_prefixes = ["communicator/"]
excluded_labels = ["build", "core", "new-plugin-contribution", "website"]
issue_header = <<-EOF
_This issue was originally opened by @${var.user} as ${var.repository}#${var.issue_number}. It was migrated here as a result of the [Packer plugin split](https://github.com/hashicorp/packer/issues/8610#issuecomment-770034737). The original body of the issue is below._
<hr>
EOF
migrated_comment = "This issue has been automatically migrated to ${var.repository}#${var.issue_number} because it looks like an issue with that plugin. If you believe this is _not_ an issue with the plugin, please reply to ${var.repository}#${var.issue_number}."
}
+3 -206
View File
@@ -1,218 +1,15 @@
## 1.7.2 (Upcoming) ## 1.7.0 (Upcoming)
## 1.7.1 (March 31, 2021)
### NOTES:
* builder/amazon: Has been vendored in this release and will no longer be
updated with Packer core. In Packer v1.8.0 the plugin will be removed
entirely. The `amazon` components will continue to work as expected until
then, but for the latest offerings of the Amazon plugin, users are
encourage to use the `packer init` command to install the latest release
version. For more details see [Installing Packer
Plugins](https://www.packer.io/docs/plugins#installing-plugins)
* builder/docker: Has been vendored in this release and will no longer be
updated with Packer core. In Packer v1.8.0 the plugin will be removed
entirely. The `docker` builder will continue to work as expected until
then, but for the latest offerings of the Docker plugin, users are
encourage to use the `packer init` command to install the latest release
version. For more details see [Installing Packer
Plugins](https://www.packer.io/docs/plugins#installing-plugins)
* darwin/arm64: Packer now includes the darwin/arm64 binary to its releases to
supports the new OSX M1. [GH-10804]
* post-processor/docker-\*: Have been vendored in this release and will no
longer be updated with Packer core. In Packer v1.8.0 the plugin will be
removed entirely. The `docker` builder will continue to work as expected
until then, but for the latest offerings of the Docker plugin, users are
encourage to use the `packer init` command to install the latest release
version. For more details see [Installing Packer
Plugins](https://www.packer.io/docs/plugins#installing-plugins)
* post-processor/exoscale-import: Has been vendored in this release and will no
longer be updated with Packer core. In Packer v1.8.0 the plugin will be
removed entirely. The `exoscale-import` post-processor will continue to
work as expected until then, but for the latest offerings of the Exoscale
plugin, users are encourage to use the `packer init` command to install the
latest release version. For more details see [Exoscale Plugin
Repostiroy](https://github.com/exoscale/packer-plugin-exoscale). [GH-10709]
### IMPROVEMENTS
* builder/amazon: allow creation of ebs snapshots without volumes. [GH-9591]
* builder/amazon: Fix issue for multi-region AMI build that fail when
encrypting with KMS and sharing across accounts. [GH-10754]
* builder/azure: Add client_cert_token_timeout option. [GH-10528]
* builder/google: Make Windows password timeout configurable. [GH-10727]
* builder/google: Update public GCP image project as gce-uefi-images are
deprecated. [GH-10724]
* builder/oracle-oci: Update Oracle Go SDK to add support for OCI flexible
shapes. [GH-10833]
* builder/proxmox: Allow using API tokens for Proxmox authentication.
[GH-10797]
* builder/qemu: Added firmware option. [GH-10683]
* builder/scaleway: add support for timeout in shutdown step. [GH-10503]
* builder/vagrant: Fix logging to be clearer when Vagrant builder overrides
values retrieved from vagrant's ssh_config call. [GH-10743]
* builder/virtualbox: Added ISO builder option to create additional disks.
[GH-10674]
* builder/virtualbox: Add options for nested virtualisation and RTC time base.
[GH-10736]
* builder/virtualbox: Add template options for chipset, firmware, nic, graphics
controller, and audio controller. [GH-10671]
* builder/virtualbox: Support for "virtio" storage and ISO drive. [GH-10632]
* builder/vmware: Added "attach_snapshot" parameter to vmware vmx builder.
[GH-10651]
* command/fmt: Adding recursive flag to formatter to format subdirectories.
[GH-10457]
* core/hcl2: Add legacy_isotime function. [GH-10780]
* core/hcl2: Add support for generating `dynamic` blocks within a `build`
block. [GH-10825]
* core/hcl2: Add templatefile function. [GH-10776]
* core/hcl2_upgrade: hcl2_upgrade command can now upgrade json var-files.
[GH-10676]
* core/init: Add implicit required_plugin blocks feature. [GH-10732]
* core: Add http_content option to serve variables from HTTP at preseed.
[GH-10801]
* core: Change template parsing error to include warning about file extensions.
[GH-10652]
* core: Update to gopsutil v3.21.1 to allow builds to work for darwin arm64.
[GH-10697]
* provisioner/inspec: Allow non-zero exit codes for inspec provisioner.
[GH-10723]
### BUG FIXES
* buider/azure: Update builder to ensure a proper clean up Azure temporary
managed Os disks. [GH-10713]
* builder/amazon: Update amazon SDK to fix an SSO login issue. [GH-10668]
* builder/azure: Don't overwrite subscription id if unset. [GH-10659]
* builder/azure: Set default for the parameter client_cert_token_timeout
[GH-10783]
* builder/google: Add new configuration field `windows_password_timeout` to
allow user to set configurable timeouts. [GH-10727]
* builder/hyperv: Make Packer respect winrm_host flag in winrm connect func.
[GH-10748]
* builder/openstack: Make Packer respect winrm_host flag in winrm connect func.
[GH-10748]
* builder/oracle-oci: Update Oracle Go SDK to fix issue with reading key file.
[GH-10560] [GH-10774]
* builder/outscale: Fix omi_description that was ignored in Osc builder
[GH-10792]
* builder/parallels: Make Packer respect winrm_host flag in winrm connect func.
[GH-10748]
* builder/proxmox: Fixes issue when using `additional_iso_files` in HCL enabled
templates. [GH-10772]
* builder/qemu: Make Packer respect winrm_host flag in winrm connect func.
[GH-10748]
* builder/virtualbox: Make Packer respect winrm_host flag in winrm connect
func. [GH-10748]
* builder/vmware: Added a fallback file check when trying to determine the
network-mapping configuration. [GH-10543]
* builder/vsphere: Fix invalid device configuration issue when creating a
vm with multiple disk on the same controller. [GH-10844]
* builder/vsphere: Fix issue where boot command would fail the build do to a
key typing error. This change will now retry to type the key on error
before giving up. [GH-10541]
* core/hcl2_upgrade: Check for nil config map when provisioner/post-processor
doesn't have config. [GH-10730]
* core/hcl2_upgrade: Fix escaped quotes in template functions [GH-10794]
* core/hcl2_upgrade: Make hcl2_upgrade command correctly translate
pause_before. [GH-10654]
* core/hcl2_upgrade: Make json variables using template engines get stored as
locals so they can be properly interpolated. [GH-10685]
* core/init: Fixes issue where `packer init` was failing to install valid
plugins containing a 'v' within its name. [GH-10760]
* core: Packer will now show a proper error message when failing to load the
contents of PACKER_CONFIG. [GH-10766]
* core: Pin Packer to Golang 1.16 to fix code generation issues. [GH-10702]
* core: Templates previously could not interpolate the environment variable
PACKER_LOG_PATH. [GH-10660]
* post-processor/vagrant-cloud: Override direct upload based on box size
[GH-10820]
* provisioner/chef-solo: HCL2 templates can support the json_string option.
[GH-10655]
* provisioner/inspec: Add new configuration field `valid_exit_codes` to allow
for non-zero exit codes. [GH-10723]
* provisioner/salt-masterless: Update urls for the bootstrap scripts used by
salt-masterless provide. [GH-10755]
## 1.7.0 (February 17, 2021)
### FEATURES
* **New Command** (HCL only) `packer init` command will download plugins defined
in a new `required_plugins` block [GH-10304] [GH-10633].
* **New Plugin Type** Data sources can be implemented (blog post forthcoming).
[GH-10440]
* **New Plugin** Aws Secrets Manager data source [GH-10505] [GH-10467]
### BACKWARDS INCOMPATIBILITIES
* core: The API that the Packer core uses to communicate with community plugins
has changed; maintainers of community plugins will need to upgrade their
plugins in order to make them compatible with v1.7.0. An upgrade guide will
be available on our guides page https://www.packer.io/guides.
### IMPROVEMENTS
* builder/amazon: Add `skip_create_ami` option for testing and situations where
artifact is not the ami. [GH-10531]
* builder/amazon: Add IMDSv2 support for AWS EBS builder. [GH-10546]
* builder/amazon: Add resource tags in the launch template used to request spot
instances. [GH-10456]
* builder/openstack: Add `skip_create_image` option for testing and situations
where artifact is not the image. [GH-10496]
* builder/oracle-oci: Add retry strategies to oci calls [GH-10591]
* core/fmt: The `packer fmt` can now read from stdin. [GH-10500]
* core/hcl: Add regex and regexall hcl2 template functions. [GH-10601]
* core/hcl: Templates now support "sensitive" locals. [GH-10509]
* core/hcl: Templates now support error-cleanup-provisioner. [GH-10604]
* hcl2_upgrade: Command now comes with a flag so you can control whether output
templates are annotated with helpful comments. [GH-10619]
* hcl2_upgrade: Command now gracefully handles options with template engine
interpolations. [GH-10625]
* hcl2_upgrade: Command will convert amazon filters to use the ami data source.
[GH-10491]
### BUG FIXES
* amazon/ebssurrogate: Apply snapshot tags at same time as when taking
snapshot. [GH-10150]
* builder/amazon: Fix bug where validation fails if optional iops value is
unset. [GH-10518]
* builder/amazon: Wrap API call to get filtered image in a retry. [GH-10610]
* builder/bsusurrogate: override bsu when omi root device is set. [GH-10490]
* builder/google: Fix bug where Packer would fail when run by users who do not
have permission to access the metadata, even though the metadata is not
necessary to the run. [GH-10458]
* builder/profitbricks: Profitbricks builder could not connect using SSH
communicator. [GH-10549]
* builder/proxmox: Ensure ISOs in additional_iso_files are mounted during VM
creation. [GH-10586]
* builder/proxmox: Improve cloud init error logging for proxmox builder.
[GH-10499]
* builder/qemu: Fix bug where vnc_min_port set to value greater then 5900 could
prevent Packer from connecting to QEMU. [GH-10450] [GH-10451]
* builder/qemu: Fix regression with cd indexing when disk_interface is `ide`.
[GH-10519]
* builder/vmware-esx: Skip credential validation, which requires ovftool to be
installed, if we are not exporting an image. [GH-10520]
* builder/yandex: Fix cloud-init config for ubuntu 20.04. [GH-10522]
* builder/yandex: Fix incorrect access to `instance_id`. [GH-10522]
* core/hcl: Fix bug where []uint8 types could not be passed to plugins.
* core/hcl: fix bug where HCL core could not handle passing []uint8 to plugins.
[GH-10516]
* core/hcl: Fix force flag for hcl2 provisioners and post-processors.
[GH-10571]
* post-processor/vsphere: Fix regression where Packer would not check the exit
status after streaming UI from the ovftool command. [GH-10468]
* post-processor/yandex-export: Changed dhclient command and supported
configuring disk for exportupdate-dump-method. Also added support for
`file` builder. [GH-10488]
## 1.6.6 (December 16, 2020) ## 1.6.6 (December 16, 2020)
### FEATURES ### FEATURES:
* **New command** `fmt` allows users to format existing HCL2 configuration * **New command** `fmt` allows users to format existing HCL2 configuration
files into a canonical style. Please see [fmt command files into a canonical style. Please see [fmt command
docs](https://packer.io/docs/commands/fmt) for more details. [GH-10225] docs](https://packer.io/docs/commands/fmt) for more details. [GH-10225]
[GH-10377] [GH-10377]
* **New function** `env` allows users to set the default value of a variable to * **New function** `env` allows users to set the default value of a variable to
the value of an environment variable. Please see [env function the value of an environment variable. Please see [env function
docs](https://www.packer.io/docs/templates/hcl_templates/functions/contextual/env) for docs](https://www.packer.io/docs/templates/hcl_templates/functions/contextual/env") for
more details. [GH-10240] more details. [GH-10240]
* **Future Scaffolding** This release contains a large number of no-op * **Future Scaffolding** This release contains a large number of no-op
refactoring changes. The Packer team at HashiCorp is preparing to split the refactoring changes. The Packer team at HashiCorp is preparing to split the
+3 -2
View File
@@ -37,8 +37,8 @@
/builder/oracle/ @prydie @owainlewis /builder/oracle/ @prydie @owainlewis
/website/pages/docs/builders/oracle* @prydie @owainlewis /website/pages/docs/builders/oracle* @prydie @owainlewis
/builder/profitbricks/ @LiviusP @mflorin /builder/profitbricks/ @jasmingacic
/website/pages/docs/builders/profitbricks* @LiviusP @mflorin /website/pages/docs/builders/profitbricks* @jasmingacic
/builder/triton/ @sean- /builder/triton/ @sean-
/website/pages/docs/builders/triton* @sean- /website/pages/docs/builders/triton* @sean-
@@ -87,6 +87,7 @@
/post-processor/alicloud-import/ dongxiao.zzh@alibaba-inc.com /post-processor/alicloud-import/ dongxiao.zzh@alibaba-inc.com
/post-processor/checksum/ v.tolstov@selfip.ru /post-processor/checksum/ v.tolstov@selfip.ru
/post-processor/exoscale-import/ @falzm @mcorbin
/post-processor/googlecompute-export/ crunkleton@google.com /post-processor/googlecompute-export/ crunkleton@google.com
/post-processor/yandex-export/ @GennadySpb /post-processor/yandex-export/ @GennadySpb
/post-processor/yandex-import/ @GennadySpb /post-processor/yandex-import/ @GennadySpb
+1 -1
View File
@@ -49,7 +49,7 @@ package:
@sh -c "$(CURDIR)/scripts/dist.sh $(VERSION)" @sh -c "$(CURDIR)/scripts/dist.sh $(VERSION)"
install-build-deps: ## Install dependencies for bin build install-build-deps: ## Install dependencies for bin build
@go install github.com/mitchellh/gox@v1.0.1 @go get github.com/mitchellh/gox
install-gen-deps: ## Install dependencies for code generation install-gen-deps: ## Install dependencies for code generation
# to avoid having to tidy our go deps, we `go get` our binaries from a temp # to avoid having to tidy our go deps, we `go get` our binaries from a temp
+30 -39
View File
@@ -1,7 +1,7 @@
# Packer # Packer
[![Build Status][circleci-badge]][circleci] [![Build Status][circleci-badge]][circleci]
[![Discuss](https://img.shields.io/badge/discuss-packer-3d89ff?style=flat)](https://discuss.hashicorp.com/c/packer) [![Windows Build Status][appveyor-badge]][appveyor]
[![PkgGoDev](https://pkg.go.dev/badge/github.com/hashicorp/packer)](https://pkg.go.dev/github.com/hashicorp/packer) [![PkgGoDev](https://pkg.go.dev/badge/github.com/hashicorp/packer)](https://pkg.go.dev/github.com/hashicorp/packer)
[![GoReportCard][report-badge]][report] [![GoReportCard][report-badge]][report]
[![codecov](https://codecov.io/gh/hashicorp/packer/branch/master/graph/badge.svg)](https://codecov.io/gh/hashicorp/packer) [![codecov](https://codecov.io/gh/hashicorp/packer/branch/master/graph/badge.svg)](https://codecov.io/gh/hashicorp/packer)
@@ -9,16 +9,15 @@
[circleci-badge]: https://circleci.com/gh/hashicorp/packer.svg?style=svg [circleci-badge]: https://circleci.com/gh/hashicorp/packer.svg?style=svg
[circleci]: https://app.circleci.com/pipelines/github/hashicorp/packer [circleci]: https://app.circleci.com/pipelines/github/hashicorp/packer
[appveyor-badge]: https://ci.appveyor.com/api/projects/status/miavlgnp989e5obc/branch/master?svg=true [appveyor-badge]: https://ci.appveyor.com/api/projects/status/miavlgnp989e5obc/branch/master?svg=true
[appveyor]: https://ci.appveyor.com/project/hashicorp/packer
[godoc-badge]: https://godoc.org/github.com/hashicorp/packer?status.svg [godoc-badge]: https://godoc.org/github.com/hashicorp/packer?status.svg
[godoc]: https://godoc.org/github.com/hashicorp/packer [godoc]: https://godoc.org/github.com/hashicorp/packer
[report-badge]: https://goreportcard.com/badge/github.com/hashicorp/packer [report-badge]: https://goreportcard.com/badge/github.com/hashicorp/packer
[report]: https://goreportcard.com/report/github.com/hashicorp/packer [report]: https://goreportcard.com/report/github.com/hashicorp/packer
<p align="center" style="text-align:center;"> * Website: https://www.packer.io
<a href="https://www.packer.io"> * IRC: `#packer-tool` on Freenode
<img alt="HashiCorp Packer logo" src="website/public/img/logo-packer-padded.svg" width="500" /> * Mailing list: [Google Groups](https://groups.google.com/forum/#!forum/packer-tool)
</a>
</p>
Packer is a tool for building identical machine images for multiple platforms Packer is a tool for building identical machine images for multiple platforms
from a single source configuration. from a single source configuration.
@@ -48,43 +47,33 @@ yourself](https://github.com/hashicorp/packer/blob/master/.github/CONTRIBUTING.m
After Packer is installed, create your first template, which tells Packer After Packer is installed, create your first template, which tells Packer
what platforms to build images for and how you want to build them. In our what platforms to build images for and how you want to build them. In our
case, we'll create a simple AMI that has Redis pre-installed. case, we'll create a simple AMI that has Redis pre-installed. Save this
file as `quick-start.json`. Export your AWS credentials as the
Save this file as `quick-start.pkr.hcl`. Export your AWS credentials as the
`AWS_ACCESS_KEY_ID` and `AWS_SECRET_ACCESS_KEY` environment variables. `AWS_ACCESS_KEY_ID` and `AWS_SECRET_ACCESS_KEY` environment variables.
```hcl ```json
variable "access_key" { {
type = string "variables": {
default = "${env("AWS_ACCESS_KEY_ID")}" "access_key": "{{env `AWS_ACCESS_KEY_ID`}}",
} "secret_key": "{{env `AWS_SECRET_ACCESS_KEY`}}"
},
variable "secret_key" { "builders": [{
type = string "type": "amazon-ebs",
default = "${env("AWS_SECRET_ACCESS_KEY")}" "access_key": "{{user `access_key`}}",
} "secret_key": "{{user `secret_key`}}",
"region": "us-east-1",
locals { timestamp = regex_replace(timestamp(), "[- TZ:]", "") } "source_ami": "ami-af22d9b9",
"instance_type": "t2.micro",
source "amazon-ebs" "quick-start" { "ssh_username": "ubuntu",
access_key = "${var.access_key}" "ami_name": "packer-example {{timestamp}}"
ami_name = "packer-example ${local.timestamp}" }]
instance_type = "t2.micro"
region = "us-east-1"
secret_key = "${var.secret_key}"
source_ami = "ami-af22d9b9"
ssh_username = "ubuntu"
}
build {
sources = ["source.amazon-ebs.quick-start"]
} }
``` ```
Next, tell Packer to build the image: Next, tell Packer to build the image:
``` ```
$ packer build quick-start.pkr.hcl $ packer build quick-start.json
... ...
``` ```
@@ -96,9 +85,11 @@ they're run, etc., is up to you.
## Documentation ## Documentation
Comprehensive documentation is viewable on the Packer website at https://www.packer.io/docs. Comprehensive documentation is viewable on the Packer website:
## Contributing to Packer https://www.packer.io/docs
## Developing Packer
See See
[CONTRIBUTING.md](https://github.com/hashicorp/packer/blob/master/.github/CONTRIBUTING.md) [CONTRIBUTING.md](https://github.com/hashicorp/packer/blob/master/.github/CONTRIBUTING.md)
+2 -6
View File
@@ -233,9 +233,7 @@ func NewAzureClient(subscriptionID, sigSubscriptionID, resourceGroupName, storag
azureClient.GalleryImageVersionsClient.ResponseInspector = byConcatDecorators(byInspecting(maxlen), errorCapture(azureClient)) azureClient.GalleryImageVersionsClient.ResponseInspector = byConcatDecorators(byInspecting(maxlen), errorCapture(azureClient))
azureClient.GalleryImageVersionsClient.UserAgent = fmt.Sprintf("%s %s", useragent.String(version.AzurePluginVersion.FormattedVersion()), azureClient.GalleryImageVersionsClient.UserAgent) azureClient.GalleryImageVersionsClient.UserAgent = fmt.Sprintf("%s %s", useragent.String(version.AzurePluginVersion.FormattedVersion()), azureClient.GalleryImageVersionsClient.UserAgent)
azureClient.GalleryImageVersionsClient.Client.PollingDuration = sharedGalleryTimeout azureClient.GalleryImageVersionsClient.Client.PollingDuration = sharedGalleryTimeout
if sigSubscriptionID != "" { azureClient.GalleryImageVersionsClient.SubscriptionID = sigSubscriptionID
azureClient.GalleryImageVersionsClient.SubscriptionID = sigSubscriptionID
}
azureClient.GalleryImagesClient = newCompute.NewGalleryImagesClientWithBaseURI(cloud.ResourceManagerEndpoint, subscriptionID) azureClient.GalleryImagesClient = newCompute.NewGalleryImagesClientWithBaseURI(cloud.ResourceManagerEndpoint, subscriptionID)
azureClient.GalleryImagesClient.Authorizer = autorest.NewBearerAuthorizer(servicePrincipalToken) azureClient.GalleryImagesClient.Authorizer = autorest.NewBearerAuthorizer(servicePrincipalToken)
@@ -243,9 +241,7 @@ func NewAzureClient(subscriptionID, sigSubscriptionID, resourceGroupName, storag
azureClient.GalleryImagesClient.ResponseInspector = byConcatDecorators(byInspecting(maxlen), errorCapture(azureClient)) azureClient.GalleryImagesClient.ResponseInspector = byConcatDecorators(byInspecting(maxlen), errorCapture(azureClient))
azureClient.GalleryImagesClient.UserAgent = fmt.Sprintf("%s %s", useragent.String(version.AzurePluginVersion.FormattedVersion()), azureClient.GalleryImagesClient.UserAgent) azureClient.GalleryImagesClient.UserAgent = fmt.Sprintf("%s %s", useragent.String(version.AzurePluginVersion.FormattedVersion()), azureClient.GalleryImagesClient.UserAgent)
azureClient.GalleryImagesClient.Client.PollingDuration = pollingDuration azureClient.GalleryImagesClient.Client.PollingDuration = pollingDuration
if sigSubscriptionID != "" { azureClient.GalleryImagesClient.SubscriptionID = sigSubscriptionID
azureClient.GalleryImagesClient.SubscriptionID = sigSubscriptionID
}
keyVaultURL, err := url.Parse(cloud.KeyVaultEndpoint) keyVaultURL, err := url.Parse(cloud.KeyVaultEndpoint)
if err != nil { if err != nil {
+8 -116
View File
@@ -20,18 +20,12 @@ package arm
// go test -v -timeout 90m -run TestBuilderAcc_.* // go test -v -timeout 90m -run TestBuilderAcc_.*
import ( import (
"bytes"
"context"
"errors"
"fmt"
"os"
"strings"
"testing" "testing"
"github.com/Azure/go-autorest/autorest" "fmt"
"github.com/Azure/go-autorest/autorest/azure/auth" "os"
builderT "github.com/hashicorp/packer-plugin-sdk/acctest" builderT "github.com/hashicorp/packer-plugin-sdk/acctest"
packersdk "github.com/hashicorp/packer-plugin-sdk/packer"
) )
const DeviceLoginAcceptanceTest = "DEVICELOGIN_TEST" const DeviceLoginAcceptanceTest = "DEVICELOGIN_TEST"
@@ -102,15 +96,10 @@ func TestBuilderAcc_ManagedDisk_Linux_AzureCLI(t *testing.T) {
return return
} }
var b Builder
builderT.Test(t, builderT.TestCase{ builderT.Test(t, builderT.TestCase{
PreCheck: func() { testAuthPreCheck(t) }, PreCheck: func() { testAccPreCheck(t) },
Builder: &b, Builder: &Builder{},
Template: testBuilderAccManagedDiskLinuxAzureCLI, Template: testBuilderAccManagedDiskLinuxAzureCLI,
Check: func([]packersdk.Artifact) error {
checkTemporaryGroupDeleted(t, &b)
return nil
},
}) })
} }
@@ -123,108 +112,15 @@ func TestBuilderAcc_Blob_Windows(t *testing.T) {
} }
func TestBuilderAcc_Blob_Linux(t *testing.T) { func TestBuilderAcc_Blob_Linux(t *testing.T) {
var b Builder
builderT.Test(t, builderT.TestCase{ builderT.Test(t, builderT.TestCase{
PreCheck: func() { testAuthPreCheck(t) }, PreCheck: func() { testAccPreCheck(t) },
Builder: &b, Builder: &Builder{},
Template: testBuilderAccBlobLinux, Template: testBuilderAccBlobLinux,
Check: func([]packersdk.Artifact) error {
checkUnmanagedVHDDeleted(t, &b)
return nil
},
}) })
} }
func testAccPreCheck(*testing.T) {} func testAccPreCheck(*testing.T) {}
func testAuthPreCheck(t *testing.T) {
_, err := auth.NewAuthorizerFromEnvironment()
if err != nil {
t.Fatalf("failed to auth to azure: %s", err)
}
}
func checkTemporaryGroupDeleted(t *testing.T, b *Builder) {
ui := testUi()
spnCloud, spnKeyVault, err := b.getServicePrincipalTokens(ui.Say)
if err != nil {
t.Fatalf("failed getting azure tokens: %s", err)
}
ui.Message("Creating test Azure Resource Manager (ARM) client ...")
azureClient, err := NewAzureClient(
b.config.ClientConfig.SubscriptionID,
b.config.SharedGalleryDestination.SigDestinationSubscription,
b.config.ResourceGroupName,
b.config.StorageAccount,
b.config.ClientConfig.CloudEnvironment(),
b.config.SharedGalleryTimeout,
b.config.PollingDurationTimeout,
spnCloud,
spnKeyVault)
if err != nil {
t.Fatalf("failed to create azure client: %s", err)
}
// Validate resource group has been deleted
_, err = azureClient.GroupsClient.Get(context.Background(), b.config.tmpResourceGroupName)
if err == nil || !resourceNotFound(err) {
t.Fatalf("failed validating resource group deletion: %s", err)
}
}
func checkUnmanagedVHDDeleted(t *testing.T, b *Builder) {
ui := testUi()
spnCloud, spnKeyVault, err := b.getServicePrincipalTokens(ui.Say)
if err != nil {
t.Fatalf("failed getting azure tokens: %s", err)
}
azureClient, err := NewAzureClient(
b.config.ClientConfig.SubscriptionID,
b.config.SharedGalleryDestination.SigDestinationSubscription,
b.config.ResourceGroupName,
b.config.StorageAccount,
b.config.ClientConfig.CloudEnvironment(),
b.config.SharedGalleryTimeout,
b.config.PollingDurationTimeout,
spnCloud,
spnKeyVault)
if err != nil {
t.Fatalf("failed to create azure client: %s", err)
}
// validate temporary os blob was deleted
blob := azureClient.BlobStorageClient.GetContainerReference("images").GetBlobReference(b.config.tmpOSDiskName)
_, err = blob.BreakLease(nil)
if err != nil && !strings.Contains(err.Error(), "BlobNotFound") {
t.Fatalf("failed validating deletion of unmanaged vhd: %s", err)
}
// Validate resource group has been deleted
_, err = azureClient.GroupsClient.Get(context.Background(), b.config.tmpResourceGroupName)
if err == nil || !resourceNotFound(err) {
t.Fatalf("failed validating resource group deletion: %s", err)
}
}
func resourceNotFound(err error) bool {
derr := autorest.DetailedError{}
return errors.As(err, &derr) && derr.StatusCode == 404
}
func testUi() *packersdk.BasicUi {
return &packersdk.BasicUi{
Reader: new(bytes.Buffer),
Writer: new(bytes.Buffer),
ErrorWriter: new(bytes.Buffer),
}
}
const testBuilderAccManagedDiskWindows = ` const testBuilderAccManagedDiskWindows = `
{ {
"variables": { "variables": {
@@ -259,7 +155,6 @@ const testBuilderAccManagedDiskWindows = `
}] }]
} }
` `
const testBuilderAccManagedDiskWindowsBuildResourceGroup = ` const testBuilderAccManagedDiskWindowsBuildResourceGroup = `
{ {
"variables": { "variables": {
@@ -392,7 +287,6 @@ const testBuilderAccManagedDiskLinux = `
}] }]
} }
` `
const testBuilderAccManagedDiskLinuxDeviceLogin = ` const testBuilderAccManagedDiskLinuxDeviceLogin = `
{ {
"variables": { "variables": {
@@ -485,7 +379,6 @@ const testBuilderAccBlobLinux = `
}] }]
} }
` `
const testBuilderAccManagedDiskLinuxAzureCLI = ` const testBuilderAccManagedDiskLinuxAzureCLI = `
{ {
"builders": [{ "builders": [{
@@ -495,8 +388,7 @@ const testBuilderAccManagedDiskLinuxAzureCLI = `
"managed_image_resource_group_name": "packer-acceptance-test", "managed_image_resource_group_name": "packer-acceptance-test",
"managed_image_name": "testBuilderAccManagedDiskLinuxAzureCLI-{{timestamp}}", "managed_image_name": "testBuilderAccManagedDiskLinuxAzureCLI-{{timestamp}}",
"temp_resource_group_name": "packer-acceptance-test-managed-cli",
"os_type": "Linux", "os_type": "Linux",
"image_publisher": "Canonical", "image_publisher": "Canonical",
"image_offer": "UbuntuServer", "image_offer": "UbuntuServer",
-2
View File
@@ -23,7 +23,6 @@ type FlatConfig struct {
ClientID *string `mapstructure:"client_id" cty:"client_id" hcl:"client_id"` ClientID *string `mapstructure:"client_id" cty:"client_id" hcl:"client_id"`
ClientSecret *string `mapstructure:"client_secret" cty:"client_secret" hcl:"client_secret"` ClientSecret *string `mapstructure:"client_secret" cty:"client_secret" hcl:"client_secret"`
ClientCertPath *string `mapstructure:"client_cert_path" cty:"client_cert_path" hcl:"client_cert_path"` ClientCertPath *string `mapstructure:"client_cert_path" cty:"client_cert_path" hcl:"client_cert_path"`
ClientCertExpireTimeout *string `mapstructure:"client_cert_token_timeout" required:"false" cty:"client_cert_token_timeout" hcl:"client_cert_token_timeout"`
ClientJWT *string `mapstructure:"client_jwt" cty:"client_jwt" hcl:"client_jwt"` ClientJWT *string `mapstructure:"client_jwt" cty:"client_jwt" hcl:"client_jwt"`
ObjectID *string `mapstructure:"object_id" cty:"object_id" hcl:"object_id"` ObjectID *string `mapstructure:"object_id" cty:"object_id" hcl:"object_id"`
TenantID *string `mapstructure:"tenant_id" required:"false" cty:"tenant_id" hcl:"tenant_id"` TenantID *string `mapstructure:"tenant_id" required:"false" cty:"tenant_id" hcl:"tenant_id"`
@@ -152,7 +151,6 @@ func (*FlatConfig) HCL2Spec() map[string]hcldec.Spec {
"client_id": &hcldec.AttrSpec{Name: "client_id", Type: cty.String, Required: false}, "client_id": &hcldec.AttrSpec{Name: "client_id", Type: cty.String, Required: false},
"client_secret": &hcldec.AttrSpec{Name: "client_secret", Type: cty.String, Required: false}, "client_secret": &hcldec.AttrSpec{Name: "client_secret", Type: cty.String, Required: false},
"client_cert_path": &hcldec.AttrSpec{Name: "client_cert_path", Type: cty.String, Required: false}, "client_cert_path": &hcldec.AttrSpec{Name: "client_cert_path", Type: cty.String, Required: false},
"client_cert_token_timeout": &hcldec.AttrSpec{Name: "client_cert_token_timeout", Type: cty.String, Required: false},
"client_jwt": &hcldec.AttrSpec{Name: "client_jwt", Type: cty.String, Required: false}, "client_jwt": &hcldec.AttrSpec{Name: "client_jwt", Type: cty.String, Required: false},
"object_id": &hcldec.AttrSpec{Name: "object_id", Type: cty.String, Required: false}, "object_id": &hcldec.AttrSpec{Name: "object_id", Type: cty.String, Required: false},
"tenant_id": &hcldec.AttrSpec{Name: "tenant_id", Type: cty.String, Required: false}, "tenant_id": &hcldec.AttrSpec{Name: "tenant_id", Type: cty.String, Required: false},
+43 -78
View File
@@ -6,7 +6,6 @@ import (
"fmt" "fmt"
"net/url" "net/url"
"strings" "strings"
"sync"
"time" "time"
"github.com/hashicorp/packer-plugin-sdk/multistep" "github.com/hashicorp/packer-plugin-sdk/multistep"
@@ -16,17 +15,16 @@ import (
) )
type StepDeployTemplate struct { type StepDeployTemplate struct {
client *AzureClient client *AzureClient
deploy func(ctx context.Context, resourceGroupName string, deploymentName string) error deploy func(ctx context.Context, resourceGroupName string, deploymentName string) error
delete func(ctx context.Context, deploymentName, resourceGroupName string) error delete func(ctx context.Context, deploymentName, resourceGroupName string) error
disk func(ctx context.Context, resourceGroupName string, computeName string) (string, string, error) disk func(ctx context.Context, resourceGroupName string, computeName string) (string, string, error)
deleteDisk func(ctx context.Context, imageType string, imageName string, resourceGroupName string) error deleteDisk func(ctx context.Context, imageType string, imageName string, resourceGroupName string) error
deleteDeployment func(ctx context.Context, state multistep.StateBag) error say func(message string)
say func(message string) error func(e error)
error func(e error) config *Config
config *Config factory templateFactoryFunc
factory templateFactoryFunc name string
name string
} }
func NewStepDeployTemplate(client *AzureClient, ui packersdk.Ui, config *Config, deploymentName string, factory templateFactoryFunc) *StepDeployTemplate { func NewStepDeployTemplate(client *AzureClient, ui packersdk.Ui, config *Config, deploymentName string, factory templateFactoryFunc) *StepDeployTemplate {
@@ -43,7 +41,6 @@ func NewStepDeployTemplate(client *AzureClient, ui packersdk.Ui, config *Config,
step.delete = step.deleteDeploymentResources step.delete = step.deleteDeploymentResources
step.disk = step.getImageDetails step.disk = step.getImageDetails
step.deleteDisk = step.deleteImage step.deleteDisk = step.deleteImage
step.deleteDeployment = step.deleteDeploymentObject
return step return step
} }
@@ -61,45 +58,32 @@ func (s *StepDeployTemplate) Run(ctx context.Context, state multistep.StateBag)
func (s *StepDeployTemplate) Cleanup(state multistep.StateBag) { func (s *StepDeployTemplate) Cleanup(state multistep.StateBag) {
defer func() { defer func() {
err := s.deleteDeployment(context.Background(), state) err := s.deleteTemplate(context.Background(), state)
if err != nil { if err != nil {
s.say(err.Error()) s.say(s.client.LastError.Error())
} }
}() }()
// Only clean up if this is an existing resource group that has been verified to exist.
// ArmIsResourceGroupCreated is set in step_create_resource_group to true, when Packer has verified that the resource group exists.
// ArmIsExistingResourceGroup is set to true when build_resource_group is set in the Packer configuration.
existingResourceGroup := state.Get(constants.ArmIsExistingResourceGroup).(bool)
resourceGroupCreated := state.Get(constants.ArmIsResourceGroupCreated).(bool)
if !existingResourceGroup || !resourceGroupCreated {
return
}
ui := state.Get("ui").(packersdk.Ui) ui := state.Get("ui").(packersdk.Ui)
ui.Say("\nDeleting individual resources ...") ui.Say("\nThe resource group was not created by Packer, deleting individual resources ...")
deploymentName := s.name deploymentName := s.name
resourceGroupName := state.Get(constants.ArmResourceGroupName).(string) resourceGroupName := state.Get(constants.ArmResourceGroupName).(string)
// Get image disk details before deleting the image; otherwise we won't be able to err := s.deleteDeploymentResources(context.TODO(), deploymentName, resourceGroupName)
// delete the disk as the image request will return a 404
computeName := state.Get(constants.ArmComputeName).(string)
imageType, imageName, err := s.disk(context.TODO(), resourceGroupName, computeName)
if err != nil && !strings.Contains(err.Error(), "ResourceNotFound") {
ui.Error(fmt.Sprintf("Could not retrieve OS Image details: %s", err))
}
err = s.delete(context.TODO(), deploymentName, resourceGroupName)
if err != nil { if err != nil {
s.reportIfError(err, resourceGroupName) s.reportIfError(err, resourceGroupName)
} }
// The disk was not found on the VM, this is an error. NewStepDeleteAdditionalDisks(s.client, ui).Run(context.TODO(), state)
if imageType == "" && imageName == "" {
ui.Error(fmt.Sprintf("Failed to find temporary OS disk on VM. Please delete manually.\n\n"+
"VM Name: %s\n"+
"Error: %s", computeName, err))
return
}
ui.Say(fmt.Sprintf(" Deleting -> %s : '%s'", imageType, imageName))
err = s.deleteDisk(context.TODO(), imageType, imageName, resourceGroupName)
if err != nil {
ui.Error(fmt.Sprintf("Error deleting resource. Please delete manually.\n\n"+
"Name: %s\n"+
"Error: %s", imageName, err))
}
} }
func (s *StepDeployTemplate) deployTemplate(ctx context.Context, resourceGroupName string, deploymentName string) error { func (s *StepDeployTemplate) deployTemplate(ctx context.Context, resourceGroupName string, deploymentName string) error {
@@ -122,7 +106,7 @@ func (s *StepDeployTemplate) deployTemplate(ctx context.Context, resourceGroupNa
return err return err
} }
func (s *StepDeployTemplate) deleteDeploymentObject(ctx context.Context, state multistep.StateBag) error { func (s *StepDeployTemplate) deleteTemplate(ctx context.Context, state multistep.StateBag) error {
deploymentName := s.name deploymentName := s.name
resourceGroupName := state.Get(constants.ArmResourceGroupName).(string) resourceGroupName := state.Get(constants.ArmResourceGroupName).(string)
ui := state.Get("ui").(packersdk.Ui) ui := state.Get("ui").(packersdk.Ui)
@@ -238,8 +222,6 @@ func (s *StepDeployTemplate) deleteDeploymentResources(ctx context.Context, depl
return err return err
} }
resources := map[string]string{}
for deploymentOperations.NotDone() { for deploymentOperations.NotDone() {
deploymentOperation := deploymentOperations.Value() deploymentOperation := deploymentOperations.Value()
// Sometimes an empty operation is added to the list by Azure // Sometimes an empty operation is added to the list by Azure
@@ -251,47 +233,30 @@ func (s *StepDeployTemplate) deleteDeploymentResources(ctx context.Context, depl
resourceName := *deploymentOperation.Properties.TargetResource.ResourceName resourceName := *deploymentOperation.Properties.TargetResource.ResourceName
resourceType := *deploymentOperation.Properties.TargetResource.ResourceType resourceType := *deploymentOperation.Properties.TargetResource.ResourceType
s.say(fmt.Sprintf("Adding to deletion queue -> %s : '%s'", resourceType, resourceName)) s.say(fmt.Sprintf(" -> %s : '%s'", resourceType, resourceName))
resources[resourceType] = resourceName
err = retry.Config{
Tries: 10,
RetryDelay: (&retry.Backoff{InitialBackoff: 10 * time.Second, MaxBackoff: 600 * time.Second, Multiplier: 2}).Linear,
}.Run(ctx, func(ctx context.Context) error {
err := deleteResource(ctx, s.client,
resourceType,
resourceName,
resourceGroupName)
if err != nil {
s.reportIfError(err, resourceName)
}
return nil
})
if err != nil {
s.reportIfError(err, resourceName)
}
if err = deploymentOperations.Next(); err != nil { if err = deploymentOperations.Next(); err != nil {
return err return err
} }
} }
var wg sync.WaitGroup
wg.Add(len(resources))
for resourceType, resourceName := range resources {
go func(resourceType, resourceName string) {
defer wg.Done()
retryConfig := retry.Config{
Tries: 10,
RetryDelay: (&retry.Backoff{InitialBackoff: 10 * time.Second, MaxBackoff: 600 * time.Second, Multiplier: 2}).Linear,
}
err = retryConfig.Run(ctx, func(ctx context.Context) error {
s.say(fmt.Sprintf("Attempting deletion -> %s : '%s'", resourceType, resourceName))
err := deleteResource(ctx, s.client,
resourceType,
resourceName,
resourceGroupName)
if err != nil {
s.say(fmt.Sprintf("Error deleting resource. Will retry.\n"+
"Name: %s\n"+
"Error: %s\n", resourceName, err.Error()))
}
return err
})
if err != nil {
s.reportIfError(err, resourceName)
}
}(resourceType, resourceName)
}
s.say("Waiting for deletion of all resources...")
wg.Wait()
return nil return nil
} }
@@ -6,7 +6,6 @@ import (
"testing" "testing"
"github.com/hashicorp/packer-plugin-sdk/multistep" "github.com/hashicorp/packer-plugin-sdk/multistep"
packersdk "github.com/hashicorp/packer-plugin-sdk/packer"
"github.com/hashicorp/packer/builder/azure/common/constants" "github.com/hashicorp/packer/builder/azure/common/constants"
) )
@@ -109,97 +108,11 @@ func TestStepDeployTemplateDeleteImageShouldFailWithInvalidImage(t *testing.T) {
} }
} }
func TestStepDeployTemplateCleanupShouldDeleteManagedOSImageInExistingResourceGroup(t *testing.T) {
var deleteDiskCounter = 0
var testSubject = createTestStepDeployTemplateDeleteOSImage(&deleteDiskCounter)
stateBag := createTestStateBagStepDeployTemplate()
stateBag.Put(constants.ArmIsManagedImage, true)
stateBag.Put(constants.ArmIsExistingResourceGroup, true)
stateBag.Put(constants.ArmIsResourceGroupCreated, true)
stateBag.Put("ui", packersdk.TestUi(t))
testSubject.Cleanup(stateBag)
if deleteDiskCounter != 1 {
t.Fatalf("Expected DeployTemplate Cleanup to invoke deleteDisk 1 time, but invoked %d times", deleteDiskCounter)
}
}
func TestStepDeployTemplateCleanupShouldDeleteManagedOSImageInTemporaryResourceGroup(t *testing.T) {
var deleteDiskCounter = 0
var testSubject = createTestStepDeployTemplateDeleteOSImage(&deleteDiskCounter)
stateBag := createTestStateBagStepDeployTemplate()
stateBag.Put(constants.ArmIsManagedImage, true)
stateBag.Put(constants.ArmIsExistingResourceGroup, false)
stateBag.Put(constants.ArmIsResourceGroupCreated, true)
stateBag.Put("ui", packersdk.TestUi(t))
testSubject.Cleanup(stateBag)
if deleteDiskCounter != 1 {
t.Fatalf("Expected DeployTemplate Cleanup to invoke deleteDisk 1 times, but invoked %d times", deleteDiskCounter)
}
}
func TestStepDeployTemplateCleanupShouldDeleteVHDOSImageInExistingResourceGroup(t *testing.T) {
var deleteDiskCounter = 0
var testSubject = createTestStepDeployTemplateDeleteOSImage(&deleteDiskCounter)
stateBag := createTestStateBagStepDeployTemplate()
stateBag.Put(constants.ArmIsManagedImage, false)
stateBag.Put(constants.ArmIsExistingResourceGroup, true)
stateBag.Put(constants.ArmIsResourceGroupCreated, true)
stateBag.Put("ui", packersdk.TestUi(t))
testSubject.Cleanup(stateBag)
if deleteDiskCounter != 1 {
t.Fatalf("Expected DeployTemplate Cleanup to invoke deleteDisk 1 time, but invoked %d times", deleteDiskCounter)
}
}
func TestStepDeployTemplateCleanupShouldVHDOSImageInTemporaryResourceGroup(t *testing.T) {
var deleteDiskCounter = 0
var testSubject = createTestStepDeployTemplateDeleteOSImage(&deleteDiskCounter)
stateBag := createTestStateBagStepDeployTemplate()
stateBag.Put(constants.ArmIsManagedImage, false)
stateBag.Put(constants.ArmIsExistingResourceGroup, false)
stateBag.Put(constants.ArmIsResourceGroupCreated, true)
stateBag.Put("ui", packersdk.TestUi(t))
testSubject.Cleanup(stateBag)
if deleteDiskCounter != 1 {
t.Fatalf("Expected DeployTemplate Cleanup to invoke deleteDisk 1 times, but invoked %d times", deleteDiskCounter)
}
}
func createTestStateBagStepDeployTemplate() multistep.StateBag { func createTestStateBagStepDeployTemplate() multistep.StateBag {
stateBag := new(multistep.BasicStateBag) stateBag := new(multistep.BasicStateBag)
stateBag.Put(constants.ArmDeploymentName, "Unit Test: DeploymentName") stateBag.Put(constants.ArmDeploymentName, "Unit Test: DeploymentName")
stateBag.Put(constants.ArmResourceGroupName, "Unit Test: ResourceGroupName") stateBag.Put(constants.ArmResourceGroupName, "Unit Test: ResourceGroupName")
stateBag.Put(constants.ArmComputeName, "Unit Test: ComputeName")
return stateBag return stateBag
} }
func createTestStepDeployTemplateDeleteOSImage(deleteDiskCounter *int) *StepDeployTemplate {
return &StepDeployTemplate{
deploy: func(context.Context, string, string) error { return nil },
say: func(message string) {},
error: func(e error) {},
deleteDisk: func(ctx context.Context, imageType string, imageName string, resourceGroupName string) error {
*deleteDiskCounter++
return nil
},
disk: func(ctx context.Context, resourceGroupName, computeName string) (string, string, error) {
return "Microsoft.Compute/disks", "", nil
},
delete: func(ctx context.Context, deploymentName, resourceGroupName string) error {
return nil
},
deleteDeployment: func(ctx context.Context, state multistep.StateBag) error {
return nil
},
}
}
-2
View File
@@ -22,7 +22,6 @@ type FlatConfig struct {
ClientID *string `mapstructure:"client_id" cty:"client_id" hcl:"client_id"` ClientID *string `mapstructure:"client_id" cty:"client_id" hcl:"client_id"`
ClientSecret *string `mapstructure:"client_secret" cty:"client_secret" hcl:"client_secret"` ClientSecret *string `mapstructure:"client_secret" cty:"client_secret" hcl:"client_secret"`
ClientCertPath *string `mapstructure:"client_cert_path" cty:"client_cert_path" hcl:"client_cert_path"` ClientCertPath *string `mapstructure:"client_cert_path" cty:"client_cert_path" hcl:"client_cert_path"`
ClientCertExpireTimeout *string `mapstructure:"client_cert_token_timeout" required:"false" cty:"client_cert_token_timeout" hcl:"client_cert_token_timeout"`
ClientJWT *string `mapstructure:"client_jwt" cty:"client_jwt" hcl:"client_jwt"` ClientJWT *string `mapstructure:"client_jwt" cty:"client_jwt" hcl:"client_jwt"`
ObjectID *string `mapstructure:"object_id" cty:"object_id" hcl:"object_id"` ObjectID *string `mapstructure:"object_id" cty:"object_id" hcl:"object_id"`
TenantID *string `mapstructure:"tenant_id" required:"false" cty:"tenant_id" hcl:"tenant_id"` TenantID *string `mapstructure:"tenant_id" required:"false" cty:"tenant_id" hcl:"tenant_id"`
@@ -77,7 +76,6 @@ func (*FlatConfig) HCL2Spec() map[string]hcldec.Spec {
"client_id": &hcldec.AttrSpec{Name: "client_id", Type: cty.String, Required: false}, "client_id": &hcldec.AttrSpec{Name: "client_id", Type: cty.String, Required: false},
"client_secret": &hcldec.AttrSpec{Name: "client_secret", Type: cty.String, Required: false}, "client_secret": &hcldec.AttrSpec{Name: "client_secret", Type: cty.String, Required: false},
"client_cert_path": &hcldec.AttrSpec{Name: "client_cert_path", Type: cty.String, Required: false}, "client_cert_path": &hcldec.AttrSpec{Name: "client_cert_path", Type: cty.String, Required: false},
"client_cert_token_timeout": &hcldec.AttrSpec{Name: "client_cert_token_timeout", Type: cty.String, Required: false},
"client_jwt": &hcldec.AttrSpec{Name: "client_jwt", Type: cty.String, Required: false}, "client_jwt": &hcldec.AttrSpec{Name: "client_jwt", Type: cty.String, Required: false},
"object_id": &hcldec.AttrSpec{Name: "object_id", Type: cty.String, Required: false}, "object_id": &hcldec.AttrSpec{Name: "object_id", Type: cty.String, Required: false},
"tenant_id": &hcldec.AttrSpec{Name: "tenant_id", Type: cty.String, Required: false}, "tenant_id": &hcldec.AttrSpec{Name: "tenant_id", Type: cty.String, Required: false},
+1 -10
View File
@@ -39,8 +39,6 @@ type Config struct {
// The path to a pem-encoded certificate that will be used to authenticate // The path to a pem-encoded certificate that will be used to authenticate
// as the specified AAD SP. // as the specified AAD SP.
ClientCertPath string `mapstructure:"client_cert_path"` ClientCertPath string `mapstructure:"client_cert_path"`
// The timeout for the JWT Token when using a [client certificate](#client_cert_path). Defaults to 1 hour.
ClientCertExpireTimeout time.Duration `mapstructure:"client_cert_token_timeout" required:"false"`
// A JWT bearer token for client auth (RFC 7523, Sec. 2.2) that will be used // A JWT bearer token for client auth (RFC 7523, Sec. 2.2) that will be used
// to authenticate the AAD SP. Provides more control over token the expiration // to authenticate the AAD SP. Provides more control over token the expiration
// when using certificate authentication than when using `client_cert_path`. // when using certificate authentication than when using `client_cert_path`.
@@ -165,9 +163,6 @@ func (c Config) Validate(errs *packersdk.MultiError) {
if _, err := os.Stat(c.ClientCertPath); err != nil { if _, err := os.Stat(c.ClientCertPath); err != nil {
errs = packersdk.MultiErrorAppend(errs, fmt.Errorf("client_cert_path is not an accessible file: %v", err)) errs = packersdk.MultiErrorAppend(errs, fmt.Errorf("client_cert_path is not an accessible file: %v", err))
} }
if c.ClientCertExpireTimeout != 0 && c.ClientCertExpireTimeout < 5*time.Minute {
errs = packersdk.MultiErrorAppend(errs, fmt.Errorf("client_cert_token_timeout will expire within 5 minutes, please set a value greater than 5 minutes"))
}
return return
} }
@@ -264,7 +259,7 @@ func (c Config) GetServicePrincipalToken(
auth = NewSecretOAuthTokenProvider(*c.cloudEnvironment, c.ClientID, c.ClientSecret, c.TenantID) auth = NewSecretOAuthTokenProvider(*c.cloudEnvironment, c.ClientID, c.ClientSecret, c.TenantID)
case authTypeClientCert: case authTypeClientCert:
say("Getting tokens using client certificate") say("Getting tokens using client certificate")
auth, err = NewCertOAuthTokenProvider(*c.cloudEnvironment, c.ClientID, c.ClientCertPath, c.TenantID, c.ClientCertExpireTimeout) auth, err = NewCertOAuthTokenProvider(*c.cloudEnvironment, c.ClientID, c.ClientCertPath, c.TenantID)
if err != nil { if err != nil {
return nil, err return nil, err
} }
@@ -341,10 +336,6 @@ func (c *Config) FillParameters() error {
c.TenantID = tenantID c.TenantID = tenantID
} }
if c.ClientCertExpireTimeout == 0 {
c.ClientCertExpireTimeout = time.Hour
}
return nil return nil
} }
@@ -95,16 +95,6 @@ func Test_ClientConfig_RequiredParametersSet(t *testing.T) {
}, },
wantErr: true, wantErr: true,
}, },
{
name: "client_cert_token_timeout should be 5 minutes or more",
config: Config{
SubscriptionID: "ok",
ClientID: "ok",
ClientCertPath: "/dev/null",
ClientCertExpireTimeout: 1 * time.Minute,
},
wantErr: true,
},
{ {
name: "too many client_* values", name: "too many client_* values",
config: Config{ config: Config{
@@ -18,14 +18,14 @@ import (
"github.com/hashicorp/packer/builder/azure/pkcs12" "github.com/hashicorp/packer/builder/azure/pkcs12"
) )
func NewCertOAuthTokenProvider(env azure.Environment, clientID, clientCertPath, tenantID string, certExpireTimeout time.Duration) (oAuthTokenProvider, error) { func NewCertOAuthTokenProvider(env azure.Environment, clientID, clientCertPath, tenantID string) (oAuthTokenProvider, error) {
cert, key, err := readCert(clientCertPath) cert, key, err := readCert(clientCertPath)
if err != nil { if err != nil {
return nil, fmt.Errorf("Error reading certificate: %v", err) return nil, fmt.Errorf("Error reading certificate: %v", err)
} }
audience := fmt.Sprintf("%s%s/oauth2/token", env.ActiveDirectoryEndpoint, tenantID) audience := fmt.Sprintf("%s%s/oauth2/token", env.ActiveDirectoryEndpoint, tenantID)
jwt, err := makeJWT(clientID, audience, cert, key, certExpireTimeout, true) jwt, err := makeJWT(clientID, audience, cert, key, time.Hour, true)
if err != nil { if err != nil {
return nil, fmt.Errorf("Error generating JWT: %v", err) return nil, fmt.Errorf("Error generating JWT: %v", err)
} }
-2
View File
@@ -49,7 +49,6 @@ type FlatConfig struct {
ClientID *string `mapstructure:"client_id" cty:"client_id" hcl:"client_id"` ClientID *string `mapstructure:"client_id" cty:"client_id" hcl:"client_id"`
ClientSecret *string `mapstructure:"client_secret" cty:"client_secret" hcl:"client_secret"` ClientSecret *string `mapstructure:"client_secret" cty:"client_secret" hcl:"client_secret"`
ClientCertPath *string `mapstructure:"client_cert_path" cty:"client_cert_path" hcl:"client_cert_path"` ClientCertPath *string `mapstructure:"client_cert_path" cty:"client_cert_path" hcl:"client_cert_path"`
ClientCertExpireTimeout *string `mapstructure:"client_cert_token_timeout" required:"false" cty:"client_cert_token_timeout" hcl:"client_cert_token_timeout"`
ClientJWT *string `mapstructure:"client_jwt" cty:"client_jwt" hcl:"client_jwt"` ClientJWT *string `mapstructure:"client_jwt" cty:"client_jwt" hcl:"client_jwt"`
ObjectID *string `mapstructure:"object_id" cty:"object_id" hcl:"object_id"` ObjectID *string `mapstructure:"object_id" cty:"object_id" hcl:"object_id"`
TenantID *string `mapstructure:"tenant_id" required:"false" cty:"tenant_id" hcl:"tenant_id"` TenantID *string `mapstructure:"tenant_id" required:"false" cty:"tenant_id" hcl:"tenant_id"`
@@ -164,7 +163,6 @@ func (*FlatConfig) HCL2Spec() map[string]hcldec.Spec {
"client_id": &hcldec.AttrSpec{Name: "client_id", Type: cty.String, Required: false}, "client_id": &hcldec.AttrSpec{Name: "client_id", Type: cty.String, Required: false},
"client_secret": &hcldec.AttrSpec{Name: "client_secret", Type: cty.String, Required: false}, "client_secret": &hcldec.AttrSpec{Name: "client_secret", Type: cty.String, Required: false},
"client_cert_path": &hcldec.AttrSpec{Name: "client_cert_path", Type: cty.String, Required: false}, "client_cert_path": &hcldec.AttrSpec{Name: "client_cert_path", Type: cty.String, Required: false},
"client_cert_token_timeout": &hcldec.AttrSpec{Name: "client_cert_token_timeout", Type: cty.String, Required: false},
"client_jwt": &hcldec.AttrSpec{Name: "client_jwt", Type: cty.String, Required: false}, "client_jwt": &hcldec.AttrSpec{Name: "client_jwt", Type: cty.String, Required: false},
"object_id": &hcldec.AttrSpec{Name: "object_id", Type: cty.String, Required: false}, "object_id": &hcldec.AttrSpec{Name: "object_id", Type: cty.String, Required: false},
"tenant_id": &hcldec.AttrSpec{Name: "tenant_id", Type: cty.String, Required: false}, "tenant_id": &hcldec.AttrSpec{Name: "tenant_id", Type: cty.String, Required: false},
+6 -1
View File
@@ -60,7 +60,12 @@ func (b *Builder) Run(ctx context.Context, ui packersdk.Ui, hook packersdk.Hook)
// Build the steps. // Build the steps.
steps := []multistep.Step{ steps := []multistep.Step{
&stepPrepareConfig{}, &stepPrepareConfig{},
commonsteps.HTTPServerFromHTTPConfig(&b.config.HTTPConfig), &commonsteps.StepHTTPServer{
HTTPDir: b.config.HTTPDir,
HTTPPortMin: b.config.HTTPPortMin,
HTTPPortMax: b.config.HTTPPortMax,
HTTPAddress: b.config.HTTPAddress,
},
&stepKeypair{ &stepKeypair{
Debug: b.config.PackerDebug, Debug: b.config.PackerDebug,
Comm: &b.config.Comm, Comm: &b.config.Comm,
-2
View File
@@ -19,7 +19,6 @@ type FlatConfig struct {
PackerUserVars map[string]string `mapstructure:"packer_user_variables" cty:"packer_user_variables" hcl:"packer_user_variables"` PackerUserVars map[string]string `mapstructure:"packer_user_variables" cty:"packer_user_variables" hcl:"packer_user_variables"`
PackerSensitiveVars []string `mapstructure:"packer_sensitive_variables" cty:"packer_sensitive_variables" hcl:"packer_sensitive_variables"` PackerSensitiveVars []string `mapstructure:"packer_sensitive_variables" cty:"packer_sensitive_variables" hcl:"packer_sensitive_variables"`
HTTPDir *string `mapstructure:"http_directory" cty:"http_directory" hcl:"http_directory"` HTTPDir *string `mapstructure:"http_directory" cty:"http_directory" hcl:"http_directory"`
HTTPContent map[string]string `mapstructure:"http_content" cty:"http_content" hcl:"http_content"`
HTTPPortMin *int `mapstructure:"http_port_min" cty:"http_port_min" hcl:"http_port_min"` HTTPPortMin *int `mapstructure:"http_port_min" cty:"http_port_min" hcl:"http_port_min"`
HTTPPortMax *int `mapstructure:"http_port_max" cty:"http_port_max" hcl:"http_port_max"` HTTPPortMax *int `mapstructure:"http_port_max" cty:"http_port_max" hcl:"http_port_max"`
HTTPAddress *string `mapstructure:"http_bind_address" cty:"http_bind_address" hcl:"http_bind_address"` HTTPAddress *string `mapstructure:"http_bind_address" cty:"http_bind_address" hcl:"http_bind_address"`
@@ -136,7 +135,6 @@ func (*FlatConfig) HCL2Spec() map[string]hcldec.Spec {
"packer_user_variables": &hcldec.AttrSpec{Name: "packer_user_variables", Type: cty.Map(cty.String), Required: false}, "packer_user_variables": &hcldec.AttrSpec{Name: "packer_user_variables", Type: cty.Map(cty.String), Required: false},
"packer_sensitive_variables": &hcldec.AttrSpec{Name: "packer_sensitive_variables", Type: cty.List(cty.String), Required: false}, "packer_sensitive_variables": &hcldec.AttrSpec{Name: "packer_sensitive_variables", Type: cty.List(cty.String), Required: false},
"http_directory": &hcldec.AttrSpec{Name: "http_directory", Type: cty.String, Required: false}, "http_directory": &hcldec.AttrSpec{Name: "http_directory", Type: cty.String, Required: false},
"http_content": &hcldec.AttrSpec{Name: "http_content", Type: cty.Map(cty.String), Required: false},
"http_port_min": &hcldec.AttrSpec{Name: "http_port_min", Type: cty.Number, Required: false}, "http_port_min": &hcldec.AttrSpec{Name: "http_port_min", Type: cty.Number, Required: false},
"http_port_max": &hcldec.AttrSpec{Name: "http_port_max", Type: cty.Number, Required: false}, "http_port_max": &hcldec.AttrSpec{Name: "http_port_max", Type: cty.Number, Required: false},
"http_bind_address": &hcldec.AttrSpec{Name: "http_bind_address", Type: cty.String, Required: false}, "http_bind_address": &hcldec.AttrSpec{Name: "http_bind_address", Type: cty.String, Required: false},
+11
View File
@@ -0,0 +1,11 @@
package docker
import (
"testing"
packersdk "github.com/hashicorp/packer-plugin-sdk/packer"
)
func TestExportArtifact_impl(t *testing.T) {
var _ packersdk.Artifact = new(ExportArtifact)
}
+58
View File
@@ -0,0 +1,58 @@
package docker
import (
"errors"
"testing"
packersdk "github.com/hashicorp/packer-plugin-sdk/packer"
)
func TestImportArtifact_impl(t *testing.T) {
var _ packersdk.Artifact = new(ImportArtifact)
}
func TestImportArtifactBuilderId(t *testing.T) {
a := &ImportArtifact{BuilderIdValue: "foo"}
if a.BuilderId() != "foo" {
t.Fatalf("bad: %#v", a.BuilderId())
}
}
func TestImportArtifactFiles(t *testing.T) {
a := &ImportArtifact{}
if a.Files() != nil {
t.Fatalf("bad: %#v", a.Files())
}
}
func TestImportArtifactId(t *testing.T) {
a := &ImportArtifact{IdValue: "foo"}
if a.Id() != "foo" {
t.Fatalf("bad: %#v", a.Id())
}
}
func TestImportArtifactDestroy(t *testing.T) {
d := new(MockDriver)
a := &ImportArtifact{
Driver: d,
IdValue: "foo",
}
// No error
if err := a.Destroy(); err != nil {
t.Fatalf("err: %s", err)
}
if !d.DeleteImageCalled {
t.Fatal("delete image should be called")
}
if d.DeleteImageId != "foo" {
t.Fatalf("bad: %#v", d.DeleteImageId)
}
// With an error
d.DeleteImageErr = errors.New("foo")
if err := a.Destroy(); err != d.DeleteImageErr {
t.Fatalf("err: %#v", err)
}
}
+11
View File
@@ -0,0 +1,11 @@
package docker
import (
"testing"
packersdk "github.com/hashicorp/packer-plugin-sdk/packer"
)
func TestBuilder_implBuilder(t *testing.T) {
var _ packersdk.Builder = new(Builder)
}
+239
View File
@@ -0,0 +1,239 @@
package docker
import (
"crypto/sha256"
"encoding/json"
"fmt"
"io/ioutil"
"os"
"os/exec"
"testing"
builderT "github.com/hashicorp/packer-plugin-sdk/acctest"
packersdk "github.com/hashicorp/packer-plugin-sdk/packer"
)
// RenderConfig helps create dynamic packer template configs for parsing by
// builderT without having to write the config to a file.
func RenderConfig(builderConfig map[string]interface{}, provisionerConfig []map[string]interface{}) string {
// set up basic build template
t := map[string][]map[string]interface{}{
"builders": {
// Setup basic docker config
map[string]interface{}{
"type": "test",
"image": "ubuntu",
"discard": true,
},
},
"provisioners": []map[string]interface{}{},
}
// apply special builder overrides
for k, v := range builderConfig {
t["builders"][0][k] = v
}
// Apply special provisioner overrides
t["provisioners"] = append(t["provisioners"], provisionerConfig...)
j, _ := json.Marshal(t)
return string(j)
}
// TestUploadDownload verifies that basic upload / download functionality works
func TestUploadDownload(t *testing.T) {
if os.Getenv("PACKER_ACC") == "" {
t.Skip("This test is only run with PACKER_ACC=1")
}
dockerBuilderExtraConfig := map[string]interface{}{
"run_command": []string{"-d", "-i", "-t", "{{.Image}}", "/bin/sh"},
}
dockerProvisionerConfig := []map[string]interface{}{
{
"type": "file",
"source": "test-fixtures/onecakes/strawberry",
"destination": "/strawberry-cake",
},
{
"type": "file",
"source": "/strawberry-cake",
"destination": "my-strawberry-cake",
"direction": "download",
},
}
configString := RenderConfig(dockerBuilderExtraConfig, dockerProvisionerConfig)
// this should be a precheck
cmd := exec.Command("docker", "-v")
err := cmd.Run()
if err != nil {
t.Error("docker command not found; please make sure docker is installed")
}
builderT.Test(t, builderT.TestCase{
Builder: &Builder{},
Template: configString,
Check: func(a []packersdk.Artifact) error {
// Verify that the thing we downloaded is the same thing we sent up.
// Complain loudly if it isn't.
inputFile, err := ioutil.ReadFile("test-fixtures/onecakes/strawberry")
if err != nil {
return fmt.Errorf("Unable to read input file: %s", err)
}
outputFile, err := ioutil.ReadFile("my-strawberry-cake")
if err != nil {
return fmt.Errorf("Unable to read output file: %s", err)
}
if sha256.Sum256(inputFile) != sha256.Sum256(outputFile) {
return fmt.Errorf("Input and output files do not match\n"+
"Input:\n%s\nOutput:\n%s\n", inputFile, outputFile)
}
return nil
},
Teardown: func() error {
// Cleanup. Honestly I don't know why you would want to get rid
// of my strawberry cake. It's so tasty! Do you not like cake? Are you a
// cake-hater? Or are you keeping all the cake all for yourself? So selfish!
os.Remove("my-strawberry-cake")
return nil
},
})
}
// TestLargeDownload verifies that files are the appropriate size after being
// downloaded. This is to identify and fix the race condition in #2793. You may
// need to use github.com/cbednarski/rerun to verify since this problem occurs
// only intermittently.
func TestLargeDownload(t *testing.T) {
if os.Getenv("PACKER_ACC") == "" {
t.Skip("This test is only run with PACKER_ACC=1")
}
dockerProvisionerConfig := []map[string]interface{}{
{
"type": "shell",
"inline": []string{
"dd if=/dev/urandom of=/tmp/cupcake bs=1M count=2",
"dd if=/dev/urandom of=/tmp/bigcake bs=1M count=100",
"sync",
"md5sum /tmp/cupcake /tmp/bigcake",
},
},
{
"type": "file",
"source": "/tmp/cupcake",
"destination": "cupcake",
"direction": "download",
},
{
"type": "file",
"source": "/tmp/bigcake",
"destination": "bigcake",
"direction": "download",
},
}
configString := RenderConfig(map[string]interface{}{}, dockerProvisionerConfig)
// this should be a precheck
cmd := exec.Command("docker", "-v")
err := cmd.Run()
if err != nil {
t.Error("docker command not found; please make sure docker is installed")
}
builderT.Test(t, builderT.TestCase{
Builder: &Builder{},
Template: configString,
Check: func(a []packersdk.Artifact) error {
// Verify that the things we downloaded are the right size. Complain loudly
// if they are not.
//
// cupcake should be 2097152 bytes
// bigcake should be 104857600 bytes
cupcake, err := os.Stat("cupcake")
if err != nil {
t.Fatalf("Unable to stat cupcake file: %s", err)
}
cupcakeExpected := int64(2097152)
if cupcake.Size() != cupcakeExpected {
t.Errorf("Expected cupcake to be %d bytes; found %d", cupcakeExpected, cupcake.Size())
}
bigcake, err := os.Stat("bigcake")
if err != nil {
t.Fatalf("Unable to stat bigcake file: %s", err)
}
bigcakeExpected := int64(104857600)
if bigcake.Size() != bigcakeExpected {
t.Errorf("Expected bigcake to be %d bytes; found %d", bigcakeExpected, bigcake.Size())
}
// TODO if we can, calculate a sha inside the container and compare to the
// one we get after we pull it down. We will probably have to parse the log
// or ui output to do this because we use /dev/urandom to create the file.
// if sha256.Sum256(inputFile) != sha256.Sum256(outputFile) {
// t.Fatalf("Input and output files do not match\n"+
// "Input:\n%s\nOutput:\n%s\n", inputFile, outputFile)
// }
return nil
},
Teardown: func() error {
os.Remove("cupcake")
os.Remove("bigcake")
return nil
},
})
}
// TestFixUploadOwner verifies that owner of uploaded files is the user the container is running as.
func TestFixUploadOwner(t *testing.T) {
if os.Getenv("PACKER_ACC") == "" {
t.Skip("This test is only run with PACKER_ACC=1")
}
cmd := exec.Command("docker", "-v")
err := cmd.Run()
if err != nil {
t.Error("docker command not found; please make sure docker is installed")
}
dockerBuilderExtraConfig := map[string]interface{}{
"run_command": []string{"-d", "-i", "-t", "-u", "42", "{{.Image}}", "/bin/sh"},
}
testFixUploadOwnerProvisionersTemplate := []map[string]interface{}{
{
"type": "file",
"source": "test-fixtures/onecakes/strawberry",
"destination": "/tmp/strawberry-cake",
},
{
"type": "file",
"source": "test-fixtures/manycakes",
"destination": "/tmp/",
},
{
"type": "shell",
"inline": "touch /tmp/testUploadOwner",
},
{
"type": "shell",
"inline": []string{
"[ $(stat -c %u /tmp/strawberry-cake) -eq 42 ] || (echo 'Invalid owner of /tmp/strawberry-cake' && exit 1)",
"[ $(stat -c %u /tmp/testUploadOwner) -eq 42 ] || (echo 'Invalid owner of /tmp/testUploadOwner' && exit 1)",
"find /tmp/manycakes | xargs -n1 -IFILE /bin/sh -c '[ $(stat -c %u FILE) -eq 42 ] || (echo \"Invalid owner of FILE\" && exit 1)'",
},
},
}
configString := RenderConfig(dockerBuilderExtraConfig, testFixUploadOwnerProvisionersTemplate)
builderT.Test(t, builderT.TestCase{
Builder: &Builder{},
Template: configString,
})
}
@@ -114,8 +114,7 @@ type Config struct {
// for the duration of the pull. If true login_server is required and // for the duration of the pull. If true login_server is required and
// login, login_username, and login_password will be ignored. For more // login, login_username, and login_password will be ignored. For more
// information see the section on ECR. // information see the section on ECR.
EcrLogin bool `mapstructure:"ecr_login" required:"false"` EcrLogin bool `mapstructure:"ecr_login" required:"false"`
AwsAccessConfig `mapstructure:",squash"`
ctx interpolate.Context ctx interpolate.Context
} }
@@ -92,10 +92,6 @@ type FlatConfig struct {
LoginServer *string `mapstructure:"login_server" required:"false" cty:"login_server" hcl:"login_server"` LoginServer *string `mapstructure:"login_server" required:"false" cty:"login_server" hcl:"login_server"`
LoginUsername *string `mapstructure:"login_username" required:"false" cty:"login_username" hcl:"login_username"` LoginUsername *string `mapstructure:"login_username" required:"false" cty:"login_username" hcl:"login_username"`
EcrLogin *bool `mapstructure:"ecr_login" required:"false" cty:"ecr_login" hcl:"ecr_login"` EcrLogin *bool `mapstructure:"ecr_login" required:"false" cty:"ecr_login" hcl:"ecr_login"`
AccessKey *string `mapstructure:"aws_access_key" required:"false" cty:"aws_access_key" hcl:"aws_access_key"`
SecretKey *string `mapstructure:"aws_secret_key" required:"false" cty:"aws_secret_key" hcl:"aws_secret_key"`
Token *string `mapstructure:"aws_token" required:"false" cty:"aws_token" hcl:"aws_token"`
Profile *string `mapstructure:"aws_profile" required:"false" cty:"aws_profile" hcl:"aws_profile"`
} }
// FlatMapstructure returns a new FlatConfig. // FlatMapstructure returns a new FlatConfig.
@@ -192,10 +188,6 @@ func (*FlatConfig) HCL2Spec() map[string]hcldec.Spec {
"login_server": &hcldec.AttrSpec{Name: "login_server", Type: cty.String, Required: false}, "login_server": &hcldec.AttrSpec{Name: "login_server", Type: cty.String, Required: false},
"login_username": &hcldec.AttrSpec{Name: "login_username", Type: cty.String, Required: false}, "login_username": &hcldec.AttrSpec{Name: "login_username", Type: cty.String, Required: false},
"ecr_login": &hcldec.AttrSpec{Name: "ecr_login", Type: cty.Bool, Required: false}, "ecr_login": &hcldec.AttrSpec{Name: "ecr_login", Type: cty.Bool, Required: false},
"aws_access_key": &hcldec.AttrSpec{Name: "aws_access_key", Type: cty.String, Required: false},
"aws_secret_key": &hcldec.AttrSpec{Name: "aws_secret_key", Type: cty.String, Required: false},
"aws_token": &hcldec.AttrSpec{Name: "aws_token", Type: cty.String, Required: false},
"aws_profile": &hcldec.AttrSpec{Name: "aws_profile", Type: cty.String, Required: false},
} }
return s return s
} }
+147
View File
@@ -0,0 +1,147 @@
package docker
import (
"io/ioutil"
"os"
"testing"
)
func testConfig() map[string]interface{} {
return map[string]interface{}{
"export_path": "foo",
"image": "bar",
}
}
func testConfigStruct(t *testing.T) *Config {
var c Config
warns, errs := c.Prepare(testConfig())
if len(warns) > 0 {
t.Fatalf("bad: %#v", len(warns))
}
if errs != nil {
t.Fatalf("bad: %#v", errs)
}
return &c
}
func testConfigErr(t *testing.T, warns []string, err error) {
if len(warns) > 0 {
t.Fatalf("bad: %#v", warns)
}
if err == nil {
t.Fatal("should error")
}
}
func testConfigOk(t *testing.T, warns []string, err error) {
if len(warns) > 0 {
t.Fatalf("bad: %#v", warns)
}
if err != nil {
t.Fatalf("bad: %s", err)
}
}
func TestConfigPrepare_exportPath(t *testing.T) {
td, err := ioutil.TempDir("", "packer")
if err != nil {
t.Fatalf("err: %s", err)
}
defer os.RemoveAll(td)
raw := testConfig()
// No export path. This is invalid. Previously this would not error during
// validation and as a result the failure would happen at build time.
delete(raw, "export_path")
var c Config
warns, errs := c.Prepare(raw)
testConfigErr(t, warns, errs)
// Good export path
raw["export_path"] = "good"
warns, errs = c.Prepare(raw)
testConfigOk(t, warns, errs)
// Bad export path (directory)
raw["export_path"] = td
warns, errs = c.Prepare(raw)
testConfigErr(t, warns, errs)
}
func TestConfigPrepare_exportPathAndCommit(t *testing.T) {
raw := testConfig()
// Export but no commit (explicit default)
raw["commit"] = false
warns, errs := (&Config{}).Prepare(raw)
testConfigOk(t, warns, errs)
// Commit AND export specified (invalid)
raw["commit"] = true
warns, errs = (&Config{}).Prepare(raw)
testConfigErr(t, warns, errs)
// Commit but no export
delete(raw, "export_path")
warns, errs = (&Config{}).Prepare(raw)
testConfigOk(t, warns, errs)
}
func TestConfigPrepare_exportDiscard(t *testing.T) {
raw := testConfig()
// Export but no discard (explicit default)
raw["discard"] = false
warns, errs := (&Config{}).Prepare(raw)
testConfigOk(t, warns, errs)
// Discard AND export (invalid)
raw["discard"] = true
warns, errs = (&Config{}).Prepare(raw)
testConfigErr(t, warns, errs)
// Discard but no export
raw["discard"] = true
delete(raw, "export_path")
warns, errs = (&Config{}).Prepare(raw)
testConfigOk(t, warns, errs)
}
func TestConfigPrepare_image(t *testing.T) {
raw := testConfig()
// No image
delete(raw, "image")
var c Config
warns, errs := c.Prepare(raw)
testConfigErr(t, warns, errs)
// Good image
raw["image"] = "path"
warns, errs = c.Prepare(raw)
testConfigOk(t, warns, errs)
}
func TestConfigPrepare_pull(t *testing.T) {
raw := testConfig()
// No pull set
delete(raw, "pull")
var c Config
warns, errs := c.Prepare(raw)
testConfigOk(t, warns, errs)
if !c.Pull {
t.Fatal("should pull by default")
}
// Pull set
raw["pull"] = false
warns, errs = c.Prepare(raw)
testConfigOk(t, warns, errs)
if c.Pull {
t.Fatal("should not pull")
}
}
+7
View File
@@ -0,0 +1,7 @@
package docker
import "testing"
func TestDockerDriver_impl(t *testing.T) {
var _ Driver = new(DockerDriver)
}
+7
View File
@@ -0,0 +1,7 @@
package docker
import "testing"
func TestMockDriver_impl(t *testing.T) {
var _ Driver = new(MockDriver)
}
+68
View File
@@ -0,0 +1,68 @@
package docker
import (
"context"
"errors"
"testing"
"github.com/hashicorp/packer-plugin-sdk/multistep"
)
func testStepCommitState(t *testing.T) multistep.StateBag {
state := testState(t)
state.Put("container_id", "foo")
return state
}
func TestStepCommit_impl(t *testing.T) {
var _ multistep.Step = new(StepCommit)
}
func TestStepCommit(t *testing.T) {
state := testStepCommitState(t)
step := new(StepCommit)
defer step.Cleanup(state)
driver := state.Get("driver").(*MockDriver)
driver.CommitImageId = "bar"
// run the step
if action := step.Run(context.Background(), state); action != multistep.ActionContinue {
t.Fatalf("bad action: %#v", action)
}
// verify we did the right thing
if !driver.CommitCalled {
t.Fatal("should've called")
}
// verify the ID is saved
idRaw, ok := state.GetOk("image_id")
if !ok {
t.Fatal("should've saved ID")
}
id := idRaw.(string)
if id != driver.CommitImageId {
t.Fatalf("bad: %#v", id)
}
}
func TestStepCommit_error(t *testing.T) {
state := testStepCommitState(t)
step := new(StepCommit)
defer step.Cleanup(state)
driver := state.Get("driver").(*MockDriver)
driver.CommitErr = errors.New("foo")
// run the step
if action := step.Run(context.Background(), state); action != multistep.ActionHalt {
t.Fatalf("bad action: %#v", action)
}
// verify the ID is not saved
if _, ok := state.GetOk("image_id"); ok {
t.Fatal("shouldn't save image ID")
}
}
+101
View File
@@ -0,0 +1,101 @@
package docker
import (
"bytes"
"context"
"errors"
"io/ioutil"
"os"
"testing"
"github.com/hashicorp/packer-plugin-sdk/multistep"
)
func testStepExportState(t *testing.T) multistep.StateBag {
state := testState(t)
state.Put("container_id", "foo")
return state
}
func TestStepExport_impl(t *testing.T) {
var _ multistep.Step = new(StepExport)
}
func TestStepExport(t *testing.T) {
state := testStepExportState(t)
step := new(StepExport)
defer step.Cleanup(state)
// Create a tempfile for our output path
tf, err := ioutil.TempFile("", "packer")
if err != nil {
t.Fatalf("err: %s", err)
}
tf.Close()
defer os.Remove(tf.Name())
config := state.Get("config").(*Config)
config.ExportPath = tf.Name()
driver := state.Get("driver").(*MockDriver)
driver.ExportReader = bytes.NewReader([]byte("data!"))
// run the step
if action := step.Run(context.Background(), state); action != multistep.ActionContinue {
t.Fatalf("bad action: %#v", action)
}
// verify we did the right thing
if !driver.ExportCalled {
t.Fatal("should've exported")
}
if driver.ExportID != "foo" {
t.Fatalf("bad: %#v", driver.ExportID)
}
// verify the data exported to the file
contents, err := ioutil.ReadFile(tf.Name())
if err != nil {
t.Fatalf("err: %s", err)
}
if string(contents) != "data!" {
t.Fatalf("bad: %#v", string(contents))
}
}
func TestStepExport_error(t *testing.T) {
state := testStepExportState(t)
step := new(StepExport)
defer step.Cleanup(state)
// Create a tempfile for our output path
tf, err := ioutil.TempFile("", "packer")
if err != nil {
t.Fatalf("err: %s", err)
}
tf.Close()
if err := os.Remove(tf.Name()); err != nil {
t.Fatalf("err: %s", err)
}
config := state.Get("config").(*Config)
config.ExportPath = tf.Name()
driver := state.Get("driver").(*MockDriver)
driver.ExportError = errors.New("foo")
// run the step
if action := step.Run(context.Background(), state); action != multistep.ActionHalt {
t.Fatalf("bad action: %#v", action)
}
// verify we have an error
if _, ok := state.GetOk("error"); !ok {
t.Fatal("should have error")
}
// verify we didn't make that file
if _, err := os.Stat(tf.Name()); err == nil {
t.Fatal("export path shouldn't exist")
}
}
@@ -29,18 +29,7 @@ func (s *StepPull) Run(ctx context.Context, state multistep.StateBag) multistep.
ui.Say(fmt.Sprintf("Pulling Docker image: %s", config.Image)) ui.Say(fmt.Sprintf("Pulling Docker image: %s", config.Image))
if config.EcrLogin { if config.EcrLogin {
ui.Message("Fetching ECR credentials...") panic("to reimplement")
username, password, err := config.EcrGetLogin(config.LoginServer)
if err != nil {
err := fmt.Errorf("Error fetching ECR credentials: %s", err)
state.Put("error", err)
ui.Error(err.Error())
return multistep.ActionHalt
}
config.LoginUsername = username
config.LoginPassword = password
} }
driver := state.Get("driver").(Driver) driver := state.Get("driver").(Driver)
+104
View File
@@ -0,0 +1,104 @@
package docker
import (
"context"
"errors"
"testing"
"github.com/hashicorp/packer-plugin-sdk/multistep"
)
func TestStepPull_impl(t *testing.T) {
var _ multistep.Step = new(StepPull)
}
func TestStepPull(t *testing.T) {
state := testState(t)
step := new(StepPull)
defer step.Cleanup(state)
config := state.Get("config").(*Config)
driver := state.Get("driver").(*MockDriver)
// run the step
if action := step.Run(context.Background(), state); action != multistep.ActionContinue {
t.Fatalf("bad action: %#v", action)
}
// verify we did the right thing
if !driver.PullCalled {
t.Fatal("should've pulled")
}
if driver.PullImage != config.Image {
t.Fatalf("bad: %#v", driver.PullImage)
}
}
func TestStepPull_error(t *testing.T) {
state := testState(t)
step := new(StepPull)
defer step.Cleanup(state)
driver := state.Get("driver").(*MockDriver)
driver.PullError = errors.New("foo")
// run the step
if action := step.Run(context.Background(), state); action != multistep.ActionHalt {
t.Fatalf("bad action: %#v", action)
}
// verify we have an error
if _, ok := state.GetOk("error"); !ok {
t.Fatal("should have error")
}
}
func TestStepPull_login(t *testing.T) {
state := testState(t)
step := new(StepPull)
defer step.Cleanup(state)
config := state.Get("config").(*Config)
driver := state.Get("driver").(*MockDriver)
config.Login = true
// run the step
if action := step.Run(context.Background(), state); action != multistep.ActionContinue {
t.Fatalf("bad action: %#v", action)
}
// verify we pulled
if !driver.PullCalled {
t.Fatal("should've pulled")
}
// verify we logged in
if !driver.LoginCalled {
t.Fatal("should've logged in")
}
if !driver.LogoutCalled {
t.Fatal("should've logged out")
}
}
func TestStepPull_noPull(t *testing.T) {
state := testState(t)
step := new(StepPull)
defer step.Cleanup(state)
config := state.Get("config").(*Config)
config.Pull = false
driver := state.Get("driver").(*MockDriver)
// run the step
if action := step.Run(context.Background(), state); action != multistep.ActionContinue {
t.Fatalf("bad action: %#v", action)
}
// verify we did the right thing
if driver.PullCalled {
t.Fatal("shouldn't have pulled")
}
}
+97
View File
@@ -0,0 +1,97 @@
package docker
import (
"context"
"errors"
"testing"
"github.com/hashicorp/packer-plugin-sdk/multistep"
)
func testStepRunState(t *testing.T) multistep.StateBag {
state := testState(t)
state.Put("temp_dir", "/foo")
return state
}
func TestStepRun_impl(t *testing.T) {
var _ multistep.Step = new(StepRun)
}
func TestStepRun(t *testing.T) {
state := testStepRunState(t)
step := new(StepRun)
defer step.Cleanup(state)
config := state.Get("config").(*Config)
driver := state.Get("driver").(*MockDriver)
driver.StartID = "foo"
// run the step
if action := step.Run(context.Background(), state); action != multistep.ActionContinue {
t.Fatalf("bad action: %#v", action)
}
// verify we did the right thing
if !driver.StartCalled {
t.Fatal("should've called")
}
if driver.StartConfig.Image != config.Image {
t.Fatalf("bad: %#v", driver.StartConfig.Image)
}
// verify the ID is saved
idRaw, ok := state.GetOk("container_id")
if !ok {
t.Fatal("should've saved ID")
}
id := idRaw.(string)
if id != "foo" {
t.Fatalf("bad: %#v", id)
}
// Verify we haven't called stop yet
if driver.KillCalled {
t.Fatal("should not have stopped")
}
// Cleanup
step.Cleanup(state)
if !driver.KillCalled {
t.Fatal("should've stopped")
}
if driver.KillID != id {
t.Fatalf("bad: %#v", driver.StopID)
}
}
func TestStepRun_error(t *testing.T) {
state := testStepRunState(t)
step := new(StepRun)
defer step.Cleanup(state)
driver := state.Get("driver").(*MockDriver)
driver.StartError = errors.New("foo")
// run the step
if action := step.Run(context.Background(), state); action != multistep.ActionHalt {
t.Fatalf("bad action: %#v", action)
}
// verify the ID is not saved
if _, ok := state.GetOk("container_id"); ok {
t.Fatal("shouldn't save container ID")
}
// Verify we haven't called stop yet
if driver.KillCalled {
t.Fatal("should not have stopped")
}
// Cleanup
step.Cleanup(state)
if driver.KillCalled {
t.Fatal("should not have stopped")
}
}
@@ -0,0 +1,51 @@
package docker
import (
"context"
"testing"
"github.com/hashicorp/packer-plugin-sdk/multistep"
"github.com/hashicorp/packer-plugin-sdk/packerbuilderdata"
)
func TestStepSetGeneratedData_Run(t *testing.T) {
state := testState(t)
step := new(StepSetGeneratedData)
step.GeneratedData = &packerbuilderdata.GeneratedData{State: state}
driver := state.Get("driver").(*MockDriver)
driver.Sha256Result = "80B3BB1B1696E73A9B19DEEF92F664F8979F948DF348088B61F9A3477655AF64"
state.Put("image_id", "12345")
if action := step.Run(context.TODO(), state); action != multistep.ActionContinue {
t.Fatalf("Should not halt")
}
if !driver.Sha256Called {
t.Fatalf("driver.SHA256 should be called")
}
if driver.Sha256Id != "12345" {
t.Fatalf("driver.SHA256 got wrong image it: %s", driver.Sha256Id)
}
genData := state.Get("generated_data").(map[string]interface{})
imgSha256 := genData["ImageSha256"].(string)
if imgSha256 != driver.Sha256Result {
t.Fatalf("Expected ImageSha256 to be %s but was %s", driver.Sha256Result, imgSha256)
}
// Image ID not implement
state = testState(t)
step.GeneratedData = &packerbuilderdata.GeneratedData{State: state}
driver = state.Get("driver").(*MockDriver)
notImplementedMsg := "ERR_IMAGE_SHA256_NOT_FOUND"
if action := step.Run(context.TODO(), state); action != multistep.ActionContinue {
t.Fatalf("Should not halt")
}
if driver.Sha256Called {
t.Fatalf("driver.SHA256 should not be called")
}
genData = state.Get("generated_data").(map[string]interface{})
imgSha256 = genData["ImageSha256"].(string)
if imgSha256 != notImplementedMsg {
t.Fatalf("Expected ImageSha256 to be %s but was %s", notImplementedMsg, imgSha256)
}
}
+52
View File
@@ -0,0 +1,52 @@
package docker
import (
"context"
"os"
"testing"
"github.com/hashicorp/packer-plugin-sdk/multistep"
)
func TestStepTempDir_impl(t *testing.T) {
var _ multistep.Step = new(StepTempDir)
}
func testStepTempDir_impl(t *testing.T) string {
state := testState(t)
step := new(StepTempDir)
defer step.Cleanup(state)
// sanity test
if _, ok := state.GetOk("temp_dir"); ok {
t.Fatalf("temp_dir should not be in state yet")
}
// run the step
if action := step.Run(context.Background(), state); action != multistep.ActionContinue {
t.Fatalf("bad action: %#v", action)
}
// Verify that we got the temp dir
dirRaw, ok := state.GetOk("temp_dir")
if !ok {
t.Fatalf("should've made temp_dir")
}
dir := dirRaw.(string)
if _, err := os.Stat(dir); err != nil {
t.Fatalf("Stat for %s failed: err: %s", err, dir)
}
// Cleanup
step.Cleanup(state)
if _, err := os.Stat(dir); err == nil {
t.Fatalf("dir should be gone")
}
return dir
}
func TestStepTempDir(t *testing.T) {
testStepTempDir_impl(t)
}
+21
View File
@@ -0,0 +1,21 @@
package docker
import (
"bytes"
"testing"
"github.com/hashicorp/packer-plugin-sdk/multistep"
packersdk "github.com/hashicorp/packer-plugin-sdk/packer"
)
func testState(t *testing.T) multistep.StateBag {
state := new(multistep.BasicStateBag)
state.Put("config", testConfigStruct(t))
state.Put("driver", &MockDriver{})
state.Put("hook", &packersdk.MockHook{})
state.Put("ui", &packersdk.BasicUi{
Reader: new(bytes.Buffer),
Writer: new(bytes.Buffer),
})
return state
}
@@ -0,0 +1 @@
chocolate!
@@ -0,0 +1 @@
vanilla!
@@ -0,0 +1 @@
strawberry!
+13
View File
@@ -0,0 +1,13 @@
package version
import (
"github.com/hashicorp/packer-plugin-sdk/version"
packerVersion "github.com/hashicorp/packer/version"
)
var DigitalOceanPluginVersion *version.PluginVersion
func init() {
DigitalOceanPluginVersion = version.InitializePluginVersion(
packerVersion.Version, packerVersion.VersionPrerelease)
}
-6
View File
@@ -210,8 +210,6 @@ type Config struct {
// - The contents of the script file will be wrapped in Packer's startup script wrapper, unless `wrap_startup_script` is disabled. See `wrap_startup_script` for more details. // - The contents of the script file will be wrapped in Packer's startup script wrapper, unless `wrap_startup_script` is disabled. See `wrap_startup_script` for more details.
// - Not supported by Windows instances. See [Startup Scripts for Windows](https://cloud.google.com/compute/docs/startupscript#providing_a_startup_script_for_windows_instances) for more details. // - Not supported by Windows instances. See [Startup Scripts for Windows](https://cloud.google.com/compute/docs/startupscript#providing_a_startup_script_for_windows_instances) for more details.
StartupScriptFile string `mapstructure:"startup_script_file" required:"false"` StartupScriptFile string `mapstructure:"startup_script_file" required:"false"`
// The time to wait for windows password to be retrieved. Defaults to "3m".
WindowsPasswordTimeout time.Duration `mapstructure:"windows_password_timeout" required:"false"`
// For backwards compatibility this option defaults to `"true"` in the future it will default to `"false"`. // For backwards compatibility this option defaults to `"true"` in the future it will default to `"false"`.
// If "true", the contents of `startup_script_file` or `"startup_script"` in the instance metadata // If "true", the contents of `startup_script_file` or `"startup_script"` in the instance metadata
// is wrapped in a Packer specific script that tracks the execution and completion of the provided // is wrapped in a Packer specific script that tracks the execution and completion of the provided
@@ -544,10 +542,6 @@ func (c *Config) Prepare(raws ...interface{}) ([]string, error) {
c.WrapStartupScriptFile = config.TriTrue c.WrapStartupScriptFile = config.TriTrue
} }
} }
// Check windows password timeout is provided
if c.WindowsPasswordTimeout == 0 {
c.WindowsPasswordTimeout = 3 * time.Minute
}
// Check for any errors. // Check for any errors.
if errs != nil && len(errs.Errors) > 0 { if errs != nil && len(errs.Errors) > 0 {
-2
View File
@@ -112,7 +112,6 @@ type FlatConfig struct {
SourceImageFamily *string `mapstructure:"source_image_family" required:"true" cty:"source_image_family" hcl:"source_image_family"` SourceImageFamily *string `mapstructure:"source_image_family" required:"true" cty:"source_image_family" hcl:"source_image_family"`
SourceImageProjectId []string `mapstructure:"source_image_project_id" required:"false" cty:"source_image_project_id" hcl:"source_image_project_id"` SourceImageProjectId []string `mapstructure:"source_image_project_id" required:"false" cty:"source_image_project_id" hcl:"source_image_project_id"`
StartupScriptFile *string `mapstructure:"startup_script_file" required:"false" cty:"startup_script_file" hcl:"startup_script_file"` StartupScriptFile *string `mapstructure:"startup_script_file" required:"false" cty:"startup_script_file" hcl:"startup_script_file"`
WindowsPasswordTimeout *string `mapstructure:"windows_password_timeout" required:"false" cty:"windows_password_timeout" hcl:"windows_password_timeout"`
WrapStartupScriptFile *bool `mapstructure:"wrap_startup_script" required:"false" cty:"wrap_startup_script" hcl:"wrap_startup_script"` WrapStartupScriptFile *bool `mapstructure:"wrap_startup_script" required:"false" cty:"wrap_startup_script" hcl:"wrap_startup_script"`
Subnetwork *string `mapstructure:"subnetwork" required:"false" cty:"subnetwork" hcl:"subnetwork"` Subnetwork *string `mapstructure:"subnetwork" required:"false" cty:"subnetwork" hcl:"subnetwork"`
Tags []string `mapstructure:"tags" required:"false" cty:"tags" hcl:"tags"` Tags []string `mapstructure:"tags" required:"false" cty:"tags" hcl:"tags"`
@@ -237,7 +236,6 @@ func (*FlatConfig) HCL2Spec() map[string]hcldec.Spec {
"source_image_family": &hcldec.AttrSpec{Name: "source_image_family", Type: cty.String, Required: false}, "source_image_family": &hcldec.AttrSpec{Name: "source_image_family", Type: cty.String, Required: false},
"source_image_project_id": &hcldec.AttrSpec{Name: "source_image_project_id", Type: cty.List(cty.String), Required: false}, "source_image_project_id": &hcldec.AttrSpec{Name: "source_image_project_id", Type: cty.List(cty.String), Required: false},
"startup_script_file": &hcldec.AttrSpec{Name: "startup_script_file", Type: cty.String, Required: false}, "startup_script_file": &hcldec.AttrSpec{Name: "startup_script_file", Type: cty.String, Required: false},
"windows_password_timeout": &hcldec.AttrSpec{Name: "windows_password_timeout", Type: cty.String, Required: false},
"wrap_startup_script": &hcldec.AttrSpec{Name: "wrap_startup_script", Type: cty.Bool, Required: false}, "wrap_startup_script": &hcldec.AttrSpec{Name: "wrap_startup_script", Type: cty.Bool, Required: false},
"subnetwork": &hcldec.AttrSpec{Name: "subnetwork", Type: cty.String, Required: false}, "subnetwork": &hcldec.AttrSpec{Name: "subnetwork", Type: cty.String, Required: false},
"tags": &hcldec.AttrSpec{Name: "tags", Type: cty.List(cty.String), Required: false}, "tags": &hcldec.AttrSpec{Name: "tags", Type: cty.List(cty.String), Required: false},
+7 -8
View File
@@ -107,14 +107,13 @@ type InstanceConfig struct {
// WindowsPasswordConfig is the data structure that GCE needs to encrypt the created // WindowsPasswordConfig is the data structure that GCE needs to encrypt the created
// windows password. // windows password.
type WindowsPasswordConfig struct { type WindowsPasswordConfig struct {
key *rsa.PrivateKey key *rsa.PrivateKey
password string password string
UserName string `json:"userName"` UserName string `json:"userName"`
Modulus string `json:"modulus"` Modulus string `json:"modulus"`
Exponent string `json:"exponent"` Exponent string `json:"exponent"`
Email string `json:"email"` Email string `json:"email"`
ExpireOn time.Time `json:"expireOn"` ExpireOn time.Time `json:"expireOn"`
WindowsPasswordTimeout time.Duration `json:"timeout"`
} }
type windowsPasswordResponse struct { type windowsPasswordResponse struct {
+2 -1
View File
@@ -253,6 +253,7 @@ func (d *driverGCE) GetImage(name string, fromFamily bool) (*Image, error) {
"ubuntu-os-cloud", "ubuntu-os-cloud",
"windows-cloud", "windows-cloud",
"windows-sql-cloud", "windows-sql-cloud",
"gce-uefi-images",
"gce-nvme", "gce-nvme",
// misc // misc
"google-containers", "google-containers",
@@ -567,7 +568,7 @@ func (d *driverGCE) createWindowsPassword(errCh chan<- error, name, zone string,
return return
} }
timeout := time.Now().Add(c.WindowsPasswordTimeout) timeout := time.Now().Add(time.Minute * 3)
hash := sha1.New() hash := sha1.New()
random := rand.Reader random := rand.Reader
@@ -60,13 +60,12 @@ func (s *StepCreateWindowsPassword) Run(ctx context.Context, state multistep.Sta
} }
data := WindowsPasswordConfig{ data := WindowsPasswordConfig{
key: priv, key: priv,
UserName: c.Comm.WinRMUser, UserName: c.Comm.WinRMUser,
Modulus: base64.StdEncoding.EncodeToString(priv.N.Bytes()), Modulus: base64.StdEncoding.EncodeToString(priv.N.Bytes()),
Exponent: base64.StdEncoding.EncodeToString(buf[1:]), Exponent: base64.StdEncoding.EncodeToString(buf[1:]),
Email: email, Email: email,
ExpireOn: time.Now().Add(time.Minute * 5), ExpireOn: time.Now().Add(time.Minute * 5),
WindowsPasswordTimeout: c.WindowsPasswordTimeout,
} }
if s.Debug { if s.Debug {
@@ -99,7 +98,7 @@ func (s *StepCreateWindowsPassword) Run(ctx context.Context, state multistep.Sta
ui.Message("Waiting for windows password to complete...") ui.Message("Waiting for windows password to complete...")
select { select {
case err = <-errCh: case err = <-errCh:
case <-time.After(c.WindowsPasswordTimeout): case <-time.After(c.StateTimeout):
err = errors.New("time out while waiting for the password to be created") err = errors.New("time out while waiting for the password to be created")
} }
} }
+7 -2
View File
@@ -211,7 +211,12 @@ func (b *Builder) Run(ctx context.Context, ui packersdk.Ui, hook packersdk.Hook)
Directories: b.config.FloppyConfig.FloppyDirectories, Directories: b.config.FloppyConfig.FloppyDirectories,
Label: b.config.FloppyConfig.FloppyLabel, Label: b.config.FloppyConfig.FloppyLabel,
}, },
commonsteps.HTTPServerFromHTTPConfig(&b.config.HTTPConfig), &commonsteps.StepHTTPServer{
HTTPDir: b.config.HTTPDir,
HTTPPortMin: b.config.HTTPPortMin,
HTTPPortMax: b.config.HTTPPortMax,
HTTPAddress: b.config.HTTPAddress,
},
&hypervcommon.StepCreateSwitch{ &hypervcommon.StepCreateSwitch{
SwitchName: b.config.SwitchName, SwitchName: b.config.SwitchName,
}, },
@@ -289,7 +294,7 @@ func (b *Builder) Run(ctx context.Context, ui packersdk.Ui, hook packersdk.Hook)
// configure the communicator ssh, winrm // configure the communicator ssh, winrm
&communicator.StepConnect{ &communicator.StepConnect{
Config: &b.config.SSHConfig.Comm, Config: &b.config.SSHConfig.Comm,
Host: hypervcommon.CommHost(b.config.SSHConfig.Comm.Host()), Host: hypervcommon.CommHost(b.config.SSHConfig.Comm.SSHHost),
SSHConfig: b.config.SSHConfig.Comm.SSHConfigFunc(), SSHConfig: b.config.SSHConfig.Comm.SSHConfigFunc(),
}, },
-2
View File
@@ -19,7 +19,6 @@ type FlatConfig struct {
PackerUserVars map[string]string `mapstructure:"packer_user_variables" cty:"packer_user_variables" hcl:"packer_user_variables"` PackerUserVars map[string]string `mapstructure:"packer_user_variables" cty:"packer_user_variables" hcl:"packer_user_variables"`
PackerSensitiveVars []string `mapstructure:"packer_sensitive_variables" cty:"packer_sensitive_variables" hcl:"packer_sensitive_variables"` PackerSensitiveVars []string `mapstructure:"packer_sensitive_variables" cty:"packer_sensitive_variables" hcl:"packer_sensitive_variables"`
HTTPDir *string `mapstructure:"http_directory" cty:"http_directory" hcl:"http_directory"` HTTPDir *string `mapstructure:"http_directory" cty:"http_directory" hcl:"http_directory"`
HTTPContent map[string]string `mapstructure:"http_content" cty:"http_content" hcl:"http_content"`
HTTPPortMin *int `mapstructure:"http_port_min" cty:"http_port_min" hcl:"http_port_min"` HTTPPortMin *int `mapstructure:"http_port_min" cty:"http_port_min" hcl:"http_port_min"`
HTTPPortMax *int `mapstructure:"http_port_max" cty:"http_port_max" hcl:"http_port_max"` HTTPPortMax *int `mapstructure:"http_port_max" cty:"http_port_max" hcl:"http_port_max"`
HTTPAddress *string `mapstructure:"http_bind_address" cty:"http_bind_address" hcl:"http_bind_address"` HTTPAddress *string `mapstructure:"http_bind_address" cty:"http_bind_address" hcl:"http_bind_address"`
@@ -142,7 +141,6 @@ func (*FlatConfig) HCL2Spec() map[string]hcldec.Spec {
"packer_user_variables": &hcldec.AttrSpec{Name: "packer_user_variables", Type: cty.Map(cty.String), Required: false}, "packer_user_variables": &hcldec.AttrSpec{Name: "packer_user_variables", Type: cty.Map(cty.String), Required: false},
"packer_sensitive_variables": &hcldec.AttrSpec{Name: "packer_sensitive_variables", Type: cty.List(cty.String), Required: false}, "packer_sensitive_variables": &hcldec.AttrSpec{Name: "packer_sensitive_variables", Type: cty.List(cty.String), Required: false},
"http_directory": &hcldec.AttrSpec{Name: "http_directory", Type: cty.String, Required: false}, "http_directory": &hcldec.AttrSpec{Name: "http_directory", Type: cty.String, Required: false},
"http_content": &hcldec.AttrSpec{Name: "http_content", Type: cty.Map(cty.String), Required: false},
"http_port_min": &hcldec.AttrSpec{Name: "http_port_min", Type: cty.Number, Required: false}, "http_port_min": &hcldec.AttrSpec{Name: "http_port_min", Type: cty.Number, Required: false},
"http_port_max": &hcldec.AttrSpec{Name: "http_port_max", Type: cty.Number, Required: false}, "http_port_max": &hcldec.AttrSpec{Name: "http_port_max", Type: cty.Number, Required: false},
"http_bind_address": &hcldec.AttrSpec{Name: "http_bind_address", Type: cty.String, Required: false}, "http_bind_address": &hcldec.AttrSpec{Name: "http_bind_address", Type: cty.String, Required: false},
+7 -2
View File
@@ -251,7 +251,12 @@ func (b *Builder) Run(ctx context.Context, ui packersdk.Ui, hook packersdk.Hook)
Directories: b.config.FloppyConfig.FloppyDirectories, Directories: b.config.FloppyConfig.FloppyDirectories,
Label: b.config.FloppyConfig.FloppyLabel, Label: b.config.FloppyConfig.FloppyLabel,
}, },
commonsteps.HTTPServerFromHTTPConfig(&b.config.HTTPConfig), &commonsteps.StepHTTPServer{
HTTPDir: b.config.HTTPDir,
HTTPPortMin: b.config.HTTPPortMin,
HTTPPortMax: b.config.HTTPPortMax,
HTTPAddress: b.config.HTTPAddress,
},
&hypervcommon.StepCreateSwitch{ &hypervcommon.StepCreateSwitch{
SwitchName: b.config.SwitchName, SwitchName: b.config.SwitchName,
}, },
@@ -329,7 +334,7 @@ func (b *Builder) Run(ctx context.Context, ui packersdk.Ui, hook packersdk.Hook)
// configure the communicator ssh, winrm // configure the communicator ssh, winrm
&communicator.StepConnect{ &communicator.StepConnect{
Config: &b.config.SSHConfig.Comm, Config: &b.config.SSHConfig.Comm,
Host: hypervcommon.CommHost(b.config.SSHConfig.Comm.Host()), Host: hypervcommon.CommHost(b.config.SSHConfig.Comm.SSHHost),
SSHConfig: b.config.SSHConfig.Comm.SSHConfigFunc(), SSHConfig: b.config.SSHConfig.Comm.SSHConfigFunc(),
}, },
-2
View File
@@ -19,7 +19,6 @@ type FlatConfig struct {
PackerUserVars map[string]string `mapstructure:"packer_user_variables" cty:"packer_user_variables" hcl:"packer_user_variables"` PackerUserVars map[string]string `mapstructure:"packer_user_variables" cty:"packer_user_variables" hcl:"packer_user_variables"`
PackerSensitiveVars []string `mapstructure:"packer_sensitive_variables" cty:"packer_sensitive_variables" hcl:"packer_sensitive_variables"` PackerSensitiveVars []string `mapstructure:"packer_sensitive_variables" cty:"packer_sensitive_variables" hcl:"packer_sensitive_variables"`
HTTPDir *string `mapstructure:"http_directory" cty:"http_directory" hcl:"http_directory"` HTTPDir *string `mapstructure:"http_directory" cty:"http_directory" hcl:"http_directory"`
HTTPContent map[string]string `mapstructure:"http_content" cty:"http_content" hcl:"http_content"`
HTTPPortMin *int `mapstructure:"http_port_min" cty:"http_port_min" hcl:"http_port_min"` HTTPPortMin *int `mapstructure:"http_port_min" cty:"http_port_min" hcl:"http_port_min"`
HTTPPortMax *int `mapstructure:"http_port_max" cty:"http_port_max" hcl:"http_port_max"` HTTPPortMax *int `mapstructure:"http_port_max" cty:"http_port_max" hcl:"http_port_max"`
HTTPAddress *string `mapstructure:"http_bind_address" cty:"http_bind_address" hcl:"http_bind_address"` HTTPAddress *string `mapstructure:"http_bind_address" cty:"http_bind_address" hcl:"http_bind_address"`
@@ -144,7 +143,6 @@ func (*FlatConfig) HCL2Spec() map[string]hcldec.Spec {
"packer_user_variables": &hcldec.AttrSpec{Name: "packer_user_variables", Type: cty.Map(cty.String), Required: false}, "packer_user_variables": &hcldec.AttrSpec{Name: "packer_user_variables", Type: cty.Map(cty.String), Required: false},
"packer_sensitive_variables": &hcldec.AttrSpec{Name: "packer_sensitive_variables", Type: cty.List(cty.String), Required: false}, "packer_sensitive_variables": &hcldec.AttrSpec{Name: "packer_sensitive_variables", Type: cty.List(cty.String), Required: false},
"http_directory": &hcldec.AttrSpec{Name: "http_directory", Type: cty.String, Required: false}, "http_directory": &hcldec.AttrSpec{Name: "http_directory", Type: cty.String, Required: false},
"http_content": &hcldec.AttrSpec{Name: "http_content", Type: cty.Map(cty.String), Required: false},
"http_port_min": &hcldec.AttrSpec{Name: "http_port_min", Type: cty.Number, Required: false}, "http_port_min": &hcldec.AttrSpec{Name: "http_port_min", Type: cty.Number, Required: false},
"http_port_max": &hcldec.AttrSpec{Name: "http_port_max", Type: cty.Number, Required: false}, "http_port_max": &hcldec.AttrSpec{Name: "http_port_max", Type: cty.Number, Required: false},
"http_bind_address": &hcldec.AttrSpec{Name: "http_bind_address", Type: cty.String, Required: false}, "http_bind_address": &hcldec.AttrSpec{Name: "http_bind_address", Type: cty.String, Required: false},
+1 -1
View File
@@ -151,7 +151,7 @@ func (b *Builder) Run(ctx context.Context, ui packersdk.Ui, hook packersdk.Hook)
&communicator.StepConnect{ &communicator.StepConnect{
Config: &b.config.RunConfig.Comm, Config: &b.config.RunConfig.Comm,
Host: CommHost( Host: CommHost(
b.config.RunConfig.Comm.Host(), b.config.RunConfig.Comm.SSHHost,
computeClient, computeClient,
b.config.SSHInterface, b.config.SSHInterface,
b.config.SSHIPVersion), b.config.SSHIPVersion),
+1 -1
View File
@@ -4,7 +4,7 @@ import (
"context" "context"
"fmt" "fmt"
"github.com/oracle/oci-go-sdk/v36/core" "github.com/oracle/oci-go-sdk/core"
) )
// Artifact is an artifact implementation that contains a built Custom Image. // Artifact is an artifact implementation that contains a built Custom Image.
+1 -1
View File
@@ -12,7 +12,7 @@ import (
"github.com/hashicorp/packer-plugin-sdk/multistep/commonsteps" "github.com/hashicorp/packer-plugin-sdk/multistep/commonsteps"
packersdk "github.com/hashicorp/packer-plugin-sdk/packer" packersdk "github.com/hashicorp/packer-plugin-sdk/packer"
ocommon "github.com/hashicorp/packer/builder/oracle/common" ocommon "github.com/hashicorp/packer/builder/oracle/common"
"github.com/oracle/oci-go-sdk/v36/core" "github.com/oracle/oci-go-sdk/core"
) )
// BuilderId uniquely identifies the builder // BuilderId uniquely identifies the builder
+5 -23
View File
@@ -1,4 +1,4 @@
//go:generate mapstructure-to-hcl2 -type Config,CreateVNICDetails,ListImagesRequest,FlexShapeConfig //go:generate mapstructure-to-hcl2 -type Config,CreateVNICDetails,ListImagesRequest
package oci package oci
@@ -19,8 +19,8 @@ import (
"github.com/hashicorp/packer-plugin-sdk/pathing" "github.com/hashicorp/packer-plugin-sdk/pathing"
"github.com/hashicorp/packer-plugin-sdk/template/config" "github.com/hashicorp/packer-plugin-sdk/template/config"
"github.com/hashicorp/packer-plugin-sdk/template/interpolate" "github.com/hashicorp/packer-plugin-sdk/template/interpolate"
ocicommon "github.com/oracle/oci-go-sdk/v36/common" ocicommon "github.com/oracle/oci-go-sdk/common"
ociauth "github.com/oracle/oci-go-sdk/v36/common/auth" ociauth "github.com/oracle/oci-go-sdk/common/auth"
) )
type CreateVNICDetails struct { type CreateVNICDetails struct {
@@ -46,11 +46,6 @@ type ListImagesRequest struct {
Shape *string `mapstructure:"shape"` Shape *string `mapstructure:"shape"`
} }
type FlexShapeConfig struct {
Ocpus *float32 `mapstructure:"ocpus" required:"false"`
MemoryInGBs *float32 `mapstructure:"memory_in_gbs" required:"false"`
}
type Config struct { type Config struct {
common.PackerConfig `mapstructure:",squash"` common.PackerConfig `mapstructure:",squash"`
Comm communicator.Config `mapstructure:",squash"` Comm communicator.Config `mapstructure:",squash"`
@@ -96,7 +91,6 @@ type Config struct {
InstanceTags map[string]string `mapstructure:"instance_tags"` InstanceTags map[string]string `mapstructure:"instance_tags"`
InstanceDefinedTags map[string]map[string]interface{} `mapstructure:"instance_defined_tags"` InstanceDefinedTags map[string]map[string]interface{} `mapstructure:"instance_defined_tags"`
Shape string `mapstructure:"shape"` Shape string `mapstructure:"shape"`
ShapeConfig FlexShapeConfig `mapstructure:"shape_config"`
BootVolumeSizeInGBs int64 `mapstructure:"disk_size"` BootVolumeSizeInGBs int64 `mapstructure:"disk_size"`
// Metadata optionally contains custom metadata key/value pairs provided in the // Metadata optionally contains custom metadata key/value pairs provided in the
@@ -228,7 +222,7 @@ func (c *Config) Prepare(raws ...interface{}) error {
} }
providers := []ocicommon.ConfigurationProvider{ providers := []ocicommon.ConfigurationProvider{
ocicommon.NewRawConfigurationProvider(c.TenancyID, c.UserID, c.Region, c.Fingerprint, string(keyContent), &c.PassPhrase), NewRawConfigurationProvider(c.TenancyID, c.UserID, c.Region, c.Fingerprint, string(keyContent), &c.PassPhrase),
} }
if fileProvider != nil { if fileProvider != nil {
@@ -259,7 +253,7 @@ func (c *Config) Prepare(raws ...interface{}) error {
if _, err := configProvider.PrivateRSAKey(); err != nil { if _, err := configProvider.PrivateRSAKey(); err != nil {
errs = packersdk.MultiErrorAppend( errs = packersdk.MultiErrorAppend(
errs, fmt.Errorf("'key_file' must be correctly specified. %w", err)) errs, errors.New("'key_file' must be specified"))
} }
c.configProvider = configProvider c.configProvider = configProvider
@@ -283,18 +277,6 @@ func (c *Config) Prepare(raws ...interface{}) error {
errs, errors.New("'shape' must be specified")) errs, errors.New("'shape' must be specified"))
} }
if strings.HasSuffix(c.Shape, "Flex") {
if c.ShapeConfig.Ocpus == nil {
errs = packersdk.MultiErrorAppend(
errs, errors.New("'Ocpus' must be specified when using flexible shapes"))
}
}
if c.ShapeConfig.MemoryInGBs != nil && c.ShapeConfig.Ocpus == nil {
errs = packersdk.MultiErrorAppend(
errs, errors.New("'Ocpus' must be specified if memory_in_gbs is specified"))
}
if (c.SubnetID == "") && (c.CreateVnicDetails.SubnetId == nil) { if (c.SubnetID == "") && (c.CreateVnicDetails.SubnetId == nil) {
errs = packersdk.MultiErrorAppend( errs = packersdk.MultiErrorAppend(
errs, errors.New("'subnet_ocid' must be specified")) errs, errors.New("'subnet_ocid' must be specified"))
+1 -28
View File
@@ -1,4 +1,4 @@
// Code generated by "mapstructure-to-hcl2 -type Config,CreateVNICDetails,ListImagesRequest,FlexShapeConfig"; DO NOT EDIT. // Code generated by "mapstructure-to-hcl2 -type Config,CreateVNICDetails,ListImagesRequest"; DO NOT EDIT.
package oci package oci
@@ -88,7 +88,6 @@ type FlatConfig struct {
InstanceTags map[string]string `mapstructure:"instance_tags" cty:"instance_tags" hcl:"instance_tags"` InstanceTags map[string]string `mapstructure:"instance_tags" cty:"instance_tags" hcl:"instance_tags"`
InstanceDefinedTags map[string]map[string]interface{} `mapstructure:"instance_defined_tags" cty:"instance_defined_tags" hcl:"instance_defined_tags"` InstanceDefinedTags map[string]map[string]interface{} `mapstructure:"instance_defined_tags" cty:"instance_defined_tags" hcl:"instance_defined_tags"`
Shape *string `mapstructure:"shape" cty:"shape" hcl:"shape"` Shape *string `mapstructure:"shape" cty:"shape" hcl:"shape"`
ShapeConfig *FlatFlexShapeConfig `mapstructure:"shape_config" cty:"shape_config" hcl:"shape_config"`
BootVolumeSizeInGBs *int64 `mapstructure:"disk_size" cty:"disk_size" hcl:"disk_size"` BootVolumeSizeInGBs *int64 `mapstructure:"disk_size" cty:"disk_size" hcl:"disk_size"`
Metadata map[string]string `mapstructure:"metadata" cty:"metadata" hcl:"metadata"` Metadata map[string]string `mapstructure:"metadata" cty:"metadata" hcl:"metadata"`
UserData *string `mapstructure:"user_data" cty:"user_data" hcl:"user_data"` UserData *string `mapstructure:"user_data" cty:"user_data" hcl:"user_data"`
@@ -189,7 +188,6 @@ func (*FlatConfig) HCL2Spec() map[string]hcldec.Spec {
"instance_tags": &hcldec.AttrSpec{Name: "instance_tags", Type: cty.Map(cty.String), Required: false}, "instance_tags": &hcldec.AttrSpec{Name: "instance_tags", Type: cty.Map(cty.String), Required: false},
"instance_defined_tags": &hcldec.AttrSpec{Name: "instance_defined_tags", Type: cty.Map(cty.String), Required: false}, "instance_defined_tags": &hcldec.AttrSpec{Name: "instance_defined_tags", Type: cty.Map(cty.String), Required: false},
"shape": &hcldec.AttrSpec{Name: "shape", Type: cty.String, Required: false}, "shape": &hcldec.AttrSpec{Name: "shape", Type: cty.String, Required: false},
"shape_config": &hcldec.BlockSpec{TypeName: "shape_config", Nested: hcldec.ObjectSpec((*FlatFlexShapeConfig)(nil).HCL2Spec())},
"disk_size": &hcldec.AttrSpec{Name: "disk_size", Type: cty.Number, Required: false}, "disk_size": &hcldec.AttrSpec{Name: "disk_size", Type: cty.Number, Required: false},
"metadata": &hcldec.AttrSpec{Name: "metadata", Type: cty.Map(cty.String), Required: false}, "metadata": &hcldec.AttrSpec{Name: "metadata", Type: cty.Map(cty.String), Required: false},
"user_data": &hcldec.AttrSpec{Name: "user_data", Type: cty.String, Required: false}, "user_data": &hcldec.AttrSpec{Name: "user_data", Type: cty.String, Required: false},
@@ -241,31 +239,6 @@ func (*FlatCreateVNICDetails) HCL2Spec() map[string]hcldec.Spec {
return s return s
} }
// FlatFlexShapeConfig is an auto-generated flat version of FlexShapeConfig.
// Where the contents of a field with a `mapstructure:,squash` tag are bubbled up.
type FlatFlexShapeConfig struct {
Ocpus *float32 `mapstructure:"ocpus" required:"false" cty:"ocpus" hcl:"ocpus"`
MemoryInGBs *float32 `mapstructure:"memory_in_gbs" required:"false" cty:"memory_in_gbs" hcl:"memory_in_gbs"`
}
// FlatMapstructure returns a new FlatFlexShapeConfig.
// FlatFlexShapeConfig is an auto-generated flat version of FlexShapeConfig.
// Where the contents a fields with a `mapstructure:,squash` tag are bubbled up.
func (*FlexShapeConfig) FlatMapstructure() interface{ HCL2Spec() map[string]hcldec.Spec } {
return new(FlatFlexShapeConfig)
}
// HCL2Spec returns the hcl spec of a FlexShapeConfig.
// This spec is used by HCL to read the fields of FlexShapeConfig.
// The decoded values from this spec will then be applied to a FlatFlexShapeConfig.
func (*FlatFlexShapeConfig) HCL2Spec() map[string]hcldec.Spec {
s := map[string]hcldec.Spec{
"ocpus": &hcldec.AttrSpec{Name: "ocpus", Type: cty.Number, Required: false},
"memory_in_gbs": &hcldec.AttrSpec{Name: "memory_in_gbs", Type: cty.Number, Required: false},
}
return s
}
// FlatListImagesRequest is an auto-generated flat version of ListImagesRequest. // FlatListImagesRequest is an auto-generated flat version of ListImagesRequest.
// Where the contents of a field with a `mapstructure:,squash` tag are bubbled up. // Where the contents of a field with a `mapstructure:,squash` tag are bubbled up.
type FlatListImagesRequest struct { type FlatListImagesRequest struct {
+76
View File
@@ -0,0 +1,76 @@
package oci
import (
"crypto/rsa"
"errors"
"fmt"
"github.com/oracle/oci-go-sdk/common"
)
// rawConfigurationProvider allows a user to simply construct a configuration
// provider from raw values. It errors on access when those values are empty.
type rawConfigurationProvider struct {
tenancy string
user string
region string
fingerprint string
privateKey string
privateKeyPassphrase *string
}
// NewRawConfigurationProvider will create a rawConfigurationProvider.
func NewRawConfigurationProvider(tenancy, user, region, fingerprint, privateKey string, privateKeyPassphrase *string) common.ConfigurationProvider {
return rawConfigurationProvider{tenancy, user, region, fingerprint, privateKey, privateKeyPassphrase}
}
func (p rawConfigurationProvider) PrivateRSAKey() (key *rsa.PrivateKey, err error) {
return common.PrivateKeyFromBytes([]byte(p.privateKey), p.privateKeyPassphrase)
}
func (p rawConfigurationProvider) KeyID() (keyID string, err error) {
tenancy, err := p.TenancyOCID()
if err != nil {
return
}
user, err := p.UserOCID()
if err != nil {
return
}
fingerprint, err := p.KeyFingerprint()
if err != nil {
return
}
return fmt.Sprintf("%s/%s/%s", tenancy, user, fingerprint), nil
}
func (p rawConfigurationProvider) TenancyOCID() (string, error) {
if p.tenancy == "" {
return "", errors.New("no tenancy provided")
}
return p.tenancy, nil
}
func (p rawConfigurationProvider) UserOCID() (string, error) {
if p.user == "" {
return "", errors.New("no user provided")
}
return p.user, nil
}
func (p rawConfigurationProvider) KeyFingerprint() (string, error) {
if p.fingerprint == "" {
return "", errors.New("no fingerprint provided")
}
return p.fingerprint, nil
}
func (p rawConfigurationProvider) Region() (string, error) {
if p.region == "" {
return "", errors.New("no region provided")
}
return p.region, nil
}
+1 -5
View File
@@ -131,7 +131,7 @@ func TestConfig(t *testing.T) {
t.Run("NoAccessConfig", func(t *testing.T) { t.Run("NoAccessConfig", func(t *testing.T) {
raw := testConfig(cfgFile) raw := testConfig(cfgFile)
raw["access_cfg_file"] = "/tmp/random/access/config/file/should/not/exist" delete(raw, "access_cfg_file")
var c Config var c Config
errs := c.Prepare(raw) errs := c.Prepare(raw)
@@ -140,10 +140,6 @@ func TestConfig(t *testing.T) {
"'user_ocid'", "'tenancy_ocid'", "'fingerprint'", "'key_file'", "'user_ocid'", "'tenancy_ocid'", "'fingerprint'", "'key_file'",
} }
if errs == nil {
t.Fatalf("Expected errors %q but got none", expectedErrors)
}
s := errs.Error() s := errs.Error()
for _, expected := range expectedErrors { for _, expected := range expectedErrors {
if !strings.Contains(s, expected) { if !strings.Contains(s, expected) {
+1 -1
View File
@@ -3,7 +3,7 @@ package oci
import ( import (
"context" "context"
"github.com/oracle/oci-go-sdk/v36/core" "github.com/oracle/oci-go-sdk/core"
) )
// Driver interfaces between the builder steps and the OCI SDK. // Driver interfaces between the builder steps and the OCI SDK.
+1 -1
View File
@@ -3,7 +3,7 @@ package oci
import ( import (
"context" "context"
"github.com/oracle/oci-go-sdk/v36/core" "github.com/oracle/oci-go-sdk/core"
) )
// driverMock implements the Driver interface and communicates with Oracle // driverMock implements the Driver interface and communicates with Oracle
+11 -70
View File
@@ -4,15 +4,10 @@ import (
"context" "context"
"errors" "errors"
"fmt" "fmt"
"math"
"math/rand"
"net/http"
"regexp" "regexp"
"sync/atomic"
"time" "time"
"github.com/oracle/oci-go-sdk/v36/common" core "github.com/oracle/oci-go-sdk/core"
core "github.com/oracle/oci-go-sdk/v36/core"
) )
// driverOCI implements the Driver interface and communicates with Oracle // driverOCI implements the Driver interface and communicates with Oracle
@@ -24,31 +19,6 @@ type driverOCI struct {
context context.Context context context.Context
} }
var retryPolicy = &common.RetryPolicy{
MaximumNumberAttempts: 10,
ShouldRetryOperation: func(res common.OCIOperationResponse) bool {
var e common.ServiceError
if errors.As(res.Error, &e) {
switch e.GetHTTPStatusCode() {
case http.StatusTooManyRequests, http.StatusInternalServerError, http.StatusServiceUnavailable:
return true
}
}
return false
},
NextDuration: func(res common.OCIOperationResponse) time.Duration {
x := uint64(res.AttemptNumber)
d := time.Duration(math.Pow(2, float64(atomic.LoadUint64(&x)))) * time.Second
j := time.Duration(rand.Float64()*(2000)) * time.Millisecond
w := d + j
return w
},
}
var requestMetadata = common.RequestMetadata{
RetryPolicy: retryPolicy,
}
// NewDriverOCI Creates a new driverOCI with a connected compute client and a connected vcn client. // NewDriverOCI Creates a new driverOCI with a connected compute client and a connected vcn client.
func NewDriverOCI(cfg *Config) (Driver, error) { func NewDriverOCI(cfg *Config) (Driver, error) {
coreClient, err := core.NewComputeClientWithConfigurationProvider(cfg.configProvider) coreClient, err := core.NewComputeClientWithConfigurationProvider(cfg.configProvider)
@@ -110,7 +80,6 @@ func (d *driverOCI) CreateInstance(ctx context.Context, publicKey string) (strin
LifecycleState: "AVAILABLE", LifecycleState: "AVAILABLE",
SortBy: "TIMECREATED", SortBy: "TIMECREATED",
SortOrder: "DESC", SortOrder: "DESC",
RequestMetadata: requestMetadata,
}) })
if err != nil { if err != nil {
return "", err return "", err
@@ -158,18 +127,7 @@ func (d *driverOCI) CreateInstance(ctx context.Context, publicKey string) (strin
Metadata: metadata, Metadata: metadata,
} }
if d.cfg.ShapeConfig.Ocpus != nil { instance, err := d.computeClient.LaunchInstance(context.TODO(), core.LaunchInstanceRequest{LaunchInstanceDetails: instanceDetails})
LaunchInstanceShapeConfigDetails := core.LaunchInstanceShapeConfigDetails{
Ocpus: d.cfg.ShapeConfig.Ocpus,
MemoryInGBs: d.cfg.ShapeConfig.MemoryInGBs,
}
instanceDetails.ShapeConfig = &LaunchInstanceShapeConfigDetails
}
instance, err := d.computeClient.LaunchInstance(context.TODO(), core.LaunchInstanceRequest{
LaunchInstanceDetails: instanceDetails,
RequestMetadata: requestMetadata,
})
if err != nil { if err != nil {
return "", err return "", err
@@ -187,9 +145,7 @@ func (d *driverOCI) CreateImage(ctx context.Context, id string) (core.Image, err
FreeformTags: d.cfg.Tags, FreeformTags: d.cfg.Tags,
DefinedTags: d.cfg.DefinedTags, DefinedTags: d.cfg.DefinedTags,
LaunchMode: core.CreateImageDetailsLaunchModeEnum(d.cfg.LaunchMode), LaunchMode: core.CreateImageDetailsLaunchModeEnum(d.cfg.LaunchMode),
}, }})
RequestMetadata: requestMetadata,
})
if err != nil { if err != nil {
return core.Image{}, err return core.Image{}, err
@@ -200,19 +156,15 @@ func (d *driverOCI) CreateImage(ctx context.Context, id string) (core.Image, err
// DeleteImage deletes a custom image. // DeleteImage deletes a custom image.
func (d *driverOCI) DeleteImage(ctx context.Context, id string) error { func (d *driverOCI) DeleteImage(ctx context.Context, id string) error {
_, err := d.computeClient.DeleteImage(ctx, core.DeleteImageRequest{ _, err := d.computeClient.DeleteImage(ctx, core.DeleteImageRequest{ImageId: &id})
ImageId: &id,
RequestMetadata: requestMetadata,
})
return err return err
} }
// GetInstanceIP returns the public or private IP corresponding to the given instance id. // GetInstanceIP returns the public or private IP corresponding to the given instance id.
func (d *driverOCI) GetInstanceIP(ctx context.Context, id string) (string, error) { func (d *driverOCI) GetInstanceIP(ctx context.Context, id string) (string, error) {
vnics, err := d.computeClient.ListVnicAttachments(ctx, core.ListVnicAttachmentsRequest{ vnics, err := d.computeClient.ListVnicAttachments(ctx, core.ListVnicAttachmentsRequest{
InstanceId: &id, InstanceId: &id,
CompartmentId: &d.cfg.CompartmentID, CompartmentId: &d.cfg.CompartmentID,
RequestMetadata: requestMetadata,
}) })
if err != nil { if err != nil {
return "", err return "", err
@@ -222,10 +174,7 @@ func (d *driverOCI) GetInstanceIP(ctx context.Context, id string) (string, error
return "", errors.New("instance has zero VNICs") return "", errors.New("instance has zero VNICs")
} }
vnic, err := d.vcnClient.GetVnic(ctx, core.GetVnicRequest{ vnic, err := d.vcnClient.GetVnic(ctx, core.GetVnicRequest{VnicId: vnics.Items[0].VnicId})
VnicId: vnics.Items[0].VnicId,
RequestMetadata: requestMetadata,
})
if err != nil { if err != nil {
return "", fmt.Errorf("Error getting VNIC details: %s", err) return "", fmt.Errorf("Error getting VNIC details: %s", err)
} }
@@ -243,8 +192,7 @@ func (d *driverOCI) GetInstanceIP(ctx context.Context, id string) (string, error
func (d *driverOCI) GetInstanceInitialCredentials(ctx context.Context, id string) (string, string, error) { func (d *driverOCI) GetInstanceInitialCredentials(ctx context.Context, id string) (string, string, error) {
credentials, err := d.computeClient.GetWindowsInstanceInitialCredentials(ctx, core.GetWindowsInstanceInitialCredentialsRequest{ credentials, err := d.computeClient.GetWindowsInstanceInitialCredentials(ctx, core.GetWindowsInstanceInitialCredentialsRequest{
InstanceId: &id, InstanceId: &id,
RequestMetadata: requestMetadata,
}) })
if err != nil { if err != nil {
return "", "", err return "", "", err
@@ -256,8 +204,7 @@ func (d *driverOCI) GetInstanceInitialCredentials(ctx context.Context, id string
// TerminateInstance terminates a compute instance. // TerminateInstance terminates a compute instance.
func (d *driverOCI) TerminateInstance(ctx context.Context, id string) error { func (d *driverOCI) TerminateInstance(ctx context.Context, id string) error {
_, err := d.computeClient.TerminateInstance(ctx, core.TerminateInstanceRequest{ _, err := d.computeClient.TerminateInstance(ctx, core.TerminateInstanceRequest{
InstanceId: &id, InstanceId: &id,
RequestMetadata: requestMetadata,
}) })
return err return err
} }
@@ -267,10 +214,7 @@ func (d *driverOCI) TerminateInstance(ctx context.Context, id string) error {
func (d *driverOCI) WaitForImageCreation(ctx context.Context, id string) error { func (d *driverOCI) WaitForImageCreation(ctx context.Context, id string) error {
return waitForResourceToReachState( return waitForResourceToReachState(
func(string) (string, error) { func(string) (string, error) {
image, err := d.computeClient.GetImage(ctx, core.GetImageRequest{ image, err := d.computeClient.GetImage(ctx, core.GetImageRequest{ImageId: &id})
ImageId: &id,
RequestMetadata: requestMetadata,
})
if err != nil { if err != nil {
return "", err return "", err
} }
@@ -289,10 +233,7 @@ func (d *driverOCI) WaitForImageCreation(ctx context.Context, id string) error {
func (d *driverOCI) WaitForInstanceState(ctx context.Context, id string, waitStates []string, terminalState string) error { func (d *driverOCI) WaitForInstanceState(ctx context.Context, id string, waitStates []string, terminalState string) error {
return waitForResourceToReachState( return waitForResourceToReachState(
func(string) (string, error) { func(string) (string, error) {
instance, err := d.computeClient.GetInstance(ctx, core.GetInstanceRequest{ instance, err := d.computeClient.GetInstance(ctx, core.GetInstanceRequest{InstanceId: &id})
InstanceId: &id,
RequestMetadata: requestMetadata,
})
if err != nil { if err != nil {
return "", err return "", err
} }
@@ -3,8 +3,6 @@ package oci
import ( import (
"crypto/rand" "crypto/rand"
"crypto/rsa" "crypto/rsa"
"github.com/oracle/oci-go-sdk/v36/common"
) )
// Mock struct to be used during testing to obtain Instance Principals. // Mock struct to be used during testing to obtain Instance Principals.
@@ -34,10 +32,3 @@ func (p instancePrincipalConfigurationProviderMock) KeyFingerprint() (string, er
func (p instancePrincipalConfigurationProviderMock) Region() (string, error) { func (p instancePrincipalConfigurationProviderMock) Region() (string, error) {
return "some_random_region", nil return "some_random_region", nil
} }
func (p instancePrincipalConfigurationProviderMock) AuthType() (common.AuthConfig, error) {
return common.AuthConfig{
AuthType: common.InstancePrincipal,
IsFromConfigFile: false,
OboToken: nil}, nil
}
-3
View File
@@ -32,9 +32,6 @@ func (s *stepCreateOMI) Run(ctx context.Context, state multistep.StateBag) multi
ImageName: omiName, ImageName: omiName,
BlockDeviceMappings: config.BlockDevices.BuildOscOMIDevices(), BlockDeviceMappings: config.BlockDevices.BuildOscOMIDevices(),
} }
if config.OMIDescription != "" {
createOpts.Description = config.OMIDescription
}
resp, _, err := oscconn.ImageApi.CreateImage(context.Background(), &osc.CreateImageOpts{ resp, _, err := oscconn.ImageApi.CreateImage(context.Background(), &osc.CreateImageOpts{
CreateImageRequest: optional.NewInterface(createOpts), CreateImageRequest: optional.NewInterface(createOpts),
@@ -37,9 +37,6 @@ func (s *StepRegisterOMI) Run(ctx context.Context, state multistep.StateBag) mul
BlockDeviceMappings: blockDevices, BlockDeviceMappings: blockDevices,
} }
if config.OMIDescription != "" {
registerOpts.Description = config.OMIDescription
}
registerResp, _, err := oscconn.ImageApi.CreateImage(context.Background(), &osc.CreateImageOpts{ registerResp, _, err := oscconn.ImageApi.CreateImage(context.Background(), &osc.CreateImageOpts{
CreateImageRequest: optional.NewInterface(registerOpts), CreateImageRequest: optional.NewInterface(registerOpts),
}) })
-4
View File
@@ -75,10 +75,6 @@ func (s *StepCreateOMI) Run(ctx context.Context, state multistep.StateBag) multi
registerOpts = buildRegisterOpts(config, image, newMappings) registerOpts = buildRegisterOpts(config, image, newMappings)
} }
if config.OMIDescription != "" {
registerOpts.Description = config.OMIDescription
}
registerResp, _, err := osconn.ImageApi.CreateImage(context.Background(), &osc.CreateImageOpts{ registerResp, _, err := osconn.ImageApi.CreateImage(context.Background(), &osc.CreateImageOpts{
CreateImageRequest: optional.NewInterface(registerOpts), CreateImageRequest: optional.NewInterface(registerOpts),
}) })
+7 -2
View File
@@ -209,7 +209,12 @@ func (b *Builder) Run(ctx context.Context, ui packersdk.Ui, hook packersdk.Hook)
Directories: b.config.FloppyConfig.FloppyDirectories, Directories: b.config.FloppyConfig.FloppyDirectories,
Label: b.config.FloppyConfig.FloppyLabel, Label: b.config.FloppyConfig.FloppyLabel,
}, },
commonsteps.HTTPServerFromHTTPConfig(&b.config.HTTPConfig), &commonsteps.StepHTTPServer{
HTTPDir: b.config.HTTPDir,
HTTPPortMin: b.config.HTTPPortMin,
HTTPPortMax: b.config.HTTPPortMax,
HTTPAddress: b.config.HTTPAddress,
},
new(stepCreateVM), new(stepCreateVM),
new(stepCreateDisk), new(stepCreateDisk),
new(stepSetBootOrder), new(stepSetBootOrder),
@@ -233,7 +238,7 @@ func (b *Builder) Run(ctx context.Context, ui packersdk.Ui, hook packersdk.Hook)
}, },
&communicator.StepConnect{ &communicator.StepConnect{
Config: &b.config.SSHConfig.Comm, Config: &b.config.SSHConfig.Comm,
Host: parallelscommon.CommHost(b.config.SSHConfig.Comm.Host()), Host: parallelscommon.CommHost(b.config.SSHConfig.Comm.SSHHost),
SSHConfig: b.config.SSHConfig.Comm.SSHConfigFunc(), SSHConfig: b.config.SSHConfig.Comm.SSHConfigFunc(),
}, },
&parallelscommon.StepUploadVersion{ &parallelscommon.StepUploadVersion{
@@ -19,7 +19,6 @@ type FlatConfig struct {
PackerUserVars map[string]string `mapstructure:"packer_user_variables" cty:"packer_user_variables" hcl:"packer_user_variables"` PackerUserVars map[string]string `mapstructure:"packer_user_variables" cty:"packer_user_variables" hcl:"packer_user_variables"`
PackerSensitiveVars []string `mapstructure:"packer_sensitive_variables" cty:"packer_sensitive_variables" hcl:"packer_sensitive_variables"` PackerSensitiveVars []string `mapstructure:"packer_sensitive_variables" cty:"packer_sensitive_variables" hcl:"packer_sensitive_variables"`
HTTPDir *string `mapstructure:"http_directory" cty:"http_directory" hcl:"http_directory"` HTTPDir *string `mapstructure:"http_directory" cty:"http_directory" hcl:"http_directory"`
HTTPContent map[string]string `mapstructure:"http_content" cty:"http_content" hcl:"http_content"`
HTTPPortMin *int `mapstructure:"http_port_min" cty:"http_port_min" hcl:"http_port_min"` HTTPPortMin *int `mapstructure:"http_port_min" cty:"http_port_min" hcl:"http_port_min"`
HTTPPortMax *int `mapstructure:"http_port_max" cty:"http_port_max" hcl:"http_port_max"` HTTPPortMax *int `mapstructure:"http_port_max" cty:"http_port_max" hcl:"http_port_max"`
HTTPAddress *string `mapstructure:"http_bind_address" cty:"http_bind_address" hcl:"http_bind_address"` HTTPAddress *string `mapstructure:"http_bind_address" cty:"http_bind_address" hcl:"http_bind_address"`
@@ -127,7 +126,6 @@ func (*FlatConfig) HCL2Spec() map[string]hcldec.Spec {
"packer_user_variables": &hcldec.AttrSpec{Name: "packer_user_variables", Type: cty.Map(cty.String), Required: false}, "packer_user_variables": &hcldec.AttrSpec{Name: "packer_user_variables", Type: cty.Map(cty.String), Required: false},
"packer_sensitive_variables": &hcldec.AttrSpec{Name: "packer_sensitive_variables", Type: cty.List(cty.String), Required: false}, "packer_sensitive_variables": &hcldec.AttrSpec{Name: "packer_sensitive_variables", Type: cty.List(cty.String), Required: false},
"http_directory": &hcldec.AttrSpec{Name: "http_directory", Type: cty.String, Required: false}, "http_directory": &hcldec.AttrSpec{Name: "http_directory", Type: cty.String, Required: false},
"http_content": &hcldec.AttrSpec{Name: "http_content", Type: cty.Map(cty.String), Required: false},
"http_port_min": &hcldec.AttrSpec{Name: "http_port_min", Type: cty.Number, Required: false}, "http_port_min": &hcldec.AttrSpec{Name: "http_port_min", Type: cty.Number, Required: false},
"http_port_max": &hcldec.AttrSpec{Name: "http_port_max", Type: cty.Number, Required: false}, "http_port_max": &hcldec.AttrSpec{Name: "http_port_max", Type: cty.Number, Required: false},
"http_bind_address": &hcldec.AttrSpec{Name: "http_bind_address", Type: cty.String, Required: false}, "http_bind_address": &hcldec.AttrSpec{Name: "http_bind_address", Type: cty.String, Required: false},
+1 -1
View File
@@ -87,7 +87,7 @@ func (b *Builder) Run(ctx context.Context, ui packersdk.Ui, hook packersdk.Hook)
}, },
&communicator.StepConnect{ &communicator.StepConnect{
Config: &b.config.SSHConfig.Comm, Config: &b.config.SSHConfig.Comm,
Host: parallelscommon.CommHost(b.config.SSHConfig.Comm.Host()), Host: parallelscommon.CommHost(b.config.SSHConfig.Comm.SSHHost),
SSHConfig: b.config.SSHConfig.Comm.SSHConfigFunc(), SSHConfig: b.config.SSHConfig.Comm.SSHConfigFunc(),
}, },
&parallelscommon.StepUploadVersion{ &parallelscommon.StepUploadVersion{
+4 -9
View File
@@ -22,11 +22,8 @@ func (s *stepCreateServer) Run(ctx context.Context, state multistep.StateBag) mu
profitbricks.SetAuth(c.PBUsername, c.PBPassword) profitbricks.SetAuth(c.PBUsername, c.PBPassword)
profitbricks.SetDepth("5") profitbricks.SetDepth("5")
if c.Comm.SSHPublicKey != nil { if sshkey, ok := state.GetOk("publicKey"); ok {
c.SSHKey = string(c.Comm.SSHPublicKey) c.SSHKey = sshkey.(string)
} else {
ui.Error("No ssh private key set; ssh authentication won't be possible. Please specify your private key in the ssh_private_key_file configuration key.")
return multistep.ActionHalt
} }
ui.Say("Creating Virtual Data Center...") ui.Say("Creating Virtual Data Center...")
img := s.getImageId(c.Image, c) img := s.getImageId(c.Image, c)
@@ -207,7 +204,7 @@ func (d *stepCreateServer) setPB(username string, password string, url string) {
func (d *stepCreateServer) checkForErrors(instance profitbricks.Resp) error { func (d *stepCreateServer) checkForErrors(instance profitbricks.Resp) error {
if instance.StatusCode > 299 { if instance.StatusCode > 299 {
return fmt.Errorf("Error occurred %s", string(instance.Body)) return errors.New(fmt.Sprintf("Error occurred %s", string(instance.Body)))
} }
return nil return nil
} }
@@ -264,9 +261,7 @@ func (d *stepCreateServer) getImageAlias(imageAlias string, location string, ui
func parseErrorMessage(raw string) (toreturn string) { func parseErrorMessage(raw string) (toreturn string) {
var tmp map[string]interface{} var tmp map[string]interface{}
if json.Unmarshal([]byte(raw), &tmp) != nil { json.Unmarshal([]byte(raw), &tmp)
return ""
}
for _, v := range tmp["messages"].([]interface{}) { for _, v := range tmp["messages"].([]interface{}) {
for index, i := range v.(map[string]interface{}) { for index, i := range v.(map[string]interface{}) {
+7 -125
View File
@@ -3,9 +3,6 @@ package profitbricks
import ( import (
"context" "context"
"encoding/json" "encoding/json"
"errors"
"fmt"
"strings"
"time" "time"
"github.com/hashicorp/packer-plugin-sdk/multistep" "github.com/hashicorp/packer-plugin-sdk/multistep"
@@ -25,32 +22,9 @@ func (s *stepTakeSnapshot) Run(ctx context.Context, state multistep.StateBag) mu
dcId := state.Get("datacenter_id").(string) dcId := state.Get("datacenter_id").(string)
volumeId := state.Get("volume_id").(string) volumeId := state.Get("volume_id").(string)
serverId := state.Get("instance_id").(string)
comm, _ := state.Get("communicator").(packersdk.Communicator)
if comm == nil {
ui.Error("no communicator found")
return multistep.ActionHalt
}
/* sync fs changes from the provisioning step */
os, err := s.getOs(dcId, serverId)
if err != nil {
ui.Error(fmt.Sprintf("an error occurred while getting the server os: %s", err.Error()))
return multistep.ActionHalt
}
ui.Say(fmt.Sprintf("Server OS is %s", os))
switch strings.ToLower(os) {
case "linux":
ui.Say("syncing file system changes")
if err := s.syncFs(ctx, comm); err != nil {
ui.Error(fmt.Sprintf("error syncing fs changes: %s", err.Error()))
return multistep.ActionHalt
}
}
snapshot := profitbricks.CreateSnapshot(dcId, volumeId, c.SnapshotName, "") snapshot := profitbricks.CreateSnapshot(dcId, volumeId, c.SnapshotName, "")
state.Put("snapshotname", c.SnapshotName) state.Put("snapshotname", c.SnapshotName)
if snapshot.StatusCode > 299 { if snapshot.StatusCode > 299 {
@@ -68,123 +42,31 @@ func (s *stepTakeSnapshot) Run(ctx context.Context, state multistep.StateBag) mu
return multistep.ActionHalt return multistep.ActionHalt
} }
ui.Say(fmt.Sprintf("Creating a snapshot for %s/volumes/%s", dcId, volumeId)) s.waitTillProvisioned(snapshot.Headers.Get("Location"), *c)
err = s.waitForRequest(snapshot.Headers.Get("Location"), *c, ui)
if err != nil {
ui.Error(fmt.Sprintf("An error occurred while waiting for the request to be done: %s", err.Error()))
return multistep.ActionHalt
}
err = s.waitTillSnapshotAvailable(snapshot.Id, *c, ui)
if err != nil {
ui.Error(fmt.Sprintf("An error occurred while waiting for the snapshot to be created: %s", err.Error()))
return multistep.ActionHalt
}
return multistep.ActionContinue return multistep.ActionContinue
} }
func (s *stepTakeSnapshot) Cleanup(_ multistep.StateBag) { func (s *stepTakeSnapshot) Cleanup(state multistep.StateBag) {
} }
func (s *stepTakeSnapshot) waitForRequest(path string, config Config, ui packersdk.Ui) error { func (d *stepTakeSnapshot) waitTillProvisioned(path string, config Config) {
d.setPB(config.PBUsername, config.PBPassword, config.PBUrl)
ui.Say(fmt.Sprintf("Watching request %s", path))
s.setPB(config.PBUsername, config.PBPassword, config.PBUrl)
waitCount := 50 waitCount := 50
var waitInterval = 10 * time.Second
if config.Retries > 0 { if config.Retries > 0 {
waitCount = config.Retries waitCount = config.Retries
} }
done := false
for i := 0; i < waitCount; i++ { for i := 0; i < waitCount; i++ {
request := profitbricks.GetRequestStatus(path) request := profitbricks.GetRequestStatus(path)
ui.Say(fmt.Sprintf("request status = %s", request.Metadata.Status))
if request.Metadata.Status == "DONE" { if request.Metadata.Status == "DONE" {
done = true
break break
} }
if request.Metadata.Status == "FAILED" { time.Sleep(10 * time.Second)
return fmt.Errorf("Request failed: %s", request.Response)
}
time.Sleep(waitInterval)
i++ i++
} }
if done == false {
return fmt.Errorf("request not fulfilled after waiting %d seconds",
int64(waitCount)*int64(waitInterval)/int64(time.Second))
}
return nil
} }
func (s *stepTakeSnapshot) waitTillSnapshotAvailable(id string, config Config, ui packersdk.Ui) error { func (d *stepTakeSnapshot) setPB(username string, password string, url string) {
s.setPB(config.PBUsername, config.PBPassword, config.PBUrl)
waitCount := 50
var waitInterval = 10 * time.Second
if config.Retries > 0 {
waitCount = config.Retries
}
done := false
ui.Say(fmt.Sprintf("waiting for snapshot %s to become available", id))
for i := 0; i < waitCount; i++ {
snap := profitbricks.GetSnapshot(id)
ui.Say(fmt.Sprintf("snapshot status = %s", snap.Metadata.State))
if snap.StatusCode != 200 {
return fmt.Errorf("%s", snap.Response)
}
if snap.Metadata.State == "AVAILABLE" {
done = true
break
}
time.Sleep(waitInterval)
i++
ui.Say(fmt.Sprintf("... still waiting, %d seconds have passed", int64(waitInterval)*int64(i)))
}
if done == false {
return fmt.Errorf("snapshot not created after waiting %d seconds",
int64(waitCount)*int64(waitInterval)/int64(time.Second))
}
ui.Say("snapshot created")
return nil
}
func (s *stepTakeSnapshot) syncFs(ctx context.Context, comm packersdk.Communicator) error {
cmd := &packersdk.RemoteCmd{
Command: "sync",
}
if err := comm.Start(ctx, cmd); err != nil {
return err
}
if cmd.Wait() != 0 {
return fmt.Errorf("sync command exited with code %d", cmd.ExitStatus())
}
return nil
}
func (s *stepTakeSnapshot) getOs(dcId string, serverId string) (string, error) {
server := profitbricks.GetServer(dcId, serverId)
if server.StatusCode != 200 {
return "", errors.New(server.Response)
}
if server.Properties.BootVolume == nil {
return "", errors.New("no boot volume found on server")
}
volumeId := server.Properties.BootVolume.Id
volume := profitbricks.GetVolume(dcId, volumeId)
if volume.StatusCode != 200 {
return "", errors.New(volume.Response)
}
return volume.Properties.LicenceType, nil
}
func (s *stepTakeSnapshot) setPB(username string, password string, url string) {
profitbricks.SetAuth(username, password) profitbricks.SetAuth(username, password)
profitbricks.SetEndpoint(url) profitbricks.SetEndpoint(url)
} }
+96 -100
View File
@@ -11,103 +11,101 @@ import (
// FlatConfig is an auto-generated flat version of Config. // FlatConfig is an auto-generated flat version of Config.
// Where the contents of a field with a `mapstructure:,squash` tag are bubbled up. // Where the contents of a field with a `mapstructure:,squash` tag are bubbled up.
type FlatConfig struct { type FlatConfig struct {
PackerBuildName *string `mapstructure:"packer_build_name" cty:"packer_build_name" hcl:"packer_build_name"` PackerBuildName *string `mapstructure:"packer_build_name" cty:"packer_build_name" hcl:"packer_build_name"`
PackerBuilderType *string `mapstructure:"packer_builder_type" cty:"packer_builder_type" hcl:"packer_builder_type"` PackerBuilderType *string `mapstructure:"packer_builder_type" cty:"packer_builder_type" hcl:"packer_builder_type"`
PackerCoreVersion *string `mapstructure:"packer_core_version" cty:"packer_core_version" hcl:"packer_core_version"` PackerCoreVersion *string `mapstructure:"packer_core_version" cty:"packer_core_version" hcl:"packer_core_version"`
PackerDebug *bool `mapstructure:"packer_debug" cty:"packer_debug" hcl:"packer_debug"` PackerDebug *bool `mapstructure:"packer_debug" cty:"packer_debug" hcl:"packer_debug"`
PackerForce *bool `mapstructure:"packer_force" cty:"packer_force" hcl:"packer_force"` PackerForce *bool `mapstructure:"packer_force" cty:"packer_force" hcl:"packer_force"`
PackerOnError *string `mapstructure:"packer_on_error" cty:"packer_on_error" hcl:"packer_on_error"` PackerOnError *string `mapstructure:"packer_on_error" cty:"packer_on_error" hcl:"packer_on_error"`
PackerUserVars map[string]string `mapstructure:"packer_user_variables" cty:"packer_user_variables" hcl:"packer_user_variables"` PackerUserVars map[string]string `mapstructure:"packer_user_variables" cty:"packer_user_variables" hcl:"packer_user_variables"`
PackerSensitiveVars []string `mapstructure:"packer_sensitive_variables" cty:"packer_sensitive_variables" hcl:"packer_sensitive_variables"` PackerSensitiveVars []string `mapstructure:"packer_sensitive_variables" cty:"packer_sensitive_variables" hcl:"packer_sensitive_variables"`
HTTPDir *string `mapstructure:"http_directory" cty:"http_directory" hcl:"http_directory"` HTTPDir *string `mapstructure:"http_directory" cty:"http_directory" hcl:"http_directory"`
HTTPContent map[string]string `mapstructure:"http_content" cty:"http_content" hcl:"http_content"` HTTPPortMin *int `mapstructure:"http_port_min" cty:"http_port_min" hcl:"http_port_min"`
HTTPPortMin *int `mapstructure:"http_port_min" cty:"http_port_min" hcl:"http_port_min"` HTTPPortMax *int `mapstructure:"http_port_max" cty:"http_port_max" hcl:"http_port_max"`
HTTPPortMax *int `mapstructure:"http_port_max" cty:"http_port_max" hcl:"http_port_max"` HTTPAddress *string `mapstructure:"http_bind_address" cty:"http_bind_address" hcl:"http_bind_address"`
HTTPAddress *string `mapstructure:"http_bind_address" cty:"http_bind_address" hcl:"http_bind_address"` HTTPInterface *string `mapstructure:"http_interface" undocumented:"true" cty:"http_interface" hcl:"http_interface"`
HTTPInterface *string `mapstructure:"http_interface" undocumented:"true" cty:"http_interface" hcl:"http_interface"` BootGroupInterval *string `mapstructure:"boot_keygroup_interval" cty:"boot_keygroup_interval" hcl:"boot_keygroup_interval"`
BootGroupInterval *string `mapstructure:"boot_keygroup_interval" cty:"boot_keygroup_interval" hcl:"boot_keygroup_interval"` BootWait *string `mapstructure:"boot_wait" cty:"boot_wait" hcl:"boot_wait"`
BootWait *string `mapstructure:"boot_wait" cty:"boot_wait" hcl:"boot_wait"` BootCommand []string `mapstructure:"boot_command" cty:"boot_command" hcl:"boot_command"`
BootCommand []string `mapstructure:"boot_command" cty:"boot_command" hcl:"boot_command"` BootKeyInterval *string `mapstructure:"boot_key_interval" cty:"boot_key_interval" hcl:"boot_key_interval"`
BootKeyInterval *string `mapstructure:"boot_key_interval" cty:"boot_key_interval" hcl:"boot_key_interval"` Type *string `mapstructure:"communicator" cty:"communicator" hcl:"communicator"`
Type *string `mapstructure:"communicator" cty:"communicator" hcl:"communicator"` PauseBeforeConnect *string `mapstructure:"pause_before_connecting" cty:"pause_before_connecting" hcl:"pause_before_connecting"`
PauseBeforeConnect *string `mapstructure:"pause_before_connecting" cty:"pause_before_connecting" hcl:"pause_before_connecting"` SSHHost *string `mapstructure:"ssh_host" cty:"ssh_host" hcl:"ssh_host"`
SSHHost *string `mapstructure:"ssh_host" cty:"ssh_host" hcl:"ssh_host"` SSHPort *int `mapstructure:"ssh_port" cty:"ssh_port" hcl:"ssh_port"`
SSHPort *int `mapstructure:"ssh_port" cty:"ssh_port" hcl:"ssh_port"` SSHUsername *string `mapstructure:"ssh_username" cty:"ssh_username" hcl:"ssh_username"`
SSHUsername *string `mapstructure:"ssh_username" cty:"ssh_username" hcl:"ssh_username"` SSHPassword *string `mapstructure:"ssh_password" cty:"ssh_password" hcl:"ssh_password"`
SSHPassword *string `mapstructure:"ssh_password" cty:"ssh_password" hcl:"ssh_password"` SSHKeyPairName *string `mapstructure:"ssh_keypair_name" undocumented:"true" cty:"ssh_keypair_name" hcl:"ssh_keypair_name"`
SSHKeyPairName *string `mapstructure:"ssh_keypair_name" undocumented:"true" cty:"ssh_keypair_name" hcl:"ssh_keypair_name"` SSHTemporaryKeyPairName *string `mapstructure:"temporary_key_pair_name" undocumented:"true" cty:"temporary_key_pair_name" hcl:"temporary_key_pair_name"`
SSHTemporaryKeyPairName *string `mapstructure:"temporary_key_pair_name" undocumented:"true" cty:"temporary_key_pair_name" hcl:"temporary_key_pair_name"` SSHTemporaryKeyPairType *string `mapstructure:"temporary_key_pair_type" cty:"temporary_key_pair_type" hcl:"temporary_key_pair_type"`
SSHTemporaryKeyPairType *string `mapstructure:"temporary_key_pair_type" cty:"temporary_key_pair_type" hcl:"temporary_key_pair_type"` SSHTemporaryKeyPairBits *int `mapstructure:"temporary_key_pair_bits" cty:"temporary_key_pair_bits" hcl:"temporary_key_pair_bits"`
SSHTemporaryKeyPairBits *int `mapstructure:"temporary_key_pair_bits" cty:"temporary_key_pair_bits" hcl:"temporary_key_pair_bits"` SSHCiphers []string `mapstructure:"ssh_ciphers" cty:"ssh_ciphers" hcl:"ssh_ciphers"`
SSHCiphers []string `mapstructure:"ssh_ciphers" cty:"ssh_ciphers" hcl:"ssh_ciphers"` SSHClearAuthorizedKeys *bool `mapstructure:"ssh_clear_authorized_keys" cty:"ssh_clear_authorized_keys" hcl:"ssh_clear_authorized_keys"`
SSHClearAuthorizedKeys *bool `mapstructure:"ssh_clear_authorized_keys" cty:"ssh_clear_authorized_keys" hcl:"ssh_clear_authorized_keys"` SSHKEXAlgos []string `mapstructure:"ssh_key_exchange_algorithms" cty:"ssh_key_exchange_algorithms" hcl:"ssh_key_exchange_algorithms"`
SSHKEXAlgos []string `mapstructure:"ssh_key_exchange_algorithms" cty:"ssh_key_exchange_algorithms" hcl:"ssh_key_exchange_algorithms"` SSHPrivateKeyFile *string `mapstructure:"ssh_private_key_file" undocumented:"true" cty:"ssh_private_key_file" hcl:"ssh_private_key_file"`
SSHPrivateKeyFile *string `mapstructure:"ssh_private_key_file" undocumented:"true" cty:"ssh_private_key_file" hcl:"ssh_private_key_file"` SSHCertificateFile *string `mapstructure:"ssh_certificate_file" cty:"ssh_certificate_file" hcl:"ssh_certificate_file"`
SSHCertificateFile *string `mapstructure:"ssh_certificate_file" cty:"ssh_certificate_file" hcl:"ssh_certificate_file"` SSHPty *bool `mapstructure:"ssh_pty" cty:"ssh_pty" hcl:"ssh_pty"`
SSHPty *bool `mapstructure:"ssh_pty" cty:"ssh_pty" hcl:"ssh_pty"` SSHTimeout *string `mapstructure:"ssh_timeout" cty:"ssh_timeout" hcl:"ssh_timeout"`
SSHTimeout *string `mapstructure:"ssh_timeout" cty:"ssh_timeout" hcl:"ssh_timeout"` SSHWaitTimeout *string `mapstructure:"ssh_wait_timeout" undocumented:"true" cty:"ssh_wait_timeout" hcl:"ssh_wait_timeout"`
SSHWaitTimeout *string `mapstructure:"ssh_wait_timeout" undocumented:"true" cty:"ssh_wait_timeout" hcl:"ssh_wait_timeout"` SSHAgentAuth *bool `mapstructure:"ssh_agent_auth" undocumented:"true" cty:"ssh_agent_auth" hcl:"ssh_agent_auth"`
SSHAgentAuth *bool `mapstructure:"ssh_agent_auth" undocumented:"true" cty:"ssh_agent_auth" hcl:"ssh_agent_auth"` SSHDisableAgentForwarding *bool `mapstructure:"ssh_disable_agent_forwarding" cty:"ssh_disable_agent_forwarding" hcl:"ssh_disable_agent_forwarding"`
SSHDisableAgentForwarding *bool `mapstructure:"ssh_disable_agent_forwarding" cty:"ssh_disable_agent_forwarding" hcl:"ssh_disable_agent_forwarding"` SSHHandshakeAttempts *int `mapstructure:"ssh_handshake_attempts" cty:"ssh_handshake_attempts" hcl:"ssh_handshake_attempts"`
SSHHandshakeAttempts *int `mapstructure:"ssh_handshake_attempts" cty:"ssh_handshake_attempts" hcl:"ssh_handshake_attempts"` SSHBastionHost *string `mapstructure:"ssh_bastion_host" cty:"ssh_bastion_host" hcl:"ssh_bastion_host"`
SSHBastionHost *string `mapstructure:"ssh_bastion_host" cty:"ssh_bastion_host" hcl:"ssh_bastion_host"` SSHBastionPort *int `mapstructure:"ssh_bastion_port" cty:"ssh_bastion_port" hcl:"ssh_bastion_port"`
SSHBastionPort *int `mapstructure:"ssh_bastion_port" cty:"ssh_bastion_port" hcl:"ssh_bastion_port"` SSHBastionAgentAuth *bool `mapstructure:"ssh_bastion_agent_auth" cty:"ssh_bastion_agent_auth" hcl:"ssh_bastion_agent_auth"`
SSHBastionAgentAuth *bool `mapstructure:"ssh_bastion_agent_auth" cty:"ssh_bastion_agent_auth" hcl:"ssh_bastion_agent_auth"` SSHBastionUsername *string `mapstructure:"ssh_bastion_username" cty:"ssh_bastion_username" hcl:"ssh_bastion_username"`
SSHBastionUsername *string `mapstructure:"ssh_bastion_username" cty:"ssh_bastion_username" hcl:"ssh_bastion_username"` SSHBastionPassword *string `mapstructure:"ssh_bastion_password" cty:"ssh_bastion_password" hcl:"ssh_bastion_password"`
SSHBastionPassword *string `mapstructure:"ssh_bastion_password" cty:"ssh_bastion_password" hcl:"ssh_bastion_password"` SSHBastionInteractive *bool `mapstructure:"ssh_bastion_interactive" cty:"ssh_bastion_interactive" hcl:"ssh_bastion_interactive"`
SSHBastionInteractive *bool `mapstructure:"ssh_bastion_interactive" cty:"ssh_bastion_interactive" hcl:"ssh_bastion_interactive"` SSHBastionPrivateKeyFile *string `mapstructure:"ssh_bastion_private_key_file" cty:"ssh_bastion_private_key_file" hcl:"ssh_bastion_private_key_file"`
SSHBastionPrivateKeyFile *string `mapstructure:"ssh_bastion_private_key_file" cty:"ssh_bastion_private_key_file" hcl:"ssh_bastion_private_key_file"` SSHBastionCertificateFile *string `mapstructure:"ssh_bastion_certificate_file" cty:"ssh_bastion_certificate_file" hcl:"ssh_bastion_certificate_file"`
SSHBastionCertificateFile *string `mapstructure:"ssh_bastion_certificate_file" cty:"ssh_bastion_certificate_file" hcl:"ssh_bastion_certificate_file"` SSHFileTransferMethod *string `mapstructure:"ssh_file_transfer_method" cty:"ssh_file_transfer_method" hcl:"ssh_file_transfer_method"`
SSHFileTransferMethod *string `mapstructure:"ssh_file_transfer_method" cty:"ssh_file_transfer_method" hcl:"ssh_file_transfer_method"` SSHProxyHost *string `mapstructure:"ssh_proxy_host" cty:"ssh_proxy_host" hcl:"ssh_proxy_host"`
SSHProxyHost *string `mapstructure:"ssh_proxy_host" cty:"ssh_proxy_host" hcl:"ssh_proxy_host"` SSHProxyPort *int `mapstructure:"ssh_proxy_port" cty:"ssh_proxy_port" hcl:"ssh_proxy_port"`
SSHProxyPort *int `mapstructure:"ssh_proxy_port" cty:"ssh_proxy_port" hcl:"ssh_proxy_port"` SSHProxyUsername *string `mapstructure:"ssh_proxy_username" cty:"ssh_proxy_username" hcl:"ssh_proxy_username"`
SSHProxyUsername *string `mapstructure:"ssh_proxy_username" cty:"ssh_proxy_username" hcl:"ssh_proxy_username"` SSHProxyPassword *string `mapstructure:"ssh_proxy_password" cty:"ssh_proxy_password" hcl:"ssh_proxy_password"`
SSHProxyPassword *string `mapstructure:"ssh_proxy_password" cty:"ssh_proxy_password" hcl:"ssh_proxy_password"` SSHKeepAliveInterval *string `mapstructure:"ssh_keep_alive_interval" cty:"ssh_keep_alive_interval" hcl:"ssh_keep_alive_interval"`
SSHKeepAliveInterval *string `mapstructure:"ssh_keep_alive_interval" cty:"ssh_keep_alive_interval" hcl:"ssh_keep_alive_interval"` SSHReadWriteTimeout *string `mapstructure:"ssh_read_write_timeout" cty:"ssh_read_write_timeout" hcl:"ssh_read_write_timeout"`
SSHReadWriteTimeout *string `mapstructure:"ssh_read_write_timeout" cty:"ssh_read_write_timeout" hcl:"ssh_read_write_timeout"` SSHRemoteTunnels []string `mapstructure:"ssh_remote_tunnels" cty:"ssh_remote_tunnels" hcl:"ssh_remote_tunnels"`
SSHRemoteTunnels []string `mapstructure:"ssh_remote_tunnels" cty:"ssh_remote_tunnels" hcl:"ssh_remote_tunnels"` SSHLocalTunnels []string `mapstructure:"ssh_local_tunnels" cty:"ssh_local_tunnels" hcl:"ssh_local_tunnels"`
SSHLocalTunnels []string `mapstructure:"ssh_local_tunnels" cty:"ssh_local_tunnels" hcl:"ssh_local_tunnels"` SSHPublicKey []byte `mapstructure:"ssh_public_key" undocumented:"true" cty:"ssh_public_key" hcl:"ssh_public_key"`
SSHPublicKey []byte `mapstructure:"ssh_public_key" undocumented:"true" cty:"ssh_public_key" hcl:"ssh_public_key"` SSHPrivateKey []byte `mapstructure:"ssh_private_key" undocumented:"true" cty:"ssh_private_key" hcl:"ssh_private_key"`
SSHPrivateKey []byte `mapstructure:"ssh_private_key" undocumented:"true" cty:"ssh_private_key" hcl:"ssh_private_key"` WinRMUser *string `mapstructure:"winrm_username" cty:"winrm_username" hcl:"winrm_username"`
WinRMUser *string `mapstructure:"winrm_username" cty:"winrm_username" hcl:"winrm_username"` WinRMPassword *string `mapstructure:"winrm_password" cty:"winrm_password" hcl:"winrm_password"`
WinRMPassword *string `mapstructure:"winrm_password" cty:"winrm_password" hcl:"winrm_password"` WinRMHost *string `mapstructure:"winrm_host" cty:"winrm_host" hcl:"winrm_host"`
WinRMHost *string `mapstructure:"winrm_host" cty:"winrm_host" hcl:"winrm_host"` WinRMNoProxy *bool `mapstructure:"winrm_no_proxy" cty:"winrm_no_proxy" hcl:"winrm_no_proxy"`
WinRMNoProxy *bool `mapstructure:"winrm_no_proxy" cty:"winrm_no_proxy" hcl:"winrm_no_proxy"` WinRMPort *int `mapstructure:"winrm_port" cty:"winrm_port" hcl:"winrm_port"`
WinRMPort *int `mapstructure:"winrm_port" cty:"winrm_port" hcl:"winrm_port"` WinRMTimeout *string `mapstructure:"winrm_timeout" cty:"winrm_timeout" hcl:"winrm_timeout"`
WinRMTimeout *string `mapstructure:"winrm_timeout" cty:"winrm_timeout" hcl:"winrm_timeout"` WinRMUseSSL *bool `mapstructure:"winrm_use_ssl" cty:"winrm_use_ssl" hcl:"winrm_use_ssl"`
WinRMUseSSL *bool `mapstructure:"winrm_use_ssl" cty:"winrm_use_ssl" hcl:"winrm_use_ssl"` WinRMInsecure *bool `mapstructure:"winrm_insecure" cty:"winrm_insecure" hcl:"winrm_insecure"`
WinRMInsecure *bool `mapstructure:"winrm_insecure" cty:"winrm_insecure" hcl:"winrm_insecure"` WinRMUseNTLM *bool `mapstructure:"winrm_use_ntlm" cty:"winrm_use_ntlm" hcl:"winrm_use_ntlm"`
WinRMUseNTLM *bool `mapstructure:"winrm_use_ntlm" cty:"winrm_use_ntlm" hcl:"winrm_use_ntlm"` ProxmoxURLRaw *string `mapstructure:"proxmox_url" cty:"proxmox_url" hcl:"proxmox_url"`
ProxmoxURLRaw *string `mapstructure:"proxmox_url" cty:"proxmox_url" hcl:"proxmox_url"` SkipCertValidation *bool `mapstructure:"insecure_skip_tls_verify" cty:"insecure_skip_tls_verify" hcl:"insecure_skip_tls_verify"`
SkipCertValidation *bool `mapstructure:"insecure_skip_tls_verify" cty:"insecure_skip_tls_verify" hcl:"insecure_skip_tls_verify"` Username *string `mapstructure:"username" cty:"username" hcl:"username"`
Username *string `mapstructure:"username" cty:"username" hcl:"username"` Password *string `mapstructure:"password" cty:"password" hcl:"password"`
Password *string `mapstructure:"password" cty:"password" hcl:"password"` Node *string `mapstructure:"node" cty:"node" hcl:"node"`
Token *string `mapstructure:"token" cty:"token" hcl:"token"` Pool *string `mapstructure:"pool" cty:"pool" hcl:"pool"`
Node *string `mapstructure:"node" cty:"node" hcl:"node"` VMName *string `mapstructure:"vm_name" cty:"vm_name" hcl:"vm_name"`
Pool *string `mapstructure:"pool" cty:"pool" hcl:"pool"` VMID *int `mapstructure:"vm_id" cty:"vm_id" hcl:"vm_id"`
VMName *string `mapstructure:"vm_name" cty:"vm_name" hcl:"vm_name"` Boot *string `mapstructure:"boot" cty:"boot" hcl:"boot"`
VMID *int `mapstructure:"vm_id" cty:"vm_id" hcl:"vm_id"` Memory *int `mapstructure:"memory" cty:"memory" hcl:"memory"`
Boot *string `mapstructure:"boot" cty:"boot" hcl:"boot"` Cores *int `mapstructure:"cores" cty:"cores" hcl:"cores"`
Memory *int `mapstructure:"memory" cty:"memory" hcl:"memory"` CPUType *string `mapstructure:"cpu_type" cty:"cpu_type" hcl:"cpu_type"`
Cores *int `mapstructure:"cores" cty:"cores" hcl:"cores"` Sockets *int `mapstructure:"sockets" cty:"sockets" hcl:"sockets"`
CPUType *string `mapstructure:"cpu_type" cty:"cpu_type" hcl:"cpu_type"` OS *string `mapstructure:"os" cty:"os" hcl:"os"`
Sockets *int `mapstructure:"sockets" cty:"sockets" hcl:"sockets"` VGA *proxmox.FlatvgaConfig `mapstructure:"vga" cty:"vga" hcl:"vga"`
OS *string `mapstructure:"os" cty:"os" hcl:"os"` NICs []proxmox.FlatnicConfig `mapstructure:"network_adapters" cty:"network_adapters" hcl:"network_adapters"`
VGA *proxmox.FlatvgaConfig `mapstructure:"vga" cty:"vga" hcl:"vga"` Disks []proxmox.FlatdiskConfig `mapstructure:"disks" cty:"disks" hcl:"disks"`
NICs []proxmox.FlatnicConfig `mapstructure:"network_adapters" cty:"network_adapters" hcl:"network_adapters"` Agent *bool `mapstructure:"qemu_agent" cty:"qemu_agent" hcl:"qemu_agent"`
Disks []proxmox.FlatdiskConfig `mapstructure:"disks" cty:"disks" hcl:"disks"` SCSIController *string `mapstructure:"scsi_controller" cty:"scsi_controller" hcl:"scsi_controller"`
Agent *bool `mapstructure:"qemu_agent" cty:"qemu_agent" hcl:"qemu_agent"` Onboot *bool `mapstructure:"onboot" cty:"onboot" hcl:"onboot"`
SCSIController *string `mapstructure:"scsi_controller" cty:"scsi_controller" hcl:"scsi_controller"` DisableKVM *bool `mapstructure:"disable_kvm" cty:"disable_kvm" hcl:"disable_kvm"`
Onboot *bool `mapstructure:"onboot" cty:"onboot" hcl:"onboot"` TemplateName *string `mapstructure:"template_name" cty:"template_name" hcl:"template_name"`
DisableKVM *bool `mapstructure:"disable_kvm" cty:"disable_kvm" hcl:"disable_kvm"` TemplateDescription *string `mapstructure:"template_description" cty:"template_description" hcl:"template_description"`
TemplateName *string `mapstructure:"template_name" cty:"template_name" hcl:"template_name"` CloudInit *bool `mapstructure:"cloud_init" cty:"cloud_init" hcl:"cloud_init"`
TemplateDescription *string `mapstructure:"template_description" cty:"template_description" hcl:"template_description"` CloudInitStoragePool *string `mapstructure:"cloud_init_storage_pool" cty:"cloud_init_storage_pool" hcl:"cloud_init_storage_pool"`
CloudInit *bool `mapstructure:"cloud_init" cty:"cloud_init" hcl:"cloud_init"` AdditionalISOFiles []proxmox.FlatstorageConfig `mapstructure:"additional_iso_files" cty:"additional_iso_files" hcl:"additional_iso_files"`
CloudInitStoragePool *string `mapstructure:"cloud_init_storage_pool" cty:"cloud_init_storage_pool" hcl:"cloud_init_storage_pool"` VMInterface *string `mapstructure:"vm_interface" cty:"vm_interface" hcl:"vm_interface"`
AdditionalISOFiles []proxmox.FlatadditionalISOsConfig `mapstructure:"additional_iso_files" cty:"additional_iso_files" hcl:"additional_iso_files"` CloneVM *string `mapstructure:"clone_vm" cty:"clone_vm" hcl:"clone_vm"`
VMInterface *string `mapstructure:"vm_interface" cty:"vm_interface" hcl:"vm_interface"` FullClone *bool `mapstructure:"full_clone" required:"false" cty:"full_clone" hcl:"full_clone"`
CloneVM *string `mapstructure:"clone_vm" cty:"clone_vm" hcl:"clone_vm"`
FullClone *bool `mapstructure:"full_clone" required:"false" cty:"full_clone" hcl:"full_clone"`
} }
// FlatMapstructure returns a new FlatConfig. // FlatMapstructure returns a new FlatConfig.
@@ -131,7 +129,6 @@ func (*FlatConfig) HCL2Spec() map[string]hcldec.Spec {
"packer_user_variables": &hcldec.AttrSpec{Name: "packer_user_variables", Type: cty.Map(cty.String), Required: false}, "packer_user_variables": &hcldec.AttrSpec{Name: "packer_user_variables", Type: cty.Map(cty.String), Required: false},
"packer_sensitive_variables": &hcldec.AttrSpec{Name: "packer_sensitive_variables", Type: cty.List(cty.String), Required: false}, "packer_sensitive_variables": &hcldec.AttrSpec{Name: "packer_sensitive_variables", Type: cty.List(cty.String), Required: false},
"http_directory": &hcldec.AttrSpec{Name: "http_directory", Type: cty.String, Required: false}, "http_directory": &hcldec.AttrSpec{Name: "http_directory", Type: cty.String, Required: false},
"http_content": &hcldec.AttrSpec{Name: "http_content", Type: cty.Map(cty.String), Required: false},
"http_port_min": &hcldec.AttrSpec{Name: "http_port_min", Type: cty.Number, Required: false}, "http_port_min": &hcldec.AttrSpec{Name: "http_port_min", Type: cty.Number, Required: false},
"http_port_max": &hcldec.AttrSpec{Name: "http_port_max", Type: cty.Number, Required: false}, "http_port_max": &hcldec.AttrSpec{Name: "http_port_max", Type: cty.Number, Required: false},
"http_bind_address": &hcldec.AttrSpec{Name: "http_bind_address", Type: cty.String, Required: false}, "http_bind_address": &hcldec.AttrSpec{Name: "http_bind_address", Type: cty.String, Required: false},
@@ -193,7 +190,6 @@ func (*FlatConfig) HCL2Spec() map[string]hcldec.Spec {
"insecure_skip_tls_verify": &hcldec.AttrSpec{Name: "insecure_skip_tls_verify", Type: cty.Bool, Required: false}, "insecure_skip_tls_verify": &hcldec.AttrSpec{Name: "insecure_skip_tls_verify", Type: cty.Bool, Required: false},
"username": &hcldec.AttrSpec{Name: "username", Type: cty.String, Required: false}, "username": &hcldec.AttrSpec{Name: "username", Type: cty.String, Required: false},
"password": &hcldec.AttrSpec{Name: "password", Type: cty.String, Required: false}, "password": &hcldec.AttrSpec{Name: "password", Type: cty.String, Required: false},
"token": &hcldec.AttrSpec{Name: "token", Type: cty.String, Required: false},
"node": &hcldec.AttrSpec{Name: "node", Type: cty.String, Required: false}, "node": &hcldec.AttrSpec{Name: "node", Type: cty.String, Required: false},
"pool": &hcldec.AttrSpec{Name: "pool", Type: cty.String, Required: false}, "pool": &hcldec.AttrSpec{Name: "pool", Type: cty.String, Required: false},
"vm_name": &hcldec.AttrSpec{Name: "vm_name", Type: cty.String, Required: false}, "vm_name": &hcldec.AttrSpec{Name: "vm_name", Type: cty.String, Required: false},
@@ -215,7 +211,7 @@ func (*FlatConfig) HCL2Spec() map[string]hcldec.Spec {
"template_description": &hcldec.AttrSpec{Name: "template_description", Type: cty.String, Required: false}, "template_description": &hcldec.AttrSpec{Name: "template_description", Type: cty.String, Required: false},
"cloud_init": &hcldec.AttrSpec{Name: "cloud_init", Type: cty.Bool, Required: false}, "cloud_init": &hcldec.AttrSpec{Name: "cloud_init", Type: cty.Bool, Required: false},
"cloud_init_storage_pool": &hcldec.AttrSpec{Name: "cloud_init_storage_pool", Type: cty.String, Required: false}, "cloud_init_storage_pool": &hcldec.AttrSpec{Name: "cloud_init_storage_pool", Type: cty.String, Required: false},
"additional_iso_files": &hcldec.BlockListSpec{TypeName: "additional_iso_files", Nested: hcldec.ObjectSpec((*proxmox.FlatadditionalISOsConfig)(nil).HCL2Spec())}, "additional_iso_files": &hcldec.BlockListSpec{TypeName: "additional_iso_files", Nested: hcldec.ObjectSpec((*proxmox.FlatstorageConfig)(nil).HCL2Spec())},
"vm_interface": &hcldec.AttrSpec{Name: "vm_interface", Type: cty.String, Required: false}, "vm_interface": &hcldec.AttrSpec{Name: "vm_interface", Type: cty.String, Required: false},
"clone_vm": &hcldec.AttrSpec{Name: "clone_vm", Type: cty.String, Required: false}, "clone_vm": &hcldec.AttrSpec{Name: "clone_vm", Type: cty.String, Required: false},
"full_clone": &hcldec.AttrSpec{Name: "full_clone", Type: cty.Bool, Required: false}, "full_clone": &hcldec.AttrSpec{Name: "full_clone", Type: cty.Bool, Required: false},
+17 -16
View File
@@ -2,6 +2,7 @@ package proxmox
import ( import (
"context" "context"
"crypto/tls"
"errors" "errors"
"fmt" "fmt"
@@ -34,7 +35,15 @@ type Builder struct {
func (b *Builder) Run(ctx context.Context, ui packersdk.Ui, hook packersdk.Hook, state multistep.StateBag) (packersdk.Artifact, error) { func (b *Builder) Run(ctx context.Context, ui packersdk.Ui, hook packersdk.Hook, state multistep.StateBag) (packersdk.Artifact, error) {
var err error var err error
b.proxmoxClient, err = newProxmoxClient(b.config) tlsConfig := &tls.Config{
InsecureSkipVerify: b.config.SkipCertValidation,
}
b.proxmoxClient, err = proxmox.NewClient(b.config.proxmoxURL.String(), nil, tlsConfig)
if err != nil {
return nil, err
}
err = b.proxmoxClient.Login(b.config.Username, b.config.Password, "")
if err != nil { if err != nil {
return nil, err return nil, err
} }
@@ -52,7 +61,12 @@ func (b *Builder) Run(ctx context.Context, ui packersdk.Ui, hook packersdk.Hook,
&stepStartVM{ &stepStartVM{
vmCreator: b.vmCreator, vmCreator: b.vmCreator,
}, },
commonsteps.HTTPServerFromHTTPConfig(&b.config.HTTPConfig), &commonsteps.StepHTTPServer{
HTTPDir: b.config.HTTPDir,
HTTPPortMin: b.config.HTTPPortMin,
HTTPPortMax: b.config.HTTPPortMax,
HTTPAddress: b.config.HTTPAddress,
},
&stepTypeBootCommand{ &stepTypeBootCommand{
BootConfig: b.config.BootConfig, BootConfig: b.config.BootConfig,
Ctx: b.config.Ctx, Ctx: b.config.Ctx,
@@ -70,20 +84,7 @@ func (b *Builder) Run(ctx context.Context, ui packersdk.Ui, hook packersdk.Hook,
&stepFinalizeTemplateConfig{}, &stepFinalizeTemplateConfig{},
&stepSuccess{}, &stepSuccess{},
} }
preSteps := b.preSteps steps := append(b.preSteps, coreSteps...)
for idx := range b.config.AdditionalISOFiles {
preSteps = append(preSteps, &commonsteps.StepDownload{
Checksum: b.config.AdditionalISOFiles[idx].ISOChecksum,
Description: "additional ISO",
Extension: b.config.AdditionalISOFiles[idx].TargetExtension,
ResultKey: b.config.AdditionalISOFiles[idx].DownloadPathKey,
TargetPath: b.config.AdditionalISOFiles[idx].DownloadPathKey,
Url: b.config.AdditionalISOFiles[idx].ISOUrls,
})
}
preSteps = append(preSteps, &stepUploadAdditionalISOs{})
steps := append(preSteps, coreSteps...)
steps = append(steps, b.postSteps...) steps = append(steps, b.postSteps...)
// Run the steps // Run the steps
b.runner = commonsteps.NewRunner(steps, b.config.PackerConfig, ui) b.runner = commonsteps.NewRunner(steps, b.config.PackerConfig, ui)
-37
View File
@@ -1,37 +0,0 @@
package proxmox
import (
"crypto/tls"
"log"
"time"
"github.com/Telmate/proxmox-api-go/proxmox"
)
const defaultTaskTimeout = 30 * time.Second
func newProxmoxClient(config Config) (*proxmox.Client, error) {
tlsConfig := &tls.Config{
InsecureSkipVerify: config.SkipCertValidation,
}
client, err := proxmox.NewClient(config.proxmoxURL.String(), nil, tlsConfig, int(defaultTaskTimeout.Seconds()))
if err != nil {
return nil, err
}
if config.Token != "" {
// configure token auth
log.Print("using token auth")
client.SetAPIToken(config.Username, config.Token)
} else {
// fallback to login if not using tokens
log.Print("using password auth")
err = client.Login(config.Username, config.Password, "")
if err != nil {
return nil, err
}
}
return client, nil
}
-91
View File
@@ -1,91 +0,0 @@
package proxmox
import (
"encoding/json"
"io/ioutil"
"net/http"
"net/http/httptest"
"net/url"
"testing"
"github.com/Telmate/proxmox-api-go/proxmox"
"github.com/stretchr/testify/require"
)
func TestTokenAuth(t *testing.T) {
mockAPI := httptest.NewServer(http.HandlerFunc(func(rw http.ResponseWriter, req *http.Request) {
if req.Header.Get("Authorization") != "PVEAPIToken=dummy@vmhost!test-token=ac5293bf-15e2-477f-b04c-a6dfa7a46b80" {
rw.WriteHeader(http.StatusUnauthorized)
return
}
}))
defer mockAPI.Close()
pmURL, _ := url.Parse(mockAPI.URL)
config := Config{
proxmoxURL: pmURL,
SkipCertValidation: false,
Username: "dummy@vmhost!test-token",
Password: "not-used",
Token: "ac5293bf-15e2-477f-b04c-a6dfa7a46b80",
}
client, err := newProxmoxClient(config)
require.NoError(t, err)
ref := proxmox.NewVmRef(110)
ref.SetNode("node1")
ref.SetVmType("qemu")
err = client.Sendkey(ref, "ping")
require.NoError(t, err)
}
func TestLogin(t *testing.T) {
mockAPI := httptest.NewServer(http.HandlerFunc(func(rw http.ResponseWriter, req *http.Request) {
// mock ticketing api
if req.Method == http.MethodPost && req.URL.Path == "/access/ticket" {
body, _ := ioutil.ReadAll(req.Body)
values, _ := url.ParseQuery(string(body))
user := values.Get("username")
pass := values.Get("password")
if user != "dummy@vmhost" || pass != "correct-horse-battery-staple" {
rw.WriteHeader(http.StatusUnauthorized)
return
}
_ = json.NewEncoder(rw).Encode(map[string]interface{}{
"data": map[string]string{
"username": user,
"ticket": "dummy-ticket",
"CSRFPreventionToken": "random-token",
},
})
return
}
// validate ticket
if val, err := req.Cookie("PVEAuthCookie"); err != nil || val.Value != "dummy-ticket" {
rw.WriteHeader(http.StatusUnauthorized)
return
}
}))
defer mockAPI.Close()
pmURL, _ := url.Parse(mockAPI.URL)
config := Config{
proxmoxURL: pmURL,
SkipCertValidation: false,
Username: "dummy@vmhost",
Password: "correct-horse-battery-staple",
Token: "",
}
client, err := newProxmoxClient(config)
require.NoError(t, err)
ref := proxmox.NewVmRef(110)
ref.SetNode("node1")
ref.SetVmType("qemu")
err = client.Sendkey(ref, "ping")
require.NoError(t, err)
}
+8 -65
View File
@@ -1,4 +1,4 @@
//go:generate mapstructure-to-hcl2 -type Config,nicConfig,diskConfig,vgaConfig,additionalISOsConfig //go:generate mapstructure-to-hcl2 -type Config,nicConfig,diskConfig,vgaConfig,storageConfig
package proxmox package proxmox
@@ -8,7 +8,6 @@ import (
"log" "log"
"net/url" "net/url"
"os" "os"
"strconv"
"strings" "strings"
"time" "time"
@@ -35,7 +34,6 @@ type Config struct {
SkipCertValidation bool `mapstructure:"insecure_skip_tls_verify"` SkipCertValidation bool `mapstructure:"insecure_skip_tls_verify"`
Username string `mapstructure:"username"` Username string `mapstructure:"username"`
Password string `mapstructure:"password"` Password string `mapstructure:"password"`
Token string `mapstructure:"token"`
Node string `mapstructure:"node"` Node string `mapstructure:"node"`
Pool string `mapstructure:"pool"` Pool string `mapstructure:"pool"`
@@ -62,20 +60,20 @@ type Config struct {
CloudInit bool `mapstructure:"cloud_init"` CloudInit bool `mapstructure:"cloud_init"`
CloudInitStoragePool string `mapstructure:"cloud_init_storage_pool"` CloudInitStoragePool string `mapstructure:"cloud_init_storage_pool"`
AdditionalISOFiles []additionalISOsConfig `mapstructure:"additional_iso_files"` AdditionalISOFiles []storageConfig `mapstructure:"additional_iso_files"`
VMInterface string `mapstructure:"vm_interface"` VMInterface string `mapstructure:"vm_interface"`
Ctx interpolate.Context `mapstructure-to-hcl2:",skip"` Ctx interpolate.Context `mapstructure-to-hcl2:",skip"`
} }
type additionalISOsConfig struct { type storageConfig struct {
commonsteps.ISOConfig `mapstructure:",squash"` commonsteps.ISOConfig `mapstructure:",squash"`
Device string `mapstructure:"device"` Device string `mapstructure:"device"`
ISOFile string `mapstructure:"iso_file"` ISOFile string `mapstructure:"iso_file"`
ISOStoragePool string `mapstructure:"iso_storage_pool"` ISOStoragePool string `mapstructure:"iso_storage_pool"`
Unmount bool `mapstructure:"unmount"` Unmount bool `mapstructure:"unmount"`
ShouldUploadISO bool `mapstructure-to-hcl2:",skip"` ShouldUploadISO bool
DownloadPathKey string `mapstructure-to-hcl2:",skip"` DownloadPathKey string
} }
type nicConfig struct { type nicConfig struct {
@@ -136,9 +134,6 @@ func (c *Config) Prepare(upper interface{}, raws ...interface{}) ([]string, []st
if c.Password == "" { if c.Password == "" {
c.Password = os.Getenv("PROXMOX_PASSWORD") c.Password = os.Getenv("PROXMOX_PASSWORD")
} }
if c.Token == "" {
c.Token = os.Getenv("PROXMOX_TOKEN")
}
if c.BootKeyInterval == 0 && os.Getenv(bootcommand.PackerKeyEnv) != "" { if c.BootKeyInterval == 0 && os.Getenv(bootcommand.PackerKeyEnv) != "" {
var err error var err error
c.BootKeyInterval, err = time.ParseDuration(os.Getenv(bootcommand.PackerKeyEnv)) c.BootKeyInterval, err = time.ParseDuration(os.Getenv(bootcommand.PackerKeyEnv))
@@ -224,8 +219,8 @@ func (c *Config) Prepare(upper interface{}, raws ...interface{}) ([]string, []st
if c.Username == "" { if c.Username == "" {
errs = packersdk.MultiErrorAppend(errs, errors.New("username must be specified")) errs = packersdk.MultiErrorAppend(errs, errors.New("username must be specified"))
} }
if c.Password == "" && c.Token == "" { if c.Password == "" {
errs = packersdk.MultiErrorAppend(errs, errors.New("password or token must be specified")) errs = packersdk.MultiErrorAppend(errs, errors.New("password must be specified"))
} }
if c.ProxmoxURLRaw == "" { if c.ProxmoxURLRaw == "" {
errs = packersdk.MultiErrorAppend(errs, errors.New("proxmox_url must be specified")) errs = packersdk.MultiErrorAppend(errs, errors.New("proxmox_url must be specified"))
@@ -255,58 +250,6 @@ func (c *Config) Prepare(upper interface{}, raws ...interface{}) ([]string, []st
errs = packersdk.MultiErrorAppend(errs, fmt.Errorf("disks[%d].storage_pool_type must be specified", idx)) errs = packersdk.MultiErrorAppend(errs, fmt.Errorf("disks[%d].storage_pool_type must be specified", idx))
} }
} }
for idx := range c.AdditionalISOFiles {
// Check AdditionalISO config
// Either a pre-uploaded ISO should be referenced in iso_file, OR a URL
// (possibly to a local file) to an ISO file that will be downloaded and
// then uploaded to Proxmox.
if c.AdditionalISOFiles[idx].ISOFile != "" {
c.AdditionalISOFiles[idx].ShouldUploadISO = false
} else {
c.AdditionalISOFiles[idx].DownloadPathKey = "downloaded_additional_iso_path_" + strconv.Itoa(idx)
isoWarnings, isoErrors := c.AdditionalISOFiles[idx].ISOConfig.Prepare(&c.Ctx)
errs = packersdk.MultiErrorAppend(errs, isoErrors...)
warnings = append(warnings, isoWarnings...)
c.AdditionalISOFiles[idx].ShouldUploadISO = true
}
if c.AdditionalISOFiles[idx].Device == "" {
log.Printf("AdditionalISOFile %d Device not set, using default 'ide3'", idx)
c.AdditionalISOFiles[idx].Device = "ide3"
}
if strings.HasPrefix(c.AdditionalISOFiles[idx].Device, "ide") {
busnumber, err := strconv.Atoi(c.AdditionalISOFiles[idx].Device[3:])
if err != nil {
errs = packersdk.MultiErrorAppend(errs, fmt.Errorf("%s is not a valid bus index", c.AdditionalISOFiles[idx].Device[3:]))
}
if busnumber == 2 {
errs = packersdk.MultiErrorAppend(errs, fmt.Errorf("IDE bus 2 is used by boot ISO"))
}
if busnumber > 3 {
errs = packersdk.MultiErrorAppend(errs, fmt.Errorf("IDE bus index can't be higher than 3"))
}
}
if strings.HasPrefix(c.AdditionalISOFiles[idx].Device, "sata") {
busnumber, err := strconv.Atoi(c.AdditionalISOFiles[idx].Device[4:])
if err != nil {
errs = packersdk.MultiErrorAppend(errs, fmt.Errorf("%s is not a valid bus index", c.AdditionalISOFiles[idx].Device[4:]))
}
if busnumber > 5 {
errs = packersdk.MultiErrorAppend(errs, fmt.Errorf("SATA bus index can't be higher than 5"))
}
}
if strings.HasPrefix(c.AdditionalISOFiles[idx].Device, "scsi") {
busnumber, err := strconv.Atoi(c.AdditionalISOFiles[idx].Device[4:])
if err != nil {
errs = packersdk.MultiErrorAppend(errs, fmt.Errorf("%s is not a valid bus index", c.AdditionalISOFiles[idx].Device[4:]))
}
if busnumber > 30 {
errs = packersdk.MultiErrorAppend(errs, fmt.Errorf("SCSI bus index can't be higher than 30"))
}
}
if (c.AdditionalISOFiles[idx].ISOFile == "" && len(c.AdditionalISOFiles[idx].ISOConfig.ISOUrls) == 0) || (c.AdditionalISOFiles[idx].ISOFile != "" && len(c.AdditionalISOFiles[idx].ISOConfig.ISOUrls) != 0) {
errs = packersdk.MultiErrorAppend(errs, fmt.Errorf("either iso_file or iso_url, but not both, must be specified for AdditionalISO file %s", c.AdditionalISOFiles[idx].Device))
}
}
if errs != nil && len(errs.Errors) > 0 { if errs != nil && len(errs.Errors) > 0 {
return nil, warnings, errs return nil, warnings, errs
+138 -138
View File
@@ -1,4 +1,4 @@
// Code generated by "mapstructure-to-hcl2 -type Config,nicConfig,diskConfig,vgaConfig,additionalISOsConfig"; DO NOT EDIT. // Code generated by "mapstructure-to-hcl2 -type Config,nicConfig,diskConfig,vgaConfig,storageConfig"; DO NOT EDIT.
package proxmox package proxmox
@@ -10,101 +10,99 @@ import (
// FlatConfig is an auto-generated flat version of Config. // FlatConfig is an auto-generated flat version of Config.
// Where the contents of a field with a `mapstructure:,squash` tag are bubbled up. // Where the contents of a field with a `mapstructure:,squash` tag are bubbled up.
type FlatConfig struct { type FlatConfig struct {
PackerBuildName *string `mapstructure:"packer_build_name" cty:"packer_build_name" hcl:"packer_build_name"` PackerBuildName *string `mapstructure:"packer_build_name" cty:"packer_build_name" hcl:"packer_build_name"`
PackerBuilderType *string `mapstructure:"packer_builder_type" cty:"packer_builder_type" hcl:"packer_builder_type"` PackerBuilderType *string `mapstructure:"packer_builder_type" cty:"packer_builder_type" hcl:"packer_builder_type"`
PackerCoreVersion *string `mapstructure:"packer_core_version" cty:"packer_core_version" hcl:"packer_core_version"` PackerCoreVersion *string `mapstructure:"packer_core_version" cty:"packer_core_version" hcl:"packer_core_version"`
PackerDebug *bool `mapstructure:"packer_debug" cty:"packer_debug" hcl:"packer_debug"` PackerDebug *bool `mapstructure:"packer_debug" cty:"packer_debug" hcl:"packer_debug"`
PackerForce *bool `mapstructure:"packer_force" cty:"packer_force" hcl:"packer_force"` PackerForce *bool `mapstructure:"packer_force" cty:"packer_force" hcl:"packer_force"`
PackerOnError *string `mapstructure:"packer_on_error" cty:"packer_on_error" hcl:"packer_on_error"` PackerOnError *string `mapstructure:"packer_on_error" cty:"packer_on_error" hcl:"packer_on_error"`
PackerUserVars map[string]string `mapstructure:"packer_user_variables" cty:"packer_user_variables" hcl:"packer_user_variables"` PackerUserVars map[string]string `mapstructure:"packer_user_variables" cty:"packer_user_variables" hcl:"packer_user_variables"`
PackerSensitiveVars []string `mapstructure:"packer_sensitive_variables" cty:"packer_sensitive_variables" hcl:"packer_sensitive_variables"` PackerSensitiveVars []string `mapstructure:"packer_sensitive_variables" cty:"packer_sensitive_variables" hcl:"packer_sensitive_variables"`
HTTPDir *string `mapstructure:"http_directory" cty:"http_directory" hcl:"http_directory"` HTTPDir *string `mapstructure:"http_directory" cty:"http_directory" hcl:"http_directory"`
HTTPContent map[string]string `mapstructure:"http_content" cty:"http_content" hcl:"http_content"` HTTPPortMin *int `mapstructure:"http_port_min" cty:"http_port_min" hcl:"http_port_min"`
HTTPPortMin *int `mapstructure:"http_port_min" cty:"http_port_min" hcl:"http_port_min"` HTTPPortMax *int `mapstructure:"http_port_max" cty:"http_port_max" hcl:"http_port_max"`
HTTPPortMax *int `mapstructure:"http_port_max" cty:"http_port_max" hcl:"http_port_max"` HTTPAddress *string `mapstructure:"http_bind_address" cty:"http_bind_address" hcl:"http_bind_address"`
HTTPAddress *string `mapstructure:"http_bind_address" cty:"http_bind_address" hcl:"http_bind_address"` HTTPInterface *string `mapstructure:"http_interface" undocumented:"true" cty:"http_interface" hcl:"http_interface"`
HTTPInterface *string `mapstructure:"http_interface" undocumented:"true" cty:"http_interface" hcl:"http_interface"` BootGroupInterval *string `mapstructure:"boot_keygroup_interval" cty:"boot_keygroup_interval" hcl:"boot_keygroup_interval"`
BootGroupInterval *string `mapstructure:"boot_keygroup_interval" cty:"boot_keygroup_interval" hcl:"boot_keygroup_interval"` BootWait *string `mapstructure:"boot_wait" cty:"boot_wait" hcl:"boot_wait"`
BootWait *string `mapstructure:"boot_wait" cty:"boot_wait" hcl:"boot_wait"` BootCommand []string `mapstructure:"boot_command" cty:"boot_command" hcl:"boot_command"`
BootCommand []string `mapstructure:"boot_command" cty:"boot_command" hcl:"boot_command"` BootKeyInterval *string `mapstructure:"boot_key_interval" cty:"boot_key_interval" hcl:"boot_key_interval"`
BootKeyInterval *string `mapstructure:"boot_key_interval" cty:"boot_key_interval" hcl:"boot_key_interval"` Type *string `mapstructure:"communicator" cty:"communicator" hcl:"communicator"`
Type *string `mapstructure:"communicator" cty:"communicator" hcl:"communicator"` PauseBeforeConnect *string `mapstructure:"pause_before_connecting" cty:"pause_before_connecting" hcl:"pause_before_connecting"`
PauseBeforeConnect *string `mapstructure:"pause_before_connecting" cty:"pause_before_connecting" hcl:"pause_before_connecting"` SSHHost *string `mapstructure:"ssh_host" cty:"ssh_host" hcl:"ssh_host"`
SSHHost *string `mapstructure:"ssh_host" cty:"ssh_host" hcl:"ssh_host"` SSHPort *int `mapstructure:"ssh_port" cty:"ssh_port" hcl:"ssh_port"`
SSHPort *int `mapstructure:"ssh_port" cty:"ssh_port" hcl:"ssh_port"` SSHUsername *string `mapstructure:"ssh_username" cty:"ssh_username" hcl:"ssh_username"`
SSHUsername *string `mapstructure:"ssh_username" cty:"ssh_username" hcl:"ssh_username"` SSHPassword *string `mapstructure:"ssh_password" cty:"ssh_password" hcl:"ssh_password"`
SSHPassword *string `mapstructure:"ssh_password" cty:"ssh_password" hcl:"ssh_password"` SSHKeyPairName *string `mapstructure:"ssh_keypair_name" undocumented:"true" cty:"ssh_keypair_name" hcl:"ssh_keypair_name"`
SSHKeyPairName *string `mapstructure:"ssh_keypair_name" undocumented:"true" cty:"ssh_keypair_name" hcl:"ssh_keypair_name"` SSHTemporaryKeyPairName *string `mapstructure:"temporary_key_pair_name" undocumented:"true" cty:"temporary_key_pair_name" hcl:"temporary_key_pair_name"`
SSHTemporaryKeyPairName *string `mapstructure:"temporary_key_pair_name" undocumented:"true" cty:"temporary_key_pair_name" hcl:"temporary_key_pair_name"` SSHTemporaryKeyPairType *string `mapstructure:"temporary_key_pair_type" cty:"temporary_key_pair_type" hcl:"temporary_key_pair_type"`
SSHTemporaryKeyPairType *string `mapstructure:"temporary_key_pair_type" cty:"temporary_key_pair_type" hcl:"temporary_key_pair_type"` SSHTemporaryKeyPairBits *int `mapstructure:"temporary_key_pair_bits" cty:"temporary_key_pair_bits" hcl:"temporary_key_pair_bits"`
SSHTemporaryKeyPairBits *int `mapstructure:"temporary_key_pair_bits" cty:"temporary_key_pair_bits" hcl:"temporary_key_pair_bits"` SSHCiphers []string `mapstructure:"ssh_ciphers" cty:"ssh_ciphers" hcl:"ssh_ciphers"`
SSHCiphers []string `mapstructure:"ssh_ciphers" cty:"ssh_ciphers" hcl:"ssh_ciphers"` SSHClearAuthorizedKeys *bool `mapstructure:"ssh_clear_authorized_keys" cty:"ssh_clear_authorized_keys" hcl:"ssh_clear_authorized_keys"`
SSHClearAuthorizedKeys *bool `mapstructure:"ssh_clear_authorized_keys" cty:"ssh_clear_authorized_keys" hcl:"ssh_clear_authorized_keys"` SSHKEXAlgos []string `mapstructure:"ssh_key_exchange_algorithms" cty:"ssh_key_exchange_algorithms" hcl:"ssh_key_exchange_algorithms"`
SSHKEXAlgos []string `mapstructure:"ssh_key_exchange_algorithms" cty:"ssh_key_exchange_algorithms" hcl:"ssh_key_exchange_algorithms"` SSHPrivateKeyFile *string `mapstructure:"ssh_private_key_file" undocumented:"true" cty:"ssh_private_key_file" hcl:"ssh_private_key_file"`
SSHPrivateKeyFile *string `mapstructure:"ssh_private_key_file" undocumented:"true" cty:"ssh_private_key_file" hcl:"ssh_private_key_file"` SSHCertificateFile *string `mapstructure:"ssh_certificate_file" cty:"ssh_certificate_file" hcl:"ssh_certificate_file"`
SSHCertificateFile *string `mapstructure:"ssh_certificate_file" cty:"ssh_certificate_file" hcl:"ssh_certificate_file"` SSHPty *bool `mapstructure:"ssh_pty" cty:"ssh_pty" hcl:"ssh_pty"`
SSHPty *bool `mapstructure:"ssh_pty" cty:"ssh_pty" hcl:"ssh_pty"` SSHTimeout *string `mapstructure:"ssh_timeout" cty:"ssh_timeout" hcl:"ssh_timeout"`
SSHTimeout *string `mapstructure:"ssh_timeout" cty:"ssh_timeout" hcl:"ssh_timeout"` SSHWaitTimeout *string `mapstructure:"ssh_wait_timeout" undocumented:"true" cty:"ssh_wait_timeout" hcl:"ssh_wait_timeout"`
SSHWaitTimeout *string `mapstructure:"ssh_wait_timeout" undocumented:"true" cty:"ssh_wait_timeout" hcl:"ssh_wait_timeout"` SSHAgentAuth *bool `mapstructure:"ssh_agent_auth" undocumented:"true" cty:"ssh_agent_auth" hcl:"ssh_agent_auth"`
SSHAgentAuth *bool `mapstructure:"ssh_agent_auth" undocumented:"true" cty:"ssh_agent_auth" hcl:"ssh_agent_auth"` SSHDisableAgentForwarding *bool `mapstructure:"ssh_disable_agent_forwarding" cty:"ssh_disable_agent_forwarding" hcl:"ssh_disable_agent_forwarding"`
SSHDisableAgentForwarding *bool `mapstructure:"ssh_disable_agent_forwarding" cty:"ssh_disable_agent_forwarding" hcl:"ssh_disable_agent_forwarding"` SSHHandshakeAttempts *int `mapstructure:"ssh_handshake_attempts" cty:"ssh_handshake_attempts" hcl:"ssh_handshake_attempts"`
SSHHandshakeAttempts *int `mapstructure:"ssh_handshake_attempts" cty:"ssh_handshake_attempts" hcl:"ssh_handshake_attempts"` SSHBastionHost *string `mapstructure:"ssh_bastion_host" cty:"ssh_bastion_host" hcl:"ssh_bastion_host"`
SSHBastionHost *string `mapstructure:"ssh_bastion_host" cty:"ssh_bastion_host" hcl:"ssh_bastion_host"` SSHBastionPort *int `mapstructure:"ssh_bastion_port" cty:"ssh_bastion_port" hcl:"ssh_bastion_port"`
SSHBastionPort *int `mapstructure:"ssh_bastion_port" cty:"ssh_bastion_port" hcl:"ssh_bastion_port"` SSHBastionAgentAuth *bool `mapstructure:"ssh_bastion_agent_auth" cty:"ssh_bastion_agent_auth" hcl:"ssh_bastion_agent_auth"`
SSHBastionAgentAuth *bool `mapstructure:"ssh_bastion_agent_auth" cty:"ssh_bastion_agent_auth" hcl:"ssh_bastion_agent_auth"` SSHBastionUsername *string `mapstructure:"ssh_bastion_username" cty:"ssh_bastion_username" hcl:"ssh_bastion_username"`
SSHBastionUsername *string `mapstructure:"ssh_bastion_username" cty:"ssh_bastion_username" hcl:"ssh_bastion_username"` SSHBastionPassword *string `mapstructure:"ssh_bastion_password" cty:"ssh_bastion_password" hcl:"ssh_bastion_password"`
SSHBastionPassword *string `mapstructure:"ssh_bastion_password" cty:"ssh_bastion_password" hcl:"ssh_bastion_password"` SSHBastionInteractive *bool `mapstructure:"ssh_bastion_interactive" cty:"ssh_bastion_interactive" hcl:"ssh_bastion_interactive"`
SSHBastionInteractive *bool `mapstructure:"ssh_bastion_interactive" cty:"ssh_bastion_interactive" hcl:"ssh_bastion_interactive"` SSHBastionPrivateKeyFile *string `mapstructure:"ssh_bastion_private_key_file" cty:"ssh_bastion_private_key_file" hcl:"ssh_bastion_private_key_file"`
SSHBastionPrivateKeyFile *string `mapstructure:"ssh_bastion_private_key_file" cty:"ssh_bastion_private_key_file" hcl:"ssh_bastion_private_key_file"` SSHBastionCertificateFile *string `mapstructure:"ssh_bastion_certificate_file" cty:"ssh_bastion_certificate_file" hcl:"ssh_bastion_certificate_file"`
SSHBastionCertificateFile *string `mapstructure:"ssh_bastion_certificate_file" cty:"ssh_bastion_certificate_file" hcl:"ssh_bastion_certificate_file"` SSHFileTransferMethod *string `mapstructure:"ssh_file_transfer_method" cty:"ssh_file_transfer_method" hcl:"ssh_file_transfer_method"`
SSHFileTransferMethod *string `mapstructure:"ssh_file_transfer_method" cty:"ssh_file_transfer_method" hcl:"ssh_file_transfer_method"` SSHProxyHost *string `mapstructure:"ssh_proxy_host" cty:"ssh_proxy_host" hcl:"ssh_proxy_host"`
SSHProxyHost *string `mapstructure:"ssh_proxy_host" cty:"ssh_proxy_host" hcl:"ssh_proxy_host"` SSHProxyPort *int `mapstructure:"ssh_proxy_port" cty:"ssh_proxy_port" hcl:"ssh_proxy_port"`
SSHProxyPort *int `mapstructure:"ssh_proxy_port" cty:"ssh_proxy_port" hcl:"ssh_proxy_port"` SSHProxyUsername *string `mapstructure:"ssh_proxy_username" cty:"ssh_proxy_username" hcl:"ssh_proxy_username"`
SSHProxyUsername *string `mapstructure:"ssh_proxy_username" cty:"ssh_proxy_username" hcl:"ssh_proxy_username"` SSHProxyPassword *string `mapstructure:"ssh_proxy_password" cty:"ssh_proxy_password" hcl:"ssh_proxy_password"`
SSHProxyPassword *string `mapstructure:"ssh_proxy_password" cty:"ssh_proxy_password" hcl:"ssh_proxy_password"` SSHKeepAliveInterval *string `mapstructure:"ssh_keep_alive_interval" cty:"ssh_keep_alive_interval" hcl:"ssh_keep_alive_interval"`
SSHKeepAliveInterval *string `mapstructure:"ssh_keep_alive_interval" cty:"ssh_keep_alive_interval" hcl:"ssh_keep_alive_interval"` SSHReadWriteTimeout *string `mapstructure:"ssh_read_write_timeout" cty:"ssh_read_write_timeout" hcl:"ssh_read_write_timeout"`
SSHReadWriteTimeout *string `mapstructure:"ssh_read_write_timeout" cty:"ssh_read_write_timeout" hcl:"ssh_read_write_timeout"` SSHRemoteTunnels []string `mapstructure:"ssh_remote_tunnels" cty:"ssh_remote_tunnels" hcl:"ssh_remote_tunnels"`
SSHRemoteTunnels []string `mapstructure:"ssh_remote_tunnels" cty:"ssh_remote_tunnels" hcl:"ssh_remote_tunnels"` SSHLocalTunnels []string `mapstructure:"ssh_local_tunnels" cty:"ssh_local_tunnels" hcl:"ssh_local_tunnels"`
SSHLocalTunnels []string `mapstructure:"ssh_local_tunnels" cty:"ssh_local_tunnels" hcl:"ssh_local_tunnels"` SSHPublicKey []byte `mapstructure:"ssh_public_key" undocumented:"true" cty:"ssh_public_key" hcl:"ssh_public_key"`
SSHPublicKey []byte `mapstructure:"ssh_public_key" undocumented:"true" cty:"ssh_public_key" hcl:"ssh_public_key"` SSHPrivateKey []byte `mapstructure:"ssh_private_key" undocumented:"true" cty:"ssh_private_key" hcl:"ssh_private_key"`
SSHPrivateKey []byte `mapstructure:"ssh_private_key" undocumented:"true" cty:"ssh_private_key" hcl:"ssh_private_key"` WinRMUser *string `mapstructure:"winrm_username" cty:"winrm_username" hcl:"winrm_username"`
WinRMUser *string `mapstructure:"winrm_username" cty:"winrm_username" hcl:"winrm_username"` WinRMPassword *string `mapstructure:"winrm_password" cty:"winrm_password" hcl:"winrm_password"`
WinRMPassword *string `mapstructure:"winrm_password" cty:"winrm_password" hcl:"winrm_password"` WinRMHost *string `mapstructure:"winrm_host" cty:"winrm_host" hcl:"winrm_host"`
WinRMHost *string `mapstructure:"winrm_host" cty:"winrm_host" hcl:"winrm_host"` WinRMNoProxy *bool `mapstructure:"winrm_no_proxy" cty:"winrm_no_proxy" hcl:"winrm_no_proxy"`
WinRMNoProxy *bool `mapstructure:"winrm_no_proxy" cty:"winrm_no_proxy" hcl:"winrm_no_proxy"` WinRMPort *int `mapstructure:"winrm_port" cty:"winrm_port" hcl:"winrm_port"`
WinRMPort *int `mapstructure:"winrm_port" cty:"winrm_port" hcl:"winrm_port"` WinRMTimeout *string `mapstructure:"winrm_timeout" cty:"winrm_timeout" hcl:"winrm_timeout"`
WinRMTimeout *string `mapstructure:"winrm_timeout" cty:"winrm_timeout" hcl:"winrm_timeout"` WinRMUseSSL *bool `mapstructure:"winrm_use_ssl" cty:"winrm_use_ssl" hcl:"winrm_use_ssl"`
WinRMUseSSL *bool `mapstructure:"winrm_use_ssl" cty:"winrm_use_ssl" hcl:"winrm_use_ssl"` WinRMInsecure *bool `mapstructure:"winrm_insecure" cty:"winrm_insecure" hcl:"winrm_insecure"`
WinRMInsecure *bool `mapstructure:"winrm_insecure" cty:"winrm_insecure" hcl:"winrm_insecure"` WinRMUseNTLM *bool `mapstructure:"winrm_use_ntlm" cty:"winrm_use_ntlm" hcl:"winrm_use_ntlm"`
WinRMUseNTLM *bool `mapstructure:"winrm_use_ntlm" cty:"winrm_use_ntlm" hcl:"winrm_use_ntlm"` ProxmoxURLRaw *string `mapstructure:"proxmox_url" cty:"proxmox_url" hcl:"proxmox_url"`
ProxmoxURLRaw *string `mapstructure:"proxmox_url" cty:"proxmox_url" hcl:"proxmox_url"` SkipCertValidation *bool `mapstructure:"insecure_skip_tls_verify" cty:"insecure_skip_tls_verify" hcl:"insecure_skip_tls_verify"`
SkipCertValidation *bool `mapstructure:"insecure_skip_tls_verify" cty:"insecure_skip_tls_verify" hcl:"insecure_skip_tls_verify"` Username *string `mapstructure:"username" cty:"username" hcl:"username"`
Username *string `mapstructure:"username" cty:"username" hcl:"username"` Password *string `mapstructure:"password" cty:"password" hcl:"password"`
Password *string `mapstructure:"password" cty:"password" hcl:"password"` Node *string `mapstructure:"node" cty:"node" hcl:"node"`
Token *string `mapstructure:"token" cty:"token" hcl:"token"` Pool *string `mapstructure:"pool" cty:"pool" hcl:"pool"`
Node *string `mapstructure:"node" cty:"node" hcl:"node"` VMName *string `mapstructure:"vm_name" cty:"vm_name" hcl:"vm_name"`
Pool *string `mapstructure:"pool" cty:"pool" hcl:"pool"` VMID *int `mapstructure:"vm_id" cty:"vm_id" hcl:"vm_id"`
VMName *string `mapstructure:"vm_name" cty:"vm_name" hcl:"vm_name"` Boot *string `mapstructure:"boot" cty:"boot" hcl:"boot"`
VMID *int `mapstructure:"vm_id" cty:"vm_id" hcl:"vm_id"` Memory *int `mapstructure:"memory" cty:"memory" hcl:"memory"`
Boot *string `mapstructure:"boot" cty:"boot" hcl:"boot"` Cores *int `mapstructure:"cores" cty:"cores" hcl:"cores"`
Memory *int `mapstructure:"memory" cty:"memory" hcl:"memory"` CPUType *string `mapstructure:"cpu_type" cty:"cpu_type" hcl:"cpu_type"`
Cores *int `mapstructure:"cores" cty:"cores" hcl:"cores"` Sockets *int `mapstructure:"sockets" cty:"sockets" hcl:"sockets"`
CPUType *string `mapstructure:"cpu_type" cty:"cpu_type" hcl:"cpu_type"` OS *string `mapstructure:"os" cty:"os" hcl:"os"`
Sockets *int `mapstructure:"sockets" cty:"sockets" hcl:"sockets"` VGA *FlatvgaConfig `mapstructure:"vga" cty:"vga" hcl:"vga"`
OS *string `mapstructure:"os" cty:"os" hcl:"os"` NICs []FlatnicConfig `mapstructure:"network_adapters" cty:"network_adapters" hcl:"network_adapters"`
VGA *FlatvgaConfig `mapstructure:"vga" cty:"vga" hcl:"vga"` Disks []FlatdiskConfig `mapstructure:"disks" cty:"disks" hcl:"disks"`
NICs []FlatnicConfig `mapstructure:"network_adapters" cty:"network_adapters" hcl:"network_adapters"` Agent *bool `mapstructure:"qemu_agent" cty:"qemu_agent" hcl:"qemu_agent"`
Disks []FlatdiskConfig `mapstructure:"disks" cty:"disks" hcl:"disks"` SCSIController *string `mapstructure:"scsi_controller" cty:"scsi_controller" hcl:"scsi_controller"`
Agent *bool `mapstructure:"qemu_agent" cty:"qemu_agent" hcl:"qemu_agent"` Onboot *bool `mapstructure:"onboot" cty:"onboot" hcl:"onboot"`
SCSIController *string `mapstructure:"scsi_controller" cty:"scsi_controller" hcl:"scsi_controller"` DisableKVM *bool `mapstructure:"disable_kvm" cty:"disable_kvm" hcl:"disable_kvm"`
Onboot *bool `mapstructure:"onboot" cty:"onboot" hcl:"onboot"` TemplateName *string `mapstructure:"template_name" cty:"template_name" hcl:"template_name"`
DisableKVM *bool `mapstructure:"disable_kvm" cty:"disable_kvm" hcl:"disable_kvm"` TemplateDescription *string `mapstructure:"template_description" cty:"template_description" hcl:"template_description"`
TemplateName *string `mapstructure:"template_name" cty:"template_name" hcl:"template_name"` CloudInit *bool `mapstructure:"cloud_init" cty:"cloud_init" hcl:"cloud_init"`
TemplateDescription *string `mapstructure:"template_description" cty:"template_description" hcl:"template_description"` CloudInitStoragePool *string `mapstructure:"cloud_init_storage_pool" cty:"cloud_init_storage_pool" hcl:"cloud_init_storage_pool"`
CloudInit *bool `mapstructure:"cloud_init" cty:"cloud_init" hcl:"cloud_init"` AdditionalISOFiles []FlatstorageConfig `mapstructure:"additional_iso_files" cty:"additional_iso_files" hcl:"additional_iso_files"`
CloudInitStoragePool *string `mapstructure:"cloud_init_storage_pool" cty:"cloud_init_storage_pool" hcl:"cloud_init_storage_pool"` VMInterface *string `mapstructure:"vm_interface" cty:"vm_interface" hcl:"vm_interface"`
AdditionalISOFiles []FlatadditionalISOsConfig `mapstructure:"additional_iso_files" cty:"additional_iso_files" hcl:"additional_iso_files"`
VMInterface *string `mapstructure:"vm_interface" cty:"vm_interface" hcl:"vm_interface"`
} }
// FlatMapstructure returns a new FlatConfig. // FlatMapstructure returns a new FlatConfig.
@@ -128,7 +126,6 @@ func (*FlatConfig) HCL2Spec() map[string]hcldec.Spec {
"packer_user_variables": &hcldec.AttrSpec{Name: "packer_user_variables", Type: cty.Map(cty.String), Required: false}, "packer_user_variables": &hcldec.AttrSpec{Name: "packer_user_variables", Type: cty.Map(cty.String), Required: false},
"packer_sensitive_variables": &hcldec.AttrSpec{Name: "packer_sensitive_variables", Type: cty.List(cty.String), Required: false}, "packer_sensitive_variables": &hcldec.AttrSpec{Name: "packer_sensitive_variables", Type: cty.List(cty.String), Required: false},
"http_directory": &hcldec.AttrSpec{Name: "http_directory", Type: cty.String, Required: false}, "http_directory": &hcldec.AttrSpec{Name: "http_directory", Type: cty.String, Required: false},
"http_content": &hcldec.AttrSpec{Name: "http_content", Type: cty.Map(cty.String), Required: false},
"http_port_min": &hcldec.AttrSpec{Name: "http_port_min", Type: cty.Number, Required: false}, "http_port_min": &hcldec.AttrSpec{Name: "http_port_min", Type: cty.Number, Required: false},
"http_port_max": &hcldec.AttrSpec{Name: "http_port_max", Type: cty.Number, Required: false}, "http_port_max": &hcldec.AttrSpec{Name: "http_port_max", Type: cty.Number, Required: false},
"http_bind_address": &hcldec.AttrSpec{Name: "http_bind_address", Type: cty.String, Required: false}, "http_bind_address": &hcldec.AttrSpec{Name: "http_bind_address", Type: cty.String, Required: false},
@@ -190,7 +187,6 @@ func (*FlatConfig) HCL2Spec() map[string]hcldec.Spec {
"insecure_skip_tls_verify": &hcldec.AttrSpec{Name: "insecure_skip_tls_verify", Type: cty.Bool, Required: false}, "insecure_skip_tls_verify": &hcldec.AttrSpec{Name: "insecure_skip_tls_verify", Type: cty.Bool, Required: false},
"username": &hcldec.AttrSpec{Name: "username", Type: cty.String, Required: false}, "username": &hcldec.AttrSpec{Name: "username", Type: cty.String, Required: false},
"password": &hcldec.AttrSpec{Name: "password", Type: cty.String, Required: false}, "password": &hcldec.AttrSpec{Name: "password", Type: cty.String, Required: false},
"token": &hcldec.AttrSpec{Name: "token", Type: cty.String, Required: false},
"node": &hcldec.AttrSpec{Name: "node", Type: cty.String, Required: false}, "node": &hcldec.AttrSpec{Name: "node", Type: cty.String, Required: false},
"pool": &hcldec.AttrSpec{Name: "pool", Type: cty.String, Required: false}, "pool": &hcldec.AttrSpec{Name: "pool", Type: cty.String, Required: false},
"vm_name": &hcldec.AttrSpec{Name: "vm_name", Type: cty.String, Required: false}, "vm_name": &hcldec.AttrSpec{Name: "vm_name", Type: cty.String, Required: false},
@@ -212,51 +208,12 @@ func (*FlatConfig) HCL2Spec() map[string]hcldec.Spec {
"template_description": &hcldec.AttrSpec{Name: "template_description", Type: cty.String, Required: false}, "template_description": &hcldec.AttrSpec{Name: "template_description", Type: cty.String, Required: false},
"cloud_init": &hcldec.AttrSpec{Name: "cloud_init", Type: cty.Bool, Required: false}, "cloud_init": &hcldec.AttrSpec{Name: "cloud_init", Type: cty.Bool, Required: false},
"cloud_init_storage_pool": &hcldec.AttrSpec{Name: "cloud_init_storage_pool", Type: cty.String, Required: false}, "cloud_init_storage_pool": &hcldec.AttrSpec{Name: "cloud_init_storage_pool", Type: cty.String, Required: false},
"additional_iso_files": &hcldec.BlockListSpec{TypeName: "additional_iso_files", Nested: hcldec.ObjectSpec((*FlatadditionalISOsConfig)(nil).HCL2Spec())}, "additional_iso_files": &hcldec.BlockListSpec{TypeName: "additional_iso_files", Nested: hcldec.ObjectSpec((*FlatstorageConfig)(nil).HCL2Spec())},
"vm_interface": &hcldec.AttrSpec{Name: "vm_interface", Type: cty.String, Required: false}, "vm_interface": &hcldec.AttrSpec{Name: "vm_interface", Type: cty.String, Required: false},
} }
return s return s
} }
// FlatadditionalISOsConfig is an auto-generated flat version of additionalISOsConfig.
// Where the contents of a field with a `mapstructure:,squash` tag are bubbled up.
type FlatadditionalISOsConfig struct {
ISOChecksum *string `mapstructure:"iso_checksum" required:"true" cty:"iso_checksum" hcl:"iso_checksum"`
RawSingleISOUrl *string `mapstructure:"iso_url" required:"true" cty:"iso_url" hcl:"iso_url"`
ISOUrls []string `mapstructure:"iso_urls" cty:"iso_urls" hcl:"iso_urls"`
TargetPath *string `mapstructure:"iso_target_path" cty:"iso_target_path" hcl:"iso_target_path"`
TargetExtension *string `mapstructure:"iso_target_extension" cty:"iso_target_extension" hcl:"iso_target_extension"`
Device *string `mapstructure:"device" cty:"device" hcl:"device"`
ISOFile *string `mapstructure:"iso_file" cty:"iso_file" hcl:"iso_file"`
ISOStoragePool *string `mapstructure:"iso_storage_pool" cty:"iso_storage_pool" hcl:"iso_storage_pool"`
Unmount *bool `mapstructure:"unmount" cty:"unmount" hcl:"unmount"`
}
// FlatMapstructure returns a new FlatadditionalISOsConfig.
// FlatadditionalISOsConfig is an auto-generated flat version of additionalISOsConfig.
// Where the contents a fields with a `mapstructure:,squash` tag are bubbled up.
func (*additionalISOsConfig) FlatMapstructure() interface{ HCL2Spec() map[string]hcldec.Spec } {
return new(FlatadditionalISOsConfig)
}
// HCL2Spec returns the hcl spec of a additionalISOsConfig.
// This spec is used by HCL to read the fields of additionalISOsConfig.
// The decoded values from this spec will then be applied to a FlatadditionalISOsConfig.
func (*FlatadditionalISOsConfig) HCL2Spec() map[string]hcldec.Spec {
s := map[string]hcldec.Spec{
"iso_checksum": &hcldec.AttrSpec{Name: "iso_checksum", Type: cty.String, Required: false},
"iso_url": &hcldec.AttrSpec{Name: "iso_url", Type: cty.String, Required: false},
"iso_urls": &hcldec.AttrSpec{Name: "iso_urls", Type: cty.List(cty.String), Required: false},
"iso_target_path": &hcldec.AttrSpec{Name: "iso_target_path", Type: cty.String, Required: false},
"iso_target_extension": &hcldec.AttrSpec{Name: "iso_target_extension", Type: cty.String, Required: false},
"device": &hcldec.AttrSpec{Name: "device", Type: cty.String, Required: false},
"iso_file": &hcldec.AttrSpec{Name: "iso_file", Type: cty.String, Required: false},
"iso_storage_pool": &hcldec.AttrSpec{Name: "iso_storage_pool", Type: cty.String, Required: false},
"unmount": &hcldec.AttrSpec{Name: "unmount", Type: cty.Bool, Required: false},
}
return s
}
// FlatdiskConfig is an auto-generated flat version of diskConfig. // FlatdiskConfig is an auto-generated flat version of diskConfig.
// Where the contents of a field with a `mapstructure:,squash` tag are bubbled up. // Where the contents of a field with a `mapstructure:,squash` tag are bubbled up.
type FlatdiskConfig struct { type FlatdiskConfig struct {
@@ -325,6 +282,49 @@ func (*FlatnicConfig) HCL2Spec() map[string]hcldec.Spec {
return s return s
} }
// FlatstorageConfig is an auto-generated flat version of storageConfig.
// Where the contents of a field with a `mapstructure:,squash` tag are bubbled up.
type FlatstorageConfig struct {
ISOChecksum *string `mapstructure:"iso_checksum" required:"true" cty:"iso_checksum" hcl:"iso_checksum"`
RawSingleISOUrl *string `mapstructure:"iso_url" required:"true" cty:"iso_url" hcl:"iso_url"`
ISOUrls []string `mapstructure:"iso_urls" cty:"iso_urls" hcl:"iso_urls"`
TargetPath *string `mapstructure:"iso_target_path" cty:"iso_target_path" hcl:"iso_target_path"`
TargetExtension *string `mapstructure:"iso_target_extension" cty:"iso_target_extension" hcl:"iso_target_extension"`
Device *string `mapstructure:"device" cty:"device" hcl:"device"`
ISOFile *string `mapstructure:"iso_file" cty:"iso_file" hcl:"iso_file"`
ISOStoragePool *string `mapstructure:"iso_storage_pool" cty:"iso_storage_pool" hcl:"iso_storage_pool"`
Unmount *bool `mapstructure:"unmount" cty:"unmount" hcl:"unmount"`
ShouldUploadISO *bool `cty:"should_upload_iso" hcl:"should_upload_iso"`
DownloadPathKey *string `cty:"download_path_key" hcl:"download_path_key"`
}
// FlatMapstructure returns a new FlatstorageConfig.
// FlatstorageConfig is an auto-generated flat version of storageConfig.
// Where the contents a fields with a `mapstructure:,squash` tag are bubbled up.
func (*storageConfig) FlatMapstructure() interface{ HCL2Spec() map[string]hcldec.Spec } {
return new(FlatstorageConfig)
}
// HCL2Spec returns the hcl spec of a storageConfig.
// This spec is used by HCL to read the fields of storageConfig.
// The decoded values from this spec will then be applied to a FlatstorageConfig.
func (*FlatstorageConfig) HCL2Spec() map[string]hcldec.Spec {
s := map[string]hcldec.Spec{
"iso_checksum": &hcldec.AttrSpec{Name: "iso_checksum", Type: cty.String, Required: false},
"iso_url": &hcldec.AttrSpec{Name: "iso_url", Type: cty.String, Required: false},
"iso_urls": &hcldec.AttrSpec{Name: "iso_urls", Type: cty.List(cty.String), Required: false},
"iso_target_path": &hcldec.AttrSpec{Name: "iso_target_path", Type: cty.String, Required: false},
"iso_target_extension": &hcldec.AttrSpec{Name: "iso_target_extension", Type: cty.String, Required: false},
"device": &hcldec.AttrSpec{Name: "device", Type: cty.String, Required: false},
"iso_file": &hcldec.AttrSpec{Name: "iso_file", Type: cty.String, Required: false},
"iso_storage_pool": &hcldec.AttrSpec{Name: "iso_storage_pool", Type: cty.String, Required: false},
"unmount": &hcldec.AttrSpec{Name: "unmount", Type: cty.Bool, Required: false},
"should_upload_iso": &hcldec.AttrSpec{Name: "should_upload_iso", Type: cty.Bool, Required: false},
"download_path_key": &hcldec.AttrSpec{Name: "download_path_key", Type: cty.String, Required: false},
}
return s
}
// FlatvgaConfig is an auto-generated flat version of vgaConfig. // FlatvgaConfig is an auto-generated flat version of vgaConfig.
// Where the contents of a field with a `mapstructure:,squash` tag are bubbled up. // Where the contents of a field with a `mapstructure:,squash` tag are bubbled up.
type FlatvgaConfig struct { type FlatvgaConfig struct {
@@ -80,30 +80,6 @@ func (s *stepFinalizeTemplateConfig) Run(ctx context.Context, state multistep.St
} }
} }
if len(c.AdditionalISOFiles) > 0 {
vmParams, err := client.GetVmConfig(vmRef)
if err != nil {
err := fmt.Errorf("Error fetching template config: %s", err)
state.Put("error", err)
ui.Error(err.Error())
return multistep.ActionHalt
}
for idx := range c.AdditionalISOFiles {
cdrom := c.AdditionalISOFiles[idx].Device
if c.AdditionalISOFiles[idx].Unmount {
if vmParams[cdrom] == nil || !strings.Contains(vmParams[cdrom].(string), "media=cdrom") {
err := fmt.Errorf("Cannot eject ISO from cdrom drive, %s is not present or not a cdrom media", cdrom)
state.Put("error", err)
ui.Error(err.Error())
return multistep.ActionHalt
}
changes[cdrom] = "none,media=cdrom"
} else {
changes[cdrom] = c.AdditionalISOFiles[idx].ISOFile + ",media=cdrom"
}
}
}
if len(changes) > 0 { if len(changes) > 0 {
_, err := client.SetVmConfig(vmRef, changes) _, err := client.SetVmConfig(vmRef, changes)
if err != nil { if err != nil {
+13 -26
View File
@@ -3,6 +3,7 @@ package proxmox
import ( import (
"context" "context"
"fmt" "fmt"
"log"
"strconv" "strconv"
"github.com/Telmate/proxmox-api-go/proxmox" "github.com/Telmate/proxmox-api-go/proxmox"
@@ -21,15 +22,10 @@ type stepStartVM struct {
type ProxmoxVMCreator interface { type ProxmoxVMCreator interface {
Create(*proxmox.VmRef, proxmox.ConfigQemu, multistep.StateBag) error Create(*proxmox.VmRef, proxmox.ConfigQemu, multistep.StateBag) error
} }
type vmStarter interface {
GetNextID(int) (int, error)
StartVm(*proxmox.VmRef) (string, error)
SetVmConfig(*proxmox.VmRef, map[string]interface{}) (interface{}, error)
}
func (s *stepStartVM) Run(ctx context.Context, state multistep.StateBag) multistep.StepAction { func (s *stepStartVM) Run(ctx context.Context, state multistep.StateBag) multistep.StepAction {
ui := state.Get("ui").(packersdk.Ui) ui := state.Get("ui").(packersdk.Ui)
client := state.Get("proxmoxClient").(vmStarter) client := state.Get("proxmoxClient").(*proxmox.Client)
c := state.Get("config").(*Config) c := state.Get("config").(*Config)
agent := 1 agent := 1
@@ -63,14 +59,21 @@ func (s *stepStartVM) Run(ctx context.Context, state multistep.StateBag) multist
if c.VMID == 0 { if c.VMID == 0 {
ui.Say("No VM ID given, getting next free from Proxmox") ui.Say("No VM ID given, getting next free from Proxmox")
id, err := client.GetNextID(0) for n := 0; n < 5; n++ {
if err != nil { id, err := proxmox.MaxVmId(client)
if err != nil {
log.Printf("Error getting max used VM ID: %v (attempt %d/5)", err, n+1)
continue
}
c.VMID = id + 1
break
}
if c.VMID == 0 {
err := fmt.Errorf("Failed to get free VM ID")
state.Put("error", err) state.Put("error", err)
ui.Error(err.Error()) ui.Error(err.Error())
return multistep.ActionHalt return multistep.ActionHalt
} }
c.VMID = id
config.VmID = id
} }
vmRef := proxmox.NewVmRef(c.VMID) vmRef := proxmox.NewVmRef(c.VMID)
vmRef.SetNode(c.Node) vmRef.SetNode(c.Node)
@@ -86,22 +89,6 @@ func (s *stepStartVM) Run(ctx context.Context, state multistep.StateBag) multist
return multistep.ActionHalt return multistep.ActionHalt
} }
// proxmox-api-go assumes all QemuDisks are actually hard disks, not cd
// drives, so we need to add them via a config update
if len(c.AdditionalISOFiles) > 0 {
addISOConfig := make(map[string]interface{})
for _, iso := range c.AdditionalISOFiles {
addISOConfig[iso.Device] = fmt.Sprintf("%s,media=cdrom", iso.ISOFile)
}
_, err := client.SetVmConfig(vmRef, addISOConfig)
if err != nil {
err := fmt.Errorf("Error updating template: %s", err)
state.Put("error", err)
ui.Error(err.Error())
return multistep.ActionHalt
}
}
// Store the vm id for later // Store the vm id for later
state.Put("vmRef", vmRef) state.Put("vmRef", vmRef)
// instance_id is the generic term used so that users can have access to the // instance_id is the generic term used so that users can have access to the
@@ -1,7 +1,6 @@
package proxmox package proxmox
import ( import (
"context"
"fmt" "fmt"
"testing" "testing"
@@ -107,80 +106,3 @@ func TestCleanupStartVM(t *testing.T) {
}) })
} }
} }
type startVMMock struct {
create func(*proxmox.VmRef, proxmox.ConfigQemu, multistep.StateBag) error
startVm func(*proxmox.VmRef) (string, error)
setVmConfig func(*proxmox.VmRef, map[string]interface{}) (interface{}, error)
}
func (m *startVMMock) Create(vmRef *proxmox.VmRef, config proxmox.ConfigQemu, state multistep.StateBag) error {
return m.create(vmRef, config, state)
}
func (m *startVMMock) StartVm(vmRef *proxmox.VmRef) (string, error) {
return m.startVm(vmRef)
}
func (m *startVMMock) SetVmConfig(vmRef *proxmox.VmRef, config map[string]interface{}) (interface{}, error) {
return m.setVmConfig(vmRef, config)
}
func (m *startVMMock) GetNextID(int) (int, error) {
return 1, nil
}
func TestStartVM(t *testing.T) {
// TODO: proxmox-api-go does a lot of manipulation on the input and does not
// give any way to access the actual data it sends to the Proxmox server,
// which means writing good tests here is quite hard. This test is mainly a
// stub to revisit when we can write better tests.
cs := []struct {
name string
config *Config
expectedAction multistep.StepAction
}{
{
name: "Example config from documentation works",
config: &Config{
Disks: []diskConfig{
{
Type: "sata",
Size: "10G",
StoragePool: "local",
StoragePoolType: "lvm",
},
},
NICs: []nicConfig{
{
Bridge: "vmbr0",
},
},
},
expectedAction: multistep.ActionContinue,
},
}
for _, c := range cs {
t.Run(c.name, func(t *testing.T) {
mock := &startVMMock{
create: func(vmRef *proxmox.VmRef, config proxmox.ConfigQemu, state multistep.StateBag) error {
return nil
},
startVm: func(*proxmox.VmRef) (string, error) {
return "", nil
},
setVmConfig: func(*proxmox.VmRef, map[string]interface{}) (interface{}, error) {
return nil, nil
},
}
state := new(multistep.BasicStateBag)
state.Put("ui", packersdk.TestUi(t))
state.Put("config", c.config)
state.Put("proxmoxClient", mock)
s := stepStartVM{vmCreator: mock}
action := s.Run(context.TODO(), state)
if action != c.expectedAction {
t.Errorf("Expected action %s, got %s", c.expectedAction, action)
}
})
}
}
+11
View File
@@ -43,8 +43,19 @@ func (b *Builder) Run(ctx context.Context, ui packersdk.Ui, hook packersdk.Hook)
Url: b.config.ISOUrls, Url: b.config.ISOUrls,
}, },
} }
for idx := range b.config.AdditionalISOFiles {
preSteps = append(preSteps, &commonsteps.StepDownload{
Checksum: b.config.AdditionalISOFiles[idx].ISOChecksum,
Description: "additional ISO",
Extension: b.config.AdditionalISOFiles[idx].TargetExtension,
ResultKey: b.config.AdditionalISOFiles[idx].DownloadPathKey,
TargetPath: b.config.AdditionalISOFiles[idx].DownloadPathKey,
Url: b.config.AdditionalISOFiles[idx].ISOUrls,
})
}
preSteps = append(preSteps, preSteps = append(preSteps,
&stepUploadISO{}, &stepUploadISO{},
&stepUploadAdditionalISOs{},
) )
postSteps := []multistep.Step{ postSteps := []multistep.Step{
&stepFinalizeISOTemplate{}, &stepFinalizeISOTemplate{},
+61 -1
View File
@@ -1,9 +1,13 @@
//go:generate mapstructure-to-hcl2 -type Config,nicConfig,diskConfig,vgaConfig,additionalISOsConfig //go:generate mapstructure-to-hcl2 -type Config,nicConfig,diskConfig,vgaConfig
package proxmoxiso package proxmoxiso
import ( import (
"errors" "errors"
"fmt"
"log"
"strconv"
"strings"
"github.com/hashicorp/packer-plugin-sdk/multistep/commonsteps" "github.com/hashicorp/packer-plugin-sdk/multistep/commonsteps"
packersdk "github.com/hashicorp/packer-plugin-sdk/packer" packersdk "github.com/hashicorp/packer-plugin-sdk/packer"
@@ -47,6 +51,62 @@ func (c *Config) Prepare(raws ...interface{}) ([]string, []string, error) {
errs = packersdk.MultiErrorAppend(errs, errors.New("when specifying iso_url, iso_storage_pool must also be specified")) errs = packersdk.MultiErrorAppend(errs, errors.New("when specifying iso_url, iso_storage_pool must also be specified"))
} }
for idx := range c.AdditionalISOFiles {
// Check AdditionalISO config
// Either a pre-uploaded ISO should be referenced in iso_file, OR a URL
// (possibly to a local file) to an ISO file that will be downloaded and
// then uploaded to Proxmox.
if c.AdditionalISOFiles[idx].ISOFile != "" {
c.AdditionalISOFiles[idx].ShouldUploadISO = false
} else {
c.AdditionalISOFiles[idx].DownloadPathKey = "downloaded_additional_iso_path_" + strconv.Itoa(idx)
isoWarnings, isoErrors := c.AdditionalISOFiles[idx].ISOConfig.Prepare(&c.Ctx)
errs = packersdk.MultiErrorAppend(errs, isoErrors...)
warnings = append(warnings, isoWarnings...)
c.AdditionalISOFiles[idx].ShouldUploadISO = true
}
if c.AdditionalISOFiles[idx].Device == "" {
log.Printf("AdditionalISOFile %d Device not set, using default 'ide3'", idx)
c.AdditionalISOFiles[idx].Device = "ide3"
}
if strings.HasPrefix(c.AdditionalISOFiles[idx].Device, "ide") {
busnumber, err := strconv.Atoi(c.AdditionalISOFiles[idx].Device[3:])
if err != nil {
errs = packersdk.MultiErrorAppend(errs, fmt.Errorf("%s is not a valid bus index", c.AdditionalISOFiles[idx].Device[3:]))
}
if busnumber == 2 {
errs = packersdk.MultiErrorAppend(errs, fmt.Errorf("IDE bus 2 is used by boot ISO"))
}
if busnumber > 3 {
errs = packersdk.MultiErrorAppend(errs, fmt.Errorf("IDE bus index can't be higher than 3"))
}
}
if strings.HasPrefix(c.AdditionalISOFiles[idx].Device, "sata") {
busnumber, err := strconv.Atoi(c.AdditionalISOFiles[idx].Device[4:])
if err != nil {
errs = packersdk.MultiErrorAppend(errs, fmt.Errorf("%s is not a valid bus index", c.AdditionalISOFiles[idx].Device[4:]))
}
if busnumber > 5 {
errs = packersdk.MultiErrorAppend(errs, fmt.Errorf("SATA bus index can't be higher than 5"))
}
}
if strings.HasPrefix(c.AdditionalISOFiles[idx].Device, "scsi") {
busnumber, err := strconv.Atoi(c.AdditionalISOFiles[idx].Device[4:])
if err != nil {
errs = packersdk.MultiErrorAppend(errs, fmt.Errorf("%s is not a valid bus index", c.AdditionalISOFiles[idx].Device[4:]))
}
if busnumber > 30 {
errs = packersdk.MultiErrorAppend(errs, fmt.Errorf("SCSI bus index can't be higher than 30"))
}
}
if (c.AdditionalISOFiles[idx].ISOFile == "" && len(c.AdditionalISOFiles[idx].ISOConfig.ISOUrls) == 0) || (c.AdditionalISOFiles[idx].ISOFile != "" && len(c.AdditionalISOFiles[idx].ISOConfig.ISOUrls) != 0) {
errs = packersdk.MultiErrorAppend(errs, fmt.Errorf("either iso_file or iso_url, but not both, must be specified for AdditionalISO file %s", c.AdditionalISOFiles[idx].Device))
}
if len(c.ISOConfig.ISOUrls) != 0 && c.ISOStoragePool == "" {
errs = packersdk.MultiErrorAppend(errs, errors.New("when specifying iso_url, iso_storage_pool must also be specified"))
}
}
if errs != nil && len(errs.Errors) > 0 { if errs != nil && len(errs.Errors) > 0 {
return nil, warnings, errs return nil, warnings, errs
} }

Some files were not shown because too many files have changed in this diff Show More