Compare commits
93 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| fd3cc596c7 | |||
| 708f9cdfbd | |||
| c3e78d2c32 | |||
| 1b8e71ca1f | |||
| 3e497e3712 | |||
| 030da4b6b9 | |||
| 3a437d4891 | |||
| 8c2f26718e | |||
| 076596cd3b | |||
| f541cd59ed | |||
| 4b6891d6d5 | |||
| 4746fc682d | |||
| f6dbc3e78a | |||
| 03d79a2c39 | |||
| 349a300213 | |||
| dfc5d76108 | |||
| 77a29fc2f8 | |||
| a588808270 | |||
| b9b1cdf75f | |||
| 2ac5fe894c | |||
| fbb9429910 | |||
| cb359e8064 | |||
| a9bec7945e | |||
| 505cbd2591 | |||
| 25fddf3199 | |||
| 166df2ce1d | |||
| 755395faf8 | |||
| b958fe7a54 | |||
| cc133ea250 | |||
| 4ea4c0570f | |||
| a665e6b822 | |||
| 2de91e4862 | |||
| 3a11820a41 | |||
| 0e3fcb589b | |||
| 70ceed1110 | |||
| 82eedc8f02 | |||
| 1d53080625 | |||
| 89931d0f2a | |||
| 5e17dbeff2 | |||
| d0512c6edd | |||
| 7732f7998c | |||
| ff01e6715a | |||
| edc19eb859 | |||
| 1bb5c455aa | |||
| 8c61ca174f | |||
| ef6093c4c3 | |||
| 4d9fb629c6 | |||
| 0993c976fa | |||
| 1e312ebc21 | |||
| b780da5750 | |||
| 565ca6627c | |||
| c100b56d44 | |||
| e6596a0a1d | |||
| 55012937a9 | |||
| 15d467eaf1 | |||
| ce896351b9 | |||
| fb0886b724 | |||
| a915ec8e05 | |||
| 667f930d3d | |||
| f2f65607eb | |||
| ecaff88af9 | |||
| a40a782408 | |||
| 80f807de4d | |||
| ac7c0f2f04 | |||
| e2e6bce4c4 | |||
| 9f647ba2bb | |||
| 7c6c399a38 | |||
| d5ccf73e91 | |||
| 502708b86a | |||
| 9b641c9bfd | |||
| 1d485988ea | |||
| de12cd318d | |||
| 597dcce2ab | |||
| 0f8a658a23 | |||
| 4242cf3151 | |||
| 618a3d42c6 | |||
| 203900e403 | |||
| bece5c3c69 | |||
| 9e03647ad7 | |||
| e68e736b6c | |||
| 26a572270d | |||
| 8b3e7e6f2f | |||
| 341308c582 | |||
| 3227d3da43 | |||
| 0f541aaf5e | |||
| 0ecc4b5e52 | |||
| 6986fb0e81 | |||
| a6f907c688 | |||
| 3e8641e30e | |||
| d0737dcd17 | |||
| f12c89bd84 | |||
| 0b5f8901cc | |||
| 64a3219f69 |
+16
-3
@@ -37,15 +37,19 @@ commands:
|
|||||||
parameters:
|
parameters:
|
||||||
GOOS:
|
GOOS:
|
||||||
type: string
|
type: string
|
||||||
|
GOARCH:
|
||||||
|
default: "amd64"
|
||||||
|
type: string
|
||||||
steps:
|
steps:
|
||||||
- checkout
|
- checkout
|
||||||
- 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: GOOS=<< parameters.GOOS >> GOARCH=<<parameters.GOARCH>> go build -ldflags="-s -w -X github.com/hashicorp/packer/version.GitCommit=${CIRCLE_SHA1}" -o ./pkg/packer_<< parameters.GOOS >>_<< parameters.GOARCH >> .
|
||||||
- run: zip ./pkg/packer_<< parameters.GOOS >>_$(go env GOARCH).zip ./pkg/packer_<< parameters.GOOS >>_$(go env GOARCH)
|
- run: zip ./pkg/packer_<< parameters.GOOS >>_<< parameters.GOARCH >>.zip ./pkg/packer_<< parameters.GOOS >>_<< parameters.GOARCH >>
|
||||||
- run: rm ./pkg/packer_<< parameters.GOOS >>_$(go env GOARCH)
|
- run: rm ./pkg/packer_<< parameters.GOOS >>_<< parameters.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
|
||||||
@@ -123,6 +127,13 @@ 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
|
||||||
@@ -203,6 +214,7 @@ 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
|
||||||
@@ -211,6 +223,7 @@ 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,6 +6,7 @@
|
|||||||
*.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
|
||||||
|
|||||||
@@ -11,6 +11,12 @@ 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
|
||||||
|
|||||||
@@ -0,0 +1,77 @@
|
|||||||
|
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();
|
||||||
@@ -0,0 +1,29 @@
|
|||||||
|
#
|
||||||
|
# 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: 3 #cancel job if no deployment is found within x minutes
|
timeout-minutes: 5 #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,3 +22,21 @@ 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}."
|
||||||
|
}
|
||||||
|
|
||||||
|
|||||||
+111
-4
@@ -1,30 +1,137 @@
|
|||||||
## 1.7.1 (Upcoming)
|
## 1.7.2 (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
|
### IMPROVEMENTS
|
||||||
* builder/amazon: allow creation of ebs snapshots wihtout volumes. [GH-9591]
|
* 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/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
|
* builder/virtualbox: Add template options for chipset, firmware, nic, graphics
|
||||||
controller, and audio controller. [GH-10671]
|
controller, and audio controller. [GH-10671]
|
||||||
* builder/virtualbox: Support for "virtio" storage and ISO drive. [GH-10632]
|
* builder/virtualbox: Support for "virtio" storage and ISO drive. [GH-10632]
|
||||||
* builder/vmware: Added "attach_snapshot" parameter to vmware vmx builder.
|
* builder/vmware: Added "attach_snapshot" parameter to vmware vmx builder.
|
||||||
[GH-10651]
|
[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.
|
* core: Change template parsing error to include warning about file extensions.
|
||||||
[GH-10652]
|
[GH-10652]
|
||||||
* hcl2_upgrade: hcl2_upgrade command can now upgrade json var-files [GH-10676]
|
* 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
|
### 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/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: 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.
|
* builder/oracle-oci: Update Oracle Go SDK to fix issue with reading key file.
|
||||||
[GH-10560]
|
[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
|
* builder/vmware: Added a fallback file check when trying to determine the
|
||||||
network-mapping configuration. [GH-10543]
|
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
|
* core/hcl2_upgrade: Make hcl2_upgrade command correctly translate
|
||||||
pause_before. [GH-10654]
|
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
|
* core: Templates previously could not interpolate the environment variable
|
||||||
PACKER_LOG_PATH. [GH-10660]
|
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.
|
* provisioner/chef-solo: HCL2 templates can support the json_string option.
|
||||||
[GH-10655]
|
[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)
|
## 1.7.0 (February 17, 2021)
|
||||||
|
|
||||||
|
|||||||
@@ -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 get github.com/mitchellh/gox
|
@go install github.com/mitchellh/gox@v1.0.1
|
||||||
|
|
||||||
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,251 +0,0 @@
|
|||||||
package chroot
|
|
||||||
|
|
||||||
import (
|
|
||||||
"testing"
|
|
||||||
|
|
||||||
packersdk "github.com/hashicorp/packer-plugin-sdk/packer"
|
|
||||||
)
|
|
||||||
|
|
||||||
func testConfig() map[string]interface{} {
|
|
||||||
return map[string]interface{}{
|
|
||||||
"ami_name": "foo",
|
|
||||||
"source_ami": "foo",
|
|
||||||
"region": "us-east-1",
|
|
||||||
// region validation logic is checked in ami_config_test
|
|
||||||
"skip_region_validation": true,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestBuilder_ImplementsBuilder(t *testing.T) {
|
|
||||||
var raw interface{}
|
|
||||||
raw = &Builder{}
|
|
||||||
if _, ok := raw.(packersdk.Builder); !ok {
|
|
||||||
t.Fatalf("Builder should be a builder")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestBuilderPrepare_AMIName(t *testing.T) {
|
|
||||||
var b Builder
|
|
||||||
config := testConfig()
|
|
||||||
|
|
||||||
// Test good
|
|
||||||
config["ami_name"] = "foo"
|
|
||||||
config["skip_region_validation"] = true
|
|
||||||
_, warnings, err := b.Prepare(config)
|
|
||||||
if len(warnings) > 0 {
|
|
||||||
t.Fatalf("bad: %#v", warnings)
|
|
||||||
}
|
|
||||||
if err != nil {
|
|
||||||
t.Fatalf("should not have error: %s", err)
|
|
||||||
}
|
|
||||||
|
|
||||||
// Test bad
|
|
||||||
config["ami_name"] = "foo {{"
|
|
||||||
b = Builder{}
|
|
||||||
_, warnings, err = b.Prepare(config)
|
|
||||||
if len(warnings) > 0 {
|
|
||||||
t.Fatalf("bad: %#v", warnings)
|
|
||||||
}
|
|
||||||
if err == nil {
|
|
||||||
t.Fatal("should have error")
|
|
||||||
}
|
|
||||||
|
|
||||||
// Test bad
|
|
||||||
delete(config, "ami_name")
|
|
||||||
b = Builder{}
|
|
||||||
_, warnings, err = b.Prepare(config)
|
|
||||||
if len(warnings) > 0 {
|
|
||||||
t.Fatalf("bad: %#v", warnings)
|
|
||||||
}
|
|
||||||
if err == nil {
|
|
||||||
t.Fatal("should have error")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestBuilderPrepare_ChrootMounts(t *testing.T) {
|
|
||||||
b := &Builder{}
|
|
||||||
config := testConfig()
|
|
||||||
|
|
||||||
config["chroot_mounts"] = nil
|
|
||||||
_, warnings, err := b.Prepare(config)
|
|
||||||
if len(warnings) > 0 {
|
|
||||||
t.Fatalf("bad: %#v", warnings)
|
|
||||||
}
|
|
||||||
if err != nil {
|
|
||||||
t.Errorf("err: %s", err)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestBuilderPrepare_ChrootMountsBadDefaults(t *testing.T) {
|
|
||||||
b := &Builder{}
|
|
||||||
config := testConfig()
|
|
||||||
|
|
||||||
config["chroot_mounts"] = [][]string{
|
|
||||||
{"bad"},
|
|
||||||
}
|
|
||||||
_, warnings, err := b.Prepare(config)
|
|
||||||
if len(warnings) > 0 {
|
|
||||||
t.Fatalf("bad: %#v", warnings)
|
|
||||||
}
|
|
||||||
if err == nil {
|
|
||||||
t.Fatal("should have error")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
func TestBuilderPrepare_SourceAmi(t *testing.T) {
|
|
||||||
b := &Builder{}
|
|
||||||
config := testConfig()
|
|
||||||
|
|
||||||
config["source_ami"] = ""
|
|
||||||
_, warnings, err := b.Prepare(config)
|
|
||||||
if len(warnings) > 0 {
|
|
||||||
t.Fatalf("bad: %#v", warnings)
|
|
||||||
}
|
|
||||||
if err == nil {
|
|
||||||
t.Fatal("should have error")
|
|
||||||
}
|
|
||||||
|
|
||||||
config["source_ami"] = "foo"
|
|
||||||
_, warnings, err = b.Prepare(config)
|
|
||||||
if len(warnings) > 0 {
|
|
||||||
t.Fatalf("bad: %#v", warnings)
|
|
||||||
}
|
|
||||||
if err != nil {
|
|
||||||
t.Errorf("err: %s", err)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestBuilderPrepare_CommandWrapper(t *testing.T) {
|
|
||||||
b := &Builder{}
|
|
||||||
config := testConfig()
|
|
||||||
|
|
||||||
config["command_wrapper"] = "echo hi; {{.Command}}"
|
|
||||||
_, warnings, err := b.Prepare(config)
|
|
||||||
if len(warnings) > 0 {
|
|
||||||
t.Fatalf("bad: %#v", warnings)
|
|
||||||
}
|
|
||||||
if err != nil {
|
|
||||||
t.Errorf("err: %s", err)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestBuilderPrepare_CopyFiles(t *testing.T) {
|
|
||||||
b := &Builder{}
|
|
||||||
config := testConfig()
|
|
||||||
|
|
||||||
_, warnings, err := b.Prepare(config)
|
|
||||||
if len(warnings) > 0 {
|
|
||||||
t.Fatalf("bad: %#v", warnings)
|
|
||||||
}
|
|
||||||
if err != nil {
|
|
||||||
t.Errorf("err: %s", err)
|
|
||||||
}
|
|
||||||
|
|
||||||
if len(b.config.CopyFiles) != 1 && b.config.CopyFiles[0] != "/etc/resolv.conf" {
|
|
||||||
t.Errorf("Was expecting default value for copy_files.")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestBuilderPrepare_CopyFilesNoDefault(t *testing.T) {
|
|
||||||
b := &Builder{}
|
|
||||||
config := testConfig()
|
|
||||||
|
|
||||||
config["copy_files"] = []string{}
|
|
||||||
_, warnings, err := b.Prepare(config)
|
|
||||||
if len(warnings) > 0 {
|
|
||||||
t.Fatalf("bad: %#v", warnings)
|
|
||||||
}
|
|
||||||
if err != nil {
|
|
||||||
t.Errorf("err: %s", err)
|
|
||||||
}
|
|
||||||
|
|
||||||
if len(b.config.CopyFiles) > 0 {
|
|
||||||
t.Errorf("Was expecting no default value for copy_files. Found %v",
|
|
||||||
b.config.CopyFiles)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestBuilderPrepare_RootDeviceNameAndAMIMappings(t *testing.T) {
|
|
||||||
var b Builder
|
|
||||||
config := testConfig()
|
|
||||||
|
|
||||||
config["root_device_name"] = "/dev/sda"
|
|
||||||
config["ami_block_device_mappings"] = []interface{}{map[string]string{}}
|
|
||||||
config["root_volume_size"] = 15
|
|
||||||
_, warnings, err := b.Prepare(config)
|
|
||||||
if len(warnings) == 0 {
|
|
||||||
t.Fatal("Missing warning, stating block device mappings will be overwritten")
|
|
||||||
} else if len(warnings) > 1 {
|
|
||||||
t.Fatalf("excessive warnings: %#v", warnings)
|
|
||||||
}
|
|
||||||
if err != nil {
|
|
||||||
t.Fatalf("should not have error: %s", err)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestBuilderPrepare_AMIMappingsNoRootDeviceName(t *testing.T) {
|
|
||||||
var b Builder
|
|
||||||
config := testConfig()
|
|
||||||
|
|
||||||
config["ami_block_device_mappings"] = []interface{}{map[string]string{}}
|
|
||||||
_, warnings, err := b.Prepare(config)
|
|
||||||
if len(warnings) > 0 {
|
|
||||||
t.Fatalf("bad: %#v", warnings)
|
|
||||||
}
|
|
||||||
if err == nil {
|
|
||||||
t.Fatalf("should have error")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestBuilderPrepare_RootDeviceNameNoAMIMappings(t *testing.T) {
|
|
||||||
var b Builder
|
|
||||||
config := testConfig()
|
|
||||||
|
|
||||||
config["root_device_name"] = "/dev/sda"
|
|
||||||
_, warnings, err := b.Prepare(config)
|
|
||||||
if len(warnings) > 0 {
|
|
||||||
t.Fatalf("bad: %#v", warnings)
|
|
||||||
}
|
|
||||||
if err == nil {
|
|
||||||
t.Fatalf("should have error")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestBuilderPrepare_ReturnGeneratedData(t *testing.T) {
|
|
||||||
var b Builder
|
|
||||||
config := testConfig()
|
|
||||||
|
|
||||||
generatedData, warnings, err := b.Prepare(config)
|
|
||||||
if len(warnings) > 0 {
|
|
||||||
t.Fatalf("bad: %#v", warnings)
|
|
||||||
}
|
|
||||||
if err != nil {
|
|
||||||
t.Fatalf("should not have error: %s", err)
|
|
||||||
}
|
|
||||||
if len(generatedData) == 0 {
|
|
||||||
t.Fatalf("Generated data should not be empty")
|
|
||||||
}
|
|
||||||
if generatedData[0] != "SourceAMIName" {
|
|
||||||
t.Fatalf("Generated data should contain SourceAMIName")
|
|
||||||
}
|
|
||||||
if generatedData[1] != "BuildRegion" {
|
|
||||||
t.Fatalf("Generated data should contain BuildRegion")
|
|
||||||
}
|
|
||||||
if generatedData[2] != "SourceAMI" {
|
|
||||||
t.Fatalf("Generated data should contain SourceAMI")
|
|
||||||
}
|
|
||||||
if generatedData[3] != "SourceAMICreationDate" {
|
|
||||||
t.Fatalf("Generated data should contain SourceAMICreationDate")
|
|
||||||
}
|
|
||||||
if generatedData[4] != "SourceAMIOwner" {
|
|
||||||
t.Fatalf("Generated data should contain SourceAMIOwner")
|
|
||||||
}
|
|
||||||
if generatedData[5] != "SourceAMIOwnerName" {
|
|
||||||
t.Fatalf("Generated data should contain SourceAMIOwnerName")
|
|
||||||
}
|
|
||||||
if generatedData[6] != "Device" {
|
|
||||||
t.Fatalf("Generated data should contain Device")
|
|
||||||
}
|
|
||||||
if generatedData[7] != "MountPath" {
|
|
||||||
t.Fatalf("Generated data should contain MountPath")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,51 +0,0 @@
|
|||||||
package chroot
|
|
||||||
|
|
||||||
import (
|
|
||||||
"fmt"
|
|
||||||
"io/ioutil"
|
|
||||||
"os"
|
|
||||||
"runtime"
|
|
||||||
"testing"
|
|
||||||
|
|
||||||
"github.com/hashicorp/packer-plugin-sdk/common"
|
|
||||||
)
|
|
||||||
|
|
||||||
func TestCopyFile(t *testing.T) {
|
|
||||||
if runtime.GOOS == "windows" {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
first, err := ioutil.TempFile("", "copy_files_test")
|
|
||||||
if err != nil {
|
|
||||||
t.Fatalf("couldn't create temp file.")
|
|
||||||
}
|
|
||||||
defer os.Remove(first.Name())
|
|
||||||
newName := first.Name() + "-new"
|
|
||||||
|
|
||||||
payload := "copy_files_test.go payload"
|
|
||||||
if _, err = first.WriteString(payload); err != nil {
|
|
||||||
t.Fatalf("Couldn't write payload to first file.")
|
|
||||||
}
|
|
||||||
first.Sync()
|
|
||||||
|
|
||||||
cmd := common.ShellCommand(fmt.Sprintf("cp %s %s", first.Name(), newName))
|
|
||||||
if err := cmd.Run(); err != nil {
|
|
||||||
t.Fatalf("Couldn't copy file")
|
|
||||||
}
|
|
||||||
defer os.Remove(newName)
|
|
||||||
|
|
||||||
second, err := os.Open(newName)
|
|
||||||
if err != nil {
|
|
||||||
t.Fatalf("Couldn't open copied file.")
|
|
||||||
}
|
|
||||||
defer second.Close()
|
|
||||||
|
|
||||||
var copiedPayload = make([]byte, len(payload))
|
|
||||||
if _, err := second.Read(copiedPayload); err != nil {
|
|
||||||
t.Fatalf("Couldn't open copied file for reading.")
|
|
||||||
}
|
|
||||||
|
|
||||||
if string(copiedPayload) != payload {
|
|
||||||
t.Fatalf("payload not copied.")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,10 +0,0 @@
|
|||||||
package chroot
|
|
||||||
|
|
||||||
import "testing"
|
|
||||||
|
|
||||||
func TestDevicePrefixMatch(t *testing.T) {
|
|
||||||
/*
|
|
||||||
if devicePrefixMatch("nvme0n1") != "" {
|
|
||||||
}
|
|
||||||
*/
|
|
||||||
}
|
|
||||||
@@ -1,15 +0,0 @@
|
|||||||
package chroot
|
|
||||||
|
|
||||||
import (
|
|
||||||
"testing"
|
|
||||||
|
|
||||||
"github.com/hashicorp/packer-plugin-sdk/chroot"
|
|
||||||
)
|
|
||||||
|
|
||||||
func TestAttachVolumeCleanupFunc_ImplementsCleanupFunc(t *testing.T) {
|
|
||||||
var raw interface{}
|
|
||||||
raw = new(StepAttachVolume)
|
|
||||||
if _, ok := raw.(chroot.Cleanup); !ok {
|
|
||||||
t.Fatalf("cleanup func should be a CleanupFunc")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,97 +0,0 @@
|
|||||||
package chroot
|
|
||||||
|
|
||||||
import (
|
|
||||||
"testing"
|
|
||||||
|
|
||||||
"github.com/aws/aws-sdk-go/aws"
|
|
||||||
"github.com/aws/aws-sdk-go/service/ec2"
|
|
||||||
confighelper "github.com/hashicorp/packer-plugin-sdk/template/config"
|
|
||||||
"github.com/stretchr/testify/assert"
|
|
||||||
)
|
|
||||||
|
|
||||||
func buildTestRootDevice() *ec2.BlockDeviceMapping {
|
|
||||||
return &ec2.BlockDeviceMapping{
|
|
||||||
Ebs: &ec2.EbsBlockDevice{
|
|
||||||
VolumeSize: aws.Int64(10),
|
|
||||||
SnapshotId: aws.String("snap-1234"),
|
|
||||||
VolumeType: aws.String("gp2"),
|
|
||||||
Encrypted: aws.Bool(false),
|
|
||||||
},
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestCreateVolume_Default(t *testing.T) {
|
|
||||||
stepCreateVolume := new(StepCreateVolume)
|
|
||||||
_, err := stepCreateVolume.buildCreateVolumeInput("test-az", buildTestRootDevice())
|
|
||||||
assert.NoError(t, err)
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestCreateVolume_Shrink(t *testing.T) {
|
|
||||||
stepCreateVolume := StepCreateVolume{RootVolumeSize: 1}
|
|
||||||
testRootDevice := buildTestRootDevice()
|
|
||||||
ret, err := stepCreateVolume.buildCreateVolumeInput("test-az", testRootDevice)
|
|
||||||
assert.NoError(t, err)
|
|
||||||
// Ensure that the new value is equal to the size of the old root device
|
|
||||||
assert.Equal(t, *ret.Size, *testRootDevice.Ebs.VolumeSize)
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestCreateVolume_Expand(t *testing.T) {
|
|
||||||
stepCreateVolume := StepCreateVolume{RootVolumeSize: 25}
|
|
||||||
testRootDevice := buildTestRootDevice()
|
|
||||||
ret, err := stepCreateVolume.buildCreateVolumeInput("test-az", testRootDevice)
|
|
||||||
assert.NoError(t, err)
|
|
||||||
// Ensure that the new value is equal to the size of the value passed in
|
|
||||||
assert.Equal(t, *ret.Size, stepCreateVolume.RootVolumeSize)
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestCreateVolume_io1_to_io1(t *testing.T) {
|
|
||||||
stepCreateVolume := StepCreateVolume{RootVolumeType: "io1"}
|
|
||||||
testRootDevice := buildTestRootDevice()
|
|
||||||
testRootDevice.Ebs.VolumeType = aws.String("io1")
|
|
||||||
testRootDevice.Ebs.Iops = aws.Int64(1000)
|
|
||||||
ret, err := stepCreateVolume.buildCreateVolumeInput("test-az", testRootDevice)
|
|
||||||
assert.NoError(t, err)
|
|
||||||
assert.Equal(t, *ret.VolumeType, stepCreateVolume.RootVolumeType)
|
|
||||||
assert.Equal(t, *ret.Iops, *testRootDevice.Ebs.Iops)
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestCreateVolume_io1_to_gp2(t *testing.T) {
|
|
||||||
stepCreateVolume := StepCreateVolume{RootVolumeType: "gp2"}
|
|
||||||
testRootDevice := buildTestRootDevice()
|
|
||||||
testRootDevice.Ebs.VolumeType = aws.String("io1")
|
|
||||||
testRootDevice.Ebs.Iops = aws.Int64(1000)
|
|
||||||
|
|
||||||
ret, err := stepCreateVolume.buildCreateVolumeInput("test-az", testRootDevice)
|
|
||||||
assert.NoError(t, err)
|
|
||||||
assert.Equal(t, *ret.VolumeType, stepCreateVolume.RootVolumeType)
|
|
||||||
assert.Nil(t, ret.Iops)
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestCreateVolume_gp2_to_io1(t *testing.T) {
|
|
||||||
stepCreateVolume := StepCreateVolume{RootVolumeType: "io1"}
|
|
||||||
testRootDevice := buildTestRootDevice()
|
|
||||||
|
|
||||||
_, err := stepCreateVolume.buildCreateVolumeInput("test-az", testRootDevice)
|
|
||||||
assert.Error(t, err)
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestCreateVolume_Encrypted(t *testing.T) {
|
|
||||||
stepCreateVolume := StepCreateVolume{RootVolumeEncryptBoot: confighelper.TrileanFromBool(true)}
|
|
||||||
testRootDevice := buildTestRootDevice()
|
|
||||||
ret, err := stepCreateVolume.buildCreateVolumeInput("test-az", testRootDevice)
|
|
||||||
assert.NoError(t, err)
|
|
||||||
// Ensure that the new value is equal to the the value passed in
|
|
||||||
assert.Equal(t, confighelper.TrileanFromBool(*ret.Encrypted), stepCreateVolume.RootVolumeEncryptBoot)
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestCreateVolume_Custom_KMS_Key_Encrypted(t *testing.T) {
|
|
||||||
stepCreateVolume := StepCreateVolume{
|
|
||||||
RootVolumeEncryptBoot: confighelper.TrileanFromBool(true),
|
|
||||||
RootVolumeKmsKeyId: "alias/1234",
|
|
||||||
}
|
|
||||||
testRootDevice := buildTestRootDevice()
|
|
||||||
ret, err := stepCreateVolume.buildCreateVolumeInput("test-az", testRootDevice)
|
|
||||||
assert.NoError(t, err)
|
|
||||||
// Ensure that the new value is equal to the value passed in
|
|
||||||
assert.Equal(t, *ret.KmsKeyId, stepCreateVolume.RootVolumeKmsKeyId)
|
|
||||||
}
|
|
||||||
@@ -1,15 +0,0 @@
|
|||||||
package chroot
|
|
||||||
|
|
||||||
import (
|
|
||||||
"testing"
|
|
||||||
|
|
||||||
"github.com/hashicorp/packer-plugin-sdk/chroot"
|
|
||||||
)
|
|
||||||
|
|
||||||
func TestFlockCleanupFunc_ImplementsCleanupFunc(t *testing.T) {
|
|
||||||
var raw interface{}
|
|
||||||
raw = new(StepFlock)
|
|
||||||
if _, ok := raw.(chroot.Cleanup); !ok {
|
|
||||||
t.Fatalf("cleanup func should be a CleanupFunc")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,15 +0,0 @@
|
|||||||
package chroot
|
|
||||||
|
|
||||||
import (
|
|
||||||
"testing"
|
|
||||||
|
|
||||||
"github.com/hashicorp/packer-plugin-sdk/chroot"
|
|
||||||
)
|
|
||||||
|
|
||||||
func TestMountDeviceCleanupFunc_ImplementsCleanupFunc(t *testing.T) {
|
|
||||||
var raw interface{}
|
|
||||||
raw = new(StepMountDevice)
|
|
||||||
if _, ok := raw.(chroot.Cleanup); !ok {
|
|
||||||
t.Fatalf("cleanup func should be a CleanupFunc")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,216 +0,0 @@
|
|||||||
package chroot
|
|
||||||
|
|
||||||
import (
|
|
||||||
"testing"
|
|
||||||
|
|
||||||
"github.com/hashicorp/packer-plugin-sdk/common"
|
|
||||||
amazon "github.com/hashicorp/packer/builder/amazon/common"
|
|
||||||
|
|
||||||
"github.com/aws/aws-sdk-go/aws"
|
|
||||||
"github.com/aws/aws-sdk-go/service/ec2"
|
|
||||||
)
|
|
||||||
|
|
||||||
func testImage() ec2.Image {
|
|
||||||
return ec2.Image{
|
|
||||||
ImageId: aws.String("ami-abcd1234"),
|
|
||||||
Name: aws.String("ami_test_name"),
|
|
||||||
Architecture: aws.String("x86_64"),
|
|
||||||
KernelId: aws.String("aki-abcd1234"),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestStepRegisterAmi_buildRegisterOpts_pv(t *testing.T) {
|
|
||||||
config := Config{}
|
|
||||||
config.AMIName = "test_ami_name"
|
|
||||||
config.AMIDescription = "test_ami_description"
|
|
||||||
config.AMIVirtType = "paravirtual"
|
|
||||||
rootDeviceName := "foo"
|
|
||||||
|
|
||||||
image := testImage()
|
|
||||||
|
|
||||||
blockDevices := []*ec2.BlockDeviceMapping{}
|
|
||||||
|
|
||||||
opts := buildRegisterOptsFromExistingImage(&config, &image, blockDevices, rootDeviceName, config.AMIName)
|
|
||||||
|
|
||||||
expected := config.AMIVirtType
|
|
||||||
if *opts.VirtualizationType != expected {
|
|
||||||
t.Fatalf("Unexpected VirtType value: expected %s got %s\n", expected, *opts.VirtualizationType)
|
|
||||||
}
|
|
||||||
|
|
||||||
expected = config.AMIName
|
|
||||||
if *opts.Name != expected {
|
|
||||||
t.Fatalf("Unexpected Name value: expected %s got %s\n", expected, *opts.Name)
|
|
||||||
}
|
|
||||||
|
|
||||||
expected = *image.KernelId
|
|
||||||
if *opts.KernelId != expected {
|
|
||||||
t.Fatalf("Unexpected KernelId value: expected %s got %s\n", expected, *opts.KernelId)
|
|
||||||
}
|
|
||||||
|
|
||||||
expected = rootDeviceName
|
|
||||||
if *opts.RootDeviceName != expected {
|
|
||||||
t.Fatalf("Unexpected RootDeviceName value: expected %s got %s\n", expected, *opts.RootDeviceName)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestStepRegisterAmi_buildRegisterOpts_hvm(t *testing.T) {
|
|
||||||
config := Config{}
|
|
||||||
config.AMIName = "test_ami_name"
|
|
||||||
config.AMIDescription = "test_ami_description"
|
|
||||||
config.AMIVirtType = "hvm"
|
|
||||||
rootDeviceName := "foo"
|
|
||||||
|
|
||||||
image := testImage()
|
|
||||||
|
|
||||||
blockDevices := []*ec2.BlockDeviceMapping{}
|
|
||||||
|
|
||||||
opts := buildRegisterOptsFromExistingImage(&config, &image, blockDevices, rootDeviceName, config.AMIName)
|
|
||||||
|
|
||||||
expected := config.AMIVirtType
|
|
||||||
if *opts.VirtualizationType != expected {
|
|
||||||
t.Fatalf("Unexpected VirtType value: expected %s got %s\n", expected, *opts.VirtualizationType)
|
|
||||||
}
|
|
||||||
|
|
||||||
expected = config.AMIName
|
|
||||||
if *opts.Name != expected {
|
|
||||||
t.Fatalf("Unexpected Name value: expected %s got %s\n", expected, *opts.Name)
|
|
||||||
}
|
|
||||||
|
|
||||||
if opts.KernelId != nil {
|
|
||||||
t.Fatalf("Unexpected KernelId value: expected nil got %s\n", *opts.KernelId)
|
|
||||||
}
|
|
||||||
|
|
||||||
expected = rootDeviceName
|
|
||||||
if *opts.RootDeviceName != expected {
|
|
||||||
t.Fatalf("Unexpected RootDeviceName value: expected %s got %s\n", expected, *opts.RootDeviceName)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestStepRegisterAmi_buildRegisterOptsFromScratch(t *testing.T) {
|
|
||||||
rootDeviceName := "/dev/sda"
|
|
||||||
snapshotID := "foo"
|
|
||||||
config := Config{
|
|
||||||
FromScratch: true,
|
|
||||||
PackerConfig: common.PackerConfig{},
|
|
||||||
AMIMappings: []amazon.BlockDevice{
|
|
||||||
{
|
|
||||||
DeviceName: rootDeviceName,
|
|
||||||
},
|
|
||||||
},
|
|
||||||
RootDeviceName: rootDeviceName,
|
|
||||||
}
|
|
||||||
registerOpts := buildBaseRegisterOpts(&config, nil, 10, snapshotID, config.AMIName)
|
|
||||||
|
|
||||||
if len(registerOpts.BlockDeviceMappings) != 1 {
|
|
||||||
t.Fatal("Expected block device mapping of length 1")
|
|
||||||
}
|
|
||||||
|
|
||||||
if *registerOpts.BlockDeviceMappings[0].Ebs.SnapshotId != snapshotID {
|
|
||||||
t.Fatalf("Snapshot ID of root disk not set to snapshot id %s", rootDeviceName)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestStepRegisterAmi_buildRegisterOptFromExistingImage(t *testing.T) {
|
|
||||||
rootDeviceName := "/dev/sda"
|
|
||||||
snapshotID := "foo"
|
|
||||||
|
|
||||||
config := Config{
|
|
||||||
FromScratch: false,
|
|
||||||
PackerConfig: common.PackerConfig{},
|
|
||||||
}
|
|
||||||
sourceImage := ec2.Image{
|
|
||||||
RootDeviceName: &rootDeviceName,
|
|
||||||
BlockDeviceMappings: []*ec2.BlockDeviceMapping{
|
|
||||||
{
|
|
||||||
DeviceName: &rootDeviceName,
|
|
||||||
Ebs: &ec2.EbsBlockDevice{
|
|
||||||
VolumeSize: aws.Int64(10),
|
|
||||||
},
|
|
||||||
},
|
|
||||||
// Throw in an ephemeral device, it seems like all devices in the return struct in a source AMI have
|
|
||||||
// a size, even if it's for ephemeral
|
|
||||||
{
|
|
||||||
DeviceName: aws.String("/dev/sdb"),
|
|
||||||
VirtualName: aws.String("ephemeral0"),
|
|
||||||
Ebs: &ec2.EbsBlockDevice{
|
|
||||||
VolumeSize: aws.Int64(0),
|
|
||||||
},
|
|
||||||
},
|
|
||||||
},
|
|
||||||
}
|
|
||||||
registerOpts := buildBaseRegisterOpts(&config, &sourceImage, 15, snapshotID, config.AMIName)
|
|
||||||
|
|
||||||
if len(registerOpts.BlockDeviceMappings) != 2 {
|
|
||||||
t.Fatal("Expected block device mapping of length 2")
|
|
||||||
}
|
|
||||||
|
|
||||||
for _, dev := range registerOpts.BlockDeviceMappings {
|
|
||||||
if dev.Ebs.SnapshotId != nil && *dev.Ebs.SnapshotId == snapshotID {
|
|
||||||
// Even though root volume size is in config, it isn't used, instead we use the root volume size
|
|
||||||
// that's derived when we build the step
|
|
||||||
if *dev.Ebs.VolumeSize != 15 {
|
|
||||||
t.Fatalf("Root volume size not 15 GB instead %d", *dev.Ebs.VolumeSize)
|
|
||||||
}
|
|
||||||
return
|
|
||||||
}
|
|
||||||
}
|
|
||||||
t.Fatalf("Could not find device with snapshot ID %s", snapshotID)
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestStepRegisterAmi_buildRegisterOptFromExistingImageWithBlockDeviceMappings(t *testing.T) {
|
|
||||||
const (
|
|
||||||
rootDeviceName = "/dev/xvda"
|
|
||||||
oldRootDevice = "/dev/sda1"
|
|
||||||
)
|
|
||||||
snapshotId := "foo"
|
|
||||||
|
|
||||||
config := Config{
|
|
||||||
FromScratch: false,
|
|
||||||
PackerConfig: common.PackerConfig{},
|
|
||||||
AMIMappings: []amazon.BlockDevice{
|
|
||||||
{
|
|
||||||
DeviceName: rootDeviceName,
|
|
||||||
},
|
|
||||||
},
|
|
||||||
RootDeviceName: rootDeviceName,
|
|
||||||
}
|
|
||||||
|
|
||||||
// Intentionally try to use a different root devicename
|
|
||||||
sourceImage := ec2.Image{
|
|
||||||
RootDeviceName: aws.String(oldRootDevice),
|
|
||||||
BlockDeviceMappings: []*ec2.BlockDeviceMapping{
|
|
||||||
{
|
|
||||||
DeviceName: aws.String(oldRootDevice),
|
|
||||||
Ebs: &ec2.EbsBlockDevice{
|
|
||||||
VolumeSize: aws.Int64(10),
|
|
||||||
},
|
|
||||||
},
|
|
||||||
// Throw in an ephemeral device, it seems like all devices in the return struct in a source AMI have
|
|
||||||
// a size, even if it's for ephemeral
|
|
||||||
{
|
|
||||||
DeviceName: aws.String("/dev/sdb"),
|
|
||||||
VirtualName: aws.String("ephemeral0"),
|
|
||||||
Ebs: &ec2.EbsBlockDevice{
|
|
||||||
VolumeSize: aws.Int64(0),
|
|
||||||
},
|
|
||||||
},
|
|
||||||
},
|
|
||||||
}
|
|
||||||
registerOpts := buildBaseRegisterOpts(&config, &sourceImage, 15, snapshotId, config.AMIName)
|
|
||||||
|
|
||||||
if len(registerOpts.BlockDeviceMappings) != 1 {
|
|
||||||
t.Fatal("Expected block device mapping of length 1")
|
|
||||||
}
|
|
||||||
|
|
||||||
if *registerOpts.BlockDeviceMappings[0].Ebs.SnapshotId != snapshotId {
|
|
||||||
t.Fatalf("Snapshot ID of root disk set to '%s' expected '%s'", *registerOpts.BlockDeviceMappings[0].Ebs.SnapshotId, rootDeviceName)
|
|
||||||
}
|
|
||||||
|
|
||||||
if *registerOpts.RootDeviceName != rootDeviceName {
|
|
||||||
t.Fatalf("Root device set to '%s' expected %s", *registerOpts.RootDeviceName, rootDeviceName)
|
|
||||||
}
|
|
||||||
|
|
||||||
if *registerOpts.BlockDeviceMappings[0].Ebs.VolumeSize != 15 {
|
|
||||||
t.Fatalf("Size of root disk not set to 15 GB, instead %d", *registerOpts.BlockDeviceMappings[0].Ebs.VolumeSize)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,47 +0,0 @@
|
|||||||
package common
|
|
||||||
|
|
||||||
import (
|
|
||||||
"testing"
|
|
||||||
|
|
||||||
"github.com/aws/aws-sdk-go/aws"
|
|
||||||
"github.com/aws/aws-sdk-go/aws/session"
|
|
||||||
)
|
|
||||||
|
|
||||||
func TestAccessConfigPrepare_Region(t *testing.T) {
|
|
||||||
c := FakeAccessConfig()
|
|
||||||
|
|
||||||
c.RawRegion = "us-east-12"
|
|
||||||
err := c.ValidateRegion(c.RawRegion)
|
|
||||||
if err == nil {
|
|
||||||
t.Fatalf("should have region validation err: %s", c.RawRegion)
|
|
||||||
}
|
|
||||||
|
|
||||||
c.RawRegion = "us-east-1"
|
|
||||||
err = c.ValidateRegion(c.RawRegion)
|
|
||||||
if err != nil {
|
|
||||||
t.Fatalf("shouldn't have region validation err: %s", c.RawRegion)
|
|
||||||
}
|
|
||||||
|
|
||||||
c.RawRegion = "custom"
|
|
||||||
err = c.ValidateRegion(c.RawRegion)
|
|
||||||
if err == nil {
|
|
||||||
t.Fatalf("should have region validation err: %s", c.RawRegion)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestAccessConfigPrepare_RegionRestricted(t *testing.T) {
|
|
||||||
c := FakeAccessConfig()
|
|
||||||
|
|
||||||
// Create a Session with a custom region
|
|
||||||
c.session = session.Must(session.NewSession(&aws.Config{
|
|
||||||
Region: aws.String("us-gov-west-1"),
|
|
||||||
}))
|
|
||||||
|
|
||||||
if err := c.Prepare(); err != nil {
|
|
||||||
t.Fatalf("shouldn't have err: %s", err)
|
|
||||||
}
|
|
||||||
|
|
||||||
if !c.IsGovCloud() {
|
|
||||||
t.Fatal("We should be in gov region.")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,244 +0,0 @@
|
|||||||
package common
|
|
||||||
|
|
||||||
import (
|
|
||||||
"fmt"
|
|
||||||
"reflect"
|
|
||||||
"testing"
|
|
||||||
|
|
||||||
"github.com/aws/aws-sdk-go/aws"
|
|
||||||
"github.com/aws/aws-sdk-go/service/ec2"
|
|
||||||
"github.com/hashicorp/packer-plugin-sdk/template/config"
|
|
||||||
)
|
|
||||||
|
|
||||||
func testAMIConfig() *AMIConfig {
|
|
||||||
return &AMIConfig{
|
|
||||||
AMIName: "foo",
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func getFakeAccessConfig(region string) *AccessConfig {
|
|
||||||
c := FakeAccessConfig()
|
|
||||||
c.RawRegion = region
|
|
||||||
return c
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestAMIConfigPrepare_name(t *testing.T) {
|
|
||||||
c := testAMIConfig()
|
|
||||||
accessConf := FakeAccessConfig()
|
|
||||||
if err := c.Prepare(accessConf, nil); err != nil {
|
|
||||||
t.Fatalf("shouldn't have err: %s", err)
|
|
||||||
}
|
|
||||||
|
|
||||||
c.AMIName = ""
|
|
||||||
if err := c.Prepare(accessConf, nil); err == nil {
|
|
||||||
t.Fatal("should have error")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func (m *mockEC2Client) DescribeRegions(*ec2.DescribeRegionsInput) (*ec2.DescribeRegionsOutput, error) {
|
|
||||||
return &ec2.DescribeRegionsOutput{
|
|
||||||
Regions: []*ec2.Region{
|
|
||||||
{RegionName: aws.String("us-east-1")},
|
|
||||||
{RegionName: aws.String("us-east-2")},
|
|
||||||
{RegionName: aws.String("us-west-1")},
|
|
||||||
},
|
|
||||||
}, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestAMIConfigPrepare_regions(t *testing.T) {
|
|
||||||
c := testAMIConfig()
|
|
||||||
c.AMIRegions = nil
|
|
||||||
|
|
||||||
var errs []error
|
|
||||||
var err error
|
|
||||||
accessConf := FakeAccessConfig()
|
|
||||||
mockConn := &mockEC2Client{}
|
|
||||||
if errs = c.prepareRegions(accessConf); len(errs) > 0 {
|
|
||||||
t.Fatalf("shouldn't have err: %#v", errs)
|
|
||||||
}
|
|
||||||
|
|
||||||
c.AMIRegions, err = listEC2Regions(mockConn)
|
|
||||||
if err != nil {
|
|
||||||
t.Fatalf("shouldn't have err: %s", err.Error())
|
|
||||||
}
|
|
||||||
if errs = c.prepareRegions(accessConf); len(errs) > 0 {
|
|
||||||
t.Fatalf("shouldn't have err: %#v", errs)
|
|
||||||
}
|
|
||||||
|
|
||||||
c.AMIRegions = []string{"us-east-1", "us-west-1", "us-east-1"}
|
|
||||||
if errs = c.prepareRegions(accessConf); len(errs) > 0 {
|
|
||||||
t.Fatalf("bad: %s", errs[0])
|
|
||||||
}
|
|
||||||
|
|
||||||
expected := []string{"us-east-1", "us-west-1"}
|
|
||||||
if !reflect.DeepEqual(c.AMIRegions, expected) {
|
|
||||||
t.Fatalf("bad: %#v", c.AMIRegions)
|
|
||||||
}
|
|
||||||
|
|
||||||
c.AMIRegions = []string{"custom"}
|
|
||||||
if errs = c.prepareRegions(accessConf); len(errs) > 0 {
|
|
||||||
t.Fatal("shouldn't have error")
|
|
||||||
}
|
|
||||||
|
|
||||||
c.AMIRegions = []string{"us-east-1", "us-east-2", "us-west-1"}
|
|
||||||
c.AMIRegionKMSKeyIDs = map[string]string{
|
|
||||||
"us-east-1": "123-456-7890",
|
|
||||||
"us-west-1": "789-012-3456",
|
|
||||||
"us-east-2": "456-789-0123",
|
|
||||||
}
|
|
||||||
if errs = c.prepareRegions(accessConf); len(errs) > 0 {
|
|
||||||
t.Fatal(fmt.Sprintf("shouldn't have error: %s", errs[0]))
|
|
||||||
}
|
|
||||||
|
|
||||||
c.AMIRegions = []string{"us-east-1", "us-east-2", "us-west-1"}
|
|
||||||
c.AMIRegionKMSKeyIDs = map[string]string{
|
|
||||||
"us-east-1": "123-456-7890",
|
|
||||||
"us-west-1": "789-012-3456",
|
|
||||||
"us-east-2": "",
|
|
||||||
}
|
|
||||||
if errs = c.prepareRegions(accessConf); len(errs) > 0 {
|
|
||||||
t.Fatal("should have passed; we are able to use default KMS key if not sharing")
|
|
||||||
}
|
|
||||||
|
|
||||||
c.SnapshotUsers = []string{"user-foo", "user-bar"}
|
|
||||||
c.AMIRegions = []string{"us-east-1", "us-east-2", "us-west-1"}
|
|
||||||
c.AMIRegionKMSKeyIDs = map[string]string{
|
|
||||||
"us-east-1": "123-456-7890",
|
|
||||||
"us-west-1": "789-012-3456",
|
|
||||||
"us-east-2": "",
|
|
||||||
}
|
|
||||||
if errs = c.prepareRegions(accessConf); len(errs) > 0 {
|
|
||||||
t.Fatal("should have an error b/c can't use default KMS key if sharing")
|
|
||||||
}
|
|
||||||
|
|
||||||
c.AMIRegions = []string{"us-east-1", "us-west-1"}
|
|
||||||
c.AMIRegionKMSKeyIDs = map[string]string{
|
|
||||||
"us-east-1": "123-456-7890",
|
|
||||||
"us-west-1": "789-012-3456",
|
|
||||||
"us-east-2": "456-789-0123",
|
|
||||||
}
|
|
||||||
if errs = c.prepareRegions(accessConf); len(errs) > 0 {
|
|
||||||
t.Fatal("should have error b/c theres a region in the key map that isn't in ami_regions")
|
|
||||||
}
|
|
||||||
|
|
||||||
c.AMIRegions = []string{"us-east-1", "us-west-1", "us-east-2"}
|
|
||||||
c.AMIRegionKMSKeyIDs = map[string]string{
|
|
||||||
"us-east-1": "123-456-7890",
|
|
||||||
"us-west-1": "789-012-3456",
|
|
||||||
}
|
|
||||||
|
|
||||||
if err := c.Prepare(accessConf, nil); err == nil {
|
|
||||||
t.Fatal("should have error b/c theres a region in in ami_regions that isn't in the key map")
|
|
||||||
}
|
|
||||||
|
|
||||||
c.SnapshotUsers = []string{"foo", "bar"}
|
|
||||||
c.AMIKmsKeyId = "123-abc-456"
|
|
||||||
c.AMIEncryptBootVolume = config.TriTrue
|
|
||||||
c.AMIRegions = []string{"us-east-1", "us-west-1"}
|
|
||||||
c.AMIRegionKMSKeyIDs = map[string]string{
|
|
||||||
"us-east-1": "123-456-7890",
|
|
||||||
"us-west-1": "",
|
|
||||||
}
|
|
||||||
if errs = c.prepareRegions(accessConf); len(errs) > 0 {
|
|
||||||
t.Fatal("should have error b/c theres a region in in ami_regions that isn't in the key map")
|
|
||||||
}
|
|
||||||
|
|
||||||
// allow rawregion to exist in ami_regions list.
|
|
||||||
accessConf = getFakeAccessConfig("us-east-1")
|
|
||||||
c.AMIRegions = []string{"us-east-1", "us-west-1", "us-east-2"}
|
|
||||||
c.AMIRegionKMSKeyIDs = nil
|
|
||||||
if errs = c.prepareRegions(accessConf); len(errs) > 0 {
|
|
||||||
t.Fatal("should allow user to have the raw region in ami_regions")
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestAMIConfigPrepare_Share_EncryptedBoot(t *testing.T) {
|
|
||||||
c := testAMIConfig()
|
|
||||||
c.AMIUsers = []string{"testAccountID"}
|
|
||||||
c.AMIEncryptBootVolume = config.TriTrue
|
|
||||||
|
|
||||||
accessConf := FakeAccessConfig()
|
|
||||||
|
|
||||||
c.AMIKmsKeyId = ""
|
|
||||||
if err := c.Prepare(accessConf, nil); err == nil {
|
|
||||||
t.Fatal("shouldn't be able to share ami with encrypted boot volume")
|
|
||||||
}
|
|
||||||
c.AMIKmsKeyId = "89c3fb9a-de87-4f2a-aedc-fddc5138193c"
|
|
||||||
if err := c.Prepare(accessConf, nil); err != nil {
|
|
||||||
t.Fatal("should be able to share ami with encrypted boot volume")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestAMIConfigPrepare_ValidateKmsKey(t *testing.T) {
|
|
||||||
c := testAMIConfig()
|
|
||||||
c.AMIEncryptBootVolume = config.TriTrue
|
|
||||||
|
|
||||||
accessConf := FakeAccessConfig()
|
|
||||||
|
|
||||||
validCases := []string{
|
|
||||||
"abcd1234-e567-890f-a12b-a123b4cd56ef",
|
|
||||||
"alias/foo/bar",
|
|
||||||
"arn:aws:kms:us-east-1:012345678910:key/abcd1234-a123-456a-a12b-a123b4cd56ef",
|
|
||||||
"arn:aws:kms:us-east-1:012345678910:alias/foo/bar",
|
|
||||||
"arn:aws-us-gov:kms:us-gov-east-1:123456789012:key/12345678-1234-abcd-0000-123456789012",
|
|
||||||
}
|
|
||||||
for _, validCase := range validCases {
|
|
||||||
c.AMIKmsKeyId = validCase
|
|
||||||
if err := c.Prepare(accessConf, nil); err != nil {
|
|
||||||
t.Fatalf("%s should not have failed KMS key validation", validCase)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
invalidCases := []string{
|
|
||||||
"ABCD1234-e567-890f-a12b-a123b4cd56ef",
|
|
||||||
"ghij1234-e567-890f-a12b-a123b4cd56ef",
|
|
||||||
"ghij1234+e567_890f-a12b-a123b4cd56ef",
|
|
||||||
"foo/bar",
|
|
||||||
"arn:aws:kms:us-east-1:012345678910:foo/bar",
|
|
||||||
"arn:foo:kms:us-east-1:012345678910:key/abcd1234-a123-456a-a12b-a123b4cd56ef",
|
|
||||||
}
|
|
||||||
for _, invalidCase := range invalidCases {
|
|
||||||
c.AMIKmsKeyId = invalidCase
|
|
||||||
if err := c.Prepare(accessConf, nil); err == nil {
|
|
||||||
t.Fatalf("%s should have failed KMS key validation", invalidCase)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestAMINameValidation(t *testing.T) {
|
|
||||||
c := testAMIConfig()
|
|
||||||
|
|
||||||
accessConf := FakeAccessConfig()
|
|
||||||
|
|
||||||
c.AMIName = "aa"
|
|
||||||
if err := c.Prepare(accessConf, nil); err == nil {
|
|
||||||
t.Fatal("shouldn't be able to have an ami name with less than 3 characters")
|
|
||||||
}
|
|
||||||
|
|
||||||
var longAmiName string
|
|
||||||
for i := 0; i < 129; i++ {
|
|
||||||
longAmiName += "a"
|
|
||||||
}
|
|
||||||
c.AMIName = longAmiName
|
|
||||||
if err := c.Prepare(accessConf, nil); err == nil {
|
|
||||||
t.Fatal("shouldn't be able to have an ami name with great than 128 characters")
|
|
||||||
}
|
|
||||||
|
|
||||||
c.AMIName = "+aaa"
|
|
||||||
if err := c.Prepare(accessConf, nil); err == nil {
|
|
||||||
t.Fatal("shouldn't be able to have an ami name with invalid characters")
|
|
||||||
}
|
|
||||||
|
|
||||||
c.AMIName = "fooBAR1()[] ./-'@_"
|
|
||||||
if err := c.Prepare(accessConf, nil); err != nil {
|
|
||||||
t.Fatal("should be able to use all of the allowed AMI characters")
|
|
||||||
}
|
|
||||||
|
|
||||||
c.AMIName = `xyz-base-2017-04-05-1934`
|
|
||||||
if err := c.Prepare(accessConf, nil); err != nil {
|
|
||||||
t.Fatalf("expected `xyz-base-2017-04-05-1934` to pass validation.")
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
@@ -1,90 +0,0 @@
|
|||||||
package common
|
|
||||||
|
|
||||||
import (
|
|
||||||
"reflect"
|
|
||||||
"testing"
|
|
||||||
|
|
||||||
packersdk "github.com/hashicorp/packer-plugin-sdk/packer"
|
|
||||||
)
|
|
||||||
|
|
||||||
func TestArtifact_Impl(t *testing.T) {
|
|
||||||
var _ packersdk.Artifact = new(Artifact)
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestArtifactId(t *testing.T) {
|
|
||||||
expected := `east:foo,west:bar`
|
|
||||||
|
|
||||||
amis := make(map[string]string)
|
|
||||||
amis["east"] = "foo"
|
|
||||||
amis["west"] = "bar"
|
|
||||||
|
|
||||||
a := &Artifact{
|
|
||||||
Amis: amis,
|
|
||||||
}
|
|
||||||
|
|
||||||
result := a.Id()
|
|
||||||
if result != expected {
|
|
||||||
t.Fatalf("bad: %s", result)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestArtifactState_atlasMetadata(t *testing.T) {
|
|
||||||
a := &Artifact{
|
|
||||||
Amis: map[string]string{
|
|
||||||
"east": "foo",
|
|
||||||
"west": "bar",
|
|
||||||
},
|
|
||||||
}
|
|
||||||
|
|
||||||
actual := a.State("atlas.artifact.metadata")
|
|
||||||
expected := map[string]string{
|
|
||||||
"region.east": "foo",
|
|
||||||
"region.west": "bar",
|
|
||||||
}
|
|
||||||
if !reflect.DeepEqual(actual, expected) {
|
|
||||||
t.Fatalf("bad: %#v", actual)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestArtifactString(t *testing.T) {
|
|
||||||
expected := `AMIs were created:
|
|
||||||
east: foo
|
|
||||||
west: bar
|
|
||||||
`
|
|
||||||
|
|
||||||
amis := make(map[string]string)
|
|
||||||
amis["east"] = "foo"
|
|
||||||
amis["west"] = "bar"
|
|
||||||
|
|
||||||
a := &Artifact{Amis: amis}
|
|
||||||
result := a.String()
|
|
||||||
if result != expected {
|
|
||||||
t.Fatalf("bad: %s", result)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestArtifactState(t *testing.T) {
|
|
||||||
expectedData := "this is the data"
|
|
||||||
artifact := &Artifact{
|
|
||||||
StateData: map[string]interface{}{"state_data": expectedData},
|
|
||||||
}
|
|
||||||
|
|
||||||
// Valid state
|
|
||||||
result := artifact.State("state_data")
|
|
||||||
if result != expectedData {
|
|
||||||
t.Fatalf("Bad: State data was %s instead of %s", result, expectedData)
|
|
||||||
}
|
|
||||||
|
|
||||||
// Invalid state
|
|
||||||
result = artifact.State("invalid_key")
|
|
||||||
if result != nil {
|
|
||||||
t.Fatalf("Bad: State should be nil for invalid state data name")
|
|
||||||
}
|
|
||||||
|
|
||||||
// Nil StateData should not fail and should return nil
|
|
||||||
artifact = &Artifact{}
|
|
||||||
result = artifact.State("key")
|
|
||||||
if result != nil {
|
|
||||||
t.Fatalf("Bad: State should be nil for nil StateData")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,401 +0,0 @@
|
|||||||
package common
|
|
||||||
|
|
||||||
import (
|
|
||||||
"testing"
|
|
||||||
|
|
||||||
"github.com/aws/aws-sdk-go/aws"
|
|
||||||
"github.com/aws/aws-sdk-go/service/ec2"
|
|
||||||
"github.com/google/go-cmp/cmp"
|
|
||||||
"github.com/hashicorp/packer-plugin-sdk/template/config"
|
|
||||||
"github.com/hashicorp/packer-plugin-sdk/template/interpolate"
|
|
||||||
)
|
|
||||||
|
|
||||||
func TestBlockDevice(t *testing.T) {
|
|
||||||
cases := []struct {
|
|
||||||
Config *BlockDevice
|
|
||||||
Result *ec2.BlockDeviceMapping
|
|
||||||
}{
|
|
||||||
{
|
|
||||||
Config: &BlockDevice{
|
|
||||||
DeviceName: "/dev/sdb",
|
|
||||||
SnapshotId: "snap-1234",
|
|
||||||
VolumeType: "standard",
|
|
||||||
VolumeSize: 8,
|
|
||||||
DeleteOnTermination: true,
|
|
||||||
},
|
|
||||||
|
|
||||||
Result: &ec2.BlockDeviceMapping{
|
|
||||||
DeviceName: aws.String("/dev/sdb"),
|
|
||||||
Ebs: &ec2.EbsBlockDevice{
|
|
||||||
SnapshotId: aws.String("snap-1234"),
|
|
||||||
VolumeType: aws.String("standard"),
|
|
||||||
VolumeSize: aws.Int64(8),
|
|
||||||
DeleteOnTermination: aws.Bool(true),
|
|
||||||
},
|
|
||||||
},
|
|
||||||
},
|
|
||||||
{
|
|
||||||
Config: &BlockDevice{
|
|
||||||
DeviceName: "/dev/sdb",
|
|
||||||
VolumeSize: 8,
|
|
||||||
},
|
|
||||||
|
|
||||||
Result: &ec2.BlockDeviceMapping{
|
|
||||||
DeviceName: aws.String("/dev/sdb"),
|
|
||||||
Ebs: &ec2.EbsBlockDevice{
|
|
||||||
VolumeSize: aws.Int64(8),
|
|
||||||
DeleteOnTermination: aws.Bool(false),
|
|
||||||
},
|
|
||||||
},
|
|
||||||
},
|
|
||||||
{
|
|
||||||
Config: &BlockDevice{
|
|
||||||
DeviceName: "/dev/sdb",
|
|
||||||
VolumeType: "io1",
|
|
||||||
VolumeSize: 8,
|
|
||||||
DeleteOnTermination: true,
|
|
||||||
IOPS: aws.Int64(1000),
|
|
||||||
},
|
|
||||||
|
|
||||||
Result: &ec2.BlockDeviceMapping{
|
|
||||||
DeviceName: aws.String("/dev/sdb"),
|
|
||||||
Ebs: &ec2.EbsBlockDevice{
|
|
||||||
VolumeType: aws.String("io1"),
|
|
||||||
VolumeSize: aws.Int64(8),
|
|
||||||
DeleteOnTermination: aws.Bool(true),
|
|
||||||
Iops: aws.Int64(1000),
|
|
||||||
},
|
|
||||||
},
|
|
||||||
},
|
|
||||||
{
|
|
||||||
Config: &BlockDevice{
|
|
||||||
DeviceName: "/dev/sdb",
|
|
||||||
VolumeType: "io2",
|
|
||||||
VolumeSize: 8,
|
|
||||||
DeleteOnTermination: true,
|
|
||||||
IOPS: aws.Int64(1000),
|
|
||||||
},
|
|
||||||
|
|
||||||
Result: &ec2.BlockDeviceMapping{
|
|
||||||
DeviceName: aws.String("/dev/sdb"),
|
|
||||||
Ebs: &ec2.EbsBlockDevice{
|
|
||||||
VolumeType: aws.String("io2"),
|
|
||||||
VolumeSize: aws.Int64(8),
|
|
||||||
DeleteOnTermination: aws.Bool(true),
|
|
||||||
Iops: aws.Int64(1000),
|
|
||||||
},
|
|
||||||
},
|
|
||||||
},
|
|
||||||
{
|
|
||||||
Config: &BlockDevice{
|
|
||||||
DeviceName: "/dev/sdb",
|
|
||||||
VolumeType: "gp2",
|
|
||||||
VolumeSize: 8,
|
|
||||||
DeleteOnTermination: true,
|
|
||||||
Encrypted: config.TriTrue,
|
|
||||||
},
|
|
||||||
|
|
||||||
Result: &ec2.BlockDeviceMapping{
|
|
||||||
DeviceName: aws.String("/dev/sdb"),
|
|
||||||
Ebs: &ec2.EbsBlockDevice{
|
|
||||||
VolumeType: aws.String("gp2"),
|
|
||||||
VolumeSize: aws.Int64(8),
|
|
||||||
DeleteOnTermination: aws.Bool(true),
|
|
||||||
Encrypted: aws.Bool(true),
|
|
||||||
},
|
|
||||||
},
|
|
||||||
},
|
|
||||||
{
|
|
||||||
Config: &BlockDevice{
|
|
||||||
DeviceName: "/dev/sdb",
|
|
||||||
VolumeType: "gp2",
|
|
||||||
VolumeSize: 8,
|
|
||||||
DeleteOnTermination: true,
|
|
||||||
Encrypted: config.TriTrue,
|
|
||||||
KmsKeyId: "2Fa48a521f-3aff-4b34-a159-376ac5d37812",
|
|
||||||
},
|
|
||||||
|
|
||||||
Result: &ec2.BlockDeviceMapping{
|
|
||||||
DeviceName: aws.String("/dev/sdb"),
|
|
||||||
Ebs: &ec2.EbsBlockDevice{
|
|
||||||
VolumeType: aws.String("gp2"),
|
|
||||||
VolumeSize: aws.Int64(8),
|
|
||||||
DeleteOnTermination: aws.Bool(true),
|
|
||||||
Encrypted: aws.Bool(true),
|
|
||||||
KmsKeyId: aws.String("2Fa48a521f-3aff-4b34-a159-376ac5d37812"),
|
|
||||||
},
|
|
||||||
},
|
|
||||||
},
|
|
||||||
{
|
|
||||||
Config: &BlockDevice{
|
|
||||||
DeviceName: "/dev/sdb",
|
|
||||||
VolumeType: "standard",
|
|
||||||
DeleteOnTermination: true,
|
|
||||||
},
|
|
||||||
|
|
||||||
Result: &ec2.BlockDeviceMapping{
|
|
||||||
DeviceName: aws.String("/dev/sdb"),
|
|
||||||
Ebs: &ec2.EbsBlockDevice{
|
|
||||||
VolumeType: aws.String("standard"),
|
|
||||||
DeleteOnTermination: aws.Bool(true),
|
|
||||||
},
|
|
||||||
},
|
|
||||||
},
|
|
||||||
{
|
|
||||||
Config: &BlockDevice{
|
|
||||||
DeviceName: "/dev/sdb",
|
|
||||||
VirtualName: "ephemeral0",
|
|
||||||
},
|
|
||||||
|
|
||||||
Result: &ec2.BlockDeviceMapping{
|
|
||||||
DeviceName: aws.String("/dev/sdb"),
|
|
||||||
VirtualName: aws.String("ephemeral0"),
|
|
||||||
},
|
|
||||||
},
|
|
||||||
{
|
|
||||||
Config: &BlockDevice{
|
|
||||||
DeviceName: "/dev/sdb",
|
|
||||||
NoDevice: true,
|
|
||||||
},
|
|
||||||
|
|
||||||
Result: &ec2.BlockDeviceMapping{
|
|
||||||
DeviceName: aws.String("/dev/sdb"),
|
|
||||||
NoDevice: aws.String(""),
|
|
||||||
},
|
|
||||||
},
|
|
||||||
{
|
|
||||||
Config: &BlockDevice{
|
|
||||||
DeviceName: "/dev/sdb",
|
|
||||||
VolumeType: "gp3",
|
|
||||||
VolumeSize: 8,
|
|
||||||
Throughput: aws.Int64(125),
|
|
||||||
IOPS: aws.Int64(3000),
|
|
||||||
DeleteOnTermination: true,
|
|
||||||
Encrypted: config.TriTrue,
|
|
||||||
},
|
|
||||||
|
|
||||||
Result: &ec2.BlockDeviceMapping{
|
|
||||||
DeviceName: aws.String("/dev/sdb"),
|
|
||||||
Ebs: &ec2.EbsBlockDevice{
|
|
||||||
VolumeType: aws.String("gp3"),
|
|
||||||
VolumeSize: aws.Int64(8),
|
|
||||||
Throughput: aws.Int64(125),
|
|
||||||
Iops: aws.Int64(3000),
|
|
||||||
DeleteOnTermination: aws.Bool(true),
|
|
||||||
Encrypted: aws.Bool(true),
|
|
||||||
},
|
|
||||||
},
|
|
||||||
},
|
|
||||||
}
|
|
||||||
|
|
||||||
for _, tc := range cases {
|
|
||||||
var amiBlockDevices BlockDevices = []BlockDevice{*tc.Config}
|
|
||||||
|
|
||||||
var launchBlockDevices BlockDevices = []BlockDevice{*tc.Config}
|
|
||||||
|
|
||||||
expected := []*ec2.BlockDeviceMapping{tc.Result}
|
|
||||||
|
|
||||||
amiResults := amiBlockDevices.BuildEC2BlockDeviceMappings()
|
|
||||||
if diff := cmp.Diff(expected, amiResults); diff != "" {
|
|
||||||
t.Fatalf("Bad block device: %s", diff)
|
|
||||||
}
|
|
||||||
|
|
||||||
launchResults := launchBlockDevices.BuildEC2BlockDeviceMappings()
|
|
||||||
if diff := cmp.Diff(expected, launchResults); diff != "" {
|
|
||||||
t.Fatalf("Bad block device: %s", diff)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestIOPSValidation(t *testing.T) {
|
|
||||||
|
|
||||||
cases := []struct {
|
|
||||||
device BlockDevice
|
|
||||||
ok bool
|
|
||||||
msg string
|
|
||||||
}{
|
|
||||||
// volume size unknown
|
|
||||||
{
|
|
||||||
device: BlockDevice{
|
|
||||||
DeviceName: "/dev/sdb",
|
|
||||||
VolumeType: "io1",
|
|
||||||
IOPS: aws.Int64(1000),
|
|
||||||
},
|
|
||||||
ok: true,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
device: BlockDevice{
|
|
||||||
DeviceName: "/dev/sdb",
|
|
||||||
VolumeType: "io2",
|
|
||||||
IOPS: aws.Int64(1000),
|
|
||||||
},
|
|
||||||
ok: true,
|
|
||||||
},
|
|
||||||
// ratio requirement satisfied
|
|
||||||
{
|
|
||||||
device: BlockDevice{
|
|
||||||
DeviceName: "/dev/sdb",
|
|
||||||
VolumeType: "io1",
|
|
||||||
VolumeSize: 50,
|
|
||||||
IOPS: aws.Int64(1000),
|
|
||||||
},
|
|
||||||
ok: true,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
device: BlockDevice{
|
|
||||||
DeviceName: "/dev/sdb",
|
|
||||||
VolumeType: "io2",
|
|
||||||
VolumeSize: 100,
|
|
||||||
IOPS: aws.Int64(1000),
|
|
||||||
},
|
|
||||||
ok: true,
|
|
||||||
},
|
|
||||||
// ratio requirement not satisfied
|
|
||||||
{
|
|
||||||
device: BlockDevice{
|
|
||||||
DeviceName: "/dev/sdb",
|
|
||||||
VolumeType: "io1",
|
|
||||||
VolumeSize: 10,
|
|
||||||
IOPS: aws.Int64(2000),
|
|
||||||
},
|
|
||||||
ok: false,
|
|
||||||
msg: "/dev/sdb: the maximum ratio of provisioned IOPS to requested volume size (in GiB) is 50:1 for io1 volumes",
|
|
||||||
},
|
|
||||||
{
|
|
||||||
device: BlockDevice{
|
|
||||||
DeviceName: "/dev/sdb",
|
|
||||||
VolumeType: "io2",
|
|
||||||
VolumeSize: 50,
|
|
||||||
IOPS: aws.Int64(30000),
|
|
||||||
},
|
|
||||||
ok: false,
|
|
||||||
msg: "/dev/sdb: the maximum ratio of provisioned IOPS to requested volume size (in GiB) is 500:1 for io2 volumes",
|
|
||||||
},
|
|
||||||
// exceed max iops
|
|
||||||
{
|
|
||||||
device: BlockDevice{
|
|
||||||
DeviceName: "/dev/sdb",
|
|
||||||
VolumeType: "io2",
|
|
||||||
VolumeSize: 500,
|
|
||||||
IOPS: aws.Int64(99999),
|
|
||||||
},
|
|
||||||
ok: false,
|
|
||||||
msg: "IOPS must be between 100 and 64000 for device /dev/sdb",
|
|
||||||
},
|
|
||||||
// lower than min iops
|
|
||||||
{
|
|
||||||
device: BlockDevice{
|
|
||||||
DeviceName: "/dev/sdb",
|
|
||||||
VolumeType: "io2",
|
|
||||||
VolumeSize: 50,
|
|
||||||
IOPS: aws.Int64(10),
|
|
||||||
},
|
|
||||||
ok: false,
|
|
||||||
msg: "IOPS must be between 100 and 64000 for device /dev/sdb",
|
|
||||||
},
|
|
||||||
// exceed max iops
|
|
||||||
{
|
|
||||||
device: BlockDevice{
|
|
||||||
DeviceName: "/dev/sdb",
|
|
||||||
VolumeType: "gp3",
|
|
||||||
VolumeSize: 50,
|
|
||||||
Throughput: aws.Int64(125),
|
|
||||||
IOPS: aws.Int64(99999),
|
|
||||||
},
|
|
||||||
ok: false,
|
|
||||||
msg: "IOPS must be between 3000 and 16000 for device /dev/sdb",
|
|
||||||
},
|
|
||||||
// lower than min iops
|
|
||||||
{
|
|
||||||
device: BlockDevice{
|
|
||||||
DeviceName: "/dev/sdb",
|
|
||||||
VolumeType: "gp3",
|
|
||||||
VolumeSize: 50,
|
|
||||||
Throughput: aws.Int64(125),
|
|
||||||
IOPS: aws.Int64(10),
|
|
||||||
},
|
|
||||||
ok: false,
|
|
||||||
msg: "IOPS must be between 3000 and 16000 for device /dev/sdb",
|
|
||||||
},
|
|
||||||
}
|
|
||||||
|
|
||||||
ctx := interpolate.Context{}
|
|
||||||
for _, testCase := range cases {
|
|
||||||
err := testCase.device.Prepare(&ctx)
|
|
||||||
if testCase.ok && err != nil {
|
|
||||||
t.Fatalf("should not error, but: %v", err)
|
|
||||||
}
|
|
||||||
if !testCase.ok {
|
|
||||||
if err == nil {
|
|
||||||
t.Fatalf("should error")
|
|
||||||
} else if err.Error() != testCase.msg {
|
|
||||||
t.Fatalf("wrong error: expected %s, found: %v", testCase.msg, err)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestThroughputValidation(t *testing.T) {
|
|
||||||
|
|
||||||
cases := []struct {
|
|
||||||
device BlockDevice
|
|
||||||
ok bool
|
|
||||||
msg string
|
|
||||||
}{
|
|
||||||
{
|
|
||||||
device: BlockDevice{
|
|
||||||
DeviceName: "/dev/sdb",
|
|
||||||
VolumeType: "gp3",
|
|
||||||
Throughput: aws.Int64(125),
|
|
||||||
IOPS: aws.Int64(3000),
|
|
||||||
},
|
|
||||||
ok: true,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
device: BlockDevice{
|
|
||||||
DeviceName: "/dev/sdb",
|
|
||||||
VolumeType: "gp3",
|
|
||||||
Throughput: aws.Int64(1000),
|
|
||||||
IOPS: aws.Int64(3000),
|
|
||||||
},
|
|
||||||
ok: true,
|
|
||||||
},
|
|
||||||
// exceed max Throughput
|
|
||||||
{
|
|
||||||
device: BlockDevice{
|
|
||||||
DeviceName: "/dev/sdb",
|
|
||||||
VolumeType: "gp3",
|
|
||||||
Throughput: aws.Int64(1001),
|
|
||||||
IOPS: aws.Int64(3000),
|
|
||||||
},
|
|
||||||
ok: false,
|
|
||||||
msg: "Throughput must be between 125 and 1000 for device /dev/sdb",
|
|
||||||
},
|
|
||||||
// lower than min Throughput
|
|
||||||
{
|
|
||||||
device: BlockDevice{
|
|
||||||
DeviceName: "/dev/sdb",
|
|
||||||
VolumeType: "gp3",
|
|
||||||
Throughput: aws.Int64(124),
|
|
||||||
IOPS: aws.Int64(3000),
|
|
||||||
},
|
|
||||||
ok: false,
|
|
||||||
msg: "Throughput must be between 125 and 1000 for device /dev/sdb",
|
|
||||||
},
|
|
||||||
}
|
|
||||||
|
|
||||||
ctx := interpolate.Context{}
|
|
||||||
for _, testCase := range cases {
|
|
||||||
err := testCase.device.Prepare(&ctx)
|
|
||||||
if testCase.ok && err != nil {
|
|
||||||
t.Fatalf("should not error, but: %v", err)
|
|
||||||
}
|
|
||||||
if !testCase.ok {
|
|
||||||
if err == nil {
|
|
||||||
t.Fatalf("should error")
|
|
||||||
} else if err.Error() != testCase.msg {
|
|
||||||
t.Fatalf("wrong error: expected %s, found: %v", testCase.msg, err)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,31 +0,0 @@
|
|||||||
package common
|
|
||||||
|
|
||||||
import (
|
|
||||||
"testing"
|
|
||||||
)
|
|
||||||
|
|
||||||
func TestStepSourceAmiInfo_BuildFilter(t *testing.T) {
|
|
||||||
filter_key := "name"
|
|
||||||
filter_value := "foo"
|
|
||||||
filter_key2 := "name2"
|
|
||||||
filter_value2 := "foo2"
|
|
||||||
|
|
||||||
inputFilter := map[string]string{filter_key: filter_value, filter_key2: filter_value2}
|
|
||||||
outputFilter := buildEc2Filters(inputFilter)
|
|
||||||
|
|
||||||
// deconstruct filter back into things we can test
|
|
||||||
foundMap := map[string]bool{filter_key: false, filter_key2: false}
|
|
||||||
for _, filter := range outputFilter {
|
|
||||||
for key, value := range inputFilter {
|
|
||||||
if *filter.Name == key && *filter.Values[0] == value {
|
|
||||||
foundMap[key] = true
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
for k, v := range foundMap {
|
|
||||||
if !v {
|
|
||||||
t.Fatalf("Fail: should have found value for key: %s", k)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,70 +0,0 @@
|
|||||||
package common
|
|
||||||
|
|
||||||
import (
|
|
||||||
"fmt"
|
|
||||||
"strings"
|
|
||||||
"testing"
|
|
||||||
|
|
||||||
"github.com/aws/aws-sdk-go/aws"
|
|
||||||
"github.com/aws/aws-sdk-go/service/sts"
|
|
||||||
|
|
||||||
"github.com/aws/aws-sdk-go/aws/awserr"
|
|
||||||
)
|
|
||||||
|
|
||||||
type mockSTS struct {
|
|
||||||
}
|
|
||||||
|
|
||||||
func (m *mockSTS) DecodeAuthorizationMessage(input *sts.DecodeAuthorizationMessageInput) (*sts.DecodeAuthorizationMessageOutput, error) {
|
|
||||||
return &sts.DecodeAuthorizationMessageOutput{
|
|
||||||
DecodedMessage: aws.String(`{
|
|
||||||
"allowed": false,
|
|
||||||
"explicitDeny": true,
|
|
||||||
"matchedStatements": {}
|
|
||||||
}`),
|
|
||||||
}, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestErrorsParsing_RequestFailure(t *testing.T) {
|
|
||||||
|
|
||||||
ae := awserr.New("UnauthorizedOperation",
|
|
||||||
`You are not authorized to perform this operation. Encoded authorization failure message: D9Q7oicjOMr9l2CC-NPP1FiZXK9Ijia1k-3l0siBFCcrK3oSuMFMkBIO5TNj0HdXE-WfwnAcdycFOohfKroNO6toPJEns8RFVfy_M_IjNGmrEFJ6E62pnmBW0OLrMsXxR9FQE4gB4gJzSM0AD6cV6S3FOfqYzWBRX-sQdOT4HryGkFNRoFBr9Xbp-tRwiadwkbdHdfnV9fbRkXmnwCdULml16NBSofC4ZPepLMKmIB5rKjwk-m179UUh2XA-J5no0si6XcRo5GbHQB5QfCIwSHL4vsro2wLZUd16-8OWKyr3tVlTbQe0ERZskqRqRQ5E28QuiBCVV6XstUyo-T4lBSr75Fgnyr3wCO-dS3b_5Ns3WzA2JD4E2AJOAStXIU8IH5YuKkAg7C-dJMuBMPpmKCBEXhNoHDwCyOo5PsV3xMlc0jSb0qYGpfst_TDDtejcZfn7NssUjxVq9qkdH-OXz2gPoQB-hX8ycmZCL5UZwKc3TCLUr7TGnudHjmnMrE9cUo-yTCWfyHPLprhiYhTCKW18EikJ0O1EKI3FJ_b4F19_jFBPARjSwQc7Ut6MNCVzrPdZGYSF6acj5gPaxdy9uSkVQwWXK7Pd5MFP7EBDE1_DgYbzodgwDO2PXeVFUbSLBHKWo_ebZS9ZX2nYPcGss_sYaly0ZVSIJXp7G58B5BoFVhvVH6jYnF9XiAOjMltuP_ycu1pQP1lki500RY3baLvfeYeAsB38XZHKEgWZzq7Fei-uh89q0cjJTmlVyrfRU3q6`,
|
|
||||||
fmt.Errorf("You can't do it!!"))
|
|
||||||
rf := awserr.NewRequestFailure(ae, 400, "abc-def-123-456")
|
|
||||||
|
|
||||||
result := decodeAWSError(&mockSTS{}, rf)
|
|
||||||
if result == nil {
|
|
||||||
t.Error("Expected resulting error")
|
|
||||||
}
|
|
||||||
if !strings.Contains(result.Error(), "Authorization failure message:") {
|
|
||||||
t.Error("Expected authorization failure message")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestErrorsParsing_NonAuthorizationFailure(t *testing.T) {
|
|
||||||
|
|
||||||
ae := awserr.New("BadRequest",
|
|
||||||
`You did something wrong. Try again`,
|
|
||||||
fmt.Errorf("Request was no good."))
|
|
||||||
rf := awserr.NewRequestFailure(ae, 400, "abc-def-123-456")
|
|
||||||
|
|
||||||
result := decodeAWSError(&mockSTS{}, rf)
|
|
||||||
if result == nil {
|
|
||||||
t.Error("Expected resulting error")
|
|
||||||
}
|
|
||||||
if result != rf {
|
|
||||||
t.Error("Expected original error to be returned unchanged")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestErrorsParsing_NonAWSError(t *testing.T) {
|
|
||||||
|
|
||||||
err := fmt.Errorf("Random error occurred")
|
|
||||||
|
|
||||||
result := decodeAWSError(&mockSTS{}, err)
|
|
||||||
if result == nil {
|
|
||||||
t.Error("Expected resulting error")
|
|
||||||
}
|
|
||||||
if result != err {
|
|
||||||
t.Error("Expected original error to be returned unchanged")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,91 +0,0 @@
|
|||||||
package common
|
|
||||||
|
|
||||||
import (
|
|
||||||
"reflect"
|
|
||||||
"testing"
|
|
||||||
|
|
||||||
"github.com/aws/aws-sdk-go/aws"
|
|
||||||
"github.com/aws/aws-sdk-go/service/ec2"
|
|
||||||
"github.com/hashicorp/packer-plugin-sdk/multistep"
|
|
||||||
"github.com/hashicorp/packer-plugin-sdk/packerbuilderdata"
|
|
||||||
)
|
|
||||||
|
|
||||||
func testImage() *ec2.Image {
|
|
||||||
return &ec2.Image{
|
|
||||||
ImageId: aws.String("ami-abcd1234"),
|
|
||||||
CreationDate: aws.String("ami_test_creation_date"),
|
|
||||||
Name: aws.String("ami_test_name"),
|
|
||||||
OwnerId: aws.String("ami_test_owner_id"),
|
|
||||||
ImageOwnerAlias: aws.String("ami_test_owner_alias"),
|
|
||||||
RootDeviceType: aws.String("ebs"),
|
|
||||||
Tags: []*ec2.Tag{
|
|
||||||
{
|
|
||||||
Key: aws.String("key-1"),
|
|
||||||
Value: aws.String("value-1"),
|
|
||||||
},
|
|
||||||
{
|
|
||||||
Key: aws.String("key-2"),
|
|
||||||
Value: aws.String("value-2"),
|
|
||||||
},
|
|
||||||
},
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func testState() multistep.StateBag {
|
|
||||||
state := new(multistep.BasicStateBag)
|
|
||||||
return state
|
|
||||||
}
|
|
||||||
|
|
||||||
func testGeneratedData(state multistep.StateBag) packerbuilderdata.GeneratedData {
|
|
||||||
generatedData := packerbuilderdata.GeneratedData{State: state}
|
|
||||||
return generatedData
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestInterpolateBuildInfo_extractBuildInfo_noSourceImage(t *testing.T) {
|
|
||||||
state := testState()
|
|
||||||
generatedData := testGeneratedData(state)
|
|
||||||
buildInfo := extractBuildInfo("foo", state, &generatedData)
|
|
||||||
|
|
||||||
expected := BuildInfoTemplate{
|
|
||||||
BuildRegion: "foo",
|
|
||||||
}
|
|
||||||
if !reflect.DeepEqual(*buildInfo, expected) {
|
|
||||||
t.Fatalf("Unexpected BuildInfoTemplate: expected %#v got %#v\n", expected, *buildInfo)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestInterpolateBuildInfo_extractBuildInfo_withSourceImage(t *testing.T) {
|
|
||||||
state := testState()
|
|
||||||
state.Put("source_image", testImage())
|
|
||||||
generatedData := testGeneratedData(state)
|
|
||||||
buildInfo := extractBuildInfo("foo", state, &generatedData)
|
|
||||||
|
|
||||||
expected := BuildInfoTemplate{
|
|
||||||
BuildRegion: "foo",
|
|
||||||
SourceAMI: "ami-abcd1234",
|
|
||||||
SourceAMICreationDate: "ami_test_creation_date",
|
|
||||||
SourceAMIName: "ami_test_name",
|
|
||||||
SourceAMIOwner: "ami_test_owner_id",
|
|
||||||
SourceAMIOwnerName: "ami_test_owner_alias",
|
|
||||||
SourceAMITags: map[string]string{
|
|
||||||
"key-1": "value-1",
|
|
||||||
"key-2": "value-2",
|
|
||||||
},
|
|
||||||
}
|
|
||||||
if !reflect.DeepEqual(*buildInfo, expected) {
|
|
||||||
t.Fatalf("Unexpected BuildInfoTemplate: expected %#v got %#v\n", expected, *buildInfo)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestInterpolateBuildInfo_extractBuildInfo_GeneratedDataWithSourceImageName(t *testing.T) {
|
|
||||||
state := testState()
|
|
||||||
state.Put("source_image", testImage())
|
|
||||||
generatedData := testGeneratedData(state)
|
|
||||||
extractBuildInfo("foo", state, &generatedData)
|
|
||||||
|
|
||||||
generatedDataState := state.Get("generated_data").(map[string]interface{})
|
|
||||||
|
|
||||||
if generatedDataState["SourceAMIName"] != "ami_test_name" {
|
|
||||||
t.Fatalf("Unexpected state SourceAMIName: expected %#v got %#v\n", "ami_test_name", generatedDataState["SourceAMIName"])
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,251 +0,0 @@
|
|||||||
package common
|
|
||||||
|
|
||||||
import (
|
|
||||||
"io/ioutil"
|
|
||||||
"os"
|
|
||||||
"regexp"
|
|
||||||
"testing"
|
|
||||||
|
|
||||||
"github.com/hashicorp/packer-plugin-sdk/communicator"
|
|
||||||
)
|
|
||||||
|
|
||||||
func init() {
|
|
||||||
// Clear out the AWS access key env vars so they don't
|
|
||||||
// affect our tests.
|
|
||||||
os.Setenv("AWS_ACCESS_KEY_ID", "")
|
|
||||||
os.Setenv("AWS_ACCESS_KEY", "")
|
|
||||||
os.Setenv("AWS_SECRET_ACCESS_KEY", "")
|
|
||||||
os.Setenv("AWS_SECRET_KEY", "")
|
|
||||||
}
|
|
||||||
|
|
||||||
func testConfig() *RunConfig {
|
|
||||||
return &RunConfig{
|
|
||||||
SourceAmi: "abcd",
|
|
||||||
InstanceType: "m1.small",
|
|
||||||
|
|
||||||
Comm: communicator.Config{
|
|
||||||
SSH: communicator.SSH{
|
|
||||||
SSHUsername: "foo",
|
|
||||||
},
|
|
||||||
},
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func testConfigFilter() *RunConfig {
|
|
||||||
config := testConfig()
|
|
||||||
config.SourceAmi = ""
|
|
||||||
config.SourceAmiFilter = AmiFilterOptions{}
|
|
||||||
return config
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestRunConfigPrepare(t *testing.T) {
|
|
||||||
c := testConfig()
|
|
||||||
err := c.Prepare(nil)
|
|
||||||
if len(err) > 0 {
|
|
||||||
t.Fatalf("err: %s", err)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestRunConfigPrepare_InstanceType(t *testing.T) {
|
|
||||||
c := testConfig()
|
|
||||||
c.InstanceType = ""
|
|
||||||
if err := c.Prepare(nil); len(err) != 1 {
|
|
||||||
t.Fatalf("Should error if an instance_type is not specified")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestRunConfigPrepare_SourceAmi(t *testing.T) {
|
|
||||||
c := testConfig()
|
|
||||||
c.SourceAmi = ""
|
|
||||||
if err := c.Prepare(nil); len(err) != 2 {
|
|
||||||
t.Fatalf("Should error if a source_ami (or source_ami_filter) is not specified")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestRunConfigPrepare_SourceAmiFilterBlank(t *testing.T) {
|
|
||||||
c := testConfigFilter()
|
|
||||||
if err := c.Prepare(nil); len(err) != 2 {
|
|
||||||
t.Fatalf("Should error if source_ami_filter is empty or not specified (and source_ami is not specified)")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestRunConfigPrepare_SourceAmiFilterOwnersBlank(t *testing.T) {
|
|
||||||
c := testConfigFilter()
|
|
||||||
filter_key := "name"
|
|
||||||
filter_value := "foo"
|
|
||||||
c.SourceAmiFilter.Filters = map[string]string{filter_key: filter_value}
|
|
||||||
if err := c.Prepare(nil); len(err) != 1 {
|
|
||||||
t.Fatalf("Should error if Owners is not specified)")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestRunConfigPrepare_SourceAmiFilterGood(t *testing.T) {
|
|
||||||
c := testConfigFilter()
|
|
||||||
owner := "123"
|
|
||||||
filter_key := "name"
|
|
||||||
filter_value := "foo"
|
|
||||||
goodFilter := AmiFilterOptions{
|
|
||||||
Owners: []string{owner},
|
|
||||||
Filters: map[string]string{filter_key: filter_value},
|
|
||||||
}
|
|
||||||
c.SourceAmiFilter = goodFilter
|
|
||||||
if err := c.Prepare(nil); len(err) != 0 {
|
|
||||||
t.Fatalf("err: %s", err)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestRunConfigPrepare_EnableT2UnlimitedGood(t *testing.T) {
|
|
||||||
c := testConfig()
|
|
||||||
// Must have a T2 instance type if T2 Unlimited is enabled
|
|
||||||
c.InstanceType = "t2.micro"
|
|
||||||
c.EnableT2Unlimited = true
|
|
||||||
err := c.Prepare(nil)
|
|
||||||
if len(err) > 0 {
|
|
||||||
t.Fatalf("err: %s", err)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestRunConfigPrepare_EnableT2UnlimitedBadInstanceType(t *testing.T) {
|
|
||||||
c := testConfig()
|
|
||||||
// T2 Unlimited cannot be used with instance types other than T2
|
|
||||||
c.InstanceType = "m5.large"
|
|
||||||
c.EnableT2Unlimited = true
|
|
||||||
err := c.Prepare(nil)
|
|
||||||
if len(err) != 1 {
|
|
||||||
t.Fatalf("Should error if T2 Unlimited is enabled with non-T2 instance_type")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestRunConfigPrepare_EnableT2UnlimitedBadWithSpotInstanceRequest(t *testing.T) {
|
|
||||||
c := testConfig()
|
|
||||||
// T2 Unlimited cannot be used with Spot Instances
|
|
||||||
c.InstanceType = "t2.micro"
|
|
||||||
c.EnableT2Unlimited = true
|
|
||||||
c.SpotPrice = "auto"
|
|
||||||
err := c.Prepare(nil)
|
|
||||||
if len(err) != 1 {
|
|
||||||
t.Fatalf("Should error if T2 Unlimited has been used in conjuntion with a Spot Price request")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestRunConfigPrepare_SpotAuto(t *testing.T) {
|
|
||||||
c := testConfig()
|
|
||||||
c.SpotPrice = "auto"
|
|
||||||
if err := c.Prepare(nil); len(err) != 0 {
|
|
||||||
t.Fatalf("err: %s", err)
|
|
||||||
}
|
|
||||||
|
|
||||||
// Shouldn't error (YET) even though SpotPriceAutoProduct is deprecated
|
|
||||||
c.SpotPriceAutoProduct = "Linux/Unix"
|
|
||||||
if err := c.Prepare(nil); len(err) != 0 {
|
|
||||||
t.Fatalf("err: %s", err)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestRunConfigPrepare_SSHPort(t *testing.T) {
|
|
||||||
c := testConfig()
|
|
||||||
c.Comm.SSHPort = 0
|
|
||||||
if err := c.Prepare(nil); len(err) != 0 {
|
|
||||||
t.Fatalf("err: %s", err)
|
|
||||||
}
|
|
||||||
|
|
||||||
if c.Comm.SSHPort != 22 {
|
|
||||||
t.Fatalf("invalid value: %d", c.Comm.SSHPort)
|
|
||||||
}
|
|
||||||
|
|
||||||
c.Comm.SSHPort = 44
|
|
||||||
if err := c.Prepare(nil); len(err) != 0 {
|
|
||||||
t.Fatalf("err: %s", err)
|
|
||||||
}
|
|
||||||
|
|
||||||
if c.Comm.SSHPort != 44 {
|
|
||||||
t.Fatalf("invalid value: %d", c.Comm.SSHPort)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestRunConfigPrepare_UserData(t *testing.T) {
|
|
||||||
c := testConfig()
|
|
||||||
tf, err := ioutil.TempFile("", "packer")
|
|
||||||
if err != nil {
|
|
||||||
t.Fatalf("err: %s", err)
|
|
||||||
}
|
|
||||||
defer os.Remove(tf.Name())
|
|
||||||
defer tf.Close()
|
|
||||||
|
|
||||||
c.UserData = "foo"
|
|
||||||
c.UserDataFile = tf.Name()
|
|
||||||
if err := c.Prepare(nil); len(err) != 1 {
|
|
||||||
t.Fatalf("Should error if user_data string and user_data_file have both been specified")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestRunConfigPrepare_UserDataFile(t *testing.T) {
|
|
||||||
c := testConfig()
|
|
||||||
if err := c.Prepare(nil); len(err) != 0 {
|
|
||||||
t.Fatalf("err: %s", err)
|
|
||||||
}
|
|
||||||
|
|
||||||
c.UserDataFile = "idontexistidontthink"
|
|
||||||
if err := c.Prepare(nil); len(err) != 1 {
|
|
||||||
t.Fatalf("Should error if the file specified by user_data_file does not exist")
|
|
||||||
}
|
|
||||||
|
|
||||||
tf, err := ioutil.TempFile("", "packer")
|
|
||||||
if err != nil {
|
|
||||||
t.Fatalf("err: %s", err)
|
|
||||||
}
|
|
||||||
defer os.Remove(tf.Name())
|
|
||||||
defer tf.Close()
|
|
||||||
|
|
||||||
c.UserDataFile = tf.Name()
|
|
||||||
if err := c.Prepare(nil); len(err) != 0 {
|
|
||||||
t.Fatalf("err: %s", err)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestRunConfigPrepare_TemporaryKeyPairName(t *testing.T) {
|
|
||||||
c := testConfig()
|
|
||||||
c.Comm.SSHTemporaryKeyPairName = ""
|
|
||||||
if err := c.Prepare(nil); len(err) != 0 {
|
|
||||||
t.Fatalf("err: %s", err)
|
|
||||||
}
|
|
||||||
|
|
||||||
if c.Comm.SSHTemporaryKeyPairName == "" {
|
|
||||||
t.Fatal("keypair name is empty")
|
|
||||||
}
|
|
||||||
|
|
||||||
// Match prefix and UUID, e.g. "packer_5790d491-a0b8-c84c-c9d2-2aea55086550".
|
|
||||||
r := regexp.MustCompile(`\Apacker_(?:(?i)[a-f\d]{8}(?:-[a-f\d]{4}){3}-[a-f\d]{12}?)\z`)
|
|
||||||
if !r.MatchString(c.Comm.SSHTemporaryKeyPairName) {
|
|
||||||
t.Fatal("keypair name is not valid")
|
|
||||||
}
|
|
||||||
|
|
||||||
c.Comm.SSHTemporaryKeyPairName = "ssh-key-123"
|
|
||||||
if err := c.Prepare(nil); len(err) != 0 {
|
|
||||||
t.Fatalf("err: %s", err)
|
|
||||||
}
|
|
||||||
|
|
||||||
if c.Comm.SSHTemporaryKeyPairName != "ssh-key-123" {
|
|
||||||
t.Fatal("keypair name does not match")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestRunConfigPrepare_TenancyBad(t *testing.T) {
|
|
||||||
c := testConfig()
|
|
||||||
c.Tenancy = "not_real"
|
|
||||||
|
|
||||||
if err := c.Prepare(nil); len(err) != 1 {
|
|
||||||
t.Fatal("Should error if tenancy is set to an invalid type")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestRunConfigPrepare_TenancyGood(t *testing.T) {
|
|
||||||
validTenancy := []string{"", "default", "dedicated", "host"}
|
|
||||||
for _, vt := range validTenancy {
|
|
||||||
c := testConfig()
|
|
||||||
c.Tenancy = vt
|
|
||||||
if err := c.Prepare(nil); len(err) != 0 {
|
|
||||||
t.Fatalf("Should not error if tenancy is set to %s", vt)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,141 +0,0 @@
|
|||||||
package common
|
|
||||||
|
|
||||||
import (
|
|
||||||
"testing"
|
|
||||||
|
|
||||||
"github.com/aws/aws-sdk-go/aws"
|
|
||||||
"github.com/aws/aws-sdk-go/service/ec2"
|
|
||||||
"github.com/hashicorp/packer-plugin-sdk/multistep"
|
|
||||||
)
|
|
||||||
|
|
||||||
const (
|
|
||||||
privateIP = "10.0.0.1"
|
|
||||||
publicIP = "192.168.1.1"
|
|
||||||
privateDNS = "private.dns.test"
|
|
||||||
publicDNS = "public.dns.test"
|
|
||||||
localhost = "localhost"
|
|
||||||
sshHostTemplate = "custom.host.value"
|
|
||||||
)
|
|
||||||
|
|
||||||
func TestSSHHost(t *testing.T) {
|
|
||||||
origSshHostSleepDuration := sshHostSleepDuration
|
|
||||||
defer func() { sshHostSleepDuration = origSshHostSleepDuration }()
|
|
||||||
sshHostSleepDuration = 0
|
|
||||||
|
|
||||||
var cases = []struct {
|
|
||||||
allowTries int
|
|
||||||
vpcId string
|
|
||||||
sshInterface string
|
|
||||||
|
|
||||||
ok bool
|
|
||||||
wantHost string
|
|
||||||
sshHostOverride string
|
|
||||||
}{
|
|
||||||
{1, "", "", true, publicDNS, ""},
|
|
||||||
{1, "", "private_ip", true, privateIP, ""},
|
|
||||||
{1, "", "session_manager", true, localhost, ""},
|
|
||||||
{1, "vpc-id", "", true, publicIP, ""},
|
|
||||||
{1, "vpc-id", "private_ip", true, privateIP, ""},
|
|
||||||
{1, "vpc-id", "private_dns", true, privateDNS, ""},
|
|
||||||
{1, "vpc-id", "public_dns", true, publicDNS, ""},
|
|
||||||
{1, "vpc-id", "public_ip", true, publicIP, ""},
|
|
||||||
{1, "vpc-id", "session_manager", true, localhost, ""},
|
|
||||||
{2, "", "", true, publicDNS, ""},
|
|
||||||
{2, "", "private_ip", true, privateIP, ""},
|
|
||||||
{2, "vpc-id", "", true, publicIP, ""},
|
|
||||||
{2, "vpc-id", "private_ip", true, privateIP, ""},
|
|
||||||
{2, "vpc-id", "private_dns", true, privateDNS, ""},
|
|
||||||
{2, "vpc-id", "public_dns", true, publicDNS, ""},
|
|
||||||
{2, "vpc-id", "public_ip", true, publicIP, ""},
|
|
||||||
{3, "", "", false, "", ""},
|
|
||||||
{3, "", "private_ip", false, "", ""},
|
|
||||||
{3, "vpc-id", "", false, "", ""},
|
|
||||||
{3, "vpc-id", "private_ip", false, "", ""},
|
|
||||||
{3, "vpc-id", "private_dns", false, "", ""},
|
|
||||||
{3, "vpc-id", "public_dns", false, "", ""},
|
|
||||||
{3, "vpc-id", "public_ip", false, "", ""},
|
|
||||||
{1, "", "", true, sshHostTemplate, sshHostTemplate},
|
|
||||||
{1, "vpc-id", "", true, sshHostTemplate, sshHostTemplate},
|
|
||||||
{2, "vpc-id", "private_dns", true, sshHostTemplate, sshHostTemplate},
|
|
||||||
}
|
|
||||||
|
|
||||||
for _, c := range cases {
|
|
||||||
testSSHHost(t, c.allowTries, c.vpcId, c.sshInterface, c.ok, c.wantHost,
|
|
||||||
c.sshHostOverride)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func testSSHHost(t *testing.T, allowTries int, vpcId string, sshInterface string,
|
|
||||||
ok bool, wantHost string, sshHostOverride string) {
|
|
||||||
t.Logf("allowTries=%d vpcId=%s sshInterface=%s ok=%t wantHost=%q sshHostOverride=%s",
|
|
||||||
allowTries, vpcId, sshInterface, ok, wantHost, sshHostOverride)
|
|
||||||
|
|
||||||
e := &fakeEC2Describer{
|
|
||||||
allowTries: allowTries,
|
|
||||||
vpcId: vpcId,
|
|
||||||
privateIP: privateIP,
|
|
||||||
publicIP: publicIP,
|
|
||||||
privateDNS: privateDNS,
|
|
||||||
publicDNS: publicDNS,
|
|
||||||
}
|
|
||||||
|
|
||||||
f := SSHHost(e, sshInterface, sshHostOverride)
|
|
||||||
st := &multistep.BasicStateBag{}
|
|
||||||
st.Put("instance", &ec2.Instance{
|
|
||||||
InstanceId: aws.String("instance-id"),
|
|
||||||
})
|
|
||||||
|
|
||||||
host, err := f(st)
|
|
||||||
|
|
||||||
if e.tries > allowTries {
|
|
||||||
t.Fatalf("got %d ec2 DescribeInstances tries, want %d", e.tries, allowTries)
|
|
||||||
}
|
|
||||||
|
|
||||||
switch {
|
|
||||||
case ok && err != nil:
|
|
||||||
t.Fatalf("expected no error, got %+v", err)
|
|
||||||
case !ok && err == nil:
|
|
||||||
t.Fatalf("expected error, got none and host %s", host)
|
|
||||||
}
|
|
||||||
|
|
||||||
if host != wantHost {
|
|
||||||
t.Fatalf("got host %s, want %s", host, wantHost)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
type fakeEC2Describer struct {
|
|
||||||
allowTries int
|
|
||||||
tries int
|
|
||||||
|
|
||||||
vpcId string
|
|
||||||
privateIP, publicIP, privateDNS, publicDNS string
|
|
||||||
}
|
|
||||||
|
|
||||||
func (d *fakeEC2Describer) DescribeInstances(in *ec2.DescribeInstancesInput) (*ec2.DescribeInstancesOutput, error) {
|
|
||||||
d.tries++
|
|
||||||
|
|
||||||
instance := &ec2.Instance{
|
|
||||||
InstanceId: aws.String("instance-id"),
|
|
||||||
}
|
|
||||||
|
|
||||||
if d.vpcId != "" {
|
|
||||||
instance.VpcId = aws.String(d.vpcId)
|
|
||||||
}
|
|
||||||
|
|
||||||
if d.tries >= d.allowTries {
|
|
||||||
instance.PublicIpAddress = aws.String(d.publicIP)
|
|
||||||
instance.PrivateIpAddress = aws.String(d.privateIP)
|
|
||||||
instance.PublicDnsName = aws.String(d.publicDNS)
|
|
||||||
instance.PrivateDnsName = aws.String(d.privateDNS)
|
|
||||||
}
|
|
||||||
|
|
||||||
out := &ec2.DescribeInstancesOutput{
|
|
||||||
Reservations: []*ec2.Reservation{
|
|
||||||
{
|
|
||||||
Instances: []*ec2.Instance{instance},
|
|
||||||
},
|
|
||||||
},
|
|
||||||
}
|
|
||||||
|
|
||||||
return out, nil
|
|
||||||
}
|
|
||||||
@@ -1,66 +0,0 @@
|
|||||||
package common
|
|
||||||
|
|
||||||
import (
|
|
||||||
"reflect"
|
|
||||||
"testing"
|
|
||||||
"time"
|
|
||||||
|
|
||||||
"github.com/aws/aws-sdk-go/aws/request"
|
|
||||||
)
|
|
||||||
|
|
||||||
func testGetWaiterOptions(t *testing.T) {
|
|
||||||
// no vars are set
|
|
||||||
envValues := overridableWaitVars{
|
|
||||||
envInfo{"AWS_POLL_DELAY_SECONDS", 2, false},
|
|
||||||
envInfo{"AWS_MAX_ATTEMPTS", 0, false},
|
|
||||||
envInfo{"AWS_TIMEOUT_SECONDS", 300, false},
|
|
||||||
}
|
|
||||||
options := applyEnvOverrides(envValues)
|
|
||||||
if len(options) > 0 {
|
|
||||||
t.Fatalf("Did not expect any waiter options to be generated; actual: %#v", options)
|
|
||||||
}
|
|
||||||
|
|
||||||
// all vars are set
|
|
||||||
envValues = overridableWaitVars{
|
|
||||||
envInfo{"AWS_POLL_DELAY_SECONDS", 1, true},
|
|
||||||
envInfo{"AWS_MAX_ATTEMPTS", 800, true},
|
|
||||||
envInfo{"AWS_TIMEOUT_SECONDS", 20, true},
|
|
||||||
}
|
|
||||||
options = applyEnvOverrides(envValues)
|
|
||||||
expected := []request.WaiterOption{
|
|
||||||
request.WithWaiterDelay(request.ConstantWaiterDelay(time.Duration(1) * time.Second)),
|
|
||||||
request.WithWaiterMaxAttempts(800),
|
|
||||||
}
|
|
||||||
if !reflect.DeepEqual(options, expected) {
|
|
||||||
t.Fatalf("expected != actual!! Expected: %#v; Actual: %#v.", expected, options)
|
|
||||||
}
|
|
||||||
|
|
||||||
// poll delay is not set
|
|
||||||
envValues = overridableWaitVars{
|
|
||||||
envInfo{"AWS_POLL_DELAY_SECONDS", 2, false},
|
|
||||||
envInfo{"AWS_MAX_ATTEMPTS", 800, true},
|
|
||||||
envInfo{"AWS_TIMEOUT_SECONDS", 300, false},
|
|
||||||
}
|
|
||||||
options = applyEnvOverrides(envValues)
|
|
||||||
expected = []request.WaiterOption{
|
|
||||||
request.WithWaiterMaxAttempts(800),
|
|
||||||
}
|
|
||||||
if !reflect.DeepEqual(options, expected) {
|
|
||||||
t.Fatalf("expected != actual!! Expected: %#v; Actual: %#v.", expected, options)
|
|
||||||
}
|
|
||||||
|
|
||||||
// poll delay is not set but timeout seconds is
|
|
||||||
envValues = overridableWaitVars{
|
|
||||||
envInfo{"AWS_POLL_DELAY_SECONDS", 2, false},
|
|
||||||
envInfo{"AWS_MAX_ATTEMPTS", 0, false},
|
|
||||||
envInfo{"AWS_TIMEOUT_SECONDS", 20, true},
|
|
||||||
}
|
|
||||||
options = applyEnvOverrides(envValues)
|
|
||||||
expected = []request.WaiterOption{
|
|
||||||
request.WithWaiterDelay(request.ConstantWaiterDelay(time.Duration(2) * time.Second)),
|
|
||||||
request.WithWaiterMaxAttempts(10),
|
|
||||||
}
|
|
||||||
if !reflect.DeepEqual(options, expected) {
|
|
||||||
t.Fatalf("expected != actual!! Expected: %#v; Actual: %#v.", expected, options)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,404 +0,0 @@
|
|||||||
package common
|
|
||||||
|
|
||||||
import (
|
|
||||||
"bytes"
|
|
||||||
"context"
|
|
||||||
"fmt"
|
|
||||||
"sync"
|
|
||||||
"testing"
|
|
||||||
|
|
||||||
"github.com/aws/aws-sdk-go/aws"
|
|
||||||
"github.com/aws/aws-sdk-go/aws/request"
|
|
||||||
"github.com/aws/aws-sdk-go/service/ec2"
|
|
||||||
"github.com/aws/aws-sdk-go/service/ec2/ec2iface"
|
|
||||||
"github.com/hashicorp/packer-plugin-sdk/multistep"
|
|
||||||
packersdk "github.com/hashicorp/packer-plugin-sdk/packer"
|
|
||||||
"github.com/hashicorp/packer-plugin-sdk/template/config"
|
|
||||||
)
|
|
||||||
|
|
||||||
// Define a mock struct to be used in unit tests for common aws steps.
|
|
||||||
type mockEC2Conn struct {
|
|
||||||
ec2iface.EC2API
|
|
||||||
Config *aws.Config
|
|
||||||
|
|
||||||
// Counters to figure out what code path was taken
|
|
||||||
copyImageCount int
|
|
||||||
describeImagesCount int
|
|
||||||
deregisterImageCount int
|
|
||||||
deleteSnapshotCount int
|
|
||||||
waitCount int
|
|
||||||
|
|
||||||
lock sync.Mutex
|
|
||||||
}
|
|
||||||
|
|
||||||
func (m *mockEC2Conn) CopyImage(copyInput *ec2.CopyImageInput) (*ec2.CopyImageOutput, error) {
|
|
||||||
m.lock.Lock()
|
|
||||||
m.copyImageCount++
|
|
||||||
m.lock.Unlock()
|
|
||||||
copiedImage := fmt.Sprintf("%s-copied-%d", *copyInput.SourceImageId, m.copyImageCount)
|
|
||||||
output := &ec2.CopyImageOutput{
|
|
||||||
ImageId: &copiedImage,
|
|
||||||
}
|
|
||||||
return output, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// functions we have to create mock responses for in order for test to run
|
|
||||||
func (m *mockEC2Conn) DescribeImages(*ec2.DescribeImagesInput) (*ec2.DescribeImagesOutput, error) {
|
|
||||||
m.lock.Lock()
|
|
||||||
m.describeImagesCount++
|
|
||||||
m.lock.Unlock()
|
|
||||||
output := &ec2.DescribeImagesOutput{
|
|
||||||
Images: []*ec2.Image{{}},
|
|
||||||
}
|
|
||||||
return output, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func (m *mockEC2Conn) DeregisterImage(*ec2.DeregisterImageInput) (*ec2.DeregisterImageOutput, error) {
|
|
||||||
m.lock.Lock()
|
|
||||||
m.deregisterImageCount++
|
|
||||||
m.lock.Unlock()
|
|
||||||
output := &ec2.DeregisterImageOutput{}
|
|
||||||
return output, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func (m *mockEC2Conn) DeleteSnapshot(*ec2.DeleteSnapshotInput) (*ec2.DeleteSnapshotOutput, error) {
|
|
||||||
m.lock.Lock()
|
|
||||||
m.deleteSnapshotCount++
|
|
||||||
m.lock.Unlock()
|
|
||||||
output := &ec2.DeleteSnapshotOutput{}
|
|
||||||
return output, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func (m *mockEC2Conn) WaitUntilImageAvailableWithContext(aws.Context, *ec2.DescribeImagesInput, ...request.WaiterOption) error {
|
|
||||||
m.lock.Lock()
|
|
||||||
m.waitCount++
|
|
||||||
m.lock.Unlock()
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func getMockConn(config *AccessConfig, target string) (ec2iface.EC2API, error) {
|
|
||||||
mockConn := &mockEC2Conn{
|
|
||||||
Config: aws.NewConfig(),
|
|
||||||
}
|
|
||||||
|
|
||||||
return mockConn, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// Create statebag for running test
|
|
||||||
func tState() multistep.StateBag {
|
|
||||||
state := new(multistep.BasicStateBag)
|
|
||||||
state.Put("ui", &packersdk.BasicUi{
|
|
||||||
Reader: new(bytes.Buffer),
|
|
||||||
Writer: new(bytes.Buffer),
|
|
||||||
})
|
|
||||||
state.Put("amis", map[string]string{"us-east-1": "ami-12345"})
|
|
||||||
state.Put("snapshots", map[string][]string{"us-east-1": {"snap-0012345"}})
|
|
||||||
conn, _ := getMockConn(&AccessConfig{}, "us-east-2")
|
|
||||||
state.Put("ec2", conn)
|
|
||||||
return state
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestStepAMIRegionCopy_duplicates(t *testing.T) {
|
|
||||||
// ------------------------------------------------------------------------
|
|
||||||
// Test that if the original region is added to both Regions and Region,
|
|
||||||
// the ami is only copied once (with encryption).
|
|
||||||
// ------------------------------------------------------------------------
|
|
||||||
|
|
||||||
stepAMIRegionCopy := StepAMIRegionCopy{
|
|
||||||
AccessConfig: FakeAccessConfig(),
|
|
||||||
Regions: []string{"us-east-1"},
|
|
||||||
AMIKmsKeyId: "12345",
|
|
||||||
// Original region key in regionkeyids is different than in amikmskeyid
|
|
||||||
RegionKeyIds: map[string]string{"us-east-1": "12345"},
|
|
||||||
EncryptBootVolume: config.TriTrue,
|
|
||||||
Name: "fake-ami-name",
|
|
||||||
OriginalRegion: "us-east-1",
|
|
||||||
}
|
|
||||||
// mock out the region connection code
|
|
||||||
stepAMIRegionCopy.getRegionConn = getMockConn
|
|
||||||
|
|
||||||
state := tState()
|
|
||||||
state.Put("intermediary_image", true)
|
|
||||||
stepAMIRegionCopy.Run(context.Background(), state)
|
|
||||||
|
|
||||||
if len(stepAMIRegionCopy.Regions) != 1 {
|
|
||||||
t.Fatalf("Should have added original ami to Regions one time only")
|
|
||||||
}
|
|
||||||
|
|
||||||
// ------------------------------------------------------------------------
|
|
||||||
// Both Region and Regions set, but no encryption - shouldn't copy anything
|
|
||||||
// ------------------------------------------------------------------------
|
|
||||||
|
|
||||||
// the ami is only copied once.
|
|
||||||
stepAMIRegionCopy = StepAMIRegionCopy{
|
|
||||||
AccessConfig: FakeAccessConfig(),
|
|
||||||
Regions: []string{"us-east-1"},
|
|
||||||
Name: "fake-ami-name",
|
|
||||||
OriginalRegion: "us-east-1",
|
|
||||||
}
|
|
||||||
// mock out the region connection code
|
|
||||||
state.Put("intermediary_image", false)
|
|
||||||
stepAMIRegionCopy.getRegionConn = getMockConn
|
|
||||||
stepAMIRegionCopy.Run(context.Background(), state)
|
|
||||||
|
|
||||||
if len(stepAMIRegionCopy.Regions) != 0 {
|
|
||||||
t.Fatalf("Should not have added original ami to Regions; not encrypting")
|
|
||||||
}
|
|
||||||
|
|
||||||
// ------------------------------------------------------------------------
|
|
||||||
// Both Region and Regions set, but no encryption - shouldn't copy anything,
|
|
||||||
// this tests false as opposed to nil value above.
|
|
||||||
// ------------------------------------------------------------------------
|
|
||||||
|
|
||||||
// the ami is only copied once.
|
|
||||||
stepAMIRegionCopy = StepAMIRegionCopy{
|
|
||||||
AccessConfig: FakeAccessConfig(),
|
|
||||||
Regions: []string{"us-east-1"},
|
|
||||||
EncryptBootVolume: config.TriFalse,
|
|
||||||
Name: "fake-ami-name",
|
|
||||||
OriginalRegion: "us-east-1",
|
|
||||||
}
|
|
||||||
// mock out the region connection code
|
|
||||||
state.Put("intermediary_image", false)
|
|
||||||
stepAMIRegionCopy.getRegionConn = getMockConn
|
|
||||||
stepAMIRegionCopy.Run(context.Background(), state)
|
|
||||||
|
|
||||||
if len(stepAMIRegionCopy.Regions) != 0 {
|
|
||||||
t.Fatalf("Should not have added original ami to Regions once; not" +
|
|
||||||
"encrypting")
|
|
||||||
}
|
|
||||||
|
|
||||||
// ------------------------------------------------------------------------
|
|
||||||
// Multiple regions, many duplicates, and encryption (this shouldn't ever
|
|
||||||
// happen because of our template validation, but good to test it.)
|
|
||||||
// ------------------------------------------------------------------------
|
|
||||||
|
|
||||||
stepAMIRegionCopy = StepAMIRegionCopy{
|
|
||||||
AccessConfig: FakeAccessConfig(),
|
|
||||||
// Many duplicates for only 3 actual values
|
|
||||||
Regions: []string{"us-east-1", "us-west-2", "us-west-2", "ap-east-1", "ap-east-1", "ap-east-1"},
|
|
||||||
AMIKmsKeyId: "IlikePancakes",
|
|
||||||
// Original region key in regionkeyids is different than in amikmskeyid
|
|
||||||
RegionKeyIds: map[string]string{"us-east-1": "12345", "us-west-2": "abcde", "ap-east-1": "xyz"},
|
|
||||||
EncryptBootVolume: config.TriTrue,
|
|
||||||
Name: "fake-ami-name",
|
|
||||||
OriginalRegion: "us-east-1",
|
|
||||||
}
|
|
||||||
// mock out the region connection code
|
|
||||||
stepAMIRegionCopy.getRegionConn = getMockConn
|
|
||||||
state.Put("intermediary_image", true)
|
|
||||||
stepAMIRegionCopy.Run(context.Background(), state)
|
|
||||||
|
|
||||||
if len(stepAMIRegionCopy.Regions) != 3 {
|
|
||||||
t.Fatalf("Each AMI should have been added to Regions one time only.")
|
|
||||||
}
|
|
||||||
|
|
||||||
// Also verify that we respect RegionKeyIds over AMIKmsKeyIds:
|
|
||||||
if stepAMIRegionCopy.RegionKeyIds["us-east-1"] != "12345" {
|
|
||||||
t.Fatalf("RegionKeyIds should take precedence over AmiKmsKeyIds")
|
|
||||||
}
|
|
||||||
|
|
||||||
// ------------------------------------------------------------------------
|
|
||||||
// Multiple regions, many duplicates, NO encryption
|
|
||||||
// ------------------------------------------------------------------------
|
|
||||||
|
|
||||||
stepAMIRegionCopy = StepAMIRegionCopy{
|
|
||||||
AccessConfig: FakeAccessConfig(),
|
|
||||||
// Many duplicates for only 3 actual values
|
|
||||||
Regions: []string{"us-east-1", "us-west-2", "us-west-2", "ap-east-1", "ap-east-1", "ap-east-1"},
|
|
||||||
Name: "fake-ami-name",
|
|
||||||
OriginalRegion: "us-east-1",
|
|
||||||
}
|
|
||||||
// mock out the region connection code
|
|
||||||
stepAMIRegionCopy.getRegionConn = getMockConn
|
|
||||||
state.Put("intermediary_image", false)
|
|
||||||
stepAMIRegionCopy.Run(context.Background(), state)
|
|
||||||
|
|
||||||
if len(stepAMIRegionCopy.Regions) != 2 {
|
|
||||||
t.Fatalf("Each AMI should have been added to Regions one time only, " +
|
|
||||||
"and original region shouldn't be added at all")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestStepAmiRegionCopy_nil_encryption(t *testing.T) {
|
|
||||||
// create step
|
|
||||||
stepAMIRegionCopy := StepAMIRegionCopy{
|
|
||||||
AccessConfig: FakeAccessConfig(),
|
|
||||||
Regions: make([]string, 0),
|
|
||||||
AMIKmsKeyId: "",
|
|
||||||
RegionKeyIds: make(map[string]string),
|
|
||||||
EncryptBootVolume: config.TriUnset,
|
|
||||||
Name: "fake-ami-name",
|
|
||||||
OriginalRegion: "us-east-1",
|
|
||||||
}
|
|
||||||
// mock out the region connection code
|
|
||||||
stepAMIRegionCopy.getRegionConn = getMockConn
|
|
||||||
|
|
||||||
state := tState()
|
|
||||||
state.Put("intermediary_image", false)
|
|
||||||
stepAMIRegionCopy.Run(context.Background(), state)
|
|
||||||
|
|
||||||
if stepAMIRegionCopy.toDelete != "" {
|
|
||||||
t.Fatalf("Shouldn't have an intermediary ami if encrypt is nil")
|
|
||||||
}
|
|
||||||
if len(stepAMIRegionCopy.Regions) != 0 {
|
|
||||||
t.Fatalf("Should not have added original ami to original region")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestStepAmiRegionCopy_true_encryption(t *testing.T) {
|
|
||||||
// create step
|
|
||||||
stepAMIRegionCopy := StepAMIRegionCopy{
|
|
||||||
AccessConfig: FakeAccessConfig(),
|
|
||||||
Regions: make([]string, 0),
|
|
||||||
AMIKmsKeyId: "",
|
|
||||||
RegionKeyIds: make(map[string]string),
|
|
||||||
EncryptBootVolume: config.TriTrue,
|
|
||||||
Name: "fake-ami-name",
|
|
||||||
OriginalRegion: "us-east-1",
|
|
||||||
}
|
|
||||||
// mock out the region connection code
|
|
||||||
stepAMIRegionCopy.getRegionConn = getMockConn
|
|
||||||
|
|
||||||
state := tState()
|
|
||||||
state.Put("intermediary_image", true)
|
|
||||||
stepAMIRegionCopy.Run(context.Background(), state)
|
|
||||||
|
|
||||||
if stepAMIRegionCopy.toDelete == "" {
|
|
||||||
t.Fatalf("Should delete original AMI if encrypted=true")
|
|
||||||
}
|
|
||||||
if len(stepAMIRegionCopy.Regions) == 0 {
|
|
||||||
t.Fatalf("Should have added original ami to Regions")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestStepAmiRegionCopy_nil_intermediary(t *testing.T) {
|
|
||||||
// create step
|
|
||||||
stepAMIRegionCopy := StepAMIRegionCopy{
|
|
||||||
AccessConfig: FakeAccessConfig(),
|
|
||||||
Regions: make([]string, 0),
|
|
||||||
AMIKmsKeyId: "",
|
|
||||||
RegionKeyIds: make(map[string]string),
|
|
||||||
EncryptBootVolume: config.TriFalse,
|
|
||||||
Name: "fake-ami-name",
|
|
||||||
OriginalRegion: "us-east-1",
|
|
||||||
}
|
|
||||||
// mock out the region connection code
|
|
||||||
stepAMIRegionCopy.getRegionConn = getMockConn
|
|
||||||
|
|
||||||
state := tState()
|
|
||||||
stepAMIRegionCopy.Run(context.Background(), state)
|
|
||||||
|
|
||||||
if stepAMIRegionCopy.toDelete != "" {
|
|
||||||
t.Fatalf("Should not delete original AMI if no intermediary")
|
|
||||||
}
|
|
||||||
if len(stepAMIRegionCopy.Regions) != 0 {
|
|
||||||
t.Fatalf("Should not have added original ami to Regions")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestStepAmiRegionCopy_AMISkipBuildRegion(t *testing.T) {
|
|
||||||
// ------------------------------------------------------------------------
|
|
||||||
// skip build region is true
|
|
||||||
// ------------------------------------------------------------------------
|
|
||||||
|
|
||||||
stepAMIRegionCopy := StepAMIRegionCopy{
|
|
||||||
AccessConfig: FakeAccessConfig(),
|
|
||||||
Regions: []string{"us-west-1"},
|
|
||||||
AMIKmsKeyId: "",
|
|
||||||
RegionKeyIds: map[string]string{"us-west-1": "abcde"},
|
|
||||||
Name: "fake-ami-name",
|
|
||||||
OriginalRegion: "us-east-1",
|
|
||||||
AMISkipBuildRegion: true,
|
|
||||||
}
|
|
||||||
// mock out the region connection code
|
|
||||||
stepAMIRegionCopy.getRegionConn = getMockConn
|
|
||||||
|
|
||||||
state := tState()
|
|
||||||
state.Put("intermediary_image", true)
|
|
||||||
stepAMIRegionCopy.Run(context.Background(), state)
|
|
||||||
|
|
||||||
if stepAMIRegionCopy.toDelete == "" {
|
|
||||||
t.Fatalf("Should delete original AMI if skip_save_build_region=true")
|
|
||||||
}
|
|
||||||
if len(stepAMIRegionCopy.Regions) != 1 {
|
|
||||||
t.Fatalf("Should not have added original ami to Regions; Regions: %#v", stepAMIRegionCopy.Regions)
|
|
||||||
}
|
|
||||||
|
|
||||||
// ------------------------------------------------------------------------
|
|
||||||
// skip build region is false.
|
|
||||||
// ------------------------------------------------------------------------
|
|
||||||
stepAMIRegionCopy = StepAMIRegionCopy{
|
|
||||||
AccessConfig: FakeAccessConfig(),
|
|
||||||
Regions: []string{"us-west-1"},
|
|
||||||
AMIKmsKeyId: "",
|
|
||||||
RegionKeyIds: make(map[string]string),
|
|
||||||
Name: "fake-ami-name",
|
|
||||||
OriginalRegion: "us-east-1",
|
|
||||||
AMISkipBuildRegion: false,
|
|
||||||
}
|
|
||||||
// mock out the region connection code
|
|
||||||
stepAMIRegionCopy.getRegionConn = getMockConn
|
|
||||||
|
|
||||||
state.Put("intermediary_image", false) // not encrypted
|
|
||||||
stepAMIRegionCopy.Run(context.Background(), state)
|
|
||||||
|
|
||||||
if stepAMIRegionCopy.toDelete != "" {
|
|
||||||
t.Fatalf("Shouldn't have an intermediary AMI, so dont delete original ami")
|
|
||||||
}
|
|
||||||
if len(stepAMIRegionCopy.Regions) != 1 {
|
|
||||||
t.Fatalf("Should not have added original ami to Regions; Regions: %#v", stepAMIRegionCopy.Regions)
|
|
||||||
}
|
|
||||||
|
|
||||||
// ------------------------------------------------------------------------
|
|
||||||
// skip build region is false, but encrypt is true
|
|
||||||
// ------------------------------------------------------------------------
|
|
||||||
stepAMIRegionCopy = StepAMIRegionCopy{
|
|
||||||
AccessConfig: FakeAccessConfig(),
|
|
||||||
Regions: []string{"us-west-1"},
|
|
||||||
AMIKmsKeyId: "",
|
|
||||||
RegionKeyIds: map[string]string{"us-west-1": "abcde"},
|
|
||||||
Name: "fake-ami-name",
|
|
||||||
OriginalRegion: "us-east-1",
|
|
||||||
AMISkipBuildRegion: false,
|
|
||||||
EncryptBootVolume: config.TriTrue,
|
|
||||||
}
|
|
||||||
// mock out the region connection code
|
|
||||||
stepAMIRegionCopy.getRegionConn = getMockConn
|
|
||||||
|
|
||||||
state.Put("intermediary_image", true) //encrypted
|
|
||||||
stepAMIRegionCopy.Run(context.Background(), state)
|
|
||||||
|
|
||||||
if stepAMIRegionCopy.toDelete == "" {
|
|
||||||
t.Fatalf("Have to delete intermediary AMI")
|
|
||||||
}
|
|
||||||
if len(stepAMIRegionCopy.Regions) != 2 {
|
|
||||||
t.Fatalf("Should have added original ami to Regions; Regions: %#v", stepAMIRegionCopy.Regions)
|
|
||||||
}
|
|
||||||
|
|
||||||
// ------------------------------------------------------------------------
|
|
||||||
// skip build region is true, and encrypt is true
|
|
||||||
// ------------------------------------------------------------------------
|
|
||||||
stepAMIRegionCopy = StepAMIRegionCopy{
|
|
||||||
AccessConfig: FakeAccessConfig(),
|
|
||||||
Regions: []string{"us-west-1"},
|
|
||||||
AMIKmsKeyId: "",
|
|
||||||
RegionKeyIds: map[string]string{"us-west-1": "abcde"},
|
|
||||||
Name: "fake-ami-name",
|
|
||||||
OriginalRegion: "us-east-1",
|
|
||||||
AMISkipBuildRegion: true,
|
|
||||||
EncryptBootVolume: config.TriTrue,
|
|
||||||
}
|
|
||||||
// mock out the region connection code
|
|
||||||
stepAMIRegionCopy.getRegionConn = getMockConn
|
|
||||||
|
|
||||||
state.Put("intermediary_image", true) //encrypted
|
|
||||||
stepAMIRegionCopy.Run(context.Background(), state)
|
|
||||||
|
|
||||||
if stepAMIRegionCopy.toDelete == "" {
|
|
||||||
t.Fatalf("Have to delete intermediary AMI")
|
|
||||||
}
|
|
||||||
if len(stepAMIRegionCopy.Regions) != 1 {
|
|
||||||
t.Fatalf("Should not have added original ami to Regions; Regions: %#v", stepAMIRegionCopy.Regions)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,70 +0,0 @@
|
|||||||
package common
|
|
||||||
|
|
||||||
import (
|
|
||||||
"fmt"
|
|
||||||
"strings"
|
|
||||||
"testing"
|
|
||||||
|
|
||||||
"github.com/aws/aws-sdk-go/aws"
|
|
||||||
"github.com/aws/aws-sdk-go/service/ec2"
|
|
||||||
)
|
|
||||||
|
|
||||||
//DescribeVpcs mocks an ec2.DescribeVpcsOutput for a given input
|
|
||||||
func (m *mockEC2Conn) DescribeVpcs(input *ec2.DescribeVpcsInput) (*ec2.DescribeVpcsOutput, error) {
|
|
||||||
|
|
||||||
if input == nil || aws.StringValue(input.VpcIds[0]) == "" {
|
|
||||||
return nil, fmt.Errorf("oops looks like we need more input")
|
|
||||||
}
|
|
||||||
|
|
||||||
var isDefault bool
|
|
||||||
vpcID := aws.StringValue(input.VpcIds[0])
|
|
||||||
|
|
||||||
//only one default VPC per region
|
|
||||||
if strings.Contains("vpc-default-id", vpcID) {
|
|
||||||
isDefault = true
|
|
||||||
}
|
|
||||||
|
|
||||||
output := &ec2.DescribeVpcsOutput{
|
|
||||||
Vpcs: []*ec2.Vpc{
|
|
||||||
{IsDefault: aws.Bool(isDefault),
|
|
||||||
VpcId: aws.String(vpcID),
|
|
||||||
},
|
|
||||||
},
|
|
||||||
}
|
|
||||||
return output, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestStepPreValidate_checkVpc(t *testing.T) {
|
|
||||||
tt := []struct {
|
|
||||||
name string
|
|
||||||
step StepPreValidate
|
|
||||||
errorExpected bool
|
|
||||||
}{
|
|
||||||
{"DefaultVpc", StepPreValidate{VpcId: "vpc-default-id"}, false},
|
|
||||||
{"NonDefaultVpcNoSubnet", StepPreValidate{VpcId: "vpc-1234567890"}, true},
|
|
||||||
{"NonDefaultVpcWithSubnet", StepPreValidate{VpcId: "vpc-1234567890", SubnetId: "subnet-1234567890"}, false},
|
|
||||||
{"SubnetWithNoVpc", StepPreValidate{SubnetId: "subnet-1234567890"}, false},
|
|
||||||
{"NoVpcInformation", StepPreValidate{}, false},
|
|
||||||
{"NonDefaultVpcWithSubnetFilter", StepPreValidate{VpcId: "vpc-1234567890", HasSubnetFilter: true}, false},
|
|
||||||
}
|
|
||||||
|
|
||||||
mockConn, err := getMockConn(nil, "")
|
|
||||||
if err != nil {
|
|
||||||
t.Fatal("unable to get a mock connection")
|
|
||||||
}
|
|
||||||
|
|
||||||
for _, tc := range tt {
|
|
||||||
t.Run(tc.name, func(t *testing.T) {
|
|
||||||
err := tc.step.checkVpc(mockConn)
|
|
||||||
|
|
||||||
if tc.errorExpected && err == nil {
|
|
||||||
t.Errorf("expected a validation error for %q but got %q", tc.name, err)
|
|
||||||
}
|
|
||||||
|
|
||||||
if !tc.errorExpected && err != nil {
|
|
||||||
t.Errorf("expected a validation to pass for %q but got %q", tc.name, err)
|
|
||||||
}
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
@@ -1,366 +0,0 @@
|
|||||||
package common
|
|
||||||
|
|
||||||
import (
|
|
||||||
"bytes"
|
|
||||||
"context"
|
|
||||||
"fmt"
|
|
||||||
"testing"
|
|
||||||
|
|
||||||
"github.com/aws/aws-sdk-go/aws"
|
|
||||||
"github.com/aws/aws-sdk-go/service/ec2"
|
|
||||||
"github.com/aws/aws-sdk-go/service/ec2/ec2iface"
|
|
||||||
"github.com/hashicorp/packer-plugin-sdk/communicator"
|
|
||||||
"github.com/hashicorp/packer-plugin-sdk/multistep"
|
|
||||||
packersdk "github.com/hashicorp/packer-plugin-sdk/packer"
|
|
||||||
)
|
|
||||||
|
|
||||||
// Create statebag for running test
|
|
||||||
func tStateSpot() multistep.StateBag {
|
|
||||||
state := new(multistep.BasicStateBag)
|
|
||||||
state.Put("ui", &packersdk.BasicUi{
|
|
||||||
Reader: new(bytes.Buffer),
|
|
||||||
Writer: new(bytes.Buffer),
|
|
||||||
})
|
|
||||||
state.Put("availability_zone", "us-east-1c")
|
|
||||||
state.Put("securityGroupIds", []string{"sg-0b8984db72f213dc3"})
|
|
||||||
state.Put("iamInstanceProfile", "packer-123")
|
|
||||||
state.Put("subnet_id", "subnet-077fde4e")
|
|
||||||
state.Put("source_image", "")
|
|
||||||
return state
|
|
||||||
}
|
|
||||||
|
|
||||||
func getBasicStep() *StepRunSpotInstance {
|
|
||||||
stepRunSpotInstance := StepRunSpotInstance{
|
|
||||||
PollingConfig: new(AWSPollingConfig),
|
|
||||||
AssociatePublicIpAddress: false,
|
|
||||||
LaunchMappings: BlockDevices{},
|
|
||||||
BlockDurationMinutes: 0,
|
|
||||||
Debug: false,
|
|
||||||
Comm: &communicator.Config{
|
|
||||||
SSH: communicator.SSH{
|
|
||||||
SSHKeyPairName: "foo",
|
|
||||||
},
|
|
||||||
},
|
|
||||||
EbsOptimized: false,
|
|
||||||
ExpectedRootDevice: "ebs",
|
|
||||||
InstanceInitiatedShutdownBehavior: "stop",
|
|
||||||
InstanceType: "t2.micro",
|
|
||||||
Region: "us-east-1",
|
|
||||||
SourceAMI: "",
|
|
||||||
SpotPrice: "auto",
|
|
||||||
SpotTags: nil,
|
|
||||||
Tags: map[string]string{},
|
|
||||||
VolumeTags: nil,
|
|
||||||
UserData: "",
|
|
||||||
UserDataFile: "",
|
|
||||||
}
|
|
||||||
|
|
||||||
return &stepRunSpotInstance
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestCreateTemplateData(t *testing.T) {
|
|
||||||
state := tStateSpot()
|
|
||||||
stepRunSpotInstance := getBasicStep()
|
|
||||||
template := stepRunSpotInstance.CreateTemplateData(aws.String("userdata"), "az", state,
|
|
||||||
&ec2.LaunchTemplateInstanceMarketOptionsRequest{})
|
|
||||||
|
|
||||||
// expected := []*ec2.LaunchTemplateInstanceNetworkInterfaceSpecificationRequest{
|
|
||||||
// &ec2.LaunchTemplateInstanceNetworkInterfaceSpecificationRequest{
|
|
||||||
// DeleteOnTermination: aws.Bool(true),
|
|
||||||
// DeviceIndex: aws.Int64(0),
|
|
||||||
// Groups: aws.StringSlice([]string{"sg-0b8984db72f213dc3"}),
|
|
||||||
// SubnetId: aws.String("subnet-077fde4e"),
|
|
||||||
// },
|
|
||||||
// }
|
|
||||||
// if expected != template.NetworkInterfaces {
|
|
||||||
if template.NetworkInterfaces == nil {
|
|
||||||
t.Fatalf("Template should have contained a networkInterface object: recieved %#v", template.NetworkInterfaces)
|
|
||||||
}
|
|
||||||
|
|
||||||
if *template.IamInstanceProfile.Name != state.Get("iamInstanceProfile") {
|
|
||||||
t.Fatalf("Template should have contained a InstanceProfile name: recieved %#v", template.IamInstanceProfile.Name)
|
|
||||||
}
|
|
||||||
|
|
||||||
// Rerun, this time testing that we set security group IDs
|
|
||||||
state.Put("subnet_id", "")
|
|
||||||
template = stepRunSpotInstance.CreateTemplateData(aws.String("userdata"), "az", state,
|
|
||||||
&ec2.LaunchTemplateInstanceMarketOptionsRequest{})
|
|
||||||
if template.NetworkInterfaces != nil {
|
|
||||||
t.Fatalf("Template shouldn't contain network interfaces object if subnet_id is unset.")
|
|
||||||
}
|
|
||||||
|
|
||||||
// Rerun, this time testing that instance doesn't have instance profile is iamInstanceProfile is unset
|
|
||||||
state.Put("iamInstanceProfile", "")
|
|
||||||
template = stepRunSpotInstance.CreateTemplateData(aws.String("userdata"), "az", state,
|
|
||||||
&ec2.LaunchTemplateInstanceMarketOptionsRequest{})
|
|
||||||
fmt.Println(template.IamInstanceProfile)
|
|
||||||
if *template.IamInstanceProfile.Name != "" {
|
|
||||||
t.Fatalf("Template shouldn't contain instance profile if iamInstanceProfile is unset.")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestCreateTemplateData_NoEphemeral(t *testing.T) {
|
|
||||||
state := tStateSpot()
|
|
||||||
stepRunSpotInstance := getBasicStep()
|
|
||||||
stepRunSpotInstance.NoEphemeral = true
|
|
||||||
template := stepRunSpotInstance.CreateTemplateData(aws.String("userdata"), "az", state,
|
|
||||||
&ec2.LaunchTemplateInstanceMarketOptionsRequest{})
|
|
||||||
if len(template.BlockDeviceMappings) != 26 {
|
|
||||||
t.Fatalf("Should have created 26 mappings to keep ephemeral drives from appearing.")
|
|
||||||
}
|
|
||||||
|
|
||||||
// Now check that noEphemeral doesn't mess with the mappings in real life.
|
|
||||||
// state = tStateSpot()
|
|
||||||
// stepRunSpotInstance = getBasicStep()
|
|
||||||
// stepRunSpotInstance.NoEphemeral = true
|
|
||||||
// mappings := []*ec2.InstanceBlockDeviceMapping{
|
|
||||||
// &ec2.InstanceBlockDeviceMapping{
|
|
||||||
// DeviceName: "xvda",
|
|
||||||
// Ebs: {
|
|
||||||
// DeleteOnTermination: true,
|
|
||||||
// Status: "attaching",
|
|
||||||
// VolumeId: "vol-044cd49c330f21c05",
|
|
||||||
// },
|
|
||||||
// },
|
|
||||||
// &ec2.InstanceBlockDeviceMapping{
|
|
||||||
// DeviceName: "/dev/xvdf",
|
|
||||||
// Ebs: {
|
|
||||||
// DeleteOnTermination: false,
|
|
||||||
// Status: "attaching",
|
|
||||||
// VolumeId: "vol-0eefaf2d6ae35827e",
|
|
||||||
// },
|
|
||||||
// },
|
|
||||||
// }
|
|
||||||
// template = stepRunSpotInstance.CreateTemplateData(aws.String("userdata"), "az", state,
|
|
||||||
// &ec2.LaunchTemplateInstanceMarketOptionsRequest{})
|
|
||||||
// if len(*template.BlockDeviceMappings) != 26 {
|
|
||||||
// t.Fatalf("Should have created 26 mappings to keep ephemeral drives from appearing.")
|
|
||||||
// }
|
|
||||||
}
|
|
||||||
|
|
||||||
type runSpotEC2ConnMock struct {
|
|
||||||
ec2iface.EC2API
|
|
||||||
|
|
||||||
CreateLaunchTemplateParams []*ec2.CreateLaunchTemplateInput
|
|
||||||
CreateLaunchTemplateFn func(*ec2.CreateLaunchTemplateInput) (*ec2.CreateLaunchTemplateOutput, error)
|
|
||||||
|
|
||||||
CreateFleetParams []*ec2.CreateFleetInput
|
|
||||||
CreateFleetFn func(*ec2.CreateFleetInput) (*ec2.CreateFleetOutput, error)
|
|
||||||
|
|
||||||
CreateTagsParams []*ec2.CreateTagsInput
|
|
||||||
CreateTagsFn func(*ec2.CreateTagsInput) (*ec2.CreateTagsOutput, error)
|
|
||||||
|
|
||||||
DescribeInstancesParams []*ec2.DescribeInstancesInput
|
|
||||||
DescribeInstancesFn func(input *ec2.DescribeInstancesInput) (*ec2.DescribeInstancesOutput, error)
|
|
||||||
}
|
|
||||||
|
|
||||||
func (m *runSpotEC2ConnMock) CreateLaunchTemplate(req *ec2.CreateLaunchTemplateInput) (*ec2.CreateLaunchTemplateOutput, error) {
|
|
||||||
m.CreateLaunchTemplateParams = append(m.CreateLaunchTemplateParams, req)
|
|
||||||
resp, err := m.CreateLaunchTemplateFn(req)
|
|
||||||
return resp, err
|
|
||||||
}
|
|
||||||
|
|
||||||
func (m *runSpotEC2ConnMock) CreateFleet(req *ec2.CreateFleetInput) (*ec2.CreateFleetOutput, error) {
|
|
||||||
m.CreateFleetParams = append(m.CreateFleetParams, req)
|
|
||||||
if m.CreateFleetFn != nil {
|
|
||||||
resp, err := m.CreateFleetFn(req)
|
|
||||||
return resp, err
|
|
||||||
} else {
|
|
||||||
return nil, nil
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func (m *runSpotEC2ConnMock) DescribeInstances(req *ec2.DescribeInstancesInput) (*ec2.DescribeInstancesOutput, error) {
|
|
||||||
m.DescribeInstancesParams = append(m.DescribeInstancesParams, req)
|
|
||||||
if m.DescribeInstancesFn != nil {
|
|
||||||
resp, err := m.DescribeInstancesFn(req)
|
|
||||||
return resp, err
|
|
||||||
} else {
|
|
||||||
return nil, nil
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func (m *runSpotEC2ConnMock) CreateTags(req *ec2.CreateTagsInput) (*ec2.CreateTagsOutput, error) {
|
|
||||||
m.CreateTagsParams = append(m.CreateTagsParams, req)
|
|
||||||
if m.CreateTagsFn != nil {
|
|
||||||
resp, err := m.CreateTagsFn(req)
|
|
||||||
return resp, err
|
|
||||||
} else {
|
|
||||||
return nil, nil
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func defaultEc2Mock(instanceId, spotRequestId, volumeId, launchTemplateId *string) *runSpotEC2ConnMock {
|
|
||||||
instance := &ec2.Instance{
|
|
||||||
InstanceId: instanceId,
|
|
||||||
SpotInstanceRequestId: spotRequestId,
|
|
||||||
BlockDeviceMappings: []*ec2.InstanceBlockDeviceMapping{
|
|
||||||
{
|
|
||||||
Ebs: &ec2.EbsInstanceBlockDevice{
|
|
||||||
VolumeId: volumeId,
|
|
||||||
},
|
|
||||||
},
|
|
||||||
},
|
|
||||||
}
|
|
||||||
return &runSpotEC2ConnMock{
|
|
||||||
CreateLaunchTemplateFn: func(in *ec2.CreateLaunchTemplateInput) (*ec2.CreateLaunchTemplateOutput, error) {
|
|
||||||
return &ec2.CreateLaunchTemplateOutput{
|
|
||||||
LaunchTemplate: &ec2.LaunchTemplate{
|
|
||||||
LaunchTemplateId: launchTemplateId,
|
|
||||||
},
|
|
||||||
Warning: nil,
|
|
||||||
}, nil
|
|
||||||
},
|
|
||||||
CreateFleetFn: func(*ec2.CreateFleetInput) (*ec2.CreateFleetOutput, error) {
|
|
||||||
return &ec2.CreateFleetOutput{
|
|
||||||
Errors: nil,
|
|
||||||
FleetId: nil,
|
|
||||||
Instances: []*ec2.CreateFleetInstance{
|
|
||||||
{
|
|
||||||
InstanceIds: []*string{instanceId},
|
|
||||||
},
|
|
||||||
},
|
|
||||||
}, nil
|
|
||||||
},
|
|
||||||
DescribeInstancesFn: func(input *ec2.DescribeInstancesInput) (*ec2.DescribeInstancesOutput, error) {
|
|
||||||
return &ec2.DescribeInstancesOutput{
|
|
||||||
NextToken: nil,
|
|
||||||
Reservations: []*ec2.Reservation{
|
|
||||||
{
|
|
||||||
Instances: []*ec2.Instance{instance},
|
|
||||||
},
|
|
||||||
},
|
|
||||||
}, nil
|
|
||||||
},
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestRun(t *testing.T) {
|
|
||||||
instanceId := aws.String("test-instance-id")
|
|
||||||
spotRequestId := aws.String("spot-id")
|
|
||||||
volumeId := aws.String("volume-id")
|
|
||||||
launchTemplateId := aws.String("launchTemplateId")
|
|
||||||
ec2Mock := defaultEc2Mock(instanceId, spotRequestId, volumeId, launchTemplateId)
|
|
||||||
|
|
||||||
uiMock := packersdk.TestUi(t)
|
|
||||||
|
|
||||||
state := tStateSpot()
|
|
||||||
state.Put("ec2", ec2Mock)
|
|
||||||
state.Put("ui", uiMock)
|
|
||||||
state.Put("source_image", testImage())
|
|
||||||
|
|
||||||
stepRunSpotInstance := getBasicStep()
|
|
||||||
stepRunSpotInstance.Tags["Name"] = "Packer Builder"
|
|
||||||
stepRunSpotInstance.Tags["test-tag"] = "test-value"
|
|
||||||
stepRunSpotInstance.SpotTags = map[string]string{
|
|
||||||
"spot-tag": "spot-tag-value",
|
|
||||||
}
|
|
||||||
stepRunSpotInstance.VolumeTags = map[string]string{
|
|
||||||
"volume-tag": "volume-tag-value",
|
|
||||||
}
|
|
||||||
|
|
||||||
ctx := context.TODO()
|
|
||||||
action := stepRunSpotInstance.Run(ctx, state)
|
|
||||||
|
|
||||||
if err := state.Get("error"); err != nil {
|
|
||||||
t.Fatalf("should not error, but: %v", err)
|
|
||||||
}
|
|
||||||
|
|
||||||
if action != multistep.ActionContinue {
|
|
||||||
t.Fatalf("shoul continue, but: %v", action)
|
|
||||||
}
|
|
||||||
|
|
||||||
if len(ec2Mock.CreateLaunchTemplateParams) != 1 {
|
|
||||||
t.Fatalf("createLaunchTemplate should be invoked once, but invoked %v", len(ec2Mock.CreateLaunchTemplateParams))
|
|
||||||
}
|
|
||||||
launchTemplateName := ec2Mock.CreateLaunchTemplateParams[0].LaunchTemplateName
|
|
||||||
|
|
||||||
if len(ec2Mock.CreateLaunchTemplateParams[0].TagSpecifications) != 1 {
|
|
||||||
t.Fatalf("exactly one launch template tag specification expected")
|
|
||||||
}
|
|
||||||
if *ec2Mock.CreateLaunchTemplateParams[0].TagSpecifications[0].ResourceType != "launch-template" {
|
|
||||||
t.Fatalf("resource type 'launch-template' expected")
|
|
||||||
}
|
|
||||||
if len(ec2Mock.CreateLaunchTemplateParams[0].TagSpecifications[0].Tags) != 1 {
|
|
||||||
t.Fatalf("1 launch template tag expected")
|
|
||||||
}
|
|
||||||
|
|
||||||
nameTag := ec2Mock.CreateLaunchTemplateParams[0].TagSpecifications[0].Tags[0]
|
|
||||||
if *nameTag.Key != "spot-tag" || *nameTag.Value != "spot-tag-value" {
|
|
||||||
t.Fatalf("expected spot-tag: spot-tag-value")
|
|
||||||
}
|
|
||||||
|
|
||||||
if len(ec2Mock.CreateFleetParams) != 1 {
|
|
||||||
t.Fatalf("createFleet should be invoked once, but invoked %v", len(ec2Mock.CreateLaunchTemplateParams))
|
|
||||||
}
|
|
||||||
if *ec2Mock.CreateFleetParams[0].TargetCapacitySpecification.DefaultTargetCapacityType != "spot" {
|
|
||||||
t.Fatalf("capacity type should be spot")
|
|
||||||
}
|
|
||||||
if *ec2Mock.CreateFleetParams[0].TargetCapacitySpecification.TotalTargetCapacity != 1 {
|
|
||||||
t.Fatalf("target capacity should be 1")
|
|
||||||
}
|
|
||||||
if len(ec2Mock.CreateFleetParams[0].LaunchTemplateConfigs) != 1 {
|
|
||||||
t.Fatalf("exactly one launch config template expected")
|
|
||||||
}
|
|
||||||
if *ec2Mock.CreateFleetParams[0].LaunchTemplateConfigs[0].LaunchTemplateSpecification.LaunchTemplateName != *launchTemplateName {
|
|
||||||
t.Fatalf("launchTemplateName should match in createLaunchTemplate and createFleet requests")
|
|
||||||
}
|
|
||||||
|
|
||||||
if len(ec2Mock.DescribeInstancesParams) != 1 {
|
|
||||||
t.Fatalf("describeInstancesParams should be invoked once, but invoked %v", len(ec2Mock.DescribeInstancesParams))
|
|
||||||
}
|
|
||||||
if *ec2Mock.DescribeInstancesParams[0].InstanceIds[0] != *instanceId {
|
|
||||||
t.Fatalf("instanceId should match from createFleet response")
|
|
||||||
}
|
|
||||||
|
|
||||||
uiMock.Say(fmt.Sprintf("%v", ec2Mock.CreateTagsParams))
|
|
||||||
if len(ec2Mock.CreateTagsParams) != 3 {
|
|
||||||
t.Fatalf("createTags should be invoked 3 times")
|
|
||||||
}
|
|
||||||
if len(ec2Mock.CreateTagsParams[0].Resources) != 1 || *ec2Mock.CreateTagsParams[0].Resources[0] != *spotRequestId {
|
|
||||||
t.Fatalf("should create tags for spot request")
|
|
||||||
}
|
|
||||||
if len(ec2Mock.CreateTagsParams[1].Resources) != 1 || *ec2Mock.CreateTagsParams[1].Resources[0] != *instanceId {
|
|
||||||
t.Fatalf("should create tags for instance")
|
|
||||||
}
|
|
||||||
if len(ec2Mock.CreateTagsParams[2].Resources) != 1 || ec2Mock.CreateTagsParams[2].Resources[0] != volumeId {
|
|
||||||
t.Fatalf("should create tags for volume")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestRun_NoSpotTags(t *testing.T) {
|
|
||||||
instanceId := aws.String("test-instance-id")
|
|
||||||
spotRequestId := aws.String("spot-id")
|
|
||||||
volumeId := aws.String("volume-id")
|
|
||||||
launchTemplateId := aws.String("lt-id")
|
|
||||||
ec2Mock := defaultEc2Mock(instanceId, spotRequestId, volumeId, launchTemplateId)
|
|
||||||
|
|
||||||
uiMock := packersdk.TestUi(t)
|
|
||||||
|
|
||||||
state := tStateSpot()
|
|
||||||
state.Put("ec2", ec2Mock)
|
|
||||||
state.Put("ui", uiMock)
|
|
||||||
state.Put("source_image", testImage())
|
|
||||||
|
|
||||||
stepRunSpotInstance := getBasicStep()
|
|
||||||
stepRunSpotInstance.Tags["Name"] = "Packer Builder"
|
|
||||||
stepRunSpotInstance.Tags["test-tag"] = "test-value"
|
|
||||||
stepRunSpotInstance.VolumeTags = map[string]string{
|
|
||||||
"volume-tag": "volume-tag-value",
|
|
||||||
}
|
|
||||||
|
|
||||||
ctx := context.TODO()
|
|
||||||
action := stepRunSpotInstance.Run(ctx, state)
|
|
||||||
|
|
||||||
if err := state.Get("error"); err != nil {
|
|
||||||
t.Fatalf("should not error, but: %v", err)
|
|
||||||
}
|
|
||||||
|
|
||||||
if action != multistep.ActionContinue {
|
|
||||||
t.Fatalf("shoul continue, but: %v", action)
|
|
||||||
}
|
|
||||||
|
|
||||||
if len(ec2Mock.CreateLaunchTemplateParams[0].TagSpecifications) != 0 {
|
|
||||||
t.Fatalf("0 launch template tags expected")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,43 +0,0 @@
|
|||||||
package common
|
|
||||||
|
|
||||||
import (
|
|
||||||
"testing"
|
|
||||||
|
|
||||||
"github.com/aws/aws-sdk-go/aws"
|
|
||||||
"github.com/aws/aws-sdk-go/service/ec2"
|
|
||||||
"github.com/stretchr/testify/assert"
|
|
||||||
)
|
|
||||||
|
|
||||||
func TestStepSourceAmiInfo_PVImage(t *testing.T) {
|
|
||||||
err := new(StepSourceAMIInfo).canEnableEnhancedNetworking(&ec2.Image{
|
|
||||||
VirtualizationType: aws.String("paravirtual"),
|
|
||||||
})
|
|
||||||
assert.Error(t, err)
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestStepSourceAmiInfo_HVMImage(t *testing.T) {
|
|
||||||
err := new(StepSourceAMIInfo).canEnableEnhancedNetworking(&ec2.Image{
|
|
||||||
VirtualizationType: aws.String("hvm"),
|
|
||||||
})
|
|
||||||
assert.NoError(t, err)
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestStepSourceAmiInfo_PVImageWithAMIVirtPV(t *testing.T) {
|
|
||||||
stepSourceAMIInfo := StepSourceAMIInfo{
|
|
||||||
AMIVirtType: "paravirtual",
|
|
||||||
}
|
|
||||||
err := stepSourceAMIInfo.canEnableEnhancedNetworking(&ec2.Image{
|
|
||||||
VirtualizationType: aws.String("paravirtual"),
|
|
||||||
})
|
|
||||||
assert.Error(t, err)
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestStepSourceAmiInfo_PVImageWithAMIVirtHVM(t *testing.T) {
|
|
||||||
stepSourceAMIInfo := StepSourceAMIInfo{
|
|
||||||
AMIVirtType: "hvm",
|
|
||||||
}
|
|
||||||
err := stepSourceAMIInfo.canEnableEnhancedNetworking(&ec2.Image{
|
|
||||||
VirtualizationType: aws.String("paravirtual"),
|
|
||||||
})
|
|
||||||
assert.NoError(t, err)
|
|
||||||
}
|
|
||||||
@@ -1,16 +0,0 @@
|
|||||||
package common
|
|
||||||
|
|
||||||
import (
|
|
||||||
"testing"
|
|
||||||
)
|
|
||||||
|
|
||||||
func TestAMITemplatePrepare_clean(t *testing.T) {
|
|
||||||
origName := "AMZamz09()./-_:&^ $%[]#'@"
|
|
||||||
expected := "AMZamz09()./-_--- --[]-'@"
|
|
||||||
|
|
||||||
name := templateCleanAMIName(origName)
|
|
||||||
|
|
||||||
if name != expected {
|
|
||||||
t.Fatalf("template names do not match: expected %s got %s\n", expected, name)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,46 +0,0 @@
|
|||||||
package amazon_acc
|
|
||||||
|
|
||||||
import (
|
|
||||||
"fmt"
|
|
||||||
|
|
||||||
"github.com/aws/aws-sdk-go/aws"
|
|
||||||
"github.com/aws/aws-sdk-go/service/ec2"
|
|
||||||
awscommon "github.com/hashicorp/packer/builder/amazon/common"
|
|
||||||
)
|
|
||||||
|
|
||||||
type AWSHelper struct {
|
|
||||||
Region string
|
|
||||||
AMIName string
|
|
||||||
}
|
|
||||||
|
|
||||||
func (a *AWSHelper) CleanUpAmi() error {
|
|
||||||
accessConfig := &awscommon.AccessConfig{}
|
|
||||||
session, err := accessConfig.Session()
|
|
||||||
if err != nil {
|
|
||||||
return fmt.Errorf("AWSAMICleanUp: Unable to create aws session %s", err.Error())
|
|
||||||
}
|
|
||||||
|
|
||||||
regionconn := ec2.New(session.Copy(&aws.Config{
|
|
||||||
Region: aws.String(a.Region),
|
|
||||||
}))
|
|
||||||
|
|
||||||
resp, err := regionconn.DescribeImages(&ec2.DescribeImagesInput{
|
|
||||||
Owners: aws.StringSlice([]string{"self"}),
|
|
||||||
Filters: []*ec2.Filter{{
|
|
||||||
Name: aws.String("name"),
|
|
||||||
Values: aws.StringSlice([]string{a.AMIName}),
|
|
||||||
}}})
|
|
||||||
if err != nil {
|
|
||||||
return fmt.Errorf("AWSAMICleanUp: Unable to find Image %s: %s", a.AMIName, err.Error())
|
|
||||||
}
|
|
||||||
|
|
||||||
if resp != nil && len(resp.Images) > 0 {
|
|
||||||
_, err = regionconn.DeregisterImage(&ec2.DeregisterImageInput{
|
|
||||||
ImageId: resp.Images[0].ImageId,
|
|
||||||
})
|
|
||||||
if err != nil {
|
|
||||||
return fmt.Errorf("AWSAMICleanUp: Unable to Deregister Image %s", err.Error())
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
@@ -1,59 +0,0 @@
|
|||||||
package amazon_acc
|
|
||||||
|
|
||||||
// This is the code necessary for running the provisioner acceptance tests.
|
|
||||||
// It provides the builder config and cleans up created resource.
|
|
||||||
|
|
||||||
import (
|
|
||||||
"fmt"
|
|
||||||
"io/ioutil"
|
|
||||||
"os"
|
|
||||||
"path/filepath"
|
|
||||||
|
|
||||||
amazonebsbuilder "github.com/hashicorp/packer/builder/amazon/ebs"
|
|
||||||
|
|
||||||
packersdk "github.com/hashicorp/packer-plugin-sdk/packer"
|
|
||||||
)
|
|
||||||
|
|
||||||
type AmazonEBSAccTest struct{}
|
|
||||||
|
|
||||||
func (s *AmazonEBSAccTest) GetConfigs() (map[string]string, error) {
|
|
||||||
fixtures := map[string]string{
|
|
||||||
"linux": "amazon-ebs.txt",
|
|
||||||
"windows": "amazon-ebs_windows.txt",
|
|
||||||
}
|
|
||||||
|
|
||||||
configs := make(map[string]string)
|
|
||||||
|
|
||||||
for distro, fixture := range fixtures {
|
|
||||||
fileName := fixture
|
|
||||||
filePath := filepath.Join("../../builder/amazon/ebs/acceptance/test-fixtures/", fileName)
|
|
||||||
config, err := os.Open(filePath)
|
|
||||||
if err != nil {
|
|
||||||
return nil, fmt.Errorf("Expected to find %s", filePath)
|
|
||||||
}
|
|
||||||
defer config.Close()
|
|
||||||
|
|
||||||
file, err := ioutil.ReadAll(config)
|
|
||||||
if err != nil {
|
|
||||||
return nil, fmt.Errorf("Unable to read %s", filePath)
|
|
||||||
}
|
|
||||||
|
|
||||||
configs[distro] = string(file)
|
|
||||||
|
|
||||||
}
|
|
||||||
return configs, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func (s *AmazonEBSAccTest) CleanUp() error {
|
|
||||||
helper := AWSHelper{
|
|
||||||
Region: "us-east-1",
|
|
||||||
AMIName: "packer-acc-test",
|
|
||||||
}
|
|
||||||
return helper.CleanUpAmi()
|
|
||||||
}
|
|
||||||
|
|
||||||
func (s *AmazonEBSAccTest) GetBuilderStore() packersdk.MapOfBuilder {
|
|
||||||
return packersdk.MapOfBuilder{
|
|
||||||
"amazon-ebs": func() (packersdk.Builder, error) { return &amazonebsbuilder.Builder{}, nil },
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,12 +0,0 @@
|
|||||||
{
|
|
||||||
"type": "amazon-ebs",
|
|
||||||
"ami_name": "packer-acc-test",
|
|
||||||
"instance_type": "m1.small",
|
|
||||||
"region": "us-east-1",
|
|
||||||
"ssh_username": "ubuntu",
|
|
||||||
"source_ami": "ami-0568456c",
|
|
||||||
"force_deregister" : true,
|
|
||||||
"tags": {
|
|
||||||
"packer-test": "true"
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,23 +0,0 @@
|
|||||||
{
|
|
||||||
"type": "amazon-ebs",
|
|
||||||
"region": "us-east-1",
|
|
||||||
"instance_type": "t2.micro",
|
|
||||||
"source_ami_filter": {
|
|
||||||
"filters": {
|
|
||||||
"virtualization-type": "hvm",
|
|
||||||
"name": "*Windows_Server-2012-R2*English-64Bit-Base*",
|
|
||||||
"root-device-type": "ebs"
|
|
||||||
},
|
|
||||||
"most_recent": true,
|
|
||||||
"owners": "amazon"
|
|
||||||
},
|
|
||||||
"ami_name": "packer-acc-test",
|
|
||||||
"user_data_file": "../../builder/amazon/ebs/acceptance/test-fixtures/scripts/bootstrap_win.txt",
|
|
||||||
"communicator": "winrm",
|
|
||||||
"winrm_username": "Administrator",
|
|
||||||
"winrm_password": "SuperS3cr3t!!!!",
|
|
||||||
"force_deregister" : true,
|
|
||||||
"tags": {
|
|
||||||
"packer-test": "true"
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,40 +0,0 @@
|
|||||||
<powershell>
|
|
||||||
# Set administrator password
|
|
||||||
net user Administrator SuperS3cr3t!!!!
|
|
||||||
wmic useraccount where "name='Administrator'" set PasswordExpires=FALSE
|
|
||||||
|
|
||||||
# First, make sure WinRM can't be connected to
|
|
||||||
netsh advfirewall firewall set rule name="Windows Remote Management (HTTP-In)" new enable=yes action=block
|
|
||||||
|
|
||||||
# Delete any existing WinRM listeners
|
|
||||||
winrm delete winrm/config/listener?Address=*+Transport=HTTP 2>$Null
|
|
||||||
winrm delete winrm/config/listener?Address=*+Transport=HTTPS 2>$Null
|
|
||||||
|
|
||||||
# Disable group policies which block basic authentication and unencrypted login
|
|
||||||
|
|
||||||
Set-ItemProperty -Path HKLM:\Software\Policies\Microsoft\Windows\WinRM\Client -Name AllowBasic -Value 1
|
|
||||||
Set-ItemProperty -Path HKLM:\Software\Policies\Microsoft\Windows\WinRM\Client -Name AllowUnencryptedTraffic -Value 1
|
|
||||||
Set-ItemProperty -Path HKLM:\Software\Policies\Microsoft\Windows\WinRM\Service -Name AllowBasic -Value 1
|
|
||||||
Set-ItemProperty -Path HKLM:\Software\Policies\Microsoft\Windows\WinRM\Service -Name AllowUnencryptedTraffic -Value 1
|
|
||||||
|
|
||||||
|
|
||||||
# Create a new WinRM listener and configure
|
|
||||||
winrm create winrm/config/listener?Address=*+Transport=HTTP
|
|
||||||
winrm set winrm/config/winrs '@{MaxMemoryPerShellMB="0"}'
|
|
||||||
winrm set winrm/config '@{MaxTimeoutms="7200000"}'
|
|
||||||
winrm set winrm/config/service '@{AllowUnencrypted="true"}'
|
|
||||||
winrm set winrm/config/service '@{MaxConcurrentOperationsPerUser="12000"}'
|
|
||||||
winrm set winrm/config/service/auth '@{Basic="true"}'
|
|
||||||
winrm set winrm/config/client/auth '@{Basic="true"}'
|
|
||||||
|
|
||||||
# Configure UAC to allow privilege elevation in remote shells
|
|
||||||
$Key = 'HKLM:\SOFTWARE\Microsoft\Windows\CurrentVersion\Policies\System'
|
|
||||||
$Setting = 'LocalAccountTokenFilterPolicy'
|
|
||||||
Set-ItemProperty -Path $Key -Name $Setting -Value 1 -Force
|
|
||||||
|
|
||||||
# Configure and restart the WinRM Service; Enable the required firewall exception
|
|
||||||
Stop-Service -Name WinRM
|
|
||||||
Set-Service -Name WinRM -StartupType Automatic
|
|
||||||
netsh advfirewall firewall set rule name="Windows Remote Management (HTTP-In)" new action=allow localip=any remoteip=any
|
|
||||||
Start-Service -Name WinRM
|
|
||||||
</powershell>
|
|
||||||
@@ -1,396 +0,0 @@
|
|||||||
/*
|
|
||||||
Deregister the test image with
|
|
||||||
aws ec2 deregister-image --image-id $(aws ec2 describe-images --output text --filters "Name=name,Values=packer-test-packer-test-dereg" --query 'Images[*].{ID:ImageId}')
|
|
||||||
*/
|
|
||||||
package ebs
|
|
||||||
|
|
||||||
import (
|
|
||||||
"fmt"
|
|
||||||
"os"
|
|
||||||
"testing"
|
|
||||||
|
|
||||||
"github.com/aws/aws-sdk-go/aws"
|
|
||||||
"github.com/aws/aws-sdk-go/service/ec2"
|
|
||||||
builderT "github.com/hashicorp/packer-plugin-sdk/acctest"
|
|
||||||
packersdk "github.com/hashicorp/packer-plugin-sdk/packer"
|
|
||||||
"github.com/hashicorp/packer/builder/amazon/common"
|
|
||||||
)
|
|
||||||
|
|
||||||
func TestBuilderAcc_basic(t *testing.T) {
|
|
||||||
builderT.Test(t, builderT.TestCase{
|
|
||||||
PreCheck: func() { testAccPreCheck(t) },
|
|
||||||
Builder: &Builder{},
|
|
||||||
Template: testBuilderAccBasic,
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestBuilderAcc_regionCopy(t *testing.T) {
|
|
||||||
builderT.Test(t, builderT.TestCase{
|
|
||||||
PreCheck: func() { testAccPreCheck(t) },
|
|
||||||
Builder: &Builder{},
|
|
||||||
Template: testBuilderAccRegionCopy,
|
|
||||||
Check: checkRegionCopy([]string{"us-east-1", "us-west-2"}),
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestBuilderAcc_forceDeregister(t *testing.T) {
|
|
||||||
// Build the same AMI name twice, with force_deregister on the second run
|
|
||||||
builderT.Test(t, builderT.TestCase{
|
|
||||||
PreCheck: func() { testAccPreCheck(t) },
|
|
||||||
Builder: &Builder{},
|
|
||||||
Template: buildForceDeregisterConfig("false", "dereg"),
|
|
||||||
SkipArtifactTeardown: true,
|
|
||||||
})
|
|
||||||
|
|
||||||
builderT.Test(t, builderT.TestCase{
|
|
||||||
PreCheck: func() { testAccPreCheck(t) },
|
|
||||||
Builder: &Builder{},
|
|
||||||
Template: buildForceDeregisterConfig("true", "dereg"),
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestBuilderAcc_forceDeleteSnapshot(t *testing.T) {
|
|
||||||
amiName := "packer-test-dereg"
|
|
||||||
|
|
||||||
// Build the same AMI name twice, with force_delete_snapshot on the second run
|
|
||||||
builderT.Test(t, builderT.TestCase{
|
|
||||||
PreCheck: func() { testAccPreCheck(t) },
|
|
||||||
Builder: &Builder{},
|
|
||||||
Template: buildForceDeleteSnapshotConfig("false", amiName),
|
|
||||||
SkipArtifactTeardown: true,
|
|
||||||
})
|
|
||||||
|
|
||||||
// Get image data by AMI name
|
|
||||||
ec2conn, _ := testEC2Conn()
|
|
||||||
describeInput := &ec2.DescribeImagesInput{Filters: []*ec2.Filter{
|
|
||||||
{
|
|
||||||
Name: aws.String("name"),
|
|
||||||
Values: []*string{aws.String(amiName)},
|
|
||||||
},
|
|
||||||
}}
|
|
||||||
ec2conn.WaitUntilImageExists(describeInput)
|
|
||||||
imageResp, _ := ec2conn.DescribeImages(describeInput)
|
|
||||||
image := imageResp.Images[0]
|
|
||||||
|
|
||||||
// Get snapshot ids for image
|
|
||||||
snapshotIds := []*string{}
|
|
||||||
for _, device := range image.BlockDeviceMappings {
|
|
||||||
if device.Ebs != nil && device.Ebs.SnapshotId != nil {
|
|
||||||
snapshotIds = append(snapshotIds, device.Ebs.SnapshotId)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
builderT.Test(t, builderT.TestCase{
|
|
||||||
PreCheck: func() { testAccPreCheck(t) },
|
|
||||||
Builder: &Builder{},
|
|
||||||
Template: buildForceDeleteSnapshotConfig("true", amiName),
|
|
||||||
Check: checkSnapshotsDeleted(snapshotIds),
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
func checkSnapshotsDeleted(snapshotIds []*string) builderT.TestCheckFunc {
|
|
||||||
return func(artifacts []packersdk.Artifact) error {
|
|
||||||
// Verify the snapshots are gone
|
|
||||||
ec2conn, _ := testEC2Conn()
|
|
||||||
snapshotResp, _ := ec2conn.DescribeSnapshots(
|
|
||||||
&ec2.DescribeSnapshotsInput{SnapshotIds: snapshotIds},
|
|
||||||
)
|
|
||||||
|
|
||||||
if len(snapshotResp.Snapshots) > 0 {
|
|
||||||
return fmt.Errorf("Snapshots weren't successfully deleted by `force_delete_snapshot`")
|
|
||||||
}
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestBuilderAcc_amiSharing(t *testing.T) {
|
|
||||||
builderT.Test(t, builderT.TestCase{
|
|
||||||
PreCheck: func() { testAccSharingPreCheck(t) },
|
|
||||||
Builder: &Builder{},
|
|
||||||
Template: buildSharingConfig(os.Getenv("TESTACC_AWS_ACCOUNT_ID")),
|
|
||||||
Check: checkAMISharing(2, os.Getenv("TESTACC_AWS_ACCOUNT_ID"), "all"),
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestBuilderAcc_encryptedBoot(t *testing.T) {
|
|
||||||
builderT.Test(t, builderT.TestCase{
|
|
||||||
PreCheck: func() { testAccPreCheck(t) },
|
|
||||||
Builder: &Builder{},
|
|
||||||
Template: testBuilderAccEncrypted,
|
|
||||||
Check: checkBootEncrypted(),
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
func checkAMISharing(count int, uid, group string) builderT.TestCheckFunc {
|
|
||||||
return func(artifacts []packersdk.Artifact) error {
|
|
||||||
if len(artifacts) > 1 {
|
|
||||||
return fmt.Errorf("more than 1 artifact")
|
|
||||||
}
|
|
||||||
|
|
||||||
// Get the actual *Artifact pointer so we can access the AMIs directly
|
|
||||||
artifactRaw := artifacts[0]
|
|
||||||
artifact, ok := artifactRaw.(*common.Artifact)
|
|
||||||
if !ok {
|
|
||||||
return fmt.Errorf("unknown artifact: %#v", artifactRaw)
|
|
||||||
}
|
|
||||||
|
|
||||||
// describe the image, get block devices with a snapshot
|
|
||||||
ec2conn, _ := testEC2Conn()
|
|
||||||
imageResp, err := ec2conn.DescribeImageAttribute(&ec2.DescribeImageAttributeInput{
|
|
||||||
Attribute: aws.String("launchPermission"),
|
|
||||||
ImageId: aws.String(artifact.Amis["us-east-1"]),
|
|
||||||
})
|
|
||||||
|
|
||||||
if err != nil {
|
|
||||||
return fmt.Errorf("Error retrieving Image Attributes for AMI Artifact (%#v) in AMI Sharing Test: %s", artifact, err)
|
|
||||||
}
|
|
||||||
|
|
||||||
// Launch Permissions are in addition to the userid that created it, so if
|
|
||||||
// you add 3 additional ami_users, you expect 2 Launch Permissions here
|
|
||||||
if len(imageResp.LaunchPermissions) != count {
|
|
||||||
return fmt.Errorf("Error in Image Attributes, expected (%d) Launch Permissions, got (%d)", count, len(imageResp.LaunchPermissions))
|
|
||||||
}
|
|
||||||
|
|
||||||
userFound := false
|
|
||||||
for _, lp := range imageResp.LaunchPermissions {
|
|
||||||
if lp.UserId != nil && uid == *lp.UserId {
|
|
||||||
userFound = true
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if !userFound {
|
|
||||||
return fmt.Errorf("Error in Image Attributes, expected User ID (%s) to have Launch Permissions, but was not found", uid)
|
|
||||||
}
|
|
||||||
|
|
||||||
groupFound := false
|
|
||||||
for _, lp := range imageResp.LaunchPermissions {
|
|
||||||
if lp.Group != nil && group == *lp.Group {
|
|
||||||
groupFound = true
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if !groupFound {
|
|
||||||
return fmt.Errorf("Error in Image Attributes, expected Group ID (%s) to have Launch Permissions, but was not found", group)
|
|
||||||
}
|
|
||||||
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func checkRegionCopy(regions []string) builderT.TestCheckFunc {
|
|
||||||
return func(artifacts []packersdk.Artifact) error {
|
|
||||||
if len(artifacts) > 1 {
|
|
||||||
return fmt.Errorf("more than 1 artifact")
|
|
||||||
}
|
|
||||||
|
|
||||||
// Get the actual *Artifact pointer so we can access the AMIs directly
|
|
||||||
artifactRaw := artifacts[0]
|
|
||||||
artifact, ok := artifactRaw.(*common.Artifact)
|
|
||||||
if !ok {
|
|
||||||
return fmt.Errorf("unknown artifact: %#v", artifactRaw)
|
|
||||||
}
|
|
||||||
|
|
||||||
// Verify that we copied to only the regions given
|
|
||||||
regionSet := make(map[string]struct{})
|
|
||||||
for _, r := range regions {
|
|
||||||
regionSet[r] = struct{}{}
|
|
||||||
}
|
|
||||||
for r := range artifact.Amis {
|
|
||||||
if _, ok := regionSet[r]; !ok {
|
|
||||||
return fmt.Errorf("unknown region: %s", r)
|
|
||||||
}
|
|
||||||
|
|
||||||
delete(regionSet, r)
|
|
||||||
}
|
|
||||||
if len(regionSet) > 0 {
|
|
||||||
return fmt.Errorf("didn't copy to: %#v", regionSet)
|
|
||||||
}
|
|
||||||
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func checkBootEncrypted() builderT.TestCheckFunc {
|
|
||||||
return func(artifacts []packersdk.Artifact) error {
|
|
||||||
|
|
||||||
// Get the actual *Artifact pointer so we can access the AMIs directly
|
|
||||||
artifactRaw := artifacts[0]
|
|
||||||
artifact, ok := artifactRaw.(*common.Artifact)
|
|
||||||
if !ok {
|
|
||||||
return fmt.Errorf("unknown artifact: %#v", artifactRaw)
|
|
||||||
}
|
|
||||||
|
|
||||||
// describe the image, get block devices with a snapshot
|
|
||||||
ec2conn, _ := testEC2Conn()
|
|
||||||
imageResp, err := ec2conn.DescribeImages(&ec2.DescribeImagesInput{
|
|
||||||
ImageIds: []*string{aws.String(artifact.Amis["us-east-1"])},
|
|
||||||
})
|
|
||||||
|
|
||||||
if err != nil {
|
|
||||||
return fmt.Errorf("Error retrieving Image Attributes for AMI (%s) in AMI Encrypted Boot Test: %s", artifact, err)
|
|
||||||
}
|
|
||||||
|
|
||||||
image := imageResp.Images[0] // Only requested a single AMI ID
|
|
||||||
|
|
||||||
rootDeviceName := image.RootDeviceName
|
|
||||||
|
|
||||||
for _, bd := range image.BlockDeviceMappings {
|
|
||||||
if *bd.DeviceName == *rootDeviceName {
|
|
||||||
if *bd.Ebs.Encrypted != true {
|
|
||||||
return fmt.Errorf("volume not encrypted: %s", *bd.Ebs.SnapshotId)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestBuilderAcc_SessionManagerInterface(t *testing.T) {
|
|
||||||
builderT.Test(t, builderT.TestCase{
|
|
||||||
PreCheck: func() { testAccPreCheck(t) },
|
|
||||||
Builder: &Builder{},
|
|
||||||
Template: testBuilderAccSessionManagerInterface,
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
func testAccPreCheck(t *testing.T) {
|
|
||||||
}
|
|
||||||
|
|
||||||
func testAccSharingPreCheck(t *testing.T) {
|
|
||||||
if v := os.Getenv("TESTACC_AWS_ACCOUNT_ID"); v == "" {
|
|
||||||
t.Fatal(fmt.Sprintf("TESTACC_AWS_ACCOUNT_ID must be set for acceptance tests"))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func testEC2Conn() (*ec2.EC2, error) {
|
|
||||||
access := &common.AccessConfig{RawRegion: "us-east-1"}
|
|
||||||
session, err := access.Session()
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
|
|
||||||
return ec2.New(session), nil
|
|
||||||
}
|
|
||||||
|
|
||||||
const testBuilderAccBasic = `
|
|
||||||
{
|
|
||||||
"builders": [{
|
|
||||||
"type": "test",
|
|
||||||
"region": "us-east-1",
|
|
||||||
"instance_type": "m3.medium",
|
|
||||||
"source_ami": "ami-76b2a71e",
|
|
||||||
"ssh_username": "ubuntu",
|
|
||||||
"ami_name": "packer-test {{timestamp}}"
|
|
||||||
}]
|
|
||||||
}
|
|
||||||
`
|
|
||||||
|
|
||||||
const testBuilderAccRegionCopy = `
|
|
||||||
{
|
|
||||||
"builders": [{
|
|
||||||
"type": "test",
|
|
||||||
"region": "us-east-1",
|
|
||||||
"instance_type": "m3.medium",
|
|
||||||
"source_ami": "ami-76b2a71e",
|
|
||||||
"ssh_username": "ubuntu",
|
|
||||||
"ami_name": "packer-test {{timestamp}}",
|
|
||||||
"ami_regions": ["us-east-1", "us-west-2"]
|
|
||||||
}]
|
|
||||||
}
|
|
||||||
`
|
|
||||||
|
|
||||||
const testBuilderAccForceDeregister = `
|
|
||||||
{
|
|
||||||
"builders": [{
|
|
||||||
"type": "test",
|
|
||||||
"region": "us-east-1",
|
|
||||||
"instance_type": "m3.medium",
|
|
||||||
"source_ami": "ami-76b2a71e",
|
|
||||||
"ssh_username": "ubuntu",
|
|
||||||
"force_deregister": "%s",
|
|
||||||
"ami_name": "%s"
|
|
||||||
}]
|
|
||||||
}
|
|
||||||
`
|
|
||||||
|
|
||||||
const testBuilderAccForceDeleteSnapshot = `
|
|
||||||
{
|
|
||||||
"builders": [{
|
|
||||||
"type": "test",
|
|
||||||
"region": "us-east-1",
|
|
||||||
"instance_type": "m3.medium",
|
|
||||||
"source_ami": "ami-76b2a71e",
|
|
||||||
"ssh_username": "ubuntu",
|
|
||||||
"force_deregister": "%s",
|
|
||||||
"force_delete_snapshot": "%s",
|
|
||||||
"ami_name": "%s"
|
|
||||||
}]
|
|
||||||
}
|
|
||||||
`
|
|
||||||
|
|
||||||
const testBuilderAccSharing = `
|
|
||||||
{
|
|
||||||
"builders": [{
|
|
||||||
"type": "test",
|
|
||||||
"region": "us-east-1",
|
|
||||||
"instance_type": "m3.medium",
|
|
||||||
"source_ami": "ami-76b2a71e",
|
|
||||||
"ssh_username": "ubuntu",
|
|
||||||
"ami_name": "packer-test {{timestamp}}",
|
|
||||||
"ami_users":["%s"],
|
|
||||||
"ami_groups":["all"]
|
|
||||||
}]
|
|
||||||
}
|
|
||||||
`
|
|
||||||
|
|
||||||
const testBuilderAccEncrypted = `
|
|
||||||
{
|
|
||||||
"builders": [{
|
|
||||||
"type": "test",
|
|
||||||
"region": "us-east-1",
|
|
||||||
"instance_type": "m3.medium",
|
|
||||||
"source_ami":"ami-c15bebaa",
|
|
||||||
"ssh_username": "ubuntu",
|
|
||||||
"ami_name": "packer-enc-test {{timestamp}}",
|
|
||||||
"encrypt_boot": true
|
|
||||||
}]
|
|
||||||
}
|
|
||||||
`
|
|
||||||
|
|
||||||
const testBuilderAccSessionManagerInterface = `
|
|
||||||
{
|
|
||||||
"builders": [{
|
|
||||||
"type": "test",
|
|
||||||
"region": "us-east-1",
|
|
||||||
"instance_type": "m3.medium",
|
|
||||||
"source_ami_filter": {
|
|
||||||
"filters": {
|
|
||||||
"virtualization-type": "hvm",
|
|
||||||
"name": "ubuntu/images/*ubuntu-xenial-16.04-amd64-server-*",
|
|
||||||
"root-device-type": "ebs"
|
|
||||||
},
|
|
||||||
"owners": [
|
|
||||||
"099720109477"
|
|
||||||
],
|
|
||||||
"most_recent": true
|
|
||||||
},
|
|
||||||
"ssh_username": "ubuntu",
|
|
||||||
"ssh_interface": "session_manager",
|
|
||||||
"iam_instance_profile": "SSMInstanceProfile",
|
|
||||||
"ami_name": "packer-ssm-test-{{timestamp}}"
|
|
||||||
}]
|
|
||||||
}
|
|
||||||
`
|
|
||||||
|
|
||||||
func buildForceDeregisterConfig(val, name string) string {
|
|
||||||
return fmt.Sprintf(testBuilderAccForceDeregister, val, name)
|
|
||||||
}
|
|
||||||
|
|
||||||
func buildForceDeleteSnapshotConfig(val, name string) string {
|
|
||||||
return fmt.Sprintf(testBuilderAccForceDeleteSnapshot, val, val, name)
|
|
||||||
}
|
|
||||||
|
|
||||||
func buildSharingConfig(val string) string {
|
|
||||||
return fmt.Sprintf(testBuilderAccSharing, val)
|
|
||||||
}
|
|
||||||
@@ -1,166 +0,0 @@
|
|||||||
package ebs
|
|
||||||
|
|
||||||
import (
|
|
||||||
"testing"
|
|
||||||
|
|
||||||
packersdk "github.com/hashicorp/packer-plugin-sdk/packer"
|
|
||||||
)
|
|
||||||
|
|
||||||
func testConfig() map[string]interface{} {
|
|
||||||
return map[string]interface{}{
|
|
||||||
"access_key": "foo",
|
|
||||||
"secret_key": "bar",
|
|
||||||
"source_ami": "foo",
|
|
||||||
"instance_type": "foo",
|
|
||||||
"region": "us-east-1",
|
|
||||||
"ssh_username": "root",
|
|
||||||
"ami_name": "foo",
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestBuilder_ImplementsBuilder(t *testing.T) {
|
|
||||||
var raw interface{}
|
|
||||||
raw = &Builder{}
|
|
||||||
if _, ok := raw.(packersdk.Builder); !ok {
|
|
||||||
t.Fatalf("Builder should be a builder")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestBuilder_Prepare_BadType(t *testing.T) {
|
|
||||||
b := &Builder{}
|
|
||||||
c := map[string]interface{}{
|
|
||||||
"access_key": []string{},
|
|
||||||
}
|
|
||||||
|
|
||||||
_, warnings, err := b.Prepare(c)
|
|
||||||
if len(warnings) > 0 {
|
|
||||||
t.Fatalf("bad: %#v", warnings)
|
|
||||||
}
|
|
||||||
if err == nil {
|
|
||||||
t.Fatalf("prepare should fail")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestBuilderPrepare_AMIName(t *testing.T) {
|
|
||||||
var b Builder
|
|
||||||
config := testConfig()
|
|
||||||
|
|
||||||
// Test good
|
|
||||||
config["ami_name"] = "foo"
|
|
||||||
_, warnings, err := b.Prepare(config)
|
|
||||||
if len(warnings) > 0 {
|
|
||||||
t.Fatalf("bad: %#v", warnings)
|
|
||||||
}
|
|
||||||
if err != nil {
|
|
||||||
t.Fatalf("should not have error: %s", err)
|
|
||||||
}
|
|
||||||
|
|
||||||
// Test bad
|
|
||||||
config["ami_name"] = "foo {{"
|
|
||||||
b = Builder{}
|
|
||||||
_, warnings, err = b.Prepare(config)
|
|
||||||
if len(warnings) > 0 {
|
|
||||||
t.Fatalf("bad: %#v", warnings)
|
|
||||||
}
|
|
||||||
if err == nil {
|
|
||||||
t.Fatal("should have error")
|
|
||||||
}
|
|
||||||
|
|
||||||
// Test bad
|
|
||||||
delete(config, "ami_name")
|
|
||||||
b = Builder{}
|
|
||||||
_, warnings, err = b.Prepare(config)
|
|
||||||
if len(warnings) > 0 {
|
|
||||||
t.Fatalf("bad: %#v", warnings)
|
|
||||||
}
|
|
||||||
if err == nil {
|
|
||||||
t.Fatal("should have error")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestBuilderPrepare_InvalidKey(t *testing.T) {
|
|
||||||
var b Builder
|
|
||||||
config := testConfig()
|
|
||||||
|
|
||||||
// Add a random key
|
|
||||||
config["i_should_not_be_valid"] = true
|
|
||||||
_, warnings, err := b.Prepare(config)
|
|
||||||
if len(warnings) > 0 {
|
|
||||||
t.Fatalf("bad: %#v", warnings)
|
|
||||||
}
|
|
||||||
if err == nil {
|
|
||||||
t.Fatal("should have error")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestBuilderPrepare_InvalidShutdownBehavior(t *testing.T) {
|
|
||||||
var b Builder
|
|
||||||
config := testConfig()
|
|
||||||
|
|
||||||
// Test good
|
|
||||||
config["shutdown_behavior"] = "terminate"
|
|
||||||
_, warnings, err := b.Prepare(config)
|
|
||||||
if len(warnings) > 0 {
|
|
||||||
t.Fatalf("bad: %#v", warnings)
|
|
||||||
}
|
|
||||||
if err != nil {
|
|
||||||
t.Fatalf("should not have error: %s", err)
|
|
||||||
}
|
|
||||||
|
|
||||||
// Test good
|
|
||||||
config["shutdown_behavior"] = "stop"
|
|
||||||
_, warnings, err = b.Prepare(config)
|
|
||||||
if len(warnings) > 0 {
|
|
||||||
t.Fatalf("bad: %#v", warnings)
|
|
||||||
}
|
|
||||||
if err != nil {
|
|
||||||
t.Fatalf("should not have error: %s", err)
|
|
||||||
}
|
|
||||||
|
|
||||||
// Test bad
|
|
||||||
config["shutdown_behavior"] = "foobar"
|
|
||||||
_, warnings, err = b.Prepare(config)
|
|
||||||
if len(warnings) > 0 {
|
|
||||||
t.Fatalf("bad: %#v", warnings)
|
|
||||||
}
|
|
||||||
if err == nil {
|
|
||||||
t.Fatal("should have error")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestBuilderPrepare_ReturnGeneratedData(t *testing.T) {
|
|
||||||
var b Builder
|
|
||||||
config := testConfig()
|
|
||||||
|
|
||||||
generatedData, warnings, err := b.Prepare(config)
|
|
||||||
if len(warnings) > 0 {
|
|
||||||
t.Fatalf("bad: %#v", warnings)
|
|
||||||
}
|
|
||||||
if err != nil {
|
|
||||||
t.Fatalf("should not have error: %s", err)
|
|
||||||
}
|
|
||||||
if len(generatedData) == 0 {
|
|
||||||
t.Fatalf("Generated data should not be empty")
|
|
||||||
}
|
|
||||||
if len(generatedData) == 0 {
|
|
||||||
t.Fatalf("Generated data should not be empty")
|
|
||||||
}
|
|
||||||
if generatedData[0] != "SourceAMIName" {
|
|
||||||
t.Fatalf("Generated data should contain SourceAMIName")
|
|
||||||
}
|
|
||||||
if generatedData[1] != "BuildRegion" {
|
|
||||||
t.Fatalf("Generated data should contain BuildRegion")
|
|
||||||
}
|
|
||||||
if generatedData[2] != "SourceAMI" {
|
|
||||||
t.Fatalf("Generated data should contain SourceAMI")
|
|
||||||
}
|
|
||||||
if generatedData[3] != "SourceAMICreationDate" {
|
|
||||||
t.Fatalf("Generated data should contain SourceAMICreationDate")
|
|
||||||
}
|
|
||||||
if generatedData[4] != "SourceAMIOwner" {
|
|
||||||
t.Fatalf("Generated data should contain SourceAMIOwner")
|
|
||||||
}
|
|
||||||
if generatedData[5] != "SourceAMIOwnerName" {
|
|
||||||
t.Fatalf("Generated data should contain SourceAMIOwnerName")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,137 +0,0 @@
|
|||||||
package ebs
|
|
||||||
|
|
||||||
import (
|
|
||||||
"encoding/json"
|
|
||||||
"fmt"
|
|
||||||
"testing"
|
|
||||||
|
|
||||||
"github.com/aws/aws-sdk-go/aws"
|
|
||||||
"github.com/aws/aws-sdk-go/service/ec2"
|
|
||||||
builderT "github.com/hashicorp/packer-plugin-sdk/acctest"
|
|
||||||
packersdk "github.com/hashicorp/packer-plugin-sdk/packer"
|
|
||||||
"github.com/hashicorp/packer/builder/amazon/common"
|
|
||||||
)
|
|
||||||
|
|
||||||
type TFBuilder struct {
|
|
||||||
Type string `json:"type"`
|
|
||||||
Region string `json:"region"`
|
|
||||||
SourceAmi string `json:"source_ami"`
|
|
||||||
InstanceType string `json:"instance_type"`
|
|
||||||
SshUsername string `json:"ssh_username"`
|
|
||||||
AmiName string `json:"ami_name"`
|
|
||||||
Tags map[string]string `json:"tags"`
|
|
||||||
SnapshotTags map[string]string `json:"snapshot_tags"`
|
|
||||||
}
|
|
||||||
|
|
||||||
type TFConfig struct {
|
|
||||||
Builders []TFBuilder `json:"builders"`
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestBuilderTagsAcc_basic(t *testing.T) {
|
|
||||||
builderT.Test(t, builderT.TestCase{
|
|
||||||
PreCheck: func() { testAccPreCheck(t) },
|
|
||||||
Builder: &Builder{},
|
|
||||||
Template: testBuilderTagsAccBasic,
|
|
||||||
Check: checkTags(),
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
func checkTags() builderT.TestCheckFunc {
|
|
||||||
return func(artifacts []packersdk.Artifact) error {
|
|
||||||
if len(artifacts) > 1 {
|
|
||||||
return fmt.Errorf("more than 1 artifact")
|
|
||||||
}
|
|
||||||
|
|
||||||
config := TFConfig{}
|
|
||||||
json.Unmarshal([]byte(testBuilderTagsAccBasic), &config)
|
|
||||||
tags := config.Builders[0].Tags
|
|
||||||
snapshotTags := config.Builders[0].SnapshotTags
|
|
||||||
|
|
||||||
// Get the actual *Artifact pointer so we can access the AMIs directly
|
|
||||||
artifactRaw := artifacts[0]
|
|
||||||
artifact, ok := artifactRaw.(*common.Artifact)
|
|
||||||
if !ok {
|
|
||||||
return fmt.Errorf("unknown artifact: %#v", artifactRaw)
|
|
||||||
}
|
|
||||||
|
|
||||||
// Describe the image, get block devices with a snapshot
|
|
||||||
ec2conn, _ := testEC2Conn()
|
|
||||||
imageResp, err := ec2conn.DescribeImages(&ec2.DescribeImagesInput{
|
|
||||||
ImageIds: []*string{aws.String(artifact.Amis["us-east-1"])},
|
|
||||||
})
|
|
||||||
|
|
||||||
if err != nil {
|
|
||||||
return fmt.Errorf("Error retrieving details for AMI Artifact (%#v) in Tags Test: %s", artifact, err)
|
|
||||||
}
|
|
||||||
|
|
||||||
if len(imageResp.Images) == 0 {
|
|
||||||
return fmt.Errorf("No images found for AMI Artifact (%#v) in Tags Test: %s", artifact, err)
|
|
||||||
}
|
|
||||||
|
|
||||||
image := imageResp.Images[0]
|
|
||||||
|
|
||||||
// Check only those with a Snapshot ID, i.e. not Ephemeral
|
|
||||||
var snapshots []*string
|
|
||||||
for _, device := range image.BlockDeviceMappings {
|
|
||||||
if device.Ebs != nil && device.Ebs.SnapshotId != nil {
|
|
||||||
snapshots = append(snapshots, device.Ebs.SnapshotId)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Grab matching snapshot info
|
|
||||||
resp, err := ec2conn.DescribeSnapshots(&ec2.DescribeSnapshotsInput{
|
|
||||||
SnapshotIds: snapshots,
|
|
||||||
})
|
|
||||||
|
|
||||||
if err != nil {
|
|
||||||
return fmt.Errorf("Error retrieving Snapshots for AMI Artifact (%#v) in Tags Test: %s", artifact, err)
|
|
||||||
}
|
|
||||||
|
|
||||||
if len(resp.Snapshots) == 0 {
|
|
||||||
return fmt.Errorf("No Snapshots found for AMI Artifact (%#v) in Tags Test", artifact)
|
|
||||||
}
|
|
||||||
|
|
||||||
// Grab the snapshots, check the tags
|
|
||||||
for _, s := range resp.Snapshots {
|
|
||||||
expected := len(tags)
|
|
||||||
for _, t := range s.Tags {
|
|
||||||
for key, value := range tags {
|
|
||||||
if val, ok := snapshotTags[key]; ok && val == *t.Value {
|
|
||||||
expected--
|
|
||||||
} else if key == *t.Key && value == *t.Value {
|
|
||||||
expected--
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if expected > 0 {
|
|
||||||
return fmt.Errorf("Not all tags found")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
const testBuilderTagsAccBasic = `
|
|
||||||
{
|
|
||||||
"builders": [
|
|
||||||
{
|
|
||||||
"type": "test",
|
|
||||||
"region": "us-east-1",
|
|
||||||
"source_ami": "ami-9eaa1cf6",
|
|
||||||
"instance_type": "t2.micro",
|
|
||||||
"ssh_username": "ubuntu",
|
|
||||||
"ami_name": "packer-tags-testing-{{timestamp}}",
|
|
||||||
"tags": {
|
|
||||||
"OS_Version": "Ubuntu",
|
|
||||||
"Release": "Latest",
|
|
||||||
"Name": "Bleep"
|
|
||||||
},
|
|
||||||
"snapshot_tags": {
|
|
||||||
"Name": "Foobar"
|
|
||||||
}
|
|
||||||
}
|
|
||||||
]
|
|
||||||
}
|
|
||||||
`
|
|
||||||
@@ -1,110 +0,0 @@
|
|||||||
package ebssurrogate
|
|
||||||
|
|
||||||
import (
|
|
||||||
"testing"
|
|
||||||
|
|
||||||
"github.com/hashicorp/packer/builder/amazon/common"
|
|
||||||
|
|
||||||
packersdk "github.com/hashicorp/packer-plugin-sdk/packer"
|
|
||||||
)
|
|
||||||
|
|
||||||
func testConfig() map[string]interface{} {
|
|
||||||
return map[string]interface{}{
|
|
||||||
"access_key": "foo",
|
|
||||||
"secret_key": "bar",
|
|
||||||
"source_ami": "foo",
|
|
||||||
"instance_type": "foo",
|
|
||||||
"region": "us-east-1",
|
|
||||||
"ssh_username": "root",
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestBuilder_ImplementsBuilder(t *testing.T) {
|
|
||||||
var raw interface{}
|
|
||||||
raw = &Builder{}
|
|
||||||
if _, ok := raw.(packersdk.Builder); !ok {
|
|
||||||
t.Fatal("Builder should be a builder")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestBuilder_Prepare_BadType(t *testing.T) {
|
|
||||||
b := &Builder{}
|
|
||||||
c := map[string]interface{}{
|
|
||||||
"access_key": []string{},
|
|
||||||
}
|
|
||||||
|
|
||||||
_, warnings, err := b.Prepare(c)
|
|
||||||
if len(warnings) > 0 {
|
|
||||||
t.Fatalf("bad: %#v", warnings)
|
|
||||||
}
|
|
||||||
if err == nil {
|
|
||||||
t.Fatal("prepare should fail")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestBuilderPrepare_InvalidKey(t *testing.T) {
|
|
||||||
var b Builder
|
|
||||||
config := testConfig()
|
|
||||||
|
|
||||||
// Add a random key
|
|
||||||
config["i_should_not_be_valid"] = true
|
|
||||||
_, warnings, err := b.Prepare(config)
|
|
||||||
if len(warnings) > 0 {
|
|
||||||
t.Fatalf("bad: %#v", warnings)
|
|
||||||
}
|
|
||||||
if err == nil {
|
|
||||||
t.Fatal("should have error")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestBuilderPrepare_ReturnGeneratedData(t *testing.T) {
|
|
||||||
var b Builder
|
|
||||||
// Basic configuration
|
|
||||||
b.config.RootDevice = RootBlockDevice{
|
|
||||||
SourceDeviceName: "device name",
|
|
||||||
DeviceName: "device name",
|
|
||||||
}
|
|
||||||
b.config.LaunchMappings = BlockDevices{
|
|
||||||
BlockDevice{
|
|
||||||
BlockDevice: common.BlockDevice{
|
|
||||||
DeviceName: "device name",
|
|
||||||
},
|
|
||||||
OmitFromArtifact: false,
|
|
||||||
},
|
|
||||||
}
|
|
||||||
b.config.AMIVirtType = "type"
|
|
||||||
config := testConfig()
|
|
||||||
config["ami_name"] = "name"
|
|
||||||
|
|
||||||
generatedData, warnings, err := b.Prepare(config)
|
|
||||||
if len(warnings) > 0 {
|
|
||||||
t.Fatalf("bad: %#v", warnings)
|
|
||||||
}
|
|
||||||
if err != nil {
|
|
||||||
t.Fatalf("should not have error: %s", err)
|
|
||||||
}
|
|
||||||
if len(generatedData) == 0 {
|
|
||||||
t.Fatalf("Generated data should not be empty")
|
|
||||||
}
|
|
||||||
if len(generatedData) == 0 {
|
|
||||||
t.Fatalf("Generated data should not be empty")
|
|
||||||
}
|
|
||||||
if generatedData[0] != "SourceAMIName" {
|
|
||||||
t.Fatalf("Generated data should contain SourceAMIName")
|
|
||||||
}
|
|
||||||
if generatedData[1] != "BuildRegion" {
|
|
||||||
t.Fatalf("Generated data should contain BuildRegion")
|
|
||||||
}
|
|
||||||
if generatedData[2] != "SourceAMI" {
|
|
||||||
t.Fatalf("Generated data should contain SourceAMI")
|
|
||||||
}
|
|
||||||
if generatedData[3] != "SourceAMICreationDate" {
|
|
||||||
t.Fatalf("Generated data should contain SourceAMICreationDate")
|
|
||||||
}
|
|
||||||
if generatedData[4] != "SourceAMIOwner" {
|
|
||||||
t.Fatalf("Generated data should contain SourceAMIOwner")
|
|
||||||
}
|
|
||||||
if generatedData[5] != "SourceAMIOwnerName" {
|
|
||||||
t.Fatalf("Generated data should contain SourceAMIOwnerName")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,249 +0,0 @@
|
|||||||
package ebssurrogate
|
|
||||||
|
|
||||||
import (
|
|
||||||
"reflect"
|
|
||||||
"sort"
|
|
||||||
"testing"
|
|
||||||
|
|
||||||
"github.com/aws/aws-sdk-go/aws"
|
|
||||||
"github.com/aws/aws-sdk-go/service/ec2"
|
|
||||||
"github.com/hashicorp/packer/builder/amazon/common"
|
|
||||||
)
|
|
||||||
|
|
||||||
const sourceDeviceName = "/dev/xvdf"
|
|
||||||
const rootDeviceName = "/dev/xvda"
|
|
||||||
|
|
||||||
func newStepRegisterAMI(amiDevices, launchDevices []*ec2.BlockDeviceMapping) *StepRegisterAMI {
|
|
||||||
return &StepRegisterAMI{
|
|
||||||
RootDevice: RootBlockDevice{
|
|
||||||
SourceDeviceName: sourceDeviceName,
|
|
||||||
DeviceName: rootDeviceName,
|
|
||||||
DeleteOnTermination: true,
|
|
||||||
VolumeType: "ebs",
|
|
||||||
VolumeSize: 10,
|
|
||||||
},
|
|
||||||
AMIDevices: amiDevices,
|
|
||||||
LaunchDevices: launchDevices,
|
|
||||||
PollingConfig: new(common.AWSPollingConfig),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func sorted(devices []*ec2.BlockDeviceMapping) []*ec2.BlockDeviceMapping {
|
|
||||||
sort.SliceStable(devices, func(i, j int) bool {
|
|
||||||
return *devices[i].DeviceName < *devices[j].DeviceName
|
|
||||||
})
|
|
||||||
return devices
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestStepRegisterAmi_combineDevices(t *testing.T) {
|
|
||||||
cases := []struct {
|
|
||||||
snapshotIds map[string]string
|
|
||||||
amiDevices []*ec2.BlockDeviceMapping
|
|
||||||
launchDevices []*ec2.BlockDeviceMapping
|
|
||||||
allDevices []*ec2.BlockDeviceMapping
|
|
||||||
}{
|
|
||||||
{
|
|
||||||
snapshotIds: map[string]string{},
|
|
||||||
amiDevices: []*ec2.BlockDeviceMapping{},
|
|
||||||
launchDevices: []*ec2.BlockDeviceMapping{},
|
|
||||||
allDevices: []*ec2.BlockDeviceMapping{},
|
|
||||||
},
|
|
||||||
{
|
|
||||||
snapshotIds: map[string]string{},
|
|
||||||
amiDevices: []*ec2.BlockDeviceMapping{},
|
|
||||||
launchDevices: []*ec2.BlockDeviceMapping{
|
|
||||||
{
|
|
||||||
Ebs: &ec2.EbsBlockDevice{},
|
|
||||||
DeviceName: aws.String(sourceDeviceName),
|
|
||||||
},
|
|
||||||
},
|
|
||||||
allDevices: []*ec2.BlockDeviceMapping{
|
|
||||||
{
|
|
||||||
Ebs: &ec2.EbsBlockDevice{},
|
|
||||||
DeviceName: aws.String(rootDeviceName),
|
|
||||||
},
|
|
||||||
},
|
|
||||||
},
|
|
||||||
{
|
|
||||||
// Minimal single device
|
|
||||||
snapshotIds: map[string]string{
|
|
||||||
sourceDeviceName: "snap-0123456789abcdef1",
|
|
||||||
},
|
|
||||||
amiDevices: []*ec2.BlockDeviceMapping{},
|
|
||||||
launchDevices: []*ec2.BlockDeviceMapping{
|
|
||||||
{
|
|
||||||
Ebs: &ec2.EbsBlockDevice{},
|
|
||||||
DeviceName: aws.String(sourceDeviceName),
|
|
||||||
},
|
|
||||||
},
|
|
||||||
allDevices: []*ec2.BlockDeviceMapping{
|
|
||||||
{
|
|
||||||
Ebs: &ec2.EbsBlockDevice{
|
|
||||||
SnapshotId: aws.String("snap-0123456789abcdef1"),
|
|
||||||
},
|
|
||||||
DeviceName: aws.String(rootDeviceName),
|
|
||||||
},
|
|
||||||
},
|
|
||||||
},
|
|
||||||
{
|
|
||||||
// Single launch device with AMI device
|
|
||||||
snapshotIds: map[string]string{
|
|
||||||
sourceDeviceName: "snap-0123456789abcdef1",
|
|
||||||
},
|
|
||||||
amiDevices: []*ec2.BlockDeviceMapping{
|
|
||||||
{
|
|
||||||
Ebs: &ec2.EbsBlockDevice{},
|
|
||||||
DeviceName: aws.String("/dev/xvdg"),
|
|
||||||
},
|
|
||||||
},
|
|
||||||
launchDevices: []*ec2.BlockDeviceMapping{
|
|
||||||
{
|
|
||||||
Ebs: &ec2.EbsBlockDevice{},
|
|
||||||
DeviceName: aws.String(sourceDeviceName),
|
|
||||||
},
|
|
||||||
},
|
|
||||||
allDevices: []*ec2.BlockDeviceMapping{
|
|
||||||
{
|
|
||||||
Ebs: &ec2.EbsBlockDevice{
|
|
||||||
SnapshotId: aws.String("snap-0123456789abcdef1"),
|
|
||||||
},
|
|
||||||
DeviceName: aws.String(rootDeviceName),
|
|
||||||
},
|
|
||||||
{
|
|
||||||
Ebs: &ec2.EbsBlockDevice{},
|
|
||||||
DeviceName: aws.String("/dev/xvdg"),
|
|
||||||
},
|
|
||||||
},
|
|
||||||
},
|
|
||||||
{
|
|
||||||
// Multiple launch devices
|
|
||||||
snapshotIds: map[string]string{
|
|
||||||
sourceDeviceName: "snap-0123456789abcdef1",
|
|
||||||
"/dev/xvdg": "snap-0123456789abcdef2",
|
|
||||||
},
|
|
||||||
amiDevices: []*ec2.BlockDeviceMapping{},
|
|
||||||
launchDevices: []*ec2.BlockDeviceMapping{
|
|
||||||
{
|
|
||||||
Ebs: &ec2.EbsBlockDevice{},
|
|
||||||
DeviceName: aws.String(sourceDeviceName),
|
|
||||||
},
|
|
||||||
{
|
|
||||||
Ebs: &ec2.EbsBlockDevice{},
|
|
||||||
DeviceName: aws.String("/dev/xvdg"),
|
|
||||||
},
|
|
||||||
},
|
|
||||||
allDevices: []*ec2.BlockDeviceMapping{
|
|
||||||
{
|
|
||||||
Ebs: &ec2.EbsBlockDevice{
|
|
||||||
SnapshotId: aws.String("snap-0123456789abcdef1"),
|
|
||||||
},
|
|
||||||
DeviceName: aws.String(rootDeviceName),
|
|
||||||
},
|
|
||||||
{
|
|
||||||
Ebs: &ec2.EbsBlockDevice{
|
|
||||||
SnapshotId: aws.String("snap-0123456789abcdef2"),
|
|
||||||
},
|
|
||||||
DeviceName: aws.String("/dev/xvdg"),
|
|
||||||
},
|
|
||||||
},
|
|
||||||
},
|
|
||||||
{
|
|
||||||
// Multiple launch devices with encryption
|
|
||||||
snapshotIds: map[string]string{
|
|
||||||
sourceDeviceName: "snap-0123456789abcdef1",
|
|
||||||
"/dev/xvdg": "snap-0123456789abcdef2",
|
|
||||||
},
|
|
||||||
amiDevices: []*ec2.BlockDeviceMapping{},
|
|
||||||
launchDevices: []*ec2.BlockDeviceMapping{
|
|
||||||
{
|
|
||||||
Ebs: &ec2.EbsBlockDevice{
|
|
||||||
Encrypted: aws.Bool(true),
|
|
||||||
},
|
|
||||||
DeviceName: aws.String(sourceDeviceName),
|
|
||||||
},
|
|
||||||
{
|
|
||||||
Ebs: &ec2.EbsBlockDevice{
|
|
||||||
Encrypted: aws.Bool(true),
|
|
||||||
},
|
|
||||||
DeviceName: aws.String("/dev/xvdg"),
|
|
||||||
},
|
|
||||||
},
|
|
||||||
allDevices: []*ec2.BlockDeviceMapping{
|
|
||||||
{
|
|
||||||
Ebs: &ec2.EbsBlockDevice{
|
|
||||||
SnapshotId: aws.String("snap-0123456789abcdef1"),
|
|
||||||
// Encrypted: true stripped from snapshotted devices
|
|
||||||
},
|
|
||||||
DeviceName: aws.String(rootDeviceName),
|
|
||||||
},
|
|
||||||
{
|
|
||||||
Ebs: &ec2.EbsBlockDevice{
|
|
||||||
SnapshotId: aws.String("snap-0123456789abcdef2"),
|
|
||||||
},
|
|
||||||
DeviceName: aws.String("/dev/xvdg"),
|
|
||||||
},
|
|
||||||
},
|
|
||||||
},
|
|
||||||
{
|
|
||||||
// Multiple launch devices and AMI devices with encryption
|
|
||||||
snapshotIds: map[string]string{
|
|
||||||
sourceDeviceName: "snap-0123456789abcdef1",
|
|
||||||
"/dev/xvdg": "snap-0123456789abcdef2",
|
|
||||||
},
|
|
||||||
amiDevices: []*ec2.BlockDeviceMapping{
|
|
||||||
{
|
|
||||||
Ebs: &ec2.EbsBlockDevice{
|
|
||||||
Encrypted: aws.Bool(true),
|
|
||||||
KmsKeyId: aws.String("keyId"),
|
|
||||||
},
|
|
||||||
// Source device name can be used in AMI devices
|
|
||||||
// since launch device of same name gets renamed
|
|
||||||
// to root device name
|
|
||||||
DeviceName: aws.String(sourceDeviceName),
|
|
||||||
},
|
|
||||||
},
|
|
||||||
launchDevices: []*ec2.BlockDeviceMapping{
|
|
||||||
{
|
|
||||||
Ebs: &ec2.EbsBlockDevice{
|
|
||||||
Encrypted: aws.Bool(true),
|
|
||||||
},
|
|
||||||
DeviceName: aws.String(sourceDeviceName),
|
|
||||||
},
|
|
||||||
{
|
|
||||||
Ebs: &ec2.EbsBlockDevice{
|
|
||||||
Encrypted: aws.Bool(true),
|
|
||||||
},
|
|
||||||
DeviceName: aws.String("/dev/xvdg"),
|
|
||||||
},
|
|
||||||
},
|
|
||||||
allDevices: []*ec2.BlockDeviceMapping{
|
|
||||||
{
|
|
||||||
Ebs: &ec2.EbsBlockDevice{
|
|
||||||
Encrypted: aws.Bool(true),
|
|
||||||
KmsKeyId: aws.String("keyId"),
|
|
||||||
},
|
|
||||||
DeviceName: aws.String(sourceDeviceName),
|
|
||||||
},
|
|
||||||
{
|
|
||||||
Ebs: &ec2.EbsBlockDevice{
|
|
||||||
SnapshotId: aws.String("snap-0123456789abcdef1"),
|
|
||||||
},
|
|
||||||
DeviceName: aws.String(rootDeviceName),
|
|
||||||
},
|
|
||||||
{
|
|
||||||
Ebs: &ec2.EbsBlockDevice{
|
|
||||||
SnapshotId: aws.String("snap-0123456789abcdef2"),
|
|
||||||
},
|
|
||||||
DeviceName: aws.String("/dev/xvdg"),
|
|
||||||
},
|
|
||||||
},
|
|
||||||
},
|
|
||||||
}
|
|
||||||
for _, tc := range cases {
|
|
||||||
stepRegisterAmi := newStepRegisterAMI(tc.amiDevices, tc.launchDevices)
|
|
||||||
allDevices := stepRegisterAmi.combineDevices(tc.snapshotIds)
|
|
||||||
if !reflect.DeepEqual(sorted(allDevices), sorted(tc.allDevices)) {
|
|
||||||
t.Fatalf("Unexpected output from combineDevices")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,29 +0,0 @@
|
|||||||
package ebsvolume
|
|
||||||
|
|
||||||
import "testing"
|
|
||||||
|
|
||||||
func TestArtifactState(t *testing.T) {
|
|
||||||
expectedData := "this is the data"
|
|
||||||
artifact := &Artifact{
|
|
||||||
StateData: map[string]interface{}{"state_data": expectedData},
|
|
||||||
}
|
|
||||||
|
|
||||||
// Valid state
|
|
||||||
result := artifact.State("state_data")
|
|
||||||
if result != expectedData {
|
|
||||||
t.Fatalf("Bad: State data was %s instead of %s", result, expectedData)
|
|
||||||
}
|
|
||||||
|
|
||||||
// Invalid state
|
|
||||||
result = artifact.State("invalid_key")
|
|
||||||
if result != nil {
|
|
||||||
t.Fatalf("Bad: State should be nil for invalid state data name")
|
|
||||||
}
|
|
||||||
|
|
||||||
// Nil StateData should not fail and should return nil
|
|
||||||
artifact = &Artifact{}
|
|
||||||
result = artifact.State("key")
|
|
||||||
if result != nil {
|
|
||||||
t.Fatalf("Bad: State should be nil for nil StateData")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,166 +0,0 @@
|
|||||||
package ebsvolume
|
|
||||||
|
|
||||||
import (
|
|
||||||
"testing"
|
|
||||||
|
|
||||||
packersdk "github.com/hashicorp/packer-plugin-sdk/packer"
|
|
||||||
)
|
|
||||||
|
|
||||||
func testConfig() map[string]interface{} {
|
|
||||||
return map[string]interface{}{
|
|
||||||
"access_key": "foo",
|
|
||||||
"secret_key": "bar",
|
|
||||||
"source_ami": "foo",
|
|
||||||
"instance_type": "foo",
|
|
||||||
"region": "us-east-1",
|
|
||||||
"ssh_username": "root",
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestBuilder_ImplementsBuilder(t *testing.T) {
|
|
||||||
var raw interface{}
|
|
||||||
raw = &Builder{}
|
|
||||||
if _, ok := raw.(packersdk.Builder); !ok {
|
|
||||||
t.Fatalf("Builder should be a builder")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestBuilder_Prepare_BadType(t *testing.T) {
|
|
||||||
b := &Builder{}
|
|
||||||
c := map[string]interface{}{
|
|
||||||
"access_key": []string{},
|
|
||||||
}
|
|
||||||
|
|
||||||
_, warnings, err := b.Prepare(c)
|
|
||||||
if len(warnings) > 0 {
|
|
||||||
t.Fatalf("bad: %#v", warnings)
|
|
||||||
}
|
|
||||||
if err == nil {
|
|
||||||
t.Fatalf("prepare should fail")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestBuilderPrepare_InvalidKey(t *testing.T) {
|
|
||||||
var b Builder
|
|
||||||
config := testConfig()
|
|
||||||
|
|
||||||
// Add a random key
|
|
||||||
config["i_should_not_be_valid"] = true
|
|
||||||
_, warnings, err := b.Prepare(config)
|
|
||||||
if len(warnings) > 0 {
|
|
||||||
t.Fatalf("bad: %#v", warnings)
|
|
||||||
}
|
|
||||||
if err == nil {
|
|
||||||
t.Fatal("should have error")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestBuilderPrepare_InvalidShutdownBehavior(t *testing.T) {
|
|
||||||
var b Builder
|
|
||||||
config := testConfig()
|
|
||||||
|
|
||||||
// Test good
|
|
||||||
config["shutdown_behavior"] = "terminate"
|
|
||||||
_, warnings, err := b.Prepare(config)
|
|
||||||
if len(warnings) > 0 {
|
|
||||||
t.Fatalf("bad: %#v", warnings)
|
|
||||||
}
|
|
||||||
if err != nil {
|
|
||||||
t.Fatalf("should not have error: %s", err)
|
|
||||||
}
|
|
||||||
|
|
||||||
// Test good
|
|
||||||
config["shutdown_behavior"] = "stop"
|
|
||||||
_, warnings, err = b.Prepare(config)
|
|
||||||
if len(warnings) > 0 {
|
|
||||||
t.Fatalf("bad: %#v", warnings)
|
|
||||||
}
|
|
||||||
if err != nil {
|
|
||||||
t.Fatalf("should not have error: %s", err)
|
|
||||||
}
|
|
||||||
|
|
||||||
// Test bad
|
|
||||||
config["shutdown_behavior"] = "foobar"
|
|
||||||
_, warnings, err = b.Prepare(config)
|
|
||||||
if len(warnings) > 0 {
|
|
||||||
t.Fatalf("bad: %#v", warnings)
|
|
||||||
}
|
|
||||||
if err == nil {
|
|
||||||
t.Fatal("should have error")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestBuilderPrepare_ReturnGeneratedData(t *testing.T) {
|
|
||||||
var b Builder
|
|
||||||
config := testConfig()
|
|
||||||
|
|
||||||
generatedData, warnings, err := b.Prepare(config)
|
|
||||||
if len(warnings) > 0 {
|
|
||||||
t.Fatalf("bad: %#v", warnings)
|
|
||||||
}
|
|
||||||
if err != nil {
|
|
||||||
t.Fatalf("should not have error: %s", err)
|
|
||||||
}
|
|
||||||
if len(generatedData) == 0 {
|
|
||||||
t.Fatalf("Generated data should not be empty")
|
|
||||||
}
|
|
||||||
if generatedData[0] != "SourceAMIName" {
|
|
||||||
t.Fatalf("Generated data should contain SourceAMIName")
|
|
||||||
}
|
|
||||||
if generatedData[1] != "BuildRegion" {
|
|
||||||
t.Fatalf("Generated data should contain BuildRegion")
|
|
||||||
}
|
|
||||||
if generatedData[2] != "SourceAMI" {
|
|
||||||
t.Fatalf("Generated data should contain SourceAMI")
|
|
||||||
}
|
|
||||||
if generatedData[3] != "SourceAMICreationDate" {
|
|
||||||
t.Fatalf("Generated data should contain SourceAMICreationDate")
|
|
||||||
}
|
|
||||||
if generatedData[4] != "SourceAMIOwner" {
|
|
||||||
t.Fatalf("Generated data should contain SourceAMIOwner")
|
|
||||||
}
|
|
||||||
if generatedData[5] != "SourceAMIOwnerName" {
|
|
||||||
t.Fatalf("Generated data should contain SourceAMIOwnerName")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestBuidler_ConfigBlockdevicemapping(t *testing.T) {
|
|
||||||
var b Builder
|
|
||||||
config := testConfig()
|
|
||||||
|
|
||||||
//Set some snapshot settings
|
|
||||||
config["ebs_volumes"] = []map[string]interface{}{
|
|
||||||
{
|
|
||||||
"device_name": "/dev/xvdb",
|
|
||||||
"volume_size": "32",
|
|
||||||
"delete_on_termination": true,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"device_name": "/dev/xvdc",
|
|
||||||
"volume_size": "32",
|
|
||||||
"delete_on_termination": true,
|
|
||||||
"snapshot_tags": map[string]string{
|
|
||||||
"Test_Tag": "tag_value",
|
|
||||||
"another tag": "another value",
|
|
||||||
},
|
|
||||||
"snapshot_users": []string{
|
|
||||||
"123", "456",
|
|
||||||
},
|
|
||||||
},
|
|
||||||
}
|
|
||||||
|
|
||||||
generatedData, warnings, err := b.Prepare(config)
|
|
||||||
|
|
||||||
if len(warnings) > 0 {
|
|
||||||
t.Fatalf("bad: %#v", warnings)
|
|
||||||
}
|
|
||||||
if err != nil {
|
|
||||||
t.Fatalf("should not have error: %s", err)
|
|
||||||
}
|
|
||||||
if len(generatedData) == 0 {
|
|
||||||
t.Fatalf("Generated data should not be empty")
|
|
||||||
}
|
|
||||||
|
|
||||||
t.Logf("Test gen %+v", b.config.VolumeMappings)
|
|
||||||
|
|
||||||
}
|
|
||||||
@@ -1,170 +0,0 @@
|
|||||||
package ebsvolume
|
|
||||||
|
|
||||||
import (
|
|
||||||
"bytes"
|
|
||||||
"context"
|
|
||||||
"fmt"
|
|
||||||
"testing"
|
|
||||||
|
|
||||||
"github.com/aws/aws-sdk-go/aws"
|
|
||||||
"github.com/aws/aws-sdk-go/aws/request"
|
|
||||||
|
|
||||||
//"github.com/aws/aws-sdk-go/service/ec2"
|
|
||||||
"github.com/aws/aws-sdk-go/service/ec2"
|
|
||||||
"github.com/aws/aws-sdk-go/service/ec2/ec2iface"
|
|
||||||
"github.com/hashicorp/packer-plugin-sdk/multistep"
|
|
||||||
"github.com/hashicorp/packer-plugin-sdk/packer"
|
|
||||||
"github.com/hashicorp/packer/builder/amazon/common"
|
|
||||||
)
|
|
||||||
|
|
||||||
// Define a mock struct to be used in unit tests for common aws steps.
|
|
||||||
type mockEC2Conn struct {
|
|
||||||
ec2iface.EC2API
|
|
||||||
Config *aws.Config
|
|
||||||
}
|
|
||||||
|
|
||||||
func (m *mockEC2Conn) CreateSnapshot(input *ec2.CreateSnapshotInput) (*ec2.Snapshot, error) {
|
|
||||||
snap := &ec2.Snapshot{
|
|
||||||
// This isn't typical amazon format, but injecting the volume id into
|
|
||||||
// this field lets us verify that the right volume was snapshotted with
|
|
||||||
// a simple string comparison
|
|
||||||
SnapshotId: aws.String(fmt.Sprintf("snap-of-%s", *input.VolumeId)),
|
|
||||||
}
|
|
||||||
|
|
||||||
return snap, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func (m *mockEC2Conn) WaitUntilSnapshotCompletedWithContext(aws.Context, *ec2.DescribeSnapshotsInput, ...request.WaiterOption) error {
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func getMockConn(config *common.AccessConfig, target string) (ec2iface.EC2API, error) {
|
|
||||||
mockConn := &mockEC2Conn{
|
|
||||||
Config: aws.NewConfig(),
|
|
||||||
}
|
|
||||||
return mockConn, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// Create statebag for running test
|
|
||||||
func tState(t *testing.T) multistep.StateBag {
|
|
||||||
state := new(multistep.BasicStateBag)
|
|
||||||
state.Put("ui", &packer.BasicUi{
|
|
||||||
Reader: new(bytes.Buffer),
|
|
||||||
Writer: new(bytes.Buffer),
|
|
||||||
})
|
|
||||||
// state.Put("amis", map[string]string{"us-east-1": "ami-12345"})
|
|
||||||
// state.Put("snapshots", map[string][]string{"us-east-1": {"snap-0012345"}})
|
|
||||||
conn, _ := getMockConn(&common.AccessConfig{}, "us-east-2")
|
|
||||||
|
|
||||||
state.Put("ec2", conn)
|
|
||||||
// Store a fake instance that contains a block device that matches the
|
|
||||||
// volumes defined in the config above
|
|
||||||
state.Put("instance", &ec2.Instance{
|
|
||||||
InstanceId: aws.String("instance-id"),
|
|
||||||
BlockDeviceMappings: []*ec2.InstanceBlockDeviceMapping{
|
|
||||||
{
|
|
||||||
DeviceName: aws.String("/dev/xvda"),
|
|
||||||
Ebs: &ec2.EbsInstanceBlockDevice{
|
|
||||||
VolumeId: aws.String("vol-1234"),
|
|
||||||
},
|
|
||||||
},
|
|
||||||
{
|
|
||||||
DeviceName: aws.String("/dev/xvdb"),
|
|
||||||
Ebs: &ec2.EbsInstanceBlockDevice{
|
|
||||||
VolumeId: aws.String("vol-5678"),
|
|
||||||
},
|
|
||||||
},
|
|
||||||
},
|
|
||||||
})
|
|
||||||
return state
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestStepSnapshot_run_simple(t *testing.T) {
|
|
||||||
var b Builder
|
|
||||||
config := testConfig() //from builder_test
|
|
||||||
|
|
||||||
//Set some snapshot settings
|
|
||||||
config["ebs_volumes"] = []map[string]interface{}{
|
|
||||||
{
|
|
||||||
"device_name": "/dev/xvdb",
|
|
||||||
"volume_size": "32",
|
|
||||||
"delete_on_termination": true,
|
|
||||||
"snapshot_volume": true,
|
|
||||||
},
|
|
||||||
}
|
|
||||||
|
|
||||||
generatedData, warnings, err := b.Prepare(config)
|
|
||||||
if len(warnings) > 0 {
|
|
||||||
t.Fatalf("bad: %#v", warnings)
|
|
||||||
}
|
|
||||||
if err != nil {
|
|
||||||
t.Fatalf("should not have error: %s", err)
|
|
||||||
}
|
|
||||||
if len(generatedData) == 0 {
|
|
||||||
t.Fatalf("Generated data should not be empty")
|
|
||||||
}
|
|
||||||
|
|
||||||
state := tState(t)
|
|
||||||
|
|
||||||
accessConfig := common.FakeAccessConfig()
|
|
||||||
|
|
||||||
step := stepSnapshotEBSVolumes{
|
|
||||||
PollingConfig: new(common.AWSPollingConfig),
|
|
||||||
AccessConfig: accessConfig,
|
|
||||||
VolumeMapping: b.config.VolumeMappings,
|
|
||||||
Ctx: b.config.ctx,
|
|
||||||
}
|
|
||||||
|
|
||||||
step.Run(context.Background(), state)
|
|
||||||
|
|
||||||
if len(step.snapshotMap) != 1 {
|
|
||||||
t.Fatalf("Missing Snapshot from step")
|
|
||||||
}
|
|
||||||
|
|
||||||
if volmapping := step.snapshotMap["snap-of-vol-5678"]; volmapping == nil {
|
|
||||||
t.Fatalf("Didn't snapshot correct volume: Map is %#v", step.snapshotMap)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestStepSnapshot_run_no_snaps(t *testing.T) {
|
|
||||||
var b Builder
|
|
||||||
config := testConfig() //from builder_test
|
|
||||||
|
|
||||||
//Set some snapshot settings
|
|
||||||
config["ebs_volumes"] = []map[string]interface{}{
|
|
||||||
{
|
|
||||||
"device_name": "/dev/xvdb",
|
|
||||||
"volume_size": "32",
|
|
||||||
"delete_on_termination": true,
|
|
||||||
"snapshot_volume": false,
|
|
||||||
},
|
|
||||||
}
|
|
||||||
|
|
||||||
generatedData, warnings, err := b.Prepare(config)
|
|
||||||
if len(warnings) > 0 {
|
|
||||||
t.Fatalf("bad: %#v", warnings)
|
|
||||||
}
|
|
||||||
if err != nil {
|
|
||||||
t.Fatalf("should not have error: %s", err)
|
|
||||||
}
|
|
||||||
if len(generatedData) == 0 {
|
|
||||||
t.Fatalf("Generated data should not be empty")
|
|
||||||
}
|
|
||||||
|
|
||||||
state := tState(t)
|
|
||||||
|
|
||||||
accessConfig := common.FakeAccessConfig()
|
|
||||||
|
|
||||||
step := stepSnapshotEBSVolumes{
|
|
||||||
PollingConfig: new(common.AWSPollingConfig),
|
|
||||||
AccessConfig: accessConfig,
|
|
||||||
VolumeMapping: b.config.VolumeMappings,
|
|
||||||
Ctx: b.config.ctx,
|
|
||||||
}
|
|
||||||
|
|
||||||
step.Run(context.Background(), state)
|
|
||||||
|
|
||||||
if len(step.snapshotMap) != 0 {
|
|
||||||
t.Fatalf("Shouldn't have snapshotted any volumes")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1 +0,0 @@
|
|||||||
command: packer build -var "accesskey=*" -var "secretkey=" -var "shellpath=packages.sh" .\apache.json
|
|
||||||
@@ -1,31 +0,0 @@
|
|||||||
{
|
|
||||||
"variables":
|
|
||||||
{
|
|
||||||
"accesskey": "",
|
|
||||||
"secretkey": "",
|
|
||||||
"shellpath": "packages.sh"
|
|
||||||
},
|
|
||||||
"builders":[
|
|
||||||
{
|
|
||||||
"type": "amazon-ebs",
|
|
||||||
"access_key": "{{user `accesskey`}}",
|
|
||||||
"secret_key": "{{user `secretkey`}}",
|
|
||||||
"region": "ap-south-1",
|
|
||||||
"source_ami": "ami-sa7608343426b",
|
|
||||||
"instance_type": "t2.micro",
|
|
||||||
"ssh_username": "ubuntu",
|
|
||||||
"ami_name": "apache",
|
|
||||||
"tags": {
|
|
||||||
"OS_Version": "Ubuntu",
|
|
||||||
"Release": "Latest"
|
|
||||||
}
|
|
||||||
}
|
|
||||||
],
|
|
||||||
"provisioners":[
|
|
||||||
{
|
|
||||||
"type": "shell",
|
|
||||||
"script": "{{user `shellpath`}}"
|
|
||||||
}
|
|
||||||
]
|
|
||||||
|
|
||||||
}
|
|
||||||
@@ -1,6 +0,0 @@
|
|||||||
echo "installing apache "
|
|
||||||
sudo apt-get update
|
|
||||||
sudo apt-get install apache2 -y
|
|
||||||
sudo apt-get update
|
|
||||||
sudo service apache2 restart
|
|
||||||
sudo apache2 --version
|
|
||||||
@@ -1 +0,0 @@
|
|||||||
command: packer build -var "accesskey=*" -var "secretkey=" -var "shellpath=packages.sh" .\nginx.json
|
|
||||||
@@ -1,31 +0,0 @@
|
|||||||
{
|
|
||||||
"variables":
|
|
||||||
{
|
|
||||||
"accesskey": "",
|
|
||||||
"secretkey": "",
|
|
||||||
"shellpath": "packages.sh"
|
|
||||||
},
|
|
||||||
"builders":[
|
|
||||||
{
|
|
||||||
"type": "amazon-ebs",
|
|
||||||
"access_key": "{{user `accesskey`}}",
|
|
||||||
"secret_key": "{{user `secretkey`}}",
|
|
||||||
"region": "ap-south-1",
|
|
||||||
"source_ami": "ami-sa7608343426b",
|
|
||||||
"instance_type": "t2.micro",
|
|
||||||
"ssh_username": "ubuntu",
|
|
||||||
"ami_name": "nginx",
|
|
||||||
"tags": {
|
|
||||||
"OS_Version": "Ubuntu",
|
|
||||||
"Release": "Latest"
|
|
||||||
}
|
|
||||||
}
|
|
||||||
],
|
|
||||||
"provisioners":[
|
|
||||||
{
|
|
||||||
"type": "shell",
|
|
||||||
"script": "{{user `shellpath`}}"
|
|
||||||
}
|
|
||||||
]
|
|
||||||
|
|
||||||
}
|
|
||||||
@@ -1,4 +0,0 @@
|
|||||||
echo "installing nginx "
|
|
||||||
sudo apt-get update
|
|
||||||
sudo apt-get install nginx -y
|
|
||||||
sudo service nginx restart
|
|
||||||
@@ -1,343 +0,0 @@
|
|||||||
package instance
|
|
||||||
|
|
||||||
import (
|
|
||||||
"io/ioutil"
|
|
||||||
"os"
|
|
||||||
"testing"
|
|
||||||
|
|
||||||
packersdk "github.com/hashicorp/packer-plugin-sdk/packer"
|
|
||||||
)
|
|
||||||
|
|
||||||
func testConfig() (config map[string]interface{}, tf *os.File) {
|
|
||||||
tf, err := ioutil.TempFile("", "packer")
|
|
||||||
if err != nil {
|
|
||||||
panic(err)
|
|
||||||
}
|
|
||||||
|
|
||||||
config = map[string]interface{}{
|
|
||||||
"account_id": "foo",
|
|
||||||
"ami_name": "foo",
|
|
||||||
"instance_type": "m1.small",
|
|
||||||
"region": "us-east-1",
|
|
||||||
"s3_bucket": "foo",
|
|
||||||
"source_ami": "foo",
|
|
||||||
"ssh_username": "bob",
|
|
||||||
"x509_cert_path": tf.Name(),
|
|
||||||
"x509_key_path": tf.Name(),
|
|
||||||
"x509_upload_path": "/foo",
|
|
||||||
}
|
|
||||||
|
|
||||||
return config, tf
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestBuilder_ImplementsBuilder(t *testing.T) {
|
|
||||||
var raw interface{}
|
|
||||||
raw = &Builder{}
|
|
||||||
if _, ok := raw.(packersdk.Builder); !ok {
|
|
||||||
t.Fatalf("Builder should be a builder")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestBuilderPrepare_AccountId(t *testing.T) {
|
|
||||||
b := &Builder{}
|
|
||||||
config, tempfile := testConfig()
|
|
||||||
config["skip_region_validation"] = true
|
|
||||||
|
|
||||||
defer os.Remove(tempfile.Name())
|
|
||||||
defer tempfile.Close()
|
|
||||||
|
|
||||||
config["account_id"] = ""
|
|
||||||
_, warnings, err := b.Prepare(config)
|
|
||||||
if len(warnings) > 0 {
|
|
||||||
t.Fatalf("bad: %#v", warnings)
|
|
||||||
}
|
|
||||||
if err == nil {
|
|
||||||
t.Fatal("should have error")
|
|
||||||
}
|
|
||||||
|
|
||||||
config["account_id"] = "foo"
|
|
||||||
_, warnings, err = b.Prepare(config)
|
|
||||||
if len(warnings) > 0 {
|
|
||||||
t.Fatalf("bad: %#v", warnings)
|
|
||||||
}
|
|
||||||
if err != nil {
|
|
||||||
t.Errorf("err: %s", err)
|
|
||||||
}
|
|
||||||
|
|
||||||
config["account_id"] = "0123-0456-7890"
|
|
||||||
_, warnings, err = b.Prepare(config)
|
|
||||||
if len(warnings) > 0 {
|
|
||||||
t.Fatalf("bad: %#v", warnings)
|
|
||||||
}
|
|
||||||
if err != nil {
|
|
||||||
t.Fatalf("err: %s", err)
|
|
||||||
}
|
|
||||||
|
|
||||||
if b.config.AccountId != "012304567890" {
|
|
||||||
t.Errorf("should strip hyphens: %s", b.config.AccountId)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestBuilderPrepare_AMIName(t *testing.T) {
|
|
||||||
var b Builder
|
|
||||||
config, tempfile := testConfig()
|
|
||||||
defer os.Remove(tempfile.Name())
|
|
||||||
defer tempfile.Close()
|
|
||||||
|
|
||||||
// Test good
|
|
||||||
config["ami_name"] = "foo"
|
|
||||||
config["skip_region_validation"] = true
|
|
||||||
_, warnings, err := b.Prepare(config)
|
|
||||||
if len(warnings) > 0 {
|
|
||||||
t.Fatalf("bad: %#v", warnings)
|
|
||||||
}
|
|
||||||
if err != nil {
|
|
||||||
t.Fatalf("should not have error: %s", err)
|
|
||||||
}
|
|
||||||
|
|
||||||
// Test bad
|
|
||||||
config["ami_name"] = "foo {{"
|
|
||||||
b = Builder{}
|
|
||||||
_, warnings, err = b.Prepare(config)
|
|
||||||
if len(warnings) > 0 {
|
|
||||||
t.Fatalf("bad: %#v", warnings)
|
|
||||||
}
|
|
||||||
if err == nil {
|
|
||||||
t.Fatal("should have error")
|
|
||||||
}
|
|
||||||
|
|
||||||
// Test bad
|
|
||||||
delete(config, "ami_name")
|
|
||||||
b = Builder{}
|
|
||||||
_, warnings, err = b.Prepare(config)
|
|
||||||
if len(warnings) > 0 {
|
|
||||||
t.Fatalf("bad: %#v", warnings)
|
|
||||||
}
|
|
||||||
if err == nil {
|
|
||||||
t.Fatal("should have error")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestBuilderPrepare_BundleDestination(t *testing.T) {
|
|
||||||
b := &Builder{}
|
|
||||||
config, tempfile := testConfig()
|
|
||||||
config["skip_region_validation"] = true
|
|
||||||
defer os.Remove(tempfile.Name())
|
|
||||||
defer tempfile.Close()
|
|
||||||
|
|
||||||
config["bundle_destination"] = ""
|
|
||||||
_, warnings, err := b.Prepare(config)
|
|
||||||
if len(warnings) > 0 {
|
|
||||||
t.Fatalf("bad: %#v", warnings)
|
|
||||||
}
|
|
||||||
if err != nil {
|
|
||||||
t.Fatalf("err: %s", err)
|
|
||||||
}
|
|
||||||
|
|
||||||
if b.config.BundleDestination != "/tmp" {
|
|
||||||
t.Fatalf("bad: %s", b.config.BundleDestination)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestBuilderPrepare_BundlePrefix(t *testing.T) {
|
|
||||||
b := &Builder{}
|
|
||||||
config, tempfile := testConfig()
|
|
||||||
config["skip_region_validation"] = true
|
|
||||||
defer os.Remove(tempfile.Name())
|
|
||||||
defer tempfile.Close()
|
|
||||||
|
|
||||||
_, warnings, err := b.Prepare(config)
|
|
||||||
if len(warnings) > 0 {
|
|
||||||
t.Fatalf("bad: %#v", warnings)
|
|
||||||
}
|
|
||||||
if err != nil {
|
|
||||||
t.Fatalf("err: %s", err)
|
|
||||||
}
|
|
||||||
|
|
||||||
if b.config.BundlePrefix == "" {
|
|
||||||
t.Fatalf("bad: %s", b.config.BundlePrefix)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestBuilderPrepare_InvalidKey(t *testing.T) {
|
|
||||||
var b Builder
|
|
||||||
config, tempfile := testConfig()
|
|
||||||
defer os.Remove(tempfile.Name())
|
|
||||||
defer tempfile.Close()
|
|
||||||
|
|
||||||
// Add a random key
|
|
||||||
config["i_should_not_be_valid"] = true
|
|
||||||
_, warnings, err := b.Prepare(config)
|
|
||||||
if len(warnings) > 0 {
|
|
||||||
t.Fatalf("bad: %#v", warnings)
|
|
||||||
}
|
|
||||||
if err == nil {
|
|
||||||
t.Fatal("should have error")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestBuilderPrepare_S3Bucket(t *testing.T) {
|
|
||||||
b := &Builder{}
|
|
||||||
config, tempfile := testConfig()
|
|
||||||
config["skip_region_validation"] = true
|
|
||||||
defer os.Remove(tempfile.Name())
|
|
||||||
defer tempfile.Close()
|
|
||||||
|
|
||||||
config["s3_bucket"] = ""
|
|
||||||
_, warnings, err := b.Prepare(config)
|
|
||||||
if len(warnings) > 0 {
|
|
||||||
t.Fatalf("bad: %#v", warnings)
|
|
||||||
}
|
|
||||||
if err == nil {
|
|
||||||
t.Fatal("should have error")
|
|
||||||
}
|
|
||||||
|
|
||||||
config["s3_bucket"] = "foo"
|
|
||||||
_, warnings, err = b.Prepare(config)
|
|
||||||
if len(warnings) > 0 {
|
|
||||||
t.Fatalf("bad: %#v", warnings)
|
|
||||||
}
|
|
||||||
if err != nil {
|
|
||||||
t.Errorf("err: %s", err)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestBuilderPrepare_X509CertPath(t *testing.T) {
|
|
||||||
b := &Builder{}
|
|
||||||
config, tempfile := testConfig()
|
|
||||||
config["skip_region_validation"] = true
|
|
||||||
defer os.Remove(tempfile.Name())
|
|
||||||
defer tempfile.Close()
|
|
||||||
|
|
||||||
config["x509_cert_path"] = ""
|
|
||||||
_, warnings, err := b.Prepare(config)
|
|
||||||
if len(warnings) > 0 {
|
|
||||||
t.Fatalf("bad: %#v", warnings)
|
|
||||||
}
|
|
||||||
if err == nil {
|
|
||||||
t.Fatal("should have error")
|
|
||||||
}
|
|
||||||
|
|
||||||
config["x509_cert_path"] = "i/am/a/file/that/doesnt/exist"
|
|
||||||
_, warnings, err = b.Prepare(config)
|
|
||||||
if len(warnings) > 0 {
|
|
||||||
t.Fatalf("bad: %#v", warnings)
|
|
||||||
}
|
|
||||||
if err == nil {
|
|
||||||
t.Error("should have error")
|
|
||||||
}
|
|
||||||
|
|
||||||
tf, err := ioutil.TempFile("", "packer")
|
|
||||||
if err != nil {
|
|
||||||
t.Fatalf("error tempfile: %s", err)
|
|
||||||
}
|
|
||||||
defer os.Remove(tf.Name())
|
|
||||||
defer tf.Close()
|
|
||||||
|
|
||||||
config["x509_cert_path"] = tf.Name()
|
|
||||||
_, warnings, err = b.Prepare(config)
|
|
||||||
if len(warnings) > 0 {
|
|
||||||
t.Fatalf("bad: %#v", warnings)
|
|
||||||
}
|
|
||||||
if err != nil {
|
|
||||||
t.Fatalf("should not have error: %s", err)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestBuilderPrepare_X509KeyPath(t *testing.T) {
|
|
||||||
b := &Builder{}
|
|
||||||
config, tempfile := testConfig()
|
|
||||||
config["skip_region_validation"] = true
|
|
||||||
defer os.Remove(tempfile.Name())
|
|
||||||
defer tempfile.Close()
|
|
||||||
|
|
||||||
config["x509_key_path"] = ""
|
|
||||||
_, warnings, err := b.Prepare(config)
|
|
||||||
if len(warnings) > 0 {
|
|
||||||
t.Fatalf("bad: %#v", warnings)
|
|
||||||
}
|
|
||||||
if err == nil {
|
|
||||||
t.Fatal("should have error")
|
|
||||||
}
|
|
||||||
|
|
||||||
config["x509_key_path"] = "i/am/a/file/that/doesnt/exist"
|
|
||||||
_, warnings, err = b.Prepare(config)
|
|
||||||
if len(warnings) > 0 {
|
|
||||||
t.Fatalf("bad: %#v", warnings)
|
|
||||||
}
|
|
||||||
if err == nil {
|
|
||||||
t.Error("should have error")
|
|
||||||
}
|
|
||||||
|
|
||||||
tf, err := ioutil.TempFile("", "packer")
|
|
||||||
if err != nil {
|
|
||||||
t.Fatalf("error tempfile: %s", err)
|
|
||||||
}
|
|
||||||
defer os.Remove(tf.Name())
|
|
||||||
defer tf.Close()
|
|
||||||
|
|
||||||
config["x509_key_path"] = tf.Name()
|
|
||||||
_, warnings, err = b.Prepare(config)
|
|
||||||
if len(warnings) > 0 {
|
|
||||||
t.Fatalf("bad: %#v", warnings)
|
|
||||||
}
|
|
||||||
if err != nil {
|
|
||||||
t.Fatalf("should not have error: %s", err)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestBuilderPrepare_X509UploadPath(t *testing.T) {
|
|
||||||
b := &Builder{}
|
|
||||||
config, tempfile := testConfig()
|
|
||||||
config["skip_region_validation"] = true
|
|
||||||
defer os.Remove(tempfile.Name())
|
|
||||||
defer tempfile.Close()
|
|
||||||
|
|
||||||
config["x509_upload_path"] = ""
|
|
||||||
_, warnings, err := b.Prepare(config)
|
|
||||||
if len(warnings) > 0 {
|
|
||||||
t.Fatalf("bad: %#v", warnings)
|
|
||||||
}
|
|
||||||
if err != nil {
|
|
||||||
t.Fatalf("should not have error: %s", err)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestBuilderPrepare_ReturnGeneratedData(t *testing.T) {
|
|
||||||
var b Builder
|
|
||||||
config, tempfile := testConfig()
|
|
||||||
defer os.Remove(tempfile.Name())
|
|
||||||
defer tempfile.Close()
|
|
||||||
|
|
||||||
generatedData, warnings, err := b.Prepare(config)
|
|
||||||
if len(warnings) > 0 {
|
|
||||||
t.Fatalf("bad: %#v", warnings)
|
|
||||||
}
|
|
||||||
if err != nil {
|
|
||||||
t.Fatalf("should not have error: %s", err)
|
|
||||||
}
|
|
||||||
if len(generatedData) == 0 {
|
|
||||||
t.Fatalf("Generated data should not be empty")
|
|
||||||
}
|
|
||||||
if len(generatedData) == 0 {
|
|
||||||
t.Fatalf("Generated data should not be empty")
|
|
||||||
}
|
|
||||||
if generatedData[0] != "SourceAMIName" {
|
|
||||||
t.Fatalf("Generated data should contain SourceAMIName")
|
|
||||||
}
|
|
||||||
if generatedData[1] != "BuildRegion" {
|
|
||||||
t.Fatalf("Generated data should contain BuildRegion")
|
|
||||||
}
|
|
||||||
if generatedData[2] != "SourceAMI" {
|
|
||||||
t.Fatalf("Generated data should contain SourceAMI")
|
|
||||||
}
|
|
||||||
if generatedData[3] != "SourceAMICreationDate" {
|
|
||||||
t.Fatalf("Generated data should contain SourceAMICreationDate")
|
|
||||||
}
|
|
||||||
if generatedData[4] != "SourceAMIOwner" {
|
|
||||||
t.Fatalf("Generated data should contain SourceAMIOwner")
|
|
||||||
}
|
|
||||||
if generatedData[5] != "SourceAMIOwnerName" {
|
|
||||||
t.Fatalf("Generated data should contain SourceAMIOwnerName")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,13 +0,0 @@
|
|||||||
package version
|
|
||||||
|
|
||||||
import (
|
|
||||||
"github.com/hashicorp/packer-plugin-sdk/version"
|
|
||||||
packerVersion "github.com/hashicorp/packer/version"
|
|
||||||
)
|
|
||||||
|
|
||||||
var AWSPluginVersion *version.PluginVersion
|
|
||||||
|
|
||||||
func init() {
|
|
||||||
AWSPluginVersion = version.InitializePluginVersion(
|
|
||||||
packerVersion.Version, packerVersion.VersionPrerelease)
|
|
||||||
}
|
|
||||||
@@ -20,12 +20,18 @@ package arm
|
|||||||
// go test -v -timeout 90m -run TestBuilderAcc_.*
|
// go test -v -timeout 90m -run TestBuilderAcc_.*
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"testing"
|
"bytes"
|
||||||
|
"context"
|
||||||
|
"errors"
|
||||||
"fmt"
|
"fmt"
|
||||||
"os"
|
"os"
|
||||||
|
"strings"
|
||||||
|
"testing"
|
||||||
|
|
||||||
|
"github.com/Azure/go-autorest/autorest"
|
||||||
|
"github.com/Azure/go-autorest/autorest/azure/auth"
|
||||||
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"
|
||||||
@@ -96,10 +102,15 @@ 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() { testAccPreCheck(t) },
|
PreCheck: func() { testAuthPreCheck(t) },
|
||||||
Builder: &Builder{},
|
Builder: &b,
|
||||||
Template: testBuilderAccManagedDiskLinuxAzureCLI,
|
Template: testBuilderAccManagedDiskLinuxAzureCLI,
|
||||||
|
Check: func([]packersdk.Artifact) error {
|
||||||
|
checkTemporaryGroupDeleted(t, &b)
|
||||||
|
return nil
|
||||||
|
},
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -112,15 +123,108 @@ 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() { testAccPreCheck(t) },
|
PreCheck: func() { testAuthPreCheck(t) },
|
||||||
Builder: &Builder{},
|
Builder: &b,
|
||||||
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": {
|
||||||
@@ -155,6 +259,7 @@ const testBuilderAccManagedDiskWindows = `
|
|||||||
}]
|
}]
|
||||||
}
|
}
|
||||||
`
|
`
|
||||||
|
|
||||||
const testBuilderAccManagedDiskWindowsBuildResourceGroup = `
|
const testBuilderAccManagedDiskWindowsBuildResourceGroup = `
|
||||||
{
|
{
|
||||||
"variables": {
|
"variables": {
|
||||||
@@ -287,6 +392,7 @@ const testBuilderAccManagedDiskLinux = `
|
|||||||
}]
|
}]
|
||||||
}
|
}
|
||||||
`
|
`
|
||||||
|
|
||||||
const testBuilderAccManagedDiskLinuxDeviceLogin = `
|
const testBuilderAccManagedDiskLinuxDeviceLogin = `
|
||||||
{
|
{
|
||||||
"variables": {
|
"variables": {
|
||||||
@@ -379,6 +485,7 @@ const testBuilderAccBlobLinux = `
|
|||||||
}]
|
}]
|
||||||
}
|
}
|
||||||
`
|
`
|
||||||
|
|
||||||
const testBuilderAccManagedDiskLinuxAzureCLI = `
|
const testBuilderAccManagedDiskLinuxAzureCLI = `
|
||||||
{
|
{
|
||||||
"builders": [{
|
"builders": [{
|
||||||
@@ -388,7 +495,8 @@ 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",
|
||||||
|
|||||||
@@ -6,6 +6,7 @@ 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"
|
||||||
@@ -15,16 +16,17 @@ 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
|
||||||
say func(message string)
|
deleteDeployment func(ctx context.Context, state multistep.StateBag) error
|
||||||
error func(e error)
|
say func(message string)
|
||||||
config *Config
|
error func(e error)
|
||||||
factory templateFactoryFunc
|
config *Config
|
||||||
name string
|
factory templateFactoryFunc
|
||||||
|
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 {
|
||||||
@@ -41,6 +43,7 @@ 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
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -58,32 +61,45 @@ 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.deleteTemplate(context.Background(), state)
|
err := s.deleteDeployment(context.Background(), state)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
s.say(s.client.LastError.Error())
|
s.say(err.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("\nThe resource group was not created by Packer, deleting individual resources ...")
|
ui.Say("\nDeleting individual resources ...")
|
||||||
|
|
||||||
deploymentName := s.name
|
deploymentName := s.name
|
||||||
resourceGroupName := state.Get(constants.ArmResourceGroupName).(string)
|
resourceGroupName := state.Get(constants.ArmResourceGroupName).(string)
|
||||||
err := s.deleteDeploymentResources(context.TODO(), deploymentName, resourceGroupName)
|
// Get image disk details before deleting the image; otherwise we won't be able to
|
||||||
|
// delete the disk as the image request will return a 404
|
||||||
|
computeName := state.Get(constants.ArmComputeName).(string)
|
||||||
|
imageType, imageName, err := s.disk(context.TODO(), resourceGroupName, computeName)
|
||||||
|
|
||||||
|
if err != nil && !strings.Contains(err.Error(), "ResourceNotFound") {
|
||||||
|
ui.Error(fmt.Sprintf("Could not retrieve OS Image details: %s", err))
|
||||||
|
}
|
||||||
|
err = s.delete(context.TODO(), deploymentName, resourceGroupName)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
s.reportIfError(err, resourceGroupName)
|
s.reportIfError(err, resourceGroupName)
|
||||||
}
|
}
|
||||||
|
|
||||||
NewStepDeleteAdditionalDisks(s.client, ui).Run(context.TODO(), state)
|
// The disk was not found on the VM, this is an error.
|
||||||
|
if imageType == "" && imageName == "" {
|
||||||
|
ui.Error(fmt.Sprintf("Failed to find temporary OS disk on VM. Please delete manually.\n\n"+
|
||||||
|
"VM Name: %s\n"+
|
||||||
|
"Error: %s", computeName, err))
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
ui.Say(fmt.Sprintf(" Deleting -> %s : '%s'", imageType, imageName))
|
||||||
|
err = s.deleteDisk(context.TODO(), imageType, imageName, resourceGroupName)
|
||||||
|
if err != nil {
|
||||||
|
ui.Error(fmt.Sprintf("Error deleting resource. Please delete manually.\n\n"+
|
||||||
|
"Name: %s\n"+
|
||||||
|
"Error: %s", imageName, err))
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s *StepDeployTemplate) deployTemplate(ctx context.Context, resourceGroupName string, deploymentName string) error {
|
func (s *StepDeployTemplate) deployTemplate(ctx context.Context, resourceGroupName string, deploymentName string) error {
|
||||||
@@ -106,7 +122,7 @@ func (s *StepDeployTemplate) deployTemplate(ctx context.Context, resourceGroupNa
|
|||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s *StepDeployTemplate) deleteTemplate(ctx context.Context, state multistep.StateBag) error {
|
func (s *StepDeployTemplate) deleteDeploymentObject(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)
|
||||||
@@ -222,6 +238,8 @@ 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
|
||||||
@@ -233,30 +251,47 @@ 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(" -> %s : '%s'", resourceType, resourceName))
|
s.say(fmt.Sprintf("Adding to deletion queue -> %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,6 +6,7 @@ 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"
|
||||||
)
|
)
|
||||||
|
|
||||||
@@ -108,11 +109,97 @@ 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
|
||||||
|
},
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|||||||
@@ -165,7 +165,7 @@ 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 < 5*time.Minute {
|
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"))
|
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
|
||||||
|
|||||||
@@ -60,12 +60,7 @@ 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.StepHTTPServer{
|
commonsteps.HTTPServerFromHTTPConfig(&b.config.HTTPConfig),
|
||||||
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,6 +19,7 @@ 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"`
|
||||||
@@ -135,6 +136,7 @@ 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},
|
||||||
|
|||||||
@@ -211,12 +211,7 @@ 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.StepHTTPServer{
|
commonsteps.HTTPServerFromHTTPConfig(&b.config.HTTPConfig),
|
||||||
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,
|
||||||
},
|
},
|
||||||
|
|||||||
@@ -19,6 +19,7 @@ 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"`
|
||||||
@@ -141,6 +142,7 @@ 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,12 +251,7 @@ 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.StepHTTPServer{
|
commonsteps.HTTPServerFromHTTPConfig(&b.config.HTTPConfig),
|
||||||
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,
|
||||||
},
|
},
|
||||||
|
|||||||
@@ -19,6 +19,7 @@ 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"`
|
||||||
@@ -143,6 +144,7 @@ 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},
|
||||||
|
|||||||
@@ -4,7 +4,7 @@ import (
|
|||||||
"context"
|
"context"
|
||||||
"fmt"
|
"fmt"
|
||||||
|
|
||||||
"github.com/oracle/oci-go-sdk/core"
|
"github.com/oracle/oci-go-sdk/v36/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/core"
|
"github.com/oracle/oci-go-sdk/v36/core"
|
||||||
)
|
)
|
||||||
|
|
||||||
// BuilderId uniquely identifies the builder
|
// BuilderId uniquely identifies the builder
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
//go:generate mapstructure-to-hcl2 -type Config,CreateVNICDetails,ListImagesRequest
|
//go:generate mapstructure-to-hcl2 -type Config,CreateVNICDetails,ListImagesRequest,FlexShapeConfig
|
||||||
|
|
||||||
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/common"
|
ocicommon "github.com/oracle/oci-go-sdk/v36/common"
|
||||||
ociauth "github.com/oracle/oci-go-sdk/common/auth"
|
ociauth "github.com/oracle/oci-go-sdk/v36/common/auth"
|
||||||
)
|
)
|
||||||
|
|
||||||
type CreateVNICDetails struct {
|
type CreateVNICDetails struct {
|
||||||
@@ -46,6 +46,11 @@ 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"`
|
||||||
@@ -91,6 +96,7 @@ 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
|
||||||
@@ -222,7 +228,7 @@ func (c *Config) Prepare(raws ...interface{}) error {
|
|||||||
}
|
}
|
||||||
|
|
||||||
providers := []ocicommon.ConfigurationProvider{
|
providers := []ocicommon.ConfigurationProvider{
|
||||||
NewRawConfigurationProvider(c.TenancyID, c.UserID, c.Region, c.Fingerprint, string(keyContent), &c.PassPhrase),
|
ocicommon.NewRawConfigurationProvider(c.TenancyID, c.UserID, c.Region, c.Fingerprint, string(keyContent), &c.PassPhrase),
|
||||||
}
|
}
|
||||||
|
|
||||||
if fileProvider != nil {
|
if fileProvider != nil {
|
||||||
@@ -253,7 +259,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, errors.New("'key_file' must be specified"))
|
errs, fmt.Errorf("'key_file' must be correctly specified. %w", err))
|
||||||
}
|
}
|
||||||
|
|
||||||
c.configProvider = configProvider
|
c.configProvider = configProvider
|
||||||
@@ -277,6 +283,18 @@ 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"; DO NOT EDIT.
|
// Code generated by "mapstructure-to-hcl2 -type Config,CreateVNICDetails,ListImagesRequest,FlexShapeConfig"; DO NOT EDIT.
|
||||||
|
|
||||||
package oci
|
package oci
|
||||||
|
|
||||||
@@ -88,6 +88,7 @@ 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"`
|
||||||
@@ -188,6 +189,7 @@ 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},
|
||||||
@@ -239,6 +241,31 @@ 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 {
|
||||||
|
|||||||
@@ -1,76 +0,0 @@
|
|||||||
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)
|
||||||
delete(raw, "access_cfg_file")
|
raw["access_cfg_file"] = "/tmp/random/access/config/file/should/not/exist"
|
||||||
|
|
||||||
var c Config
|
var c Config
|
||||||
errs := c.Prepare(raw)
|
errs := c.Prepare(raw)
|
||||||
@@ -140,6 +140,10 @@ 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/core"
|
"github.com/oracle/oci-go-sdk/v36/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/core"
|
"github.com/oracle/oci-go-sdk/v36/core"
|
||||||
)
|
)
|
||||||
|
|
||||||
// driverMock implements the Driver interface and communicates with Oracle
|
// driverMock implements the Driver interface and communicates with Oracle
|
||||||
|
|||||||
@@ -11,8 +11,8 @@ import (
|
|||||||
"sync/atomic"
|
"sync/atomic"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
"github.com/oracle/oci-go-sdk/common"
|
"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
|
||||||
@@ -158,6 +158,14 @@ func (d *driverOCI) CreateInstance(ctx context.Context, publicKey string) (strin
|
|||||||
Metadata: metadata,
|
Metadata: metadata,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if d.cfg.ShapeConfig.Ocpus != nil {
|
||||||
|
LaunchInstanceShapeConfigDetails := core.LaunchInstanceShapeConfigDetails{
|
||||||
|
Ocpus: d.cfg.ShapeConfig.Ocpus,
|
||||||
|
MemoryInGBs: d.cfg.ShapeConfig.MemoryInGBs,
|
||||||
|
}
|
||||||
|
instanceDetails.ShapeConfig = &LaunchInstanceShapeConfigDetails
|
||||||
|
}
|
||||||
|
|
||||||
instance, err := d.computeClient.LaunchInstance(context.TODO(), core.LaunchInstanceRequest{
|
instance, err := d.computeClient.LaunchInstance(context.TODO(), core.LaunchInstanceRequest{
|
||||||
LaunchInstanceDetails: instanceDetails,
|
LaunchInstanceDetails: instanceDetails,
|
||||||
RequestMetadata: requestMetadata,
|
RequestMetadata: requestMetadata,
|
||||||
|
|||||||
@@ -3,6 +3,8 @@ 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.
|
||||||
@@ -32,3 +34,10 @@ 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,6 +32,9 @@ 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,6 +37,9 @@ 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,6 +75,10 @@ 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,12 +209,7 @@ 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.StepHTTPServer{
|
commonsteps.HTTPServerFromHTTPConfig(&b.config.HTTPConfig),
|
||||||
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),
|
||||||
|
|||||||
@@ -19,6 +19,7 @@ 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"`
|
||||||
@@ -126,6 +127,7 @@ 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},
|
||||||
|
|||||||
@@ -20,6 +20,7 @@ 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"`
|
||||||
@@ -81,6 +82,7 @@ type FlatConfig struct {
|
|||||||
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"`
|
||||||
|
Token *string `mapstructure:"token" cty:"token" hcl:"token"`
|
||||||
Node *string `mapstructure:"node" cty:"node" hcl:"node"`
|
Node *string `mapstructure:"node" cty:"node" hcl:"node"`
|
||||||
Pool *string `mapstructure:"pool" cty:"pool" hcl:"pool"`
|
Pool *string `mapstructure:"pool" cty:"pool" hcl:"pool"`
|
||||||
VMName *string `mapstructure:"vm_name" cty:"vm_name" hcl:"vm_name"`
|
VMName *string `mapstructure:"vm_name" cty:"vm_name" hcl:"vm_name"`
|
||||||
@@ -129,6 +131,7 @@ 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,6 +193,7 @@ 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},
|
||||||
|
|||||||
@@ -2,7 +2,6 @@ package proxmox
|
|||||||
|
|
||||||
import (
|
import (
|
||||||
"context"
|
"context"
|
||||||
"crypto/tls"
|
|
||||||
"errors"
|
"errors"
|
||||||
"fmt"
|
"fmt"
|
||||||
|
|
||||||
@@ -35,15 +34,7 @@ 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
|
||||||
tlsConfig := &tls.Config{
|
b.proxmoxClient, err = newProxmoxClient(b.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
|
||||||
}
|
}
|
||||||
@@ -61,12 +52,7 @@ func (b *Builder) Run(ctx context.Context, ui packersdk.Ui, hook packersdk.Hook,
|
|||||||
&stepStartVM{
|
&stepStartVM{
|
||||||
vmCreator: b.vmCreator,
|
vmCreator: b.vmCreator,
|
||||||
},
|
},
|
||||||
&commonsteps.StepHTTPServer{
|
commonsteps.HTTPServerFromHTTPConfig(&b.config.HTTPConfig),
|
||||||
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,
|
||||||
|
|||||||
@@ -0,0 +1,37 @@
|
|||||||
|
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
|
||||||
|
}
|
||||||
@@ -0,0 +1,91 @@
|
|||||||
|
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)
|
||||||
|
}
|
||||||
@@ -35,6 +35,7 @@ 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"`
|
||||||
|
|
||||||
@@ -73,8 +74,8 @@ type additionalISOsConfig struct {
|
|||||||
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
|
ShouldUploadISO bool `mapstructure-to-hcl2:",skip"`
|
||||||
DownloadPathKey string
|
DownloadPathKey string `mapstructure-to-hcl2:",skip"`
|
||||||
}
|
}
|
||||||
|
|
||||||
type nicConfig struct {
|
type nicConfig struct {
|
||||||
@@ -135,6 +136,9 @@ 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))
|
||||||
@@ -220,8 +224,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 == "" {
|
if c.Password == "" && c.Token == "" {
|
||||||
errs = packersdk.MultiErrorAppend(errs, errors.New("password must be specified"))
|
errs = packersdk.MultiErrorAppend(errs, errors.New("password or token 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"))
|
||||||
|
|||||||
@@ -19,6 +19,7 @@ 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"`
|
||||||
@@ -80,6 +81,7 @@ type FlatConfig struct {
|
|||||||
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"`
|
||||||
|
Token *string `mapstructure:"token" cty:"token" hcl:"token"`
|
||||||
Node *string `mapstructure:"node" cty:"node" hcl:"node"`
|
Node *string `mapstructure:"node" cty:"node" hcl:"node"`
|
||||||
Pool *string `mapstructure:"pool" cty:"pool" hcl:"pool"`
|
Pool *string `mapstructure:"pool" cty:"pool" hcl:"pool"`
|
||||||
VMName *string `mapstructure:"vm_name" cty:"vm_name" hcl:"vm_name"`
|
VMName *string `mapstructure:"vm_name" cty:"vm_name" hcl:"vm_name"`
|
||||||
@@ -126,6 +128,7 @@ 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},
|
||||||
@@ -187,6 +190,7 @@ 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},
|
||||||
@@ -226,8 +230,6 @@ type FlatadditionalISOsConfig struct {
|
|||||||
ISOFile *string `mapstructure:"iso_file" cty:"iso_file" hcl:"iso_file"`
|
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"`
|
ISOStoragePool *string `mapstructure:"iso_storage_pool" cty:"iso_storage_pool" hcl:"iso_storage_pool"`
|
||||||
Unmount *bool `mapstructure:"unmount" cty:"unmount" hcl:"unmount"`
|
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 FlatadditionalISOsConfig.
|
// FlatMapstructure returns a new FlatadditionalISOsConfig.
|
||||||
@@ -251,8 +253,6 @@ func (*FlatadditionalISOsConfig) HCL2Spec() map[string]hcldec.Spec {
|
|||||||
"iso_file": &hcldec.AttrSpec{Name: "iso_file", 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},
|
"iso_storage_pool": &hcldec.AttrSpec{Name: "iso_storage_pool", Type: cty.String, Required: false},
|
||||||
"unmount": &hcldec.AttrSpec{Name: "unmount", Type: cty.Bool, 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
|
return s
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -20,6 +20,7 @@ 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"`
|
||||||
@@ -81,6 +82,7 @@ type FlatConfig struct {
|
|||||||
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"`
|
||||||
|
Token *string `mapstructure:"token" cty:"token" hcl:"token"`
|
||||||
Node *string `mapstructure:"node" cty:"node" hcl:"node"`
|
Node *string `mapstructure:"node" cty:"node" hcl:"node"`
|
||||||
Pool *string `mapstructure:"pool" cty:"pool" hcl:"pool"`
|
Pool *string `mapstructure:"pool" cty:"pool" hcl:"pool"`
|
||||||
VMName *string `mapstructure:"vm_name" cty:"vm_name" hcl:"vm_name"`
|
VMName *string `mapstructure:"vm_name" cty:"vm_name" hcl:"vm_name"`
|
||||||
@@ -135,6 +137,7 @@ 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},
|
||||||
@@ -196,6 +199,7 @@ 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},
|
||||||
|
|||||||
@@ -96,12 +96,7 @@ func (b *Builder) Run(ctx context.Context, ui packersdk.Ui, hook packersdk.Hook)
|
|||||||
QemuImgArgs: b.config.QemuImgArgs,
|
QemuImgArgs: b.config.QemuImgArgs,
|
||||||
},
|
},
|
||||||
new(stepHTTPIPDiscover),
|
new(stepHTTPIPDiscover),
|
||||||
&commonsteps.StepHTTPServer{
|
commonsteps.HTTPServerFromHTTPConfig(&b.config.HTTPConfig),
|
||||||
HTTPDir: b.config.HTTPDir,
|
|
||||||
HTTPPortMin: b.config.HTTPPortMin,
|
|
||||||
HTTPPortMax: b.config.HTTPPortMax,
|
|
||||||
HTTPAddress: b.config.HTTPAddress,
|
|
||||||
},
|
|
||||||
&stepPortForward{
|
&stepPortForward{
|
||||||
CommunicatorType: b.config.CommConfig.Comm.Type,
|
CommunicatorType: b.config.CommConfig.Comm.Type,
|
||||||
NetBridge: b.config.NetBridge,
|
NetBridge: b.config.NetBridge,
|
||||||
|
|||||||
@@ -19,6 +19,7 @@ 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"`
|
||||||
@@ -153,6 +154,7 @@ 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},
|
||||||
|
|||||||
@@ -298,6 +298,7 @@ func (s *stepRun) applyUserOverrides(defaultArgs map[string]interface{}, config
|
|||||||
HTTPIP string
|
HTTPIP string
|
||||||
HTTPPort int
|
HTTPPort int
|
||||||
HTTPDir string
|
HTTPDir string
|
||||||
|
HTTPContent map[string]string
|
||||||
OutputDir string
|
OutputDir string
|
||||||
Name string
|
Name string
|
||||||
SSHHostPort int
|
SSHHostPort int
|
||||||
@@ -308,6 +309,7 @@ func (s *stepRun) applyUserOverrides(defaultArgs map[string]interface{}, config
|
|||||||
HTTPIP: httpIp,
|
HTTPIP: httpIp,
|
||||||
HTTPPort: httpPort,
|
HTTPPort: httpPort,
|
||||||
HTTPDir: config.HTTPDir,
|
HTTPDir: config.HTTPDir,
|
||||||
|
HTTPContent: config.HTTPContent,
|
||||||
OutputDir: config.OutputDir,
|
OutputDir: config.OutputDir,
|
||||||
Name: config.VMName,
|
Name: config.VMName,
|
||||||
SSHHostPort: commHostPort,
|
SSHHostPort: commHostPort,
|
||||||
|
|||||||
@@ -19,6 +19,7 @@ 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"`
|
||||||
@@ -128,6 +129,7 @@ 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},
|
||||||
|
|||||||
@@ -406,12 +406,7 @@ func (b *Builder) Run(ctx context.Context, ui packersdk.Ui, hook packersdk.Hook)
|
|||||||
Label: b.config.CDConfig.CDLabel,
|
Label: b.config.CDConfig.CDLabel,
|
||||||
},
|
},
|
||||||
new(vboxcommon.StepHTTPIPDiscover),
|
new(vboxcommon.StepHTTPIPDiscover),
|
||||||
&commonsteps.StepHTTPServer{
|
commonsteps.HTTPServerFromHTTPConfig(&b.config.HTTPConfig),
|
||||||
HTTPDir: b.config.HTTPDir,
|
|
||||||
HTTPPortMin: b.config.HTTPPortMin,
|
|
||||||
HTTPPortMax: b.config.HTTPPortMax,
|
|
||||||
HTTPAddress: b.config.HTTPAddress,
|
|
||||||
},
|
|
||||||
&vboxcommon.StepSshKeyPair{
|
&vboxcommon.StepSshKeyPair{
|
||||||
Debug: b.config.PackerDebug,
|
Debug: b.config.PackerDebug,
|
||||||
DebugKeyPath: fmt.Sprintf("%s.pem", b.config.PackerBuildName),
|
DebugKeyPath: fmt.Sprintf("%s.pem", b.config.PackerBuildName),
|
||||||
|
|||||||
@@ -19,6 +19,7 @@ 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"`
|
||||||
@@ -162,6 +163,7 @@ 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},
|
||||||
|
|||||||
@@ -65,12 +65,7 @@ func (b *Builder) Run(ctx context.Context, ui packersdk.Ui, hook packersdk.Hook)
|
|||||||
Label: b.config.CDConfig.CDLabel,
|
Label: b.config.CDConfig.CDLabel,
|
||||||
},
|
},
|
||||||
new(vboxcommon.StepHTTPIPDiscover),
|
new(vboxcommon.StepHTTPIPDiscover),
|
||||||
&commonsteps.StepHTTPServer{
|
commonsteps.HTTPServerFromHTTPConfig(&b.config.HTTPConfig),
|
||||||
HTTPDir: b.config.HTTPDir,
|
|
||||||
HTTPPortMin: b.config.HTTPPortMin,
|
|
||||||
HTTPPortMax: b.config.HTTPPortMax,
|
|
||||||
HTTPAddress: b.config.HTTPAddress,
|
|
||||||
},
|
|
||||||
&vboxcommon.StepSshKeyPair{
|
&vboxcommon.StepSshKeyPair{
|
||||||
Debug: b.config.PackerDebug,
|
Debug: b.config.PackerDebug,
|
||||||
DebugKeyPath: fmt.Sprintf("%s.pem", b.config.PackerBuildName),
|
DebugKeyPath: fmt.Sprintf("%s.pem", b.config.PackerBuildName),
|
||||||
|
|||||||
@@ -19,6 +19,7 @@ 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"`
|
||||||
@@ -138,6 +139,7 @@ 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},
|
||||||
|
|||||||
@@ -64,12 +64,7 @@ func (b *Builder) Run(ctx context.Context, ui packersdk.Ui, hook packersdk.Hook)
|
|||||||
KeepRegistered: b.config.KeepRegistered,
|
KeepRegistered: b.config.KeepRegistered,
|
||||||
},
|
},
|
||||||
new(vboxcommon.StepHTTPIPDiscover),
|
new(vboxcommon.StepHTTPIPDiscover),
|
||||||
&commonsteps.StepHTTPServer{
|
commonsteps.HTTPServerFromHTTPConfig(&b.config.HTTPConfig),
|
||||||
HTTPDir: b.config.HTTPDir,
|
|
||||||
HTTPPortMin: b.config.HTTPPortMin,
|
|
||||||
HTTPPortMax: b.config.HTTPPortMax,
|
|
||||||
HTTPAddress: b.config.HTTPAddress,
|
|
||||||
},
|
|
||||||
&vboxcommon.StepDownloadGuestAdditions{
|
&vboxcommon.StepDownloadGuestAdditions{
|
||||||
GuestAdditionsMode: b.config.GuestAdditionsMode,
|
GuestAdditionsMode: b.config.GuestAdditionsMode,
|
||||||
GuestAdditionsURL: b.config.GuestAdditionsURL,
|
GuestAdditionsURL: b.config.GuestAdditionsURL,
|
||||||
|
|||||||
@@ -19,6 +19,7 @@ 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,6 +137,7 @@ 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},
|
||||||
|
|||||||
@@ -117,12 +117,7 @@ func (b *Builder) Run(ctx context.Context, ui packersdk.Ui, hook packersdk.Hook)
|
|||||||
},
|
},
|
||||||
&vmwcommon.StepSuppressMessages{},
|
&vmwcommon.StepSuppressMessages{},
|
||||||
&vmwcommon.StepHTTPIPDiscover{},
|
&vmwcommon.StepHTTPIPDiscover{},
|
||||||
&commonsteps.StepHTTPServer{
|
commonsteps.HTTPServerFromHTTPConfig(&b.config.HTTPConfig),
|
||||||
HTTPDir: b.config.HTTPDir,
|
|
||||||
HTTPPortMin: b.config.HTTPPortMin,
|
|
||||||
HTTPPortMax: b.config.HTTPPortMax,
|
|
||||||
HTTPAddress: b.config.HTTPAddress,
|
|
||||||
},
|
|
||||||
&vmwcommon.StepConfigureVNC{
|
&vmwcommon.StepConfigureVNC{
|
||||||
Enabled: !b.config.DisableVNC && !b.config.VNCOverWebsocket,
|
Enabled: !b.config.DisableVNC && !b.config.VNCOverWebsocket,
|
||||||
VNCBindAddress: b.config.VNCBindAddress,
|
VNCBindAddress: b.config.VNCBindAddress,
|
||||||
|
|||||||
@@ -19,6 +19,7 @@ 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"`
|
||||||
@@ -167,6 +168,7 @@ 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},
|
||||||
|
|||||||
@@ -108,12 +108,7 @@ func (b *Builder) Run(ctx context.Context, ui packersdk.Ui, hook packersdk.Hook)
|
|||||||
},
|
},
|
||||||
&vmwcommon.StepSuppressMessages{},
|
&vmwcommon.StepSuppressMessages{},
|
||||||
&vmwcommon.StepHTTPIPDiscover{},
|
&vmwcommon.StepHTTPIPDiscover{},
|
||||||
&commonsteps.StepHTTPServer{
|
commonsteps.HTTPServerFromHTTPConfig(&b.config.HTTPConfig),
|
||||||
HTTPDir: b.config.HTTPDir,
|
|
||||||
HTTPPortMin: b.config.HTTPPortMin,
|
|
||||||
HTTPPortMax: b.config.HTTPPortMax,
|
|
||||||
HTTPAddress: b.config.HTTPAddress,
|
|
||||||
},
|
|
||||||
&vmwcommon.StepUploadVMX{
|
&vmwcommon.StepUploadVMX{
|
||||||
RemoteType: b.config.RemoteType,
|
RemoteType: b.config.RemoteType,
|
||||||
},
|
},
|
||||||
|
|||||||
@@ -19,6 +19,7 @@ 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"`
|
||||||
@@ -149,6 +150,7 @@ 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},
|
||||||
|
|||||||
@@ -89,12 +89,7 @@ func (b *Builder) Run(ctx context.Context, ui packersdk.Ui, hook packersdk.Hook)
|
|||||||
HTTPIP: b.config.BootConfig.HTTPIP,
|
HTTPIP: b.config.BootConfig.HTTPIP,
|
||||||
Network: b.config.WaitIpConfig.GetIPNet(),
|
Network: b.config.WaitIpConfig.GetIPNet(),
|
||||||
},
|
},
|
||||||
&commonsteps.StepHTTPServer{
|
commonsteps.HTTPServerFromHTTPConfig(&b.config.HTTPConfig),
|
||||||
HTTPDir: b.config.HTTPDir,
|
|
||||||
HTTPPortMin: b.config.HTTPPortMin,
|
|
||||||
HTTPPortMax: b.config.HTTPPortMax,
|
|
||||||
HTTPAddress: b.config.HTTPAddress,
|
|
||||||
},
|
|
||||||
&common.StepSshKeyPair{
|
&common.StepSshKeyPair{
|
||||||
Debug: b.config.PackerDebug,
|
Debug: b.config.PackerDebug,
|
||||||
DebugKeyPath: fmt.Sprintf("%s.pem", b.config.PackerBuildName),
|
DebugKeyPath: fmt.Sprintf("%s.pem", b.config.PackerBuildName),
|
||||||
|
|||||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user