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