Compare commits
9 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| 429e5149d3 | |||
| 0c00ef62a5 | |||
| 38254bca28 | |||
| fa034ed424 | |||
| 56b4a512ed | |||
| 3f7550cad1 | |||
| 559c5c040b | |||
| 26b952e66d | |||
| 453e56e554 |
+9
-20
@@ -7,7 +7,7 @@ version: 2.1
|
||||
executors:
|
||||
golang:
|
||||
docker:
|
||||
- image: docker.mirror.hashicorp.services/circleci/golang:1.16
|
||||
- image: docker.mirror.hashicorp.services/circleci/golang:1.15
|
||||
resource_class: medium+
|
||||
darwin:
|
||||
macos:
|
||||
@@ -37,19 +37,15 @@ commands:
|
||||
parameters:
|
||||
GOOS:
|
||||
type: string
|
||||
GOARCH:
|
||||
default: "amd64"
|
||||
type: string
|
||||
steps:
|
||||
- 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: zip ./pkg/packer_<< parameters.GOOS >>_<< parameters.GOARCH >>.zip ./pkg/packer_<< parameters.GOOS >>_<< parameters.GOARCH >>
|
||||
- run: rm ./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 >>_$(go env GOARCH).zip ./pkg/packer_<< parameters.GOOS >>_$(go env GOARCH)
|
||||
- run: rm ./pkg/packer_<< parameters.GOOS >>_$(go env GOARCH)
|
||||
- persist_to_workspace:
|
||||
root: .
|
||||
paths:
|
||||
- ./pkg/
|
||||
|
||||
# Golang CircleCI 2.0 configuration file
|
||||
#
|
||||
# Check https://circleci.com/docs/2.0/language-go/ for more details
|
||||
@@ -65,11 +61,13 @@ jobs:
|
||||
file: coverage.txt
|
||||
test-darwin:
|
||||
executor: darwin
|
||||
working_directory: ~/go/github.com/hashicorp/packer
|
||||
working_directory: ~/go/src/github.com/hashicorp/packer
|
||||
environment:
|
||||
GO111MODULE: "off"
|
||||
steps:
|
||||
- install-go-run-tests-unix:
|
||||
GOOS: darwin
|
||||
GOVERSION: "1.16"
|
||||
GOVERSION: "1.15"
|
||||
- codecov/upload:
|
||||
file: coverage.txt
|
||||
test-windows:
|
||||
@@ -78,7 +76,7 @@ jobs:
|
||||
shell: bash.exe
|
||||
steps:
|
||||
- install-go-run-tests-windows:
|
||||
GOVERSION: "1.16"
|
||||
GOVERSION: "1.15"
|
||||
- codecov/upload:
|
||||
file: coverage.txt
|
||||
check-lint:
|
||||
@@ -127,13 +125,6 @@ jobs:
|
||||
steps:
|
||||
- build-and-persist-packer-binary:
|
||||
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:
|
||||
executor: golang
|
||||
working_directory: /go/src/github.com/hashicorp/packer
|
||||
@@ -214,7 +205,6 @@ workflows:
|
||||
jobs:
|
||||
- build_linux
|
||||
- build_darwin
|
||||
- build_darwin_arm64
|
||||
- build_windows
|
||||
- build_freebsd
|
||||
- build_openbsd
|
||||
@@ -223,7 +213,6 @@ workflows:
|
||||
requires:
|
||||
- build_linux
|
||||
- build_darwin
|
||||
- build_darwin_arm64
|
||||
- build_windows
|
||||
- build_freebsd
|
||||
- build_openbsd
|
||||
|
||||
@@ -6,7 +6,6 @@
|
||||
*.mdx text eol=lf
|
||||
*.ps1 text eol=lf
|
||||
*.hcl text eol=lf
|
||||
*.tmpl text eol=lf
|
||||
*.txt text eol=lf
|
||||
go.mod text eol=lf
|
||||
go.sum text eol=lf
|
||||
|
||||
+4
-11
@@ -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
|
||||
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
|
||||
|
||||
### 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.
|
||||
|
||||
- Run the command with debug output with the environment variable `PACKER_LOG`.
|
||||
For example: `PACKER_LOG=1 packer build template.pkr.hcl`. Take the _entire_
|
||||
For example: `PACKER_LOG=1 packer build template.json`. Take the _entire_
|
||||
output and create a [gist](https://gist.github.com) for linking to in your
|
||||
issue. Packer should strip sensitive keys from the output, but take a look
|
||||
through just in case.
|
||||
@@ -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
|
||||
runtime in order to build packer.
|
||||
|
||||
1. This project always releases from the latest version of golang.
|
||||
[Install go](https://golang.org/doc/install#install) To properly build from
|
||||
source, you need to have golang >= v1.16
|
||||
1. This project always releases from the latest version of golang. [Install go](https://golang.org/doc/install#install)
|
||||
|
||||
## 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
|
||||
POSIX-like environments (macOS, Linux, Cygwin, etc.) so you may need to
|
||||
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
|
||||
`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
|
||||
`$GOPATH/src/github.com/hashicorp/packer/bin/packer` to build a machine and
|
||||
verify your changes work. For instance:
|
||||
`$GOPATH/src/github.com/hashicorp/packer/bin/packer build template.pkr.hcl`.
|
||||
`$GOPATH/src/github.com/hashicorp/packer/bin/packer build template.json`.
|
||||
|
||||
5. If everything works well and the tests pass, run `go fmt` on your code before
|
||||
submitting a pull-request.
|
||||
|
||||
@@ -10,7 +10,7 @@ Describe the change you are making here!
|
||||
|
||||
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
|
||||
|
||||
If your PR resolves any open issue(s), please indicate them like this so they will be closed when your PR is merged:
|
||||
|
||||
@@ -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();
|
||||
@@ -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
|
||||
@@ -7,7 +7,7 @@ name: Check markdown links on modified website files
|
||||
jobs:
|
||||
vercel-deployment-poll:
|
||||
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:
|
||||
url: ${{ steps.waitForVercelPreviewDeployment.outputs.url }}
|
||||
steps:
|
||||
|
||||
@@ -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.
|
||||
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
-218
@@ -1,230 +1,15 @@
|
||||
## 1.7.2 (April 05, 2021)
|
||||
|
||||
### IMPROVEMENTS:
|
||||
|
||||
* builder/alicloud: Add `ramrole` configuration to ECS instance. [GH-10845]
|
||||
|
||||
### BUG FIXES:
|
||||
|
||||
* builder/proxmox: Update Proxmox Go API to ensure only the first non-loopback
|
||||
IPv4 address gets returned. [GH-10858]
|
||||
* builder/vsphere: Fix primary disk resize on clone. [GH-10848]
|
||||
* core: Fix bug where call to "packer version" sent output to stderr instead of
|
||||
stdout. [GH-10850]
|
||||
|
||||
## 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.7.0 (Upcoming)
|
||||
|
||||
## 1.6.6 (December 16, 2020)
|
||||
|
||||
### FEATURES
|
||||
### FEATURES:
|
||||
* **New command** `fmt` allows users to format existing HCL2 configuration
|
||||
files into a canonical style. Please see [fmt command
|
||||
docs](https://packer.io/docs/commands/fmt) for more details. [GH-10225]
|
||||
[GH-10377]
|
||||
* **New function** `env` allows users to set the default value of a variable to
|
||||
the value of an environment variable. Please see [env function
|
||||
docs](https://www.packer.io/docs/templates/hcl_templates/functions/contextual/env) for
|
||||
docs](https://www.packer.io/docs/templates/hcl_templates/functions/contextual/env") for
|
||||
more details. [GH-10240]
|
||||
* **Future Scaffolding** This release contains a large number of no-op
|
||||
refactoring changes. The Packer team at HashiCorp is preparing to split the
|
||||
|
||||
+3
-2
@@ -37,8 +37,8 @@
|
||||
/builder/oracle/ @prydie @owainlewis
|
||||
/website/pages/docs/builders/oracle* @prydie @owainlewis
|
||||
|
||||
/builder/profitbricks/ @LiviusP @mflorin
|
||||
/website/pages/docs/builders/profitbricks* @LiviusP @mflorin
|
||||
/builder/profitbricks/ @jasmingacic
|
||||
/website/pages/docs/builders/profitbricks* @jasmingacic
|
||||
|
||||
/builder/triton/ @sean-
|
||||
/website/pages/docs/builders/triton* @sean-
|
||||
@@ -87,6 +87,7 @@
|
||||
|
||||
/post-processor/alicloud-import/ dongxiao.zzh@alibaba-inc.com
|
||||
/post-processor/checksum/ v.tolstov@selfip.ru
|
||||
/post-processor/exoscale-import/ @falzm @mcorbin
|
||||
/post-processor/googlecompute-export/ crunkleton@google.com
|
||||
/post-processor/yandex-export/ @GennadySpb
|
||||
/post-processor/yandex-import/ @GennadySpb
|
||||
|
||||
@@ -49,7 +49,7 @@ package:
|
||||
@sh -c "$(CURDIR)/scripts/dist.sh $(VERSION)"
|
||||
|
||||
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
|
||||
# to avoid having to tidy our go deps, we `go get` our binaries from a temp
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
# Packer
|
||||
|
||||
[![Build Status][circleci-badge]][circleci]
|
||||
[](https://discuss.hashicorp.com/c/packer)
|
||||
[![Windows Build Status][appveyor-badge]][appveyor]
|
||||
[](https://pkg.go.dev/github.com/hashicorp/packer)
|
||||
[![GoReportCard][report-badge]][report]
|
||||
[](https://codecov.io/gh/hashicorp/packer)
|
||||
@@ -9,16 +9,15 @@
|
||||
[circleci-badge]: https://circleci.com/gh/hashicorp/packer.svg?style=svg
|
||||
[circleci]: https://app.circleci.com/pipelines/github/hashicorp/packer
|
||||
[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]: https://godoc.org/github.com/hashicorp/packer
|
||||
[report-badge]: https://goreportcard.com/badge/github.com/hashicorp/packer
|
||||
[godoc]: https://godoc.org/github.com/hashicorp/packer
|
||||
[report-badge]: https://goreportcard.com/badge/github.com/hashicorp/packer
|
||||
[report]: https://goreportcard.com/report/github.com/hashicorp/packer
|
||||
|
||||
<p align="center" style="text-align:center;">
|
||||
<a href="https://www.packer.io">
|
||||
<img alt="HashiCorp Packer logo" src="website/public/img/logo-packer-padded.svg" width="500" />
|
||||
</a>
|
||||
</p>
|
||||
* Website: https://www.packer.io
|
||||
* IRC: `#packer-tool` on Freenode
|
||||
* Mailing list: [Google Groups](https://groups.google.com/forum/#!forum/packer-tool)
|
||||
|
||||
Packer is a tool for building identical machine images for multiple platforms
|
||||
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
|
||||
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.
|
||||
|
||||
Save this file as `quick-start.pkr.hcl`. Export your AWS credentials as the
|
||||
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
|
||||
`AWS_ACCESS_KEY_ID` and `AWS_SECRET_ACCESS_KEY` environment variables.
|
||||
|
||||
```hcl
|
||||
variable "access_key" {
|
||||
type = string
|
||||
default = "${env("AWS_ACCESS_KEY_ID")}"
|
||||
}
|
||||
|
||||
variable "secret_key" {
|
||||
type = string
|
||||
default = "${env("AWS_SECRET_ACCESS_KEY")}"
|
||||
}
|
||||
|
||||
locals { timestamp = regex_replace(timestamp(), "[- TZ:]", "") }
|
||||
|
||||
source "amazon-ebs" "quick-start" {
|
||||
access_key = "${var.access_key}"
|
||||
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"]
|
||||
```json
|
||||
{
|
||||
"variables": {
|
||||
"access_key": "{{env `AWS_ACCESS_KEY_ID`}}",
|
||||
"secret_key": "{{env `AWS_SECRET_ACCESS_KEY`}}"
|
||||
},
|
||||
"builders": [{
|
||||
"type": "amazon-ebs",
|
||||
"access_key": "{{user `access_key`}}",
|
||||
"secret_key": "{{user `secret_key`}}",
|
||||
"region": "us-east-1",
|
||||
"source_ami": "ami-af22d9b9",
|
||||
"instance_type": "t2.micro",
|
||||
"ssh_username": "ubuntu",
|
||||
"ami_name": "packer-example {{timestamp}}"
|
||||
}]
|
||||
}
|
||||
```
|
||||
|
||||
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
|
||||
|
||||
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
|
||||
[CONTRIBUTING.md](https://github.com/hashicorp/packer/blob/master/.github/CONTRIBUTING.md)
|
||||
|
||||
@@ -135,7 +135,6 @@ func (b *Builder) Run(ctx context.Context, ui packersdk.Ui, hook packersdk.Hook)
|
||||
InstanceType: b.config.InstanceType,
|
||||
UserData: b.config.UserData,
|
||||
UserDataFile: b.config.UserDataFile,
|
||||
RamRoleName: b.config.RamRoleName,
|
||||
RegionId: b.config.AlicloudRegion,
|
||||
InternetChargeType: b.config.InternetChargeType,
|
||||
InternetMaxBandwidthOut: b.config.InternetMaxBandwidthOut,
|
||||
|
||||
@@ -88,7 +88,6 @@ type FlatConfig struct {
|
||||
AlicloudSourceImage *string `mapstructure:"source_image" required:"true" cty:"source_image" hcl:"source_image"`
|
||||
ForceStopInstance *bool `mapstructure:"force_stop_instance" required:"false" cty:"force_stop_instance" hcl:"force_stop_instance"`
|
||||
DisableStopInstance *bool `mapstructure:"disable_stop_instance" required:"false" cty:"disable_stop_instance" hcl:"disable_stop_instance"`
|
||||
RamRoleName *string `mapstructure:"ram_role_name" required:"false" cty:"ram_role_name" hcl:"ram_role_name"`
|
||||
SecurityGroupId *string `mapstructure:"security_group_id" required:"false" cty:"security_group_id" hcl:"security_group_id"`
|
||||
SecurityGroupName *string `mapstructure:"security_group_name" required:"false" cty:"security_group_name" hcl:"security_group_name"`
|
||||
UserData *string `mapstructure:"user_data" required:"false" cty:"user_data" hcl:"user_data"`
|
||||
@@ -206,7 +205,6 @@ func (*FlatConfig) HCL2Spec() map[string]hcldec.Spec {
|
||||
"source_image": &hcldec.AttrSpec{Name: "source_image", Type: cty.String, Required: false},
|
||||
"force_stop_instance": &hcldec.AttrSpec{Name: "force_stop_instance", Type: cty.Bool, Required: false},
|
||||
"disable_stop_instance": &hcldec.AttrSpec{Name: "disable_stop_instance", Type: cty.Bool, Required: false},
|
||||
"ram_role_name": &hcldec.AttrSpec{Name: "ram_role_name", Type: cty.String, Required: false},
|
||||
"security_group_id": &hcldec.AttrSpec{Name: "security_group_id", Type: cty.String, Required: false},
|
||||
"security_group_name": &hcldec.AttrSpec{Name: "security_group_name", Type: cty.String, Required: false},
|
||||
"user_data": &hcldec.AttrSpec{Name: "user_data", Type: cty.String, Required: false},
|
||||
|
||||
@@ -47,8 +47,6 @@ type RunConfig struct {
|
||||
// E.g., Sysprep a windows which may shutdown the instance within its command.
|
||||
// The default value is false.
|
||||
DisableStopInstance bool `mapstructure:"disable_stop_instance" required:"false"`
|
||||
// Ram Role to apply when launching the instance.
|
||||
RamRoleName string `mapstructure:"ram_role_name" required:"false"`
|
||||
// ID of the security group to which a newly
|
||||
// created instance belongs. Mutual access is allowed between instances in one
|
||||
// security group. If not specified, the newly created instance will be added
|
||||
|
||||
@@ -23,7 +23,6 @@ type stepCreateAlicloudInstance struct {
|
||||
UserData string
|
||||
UserDataFile string
|
||||
instanceId string
|
||||
RamRoleName string
|
||||
RegionId string
|
||||
InternetChargeType string
|
||||
InternetMaxBandwidthOut int
|
||||
@@ -116,7 +115,6 @@ func (s *stepCreateAlicloudInstance) buildCreateInstanceRequest(state multistep.
|
||||
request.RegionId = s.RegionId
|
||||
request.InstanceType = s.InstanceType
|
||||
request.InstanceName = s.InstanceName
|
||||
request.RamRoleName = s.RamRoleName
|
||||
request.ZoneId = s.ZoneId
|
||||
|
||||
sourceImage := state.Get("source_image").(*ecs.Image)
|
||||
|
||||
@@ -233,9 +233,7 @@ func NewAzureClient(subscriptionID, sigSubscriptionID, resourceGroupName, storag
|
||||
azureClient.GalleryImageVersionsClient.ResponseInspector = byConcatDecorators(byInspecting(maxlen), errorCapture(azureClient))
|
||||
azureClient.GalleryImageVersionsClient.UserAgent = fmt.Sprintf("%s %s", useragent.String(version.AzurePluginVersion.FormattedVersion()), azureClient.GalleryImageVersionsClient.UserAgent)
|
||||
azureClient.GalleryImageVersionsClient.Client.PollingDuration = sharedGalleryTimeout
|
||||
if sigSubscriptionID != "" {
|
||||
azureClient.GalleryImageVersionsClient.SubscriptionID = sigSubscriptionID
|
||||
}
|
||||
azureClient.GalleryImageVersionsClient.SubscriptionID = sigSubscriptionID
|
||||
|
||||
azureClient.GalleryImagesClient = newCompute.NewGalleryImagesClientWithBaseURI(cloud.ResourceManagerEndpoint, subscriptionID)
|
||||
azureClient.GalleryImagesClient.Authorizer = autorest.NewBearerAuthorizer(servicePrincipalToken)
|
||||
@@ -243,9 +241,7 @@ func NewAzureClient(subscriptionID, sigSubscriptionID, resourceGroupName, storag
|
||||
azureClient.GalleryImagesClient.ResponseInspector = byConcatDecorators(byInspecting(maxlen), errorCapture(azureClient))
|
||||
azureClient.GalleryImagesClient.UserAgent = fmt.Sprintf("%s %s", useragent.String(version.AzurePluginVersion.FormattedVersion()), azureClient.GalleryImagesClient.UserAgent)
|
||||
azureClient.GalleryImagesClient.Client.PollingDuration = pollingDuration
|
||||
if sigSubscriptionID != "" {
|
||||
azureClient.GalleryImagesClient.SubscriptionID = sigSubscriptionID
|
||||
}
|
||||
azureClient.GalleryImagesClient.SubscriptionID = sigSubscriptionID
|
||||
|
||||
keyVaultURL, err := url.Parse(cloud.KeyVaultEndpoint)
|
||||
if err != nil {
|
||||
|
||||
@@ -20,18 +20,12 @@ package arm
|
||||
// go test -v -timeout 90m -run TestBuilderAcc_.*
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"context"
|
||||
"errors"
|
||||
"fmt"
|
||||
"os"
|
||||
"strings"
|
||||
"testing"
|
||||
|
||||
"github.com/Azure/go-autorest/autorest"
|
||||
"github.com/Azure/go-autorest/autorest/azure/auth"
|
||||
"fmt"
|
||||
"os"
|
||||
|
||||
builderT "github.com/hashicorp/packer-plugin-sdk/acctest"
|
||||
packersdk "github.com/hashicorp/packer-plugin-sdk/packer"
|
||||
)
|
||||
|
||||
const DeviceLoginAcceptanceTest = "DEVICELOGIN_TEST"
|
||||
@@ -102,15 +96,10 @@ func TestBuilderAcc_ManagedDisk_Linux_AzureCLI(t *testing.T) {
|
||||
return
|
||||
}
|
||||
|
||||
var b Builder
|
||||
builderT.Test(t, builderT.TestCase{
|
||||
PreCheck: func() { testAuthPreCheck(t) },
|
||||
Builder: &b,
|
||||
PreCheck: func() { testAccPreCheck(t) },
|
||||
Builder: &Builder{},
|
||||
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) {
|
||||
var b Builder
|
||||
builderT.Test(t, builderT.TestCase{
|
||||
PreCheck: func() { testAuthPreCheck(t) },
|
||||
Builder: &b,
|
||||
PreCheck: func() { testAccPreCheck(t) },
|
||||
Builder: &Builder{},
|
||||
Template: testBuilderAccBlobLinux,
|
||||
Check: func([]packersdk.Artifact) error {
|
||||
checkUnmanagedVHDDeleted(t, &b)
|
||||
return nil
|
||||
},
|
||||
})
|
||||
}
|
||||
|
||||
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 = `
|
||||
{
|
||||
"variables": {
|
||||
@@ -259,7 +155,6 @@ const testBuilderAccManagedDiskWindows = `
|
||||
}]
|
||||
}
|
||||
`
|
||||
|
||||
const testBuilderAccManagedDiskWindowsBuildResourceGroup = `
|
||||
{
|
||||
"variables": {
|
||||
@@ -392,7 +287,6 @@ const testBuilderAccManagedDiskLinux = `
|
||||
}]
|
||||
}
|
||||
`
|
||||
|
||||
const testBuilderAccManagedDiskLinuxDeviceLogin = `
|
||||
{
|
||||
"variables": {
|
||||
@@ -485,7 +379,6 @@ const testBuilderAccBlobLinux = `
|
||||
}]
|
||||
}
|
||||
`
|
||||
|
||||
const testBuilderAccManagedDiskLinuxAzureCLI = `
|
||||
{
|
||||
"builders": [{
|
||||
@@ -495,8 +388,7 @@ const testBuilderAccManagedDiskLinuxAzureCLI = `
|
||||
|
||||
"managed_image_resource_group_name": "packer-acceptance-test",
|
||||
"managed_image_name": "testBuilderAccManagedDiskLinuxAzureCLI-{{timestamp}}",
|
||||
"temp_resource_group_name": "packer-acceptance-test-managed-cli",
|
||||
|
||||
|
||||
"os_type": "Linux",
|
||||
"image_publisher": "Canonical",
|
||||
"image_offer": "UbuntuServer",
|
||||
|
||||
@@ -23,7 +23,6 @@ type FlatConfig struct {
|
||||
ClientID *string `mapstructure:"client_id" cty:"client_id" hcl:"client_id"`
|
||||
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"`
|
||||
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"`
|
||||
ObjectID *string `mapstructure:"object_id" cty:"object_id" hcl:"object_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_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_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},
|
||||
"object_id": &hcldec.AttrSpec{Name: "object_id", Type: cty.String, Required: false},
|
||||
"tenant_id": &hcldec.AttrSpec{Name: "tenant_id", Type: cty.String, Required: false},
|
||||
|
||||
@@ -6,7 +6,6 @@ import (
|
||||
"fmt"
|
||||
"net/url"
|
||||
"strings"
|
||||
"sync"
|
||||
"time"
|
||||
|
||||
"github.com/hashicorp/packer-plugin-sdk/multistep"
|
||||
@@ -16,17 +15,16 @@ import (
|
||||
)
|
||||
|
||||
type StepDeployTemplate struct {
|
||||
client *AzureClient
|
||||
deploy func(ctx context.Context, resourceGroupName string, deploymentName string) error
|
||||
delete func(ctx context.Context, deploymentName, resourceGroupName 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
|
||||
deleteDeployment func(ctx context.Context, state multistep.StateBag) error
|
||||
say func(message string)
|
||||
error func(e error)
|
||||
config *Config
|
||||
factory templateFactoryFunc
|
||||
name string
|
||||
client *AzureClient
|
||||
deploy func(ctx context.Context, resourceGroupName string, deploymentName string) error
|
||||
delete func(ctx context.Context, deploymentName, resourceGroupName 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
|
||||
say func(message string)
|
||||
error func(e error)
|
||||
config *Config
|
||||
factory templateFactoryFunc
|
||||
name string
|
||||
}
|
||||
|
||||
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.disk = step.getImageDetails
|
||||
step.deleteDisk = step.deleteImage
|
||||
step.deleteDeployment = step.deleteDeploymentObject
|
||||
return step
|
||||
}
|
||||
|
||||
@@ -61,45 +58,32 @@ func (s *StepDeployTemplate) Run(ctx context.Context, state multistep.StateBag)
|
||||
|
||||
func (s *StepDeployTemplate) Cleanup(state multistep.StateBag) {
|
||||
defer func() {
|
||||
err := s.deleteDeployment(context.Background(), state)
|
||||
err := s.deleteTemplate(context.Background(), state)
|
||||
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.Say("\nDeleting individual resources ...")
|
||||
ui.Say("\nThe resource group was not created by Packer, deleting individual resources ...")
|
||||
|
||||
deploymentName := s.name
|
||||
resourceGroupName := state.Get(constants.ArmResourceGroupName).(string)
|
||||
// Get image disk details before deleting the image; otherwise we won't be able to
|
||||
// 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)
|
||||
err := s.deleteDeploymentResources(context.TODO(), deploymentName, resourceGroupName)
|
||||
if err != nil {
|
||||
s.reportIfError(err, resourceGroupName)
|
||||
}
|
||||
|
||||
// The disk was not found on the VM, this is an error.
|
||||
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))
|
||||
}
|
||||
NewStepDeleteAdditionalDisks(s.client, ui).Run(context.TODO(), state)
|
||||
}
|
||||
|
||||
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
|
||||
}
|
||||
|
||||
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
|
||||
resourceGroupName := state.Get(constants.ArmResourceGroupName).(string)
|
||||
ui := state.Get("ui").(packersdk.Ui)
|
||||
@@ -238,8 +222,6 @@ func (s *StepDeployTemplate) deleteDeploymentResources(ctx context.Context, depl
|
||||
return err
|
||||
}
|
||||
|
||||
resources := map[string]string{}
|
||||
|
||||
for deploymentOperations.NotDone() {
|
||||
deploymentOperation := deploymentOperations.Value()
|
||||
// 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
|
||||
resourceType := *deploymentOperation.Properties.TargetResource.ResourceType
|
||||
|
||||
s.say(fmt.Sprintf("Adding to deletion queue -> %s : '%s'", resourceType, resourceName))
|
||||
resources[resourceType] = resourceName
|
||||
s.say(fmt.Sprintf(" -> %s : '%s'", 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 {
|
||||
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
|
||||
}
|
||||
|
||||
|
||||
@@ -6,7 +6,6 @@ import (
|
||||
"testing"
|
||||
|
||||
"github.com/hashicorp/packer-plugin-sdk/multistep"
|
||||
packersdk "github.com/hashicorp/packer-plugin-sdk/packer"
|
||||
"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 {
|
||||
stateBag := new(multistep.BasicStateBag)
|
||||
|
||||
stateBag.Put(constants.ArmDeploymentName, "Unit Test: DeploymentName")
|
||||
stateBag.Put(constants.ArmResourceGroupName, "Unit Test: ResourceGroupName")
|
||||
stateBag.Put(constants.ArmComputeName, "Unit Test: ComputeName")
|
||||
|
||||
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
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
@@ -22,7 +22,6 @@ type FlatConfig struct {
|
||||
ClientID *string `mapstructure:"client_id" cty:"client_id" hcl:"client_id"`
|
||||
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"`
|
||||
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"`
|
||||
ObjectID *string `mapstructure:"object_id" cty:"object_id" hcl:"object_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_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_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},
|
||||
"object_id": &hcldec.AttrSpec{Name: "object_id", Type: cty.String, Required: false},
|
||||
"tenant_id": &hcldec.AttrSpec{Name: "tenant_id", Type: cty.String, Required: false},
|
||||
|
||||
@@ -39,8 +39,6 @@ type Config struct {
|
||||
// The path to a pem-encoded certificate that will be used to authenticate
|
||||
// as the specified AAD SP.
|
||||
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
|
||||
// to authenticate the AAD SP. Provides more control over token the expiration
|
||||
// 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 {
|
||||
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
|
||||
}
|
||||
|
||||
@@ -264,7 +259,7 @@ func (c Config) GetServicePrincipalToken(
|
||||
auth = NewSecretOAuthTokenProvider(*c.cloudEnvironment, c.ClientID, c.ClientSecret, c.TenantID)
|
||||
case authTypeClientCert:
|
||||
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 {
|
||||
return nil, err
|
||||
}
|
||||
@@ -341,10 +336,6 @@ func (c *Config) FillParameters() error {
|
||||
c.TenantID = tenantID
|
||||
}
|
||||
|
||||
if c.ClientCertExpireTimeout == 0 {
|
||||
c.ClientCertExpireTimeout = time.Hour
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
|
||||
@@ -95,16 +95,6 @@ func Test_ClientConfig_RequiredParametersSet(t *testing.T) {
|
||||
},
|
||||
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",
|
||||
config: Config{
|
||||
|
||||
@@ -18,14 +18,14 @@ import (
|
||||
"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)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("Error reading certificate: %v", err)
|
||||
}
|
||||
|
||||
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 {
|
||||
return nil, fmt.Errorf("Error generating JWT: %v", err)
|
||||
}
|
||||
|
||||
@@ -49,7 +49,6 @@ type FlatConfig struct {
|
||||
ClientID *string `mapstructure:"client_id" cty:"client_id" hcl:"client_id"`
|
||||
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"`
|
||||
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"`
|
||||
ObjectID *string `mapstructure:"object_id" cty:"object_id" hcl:"object_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_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_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},
|
||||
"object_id": &hcldec.AttrSpec{Name: "object_id", Type: cty.String, Required: false},
|
||||
"tenant_id": &hcldec.AttrSpec{Name: "tenant_id", Type: cty.String, Required: false},
|
||||
|
||||
@@ -60,7 +60,12 @@ func (b *Builder) Run(ctx context.Context, ui packersdk.Ui, hook packersdk.Hook)
|
||||
// Build the steps.
|
||||
steps := []multistep.Step{
|
||||
&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{
|
||||
Debug: b.config.PackerDebug,
|
||||
Comm: &b.config.Comm,
|
||||
|
||||
@@ -19,7 +19,6 @@ type FlatConfig struct {
|
||||
PackerUserVars map[string]string `mapstructure:"packer_user_variables" cty:"packer_user_variables" hcl:"packer_user_variables"`
|
||||
PackerSensitiveVars []string `mapstructure:"packer_sensitive_variables" cty:"packer_sensitive_variables" hcl:"packer_sensitive_variables"`
|
||||
HTTPDir *string `mapstructure:"http_directory" cty:"http_directory" hcl:"http_directory"`
|
||||
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"`
|
||||
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"`
|
||||
@@ -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_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_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_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},
|
||||
|
||||
@@ -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)
|
||||
}
|
||||
@@ -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)
|
||||
}
|
||||
}
|
||||
@@ -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)
|
||||
}
|
||||
@@ -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,
|
||||
})
|
||||
}
|
||||
+1
-2
@@ -114,8 +114,7 @@ type Config struct {
|
||||
// for the duration of the pull. If true login_server is required and
|
||||
// login, login_username, and login_password will be ignored. For more
|
||||
// information see the section on ECR.
|
||||
EcrLogin bool `mapstructure:"ecr_login" required:"false"`
|
||||
AwsAccessConfig `mapstructure:",squash"`
|
||||
EcrLogin bool `mapstructure:"ecr_login" required:"false"`
|
||||
|
||||
ctx interpolate.Context
|
||||
}
|
||||
-8
@@ -92,10 +92,6 @@ type FlatConfig struct {
|
||||
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"`
|
||||
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.
|
||||
@@ -192,10 +188,6 @@ func (*FlatConfig) HCL2Spec() map[string]hcldec.Spec {
|
||||
"login_server": &hcldec.AttrSpec{Name: "login_server", 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},
|
||||
"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
|
||||
}
|
||||
@@ -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")
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,7 @@
|
||||
package docker
|
||||
|
||||
import "testing"
|
||||
|
||||
func TestDockerDriver_impl(t *testing.T) {
|
||||
var _ Driver = new(DockerDriver)
|
||||
}
|
||||
@@ -0,0 +1,7 @@
|
||||
package docker
|
||||
|
||||
import "testing"
|
||||
|
||||
func TestMockDriver_impl(t *testing.T) {
|
||||
var _ Driver = new(MockDriver)
|
||||
}
|
||||
@@ -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")
|
||||
}
|
||||
}
|
||||
@@ -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")
|
||||
}
|
||||
}
|
||||
+1
-12
@@ -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))
|
||||
|
||||
if config.EcrLogin {
|
||||
ui.Message("Fetching ECR credentials...")
|
||||
|
||||
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
|
||||
panic("to reimplement")
|
||||
}
|
||||
|
||||
driver := state.Get("driver").(Driver)
|
||||
@@ -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")
|
||||
}
|
||||
}
|
||||
@@ -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)
|
||||
}
|
||||
}
|
||||
@@ -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)
|
||||
}
|
||||
@@ -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!
|
||||
@@ -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)
|
||||
}
|
||||
@@ -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.
|
||||
// - 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"`
|
||||
// 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"`.
|
||||
// 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
|
||||
@@ -544,10 +542,6 @@ func (c *Config) Prepare(raws ...interface{}) ([]string, error) {
|
||||
c.WrapStartupScriptFile = config.TriTrue
|
||||
}
|
||||
}
|
||||
// Check windows password timeout is provided
|
||||
if c.WindowsPasswordTimeout == 0 {
|
||||
c.WindowsPasswordTimeout = 3 * time.Minute
|
||||
}
|
||||
|
||||
// Check for any errors.
|
||||
if errs != nil && len(errs.Errors) > 0 {
|
||||
|
||||
@@ -112,7 +112,6 @@ type FlatConfig struct {
|
||||
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"`
|
||||
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"`
|
||||
Subnetwork *string `mapstructure:"subnetwork" required:"false" cty:"subnetwork" hcl:"subnetwork"`
|
||||
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_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},
|
||||
"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},
|
||||
"subnetwork": &hcldec.AttrSpec{Name: "subnetwork", Type: cty.String, Required: false},
|
||||
"tags": &hcldec.AttrSpec{Name: "tags", Type: cty.List(cty.String), Required: false},
|
||||
|
||||
@@ -107,14 +107,13 @@ type InstanceConfig struct {
|
||||
// WindowsPasswordConfig is the data structure that GCE needs to encrypt the created
|
||||
// windows password.
|
||||
type WindowsPasswordConfig struct {
|
||||
key *rsa.PrivateKey
|
||||
password string
|
||||
UserName string `json:"userName"`
|
||||
Modulus string `json:"modulus"`
|
||||
Exponent string `json:"exponent"`
|
||||
Email string `json:"email"`
|
||||
ExpireOn time.Time `json:"expireOn"`
|
||||
WindowsPasswordTimeout time.Duration `json:"timeout"`
|
||||
key *rsa.PrivateKey
|
||||
password string
|
||||
UserName string `json:"userName"`
|
||||
Modulus string `json:"modulus"`
|
||||
Exponent string `json:"exponent"`
|
||||
Email string `json:"email"`
|
||||
ExpireOn time.Time `json:"expireOn"`
|
||||
}
|
||||
|
||||
type windowsPasswordResponse struct {
|
||||
|
||||
@@ -253,6 +253,7 @@ func (d *driverGCE) GetImage(name string, fromFamily bool) (*Image, error) {
|
||||
"ubuntu-os-cloud",
|
||||
"windows-cloud",
|
||||
"windows-sql-cloud",
|
||||
"gce-uefi-images",
|
||||
"gce-nvme",
|
||||
// misc
|
||||
"google-containers",
|
||||
@@ -567,7 +568,7 @@ func (d *driverGCE) createWindowsPassword(errCh chan<- error, name, zone string,
|
||||
return
|
||||
}
|
||||
|
||||
timeout := time.Now().Add(c.WindowsPasswordTimeout)
|
||||
timeout := time.Now().Add(time.Minute * 3)
|
||||
hash := sha1.New()
|
||||
random := rand.Reader
|
||||
|
||||
|
||||
@@ -60,13 +60,12 @@ func (s *StepCreateWindowsPassword) Run(ctx context.Context, state multistep.Sta
|
||||
}
|
||||
|
||||
data := WindowsPasswordConfig{
|
||||
key: priv,
|
||||
UserName: c.Comm.WinRMUser,
|
||||
Modulus: base64.StdEncoding.EncodeToString(priv.N.Bytes()),
|
||||
Exponent: base64.StdEncoding.EncodeToString(buf[1:]),
|
||||
Email: email,
|
||||
ExpireOn: time.Now().Add(time.Minute * 5),
|
||||
WindowsPasswordTimeout: c.WindowsPasswordTimeout,
|
||||
key: priv,
|
||||
UserName: c.Comm.WinRMUser,
|
||||
Modulus: base64.StdEncoding.EncodeToString(priv.N.Bytes()),
|
||||
Exponent: base64.StdEncoding.EncodeToString(buf[1:]),
|
||||
Email: email,
|
||||
ExpireOn: time.Now().Add(time.Minute * 5),
|
||||
}
|
||||
|
||||
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...")
|
||||
select {
|
||||
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")
|
||||
}
|
||||
}
|
||||
|
||||
@@ -211,7 +211,12 @@ func (b *Builder) Run(ctx context.Context, ui packersdk.Ui, hook packersdk.Hook)
|
||||
Directories: b.config.FloppyConfig.FloppyDirectories,
|
||||
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{
|
||||
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
|
||||
&communicator.StepConnect{
|
||||
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(),
|
||||
},
|
||||
|
||||
|
||||
@@ -19,7 +19,6 @@ type FlatConfig struct {
|
||||
PackerUserVars map[string]string `mapstructure:"packer_user_variables" cty:"packer_user_variables" hcl:"packer_user_variables"`
|
||||
PackerSensitiveVars []string `mapstructure:"packer_sensitive_variables" cty:"packer_sensitive_variables" hcl:"packer_sensitive_variables"`
|
||||
HTTPDir *string `mapstructure:"http_directory" cty:"http_directory" hcl:"http_directory"`
|
||||
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"`
|
||||
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"`
|
||||
@@ -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_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_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_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},
|
||||
|
||||
@@ -251,7 +251,12 @@ func (b *Builder) Run(ctx context.Context, ui packersdk.Ui, hook packersdk.Hook)
|
||||
Directories: b.config.FloppyConfig.FloppyDirectories,
|
||||
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{
|
||||
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
|
||||
&communicator.StepConnect{
|
||||
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(),
|
||||
},
|
||||
|
||||
|
||||
@@ -19,7 +19,6 @@ type FlatConfig struct {
|
||||
PackerUserVars map[string]string `mapstructure:"packer_user_variables" cty:"packer_user_variables" hcl:"packer_user_variables"`
|
||||
PackerSensitiveVars []string `mapstructure:"packer_sensitive_variables" cty:"packer_sensitive_variables" hcl:"packer_sensitive_variables"`
|
||||
HTTPDir *string `mapstructure:"http_directory" cty:"http_directory" hcl:"http_directory"`
|
||||
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"`
|
||||
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"`
|
||||
@@ -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_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_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_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},
|
||||
|
||||
@@ -151,7 +151,7 @@ func (b *Builder) Run(ctx context.Context, ui packersdk.Ui, hook packersdk.Hook)
|
||||
&communicator.StepConnect{
|
||||
Config: &b.config.RunConfig.Comm,
|
||||
Host: CommHost(
|
||||
b.config.RunConfig.Comm.Host(),
|
||||
b.config.RunConfig.Comm.SSHHost,
|
||||
computeClient,
|
||||
b.config.SSHInterface,
|
||||
b.config.SSHIPVersion),
|
||||
|
||||
@@ -4,7 +4,7 @@ import (
|
||||
"context"
|
||||
"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.
|
||||
|
||||
@@ -12,7 +12,7 @@ import (
|
||||
"github.com/hashicorp/packer-plugin-sdk/multistep/commonsteps"
|
||||
packersdk "github.com/hashicorp/packer-plugin-sdk/packer"
|
||||
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
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -19,8 +19,8 @@ import (
|
||||
"github.com/hashicorp/packer-plugin-sdk/pathing"
|
||||
"github.com/hashicorp/packer-plugin-sdk/template/config"
|
||||
"github.com/hashicorp/packer-plugin-sdk/template/interpolate"
|
||||
ocicommon "github.com/oracle/oci-go-sdk/v36/common"
|
||||
ociauth "github.com/oracle/oci-go-sdk/v36/common/auth"
|
||||
ocicommon "github.com/oracle/oci-go-sdk/common"
|
||||
ociauth "github.com/oracle/oci-go-sdk/common/auth"
|
||||
)
|
||||
|
||||
type CreateVNICDetails struct {
|
||||
@@ -46,11 +46,6 @@ type ListImagesRequest struct {
|
||||
Shape *string `mapstructure:"shape"`
|
||||
}
|
||||
|
||||
type FlexShapeConfig struct {
|
||||
Ocpus *float32 `mapstructure:"ocpus" required:"false"`
|
||||
MemoryInGBs *float32 `mapstructure:"memory_in_gbs" required:"false"`
|
||||
}
|
||||
|
||||
type Config struct {
|
||||
common.PackerConfig `mapstructure:",squash"`
|
||||
Comm communicator.Config `mapstructure:",squash"`
|
||||
@@ -96,7 +91,6 @@ type Config struct {
|
||||
InstanceTags map[string]string `mapstructure:"instance_tags"`
|
||||
InstanceDefinedTags map[string]map[string]interface{} `mapstructure:"instance_defined_tags"`
|
||||
Shape string `mapstructure:"shape"`
|
||||
ShapeConfig FlexShapeConfig `mapstructure:"shape_config"`
|
||||
BootVolumeSizeInGBs int64 `mapstructure:"disk_size"`
|
||||
|
||||
// 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{
|
||||
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 {
|
||||
@@ -259,7 +253,7 @@ func (c *Config) Prepare(raws ...interface{}) error {
|
||||
|
||||
if _, err := configProvider.PrivateRSAKey(); err != nil {
|
||||
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
|
||||
@@ -283,18 +277,6 @@ func (c *Config) Prepare(raws ...interface{}) error {
|
||||
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) {
|
||||
errs = packersdk.MultiErrorAppend(
|
||||
errs, errors.New("'subnet_ocid' must be specified"))
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -88,7 +88,6 @@ type FlatConfig struct {
|
||||
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"`
|
||||
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"`
|
||||
Metadata map[string]string `mapstructure:"metadata" cty:"metadata" hcl:"metadata"`
|
||||
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_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_config": &hcldec.BlockSpec{TypeName: "shape_config", Nested: hcldec.ObjectSpec((*FlatFlexShapeConfig)(nil).HCL2Spec())},
|
||||
"disk_size": &hcldec.AttrSpec{Name: "disk_size", Type: cty.Number, 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},
|
||||
@@ -241,31 +239,6 @@ func (*FlatCreateVNICDetails) HCL2Spec() map[string]hcldec.Spec {
|
||||
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.
|
||||
// Where the contents of a field with a `mapstructure:,squash` tag are bubbled up.
|
||||
type FlatListImagesRequest struct {
|
||||
|
||||
@@ -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
|
||||
}
|
||||
@@ -131,7 +131,7 @@ func TestConfig(t *testing.T) {
|
||||
|
||||
t.Run("NoAccessConfig", func(t *testing.T) {
|
||||
raw := testConfig(cfgFile)
|
||||
raw["access_cfg_file"] = "/tmp/random/access/config/file/should/not/exist"
|
||||
delete(raw, "access_cfg_file")
|
||||
|
||||
var c Config
|
||||
errs := c.Prepare(raw)
|
||||
@@ -140,10 +140,6 @@ func TestConfig(t *testing.T) {
|
||||
"'user_ocid'", "'tenancy_ocid'", "'fingerprint'", "'key_file'",
|
||||
}
|
||||
|
||||
if errs == nil {
|
||||
t.Fatalf("Expected errors %q but got none", expectedErrors)
|
||||
}
|
||||
|
||||
s := errs.Error()
|
||||
for _, expected := range expectedErrors {
|
||||
if !strings.Contains(s, expected) {
|
||||
|
||||
@@ -3,7 +3,7 @@ package oci
|
||||
import (
|
||||
"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.
|
||||
|
||||
@@ -3,7 +3,7 @@ package oci
|
||||
import (
|
||||
"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
|
||||
|
||||
@@ -4,15 +4,10 @@ import (
|
||||
"context"
|
||||
"errors"
|
||||
"fmt"
|
||||
"math"
|
||||
"math/rand"
|
||||
"net/http"
|
||||
"regexp"
|
||||
"sync/atomic"
|
||||
"time"
|
||||
|
||||
"github.com/oracle/oci-go-sdk/v36/common"
|
||||
core "github.com/oracle/oci-go-sdk/v36/core"
|
||||
core "github.com/oracle/oci-go-sdk/core"
|
||||
)
|
||||
|
||||
// driverOCI implements the Driver interface and communicates with Oracle
|
||||
@@ -24,31 +19,6 @@ type driverOCI struct {
|
||||
context context.Context
|
||||
}
|
||||
|
||||
var retryPolicy = &common.RetryPolicy{
|
||||
MaximumNumberAttempts: 10,
|
||||
ShouldRetryOperation: func(res common.OCIOperationResponse) bool {
|
||||
var e common.ServiceError
|
||||
if errors.As(res.Error, &e) {
|
||||
switch e.GetHTTPStatusCode() {
|
||||
case http.StatusTooManyRequests, http.StatusInternalServerError, http.StatusServiceUnavailable:
|
||||
return true
|
||||
}
|
||||
}
|
||||
return false
|
||||
},
|
||||
NextDuration: func(res common.OCIOperationResponse) time.Duration {
|
||||
x := uint64(res.AttemptNumber)
|
||||
d := time.Duration(math.Pow(2, float64(atomic.LoadUint64(&x)))) * time.Second
|
||||
j := time.Duration(rand.Float64()*(2000)) * time.Millisecond
|
||||
w := d + j
|
||||
return w
|
||||
},
|
||||
}
|
||||
|
||||
var requestMetadata = common.RequestMetadata{
|
||||
RetryPolicy: retryPolicy,
|
||||
}
|
||||
|
||||
// NewDriverOCI Creates a new driverOCI with a connected compute client and a connected vcn client.
|
||||
func NewDriverOCI(cfg *Config) (Driver, error) {
|
||||
coreClient, err := core.NewComputeClientWithConfigurationProvider(cfg.configProvider)
|
||||
@@ -110,7 +80,6 @@ func (d *driverOCI) CreateInstance(ctx context.Context, publicKey string) (strin
|
||||
LifecycleState: "AVAILABLE",
|
||||
SortBy: "TIMECREATED",
|
||||
SortOrder: "DESC",
|
||||
RequestMetadata: requestMetadata,
|
||||
})
|
||||
if err != nil {
|
||||
return "", err
|
||||
@@ -158,18 +127,7 @@ func (d *driverOCI) CreateInstance(ctx context.Context, publicKey string) (strin
|
||||
Metadata: metadata,
|
||||
}
|
||||
|
||||
if d.cfg.ShapeConfig.Ocpus != nil {
|
||||
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,
|
||||
})
|
||||
instance, err := d.computeClient.LaunchInstance(context.TODO(), core.LaunchInstanceRequest{LaunchInstanceDetails: instanceDetails})
|
||||
|
||||
if err != nil {
|
||||
return "", err
|
||||
@@ -187,9 +145,7 @@ func (d *driverOCI) CreateImage(ctx context.Context, id string) (core.Image, err
|
||||
FreeformTags: d.cfg.Tags,
|
||||
DefinedTags: d.cfg.DefinedTags,
|
||||
LaunchMode: core.CreateImageDetailsLaunchModeEnum(d.cfg.LaunchMode),
|
||||
},
|
||||
RequestMetadata: requestMetadata,
|
||||
})
|
||||
}})
|
||||
|
||||
if err != nil {
|
||||
return core.Image{}, err
|
||||
@@ -200,19 +156,15 @@ func (d *driverOCI) CreateImage(ctx context.Context, id string) (core.Image, err
|
||||
|
||||
// DeleteImage deletes a custom image.
|
||||
func (d *driverOCI) DeleteImage(ctx context.Context, id string) error {
|
||||
_, err := d.computeClient.DeleteImage(ctx, core.DeleteImageRequest{
|
||||
ImageId: &id,
|
||||
RequestMetadata: requestMetadata,
|
||||
})
|
||||
_, err := d.computeClient.DeleteImage(ctx, core.DeleteImageRequest{ImageId: &id})
|
||||
return err
|
||||
}
|
||||
|
||||
// GetInstanceIP returns the public or private IP corresponding to the given instance id.
|
||||
func (d *driverOCI) GetInstanceIP(ctx context.Context, id string) (string, error) {
|
||||
vnics, err := d.computeClient.ListVnicAttachments(ctx, core.ListVnicAttachmentsRequest{
|
||||
InstanceId: &id,
|
||||
CompartmentId: &d.cfg.CompartmentID,
|
||||
RequestMetadata: requestMetadata,
|
||||
InstanceId: &id,
|
||||
CompartmentId: &d.cfg.CompartmentID,
|
||||
})
|
||||
if err != nil {
|
||||
return "", err
|
||||
@@ -222,10 +174,7 @@ func (d *driverOCI) GetInstanceIP(ctx context.Context, id string) (string, error
|
||||
return "", errors.New("instance has zero VNICs")
|
||||
}
|
||||
|
||||
vnic, err := d.vcnClient.GetVnic(ctx, core.GetVnicRequest{
|
||||
VnicId: vnics.Items[0].VnicId,
|
||||
RequestMetadata: requestMetadata,
|
||||
})
|
||||
vnic, err := d.vcnClient.GetVnic(ctx, core.GetVnicRequest{VnicId: vnics.Items[0].VnicId})
|
||||
if err != nil {
|
||||
return "", fmt.Errorf("Error getting VNIC details: %s", err)
|
||||
}
|
||||
@@ -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) {
|
||||
credentials, err := d.computeClient.GetWindowsInstanceInitialCredentials(ctx, core.GetWindowsInstanceInitialCredentialsRequest{
|
||||
InstanceId: &id,
|
||||
RequestMetadata: requestMetadata,
|
||||
InstanceId: &id,
|
||||
})
|
||||
if err != nil {
|
||||
return "", "", err
|
||||
@@ -256,8 +204,7 @@ func (d *driverOCI) GetInstanceInitialCredentials(ctx context.Context, id string
|
||||
// TerminateInstance terminates a compute instance.
|
||||
func (d *driverOCI) TerminateInstance(ctx context.Context, id string) error {
|
||||
_, err := d.computeClient.TerminateInstance(ctx, core.TerminateInstanceRequest{
|
||||
InstanceId: &id,
|
||||
RequestMetadata: requestMetadata,
|
||||
InstanceId: &id,
|
||||
})
|
||||
return err
|
||||
}
|
||||
@@ -267,10 +214,7 @@ func (d *driverOCI) TerminateInstance(ctx context.Context, id string) error {
|
||||
func (d *driverOCI) WaitForImageCreation(ctx context.Context, id string) error {
|
||||
return waitForResourceToReachState(
|
||||
func(string) (string, error) {
|
||||
image, err := d.computeClient.GetImage(ctx, core.GetImageRequest{
|
||||
ImageId: &id,
|
||||
RequestMetadata: requestMetadata,
|
||||
})
|
||||
image, err := d.computeClient.GetImage(ctx, core.GetImageRequest{ImageId: &id})
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
@@ -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 {
|
||||
return waitForResourceToReachState(
|
||||
func(string) (string, error) {
|
||||
instance, err := d.computeClient.GetInstance(ctx, core.GetInstanceRequest{
|
||||
InstanceId: &id,
|
||||
RequestMetadata: requestMetadata,
|
||||
})
|
||||
instance, err := d.computeClient.GetInstance(ctx, core.GetInstanceRequest{InstanceId: &id})
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
|
||||
@@ -3,8 +3,6 @@ package oci
|
||||
import (
|
||||
"crypto/rand"
|
||||
"crypto/rsa"
|
||||
|
||||
"github.com/oracle/oci-go-sdk/v36/common"
|
||||
)
|
||||
|
||||
// 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) {
|
||||
return "some_random_region", nil
|
||||
}
|
||||
|
||||
func (p instancePrincipalConfigurationProviderMock) AuthType() (common.AuthConfig, error) {
|
||||
return common.AuthConfig{
|
||||
AuthType: common.InstancePrincipal,
|
||||
IsFromConfigFile: false,
|
||||
OboToken: nil}, nil
|
||||
}
|
||||
|
||||
@@ -32,9 +32,6 @@ func (s *stepCreateOMI) Run(ctx context.Context, state multistep.StateBag) multi
|
||||
ImageName: omiName,
|
||||
BlockDeviceMappings: config.BlockDevices.BuildOscOMIDevices(),
|
||||
}
|
||||
if config.OMIDescription != "" {
|
||||
createOpts.Description = config.OMIDescription
|
||||
}
|
||||
|
||||
resp, _, err := oscconn.ImageApi.CreateImage(context.Background(), &osc.CreateImageOpts{
|
||||
CreateImageRequest: optional.NewInterface(createOpts),
|
||||
|
||||
@@ -37,9 +37,6 @@ func (s *StepRegisterOMI) Run(ctx context.Context, state multistep.StateBag) mul
|
||||
BlockDeviceMappings: blockDevices,
|
||||
}
|
||||
|
||||
if config.OMIDescription != "" {
|
||||
registerOpts.Description = config.OMIDescription
|
||||
}
|
||||
registerResp, _, err := oscconn.ImageApi.CreateImage(context.Background(), &osc.CreateImageOpts{
|
||||
CreateImageRequest: optional.NewInterface(registerOpts),
|
||||
})
|
||||
|
||||
@@ -75,10 +75,6 @@ func (s *StepCreateOMI) Run(ctx context.Context, state multistep.StateBag) multi
|
||||
registerOpts = buildRegisterOpts(config, image, newMappings)
|
||||
}
|
||||
|
||||
if config.OMIDescription != "" {
|
||||
registerOpts.Description = config.OMIDescription
|
||||
}
|
||||
|
||||
registerResp, _, err := osconn.ImageApi.CreateImage(context.Background(), &osc.CreateImageOpts{
|
||||
CreateImageRequest: optional.NewInterface(registerOpts),
|
||||
})
|
||||
|
||||
@@ -209,7 +209,12 @@ func (b *Builder) Run(ctx context.Context, ui packersdk.Ui, hook packersdk.Hook)
|
||||
Directories: b.config.FloppyConfig.FloppyDirectories,
|
||||
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(stepCreateDisk),
|
||||
new(stepSetBootOrder),
|
||||
@@ -233,7 +238,7 @@ func (b *Builder) Run(ctx context.Context, ui packersdk.Ui, hook packersdk.Hook)
|
||||
},
|
||||
&communicator.StepConnect{
|
||||
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(),
|
||||
},
|
||||
¶llelscommon.StepUploadVersion{
|
||||
|
||||
@@ -19,7 +19,6 @@ type FlatConfig struct {
|
||||
PackerUserVars map[string]string `mapstructure:"packer_user_variables" cty:"packer_user_variables" hcl:"packer_user_variables"`
|
||||
PackerSensitiveVars []string `mapstructure:"packer_sensitive_variables" cty:"packer_sensitive_variables" hcl:"packer_sensitive_variables"`
|
||||
HTTPDir *string `mapstructure:"http_directory" cty:"http_directory" hcl:"http_directory"`
|
||||
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"`
|
||||
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"`
|
||||
@@ -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_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_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_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},
|
||||
|
||||
@@ -87,7 +87,7 @@ func (b *Builder) Run(ctx context.Context, ui packersdk.Ui, hook packersdk.Hook)
|
||||
},
|
||||
&communicator.StepConnect{
|
||||
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(),
|
||||
},
|
||||
¶llelscommon.StepUploadVersion{
|
||||
|
||||
@@ -22,11 +22,8 @@ func (s *stepCreateServer) Run(ctx context.Context, state multistep.StateBag) mu
|
||||
|
||||
profitbricks.SetAuth(c.PBUsername, c.PBPassword)
|
||||
profitbricks.SetDepth("5")
|
||||
if c.Comm.SSHPublicKey != nil {
|
||||
c.SSHKey = string(c.Comm.SSHPublicKey)
|
||||
} else {
|
||||
ui.Error("No ssh private key set; ssh authentication won't be possible. Please specify your private key in the ssh_private_key_file configuration key.")
|
||||
return multistep.ActionHalt
|
||||
if sshkey, ok := state.GetOk("publicKey"); ok {
|
||||
c.SSHKey = sshkey.(string)
|
||||
}
|
||||
ui.Say("Creating Virtual Data Center...")
|
||||
img := s.getImageId(c.Image, c)
|
||||
@@ -207,7 +204,7 @@ func (d *stepCreateServer) setPB(username string, password string, url string) {
|
||||
|
||||
func (d *stepCreateServer) checkForErrors(instance profitbricks.Resp) error {
|
||||
if instance.StatusCode > 299 {
|
||||
return fmt.Errorf("Error occurred %s", string(instance.Body))
|
||||
return errors.New(fmt.Sprintf("Error occurred %s", string(instance.Body)))
|
||||
}
|
||||
return nil
|
||||
}
|
||||
@@ -264,9 +261,7 @@ func (d *stepCreateServer) getImageAlias(imageAlias string, location string, ui
|
||||
|
||||
func parseErrorMessage(raw string) (toreturn string) {
|
||||
var tmp map[string]interface{}
|
||||
if json.Unmarshal([]byte(raw), &tmp) != nil {
|
||||
return ""
|
||||
}
|
||||
json.Unmarshal([]byte(raw), &tmp)
|
||||
|
||||
for _, v := range tmp["messages"].([]interface{}) {
|
||||
for index, i := range v.(map[string]interface{}) {
|
||||
|
||||
@@ -3,9 +3,6 @@ package profitbricks
|
||||
import (
|
||||
"context"
|
||||
"encoding/json"
|
||||
"errors"
|
||||
"fmt"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
"github.com/hashicorp/packer-plugin-sdk/multistep"
|
||||
@@ -25,32 +22,9 @@ func (s *stepTakeSnapshot) Run(ctx context.Context, state multistep.StateBag) mu
|
||||
|
||||
dcId := state.Get("datacenter_id").(string)
|
||||
volumeId := state.Get("volume_id").(string)
|
||||
serverId := state.Get("instance_id").(string)
|
||||
|
||||
comm, _ := state.Get("communicator").(packersdk.Communicator)
|
||||
if comm == nil {
|
||||
ui.Error("no communicator found")
|
||||
return multistep.ActionHalt
|
||||
}
|
||||
|
||||
/* sync fs changes from the provisioning step */
|
||||
os, err := s.getOs(dcId, serverId)
|
||||
if err != nil {
|
||||
ui.Error(fmt.Sprintf("an error occurred while getting the server os: %s", err.Error()))
|
||||
return multistep.ActionHalt
|
||||
}
|
||||
ui.Say(fmt.Sprintf("Server OS is %s", os))
|
||||
|
||||
switch strings.ToLower(os) {
|
||||
case "linux":
|
||||
ui.Say("syncing file system changes")
|
||||
if err := s.syncFs(ctx, comm); err != nil {
|
||||
ui.Error(fmt.Sprintf("error syncing fs changes: %s", err.Error()))
|
||||
return multistep.ActionHalt
|
||||
}
|
||||
}
|
||||
|
||||
snapshot := profitbricks.CreateSnapshot(dcId, volumeId, c.SnapshotName, "")
|
||||
|
||||
state.Put("snapshotname", c.SnapshotName)
|
||||
|
||||
if snapshot.StatusCode > 299 {
|
||||
@@ -68,123 +42,31 @@ func (s *stepTakeSnapshot) Run(ctx context.Context, state multistep.StateBag) mu
|
||||
return multistep.ActionHalt
|
||||
}
|
||||
|
||||
ui.Say(fmt.Sprintf("Creating a snapshot for %s/volumes/%s", dcId, volumeId))
|
||||
|
||||
err = s.waitForRequest(snapshot.Headers.Get("Location"), *c, ui)
|
||||
if err != nil {
|
||||
ui.Error(fmt.Sprintf("An error occurred while waiting for the request to be done: %s", err.Error()))
|
||||
return multistep.ActionHalt
|
||||
}
|
||||
|
||||
err = s.waitTillSnapshotAvailable(snapshot.Id, *c, ui)
|
||||
if err != nil {
|
||||
ui.Error(fmt.Sprintf("An error occurred while waiting for the snapshot to be created: %s", err.Error()))
|
||||
return multistep.ActionHalt
|
||||
}
|
||||
s.waitTillProvisioned(snapshot.Headers.Get("Location"), *c)
|
||||
|
||||
return multistep.ActionContinue
|
||||
}
|
||||
|
||||
func (s *stepTakeSnapshot) Cleanup(_ multistep.StateBag) {
|
||||
func (s *stepTakeSnapshot) Cleanup(state multistep.StateBag) {
|
||||
}
|
||||
|
||||
func (s *stepTakeSnapshot) waitForRequest(path string, config Config, ui packersdk.Ui) error {
|
||||
|
||||
ui.Say(fmt.Sprintf("Watching request %s", path))
|
||||
s.setPB(config.PBUsername, config.PBPassword, config.PBUrl)
|
||||
func (d *stepTakeSnapshot) waitTillProvisioned(path string, config Config) {
|
||||
d.setPB(config.PBUsername, config.PBPassword, config.PBUrl)
|
||||
waitCount := 50
|
||||
var waitInterval = 10 * time.Second
|
||||
if config.Retries > 0 {
|
||||
waitCount = config.Retries
|
||||
}
|
||||
done := false
|
||||
for i := 0; i < waitCount; i++ {
|
||||
request := profitbricks.GetRequestStatus(path)
|
||||
ui.Say(fmt.Sprintf("request status = %s", request.Metadata.Status))
|
||||
if request.Metadata.Status == "DONE" {
|
||||
done = true
|
||||
break
|
||||
}
|
||||
if request.Metadata.Status == "FAILED" {
|
||||
return fmt.Errorf("Request failed: %s", request.Response)
|
||||
}
|
||||
time.Sleep(waitInterval)
|
||||
time.Sleep(10 * time.Second)
|
||||
i++
|
||||
}
|
||||
|
||||
if done == false {
|
||||
return fmt.Errorf("request not fulfilled after waiting %d seconds",
|
||||
int64(waitCount)*int64(waitInterval)/int64(time.Second))
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (s *stepTakeSnapshot) waitTillSnapshotAvailable(id string, config Config, ui packersdk.Ui) error {
|
||||
s.setPB(config.PBUsername, config.PBPassword, config.PBUrl)
|
||||
waitCount := 50
|
||||
var waitInterval = 10 * time.Second
|
||||
if config.Retries > 0 {
|
||||
waitCount = config.Retries
|
||||
}
|
||||
done := false
|
||||
ui.Say(fmt.Sprintf("waiting for snapshot %s to become available", id))
|
||||
for i := 0; i < waitCount; i++ {
|
||||
snap := profitbricks.GetSnapshot(id)
|
||||
ui.Say(fmt.Sprintf("snapshot status = %s", snap.Metadata.State))
|
||||
if snap.StatusCode != 200 {
|
||||
return fmt.Errorf("%s", snap.Response)
|
||||
}
|
||||
if snap.Metadata.State == "AVAILABLE" {
|
||||
done = true
|
||||
break
|
||||
}
|
||||
time.Sleep(waitInterval)
|
||||
i++
|
||||
ui.Say(fmt.Sprintf("... still waiting, %d seconds have passed", int64(waitInterval)*int64(i)))
|
||||
}
|
||||
|
||||
if done == false {
|
||||
return fmt.Errorf("snapshot not created after waiting %d seconds",
|
||||
int64(waitCount)*int64(waitInterval)/int64(time.Second))
|
||||
}
|
||||
|
||||
ui.Say("snapshot created")
|
||||
return nil
|
||||
}
|
||||
|
||||
func (s *stepTakeSnapshot) syncFs(ctx context.Context, comm packersdk.Communicator) error {
|
||||
cmd := &packersdk.RemoteCmd{
|
||||
Command: "sync",
|
||||
}
|
||||
if err := comm.Start(ctx, cmd); err != nil {
|
||||
return err
|
||||
}
|
||||
if cmd.Wait() != 0 {
|
||||
return fmt.Errorf("sync command exited with code %d", cmd.ExitStatus())
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (s *stepTakeSnapshot) getOs(dcId string, serverId string) (string, error) {
|
||||
server := profitbricks.GetServer(dcId, serverId)
|
||||
if server.StatusCode != 200 {
|
||||
return "", errors.New(server.Response)
|
||||
}
|
||||
|
||||
if server.Properties.BootVolume == nil {
|
||||
return "", errors.New("no boot volume found on server")
|
||||
}
|
||||
|
||||
volumeId := server.Properties.BootVolume.Id
|
||||
volume := profitbricks.GetVolume(dcId, volumeId)
|
||||
if volume.StatusCode != 200 {
|
||||
return "", errors.New(volume.Response)
|
||||
}
|
||||
|
||||
return volume.Properties.LicenceType, nil
|
||||
}
|
||||
|
||||
func (s *stepTakeSnapshot) setPB(username string, password string, url string) {
|
||||
func (d *stepTakeSnapshot) setPB(username string, password string, url string) {
|
||||
profitbricks.SetAuth(username, password)
|
||||
profitbricks.SetEndpoint(url)
|
||||
}
|
||||
|
||||
@@ -11,103 +11,101 @@ import (
|
||||
// FlatConfig is an auto-generated flat version of Config.
|
||||
// Where the contents of a field with a `mapstructure:,squash` tag are bubbled up.
|
||||
type FlatConfig struct {
|
||||
PackerBuildName *string `mapstructure:"packer_build_name" cty:"packer_build_name" hcl:"packer_build_name"`
|
||||
PackerBuilderType *string `mapstructure:"packer_builder_type" cty:"packer_builder_type" hcl:"packer_builder_type"`
|
||||
PackerCoreVersion *string `mapstructure:"packer_core_version" cty:"packer_core_version" hcl:"packer_core_version"`
|
||||
PackerDebug *bool `mapstructure:"packer_debug" cty:"packer_debug" hcl:"packer_debug"`
|
||||
PackerForce *bool `mapstructure:"packer_force" cty:"packer_force" hcl:"packer_force"`
|
||||
PackerOnError *string `mapstructure:"packer_on_error" cty:"packer_on_error" hcl:"packer_on_error"`
|
||||
PackerUserVars map[string]string `mapstructure:"packer_user_variables" cty:"packer_user_variables" hcl:"packer_user_variables"`
|
||||
PackerSensitiveVars []string `mapstructure:"packer_sensitive_variables" cty:"packer_sensitive_variables" hcl:"packer_sensitive_variables"`
|
||||
HTTPDir *string `mapstructure:"http_directory" cty:"http_directory" hcl:"http_directory"`
|
||||
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"`
|
||||
HTTPPortMax *int `mapstructure:"http_port_max" cty:"http_port_max" hcl:"http_port_max"`
|
||||
HTTPAddress *string `mapstructure:"http_bind_address" cty:"http_bind_address" hcl:"http_bind_address"`
|
||||
HTTPInterface *string `mapstructure:"http_interface" undocumented:"true" cty:"http_interface" hcl:"http_interface"`
|
||||
BootGroupInterval *string `mapstructure:"boot_keygroup_interval" cty:"boot_keygroup_interval" hcl:"boot_keygroup_interval"`
|
||||
BootWait *string `mapstructure:"boot_wait" cty:"boot_wait" hcl:"boot_wait"`
|
||||
BootCommand []string `mapstructure:"boot_command" cty:"boot_command" hcl:"boot_command"`
|
||||
BootKeyInterval *string `mapstructure:"boot_key_interval" cty:"boot_key_interval" hcl:"boot_key_interval"`
|
||||
Type *string `mapstructure:"communicator" cty:"communicator" hcl:"communicator"`
|
||||
PauseBeforeConnect *string `mapstructure:"pause_before_connecting" cty:"pause_before_connecting" hcl:"pause_before_connecting"`
|
||||
SSHHost *string `mapstructure:"ssh_host" cty:"ssh_host" hcl:"ssh_host"`
|
||||
SSHPort *int `mapstructure:"ssh_port" cty:"ssh_port" hcl:"ssh_port"`
|
||||
SSHUsername *string `mapstructure:"ssh_username" cty:"ssh_username" hcl:"ssh_username"`
|
||||
SSHPassword *string `mapstructure:"ssh_password" cty:"ssh_password" hcl:"ssh_password"`
|
||||
SSHKeyPairName *string `mapstructure:"ssh_keypair_name" undocumented:"true" cty:"ssh_keypair_name" hcl:"ssh_keypair_name"`
|
||||
SSHTemporaryKeyPairName *string `mapstructure:"temporary_key_pair_name" undocumented:"true" cty:"temporary_key_pair_name" hcl:"temporary_key_pair_name"`
|
||||
SSHTemporaryKeyPairType *string `mapstructure:"temporary_key_pair_type" cty:"temporary_key_pair_type" hcl:"temporary_key_pair_type"`
|
||||
SSHTemporaryKeyPairBits *int `mapstructure:"temporary_key_pair_bits" cty:"temporary_key_pair_bits" hcl:"temporary_key_pair_bits"`
|
||||
SSHCiphers []string `mapstructure:"ssh_ciphers" cty:"ssh_ciphers" hcl:"ssh_ciphers"`
|
||||
SSHClearAuthorizedKeys *bool `mapstructure:"ssh_clear_authorized_keys" cty:"ssh_clear_authorized_keys" hcl:"ssh_clear_authorized_keys"`
|
||||
SSHKEXAlgos []string `mapstructure:"ssh_key_exchange_algorithms" cty:"ssh_key_exchange_algorithms" hcl:"ssh_key_exchange_algorithms"`
|
||||
SSHPrivateKeyFile *string `mapstructure:"ssh_private_key_file" undocumented:"true" cty:"ssh_private_key_file" hcl:"ssh_private_key_file"`
|
||||
SSHCertificateFile *string `mapstructure:"ssh_certificate_file" cty:"ssh_certificate_file" hcl:"ssh_certificate_file"`
|
||||
SSHPty *bool `mapstructure:"ssh_pty" cty:"ssh_pty" hcl:"ssh_pty"`
|
||||
SSHTimeout *string `mapstructure:"ssh_timeout" cty:"ssh_timeout" hcl:"ssh_timeout"`
|
||||
SSHWaitTimeout *string `mapstructure:"ssh_wait_timeout" undocumented:"true" cty:"ssh_wait_timeout" hcl:"ssh_wait_timeout"`
|
||||
SSHAgentAuth *bool `mapstructure:"ssh_agent_auth" undocumented:"true" cty:"ssh_agent_auth" hcl:"ssh_agent_auth"`
|
||||
SSHDisableAgentForwarding *bool `mapstructure:"ssh_disable_agent_forwarding" cty:"ssh_disable_agent_forwarding" hcl:"ssh_disable_agent_forwarding"`
|
||||
SSHHandshakeAttempts *int `mapstructure:"ssh_handshake_attempts" cty:"ssh_handshake_attempts" hcl:"ssh_handshake_attempts"`
|
||||
SSHBastionHost *string `mapstructure:"ssh_bastion_host" cty:"ssh_bastion_host" hcl:"ssh_bastion_host"`
|
||||
SSHBastionPort *int `mapstructure:"ssh_bastion_port" cty:"ssh_bastion_port" hcl:"ssh_bastion_port"`
|
||||
SSHBastionAgentAuth *bool `mapstructure:"ssh_bastion_agent_auth" cty:"ssh_bastion_agent_auth" hcl:"ssh_bastion_agent_auth"`
|
||||
SSHBastionUsername *string `mapstructure:"ssh_bastion_username" cty:"ssh_bastion_username" hcl:"ssh_bastion_username"`
|
||||
SSHBastionPassword *string `mapstructure:"ssh_bastion_password" cty:"ssh_bastion_password" hcl:"ssh_bastion_password"`
|
||||
SSHBastionInteractive *bool `mapstructure:"ssh_bastion_interactive" cty:"ssh_bastion_interactive" hcl:"ssh_bastion_interactive"`
|
||||
SSHBastionPrivateKeyFile *string `mapstructure:"ssh_bastion_private_key_file" cty:"ssh_bastion_private_key_file" hcl:"ssh_bastion_private_key_file"`
|
||||
SSHBastionCertificateFile *string `mapstructure:"ssh_bastion_certificate_file" cty:"ssh_bastion_certificate_file" hcl:"ssh_bastion_certificate_file"`
|
||||
SSHFileTransferMethod *string `mapstructure:"ssh_file_transfer_method" cty:"ssh_file_transfer_method" hcl:"ssh_file_transfer_method"`
|
||||
SSHProxyHost *string `mapstructure:"ssh_proxy_host" cty:"ssh_proxy_host" hcl:"ssh_proxy_host"`
|
||||
SSHProxyPort *int `mapstructure:"ssh_proxy_port" cty:"ssh_proxy_port" hcl:"ssh_proxy_port"`
|
||||
SSHProxyUsername *string `mapstructure:"ssh_proxy_username" cty:"ssh_proxy_username" hcl:"ssh_proxy_username"`
|
||||
SSHProxyPassword *string `mapstructure:"ssh_proxy_password" cty:"ssh_proxy_password" hcl:"ssh_proxy_password"`
|
||||
SSHKeepAliveInterval *string `mapstructure:"ssh_keep_alive_interval" cty:"ssh_keep_alive_interval" hcl:"ssh_keep_alive_interval"`
|
||||
SSHReadWriteTimeout *string `mapstructure:"ssh_read_write_timeout" cty:"ssh_read_write_timeout" hcl:"ssh_read_write_timeout"`
|
||||
SSHRemoteTunnels []string `mapstructure:"ssh_remote_tunnels" cty:"ssh_remote_tunnels" hcl:"ssh_remote_tunnels"`
|
||||
SSHLocalTunnels []string `mapstructure:"ssh_local_tunnels" cty:"ssh_local_tunnels" hcl:"ssh_local_tunnels"`
|
||||
SSHPublicKey []byte `mapstructure:"ssh_public_key" undocumented:"true" cty:"ssh_public_key" hcl:"ssh_public_key"`
|
||||
SSHPrivateKey []byte `mapstructure:"ssh_private_key" undocumented:"true" cty:"ssh_private_key" hcl:"ssh_private_key"`
|
||||
WinRMUser *string `mapstructure:"winrm_username" cty:"winrm_username" hcl:"winrm_username"`
|
||||
WinRMPassword *string `mapstructure:"winrm_password" cty:"winrm_password" hcl:"winrm_password"`
|
||||
WinRMHost *string `mapstructure:"winrm_host" cty:"winrm_host" hcl:"winrm_host"`
|
||||
WinRMNoProxy *bool `mapstructure:"winrm_no_proxy" cty:"winrm_no_proxy" hcl:"winrm_no_proxy"`
|
||||
WinRMPort *int `mapstructure:"winrm_port" cty:"winrm_port" hcl:"winrm_port"`
|
||||
WinRMTimeout *string `mapstructure:"winrm_timeout" cty:"winrm_timeout" hcl:"winrm_timeout"`
|
||||
WinRMUseSSL *bool `mapstructure:"winrm_use_ssl" cty:"winrm_use_ssl" hcl:"winrm_use_ssl"`
|
||||
WinRMInsecure *bool `mapstructure:"winrm_insecure" cty:"winrm_insecure" hcl:"winrm_insecure"`
|
||||
WinRMUseNTLM *bool `mapstructure:"winrm_use_ntlm" cty:"winrm_use_ntlm" hcl:"winrm_use_ntlm"`
|
||||
ProxmoxURLRaw *string `mapstructure:"proxmox_url" cty:"proxmox_url" hcl:"proxmox_url"`
|
||||
SkipCertValidation *bool `mapstructure:"insecure_skip_tls_verify" cty:"insecure_skip_tls_verify" hcl:"insecure_skip_tls_verify"`
|
||||
Username *string `mapstructure:"username" cty:"username" hcl:"username"`
|
||||
Password *string `mapstructure:"password" cty:"password" hcl:"password"`
|
||||
Token *string `mapstructure:"token" cty:"token" hcl:"token"`
|
||||
Node *string `mapstructure:"node" cty:"node" hcl:"node"`
|
||||
Pool *string `mapstructure:"pool" cty:"pool" hcl:"pool"`
|
||||
VMName *string `mapstructure:"vm_name" cty:"vm_name" hcl:"vm_name"`
|
||||
VMID *int `mapstructure:"vm_id" cty:"vm_id" hcl:"vm_id"`
|
||||
Boot *string `mapstructure:"boot" cty:"boot" hcl:"boot"`
|
||||
Memory *int `mapstructure:"memory" cty:"memory" hcl:"memory"`
|
||||
Cores *int `mapstructure:"cores" cty:"cores" hcl:"cores"`
|
||||
CPUType *string `mapstructure:"cpu_type" cty:"cpu_type" hcl:"cpu_type"`
|
||||
Sockets *int `mapstructure:"sockets" cty:"sockets" hcl:"sockets"`
|
||||
OS *string `mapstructure:"os" cty:"os" hcl:"os"`
|
||||
VGA *proxmox.FlatvgaConfig `mapstructure:"vga" cty:"vga" hcl:"vga"`
|
||||
NICs []proxmox.FlatnicConfig `mapstructure:"network_adapters" cty:"network_adapters" hcl:"network_adapters"`
|
||||
Disks []proxmox.FlatdiskConfig `mapstructure:"disks" cty:"disks" hcl:"disks"`
|
||||
Agent *bool `mapstructure:"qemu_agent" cty:"qemu_agent" hcl:"qemu_agent"`
|
||||
SCSIController *string `mapstructure:"scsi_controller" cty:"scsi_controller" hcl:"scsi_controller"`
|
||||
Onboot *bool `mapstructure:"onboot" cty:"onboot" hcl:"onboot"`
|
||||
DisableKVM *bool `mapstructure:"disable_kvm" cty:"disable_kvm" hcl:"disable_kvm"`
|
||||
TemplateName *string `mapstructure:"template_name" cty:"template_name" hcl:"template_name"`
|
||||
TemplateDescription *string `mapstructure:"template_description" cty:"template_description" hcl:"template_description"`
|
||||
CloudInit *bool `mapstructure:"cloud_init" cty:"cloud_init" hcl:"cloud_init"`
|
||||
CloudInitStoragePool *string `mapstructure:"cloud_init_storage_pool" cty:"cloud_init_storage_pool" hcl:"cloud_init_storage_pool"`
|
||||
AdditionalISOFiles []proxmox.FlatadditionalISOsConfig `mapstructure:"additional_iso_files" cty:"additional_iso_files" hcl:"additional_iso_files"`
|
||||
VMInterface *string `mapstructure:"vm_interface" cty:"vm_interface" hcl:"vm_interface"`
|
||||
CloneVM *string `mapstructure:"clone_vm" cty:"clone_vm" hcl:"clone_vm"`
|
||||
FullClone *bool `mapstructure:"full_clone" required:"false" cty:"full_clone" hcl:"full_clone"`
|
||||
PackerBuildName *string `mapstructure:"packer_build_name" cty:"packer_build_name" hcl:"packer_build_name"`
|
||||
PackerBuilderType *string `mapstructure:"packer_builder_type" cty:"packer_builder_type" hcl:"packer_builder_type"`
|
||||
PackerCoreVersion *string `mapstructure:"packer_core_version" cty:"packer_core_version" hcl:"packer_core_version"`
|
||||
PackerDebug *bool `mapstructure:"packer_debug" cty:"packer_debug" hcl:"packer_debug"`
|
||||
PackerForce *bool `mapstructure:"packer_force" cty:"packer_force" hcl:"packer_force"`
|
||||
PackerOnError *string `mapstructure:"packer_on_error" cty:"packer_on_error" hcl:"packer_on_error"`
|
||||
PackerUserVars map[string]string `mapstructure:"packer_user_variables" cty:"packer_user_variables" hcl:"packer_user_variables"`
|
||||
PackerSensitiveVars []string `mapstructure:"packer_sensitive_variables" cty:"packer_sensitive_variables" hcl:"packer_sensitive_variables"`
|
||||
HTTPDir *string `mapstructure:"http_directory" cty:"http_directory" hcl:"http_directory"`
|
||||
HTTPPortMin *int `mapstructure:"http_port_min" cty:"http_port_min" hcl:"http_port_min"`
|
||||
HTTPPortMax *int `mapstructure:"http_port_max" cty:"http_port_max" hcl:"http_port_max"`
|
||||
HTTPAddress *string `mapstructure:"http_bind_address" cty:"http_bind_address" hcl:"http_bind_address"`
|
||||
HTTPInterface *string `mapstructure:"http_interface" undocumented:"true" cty:"http_interface" hcl:"http_interface"`
|
||||
BootGroupInterval *string `mapstructure:"boot_keygroup_interval" cty:"boot_keygroup_interval" hcl:"boot_keygroup_interval"`
|
||||
BootWait *string `mapstructure:"boot_wait" cty:"boot_wait" hcl:"boot_wait"`
|
||||
BootCommand []string `mapstructure:"boot_command" cty:"boot_command" hcl:"boot_command"`
|
||||
BootKeyInterval *string `mapstructure:"boot_key_interval" cty:"boot_key_interval" hcl:"boot_key_interval"`
|
||||
Type *string `mapstructure:"communicator" cty:"communicator" hcl:"communicator"`
|
||||
PauseBeforeConnect *string `mapstructure:"pause_before_connecting" cty:"pause_before_connecting" hcl:"pause_before_connecting"`
|
||||
SSHHost *string `mapstructure:"ssh_host" cty:"ssh_host" hcl:"ssh_host"`
|
||||
SSHPort *int `mapstructure:"ssh_port" cty:"ssh_port" hcl:"ssh_port"`
|
||||
SSHUsername *string `mapstructure:"ssh_username" cty:"ssh_username" hcl:"ssh_username"`
|
||||
SSHPassword *string `mapstructure:"ssh_password" cty:"ssh_password" hcl:"ssh_password"`
|
||||
SSHKeyPairName *string `mapstructure:"ssh_keypair_name" undocumented:"true" cty:"ssh_keypair_name" hcl:"ssh_keypair_name"`
|
||||
SSHTemporaryKeyPairName *string `mapstructure:"temporary_key_pair_name" undocumented:"true" cty:"temporary_key_pair_name" hcl:"temporary_key_pair_name"`
|
||||
SSHTemporaryKeyPairType *string `mapstructure:"temporary_key_pair_type" cty:"temporary_key_pair_type" hcl:"temporary_key_pair_type"`
|
||||
SSHTemporaryKeyPairBits *int `mapstructure:"temporary_key_pair_bits" cty:"temporary_key_pair_bits" hcl:"temporary_key_pair_bits"`
|
||||
SSHCiphers []string `mapstructure:"ssh_ciphers" cty:"ssh_ciphers" hcl:"ssh_ciphers"`
|
||||
SSHClearAuthorizedKeys *bool `mapstructure:"ssh_clear_authorized_keys" cty:"ssh_clear_authorized_keys" hcl:"ssh_clear_authorized_keys"`
|
||||
SSHKEXAlgos []string `mapstructure:"ssh_key_exchange_algorithms" cty:"ssh_key_exchange_algorithms" hcl:"ssh_key_exchange_algorithms"`
|
||||
SSHPrivateKeyFile *string `mapstructure:"ssh_private_key_file" undocumented:"true" cty:"ssh_private_key_file" hcl:"ssh_private_key_file"`
|
||||
SSHCertificateFile *string `mapstructure:"ssh_certificate_file" cty:"ssh_certificate_file" hcl:"ssh_certificate_file"`
|
||||
SSHPty *bool `mapstructure:"ssh_pty" cty:"ssh_pty" hcl:"ssh_pty"`
|
||||
SSHTimeout *string `mapstructure:"ssh_timeout" cty:"ssh_timeout" hcl:"ssh_timeout"`
|
||||
SSHWaitTimeout *string `mapstructure:"ssh_wait_timeout" undocumented:"true" cty:"ssh_wait_timeout" hcl:"ssh_wait_timeout"`
|
||||
SSHAgentAuth *bool `mapstructure:"ssh_agent_auth" undocumented:"true" cty:"ssh_agent_auth" hcl:"ssh_agent_auth"`
|
||||
SSHDisableAgentForwarding *bool `mapstructure:"ssh_disable_agent_forwarding" cty:"ssh_disable_agent_forwarding" hcl:"ssh_disable_agent_forwarding"`
|
||||
SSHHandshakeAttempts *int `mapstructure:"ssh_handshake_attempts" cty:"ssh_handshake_attempts" hcl:"ssh_handshake_attempts"`
|
||||
SSHBastionHost *string `mapstructure:"ssh_bastion_host" cty:"ssh_bastion_host" hcl:"ssh_bastion_host"`
|
||||
SSHBastionPort *int `mapstructure:"ssh_bastion_port" cty:"ssh_bastion_port" hcl:"ssh_bastion_port"`
|
||||
SSHBastionAgentAuth *bool `mapstructure:"ssh_bastion_agent_auth" cty:"ssh_bastion_agent_auth" hcl:"ssh_bastion_agent_auth"`
|
||||
SSHBastionUsername *string `mapstructure:"ssh_bastion_username" cty:"ssh_bastion_username" hcl:"ssh_bastion_username"`
|
||||
SSHBastionPassword *string `mapstructure:"ssh_bastion_password" cty:"ssh_bastion_password" hcl:"ssh_bastion_password"`
|
||||
SSHBastionInteractive *bool `mapstructure:"ssh_bastion_interactive" cty:"ssh_bastion_interactive" hcl:"ssh_bastion_interactive"`
|
||||
SSHBastionPrivateKeyFile *string `mapstructure:"ssh_bastion_private_key_file" cty:"ssh_bastion_private_key_file" hcl:"ssh_bastion_private_key_file"`
|
||||
SSHBastionCertificateFile *string `mapstructure:"ssh_bastion_certificate_file" cty:"ssh_bastion_certificate_file" hcl:"ssh_bastion_certificate_file"`
|
||||
SSHFileTransferMethod *string `mapstructure:"ssh_file_transfer_method" cty:"ssh_file_transfer_method" hcl:"ssh_file_transfer_method"`
|
||||
SSHProxyHost *string `mapstructure:"ssh_proxy_host" cty:"ssh_proxy_host" hcl:"ssh_proxy_host"`
|
||||
SSHProxyPort *int `mapstructure:"ssh_proxy_port" cty:"ssh_proxy_port" hcl:"ssh_proxy_port"`
|
||||
SSHProxyUsername *string `mapstructure:"ssh_proxy_username" cty:"ssh_proxy_username" hcl:"ssh_proxy_username"`
|
||||
SSHProxyPassword *string `mapstructure:"ssh_proxy_password" cty:"ssh_proxy_password" hcl:"ssh_proxy_password"`
|
||||
SSHKeepAliveInterval *string `mapstructure:"ssh_keep_alive_interval" cty:"ssh_keep_alive_interval" hcl:"ssh_keep_alive_interval"`
|
||||
SSHReadWriteTimeout *string `mapstructure:"ssh_read_write_timeout" cty:"ssh_read_write_timeout" hcl:"ssh_read_write_timeout"`
|
||||
SSHRemoteTunnels []string `mapstructure:"ssh_remote_tunnels" cty:"ssh_remote_tunnels" hcl:"ssh_remote_tunnels"`
|
||||
SSHLocalTunnels []string `mapstructure:"ssh_local_tunnels" cty:"ssh_local_tunnels" hcl:"ssh_local_tunnels"`
|
||||
SSHPublicKey []byte `mapstructure:"ssh_public_key" undocumented:"true" cty:"ssh_public_key" hcl:"ssh_public_key"`
|
||||
SSHPrivateKey []byte `mapstructure:"ssh_private_key" undocumented:"true" cty:"ssh_private_key" hcl:"ssh_private_key"`
|
||||
WinRMUser *string `mapstructure:"winrm_username" cty:"winrm_username" hcl:"winrm_username"`
|
||||
WinRMPassword *string `mapstructure:"winrm_password" cty:"winrm_password" hcl:"winrm_password"`
|
||||
WinRMHost *string `mapstructure:"winrm_host" cty:"winrm_host" hcl:"winrm_host"`
|
||||
WinRMNoProxy *bool `mapstructure:"winrm_no_proxy" cty:"winrm_no_proxy" hcl:"winrm_no_proxy"`
|
||||
WinRMPort *int `mapstructure:"winrm_port" cty:"winrm_port" hcl:"winrm_port"`
|
||||
WinRMTimeout *string `mapstructure:"winrm_timeout" cty:"winrm_timeout" hcl:"winrm_timeout"`
|
||||
WinRMUseSSL *bool `mapstructure:"winrm_use_ssl" cty:"winrm_use_ssl" hcl:"winrm_use_ssl"`
|
||||
WinRMInsecure *bool `mapstructure:"winrm_insecure" cty:"winrm_insecure" hcl:"winrm_insecure"`
|
||||
WinRMUseNTLM *bool `mapstructure:"winrm_use_ntlm" cty:"winrm_use_ntlm" hcl:"winrm_use_ntlm"`
|
||||
ProxmoxURLRaw *string `mapstructure:"proxmox_url" cty:"proxmox_url" hcl:"proxmox_url"`
|
||||
SkipCertValidation *bool `mapstructure:"insecure_skip_tls_verify" cty:"insecure_skip_tls_verify" hcl:"insecure_skip_tls_verify"`
|
||||
Username *string `mapstructure:"username" cty:"username" hcl:"username"`
|
||||
Password *string `mapstructure:"password" cty:"password" hcl:"password"`
|
||||
Node *string `mapstructure:"node" cty:"node" hcl:"node"`
|
||||
Pool *string `mapstructure:"pool" cty:"pool" hcl:"pool"`
|
||||
VMName *string `mapstructure:"vm_name" cty:"vm_name" hcl:"vm_name"`
|
||||
VMID *int `mapstructure:"vm_id" cty:"vm_id" hcl:"vm_id"`
|
||||
Boot *string `mapstructure:"boot" cty:"boot" hcl:"boot"`
|
||||
Memory *int `mapstructure:"memory" cty:"memory" hcl:"memory"`
|
||||
Cores *int `mapstructure:"cores" cty:"cores" hcl:"cores"`
|
||||
CPUType *string `mapstructure:"cpu_type" cty:"cpu_type" hcl:"cpu_type"`
|
||||
Sockets *int `mapstructure:"sockets" cty:"sockets" hcl:"sockets"`
|
||||
OS *string `mapstructure:"os" cty:"os" hcl:"os"`
|
||||
VGA *proxmox.FlatvgaConfig `mapstructure:"vga" cty:"vga" hcl:"vga"`
|
||||
NICs []proxmox.FlatnicConfig `mapstructure:"network_adapters" cty:"network_adapters" hcl:"network_adapters"`
|
||||
Disks []proxmox.FlatdiskConfig `mapstructure:"disks" cty:"disks" hcl:"disks"`
|
||||
Agent *bool `mapstructure:"qemu_agent" cty:"qemu_agent" hcl:"qemu_agent"`
|
||||
SCSIController *string `mapstructure:"scsi_controller" cty:"scsi_controller" hcl:"scsi_controller"`
|
||||
Onboot *bool `mapstructure:"onboot" cty:"onboot" hcl:"onboot"`
|
||||
DisableKVM *bool `mapstructure:"disable_kvm" cty:"disable_kvm" hcl:"disable_kvm"`
|
||||
TemplateName *string `mapstructure:"template_name" cty:"template_name" hcl:"template_name"`
|
||||
TemplateDescription *string `mapstructure:"template_description" cty:"template_description" hcl:"template_description"`
|
||||
CloudInit *bool `mapstructure:"cloud_init" cty:"cloud_init" hcl:"cloud_init"`
|
||||
CloudInitStoragePool *string `mapstructure:"cloud_init_storage_pool" cty:"cloud_init_storage_pool" hcl:"cloud_init_storage_pool"`
|
||||
AdditionalISOFiles []proxmox.FlatstorageConfig `mapstructure:"additional_iso_files" cty:"additional_iso_files" hcl:"additional_iso_files"`
|
||||
VMInterface *string `mapstructure:"vm_interface" cty:"vm_interface" hcl:"vm_interface"`
|
||||
CloneVM *string `mapstructure:"clone_vm" cty:"clone_vm" hcl:"clone_vm"`
|
||||
FullClone *bool `mapstructure:"full_clone" required:"false" cty:"full_clone" hcl:"full_clone"`
|
||||
}
|
||||
|
||||
// FlatMapstructure returns a new FlatConfig.
|
||||
@@ -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_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_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_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},
|
||||
@@ -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},
|
||||
"username": &hcldec.AttrSpec{Name: "username", 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},
|
||||
"pool": &hcldec.AttrSpec{Name: "pool", 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},
|
||||
"cloud_init": &hcldec.AttrSpec{Name: "cloud_init", Type: cty.Bool, Required: false},
|
||||
"cloud_init_storage_pool": &hcldec.AttrSpec{Name: "cloud_init_storage_pool", Type: cty.String, Required: false},
|
||||
"additional_iso_files": &hcldec.BlockListSpec{TypeName: "additional_iso_files", Nested: hcldec.ObjectSpec((*proxmox.FlatadditionalISOsConfig)(nil).HCL2Spec())},
|
||||
"additional_iso_files": &hcldec.BlockListSpec{TypeName: "additional_iso_files", Nested: hcldec.ObjectSpec((*proxmox.FlatstorageConfig)(nil).HCL2Spec())},
|
||||
"vm_interface": &hcldec.AttrSpec{Name: "vm_interface", Type: cty.String, Required: false},
|
||||
"clone_vm": &hcldec.AttrSpec{Name: "clone_vm", Type: cty.String, Required: false},
|
||||
"full_clone": &hcldec.AttrSpec{Name: "full_clone", Type: cty.Bool, Required: false},
|
||||
|
||||
@@ -2,6 +2,7 @@ package proxmox
|
||||
|
||||
import (
|
||||
"context"
|
||||
"crypto/tls"
|
||||
"errors"
|
||||
"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) {
|
||||
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 {
|
||||
return nil, err
|
||||
}
|
||||
@@ -52,7 +61,12 @@ func (b *Builder) Run(ctx context.Context, ui packersdk.Ui, hook packersdk.Hook,
|
||||
&stepStartVM{
|
||||
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{
|
||||
BootConfig: b.config.BootConfig,
|
||||
Ctx: b.config.Ctx,
|
||||
@@ -70,20 +84,7 @@ func (b *Builder) Run(ctx context.Context, ui packersdk.Ui, hook packersdk.Hook,
|
||||
&stepFinalizeTemplateConfig{},
|
||||
&stepSuccess{},
|
||||
}
|
||||
preSteps := b.preSteps
|
||||
for idx := range b.config.AdditionalISOFiles {
|
||||
preSteps = append(preSteps, &commonsteps.StepDownload{
|
||||
Checksum: b.config.AdditionalISOFiles[idx].ISOChecksum,
|
||||
Description: "additional ISO",
|
||||
Extension: b.config.AdditionalISOFiles[idx].TargetExtension,
|
||||
ResultKey: b.config.AdditionalISOFiles[idx].DownloadPathKey,
|
||||
TargetPath: b.config.AdditionalISOFiles[idx].DownloadPathKey,
|
||||
Url: b.config.AdditionalISOFiles[idx].ISOUrls,
|
||||
})
|
||||
}
|
||||
preSteps = append(preSteps, &stepUploadAdditionalISOs{})
|
||||
|
||||
steps := append(preSteps, coreSteps...)
|
||||
steps := append(b.preSteps, coreSteps...)
|
||||
steps = append(steps, b.postSteps...)
|
||||
// Run the steps
|
||||
b.runner = commonsteps.NewRunner(steps, b.config.PackerConfig, ui)
|
||||
@@ -158,9 +159,6 @@ func getVMIP(state multistep.StateBag) (string, error) {
|
||||
if addr.IsLoopback() {
|
||||
continue
|
||||
}
|
||||
if addr.To4() == nil {
|
||||
continue
|
||||
}
|
||||
return addr.String(), nil
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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
|
||||
}
|
||||
@@ -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)
|
||||
}
|
||||
@@ -1,4 +1,4 @@
|
||||
//go:generate mapstructure-to-hcl2 -type Config,nicConfig,diskConfig,vgaConfig,additionalISOsConfig
|
||||
//go:generate mapstructure-to-hcl2 -type Config,nicConfig,diskConfig,vgaConfig,storageConfig
|
||||
|
||||
package proxmox
|
||||
|
||||
@@ -8,7 +8,6 @@ import (
|
||||
"log"
|
||||
"net/url"
|
||||
"os"
|
||||
"strconv"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
@@ -35,7 +34,6 @@ type Config struct {
|
||||
SkipCertValidation bool `mapstructure:"insecure_skip_tls_verify"`
|
||||
Username string `mapstructure:"username"`
|
||||
Password string `mapstructure:"password"`
|
||||
Token string `mapstructure:"token"`
|
||||
Node string `mapstructure:"node"`
|
||||
Pool string `mapstructure:"pool"`
|
||||
|
||||
@@ -62,20 +60,20 @@ type Config struct {
|
||||
CloudInit bool `mapstructure:"cloud_init"`
|
||||
CloudInitStoragePool string `mapstructure:"cloud_init_storage_pool"`
|
||||
|
||||
AdditionalISOFiles []additionalISOsConfig `mapstructure:"additional_iso_files"`
|
||||
VMInterface string `mapstructure:"vm_interface"`
|
||||
AdditionalISOFiles []storageConfig `mapstructure:"additional_iso_files"`
|
||||
VMInterface string `mapstructure:"vm_interface"`
|
||||
|
||||
Ctx interpolate.Context `mapstructure-to-hcl2:",skip"`
|
||||
}
|
||||
|
||||
type additionalISOsConfig struct {
|
||||
type storageConfig struct {
|
||||
commonsteps.ISOConfig `mapstructure:",squash"`
|
||||
Device string `mapstructure:"device"`
|
||||
ISOFile string `mapstructure:"iso_file"`
|
||||
ISOStoragePool string `mapstructure:"iso_storage_pool"`
|
||||
Unmount bool `mapstructure:"unmount"`
|
||||
ShouldUploadISO bool `mapstructure-to-hcl2:",skip"`
|
||||
DownloadPathKey string `mapstructure-to-hcl2:",skip"`
|
||||
ShouldUploadISO bool
|
||||
DownloadPathKey string
|
||||
}
|
||||
|
||||
type nicConfig struct {
|
||||
@@ -136,9 +134,6 @@ func (c *Config) Prepare(upper interface{}, raws ...interface{}) ([]string, []st
|
||||
if c.Password == "" {
|
||||
c.Password = os.Getenv("PROXMOX_PASSWORD")
|
||||
}
|
||||
if c.Token == "" {
|
||||
c.Token = os.Getenv("PROXMOX_TOKEN")
|
||||
}
|
||||
if c.BootKeyInterval == 0 && os.Getenv(bootcommand.PackerKeyEnv) != "" {
|
||||
var err error
|
||||
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 == "" {
|
||||
errs = packersdk.MultiErrorAppend(errs, errors.New("username must be specified"))
|
||||
}
|
||||
if c.Password == "" && c.Token == "" {
|
||||
errs = packersdk.MultiErrorAppend(errs, errors.New("password or token must be specified"))
|
||||
if c.Password == "" {
|
||||
errs = packersdk.MultiErrorAppend(errs, errors.New("password must be specified"))
|
||||
}
|
||||
if c.ProxmoxURLRaw == "" {
|
||||
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))
|
||||
}
|
||||
}
|
||||
for idx := range c.AdditionalISOFiles {
|
||||
// Check AdditionalISO config
|
||||
// Either a pre-uploaded ISO should be referenced in iso_file, OR a URL
|
||||
// (possibly to a local file) to an ISO file that will be downloaded and
|
||||
// then uploaded to Proxmox.
|
||||
if c.AdditionalISOFiles[idx].ISOFile != "" {
|
||||
c.AdditionalISOFiles[idx].ShouldUploadISO = false
|
||||
} else {
|
||||
c.AdditionalISOFiles[idx].DownloadPathKey = "downloaded_additional_iso_path_" + strconv.Itoa(idx)
|
||||
isoWarnings, isoErrors := c.AdditionalISOFiles[idx].ISOConfig.Prepare(&c.Ctx)
|
||||
errs = packersdk.MultiErrorAppend(errs, isoErrors...)
|
||||
warnings = append(warnings, isoWarnings...)
|
||||
c.AdditionalISOFiles[idx].ShouldUploadISO = true
|
||||
}
|
||||
if c.AdditionalISOFiles[idx].Device == "" {
|
||||
log.Printf("AdditionalISOFile %d Device not set, using default 'ide3'", idx)
|
||||
c.AdditionalISOFiles[idx].Device = "ide3"
|
||||
}
|
||||
if strings.HasPrefix(c.AdditionalISOFiles[idx].Device, "ide") {
|
||||
busnumber, err := strconv.Atoi(c.AdditionalISOFiles[idx].Device[3:])
|
||||
if err != nil {
|
||||
errs = packersdk.MultiErrorAppend(errs, fmt.Errorf("%s is not a valid bus index", c.AdditionalISOFiles[idx].Device[3:]))
|
||||
}
|
||||
if busnumber == 2 {
|
||||
errs = packersdk.MultiErrorAppend(errs, fmt.Errorf("IDE bus 2 is used by boot ISO"))
|
||||
}
|
||||
if busnumber > 3 {
|
||||
errs = packersdk.MultiErrorAppend(errs, fmt.Errorf("IDE bus index can't be higher than 3"))
|
||||
}
|
||||
}
|
||||
if strings.HasPrefix(c.AdditionalISOFiles[idx].Device, "sata") {
|
||||
busnumber, err := strconv.Atoi(c.AdditionalISOFiles[idx].Device[4:])
|
||||
if err != nil {
|
||||
errs = packersdk.MultiErrorAppend(errs, fmt.Errorf("%s is not a valid bus index", c.AdditionalISOFiles[idx].Device[4:]))
|
||||
}
|
||||
if busnumber > 5 {
|
||||
errs = packersdk.MultiErrorAppend(errs, fmt.Errorf("SATA bus index can't be higher than 5"))
|
||||
}
|
||||
}
|
||||
if strings.HasPrefix(c.AdditionalISOFiles[idx].Device, "scsi") {
|
||||
busnumber, err := strconv.Atoi(c.AdditionalISOFiles[idx].Device[4:])
|
||||
if err != nil {
|
||||
errs = packersdk.MultiErrorAppend(errs, fmt.Errorf("%s is not a valid bus index", c.AdditionalISOFiles[idx].Device[4:]))
|
||||
}
|
||||
if busnumber > 30 {
|
||||
errs = packersdk.MultiErrorAppend(errs, fmt.Errorf("SCSI bus index can't be higher than 30"))
|
||||
}
|
||||
}
|
||||
if (c.AdditionalISOFiles[idx].ISOFile == "" && len(c.AdditionalISOFiles[idx].ISOConfig.ISOUrls) == 0) || (c.AdditionalISOFiles[idx].ISOFile != "" && len(c.AdditionalISOFiles[idx].ISOConfig.ISOUrls) != 0) {
|
||||
errs = packersdk.MultiErrorAppend(errs, fmt.Errorf("either iso_file or iso_url, but not both, must be specified for AdditionalISO file %s", c.AdditionalISOFiles[idx].Device))
|
||||
}
|
||||
}
|
||||
|
||||
if errs != nil && len(errs.Errors) > 0 {
|
||||
return nil, warnings, errs
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
// Code generated by "mapstructure-to-hcl2 -type Config,nicConfig,diskConfig,vgaConfig,additionalISOsConfig"; DO NOT EDIT.
|
||||
// Code generated by "mapstructure-to-hcl2 -type Config,nicConfig,diskConfig,vgaConfig,storageConfig"; DO NOT EDIT.
|
||||
|
||||
package proxmox
|
||||
|
||||
@@ -10,101 +10,99 @@ import (
|
||||
// FlatConfig is an auto-generated flat version of Config.
|
||||
// Where the contents of a field with a `mapstructure:,squash` tag are bubbled up.
|
||||
type FlatConfig struct {
|
||||
PackerBuildName *string `mapstructure:"packer_build_name" cty:"packer_build_name" hcl:"packer_build_name"`
|
||||
PackerBuilderType *string `mapstructure:"packer_builder_type" cty:"packer_builder_type" hcl:"packer_builder_type"`
|
||||
PackerCoreVersion *string `mapstructure:"packer_core_version" cty:"packer_core_version" hcl:"packer_core_version"`
|
||||
PackerDebug *bool `mapstructure:"packer_debug" cty:"packer_debug" hcl:"packer_debug"`
|
||||
PackerForce *bool `mapstructure:"packer_force" cty:"packer_force" hcl:"packer_force"`
|
||||
PackerOnError *string `mapstructure:"packer_on_error" cty:"packer_on_error" hcl:"packer_on_error"`
|
||||
PackerUserVars map[string]string `mapstructure:"packer_user_variables" cty:"packer_user_variables" hcl:"packer_user_variables"`
|
||||
PackerSensitiveVars []string `mapstructure:"packer_sensitive_variables" cty:"packer_sensitive_variables" hcl:"packer_sensitive_variables"`
|
||||
HTTPDir *string `mapstructure:"http_directory" cty:"http_directory" hcl:"http_directory"`
|
||||
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"`
|
||||
HTTPPortMax *int `mapstructure:"http_port_max" cty:"http_port_max" hcl:"http_port_max"`
|
||||
HTTPAddress *string `mapstructure:"http_bind_address" cty:"http_bind_address" hcl:"http_bind_address"`
|
||||
HTTPInterface *string `mapstructure:"http_interface" undocumented:"true" cty:"http_interface" hcl:"http_interface"`
|
||||
BootGroupInterval *string `mapstructure:"boot_keygroup_interval" cty:"boot_keygroup_interval" hcl:"boot_keygroup_interval"`
|
||||
BootWait *string `mapstructure:"boot_wait" cty:"boot_wait" hcl:"boot_wait"`
|
||||
BootCommand []string `mapstructure:"boot_command" cty:"boot_command" hcl:"boot_command"`
|
||||
BootKeyInterval *string `mapstructure:"boot_key_interval" cty:"boot_key_interval" hcl:"boot_key_interval"`
|
||||
Type *string `mapstructure:"communicator" cty:"communicator" hcl:"communicator"`
|
||||
PauseBeforeConnect *string `mapstructure:"pause_before_connecting" cty:"pause_before_connecting" hcl:"pause_before_connecting"`
|
||||
SSHHost *string `mapstructure:"ssh_host" cty:"ssh_host" hcl:"ssh_host"`
|
||||
SSHPort *int `mapstructure:"ssh_port" cty:"ssh_port" hcl:"ssh_port"`
|
||||
SSHUsername *string `mapstructure:"ssh_username" cty:"ssh_username" hcl:"ssh_username"`
|
||||
SSHPassword *string `mapstructure:"ssh_password" cty:"ssh_password" hcl:"ssh_password"`
|
||||
SSHKeyPairName *string `mapstructure:"ssh_keypair_name" undocumented:"true" cty:"ssh_keypair_name" hcl:"ssh_keypair_name"`
|
||||
SSHTemporaryKeyPairName *string `mapstructure:"temporary_key_pair_name" undocumented:"true" cty:"temporary_key_pair_name" hcl:"temporary_key_pair_name"`
|
||||
SSHTemporaryKeyPairType *string `mapstructure:"temporary_key_pair_type" cty:"temporary_key_pair_type" hcl:"temporary_key_pair_type"`
|
||||
SSHTemporaryKeyPairBits *int `mapstructure:"temporary_key_pair_bits" cty:"temporary_key_pair_bits" hcl:"temporary_key_pair_bits"`
|
||||
SSHCiphers []string `mapstructure:"ssh_ciphers" cty:"ssh_ciphers" hcl:"ssh_ciphers"`
|
||||
SSHClearAuthorizedKeys *bool `mapstructure:"ssh_clear_authorized_keys" cty:"ssh_clear_authorized_keys" hcl:"ssh_clear_authorized_keys"`
|
||||
SSHKEXAlgos []string `mapstructure:"ssh_key_exchange_algorithms" cty:"ssh_key_exchange_algorithms" hcl:"ssh_key_exchange_algorithms"`
|
||||
SSHPrivateKeyFile *string `mapstructure:"ssh_private_key_file" undocumented:"true" cty:"ssh_private_key_file" hcl:"ssh_private_key_file"`
|
||||
SSHCertificateFile *string `mapstructure:"ssh_certificate_file" cty:"ssh_certificate_file" hcl:"ssh_certificate_file"`
|
||||
SSHPty *bool `mapstructure:"ssh_pty" cty:"ssh_pty" hcl:"ssh_pty"`
|
||||
SSHTimeout *string `mapstructure:"ssh_timeout" cty:"ssh_timeout" hcl:"ssh_timeout"`
|
||||
SSHWaitTimeout *string `mapstructure:"ssh_wait_timeout" undocumented:"true" cty:"ssh_wait_timeout" hcl:"ssh_wait_timeout"`
|
||||
SSHAgentAuth *bool `mapstructure:"ssh_agent_auth" undocumented:"true" cty:"ssh_agent_auth" hcl:"ssh_agent_auth"`
|
||||
SSHDisableAgentForwarding *bool `mapstructure:"ssh_disable_agent_forwarding" cty:"ssh_disable_agent_forwarding" hcl:"ssh_disable_agent_forwarding"`
|
||||
SSHHandshakeAttempts *int `mapstructure:"ssh_handshake_attempts" cty:"ssh_handshake_attempts" hcl:"ssh_handshake_attempts"`
|
||||
SSHBastionHost *string `mapstructure:"ssh_bastion_host" cty:"ssh_bastion_host" hcl:"ssh_bastion_host"`
|
||||
SSHBastionPort *int `mapstructure:"ssh_bastion_port" cty:"ssh_bastion_port" hcl:"ssh_bastion_port"`
|
||||
SSHBastionAgentAuth *bool `mapstructure:"ssh_bastion_agent_auth" cty:"ssh_bastion_agent_auth" hcl:"ssh_bastion_agent_auth"`
|
||||
SSHBastionUsername *string `mapstructure:"ssh_bastion_username" cty:"ssh_bastion_username" hcl:"ssh_bastion_username"`
|
||||
SSHBastionPassword *string `mapstructure:"ssh_bastion_password" cty:"ssh_bastion_password" hcl:"ssh_bastion_password"`
|
||||
SSHBastionInteractive *bool `mapstructure:"ssh_bastion_interactive" cty:"ssh_bastion_interactive" hcl:"ssh_bastion_interactive"`
|
||||
SSHBastionPrivateKeyFile *string `mapstructure:"ssh_bastion_private_key_file" cty:"ssh_bastion_private_key_file" hcl:"ssh_bastion_private_key_file"`
|
||||
SSHBastionCertificateFile *string `mapstructure:"ssh_bastion_certificate_file" cty:"ssh_bastion_certificate_file" hcl:"ssh_bastion_certificate_file"`
|
||||
SSHFileTransferMethod *string `mapstructure:"ssh_file_transfer_method" cty:"ssh_file_transfer_method" hcl:"ssh_file_transfer_method"`
|
||||
SSHProxyHost *string `mapstructure:"ssh_proxy_host" cty:"ssh_proxy_host" hcl:"ssh_proxy_host"`
|
||||
SSHProxyPort *int `mapstructure:"ssh_proxy_port" cty:"ssh_proxy_port" hcl:"ssh_proxy_port"`
|
||||
SSHProxyUsername *string `mapstructure:"ssh_proxy_username" cty:"ssh_proxy_username" hcl:"ssh_proxy_username"`
|
||||
SSHProxyPassword *string `mapstructure:"ssh_proxy_password" cty:"ssh_proxy_password" hcl:"ssh_proxy_password"`
|
||||
SSHKeepAliveInterval *string `mapstructure:"ssh_keep_alive_interval" cty:"ssh_keep_alive_interval" hcl:"ssh_keep_alive_interval"`
|
||||
SSHReadWriteTimeout *string `mapstructure:"ssh_read_write_timeout" cty:"ssh_read_write_timeout" hcl:"ssh_read_write_timeout"`
|
||||
SSHRemoteTunnels []string `mapstructure:"ssh_remote_tunnels" cty:"ssh_remote_tunnels" hcl:"ssh_remote_tunnels"`
|
||||
SSHLocalTunnels []string `mapstructure:"ssh_local_tunnels" cty:"ssh_local_tunnels" hcl:"ssh_local_tunnels"`
|
||||
SSHPublicKey []byte `mapstructure:"ssh_public_key" undocumented:"true" cty:"ssh_public_key" hcl:"ssh_public_key"`
|
||||
SSHPrivateKey []byte `mapstructure:"ssh_private_key" undocumented:"true" cty:"ssh_private_key" hcl:"ssh_private_key"`
|
||||
WinRMUser *string `mapstructure:"winrm_username" cty:"winrm_username" hcl:"winrm_username"`
|
||||
WinRMPassword *string `mapstructure:"winrm_password" cty:"winrm_password" hcl:"winrm_password"`
|
||||
WinRMHost *string `mapstructure:"winrm_host" cty:"winrm_host" hcl:"winrm_host"`
|
||||
WinRMNoProxy *bool `mapstructure:"winrm_no_proxy" cty:"winrm_no_proxy" hcl:"winrm_no_proxy"`
|
||||
WinRMPort *int `mapstructure:"winrm_port" cty:"winrm_port" hcl:"winrm_port"`
|
||||
WinRMTimeout *string `mapstructure:"winrm_timeout" cty:"winrm_timeout" hcl:"winrm_timeout"`
|
||||
WinRMUseSSL *bool `mapstructure:"winrm_use_ssl" cty:"winrm_use_ssl" hcl:"winrm_use_ssl"`
|
||||
WinRMInsecure *bool `mapstructure:"winrm_insecure" cty:"winrm_insecure" hcl:"winrm_insecure"`
|
||||
WinRMUseNTLM *bool `mapstructure:"winrm_use_ntlm" cty:"winrm_use_ntlm" hcl:"winrm_use_ntlm"`
|
||||
ProxmoxURLRaw *string `mapstructure:"proxmox_url" cty:"proxmox_url" hcl:"proxmox_url"`
|
||||
SkipCertValidation *bool `mapstructure:"insecure_skip_tls_verify" cty:"insecure_skip_tls_verify" hcl:"insecure_skip_tls_verify"`
|
||||
Username *string `mapstructure:"username" cty:"username" hcl:"username"`
|
||||
Password *string `mapstructure:"password" cty:"password" hcl:"password"`
|
||||
Token *string `mapstructure:"token" cty:"token" hcl:"token"`
|
||||
Node *string `mapstructure:"node" cty:"node" hcl:"node"`
|
||||
Pool *string `mapstructure:"pool" cty:"pool" hcl:"pool"`
|
||||
VMName *string `mapstructure:"vm_name" cty:"vm_name" hcl:"vm_name"`
|
||||
VMID *int `mapstructure:"vm_id" cty:"vm_id" hcl:"vm_id"`
|
||||
Boot *string `mapstructure:"boot" cty:"boot" hcl:"boot"`
|
||||
Memory *int `mapstructure:"memory" cty:"memory" hcl:"memory"`
|
||||
Cores *int `mapstructure:"cores" cty:"cores" hcl:"cores"`
|
||||
CPUType *string `mapstructure:"cpu_type" cty:"cpu_type" hcl:"cpu_type"`
|
||||
Sockets *int `mapstructure:"sockets" cty:"sockets" hcl:"sockets"`
|
||||
OS *string `mapstructure:"os" cty:"os" hcl:"os"`
|
||||
VGA *FlatvgaConfig `mapstructure:"vga" cty:"vga" hcl:"vga"`
|
||||
NICs []FlatnicConfig `mapstructure:"network_adapters" cty:"network_adapters" hcl:"network_adapters"`
|
||||
Disks []FlatdiskConfig `mapstructure:"disks" cty:"disks" hcl:"disks"`
|
||||
Agent *bool `mapstructure:"qemu_agent" cty:"qemu_agent" hcl:"qemu_agent"`
|
||||
SCSIController *string `mapstructure:"scsi_controller" cty:"scsi_controller" hcl:"scsi_controller"`
|
||||
Onboot *bool `mapstructure:"onboot" cty:"onboot" hcl:"onboot"`
|
||||
DisableKVM *bool `mapstructure:"disable_kvm" cty:"disable_kvm" hcl:"disable_kvm"`
|
||||
TemplateName *string `mapstructure:"template_name" cty:"template_name" hcl:"template_name"`
|
||||
TemplateDescription *string `mapstructure:"template_description" cty:"template_description" hcl:"template_description"`
|
||||
CloudInit *bool `mapstructure:"cloud_init" cty:"cloud_init" hcl:"cloud_init"`
|
||||
CloudInitStoragePool *string `mapstructure:"cloud_init_storage_pool" cty:"cloud_init_storage_pool" hcl:"cloud_init_storage_pool"`
|
||||
AdditionalISOFiles []FlatadditionalISOsConfig `mapstructure:"additional_iso_files" cty:"additional_iso_files" hcl:"additional_iso_files"`
|
||||
VMInterface *string `mapstructure:"vm_interface" cty:"vm_interface" hcl:"vm_interface"`
|
||||
PackerBuildName *string `mapstructure:"packer_build_name" cty:"packer_build_name" hcl:"packer_build_name"`
|
||||
PackerBuilderType *string `mapstructure:"packer_builder_type" cty:"packer_builder_type" hcl:"packer_builder_type"`
|
||||
PackerCoreVersion *string `mapstructure:"packer_core_version" cty:"packer_core_version" hcl:"packer_core_version"`
|
||||
PackerDebug *bool `mapstructure:"packer_debug" cty:"packer_debug" hcl:"packer_debug"`
|
||||
PackerForce *bool `mapstructure:"packer_force" cty:"packer_force" hcl:"packer_force"`
|
||||
PackerOnError *string `mapstructure:"packer_on_error" cty:"packer_on_error" hcl:"packer_on_error"`
|
||||
PackerUserVars map[string]string `mapstructure:"packer_user_variables" cty:"packer_user_variables" hcl:"packer_user_variables"`
|
||||
PackerSensitiveVars []string `mapstructure:"packer_sensitive_variables" cty:"packer_sensitive_variables" hcl:"packer_sensitive_variables"`
|
||||
HTTPDir *string `mapstructure:"http_directory" cty:"http_directory" hcl:"http_directory"`
|
||||
HTTPPortMin *int `mapstructure:"http_port_min" cty:"http_port_min" hcl:"http_port_min"`
|
||||
HTTPPortMax *int `mapstructure:"http_port_max" cty:"http_port_max" hcl:"http_port_max"`
|
||||
HTTPAddress *string `mapstructure:"http_bind_address" cty:"http_bind_address" hcl:"http_bind_address"`
|
||||
HTTPInterface *string `mapstructure:"http_interface" undocumented:"true" cty:"http_interface" hcl:"http_interface"`
|
||||
BootGroupInterval *string `mapstructure:"boot_keygroup_interval" cty:"boot_keygroup_interval" hcl:"boot_keygroup_interval"`
|
||||
BootWait *string `mapstructure:"boot_wait" cty:"boot_wait" hcl:"boot_wait"`
|
||||
BootCommand []string `mapstructure:"boot_command" cty:"boot_command" hcl:"boot_command"`
|
||||
BootKeyInterval *string `mapstructure:"boot_key_interval" cty:"boot_key_interval" hcl:"boot_key_interval"`
|
||||
Type *string `mapstructure:"communicator" cty:"communicator" hcl:"communicator"`
|
||||
PauseBeforeConnect *string `mapstructure:"pause_before_connecting" cty:"pause_before_connecting" hcl:"pause_before_connecting"`
|
||||
SSHHost *string `mapstructure:"ssh_host" cty:"ssh_host" hcl:"ssh_host"`
|
||||
SSHPort *int `mapstructure:"ssh_port" cty:"ssh_port" hcl:"ssh_port"`
|
||||
SSHUsername *string `mapstructure:"ssh_username" cty:"ssh_username" hcl:"ssh_username"`
|
||||
SSHPassword *string `mapstructure:"ssh_password" cty:"ssh_password" hcl:"ssh_password"`
|
||||
SSHKeyPairName *string `mapstructure:"ssh_keypair_name" undocumented:"true" cty:"ssh_keypair_name" hcl:"ssh_keypair_name"`
|
||||
SSHTemporaryKeyPairName *string `mapstructure:"temporary_key_pair_name" undocumented:"true" cty:"temporary_key_pair_name" hcl:"temporary_key_pair_name"`
|
||||
SSHTemporaryKeyPairType *string `mapstructure:"temporary_key_pair_type" cty:"temporary_key_pair_type" hcl:"temporary_key_pair_type"`
|
||||
SSHTemporaryKeyPairBits *int `mapstructure:"temporary_key_pair_bits" cty:"temporary_key_pair_bits" hcl:"temporary_key_pair_bits"`
|
||||
SSHCiphers []string `mapstructure:"ssh_ciphers" cty:"ssh_ciphers" hcl:"ssh_ciphers"`
|
||||
SSHClearAuthorizedKeys *bool `mapstructure:"ssh_clear_authorized_keys" cty:"ssh_clear_authorized_keys" hcl:"ssh_clear_authorized_keys"`
|
||||
SSHKEXAlgos []string `mapstructure:"ssh_key_exchange_algorithms" cty:"ssh_key_exchange_algorithms" hcl:"ssh_key_exchange_algorithms"`
|
||||
SSHPrivateKeyFile *string `mapstructure:"ssh_private_key_file" undocumented:"true" cty:"ssh_private_key_file" hcl:"ssh_private_key_file"`
|
||||
SSHCertificateFile *string `mapstructure:"ssh_certificate_file" cty:"ssh_certificate_file" hcl:"ssh_certificate_file"`
|
||||
SSHPty *bool `mapstructure:"ssh_pty" cty:"ssh_pty" hcl:"ssh_pty"`
|
||||
SSHTimeout *string `mapstructure:"ssh_timeout" cty:"ssh_timeout" hcl:"ssh_timeout"`
|
||||
SSHWaitTimeout *string `mapstructure:"ssh_wait_timeout" undocumented:"true" cty:"ssh_wait_timeout" hcl:"ssh_wait_timeout"`
|
||||
SSHAgentAuth *bool `mapstructure:"ssh_agent_auth" undocumented:"true" cty:"ssh_agent_auth" hcl:"ssh_agent_auth"`
|
||||
SSHDisableAgentForwarding *bool `mapstructure:"ssh_disable_agent_forwarding" cty:"ssh_disable_agent_forwarding" hcl:"ssh_disable_agent_forwarding"`
|
||||
SSHHandshakeAttempts *int `mapstructure:"ssh_handshake_attempts" cty:"ssh_handshake_attempts" hcl:"ssh_handshake_attempts"`
|
||||
SSHBastionHost *string `mapstructure:"ssh_bastion_host" cty:"ssh_bastion_host" hcl:"ssh_bastion_host"`
|
||||
SSHBastionPort *int `mapstructure:"ssh_bastion_port" cty:"ssh_bastion_port" hcl:"ssh_bastion_port"`
|
||||
SSHBastionAgentAuth *bool `mapstructure:"ssh_bastion_agent_auth" cty:"ssh_bastion_agent_auth" hcl:"ssh_bastion_agent_auth"`
|
||||
SSHBastionUsername *string `mapstructure:"ssh_bastion_username" cty:"ssh_bastion_username" hcl:"ssh_bastion_username"`
|
||||
SSHBastionPassword *string `mapstructure:"ssh_bastion_password" cty:"ssh_bastion_password" hcl:"ssh_bastion_password"`
|
||||
SSHBastionInteractive *bool `mapstructure:"ssh_bastion_interactive" cty:"ssh_bastion_interactive" hcl:"ssh_bastion_interactive"`
|
||||
SSHBastionPrivateKeyFile *string `mapstructure:"ssh_bastion_private_key_file" cty:"ssh_bastion_private_key_file" hcl:"ssh_bastion_private_key_file"`
|
||||
SSHBastionCertificateFile *string `mapstructure:"ssh_bastion_certificate_file" cty:"ssh_bastion_certificate_file" hcl:"ssh_bastion_certificate_file"`
|
||||
SSHFileTransferMethod *string `mapstructure:"ssh_file_transfer_method" cty:"ssh_file_transfer_method" hcl:"ssh_file_transfer_method"`
|
||||
SSHProxyHost *string `mapstructure:"ssh_proxy_host" cty:"ssh_proxy_host" hcl:"ssh_proxy_host"`
|
||||
SSHProxyPort *int `mapstructure:"ssh_proxy_port" cty:"ssh_proxy_port" hcl:"ssh_proxy_port"`
|
||||
SSHProxyUsername *string `mapstructure:"ssh_proxy_username" cty:"ssh_proxy_username" hcl:"ssh_proxy_username"`
|
||||
SSHProxyPassword *string `mapstructure:"ssh_proxy_password" cty:"ssh_proxy_password" hcl:"ssh_proxy_password"`
|
||||
SSHKeepAliveInterval *string `mapstructure:"ssh_keep_alive_interval" cty:"ssh_keep_alive_interval" hcl:"ssh_keep_alive_interval"`
|
||||
SSHReadWriteTimeout *string `mapstructure:"ssh_read_write_timeout" cty:"ssh_read_write_timeout" hcl:"ssh_read_write_timeout"`
|
||||
SSHRemoteTunnels []string `mapstructure:"ssh_remote_tunnels" cty:"ssh_remote_tunnels" hcl:"ssh_remote_tunnels"`
|
||||
SSHLocalTunnels []string `mapstructure:"ssh_local_tunnels" cty:"ssh_local_tunnels" hcl:"ssh_local_tunnels"`
|
||||
SSHPublicKey []byte `mapstructure:"ssh_public_key" undocumented:"true" cty:"ssh_public_key" hcl:"ssh_public_key"`
|
||||
SSHPrivateKey []byte `mapstructure:"ssh_private_key" undocumented:"true" cty:"ssh_private_key" hcl:"ssh_private_key"`
|
||||
WinRMUser *string `mapstructure:"winrm_username" cty:"winrm_username" hcl:"winrm_username"`
|
||||
WinRMPassword *string `mapstructure:"winrm_password" cty:"winrm_password" hcl:"winrm_password"`
|
||||
WinRMHost *string `mapstructure:"winrm_host" cty:"winrm_host" hcl:"winrm_host"`
|
||||
WinRMNoProxy *bool `mapstructure:"winrm_no_proxy" cty:"winrm_no_proxy" hcl:"winrm_no_proxy"`
|
||||
WinRMPort *int `mapstructure:"winrm_port" cty:"winrm_port" hcl:"winrm_port"`
|
||||
WinRMTimeout *string `mapstructure:"winrm_timeout" cty:"winrm_timeout" hcl:"winrm_timeout"`
|
||||
WinRMUseSSL *bool `mapstructure:"winrm_use_ssl" cty:"winrm_use_ssl" hcl:"winrm_use_ssl"`
|
||||
WinRMInsecure *bool `mapstructure:"winrm_insecure" cty:"winrm_insecure" hcl:"winrm_insecure"`
|
||||
WinRMUseNTLM *bool `mapstructure:"winrm_use_ntlm" cty:"winrm_use_ntlm" hcl:"winrm_use_ntlm"`
|
||||
ProxmoxURLRaw *string `mapstructure:"proxmox_url" cty:"proxmox_url" hcl:"proxmox_url"`
|
||||
SkipCertValidation *bool `mapstructure:"insecure_skip_tls_verify" cty:"insecure_skip_tls_verify" hcl:"insecure_skip_tls_verify"`
|
||||
Username *string `mapstructure:"username" cty:"username" hcl:"username"`
|
||||
Password *string `mapstructure:"password" cty:"password" hcl:"password"`
|
||||
Node *string `mapstructure:"node" cty:"node" hcl:"node"`
|
||||
Pool *string `mapstructure:"pool" cty:"pool" hcl:"pool"`
|
||||
VMName *string `mapstructure:"vm_name" cty:"vm_name" hcl:"vm_name"`
|
||||
VMID *int `mapstructure:"vm_id" cty:"vm_id" hcl:"vm_id"`
|
||||
Boot *string `mapstructure:"boot" cty:"boot" hcl:"boot"`
|
||||
Memory *int `mapstructure:"memory" cty:"memory" hcl:"memory"`
|
||||
Cores *int `mapstructure:"cores" cty:"cores" hcl:"cores"`
|
||||
CPUType *string `mapstructure:"cpu_type" cty:"cpu_type" hcl:"cpu_type"`
|
||||
Sockets *int `mapstructure:"sockets" cty:"sockets" hcl:"sockets"`
|
||||
OS *string `mapstructure:"os" cty:"os" hcl:"os"`
|
||||
VGA *FlatvgaConfig `mapstructure:"vga" cty:"vga" hcl:"vga"`
|
||||
NICs []FlatnicConfig `mapstructure:"network_adapters" cty:"network_adapters" hcl:"network_adapters"`
|
||||
Disks []FlatdiskConfig `mapstructure:"disks" cty:"disks" hcl:"disks"`
|
||||
Agent *bool `mapstructure:"qemu_agent" cty:"qemu_agent" hcl:"qemu_agent"`
|
||||
SCSIController *string `mapstructure:"scsi_controller" cty:"scsi_controller" hcl:"scsi_controller"`
|
||||
Onboot *bool `mapstructure:"onboot" cty:"onboot" hcl:"onboot"`
|
||||
DisableKVM *bool `mapstructure:"disable_kvm" cty:"disable_kvm" hcl:"disable_kvm"`
|
||||
TemplateName *string `mapstructure:"template_name" cty:"template_name" hcl:"template_name"`
|
||||
TemplateDescription *string `mapstructure:"template_description" cty:"template_description" hcl:"template_description"`
|
||||
CloudInit *bool `mapstructure:"cloud_init" cty:"cloud_init" hcl:"cloud_init"`
|
||||
CloudInitStoragePool *string `mapstructure:"cloud_init_storage_pool" cty:"cloud_init_storage_pool" hcl:"cloud_init_storage_pool"`
|
||||
AdditionalISOFiles []FlatstorageConfig `mapstructure:"additional_iso_files" cty:"additional_iso_files" hcl:"additional_iso_files"`
|
||||
VMInterface *string `mapstructure:"vm_interface" cty:"vm_interface" hcl:"vm_interface"`
|
||||
}
|
||||
|
||||
// FlatMapstructure returns a new FlatConfig.
|
||||
@@ -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_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_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_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},
|
||||
@@ -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},
|
||||
"username": &hcldec.AttrSpec{Name: "username", 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},
|
||||
"pool": &hcldec.AttrSpec{Name: "pool", 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},
|
||||
"cloud_init": &hcldec.AttrSpec{Name: "cloud_init", Type: cty.Bool, Required: false},
|
||||
"cloud_init_storage_pool": &hcldec.AttrSpec{Name: "cloud_init_storage_pool", Type: cty.String, Required: false},
|
||||
"additional_iso_files": &hcldec.BlockListSpec{TypeName: "additional_iso_files", Nested: hcldec.ObjectSpec((*FlatadditionalISOsConfig)(nil).HCL2Spec())},
|
||||
"additional_iso_files": &hcldec.BlockListSpec{TypeName: "additional_iso_files", Nested: hcldec.ObjectSpec((*FlatstorageConfig)(nil).HCL2Spec())},
|
||||
"vm_interface": &hcldec.AttrSpec{Name: "vm_interface", Type: cty.String, Required: false},
|
||||
}
|
||||
return s
|
||||
}
|
||||
|
||||
// FlatadditionalISOsConfig is an auto-generated flat version of additionalISOsConfig.
|
||||
// Where the contents of a field with a `mapstructure:,squash` tag are bubbled up.
|
||||
type FlatadditionalISOsConfig struct {
|
||||
ISOChecksum *string `mapstructure:"iso_checksum" required:"true" cty:"iso_checksum" hcl:"iso_checksum"`
|
||||
RawSingleISOUrl *string `mapstructure:"iso_url" required:"true" cty:"iso_url" hcl:"iso_url"`
|
||||
ISOUrls []string `mapstructure:"iso_urls" cty:"iso_urls" hcl:"iso_urls"`
|
||||
TargetPath *string `mapstructure:"iso_target_path" cty:"iso_target_path" hcl:"iso_target_path"`
|
||||
TargetExtension *string `mapstructure:"iso_target_extension" cty:"iso_target_extension" hcl:"iso_target_extension"`
|
||||
Device *string `mapstructure:"device" cty:"device" hcl:"device"`
|
||||
ISOFile *string `mapstructure:"iso_file" cty:"iso_file" hcl:"iso_file"`
|
||||
ISOStoragePool *string `mapstructure:"iso_storage_pool" cty:"iso_storage_pool" hcl:"iso_storage_pool"`
|
||||
Unmount *bool `mapstructure:"unmount" cty:"unmount" hcl:"unmount"`
|
||||
}
|
||||
|
||||
// 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.
|
||||
// Where the contents of a field with a `mapstructure:,squash` tag are bubbled up.
|
||||
type FlatdiskConfig struct {
|
||||
@@ -325,6 +282,49 @@ func (*FlatnicConfig) HCL2Spec() map[string]hcldec.Spec {
|
||||
return s
|
||||
}
|
||||
|
||||
// FlatstorageConfig is an auto-generated flat version of storageConfig.
|
||||
// Where the contents of a field with a `mapstructure:,squash` tag are bubbled up.
|
||||
type FlatstorageConfig struct {
|
||||
ISOChecksum *string `mapstructure:"iso_checksum" required:"true" cty:"iso_checksum" hcl:"iso_checksum"`
|
||||
RawSingleISOUrl *string `mapstructure:"iso_url" required:"true" cty:"iso_url" hcl:"iso_url"`
|
||||
ISOUrls []string `mapstructure:"iso_urls" cty:"iso_urls" hcl:"iso_urls"`
|
||||
TargetPath *string `mapstructure:"iso_target_path" cty:"iso_target_path" hcl:"iso_target_path"`
|
||||
TargetExtension *string `mapstructure:"iso_target_extension" cty:"iso_target_extension" hcl:"iso_target_extension"`
|
||||
Device *string `mapstructure:"device" cty:"device" hcl:"device"`
|
||||
ISOFile *string `mapstructure:"iso_file" cty:"iso_file" hcl:"iso_file"`
|
||||
ISOStoragePool *string `mapstructure:"iso_storage_pool" cty:"iso_storage_pool" hcl:"iso_storage_pool"`
|
||||
Unmount *bool `mapstructure:"unmount" cty:"unmount" hcl:"unmount"`
|
||||
ShouldUploadISO *bool `cty:"should_upload_iso" hcl:"should_upload_iso"`
|
||||
DownloadPathKey *string `cty:"download_path_key" hcl:"download_path_key"`
|
||||
}
|
||||
|
||||
// FlatMapstructure returns a new FlatstorageConfig.
|
||||
// FlatstorageConfig is an auto-generated flat version of storageConfig.
|
||||
// Where the contents a fields with a `mapstructure:,squash` tag are bubbled up.
|
||||
func (*storageConfig) FlatMapstructure() interface{ HCL2Spec() map[string]hcldec.Spec } {
|
||||
return new(FlatstorageConfig)
|
||||
}
|
||||
|
||||
// HCL2Spec returns the hcl spec of a storageConfig.
|
||||
// This spec is used by HCL to read the fields of storageConfig.
|
||||
// The decoded values from this spec will then be applied to a FlatstorageConfig.
|
||||
func (*FlatstorageConfig) HCL2Spec() map[string]hcldec.Spec {
|
||||
s := map[string]hcldec.Spec{
|
||||
"iso_checksum": &hcldec.AttrSpec{Name: "iso_checksum", Type: cty.String, Required: false},
|
||||
"iso_url": &hcldec.AttrSpec{Name: "iso_url", Type: cty.String, Required: false},
|
||||
"iso_urls": &hcldec.AttrSpec{Name: "iso_urls", Type: cty.List(cty.String), Required: false},
|
||||
"iso_target_path": &hcldec.AttrSpec{Name: "iso_target_path", Type: cty.String, Required: false},
|
||||
"iso_target_extension": &hcldec.AttrSpec{Name: "iso_target_extension", Type: cty.String, Required: false},
|
||||
"device": &hcldec.AttrSpec{Name: "device", Type: cty.String, Required: false},
|
||||
"iso_file": &hcldec.AttrSpec{Name: "iso_file", Type: cty.String, Required: false},
|
||||
"iso_storage_pool": &hcldec.AttrSpec{Name: "iso_storage_pool", Type: cty.String, Required: false},
|
||||
"unmount": &hcldec.AttrSpec{Name: "unmount", Type: cty.Bool, Required: false},
|
||||
"should_upload_iso": &hcldec.AttrSpec{Name: "should_upload_iso", Type: cty.Bool, Required: false},
|
||||
"download_path_key": &hcldec.AttrSpec{Name: "download_path_key", Type: cty.String, Required: false},
|
||||
}
|
||||
return s
|
||||
}
|
||||
|
||||
// FlatvgaConfig is an auto-generated flat version of vgaConfig.
|
||||
// Where the contents of a field with a `mapstructure:,squash` tag are bubbled up.
|
||||
type FlatvgaConfig struct {
|
||||
|
||||
@@ -80,30 +80,6 @@ func (s *stepFinalizeTemplateConfig) Run(ctx context.Context, state multistep.St
|
||||
}
|
||||
}
|
||||
|
||||
if len(c.AdditionalISOFiles) > 0 {
|
||||
vmParams, err := client.GetVmConfig(vmRef)
|
||||
if err != nil {
|
||||
err := fmt.Errorf("Error fetching template config: %s", err)
|
||||
state.Put("error", err)
|
||||
ui.Error(err.Error())
|
||||
return multistep.ActionHalt
|
||||
}
|
||||
for idx := range c.AdditionalISOFiles {
|
||||
cdrom := c.AdditionalISOFiles[idx].Device
|
||||
if c.AdditionalISOFiles[idx].Unmount {
|
||||
if vmParams[cdrom] == nil || !strings.Contains(vmParams[cdrom].(string), "media=cdrom") {
|
||||
err := fmt.Errorf("Cannot eject ISO from cdrom drive, %s is not present or not a cdrom media", cdrom)
|
||||
state.Put("error", err)
|
||||
ui.Error(err.Error())
|
||||
return multistep.ActionHalt
|
||||
}
|
||||
changes[cdrom] = "none,media=cdrom"
|
||||
} else {
|
||||
changes[cdrom] = c.AdditionalISOFiles[idx].ISOFile + ",media=cdrom"
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if len(changes) > 0 {
|
||||
_, err := client.SetVmConfig(vmRef, changes)
|
||||
if err != nil {
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user