Compare commits
178 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| 780f9c8b41 | |||
| 3629aeaa01 | |||
| c207e4acad | |||
| 519dfa1959 | |||
| 70ceed1110 | |||
| 82eedc8f02 | |||
| 1d53080625 | |||
| 89931d0f2a | |||
| 5e17dbeff2 | |||
| d0512c6edd | |||
| 51024ed507 | |||
| a060cd81a5 | |||
| 4ab0d14f8c | |||
| 4cd4616641 | |||
| e470ec0ffc | |||
| e976c30964 | |||
| 7ed9e672a9 | |||
| ff5b5221c4 | |||
| b345a44a07 | |||
| 7732f7998c | |||
| ff01e6715a | |||
| edc19eb859 | |||
| 1bb5c455aa | |||
| 8c61ca174f | |||
| ef6093c4c3 | |||
| 4d9fb629c6 | |||
| 0993c976fa | |||
| 1e312ebc21 | |||
| b780da5750 | |||
| 565ca6627c | |||
| c100b56d44 | |||
| 55012937a9 | |||
| 15d467eaf1 | |||
| ce896351b9 | |||
| fb0886b724 | |||
| 667f930d3d | |||
| f2f65607eb | |||
| ecaff88af9 | |||
| a40a782408 | |||
| 80f807de4d | |||
| ac7c0f2f04 | |||
| e2e6bce4c4 | |||
| 9f647ba2bb | |||
| 7c6c399a38 | |||
| d5ccf73e91 | |||
| d8cbfcc075 | |||
| eba081a155 | |||
| 7ccfe38475 | |||
| dacf52b5b3 | |||
| 502708b86a | |||
| 1d485988ea | |||
| de12cd318d | |||
| 597dcce2ab | |||
| 0f8a658a23 | |||
| 4242cf3151 | |||
| 618a3d42c6 | |||
| 203900e403 | |||
| bece5c3c69 | |||
| 9e03647ad7 | |||
| e68e736b6c | |||
| 26a572270d | |||
| 8b3e7e6f2f | |||
| 341308c582 | |||
| 3227d3da43 | |||
| 1926fd6176 | |||
| 0f541aaf5e | |||
| 0ecc4b5e52 | |||
| 6986fb0e81 | |||
| a6f907c688 | |||
| 3e8641e30e | |||
| d0737dcd17 | |||
| 15f4b93cd2 | |||
| 46b6a21a9f | |||
| 53dc1eb87b | |||
| 0c45f75bf6 | |||
| 9b41474ab5 | |||
| 2f4c5c0a24 | |||
| 85aef9d3a6 | |||
| ad21a101c8 | |||
| ef974dd75c | |||
| 69de942647 | |||
| 2e9307de3f | |||
| 27fb9b2525 | |||
| 47fa09a9c9 | |||
| 7e4948502f | |||
| f00ea7a166 | |||
| f12c89bd84 | |||
| bdc8ac2813 | |||
| 0b5f8901cc | |||
| 7ce5a2b9cf | |||
| 2c48e04f1f | |||
| dba5171fa2 | |||
| a1de1559bf | |||
| 5497139f2a | |||
| 64a3219f69 | |||
| 60c961c021 | |||
| 172cec7223 | |||
| f32b67c3bb | |||
| 160d932cce | |||
| e0fe579837 | |||
| fe21ff905d | |||
| 8ee8420408 | |||
| 25ee6a19a6 | |||
| a6321ac137 | |||
| 832c0f2b2a | |||
| 33cf6bf454 | |||
| 22d373c279 | |||
| 77ce2b39d8 | |||
| 4bbeec4733 | |||
| 7ae42bbb21 | |||
| d1254a5e48 | |||
| cf65b7b494 | |||
| be7d7313c5 | |||
| 7d30a5d79d | |||
| a115b428ac | |||
| 4e22147909 | |||
| f1017a19d3 | |||
| 3ced522659 | |||
| 6c8e997d19 | |||
| 44f0be2314 | |||
| bf9bb7b5c7 | |||
| 1a5678dd86 | |||
| b2d692c33d | |||
| e8b3a0e3bf | |||
| 9944031580 | |||
| 35604a98a7 | |||
| 8bb3df7121 | |||
| d2ec658e78 | |||
| bb54b8bf10 | |||
| 1e27138857 | |||
| 08b0884521 | |||
| 3224438a34 | |||
| 2b4812837a | |||
| 123517aec1 | |||
| c4535b2552 | |||
| 1d10da1126 | |||
| 8f7b148cbb | |||
| 924fb0f8f8 | |||
| df4c5a1314 | |||
| 37e494a6e8 | |||
| 3c282de6c3 | |||
| beceace7b7 | |||
| 6f23bc0d97 | |||
| edd4567096 | |||
| 9a1f2d0c97 | |||
| 125a2f1f76 | |||
| 3ac5046260 | |||
| 8c18f8fa6d | |||
| 0ea92f6369 | |||
| bf1046e970 | |||
| de50cadb5c | |||
| 4f2f9f8a1e | |||
| 68f810891f | |||
| be8b2968bd | |||
| 4c9f3eb9ca | |||
| f4caf5978f | |||
| cd93957225 | |||
| 71b815a5fd | |||
| 79481ed7ec | |||
| a101d46589 | |||
| 3058c437a3 | |||
| 10e1573930 | |||
| c3e48ebb71 | |||
| 04cbcd7ae9 | |||
| 1c3c1f17d9 | |||
| 9331afcf80 | |||
| a5b0e37d7e | |||
| 6b5a3dacd4 | |||
| 22cb31cb7b | |||
| 0637601eda | |||
| d85286e228 | |||
| d3754e3021 | |||
| ab7e89781a | |||
| 40a97e29db | |||
| 93df53a275 | |||
| 6adf1f6659 | |||
| 261abe0cae | |||
| ab4b3a8465 |
@@ -6,6 +6,7 @@
|
||||
*.mdx text eol=lf
|
||||
*.ps1 text eol=lf
|
||||
*.hcl text eol=lf
|
||||
*.tmpl text eol=lf
|
||||
*.txt text eol=lf
|
||||
go.mod text eol=lf
|
||||
go.sum text eol=lf
|
||||
|
||||
@@ -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
|
||||
can quickly merge or address your contributions.
|
||||
|
||||
When contributing in any way to the Packer project (new issue, PR, etc), please
|
||||
be aware that our team identifies with many gender pronouns. Please remember to
|
||||
use nonbinary pronouns (they/them) and gender neutral language ("Hello folks")
|
||||
when addressing our team. For more reading on our code of conduct, please see the
|
||||
[HashiCorp community guidelines](https://www.hashicorp.com/community-guidelines).
|
||||
|
||||
## Issues
|
||||
|
||||
### Reporting an Issue
|
||||
|
||||
@@ -10,7 +10,7 @@ Describe the change you are making here!
|
||||
|
||||
Please include tests. Check out these examples:
|
||||
|
||||
- https://github.com/hashicorp/packer/blob/master/builder/virtualbox/common/ssh_config_test.go#L19-L37
|
||||
- https://github.com/hashicorp/packer/blob/master/builder/parallels/common/ssh_config_test.go#L34
|
||||
- https://github.com/hashicorp/packer/blob/master/post-processor/compress/post-processor_test.go#L153-L182
|
||||
|
||||
If your PR resolves any open issue(s), please indicate them like this so they will be closed when your PR is merged:
|
||||
|
||||
@@ -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:
|
||||
vercel-deployment-poll:
|
||||
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:
|
||||
url: ${{ steps.waitForVercelPreviewDeployment.outputs.url }}
|
||||
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.
|
||||
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}."
|
||||
}
|
||||
|
||||
|
||||
+89
-2
@@ -1,18 +1,105 @@
|
||||
## 1.7.1 (Upcoming)
|
||||
|
||||
### FEATURES
|
||||
### NOTES:
|
||||
|
||||
* 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)
|
||||
* post-processor/docker-\*: Have been vendored in this release and will no
|
||||
longer be updated with Packer core. In Packer v1.8.0 the plugin will be
|
||||
removed entirely. The `docker` builder will continue to work as expected
|
||||
until then, but for the latest offerings of the Docker plugin, users are
|
||||
encourage to use the `packer init` command to install the latest release
|
||||
version. For more details see [Installing Packer
|
||||
Plugins](https://www.packer.io/docs/plugins#installing- plugins)
|
||||
* post-processor/exoscale-import: Has been vendored in this release and will no
|
||||
longer be updated with Packer core. In Packer v1.8.0 the plugin will be
|
||||
removed entirely. The `exoscale-import` post-processor will continue to
|
||||
work as expected until then, but for the latest offerings of the Exoscale
|
||||
plugin, users are encourage to use the `packer init` command to install the
|
||||
latest release version. For more details see [Exoscale Plugin
|
||||
Repostiroy](https://github.com/exoscale/packer-plugin-exoscale). [GH-10709]
|
||||
|
||||
### IMPROVEMENTS
|
||||
* builder/amazon: allow creation of ebs snapshots wihtout 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/qemu: Added firmware option. [GH-10683]
|
||||
* builder/scaleway: add support for timeout in shutdown step. [GH-10503]
|
||||
* builder/vagrant: Fix logging to be clearer when Vagrant builder overrides
|
||||
values retrieved from vagrant's ssh_config call. [GH-10743]
|
||||
* builder/virtualbox: Added ISO builder option to create additional disks.
|
||||
[GH-10674]
|
||||
* builder/virtualbox: Add options for nested virtualisation and RTC time base.
|
||||
[GH-10736]
|
||||
* builder/virtualbox: Add template options for chipset, firmware, nic, graphics
|
||||
controller, and audio controller. [GH-10671]
|
||||
* builder/virtualbox: Support for "virtio" storage and ISO drive. [GH-10632]
|
||||
* builder/vmware: Added "attach_snapshot" parameter to vmware vmx builder.
|
||||
[GH-10651]
|
||||
* command/fmt: Adding recursive flag to formatter to format subdirectories.
|
||||
[GH-10457]
|
||||
* core: Change template parsing error to include warning about file extensions.
|
||||
[GH-10652]
|
||||
* core: Update to gopsutil v3.21.1 to allow builds to work for darwin arm64.
|
||||
[GH-10697]
|
||||
* hcl2_upgrade: hcl2_upgrade command can now upgrade json var-files [GH-10676]
|
||||
|
||||
### BUG FIXES
|
||||
* buider/azure: Update builder to ensure a proper clean up Azure temporary
|
||||
managed Os disks. [GH-10713]
|
||||
* builder/amazon: Update amazon SDK to fix an SSO login issue. [GH-10668]
|
||||
* builder/azure: Don't overwrite subscription id if unset. [GH-10659]
|
||||
* builder/azure: Set default for the parameter client_cert_token_timeout
|
||||
[GH-10783]
|
||||
* builder/google: Add new configuration field `windows_password_timeout` to
|
||||
allow user to set configurable timeouts. [GH-10727]
|
||||
* builder/hyperv: Make Packer respect winrm_host flag in winrm connect func.
|
||||
[GH-10748]
|
||||
* builder/openstack: Make Packer respect winrm_host flag in winrm connect func.
|
||||
[GH-10748]
|
||||
* builder/oracle-oci: Update Oracle Go SDK to fix issue with reading key file.
|
||||
[GH-10560]
|
||||
[GH-10560] [GH-10774]
|
||||
* builder/parallels: Make Packer respect winrm_host flag in winrm connect func.
|
||||
[GH-10748]
|
||||
* builder/proxmox: Fixes issue when using `additional_iso_files` in HCL enabled
|
||||
templates. [GH-10772]
|
||||
* builder/qemu: Make Packer respect winrm_host flag in winrm connect func.
|
||||
[GH-10748]
|
||||
* builder/virtualbox: Make Packer respect winrm_host flag in winrm connect
|
||||
func. [GH-10748]
|
||||
* builder/vmware: Added a fallback file check when trying to determine the
|
||||
network-mapping configuration. [GH-10543]
|
||||
* builder/vsphere: Fix 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: Make hcl2_upgrade command correctly translate
|
||||
pause_before. [GH-10654]
|
||||
* core/hcl2_upgrade: Make json variables using template engines get stored as
|
||||
locals so they can be properly interpolated. [GH-10685]
|
||||
* core/init: Fixes issue where `packer init` was failing to install valid
|
||||
plugins containing a 'v' within its name. [GH-10760]
|
||||
* core: Packer will now show a proper error message when failing to load the
|
||||
contents of PACKER_CONFIG. [GH-10766]
|
||||
* core: Pin Packer to Golang 1.16 to fix code generation issues. [GH-10702]
|
||||
* core: Templates previously could not interpolate the environment variable
|
||||
PACKER_LOG_PATH. [GH-10660]
|
||||
* provisioner/chef-solo: HCL2 templates can support the json_string option.
|
||||
[GH-10655]
|
||||
* provisioner/inspec: Add new configuration field `valid_exit_codes` to allow
|
||||
for non-zero exit codes. [GH-10723]
|
||||
* provisioner/salt-masterless: Update urls for the bootstrap scripts used by
|
||||
salt-masterless provide. [GH-10755]
|
||||
|
||||
## 1.7.0 (February 17, 2021)
|
||||
|
||||
|
||||
@@ -150,7 +150,7 @@ type AccessConfig struct {
|
||||
// environmental variable.
|
||||
Token string `mapstructure:"token" required:"false"`
|
||||
session *session.Session
|
||||
// Get credentials from Hashicorp Vault's aws secrets engine. You must
|
||||
// Get credentials from HashiCorp Vault's aws secrets engine. You must
|
||||
// already have created a role to use. For more information about
|
||||
// generating credentials via the Vault engine, see the [Vault
|
||||
// docs.](https://www.vaultproject.io/api/secret/aws#generate-credentials)
|
||||
|
||||
@@ -172,7 +172,7 @@ func (c *AMIConfig) Prepare(accessConfig *AccessConfig, ctx *interpolate.Context
|
||||
|
||||
// Prevent sharing of default KMS key encrypted volumes with other aws users
|
||||
if len(c.AMIUsers) > 0 {
|
||||
if len(c.AMIKmsKeyId) == 0 && c.AMIEncryptBootVolume.True() {
|
||||
if len(c.AMIKmsKeyId) == 0 && len(c.AMIRegionKMSKeyIDs) == 0 && c.AMIEncryptBootVolume.True() {
|
||||
errs = append(errs, fmt.Errorf("Cannot share AMI encrypted with default KMS key"))
|
||||
}
|
||||
if len(c.AMIRegionKMSKeyIDs) > 0 {
|
||||
|
||||
@@ -20,12 +20,18 @@ package arm
|
||||
// go test -v -timeout 90m -run TestBuilderAcc_.*
|
||||
|
||||
import (
|
||||
"testing"
|
||||
|
||||
"bytes"
|
||||
"context"
|
||||
"errors"
|
||||
"fmt"
|
||||
"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"
|
||||
packersdk "github.com/hashicorp/packer-plugin-sdk/packer"
|
||||
)
|
||||
|
||||
const DeviceLoginAcceptanceTest = "DEVICELOGIN_TEST"
|
||||
@@ -96,10 +102,15 @@ func TestBuilderAcc_ManagedDisk_Linux_AzureCLI(t *testing.T) {
|
||||
return
|
||||
}
|
||||
|
||||
var b Builder
|
||||
builderT.Test(t, builderT.TestCase{
|
||||
PreCheck: func() { testAccPreCheck(t) },
|
||||
Builder: &Builder{},
|
||||
PreCheck: func() { testAuthPreCheck(t) },
|
||||
Builder: &b,
|
||||
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) {
|
||||
var b Builder
|
||||
builderT.Test(t, builderT.TestCase{
|
||||
PreCheck: func() { testAccPreCheck(t) },
|
||||
Builder: &Builder{},
|
||||
PreCheck: func() { testAuthPreCheck(t) },
|
||||
Builder: &b,
|
||||
Template: testBuilderAccBlobLinux,
|
||||
Check: func([]packersdk.Artifact) error {
|
||||
checkUnmanagedVHDDeleted(t, &b)
|
||||
return nil
|
||||
},
|
||||
})
|
||||
}
|
||||
|
||||
func testAccPreCheck(*testing.T) {}
|
||||
|
||||
func testAuthPreCheck(t *testing.T) {
|
||||
_, err := auth.NewAuthorizerFromEnvironment()
|
||||
if err != nil {
|
||||
t.Fatalf("failed to auth to azure: %s", err)
|
||||
}
|
||||
}
|
||||
|
||||
func checkTemporaryGroupDeleted(t *testing.T, b *Builder) {
|
||||
ui := testUi()
|
||||
|
||||
spnCloud, spnKeyVault, err := b.getServicePrincipalTokens(ui.Say)
|
||||
if err != nil {
|
||||
t.Fatalf("failed getting azure tokens: %s", err)
|
||||
}
|
||||
|
||||
ui.Message("Creating test Azure Resource Manager (ARM) client ...")
|
||||
azureClient, err := NewAzureClient(
|
||||
b.config.ClientConfig.SubscriptionID,
|
||||
b.config.SharedGalleryDestination.SigDestinationSubscription,
|
||||
b.config.ResourceGroupName,
|
||||
b.config.StorageAccount,
|
||||
b.config.ClientConfig.CloudEnvironment(),
|
||||
b.config.SharedGalleryTimeout,
|
||||
b.config.PollingDurationTimeout,
|
||||
spnCloud,
|
||||
spnKeyVault)
|
||||
|
||||
if err != nil {
|
||||
t.Fatalf("failed to create azure client: %s", err)
|
||||
}
|
||||
|
||||
// Validate resource group has been deleted
|
||||
_, err = azureClient.GroupsClient.Get(context.Background(), b.config.tmpResourceGroupName)
|
||||
if err == nil || !resourceNotFound(err) {
|
||||
t.Fatalf("failed validating resource group deletion: %s", err)
|
||||
}
|
||||
}
|
||||
|
||||
func checkUnmanagedVHDDeleted(t *testing.T, b *Builder) {
|
||||
ui := testUi()
|
||||
|
||||
spnCloud, spnKeyVault, err := b.getServicePrincipalTokens(ui.Say)
|
||||
if err != nil {
|
||||
t.Fatalf("failed getting azure tokens: %s", err)
|
||||
}
|
||||
|
||||
azureClient, err := NewAzureClient(
|
||||
b.config.ClientConfig.SubscriptionID,
|
||||
b.config.SharedGalleryDestination.SigDestinationSubscription,
|
||||
b.config.ResourceGroupName,
|
||||
b.config.StorageAccount,
|
||||
b.config.ClientConfig.CloudEnvironment(),
|
||||
b.config.SharedGalleryTimeout,
|
||||
b.config.PollingDurationTimeout,
|
||||
spnCloud,
|
||||
spnKeyVault)
|
||||
|
||||
if err != nil {
|
||||
t.Fatalf("failed to create azure client: %s", err)
|
||||
}
|
||||
|
||||
// validate temporary os blob was deleted
|
||||
blob := azureClient.BlobStorageClient.GetContainerReference("images").GetBlobReference(b.config.tmpOSDiskName)
|
||||
_, err = blob.BreakLease(nil)
|
||||
if err != nil && !strings.Contains(err.Error(), "BlobNotFound") {
|
||||
t.Fatalf("failed validating deletion of unmanaged vhd: %s", err)
|
||||
}
|
||||
|
||||
// Validate resource group has been deleted
|
||||
_, err = azureClient.GroupsClient.Get(context.Background(), b.config.tmpResourceGroupName)
|
||||
if err == nil || !resourceNotFound(err) {
|
||||
t.Fatalf("failed validating resource group deletion: %s", err)
|
||||
}
|
||||
}
|
||||
|
||||
func resourceNotFound(err error) bool {
|
||||
derr := autorest.DetailedError{}
|
||||
return errors.As(err, &derr) && derr.StatusCode == 404
|
||||
}
|
||||
|
||||
func testUi() *packersdk.BasicUi {
|
||||
return &packersdk.BasicUi{
|
||||
Reader: new(bytes.Buffer),
|
||||
Writer: new(bytes.Buffer),
|
||||
ErrorWriter: new(bytes.Buffer),
|
||||
}
|
||||
}
|
||||
|
||||
const testBuilderAccManagedDiskWindows = `
|
||||
{
|
||||
"variables": {
|
||||
@@ -155,6 +259,7 @@ const testBuilderAccManagedDiskWindows = `
|
||||
}]
|
||||
}
|
||||
`
|
||||
|
||||
const testBuilderAccManagedDiskWindowsBuildResourceGroup = `
|
||||
{
|
||||
"variables": {
|
||||
@@ -287,6 +392,7 @@ const testBuilderAccManagedDiskLinux = `
|
||||
}]
|
||||
}
|
||||
`
|
||||
|
||||
const testBuilderAccManagedDiskLinuxDeviceLogin = `
|
||||
{
|
||||
"variables": {
|
||||
@@ -379,6 +485,7 @@ const testBuilderAccBlobLinux = `
|
||||
}]
|
||||
}
|
||||
`
|
||||
|
||||
const testBuilderAccManagedDiskLinuxAzureCLI = `
|
||||
{
|
||||
"builders": [{
|
||||
@@ -388,7 +495,8 @@ const testBuilderAccManagedDiskLinuxAzureCLI = `
|
||||
|
||||
"managed_image_resource_group_name": "packer-acceptance-test",
|
||||
"managed_image_name": "testBuilderAccManagedDiskLinuxAzureCLI-{{timestamp}}",
|
||||
|
||||
"temp_resource_group_name": "packer-acceptance-test-managed-cli",
|
||||
|
||||
"os_type": "Linux",
|
||||
"image_publisher": "Canonical",
|
||||
"image_offer": "UbuntuServer",
|
||||
|
||||
@@ -6,6 +6,7 @@ import (
|
||||
"fmt"
|
||||
"net/url"
|
||||
"strings"
|
||||
"sync"
|
||||
"time"
|
||||
|
||||
"github.com/hashicorp/packer-plugin-sdk/multistep"
|
||||
@@ -15,16 +16,17 @@ import (
|
||||
)
|
||||
|
||||
type StepDeployTemplate struct {
|
||||
client *AzureClient
|
||||
deploy func(ctx context.Context, resourceGroupName string, deploymentName string) error
|
||||
delete func(ctx context.Context, deploymentName, resourceGroupName string) error
|
||||
disk func(ctx context.Context, resourceGroupName string, computeName string) (string, string, error)
|
||||
deleteDisk func(ctx context.Context, imageType string, imageName string, resourceGroupName string) error
|
||||
say func(message string)
|
||||
error func(e error)
|
||||
config *Config
|
||||
factory templateFactoryFunc
|
||||
name string
|
||||
client *AzureClient
|
||||
deploy func(ctx context.Context, resourceGroupName string, deploymentName string) error
|
||||
delete func(ctx context.Context, deploymentName, resourceGroupName string) error
|
||||
disk func(ctx context.Context, resourceGroupName string, computeName string) (string, string, error)
|
||||
deleteDisk func(ctx context.Context, imageType string, imageName string, resourceGroupName string) error
|
||||
deleteDeployment func(ctx context.Context, state multistep.StateBag) error
|
||||
say func(message string)
|
||||
error func(e error)
|
||||
config *Config
|
||||
factory templateFactoryFunc
|
||||
name string
|
||||
}
|
||||
|
||||
func NewStepDeployTemplate(client *AzureClient, ui packersdk.Ui, config *Config, deploymentName string, factory templateFactoryFunc) *StepDeployTemplate {
|
||||
@@ -41,6 +43,7 @@ func NewStepDeployTemplate(client *AzureClient, ui packersdk.Ui, config *Config,
|
||||
step.delete = step.deleteDeploymentResources
|
||||
step.disk = step.getImageDetails
|
||||
step.deleteDisk = step.deleteImage
|
||||
step.deleteDeployment = step.deleteDeploymentObject
|
||||
return step
|
||||
}
|
||||
|
||||
@@ -58,32 +61,45 @@ func (s *StepDeployTemplate) Run(ctx context.Context, state multistep.StateBag)
|
||||
|
||||
func (s *StepDeployTemplate) Cleanup(state multistep.StateBag) {
|
||||
defer func() {
|
||||
err := s.deleteTemplate(context.Background(), state)
|
||||
err := s.deleteDeployment(context.Background(), state)
|
||||
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.Say("\nThe resource group was not created by Packer, deleting individual resources ...")
|
||||
ui.Say("\nDeleting individual resources ...")
|
||||
|
||||
deploymentName := s.name
|
||||
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 {
|
||||
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 {
|
||||
@@ -106,7 +122,7 @@ func (s *StepDeployTemplate) deployTemplate(ctx context.Context, resourceGroupNa
|
||||
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
|
||||
resourceGroupName := state.Get(constants.ArmResourceGroupName).(string)
|
||||
ui := state.Get("ui").(packersdk.Ui)
|
||||
@@ -222,6 +238,8 @@ func (s *StepDeployTemplate) deleteDeploymentResources(ctx context.Context, depl
|
||||
return err
|
||||
}
|
||||
|
||||
resources := map[string]string{}
|
||||
|
||||
for deploymentOperations.NotDone() {
|
||||
deploymentOperation := deploymentOperations.Value()
|
||||
// Sometimes an empty operation is added to the list by Azure
|
||||
@@ -233,30 +251,47 @@ func (s *StepDeployTemplate) deleteDeploymentResources(ctx context.Context, depl
|
||||
resourceName := *deploymentOperation.Properties.TargetResource.ResourceName
|
||||
resourceType := *deploymentOperation.Properties.TargetResource.ResourceType
|
||||
|
||||
s.say(fmt.Sprintf(" -> %s : '%s'", resourceType, resourceName))
|
||||
|
||||
err = retry.Config{
|
||||
Tries: 10,
|
||||
RetryDelay: (&retry.Backoff{InitialBackoff: 10 * time.Second, MaxBackoff: 600 * time.Second, Multiplier: 2}).Linear,
|
||||
}.Run(ctx, func(ctx context.Context) error {
|
||||
err := deleteResource(ctx, s.client,
|
||||
resourceType,
|
||||
resourceName,
|
||||
resourceGroupName)
|
||||
if err != nil {
|
||||
s.reportIfError(err, resourceName)
|
||||
}
|
||||
return nil
|
||||
})
|
||||
if err != nil {
|
||||
s.reportIfError(err, resourceName)
|
||||
}
|
||||
s.say(fmt.Sprintf("Adding to deletion queue -> %s : '%s'", resourceType, resourceName))
|
||||
resources[resourceType] = resourceName
|
||||
|
||||
if err = deploymentOperations.Next(); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
var wg sync.WaitGroup
|
||||
wg.Add(len(resources))
|
||||
|
||||
for resourceType, resourceName := range resources {
|
||||
go func(resourceType, resourceName string) {
|
||||
defer wg.Done()
|
||||
retryConfig := retry.Config{
|
||||
Tries: 10,
|
||||
RetryDelay: (&retry.Backoff{InitialBackoff: 10 * time.Second, MaxBackoff: 600 * time.Second, Multiplier: 2}).Linear,
|
||||
}
|
||||
|
||||
err = retryConfig.Run(ctx, func(ctx context.Context) error {
|
||||
s.say(fmt.Sprintf("Attempting deletion -> %s : '%s'", resourceType, resourceName))
|
||||
err := deleteResource(ctx, s.client,
|
||||
resourceType,
|
||||
resourceName,
|
||||
resourceGroupName)
|
||||
if err != nil {
|
||||
s.say(fmt.Sprintf("Error deleting resource. Will retry.\n"+
|
||||
"Name: %s\n"+
|
||||
"Error: %s\n", resourceName, err.Error()))
|
||||
}
|
||||
return err
|
||||
})
|
||||
if err != nil {
|
||||
s.reportIfError(err, resourceName)
|
||||
}
|
||||
}(resourceType, resourceName)
|
||||
}
|
||||
|
||||
s.say("Waiting for deletion of all resources...")
|
||||
wg.Wait()
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
|
||||
@@ -6,6 +6,7 @@ import (
|
||||
"testing"
|
||||
|
||||
"github.com/hashicorp/packer-plugin-sdk/multistep"
|
||||
packersdk "github.com/hashicorp/packer-plugin-sdk/packer"
|
||||
"github.com/hashicorp/packer/builder/azure/common/constants"
|
||||
)
|
||||
|
||||
@@ -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 {
|
||||
stateBag := new(multistep.BasicStateBag)
|
||||
|
||||
stateBag.Put(constants.ArmDeploymentName, "Unit Test: DeploymentName")
|
||||
stateBag.Put(constants.ArmResourceGroupName, "Unit Test: ResourceGroupName")
|
||||
stateBag.Put(constants.ArmComputeName, "Unit Test: ComputeName")
|
||||
|
||||
return stateBag
|
||||
}
|
||||
|
||||
func createTestStepDeployTemplateDeleteOSImage(deleteDiskCounter *int) *StepDeployTemplate {
|
||||
return &StepDeployTemplate{
|
||||
deploy: func(context.Context, string, string) error { return nil },
|
||||
say: func(message string) {},
|
||||
error: func(e error) {},
|
||||
deleteDisk: func(ctx context.Context, imageType string, imageName string, resourceGroupName string) error {
|
||||
*deleteDiskCounter++
|
||||
return nil
|
||||
},
|
||||
disk: func(ctx context.Context, resourceGroupName, computeName string) (string, string, error) {
|
||||
return "Microsoft.Compute/disks", "", nil
|
||||
},
|
||||
delete: func(ctx context.Context, deploymentName, resourceGroupName string) error {
|
||||
return nil
|
||||
},
|
||||
deleteDeployment: func(ctx context.Context, state multistep.StateBag) error {
|
||||
return nil
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
@@ -165,7 +165,7 @@ func (c Config) Validate(errs *packersdk.MultiError) {
|
||||
if _, err := os.Stat(c.ClientCertPath); err != nil {
|
||||
errs = packersdk.MultiErrorAppend(errs, fmt.Errorf("client_cert_path is not an accessible file: %v", err))
|
||||
}
|
||||
if c.ClientCertExpireTimeout < 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"))
|
||||
}
|
||||
return
|
||||
|
||||
@@ -60,12 +60,7 @@ func (b *Builder) Run(ctx context.Context, ui packersdk.Ui, hook packersdk.Hook)
|
||||
// Build the steps.
|
||||
steps := []multistep.Step{
|
||||
&stepPrepareConfig{},
|
||||
&commonsteps.StepHTTPServer{
|
||||
HTTPDir: b.config.HTTPDir,
|
||||
HTTPPortMin: b.config.HTTPPortMin,
|
||||
HTTPPortMax: b.config.HTTPPortMax,
|
||||
HTTPAddress: b.config.HTTPAddress,
|
||||
},
|
||||
commonsteps.HTTPServerFromHTTPConfig(&b.config.HTTPConfig),
|
||||
&stepKeypair{
|
||||
Debug: b.config.PackerDebug,
|
||||
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"`
|
||||
PackerSensitiveVars []string `mapstructure:"packer_sensitive_variables" cty:"packer_sensitive_variables" hcl:"packer_sensitive_variables"`
|
||||
HTTPDir *string `mapstructure:"http_directory" cty:"http_directory" hcl:"http_directory"`
|
||||
HTTPContent map[string]string `mapstructure:"http_content" cty:"http_content" hcl:"http_content"`
|
||||
HTTPPortMin *int `mapstructure:"http_port_min" cty:"http_port_min" hcl:"http_port_min"`
|
||||
HTTPPortMax *int `mapstructure:"http_port_max" cty:"http_port_max" hcl:"http_port_max"`
|
||||
HTTPAddress *string `mapstructure:"http_bind_address" cty:"http_bind_address" hcl:"http_bind_address"`
|
||||
@@ -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_sensitive_variables": &hcldec.AttrSpec{Name: "packer_sensitive_variables", Type: cty.List(cty.String), Required: false},
|
||||
"http_directory": &hcldec.AttrSpec{Name: "http_directory", Type: cty.String, Required: false},
|
||||
"http_content": &hcldec.AttrSpec{Name: "http_content", Type: cty.Map(cty.String), Required: false},
|
||||
"http_port_min": &hcldec.AttrSpec{Name: "http_port_min", Type: cty.Number, Required: false},
|
||||
"http_port_max": &hcldec.AttrSpec{Name: "http_port_max", Type: cty.Number, Required: false},
|
||||
"http_bind_address": &hcldec.AttrSpec{Name: "http_bind_address", Type: cty.String, Required: false},
|
||||
|
||||
@@ -1,11 +0,0 @@
|
||||
package docker
|
||||
|
||||
import (
|
||||
"testing"
|
||||
|
||||
packersdk "github.com/hashicorp/packer-plugin-sdk/packer"
|
||||
)
|
||||
|
||||
func TestExportArtifact_impl(t *testing.T) {
|
||||
var _ packersdk.Artifact = new(ExportArtifact)
|
||||
}
|
||||
@@ -1,58 +0,0 @@
|
||||
package docker
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"testing"
|
||||
|
||||
packersdk "github.com/hashicorp/packer-plugin-sdk/packer"
|
||||
)
|
||||
|
||||
func TestImportArtifact_impl(t *testing.T) {
|
||||
var _ packersdk.Artifact = new(ImportArtifact)
|
||||
}
|
||||
|
||||
func TestImportArtifactBuilderId(t *testing.T) {
|
||||
a := &ImportArtifact{BuilderIdValue: "foo"}
|
||||
if a.BuilderId() != "foo" {
|
||||
t.Fatalf("bad: %#v", a.BuilderId())
|
||||
}
|
||||
}
|
||||
|
||||
func TestImportArtifactFiles(t *testing.T) {
|
||||
a := &ImportArtifact{}
|
||||
if a.Files() != nil {
|
||||
t.Fatalf("bad: %#v", a.Files())
|
||||
}
|
||||
}
|
||||
|
||||
func TestImportArtifactId(t *testing.T) {
|
||||
a := &ImportArtifact{IdValue: "foo"}
|
||||
if a.Id() != "foo" {
|
||||
t.Fatalf("bad: %#v", a.Id())
|
||||
}
|
||||
}
|
||||
|
||||
func TestImportArtifactDestroy(t *testing.T) {
|
||||
d := new(MockDriver)
|
||||
a := &ImportArtifact{
|
||||
Driver: d,
|
||||
IdValue: "foo",
|
||||
}
|
||||
|
||||
// No error
|
||||
if err := a.Destroy(); err != nil {
|
||||
t.Fatalf("err: %s", err)
|
||||
}
|
||||
if !d.DeleteImageCalled {
|
||||
t.Fatal("delete image should be called")
|
||||
}
|
||||
if d.DeleteImageId != "foo" {
|
||||
t.Fatalf("bad: %#v", d.DeleteImageId)
|
||||
}
|
||||
|
||||
// With an error
|
||||
d.DeleteImageErr = errors.New("foo")
|
||||
if err := a.Destroy(); err != d.DeleteImageErr {
|
||||
t.Fatalf("err: %#v", err)
|
||||
}
|
||||
}
|
||||
@@ -1,11 +0,0 @@
|
||||
package docker
|
||||
|
||||
import (
|
||||
"testing"
|
||||
|
||||
packersdk "github.com/hashicorp/packer-plugin-sdk/packer"
|
||||
)
|
||||
|
||||
func TestBuilder_implBuilder(t *testing.T) {
|
||||
var _ packersdk.Builder = new(Builder)
|
||||
}
|
||||
@@ -1,239 +0,0 @@
|
||||
package docker
|
||||
|
||||
import (
|
||||
"crypto/sha256"
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"io/ioutil"
|
||||
"os"
|
||||
"os/exec"
|
||||
"testing"
|
||||
|
||||
builderT "github.com/hashicorp/packer-plugin-sdk/acctest"
|
||||
packersdk "github.com/hashicorp/packer-plugin-sdk/packer"
|
||||
)
|
||||
|
||||
// RenderConfig helps create dynamic packer template configs for parsing by
|
||||
// builderT without having to write the config to a file.
|
||||
func RenderConfig(builderConfig map[string]interface{}, provisionerConfig []map[string]interface{}) string {
|
||||
// set up basic build template
|
||||
t := map[string][]map[string]interface{}{
|
||||
"builders": {
|
||||
// Setup basic docker config
|
||||
map[string]interface{}{
|
||||
"type": "test",
|
||||
"image": "ubuntu",
|
||||
"discard": true,
|
||||
},
|
||||
},
|
||||
"provisioners": []map[string]interface{}{},
|
||||
}
|
||||
// apply special builder overrides
|
||||
for k, v := range builderConfig {
|
||||
t["builders"][0][k] = v
|
||||
}
|
||||
// Apply special provisioner overrides
|
||||
t["provisioners"] = append(t["provisioners"], provisionerConfig...)
|
||||
|
||||
j, _ := json.Marshal(t)
|
||||
return string(j)
|
||||
}
|
||||
|
||||
// TestUploadDownload verifies that basic upload / download functionality works
|
||||
func TestUploadDownload(t *testing.T) {
|
||||
if os.Getenv("PACKER_ACC") == "" {
|
||||
t.Skip("This test is only run with PACKER_ACC=1")
|
||||
}
|
||||
|
||||
dockerBuilderExtraConfig := map[string]interface{}{
|
||||
"run_command": []string{"-d", "-i", "-t", "{{.Image}}", "/bin/sh"},
|
||||
}
|
||||
|
||||
dockerProvisionerConfig := []map[string]interface{}{
|
||||
{
|
||||
"type": "file",
|
||||
"source": "test-fixtures/onecakes/strawberry",
|
||||
"destination": "/strawberry-cake",
|
||||
},
|
||||
{
|
||||
"type": "file",
|
||||
"source": "/strawberry-cake",
|
||||
"destination": "my-strawberry-cake",
|
||||
"direction": "download",
|
||||
},
|
||||
}
|
||||
|
||||
configString := RenderConfig(dockerBuilderExtraConfig, dockerProvisionerConfig)
|
||||
|
||||
// this should be a precheck
|
||||
cmd := exec.Command("docker", "-v")
|
||||
err := cmd.Run()
|
||||
if err != nil {
|
||||
t.Error("docker command not found; please make sure docker is installed")
|
||||
}
|
||||
|
||||
builderT.Test(t, builderT.TestCase{
|
||||
Builder: &Builder{},
|
||||
Template: configString,
|
||||
Check: func(a []packersdk.Artifact) error {
|
||||
// Verify that the thing we downloaded is the same thing we sent up.
|
||||
// Complain loudly if it isn't.
|
||||
inputFile, err := ioutil.ReadFile("test-fixtures/onecakes/strawberry")
|
||||
if err != nil {
|
||||
return fmt.Errorf("Unable to read input file: %s", err)
|
||||
}
|
||||
outputFile, err := ioutil.ReadFile("my-strawberry-cake")
|
||||
if err != nil {
|
||||
return fmt.Errorf("Unable to read output file: %s", err)
|
||||
}
|
||||
if sha256.Sum256(inputFile) != sha256.Sum256(outputFile) {
|
||||
return fmt.Errorf("Input and output files do not match\n"+
|
||||
"Input:\n%s\nOutput:\n%s\n", inputFile, outputFile)
|
||||
}
|
||||
return nil
|
||||
},
|
||||
Teardown: func() error {
|
||||
// Cleanup. Honestly I don't know why you would want to get rid
|
||||
// of my strawberry cake. It's so tasty! Do you not like cake? Are you a
|
||||
// cake-hater? Or are you keeping all the cake all for yourself? So selfish!
|
||||
os.Remove("my-strawberry-cake")
|
||||
return nil
|
||||
},
|
||||
})
|
||||
}
|
||||
|
||||
// TestLargeDownload verifies that files are the appropriate size after being
|
||||
// downloaded. This is to identify and fix the race condition in #2793. You may
|
||||
// need to use github.com/cbednarski/rerun to verify since this problem occurs
|
||||
// only intermittently.
|
||||
func TestLargeDownload(t *testing.T) {
|
||||
if os.Getenv("PACKER_ACC") == "" {
|
||||
t.Skip("This test is only run with PACKER_ACC=1")
|
||||
}
|
||||
|
||||
dockerProvisionerConfig := []map[string]interface{}{
|
||||
{
|
||||
"type": "shell",
|
||||
"inline": []string{
|
||||
"dd if=/dev/urandom of=/tmp/cupcake bs=1M count=2",
|
||||
"dd if=/dev/urandom of=/tmp/bigcake bs=1M count=100",
|
||||
"sync",
|
||||
"md5sum /tmp/cupcake /tmp/bigcake",
|
||||
},
|
||||
},
|
||||
{
|
||||
"type": "file",
|
||||
"source": "/tmp/cupcake",
|
||||
"destination": "cupcake",
|
||||
"direction": "download",
|
||||
},
|
||||
{
|
||||
"type": "file",
|
||||
"source": "/tmp/bigcake",
|
||||
"destination": "bigcake",
|
||||
"direction": "download",
|
||||
},
|
||||
}
|
||||
|
||||
configString := RenderConfig(map[string]interface{}{}, dockerProvisionerConfig)
|
||||
|
||||
// this should be a precheck
|
||||
cmd := exec.Command("docker", "-v")
|
||||
err := cmd.Run()
|
||||
if err != nil {
|
||||
t.Error("docker command not found; please make sure docker is installed")
|
||||
}
|
||||
|
||||
builderT.Test(t, builderT.TestCase{
|
||||
Builder: &Builder{},
|
||||
Template: configString,
|
||||
Check: func(a []packersdk.Artifact) error {
|
||||
// Verify that the things we downloaded are the right size. Complain loudly
|
||||
// if they are not.
|
||||
//
|
||||
// cupcake should be 2097152 bytes
|
||||
// bigcake should be 104857600 bytes
|
||||
cupcake, err := os.Stat("cupcake")
|
||||
if err != nil {
|
||||
t.Fatalf("Unable to stat cupcake file: %s", err)
|
||||
}
|
||||
cupcakeExpected := int64(2097152)
|
||||
if cupcake.Size() != cupcakeExpected {
|
||||
t.Errorf("Expected cupcake to be %d bytes; found %d", cupcakeExpected, cupcake.Size())
|
||||
}
|
||||
|
||||
bigcake, err := os.Stat("bigcake")
|
||||
if err != nil {
|
||||
t.Fatalf("Unable to stat bigcake file: %s", err)
|
||||
}
|
||||
bigcakeExpected := int64(104857600)
|
||||
if bigcake.Size() != bigcakeExpected {
|
||||
t.Errorf("Expected bigcake to be %d bytes; found %d", bigcakeExpected, bigcake.Size())
|
||||
}
|
||||
|
||||
// TODO if we can, calculate a sha inside the container and compare to the
|
||||
// one we get after we pull it down. We will probably have to parse the log
|
||||
// or ui output to do this because we use /dev/urandom to create the file.
|
||||
|
||||
// if sha256.Sum256(inputFile) != sha256.Sum256(outputFile) {
|
||||
// t.Fatalf("Input and output files do not match\n"+
|
||||
// "Input:\n%s\nOutput:\n%s\n", inputFile, outputFile)
|
||||
// }
|
||||
return nil
|
||||
},
|
||||
Teardown: func() error {
|
||||
os.Remove("cupcake")
|
||||
os.Remove("bigcake")
|
||||
return nil
|
||||
},
|
||||
})
|
||||
|
||||
}
|
||||
|
||||
// TestFixUploadOwner verifies that owner of uploaded files is the user the container is running as.
|
||||
func TestFixUploadOwner(t *testing.T) {
|
||||
if os.Getenv("PACKER_ACC") == "" {
|
||||
t.Skip("This test is only run with PACKER_ACC=1")
|
||||
}
|
||||
|
||||
cmd := exec.Command("docker", "-v")
|
||||
err := cmd.Run()
|
||||
if err != nil {
|
||||
t.Error("docker command not found; please make sure docker is installed")
|
||||
}
|
||||
|
||||
dockerBuilderExtraConfig := map[string]interface{}{
|
||||
"run_command": []string{"-d", "-i", "-t", "-u", "42", "{{.Image}}", "/bin/sh"},
|
||||
}
|
||||
|
||||
testFixUploadOwnerProvisionersTemplate := []map[string]interface{}{
|
||||
{
|
||||
"type": "file",
|
||||
"source": "test-fixtures/onecakes/strawberry",
|
||||
"destination": "/tmp/strawberry-cake",
|
||||
},
|
||||
{
|
||||
"type": "file",
|
||||
"source": "test-fixtures/manycakes",
|
||||
"destination": "/tmp/",
|
||||
},
|
||||
{
|
||||
"type": "shell",
|
||||
"inline": "touch /tmp/testUploadOwner",
|
||||
},
|
||||
{
|
||||
"type": "shell",
|
||||
"inline": []string{
|
||||
"[ $(stat -c %u /tmp/strawberry-cake) -eq 42 ] || (echo 'Invalid owner of /tmp/strawberry-cake' && exit 1)",
|
||||
"[ $(stat -c %u /tmp/testUploadOwner) -eq 42 ] || (echo 'Invalid owner of /tmp/testUploadOwner' && exit 1)",
|
||||
"find /tmp/manycakes | xargs -n1 -IFILE /bin/sh -c '[ $(stat -c %u FILE) -eq 42 ] || (echo \"Invalid owner of FILE\" && exit 1)'",
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
configString := RenderConfig(dockerBuilderExtraConfig, testFixUploadOwnerProvisionersTemplate)
|
||||
builderT.Test(t, builderT.TestCase{
|
||||
Builder: &Builder{},
|
||||
Template: configString,
|
||||
})
|
||||
}
|
||||
@@ -1,147 +0,0 @@
|
||||
package docker
|
||||
|
||||
import (
|
||||
"io/ioutil"
|
||||
"os"
|
||||
"testing"
|
||||
)
|
||||
|
||||
func testConfig() map[string]interface{} {
|
||||
return map[string]interface{}{
|
||||
"export_path": "foo",
|
||||
"image": "bar",
|
||||
}
|
||||
}
|
||||
|
||||
func testConfigStruct(t *testing.T) *Config {
|
||||
var c Config
|
||||
warns, errs := c.Prepare(testConfig())
|
||||
if len(warns) > 0 {
|
||||
t.Fatalf("bad: %#v", len(warns))
|
||||
}
|
||||
if errs != nil {
|
||||
t.Fatalf("bad: %#v", errs)
|
||||
}
|
||||
|
||||
return &c
|
||||
}
|
||||
|
||||
func testConfigErr(t *testing.T, warns []string, err error) {
|
||||
if len(warns) > 0 {
|
||||
t.Fatalf("bad: %#v", warns)
|
||||
}
|
||||
if err == nil {
|
||||
t.Fatal("should error")
|
||||
}
|
||||
}
|
||||
|
||||
func testConfigOk(t *testing.T, warns []string, err error) {
|
||||
if len(warns) > 0 {
|
||||
t.Fatalf("bad: %#v", warns)
|
||||
}
|
||||
if err != nil {
|
||||
t.Fatalf("bad: %s", err)
|
||||
}
|
||||
}
|
||||
|
||||
func TestConfigPrepare_exportPath(t *testing.T) {
|
||||
td, err := ioutil.TempDir("", "packer")
|
||||
if err != nil {
|
||||
t.Fatalf("err: %s", err)
|
||||
}
|
||||
defer os.RemoveAll(td)
|
||||
|
||||
raw := testConfig()
|
||||
|
||||
// No export path. This is invalid. Previously this would not error during
|
||||
// validation and as a result the failure would happen at build time.
|
||||
delete(raw, "export_path")
|
||||
var c Config
|
||||
warns, errs := c.Prepare(raw)
|
||||
testConfigErr(t, warns, errs)
|
||||
|
||||
// Good export path
|
||||
raw["export_path"] = "good"
|
||||
warns, errs = c.Prepare(raw)
|
||||
testConfigOk(t, warns, errs)
|
||||
|
||||
// Bad export path (directory)
|
||||
raw["export_path"] = td
|
||||
warns, errs = c.Prepare(raw)
|
||||
testConfigErr(t, warns, errs)
|
||||
}
|
||||
|
||||
func TestConfigPrepare_exportPathAndCommit(t *testing.T) {
|
||||
raw := testConfig()
|
||||
|
||||
// Export but no commit (explicit default)
|
||||
raw["commit"] = false
|
||||
warns, errs := (&Config{}).Prepare(raw)
|
||||
testConfigOk(t, warns, errs)
|
||||
|
||||
// Commit AND export specified (invalid)
|
||||
raw["commit"] = true
|
||||
warns, errs = (&Config{}).Prepare(raw)
|
||||
testConfigErr(t, warns, errs)
|
||||
|
||||
// Commit but no export
|
||||
delete(raw, "export_path")
|
||||
warns, errs = (&Config{}).Prepare(raw)
|
||||
testConfigOk(t, warns, errs)
|
||||
}
|
||||
|
||||
func TestConfigPrepare_exportDiscard(t *testing.T) {
|
||||
raw := testConfig()
|
||||
|
||||
// Export but no discard (explicit default)
|
||||
raw["discard"] = false
|
||||
warns, errs := (&Config{}).Prepare(raw)
|
||||
testConfigOk(t, warns, errs)
|
||||
|
||||
// Discard AND export (invalid)
|
||||
raw["discard"] = true
|
||||
warns, errs = (&Config{}).Prepare(raw)
|
||||
testConfigErr(t, warns, errs)
|
||||
|
||||
// Discard but no export
|
||||
raw["discard"] = true
|
||||
delete(raw, "export_path")
|
||||
warns, errs = (&Config{}).Prepare(raw)
|
||||
testConfigOk(t, warns, errs)
|
||||
}
|
||||
|
||||
func TestConfigPrepare_image(t *testing.T) {
|
||||
raw := testConfig()
|
||||
|
||||
// No image
|
||||
delete(raw, "image")
|
||||
var c Config
|
||||
warns, errs := c.Prepare(raw)
|
||||
testConfigErr(t, warns, errs)
|
||||
|
||||
// Good image
|
||||
raw["image"] = "path"
|
||||
warns, errs = c.Prepare(raw)
|
||||
testConfigOk(t, warns, errs)
|
||||
}
|
||||
|
||||
func TestConfigPrepare_pull(t *testing.T) {
|
||||
raw := testConfig()
|
||||
|
||||
// No pull set
|
||||
delete(raw, "pull")
|
||||
var c Config
|
||||
warns, errs := c.Prepare(raw)
|
||||
testConfigOk(t, warns, errs)
|
||||
if !c.Pull {
|
||||
t.Fatal("should pull by default")
|
||||
}
|
||||
|
||||
// Pull set
|
||||
raw["pull"] = false
|
||||
warns, errs = c.Prepare(raw)
|
||||
testConfigOk(t, warns, errs)
|
||||
if c.Pull {
|
||||
t.Fatal("should not pull")
|
||||
}
|
||||
}
|
||||
@@ -1,7 +0,0 @@
|
||||
package docker
|
||||
|
||||
import "testing"
|
||||
|
||||
func TestDockerDriver_impl(t *testing.T) {
|
||||
var _ Driver = new(DockerDriver)
|
||||
}
|
||||
@@ -1,7 +0,0 @@
|
||||
package docker
|
||||
|
||||
import "testing"
|
||||
|
||||
func TestMockDriver_impl(t *testing.T) {
|
||||
var _ Driver = new(MockDriver)
|
||||
}
|
||||
@@ -1,68 +0,0 @@
|
||||
package docker
|
||||
|
||||
import (
|
||||
"context"
|
||||
"errors"
|
||||
"testing"
|
||||
|
||||
"github.com/hashicorp/packer-plugin-sdk/multistep"
|
||||
)
|
||||
|
||||
func testStepCommitState(t *testing.T) multistep.StateBag {
|
||||
state := testState(t)
|
||||
state.Put("container_id", "foo")
|
||||
return state
|
||||
}
|
||||
|
||||
func TestStepCommit_impl(t *testing.T) {
|
||||
var _ multistep.Step = new(StepCommit)
|
||||
}
|
||||
|
||||
func TestStepCommit(t *testing.T) {
|
||||
state := testStepCommitState(t)
|
||||
step := new(StepCommit)
|
||||
defer step.Cleanup(state)
|
||||
|
||||
driver := state.Get("driver").(*MockDriver)
|
||||
driver.CommitImageId = "bar"
|
||||
|
||||
// run the step
|
||||
if action := step.Run(context.Background(), state); action != multistep.ActionContinue {
|
||||
t.Fatalf("bad action: %#v", action)
|
||||
}
|
||||
|
||||
// verify we did the right thing
|
||||
if !driver.CommitCalled {
|
||||
t.Fatal("should've called")
|
||||
}
|
||||
|
||||
// verify the ID is saved
|
||||
idRaw, ok := state.GetOk("image_id")
|
||||
if !ok {
|
||||
t.Fatal("should've saved ID")
|
||||
}
|
||||
|
||||
id := idRaw.(string)
|
||||
if id != driver.CommitImageId {
|
||||
t.Fatalf("bad: %#v", id)
|
||||
}
|
||||
}
|
||||
|
||||
func TestStepCommit_error(t *testing.T) {
|
||||
state := testStepCommitState(t)
|
||||
step := new(StepCommit)
|
||||
defer step.Cleanup(state)
|
||||
|
||||
driver := state.Get("driver").(*MockDriver)
|
||||
driver.CommitErr = errors.New("foo")
|
||||
|
||||
// run the step
|
||||
if action := step.Run(context.Background(), state); action != multistep.ActionHalt {
|
||||
t.Fatalf("bad action: %#v", action)
|
||||
}
|
||||
|
||||
// verify the ID is not saved
|
||||
if _, ok := state.GetOk("image_id"); ok {
|
||||
t.Fatal("shouldn't save image ID")
|
||||
}
|
||||
}
|
||||
@@ -1,101 +0,0 @@
|
||||
package docker
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"context"
|
||||
"errors"
|
||||
"io/ioutil"
|
||||
"os"
|
||||
"testing"
|
||||
|
||||
"github.com/hashicorp/packer-plugin-sdk/multistep"
|
||||
)
|
||||
|
||||
func testStepExportState(t *testing.T) multistep.StateBag {
|
||||
state := testState(t)
|
||||
state.Put("container_id", "foo")
|
||||
return state
|
||||
}
|
||||
|
||||
func TestStepExport_impl(t *testing.T) {
|
||||
var _ multistep.Step = new(StepExport)
|
||||
}
|
||||
|
||||
func TestStepExport(t *testing.T) {
|
||||
state := testStepExportState(t)
|
||||
step := new(StepExport)
|
||||
defer step.Cleanup(state)
|
||||
|
||||
// Create a tempfile for our output path
|
||||
tf, err := ioutil.TempFile("", "packer")
|
||||
if err != nil {
|
||||
t.Fatalf("err: %s", err)
|
||||
}
|
||||
tf.Close()
|
||||
defer os.Remove(tf.Name())
|
||||
|
||||
config := state.Get("config").(*Config)
|
||||
config.ExportPath = tf.Name()
|
||||
driver := state.Get("driver").(*MockDriver)
|
||||
driver.ExportReader = bytes.NewReader([]byte("data!"))
|
||||
|
||||
// run the step
|
||||
if action := step.Run(context.Background(), state); action != multistep.ActionContinue {
|
||||
t.Fatalf("bad action: %#v", action)
|
||||
}
|
||||
|
||||
// verify we did the right thing
|
||||
if !driver.ExportCalled {
|
||||
t.Fatal("should've exported")
|
||||
}
|
||||
if driver.ExportID != "foo" {
|
||||
t.Fatalf("bad: %#v", driver.ExportID)
|
||||
}
|
||||
|
||||
// verify the data exported to the file
|
||||
contents, err := ioutil.ReadFile(tf.Name())
|
||||
if err != nil {
|
||||
t.Fatalf("err: %s", err)
|
||||
}
|
||||
|
||||
if string(contents) != "data!" {
|
||||
t.Fatalf("bad: %#v", string(contents))
|
||||
}
|
||||
}
|
||||
|
||||
func TestStepExport_error(t *testing.T) {
|
||||
state := testStepExportState(t)
|
||||
step := new(StepExport)
|
||||
defer step.Cleanup(state)
|
||||
|
||||
// Create a tempfile for our output path
|
||||
tf, err := ioutil.TempFile("", "packer")
|
||||
if err != nil {
|
||||
t.Fatalf("err: %s", err)
|
||||
}
|
||||
tf.Close()
|
||||
|
||||
if err := os.Remove(tf.Name()); err != nil {
|
||||
t.Fatalf("err: %s", err)
|
||||
}
|
||||
|
||||
config := state.Get("config").(*Config)
|
||||
config.ExportPath = tf.Name()
|
||||
driver := state.Get("driver").(*MockDriver)
|
||||
driver.ExportError = errors.New("foo")
|
||||
|
||||
// run the step
|
||||
if action := step.Run(context.Background(), state); action != multistep.ActionHalt {
|
||||
t.Fatalf("bad action: %#v", action)
|
||||
}
|
||||
|
||||
// verify we have an error
|
||||
if _, ok := state.GetOk("error"); !ok {
|
||||
t.Fatal("should have error")
|
||||
}
|
||||
|
||||
// verify we didn't make that file
|
||||
if _, err := os.Stat(tf.Name()); err == nil {
|
||||
t.Fatal("export path shouldn't exist")
|
||||
}
|
||||
}
|
||||
@@ -1,104 +0,0 @@
|
||||
package docker
|
||||
|
||||
import (
|
||||
"context"
|
||||
"errors"
|
||||
"testing"
|
||||
|
||||
"github.com/hashicorp/packer-plugin-sdk/multistep"
|
||||
)
|
||||
|
||||
func TestStepPull_impl(t *testing.T) {
|
||||
var _ multistep.Step = new(StepPull)
|
||||
}
|
||||
|
||||
func TestStepPull(t *testing.T) {
|
||||
state := testState(t)
|
||||
step := new(StepPull)
|
||||
defer step.Cleanup(state)
|
||||
|
||||
config := state.Get("config").(*Config)
|
||||
driver := state.Get("driver").(*MockDriver)
|
||||
|
||||
// run the step
|
||||
if action := step.Run(context.Background(), state); action != multistep.ActionContinue {
|
||||
t.Fatalf("bad action: %#v", action)
|
||||
}
|
||||
|
||||
// verify we did the right thing
|
||||
if !driver.PullCalled {
|
||||
t.Fatal("should've pulled")
|
||||
}
|
||||
if driver.PullImage != config.Image {
|
||||
t.Fatalf("bad: %#v", driver.PullImage)
|
||||
}
|
||||
}
|
||||
|
||||
func TestStepPull_error(t *testing.T) {
|
||||
state := testState(t)
|
||||
step := new(StepPull)
|
||||
defer step.Cleanup(state)
|
||||
|
||||
driver := state.Get("driver").(*MockDriver)
|
||||
driver.PullError = errors.New("foo")
|
||||
|
||||
// run the step
|
||||
if action := step.Run(context.Background(), state); action != multistep.ActionHalt {
|
||||
t.Fatalf("bad action: %#v", action)
|
||||
}
|
||||
|
||||
// verify we have an error
|
||||
if _, ok := state.GetOk("error"); !ok {
|
||||
t.Fatal("should have error")
|
||||
}
|
||||
}
|
||||
|
||||
func TestStepPull_login(t *testing.T) {
|
||||
state := testState(t)
|
||||
step := new(StepPull)
|
||||
defer step.Cleanup(state)
|
||||
|
||||
config := state.Get("config").(*Config)
|
||||
driver := state.Get("driver").(*MockDriver)
|
||||
|
||||
config.Login = true
|
||||
|
||||
// run the step
|
||||
if action := step.Run(context.Background(), state); action != multistep.ActionContinue {
|
||||
t.Fatalf("bad action: %#v", action)
|
||||
}
|
||||
|
||||
// verify we pulled
|
||||
if !driver.PullCalled {
|
||||
t.Fatal("should've pulled")
|
||||
}
|
||||
|
||||
// verify we logged in
|
||||
if !driver.LoginCalled {
|
||||
t.Fatal("should've logged in")
|
||||
}
|
||||
if !driver.LogoutCalled {
|
||||
t.Fatal("should've logged out")
|
||||
}
|
||||
}
|
||||
|
||||
func TestStepPull_noPull(t *testing.T) {
|
||||
state := testState(t)
|
||||
step := new(StepPull)
|
||||
defer step.Cleanup(state)
|
||||
|
||||
config := state.Get("config").(*Config)
|
||||
config.Pull = false
|
||||
|
||||
driver := state.Get("driver").(*MockDriver)
|
||||
|
||||
// run the step
|
||||
if action := step.Run(context.Background(), state); action != multistep.ActionContinue {
|
||||
t.Fatalf("bad action: %#v", action)
|
||||
}
|
||||
|
||||
// verify we did the right thing
|
||||
if driver.PullCalled {
|
||||
t.Fatal("shouldn't have pulled")
|
||||
}
|
||||
}
|
||||
@@ -1,97 +0,0 @@
|
||||
package docker
|
||||
|
||||
import (
|
||||
"context"
|
||||
"errors"
|
||||
"testing"
|
||||
|
||||
"github.com/hashicorp/packer-plugin-sdk/multistep"
|
||||
)
|
||||
|
||||
func testStepRunState(t *testing.T) multistep.StateBag {
|
||||
state := testState(t)
|
||||
state.Put("temp_dir", "/foo")
|
||||
return state
|
||||
}
|
||||
|
||||
func TestStepRun_impl(t *testing.T) {
|
||||
var _ multistep.Step = new(StepRun)
|
||||
}
|
||||
|
||||
func TestStepRun(t *testing.T) {
|
||||
state := testStepRunState(t)
|
||||
step := new(StepRun)
|
||||
defer step.Cleanup(state)
|
||||
|
||||
config := state.Get("config").(*Config)
|
||||
driver := state.Get("driver").(*MockDriver)
|
||||
driver.StartID = "foo"
|
||||
|
||||
// run the step
|
||||
if action := step.Run(context.Background(), state); action != multistep.ActionContinue {
|
||||
t.Fatalf("bad action: %#v", action)
|
||||
}
|
||||
|
||||
// verify we did the right thing
|
||||
if !driver.StartCalled {
|
||||
t.Fatal("should've called")
|
||||
}
|
||||
if driver.StartConfig.Image != config.Image {
|
||||
t.Fatalf("bad: %#v", driver.StartConfig.Image)
|
||||
}
|
||||
|
||||
// verify the ID is saved
|
||||
idRaw, ok := state.GetOk("container_id")
|
||||
if !ok {
|
||||
t.Fatal("should've saved ID")
|
||||
}
|
||||
|
||||
id := idRaw.(string)
|
||||
if id != "foo" {
|
||||
t.Fatalf("bad: %#v", id)
|
||||
}
|
||||
|
||||
// Verify we haven't called stop yet
|
||||
if driver.KillCalled {
|
||||
t.Fatal("should not have stopped")
|
||||
}
|
||||
|
||||
// Cleanup
|
||||
step.Cleanup(state)
|
||||
if !driver.KillCalled {
|
||||
t.Fatal("should've stopped")
|
||||
}
|
||||
if driver.KillID != id {
|
||||
t.Fatalf("bad: %#v", driver.StopID)
|
||||
}
|
||||
}
|
||||
|
||||
func TestStepRun_error(t *testing.T) {
|
||||
state := testStepRunState(t)
|
||||
step := new(StepRun)
|
||||
defer step.Cleanup(state)
|
||||
|
||||
driver := state.Get("driver").(*MockDriver)
|
||||
driver.StartError = errors.New("foo")
|
||||
|
||||
// run the step
|
||||
if action := step.Run(context.Background(), state); action != multistep.ActionHalt {
|
||||
t.Fatalf("bad action: %#v", action)
|
||||
}
|
||||
|
||||
// verify the ID is not saved
|
||||
if _, ok := state.GetOk("container_id"); ok {
|
||||
t.Fatal("shouldn't save container ID")
|
||||
}
|
||||
|
||||
// Verify we haven't called stop yet
|
||||
if driver.KillCalled {
|
||||
t.Fatal("should not have stopped")
|
||||
}
|
||||
|
||||
// Cleanup
|
||||
step.Cleanup(state)
|
||||
if driver.KillCalled {
|
||||
t.Fatal("should not have stopped")
|
||||
}
|
||||
}
|
||||
@@ -1,51 +0,0 @@
|
||||
package docker
|
||||
|
||||
import (
|
||||
"context"
|
||||
"testing"
|
||||
|
||||
"github.com/hashicorp/packer-plugin-sdk/multistep"
|
||||
"github.com/hashicorp/packer-plugin-sdk/packerbuilderdata"
|
||||
)
|
||||
|
||||
func TestStepSetGeneratedData_Run(t *testing.T) {
|
||||
state := testState(t)
|
||||
step := new(StepSetGeneratedData)
|
||||
step.GeneratedData = &packerbuilderdata.GeneratedData{State: state}
|
||||
driver := state.Get("driver").(*MockDriver)
|
||||
driver.Sha256Result = "80B3BB1B1696E73A9B19DEEF92F664F8979F948DF348088B61F9A3477655AF64"
|
||||
state.Put("image_id", "12345")
|
||||
|
||||
if action := step.Run(context.TODO(), state); action != multistep.ActionContinue {
|
||||
t.Fatalf("Should not halt")
|
||||
}
|
||||
if !driver.Sha256Called {
|
||||
t.Fatalf("driver.SHA256 should be called")
|
||||
}
|
||||
if driver.Sha256Id != "12345" {
|
||||
t.Fatalf("driver.SHA256 got wrong image it: %s", driver.Sha256Id)
|
||||
}
|
||||
genData := state.Get("generated_data").(map[string]interface{})
|
||||
imgSha256 := genData["ImageSha256"].(string)
|
||||
if imgSha256 != driver.Sha256Result {
|
||||
t.Fatalf("Expected ImageSha256 to be %s but was %s", driver.Sha256Result, imgSha256)
|
||||
}
|
||||
|
||||
// Image ID not implement
|
||||
state = testState(t)
|
||||
step.GeneratedData = &packerbuilderdata.GeneratedData{State: state}
|
||||
driver = state.Get("driver").(*MockDriver)
|
||||
notImplementedMsg := "ERR_IMAGE_SHA256_NOT_FOUND"
|
||||
|
||||
if action := step.Run(context.TODO(), state); action != multistep.ActionContinue {
|
||||
t.Fatalf("Should not halt")
|
||||
}
|
||||
if driver.Sha256Called {
|
||||
t.Fatalf("driver.SHA256 should not be called")
|
||||
}
|
||||
genData = state.Get("generated_data").(map[string]interface{})
|
||||
imgSha256 = genData["ImageSha256"].(string)
|
||||
if imgSha256 != notImplementedMsg {
|
||||
t.Fatalf("Expected ImageSha256 to be %s but was %s", notImplementedMsg, imgSha256)
|
||||
}
|
||||
}
|
||||
@@ -1,52 +0,0 @@
|
||||
package docker
|
||||
|
||||
import (
|
||||
"context"
|
||||
"os"
|
||||
"testing"
|
||||
|
||||
"github.com/hashicorp/packer-plugin-sdk/multistep"
|
||||
)
|
||||
|
||||
func TestStepTempDir_impl(t *testing.T) {
|
||||
var _ multistep.Step = new(StepTempDir)
|
||||
}
|
||||
|
||||
func testStepTempDir_impl(t *testing.T) string {
|
||||
state := testState(t)
|
||||
step := new(StepTempDir)
|
||||
defer step.Cleanup(state)
|
||||
|
||||
// sanity test
|
||||
if _, ok := state.GetOk("temp_dir"); ok {
|
||||
t.Fatalf("temp_dir should not be in state yet")
|
||||
}
|
||||
|
||||
// run the step
|
||||
if action := step.Run(context.Background(), state); action != multistep.ActionContinue {
|
||||
t.Fatalf("bad action: %#v", action)
|
||||
}
|
||||
|
||||
// Verify that we got the temp dir
|
||||
dirRaw, ok := state.GetOk("temp_dir")
|
||||
if !ok {
|
||||
t.Fatalf("should've made temp_dir")
|
||||
}
|
||||
dir := dirRaw.(string)
|
||||
|
||||
if _, err := os.Stat(dir); err != nil {
|
||||
t.Fatalf("Stat for %s failed: err: %s", err, dir)
|
||||
}
|
||||
|
||||
// Cleanup
|
||||
step.Cleanup(state)
|
||||
if _, err := os.Stat(dir); err == nil {
|
||||
t.Fatalf("dir should be gone")
|
||||
}
|
||||
|
||||
return dir
|
||||
}
|
||||
|
||||
func TestStepTempDir(t *testing.T) {
|
||||
testStepTempDir_impl(t)
|
||||
}
|
||||
@@ -1,21 +0,0 @@
|
||||
package docker
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"testing"
|
||||
|
||||
"github.com/hashicorp/packer-plugin-sdk/multistep"
|
||||
packersdk "github.com/hashicorp/packer-plugin-sdk/packer"
|
||||
)
|
||||
|
||||
func testState(t *testing.T) multistep.StateBag {
|
||||
state := new(multistep.BasicStateBag)
|
||||
state.Put("config", testConfigStruct(t))
|
||||
state.Put("driver", &MockDriver{})
|
||||
state.Put("hook", &packersdk.MockHook{})
|
||||
state.Put("ui", &packersdk.BasicUi{
|
||||
Reader: new(bytes.Buffer),
|
||||
Writer: new(bytes.Buffer),
|
||||
})
|
||||
return state
|
||||
}
|
||||
@@ -1 +0,0 @@
|
||||
chocolate!
|
||||
@@ -1 +0,0 @@
|
||||
vanilla!
|
||||
@@ -1 +0,0 @@
|
||||
strawberry!
|
||||
@@ -1,13 +0,0 @@
|
||||
package version
|
||||
|
||||
import (
|
||||
"github.com/hashicorp/packer-plugin-sdk/version"
|
||||
packerVersion "github.com/hashicorp/packer/version"
|
||||
)
|
||||
|
||||
var DigitalOceanPluginVersion *version.PluginVersion
|
||||
|
||||
func init() {
|
||||
DigitalOceanPluginVersion = version.InitializePluginVersion(
|
||||
packerVersion.Version, packerVersion.VersionPrerelease)
|
||||
}
|
||||
@@ -210,6 +210,8 @@ type Config struct {
|
||||
// - The contents of the script file will be wrapped in Packer's startup script wrapper, unless `wrap_startup_script` is disabled. See `wrap_startup_script` for more details.
|
||||
// - Not supported by Windows instances. See [Startup Scripts for Windows](https://cloud.google.com/compute/docs/startupscript#providing_a_startup_script_for_windows_instances) for more details.
|
||||
StartupScriptFile string `mapstructure:"startup_script_file" required:"false"`
|
||||
// The time to wait for windows password to be retrieved. Defaults to "3m".
|
||||
WindowsPasswordTimeout time.Duration `mapstructure:"windows_password_timeout" required:"false"`
|
||||
// For backwards compatibility this option defaults to `"true"` in the future it will default to `"false"`.
|
||||
// If "true", the contents of `startup_script_file` or `"startup_script"` in the instance metadata
|
||||
// is wrapped in a Packer specific script that tracks the execution and completion of the provided
|
||||
@@ -542,6 +544,10 @@ func (c *Config) Prepare(raws ...interface{}) ([]string, error) {
|
||||
c.WrapStartupScriptFile = config.TriTrue
|
||||
}
|
||||
}
|
||||
// Check windows password timeout is provided
|
||||
if c.WindowsPasswordTimeout == 0 {
|
||||
c.WindowsPasswordTimeout = 3 * time.Minute
|
||||
}
|
||||
|
||||
// Check for any errors.
|
||||
if errs != nil && len(errs.Errors) > 0 {
|
||||
|
||||
@@ -112,6 +112,7 @@ type FlatConfig struct {
|
||||
SourceImageFamily *string `mapstructure:"source_image_family" required:"true" cty:"source_image_family" hcl:"source_image_family"`
|
||||
SourceImageProjectId []string `mapstructure:"source_image_project_id" required:"false" cty:"source_image_project_id" hcl:"source_image_project_id"`
|
||||
StartupScriptFile *string `mapstructure:"startup_script_file" required:"false" cty:"startup_script_file" hcl:"startup_script_file"`
|
||||
WindowsPasswordTimeout *string `mapstructure:"windows_password_timeout" required:"false" cty:"windows_password_timeout" hcl:"windows_password_timeout"`
|
||||
WrapStartupScriptFile *bool `mapstructure:"wrap_startup_script" required:"false" cty:"wrap_startup_script" hcl:"wrap_startup_script"`
|
||||
Subnetwork *string `mapstructure:"subnetwork" required:"false" cty:"subnetwork" hcl:"subnetwork"`
|
||||
Tags []string `mapstructure:"tags" required:"false" cty:"tags" hcl:"tags"`
|
||||
@@ -236,6 +237,7 @@ func (*FlatConfig) HCL2Spec() map[string]hcldec.Spec {
|
||||
"source_image_family": &hcldec.AttrSpec{Name: "source_image_family", Type: cty.String, Required: false},
|
||||
"source_image_project_id": &hcldec.AttrSpec{Name: "source_image_project_id", Type: cty.List(cty.String), Required: false},
|
||||
"startup_script_file": &hcldec.AttrSpec{Name: "startup_script_file", Type: cty.String, Required: false},
|
||||
"windows_password_timeout": &hcldec.AttrSpec{Name: "windows_password_timeout", Type: cty.String, Required: false},
|
||||
"wrap_startup_script": &hcldec.AttrSpec{Name: "wrap_startup_script", Type: cty.Bool, Required: false},
|
||||
"subnetwork": &hcldec.AttrSpec{Name: "subnetwork", Type: cty.String, Required: false},
|
||||
"tags": &hcldec.AttrSpec{Name: "tags", Type: cty.List(cty.String), Required: false},
|
||||
|
||||
@@ -107,13 +107,14 @@ type InstanceConfig struct {
|
||||
// WindowsPasswordConfig is the data structure that GCE needs to encrypt the created
|
||||
// windows password.
|
||||
type WindowsPasswordConfig struct {
|
||||
key *rsa.PrivateKey
|
||||
password string
|
||||
UserName string `json:"userName"`
|
||||
Modulus string `json:"modulus"`
|
||||
Exponent string `json:"exponent"`
|
||||
Email string `json:"email"`
|
||||
ExpireOn time.Time `json:"expireOn"`
|
||||
key *rsa.PrivateKey
|
||||
password string
|
||||
UserName string `json:"userName"`
|
||||
Modulus string `json:"modulus"`
|
||||
Exponent string `json:"exponent"`
|
||||
Email string `json:"email"`
|
||||
ExpireOn time.Time `json:"expireOn"`
|
||||
WindowsPasswordTimeout time.Duration `json:"timeout"`
|
||||
}
|
||||
|
||||
type windowsPasswordResponse struct {
|
||||
|
||||
@@ -253,7 +253,6 @@ func (d *driverGCE) GetImage(name string, fromFamily bool) (*Image, error) {
|
||||
"ubuntu-os-cloud",
|
||||
"windows-cloud",
|
||||
"windows-sql-cloud",
|
||||
"gce-uefi-images",
|
||||
"gce-nvme",
|
||||
// misc
|
||||
"google-containers",
|
||||
@@ -568,7 +567,7 @@ func (d *driverGCE) createWindowsPassword(errCh chan<- error, name, zone string,
|
||||
return
|
||||
}
|
||||
|
||||
timeout := time.Now().Add(time.Minute * 3)
|
||||
timeout := time.Now().Add(c.WindowsPasswordTimeout)
|
||||
hash := sha1.New()
|
||||
random := rand.Reader
|
||||
|
||||
|
||||
@@ -60,12 +60,13 @@ func (s *StepCreateWindowsPassword) Run(ctx context.Context, state multistep.Sta
|
||||
}
|
||||
|
||||
data := WindowsPasswordConfig{
|
||||
key: priv,
|
||||
UserName: c.Comm.WinRMUser,
|
||||
Modulus: base64.StdEncoding.EncodeToString(priv.N.Bytes()),
|
||||
Exponent: base64.StdEncoding.EncodeToString(buf[1:]),
|
||||
Email: email,
|
||||
ExpireOn: time.Now().Add(time.Minute * 5),
|
||||
key: priv,
|
||||
UserName: c.Comm.WinRMUser,
|
||||
Modulus: base64.StdEncoding.EncodeToString(priv.N.Bytes()),
|
||||
Exponent: base64.StdEncoding.EncodeToString(buf[1:]),
|
||||
Email: email,
|
||||
ExpireOn: time.Now().Add(time.Minute * 5),
|
||||
WindowsPasswordTimeout: c.WindowsPasswordTimeout,
|
||||
}
|
||||
|
||||
if s.Debug {
|
||||
@@ -98,7 +99,7 @@ func (s *StepCreateWindowsPassword) Run(ctx context.Context, state multistep.Sta
|
||||
ui.Message("Waiting for windows password to complete...")
|
||||
select {
|
||||
case err = <-errCh:
|
||||
case <-time.After(c.StateTimeout):
|
||||
case <-time.After(c.WindowsPasswordTimeout):
|
||||
err = errors.New("time out while waiting for the password to be created")
|
||||
}
|
||||
}
|
||||
|
||||
@@ -211,12 +211,7 @@ func (b *Builder) Run(ctx context.Context, ui packersdk.Ui, hook packersdk.Hook)
|
||||
Directories: b.config.FloppyConfig.FloppyDirectories,
|
||||
Label: b.config.FloppyConfig.FloppyLabel,
|
||||
},
|
||||
&commonsteps.StepHTTPServer{
|
||||
HTTPDir: b.config.HTTPDir,
|
||||
HTTPPortMin: b.config.HTTPPortMin,
|
||||
HTTPPortMax: b.config.HTTPPortMax,
|
||||
HTTPAddress: b.config.HTTPAddress,
|
||||
},
|
||||
commonsteps.HTTPServerFromHTTPConfig(&b.config.HTTPConfig),
|
||||
&hypervcommon.StepCreateSwitch{
|
||||
SwitchName: b.config.SwitchName,
|
||||
},
|
||||
@@ -294,7 +289,7 @@ func (b *Builder) Run(ctx context.Context, ui packersdk.Ui, hook packersdk.Hook)
|
||||
// configure the communicator ssh, winrm
|
||||
&communicator.StepConnect{
|
||||
Config: &b.config.SSHConfig.Comm,
|
||||
Host: hypervcommon.CommHost(b.config.SSHConfig.Comm.SSHHost),
|
||||
Host: hypervcommon.CommHost(b.config.SSHConfig.Comm.Host()),
|
||||
SSHConfig: b.config.SSHConfig.Comm.SSHConfigFunc(),
|
||||
},
|
||||
|
||||
|
||||
@@ -19,6 +19,7 @@ type FlatConfig struct {
|
||||
PackerUserVars map[string]string `mapstructure:"packer_user_variables" cty:"packer_user_variables" hcl:"packer_user_variables"`
|
||||
PackerSensitiveVars []string `mapstructure:"packer_sensitive_variables" cty:"packer_sensitive_variables" hcl:"packer_sensitive_variables"`
|
||||
HTTPDir *string `mapstructure:"http_directory" cty:"http_directory" hcl:"http_directory"`
|
||||
HTTPContent map[string]string `mapstructure:"http_content" cty:"http_content" hcl:"http_content"`
|
||||
HTTPPortMin *int `mapstructure:"http_port_min" cty:"http_port_min" hcl:"http_port_min"`
|
||||
HTTPPortMax *int `mapstructure:"http_port_max" cty:"http_port_max" hcl:"http_port_max"`
|
||||
HTTPAddress *string `mapstructure:"http_bind_address" cty:"http_bind_address" hcl:"http_bind_address"`
|
||||
@@ -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_sensitive_variables": &hcldec.AttrSpec{Name: "packer_sensitive_variables", Type: cty.List(cty.String), Required: false},
|
||||
"http_directory": &hcldec.AttrSpec{Name: "http_directory", Type: cty.String, Required: false},
|
||||
"http_content": &hcldec.AttrSpec{Name: "http_content", Type: cty.Map(cty.String), Required: false},
|
||||
"http_port_min": &hcldec.AttrSpec{Name: "http_port_min", Type: cty.Number, Required: false},
|
||||
"http_port_max": &hcldec.AttrSpec{Name: "http_port_max", Type: cty.Number, Required: false},
|
||||
"http_bind_address": &hcldec.AttrSpec{Name: "http_bind_address", Type: cty.String, Required: false},
|
||||
|
||||
@@ -251,12 +251,7 @@ func (b *Builder) Run(ctx context.Context, ui packersdk.Ui, hook packersdk.Hook)
|
||||
Directories: b.config.FloppyConfig.FloppyDirectories,
|
||||
Label: b.config.FloppyConfig.FloppyLabel,
|
||||
},
|
||||
&commonsteps.StepHTTPServer{
|
||||
HTTPDir: b.config.HTTPDir,
|
||||
HTTPPortMin: b.config.HTTPPortMin,
|
||||
HTTPPortMax: b.config.HTTPPortMax,
|
||||
HTTPAddress: b.config.HTTPAddress,
|
||||
},
|
||||
commonsteps.HTTPServerFromHTTPConfig(&b.config.HTTPConfig),
|
||||
&hypervcommon.StepCreateSwitch{
|
||||
SwitchName: b.config.SwitchName,
|
||||
},
|
||||
@@ -334,7 +329,7 @@ func (b *Builder) Run(ctx context.Context, ui packersdk.Ui, hook packersdk.Hook)
|
||||
// configure the communicator ssh, winrm
|
||||
&communicator.StepConnect{
|
||||
Config: &b.config.SSHConfig.Comm,
|
||||
Host: hypervcommon.CommHost(b.config.SSHConfig.Comm.SSHHost),
|
||||
Host: hypervcommon.CommHost(b.config.SSHConfig.Comm.Host()),
|
||||
SSHConfig: b.config.SSHConfig.Comm.SSHConfigFunc(),
|
||||
},
|
||||
|
||||
|
||||
@@ -19,6 +19,7 @@ type FlatConfig struct {
|
||||
PackerUserVars map[string]string `mapstructure:"packer_user_variables" cty:"packer_user_variables" hcl:"packer_user_variables"`
|
||||
PackerSensitiveVars []string `mapstructure:"packer_sensitive_variables" cty:"packer_sensitive_variables" hcl:"packer_sensitive_variables"`
|
||||
HTTPDir *string `mapstructure:"http_directory" cty:"http_directory" hcl:"http_directory"`
|
||||
HTTPContent map[string]string `mapstructure:"http_content" cty:"http_content" hcl:"http_content"`
|
||||
HTTPPortMin *int `mapstructure:"http_port_min" cty:"http_port_min" hcl:"http_port_min"`
|
||||
HTTPPortMax *int `mapstructure:"http_port_max" cty:"http_port_max" hcl:"http_port_max"`
|
||||
HTTPAddress *string `mapstructure:"http_bind_address" cty:"http_bind_address" hcl:"http_bind_address"`
|
||||
@@ -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_sensitive_variables": &hcldec.AttrSpec{Name: "packer_sensitive_variables", Type: cty.List(cty.String), Required: false},
|
||||
"http_directory": &hcldec.AttrSpec{Name: "http_directory", Type: cty.String, Required: false},
|
||||
"http_content": &hcldec.AttrSpec{Name: "http_content", Type: cty.Map(cty.String), Required: false},
|
||||
"http_port_min": &hcldec.AttrSpec{Name: "http_port_min", Type: cty.Number, Required: false},
|
||||
"http_port_max": &hcldec.AttrSpec{Name: "http_port_max", Type: cty.Number, Required: false},
|
||||
"http_bind_address": &hcldec.AttrSpec{Name: "http_bind_address", Type: cty.String, Required: false},
|
||||
|
||||
@@ -151,7 +151,7 @@ func (b *Builder) Run(ctx context.Context, ui packersdk.Ui, hook packersdk.Hook)
|
||||
&communicator.StepConnect{
|
||||
Config: &b.config.RunConfig.Comm,
|
||||
Host: CommHost(
|
||||
b.config.RunConfig.Comm.SSHHost,
|
||||
b.config.RunConfig.Comm.Host(),
|
||||
computeClient,
|
||||
b.config.SSHInterface,
|
||||
b.config.SSHIPVersion),
|
||||
|
||||
@@ -253,7 +253,7 @@ func (c *Config) Prepare(raws ...interface{}) error {
|
||||
|
||||
if _, err := configProvider.PrivateRSAKey(); err != nil {
|
||||
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
|
||||
|
||||
@@ -32,6 +32,9 @@ func (s *stepCreateOMI) Run(ctx context.Context, state multistep.StateBag) multi
|
||||
ImageName: omiName,
|
||||
BlockDeviceMappings: config.BlockDevices.BuildOscOMIDevices(),
|
||||
}
|
||||
if config.OMIDescription != "" {
|
||||
createOpts.Description = config.OMIDescription
|
||||
}
|
||||
|
||||
resp, _, err := oscconn.ImageApi.CreateImage(context.Background(), &osc.CreateImageOpts{
|
||||
CreateImageRequest: optional.NewInterface(createOpts),
|
||||
|
||||
@@ -37,6 +37,9 @@ func (s *StepRegisterOMI) Run(ctx context.Context, state multistep.StateBag) mul
|
||||
BlockDeviceMappings: blockDevices,
|
||||
}
|
||||
|
||||
if config.OMIDescription != "" {
|
||||
registerOpts.Description = config.OMIDescription
|
||||
}
|
||||
registerResp, _, err := oscconn.ImageApi.CreateImage(context.Background(), &osc.CreateImageOpts{
|
||||
CreateImageRequest: optional.NewInterface(registerOpts),
|
||||
})
|
||||
|
||||
@@ -75,6 +75,10 @@ func (s *StepCreateOMI) Run(ctx context.Context, state multistep.StateBag) multi
|
||||
registerOpts = buildRegisterOpts(config, image, newMappings)
|
||||
}
|
||||
|
||||
if config.OMIDescription != "" {
|
||||
registerOpts.Description = config.OMIDescription
|
||||
}
|
||||
|
||||
registerResp, _, err := osconn.ImageApi.CreateImage(context.Background(), &osc.CreateImageOpts{
|
||||
CreateImageRequest: optional.NewInterface(registerOpts),
|
||||
})
|
||||
|
||||
@@ -209,12 +209,7 @@ func (b *Builder) Run(ctx context.Context, ui packersdk.Ui, hook packersdk.Hook)
|
||||
Directories: b.config.FloppyConfig.FloppyDirectories,
|
||||
Label: b.config.FloppyConfig.FloppyLabel,
|
||||
},
|
||||
&commonsteps.StepHTTPServer{
|
||||
HTTPDir: b.config.HTTPDir,
|
||||
HTTPPortMin: b.config.HTTPPortMin,
|
||||
HTTPPortMax: b.config.HTTPPortMax,
|
||||
HTTPAddress: b.config.HTTPAddress,
|
||||
},
|
||||
commonsteps.HTTPServerFromHTTPConfig(&b.config.HTTPConfig),
|
||||
new(stepCreateVM),
|
||||
new(stepCreateDisk),
|
||||
new(stepSetBootOrder),
|
||||
@@ -238,7 +233,7 @@ func (b *Builder) Run(ctx context.Context, ui packersdk.Ui, hook packersdk.Hook)
|
||||
},
|
||||
&communicator.StepConnect{
|
||||
Config: &b.config.SSHConfig.Comm,
|
||||
Host: parallelscommon.CommHost(b.config.SSHConfig.Comm.SSHHost),
|
||||
Host: parallelscommon.CommHost(b.config.SSHConfig.Comm.Host()),
|
||||
SSHConfig: b.config.SSHConfig.Comm.SSHConfigFunc(),
|
||||
},
|
||||
¶llelscommon.StepUploadVersion{
|
||||
|
||||
@@ -19,6 +19,7 @@ type FlatConfig struct {
|
||||
PackerUserVars map[string]string `mapstructure:"packer_user_variables" cty:"packer_user_variables" hcl:"packer_user_variables"`
|
||||
PackerSensitiveVars []string `mapstructure:"packer_sensitive_variables" cty:"packer_sensitive_variables" hcl:"packer_sensitive_variables"`
|
||||
HTTPDir *string `mapstructure:"http_directory" cty:"http_directory" hcl:"http_directory"`
|
||||
HTTPContent map[string]string `mapstructure:"http_content" cty:"http_content" hcl:"http_content"`
|
||||
HTTPPortMin *int `mapstructure:"http_port_min" cty:"http_port_min" hcl:"http_port_min"`
|
||||
HTTPPortMax *int `mapstructure:"http_port_max" cty:"http_port_max" hcl:"http_port_max"`
|
||||
HTTPAddress *string `mapstructure:"http_bind_address" cty:"http_bind_address" hcl:"http_bind_address"`
|
||||
@@ -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_sensitive_variables": &hcldec.AttrSpec{Name: "packer_sensitive_variables", Type: cty.List(cty.String), Required: false},
|
||||
"http_directory": &hcldec.AttrSpec{Name: "http_directory", Type: cty.String, Required: false},
|
||||
"http_content": &hcldec.AttrSpec{Name: "http_content", Type: cty.Map(cty.String), Required: false},
|
||||
"http_port_min": &hcldec.AttrSpec{Name: "http_port_min", Type: cty.Number, Required: false},
|
||||
"http_port_max": &hcldec.AttrSpec{Name: "http_port_max", Type: cty.Number, Required: false},
|
||||
"http_bind_address": &hcldec.AttrSpec{Name: "http_bind_address", Type: cty.String, Required: false},
|
||||
|
||||
@@ -87,7 +87,7 @@ func (b *Builder) Run(ctx context.Context, ui packersdk.Ui, hook packersdk.Hook)
|
||||
},
|
||||
&communicator.StepConnect{
|
||||
Config: &b.config.SSHConfig.Comm,
|
||||
Host: parallelscommon.CommHost(b.config.SSHConfig.Comm.SSHHost),
|
||||
Host: parallelscommon.CommHost(b.config.SSHConfig.Comm.Host()),
|
||||
SSHConfig: b.config.SSHConfig.Comm.SSHConfigFunc(),
|
||||
},
|
||||
¶llelscommon.StepUploadVersion{
|
||||
|
||||
@@ -20,6 +20,7 @@ type FlatConfig struct {
|
||||
PackerUserVars map[string]string `mapstructure:"packer_user_variables" cty:"packer_user_variables" hcl:"packer_user_variables"`
|
||||
PackerSensitiveVars []string `mapstructure:"packer_sensitive_variables" cty:"packer_sensitive_variables" hcl:"packer_sensitive_variables"`
|
||||
HTTPDir *string `mapstructure:"http_directory" cty:"http_directory" hcl:"http_directory"`
|
||||
HTTPContent map[string]string `mapstructure:"http_content" cty:"http_content" hcl:"http_content"`
|
||||
HTTPPortMin *int `mapstructure:"http_port_min" cty:"http_port_min" hcl:"http_port_min"`
|
||||
HTTPPortMax *int `mapstructure:"http_port_max" cty:"http_port_max" hcl:"http_port_max"`
|
||||
HTTPAddress *string `mapstructure:"http_bind_address" cty:"http_bind_address" hcl:"http_bind_address"`
|
||||
@@ -81,6 +82,7 @@ type FlatConfig struct {
|
||||
SkipCertValidation *bool `mapstructure:"insecure_skip_tls_verify" cty:"insecure_skip_tls_verify" hcl:"insecure_skip_tls_verify"`
|
||||
Username *string `mapstructure:"username" cty:"username" hcl:"username"`
|
||||
Password *string `mapstructure:"password" cty:"password" hcl:"password"`
|
||||
Token *string `mapstructure:"token" cty:"token" hcl:"token"`
|
||||
Node *string `mapstructure:"node" cty:"node" hcl:"node"`
|
||||
Pool *string `mapstructure:"pool" cty:"pool" hcl:"pool"`
|
||||
VMName *string `mapstructure:"vm_name" cty:"vm_name" hcl:"vm_name"`
|
||||
@@ -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_sensitive_variables": &hcldec.AttrSpec{Name: "packer_sensitive_variables", Type: cty.List(cty.String), Required: false},
|
||||
"http_directory": &hcldec.AttrSpec{Name: "http_directory", Type: cty.String, Required: false},
|
||||
"http_content": &hcldec.AttrSpec{Name: "http_content", Type: cty.Map(cty.String), Required: false},
|
||||
"http_port_min": &hcldec.AttrSpec{Name: "http_port_min", Type: cty.Number, Required: false},
|
||||
"http_port_max": &hcldec.AttrSpec{Name: "http_port_max", Type: cty.Number, Required: false},
|
||||
"http_bind_address": &hcldec.AttrSpec{Name: "http_bind_address", Type: cty.String, Required: false},
|
||||
@@ -190,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},
|
||||
"username": &hcldec.AttrSpec{Name: "username", Type: cty.String, Required: false},
|
||||
"password": &hcldec.AttrSpec{Name: "password", Type: cty.String, Required: false},
|
||||
"token": &hcldec.AttrSpec{Name: "token", Type: cty.String, Required: false},
|
||||
"node": &hcldec.AttrSpec{Name: "node", Type: cty.String, Required: false},
|
||||
"pool": &hcldec.AttrSpec{Name: "pool", Type: cty.String, Required: false},
|
||||
"vm_name": &hcldec.AttrSpec{Name: "vm_name", Type: cty.String, Required: false},
|
||||
|
||||
@@ -2,7 +2,6 @@ package proxmox
|
||||
|
||||
import (
|
||||
"context"
|
||||
"crypto/tls"
|
||||
"errors"
|
||||
"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) {
|
||||
var err error
|
||||
tlsConfig := &tls.Config{
|
||||
InsecureSkipVerify: b.config.SkipCertValidation,
|
||||
}
|
||||
b.proxmoxClient, err = proxmox.NewClient(b.config.proxmoxURL.String(), nil, tlsConfig)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
err = b.proxmoxClient.Login(b.config.Username, b.config.Password, "")
|
||||
b.proxmoxClient, err = newProxmoxClient(b.config)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
@@ -61,12 +52,7 @@ func (b *Builder) Run(ctx context.Context, ui packersdk.Ui, hook packersdk.Hook,
|
||||
&stepStartVM{
|
||||
vmCreator: b.vmCreator,
|
||||
},
|
||||
&commonsteps.StepHTTPServer{
|
||||
HTTPDir: b.config.HTTPDir,
|
||||
HTTPPortMin: b.config.HTTPPortMin,
|
||||
HTTPPortMax: b.config.HTTPPortMax,
|
||||
HTTPAddress: b.config.HTTPAddress,
|
||||
},
|
||||
commonsteps.HTTPServerFromHTTPConfig(&b.config.HTTPConfig),
|
||||
&stepTypeBootCommand{
|
||||
BootConfig: b.config.BootConfig,
|
||||
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"`
|
||||
Username string `mapstructure:"username"`
|
||||
Password string `mapstructure:"password"`
|
||||
Token string `mapstructure:"token"`
|
||||
Node string `mapstructure:"node"`
|
||||
Pool string `mapstructure:"pool"`
|
||||
|
||||
@@ -73,8 +74,8 @@ type additionalISOsConfig struct {
|
||||
ISOFile string `mapstructure:"iso_file"`
|
||||
ISOStoragePool string `mapstructure:"iso_storage_pool"`
|
||||
Unmount bool `mapstructure:"unmount"`
|
||||
ShouldUploadISO bool
|
||||
DownloadPathKey string
|
||||
ShouldUploadISO bool `mapstructure-to-hcl2:",skip"`
|
||||
DownloadPathKey string `mapstructure-to-hcl2:",skip"`
|
||||
}
|
||||
|
||||
type nicConfig struct {
|
||||
@@ -135,6 +136,9 @@ func (c *Config) Prepare(upper interface{}, raws ...interface{}) ([]string, []st
|
||||
if c.Password == "" {
|
||||
c.Password = os.Getenv("PROXMOX_PASSWORD")
|
||||
}
|
||||
if c.Token == "" {
|
||||
c.Token = os.Getenv("PROXMOX_TOKEN")
|
||||
}
|
||||
if c.BootKeyInterval == 0 && os.Getenv(bootcommand.PackerKeyEnv) != "" {
|
||||
var err error
|
||||
c.BootKeyInterval, err = time.ParseDuration(os.Getenv(bootcommand.PackerKeyEnv))
|
||||
@@ -220,8 +224,8 @@ func (c *Config) Prepare(upper interface{}, raws ...interface{}) ([]string, []st
|
||||
if c.Username == "" {
|
||||
errs = packersdk.MultiErrorAppend(errs, errors.New("username must be specified"))
|
||||
}
|
||||
if c.Password == "" {
|
||||
errs = packersdk.MultiErrorAppend(errs, errors.New("password must be specified"))
|
||||
if c.Password == "" && c.Token == "" {
|
||||
errs = packersdk.MultiErrorAppend(errs, errors.New("password or token must be specified"))
|
||||
}
|
||||
if c.ProxmoxURLRaw == "" {
|
||||
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"`
|
||||
PackerSensitiveVars []string `mapstructure:"packer_sensitive_variables" cty:"packer_sensitive_variables" hcl:"packer_sensitive_variables"`
|
||||
HTTPDir *string `mapstructure:"http_directory" cty:"http_directory" hcl:"http_directory"`
|
||||
HTTPContent map[string]string `mapstructure:"http_content" cty:"http_content" hcl:"http_content"`
|
||||
HTTPPortMin *int `mapstructure:"http_port_min" cty:"http_port_min" hcl:"http_port_min"`
|
||||
HTTPPortMax *int `mapstructure:"http_port_max" cty:"http_port_max" hcl:"http_port_max"`
|
||||
HTTPAddress *string `mapstructure:"http_bind_address" cty:"http_bind_address" hcl:"http_bind_address"`
|
||||
@@ -80,6 +81,7 @@ type FlatConfig struct {
|
||||
SkipCertValidation *bool `mapstructure:"insecure_skip_tls_verify" cty:"insecure_skip_tls_verify" hcl:"insecure_skip_tls_verify"`
|
||||
Username *string `mapstructure:"username" cty:"username" hcl:"username"`
|
||||
Password *string `mapstructure:"password" cty:"password" hcl:"password"`
|
||||
Token *string `mapstructure:"token" cty:"token" hcl:"token"`
|
||||
Node *string `mapstructure:"node" cty:"node" hcl:"node"`
|
||||
Pool *string `mapstructure:"pool" cty:"pool" hcl:"pool"`
|
||||
VMName *string `mapstructure:"vm_name" cty:"vm_name" hcl:"vm_name"`
|
||||
@@ -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_sensitive_variables": &hcldec.AttrSpec{Name: "packer_sensitive_variables", Type: cty.List(cty.String), Required: false},
|
||||
"http_directory": &hcldec.AttrSpec{Name: "http_directory", Type: cty.String, Required: false},
|
||||
"http_content": &hcldec.AttrSpec{Name: "http_content", Type: cty.Map(cty.String), Required: false},
|
||||
"http_port_min": &hcldec.AttrSpec{Name: "http_port_min", Type: cty.Number, Required: false},
|
||||
"http_port_max": &hcldec.AttrSpec{Name: "http_port_max", Type: cty.Number, Required: false},
|
||||
"http_bind_address": &hcldec.AttrSpec{Name: "http_bind_address", Type: cty.String, Required: false},
|
||||
@@ -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},
|
||||
"username": &hcldec.AttrSpec{Name: "username", Type: cty.String, Required: false},
|
||||
"password": &hcldec.AttrSpec{Name: "password", Type: cty.String, Required: false},
|
||||
"token": &hcldec.AttrSpec{Name: "token", Type: cty.String, Required: false},
|
||||
"node": &hcldec.AttrSpec{Name: "node", Type: cty.String, Required: false},
|
||||
"pool": &hcldec.AttrSpec{Name: "pool", Type: cty.String, Required: false},
|
||||
"vm_name": &hcldec.AttrSpec{Name: "vm_name", Type: cty.String, Required: false},
|
||||
@@ -226,8 +230,6 @@ type FlatadditionalISOsConfig struct {
|
||||
ISOFile *string `mapstructure:"iso_file" cty:"iso_file" hcl:"iso_file"`
|
||||
ISOStoragePool *string `mapstructure:"iso_storage_pool" cty:"iso_storage_pool" hcl:"iso_storage_pool"`
|
||||
Unmount *bool `mapstructure:"unmount" cty:"unmount" hcl:"unmount"`
|
||||
ShouldUploadISO *bool `cty:"should_upload_iso" hcl:"should_upload_iso"`
|
||||
DownloadPathKey *string `cty:"download_path_key" hcl:"download_path_key"`
|
||||
}
|
||||
|
||||
// FlatMapstructure returns a new 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_storage_pool": &hcldec.AttrSpec{Name: "iso_storage_pool", Type: cty.String, Required: false},
|
||||
"unmount": &hcldec.AttrSpec{Name: "unmount", Type: cty.Bool, Required: false},
|
||||
"should_upload_iso": &hcldec.AttrSpec{Name: "should_upload_iso", Type: cty.Bool, Required: false},
|
||||
"download_path_key": &hcldec.AttrSpec{Name: "download_path_key", Type: cty.String, Required: false},
|
||||
}
|
||||
return s
|
||||
}
|
||||
|
||||
@@ -20,6 +20,7 @@ type FlatConfig struct {
|
||||
PackerUserVars map[string]string `mapstructure:"packer_user_variables" cty:"packer_user_variables" hcl:"packer_user_variables"`
|
||||
PackerSensitiveVars []string `mapstructure:"packer_sensitive_variables" cty:"packer_sensitive_variables" hcl:"packer_sensitive_variables"`
|
||||
HTTPDir *string `mapstructure:"http_directory" cty:"http_directory" hcl:"http_directory"`
|
||||
HTTPContent map[string]string `mapstructure:"http_content" cty:"http_content" hcl:"http_content"`
|
||||
HTTPPortMin *int `mapstructure:"http_port_min" cty:"http_port_min" hcl:"http_port_min"`
|
||||
HTTPPortMax *int `mapstructure:"http_port_max" cty:"http_port_max" hcl:"http_port_max"`
|
||||
HTTPAddress *string `mapstructure:"http_bind_address" cty:"http_bind_address" hcl:"http_bind_address"`
|
||||
@@ -81,6 +82,7 @@ type FlatConfig struct {
|
||||
SkipCertValidation *bool `mapstructure:"insecure_skip_tls_verify" cty:"insecure_skip_tls_verify" hcl:"insecure_skip_tls_verify"`
|
||||
Username *string `mapstructure:"username" cty:"username" hcl:"username"`
|
||||
Password *string `mapstructure:"password" cty:"password" hcl:"password"`
|
||||
Token *string `mapstructure:"token" cty:"token" hcl:"token"`
|
||||
Node *string `mapstructure:"node" cty:"node" hcl:"node"`
|
||||
Pool *string `mapstructure:"pool" cty:"pool" hcl:"pool"`
|
||||
VMName *string `mapstructure:"vm_name" cty:"vm_name" hcl:"vm_name"`
|
||||
@@ -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_sensitive_variables": &hcldec.AttrSpec{Name: "packer_sensitive_variables", Type: cty.List(cty.String), Required: false},
|
||||
"http_directory": &hcldec.AttrSpec{Name: "http_directory", Type: cty.String, Required: false},
|
||||
"http_content": &hcldec.AttrSpec{Name: "http_content", Type: cty.Map(cty.String), Required: false},
|
||||
"http_port_min": &hcldec.AttrSpec{Name: "http_port_min", Type: cty.Number, Required: false},
|
||||
"http_port_max": &hcldec.AttrSpec{Name: "http_port_max", Type: cty.Number, Required: false},
|
||||
"http_bind_address": &hcldec.AttrSpec{Name: "http_bind_address", Type: cty.String, Required: false},
|
||||
@@ -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},
|
||||
"username": &hcldec.AttrSpec{Name: "username", Type: cty.String, Required: false},
|
||||
"password": &hcldec.AttrSpec{Name: "password", Type: cty.String, Required: false},
|
||||
"token": &hcldec.AttrSpec{Name: "token", Type: cty.String, Required: false},
|
||||
"node": &hcldec.AttrSpec{Name: "node", Type: cty.String, Required: false},
|
||||
"pool": &hcldec.AttrSpec{Name: "pool", Type: cty.String, Required: false},
|
||||
"vm_name": &hcldec.AttrSpec{Name: "vm_name", Type: cty.String, Required: false},
|
||||
|
||||
@@ -96,12 +96,7 @@ func (b *Builder) Run(ctx context.Context, ui packersdk.Ui, hook packersdk.Hook)
|
||||
QemuImgArgs: b.config.QemuImgArgs,
|
||||
},
|
||||
new(stepHTTPIPDiscover),
|
||||
&commonsteps.StepHTTPServer{
|
||||
HTTPDir: b.config.HTTPDir,
|
||||
HTTPPortMin: b.config.HTTPPortMin,
|
||||
HTTPPortMax: b.config.HTTPPortMax,
|
||||
HTTPAddress: b.config.HTTPAddress,
|
||||
},
|
||||
commonsteps.HTTPServerFromHTTPConfig(&b.config.HTTPConfig),
|
||||
&stepPortForward{
|
||||
CommunicatorType: b.config.CommConfig.Comm.Type,
|
||||
NetBridge: b.config.NetBridge,
|
||||
|
||||
@@ -48,6 +48,7 @@ func (c *CommConfig) Prepare(ctx *interpolate.Context) (warnings []string, errs
|
||||
|
||||
if c.Comm.SSHHost == "" && c.SkipNatMapping {
|
||||
c.Comm.SSHHost = "127.0.0.1"
|
||||
c.Comm.WinRMHost = "127.0.0.1"
|
||||
}
|
||||
|
||||
if c.HostPortMin == 0 {
|
||||
|
||||
@@ -19,6 +19,7 @@ type FlatConfig struct {
|
||||
PackerUserVars map[string]string `mapstructure:"packer_user_variables" cty:"packer_user_variables" hcl:"packer_user_variables"`
|
||||
PackerSensitiveVars []string `mapstructure:"packer_sensitive_variables" cty:"packer_sensitive_variables" hcl:"packer_sensitive_variables"`
|
||||
HTTPDir *string `mapstructure:"http_directory" cty:"http_directory" hcl:"http_directory"`
|
||||
HTTPContent map[string]string `mapstructure:"http_content" cty:"http_content" hcl:"http_content"`
|
||||
HTTPPortMin *int `mapstructure:"http_port_min" cty:"http_port_min" hcl:"http_port_min"`
|
||||
HTTPPortMax *int `mapstructure:"http_port_max" cty:"http_port_max" hcl:"http_port_max"`
|
||||
HTTPAddress *string `mapstructure:"http_bind_address" cty:"http_bind_address" hcl:"http_bind_address"`
|
||||
@@ -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_sensitive_variables": &hcldec.AttrSpec{Name: "packer_sensitive_variables", Type: cty.List(cty.String), Required: false},
|
||||
"http_directory": &hcldec.AttrSpec{Name: "http_directory", Type: cty.String, Required: false},
|
||||
"http_content": &hcldec.AttrSpec{Name: "http_content", Type: cty.Map(cty.String), Required: false},
|
||||
"http_port_min": &hcldec.AttrSpec{Name: "http_port_min", Type: cty.Number, Required: false},
|
||||
"http_port_max": &hcldec.AttrSpec{Name: "http_port_max", Type: cty.Number, Required: false},
|
||||
"http_bind_address": &hcldec.AttrSpec{Name: "http_bind_address", Type: cty.String, Required: false},
|
||||
|
||||
@@ -298,6 +298,7 @@ func (s *stepRun) applyUserOverrides(defaultArgs map[string]interface{}, config
|
||||
HTTPIP string
|
||||
HTTPPort int
|
||||
HTTPDir string
|
||||
HTTPContent map[string]string
|
||||
OutputDir string
|
||||
Name string
|
||||
SSHHostPort int
|
||||
@@ -308,6 +309,7 @@ func (s *stepRun) applyUserOverrides(defaultArgs map[string]interface{}, config
|
||||
HTTPIP: httpIp,
|
||||
HTTPPort: httpPort,
|
||||
HTTPDir: config.HTTPDir,
|
||||
HTTPContent: config.HTTPContent,
|
||||
OutputDir: config.OutputDir,
|
||||
Name: config.VMName,
|
||||
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"`
|
||||
PackerSensitiveVars []string `mapstructure:"packer_sensitive_variables" cty:"packer_sensitive_variables" hcl:"packer_sensitive_variables"`
|
||||
HTTPDir *string `mapstructure:"http_directory" cty:"http_directory" hcl:"http_directory"`
|
||||
HTTPContent map[string]string `mapstructure:"http_content" cty:"http_content" hcl:"http_content"`
|
||||
HTTPPortMin *int `mapstructure:"http_port_min" cty:"http_port_min" hcl:"http_port_min"`
|
||||
HTTPPortMax *int `mapstructure:"http_port_max" cty:"http_port_max" hcl:"http_port_max"`
|
||||
HTTPAddress *string `mapstructure:"http_bind_address" cty:"http_bind_address" hcl:"http_bind_address"`
|
||||
@@ -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_sensitive_variables": &hcldec.AttrSpec{Name: "packer_sensitive_variables", Type: cty.List(cty.String), Required: false},
|
||||
"http_directory": &hcldec.AttrSpec{Name: "http_directory", Type: cty.String, Required: false},
|
||||
"http_content": &hcldec.AttrSpec{Name: "http_content", Type: cty.Map(cty.String), Required: false},
|
||||
"http_port_min": &hcldec.AttrSpec{Name: "http_port_min", Type: cty.Number, Required: false},
|
||||
"http_port_max": &hcldec.AttrSpec{Name: "http_port_max", Type: cty.Number, Required: false},
|
||||
"http_bind_address": &hcldec.AttrSpec{Name: "http_bind_address", Type: cty.String, Required: false},
|
||||
|
||||
@@ -41,17 +41,23 @@ func (s *StepSSHConfig) Run(ctx context.Context, state multistep.StateBag) multi
|
||||
return multistep.ActionHalt
|
||||
}
|
||||
|
||||
config.Comm.SSHHost = sshConfig.Hostname
|
||||
port, err := strconv.Atoi(sshConfig.Port)
|
||||
if err != nil {
|
||||
state.Put("error", err)
|
||||
return multistep.ActionHalt
|
||||
if config.Comm.SSHHost == "" {
|
||||
config.Comm.SSHHost = sshConfig.Hostname
|
||||
}
|
||||
if config.Comm.SSHPort == 0 {
|
||||
port, err := strconv.Atoi(sshConfig.Port)
|
||||
if err != nil {
|
||||
state.Put("error", err)
|
||||
return multistep.ActionHalt
|
||||
}
|
||||
config.Comm.SSHPort = port
|
||||
}
|
||||
config.Comm.SSHPort = port
|
||||
|
||||
if config.Comm.SSHUsername != "" {
|
||||
// If user has set the username within the communicator, use the
|
||||
// auth provided there.
|
||||
// username, password, and/or keyfile auth provided there.
|
||||
log.Printf("Overriding SSH config from Vagrant with the username, " +
|
||||
"password, and private key information provided to the Packer template.")
|
||||
return multistep.ActionContinue
|
||||
}
|
||||
log.Printf("identity file is %s", sshConfig.IdentityFile)
|
||||
|
||||
@@ -4,6 +4,7 @@ import (
|
||||
"context"
|
||||
"testing"
|
||||
|
||||
"github.com/hashicorp/packer-plugin-sdk/communicator"
|
||||
"github.com/hashicorp/packer-plugin-sdk/multistep"
|
||||
)
|
||||
|
||||
@@ -15,6 +16,83 @@ func TestStepSSHConfig_Impl(t *testing.T) {
|
||||
}
|
||||
}
|
||||
|
||||
func TestPrepStepSSHConfig_sshOverrides(t *testing.T) {
|
||||
type testcase struct {
|
||||
name string
|
||||
inputSSHConfig communicator.SSH
|
||||
expectedSSHConfig communicator.SSH
|
||||
}
|
||||
tcs := []testcase{
|
||||
{
|
||||
// defaults to overriding with the ssh config from vagrant\
|
||||
name: "default",
|
||||
inputSSHConfig: communicator.SSH{},
|
||||
expectedSSHConfig: communicator.SSH{
|
||||
SSHHost: "127.0.0.1",
|
||||
SSHPort: 2222,
|
||||
SSHUsername: "vagrant",
|
||||
SSHPassword: "",
|
||||
},
|
||||
},
|
||||
{
|
||||
// respects SSH host and port overrides independent of credential
|
||||
// overrides
|
||||
name: "host_override",
|
||||
inputSSHConfig: communicator.SSH{
|
||||
SSHHost: "123.45.67.8",
|
||||
SSHPort: 1234,
|
||||
},
|
||||
expectedSSHConfig: communicator.SSH{
|
||||
SSHHost: "123.45.67.8",
|
||||
SSHPort: 1234,
|
||||
SSHUsername: "vagrant",
|
||||
SSHPassword: "",
|
||||
},
|
||||
},
|
||||
{
|
||||
// respects credential overrides
|
||||
name: "credential_override",
|
||||
inputSSHConfig: communicator.SSH{
|
||||
SSHUsername: "megan",
|
||||
SSHPassword: "SoSecure",
|
||||
},
|
||||
expectedSSHConfig: communicator.SSH{
|
||||
SSHHost: "127.0.0.1",
|
||||
SSHPort: 2222,
|
||||
SSHUsername: "megan",
|
||||
SSHPassword: "SoSecure",
|
||||
},
|
||||
},
|
||||
}
|
||||
for _, tc := range tcs {
|
||||
driver := &MockVagrantDriver{}
|
||||
config := &Config{
|
||||
Comm: communicator.Config{
|
||||
SSH: tc.inputSSHConfig,
|
||||
},
|
||||
}
|
||||
state := new(multistep.BasicStateBag)
|
||||
state.Put("driver", driver)
|
||||
state.Put("config", config)
|
||||
|
||||
step := StepSSHConfig{}
|
||||
_ = step.Run(context.Background(), state)
|
||||
|
||||
if config.Comm.SSHHost != tc.expectedSSHConfig.SSHHost {
|
||||
t.Fatalf("unexpected sshconfig host: name: %s, recieved %s", tc.name, config.Comm.SSHHost)
|
||||
}
|
||||
if config.Comm.SSHPort != tc.expectedSSHConfig.SSHPort {
|
||||
t.Fatalf("unexpected sshconfig port: name: %s, recieved %d", tc.name, config.Comm.SSHPort)
|
||||
}
|
||||
if config.Comm.SSHUsername != tc.expectedSSHConfig.SSHUsername {
|
||||
t.Fatalf("unexpected sshconfig SSHUsername: name: %s, recieved %s", tc.name, config.Comm.SSHUsername)
|
||||
}
|
||||
if config.Comm.SSHPassword != tc.expectedSSHConfig.SSHPassword {
|
||||
t.Fatalf("unexpected sshconfig SSHUsername: name: %s, recieved %s", tc.name, config.Comm.SSHPassword)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func TestPrepStepSSHConfig_GlobalID(t *testing.T) {
|
||||
driver := &MockVagrantDriver{}
|
||||
config := &Config{}
|
||||
|
||||
@@ -50,8 +50,9 @@ func (c *CommConfig) Prepare(ctx *interpolate.Context) []error {
|
||||
c.SkipNatMapping = c.SSHSkipNatMapping
|
||||
}
|
||||
|
||||
if c.Comm.SSHHost == "" {
|
||||
if c.Comm.Host() == "" {
|
||||
c.Comm.SSHHost = "127.0.0.1"
|
||||
c.Comm.WinRMHost = "127.0.0.1"
|
||||
}
|
||||
|
||||
if c.HostPortMin == 0 {
|
||||
|
||||
@@ -8,6 +8,8 @@ import (
|
||||
|
||||
"github.com/hashicorp/packer-plugin-sdk/multistep"
|
||||
packersdk "github.com/hashicorp/packer-plugin-sdk/packer"
|
||||
|
||||
"strconv"
|
||||
)
|
||||
|
||||
// This step attaches the boot ISO, cd_files iso, and guest additions to the
|
||||
@@ -69,49 +71,50 @@ func (s *StepAttachISOs) Run(ctx context.Context, state multistep.StateBag) mult
|
||||
|
||||
// We have three different potential isos we can attach, so let's
|
||||
// assign each one its own spot so they don't conflict.
|
||||
var controllerName, device, port string
|
||||
var controllerName string
|
||||
var device, port int
|
||||
switch diskCategory {
|
||||
case "boot_iso":
|
||||
// figure out controller path
|
||||
controllerName = "IDE Controller"
|
||||
port = "0"
|
||||
device = "1"
|
||||
port = 0
|
||||
device = 1
|
||||
if s.ISOInterface == "sata" {
|
||||
controllerName = "SATA Controller"
|
||||
port = "1"
|
||||
device = "0"
|
||||
port = 15
|
||||
device = 0
|
||||
} else if s.ISOInterface == "virtio" {
|
||||
controllerName = "VirtIO Controller"
|
||||
port = "1"
|
||||
device = "0"
|
||||
port = 15
|
||||
device = 0
|
||||
}
|
||||
ui.Message("Mounting boot ISO...")
|
||||
case "guest_additions":
|
||||
controllerName = "IDE Controller"
|
||||
port = "1"
|
||||
device = "0"
|
||||
port = 1
|
||||
device = 0
|
||||
if s.GuestAdditionsInterface == "sata" {
|
||||
controllerName = "SATA Controller"
|
||||
port = "2"
|
||||
device = "0"
|
||||
port = 14
|
||||
device = 0
|
||||
} else if s.GuestAdditionsInterface == "virtio" {
|
||||
controllerName = "VirtIO Controller"
|
||||
port = "2"
|
||||
device = "0"
|
||||
port = 14
|
||||
device = 0
|
||||
}
|
||||
ui.Message("Mounting guest additions ISO...")
|
||||
case "cd_files":
|
||||
controllerName = "IDE Controller"
|
||||
port = "1"
|
||||
device = "1"
|
||||
port = 1
|
||||
device = 1
|
||||
if s.ISOInterface == "sata" {
|
||||
controllerName = "SATA Controller"
|
||||
port = "3"
|
||||
device = "0"
|
||||
port = 13
|
||||
device = 0
|
||||
} else if s.ISOInterface == "virtio" {
|
||||
controllerName = "VirtIO Controller"
|
||||
port = "3"
|
||||
device = "0"
|
||||
port = 13
|
||||
device = 0
|
||||
}
|
||||
ui.Message("Mounting cd_files ISO...")
|
||||
}
|
||||
@@ -120,8 +123,8 @@ func (s *StepAttachISOs) Run(ctx context.Context, state multistep.StateBag) mult
|
||||
command := []string{
|
||||
"storageattach", vmName,
|
||||
"--storagectl", controllerName,
|
||||
"--port", port,
|
||||
"--device", device,
|
||||
"--port", strconv.Itoa(port),
|
||||
"--device", strconv.Itoa(device),
|
||||
"--type", "dvddrive",
|
||||
"--medium", isoPath,
|
||||
}
|
||||
@@ -137,8 +140,8 @@ func (s *StepAttachISOs) Run(ctx context.Context, state multistep.StateBag) mult
|
||||
unmountCommand := []string{
|
||||
"storageattach", vmName,
|
||||
"--storagectl", controllerName,
|
||||
"--port", port,
|
||||
"--device", device,
|
||||
"--port", strconv.Itoa(port),
|
||||
"--device", strconv.Itoa(device),
|
||||
"--type", "dvddrive",
|
||||
"--medium", "none",
|
||||
}
|
||||
|
||||
@@ -7,6 +7,7 @@ import (
|
||||
"context"
|
||||
"errors"
|
||||
"fmt"
|
||||
"regexp"
|
||||
|
||||
"github.com/hashicorp/hcl/v2/hcldec"
|
||||
"github.com/hashicorp/packer-plugin-sdk/bootcommand"
|
||||
@@ -52,6 +53,14 @@ type Config struct {
|
||||
// When set to bios, the firmare is BIOS. This is the default.
|
||||
// When set to efi, the firmare is EFI.
|
||||
Firmware string `mapstructure:"firmware" required:"false"`
|
||||
// Nested virtualization: false or true.
|
||||
// When set to true, nested virtualisation (VT-x/AMD-V) is enabled.
|
||||
// When set to false, nested virtualisation is disabled. This is the default.
|
||||
NestedVirt bool `mapstructure:"nested_virt" required:"false"`
|
||||
// RTC time base: UTC or local.
|
||||
// When set to true, the RTC is set as UTC time.
|
||||
// When set to false, the RTC is set as local time. This is the default.
|
||||
RTCTimeBase string `mapstructure:"rtc_time_base" required:"false"`
|
||||
// The size, in megabytes, of the hard disk to create for the VM. By
|
||||
// default, this is 40000 (about 40 GB).
|
||||
DiskSize uint `mapstructure:"disk_size" required:"false"`
|
||||
@@ -75,6 +84,17 @@ type Config struct {
|
||||
// When set to vmsvga, the graphics controller is VMware SVGA.
|
||||
// When set to none, the graphics controller is disabled.
|
||||
GfxController string `mapstructure:"gfx_controller" required:"false"`
|
||||
// The VRAM size to be used. By default, this is 4 MiB.
|
||||
GfxVramSize uint `mapstructure:"gfx_vram_size" required:"false"`
|
||||
// 3D acceleration: true or false.
|
||||
// When set to true, 3D acceleration is enabled.
|
||||
// When set to false, 3D acceleration is disabled. This is the default.
|
||||
GfxAccelerate3D bool `mapstructure:"gfx_accelerate_3d" required:"false"`
|
||||
// Screen resolution in EFI mode: WIDTHxHEIGHT.
|
||||
// When set to WIDTHxHEIGHT, it provides the given width and height as screen resolution
|
||||
// to EFI, for example 1920x1080 for Full-HD resolution. By default, no screen resolution
|
||||
// is set. Note, that this option only affects EFI boot, not the (default) BIOS boot.
|
||||
GfxEFIResolution string `mapstructure:"gfx_efi_resolution" required:"false"`
|
||||
// The guest OS type being installed. By default this is other, but you can
|
||||
// get dramatic performance improvements by setting this to the proper
|
||||
// value. To view all available values for this run VBoxManage list
|
||||
@@ -129,6 +149,12 @@ type Config struct {
|
||||
// When set to sata, the drive is attached to an AHCI SATA controller.
|
||||
// When set to virtio, the drive is attached to a VirtIO controller.
|
||||
ISOInterface string `mapstructure:"iso_interface" required:"false"`
|
||||
// Additional disks to create. Uses `vm_name` as the disk name template and
|
||||
// appends `-#` where `#` is the position in the array. `#` starts at 1 since 0
|
||||
// is the default disk. Each value represents the disk image size in MiB.
|
||||
// Each additional disk uses the same disk parameters as the default disk.
|
||||
// Unset by default.
|
||||
AdditionalDiskSize []uint `mapstructure:"disk_additional_size" required:"false"`
|
||||
// Set this to true if you would like to keep the VM registered with
|
||||
// virtualbox. Defaults to false.
|
||||
KeepRegistered bool `mapstructure:"keep_registered" required:"false"`
|
||||
@@ -212,6 +238,17 @@ func (b *Builder) Prepare(raws ...interface{}) ([]string, []string, error) {
|
||||
errs, errors.New("firmware can only be bios or efi"))
|
||||
}
|
||||
|
||||
if b.config.RTCTimeBase == "" {
|
||||
b.config.RTCTimeBase = "local"
|
||||
}
|
||||
switch b.config.RTCTimeBase {
|
||||
case "UTC", "local":
|
||||
// do nothing
|
||||
default:
|
||||
errs = packersdk.MultiErrorAppend(
|
||||
errs, errors.New("rtc_time_base can only be UTC or local"))
|
||||
}
|
||||
|
||||
if b.config.DiskSize == 0 {
|
||||
b.config.DiskSize = 40000
|
||||
}
|
||||
@@ -242,6 +279,24 @@ func (b *Builder) Prepare(raws ...interface{}) ([]string, []string, error) {
|
||||
errs, errors.New("Graphics controller type can only be vboxvga, vboxsvga, vmsvga, none"))
|
||||
}
|
||||
|
||||
if b.config.GfxVramSize == 0 {
|
||||
b.config.GfxVramSize = 4
|
||||
} else {
|
||||
if b.config.GfxVramSize < 1 || b.config.GfxVramSize > 128 {
|
||||
errs = packersdk.MultiErrorAppend(
|
||||
errs, errors.New("VGRAM size must be from 0 (use default) to 128"))
|
||||
}
|
||||
}
|
||||
|
||||
if b.config.GfxEFIResolution != "" {
|
||||
re := regexp.MustCompile(`^[\d]+x[\d]+$`)
|
||||
matched := re.MatchString(b.config.GfxEFIResolution)
|
||||
if !matched {
|
||||
errs = packersdk.MultiErrorAppend(
|
||||
errs, errors.New("EFI resolution must be in the format WIDTHxHEIGHT, e.g. 1920x1080"))
|
||||
}
|
||||
}
|
||||
|
||||
if b.config.AudioController == "" {
|
||||
b.config.AudioController = "ac97"
|
||||
}
|
||||
@@ -351,12 +406,7 @@ func (b *Builder) Run(ctx context.Context, ui packersdk.Ui, hook packersdk.Hook)
|
||||
Label: b.config.CDConfig.CDLabel,
|
||||
},
|
||||
new(vboxcommon.StepHTTPIPDiscover),
|
||||
&commonsteps.StepHTTPServer{
|
||||
HTTPDir: b.config.HTTPDir,
|
||||
HTTPPortMin: b.config.HTTPPortMin,
|
||||
HTTPPortMax: b.config.HTTPPortMax,
|
||||
HTTPAddress: b.config.HTTPAddress,
|
||||
},
|
||||
commonsteps.HTTPServerFromHTTPConfig(&b.config.HTTPConfig),
|
||||
&vboxcommon.StepSshKeyPair{
|
||||
Debug: b.config.PackerDebug,
|
||||
DebugKeyPath: fmt.Sprintf("%s.pem", b.config.PackerBuildName),
|
||||
@@ -400,7 +450,7 @@ func (b *Builder) Run(ctx context.Context, ui packersdk.Ui, hook packersdk.Hook)
|
||||
},
|
||||
&communicator.StepConnect{
|
||||
Config: &b.config.CommConfig.Comm,
|
||||
Host: vboxcommon.CommHost(b.config.CommConfig.Comm.SSHHost),
|
||||
Host: vboxcommon.CommHost(b.config.CommConfig.Comm.Host()),
|
||||
SSHConfig: b.config.CommConfig.Comm.SSHConfigFunc(),
|
||||
SSHPort: vboxcommon.CommPort,
|
||||
WinRMPort: vboxcommon.CommPort,
|
||||
|
||||
@@ -19,6 +19,7 @@ type FlatConfig struct {
|
||||
PackerUserVars map[string]string `mapstructure:"packer_user_variables" cty:"packer_user_variables" hcl:"packer_user_variables"`
|
||||
PackerSensitiveVars []string `mapstructure:"packer_sensitive_variables" cty:"packer_sensitive_variables" hcl:"packer_sensitive_variables"`
|
||||
HTTPDir *string `mapstructure:"http_directory" cty:"http_directory" hcl:"http_directory"`
|
||||
HTTPContent map[string]string `mapstructure:"http_content" cty:"http_content" hcl:"http_content"`
|
||||
HTTPPortMin *int `mapstructure:"http_port_min" cty:"http_port_min" hcl:"http_port_min"`
|
||||
HTTPPortMax *int `mapstructure:"http_port_max" cty:"http_port_max" hcl:"http_port_max"`
|
||||
HTTPAddress *string `mapstructure:"http_bind_address" cty:"http_bind_address" hcl:"http_bind_address"`
|
||||
@@ -119,10 +120,15 @@ type FlatConfig struct {
|
||||
GuestAdditionsURL *string `mapstructure:"guest_additions_url" required:"false" cty:"guest_additions_url" hcl:"guest_additions_url"`
|
||||
Chipset *string `mapstructure:"chipset" required:"false" cty:"chipset" hcl:"chipset"`
|
||||
Firmware *string `mapstructure:"firmware" required:"false" cty:"firmware" hcl:"firmware"`
|
||||
NestedVirt *bool `mapstructure:"nested_virt" required:"false" cty:"nested_virt" hcl:"nested_virt"`
|
||||
RTCTimeBase *string `mapstructure:"rtc_time_base" required:"false" cty:"rtc_time_base" hcl:"rtc_time_base"`
|
||||
DiskSize *uint `mapstructure:"disk_size" required:"false" cty:"disk_size" hcl:"disk_size"`
|
||||
NICType *string `mapstructure:"nic_type" required:"false" cty:"nic_type" hcl:"nic_type"`
|
||||
AudioController *string `mapstructure:"audio_controller" required:"false" cty:"audio_controller" hcl:"audio_controller"`
|
||||
GfxController *string `mapstructure:"gfx_controller" required:"false" cty:"gfx_controller" hcl:"gfx_controller"`
|
||||
GfxVramSize *uint `mapstructure:"gfx_vram_size" required:"false" cty:"gfx_vram_size" hcl:"gfx_vram_size"`
|
||||
GfxAccelerate3D *bool `mapstructure:"gfx_accelerate_3d" required:"false" cty:"gfx_accelerate_3d" hcl:"gfx_accelerate_3d"`
|
||||
GfxEFIResolution *string `mapstructure:"gfx_efi_resolution" required:"false" cty:"gfx_efi_resolution" hcl:"gfx_efi_resolution"`
|
||||
GuestOSType *string `mapstructure:"guest_os_type" required:"false" cty:"guest_os_type" hcl:"guest_os_type"`
|
||||
HardDriveDiscard *bool `mapstructure:"hard_drive_discard" required:"false" cty:"hard_drive_discard" hcl:"hard_drive_discard"`
|
||||
HardDriveInterface *string `mapstructure:"hard_drive_interface" required:"false" cty:"hard_drive_interface" hcl:"hard_drive_interface"`
|
||||
@@ -130,6 +136,7 @@ type FlatConfig struct {
|
||||
NVMePortCount *int `mapstructure:"nvme_port_count" required:"false" cty:"nvme_port_count" hcl:"nvme_port_count"`
|
||||
HardDriveNonrotational *bool `mapstructure:"hard_drive_nonrotational" required:"false" cty:"hard_drive_nonrotational" hcl:"hard_drive_nonrotational"`
|
||||
ISOInterface *string `mapstructure:"iso_interface" required:"false" cty:"iso_interface" hcl:"iso_interface"`
|
||||
AdditionalDiskSize []uint `mapstructure:"disk_additional_size" required:"false" cty:"disk_additional_size" hcl:"disk_additional_size"`
|
||||
KeepRegistered *bool `mapstructure:"keep_registered" required:"false" cty:"keep_registered" hcl:"keep_registered"`
|
||||
SkipExport *bool `mapstructure:"skip_export" required:"false" cty:"skip_export" hcl:"skip_export"`
|
||||
VMName *string `mapstructure:"vm_name" required:"false" cty:"vm_name" hcl:"vm_name"`
|
||||
@@ -156,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_sensitive_variables": &hcldec.AttrSpec{Name: "packer_sensitive_variables", Type: cty.List(cty.String), Required: false},
|
||||
"http_directory": &hcldec.AttrSpec{Name: "http_directory", Type: cty.String, Required: false},
|
||||
"http_content": &hcldec.AttrSpec{Name: "http_content", Type: cty.Map(cty.String), Required: false},
|
||||
"http_port_min": &hcldec.AttrSpec{Name: "http_port_min", Type: cty.Number, Required: false},
|
||||
"http_port_max": &hcldec.AttrSpec{Name: "http_port_max", Type: cty.Number, Required: false},
|
||||
"http_bind_address": &hcldec.AttrSpec{Name: "http_bind_address", Type: cty.String, Required: false},
|
||||
@@ -256,10 +264,15 @@ func (*FlatConfig) HCL2Spec() map[string]hcldec.Spec {
|
||||
"guest_additions_url": &hcldec.AttrSpec{Name: "guest_additions_url", Type: cty.String, Required: false},
|
||||
"chipset": &hcldec.AttrSpec{Name: "chipset", Type: cty.String, Required: false},
|
||||
"firmware": &hcldec.AttrSpec{Name: "firmware", Type: cty.String, Required: false},
|
||||
"nested_virt": &hcldec.AttrSpec{Name: "nested_virt", Type: cty.Bool, Required: false},
|
||||
"rtc_time_base": &hcldec.AttrSpec{Name: "rtc_time_base", Type: cty.String, Required: false},
|
||||
"disk_size": &hcldec.AttrSpec{Name: "disk_size", Type: cty.Number, Required: false},
|
||||
"nic_type": &hcldec.AttrSpec{Name: "nic_type", Type: cty.String, Required: false},
|
||||
"audio_controller": &hcldec.AttrSpec{Name: "audio_controller", Type: cty.String, Required: false},
|
||||
"gfx_controller": &hcldec.AttrSpec{Name: "gfx_controller", Type: cty.String, Required: false},
|
||||
"gfx_vram_size": &hcldec.AttrSpec{Name: "gfx_vram_size", Type: cty.Number, Required: false},
|
||||
"gfx_accelerate_3d": &hcldec.AttrSpec{Name: "gfx_accelerate_3d", Type: cty.Bool, Required: false},
|
||||
"gfx_efi_resolution": &hcldec.AttrSpec{Name: "gfx_efi_resolution", Type: cty.String, Required: false},
|
||||
"guest_os_type": &hcldec.AttrSpec{Name: "guest_os_type", Type: cty.String, Required: false},
|
||||
"hard_drive_discard": &hcldec.AttrSpec{Name: "hard_drive_discard", Type: cty.Bool, Required: false},
|
||||
"hard_drive_interface": &hcldec.AttrSpec{Name: "hard_drive_interface", Type: cty.String, Required: false},
|
||||
@@ -267,6 +280,7 @@ func (*FlatConfig) HCL2Spec() map[string]hcldec.Spec {
|
||||
"nvme_port_count": &hcldec.AttrSpec{Name: "nvme_port_count", Type: cty.Number, Required: false},
|
||||
"hard_drive_nonrotational": &hcldec.AttrSpec{Name: "hard_drive_nonrotational", Type: cty.Bool, Required: false},
|
||||
"iso_interface": &hcldec.AttrSpec{Name: "iso_interface", Type: cty.String, Required: false},
|
||||
"disk_additional_size": &hcldec.AttrSpec{Name: "disk_additional_size", Type: cty.List(cty.Number), Required: false},
|
||||
"keep_registered": &hcldec.AttrSpec{Name: "keep_registered", Type: cty.Bool, Required: false},
|
||||
"skip_export": &hcldec.AttrSpec{Name: "skip_export", Type: cty.Bool, Required: false},
|
||||
"vm_name": &hcldec.AttrSpec{Name: "vm_name", Type: cty.String, Required: false},
|
||||
|
||||
@@ -22,31 +22,50 @@ func (s *stepCreateDisk) Run(ctx context.Context, state multistep.StateBag) mult
|
||||
driver := state.Get("driver").(vboxcommon.Driver)
|
||||
ui := state.Get("ui").(packersdk.Ui)
|
||||
vmName := state.Get("vmName").(string)
|
||||
|
||||
format := "VDI"
|
||||
path := filepath.Join(config.OutputDir, fmt.Sprintf("%s.%s", config.VMName, strings.ToLower(format)))
|
||||
|
||||
command := []string{
|
||||
"createhd",
|
||||
"--filename", path,
|
||||
"--size", strconv.FormatUint(uint64(config.DiskSize), 10),
|
||||
"--format", format,
|
||||
"--variant", "Standard",
|
||||
// The main disk and additional disks
|
||||
diskFullPaths := []string{}
|
||||
diskSizes := []uint{config.DiskSize}
|
||||
if len(config.AdditionalDiskSize) == 0 {
|
||||
// If there are no additional disks, use disk naming as before
|
||||
diskFullPaths = append(diskFullPaths, filepath.Join(config.OutputDir, fmt.Sprintf("%s.%s", config.VMName, strings.ToLower(format))))
|
||||
} else {
|
||||
// If there are additional disks, use consistent naming with numbers
|
||||
diskFullPaths = append(diskFullPaths, filepath.Join(config.OutputDir, fmt.Sprintf("%s-0.%s", config.VMName, strings.ToLower(format))))
|
||||
|
||||
for i, diskSize := range config.AdditionalDiskSize {
|
||||
path := filepath.Join(config.OutputDir, fmt.Sprintf("%s-%d.%s", config.VMName, i+1, strings.ToLower(format)))
|
||||
diskFullPaths = append(diskFullPaths, path)
|
||||
diskSizes = append(diskSizes, diskSize)
|
||||
}
|
||||
}
|
||||
|
||||
ui.Say("Creating hard drive...")
|
||||
err := driver.VBoxManage(command...)
|
||||
if err != nil {
|
||||
err := fmt.Errorf("Error creating hard drive: %s", err)
|
||||
state.Put("error", err)
|
||||
ui.Error(err.Error())
|
||||
return multistep.ActionHalt
|
||||
// Create all required disks
|
||||
for i := range diskFullPaths {
|
||||
ui.Say(fmt.Sprintf("Creating hard drive %s with size %d MiB...", diskFullPaths[i], diskSizes[i]))
|
||||
|
||||
command := []string{
|
||||
"createhd",
|
||||
"--filename", diskFullPaths[i],
|
||||
"--size", strconv.FormatUint(uint64(diskSizes[i]), 10),
|
||||
"--format", format,
|
||||
"--variant", "Standard",
|
||||
}
|
||||
|
||||
err := driver.VBoxManage(command...)
|
||||
if err != nil {
|
||||
err := fmt.Errorf("Error creating hard drive: %s", err)
|
||||
state.Put("error", err)
|
||||
ui.Error(err.Error())
|
||||
return multistep.ActionHalt
|
||||
}
|
||||
}
|
||||
|
||||
// Add the IDE controller so we can later attach the disk.
|
||||
// When the hard disk controller is not IDE, this device is still used
|
||||
// by VirtualBox to deliver the guest extensions.
|
||||
err = driver.VBoxManage("storagectl", vmName, "--name", "IDE Controller", "--add", "ide")
|
||||
err := driver.VBoxManage("storagectl", vmName, "--name", "IDE Controller", "--add", "ide")
|
||||
if err != nil {
|
||||
err := fmt.Errorf("Error creating disk controller: %s", err)
|
||||
state.Put("error", err)
|
||||
@@ -116,21 +135,23 @@ func (s *stepCreateDisk) Run(ctx context.Context, state multistep.StateBag) mult
|
||||
discard = "on"
|
||||
}
|
||||
|
||||
command = []string{
|
||||
"storageattach", vmName,
|
||||
"--storagectl", controllerName,
|
||||
"--port", "0",
|
||||
"--device", "0",
|
||||
"--type", "hdd",
|
||||
"--medium", path,
|
||||
"--nonrotational", nonrotational,
|
||||
"--discard", discard,
|
||||
}
|
||||
if err := driver.VBoxManage(command...); err != nil {
|
||||
err := fmt.Errorf("Error attaching hard drive: %s", err)
|
||||
state.Put("error", err)
|
||||
ui.Error(err.Error())
|
||||
return multistep.ActionHalt
|
||||
for i := range diskFullPaths {
|
||||
command := []string{
|
||||
"storageattach", vmName,
|
||||
"--storagectl", controllerName,
|
||||
"--port", strconv.FormatUint(uint64(i), 10),
|
||||
"--device", "0",
|
||||
"--type", "hdd",
|
||||
"--medium", diskFullPaths[i],
|
||||
"--nonrotational", nonrotational,
|
||||
"--discard", discard,
|
||||
}
|
||||
if err := driver.VBoxManage(command...); err != nil {
|
||||
err := fmt.Errorf("Error attaching hard drive: %s", err)
|
||||
state.Put("error", err)
|
||||
ui.Error(err.Error())
|
||||
return multistep.ActionHalt
|
||||
}
|
||||
}
|
||||
|
||||
return multistep.ActionContinue
|
||||
|
||||
@@ -26,7 +26,7 @@ func (s *stepCreateVM) Run(ctx context.Context, state multistep.StateBag) multis
|
||||
|
||||
name := config.VMName
|
||||
|
||||
commands := make([][]string, 10)
|
||||
commands := make([][]string, 14)
|
||||
commands[0] = []string{
|
||||
"createvm", "--name", name,
|
||||
"--ostype", config.GuestOSType, "--register",
|
||||
@@ -59,7 +59,26 @@ func (s *stepCreateVM) Run(ctx context.Context, state multistep.StateBag) multis
|
||||
"--nictype6", config.NICType,
|
||||
"--nictype7", config.NICType,
|
||||
"--nictype8", config.NICType}
|
||||
commands[9] = []string{"modifyvm", name, "--graphicscontroller", config.GfxController}
|
||||
commands[9] = []string{"modifyvm", name, "--graphicscontroller", config.GfxController, "--vram", strconv.FormatUint(uint64(config.GfxVramSize), 10)}
|
||||
if config.RTCTimeBase == "UTC" {
|
||||
commands[10] = []string{"modifyvm", name, "--rtcuseutc", "on"}
|
||||
} else {
|
||||
commands[10] = []string{"modifyvm", name, "--rtcuseutc", "off"}
|
||||
}
|
||||
if config.NestedVirt == true {
|
||||
commands[11] = []string{"modifyvm", name, "--nested-hw-virt", "on"}
|
||||
} else {
|
||||
commands[11] = []string{"modifyvm", name, "--nested-hw-virt", "off"}
|
||||
}
|
||||
|
||||
if config.GfxAccelerate3D {
|
||||
commands[12] = []string{"modifyvm", name, "--accelerate3d", "on"}
|
||||
} else {
|
||||
commands[12] = []string{"modifyvm", name, "--accelerate3d", "off"}
|
||||
}
|
||||
if config.GfxEFIResolution != "" {
|
||||
commands[13] = []string{"setextradata", name, "VBoxInternal2/EfiGraphicsResolution", config.GfxEFIResolution}
|
||||
}
|
||||
|
||||
ui.Say("Creating virtual machine...")
|
||||
for _, command := range commands {
|
||||
|
||||
@@ -65,12 +65,7 @@ func (b *Builder) Run(ctx context.Context, ui packersdk.Ui, hook packersdk.Hook)
|
||||
Label: b.config.CDConfig.CDLabel,
|
||||
},
|
||||
new(vboxcommon.StepHTTPIPDiscover),
|
||||
&commonsteps.StepHTTPServer{
|
||||
HTTPDir: b.config.HTTPDir,
|
||||
HTTPPortMin: b.config.HTTPPortMin,
|
||||
HTTPPortMax: b.config.HTTPPortMax,
|
||||
HTTPAddress: b.config.HTTPAddress,
|
||||
},
|
||||
commonsteps.HTTPServerFromHTTPConfig(&b.config.HTTPConfig),
|
||||
&vboxcommon.StepSshKeyPair{
|
||||
Debug: b.config.PackerDebug,
|
||||
DebugKeyPath: fmt.Sprintf("%s.pem", b.config.PackerBuildName),
|
||||
@@ -130,7 +125,7 @@ func (b *Builder) Run(ctx context.Context, ui packersdk.Ui, hook packersdk.Hook)
|
||||
},
|
||||
&communicator.StepConnect{
|
||||
Config: &b.config.CommConfig.Comm,
|
||||
Host: vboxcommon.CommHost(b.config.CommConfig.Comm.SSHHost),
|
||||
Host: vboxcommon.CommHost(b.config.CommConfig.Comm.Host()),
|
||||
SSHConfig: b.config.CommConfig.Comm.SSHConfigFunc(),
|
||||
SSHPort: vboxcommon.CommPort,
|
||||
WinRMPort: vboxcommon.CommPort,
|
||||
|
||||
@@ -19,6 +19,7 @@ type FlatConfig struct {
|
||||
PackerUserVars map[string]string `mapstructure:"packer_user_variables" cty:"packer_user_variables" hcl:"packer_user_variables"`
|
||||
PackerSensitiveVars []string `mapstructure:"packer_sensitive_variables" cty:"packer_sensitive_variables" hcl:"packer_sensitive_variables"`
|
||||
HTTPDir *string `mapstructure:"http_directory" cty:"http_directory" hcl:"http_directory"`
|
||||
HTTPContent map[string]string `mapstructure:"http_content" cty:"http_content" hcl:"http_content"`
|
||||
HTTPPortMin *int `mapstructure:"http_port_min" cty:"http_port_min" hcl:"http_port_min"`
|
||||
HTTPPortMax *int `mapstructure:"http_port_max" cty:"http_port_max" hcl:"http_port_max"`
|
||||
HTTPAddress *string `mapstructure:"http_bind_address" cty:"http_bind_address" hcl:"http_bind_address"`
|
||||
@@ -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_sensitive_variables": &hcldec.AttrSpec{Name: "packer_sensitive_variables", Type: cty.List(cty.String), Required: false},
|
||||
"http_directory": &hcldec.AttrSpec{Name: "http_directory", Type: cty.String, Required: false},
|
||||
"http_content": &hcldec.AttrSpec{Name: "http_content", Type: cty.Map(cty.String), Required: false},
|
||||
"http_port_min": &hcldec.AttrSpec{Name: "http_port_min", Type: cty.Number, Required: false},
|
||||
"http_port_max": &hcldec.AttrSpec{Name: "http_port_max", Type: cty.Number, Required: false},
|
||||
"http_bind_address": &hcldec.AttrSpec{Name: "http_bind_address", Type: cty.String, Required: false},
|
||||
|
||||
@@ -64,12 +64,7 @@ func (b *Builder) Run(ctx context.Context, ui packersdk.Ui, hook packersdk.Hook)
|
||||
KeepRegistered: b.config.KeepRegistered,
|
||||
},
|
||||
new(vboxcommon.StepHTTPIPDiscover),
|
||||
&commonsteps.StepHTTPServer{
|
||||
HTTPDir: b.config.HTTPDir,
|
||||
HTTPPortMin: b.config.HTTPPortMin,
|
||||
HTTPPortMax: b.config.HTTPPortMax,
|
||||
HTTPAddress: b.config.HTTPAddress,
|
||||
},
|
||||
commonsteps.HTTPServerFromHTTPConfig(&b.config.HTTPConfig),
|
||||
&vboxcommon.StepDownloadGuestAdditions{
|
||||
GuestAdditionsMode: b.config.GuestAdditionsMode,
|
||||
GuestAdditionsURL: b.config.GuestAdditionsURL,
|
||||
@@ -114,7 +109,7 @@ func (b *Builder) Run(ctx context.Context, ui packersdk.Ui, hook packersdk.Hook)
|
||||
},
|
||||
&communicator.StepConnect{
|
||||
Config: &b.config.CommConfig.Comm,
|
||||
Host: vboxcommon.CommHost(b.config.CommConfig.Comm.SSHHost),
|
||||
Host: vboxcommon.CommHost(b.config.CommConfig.Comm.Host()),
|
||||
SSHConfig: b.config.CommConfig.Comm.SSHConfigFunc(),
|
||||
SSHPort: vboxcommon.CommPort,
|
||||
WinRMPort: vboxcommon.CommPort,
|
||||
|
||||
@@ -19,6 +19,7 @@ type FlatConfig struct {
|
||||
PackerUserVars map[string]string `mapstructure:"packer_user_variables" cty:"packer_user_variables" hcl:"packer_user_variables"`
|
||||
PackerSensitiveVars []string `mapstructure:"packer_sensitive_variables" cty:"packer_sensitive_variables" hcl:"packer_sensitive_variables"`
|
||||
HTTPDir *string `mapstructure:"http_directory" cty:"http_directory" hcl:"http_directory"`
|
||||
HTTPContent map[string]string `mapstructure:"http_content" cty:"http_content" hcl:"http_content"`
|
||||
HTTPPortMin *int `mapstructure:"http_port_min" cty:"http_port_min" hcl:"http_port_min"`
|
||||
HTTPPortMax *int `mapstructure:"http_port_max" cty:"http_port_max" hcl:"http_port_max"`
|
||||
HTTPAddress *string `mapstructure:"http_bind_address" cty:"http_bind_address" hcl:"http_bind_address"`
|
||||
@@ -136,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_sensitive_variables": &hcldec.AttrSpec{Name: "packer_sensitive_variables", Type: cty.List(cty.String), Required: false},
|
||||
"http_directory": &hcldec.AttrSpec{Name: "http_directory", Type: cty.String, Required: false},
|
||||
"http_content": &hcldec.AttrSpec{Name: "http_content", Type: cty.Map(cty.String), Required: false},
|
||||
"http_port_min": &hcldec.AttrSpec{Name: "http_port_min", Type: cty.Number, Required: false},
|
||||
"http_port_max": &hcldec.AttrSpec{Name: "http_port_max", Type: cty.Number, Required: false},
|
||||
"http_bind_address": &hcldec.AttrSpec{Name: "http_bind_address", Type: cty.String, Required: false},
|
||||
|
||||
@@ -117,12 +117,7 @@ func (b *Builder) Run(ctx context.Context, ui packersdk.Ui, hook packersdk.Hook)
|
||||
},
|
||||
&vmwcommon.StepSuppressMessages{},
|
||||
&vmwcommon.StepHTTPIPDiscover{},
|
||||
&commonsteps.StepHTTPServer{
|
||||
HTTPDir: b.config.HTTPDir,
|
||||
HTTPPortMin: b.config.HTTPPortMin,
|
||||
HTTPPortMax: b.config.HTTPPortMax,
|
||||
HTTPAddress: b.config.HTTPAddress,
|
||||
},
|
||||
commonsteps.HTTPServerFromHTTPConfig(&b.config.HTTPConfig),
|
||||
&vmwcommon.StepConfigureVNC{
|
||||
Enabled: !b.config.DisableVNC && !b.config.VNCOverWebsocket,
|
||||
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"`
|
||||
PackerSensitiveVars []string `mapstructure:"packer_sensitive_variables" cty:"packer_sensitive_variables" hcl:"packer_sensitive_variables"`
|
||||
HTTPDir *string `mapstructure:"http_directory" cty:"http_directory" hcl:"http_directory"`
|
||||
HTTPContent map[string]string `mapstructure:"http_content" cty:"http_content" hcl:"http_content"`
|
||||
HTTPPortMin *int `mapstructure:"http_port_min" cty:"http_port_min" hcl:"http_port_min"`
|
||||
HTTPPortMax *int `mapstructure:"http_port_max" cty:"http_port_max" hcl:"http_port_max"`
|
||||
HTTPAddress *string `mapstructure:"http_bind_address" cty:"http_bind_address" hcl:"http_bind_address"`
|
||||
@@ -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_sensitive_variables": &hcldec.AttrSpec{Name: "packer_sensitive_variables", Type: cty.List(cty.String), Required: false},
|
||||
"http_directory": &hcldec.AttrSpec{Name: "http_directory", Type: cty.String, Required: false},
|
||||
"http_content": &hcldec.AttrSpec{Name: "http_content", Type: cty.Map(cty.String), Required: false},
|
||||
"http_port_min": &hcldec.AttrSpec{Name: "http_port_min", Type: cty.Number, Required: false},
|
||||
"http_port_max": &hcldec.AttrSpec{Name: "http_port_max", Type: cty.Number, Required: false},
|
||||
"http_bind_address": &hcldec.AttrSpec{Name: "http_bind_address", Type: cty.String, Required: false},
|
||||
|
||||
@@ -108,12 +108,7 @@ func (b *Builder) Run(ctx context.Context, ui packersdk.Ui, hook packersdk.Hook)
|
||||
},
|
||||
&vmwcommon.StepSuppressMessages{},
|
||||
&vmwcommon.StepHTTPIPDiscover{},
|
||||
&commonsteps.StepHTTPServer{
|
||||
HTTPDir: b.config.HTTPDir,
|
||||
HTTPPortMin: b.config.HTTPPortMin,
|
||||
HTTPPortMax: b.config.HTTPPortMax,
|
||||
HTTPAddress: b.config.HTTPAddress,
|
||||
},
|
||||
commonsteps.HTTPServerFromHTTPConfig(&b.config.HTTPConfig),
|
||||
&vmwcommon.StepUploadVMX{
|
||||
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"`
|
||||
PackerSensitiveVars []string `mapstructure:"packer_sensitive_variables" cty:"packer_sensitive_variables" hcl:"packer_sensitive_variables"`
|
||||
HTTPDir *string `mapstructure:"http_directory" cty:"http_directory" hcl:"http_directory"`
|
||||
HTTPContent map[string]string `mapstructure:"http_content" cty:"http_content" hcl:"http_content"`
|
||||
HTTPPortMin *int `mapstructure:"http_port_min" cty:"http_port_min" hcl:"http_port_min"`
|
||||
HTTPPortMax *int `mapstructure:"http_port_max" cty:"http_port_max" hcl:"http_port_max"`
|
||||
HTTPAddress *string `mapstructure:"http_bind_address" cty:"http_bind_address" hcl:"http_bind_address"`
|
||||
@@ -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_sensitive_variables": &hcldec.AttrSpec{Name: "packer_sensitive_variables", Type: cty.List(cty.String), Required: false},
|
||||
"http_directory": &hcldec.AttrSpec{Name: "http_directory", Type: cty.String, Required: false},
|
||||
"http_content": &hcldec.AttrSpec{Name: "http_content", Type: cty.Map(cty.String), Required: false},
|
||||
"http_port_min": &hcldec.AttrSpec{Name: "http_port_min", Type: cty.Number, Required: false},
|
||||
"http_port_max": &hcldec.AttrSpec{Name: "http_port_max", Type: cty.Number, Required: false},
|
||||
"http_bind_address": &hcldec.AttrSpec{Name: "http_bind_address", Type: cty.String, Required: false},
|
||||
|
||||
@@ -89,12 +89,7 @@ func (b *Builder) Run(ctx context.Context, ui packersdk.Ui, hook packersdk.Hook)
|
||||
HTTPIP: b.config.BootConfig.HTTPIP,
|
||||
Network: b.config.WaitIpConfig.GetIPNet(),
|
||||
},
|
||||
&commonsteps.StepHTTPServer{
|
||||
HTTPDir: b.config.HTTPDir,
|
||||
HTTPPortMin: b.config.HTTPPortMin,
|
||||
HTTPPortMax: b.config.HTTPPortMax,
|
||||
HTTPAddress: b.config.HTTPAddress,
|
||||
},
|
||||
commonsteps.HTTPServerFromHTTPConfig(&b.config.HTTPConfig),
|
||||
&common.StepSshKeyPair{
|
||||
Debug: b.config.PackerDebug,
|
||||
DebugKeyPath: fmt.Sprintf("%s.pem", b.config.PackerBuildName),
|
||||
|
||||
@@ -20,6 +20,7 @@ type FlatConfig struct {
|
||||
PackerUserVars map[string]string `mapstructure:"packer_user_variables" cty:"packer_user_variables" hcl:"packer_user_variables"`
|
||||
PackerSensitiveVars []string `mapstructure:"packer_sensitive_variables" cty:"packer_sensitive_variables" hcl:"packer_sensitive_variables"`
|
||||
HTTPDir *string `mapstructure:"http_directory" cty:"http_directory" hcl:"http_directory"`
|
||||
HTTPContent map[string]string `mapstructure:"http_content" cty:"http_content" hcl:"http_content"`
|
||||
HTTPPortMin *int `mapstructure:"http_port_min" cty:"http_port_min" hcl:"http_port_min"`
|
||||
HTTPPortMax *int `mapstructure:"http_port_max" cty:"http_port_max" hcl:"http_port_max"`
|
||||
HTTPAddress *string `mapstructure:"http_bind_address" cty:"http_bind_address" hcl:"http_bind_address"`
|
||||
@@ -159,6 +160,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_sensitive_variables": &hcldec.AttrSpec{Name: "packer_sensitive_variables", Type: cty.List(cty.String), Required: false},
|
||||
"http_directory": &hcldec.AttrSpec{Name: "http_directory", Type: cty.String, Required: false},
|
||||
"http_content": &hcldec.AttrSpec{Name: "http_content", Type: cty.Map(cty.String), Required: false},
|
||||
"http_port_min": &hcldec.AttrSpec{Name: "http_port_min", Type: cty.Number, Required: false},
|
||||
"http_port_max": &hcldec.AttrSpec{Name: "http_port_max", Type: cty.Number, Required: false},
|
||||
"http_bind_address": &hcldec.AttrSpec{Name: "http_bind_address", Type: cty.String, Required: false},
|
||||
|
||||
@@ -99,7 +99,19 @@ func (s *StepBootCommand) Run(ctx context.Context, state multistep.StateBag) mul
|
||||
Shift: shift,
|
||||
})
|
||||
if err != nil {
|
||||
return fmt.Errorf("error typing a boot command (code, down) `%d, %t`: %w", code, down, err)
|
||||
// retry once if error
|
||||
ui.Error(fmt.Errorf("error typing a boot command (code, down) `%d, %t`: %w", code, down, err).Error())
|
||||
ui.Say("trying key input again")
|
||||
time.Sleep(s.Config.BootGroupInterval)
|
||||
_, err = vm.TypeOnKeyboard(driver.KeyInput{
|
||||
Scancode: code,
|
||||
Ctrl: keyCtrl,
|
||||
Alt: keyAlt,
|
||||
Shift: shift,
|
||||
})
|
||||
if err != nil {
|
||||
return fmt.Errorf("error typing a boot command (code, down) `%d, %t`: %w", code, down, err)
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
@@ -92,12 +92,7 @@ func (b *Builder) Run(ctx context.Context, ui packersdk.Ui, hook packersdk.Hook)
|
||||
HTTPIP: b.config.BootConfig.HTTPIP,
|
||||
Network: b.config.WaitIpConfig.GetIPNet(),
|
||||
},
|
||||
&commonsteps.StepHTTPServer{
|
||||
HTTPDir: b.config.HTTPDir,
|
||||
HTTPPortMin: b.config.HTTPPortMin,
|
||||
HTTPPortMax: b.config.HTTPPortMax,
|
||||
HTTPAddress: b.config.HTTPAddress,
|
||||
},
|
||||
commonsteps.HTTPServerFromHTTPConfig(&b.config.HTTPConfig),
|
||||
&common.StepRun{
|
||||
Config: &b.config.RunConfig,
|
||||
SetOrder: true,
|
||||
|
||||
@@ -20,6 +20,7 @@ type FlatConfig struct {
|
||||
PackerUserVars map[string]string `mapstructure:"packer_user_variables" cty:"packer_user_variables" hcl:"packer_user_variables"`
|
||||
PackerSensitiveVars []string `mapstructure:"packer_sensitive_variables" cty:"packer_sensitive_variables" hcl:"packer_sensitive_variables"`
|
||||
HTTPDir *string `mapstructure:"http_directory" cty:"http_directory" hcl:"http_directory"`
|
||||
HTTPContent map[string]string `mapstructure:"http_content" cty:"http_content" hcl:"http_content"`
|
||||
HTTPPortMin *int `mapstructure:"http_port_min" cty:"http_port_min" hcl:"http_port_min"`
|
||||
HTTPPortMax *int `mapstructure:"http_port_max" cty:"http_port_max" hcl:"http_port_max"`
|
||||
HTTPAddress *string `mapstructure:"http_bind_address" cty:"http_bind_address" hcl:"http_bind_address"`
|
||||
@@ -161,6 +162,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_sensitive_variables": &hcldec.AttrSpec{Name: "packer_sensitive_variables", Type: cty.List(cty.String), Required: false},
|
||||
"http_directory": &hcldec.AttrSpec{Name: "http_directory", Type: cty.String, Required: false},
|
||||
"http_content": &hcldec.AttrSpec{Name: "http_content", Type: cty.Map(cty.String), Required: false},
|
||||
"http_port_min": &hcldec.AttrSpec{Name: "http_port_min", Type: cty.Number, Required: false},
|
||||
"http_port_max": &hcldec.AttrSpec{Name: "http_port_max", Type: cty.Number, Required: false},
|
||||
"http_bind_address": &hcldec.AttrSpec{Name: "http_bind_address", Type: cty.String, Required: false},
|
||||
|
||||
+8
-16
@@ -389,7 +389,7 @@ func TestBuild(t *testing.T) {
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
defer tt.cleanup(t)
|
||||
run(t, tt.args, tt.expectedCode)
|
||||
tt.fileCheck.verify(t)
|
||||
tt.fileCheck.verify(t, "")
|
||||
})
|
||||
}
|
||||
}
|
||||
@@ -732,7 +732,7 @@ func TestHCL2PostProcessorForceFlag(t *testing.T) {
|
||||
if code := c.Run(args); code != 0 {
|
||||
fatalCommand(t, c.Meta)
|
||||
}
|
||||
fCheck.verify(t)
|
||||
fCheck.verify(t, "")
|
||||
|
||||
// Second build should override previous manifest
|
||||
UUID, _ = uuid.GenerateUUID()
|
||||
@@ -766,7 +766,7 @@ func TestHCL2PostProcessorForceFlag(t *testing.T) {
|
||||
if code := c.Run(args); code != 0 {
|
||||
fatalCommand(t, c.Meta)
|
||||
}
|
||||
fCheck.verify(t)
|
||||
fCheck.verify(t, "")
|
||||
}
|
||||
|
||||
func TestBuildCommand_HCLOnlyExceptOptions(t *testing.T) {
|
||||
@@ -887,19 +887,19 @@ func (fc fileCheck) expectedFiles() []string {
|
||||
return expected
|
||||
}
|
||||
|
||||
func (fc fileCheck) verify(t *testing.T) {
|
||||
func (fc fileCheck) verify(t *testing.T, dir string) {
|
||||
for _, f := range fc.expectedFiles() {
|
||||
if !fileExists(f) {
|
||||
t.Errorf("Expected to find %s", f)
|
||||
if _, err := os.Stat(filepath.Join(dir, f)); err != nil {
|
||||
t.Errorf("Expected to find %s: %v", f, err)
|
||||
}
|
||||
}
|
||||
for _, f := range fc.notExpected {
|
||||
if fileExists(f) {
|
||||
if _, err := os.Stat(filepath.Join(dir, f)); err == nil {
|
||||
t.Errorf("Expected to not find %s", f)
|
||||
}
|
||||
}
|
||||
for file, expectedContent := range fc.expectedContent {
|
||||
content, err := ioutil.ReadFile(file)
|
||||
content, err := ioutil.ReadFile(filepath.Join(dir, file))
|
||||
if err != nil {
|
||||
t.Fatalf("ioutil.ReadFile: %v", err)
|
||||
}
|
||||
@@ -909,14 +909,6 @@ func (fc fileCheck) verify(t *testing.T) {
|
||||
}
|
||||
}
|
||||
|
||||
// fileExists returns true if the filename is found
|
||||
func fileExists(filename string) bool {
|
||||
if _, err := os.Stat(filename); err == nil {
|
||||
return true
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
// testCoreConfigBuilder creates a packer CoreConfig that has a file builder
|
||||
// available. This allows us to test a builder that writes files to disk.
|
||||
func testCoreConfigBuilder(t *testing.T) *packer.CoreConfig {
|
||||
|
||||
+2
-2
@@ -161,12 +161,12 @@ func (va *FormatArgs) AddFlagSets(flags *flag.FlagSet) {
|
||||
flags.BoolVar(&va.Check, "check", false, "check if the input is formatted")
|
||||
flags.BoolVar(&va.Diff, "diff", false, "display the diff of formatting changes")
|
||||
flags.BoolVar(&va.Write, "write", true, "overwrite source files instead of writing to stdout")
|
||||
|
||||
flags.BoolVar(&va.Recursive, "recursive", false, "Also process files in subdirectories")
|
||||
va.MetaArgs.AddFlagSets(flags)
|
||||
}
|
||||
|
||||
// FormatArgs represents a parsed cli line for `packer fmt`
|
||||
type FormatArgs struct {
|
||||
MetaArgs
|
||||
Check, Diff, Write bool
|
||||
Check, Diff, Write, Recursive bool
|
||||
}
|
||||
|
||||
@@ -87,6 +87,8 @@ func TestHelperProcess(*testing.T) {
|
||||
switch cmd {
|
||||
case "console":
|
||||
os.Exit((&ConsoleCommand{Meta: commandMeta()}).Run(args))
|
||||
case "fmt":
|
||||
os.Exit((&FormatCommand{Meta: commandMeta()}).Run(args))
|
||||
case "inspect":
|
||||
os.Exit((&InspectCommand{Meta: commandMeta()}).Run(args))
|
||||
case "build":
|
||||
|
||||
+10
-6
@@ -48,9 +48,10 @@ func (c *FormatCommand) RunContext(ctx context.Context, cla *FormatArgs) int {
|
||||
}
|
||||
|
||||
formatter := hclutils.HCL2Formatter{
|
||||
ShowDiff: cla.Diff,
|
||||
Write: cla.Write,
|
||||
Output: os.Stdout,
|
||||
ShowDiff: cla.Diff,
|
||||
Write: cla.Write,
|
||||
Output: os.Stdout,
|
||||
Recursive: cla.Recursive,
|
||||
}
|
||||
|
||||
bytesModified, diags := formatter.Format(cla.Path)
|
||||
@@ -90,6 +91,8 @@ Options:
|
||||
-write=false Don't write to source files
|
||||
(always disabled if using -check)
|
||||
|
||||
-recursive Also process files in subdirectories. By default, only the
|
||||
given directory (or current directory) is processed.
|
||||
`
|
||||
|
||||
return strings.TrimSpace(helpText)
|
||||
@@ -105,8 +108,9 @@ func (*FormatCommand) AutocompleteArgs() complete.Predictor {
|
||||
|
||||
func (*FormatCommand) AutocompleteFlags() complete.Flags {
|
||||
return complete.Flags{
|
||||
"-check": complete.PredictNothing,
|
||||
"-diff": complete.PredictNothing,
|
||||
"-write": complete.PredictNothing,
|
||||
"-check": complete.PredictNothing,
|
||||
"-diff": complete.PredictNothing,
|
||||
"-write": complete.PredictNothing,
|
||||
"-recursive": complete.PredictNothing,
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,10 +1,15 @@
|
||||
package command
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"fmt"
|
||||
"io/ioutil"
|
||||
"os"
|
||||
"path/filepath"
|
||||
"strings"
|
||||
"testing"
|
||||
|
||||
"github.com/google/go-cmp/cmp"
|
||||
packersdk "github.com/hashicorp/packer-plugin-sdk/packer"
|
||||
"github.com/stretchr/testify/assert"
|
||||
)
|
||||
@@ -50,3 +55,122 @@ func TestFmt_unfomattedTemlateDirectory(t *testing.T) {
|
||||
fatalCommand(t, c.Meta)
|
||||
}
|
||||
}
|
||||
|
||||
const (
|
||||
unformattedHCL = `
|
||||
ami_filter_name ="amzn2-ami-hvm-*-x86_64-gp2"
|
||||
ami_filter_owners =[ "137112412989" ]
|
||||
|
||||
`
|
||||
formattedHCL = `
|
||||
ami_filter_name = "amzn2-ami-hvm-*-x86_64-gp2"
|
||||
ami_filter_owners = ["137112412989"]
|
||||
|
||||
`
|
||||
)
|
||||
|
||||
func TestFmt_Recursive(t *testing.T) {
|
||||
|
||||
tests := []struct {
|
||||
name string
|
||||
formatArgs []string // arguments passed to format
|
||||
alreadyPresentContent map[string]string
|
||||
fileCheck
|
||||
}{
|
||||
{
|
||||
name: "nested formats recursively",
|
||||
formatArgs: []string{"-recursive=true"},
|
||||
alreadyPresentContent: map[string]string{
|
||||
"foo/bar/baz.pkr.hcl": unformattedHCL,
|
||||
"foo/bar/baz/woo.pkrvars.hcl": unformattedHCL,
|
||||
"potato": unformattedHCL,
|
||||
"foo/bar/potato": unformattedHCL,
|
||||
"bar.pkr.hcl": unformattedHCL,
|
||||
"-": unformattedHCL,
|
||||
},
|
||||
fileCheck: fileCheck{
|
||||
expectedContent: map[string]string{
|
||||
"foo/bar/baz.pkr.hcl": formattedHCL,
|
||||
"foo/bar/baz/woo.pkrvars.hcl": formattedHCL,
|
||||
"potato": unformattedHCL,
|
||||
"foo/bar/potato": unformattedHCL,
|
||||
"bar.pkr.hcl": formattedHCL,
|
||||
"-": unformattedHCL,
|
||||
}},
|
||||
},
|
||||
{
|
||||
name: "nested no recursive format",
|
||||
formatArgs: []string{},
|
||||
alreadyPresentContent: map[string]string{
|
||||
"foo/bar/baz.pkr.hcl": unformattedHCL,
|
||||
"foo/bar/baz/woo.pkrvars.hcl": unformattedHCL,
|
||||
"bar.pkr.hcl": unformattedHCL,
|
||||
"-": unformattedHCL,
|
||||
},
|
||||
fileCheck: fileCheck{
|
||||
expectedContent: map[string]string{
|
||||
"foo/bar/baz.pkr.hcl": unformattedHCL,
|
||||
"foo/bar/baz/woo.pkrvars.hcl": unformattedHCL,
|
||||
"bar.pkr.hcl": formattedHCL,
|
||||
"-": unformattedHCL,
|
||||
}},
|
||||
},
|
||||
}
|
||||
|
||||
c := &FormatCommand{
|
||||
Meta: testMeta(t),
|
||||
}
|
||||
|
||||
testDir := "test-fixtures/fmt"
|
||||
|
||||
for _, tt := range tests {
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
tempDirectory := mustString(ioutil.TempDir(testDir, "test-dir-*"))
|
||||
defer os.RemoveAll(tempDirectory)
|
||||
|
||||
createFiles(tempDirectory, tt.alreadyPresentContent)
|
||||
|
||||
testArgs := append(tt.formatArgs, tempDirectory)
|
||||
if code := c.Run(testArgs); code != 0 {
|
||||
ui := c.Meta.Ui.(*packersdk.BasicUi)
|
||||
out := ui.Writer.(*bytes.Buffer)
|
||||
err := ui.ErrorWriter.(*bytes.Buffer)
|
||||
t.Fatalf(
|
||||
"Bad exit code for test case: %s.\n\nStdout:\n\n%s\n\nStderr:\n\n%s",
|
||||
tt.name,
|
||||
out.String(),
|
||||
err.String())
|
||||
}
|
||||
|
||||
tt.fileCheck.verify(t, tempDirectory)
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func Test_fmt_pipe(t *testing.T) {
|
||||
|
||||
tc := []struct {
|
||||
piped string
|
||||
command []string
|
||||
env []string
|
||||
expected string
|
||||
}{
|
||||
{unformattedHCL, []string{"fmt", "-"}, nil, formattedHCL},
|
||||
}
|
||||
|
||||
for _, tc := range tc {
|
||||
t.Run(fmt.Sprintf("echo %q | packer %s", tc.piped, tc.command), func(t *testing.T) {
|
||||
p := helperCommand(t, tc.command...)
|
||||
p.Stdin = strings.NewReader(tc.piped)
|
||||
p.Env = append(p.Env, tc.env...)
|
||||
fmt.Println(fmt.Sprintf("Path: %s", p.Path))
|
||||
bs, err := p.Output()
|
||||
if err != nil {
|
||||
t.Fatalf("Error occurred running command %v: %s", err, bs)
|
||||
}
|
||||
if diff := cmp.Diff(tc.expected, string(bs)); diff != "" {
|
||||
t.Fatalf("Error in diff: %s", diff)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
+128
-49
@@ -8,7 +8,9 @@ import (
|
||||
"os"
|
||||
"path/filepath"
|
||||
"reflect"
|
||||
"regexp"
|
||||
"sort"
|
||||
"strconv"
|
||||
"strings"
|
||||
texttemplate "text/template"
|
||||
|
||||
@@ -120,6 +122,7 @@ var (
|
||||
amazonSecretsManagerMap = map[string]map[string]interface{}{}
|
||||
localsVariableMap = map[string]string{}
|
||||
timestamp = false
|
||||
isotime = false
|
||||
)
|
||||
|
||||
type BlockParser interface {
|
||||
@@ -269,18 +272,18 @@ func (uc UnhandleableArgumentError) Error() string {
|
||||
# Visit %s for more infos.`, uc.Call, uc.Correspondance, uc.Docs)
|
||||
}
|
||||
|
||||
func fallbackReturn(err error, s []byte) []byte {
|
||||
if strings.Contains(err.Error(), "unhandled") {
|
||||
return append([]byte(fmt.Sprintf("\n# %s\n", err)), s...)
|
||||
}
|
||||
|
||||
return append([]byte(fmt.Sprintf("\n# could not parse template for following block: %q\n", err)), s...)
|
||||
}
|
||||
|
||||
// transposeTemplatingCalls executes parts of blocks as go template files and replaces
|
||||
// their result with their hcl2 variant. If something goes wrong the template
|
||||
// containing the go template string is returned.
|
||||
func transposeTemplatingCalls(s []byte) []byte {
|
||||
fallbackReturn := func(err error) []byte {
|
||||
if strings.Contains(err.Error(), "unhandled") {
|
||||
return append([]byte(fmt.Sprintf("\n# %s\n", err)), s...)
|
||||
}
|
||||
|
||||
return append([]byte(fmt.Sprintf("\n# could not parse template for following block: %q\n", err)), s...)
|
||||
}
|
||||
|
||||
funcErrors := &multierror.Error{
|
||||
ErrorFormat: func(es []error) string {
|
||||
if len(es) == 1 {
|
||||
@@ -336,8 +339,14 @@ func transposeTemplatingCalls(s []byte) []byte {
|
||||
return "${local.timestamp}"
|
||||
},
|
||||
"isotime": func(a ...string) string {
|
||||
timestamp = true
|
||||
return "${local.timestamp}"
|
||||
if len(a) == 0 {
|
||||
// returns rfc3339 formatted string.
|
||||
return "${timestamp()}"
|
||||
}
|
||||
// otherwise a valid isotime func has one input.
|
||||
isotime = true
|
||||
return fmt.Sprintf("${legacy_isotime(\"%s\")}", a[0])
|
||||
|
||||
},
|
||||
"user": func(in string) string {
|
||||
if _, ok := localsVariableMap[in]; ok {
|
||||
@@ -430,14 +439,29 @@ func transposeTemplatingCalls(s []byte) []byte {
|
||||
Parse(string(s))
|
||||
|
||||
if err != nil {
|
||||
return fallbackReturn(err)
|
||||
if strings.Contains(err.Error(), "unexpected \"\\\\\" in operand") {
|
||||
// This error occurs if the operand in the text template used
|
||||
// escaped quoting \" instead of bactick quoting `
|
||||
// Create a regex to do a string replace on this block, to fix
|
||||
// quoting.
|
||||
q := fixQuoting(string(s))
|
||||
unquoted := []byte(q)
|
||||
tpl, err = texttemplate.New("hcl2_upgrade").
|
||||
Funcs(funcMap).
|
||||
Parse(string(unquoted))
|
||||
if err != nil {
|
||||
return fallbackReturn(err, unquoted)
|
||||
}
|
||||
} else {
|
||||
return fallbackReturn(err, s)
|
||||
}
|
||||
}
|
||||
|
||||
str := &bytes.Buffer{}
|
||||
// PASSTHROUGHS is a map of variable-specific golang text template fields
|
||||
// that should remain in the text template format.
|
||||
if err := tpl.Execute(str, PASSTHROUGHS); err != nil {
|
||||
return fallbackReturn(err)
|
||||
return fallbackReturn(err, s)
|
||||
}
|
||||
|
||||
out := str.Bytes()
|
||||
@@ -454,14 +478,6 @@ func transposeTemplatingCalls(s []byte) []byte {
|
||||
// In variableTransposeTemplatingCalls the definition of aws_secretsmanager function will create a data source
|
||||
// with the same name as the variable.
|
||||
func variableTransposeTemplatingCalls(s []byte) (isLocal bool, body []byte) {
|
||||
fallbackReturn := func(err error) []byte {
|
||||
if strings.Contains(err.Error(), "unhandled") {
|
||||
return append([]byte(fmt.Sprintf("\n# %s\n", err)), s...)
|
||||
}
|
||||
|
||||
return append([]byte(fmt.Sprintf("\n# could not parse template for following block: %q\n", err)), s...)
|
||||
}
|
||||
|
||||
setIsLocal := func(a ...string) string {
|
||||
isLocal = true
|
||||
return ""
|
||||
@@ -503,14 +519,29 @@ func variableTransposeTemplatingCalls(s []byte) (isLocal bool, body []byte) {
|
||||
Parse(string(s))
|
||||
|
||||
if err != nil {
|
||||
return isLocal, fallbackReturn(err)
|
||||
if strings.Contains(err.Error(), "unexpected \"\\\\\" in operand") {
|
||||
// This error occurs if the operand in the text template used
|
||||
// escaped quoting \" instead of bactick quoting `
|
||||
// Create a regex to do a string replace on this block, to fix
|
||||
// quoting.
|
||||
q := fixQuoting(string(s))
|
||||
unquoted := []byte(q)
|
||||
tpl, err = texttemplate.New("hcl2_upgrade").
|
||||
Funcs(funcMap).
|
||||
Parse(string(unquoted))
|
||||
if err != nil {
|
||||
return isLocal, fallbackReturn(err, unquoted)
|
||||
}
|
||||
} else {
|
||||
return isLocal, fallbackReturn(err, s)
|
||||
}
|
||||
}
|
||||
|
||||
str := &bytes.Buffer{}
|
||||
// PASSTHROUGHS is a map of variable-specific golang text template fields
|
||||
// that should remain in the text template format.
|
||||
if err := tpl.Execute(str, PASSTHROUGHS); err != nil {
|
||||
return isLocal, fallbackReturn(err)
|
||||
return isLocal, fallbackReturn(err, s)
|
||||
}
|
||||
|
||||
return isLocal, str.Bytes()
|
||||
@@ -675,12 +706,49 @@ type VariableParser struct {
|
||||
localsOut []byte
|
||||
}
|
||||
|
||||
func makeLocal(variable *template.Variable, sensitive bool, localBody *hclwrite.Body, localsContent *hclwrite.File, hasLocals *bool) []byte {
|
||||
if sensitive {
|
||||
// Create Local block because this is sensitive
|
||||
sensitiveLocalContent := hclwrite.NewEmptyFile()
|
||||
body := sensitiveLocalContent.Body()
|
||||
body.AppendNewline()
|
||||
sensitiveLocalBody := body.AppendNewBlock("local", []string{variable.Key}).Body()
|
||||
sensitiveLocalBody.SetAttributeValue("sensitive", cty.BoolVal(true))
|
||||
sensitiveLocalBody.SetAttributeValue("expression", hcl2shim.HCL2ValueFromConfigValue(variable.Default))
|
||||
localsVariableMap[variable.Key] = "local"
|
||||
return sensitiveLocalContent.Bytes()
|
||||
}
|
||||
localBody.SetAttributeValue(variable.Key, hcl2shim.HCL2ValueFromConfigValue(variable.Default))
|
||||
localsVariableMap[variable.Key] = "locals"
|
||||
*hasLocals = true
|
||||
return []byte{}
|
||||
}
|
||||
|
||||
func makeVariable(variable *template.Variable, sensitive bool) []byte {
|
||||
variablesContent := hclwrite.NewEmptyFile()
|
||||
variablesBody := variablesContent.Body()
|
||||
variablesBody.AppendNewline()
|
||||
variableBody := variablesBody.AppendNewBlock("variable", []string{variable.Key}).Body()
|
||||
variableBody.SetAttributeRaw("type", hclwrite.Tokens{&hclwrite.Token{Bytes: []byte("string")}})
|
||||
|
||||
if variable.Default != "" || !variable.Required {
|
||||
shimmed := hcl2shim.HCL2ValueFromConfigValue(variable.Default)
|
||||
variableBody.SetAttributeValue("default", shimmed)
|
||||
}
|
||||
if sensitive {
|
||||
variableBody.SetAttributeValue("sensitive", cty.BoolVal(true))
|
||||
}
|
||||
|
||||
return variablesContent.Bytes()
|
||||
}
|
||||
|
||||
func (p *VariableParser) Parse(tpl *template.Template) error {
|
||||
// OutPut Locals and Local blocks
|
||||
// Output Locals and Local blocks
|
||||
localsContent := hclwrite.NewEmptyFile()
|
||||
localsBody := localsContent.Body()
|
||||
localsBody.AppendNewline()
|
||||
localBody := localsBody.AppendNewBlock("locals", nil).Body()
|
||||
hasLocals := false
|
||||
|
||||
if len(p.variablesOut) == 0 {
|
||||
p.variablesOut = []byte{}
|
||||
@@ -700,47 +768,34 @@ func (p *VariableParser) Parse(tpl *template.Template) error {
|
||||
})
|
||||
}
|
||||
|
||||
hasLocals := false
|
||||
for _, variable := range variables {
|
||||
variablesContent := hclwrite.NewEmptyFile()
|
||||
variablesBody := variablesContent.Body()
|
||||
variablesBody.AppendNewline()
|
||||
variableBody := variablesBody.AppendNewBlock("variable", []string{variable.Key}).Body()
|
||||
variableBody.SetAttributeRaw("type", hclwrite.Tokens{&hclwrite.Token{Bytes: []byte("string")}})
|
||||
// Create new HCL2 "variables" block, and populate the "value"
|
||||
// field with the "Default" value from the JSON variable.
|
||||
|
||||
if variable.Default != "" || !variable.Required {
|
||||
variableBody.SetAttributeValue("default", hcl2shim.HCL2ValueFromConfigValue(variable.Default))
|
||||
}
|
||||
// Interpolate Jsonval first as an hcl variable to determine if it is
|
||||
// a local.
|
||||
isLocal, _ := variableTransposeTemplatingCalls([]byte(variable.Default))
|
||||
sensitive := false
|
||||
if isSensitiveVariable(variable.Key, tpl.SensitiveVariables) {
|
||||
sensitive = true
|
||||
variableBody.SetAttributeValue("sensitive", cty.BoolVal(true))
|
||||
}
|
||||
isLocal, out := variableTransposeTemplatingCalls(variablesContent.Bytes())
|
||||
// Create final HCL block and append.
|
||||
if isLocal {
|
||||
if sensitive {
|
||||
// Create Local block because this is sensitive
|
||||
localContent := hclwrite.NewEmptyFile()
|
||||
body := localContent.Body()
|
||||
body.AppendNewline()
|
||||
localBody := body.AppendNewBlock("local", []string{variable.Key}).Body()
|
||||
localBody.SetAttributeValue("sensitive", cty.BoolVal(true))
|
||||
localBody.SetAttributeValue("expression", hcl2shim.HCL2ValueFromConfigValue(variable.Default))
|
||||
p.localsOut = append(p.localsOut, transposeTemplatingCalls(localContent.Bytes())...)
|
||||
localsVariableMap[variable.Key] = "local"
|
||||
continue
|
||||
sensitiveBlocks := makeLocal(variable, sensitive, localBody, localsContent, &hasLocals)
|
||||
if len(sensitiveBlocks) > 0 {
|
||||
p.localsOut = append(p.localsOut, transposeTemplatingCalls(sensitiveBlocks)...)
|
||||
}
|
||||
localBody.SetAttributeValue(variable.Key, hcl2shim.HCL2ValueFromConfigValue(variable.Default))
|
||||
localsVariableMap[variable.Key] = "locals"
|
||||
hasLocals = true
|
||||
continue
|
||||
}
|
||||
varbytes := makeVariable(variable, sensitive)
|
||||
_, out := variableTransposeTemplatingCalls(varbytes)
|
||||
p.variablesOut = append(p.variablesOut, out...)
|
||||
}
|
||||
|
||||
if hasLocals {
|
||||
if hasLocals == true {
|
||||
p.localsOut = append(p.localsOut, transposeTemplatingCalls(localsContent.Bytes())...)
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
@@ -771,6 +826,9 @@ func (p *LocalsParser) Write(out *bytes.Buffer) {
|
||||
}
|
||||
fmt.Fprintln(out, `locals { timestamp = regex_replace(timestamp(), "[- TZ:]", "") }`)
|
||||
}
|
||||
if isotime {
|
||||
fmt.Fprintln(out, `# The "legacy_isotime" function has been provided for backwards compatability, but we recommend switching to the timestamp and formatdate functions.`)
|
||||
}
|
||||
if len(p.LocalsOut) > 0 {
|
||||
if p.WithAnnotations {
|
||||
out.Write([]byte(localsVarHeader))
|
||||
@@ -1158,6 +1216,7 @@ var PASSTHROUGHS = map[string]string{"NVME_Present": "{{ .NVME_Present }}",
|
||||
"WinRMPassword": "{{ .WinRMPassword }}",
|
||||
"DefaultOrganizationID": "{{ .DefaultOrganizationID }}",
|
||||
"HTTPDir": "{{ .HTTPDir }}",
|
||||
"HTTPContent": "{{ .HTTPContent }}",
|
||||
"SegmentPath": "{{ .SegmentPath }}",
|
||||
"NewVHDSizeBytes": "{{ .NewVHDSizeBytes }}",
|
||||
"CTyp": "{{ .CTyp }}",
|
||||
@@ -1221,3 +1280,23 @@ var PASSTHROUGHS = map[string]string{"NVME_Present": "{{ .NVME_Present }}",
|
||||
"ProviderVagrantfile": "{{ .ProviderVagrantfile }}",
|
||||
"Sound_Present": "{{ .Sound_Present }}",
|
||||
}
|
||||
|
||||
func fixQuoting(old string) string {
|
||||
// This regex captures golang template functions that use escaped quotes:
|
||||
// {{ env \"myvar\" }}
|
||||
re := regexp.MustCompile(`{{\s*\w*\s*(\\".*\\")\s*}}`)
|
||||
|
||||
body := re.ReplaceAllFunc([]byte(old), func(s []byte) []byte {
|
||||
// Get the capture group
|
||||
group := re.ReplaceAllString(string(s), `$1`)
|
||||
|
||||
unquoted, err := strconv.Unquote(fmt.Sprintf("\"%s\"", group))
|
||||
if err != nil {
|
||||
return s
|
||||
}
|
||||
return []byte(strings.Replace(string(s), group, unquoted, 1))
|
||||
|
||||
})
|
||||
|
||||
return string(body)
|
||||
}
|
||||
|
||||
@@ -29,6 +29,7 @@ func Test_hcl2_upgrade(t *testing.T) {
|
||||
{folder: "variables-only", flags: []string{}},
|
||||
{folder: "variables-with-variables", flags: []string{}},
|
||||
{folder: "complete-variables-with-template-engine", flags: []string{}},
|
||||
{folder: "escaping", flags: []string{}},
|
||||
}
|
||||
|
||||
for _, tc := range tc {
|
||||
@@ -46,8 +47,8 @@ func Test_hcl2_upgrade(t *testing.T) {
|
||||
if err != nil {
|
||||
t.Fatalf("%v %s", err, bs)
|
||||
}
|
||||
expected := mustBytes(ioutil.ReadFile(expectedPath))
|
||||
actual := mustBytes(ioutil.ReadFile(outputPath))
|
||||
expected := string(mustBytes(ioutil.ReadFile(expectedPath)))
|
||||
actual := string(mustBytes(ioutil.ReadFile(outputPath)))
|
||||
|
||||
if diff := cmp.Diff(expected, actual); diff != "" {
|
||||
t.Fatalf("unexpected output: %s", diff)
|
||||
|
||||
+45
-2
@@ -121,13 +121,56 @@ func (c *InitCommand) RunContext(buildCtx context.Context, cla *InitArgs) int {
|
||||
Getters: getters,
|
||||
})
|
||||
if err != nil {
|
||||
c.Ui.Error(err.Error())
|
||||
ret = 1
|
||||
if pluginRequirement.Implicit {
|
||||
msg := fmt.Sprintf(`
|
||||
Warning! At least one component used in your config file(s) has moved out of
|
||||
Packer into the %q plugin.
|
||||
For that reason, Packer init tried to install the latest version of the %s
|
||||
plugin. Unfortunately, this failed :
|
||||
%s`,
|
||||
pluginRequirement.Identifier,
|
||||
pluginRequirement.Identifier.Type,
|
||||
err)
|
||||
c.Ui.Say(msg)
|
||||
} else {
|
||||
c.Ui.Error(err.Error())
|
||||
ret = 1
|
||||
}
|
||||
}
|
||||
if newInstall != nil {
|
||||
if pluginRequirement.Implicit {
|
||||
msg := fmt.Sprintf("Installed implicitly required plugin %s %s in %q", pluginRequirement.Identifier, newInstall.Version, newInstall.BinaryPath)
|
||||
ui.Say(msg)
|
||||
|
||||
warn := fmt.Sprintf(`
|
||||
Warning, at least one component used in your config file(s) has moved out of
|
||||
Packer into the %[2]q plugin and is now being implicitly required.
|
||||
For more details on implicitly required plugins see https://packer.io/docs/commands/init#implicit-required-plugin
|
||||
|
||||
To avoid any backward incompatible changes with your
|
||||
config file you may want to lock the plugin version by pasting the following to your config:
|
||||
|
||||
packer {
|
||||
required_plugins {
|
||||
%[1]s = {
|
||||
source = "%[2]s"
|
||||
version = "~> %[3]s"
|
||||
}
|
||||
}
|
||||
}
|
||||
`,
|
||||
pluginRequirement.Identifier.Type,
|
||||
pluginRequirement.Identifier,
|
||||
newInstall.Version,
|
||||
)
|
||||
ui.Error(warn)
|
||||
continue
|
||||
}
|
||||
msg := fmt.Sprintf("Installed plugin %s %s in %q", pluginRequirement.Identifier, newInstall.Version, newInstall.BinaryPath)
|
||||
ui.Say(msg)
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
return ret
|
||||
}
|
||||
|
||||
@@ -24,7 +24,6 @@ import (
|
||||
azuredtlbuilder "github.com/hashicorp/packer/builder/azure/dtl"
|
||||
cloudstackbuilder "github.com/hashicorp/packer/builder/cloudstack"
|
||||
digitaloceanbuilder "github.com/hashicorp/packer/builder/digitalocean"
|
||||
dockerbuilder "github.com/hashicorp/packer/builder/docker"
|
||||
filebuilder "github.com/hashicorp/packer/builder/file"
|
||||
googlecomputebuilder "github.com/hashicorp/packer/builder/googlecompute"
|
||||
hcloudbuilder "github.com/hashicorp/packer/builder/hcloud"
|
||||
@@ -73,10 +72,6 @@ import (
|
||||
checksumpostprocessor "github.com/hashicorp/packer/post-processor/checksum"
|
||||
compresspostprocessor "github.com/hashicorp/packer/post-processor/compress"
|
||||
digitaloceanimportpostprocessor "github.com/hashicorp/packer/post-processor/digitalocean-import"
|
||||
dockerimportpostprocessor "github.com/hashicorp/packer/post-processor/docker-import"
|
||||
dockerpushpostprocessor "github.com/hashicorp/packer/post-processor/docker-push"
|
||||
dockersavepostprocessor "github.com/hashicorp/packer/post-processor/docker-save"
|
||||
dockertagpostprocessor "github.com/hashicorp/packer/post-processor/docker-tag"
|
||||
googlecomputeexportpostprocessor "github.com/hashicorp/packer/post-processor/googlecompute-export"
|
||||
googlecomputeimportpostprocessor "github.com/hashicorp/packer/post-processor/googlecompute-import"
|
||||
manifestpostprocessor "github.com/hashicorp/packer/post-processor/manifest"
|
||||
@@ -124,7 +119,6 @@ var Builders = map[string]packersdk.Builder{
|
||||
"azure-dtl": new(azuredtlbuilder.Builder),
|
||||
"cloudstack": new(cloudstackbuilder.Builder),
|
||||
"digitalocean": new(digitaloceanbuilder.Builder),
|
||||
"docker": new(dockerbuilder.Builder),
|
||||
"file": new(filebuilder.Builder),
|
||||
"googlecompute": new(googlecomputebuilder.Builder),
|
||||
"hcloud": new(hcloudbuilder.Builder),
|
||||
@@ -195,10 +189,6 @@ var PostProcessors = map[string]packersdk.PostProcessor{
|
||||
"checksum": new(checksumpostprocessor.PostProcessor),
|
||||
"compress": new(compresspostprocessor.PostProcessor),
|
||||
"digitalocean-import": new(digitaloceanimportpostprocessor.PostProcessor),
|
||||
"docker-import": new(dockerimportpostprocessor.PostProcessor),
|
||||
"docker-push": new(dockerpushpostprocessor.PostProcessor),
|
||||
"docker-save": new(dockersavepostprocessor.PostProcessor),
|
||||
"docker-tag": new(dockertagpostprocessor.PostProcessor),
|
||||
"googlecompute-export": new(googlecomputeexportpostprocessor.PostProcessor),
|
||||
"googlecompute-import": new(googlecomputeimportpostprocessor.PostProcessor),
|
||||
"manifest": new(manifestpostprocessor.PostProcessor),
|
||||
|
||||
+2
-1
@@ -5,6 +5,7 @@ variable "env_test" {
|
||||
}
|
||||
|
||||
locals { timestamp = regex_replace(timestamp(), "[- TZ:]", "") }
|
||||
# The "legacy_isotime" function has been provided for backwards compatability, but we recommend switching to the timestamp and formatdate functions.
|
||||
|
||||
# 5 errors occurred upgrading the following block:
|
||||
# unhandled "lower" call:
|
||||
@@ -33,7 +34,7 @@ locals { timestamp = regex_replace(timestamp(), "[- TZ:]", "") }
|
||||
# Visit https://www.packer.io/docs/templates/hcl_templates/functions/string/upper for more infos.
|
||||
locals {
|
||||
build_timestamp = "${local.timestamp}"
|
||||
iso_datetime = "${local.timestamp}"
|
||||
iso_datetime = "${legacy_isotime("2006-01-02T15:04:05Z07:00")}"
|
||||
lower = "{{ lower `HELLO` }}"
|
||||
pwd = "${path.cwd}"
|
||||
replace = "{{ replace `b` `c` `ababa` 2 }}"
|
||||
|
||||
@@ -198,7 +198,7 @@ build {
|
||||
# Please manually upgrade to use custom validation rules, `replace(string, substring, replacement)` or `regex_replace(string, substring, replacement)`
|
||||
# Visit https://packer.io/docs/templates/hcl_templates/variables#custom-validation-rules , https://www.packer.io/docs/templates/hcl_templates/functions/string/replace or https://www.packer.io/docs/templates/hcl_templates/functions/string/regex_replace for more infos.
|
||||
provisioner "shell" {
|
||||
inline = ["echo mybuild-{{ clean_resource_name `${local.timestamp}` }}"]
|
||||
inline = ["echo mybuild-{{ clean_resource_name `${timestamp()}` }}"]
|
||||
}
|
||||
|
||||
|
||||
|
||||
@@ -0,0 +1,31 @@
|
||||
|
||||
variable "conf" {
|
||||
type = string
|
||||
default = "${env("ONE")}-${env("ANOTHER")}-${env("BACKTICKED")}"
|
||||
}
|
||||
|
||||
variable "manyspaces" {
|
||||
type = string
|
||||
default = "${env("ASDFASDF")}"
|
||||
}
|
||||
|
||||
variable "nospaces" {
|
||||
type = string
|
||||
default = "${env("SOMETHING")}"
|
||||
}
|
||||
|
||||
locals { timestamp = regex_replace(timestamp(), "[- TZ:]", "") }
|
||||
# The "legacy_isotime" function has been provided for backwards compatability, but we recommend switching to the timestamp and formatdate functions.
|
||||
|
||||
source "null" "autogenerated_1" {
|
||||
communicator = "none"
|
||||
}
|
||||
|
||||
build {
|
||||
sources = ["source.null.autogenerated_1"]
|
||||
|
||||
provisioner "shell-local" {
|
||||
inline = ["echo ${var.conf}-${local.timestamp}-${legacy_isotime("01-02-2006")}"]
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,21 @@
|
||||
{
|
||||
"variables": {
|
||||
"conf": "{{ env \"ONE\" }}-{{env \"ANOTHER\"}}-{{ env `BACKTICKED` }}",
|
||||
"nospaces": "{{env \"SOMETHING\"}}",
|
||||
"manyspaces": "{{ env \"ASDFASDF\"}}"
|
||||
},
|
||||
"builders": [
|
||||
{
|
||||
"type": "null",
|
||||
"communicator": "none"
|
||||
}
|
||||
],
|
||||
"provisioners": [
|
||||
{
|
||||
"type": "shell-local",
|
||||
"inline": [
|
||||
"echo {{user \"conf\"}}-{{timestamp}}-{{isotime \"01-02-2006\"}}"
|
||||
]
|
||||
}
|
||||
]
|
||||
}
|
||||
@@ -151,7 +151,7 @@ build {
|
||||
# Please manually upgrade to use custom validation rules, `replace(string, substring, replacement)` or `regex_replace(string, substring, replacement)`
|
||||
# Visit https://packer.io/docs/templates/hcl_templates/variables#custom-validation-rules , https://www.packer.io/docs/templates/hcl_templates/functions/string/replace or https://www.packer.io/docs/templates/hcl_templates/functions/string/regex_replace for more infos.
|
||||
provisioner "shell" {
|
||||
inline = ["echo mybuild-{{ clean_resource_name `${local.timestamp}` }}"]
|
||||
inline = ["echo mybuild-{{ clean_resource_name `${timestamp()}` }}"]
|
||||
}
|
||||
|
||||
|
||||
|
||||
@@ -39,3 +39,11 @@ func (c *configDirSingleton) dir(key string) string {
|
||||
c.dirs[key] = mustString(ioutil.TempDir("", "pkr-test-cfg-dir-"+key))
|
||||
return c.dirs[key]
|
||||
}
|
||||
|
||||
// fileExists returns true if the filename is found
|
||||
func fileExists(filename string) bool {
|
||||
if _, err := os.Stat(filename); err == nil {
|
||||
return true
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
@@ -0,0 +1,60 @@
|
||||
package command
|
||||
|
||||
import (
|
||||
packersdk "github.com/hashicorp/packer-plugin-sdk/packer"
|
||||
|
||||
// Previously core-bundled components, split into their own plugins but
|
||||
// still vendored with Packer for now. Importing as library instead of
|
||||
// forcing use of packer init, until packer v1.8.0
|
||||
exoscaleimportpostprocessor "github.com/exoscale/packer-plugin-exoscale/post-processor/exoscale-import"
|
||||
dockerbuilder "github.com/hashicorp/packer-plugin-docker/builder/docker"
|
||||
dockerimportpostprocessor "github.com/hashicorp/packer-plugin-docker/post-processor/docker-import"
|
||||
dockerpushpostprocessor "github.com/hashicorp/packer-plugin-docker/post-processor/docker-push"
|
||||
dockersavepostprocessor "github.com/hashicorp/packer-plugin-docker/post-processor/docker-save"
|
||||
dockertagpostprocessor "github.com/hashicorp/packer-plugin-docker/post-processor/docker-tag"
|
||||
)
|
||||
|
||||
// VendoredBuilders are builder components that were once bundled with the
|
||||
// Packer core, but are now being imported from their counterpart plugin repos
|
||||
var VendoredBuilders = map[string]packersdk.Builder{
|
||||
"docker": new(dockerbuilder.Builder),
|
||||
}
|
||||
|
||||
// VendoredProvisioners are provisioner components that were once bundled with the
|
||||
// Packer core, but are now being imported from their counterpart plugin repos
|
||||
var VendoredProvisioners = map[string]packersdk.Provisioner{}
|
||||
|
||||
// VendoredPostProcessors are post-processor components that were once bundled with the
|
||||
// Packer core, but are now being imported from their counterpart plugin repos
|
||||
var VendoredPostProcessors = map[string]packersdk.PostProcessor{
|
||||
"docker-import": new(dockerimportpostprocessor.PostProcessor),
|
||||
"docker-push": new(dockerpushpostprocessor.PostProcessor),
|
||||
"docker-save": new(dockersavepostprocessor.PostProcessor),
|
||||
"docker-tag": new(dockertagpostprocessor.PostProcessor),
|
||||
"exoscale-import": new(exoscaleimportpostprocessor.PostProcessor),
|
||||
}
|
||||
|
||||
// Upon init lets load up any plugins that were vendored manually into the default
|
||||
// set of plugins.
|
||||
func init() {
|
||||
for k, v := range VendoredBuilders {
|
||||
if _, ok := Builders[k]; ok {
|
||||
continue
|
||||
}
|
||||
Builders[k] = v
|
||||
}
|
||||
|
||||
for k, v := range VendoredProvisioners {
|
||||
if _, ok := Provisioners[k]; ok {
|
||||
continue
|
||||
}
|
||||
Provisioners[k] = v
|
||||
}
|
||||
|
||||
for k, v := range VendoredPostProcessors {
|
||||
if _, ok := PostProcessors[k]; ok {
|
||||
continue
|
||||
}
|
||||
PostProcessors[k] = v
|
||||
}
|
||||
}
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user