mirror of
https://github.com/hashicorp/packer.git
synced 2026-09-22 07:51:39 -04:00
Merge remote-tracking branch 'origin/master' into azr_implicit_requried_plugin
This commit is contained in:
@@ -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)
|
||||
|
||||
|
||||
@@ -87,7 +87,6 @@
|
||||
|
||||
/post-processor/alicloud-import/ [email protected]
|
||||
/post-processor/checksum/ [email protected]
|
||||
/post-processor/exoscale-import/ @falzm @mcorbin
|
||||
/post-processor/googlecompute-export/ [email protected]
|
||||
/post-processor/yandex-export/ @GennadySpb
|
||||
/post-processor/yandex-import/ @GennadySpb
|
||||
|
||||
@@ -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},
|
||||
|
||||
@@ -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",
|
||||
|
||||
@@ -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",
|
||||
}
|
||||
|
||||
@@ -149,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"`
|
||||
@@ -400,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),
|
||||
@@ -449,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"`
|
||||
@@ -135,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"`
|
||||
@@ -161,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},
|
||||
@@ -277,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
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -72,7 +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"
|
||||
exoscaleimportpostprocessor "github.com/hashicorp/packer/post-processor/exoscale-import"
|
||||
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"
|
||||
@@ -190,7 +189,6 @@ var PostProcessors = map[string]packersdk.PostProcessor{
|
||||
"checksum": new(checksumpostprocessor.PostProcessor),
|
||||
"compress": new(compresspostprocessor.PostProcessor),
|
||||
"digitalocean-import": new(digitaloceanimportpostprocessor.PostProcessor),
|
||||
"exoscale-import": new(exoscaleimportpostprocessor.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
|
||||
}
|
||||
|
||||
@@ -6,6 +6,7 @@ import (
|
||||
// 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"
|
||||
@@ -26,14 +27,15 @@ 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),
|
||||
"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 us load up any plugins that were vendored manually into the
|
||||
// default set of plugins.
|
||||
// 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 {
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
source "parallels-iso" "base-ubuntu-amd64" {
|
||||
boot_wait = "10s"
|
||||
guest_os_type = "ubuntu"
|
||||
http_directory = local.http_directory
|
||||
http_content = local.http_directory_content
|
||||
parallels_tools_flavor = "lin"
|
||||
prlctl_version_file = ".prlctl_version"
|
||||
shutdown_command = "echo 'vagrant' | sudo -S shutdown -P now"
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
source "virtualbox-iso" "base-ubuntu-amd64" {
|
||||
headless = var.headless
|
||||
guest_os_type = "Ubuntu_64"
|
||||
http_directory = local.http_directory
|
||||
http_content = local.http_directory_content
|
||||
shutdown_command = "echo 'vagrant' | sudo -S shutdown -P now"
|
||||
ssh_username = "vagrant"
|
||||
ssh_password = "vagrant"
|
||||
|
||||
@@ -1,9 +1,9 @@
|
||||
variable "ubuntu_1804_version" {
|
||||
default = "18.04.4"
|
||||
default = "18.04.5"
|
||||
}
|
||||
|
||||
locals {
|
||||
iso_url_ubuntu_1804 = "http://cdimage.ubuntu.com/ubuntu/releases/18.04/release/ubuntu-18.04.4-server-amd64.iso"
|
||||
iso_url_ubuntu_1804 = "http://cdimage.ubuntu.com/ubuntu/releases/18.04/release/ubuntu-${var.ubuntu_1804_version}-server-amd64.iso"
|
||||
iso_checksum_url_ubuntu_1804 = "http://cdimage.ubuntu.com/ubuntu/releases/18.04/release/SHA256SUMS"
|
||||
ubuntu_1804_boot_command = [
|
||||
"<esc><wait>",
|
||||
|
||||
@@ -20,4 +20,10 @@ locals {
|
||||
// value. This validates that the http directory exists even before starting
|
||||
// any builder/provisioner.
|
||||
http_directory = dirname(convert(fileset(".", "etc/http/*"), list(string))[0])
|
||||
http_directory_content = {
|
||||
"/alpine-answers" = file("${local.http_directory}/alpine-answers"),
|
||||
"/alpine-setup.sh" = file("${local.http_directory}/alpine-setup.sh"),
|
||||
"/preseed_hardcoded_ip.cfg" = file("${local.http_directory}/preseed_hardcoded_ip.cfg"),
|
||||
"/preseed.cfg" = file("${local.http_directory}/preseed.cfg"),
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,39 +0,0 @@
|
||||
source "amazon-ebs" "example" {
|
||||
communicator = "none"
|
||||
source_ami = "potato"
|
||||
ami_name = "potato"
|
||||
instance_type = "potato"
|
||||
}
|
||||
|
||||
build {
|
||||
name = "my-provisioners-are-cooler"
|
||||
sources = ["source.amazon-ebs.example"]
|
||||
|
||||
provisioner "comment-that-works" {
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
packer {
|
||||
required_plugins {
|
||||
comment = {
|
||||
source = "sylviamoss/comment"
|
||||
version = "v0.2.15"
|
||||
}
|
||||
comment-that-works = {
|
||||
source = "sylviamoss/comment"
|
||||
version = "v0.2.19"
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
build {
|
||||
sources = ["source.amazon-ebs.example"]
|
||||
|
||||
provisioner "comment-my-provisioner" {
|
||||
|
||||
}
|
||||
provisioner "shell-local" {
|
||||
inline = ["yo"]
|
||||
}
|
||||
}
|
||||
@@ -12,7 +12,7 @@ require (
|
||||
github.com/Azure/go-autorest/autorest/to v0.3.0
|
||||
github.com/ChrisTrenkamp/goxpath v0.0.0-20170922090931-c385f95c6022
|
||||
github.com/NaverCloudPlatform/ncloud-sdk-go-v2 v1.1.0
|
||||
github.com/Telmate/proxmox-api-go v0.0.0-20200715182505-ec97c70ba887
|
||||
github.com/Telmate/proxmox-api-go v0.0.0-20210320143302-fea68269e6b0
|
||||
github.com/aliyun/alibaba-cloud-sdk-go v0.0.0-20190418113227-25233c783f4e
|
||||
github.com/aliyun/aliyun-oss-go-sdk v0.0.0-20170113022742-e6dbea820a9f
|
||||
github.com/antihax/optional v1.0.0
|
||||
@@ -25,7 +25,7 @@ require (
|
||||
github.com/dgrijalva/jwt-go v3.2.0+incompatible
|
||||
github.com/digitalocean/go-qemu v0.0.0-20201211181942-d361e7b4965f
|
||||
github.com/digitalocean/godo v1.11.1
|
||||
github.com/exoscale/egoscale v0.18.1
|
||||
github.com/exoscale/packer-plugin-exoscale v0.1.0
|
||||
github.com/fatih/camelcase v1.0.0
|
||||
github.com/fatih/structtag v1.0.0
|
||||
github.com/go-ini/ini v1.25.4
|
||||
@@ -51,7 +51,7 @@ require (
|
||||
github.com/hashicorp/go-version v1.2.0
|
||||
github.com/hashicorp/hcl/v2 v2.8.0
|
||||
github.com/hashicorp/packer-plugin-docker v0.0.2
|
||||
github.com/hashicorp/packer-plugin-sdk v0.0.14
|
||||
github.com/hashicorp/packer-plugin-sdk v0.1.1-0.20210323111710-5a7ab7f7a1c0
|
||||
github.com/hashicorp/vault/api v1.0.4
|
||||
github.com/hetznercloud/hcloud-go v1.15.1
|
||||
github.com/hyperonecom/h1-client-go v0.0.0-20191203060043-b46280e4c4a4
|
||||
@@ -77,7 +77,7 @@ require (
|
||||
github.com/profitbricks/profitbricks-sdk-go v4.0.2+incompatible
|
||||
github.com/scaleway/scaleway-sdk-go v1.0.0-beta.7
|
||||
github.com/shirou/gopsutil v3.21.1+incompatible
|
||||
github.com/stretchr/testify v1.6.1
|
||||
github.com/stretchr/testify v1.7.0
|
||||
github.com/tencentcloud/tencentcloud-sdk-go v3.0.222+incompatible
|
||||
github.com/ucloud/ucloud-sdk-go v0.16.3
|
||||
github.com/ufilesdk-dev/ufile-gosdk v0.0.0-20190830075812-b4dbc4ef43a6
|
||||
@@ -88,13 +88,13 @@ require (
|
||||
github.com/yandex-cloud/go-sdk v0.0.0-20200921111412-ef15ded2014c
|
||||
github.com/zclconf/go-cty v1.7.0
|
||||
github.com/zclconf/go-cty-yaml v1.0.1
|
||||
golang.org/x/crypto v0.0.0-20201208171446-5f87f3452ae9
|
||||
golang.org/x/crypto v0.0.0-20201221181555-eec23a3978ad
|
||||
golang.org/x/mobile v0.0.0-20201208152944-da85bec010a2
|
||||
golang.org/x/mod v0.3.0
|
||||
golang.org/x/net v0.0.0-20201209123823-ac852fbbde11
|
||||
golang.org/x/net v0.0.0-20210119194325-5f4716e94777
|
||||
golang.org/x/oauth2 v0.0.0-20200902213428-5d25da1a8d43
|
||||
golang.org/x/sync v0.0.0-20201207232520-09787c993a3a
|
||||
golang.org/x/sys v0.0.0-20201119102817-f84b799fce68
|
||||
golang.org/x/sys v0.0.0-20210124154548-22da62e12c0c
|
||||
golang.org/x/tools v0.0.0-20201111133315-69daaf961d65
|
||||
google.golang.org/api v0.32.0
|
||||
google.golang.org/grpc v1.32.0
|
||||
|
||||
@@ -81,8 +81,9 @@ github.com/NaverCloudPlatform/ncloud-sdk-go-v2 v1.1.0 h1:0nxjOH7NurPGUWNG5BCrASW
|
||||
github.com/NaverCloudPlatform/ncloud-sdk-go-v2 v1.1.0/go.mod h1:P+3VS0ETiQPyWOx3vB/oeC8J3qd7jnVZLYAFwWgGRt8=
|
||||
github.com/StackExchange/wmi v0.0.0-20190523213315-cbe66965904d h1:G0m3OIz70MZUWq3EgK3CesDbo8upS2Vm9/P3FtgI+Jk=
|
||||
github.com/StackExchange/wmi v0.0.0-20190523213315-cbe66965904d/go.mod h1:3eOhrUMpNV+6aFIbp5/iudMxNCF27Vw2OZgy4xEx0Fg=
|
||||
github.com/Telmate/proxmox-api-go v0.0.0-20200715182505-ec97c70ba887 h1:Q65o4V0g/KR1sSUZIMf4m1rShb7f1tVHuEt30hfnh2A=
|
||||
github.com/Telmate/proxmox-api-go v0.0.0-20200715182505-ec97c70ba887/go.mod h1:OGWyIMJ87/k/GCz8CGiWB2HOXsOVDM6Lpe/nFPkC4IQ=
|
||||
github.com/Telmate/proxmox-api-go v0.0.0-20210320143302-fea68269e6b0 h1:LeBf+Ex12uqA6dWZp73Qf3dzpV/LvB9SRmHgPBwnXrQ=
|
||||
github.com/Telmate/proxmox-api-go v0.0.0-20210320143302-fea68269e6b0/go.mod h1:ayPkdmEKnlssqLQ9K1BE1jlsaYhXVwkoduXI30oQF0I=
|
||||
github.com/abdullin/seq v0.0.0-20160510034733-d5467c17e7af h1:DBNMBMuMiWYu0b+8KMJuWmfCkcxl09JwdlqwDZZ6U14=
|
||||
github.com/abdullin/seq v0.0.0-20160510034733-d5467c17e7af/go.mod h1:5Jv4cbFiHJMsVxt52+i0Ha45fjshj6wxYr1r19tB9bw=
|
||||
github.com/agext/levenshtein v1.2.1 h1:QmvMAjj2aEICytGiWzmxoE0x2KZvE0fvmqMOfy2tjT8=
|
||||
@@ -148,10 +149,14 @@ github.com/circonus-labs/circonus-gometrics v2.3.1+incompatible/go.mod h1:nmEj6D
|
||||
github.com/circonus-labs/circonusllhist v0.1.3/go.mod h1:kMXHVDlOchFAehlya5ePtbp5jckzBHf4XRpQvBOLI+I=
|
||||
github.com/client9/misspell v0.3.4/go.mod h1:qj6jICC3Q7zFZvVWo7KLAzC3yx5G7kyvSDkc90ppPyw=
|
||||
github.com/cncf/udpa/go v0.0.0-20191209042840-269d4d468f6f/go.mod h1:M8M6+tZqaGXZJjfX53e64911xZQV5JYwmTeXPW+k8Sc=
|
||||
github.com/cyberdelia/templates v0.0.0-20141128023046-ca7fffd4298c/go.mod h1:GyV+0YP4qX0UQ7r2MoYZ+AvYDp12OF5yg4q8rGnyNh4=
|
||||
github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
|
||||
github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c=
|
||||
github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
|
||||
github.com/davecgh/go-xdr v0.0.0-20161123171359-e6a2ba005892/go.mod h1:CTDl0pzVzE5DEzZhPfvhY/9sPFMQIxaJ9VAMs9AagrE=
|
||||
github.com/deepmap/oapi-codegen v1.3.11/go.mod h1:suMvK7+rKlx3+tpa8ByptmvoXbAV70wERKTOGH3hLp0=
|
||||
github.com/deepmap/oapi-codegen v1.5.1 h1:Xx8/OynzWhQeKFWr182hg6Ja2evS1gcqdja7qMn05yU=
|
||||
github.com/deepmap/oapi-codegen v1.5.1/go.mod h1:Eb1vtV3f58zvm37CJV4UAQ1bECb0fgAVvTdonC1ftJg=
|
||||
github.com/dgrijalva/jwt-go v3.2.0+incompatible h1:7qlOGliEKZXTDg6OTjfoBKDXWrumCAMpl/TFQ4/5kLM=
|
||||
github.com/dgrijalva/jwt-go v3.2.0+incompatible/go.mod h1:E3ru+11k8xSBh+hMPgOLZmtrrCbhqsmaPHjLKYnJCaQ=
|
||||
github.com/digitalocean/go-libvirt v0.0.0-20190626172931-4d226dd6c437/go.mod h1:PRcPVAAma6zcLpFd4GZrjR/MRpood3TamjKI2m/z/Uw=
|
||||
@@ -176,8 +181,11 @@ github.com/envoyproxy/go-control-plane v0.9.0/go.mod h1:YTl/9mNaCwkRvm6d1a2C3ymF
|
||||
github.com/envoyproxy/go-control-plane v0.9.1-0.20191026205805-5f8ba28d4473/go.mod h1:YTl/9mNaCwkRvm6d1a2C3ymFceY/DCBVvsKhRF0iEA4=
|
||||
github.com/envoyproxy/go-control-plane v0.9.4/go.mod h1:6rpuAdCZL397s3pYoYcLgu1mIlRU8Am5FuJP05cCM98=
|
||||
github.com/envoyproxy/protoc-gen-validate v0.1.0/go.mod h1:iSmxcyjqTsJpI2R4NaDN7+kN2VEUnK/pcBlmesArF7c=
|
||||
github.com/exoscale/egoscale v0.18.1 h1:1FNZVk8jHUx0AvWhOZxLEDNlacTU0chMXUUNkm9EZaI=
|
||||
github.com/exoscale/egoscale v0.18.1/go.mod h1:Z7OOdzzTOz1Q1PjQXumlz9Wn/CddH0zSYdCF3rnBKXE=
|
||||
github.com/exoscale/egoscale v0.43.1 h1:Lhr0UOfg3t3Y56yh1DsYCjQuUHqFvsC8iUVqvub8+0Q=
|
||||
github.com/exoscale/egoscale v0.43.1/go.mod h1:mpEXBpROAa/2i5GC0r33rfxG+TxSEka11g1PIXt9+zc=
|
||||
github.com/exoscale/packer-plugin-exoscale v0.1.0 h1:p4ymqF1tNiTuxgSdnEjGqXehMdDQbV7BPaLsMxGav24=
|
||||
github.com/exoscale/packer-plugin-exoscale v0.1.0/go.mod h1:ZmJRkxsAlmEsVYOMxYPupDkax54uZ+ph0h3W59aIMZ8=
|
||||
github.com/fatih/camelcase v1.0.0 h1:hxNvNX/xYBp0ovncs8WyWZrOrpBNub/JfaMvbURyft8=
|
||||
github.com/fatih/camelcase v1.0.0/go.mod h1:yN2Sb0lFhZJUdVvtELVWefmrXpuZESvPmqwoZc+/fpc=
|
||||
github.com/fatih/color v1.7.0/go.mod h1:Zm6kSWBoL9eyXnKyktHP6abPY2pDugNf5KwzbycvMj4=
|
||||
@@ -186,8 +194,12 @@ github.com/fatih/color v1.9.0/go.mod h1:eQcE1qtQxscV5RaZvpXrrb8Drkc3/DdQ+uUYCNjL
|
||||
github.com/fatih/structs v1.1.0/go.mod h1:9NiDSp5zOcgEDl+j00MP/WkGVPOlPRLejGD8Ga6PJ7M=
|
||||
github.com/fatih/structtag v1.0.0 h1:pTHj65+u3RKWYPSGaU290FpI/dXxTaHdVwVwbcPKmEc=
|
||||
github.com/fatih/structtag v1.0.0/go.mod h1:IKitwq45uXL/yqi5mYghiD3w9H6eTOvI9vnk8tXMphA=
|
||||
github.com/getkin/kin-openapi v0.13.0/go.mod h1:WGRs2ZMM1Q8LR1QBEwUxC6RJEfaBcD0s+pcEVXFuAjw=
|
||||
github.com/getkin/kin-openapi v0.37.0/go.mod h1:ZJSfy1PxJv2QQvH9EdBj3nupRTVvV42mkW6zKUlRBwk=
|
||||
github.com/ghodss/yaml v1.0.0 h1:wQHKEahhL6wmXdzwWG11gIVCkOv05bNOh+Rxn0yngAk=
|
||||
github.com/ghodss/yaml v1.0.0/go.mod h1:4dBDuWmgqj2HViK6kFavaiC9ZROes6MMH2rRYeMEF04=
|
||||
github.com/go-chi/chi v1.5.1/go.mod h1:REp24E+25iKvxgeTfHmdUoL5x15kBiDBlnIl5bCwe2k=
|
||||
github.com/go-chi/chi v4.0.2+incompatible/go.mod h1:eB3wogJHnLi3x/kFX2A+IbTBlXxmMeXJVKy9tTv1XzQ=
|
||||
github.com/go-gl/glfw v0.0.0-20190409004039-e6da0acd62b1/go.mod h1:vR7hzQXu2zJy9AVAgeJqvqgH9Q5CA+iKCZ2gyEVpxRU=
|
||||
github.com/go-gl/glfw/v3.3/glfw v0.0.0-20191125211704-12ad95a8df72/go.mod h1:tQ2UAYgL5IevRw8kRxooKSPJfGvJ9fJQFa0TUsXzTg8=
|
||||
github.com/go-gl/glfw/v3.3/glfw v0.0.0-20200222043503-6f7a984d4dc4/go.mod h1:tQ2UAYgL5IevRw8kRxooKSPJfGvJ9fJQFa0TUsXzTg8=
|
||||
@@ -197,6 +209,8 @@ github.com/go-ldap/ldap v3.0.2+incompatible/go.mod h1:qfd9rJvER9Q0/D/Sqn1DfHRoBp
|
||||
github.com/go-ole/go-ole v1.2.4/go.mod h1:XCwSNxSkXRo4vlyPy93sltvi/qJq0jqQhjqQNIwKuxM=
|
||||
github.com/go-ole/go-ole v1.2.5 h1:t4MGB5xEDZvXI+0rMjjsfBsD7yAgp/s9ZDkL1JndXwY=
|
||||
github.com/go-ole/go-ole v1.2.5/go.mod h1:pprOEPIfldk/42T2oK7lQ4v4JSDwmV0As9GaiUsvbm0=
|
||||
github.com/go-openapi/jsonpointer v0.19.5/go.mod h1:Pl9vOtqEWErmShwVjC8pYs9cog34VGT37dQOVbmoatg=
|
||||
github.com/go-openapi/swag v0.19.5/go.mod h1:POnQmlKehdgb5mhVOsnJFsivZCEZ/vjK9gh66Z9tfKk=
|
||||
github.com/go-resty/resty/v2 v2.1.1-0.20191201195748-d7b97669fe48/go.mod h1:dZGr0i9PLlaaTD4H/hoZIDjQ+r6xq8mgbRzHZf7f2J8=
|
||||
github.com/go-resty/resty/v2 v2.3.0 h1:JOOeAvjSlapTT92p8xiS19Zxev1neGikoHsXJeOq8So=
|
||||
github.com/go-resty/resty/v2 v2.3.0/go.mod h1:UpN9CgLZNsv4e9XG50UU8xdI0F43UQ4HmxLBDwaroHU=
|
||||
@@ -208,8 +222,9 @@ github.com/gobwas/glob v0.2.3 h1:A4xDbljILXROh+kObIiy5kIaPYD8e96x1tgBhUI5J+Y=
|
||||
github.com/gobwas/glob v0.2.3/go.mod h1:d3Ez4x06l9bZtSvzIay5+Yzi0fmZzPgnTbPcKjJAkT8=
|
||||
github.com/gofrs/flock v0.7.3 h1:I0EKY9l8HZCXTMYC4F80vwT6KNypV9uYKP3Alm/hjmQ=
|
||||
github.com/gofrs/flock v0.7.3/go.mod h1:F1TvTiK9OcQqauNUHlbJvyl9Qa1QvF/gOUDKA14jxHU=
|
||||
github.com/gofrs/uuid v3.2.0+incompatible h1:y12jRkkFxsd7GpqdSZ+/KCs/fJbqpEXSGd4+jfEaewE=
|
||||
github.com/gofrs/uuid v3.2.0+incompatible/go.mod h1:b2aQJv3Z4Fp6yNu3cdSllBxTCLRxnplIgP/c0N/04lM=
|
||||
github.com/gofrs/uuid v4.0.0+incompatible h1:1SD/1F5pU8p29ybwgQSwpQk+mwdRrXCYuPhW6m+TnJw=
|
||||
github.com/gofrs/uuid v4.0.0+incompatible/go.mod h1:b2aQJv3Z4Fp6yNu3cdSllBxTCLRxnplIgP/c0N/04lM=
|
||||
github.com/gogo/protobuf v1.2.1/go.mod h1:hp+jE20tsWTFYpLwKvXlhS1hjn+gTNwPg2I6zVXpSg4=
|
||||
github.com/golang-collections/collections v0.0.0-20130729185459-604e922904d3 h1:zN2lZNZRflqFyxVaTIU61KNKQ9C0055u9CAfpmqUvo4=
|
||||
github.com/golang-collections/collections v0.0.0-20130729185459-604e922904d3/go.mod h1:nPpo7qLxd6XL3hWJG/O60sR8ZKfMCiIoNap5GvD12KU=
|
||||
@@ -242,6 +257,7 @@ github.com/golang/protobuf v1.4.2 h1:+Z5KGCizgyZCbGh1KZqA0fcLLkwbsjIzS4aV2v7wJX0
|
||||
github.com/golang/protobuf v1.4.2/go.mod h1:oDoupMAO8OvCJWAcko0GGGIgR6R6ocIYbsSw735rRwI=
|
||||
github.com/golang/snappy v0.0.1 h1:Qgr9rKW7uDUkrbSmQeiDsGa8SjGyCOGtuasMWwvp2P4=
|
||||
github.com/golang/snappy v0.0.1/go.mod h1:/XxbfmMg8lxefKM7IXC3fBNl/7bRcc72aCRzEWrmP2Q=
|
||||
github.com/golangci/lint-1 v0.0.0-20181222135242-d2cdd8c08219/go.mod h1:/X8TswGSh1pIozq4ZwCfxS0WA5JGXguxk94ar/4c87Y=
|
||||
github.com/google/btree v0.0.0-20180813153112-4030bb1f1f0c/go.mod h1:lNA+9X1NB3Zf8V7Ke586lFgjr2dZNuvo3lPJSGZ5JPQ=
|
||||
github.com/google/btree v1.0.0 h1:0udJVsspx3VBr5FwtLhQQtuAsVc79tTq0ocGIPAU6qo=
|
||||
github.com/google/btree v1.0.0/go.mod h1:lNA+9X1NB3Zf8V7Ke586lFgjr2dZNuvo3lPJSGZ5JPQ=
|
||||
@@ -378,18 +394,19 @@ github.com/hashicorp/packer v1.6.7-0.20210125170305-539638b0f951/go.mod h1:Z3eun
|
||||
github.com/hashicorp/packer v1.6.7-0.20210126105722-aef4ced967ec/go.mod h1:2+Vo/c/fA+TD9yFc/h9jQMFm4yG+IymQIr0OdJJOPiE=
|
||||
github.com/hashicorp/packer v1.6.7-0.20210208125835-f616955ebcb6/go.mod h1:7f5ZpTTRG53rQ58BcTADuTnpiBcB3wapuxl4sF2sGMM=
|
||||
github.com/hashicorp/packer v1.6.7-0.20210217093213-201869d627bf/go.mod h1:+EWPPcqee4h8S/y913Dnta1eJkgiqsGXBQgB75A2qV0=
|
||||
github.com/hashicorp/packer v1.7.0/go.mod h1:3KRJcwOctl2JaAGpQMI1bWQRArfWNWqcYjO6AOsVVGQ=
|
||||
github.com/hashicorp/packer-plugin-docker v0.0.2 h1:j/hQTogaN2pZfZohlZTRu5YvNZg2/qtYYHkxPBxv2Oo=
|
||||
github.com/hashicorp/packer-plugin-docker v0.0.2/go.mod h1:A2p9qztS4n88KsNF+qBM7BWw2HndW636GpFIjNSvbKM=
|
||||
github.com/hashicorp/packer-plugin-sdk v0.0.6/go.mod h1:Nvh28f+Jmpp2rcaN79bULTouNkGNDRfHckhHKTAXtyU=
|
||||
github.com/hashicorp/packer-plugin-sdk v0.0.6/go.mod h1:Nvh28f+Jmpp2rcaN79bULTouNkGNDRfHckhHKTAXtyU=
|
||||
github.com/hashicorp/packer-plugin-sdk v0.0.7-0.20210111224258-fd30ebb797f0/go.mod h1:YdWTt5w6cYfaQG7IOi5iorL+3SXnz8hI0gJCi8Db/LI=
|
||||
github.com/hashicorp/packer-plugin-sdk v0.0.7-0.20210120105339-f6fd68d2570a/go.mod h1:exN0C+Pe+3zu18l4nxueNjX5cfmslxUX/m/xk4IVmZQ=
|
||||
github.com/hashicorp/packer-plugin-sdk v0.0.7-0.20210122130548-45a6ca0a9365/go.mod h1:K7VsU0lfJBDyiUrSNnS/j+zMxSRwwH9WC9QvHv32KsU=
|
||||
github.com/hashicorp/packer-plugin-sdk v0.0.10-0.20210126105622-8e1648006d93/go.mod h1:AtWQLNfpn7cgH2SmZ1PTedwqNOhiPvzcuKfH5sDvIQ0=
|
||||
github.com/hashicorp/packer-plugin-sdk v0.0.11/go.mod h1:GNb0WNs7zibb8vzUZce1As64z2AW0FEMwhe2J7/NW5I=
|
||||
github.com/hashicorp/packer-plugin-sdk v0.0.12/go.mod h1:hs82OYeufirGG6KRENMpjBWomnIlte99X6wXAPThJ5I=
|
||||
github.com/hashicorp/packer-plugin-sdk v0.0.14 h1:42WOZLmIbAYYC1WXxtlrQZN+fFdysVvTmj3jtoI6gOU=
|
||||
github.com/hashicorp/packer-plugin-sdk v0.0.14/go.mod h1:tNb3XzJPnjMl3QuUdKmF47B5ImerdTakalHzUAvW0aw=
|
||||
github.com/hashicorp/packer-plugin-sdk v0.1.1-0.20210323111710-5a7ab7f7a1c0 h1:nw4RqF7C4jUWGo5PGDG4dSclU+G/vXyVBHIu5j7akd4=
|
||||
github.com/hashicorp/packer-plugin-sdk v0.1.1-0.20210323111710-5a7ab7f7a1c0/go.mod h1:1d3nqB9LUsXMQaNUiL67Q+WYEtjsVcLNTX8ikVlpBrc=
|
||||
github.com/hashicorp/serf v0.8.2/go.mod h1:6hOLApaqBFA1NXqRQAsxw9QxuDEvNxSQRwA/JwenrHc=
|
||||
github.com/hashicorp/serf v0.9.2 h1:yJoyfZXo4Pk2p/M/viW+YLibBFiIbKoP79gu7kDAFP0=
|
||||
github.com/hashicorp/serf v0.9.2/go.mod h1:UWDWwZeL5cuWDJdl0C6wrvrUwEqtQ4ZKBKKENpqIUyk=
|
||||
@@ -405,6 +422,9 @@ github.com/hetznercloud/hcloud-go v1.15.1/go.mod h1:8lR3yHBHZWy2uGcUi9Ibt4UOoop2
|
||||
github.com/hyperonecom/h1-client-go v0.0.0-20191203060043-b46280e4c4a4 h1:mSmyzhwBeQt2TlHbsXYLona9pwjWAvYGwQJ2Cq/k3VE=
|
||||
github.com/hyperonecom/h1-client-go v0.0.0-20191203060043-b46280e4c4a4/go.mod h1:yNUVHSleURKSaYUKq4Wx0i/vjCen2aq7CvPyHd/Vj2Q=
|
||||
github.com/ianlancetaylor/demangle v0.0.0-20181102032728-5e5cf60278f6/go.mod h1:aSSvb/t6k1mPoxDqO4vJh6VOCGPwU4O0C2/Eqndh1Sc=
|
||||
github.com/jarcoal/httpmock v1.0.6/go.mod h1:ATjnClrvW/3tijVmpL/va5Z3aAyGvqU3gCT8nX0Txik=
|
||||
github.com/jarcoal/httpmock v1.0.8 h1:8kI16SoO6LQKgPE7PvQuV+YuD/inwHd7fOOe2zMbo4k=
|
||||
github.com/jarcoal/httpmock v1.0.8/go.mod h1:ATjnClrvW/3tijVmpL/va5Z3aAyGvqU3gCT8nX0Txik=
|
||||
github.com/jdcloud-api/jdcloud-sdk-go v1.9.1-0.20190605102154-3d81a50ca961 h1:a2/K4HRhg31A5vafiz5yYiGMjaCxwRpyjJStfVquKds=
|
||||
github.com/jdcloud-api/jdcloud-sdk-go v1.9.1-0.20190605102154-3d81a50ca961/go.mod h1:UrKjuULIWLjHFlG6aSPunArE5QX57LftMmStAZJBEX8=
|
||||
github.com/jehiah/go-strftime v0.0.0-20171201141054-1d33003b3869 h1:IPJ3dvxmJ4uczJe5YQdrYB16oTJlGSC/OyZDqUk9xX4=
|
||||
@@ -452,21 +472,31 @@ github.com/kr/text v0.1.0 h1:45sCR5RtlFHMR4UwH9sdQ5TC8v0qDQCHnXt+kaKSTVE=
|
||||
github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI=
|
||||
github.com/kylelemons/godebug v0.0.0-20170820004349-d65d576e9348 h1:MtvEpTB6LX3vkb4ax0b5D2DHbNAUsen0Gx5wZoq3lV4=
|
||||
github.com/kylelemons/godebug v0.0.0-20170820004349-d65d576e9348/go.mod h1:B69LEHPfb2qLo0BaaOLcbitczOKLWTsrBG9LczfCD4k=
|
||||
github.com/labstack/echo/v4 v4.1.11/go.mod h1:i541M3Fj6f76NZtHSj7TXnyM8n2gaodfvfxNnFqi74g=
|
||||
github.com/labstack/echo/v4 v4.1.17/go.mod h1:Tn2yRQL/UclUalpb5rPdXDevbkJ+lp/2svdyFBg6CHQ=
|
||||
github.com/labstack/gommon v0.3.0/go.mod h1:MULnywXg0yavhxWKc+lOruYdAhDwPK9wf0OL7NoOu+k=
|
||||
github.com/linode/linodego v0.14.0 h1:0APKMjiVGyry2TTUVDiok72H6cWpFNMMrFWBFn14aFU=
|
||||
github.com/linode/linodego v0.14.0/go.mod h1:2ce3S00NrDqJfp4i55ZuSlT0U3cKNELNYACWBPI8Tnw=
|
||||
github.com/mailru/easyjson v0.0.0-20190614124828-94de47d64c63/go.mod h1:C1wdFJiN94OJF2b5HbByQZoLdCWB1Yqtg26g4irojpc=
|
||||
github.com/mailru/easyjson v0.0.0-20190626092158-b2ccc519800e/go.mod h1:C1wdFJiN94OJF2b5HbByQZoLdCWB1Yqtg26g4irojpc=
|
||||
github.com/masterzen/simplexml v0.0.0-20160608183007-4572e39b1ab9/go.mod h1:kCEbxUJlNDEBNbdQMkPSp6yaKcRXVI6f4ddk8Riv4bc=
|
||||
github.com/masterzen/simplexml v0.0.0-20190410153822-31eea3082786 h1:2ZKn+w/BJeL43sCxI2jhPLRv73oVVOjEKZjKkflyqxg=
|
||||
github.com/masterzen/simplexml v0.0.0-20190410153822-31eea3082786/go.mod h1:kCEbxUJlNDEBNbdQMkPSp6yaKcRXVI6f4ddk8Riv4bc=
|
||||
github.com/masterzen/winrm v0.0.0-20200615185753-c42b5136ff88/go.mod h1:a2HXwefeat3evJHxFXSayvRHpYEPJYtErl4uIzfaUqY=
|
||||
github.com/masterzen/winrm v0.0.0-20201030141608-56ca5c5f2380 h1:uKhPH5dYpx3Z8ZAnaTGfGZUiHOWa5p5mdG8wZlh+tLo=
|
||||
github.com/masterzen/winrm v0.0.0-20201030141608-56ca5c5f2380/go.mod h1:a2HXwefeat3evJHxFXSayvRHpYEPJYtErl4uIzfaUqY=
|
||||
github.com/matryer/moq v0.0.0-20190312154309-6cfb0558e1bd/go.mod h1:9ELz6aaclSIGnZBoaSLZ3NAl1VTufbOrXBPvtcy6WiQ=
|
||||
github.com/mattn/go-colorable v0.0.9/go.mod h1:9vuHe8Xs5qXnSaW/c/ABM9alt+Vo+STaOChaDxuIBZU=
|
||||
github.com/mattn/go-colorable v0.1.2/go.mod h1:U0ppj6V5qS13XJ6of8GYAs25YV2eR4EVcfRqFIhoBtE=
|
||||
github.com/mattn/go-colorable v0.1.4/go.mod h1:U0ppj6V5qS13XJ6of8GYAs25YV2eR4EVcfRqFIhoBtE=
|
||||
github.com/mattn/go-colorable v0.1.6 h1:6Su7aK7lXmJ/U79bYtBjLNaha4Fs1Rg9plHpcH+vvnE=
|
||||
github.com/mattn/go-colorable v0.1.6/go.mod h1:u6P/XSegPjTcexA+o6vUJrdnUu04hMope9wVRipJSqc=
|
||||
github.com/mattn/go-colorable v0.1.7/go.mod h1:u6P/XSegPjTcexA+o6vUJrdnUu04hMope9wVRipJSqc=
|
||||
github.com/mattn/go-colorable v0.1.8 h1:c1ghPdyEDarC70ftn0y+A/Ee++9zz8ljHG1b13eJ0s8=
|
||||
github.com/mattn/go-colorable v0.1.8/go.mod h1:u6P/XSegPjTcexA+o6vUJrdnUu04hMope9wVRipJSqc=
|
||||
github.com/mattn/go-isatty v0.0.3/go.mod h1:M+lRXTBqGeGNdLjl/ufCoiOlB5xdOkqRJdNxMWT7Zi4=
|
||||
github.com/mattn/go-isatty v0.0.4/go.mod h1:M+lRXTBqGeGNdLjl/ufCoiOlB5xdOkqRJdNxMWT7Zi4=
|
||||
github.com/mattn/go-isatty v0.0.8/go.mod h1:Iq45c/XA43vh69/j3iqttzPXn0bhXyGjM0Hdxcsrc5s=
|
||||
github.com/mattn/go-isatty v0.0.9/go.mod h1:YNRxwqDuOph6SZLI9vUUz6OYw3QyUt7WiY2yME+cCiQ=
|
||||
github.com/mattn/go-isatty v0.0.10/go.mod h1:qgIWMr58cqv1PHHyhnkY9lrL7etaEgOFcMEpPG5Rm84=
|
||||
github.com/mattn/go-isatty v0.0.11/go.mod h1:PhnuNfih5lzO57/f3n+odYbM4JtupLOxQOAqxQCu2WE=
|
||||
github.com/mattn/go-isatty v0.0.12 h1:wuysRhFDzyxgEmMf5xjvJ2M9dZoWAXNNr5LSBS7uHXY=
|
||||
@@ -558,6 +588,7 @@ github.com/prometheus/procfs v0.0.0-20181204211112-1dc9a6cbc91a/go.mod h1:c3At6R
|
||||
github.com/rivo/uniseg v0.1.0 h1:+2KBaVoUmb9XzDsrx/Ct0W/EYOSFf/nWTauy++DprtY=
|
||||
github.com/rivo/uniseg v0.1.0/go.mod h1:J6wj4VEh+S6ZtnVlnTBMWIodfgj8LQOQFoIToxlJtxc=
|
||||
github.com/rogpeppe/go-internal v1.3.0/go.mod h1:M8bDsm7K2OlrFYOpmOWEs/qY81heoFRclV5y23lUDJ4=
|
||||
github.com/rs/xid v1.2.1/go.mod h1:+uKXf+4Djp6Md1KODXJxgGQPKngRmWyn10oCKFzNHOQ=
|
||||
github.com/ryanuber/columnize v0.0.0-20160712163229-9b3edd62028f/go.mod h1:sm1tb6uqfes/u+d4ooFouqFdy9/2g9QGwK3SQygK0Ts=
|
||||
github.com/ryanuber/columnize v2.1.0+incompatible/go.mod h1:sm1tb6uqfes/u+d4ooFouqFdy9/2g9QGwK3SQygK0Ts=
|
||||
github.com/ryanuber/go-glob v1.0.0 h1:iQh3xXAumdQ+4Ufa5b25cRpC5TYKlno6hsv6Cb3pkBk=
|
||||
@@ -586,14 +617,16 @@ github.com/spf13/pflag v1.0.2/go.mod h1:DYY7MBk1bdzusC3SYhjObp+wFpr4gzcvqqNjLnIn
|
||||
github.com/spf13/pflag v1.0.5/go.mod h1:McXfInJRrz4CZXVZOBLb0bTZqETkiAhM9Iw0y3An2Bg=
|
||||
github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
|
||||
github.com/stretchr/objx v0.1.1/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
|
||||
github.com/stretchr/objx v0.2.0 h1:Hbg2NidpLE8veEBkEZTL3CvlkUIVzuU9jDplZO54c48=
|
||||
github.com/stretchr/objx v0.2.0/go.mod h1:qt09Ya8vawLte6SNmTgCsAVtYtaKzEcn8ATUoHMkEqE=
|
||||
github.com/stretchr/objx v0.3.0 h1:NGXK3lHquSN08v5vWalVI/L8XU9hdzE/G6xsrze47As=
|
||||
github.com/stretchr/objx v0.3.0/go.mod h1:qt09Ya8vawLte6SNmTgCsAVtYtaKzEcn8ATUoHMkEqE=
|
||||
github.com/stretchr/testify v1.2.2/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs=
|
||||
github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI=
|
||||
github.com/stretchr/testify v1.4.0/go.mod h1:j7eGeouHqKxXV5pUuKE4zz7dFj8WfuZ+81PSLYec5m4=
|
||||
github.com/stretchr/testify v1.5.1/go.mod h1:5W2xD1RspED5o8YsWQXVCued0rvSQ+mT+I5cxcmMvtA=
|
||||
github.com/stretchr/testify v1.6.1 h1:hDPOHmpOpP40lSULcqw7IrRb/u7w6RpDC9399XyoNd0=
|
||||
github.com/stretchr/testify v1.6.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
|
||||
github.com/stretchr/testify v1.7.0 h1:nwc3DEeHmmLAfoZucVR881uASk0Mfjw8xYJ99tb5CcY=
|
||||
github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
|
||||
github.com/tencentcloud/tencentcloud-sdk-go v3.0.222+incompatible h1:bs+0lcG4RELNbE8PsBC9oaPP0/qExr0DuEGnZyocm84=
|
||||
github.com/tencentcloud/tencentcloud-sdk-go v3.0.222+incompatible/go.mod h1:0PfYow01SHPMhKY31xa+EFz2RStxIqj6JFAJS+IkCi4=
|
||||
github.com/tv42/httpunix v0.0.0-20150427012821-b75d8614f926/go.mod h1:9ESjWnEqriFuLhtthL60Sar/7RFoluCcXsuvEwTV5KM=
|
||||
@@ -608,6 +641,10 @@ github.com/ugorji/go/codec v1.2.4 h1:C5VurWRRCKjuENsbM6GYVw8W++WVW9rSxoACKIvxzz8
|
||||
github.com/ugorji/go/codec v1.2.4/go.mod h1:bWBu1+kIRWcF8uMklKaJrR6fTWQOwAlrIzX22pHwryA=
|
||||
github.com/ulikunitz/xz v0.5.5 h1:pFrO0lVpTBXLpYw+pnLj6TbvHuyjXMfjGeCwSqCVwok=
|
||||
github.com/ulikunitz/xz v0.5.5/go.mod h1:2bypXElzHzzJZwzH67Y6wb67pO62Rzfn7BSiF4ABRW8=
|
||||
github.com/valyala/bytebufferpool v1.0.0/go.mod h1:6bBcMArwyJ5K/AmCkWv1jt77kVWyCJ6HpOuEn7z0Csc=
|
||||
github.com/valyala/fasttemplate v1.0.1/go.mod h1:UQGH1tvbgY+Nz5t2n7tXsz52dQxojPUpymEIMZ47gx8=
|
||||
github.com/valyala/fasttemplate v1.1.0/go.mod h1:UQGH1tvbgY+Nz5t2n7tXsz52dQxojPUpymEIMZ47gx8=
|
||||
github.com/valyala/fasttemplate v1.2.1/go.mod h1:KHLXt3tVN2HBp8eijSv/kGJopbvo7S+qRAEEKiv+SiQ=
|
||||
github.com/vmihailenco/msgpack v3.3.3+incompatible/go.mod h1:fy3FlTQTDXWkZ7Bh6AcGMlsjHatGryHQYUTf1ShIgkk=
|
||||
github.com/vmihailenco/msgpack/v4 v4.3.12/go.mod h1:gborTTJjAo/GWTqqRjrLCn9pgNN+NXzzngzBKDPIqw4=
|
||||
github.com/vmihailenco/tagparser v0.1.1/go.mod h1:OeAg3pn3UbLjkWt+rN9oFYB6u/cQgqMEUPoW2WPyhdI=
|
||||
@@ -647,14 +684,18 @@ golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACk
|
||||
golang.org/x/crypto v0.0.0-20190426145343-a29dc8fdc734/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
|
||||
golang.org/x/crypto v0.0.0-20190510104115-cbcb75029529/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
|
||||
golang.org/x/crypto v0.0.0-20190605123033-f99c8df09eb5/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
|
||||
golang.org/x/crypto v0.0.0-20190701094942-4def268fd1a4/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
|
||||
golang.org/x/crypto v0.0.0-20190923035154-9ee001bba392/go.mod h1:/lpIB1dKB+9EgE3H3cr1v9wB50oz8l4C4h62xy7jSTY=
|
||||
golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
|
||||
golang.org/x/crypto v0.0.0-20191112222119-e1110fd1c708/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=
|
||||
golang.org/x/crypto v0.0.0-20191202143827-86a70503ff7e/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=
|
||||
golang.org/x/crypto v0.0.0-20191206172530-e9b2fee46413/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=
|
||||
golang.org/x/crypto v0.0.0-20200422194213-44a606286825/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=
|
||||
golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=
|
||||
golang.org/x/crypto v0.0.0-20201208171446-5f87f3452ae9 h1:sYNJzB4J8toYPQTM6pAkcmBRgw9SnQKP9oXCHfgy604=
|
||||
golang.org/x/crypto v0.0.0-20200820211705-5c72a883971a/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=
|
||||
golang.org/x/crypto v0.0.0-20201208171446-5f87f3452ae9/go.mod h1:jdWPYTVW3xRLrWPugEBEK3UY2ZEsg3UU495nc5E+M+I=
|
||||
golang.org/x/crypto v0.0.0-20201221181555-eec23a3978ad h1:DN0cp81fZ3njFcrLCytUHRSUkqBjfTo4Tx9RJTWs0EY=
|
||||
golang.org/x/crypto v0.0.0-20201221181555-eec23a3978ad/go.mod h1:jdWPYTVW3xRLrWPugEBEK3UY2ZEsg3UU495nc5E+M+I=
|
||||
golang.org/x/exp v0.0.0-20190121172915-509febef88a4/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA=
|
||||
golang.org/x/exp v0.0.0-20190306152737-a1d7652674e8/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA=
|
||||
golang.org/x/exp v0.0.0-20190510132918-efd6b22b2522/go.mod h1:ZjyILWgesfNpC6sMxTJOJm9Kp84zZh5NQWvqDGG3Qr8=
|
||||
@@ -708,6 +749,7 @@ golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLL
|
||||
golang.org/x/net v0.0.0-20190628185345-da137c7871d7/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
|
||||
golang.org/x/net v0.0.0-20190724013045-ca1201d0de80/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
|
||||
golang.org/x/net v0.0.0-20190923162816-aa69164e4478/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
|
||||
golang.org/x/net v0.0.0-20191112182307-2180aed22343/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
|
||||
golang.org/x/net v0.0.0-20191126235420-ef20fe5d7933/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
|
||||
golang.org/x/net v0.0.0-20191209160850-c0dbc17a3553/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
|
||||
golang.org/x/net v0.0.0-20200114155413-6afb5195e5aa/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
|
||||
@@ -727,8 +769,9 @@ golang.org/x/net v0.0.0-20200822124328-c89045814202/go.mod h1:/O7V0waA8r7cgGh81R
|
||||
golang.org/x/net v0.0.0-20200904194848-62affa334b73/go.mod h1:/O7V0waA8r7cgGh81Ro3o1hOxt32SMVPicZroKQ2sZA=
|
||||
golang.org/x/net v0.0.0-20201021035429-f5854403a974/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU=
|
||||
golang.org/x/net v0.0.0-20201110031124-69a78807bb2b/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU=
|
||||
golang.org/x/net v0.0.0-20201209123823-ac852fbbde11 h1:lwlPPsmjDKK0J6eG6xDWd5XPehI0R024zxjDnw3esPA=
|
||||
golang.org/x/net v0.0.0-20201209123823-ac852fbbde11/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg=
|
||||
golang.org/x/net v0.0.0-20210119194325-5f4716e94777 h1:003p0dJM77cxMSyCPFphvZf/Y5/NXf5fzg6ufd1/Oew=
|
||||
golang.org/x/net v0.0.0-20210119194325-5f4716e94777/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg=
|
||||
golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U=
|
||||
golang.org/x/oauth2 v0.0.0-20190226205417-e64efc72b421/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw=
|
||||
golang.org/x/oauth2 v0.0.0-20190604053449-0f29369cfe45/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw=
|
||||
@@ -764,12 +807,14 @@ golang.org/x/sys v0.0.0-20190507160741-ecd444e8653b/go.mod h1:h1NjWce9XRLGQEsW7w
|
||||
golang.org/x/sys v0.0.0-20190606165138-5da285871e9c/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20190624142023-c5567b49c5d0/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20190726091711-fc99dfbffb4e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20190813064441-fde4db37ae7a/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20190916202348-b4ddaad3f8a3/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20190922100055-0a153f010e69/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20190924154521-2837fb4f24fe/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20191001151750-bb3f8db39f24/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20191008105621-543471e840be/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20191026070338-33540a1f6037/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20191115151921-52ab43148777/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20191128015809-6d18c012aee9/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20191204072324-ce4227a45e2e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20191228213918-04cbcbbfeed8/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
@@ -788,11 +833,13 @@ golang.org/x/sys v0.0.0-20200511232937-7e40ca221e25/go.mod h1:h1NjWce9XRLGQEsW7w
|
||||
golang.org/x/sys v0.0.0-20200515095857-1151b9dac4a9/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20200523222454-059865788121/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20200803210538-64077c9b5642/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20200826173525-f9321e4c35a6/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20200828194041-157a740278f4/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20200905004654-be1d3432aa8f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20200930185726-fdedc70b468f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20201119102817-f84b799fce68 h1:nxC68pudNYkKU6jWhgrqdreuFiOQWj1Fs7T3VrH4Pjw=
|
||||
golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20210124154548-22da62e12c0c h1:VwygUrnw9jn88c4u8GD3rZQbqrP/tgas88tPUbBxQrk=
|
||||
golang.org/x/sys v0.0.0-20210124154548-22da62e12c0c/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/term v0.0.0-20201117132131-f5c789dd3221/go.mod h1:Nr5EML6q2oocZ2LXRh80K7BxOlk5/8JxuGnuhpl+muw=
|
||||
golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1 h1:v+OssWQX+hTHEmOBgwxdZxK4zHq3yOs8F9J7mk0PY8E=
|
||||
golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo=
|
||||
@@ -801,8 +848,9 @@ golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
|
||||
golang.org/x/text v0.3.1-0.20180807135948-17ff2d5776d2/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
|
||||
golang.org/x/text v0.3.1-0.20181227161524-e6919f6577db/go.mod h1:bEr9sfX3Q8Zfm5fL9x+3itogRgK3+ptLWKqgva+5dAk=
|
||||
golang.org/x/text v0.3.2/go.mod h1:bEr9sfX3Q8Zfm5fL9x+3itogRgK3+ptLWKqgva+5dAk=
|
||||
golang.org/x/text v0.3.3 h1:cokOdA+Jmi5PJGXLlLllQSgYigAEfHXJAERHVMaCc2k=
|
||||
golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
|
||||
golang.org/x/text v0.3.5 h1:i6eZZ+zk0SOf0xgBpEpPD18qWcJda6q1sxt3S0kzyUQ=
|
||||
golang.org/x/text v0.3.5/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
|
||||
golang.org/x/time v0.0.0-20181108054448-85acf8d2951c/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=
|
||||
golang.org/x/time v0.0.0-20190308202827-9d24e82272b4/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=
|
||||
golang.org/x/time v0.0.0-20191024005414-555d28b269f0 h1:/5xXl8Y5W96D+TtHSlonuFqGHIWVuyCkGJLwGh9JJFs=
|
||||
@@ -977,8 +1025,9 @@ gopkg.in/yaml.v2 v2.2.7/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
|
||||
gopkg.in/yaml.v2 v2.2.8/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
|
||||
gopkg.in/yaml.v2 v2.3.0 h1:clyUAQHOM3G0M3f5vQj7LuJrETvjVot3Z5el9nffUtU=
|
||||
gopkg.in/yaml.v2 v2.3.0/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
|
||||
gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c h1:dUUwHk2QECo/6vqA44rthZ8ie2QXMNeKRTHCNY2nXvo=
|
||||
gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
|
||||
gopkg.in/yaml.v3 v3.0.0-20210107192922-496545a6307b h1:h8qDotaEPuJATrMmW04NCwg7v22aHH28wwpauUhK9Oo=
|
||||
gopkg.in/yaml.v3 v3.0.0-20210107192922-496545a6307b/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
|
||||
honnef.co/go/tools v0.0.0-20190102054323-c2f93a96b099/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4=
|
||||
honnef.co/go/tools v0.0.0-20190106161140-3f1c8253044a/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4=
|
||||
honnef.co/go/tools v0.0.0-20190418001031-e561f6794a2a/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4=
|
||||
|
||||
+64
-39
@@ -7,6 +7,8 @@ import (
|
||||
"io/ioutil"
|
||||
"os"
|
||||
"os/exec"
|
||||
"path/filepath"
|
||||
"strings"
|
||||
|
||||
"github.com/hashicorp/hcl/v2"
|
||||
"github.com/hashicorp/hcl/v2/hclparse"
|
||||
@@ -14,9 +16,9 @@ import (
|
||||
)
|
||||
|
||||
type HCL2Formatter struct {
|
||||
ShowDiff, Write bool
|
||||
Output io.Writer
|
||||
parser *hclparse.Parser
|
||||
ShowDiff, Write, Recursive bool
|
||||
Output io.Writer
|
||||
parser *hclparse.Parser
|
||||
}
|
||||
|
||||
// NewHCL2Formatter creates a new formatter, ready to format configuration files.
|
||||
@@ -26,55 +28,77 @@ func NewHCL2Formatter() *HCL2Formatter {
|
||||
}
|
||||
}
|
||||
|
||||
func isHcl2FileOrVarFile(path string) bool {
|
||||
if strings.HasSuffix(path, hcl2FileExt) || strings.HasSuffix(path, hcl2VarFileExt) {
|
||||
return true
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
func (f *HCL2Formatter) formatFile(path string, diags hcl.Diagnostics, bytesModified int) (int, hcl.Diagnostics) {
|
||||
data, err := f.processFile(path)
|
||||
if err != nil {
|
||||
diags = append(diags, &hcl.Diagnostic{
|
||||
Severity: hcl.DiagError,
|
||||
Summary: fmt.Sprintf("encountered an error while formatting %s", path),
|
||||
Detail: err.Error(),
|
||||
})
|
||||
}
|
||||
bytesModified += len(data)
|
||||
return bytesModified, diags
|
||||
}
|
||||
|
||||
// Format all HCL2 files in path and return the total bytes formatted.
|
||||
// If any error is encountered, zero bytes will be returned.
|
||||
//
|
||||
// Path can be a directory or a file.
|
||||
func (f *HCL2Formatter) Format(path string) (int, hcl.Diagnostics) {
|
||||
var diags hcl.Diagnostics
|
||||
var bytesModified int
|
||||
|
||||
var allHclFiles []string
|
||||
var diags []*hcl.Diagnostic
|
||||
|
||||
if path == "-" {
|
||||
allHclFiles = []string{"-"}
|
||||
} else {
|
||||
hclFiles, _, diags := GetHCL2Files(path, hcl2FileExt, hcl2JsonFileExt)
|
||||
if diags.HasErrors() {
|
||||
return 0, diags
|
||||
}
|
||||
|
||||
hclVarFiles, _, diags := GetHCL2Files(path, hcl2VarFileExt, hcl2VarJsonFileExt)
|
||||
if diags.HasErrors() {
|
||||
return 0, diags
|
||||
}
|
||||
|
||||
allHclFiles = append(hclFiles, hclVarFiles...)
|
||||
|
||||
if len(allHclFiles) == 0 {
|
||||
diags = append(diags, &hcl.Diagnostic{
|
||||
Severity: hcl.DiagError,
|
||||
Summary: fmt.Sprintf("Cannot tell whether %s contains HCL2 configuration data", path),
|
||||
})
|
||||
|
||||
return 0, diags
|
||||
}
|
||||
if path == "" {
|
||||
diags = append(diags, &hcl.Diagnostic{
|
||||
Severity: hcl.DiagError,
|
||||
Summary: "path is empty, cannot format",
|
||||
Detail: "path is empty, cannot format",
|
||||
})
|
||||
return bytesModified, diags
|
||||
}
|
||||
|
||||
if f.parser == nil {
|
||||
f.parser = hclparse.NewParser()
|
||||
}
|
||||
|
||||
var bytesModified int
|
||||
for _, fn := range allHclFiles {
|
||||
data, err := f.processFile(fn)
|
||||
if err != nil {
|
||||
diags = append(diags, &hcl.Diagnostic{
|
||||
Severity: hcl.DiagError,
|
||||
Summary: fmt.Sprintf("encountered an error while formatting %s", fn),
|
||||
Detail: err.Error(),
|
||||
})
|
||||
if s, err := os.Stat(path); err != nil || !s.IsDir() {
|
||||
return f.formatFile(path, diags, bytesModified)
|
||||
}
|
||||
|
||||
fileInfos, err := ioutil.ReadDir(path)
|
||||
if err != nil {
|
||||
diag := &hcl.Diagnostic{
|
||||
Severity: hcl.DiagError,
|
||||
Summary: "Cannot read hcl directory",
|
||||
Detail: err.Error(),
|
||||
}
|
||||
diags = append(diags, diag)
|
||||
return bytesModified, diags
|
||||
}
|
||||
|
||||
for _, fileInfo := range fileInfos {
|
||||
filename := filepath.Join(path, fileInfo.Name())
|
||||
if fileInfo.IsDir() {
|
||||
if f.Recursive {
|
||||
var tempDiags hcl.Diagnostics
|
||||
var tempBytesModified int
|
||||
tempBytesModified, tempDiags = f.Format(filename)
|
||||
bytesModified += tempBytesModified
|
||||
diags = diags.Extend(tempDiags)
|
||||
}
|
||||
continue
|
||||
}
|
||||
if isHcl2FileOrVarFile(filename) {
|
||||
bytesModified, diags = f.formatFile(filename, diags, bytesModified)
|
||||
}
|
||||
bytesModified += len(data)
|
||||
}
|
||||
|
||||
return bytesModified, diags
|
||||
@@ -84,6 +108,7 @@ func (f *HCL2Formatter) Format(path string) (int, hcl.Diagnostics) {
|
||||
// overwriting the contents of the original when the f.Write is true; a diff of the changes
|
||||
// will be outputted if f.ShowDiff is true.
|
||||
func (f *HCL2Formatter) processFile(filename string) ([]byte, error) {
|
||||
|
||||
if f.Output == nil {
|
||||
f.Output = os.Stdout
|
||||
}
|
||||
|
||||
@@ -32,11 +32,9 @@ func TestHCL2Formatter_Format(t *testing.T) {
|
||||
if diags.HasErrors() {
|
||||
t.Fatalf("the call to Format failed unexpectedly %s", diags.Error())
|
||||
}
|
||||
|
||||
if buf.String() != "" && tc.FormatExpected == false {
|
||||
t.Errorf("Format(%q) should contain the name of the formatted file(s), but got %q", tc.Path, buf.String())
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -1,12 +1,22 @@
|
||||
package function
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"time"
|
||||
|
||||
"github.com/zclconf/go-cty/cty"
|
||||
"github.com/zclconf/go-cty/cty/function"
|
||||
)
|
||||
|
||||
// InitTime is the UTC time when this package was initialized. It is
|
||||
// used as the timestamp for all configuration templates so that they
|
||||
// match for a single build.
|
||||
var InitTime time.Time
|
||||
|
||||
func init() {
|
||||
InitTime = time.Now().UTC()
|
||||
}
|
||||
|
||||
// TimestampFunc constructs a function that returns a string representation of the current date and time.
|
||||
var TimestampFunc = function.New(&function.Spec{
|
||||
Params: []function.Parameter{},
|
||||
@@ -24,3 +34,44 @@ var TimestampFunc = function.New(&function.Spec{
|
||||
func Timestamp() (cty.Value, error) {
|
||||
return TimestampFunc.Call([]cty.Value{})
|
||||
}
|
||||
|
||||
// LegacyIsotimeFunc constructs a function that returns a string representation
|
||||
// of the current date and time using golang's datetime formatting.
|
||||
var LegacyIsotimeFunc = function.New(&function.Spec{
|
||||
Params: []function.Parameter{},
|
||||
VarParam: &function.Parameter{
|
||||
Name: "format",
|
||||
Type: cty.String,
|
||||
},
|
||||
Type: function.StaticReturnType(cty.String),
|
||||
Impl: func(args []cty.Value, retType cty.Type) (cty.Value, error) {
|
||||
if len(args) > 1 {
|
||||
return cty.StringVal(""), fmt.Errorf("too many values, 1 needed: %v", args)
|
||||
} else if len(args) == 0 {
|
||||
return cty.StringVal(InitTime.Format(time.RFC3339)), nil
|
||||
}
|
||||
format := args[0].AsString()
|
||||
return cty.StringVal(InitTime.Format(format)), nil
|
||||
},
|
||||
})
|
||||
|
||||
// LegacyIsotimeFunc returns a string representation of the current date and
|
||||
// time using the given format string. The format string follows golang's
|
||||
// datetime formatting. See
|
||||
// https://www.packer.io/docs/templates/legacy_json_templates/engine#isotime-function-format-reference
|
||||
// for more details.
|
||||
//
|
||||
// This function has been provided to create backwards compatability with
|
||||
// Packer's legacy JSON templates. However, we recommend that you upgrade your
|
||||
// HCL Packer template to use Timestamp and FormatDate together as soon as is
|
||||
// convenient.
|
||||
//
|
||||
// Please note that if you are using a large number of builders, provisioners
|
||||
// or post-processors, the isotime may be slightly different for each one
|
||||
// because it is from when the plugin is launched not the initial Packer
|
||||
// process. In order to avoid this and make the timestamp consistent across all
|
||||
// plugins, set it as a user variable and then access the user variable within
|
||||
// your plugins.
|
||||
func LegacyIsotime(format cty.Value) (cty.Value, error) {
|
||||
return LegacyIsotimeFunc.Call([]cty.Value{format})
|
||||
}
|
||||
|
||||
@@ -0,0 +1,64 @@
|
||||
package function
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"regexp"
|
||||
"testing"
|
||||
"time"
|
||||
|
||||
"github.com/zclconf/go-cty/cty"
|
||||
)
|
||||
|
||||
// HCL template usage example:
|
||||
//
|
||||
// locals {
|
||||
// emptyformat = legacy_isotime()
|
||||
// golangformat = legacy_isotime("01-02-2006")
|
||||
// }
|
||||
|
||||
func TestLegacyIsotime_empty(t *testing.T) {
|
||||
got, err := LegacyIsotimeFunc.Call([]cty.Value{})
|
||||
if err != nil {
|
||||
t.Fatalf("unexpected error: %s", err)
|
||||
}
|
||||
|
||||
_, err = time.Parse(time.RFC3339, got.AsString())
|
||||
if err != nil {
|
||||
t.Fatalf("Didn't get an RFC3339 string from empty case: %s", err)
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
func TestLegacyIsotime_inputs(t *testing.T) {
|
||||
tests := []struct {
|
||||
Value cty.Value
|
||||
Want string
|
||||
}{
|
||||
{
|
||||
cty.StringVal("01-02-2006"),
|
||||
`^\d{2}-\d{2}-\d{4}$`,
|
||||
},
|
||||
{
|
||||
cty.StringVal("Mon Jan 02, 2006"),
|
||||
`^(Mon|Tues|Wed|Thu|Fri|Sat|Sun){1} (Jan|Feb|Mar|Apr|May|Jun|Jul|Aug|Sep|Oct|Nov|Dec){1} \d{2}, \d{4}$`,
|
||||
},
|
||||
}
|
||||
|
||||
for _, test := range tests {
|
||||
t.Run(fmt.Sprintf("legacy_isotime(%#v)", test.Value), func(t *testing.T) {
|
||||
got, err := LegacyIsotime(test.Value)
|
||||
|
||||
if err != nil {
|
||||
t.Fatalf("unexpected error: %s", err)
|
||||
}
|
||||
re, err := regexp.Compile(test.Want)
|
||||
if err != nil {
|
||||
t.Fatalf("Bad regular expression test string: %#v", err)
|
||||
}
|
||||
|
||||
if !re.MatchString(got.AsString()) {
|
||||
t.Errorf("wrong result\ngot: %#v\nwant: %#v", got, test.Want)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,140 @@
|
||||
package function
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
|
||||
"github.com/hashicorp/go-cty-funcs/filesystem"
|
||||
"github.com/hashicorp/hcl/v2"
|
||||
"github.com/hashicorp/hcl/v2/hclsyntax"
|
||||
"github.com/zclconf/go-cty/cty"
|
||||
"github.com/zclconf/go-cty/cty/function"
|
||||
)
|
||||
|
||||
// MakeTemplateFileFunc constructs a function that takes a file path and
|
||||
// an arbitrary object of named values and attempts to render the referenced
|
||||
// file as a template using HCL template syntax.
|
||||
//
|
||||
// The template itself may recursively call other functions so a callback
|
||||
// must be provided to get access to those functions. The template cannot,
|
||||
// however, access any variables defined in the scope: it is restricted only to
|
||||
// those variables provided in the second function argument.
|
||||
//
|
||||
// As a special exception, a referenced template file may not recursively call
|
||||
// the templatefile function, since that would risk the same file being
|
||||
// included into itself indefinitely.
|
||||
func MakeTemplateFileFunc(baseDir string, funcsCb func() map[string]function.Function) function.Function {
|
||||
|
||||
params := []function.Parameter{
|
||||
{
|
||||
Name: "path",
|
||||
Type: cty.String,
|
||||
},
|
||||
{
|
||||
Name: "vars",
|
||||
Type: cty.DynamicPseudoType,
|
||||
},
|
||||
}
|
||||
|
||||
loadTmpl := func(fn string) (hcl.Expression, error) {
|
||||
// We re-use File here to ensure the same filename interpretation
|
||||
// as it does, along with its other safety checks.
|
||||
tmplVal, err := filesystem.File(baseDir, cty.StringVal(fn))
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
expr, diags := hclsyntax.ParseTemplate([]byte(tmplVal.AsString()), fn, hcl.Pos{Line: 1, Column: 1})
|
||||
if diags.HasErrors() {
|
||||
return nil, diags
|
||||
}
|
||||
|
||||
return expr, nil
|
||||
}
|
||||
|
||||
renderTmpl := func(expr hcl.Expression, varsVal cty.Value) (cty.Value, error) {
|
||||
if varsTy := varsVal.Type(); !(varsTy.IsMapType() || varsTy.IsObjectType()) {
|
||||
return cty.DynamicVal, function.NewArgErrorf(1, "invalid vars value: must be a map") // or an object, but we don't strongly distinguish these most of the time
|
||||
}
|
||||
|
||||
ctx := &hcl.EvalContext{
|
||||
Variables: varsVal.AsValueMap(),
|
||||
}
|
||||
|
||||
// We require all of the variables to be valid HCL identifiers, because
|
||||
// otherwise there would be no way to refer to them in the template
|
||||
// anyway. Rejecting this here gives better feedback to the user
|
||||
// than a syntax error somewhere in the template itself.
|
||||
for n := range ctx.Variables {
|
||||
if !hclsyntax.ValidIdentifier(n) {
|
||||
// This error message intentionally doesn't describe _all_ of
|
||||
// the different permutations that are technically valid as an
|
||||
// HCL identifier, but rather focuses on what we might
|
||||
// consider to be an "idiomatic" variable name.
|
||||
return cty.DynamicVal, function.NewArgErrorf(1, "invalid template variable name %q: must start with a letter, followed by zero or more letters, digits, and underscores", n)
|
||||
}
|
||||
}
|
||||
|
||||
// We'll pre-check references in the template here so we can give a
|
||||
// more specialized error message than HCL would by default, so it's
|
||||
// clearer that this problem is coming from a templatefile call.
|
||||
for _, traversal := range expr.Variables() {
|
||||
root := traversal.RootName()
|
||||
if _, ok := ctx.Variables[root]; !ok {
|
||||
return cty.DynamicVal, function.NewArgErrorf(1, "vars map does not contain key %q, referenced at %s", root, traversal[0].SourceRange())
|
||||
}
|
||||
}
|
||||
|
||||
givenFuncs := funcsCb() // this callback indirection is to avoid chicken/egg problems
|
||||
funcs := make(map[string]function.Function, len(givenFuncs))
|
||||
for name, fn := range givenFuncs {
|
||||
if name == "templatefile" {
|
||||
// We stub this one out to prevent recursive calls.
|
||||
funcs[name] = function.New(&function.Spec{
|
||||
Params: params,
|
||||
Type: func(args []cty.Value) (cty.Type, error) {
|
||||
return cty.NilType, fmt.Errorf("cannot recursively call templatefile from inside templatefile call")
|
||||
},
|
||||
})
|
||||
continue
|
||||
}
|
||||
funcs[name] = fn
|
||||
}
|
||||
ctx.Functions = funcs
|
||||
|
||||
val, diags := expr.Value(ctx)
|
||||
if diags.HasErrors() {
|
||||
return cty.DynamicVal, diags
|
||||
}
|
||||
return val, nil
|
||||
}
|
||||
|
||||
return function.New(&function.Spec{
|
||||
Params: params,
|
||||
Type: func(args []cty.Value) (cty.Type, error) {
|
||||
if !(args[0].IsKnown() && args[1].IsKnown()) {
|
||||
return cty.DynamicPseudoType, nil
|
||||
}
|
||||
|
||||
// We'll render our template now to see what result type it
|
||||
// produces. A template consisting only of a single interpolation
|
||||
// can potentially return any type.
|
||||
expr, err := loadTmpl(args[0].AsString())
|
||||
if err != nil {
|
||||
return cty.DynamicPseudoType, err
|
||||
}
|
||||
|
||||
// This is safe even if args[1] contains unknowns because the HCL
|
||||
// template renderer itself knows how to short-circuit those.
|
||||
val, err := renderTmpl(expr, args[1])
|
||||
return val.Type(), err
|
||||
},
|
||||
Impl: func(args []cty.Value, retType cty.Type) (cty.Value, error) {
|
||||
expr, err := loadTmpl(args[0].AsString())
|
||||
if err != nil {
|
||||
return cty.DynamicVal, err
|
||||
}
|
||||
return renderTmpl(expr, args[1])
|
||||
},
|
||||
})
|
||||
|
||||
}
|
||||
@@ -0,0 +1,151 @@
|
||||
package function
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"path/filepath"
|
||||
"testing"
|
||||
|
||||
"github.com/hashicorp/go-cty-funcs/filesystem"
|
||||
"github.com/zclconf/go-cty/cty"
|
||||
"github.com/zclconf/go-cty/cty/function"
|
||||
"github.com/zclconf/go-cty/cty/function/stdlib"
|
||||
)
|
||||
|
||||
func TestTemplateFile(t *testing.T) {
|
||||
tests := []struct {
|
||||
Path cty.Value
|
||||
Vars cty.Value
|
||||
Want cty.Value
|
||||
Err string
|
||||
}{
|
||||
{
|
||||
cty.StringVal("testdata/hello.txt"),
|
||||
cty.EmptyObjectVal,
|
||||
cty.StringVal("Hello World"),
|
||||
``,
|
||||
},
|
||||
{
|
||||
cty.StringVal("testdata/icon.png"),
|
||||
cty.EmptyObjectVal,
|
||||
cty.NilVal,
|
||||
`contents of testdata/icon.png are not valid UTF-8; use the filebase64 function to obtain the Base64 encoded contents or the other file functions (e.g. filemd5, filesha256) to obtain file hashing results instead`,
|
||||
},
|
||||
{
|
||||
cty.StringVal("testdata/missing"),
|
||||
cty.EmptyObjectVal,
|
||||
cty.NilVal,
|
||||
`no file exists at ` + filepath.Clean("testdata/missing"),
|
||||
},
|
||||
{
|
||||
cty.StringVal("testdata/hello.tmpl"),
|
||||
cty.MapVal(map[string]cty.Value{
|
||||
"name": cty.StringVal("Jodie"),
|
||||
}),
|
||||
cty.StringVal("Hello, Jodie!"),
|
||||
``,
|
||||
},
|
||||
{
|
||||
cty.StringVal("testdata/hello.tmpl"),
|
||||
cty.MapVal(map[string]cty.Value{
|
||||
"name!": cty.StringVal("Jodie"),
|
||||
}),
|
||||
cty.NilVal,
|
||||
`invalid template variable name "name!": must start with a letter, followed by zero or more letters, digits, and underscores`,
|
||||
},
|
||||
{
|
||||
cty.StringVal("testdata/hello.tmpl"),
|
||||
cty.ObjectVal(map[string]cty.Value{
|
||||
"name": cty.StringVal("Jimbo"),
|
||||
}),
|
||||
cty.StringVal("Hello, Jimbo!"),
|
||||
``,
|
||||
},
|
||||
{
|
||||
cty.StringVal("testdata/hello.tmpl"),
|
||||
cty.EmptyObjectVal,
|
||||
cty.NilVal,
|
||||
`vars map does not contain key "name", referenced at testdata/hello.tmpl:1,10-14`,
|
||||
},
|
||||
{
|
||||
cty.StringVal("testdata/func.tmpl"),
|
||||
cty.ObjectVal(map[string]cty.Value{
|
||||
"list": cty.ListVal([]cty.Value{
|
||||
cty.StringVal("a"),
|
||||
cty.StringVal("b"),
|
||||
cty.StringVal("c"),
|
||||
}),
|
||||
}),
|
||||
cty.StringVal("The items are a, b, c"),
|
||||
``,
|
||||
},
|
||||
{
|
||||
cty.StringVal("testdata/recursive.tmpl"),
|
||||
cty.MapValEmpty(cty.String),
|
||||
cty.NilVal,
|
||||
`testdata/recursive.tmpl:1,3-16: Error in function call; Call to function "templatefile" failed: cannot recursively call templatefile from inside templatefile call.`,
|
||||
},
|
||||
{
|
||||
cty.StringVal("testdata/list.tmpl"),
|
||||
cty.ObjectVal(map[string]cty.Value{
|
||||
"list": cty.ListVal([]cty.Value{
|
||||
cty.StringVal("a"),
|
||||
cty.StringVal("b"),
|
||||
cty.StringVal("c"),
|
||||
}),
|
||||
}),
|
||||
cty.StringVal("- a\n- b\n- c\n"),
|
||||
``,
|
||||
},
|
||||
{
|
||||
cty.StringVal("testdata/list.tmpl"),
|
||||
cty.ObjectVal(map[string]cty.Value{
|
||||
"list": cty.True,
|
||||
}),
|
||||
cty.NilVal,
|
||||
`testdata/list.tmpl:1,13-17: Iteration over non-iterable value; A value of type bool cannot be used as the collection in a 'for' expression.`,
|
||||
},
|
||||
{
|
||||
cty.StringVal("testdata/bare.tmpl"),
|
||||
cty.ObjectVal(map[string]cty.Value{
|
||||
"val": cty.True,
|
||||
}),
|
||||
cty.True, // since this template contains only an interpolation, its true value shines through
|
||||
``,
|
||||
},
|
||||
}
|
||||
|
||||
templateFileFn := MakeTemplateFileFunc(".", func() map[string]function.Function {
|
||||
return map[string]function.Function{
|
||||
"join": stdlib.JoinFunc,
|
||||
"templatefile": filesystem.MakeFileFunc(".", false), // just a placeholder, since templatefile itself overrides this
|
||||
}
|
||||
})
|
||||
|
||||
for _, test := range tests {
|
||||
t.Run(fmt.Sprintf("TemplateFile(%#v, %#v)", test.Path, test.Vars), func(t *testing.T) {
|
||||
got, err := templateFileFn.Call([]cty.Value{test.Path, test.Vars})
|
||||
|
||||
if argErr, ok := err.(function.ArgError); ok {
|
||||
if argErr.Index < 0 || argErr.Index > 1 {
|
||||
t.Errorf("ArgError index %d is out of range for templatefile (must be 0 or 1)", argErr.Index)
|
||||
}
|
||||
}
|
||||
|
||||
if test.Err != "" {
|
||||
if err == nil {
|
||||
t.Fatal("succeeded; want error")
|
||||
}
|
||||
if got, want := err.Error(), test.Err; got != want {
|
||||
t.Errorf("wrong error\ngot: %s\nwant: %s", got, want)
|
||||
}
|
||||
return
|
||||
} else if err != nil {
|
||||
t.Fatalf("unexpected error: %s", err)
|
||||
}
|
||||
|
||||
if !got.RawEquals(test.Want) {
|
||||
t.Errorf("wrong result\ngot: %#v\nwant: %#v", got, test.Want)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
+1
@@ -0,0 +1 @@
|
||||
${val}
|
||||
+1
@@ -0,0 +1 @@
|
||||
The items are ${join(", ", list)}
|
||||
+1
@@ -0,0 +1 @@
|
||||
Hello, ${name}!
|
||||
+1
@@ -0,0 +1 @@
|
||||
Hello World
|
||||
BIN
Binary file not shown.
|
After Width: | Height: | Size: 806 B |
+3
@@ -0,0 +1,3 @@
|
||||
%{ for x in list ~}
|
||||
- ${x}
|
||||
%{ endfor ~}
|
||||
@@ -0,0 +1 @@
|
||||
${templatefile("recursive.tmpl", {})}
|
||||
@@ -68,6 +68,7 @@ func Functions(basedir string) map[string]function.Function {
|
||||
"jsondecode": stdlib.JSONDecodeFunc,
|
||||
"jsonencode": stdlib.JSONEncodeFunc,
|
||||
"keys": stdlib.KeysFunc,
|
||||
"legacy_isotime": pkrfunction.LegacyIsotimeFunc,
|
||||
"length": pkrfunction.LengthFunc,
|
||||
"log": stdlib.LogFunc,
|
||||
"lookup": stdlib.LookupFunc,
|
||||
@@ -117,6 +118,12 @@ func Functions(basedir string) map[string]function.Function {
|
||||
"zipmap": stdlib.ZipmapFunc,
|
||||
}
|
||||
|
||||
funcs["templatefile"] = pkrfunction.MakeTemplateFileFunc(basedir, func() map[string]function.Function {
|
||||
// The templatefile function prevents recursive calls to itself
|
||||
// by copying this map and overwriting the "templatefile" entry.
|
||||
return funcs
|
||||
})
|
||||
|
||||
return funcs
|
||||
}
|
||||
|
||||
|
||||
@@ -90,6 +90,11 @@ func (p *Parser) Parse(filename string, varFiles []string, argVars map[string]st
|
||||
if filename != "" {
|
||||
hclFiles, jsonFiles, moreDiags := GetHCL2Files(filename, hcl2FileExt, hcl2JsonFileExt)
|
||||
diags = append(diags, moreDiags...)
|
||||
if moreDiags.HasErrors() {
|
||||
// here this probably means that the file was not found, let's
|
||||
// simply leave early.
|
||||
return nil, diags
|
||||
}
|
||||
if len(hclFiles)+len(jsonFiles) == 0 {
|
||||
diags = append(diags, &hcl.Diagnostic{
|
||||
Severity: hcl.DiagError,
|
||||
|
||||
@@ -57,7 +57,6 @@ func GetHCL2Files(filename, hclSuffix, jsonSuffix string) (hclFiles, jsonFiles [
|
||||
if err != nil {
|
||||
diags = append(diags, &hcl.Diagnostic{
|
||||
Severity: hcl.DiagError,
|
||||
Summary: "Cannot tell wether " + filename + " is a directory",
|
||||
Detail: err.Error(),
|
||||
})
|
||||
return nil, nil, diags
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user