Merge remote-tracking branch 'origin/master' into fix_8730
This commit is contained in:
commit
59cc246942
|
@ -259,6 +259,28 @@ localized code generation. Say you are working on the Amazon builder: running
|
|||
`go generate ./builder/amazon/...` will do that for you. Make sure that the
|
||||
latest code generation tool is installed by running `make install-gen-deps`.
|
||||
|
||||
#### Code linting
|
||||
|
||||
Packer relies on [golangci-lint](https://github.com/golangci/golangci-lint) for linting its Go code base, excluding any generated code created by `go generate`. Linting is executed on new files during Travis builds via `make ci`; the linting of existing code base is only executed when running `make lint`. Linting a large project like Packer is an iterative process so existing code base will have issues that are actively being fixed; pull-requests that fix existing linting issues are always welcomed :smile:.
|
||||
|
||||
The main configuration for golangci-lint is the `.golangci.yml` in the project root. See `golangci-lint --help` for a list of flags that can be used to override the default configuration.
|
||||
|
||||
Run golangci-lint on the entire Packer code base.
|
||||
```
|
||||
make lint
|
||||
```
|
||||
|
||||
Run golangci-lint on a single pkg or directory; PKG_NAME expands to /builder/amazon/...
|
||||
```
|
||||
make lint PKG_NAME=builder/amazon
|
||||
```
|
||||
|
||||
Note: linting on Travis uses the `--new-from-rev=origin/master` flag to only lint new files added within a branch or pull-request. To run this check locally you can use the `ci-lint` make target. See [golangci-lint in CI](https://github.com/golangci/golangci-lint#faq) for more information.
|
||||
|
||||
```
|
||||
make ci-lint
|
||||
```
|
||||
|
||||
#### Running Unit Tests
|
||||
|
||||
You can run tests for individual packages using commands like this:
|
||||
|
|
|
@ -0,0 +1,124 @@
|
|||
issues:
|
||||
# List of regexps of issue texts to exclude, empty list by default.
|
||||
# But independently from this option we use default exclude patterns,
|
||||
# it can be disabled by `exclude-use-default: false`. To list all
|
||||
# excluded by default patterns execute `golangci-lint run --help`
|
||||
|
||||
exclude-rules:
|
||||
# Exclude gosimple bool check
|
||||
- linters:
|
||||
- gosimple
|
||||
text: "S(1002|1008|1021)"
|
||||
# Exclude failing staticchecks for now
|
||||
- linters:
|
||||
- staticcheck
|
||||
text: "SA(1006|1019|4006|4010|4017|5007|6005|9004):"
|
||||
# Exclude lll issues for long lines with go:generate
|
||||
- linters:
|
||||
- lll
|
||||
source: "^//go:generate "
|
||||
|
||||
# Maximum issues count per one linter. Set to 0 to disable. Default is 50.
|
||||
max-issues-per-linter: 0
|
||||
|
||||
# Maximum count of issues with the same text. Set to 0 to disable. Default is 3.
|
||||
max-same-issues: 0
|
||||
|
||||
linters:
|
||||
disable-all: true
|
||||
enable:
|
||||
- deadcode
|
||||
- errcheck
|
||||
- goimports
|
||||
- gosimple
|
||||
- govet
|
||||
- ineffassign
|
||||
- staticcheck
|
||||
- unconvert
|
||||
- unused
|
||||
- varcheck
|
||||
fast: true
|
||||
|
||||
# options for analysis running
|
||||
run:
|
||||
# default concurrency is a available CPU number
|
||||
concurrency: 4
|
||||
|
||||
# timeout for analysis, e.g. 30s, 5m, default is 1m
|
||||
timeout: 10m
|
||||
|
||||
# exit code when at least one issue was found, default is 1
|
||||
issues-exit-code: 1
|
||||
|
||||
# include test files or not, default is true
|
||||
tests: true
|
||||
|
||||
# list of build tags, all linters use it. Default is empty list.
|
||||
#build-tags:
|
||||
# - mytag
|
||||
|
||||
# which dirs to skip: issues from them won't be reported;
|
||||
# can use regexp here: generated.*, regexp is applied on full path;
|
||||
# default value is empty list, but default dirs are skipped independently
|
||||
# from this option's value (see skip-dirs-use-default).
|
||||
#skip-dirs:
|
||||
# - src/external_libs
|
||||
# - autogenerated_by_my_lib
|
||||
|
||||
# default is true. Enables skipping of directories:
|
||||
# vendor$, third_party$, testdata$, examples$, Godeps$, builtin$
|
||||
skip-dirs-use-default: true
|
||||
|
||||
# which files to skip: they will be analyzed, but issues from them
|
||||
# won't be reported. Default value is empty list, but there is
|
||||
# no need to include all autogenerated files, we confidently recognize
|
||||
# autogenerated files. If it's not please let us know.
|
||||
skip-files:
|
||||
- ".*\\.hcl2spec\\.go$"
|
||||
# - lib/bad.go
|
||||
|
||||
# by default isn't set. If set we pass it to "go list -mod={option}". From "go help modules":
|
||||
# If invoked with -mod=readonly, the go command is disallowed from the implicit
|
||||
# automatic updating of go.mod described above. Instead, it fails when any changes
|
||||
# to go.mod are needed. This setting is most useful to check that go.mod does
|
||||
# not need updates, such as in a continuous integration and testing system.
|
||||
# If invoked with -mod=vendor, the go command assumes that the vendor
|
||||
# directory holds the correct copies of dependencies and ignores
|
||||
# the dependency descriptions in go.mod.
|
||||
modules-download-mode: vendor
|
||||
|
||||
|
||||
# output configuration options
|
||||
output:
|
||||
# colored-line-number|line-number|json|tab|checkstyle|code-climate, default is "colored-line-number"
|
||||
format: colored-line-number
|
||||
|
||||
# print lines of code with issue, default is true
|
||||
print-issued-lines: true
|
||||
|
||||
# print linter name in the end of issue text, default is true
|
||||
print-linter-name: true
|
||||
|
||||
# make issues output unique by line, default is true
|
||||
uniq-by-line: true
|
||||
|
||||
|
||||
# all available settings of specific linters
|
||||
linters-settings:
|
||||
errcheck:
|
||||
# report about not checking of errors in type assetions: `a := b.(MyStruct)`;
|
||||
# default is false: such cases aren't reported by default.
|
||||
check-type-assertions: false
|
||||
|
||||
# report about assignment of errors to blank identifier: `num, _ := strconv.Atoi(numStr)`;
|
||||
# default is false: such cases aren't reported by default.
|
||||
check-blank: false
|
||||
|
||||
# [deprecated] comma-separated list of pairs of the form pkg:regex
|
||||
# the regex is used to ignore names within pkg. (default "fmt:.*").
|
||||
# see https://github.com/kisielk/errcheck#the-deprecated-method for details
|
||||
ignore: fmt:.*,io/ioutil:^Read.*,io:Close
|
||||
|
||||
# path to a file containing a list of functions to exclude from checking
|
||||
# see https://github.com/kisielk/errcheck#excluding-functions for details
|
||||
#exclude: /path/to/file.txt
|
21
.travis.yml
21
.travis.yml
|
@ -4,20 +4,21 @@ env:
|
|||
os:
|
||||
- osx
|
||||
|
||||
sudo: false
|
||||
|
||||
language: go
|
||||
|
||||
go:
|
||||
- 1.13.x
|
||||
|
||||
script:
|
||||
- df -h
|
||||
- travis_wait make ci
|
||||
|
||||
branches:
|
||||
only:
|
||||
- master
|
||||
|
||||
matrix:
|
||||
jobs:
|
||||
fast_finish: true
|
||||
include:
|
||||
- go: "1.13.x"
|
||||
name: "go test"
|
||||
script:
|
||||
- df -h
|
||||
- travis_wait make ci
|
||||
- go: "1.13.x"
|
||||
name: "go lint"
|
||||
script: travis_wait make ci-lint
|
||||
|
||||
|
|
26
CHANGELOG.md
26
CHANGELOG.md
|
@ -1,3 +1,29 @@
|
|||
## 1.5.5 (Upcoming)
|
||||
|
||||
### IMPROVEMENTS:
|
||||
- builder/tencentcloud: Show tencentcloud image id after copy to desination
|
||||
region. [GH-8763]
|
||||
|
||||
### Bug Fixes:
|
||||
- builder/vsphere: Fix network object interface panic. [GH-8753]
|
||||
|
||||
## 1.5.4 (February 14, 2020)
|
||||
no-change release to fix code-signing on OSX binaries. Since checksums for these
|
||||
binaries has changed, we are releasing a second time to prevent confusion.
|
||||
|
||||
## 1.5.3 (February 14, 2020)
|
||||
|
||||
### IMPROVEMENTS:
|
||||
* builder/vsphere: Add ability to define multiple NICs for vsphere-iso
|
||||
[GH-8739]
|
||||
* builder/vsphere: Add option to remove CD-ROM drives. [GH-8690]
|
||||
* core: Add validation to catch when users accidentally add duplicate fields to
|
||||
template [GH-8725]
|
||||
|
||||
### Bug Fixes:
|
||||
* core/hcl2: Fix template prepare/validation for HCL2 templates [GH-8742]
|
||||
* core: Fix `build` template function interpolation [GH-8727]
|
||||
|
||||
## 1.5.2 (February 12, 2020)
|
||||
**New Builder** The vsphere-iso builder, previously maintained by JetBrains,
|
||||
has been merged with the Packer core. It will be officially supported by the
|
||||
|
|
23
Makefile
23
Makefile
|
@ -18,7 +18,8 @@ GOLDFLAGS=-X $(GIT_IMPORT).GitCommit=$(GIT_COMMIT)$(GIT_DIRTY)
|
|||
|
||||
export GOLDFLAGS
|
||||
|
||||
.PHONY: bin checkversion ci default install-build-deps install-gen-deps fmt fmt-docs fmt-examples generate releasebin test testacc testrace
|
||||
.PHONY: bin checkversion ci ci-lint default install-build-deps install-gen-deps fmt fmt-docs fmt-examples generate install-lint-deps lint \
|
||||
releasebin test testacc testrace
|
||||
|
||||
default: install-build-deps install-gen-deps generate testrace dev releasebin package dev fmt fmt-check mode-check fmt-docs fmt-examples
|
||||
|
||||
|
@ -49,12 +50,16 @@ install-gen-deps: ## Install dependencies for code generation
|
|||
# dir. `go get` will change our deps and the following deps are not part of
|
||||
# out code dependencies; so a go mod tidy will remove them again. `go
|
||||
# install` seems to install the last tagged version and we want to install
|
||||
# master.
|
||||
# master.
|
||||
@(cd $(TEMPDIR) && GO111MODULE=on go get github.com/mna/pigeon@master)
|
||||
@(cd $(TEMPDIR) && GO111MODULE=on go get github.com/alvaroloes/enumer@master)
|
||||
@go install ./cmd/struct-markdown
|
||||
@go install ./cmd/mapstructure-to-hcl2
|
||||
|
||||
install-lint-deps: ## Install linter dependencies
|
||||
@echo "==> Updating linter dependencies..."
|
||||
@curl -sSfL -q https://raw.githubusercontent.com/golangci/golangci-lint/master/install.sh | sh -s -- -b $(GOPATH)/bin v1.23.1
|
||||
|
||||
dev: ## Build and install a development build
|
||||
@grep 'const VersionPrerelease = ""' version/version.go > /dev/null ; if [ $$? -eq 0 ]; then \
|
||||
echo "ERROR: You must add prerelease tags to version/version.go prior to making a dev build."; \
|
||||
|
@ -66,6 +71,20 @@ dev: ## Build and install a development build
|
|||
@cp $(GOPATH)/bin/packer bin/packer
|
||||
@cp $(GOPATH)/bin/packer pkg/$(GOOS)_$(GOARCH)
|
||||
|
||||
lint: install-lint-deps ## Lint Go code
|
||||
@if [ ! -z $(PKG_NAME) ]; then \
|
||||
echo "golangci-lint run ./$(PKG_NAME)/..."; \
|
||||
golangci-lint run ./$(PKG_NAME)/...; \
|
||||
else \
|
||||
echo "golangci-lint run ./..."; \
|
||||
golangci-lint run ./...; \
|
||||
fi
|
||||
|
||||
ci-lint: install-lint-deps ## On ci only lint newly added Go source files
|
||||
@echo "==> Running linter on newly added Go source files..."
|
||||
GO111MODULE=on golangci-lint run --new-from-rev=origin/master ./...
|
||||
|
||||
|
||||
fmt: ## Format Go code
|
||||
@go fmt ./...
|
||||
|
||||
|
|
|
@ -10,11 +10,11 @@ package chroot
|
|||
import (
|
||||
"context"
|
||||
"errors"
|
||||
"github.com/hashicorp/packer/builder"
|
||||
"runtime"
|
||||
|
||||
"github.com/aws/aws-sdk-go/service/ec2"
|
||||
"github.com/hashicorp/hcl/v2/hcldec"
|
||||
"github.com/hashicorp/packer/builder"
|
||||
awscommon "github.com/hashicorp/packer/builder/amazon/common"
|
||||
"github.com/hashicorp/packer/common"
|
||||
"github.com/hashicorp/packer/common/chroot"
|
||||
|
|
|
@ -4,13 +4,13 @@ import (
|
|||
"bytes"
|
||||
"context"
|
||||
"fmt"
|
||||
"github.com/hashicorp/packer/builder"
|
||||
"log"
|
||||
"os"
|
||||
"path/filepath"
|
||||
"strings"
|
||||
|
||||
"github.com/aws/aws-sdk-go/service/ec2"
|
||||
"github.com/hashicorp/packer/builder"
|
||||
"github.com/hashicorp/packer/common"
|
||||
"github.com/hashicorp/packer/helper/multistep"
|
||||
"github.com/hashicorp/packer/packer"
|
||||
|
|
|
@ -3,10 +3,10 @@ package chroot
|
|||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"github.com/hashicorp/packer/builder"
|
||||
"log"
|
||||
"os"
|
||||
|
||||
"github.com/hashicorp/packer/builder"
|
||||
"github.com/hashicorp/packer/helper/multistep"
|
||||
"github.com/hashicorp/packer/packer"
|
||||
)
|
||||
|
|
|
@ -19,9 +19,9 @@ type AMIConfig struct {
|
|||
// engine](../templates/engine.html) for more info).
|
||||
AMIName string `mapstructure:"ami_name" required:"true"`
|
||||
// The description to set for the resulting
|
||||
// AMI(s). By default this description is empty. This is a template
|
||||
// engine, see Build template
|
||||
// data for more information.
|
||||
// AMI(s). By default this description is empty. This is a
|
||||
// [template engine](/docs/templates/engine.html), see [Build template
|
||||
// data](#build-template-data) for more information.
|
||||
AMIDescription string `mapstructure:"ami_description" required:"false"`
|
||||
// The type of virtualization for the AMI
|
||||
// you are building. This option is required to register HVM images. Can be
|
||||
|
|
|
@ -1,12 +1,12 @@
|
|||
package common
|
||||
|
||||
import (
|
||||
"github.com/hashicorp/packer/builder"
|
||||
"reflect"
|
||||
"testing"
|
||||
|
||||
"github.com/aws/aws-sdk-go/aws"
|
||||
"github.com/aws/aws-sdk-go/service/ec2"
|
||||
"github.com/hashicorp/packer/builder"
|
||||
"github.com/hashicorp/packer/helper/multistep"
|
||||
)
|
||||
|
||||
|
|
|
@ -3,6 +3,7 @@ package common
|
|||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
|
||||
"github.com/aws/aws-sdk-go/aws"
|
||||
"github.com/aws/aws-sdk-go/aws/session"
|
||||
"github.com/aws/aws-sdk-go/service/ec2"
|
||||
|
|
|
@ -11,6 +11,7 @@ package ebs
|
|||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
|
||||
"github.com/aws/aws-sdk-go/service/ec2"
|
||||
"github.com/aws/aws-sdk-go/service/iam"
|
||||
"github.com/hashicorp/hcl/v2/hcldec"
|
||||
|
|
|
@ -9,6 +9,7 @@ import (
|
|||
"context"
|
||||
"errors"
|
||||
"fmt"
|
||||
|
||||
"github.com/aws/aws-sdk-go/service/ec2"
|
||||
"github.com/aws/aws-sdk-go/service/iam"
|
||||
"github.com/hashicorp/hcl/v2/hcldec"
|
||||
|
|
|
@ -1,9 +1,10 @@
|
|||
package ebssurrogate
|
||||
|
||||
import (
|
||||
"github.com/hashicorp/packer/builder/amazon/common"
|
||||
"testing"
|
||||
|
||||
"github.com/hashicorp/packer/builder/amazon/common"
|
||||
|
||||
"github.com/hashicorp/packer/packer"
|
||||
)
|
||||
|
||||
|
|
|
@ -9,13 +9,13 @@ import (
|
|||
"context"
|
||||
"errors"
|
||||
"fmt"
|
||||
"github.com/hashicorp/packer/builder"
|
||||
"os"
|
||||
"strings"
|
||||
|
||||
"github.com/aws/aws-sdk-go/service/ec2"
|
||||
"github.com/aws/aws-sdk-go/service/iam"
|
||||
"github.com/hashicorp/hcl/v2/hcldec"
|
||||
"github.com/hashicorp/packer/builder"
|
||||
awscommon "github.com/hashicorp/packer/builder/amazon/common"
|
||||
"github.com/hashicorp/packer/common"
|
||||
"github.com/hashicorp/packer/helper/communicator"
|
||||
|
|
|
@ -371,8 +371,8 @@ type Config struct {
|
|||
AllowedInboundIpAddresses []string `mapstructure:"allowed_inbound_ip_addresses"`
|
||||
|
||||
// Runtime Values
|
||||
UserName string
|
||||
Password string
|
||||
UserName string `mapstructure-to-hcl2:",skip"`
|
||||
Password string `mapstructure-to-hcl2:",skip"`
|
||||
tmpAdminPassword string
|
||||
tmpCertificatePassword string
|
||||
tmpResourceGroupName string
|
||||
|
|
|
@ -66,8 +66,6 @@ type FlatConfig struct {
|
|||
AdditionalDiskSize []int32 `mapstructure:"disk_additional_size" required:"false" cty:"disk_additional_size"`
|
||||
DiskCachingType *string `mapstructure:"disk_caching_type" required:"false" cty:"disk_caching_type"`
|
||||
AllowedInboundIpAddresses []string `mapstructure:"allowed_inbound_ip_addresses" cty:"allowed_inbound_ip_addresses"`
|
||||
UserName *string `cty:"user_name"`
|
||||
Password *string `cty:"password"`
|
||||
Type *string `mapstructure:"communicator" cty:"communicator"`
|
||||
PauseBeforeConnect *string `mapstructure:"pause_before_connecting" cty:"pause_before_connecting"`
|
||||
SSHHost *string `mapstructure:"ssh_host" cty:"ssh_host"`
|
||||
|
@ -180,8 +178,6 @@ func (*FlatConfig) HCL2Spec() map[string]hcldec.Spec {
|
|||
"disk_additional_size": &hcldec.AttrSpec{Name: "disk_additional_size", Type: cty.List(cty.Number), Required: false},
|
||||
"disk_caching_type": &hcldec.AttrSpec{Name: "disk_caching_type", Type: cty.String, Required: false},
|
||||
"allowed_inbound_ip_addresses": &hcldec.AttrSpec{Name: "allowed_inbound_ip_addresses", Type: cty.List(cty.String), Required: false},
|
||||
"user_name": &hcldec.AttrSpec{Name: "user_name", Type: cty.String, Required: false},
|
||||
"password": &hcldec.AttrSpec{Name: "password", Type: cty.String, Required: false},
|
||||
"communicator": &hcldec.AttrSpec{Name: "communicator", Type: cty.String, Required: false},
|
||||
"pause_before_connecting": &hcldec.AttrSpec{Name: "pause_before_connecting", Type: cty.String, Required: false},
|
||||
"ssh_host": &hcldec.AttrSpec{Name: "ssh_host", Type: cty.String, Required: false},
|
||||
|
|
|
@ -120,7 +120,7 @@ type Config struct {
|
|||
// provisioners should connect to the local IP address of the instance.
|
||||
UseLocalIPAddress bool `mapstructure:"use_local_ip_address" required:"false"`
|
||||
// User data to launch with the instance. This is a
|
||||
// template engine see User Data bellow for
|
||||
// template engine; see "User Data" bellow for
|
||||
// more details. Packer will not automatically wait for a user script to
|
||||
// finish before shutting down the instance this must be handled in a
|
||||
// provisioner.
|
||||
|
|
|
@ -1,8 +1,9 @@
|
|||
package builder
|
||||
|
||||
import (
|
||||
"github.com/hashicorp/packer/helper/multistep"
|
||||
"testing"
|
||||
|
||||
"github.com/hashicorp/packer/helper/multistep"
|
||||
)
|
||||
|
||||
func TestGeneratedData_Put(t *testing.T) {
|
||||
|
|
|
@ -91,17 +91,29 @@ type Config struct {
|
|||
// Can be useful when using custom api_url. Defaults to public.
|
||||
PublicNetAdpService string `mapstructure:"public_netadp_service" required:"false"`
|
||||
|
||||
ChrootDisk bool `mapstructure:"chroot_disk"`
|
||||
ChrootDiskSize float32 `mapstructure:"chroot_disk_size"`
|
||||
ChrootDiskType string `mapstructure:"chroot_disk_type"`
|
||||
ChrootMountPath string `mapstructure:"chroot_mount_path"`
|
||||
ChrootMounts [][]string `mapstructure:"chroot_mounts"`
|
||||
ChrootCopyFiles []string `mapstructure:"chroot_copy_files"`
|
||||
ChrootCommandWrapper string `mapstructure:"chroot_command_wrapper"`
|
||||
ChrootDisk bool `mapstructure:"chroot_disk"`
|
||||
ChrootDiskSize float32 `mapstructure:"chroot_disk_size"`
|
||||
ChrootDiskType string `mapstructure:"chroot_disk_type"`
|
||||
ChrootMountPath string `mapstructure:"chroot_mount_path"`
|
||||
ChrootMounts [][]string `mapstructure:"chroot_mounts"`
|
||||
ChrootCopyFiles []string `mapstructure:"chroot_copy_files"`
|
||||
// How to run shell commands. This defaults to {{.Command}}. This may be
|
||||
// useful to set if you want to set environmental variables or perhaps run
|
||||
// it with sudo or so on. This is a configuration template where the
|
||||
// .Command variable is replaced with the command to be run. Defaults to
|
||||
// {{.Command}}.
|
||||
ChrootCommandWrapper string `mapstructure:"chroot_command_wrapper"`
|
||||
|
||||
MountOptions []string `mapstructure:"mount_options"`
|
||||
MountPartition string `mapstructure:"mount_partition"`
|
||||
PreMountCommands []string `mapstructure:"pre_mount_commands"`
|
||||
MountOptions []string `mapstructure:"mount_options"`
|
||||
MountPartition string `mapstructure:"mount_partition"`
|
||||
// A series of commands to execute after attaching the root volume and
|
||||
// before mounting the chroot. This is not required unless using
|
||||
// from_scratch. If so, this should include any partitioning and filesystem
|
||||
// creation commands. The path to the device is provided by {{.Device}}.
|
||||
PreMountCommands []string `mapstructure:"pre_mount_commands"`
|
||||
// As pre_mount_commands, but the commands are executed after mounting the
|
||||
// root device and before the extra mount and copy steps. The device and
|
||||
// mount path are provided by {{.Device}} and {{.MountPath}}.
|
||||
PostMountCommands []string `mapstructure:"post_mount_commands"`
|
||||
// List of SSH keys by name or id to be added
|
||||
// to the server on launch.
|
||||
|
|
|
@ -148,6 +148,21 @@ type CommonConfig struct {
|
|||
// built. When this value is set to true, the machine will start without a
|
||||
// console.
|
||||
Headless bool `mapstructure:"headless" required:"false"`
|
||||
// When configured, determines the device or device type that is given preferential
|
||||
// treatment when choosing a boot device.
|
||||
//
|
||||
// For Generation 1:
|
||||
// - `IDE`
|
||||
// - `CD` *or* `DVD`
|
||||
// - `Floppy`
|
||||
// - `NET`
|
||||
//
|
||||
// For Generation 2:
|
||||
// - `IDE:x:y`
|
||||
// - `SCSI:x:y`
|
||||
// - `CD` *or* `DVD`
|
||||
// - `NET`
|
||||
FirstBootDevice string `mapstructure:"first_boot_device" required:"false"`
|
||||
}
|
||||
|
||||
func (c *CommonConfig) Prepare(ctx *interpolate.Context, pc *common.PackerConfig) ([]error, []string) {
|
||||
|
@ -268,6 +283,13 @@ func (c *CommonConfig) Prepare(ctx *interpolate.Context, pc *common.PackerConfig
|
|||
}
|
||||
}
|
||||
|
||||
if c.FirstBootDevice != "" {
|
||||
_, _, _, err := ParseBootDeviceIdentifier(c.FirstBootDevice, c.Generation)
|
||||
if err != nil {
|
||||
errs = append(errs, fmt.Errorf("first_boot_device: %s", err))
|
||||
}
|
||||
}
|
||||
|
||||
if c.EnableVirtualizationExtensions {
|
||||
if c.EnableDynamicMemory {
|
||||
warning := fmt.Sprintf("For nested virtualization, when virtualization extension is enabled, " +
|
||||
|
|
|
@ -113,6 +113,8 @@ type Driver interface {
|
|||
|
||||
SetBootDvdDrive(string, uint, uint, uint) error
|
||||
|
||||
SetFirstBootDevice(string, string, uint, uint, uint) error
|
||||
|
||||
UnmountDvdDrive(string, uint, uint) error
|
||||
|
||||
DeleteDvdDrive(string, uint, uint) error
|
||||
|
|
|
@ -236,6 +236,14 @@ type DriverMock struct {
|
|||
SetBootDvdDrive_Generation uint
|
||||
SetBootDvdDrive_Err error
|
||||
|
||||
SetFirstBootDevice_Called bool
|
||||
SetFirstBootDevice_VmName string
|
||||
SetFirstBootDevice_ControllerType string
|
||||
SetFirstBootDevice_ControllerNumber uint
|
||||
SetFirstBootDevice_ControllerLocation uint
|
||||
SetFirstBootDevice_Generation uint
|
||||
SetFirstBootDevice_Err error
|
||||
|
||||
UnmountDvdDrive_Called bool
|
||||
UnmountDvdDrive_VmName string
|
||||
UnmountDvdDrive_ControllerNumber uint
|
||||
|
@ -575,6 +583,17 @@ func (d *DriverMock) SetBootDvdDrive(vmName string, controllerNumber uint, contr
|
|||
return d.SetBootDvdDrive_Err
|
||||
}
|
||||
|
||||
func (d *DriverMock) SetFirstBootDevice(vmName string, controllerType string, controllerNumber uint,
|
||||
controllerLocation uint, generation uint) error {
|
||||
d.SetFirstBootDevice_Called = true
|
||||
d.SetFirstBootDevice_VmName = vmName
|
||||
d.SetFirstBootDevice_ControllerType = controllerType
|
||||
d.SetFirstBootDevice_ControllerNumber = controllerNumber
|
||||
d.SetFirstBootDevice_ControllerLocation = controllerLocation
|
||||
d.SetFirstBootDevice_Generation = generation
|
||||
return d.SetFirstBootDevice_Err
|
||||
}
|
||||
|
||||
func (d *DriverMock) UnmountDvdDrive(vmName string, controllerNumber uint, controllerLocation uint) error {
|
||||
d.UnmountDvdDrive_Called = true
|
||||
d.UnmountDvdDrive_VmName = vmName
|
||||
|
|
|
@ -267,6 +267,11 @@ func (d *HypervPS4Driver) SetBootDvdDrive(vmName string, controllerNumber uint,
|
|||
return hyperv.SetBootDvdDrive(vmName, controllerNumber, controllerLocation, generation)
|
||||
}
|
||||
|
||||
func (d *HypervPS4Driver) SetFirstBootDevice(vmName string, controllerType string, controllerNumber uint,
|
||||
controllerLocation uint, generation uint) error {
|
||||
return hyperv.SetFirstBootDevice(vmName, controllerType, controllerNumber, controllerLocation, generation)
|
||||
}
|
||||
|
||||
func (d *HypervPS4Driver) UnmountDvdDrive(vmName string, controllerNumber uint, controllerLocation uint) error {
|
||||
return hyperv.UnmountDvdDrive(vmName, controllerNumber, controllerLocation)
|
||||
}
|
||||
|
|
|
@ -12,7 +12,8 @@ import (
|
|||
)
|
||||
|
||||
type StepMountDvdDrive struct {
|
||||
Generation uint
|
||||
Generation uint
|
||||
FirstBootDevice string
|
||||
}
|
||||
|
||||
func (s *StepMountDvdDrive) Run(ctx context.Context, state multistep.StateBag) multistep.StepAction {
|
||||
|
@ -57,13 +58,24 @@ func (s *StepMountDvdDrive) Run(ctx context.Context, state multistep.StateBag) m
|
|||
|
||||
state.Put("os.dvd.properties", dvdControllerProperties)
|
||||
|
||||
ui.Say(fmt.Sprintf("Setting boot drive to os dvd drive %s ...", isoPath))
|
||||
err = driver.SetBootDvdDrive(vmName, controllerNumber, controllerLocation, s.Generation)
|
||||
if err != nil {
|
||||
err := fmt.Errorf(errorMsg, err)
|
||||
state.Put("error", err)
|
||||
ui.Error(err.Error())
|
||||
return multistep.ActionHalt
|
||||
// the "first_boot_device" setting has precedence over the legacy boot order
|
||||
// configuration, but only if its been assigned a value.
|
||||
|
||||
if s.FirstBootDevice == "" {
|
||||
|
||||
if s.Generation > 1 {
|
||||
// only print this message for Gen2, it's not a true statement for Gen1 VMs
|
||||
ui.Say(fmt.Sprintf("Setting boot drive to os dvd drive %s ...", isoPath))
|
||||
}
|
||||
|
||||
err = driver.SetBootDvdDrive(vmName, controllerNumber, controllerLocation, s.Generation)
|
||||
if err != nil {
|
||||
err := fmt.Errorf(errorMsg, err)
|
||||
state.Put("error", err)
|
||||
ui.Error(err.Error())
|
||||
return multistep.ActionHalt
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
ui.Say(fmt.Sprintf("Mounting os dvd drive %s ...", isoPath))
|
||||
|
|
|
@ -0,0 +1,160 @@
|
|||
package common
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"regexp"
|
||||
"strconv"
|
||||
"strings"
|
||||
|
||||
"github.com/hashicorp/packer/helper/multistep"
|
||||
"github.com/hashicorp/packer/packer"
|
||||
)
|
||||
|
||||
type StepSetFirstBootDevice struct {
|
||||
Generation uint
|
||||
FirstBootDevice string
|
||||
}
|
||||
|
||||
func ParseBootDeviceIdentifier(deviceIdentifier string, generation uint) (string, uint, uint, error) {
|
||||
|
||||
// all input strings are forced to upperCase for comparison, I believe this is
|
||||
// safe as all of our values are 7bit ASCII clean.
|
||||
|
||||
lookupDeviceIdentifier := strings.ToUpper(deviceIdentifier)
|
||||
|
||||
if generation == 1 {
|
||||
|
||||
// Gen1 values are a simple set of if/then/else values, which we coalesce into a map
|
||||
// here for simplicity
|
||||
|
||||
lookupTable := map[string]string{
|
||||
"FLOPPY": "FLOPPY",
|
||||
"IDE": "IDE",
|
||||
"NET": "NET",
|
||||
"CD": "CD",
|
||||
"DVD": "CD",
|
||||
}
|
||||
|
||||
controllerType, isDefined := lookupTable[lookupDeviceIdentifier]
|
||||
if !isDefined {
|
||||
|
||||
return "", 0, 0, fmt.Errorf("The value %q is not a properly formatted device group identifier.", deviceIdentifier)
|
||||
|
||||
}
|
||||
|
||||
// success
|
||||
return controllerType, 0, 0, nil
|
||||
}
|
||||
|
||||
// everything else is treated as generation 2... the first set of lookups covers
|
||||
// the simple options..
|
||||
|
||||
lookupTable := map[string]string{
|
||||
"CD": "CD",
|
||||
"DVD": "CD",
|
||||
"NET": "NET",
|
||||
}
|
||||
|
||||
controllerType, isDefined := lookupTable[lookupDeviceIdentifier]
|
||||
if isDefined {
|
||||
|
||||
// these types do not require controllerNumber or controllerLocation
|
||||
return controllerType, 0, 0, nil
|
||||
|
||||
}
|
||||
|
||||
// not a simple option, check for a controllerType:controllerNumber:controllerLocation formatted
|
||||
// device..
|
||||
|
||||
r, err := regexp.Compile(`^(IDE|SCSI):(\d+):(\d+)$`)
|
||||
if err != nil {
|
||||
return "", 0, 0, err
|
||||
}
|
||||
|
||||
controllerMatch := r.FindStringSubmatch(lookupDeviceIdentifier)
|
||||
if controllerMatch != nil {
|
||||
|
||||
var controllerLocation int64
|
||||
var controllerNumber int64
|
||||
|
||||
// NOTE: controllerNumber and controllerLocation cannot be negative, the regex expression
|
||||
// would not have matched if either number was signed
|
||||
|
||||
controllerNumber, err = strconv.ParseInt(controllerMatch[2], 10, 8)
|
||||
if err == nil {
|
||||
|
||||
controllerLocation, err = strconv.ParseInt(controllerMatch[3], 10, 8)
|
||||
if err == nil {
|
||||
|
||||
return controllerMatch[1], uint(controllerNumber), uint(controllerLocation), nil
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
return "", 0, 0, err
|
||||
|
||||
}
|
||||
|
||||
return "", 0, 0, fmt.Errorf("The value %q is not a properly formatted device identifier.", deviceIdentifier)
|
||||
}
|
||||
|
||||
func (s *StepSetFirstBootDevice) Run(ctx context.Context, state multistep.StateBag) multistep.StepAction {
|
||||
|
||||
driver := state.Get("driver").(Driver)
|
||||
ui := state.Get("ui").(packer.Ui)
|
||||
vmName := state.Get("vmName").(string)
|
||||
|
||||
if s.FirstBootDevice != "" {
|
||||
|
||||
controllerType, controllerNumber, controllerLocation, err := ParseBootDeviceIdentifier(s.FirstBootDevice, s.Generation)
|
||||
if err == nil {
|
||||
|
||||
switch {
|
||||
|
||||
case controllerType == "CD":
|
||||
{
|
||||
// the "DVD" controller is special, we only apply the setting if we actually mounted
|
||||
// an ISO and only if that was mounted as the "IsoUrl" not a secondary ISO.
|
||||
|
||||
dvdControllerState := state.Get("os.dvd.properties")
|
||||
if dvdControllerState == nil {
|
||||
|
||||
ui.Say("First Boot Device is DVD, but no primary ISO mounted. Ignoring.")
|
||||
return multistep.ActionContinue
|
||||
|
||||
}
|
||||
|
||||
ui.Say(fmt.Sprintf("Setting boot device to %q", s.FirstBootDevice))
|
||||
dvdController := dvdControllerState.(DvdControllerProperties)
|
||||
err = driver.SetFirstBootDevice(vmName, controllerType, dvdController.ControllerNumber, dvdController.ControllerLocation, s.Generation)
|
||||
|
||||
}
|
||||
|
||||
default:
|
||||
{
|
||||
// anything else, we just pass as is..
|
||||
ui.Say(fmt.Sprintf("Setting boot device to %q", s.FirstBootDevice))
|
||||
err = driver.SetFirstBootDevice(vmName, controllerType, controllerNumber, controllerLocation, s.Generation)
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
if err != nil {
|
||||
err := fmt.Errorf("Error setting first boot device: %s", err)
|
||||
state.Put("error", err)
|
||||
ui.Error(err.Error())
|
||||
return multistep.ActionHalt
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
return multistep.ActionContinue
|
||||
}
|
||||
|
||||
func (s *StepSetFirstBootDevice) Cleanup(state multistep.StateBag) {
|
||||
// do nothing
|
||||
}
|
|
@ -0,0 +1,170 @@
|
|||
package common
|
||||
|
||||
import (
|
||||
"context"
|
||||
"testing"
|
||||
|
||||
"github.com/hashicorp/packer/helper/multistep"
|
||||
)
|
||||
|
||||
type parseBootDeviceIdentifierTest struct {
|
||||
generation uint
|
||||
deviceIdentifier string
|
||||
controllerType string
|
||||
controllerNumber uint
|
||||
controllerLocation uint
|
||||
failInParse bool // true if ParseBootDeviceIdentifier should return an error
|
||||
haltStep bool // true if Step.Run should return Halt action
|
||||
shouldCallSet bool // true if driver.SetFirstBootDevice should have been called
|
||||
setDvdProps bool // true to set DvdDeviceProperties state
|
||||
}
|
||||
|
||||
var parseIdentifierTests = [...]parseBootDeviceIdentifierTest{
|
||||
{1, "IDE", "IDE", 0, 0, false, false, true, false},
|
||||
{1, "idE", "IDE", 0, 0, false, false, true, false},
|
||||
{1, "CD", "CD", 0, 0, false, false, false, false},
|
||||
{1, "CD", "CD", 0, 0, false, false, true, true},
|
||||
{1, "cD", "CD", 0, 0, false, false, false, false},
|
||||
{1, "DVD", "CD", 0, 0, false, false, false, false},
|
||||
{1, "DVD", "CD", 0, 0, false, false, true, true},
|
||||
{1, "Dvd", "CD", 0, 0, false, false, false, false},
|
||||
{1, "FLOPPY", "FLOPPY", 0, 0, false, false, true, false},
|
||||
{1, "FloppY", "FLOPPY", 0, 0, false, false, true, false},
|
||||
{1, "NET", "NET", 0, 0, false, false, true, false},
|
||||
{1, "net", "NET", 0, 0, false, false, true, false},
|
||||
{1, "", "", 0, 0, true, false, false, false},
|
||||
{1, "bad", "", 0, 0, true, true, false, false},
|
||||
{1, "IDE:0:0", "", 0, 0, true, true, true, false},
|
||||
{1, "SCSI:0:0", "", 0, 0, true, true, true, false},
|
||||
{2, "IDE", "", 0, 0, true, true, true, false},
|
||||
{2, "idE", "", 0, 0, true, true, true, false},
|
||||
{2, "CD", "CD", 0, 0, false, false, false, false},
|
||||
{2, "CD", "CD", 0, 0, false, false, true, true},
|
||||
{2, "cD", "CD", 0, 0, false, false, false, false},
|
||||
{2, "DVD", "CD", 0, 0, false, false, false, false},
|
||||
{2, "DVD", "CD", 0, 0, false, false, true, true},
|
||||
{2, "Dvd", "CD", 0, 0, false, false, false, false},
|
||||
{2, "FLOPPY", "", 0, 0, true, true, true, false},
|
||||
{2, "FloppY", "", 0, 0, true, true, true, false},
|
||||
{2, "NET", "NET", 0, 0, false, false, true, false},
|
||||
{2, "net", "NET", 0, 0, false, false, true, false},
|
||||
{2, "", "", 0, 0, true, false, false, false},
|
||||
{2, "bad", "", 0, 0, true, true, false, false},
|
||||
{2, "IDE:0:0", "IDE", 0, 0, false, false, true, false},
|
||||
{2, "SCSI:0:0", "SCSI", 0, 0, false, false, true, false},
|
||||
{2, "Ide:0:0", "IDE", 0, 0, false, false, true, false},
|
||||
{2, "sCsI:0:0", "SCSI", 0, 0, false, false, true, false},
|
||||
{2, "IDEscsi:0:0", "", 0, 0, true, true, false, false},
|
||||
{2, "SCSIide:0:0", "", 0, 0, true, true, false, false},
|
||||
{2, "IDE:0", "", 0, 0, true, true, false, false},
|
||||
{2, "SCSI:0", "", 0, 0, true, true, false, false},
|
||||
{2, "IDE:0:a", "", 0, 0, true, true, false, false},
|
||||
{2, "SCSI:0:a", "", 0, 0, true, true, false, false},
|
||||
{2, "IDE:0:653", "", 0, 0, true, true, false, false},
|
||||
{2, "SCSI:-10:0", "", 0, 0, true, true, false, false},
|
||||
}
|
||||
|
||||
func TestStepSetFirstBootDevice_impl(t *testing.T) {
|
||||
var _ multistep.Step = new(StepSetFirstBootDevice)
|
||||
}
|
||||
|
||||
func TestStepSetFirstBootDevice_ParseIdentifier(t *testing.T) {
|
||||
|
||||
for _, identifierTest := range parseIdentifierTests {
|
||||
|
||||
controllerType, controllerNumber, controllerLocation, err := ParseBootDeviceIdentifier(
|
||||
identifierTest.deviceIdentifier,
|
||||
identifierTest.generation)
|
||||
|
||||
if (err != nil) != identifierTest.failInParse {
|
||||
|
||||
t.Fatalf("Test %q (gen %v): failInParse: %v but err: %v", identifierTest.deviceIdentifier,
|
||||
identifierTest.generation, identifierTest.failInParse, err)
|
||||
|
||||
}
|
||||
|
||||
switch {
|
||||
|
||||
case controllerType != identifierTest.controllerType:
|
||||
t.Fatalf("Test %q (gen %v): controllerType: %q != %q", identifierTest.deviceIdentifier, identifierTest.generation,
|
||||
identifierTest.controllerType, controllerType)
|
||||
|
||||
case controllerNumber != identifierTest.controllerNumber:
|
||||
t.Fatalf("Test %q (gen %v): controllerNumber: %v != %v", identifierTest.deviceIdentifier, identifierTest.generation,
|
||||
identifierTest.controllerNumber, controllerNumber)
|
||||
|
||||
case controllerLocation != identifierTest.controllerLocation:
|
||||
t.Fatalf("Test %q (gen %v): controllerLocation: %v != %v", identifierTest.deviceIdentifier, identifierTest.generation,
|
||||
identifierTest.controllerLocation, controllerLocation)
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func TestStepSetFirstBootDevice(t *testing.T) {
|
||||
|
||||
step := new(StepSetFirstBootDevice)
|
||||
|
||||
for _, identifierTest := range parseIdentifierTests {
|
||||
|
||||
state := testState(t)
|
||||
driver := state.Get("driver").(*DriverMock)
|
||||
|
||||
// requires the vmName state value
|
||||
vmName := "foo"
|
||||
state.Put("vmName", vmName)
|
||||
|
||||
// pretend that we mounted a DVD somewhere (CD:0:0)
|
||||
if identifierTest.setDvdProps {
|
||||
var dvdControllerProperties DvdControllerProperties
|
||||
dvdControllerProperties.ControllerNumber = 0
|
||||
dvdControllerProperties.ControllerLocation = 0
|
||||
dvdControllerProperties.Existing = false
|
||||
state.Put("os.dvd.properties", dvdControllerProperties)
|
||||
}
|
||||
|
||||
step.Generation = identifierTest.generation
|
||||
step.FirstBootDevice = identifierTest.deviceIdentifier
|
||||
|
||||
action := step.Run(context.Background(), state)
|
||||
if (action != multistep.ActionContinue) != identifierTest.haltStep {
|
||||
t.Fatalf("Test %q (gen %v): Bad action: %v", identifierTest.deviceIdentifier, identifierTest.generation, action)
|
||||
}
|
||||
|
||||
if identifierTest.haltStep {
|
||||
|
||||
if _, ok := state.GetOk("error"); !ok {
|
||||
t.Fatalf("Test %q (gen %v): Should have error", identifierTest.deviceIdentifier, identifierTest.generation)
|
||||
}
|
||||
|
||||
// don't perform the remaining checks..
|
||||
continue
|
||||
|
||||
} else {
|
||||
|
||||
if _, ok := state.GetOk("error"); ok {
|
||||
t.Fatalf("Test %q (gen %v): Should NOT have error", identifierTest.deviceIdentifier, identifierTest.generation)
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
if driver.SetFirstBootDevice_Called != identifierTest.shouldCallSet {
|
||||
if identifierTest.shouldCallSet {
|
||||
t.Fatalf("Test %q (gen %v): Should have called SetFirstBootDevice", identifierTest.deviceIdentifier, identifierTest.generation)
|
||||
}
|
||||
|
||||
t.Fatalf("Test %q (gen %v): Should NOT have called SetFirstBootDevice", identifierTest.deviceIdentifier, identifierTest.generation)
|
||||
}
|
||||
|
||||
if (driver.SetFirstBootDevice_Called) &&
|
||||
((driver.SetFirstBootDevice_VmName != vmName) ||
|
||||
(driver.SetFirstBootDevice_ControllerType != identifierTest.controllerType) ||
|
||||
(driver.SetFirstBootDevice_ControllerNumber != identifierTest.controllerNumber) ||
|
||||
(driver.SetFirstBootDevice_ControllerLocation != identifierTest.controllerLocation) ||
|
||||
(driver.SetFirstBootDevice_Generation != identifierTest.generation)) {
|
||||
|
||||
t.Fatalf("Test %q (gen %v): Called SetFirstBootDevice with unexpected arguments.", identifierTest.deviceIdentifier, identifierTest.generation)
|
||||
|
||||
}
|
||||
}
|
||||
}
|
|
@ -242,7 +242,8 @@ func (b *Builder) Run(ctx context.Context, ui packer.Ui, hook packer.Hook) (pack
|
|||
&hypervcommon.StepEnableIntegrationService{},
|
||||
|
||||
&hypervcommon.StepMountDvdDrive{
|
||||
Generation: b.config.Generation,
|
||||
Generation: b.config.Generation,
|
||||
FirstBootDevice: b.config.FirstBootDevice,
|
||||
},
|
||||
&hypervcommon.StepMountFloppydrive{
|
||||
Generation: b.config.Generation,
|
||||
|
@ -264,6 +265,11 @@ func (b *Builder) Run(ctx context.Context, ui packer.Ui, hook packer.Hook) (pack
|
|||
SwitchVlanId: b.config.SwitchVlanId,
|
||||
},
|
||||
|
||||
&hypervcommon.StepSetFirstBootDevice{
|
||||
Generation: b.config.Generation,
|
||||
FirstBootDevice: b.config.FirstBootDevice,
|
||||
},
|
||||
|
||||
&hypervcommon.StepRun{
|
||||
Headless: b.config.Headless,
|
||||
SwitchName: b.config.SwitchName,
|
||||
|
|
|
@ -97,6 +97,7 @@ type FlatConfig struct {
|
|||
SkipCompaction *bool `mapstructure:"skip_compaction" required:"false" cty:"skip_compaction"`
|
||||
SkipExport *bool `mapstructure:"skip_export" required:"false" cty:"skip_export"`
|
||||
Headless *bool `mapstructure:"headless" required:"false" cty:"headless"`
|
||||
FirstBootDevice *string `mapstructure:"first_boot_device" required:"false" cty:"first_boot_device"`
|
||||
ShutdownCommand *string `mapstructure:"shutdown_command" required:"false" cty:"shutdown_command"`
|
||||
ShutdownTimeout *string `mapstructure:"shutdown_timeout" required:"false" cty:"shutdown_timeout"`
|
||||
DiskSize *uint `mapstructure:"disk_size" required:"false" cty:"disk_size"`
|
||||
|
@ -205,6 +206,7 @@ func (*FlatConfig) HCL2Spec() map[string]hcldec.Spec {
|
|||
"skip_compaction": &hcldec.AttrSpec{Name: "skip_compaction", Type: cty.Bool, Required: false},
|
||||
"skip_export": &hcldec.AttrSpec{Name: "skip_export", Type: cty.Bool, Required: false},
|
||||
"headless": &hcldec.AttrSpec{Name: "headless", Type: cty.Bool, Required: false},
|
||||
"first_boot_device": &hcldec.AttrSpec{Name: "first_boot_device", Type: cty.String, Required: false},
|
||||
"shutdown_command": &hcldec.AttrSpec{Name: "shutdown_command", Type: cty.String, Required: false},
|
||||
"shutdown_timeout": &hcldec.AttrSpec{Name: "shutdown_timeout", Type: cty.String, Required: false},
|
||||
"disk_size": &hcldec.AttrSpec{Name: "disk_size", Type: cty.Number, Required: false},
|
||||
|
|
|
@ -282,7 +282,8 @@ func (b *Builder) Run(ctx context.Context, ui packer.Ui, hook packer.Hook) (pack
|
|||
&hypervcommon.StepEnableIntegrationService{},
|
||||
|
||||
&hypervcommon.StepMountDvdDrive{
|
||||
Generation: b.config.Generation,
|
||||
Generation: b.config.Generation,
|
||||
FirstBootDevice: b.config.FirstBootDevice,
|
||||
},
|
||||
&hypervcommon.StepMountFloppydrive{
|
||||
Generation: b.config.Generation,
|
||||
|
@ -304,6 +305,11 @@ func (b *Builder) Run(ctx context.Context, ui packer.Ui, hook packer.Hook) (pack
|
|||
SwitchVlanId: b.config.SwitchVlanId,
|
||||
},
|
||||
|
||||
&hypervcommon.StepSetFirstBootDevice{
|
||||
Generation: b.config.Generation,
|
||||
FirstBootDevice: b.config.FirstBootDevice,
|
||||
},
|
||||
|
||||
&hypervcommon.StepRun{
|
||||
Headless: b.config.Headless,
|
||||
SwitchName: b.config.SwitchName,
|
||||
|
|
|
@ -97,6 +97,7 @@ type FlatConfig struct {
|
|||
SkipCompaction *bool `mapstructure:"skip_compaction" required:"false" cty:"skip_compaction"`
|
||||
SkipExport *bool `mapstructure:"skip_export" required:"false" cty:"skip_export"`
|
||||
Headless *bool `mapstructure:"headless" required:"false" cty:"headless"`
|
||||
FirstBootDevice *string `mapstructure:"first_boot_device" required:"false" cty:"first_boot_device"`
|
||||
ShutdownCommand *string `mapstructure:"shutdown_command" required:"false" cty:"shutdown_command"`
|
||||
ShutdownTimeout *string `mapstructure:"shutdown_timeout" required:"false" cty:"shutdown_timeout"`
|
||||
CloneFromVMCXPath *string `mapstructure:"clone_from_vmcx_path" cty:"clone_from_vmcx_path"`
|
||||
|
@ -207,6 +208,7 @@ func (*FlatConfig) HCL2Spec() map[string]hcldec.Spec {
|
|||
"skip_compaction": &hcldec.AttrSpec{Name: "skip_compaction", Type: cty.Bool, Required: false},
|
||||
"skip_export": &hcldec.AttrSpec{Name: "skip_export", Type: cty.Bool, Required: false},
|
||||
"headless": &hcldec.AttrSpec{Name: "headless", Type: cty.Bool, Required: false},
|
||||
"first_boot_device": &hcldec.AttrSpec{Name: "first_boot_device", Type: cty.String, Required: false},
|
||||
"shutdown_command": &hcldec.AttrSpec{Name: "shutdown_command", Type: cty.String, Required: false},
|
||||
"shutdown_timeout": &hcldec.AttrSpec{Name: "shutdown_timeout", Type: cty.String, Required: false},
|
||||
"clone_from_vmcx_path": &hcldec.AttrSpec{Name: "clone_from_vmcx_path", Type: cty.String, Required: false},
|
||||
|
|
|
@ -28,7 +28,7 @@ type Config struct {
|
|||
ContainerName string `mapstructure:"container_name" required:"false"`
|
||||
// Allows you to specify a wrapper command, such
|
||||
// as ssh so you can execute packer builds on a remote host. Defaults to
|
||||
// Empty.
|
||||
// `{{.Command}}`; i.e. no wrapper.
|
||||
CommandWrapper string `mapstructure:"command_wrapper" required:"false"`
|
||||
// The timeout in seconds to wait for the the
|
||||
// container to start. Defaults to 20 seconds.
|
||||
|
|
|
@ -2,9 +2,9 @@ package lxc
|
|||
|
||||
import (
|
||||
"context"
|
||||
"github.com/hashicorp/packer/common"
|
||||
"log"
|
||||
|
||||
"github.com/hashicorp/packer/common"
|
||||
"github.com/hashicorp/packer/helper/multistep"
|
||||
"github.com/hashicorp/packer/packer"
|
||||
)
|
||||
|
|
|
@ -20,7 +20,8 @@ type Config struct {
|
|||
OutputImage string `mapstructure:"output_image" required:"false"`
|
||||
ContainerName string `mapstructure:"container_name"`
|
||||
// Lets you prefix all builder commands, such as
|
||||
// with ssh for a remote build host. Defaults to "".
|
||||
// with ssh for a remote build host. Defaults to "{{.Command}}"; i.e. no
|
||||
// wrapper.
|
||||
CommandWrapper string `mapstructure:"command_wrapper" required:"false"`
|
||||
// The source image to use when creating the build
|
||||
// container. This can be a (local or remote) image (name or fingerprint).
|
||||
|
|
|
@ -2,9 +2,9 @@ package lxd
|
|||
|
||||
import (
|
||||
"context"
|
||||
"github.com/hashicorp/packer/common"
|
||||
"log"
|
||||
|
||||
"github.com/hashicorp/packer/common"
|
||||
"github.com/hashicorp/packer/helper/multistep"
|
||||
"github.com/hashicorp/packer/packer"
|
||||
)
|
||||
|
|
|
@ -2,9 +2,9 @@ package chroot
|
|||
|
||||
import (
|
||||
"context"
|
||||
"github.com/hashicorp/packer/common"
|
||||
"log"
|
||||
|
||||
"github.com/hashicorp/packer/common"
|
||||
"github.com/hashicorp/packer/helper/multistep"
|
||||
"github.com/hashicorp/packer/packer"
|
||||
)
|
||||
|
|
|
@ -1,9 +1,10 @@
|
|||
package common
|
||||
|
||||
import (
|
||||
"time"
|
||||
|
||||
"github.com/hashicorp/packer/helper/communicator"
|
||||
"github.com/hashicorp/packer/template/interpolate"
|
||||
"time"
|
||||
)
|
||||
|
||||
// SSHConfig contains the configuration for SSH communicator.
|
||||
|
|
|
@ -1,12 +1,13 @@
|
|||
package common
|
||||
|
||||
import (
|
||||
"github.com/hashicorp/packer/helper/communicator"
|
||||
"github.com/hashicorp/packer/template/interpolate"
|
||||
"io/ioutil"
|
||||
"os"
|
||||
"testing"
|
||||
"time"
|
||||
|
||||
"github.com/hashicorp/packer/helper/communicator"
|
||||
"github.com/hashicorp/packer/template/interpolate"
|
||||
)
|
||||
|
||||
func testSSHConfig() *SSHConfig {
|
||||
|
|
|
@ -8,6 +8,7 @@ import (
|
|||
"log"
|
||||
"net/url"
|
||||
"os"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
"github.com/hashicorp/packer/common"
|
||||
|
@ -213,6 +214,9 @@ func (c *Config) Prepare(raws ...interface{}) ([]string, error) {
|
|||
if c.Node == "" {
|
||||
errs = packer.MultiErrorAppend(errs, errors.New("node must be specified"))
|
||||
}
|
||||
if strings.ContainsAny(c.TemplateName, " ") {
|
||||
errs = packer.MultiErrorAppend(errs, errors.New("template_name must not contain spaces"))
|
||||
}
|
||||
for idx := range c.NICs {
|
||||
if c.NICs[idx].Bridge == "" {
|
||||
errs = packer.MultiErrorAppend(errs, errors.New(fmt.Sprintf("network_adapters[%d].bridge must be specified", idx)))
|
||||
|
|
|
@ -281,6 +281,10 @@ type Config struct {
|
|||
// This will also work with WinRM, just change the port forward in
|
||||
// `qemuargs` to map to WinRM's default port of `5985` or whatever value
|
||||
// you have the service set to listen on.
|
||||
//
|
||||
// This is a template engine and allows access to the following variables:
|
||||
// `{{ .HTTPIP }}`, `{{ .HTTPPort }}`, `{{ .HTTPDir }}`,
|
||||
// `{{ .OutputDir }}`, `{{ .Name }}`, and `{{ .SSHHostPort }}`
|
||||
QemuArgs [][]string `mapstructure:"qemuargs" required:"false"`
|
||||
// The name of the Qemu binary to look for. This
|
||||
// defaults to qemu-system-x86_64, but may need to be changed for
|
||||
|
|
|
@ -2,6 +2,7 @@ package qemu
|
|||
|
||||
import (
|
||||
"context"
|
||||
|
||||
"github.com/hashicorp/packer/common"
|
||||
"github.com/hashicorp/packer/helper/multistep"
|
||||
)
|
||||
|
|
|
@ -2,9 +2,10 @@ package qemu
|
|||
|
||||
import (
|
||||
"context"
|
||||
"testing"
|
||||
|
||||
"github.com/hashicorp/packer/common"
|
||||
"github.com/hashicorp/packer/helper/multistep"
|
||||
"testing"
|
||||
)
|
||||
|
||||
func TestStepHTTPIPDiscover_Run(t *testing.T) {
|
||||
|
|
|
@ -8,8 +8,6 @@ import (
|
|||
"os"
|
||||
|
||||
"github.com/hashicorp/packer/template/interpolate"
|
||||
"github.com/tencentcloud/tencentcloud-sdk-go/tencentcloud/common"
|
||||
"github.com/tencentcloud/tencentcloud-sdk-go/tencentcloud/common/profile"
|
||||
cvm "github.com/tencentcloud/tencentcloud-sdk-go/tencentcloud/cvm/v20170312"
|
||||
vpc "github.com/tencentcloud/tencentcloud-sdk-go/tencentcloud/vpc/v20170312"
|
||||
)
|
||||
|
@ -81,17 +79,11 @@ func (cf *TencentCloudAccessConfig) Client() (*cvm.Client, *vpc.Client, error) {
|
|||
return nil, nil, fmt.Errorf("parameter zone must be set")
|
||||
}
|
||||
|
||||
credential := common.NewCredential(cf.SecretId, cf.SecretKey)
|
||||
cpf := profile.NewClientProfile()
|
||||
cpf.HttpProfile.ReqMethod = "POST"
|
||||
cpf.HttpProfile.ReqTimeout = 300
|
||||
cpf.Language = "en-US"
|
||||
|
||||
if cvm_client, err = cvm.NewClient(credential, cf.Region, cpf); err != nil {
|
||||
if cvm_client, err = NewCvmClient(cf.SecretId, cf.SecretKey, cf.Region); err != nil {
|
||||
return nil, nil, err
|
||||
}
|
||||
|
||||
if vpc_client, err = vpc.NewClient(credential, cf.Region, cpf); err != nil {
|
||||
if vpc_client, err = NewVpcClient(cf.SecretId, cf.SecretKey, cf.Region); err != nil {
|
||||
return nil, nil, err
|
||||
}
|
||||
|
||||
|
|
|
@ -10,16 +10,18 @@ import (
|
|||
"github.com/hashicorp/packer/common/retry"
|
||||
"github.com/hashicorp/packer/helper/multistep"
|
||||
"github.com/hashicorp/packer/packer"
|
||||
"github.com/tencentcloud/tencentcloud-sdk-go/tencentcloud/common"
|
||||
"github.com/tencentcloud/tencentcloud-sdk-go/tencentcloud/common/errors"
|
||||
"github.com/tencentcloud/tencentcloud-sdk-go/tencentcloud/common/profile"
|
||||
cvm "github.com/tencentcloud/tencentcloud-sdk-go/tencentcloud/cvm/v20170312"
|
||||
vpc "github.com/tencentcloud/tencentcloud-sdk-go/tencentcloud/vpc/v20170312"
|
||||
)
|
||||
|
||||
// DefaultWaitForInterval is sleep interval when wait statue
|
||||
const DefaultWaitForInterval = 5
|
||||
|
||||
// WaitForInstance wait for instance reaches statue
|
||||
func WaitForInstance(client *cvm.Client, instanceId string, status string, timeout int) error {
|
||||
ctx := context.TODO()
|
||||
func WaitForInstance(ctx context.Context, client *cvm.Client, instanceId string, status string, timeout int) error {
|
||||
req := cvm.NewDescribeInstancesRequest()
|
||||
req.InstanceIds = []*string{&instanceId}
|
||||
|
||||
|
@ -50,54 +52,86 @@ func WaitForInstance(client *cvm.Client, instanceId string, status string, timeo
|
|||
}
|
||||
|
||||
// WaitForImageReady wait for image reaches statue
|
||||
func WaitForImageReady(client *cvm.Client, imageName string, status string, timeout int) error {
|
||||
ctx := context.TODO()
|
||||
req := cvm.NewDescribeImagesRequest()
|
||||
FILTER_IMAGE_NAME := "image-name"
|
||||
req.Filters = []*cvm.Filter{
|
||||
{
|
||||
Name: &FILTER_IMAGE_NAME,
|
||||
Values: []*string{&imageName},
|
||||
},
|
||||
}
|
||||
|
||||
func WaitForImageReady(ctx context.Context, client *cvm.Client, imageName string, status string, timeout int) error {
|
||||
for {
|
||||
var resp *cvm.DescribeImagesResponse
|
||||
err := Retry(ctx, func(ctx context.Context) error {
|
||||
var e error
|
||||
resp, e = client.DescribeImages(req)
|
||||
return e
|
||||
})
|
||||
image, err := GetImageByName(ctx, client, imageName)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
find := false
|
||||
for _, image := range resp.Response.ImageSet {
|
||||
if *image.ImageName == imageName && *image.ImageState == status {
|
||||
find = true
|
||||
break
|
||||
}
|
||||
}
|
||||
if find {
|
||||
break
|
||||
|
||||
if image != nil && *image.ImageState == status {
|
||||
return nil
|
||||
}
|
||||
|
||||
time.Sleep(DefaultWaitForInterval * time.Second)
|
||||
timeout = timeout - DefaultWaitForInterval
|
||||
if timeout <= 0 {
|
||||
return fmt.Errorf("wait image(%s) status(%s) timeout", imageName, status)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return nil
|
||||
// GetImageByName get image by image name
|
||||
func GetImageByName(ctx context.Context, client *cvm.Client, imageName string) (*cvm.Image, error) {
|
||||
req := cvm.NewDescribeImagesRequest()
|
||||
req.Filters = []*cvm.Filter{
|
||||
{
|
||||
Name: common.StringPtr("image-name"),
|
||||
Values: []*string{&imageName},
|
||||
},
|
||||
}
|
||||
|
||||
var resp *cvm.DescribeImagesResponse
|
||||
err := Retry(ctx, func(ctx context.Context) error {
|
||||
var e error
|
||||
resp, e = client.DescribeImages(req)
|
||||
return e
|
||||
})
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
if *resp.Response.TotalCount > 0 {
|
||||
for _, image := range resp.Response.ImageSet {
|
||||
if *image.ImageName == imageName {
|
||||
return image, nil
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return nil, nil
|
||||
}
|
||||
|
||||
// NewCvmClient returns a new cvm client
|
||||
func NewCvmClient(secretId, secretKey, region string) (client *cvm.Client, err error) {
|
||||
cpf := profile.NewClientProfile()
|
||||
cpf.HttpProfile.ReqMethod = "POST"
|
||||
cpf.HttpProfile.ReqTimeout = 300
|
||||
cpf.Language = "en-US"
|
||||
|
||||
credential := common.NewCredential(secretId, secretKey)
|
||||
client, err = cvm.NewClient(credential, region, cpf)
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
// NewVpcClient returns a new vpc client
|
||||
func NewVpcClient(secretId, secretKey, region string) (client *vpc.Client, err error) {
|
||||
cpf := profile.NewClientProfile()
|
||||
cpf.HttpProfile.ReqMethod = "POST"
|
||||
cpf.HttpProfile.ReqTimeout = 300
|
||||
cpf.Language = "en-US"
|
||||
|
||||
credential := common.NewCredential(secretId, secretKey)
|
||||
client, err = vpc.NewClient(credential, region, cpf)
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
// CheckResourceIdFormat check resource id format
|
||||
func CheckResourceIdFormat(resource string, id string) bool {
|
||||
regex := regexp.MustCompile(fmt.Sprintf("%s-[0-9a-z]{8}$", resource))
|
||||
if !regex.MatchString(id) {
|
||||
return false
|
||||
}
|
||||
return true
|
||||
return regex.MatchString(id)
|
||||
}
|
||||
|
||||
// SSHHost returns a function that can be given to the SSH communicator
|
||||
|
|
|
@ -2,6 +2,7 @@ package cvm
|
|||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"strings"
|
||||
|
||||
"github.com/hashicorp/packer/helper/multistep"
|
||||
|
@ -19,6 +20,7 @@ func (s *stepCopyImage) Run(ctx context.Context, state multistep.StateBag) multi
|
|||
return multistep.ActionContinue
|
||||
}
|
||||
|
||||
config := state.Get("config").(*Config)
|
||||
client := state.Get("cvm_client").(*cvm.Client)
|
||||
|
||||
imageId := state.Get("image").(*cvm.Image).ImageId
|
||||
|
@ -43,6 +45,34 @@ func (s *stepCopyImage) Run(ctx context.Context, state multistep.StateBag) multi
|
|||
return Halt(state, err, "Failed to copy image")
|
||||
}
|
||||
|
||||
Message(state, "Waiting for image ready", "")
|
||||
tencentCloudImages := state.Get("tencentcloudimages").(map[string]string)
|
||||
|
||||
for _, region := range req.DestinationRegions {
|
||||
rc, err := NewCvmClient(config.SecretId, config.SecretKey, *region)
|
||||
if err != nil {
|
||||
return Halt(state, err, "Failed to init client")
|
||||
}
|
||||
|
||||
err = WaitForImageReady(ctx, rc, config.ImageName, "NORMAL", 1800)
|
||||
if err != nil {
|
||||
return Halt(state, err, "Failed to wait for image ready")
|
||||
}
|
||||
|
||||
image, err := GetImageByName(ctx, rc, config.ImageName)
|
||||
if err != nil {
|
||||
return Halt(state, err, "Failed to get image")
|
||||
}
|
||||
|
||||
if image == nil {
|
||||
return Halt(state, err, "Failed to wait for image ready")
|
||||
}
|
||||
|
||||
tencentCloudImages[*region] = *image.ImageId
|
||||
Message(state, fmt.Sprintf("Copy image from %s(%s) to %s(%s)", s.SourceRegion, *imageId, *region, *image.ImageId), "")
|
||||
}
|
||||
|
||||
state.Put("tencentcloudimages", tencentCloudImages)
|
||||
Message(state, "Image copied", "")
|
||||
|
||||
return multistep.ActionContinue
|
||||
|
|
|
@ -58,36 +58,22 @@ func (s *stepCreateImage) Run(ctx context.Context, state multistep.StateBag) mul
|
|||
}
|
||||
|
||||
Message(state, "Waiting for image ready", "")
|
||||
err = WaitForImageReady(client, config.ImageName, "NORMAL", 3600)
|
||||
err = WaitForImageReady(ctx, client, config.ImageName, "NORMAL", 3600)
|
||||
if err != nil {
|
||||
return Halt(state, err, "Failed to wait for image ready")
|
||||
}
|
||||
|
||||
describeReq := cvm.NewDescribeImagesRequest()
|
||||
FILTER_IMAGE_NAME := "image-name"
|
||||
describeReq.Filters = []*cvm.Filter{
|
||||
{
|
||||
Name: &FILTER_IMAGE_NAME,
|
||||
Values: []*string{&config.ImageName},
|
||||
},
|
||||
}
|
||||
|
||||
var describeResp *cvm.DescribeImagesResponse
|
||||
err = Retry(ctx, func(ctx context.Context) error {
|
||||
var e error
|
||||
describeResp, e = client.DescribeImages(describeReq)
|
||||
return e
|
||||
})
|
||||
image, err := GetImageByName(ctx, client, config.ImageName)
|
||||
if err != nil {
|
||||
return Halt(state, err, "Failed to wait for image ready")
|
||||
return Halt(state, err, "Failed to get image")
|
||||
}
|
||||
|
||||
if *describeResp.Response.TotalCount == 0 {
|
||||
if image == nil {
|
||||
return Halt(state, fmt.Errorf("No image return"), "Failed to crate image")
|
||||
}
|
||||
|
||||
s.imageId = *describeResp.Response.ImageSet[0].ImageId
|
||||
state.Put("image", describeResp.Response.ImageSet[0])
|
||||
s.imageId = *image.ImageId
|
||||
state.Put("image", image)
|
||||
Message(state, s.imageId, "Image created")
|
||||
|
||||
tencentCloudImages := make(map[string]string)
|
||||
|
|
|
@ -5,7 +5,6 @@ import (
|
|||
"fmt"
|
||||
|
||||
"github.com/hashicorp/packer/helper/multistep"
|
||||
"github.com/tencentcloud/tencentcloud-sdk-go/tencentcloud/common"
|
||||
cvm "github.com/tencentcloud/tencentcloud-sdk-go/tencentcloud/cvm/v20170312"
|
||||
)
|
||||
|
||||
|
@ -18,24 +17,12 @@ func (s *stepPreValidate) Run(ctx context.Context, state multistep.StateBag) mul
|
|||
|
||||
Say(state, config.ImageName, "Trying to check image name")
|
||||
|
||||
req := cvm.NewDescribeImagesRequest()
|
||||
req.Filters = []*cvm.Filter{
|
||||
{
|
||||
Name: common.StringPtr("image-name"),
|
||||
Values: []*string{&config.ImageName},
|
||||
},
|
||||
}
|
||||
var resp *cvm.DescribeImagesResponse
|
||||
err := Retry(ctx, func(ctx context.Context) error {
|
||||
var err error
|
||||
resp, err = client.DescribeImages(req)
|
||||
return err
|
||||
})
|
||||
image, err := GetImageByName(ctx, client, config.ImageName)
|
||||
if err != nil {
|
||||
return Halt(state, err, "Failed to get images info")
|
||||
}
|
||||
|
||||
if *resp.Response.TotalCount > 0 {
|
||||
if image != nil {
|
||||
return Halt(state, fmt.Errorf("Image name %s has exists", config.ImageName), "")
|
||||
}
|
||||
|
||||
|
|
|
@ -157,7 +157,7 @@ func (s *stepRunInstance) Run(ctx context.Context, state multistep.StateBag) mul
|
|||
s.instanceId = *resp.Response.InstanceIdSet[0]
|
||||
Message(state, "Waiting for instance ready", "")
|
||||
|
||||
err = WaitForInstance(client, s.instanceId, "RUNNING", 1800)
|
||||
err = WaitForInstance(ctx, client, s.instanceId, "RUNNING", 1800)
|
||||
if err != nil {
|
||||
return Halt(state, err, "Failed to wait for instance ready")
|
||||
}
|
||||
|
|
|
@ -0,0 +1,88 @@
|
|||
package vagrant
|
||||
|
||||
// Create a mock driver so that we can test Vagrant builder steps
|
||||
type MockVagrantDriver struct {
|
||||
InitCalled bool
|
||||
AddCalled bool
|
||||
UpCalled bool
|
||||
HaltCalled bool
|
||||
SuspendCalled bool
|
||||
SSHConfigCalled bool
|
||||
DestroyCalled bool
|
||||
PackageCalled bool
|
||||
VerifyCalled bool
|
||||
VersionCalled bool
|
||||
|
||||
ReturnError error
|
||||
ReturnSSHConfig *VagrantSSHConfig
|
||||
GlobalID string
|
||||
}
|
||||
|
||||
func (d *MockVagrantDriver) Init([]string) error {
|
||||
d.InitCalled = true
|
||||
return d.ReturnError
|
||||
}
|
||||
|
||||
func (d *MockVagrantDriver) Add([]string) error {
|
||||
d.AddCalled = true
|
||||
return d.ReturnError
|
||||
}
|
||||
|
||||
func (d *MockVagrantDriver) Up([]string) (string, string, error) {
|
||||
d.UpCalled = true
|
||||
return "", "", nil
|
||||
}
|
||||
|
||||
func (d *MockVagrantDriver) Halt(string) error {
|
||||
d.HaltCalled = true
|
||||
return d.ReturnError
|
||||
}
|
||||
|
||||
func (d *MockVagrantDriver) Suspend(string) error {
|
||||
d.SuspendCalled = true
|
||||
return d.ReturnError
|
||||
}
|
||||
|
||||
func (d *MockVagrantDriver) SSHConfig(gid string) (*VagrantSSHConfig, error) {
|
||||
d.SSHConfigCalled = true
|
||||
// track the input value
|
||||
d.GlobalID = gid
|
||||
|
||||
if d.ReturnSSHConfig != nil {
|
||||
return d.ReturnSSHConfig, nil
|
||||
}
|
||||
|
||||
sshConfig := VagrantSSHConfig{
|
||||
Hostname: "127.0.0.1",
|
||||
User: "vagrant",
|
||||
Port: "2222",
|
||||
UserKnownHostsFile: "/dev/null",
|
||||
StrictHostKeyChecking: false,
|
||||
PasswordAuthentication: false,
|
||||
IdentityFile: "\"/path with spaces/insecure_private_key\"",
|
||||
IdentitiesOnly: true,
|
||||
LogLevel: "FATAL"}
|
||||
return &sshConfig, d.ReturnError
|
||||
}
|
||||
|
||||
func (d *MockVagrantDriver) Destroy(string) error {
|
||||
d.DestroyCalled = true
|
||||
return d.ReturnError
|
||||
}
|
||||
|
||||
func (d *MockVagrantDriver) Package([]string) error {
|
||||
d.PackageCalled = true
|
||||
return d.ReturnError
|
||||
}
|
||||
|
||||
func (d *MockVagrantDriver) Verify() error {
|
||||
d.VerifyCalled = true
|
||||
return d.ReturnError
|
||||
}
|
||||
|
||||
func (d *MockVagrantDriver) Version() (string, error) {
|
||||
d.VersionCalled = true
|
||||
return "", d.ReturnError
|
||||
}
|
||||
|
||||
// End of mock definition
|
|
@ -56,9 +56,9 @@ func (s *StepSSHConfig) Run(ctx context.Context, state multistep.StateBag) multi
|
|||
}
|
||||
log.Printf("identity file is %s", sshConfig.IdentityFile)
|
||||
log.Printf("Removing quotes from identity file")
|
||||
sshConfig.IdentityFile, err = strconv.Unquote(sshConfig.IdentityFile)
|
||||
if err != nil {
|
||||
log.Printf("Error unquoting identity file: %s", err)
|
||||
unquoted, err := strconv.Unquote(sshConfig.IdentityFile)
|
||||
if err == nil {
|
||||
sshConfig.IdentityFile = unquoted
|
||||
}
|
||||
config.Comm.SSHPrivateKeyFile = sshConfig.IdentityFile
|
||||
config.Comm.SSHUsername = sshConfig.User
|
||||
|
|
|
@ -0,0 +1,98 @@
|
|||
package vagrant
|
||||
|
||||
import (
|
||||
"context"
|
||||
"testing"
|
||||
|
||||
"github.com/hashicorp/packer/helper/multistep"
|
||||
)
|
||||
|
||||
func TestStepSSHConfig_Impl(t *testing.T) {
|
||||
var raw interface{}
|
||||
raw = new(StepSSHConfig)
|
||||
if _, ok := raw.(multistep.Step); !ok {
|
||||
t.Fatalf("initialize should be a step")
|
||||
}
|
||||
}
|
||||
|
||||
func TestPrepStepSSHConfig_GlobalID(t *testing.T) {
|
||||
driver := &MockVagrantDriver{}
|
||||
config := &Config{}
|
||||
state := new(multistep.BasicStateBag)
|
||||
state.Put("driver", driver)
|
||||
state.Put("config", config)
|
||||
|
||||
step := StepSSHConfig{
|
||||
GlobalID: "adsfadf",
|
||||
}
|
||||
_ = step.Run(context.Background(), state)
|
||||
if driver.GlobalID != "adsfadf" {
|
||||
t.Fatalf("Should have called SSHConfig with GlobalID asdfasdf")
|
||||
}
|
||||
}
|
||||
|
||||
func TestPrepStepSSHConfig_NoGlobalID(t *testing.T) {
|
||||
driver := &MockVagrantDriver{}
|
||||
config := &Config{}
|
||||
state := new(multistep.BasicStateBag)
|
||||
state.Put("driver", driver)
|
||||
state.Put("config", config)
|
||||
|
||||
step := StepSSHConfig{}
|
||||
_ = step.Run(context.Background(), state)
|
||||
if driver.GlobalID != "source" {
|
||||
t.Fatalf("Should have called SSHConfig with GlobalID source")
|
||||
}
|
||||
}
|
||||
|
||||
func TestPrepStepSSHConfig_SpacesInPath(t *testing.T) {
|
||||
driver := &MockVagrantDriver{}
|
||||
driver.ReturnSSHConfig = &VagrantSSHConfig{
|
||||
Hostname: "127.0.0.1",
|
||||
User: "vagrant",
|
||||
Port: "2222",
|
||||
UserKnownHostsFile: "/dev/null",
|
||||
StrictHostKeyChecking: false,
|
||||
PasswordAuthentication: false,
|
||||
IdentityFile: "\"/path with spaces/insecure_private_key\"",
|
||||
IdentitiesOnly: true,
|
||||
LogLevel: "FATAL"}
|
||||
|
||||
config := &Config{}
|
||||
state := new(multistep.BasicStateBag)
|
||||
state.Put("driver", driver)
|
||||
state.Put("config", config)
|
||||
|
||||
step := StepSSHConfig{}
|
||||
_ = step.Run(context.Background(), state)
|
||||
expected := "/path with spaces/insecure_private_key"
|
||||
if config.Comm.SSHPrivateKeyFile != expected {
|
||||
t.Fatalf("Bad config private key. Recieved: %s; expected: %s.", config.Comm.SSHPrivateKeyFile, expected)
|
||||
}
|
||||
}
|
||||
|
||||
func TestPrepStepSSHConfig_NoSpacesInPath(t *testing.T) {
|
||||
driver := &MockVagrantDriver{}
|
||||
driver.ReturnSSHConfig = &VagrantSSHConfig{
|
||||
Hostname: "127.0.0.1",
|
||||
User: "vagrant",
|
||||
Port: "2222",
|
||||
UserKnownHostsFile: "/dev/null",
|
||||
StrictHostKeyChecking: false,
|
||||
PasswordAuthentication: false,
|
||||
IdentityFile: "/path/without/spaces/insecure_private_key",
|
||||
IdentitiesOnly: true,
|
||||
LogLevel: "FATAL"}
|
||||
|
||||
config := &Config{}
|
||||
state := new(multistep.BasicStateBag)
|
||||
state.Put("driver", driver)
|
||||
state.Put("config", config)
|
||||
|
||||
step := StepSSHConfig{}
|
||||
_ = step.Run(context.Background(), state)
|
||||
expected := "/path/without/spaces/insecure_private_key"
|
||||
if config.Comm.SSHPrivateKeyFile != expected {
|
||||
t.Fatalf("Bad config private key. Recieved: %s; expected: %s.", config.Comm.SSHPrivateKeyFile, expected)
|
||||
}
|
||||
}
|
|
@ -57,8 +57,8 @@ type Config struct {
|
|||
// The path on the guest virtual machine where the VirtualBox guest
|
||||
// additions ISO will be uploaded. By default this is
|
||||
// VBoxGuestAdditions.iso which should upload into the login directory of
|
||||
// the user. This is a configuration template where the Version variable is
|
||||
// replaced with the VirtualBox version.
|
||||
// the user. This is a configuration template where the `{{ .Version }}`
|
||||
// variable is replaced with the VirtualBox version.
|
||||
GuestAdditionsPath string `mapstructure:"guest_additions_path" required:"false"`
|
||||
// The SHA256 checksum of the guest additions ISO that will be uploaded to
|
||||
// the guest VM. By default the checksums will be downloaded from the
|
||||
|
@ -69,7 +69,8 @@ type Config struct {
|
|||
// URL if the ISO is at a local path. By default, the VirtualBox builder
|
||||
// will attempt to find the guest additions ISO on the local file system.
|
||||
// If it is not available locally, the builder will download the proper
|
||||
// guest additions ISO from the internet.
|
||||
// guest additions ISO from the internet. This is a template engine, and you
|
||||
// have access to the variable `{{ .Version }}`.
|
||||
GuestAdditionsURL string `mapstructure:"guest_additions_url" required:"false"`
|
||||
// The interface type to use to mount guest additions when
|
||||
// guest_additions_mode is set to attach. Will default to the value set in
|
||||
|
|
|
@ -3,10 +3,11 @@ package common
|
|||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"log"
|
||||
|
||||
"github.com/hashicorp/packer/common"
|
||||
"github.com/hashicorp/packer/helper/multistep"
|
||||
"github.com/hashicorp/packer/packer"
|
||||
"log"
|
||||
)
|
||||
|
||||
// Step to discover the http ip
|
||||
|
|
|
@ -3,9 +3,10 @@ package common
|
|||
import (
|
||||
"context"
|
||||
"errors"
|
||||
"testing"
|
||||
|
||||
"github.com/hashicorp/packer/common"
|
||||
"github.com/hashicorp/packer/helper/multistep"
|
||||
"testing"
|
||||
)
|
||||
|
||||
func TestStepHTTPIPDiscover_Run(t *testing.T) {
|
||||
|
|
|
@ -92,7 +92,9 @@ type Config struct {
|
|||
// set in the VMware VMX. By default this is other. By specifying a more
|
||||
// specific OS type, VMware may perform some optimizations or virtual hardware
|
||||
// changes to better support the operating system running in the
|
||||
// virtual machine.
|
||||
// virtual machine. Valid values differ by platform and version numbers, and may
|
||||
// not match other VMware API's representation of the guest OS names. Consult your
|
||||
// platform for valid values.
|
||||
GuestOSType string `mapstructure:"guest_os_type" required:"false"`
|
||||
// The [vmx hardware
|
||||
// version](http://kb.vmware.com/selfservice/microsites/search.do?language=en_US&cmd=displayKC&externalId=1003746)
|
||||
|
@ -106,8 +108,11 @@ type Config struct {
|
|||
|
||||
VMXDiskTemplatePath string `mapstructure:"vmx_disk_template_path"`
|
||||
// Path to a [configuration template](/docs/templates/engine.html) that
|
||||
// defines the contents of the virtual machine VMX file for VMware. This is
|
||||
// for **advanced users only** as this can render the virtual machine
|
||||
// defines the contents of the virtual machine VMX file for VMware. The
|
||||
// engine has access to the template variables `{{ .DiskNumber }}` and
|
||||
// `{{ .DiskName }}`.
|
||||
//
|
||||
// This is for **advanced users only** as this can render the virtual machine
|
||||
// non-functional. See below for more information. For basic VMX
|
||||
// modifications, try `vmx_data` first.
|
||||
VMXTemplatePath string `mapstructure:"vmx_template_path" required:"false"`
|
||||
|
|
|
@ -2,6 +2,7 @@ package clone
|
|||
|
||||
import (
|
||||
"context"
|
||||
|
||||
"github.com/hashicorp/hcl/v2/hcldec"
|
||||
"github.com/hashicorp/packer/builder/vsphere/common"
|
||||
"github.com/hashicorp/packer/builder/vsphere/driver"
|
||||
|
|
|
@ -1,13 +1,14 @@
|
|||
package clone
|
||||
|
||||
import (
|
||||
"os"
|
||||
"testing"
|
||||
|
||||
"github.com/hashicorp/packer/builder/vsphere/common"
|
||||
commonT "github.com/hashicorp/packer/builder/vsphere/common/testing"
|
||||
builderT "github.com/hashicorp/packer/helper/builder/testing"
|
||||
"github.com/hashicorp/packer/packer"
|
||||
"github.com/vmware/govmomi/vim25/types"
|
||||
"os"
|
||||
"testing"
|
||||
)
|
||||
|
||||
func TestCloneBuilderAcc_default(t *testing.T) {
|
||||
|
|
|
@ -1,8 +1,9 @@
|
|||
package clone
|
||||
|
||||
import (
|
||||
"github.com/hashicorp/packer/packer"
|
||||
"testing"
|
||||
|
||||
"github.com/hashicorp/packer/packer"
|
||||
)
|
||||
|
||||
func TestCloneBuilder_ImplementsBuilder(t *testing.T) {
|
||||
|
|
|
@ -6,6 +6,7 @@ package clone
|
|||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
|
||||
"github.com/hashicorp/packer/builder/vsphere/common"
|
||||
"github.com/hashicorp/packer/builder/vsphere/driver"
|
||||
"github.com/hashicorp/packer/helper/multistep"
|
||||
|
|
|
@ -6,6 +6,7 @@ package common
|
|||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
|
||||
"github.com/hashicorp/packer/builder/vsphere/driver"
|
||||
"github.com/hashicorp/packer/helper/multistep"
|
||||
"github.com/hashicorp/packer/packer"
|
||||
|
|
|
@ -6,6 +6,7 @@ package common
|
|||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
|
||||
"github.com/hashicorp/packer/builder/vsphere/driver"
|
||||
"github.com/hashicorp/packer/helper/multistep"
|
||||
)
|
||||
|
|
|
@ -6,6 +6,7 @@ package common
|
|||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
|
||||
"github.com/hashicorp/packer/builder/vsphere/driver"
|
||||
"github.com/hashicorp/packer/helper/multistep"
|
||||
"github.com/hashicorp/packer/packer"
|
||||
|
|
|
@ -5,10 +5,11 @@ package common
|
|||
|
||||
import (
|
||||
"context"
|
||||
"strings"
|
||||
|
||||
"github.com/hashicorp/packer/builder/vsphere/driver"
|
||||
"github.com/hashicorp/packer/helper/multistep"
|
||||
"github.com/hashicorp/packer/packer"
|
||||
"strings"
|
||||
)
|
||||
|
||||
type RunConfig struct {
|
||||
|
|
|
@ -7,11 +7,12 @@ import (
|
|||
"bytes"
|
||||
"context"
|
||||
"fmt"
|
||||
"log"
|
||||
"time"
|
||||
|
||||
"github.com/hashicorp/packer/builder/vsphere/driver"
|
||||
"github.com/hashicorp/packer/helper/multistep"
|
||||
"github.com/hashicorp/packer/packer"
|
||||
"log"
|
||||
"time"
|
||||
)
|
||||
|
||||
type ShutdownConfig struct {
|
||||
|
|
|
@ -2,6 +2,7 @@ package common
|
|||
|
||||
import (
|
||||
"context"
|
||||
|
||||
"github.com/hashicorp/packer/builder/vsphere/driver"
|
||||
"github.com/hashicorp/packer/helper/multistep"
|
||||
"github.com/hashicorp/packer/packer"
|
||||
|
|
|
@ -2,6 +2,7 @@ package common
|
|||
|
||||
import (
|
||||
"context"
|
||||
|
||||
"github.com/hashicorp/packer/builder/vsphere/driver"
|
||||
"github.com/hashicorp/packer/helper/multistep"
|
||||
"github.com/hashicorp/packer/packer"
|
||||
|
|
|
@ -6,11 +6,12 @@ package common
|
|||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"log"
|
||||
"time"
|
||||
|
||||
"github.com/hashicorp/packer/builder/vsphere/driver"
|
||||
"github.com/hashicorp/packer/helper/multistep"
|
||||
"github.com/hashicorp/packer/packer"
|
||||
"log"
|
||||
"time"
|
||||
)
|
||||
|
||||
type WaitIpConfig struct {
|
||||
|
|
|
@ -3,13 +3,14 @@ package testing
|
|||
import (
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"github.com/hashicorp/packer/builder/vsphere/common"
|
||||
"github.com/hashicorp/packer/builder/vsphere/driver"
|
||||
"github.com/hashicorp/packer/packer"
|
||||
"math/rand"
|
||||
"os"
|
||||
"testing"
|
||||
"time"
|
||||
|
||||
"github.com/hashicorp/packer/builder/vsphere/common"
|
||||
"github.com/hashicorp/packer/builder/vsphere/driver"
|
||||
"github.com/hashicorp/packer/packer"
|
||||
)
|
||||
|
||||
func NewVMName() string {
|
||||
|
|
|
@ -2,6 +2,7 @@ package driver
|
|||
|
||||
import (
|
||||
"fmt"
|
||||
|
||||
"github.com/vmware/govmomi/object"
|
||||
"github.com/vmware/govmomi/vim25/mo"
|
||||
"github.com/vmware/govmomi/vim25/soap"
|
||||
|
|
|
@ -3,14 +3,15 @@ package driver
|
|||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"net/url"
|
||||
"time"
|
||||
|
||||
"github.com/vmware/govmomi"
|
||||
"github.com/vmware/govmomi/find"
|
||||
"github.com/vmware/govmomi/object"
|
||||
"github.com/vmware/govmomi/session"
|
||||
"github.com/vmware/govmomi/vim25"
|
||||
"github.com/vmware/govmomi/vim25/soap"
|
||||
"net/url"
|
||||
"time"
|
||||
)
|
||||
|
||||
type Driver struct {
|
||||
|
|
|
@ -2,6 +2,7 @@ package driver
|
|||
|
||||
import (
|
||||
"fmt"
|
||||
|
||||
"github.com/vmware/govmomi/object"
|
||||
"github.com/vmware/govmomi/vim25/mo"
|
||||
"github.com/vmware/govmomi/vim25/types"
|
||||
|
|
|
@ -1,6 +1,8 @@
|
|||
package driver
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
|
||||
"github.com/vmware/govmomi/object"
|
||||
"github.com/vmware/govmomi/vim25/mo"
|
||||
"github.com/vmware/govmomi/vim25/types"
|
||||
|
@ -8,7 +10,7 @@ import (
|
|||
|
||||
type Network struct {
|
||||
driver *Driver
|
||||
network *object.Network
|
||||
network object.NetworkReference
|
||||
}
|
||||
|
||||
func (d *Driver) NewNetwork(ref *types.ManagedObjectReference) *Network {
|
||||
|
@ -24,7 +26,7 @@ func (d *Driver) FindNetwork(name string) (*Network, error) {
|
|||
return nil, err
|
||||
}
|
||||
return &Network{
|
||||
network: n.(*object.Network),
|
||||
network: n,
|
||||
driver: d,
|
||||
}, nil
|
||||
}
|
||||
|
@ -37,7 +39,13 @@ func (n *Network) Info(params ...string) (*mo.Network, error) {
|
|||
p = params
|
||||
}
|
||||
var info mo.Network
|
||||
err := n.network.Properties(n.driver.ctx, n.network.Reference(), p, &info)
|
||||
|
||||
network, ok := n.network.(*object.Network)
|
||||
if !ok {
|
||||
return nil, fmt.Errorf("unexpected %t network object type", n.network)
|
||||
}
|
||||
|
||||
err := network.Properties(n.driver.ctx, network.Reference(), p, &info)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
|
|
@ -2,6 +2,7 @@ package driver
|
|||
|
||||
import (
|
||||
"fmt"
|
||||
|
||||
"github.com/vmware/govmomi/object"
|
||||
"github.com/vmware/govmomi/vim25/mo"
|
||||
"github.com/vmware/govmomi/vim25/types"
|
||||
|
|
|
@ -4,12 +4,13 @@ import (
|
|||
"context"
|
||||
"errors"
|
||||
"fmt"
|
||||
"github.com/vmware/govmomi/object"
|
||||
"github.com/vmware/govmomi/vim25/mo"
|
||||
"github.com/vmware/govmomi/vim25/types"
|
||||
"log"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
"github.com/vmware/govmomi/object"
|
||||
"github.com/vmware/govmomi/vim25/mo"
|
||||
"github.com/vmware/govmomi/vim25/types"
|
||||
)
|
||||
|
||||
type VirtualMachine struct {
|
||||
|
@ -43,8 +44,16 @@ type HardwareConfig struct {
|
|||
VideoRAM int64
|
||||
}
|
||||
|
||||
type NIC struct {
|
||||
Network string // "" for default network
|
||||
NetworkCard string // example: vmxnet3
|
||||
MacAddress string // set mac if want specific address
|
||||
Passthrough *bool // direct path i/o
|
||||
}
|
||||
|
||||
type CreateConfig struct {
|
||||
DiskThinProvisioned bool
|
||||
DiskEagerlyScrub bool
|
||||
DiskControllerType string // example: "scsi", "pvscsi"
|
||||
DiskSize int64
|
||||
|
||||
|
@ -56,8 +65,7 @@ type CreateConfig struct {
|
|||
ResourcePool string
|
||||
Datastore string
|
||||
GuestOS string // example: otherGuest
|
||||
Network string // "" for default network
|
||||
NetworkCard string // example: vmxnet3
|
||||
NICs []NIC
|
||||
USBController bool
|
||||
Version uint // example: 10
|
||||
Firmware string // efi or bios
|
||||
|
@ -496,6 +504,7 @@ func addDisk(_ *Driver, devices object.VirtualDeviceList, config *CreateConfig)
|
|||
Backing: &types.VirtualDiskFlatVer2BackingInfo{
|
||||
DiskMode: string(types.VirtualDiskModePersistent),
|
||||
ThinProvisioned: types.NewBool(config.DiskThinProvisioned),
|
||||
EagerlyScrub: types.NewBool(config.DiskEagerlyScrub),
|
||||
},
|
||||
},
|
||||
CapacityInKB: config.DiskSize * 1024,
|
||||
|
@ -508,42 +517,56 @@ func addDisk(_ *Driver, devices object.VirtualDeviceList, config *CreateConfig)
|
|||
}
|
||||
|
||||
func addNetwork(d *Driver, devices object.VirtualDeviceList, config *CreateConfig) (object.VirtualDeviceList, error) {
|
||||
if len(config.NICs) == 0 {
|
||||
return nil, errors.New("no network adapters have been defined")
|
||||
}
|
||||
|
||||
var network object.NetworkReference
|
||||
if config.Network == "" {
|
||||
h, err := d.FindHost(config.Host)
|
||||
for _, nic := range config.NICs {
|
||||
if nic.Network == "" {
|
||||
h, err := d.FindHost(config.Host)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
i, err := h.Info("network")
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
if len(i.Network) > 1 {
|
||||
return nil, fmt.Errorf("Host has multiple networks. Specify it explicitly")
|
||||
}
|
||||
|
||||
network = object.NewNetwork(d.client.Client, i.Network[0])
|
||||
} else {
|
||||
var err error
|
||||
network, err = d.finder.Network(d.ctx, nic.Network)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
}
|
||||
|
||||
backing, err := network.EthernetCardBackingInfo(d.ctx)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
i, err := h.Info("network")
|
||||
device, err := object.EthernetCardTypes().CreateEthernetCard(nic.NetworkCard, backing)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
if len(i.Network) > 1 {
|
||||
return nil, fmt.Errorf("Host has multiple networks. Specify it explicitly")
|
||||
card := device.(types.BaseVirtualEthernetCard).GetVirtualEthernetCard()
|
||||
if nic.MacAddress != "" {
|
||||
card.AddressType = string(types.VirtualEthernetCardMacTypeManual)
|
||||
card.MacAddress = nic.MacAddress
|
||||
}
|
||||
card.UptCompatibilityEnabled = nic.Passthrough
|
||||
|
||||
network = object.NewNetwork(d.client.Client, i.Network[0])
|
||||
} else {
|
||||
var err error
|
||||
network, err = d.finder.Network(d.ctx, config.Network)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
devices = append(devices, device)
|
||||
}
|
||||
|
||||
backing, err := network.EthernetCardBackingInfo(d.ctx)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
device, err := object.EthernetCardTypes().CreateEthernetCard(config.NetworkCard, backing)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return append(devices, device), nil
|
||||
return devices, nil
|
||||
}
|
||||
|
||||
func (vm *VirtualMachine) AddCdrom(controllerType string, isoPath string) error {
|
||||
|
|
|
@ -2,6 +2,7 @@ package driver
|
|||
|
||||
import (
|
||||
"errors"
|
||||
|
||||
"github.com/vmware/govmomi/vim25/types"
|
||||
)
|
||||
|
||||
|
|
|
@ -1,11 +1,12 @@
|
|||
package driver
|
||||
|
||||
import (
|
||||
"strings"
|
||||
"unicode"
|
||||
|
||||
"github.com/vmware/govmomi/vim25/methods"
|
||||
"github.com/vmware/govmomi/vim25/types"
|
||||
"golang.org/x/mobile/event/key"
|
||||
"strings"
|
||||
"unicode"
|
||||
)
|
||||
|
||||
type KeyInput struct {
|
||||
|
|
|
@ -2,6 +2,7 @@ package main
|
|||
|
||||
import (
|
||||
"fmt"
|
||||
|
||||
"github.com/hashicorp/packer/builder/vsphere/driver"
|
||||
)
|
||||
|
||||
|
|
|
@ -2,13 +2,14 @@ package iso
|
|||
|
||||
import (
|
||||
"fmt"
|
||||
"io/ioutil"
|
||||
"os"
|
||||
"testing"
|
||||
|
||||
commonT "github.com/hashicorp/packer/builder/vsphere/common/testing"
|
||||
builderT "github.com/hashicorp/packer/helper/builder/testing"
|
||||
"github.com/hashicorp/packer/packer"
|
||||
"github.com/vmware/govmomi/vim25/types"
|
||||
"io/ioutil"
|
||||
"os"
|
||||
"testing"
|
||||
)
|
||||
|
||||
func TestISOBuilderAcc_default(t *testing.T) {
|
||||
|
|
|
@ -30,8 +30,10 @@ type FlatConfig struct {
|
|||
DiskControllerType *string `mapstructure:"disk_controller_type" cty:"disk_controller_type"`
|
||||
DiskSize *int64 `mapstructure:"disk_size" cty:"disk_size"`
|
||||
DiskThinProvisioned *bool `mapstructure:"disk_thin_provisioned" cty:"disk_thin_provisioned"`
|
||||
DiskEagerlyScrub *bool `mapstructure:"disk_eagerly_scrub" cty:"disk_eagerly_scrub"`
|
||||
Network *string `mapstructure:"network" cty:"network"`
|
||||
NetworkCard *string `mapstructure:"network_card" cty:"network_card"`
|
||||
NICs []FlatNIC `mapstructure:"network_adapters" cty:"network_adapters"`
|
||||
USBController *bool `mapstructure:"usb_controller" cty:"usb_controller"`
|
||||
Notes *string `mapstructure:"notes" cty:"notes"`
|
||||
VMName *string `mapstructure:"vm_name" cty:"vm_name"`
|
||||
|
@ -150,8 +152,10 @@ func (*FlatConfig) HCL2Spec() map[string]hcldec.Spec {
|
|||
"disk_controller_type": &hcldec.AttrSpec{Name: "disk_controller_type", Type: cty.String, Required: false},
|
||||
"disk_size": &hcldec.AttrSpec{Name: "disk_size", Type: cty.Number, Required: false},
|
||||
"disk_thin_provisioned": &hcldec.AttrSpec{Name: "disk_thin_provisioned", Type: cty.Bool, Required: false},
|
||||
"disk_eagerly_scrub": &hcldec.AttrSpec{Name: "disk_eagerly_scrub", Type: cty.Bool, Required: false},
|
||||
"network": &hcldec.AttrSpec{Name: "network", Type: cty.String, Required: false},
|
||||
"network_card": &hcldec.AttrSpec{Name: "network_card", Type: cty.String, Required: false},
|
||||
"network_adapters": &hcldec.BlockListSpec{TypeName: "network_adapters", Nested: hcldec.ObjectSpec((*FlatNIC)(nil).HCL2Spec())},
|
||||
"usb_controller": &hcldec.AttrSpec{Name: "usb_controller", Type: cty.Bool, Required: false},
|
||||
"notes": &hcldec.AttrSpec{Name: "notes", Type: cty.String, Required: false},
|
||||
"vm_name": &hcldec.AttrSpec{Name: "vm_name", Type: cty.String, Required: false},
|
||||
|
|
|
@ -6,6 +6,7 @@ package iso
|
|||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
|
||||
"github.com/hashicorp/packer/builder/vsphere/driver"
|
||||
"github.com/hashicorp/packer/helper/multistep"
|
||||
"github.com/hashicorp/packer/packer"
|
||||
|
|
|
@ -6,6 +6,7 @@ package iso
|
|||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
|
||||
"github.com/hashicorp/packer/builder/vsphere/driver"
|
||||
"github.com/hashicorp/packer/helper/multistep"
|
||||
"github.com/hashicorp/packer/packer"
|
||||
|
|
|
@ -3,18 +3,19 @@ package iso
|
|||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"github.com/hashicorp/packer/builder/vsphere/driver"
|
||||
packerCommon "github.com/hashicorp/packer/common"
|
||||
"github.com/hashicorp/packer/helper/multistep"
|
||||
"github.com/hashicorp/packer/packer"
|
||||
"github.com/hashicorp/packer/template/interpolate"
|
||||
"golang.org/x/mobile/event/key"
|
||||
"log"
|
||||
"net"
|
||||
"os"
|
||||
"strings"
|
||||
"time"
|
||||
"unicode/utf8"
|
||||
|
||||
"github.com/hashicorp/packer/builder/vsphere/driver"
|
||||
packerCommon "github.com/hashicorp/packer/common"
|
||||
"github.com/hashicorp/packer/helper/multistep"
|
||||
"github.com/hashicorp/packer/packer"
|
||||
"github.com/hashicorp/packer/template/interpolate"
|
||||
"golang.org/x/mobile/event/key"
|
||||
)
|
||||
|
||||
type BootConfig struct {
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
//go:generate struct-markdown
|
||||
//go:generate mapstructure-to-hcl2 -type CreateConfig
|
||||
//go:generate mapstructure-to-hcl2 -type NIC,CreateConfig
|
||||
|
||||
package iso
|
||||
|
||||
|
@ -13,6 +13,17 @@ import (
|
|||
"github.com/hashicorp/packer/packer"
|
||||
)
|
||||
|
||||
type NIC struct {
|
||||
// Set network VM will be connected to.
|
||||
Network string `mapstructure:"network"`
|
||||
// Set VM network card type. Example `vmxnet3`.
|
||||
NetworkCard string `mapstructure:"network_card" required:"true"`
|
||||
// Set network card MAC address
|
||||
MacAddress string `mapstructure:"mac_address"`
|
||||
// Enable DirectPath I/O passthrough
|
||||
Passthrough *bool `mapstructure:"passthrough"`
|
||||
}
|
||||
|
||||
type CreateConfig struct {
|
||||
// Set VM hardware version. Defaults to the most current VM hardware
|
||||
// version supported by vCenter. See
|
||||
|
@ -31,10 +42,14 @@ type CreateConfig struct {
|
|||
DiskSize int64 `mapstructure:"disk_size"`
|
||||
// Enable VMDK thin provisioning for VM. Defaults to `false`.
|
||||
DiskThinProvisioned bool `mapstructure:"disk_thin_provisioned"`
|
||||
// Enable VMDK eager scrubbing for VM. Defaults to `false`.
|
||||
DiskEagerlyScrub bool `mapstructure:"disk_eagerly_scrub"`
|
||||
// Set network VM will be connected to.
|
||||
Network string `mapstructure:"network"`
|
||||
// Set VM network card type. Example `vmxnet3`.
|
||||
NetworkCard string `mapstructure:"network_card"`
|
||||
// Network adapters
|
||||
NICs []NIC `mapstructure:"network_adapters"`
|
||||
// Create USB controller for virtual machine. Defaults to `false`.
|
||||
USBController bool `mapstructure:"usb_controller"`
|
||||
// VM notes.
|
||||
|
@ -83,8 +98,26 @@ func (s *StepCreateVM) Run(_ context.Context, state multistep.StateBag) multiste
|
|||
}
|
||||
|
||||
ui.Say("Creating VM...")
|
||||
|
||||
// add network/network card an the first nic for backwards compatibility in the type is defined
|
||||
var networkCards []driver.NIC
|
||||
if s.Config.NetworkCard != "" {
|
||||
networkCards = append(networkCards, driver.NIC{
|
||||
NetworkCard: s.Config.NetworkCard,
|
||||
Network: s.Config.Network})
|
||||
}
|
||||
for _, nic := range s.Config.NICs {
|
||||
networkCards = append(networkCards, driver.NIC{
|
||||
Network: nic.Network,
|
||||
NetworkCard: nic.NetworkCard,
|
||||
MacAddress: nic.MacAddress,
|
||||
Passthrough: nic.Passthrough,
|
||||
})
|
||||
}
|
||||
|
||||
vm, err = d.CreateVM(&driver.CreateConfig{
|
||||
DiskThinProvisioned: s.Config.DiskThinProvisioned,
|
||||
DiskEagerlyScrub: s.Config.DiskEagerlyScrub,
|
||||
DiskControllerType: s.Config.DiskControllerType,
|
||||
DiskSize: s.Config.DiskSize,
|
||||
Name: s.Location.VMName,
|
||||
|
@ -94,8 +127,7 @@ func (s *StepCreateVM) Run(_ context.Context, state multistep.StateBag) multiste
|
|||
ResourcePool: s.Location.ResourcePool,
|
||||
Datastore: s.Location.Datastore,
|
||||
GuestOS: s.Config.GuestOSType,
|
||||
Network: s.Config.Network,
|
||||
NetworkCard: s.Config.NetworkCard,
|
||||
NICs: networkCards,
|
||||
USBController: s.Config.USBController,
|
||||
Version: s.Config.Version,
|
||||
Firmware: s.Config.Firmware,
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
// Code generated by "mapstructure-to-hcl2 -type CreateConfig"; DO NOT EDIT.
|
||||
// Code generated by "mapstructure-to-hcl2 -type NIC,CreateConfig"; DO NOT EDIT.
|
||||
package iso
|
||||
|
||||
import (
|
||||
|
@ -9,16 +9,18 @@ import (
|
|||
// FlatCreateConfig is an auto-generated flat version of CreateConfig.
|
||||
// Where the contents of a field with a `mapstructure:,squash` tag are bubbled up.
|
||||
type FlatCreateConfig struct {
|
||||
Version *uint `mapstructure:"vm_version" cty:"vm_version"`
|
||||
GuestOSType *string `mapstructure:"guest_os_type" cty:"guest_os_type"`
|
||||
Firmware *string `mapstructure:"firmware" cty:"firmware"`
|
||||
DiskControllerType *string `mapstructure:"disk_controller_type" cty:"disk_controller_type"`
|
||||
DiskSize *int64 `mapstructure:"disk_size" cty:"disk_size"`
|
||||
DiskThinProvisioned *bool `mapstructure:"disk_thin_provisioned" cty:"disk_thin_provisioned"`
|
||||
Network *string `mapstructure:"network" cty:"network"`
|
||||
NetworkCard *string `mapstructure:"network_card" cty:"network_card"`
|
||||
USBController *bool `mapstructure:"usb_controller" cty:"usb_controller"`
|
||||
Notes *string `mapstructure:"notes" cty:"notes"`
|
||||
Version *uint `mapstructure:"vm_version" cty:"vm_version"`
|
||||
GuestOSType *string `mapstructure:"guest_os_type" cty:"guest_os_type"`
|
||||
Firmware *string `mapstructure:"firmware" cty:"firmware"`
|
||||
DiskControllerType *string `mapstructure:"disk_controller_type" cty:"disk_controller_type"`
|
||||
DiskSize *int64 `mapstructure:"disk_size" cty:"disk_size"`
|
||||
DiskThinProvisioned *bool `mapstructure:"disk_thin_provisioned" cty:"disk_thin_provisioned"`
|
||||
DiskEagerlyScrub *bool `mapstructure:"disk_eagerly_scrub" cty:"disk_eagerly_scrub"`
|
||||
Network *string `mapstructure:"network" cty:"network"`
|
||||
NetworkCard *string `mapstructure:"network_card" cty:"network_card"`
|
||||
NICs []FlatNIC `mapstructure:"network_adapters" cty:"network_adapters"`
|
||||
USBController *bool `mapstructure:"usb_controller" cty:"usb_controller"`
|
||||
Notes *string `mapstructure:"notes" cty:"notes"`
|
||||
}
|
||||
|
||||
// FlatMapstructure returns a new FlatCreateConfig.
|
||||
|
@ -39,10 +41,39 @@ func (*FlatCreateConfig) HCL2Spec() map[string]hcldec.Spec {
|
|||
"disk_controller_type": &hcldec.AttrSpec{Name: "disk_controller_type", Type: cty.String, Required: false},
|
||||
"disk_size": &hcldec.AttrSpec{Name: "disk_size", Type: cty.Number, Required: false},
|
||||
"disk_thin_provisioned": &hcldec.AttrSpec{Name: "disk_thin_provisioned", Type: cty.Bool, Required: false},
|
||||
"disk_eagerly_scrub": &hcldec.AttrSpec{Name: "disk_eagerly_scrub", Type: cty.Bool, Required: false},
|
||||
"network": &hcldec.AttrSpec{Name: "network", Type: cty.String, Required: false},
|
||||
"network_card": &hcldec.AttrSpec{Name: "network_card", Type: cty.String, Required: false},
|
||||
"network_adapters": &hcldec.BlockListSpec{TypeName: "network_adapters", Nested: hcldec.ObjectSpec((*FlatNIC)(nil).HCL2Spec())},
|
||||
"usb_controller": &hcldec.AttrSpec{Name: "usb_controller", Type: cty.Bool, Required: false},
|
||||
"notes": &hcldec.AttrSpec{Name: "notes", Type: cty.String, Required: false},
|
||||
}
|
||||
return s
|
||||
}
|
||||
|
||||
// FlatNIC is an auto-generated flat version of NIC.
|
||||
// Where the contents of a field with a `mapstructure:,squash` tag are bubbled up.
|
||||
type FlatNIC struct {
|
||||
Network *string `mapstructure:"network" cty:"network"`
|
||||
NetworkCard *string `mapstructure:"network_card" required:"true" cty:"network_card"`
|
||||
MacAddress *string `mapstructure:"mac_address" cty:"mac_address"`
|
||||
Passthrough *bool `mapstructure:"passthrough" cty:"passthrough"`
|
||||
}
|
||||
|
||||
// FlatMapstructure returns a new FlatNIC.
|
||||
// FlatNIC is an auto-generated flat version of NIC.
|
||||
// Where the contents a fields with a `mapstructure:,squash` tag are bubbled up.
|
||||
func (*NIC) FlatMapstructure() interface{ HCL2Spec() map[string]hcldec.Spec } { return new(FlatNIC) }
|
||||
|
||||
// HCL2Spec returns the hcl spec of a NIC.
|
||||
// This spec is used by HCL to read the fields of NIC.
|
||||
// The decoded values from this spec will then be applied to a FlatNIC.
|
||||
func (*FlatNIC) HCL2Spec() map[string]hcldec.Spec {
|
||||
s := map[string]hcldec.Spec{
|
||||
"network": &hcldec.AttrSpec{Name: "network", Type: cty.String, Required: false},
|
||||
"network_card": &hcldec.AttrSpec{Name: "network_card", Type: cty.String, Required: false},
|
||||
"mac_address": &hcldec.AttrSpec{Name: "mac_address", Type: cty.String, Required: false},
|
||||
"passthrough": &hcldec.AttrSpec{Name: "passthrough", Type: cty.Bool, Required: false},
|
||||
}
|
||||
return s
|
||||
}
|
||||
|
|
|
@ -3,10 +3,11 @@ package iso
|
|||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"path/filepath"
|
||||
|
||||
"github.com/hashicorp/packer/builder/vsphere/driver"
|
||||
"github.com/hashicorp/packer/helper/multistep"
|
||||
"github.com/hashicorp/packer/packer"
|
||||
"path/filepath"
|
||||
)
|
||||
|
||||
type StepRemoteUpload struct {
|
||||
|
|
|
@ -5,6 +5,7 @@ package iso
|
|||
|
||||
import (
|
||||
"context"
|
||||
|
||||
"github.com/hashicorp/packer/builder/vsphere/driver"
|
||||
"github.com/hashicorp/packer/helper/multistep"
|
||||
"github.com/hashicorp/packer/packer"
|
||||
|
|
|
@ -2,6 +2,7 @@ package iso
|
|||
|
||||
import (
|
||||
"context"
|
||||
|
||||
"github.com/hashicorp/packer/builder/vsphere/driver"
|
||||
"github.com/hashicorp/packer/helper/multistep"
|
||||
"github.com/hashicorp/packer/packer"
|
||||
|
|
|
@ -451,19 +451,31 @@ func getMapstructureSquashedStruct(topPkg *types.Package, utStruct *types.Struct
|
|||
log.Printf("could not parse field tag %s of : %v", tag, err)
|
||||
continue
|
||||
}
|
||||
if ms, err := structtag.Get("mapstructure"); err != nil {
|
||||
//no mapstructure tag
|
||||
} else if ms.HasOption("squash") {
|
||||
ot := field.Type()
|
||||
uot := ot.Underlying()
|
||||
utStruct, utOk := uot.(*types.Struct)
|
||||
if !utOk {
|
||||
|
||||
// Contains mapstructure-to-hcl2 tag
|
||||
if ms, err := structtag.Get("mapstructure-to-hcl2"); err == nil {
|
||||
// Stop if is telling to skip it
|
||||
if ms.HasOption("skip") {
|
||||
continue
|
||||
}
|
||||
|
||||
res = squashStructs(res, getMapstructureSquashedStruct(topPkg, utStruct))
|
||||
continue
|
||||
}
|
||||
|
||||
// Contains mapstructure tag
|
||||
if ms, err := structtag.Get("mapstructure"); err == nil {
|
||||
// Squash structs
|
||||
if ms.HasOption("squash") {
|
||||
ot := field.Type()
|
||||
uot := ot.Underlying()
|
||||
utStruct, utOk := uot.(*types.Struct)
|
||||
if !utOk {
|
||||
continue
|
||||
}
|
||||
|
||||
res = squashStructs(res, getMapstructureSquashedStruct(topPkg, utStruct))
|
||||
continue
|
||||
}
|
||||
}
|
||||
|
||||
if field.Pkg() != topPkg {
|
||||
field = types.NewField(field.Pos(), topPkg, field.Name(), field.Type(), field.Embedded())
|
||||
}
|
||||
|
|
|
@ -419,7 +419,7 @@ Options:
|
|||
-parallel-builds=1 Number of builds to run in parallel. 0 means no limit (Default: 0)
|
||||
-timestamp-ui Enable prefixing of each ui output with an RFC3339 timestamp.
|
||||
-var 'key=value' Variable for templates, can be used multiple times.
|
||||
-var-file=path JSON file containing user variables.
|
||||
-var-file=path JSON file containing user variables. [ Note that even in HCL mode this expects file to contain JSON, a fix is comming soon ]
|
||||
`
|
||||
|
||||
return strings.TrimSpace(helpText)
|
||||
|
|
|
@ -4,11 +4,12 @@ import (
|
|||
"bytes"
|
||||
"context"
|
||||
"fmt"
|
||||
"github.com/hashicorp/hcl/v2/hcldec"
|
||||
"path/filepath"
|
||||
"sync"
|
||||
"testing"
|
||||
|
||||
"github.com/hashicorp/hcl/v2/hcldec"
|
||||
|
||||
"golang.org/x/sync/errgroup"
|
||||
|
||||
"github.com/hashicorp/packer/builder/file"
|
||||
|
|
|
@ -92,7 +92,7 @@ Usage: packer console [options] [TEMPLATE]
|
|||
|
||||
Options:
|
||||
-var 'key=value' Variable for templates, can be used multiple times.
|
||||
-var-file=path JSON file containing user variables.
|
||||
-var-file=path JSON file containing user variables. [ Note that even in HCL mode this expects file to contain JSON, a fix is comming soon ]
|
||||
`
|
||||
|
||||
return strings.TrimSpace(helpText)
|
||||
|
|
|
@ -183,7 +183,7 @@ Options:
|
|||
-except=foo,bar,baz Validate all builds other than these.
|
||||
-only=foo,bar,baz Validate only these builds.
|
||||
-var 'key=value' Variable for templates, can be used multiple times.
|
||||
-var-file=path JSON file containing user variables.
|
||||
-var-file=path JSON file containing user variables. [ Note that even in HCL mode this expects file to contain JSON, a fix is comming soon ]
|
||||
`
|
||||
|
||||
return strings.TrimSpace(helpText)
|
||||
|
|
|
@ -99,6 +99,7 @@ var g = &grammar{
|
|||
pos: position{line: 14, col: 18, offset: 167},
|
||||
val: "wait",
|
||||
ignoreCase: false,
|
||||
want: "\"wait\"",
|
||||
},
|
||||
&labeledExpr{
|
||||
pos: position{line: 14, col: 25, offset: 174},
|
||||
|
@ -238,6 +239,7 @@ var g = &grammar{
|
|||
pos: position{line: 39, col: 10, offset: 819},
|
||||
val: "-",
|
||||
ignoreCase: false,
|
||||
want: "\"-\"",
|
||||
},
|
||||
},
|
||||
&ruleRefExpr{
|
||||
|
@ -253,6 +255,7 @@ var g = &grammar{
|
|||
pos: position{line: 39, col: 25, offset: 834},
|
||||
val: ".",
|
||||
ignoreCase: false,
|
||||
want: "\".\"",
|
||||
},
|
||||
&oneOrMoreExpr{
|
||||
pos: position{line: 39, col: 29, offset: 838},
|
||||
|
@ -278,6 +281,7 @@ var g = &grammar{
|
|||
pos: position{line: 43, col: 11, offset: 894},
|
||||
val: "0",
|
||||
ignoreCase: false,
|
||||
want: "\"0\"",
|
||||
},
|
||||
&actionExpr{
|
||||
pos: position{line: 43, col: 17, offset: 900},
|
||||
|
@ -336,6 +340,7 @@ var g = &grammar{
|
|||
pos: position{line: 51, col: 6, offset: 1065},
|
||||
val: "on",
|
||||
ignoreCase: true,
|
||||
want: "\"on\"i",
|
||||
},
|
||||
},
|
||||
},
|
||||
|
@ -349,6 +354,7 @@ var g = &grammar{
|
|||
pos: position{line: 55, col: 7, offset: 1104},
|
||||
val: "off",
|
||||
ignoreCase: true,
|
||||
want: "\"off\"i",
|
||||
},
|
||||
},
|
||||
},
|
||||
|
@ -370,6 +376,7 @@ var g = &grammar{
|
|||
pos: position{line: 64, col: 11, offset: 1240},
|
||||
val: ">",
|
||||
ignoreCase: false,
|
||||
want: "\">\"",
|
||||
},
|
||||
},
|
||||
{
|
||||
|
@ -379,6 +386,7 @@ var g = &grammar{
|
|||
pos: position{line: 65, col: 13, offset: 1256},
|
||||
val: "<",
|
||||
ignoreCase: false,
|
||||
want: "\"<\"",
|
||||
},
|
||||
},
|
||||
{
|
||||
|
@ -391,181 +399,217 @@ var g = &grammar{
|
|||
pos: position{line: 66, col: 14, offset: 1273},
|
||||
val: "bs",
|
||||
ignoreCase: true,
|
||||
want: "\"bs\"i",
|
||||
},
|
||||
&litMatcher{
|
||||
pos: position{line: 66, col: 22, offset: 1281},
|
||||
val: "del",
|
||||
ignoreCase: true,
|
||||
want: "\"del\"i",
|
||||
},
|
||||
&litMatcher{
|
||||
pos: position{line: 66, col: 31, offset: 1290},
|
||||
val: "enter",
|
||||
ignoreCase: true,
|
||||
want: "\"enter\"i",
|
||||
},
|
||||
&litMatcher{
|
||||
pos: position{line: 66, col: 42, offset: 1301},
|
||||
val: "esc",
|
||||
ignoreCase: true,
|
||||
want: "\"esc\"i",
|
||||
},
|
||||
&litMatcher{
|
||||
pos: position{line: 66, col: 51, offset: 1310},
|
||||
val: "f10",
|
||||
ignoreCase: true,
|
||||
want: "\"f10\"i",
|
||||
},
|
||||
&litMatcher{
|
||||
pos: position{line: 66, col: 60, offset: 1319},
|
||||
val: "f11",
|
||||
ignoreCase: true,
|
||||
want: "\"f11\"i",
|
||||
},
|
||||
&litMatcher{
|
||||
pos: position{line: 66, col: 69, offset: 1328},
|
||||
val: "f12",
|
||||
ignoreCase: true,
|
||||
want: "\"f12\"i",
|
||||
},
|
||||
&litMatcher{
|
||||
pos: position{line: 67, col: 11, offset: 1345},
|
||||
val: "f1",
|
||||
ignoreCase: true,
|
||||
want: "\"f1\"i",
|
||||
},
|
||||
&litMatcher{
|
||||
pos: position{line: 67, col: 19, offset: 1353},
|
||||
val: "f2",
|
||||
ignoreCase: true,
|
||||
want: "\"f2\"i",
|
||||
},
|
||||
&litMatcher{
|
||||
pos: position{line: 67, col: 27, offset: 1361},
|
||||
val: "f3",
|
||||
ignoreCase: true,
|
||||
want: "\"f3\"i",
|
||||
},
|
||||
&litMatcher{
|
||||
pos: position{line: 67, col: 35, offset: 1369},
|
||||
val: "f4",
|
||||
ignoreCase: true,
|
||||
want: "\"f4\"i",
|
||||
},
|
||||
&litMatcher{
|
||||
pos: position{line: 67, col: 43, offset: 1377},
|
||||
val: "f5",
|
||||
ignoreCase: true,
|
||||
want: "\"f5\"i",
|
||||
},
|
||||
&litMatcher{
|
||||
pos: position{line: 67, col: 51, offset: 1385},
|
||||
val: "f6",
|
||||
ignoreCase: true,
|
||||
want: "\"f6\"i",
|
||||
},
|
||||
&litMatcher{
|
||||
pos: position{line: 67, col: 59, offset: 1393},
|
||||
val: "f7",
|
||||
ignoreCase: true,
|
||||
want: "\"f7\"i",
|
||||
},
|
||||
&litMatcher{
|
||||
pos: position{line: 67, col: 67, offset: 1401},
|
||||
val: "f8",
|
||||
ignoreCase: true,
|
||||
want: "\"f8\"i",
|
||||
},
|
||||
&litMatcher{
|
||||
pos: position{line: 67, col: 75, offset: 1409},
|
||||
val: "f9",
|
||||
ignoreCase: true,
|
||||
want: "\"f9\"i",
|
||||
},
|
||||
&litMatcher{
|
||||
pos: position{line: 68, col: 12, offset: 1426},
|
||||
val: "return",
|
||||
ignoreCase: true,
|
||||
want: "\"return\"i",
|
||||
},
|
||||
&litMatcher{
|
||||
pos: position{line: 68, col: 24, offset: 1438},
|
||||
val: "tab",
|
||||
ignoreCase: true,
|
||||
want: "\"tab\"i",
|
||||
},
|
||||
&litMatcher{
|
||||
pos: position{line: 68, col: 33, offset: 1447},
|
||||
val: "up",
|
||||
ignoreCase: true,
|
||||
want: "\"up\"i",
|
||||
},
|
||||
&litMatcher{
|
||||
pos: position{line: 68, col: 41, offset: 1455},
|
||||
val: "down",
|
||||
ignoreCase: true,
|
||||
want: "\"down\"i",
|
||||
},
|
||||
&litMatcher{
|
||||
pos: position{line: 68, col: 51, offset: 1465},
|
||||
val: "spacebar",
|
||||
ignoreCase: true,
|
||||
want: "\"spacebar\"i",
|
||||
},
|
||||
&litMatcher{
|
||||
pos: position{line: 68, col: 65, offset: 1479},
|
||||
val: "insert",
|
||||
ignoreCase: true,
|
||||
want: "\"insert\"i",
|
||||
},
|
||||
&litMatcher{
|
||||
pos: position{line: 68, col: 77, offset: 1491},
|
||||
val: "home",
|
||||
ignoreCase: true,
|
||||
want: "\"home\"i",
|
||||
},
|
||||
&litMatcher{
|
||||
pos: position{line: 69, col: 11, offset: 1509},
|
||||
val: "end",
|
||||
ignoreCase: true,
|
||||
want: "\"end\"i",
|
||||
},
|
||||
&litMatcher{
|
||||
pos: position{line: 69, col: 20, offset: 1518},
|
||||
val: "pageup",
|
||||
ignoreCase: true,
|
||||
want: "\"pageUp\"i",
|
||||
},
|
||||
&litMatcher{
|
||||
pos: position{line: 69, col: 32, offset: 1530},
|
||||
val: "pagedown",
|
||||
ignoreCase: true,
|
||||
want: "\"pageDown\"i",
|
||||
},
|
||||
&litMatcher{
|
||||
pos: position{line: 69, col: 46, offset: 1544},
|
||||
val: "leftalt",
|
||||
ignoreCase: true,
|
||||
want: "\"leftAlt\"i",
|
||||
},
|
||||
&litMatcher{
|
||||
pos: position{line: 69, col: 59, offset: 1557},
|
||||
val: "leftctrl",
|
||||
ignoreCase: true,
|
||||
want: "\"leftCtrl\"i",
|
||||
},
|
||||
&litMatcher{
|
||||
pos: position{line: 69, col: 73, offset: 1571},
|
||||
val: "leftshift",
|
||||
ignoreCase: true,
|
||||
want: "\"leftShift\"i",
|
||||
},
|
||||
&litMatcher{
|
||||
pos: position{line: 70, col: 11, offset: 1594},
|
||||
val: "rightalt",
|
||||
ignoreCase: true,
|
||||
want: "\"rightAlt\"i",
|
||||
},
|
||||
&litMatcher{
|
||||
pos: position{line: 70, col: 25, offset: 1608},
|
||||
val: "rightctrl",
|
||||
ignoreCase: true,
|
||||
want: "\"rightCtrl\"i",
|
||||
},
|
||||
&litMatcher{
|
||||
pos: position{line: 70, col: 40, offset: 1623},
|
||||
val: "rightshift",
|
||||
ignoreCase: true,
|
||||
want: "\"rightShift\"i",
|
||||
},
|
||||
&litMatcher{
|
||||
pos: position{line: 70, col: 56, offset: 1639},
|
||||
val: "leftsuper",
|
||||
ignoreCase: true,
|
||||
want: "\"leftSuper\"i",
|
||||
},
|
||||
&litMatcher{
|
||||
pos: position{line: 70, col: 71, offset: 1654},
|
||||
val: "rightsuper",
|
||||
ignoreCase: true,
|
||||
want: "\"rightSuper\"i",
|
||||
},
|
||||
&litMatcher{
|
||||
pos: position{line: 71, col: 11, offset: 1678},
|
||||
val: "left",
|
||||
ignoreCase: true,
|
||||
want: "\"left\"i",
|
||||
},
|
||||
&litMatcher{
|
||||
pos: position{line: 71, col: 21, offset: 1688},
|
||||
val: "right",
|
||||
ignoreCase: true,
|
||||
want: "\"right\"i",
|
||||
},
|
||||
},
|
||||
},
|
||||
|
@ -602,36 +646,43 @@ var g = &grammar{
|
|||
pos: position{line: 75, col: 13, offset: 1745},
|
||||
val: "ns",
|
||||
ignoreCase: false,
|
||||
want: "\"ns\"",
|
||||
},
|
||||
&litMatcher{
|
||||
pos: position{line: 75, col: 20, offset: 1752},
|
||||
val: "us",
|
||||
ignoreCase: false,
|
||||
want: "\"us\"",
|
||||
},
|
||||
&litMatcher{
|
||||
pos: position{line: 75, col: 27, offset: 1759},
|
||||
val: "µs",
|
||||
ignoreCase: false,
|
||||
want: "\"µs\"",
|
||||
},
|
||||
&litMatcher{
|
||||
pos: position{line: 75, col: 34, offset: 1767},
|
||||
val: "ms",
|
||||
ignoreCase: false,
|
||||
want: "\"ms\"",
|
||||
},
|
||||
&litMatcher{
|
||||
pos: position{line: 75, col: 41, offset: 1774},
|
||||
val: "s",
|
||||
ignoreCase: false,
|
||||
want: "\"s\"",
|
||||
},
|
||||
&litMatcher{
|
||||
pos: position{line: 75, col: 47, offset: 1780},
|
||||
val: "m",
|
||||
ignoreCase: false,
|
||||
want: "\"m\"",
|
||||
},
|
||||
&litMatcher{
|
||||
pos: position{line: 75, col: 53, offset: 1786},
|
||||
val: "h",
|
||||
ignoreCase: false,
|
||||
want: "\"h\"",
|
||||
},
|
||||
},
|
||||
},
|
||||
|
@ -1095,6 +1146,7 @@ type litMatcher struct {
|
|||
pos position
|
||||
val string
|
||||
ignoreCase bool
|
||||
want string
|
||||
}
|
||||
|
||||
type charClassMatcher struct {
|
||||
|
@ -1889,11 +1941,6 @@ func (p *parser) parseLitMatcher(lit *litMatcher) (interface{}, bool) {
|
|||
defer p.out(p.in("parseLitMatcher"))
|
||||
}
|
||||
|
||||
ignoreCase := ""
|
||||
if lit.ignoreCase {
|
||||
ignoreCase = "i"
|
||||
}
|
||||
val := string(strconv.AppendQuote([]byte{}, lit.val)) + ignoreCase // wrap 'lit.val' with double quotes
|
||||
start := p.pt
|
||||
for _, want := range lit.val {
|
||||
cur := p.pt.rn
|
||||
|
@ -1901,13 +1948,13 @@ func (p *parser) parseLitMatcher(lit *litMatcher) (interface{}, bool) {
|
|||
cur = unicode.ToLower(cur)
|
||||
}
|
||||
if cur != want {
|
||||
p.failAt(false, start.position, val)
|
||||
p.failAt(false, start.position, lit.want)
|
||||
p.restore(start)
|
||||
return nil, false
|
||||
}
|
||||
p.read()
|
||||
}
|
||||
p.failAt(true, start.position, val)
|
||||
p.failAt(true, start.position, lit.want)
|
||||
return p.sliceFrom(start), true
|
||||
}
|
||||
|
||||
|
|
|
@ -76,7 +76,7 @@ import (
|
|||
// `http_directory` configuration parameter. If `http_directory` isn't
|
||||
// specified, these will be blank!
|
||||
//
|
||||
// - `Name` - The name of the VM.
|
||||
// - `{{ .Name }}` - The name of the VM.
|
||||
//
|
||||
// Example boot command. This is actually a working boot command used to start an
|
||||
// CentOS 6.4 installer:
|
||||
|
|
|
@ -180,6 +180,75 @@ Hyper-V\Set-VMFirmware -VMName $vmName -FirstBootDevice $vmDvdDrive -ErrorAction
|
|||
}
|
||||
}
|
||||
|
||||
func SetFirstBootDeviceGen1(vmName string, controllerType string) error {
|
||||
|
||||
// for Generation 1 VMs, we read the value of the VM's boot order, strip the value specified in
|
||||
// controllerType and insert that value back at the beginning of the list.
|
||||
//
|
||||
// controllerType must be 'NET', 'DVD', 'IDE' or 'FLOPPY' (case sensitive)
|
||||
// The 'NET' value is always replaced with 'LegacyNetworkAdapter'
|
||||
|
||||
if controllerType == "NET" {
|
||||
controllerType = "LegacyNetworkAdapter"
|
||||
}
|
||||
|
||||
script := `
|
||||
param([string] $vmName, [string] $controllerType)
|
||||
$vmBootOrder = Hyper-V\Get-VMBios -VMName $vmName | Select-Object -ExpandProperty StartupOrder | Where-Object { $_ -ne $controllerType }
|
||||
Hyper-V\Set-VMBios -VMName $vmName -StartupOrder (@($controllerType) + $vmBootOrder)
|
||||
`
|
||||
|
||||
var ps powershell.PowerShellCmd
|
||||
err := ps.Run(script, vmName, controllerType)
|
||||
return err
|
||||
}
|
||||
|
||||
func SetFirstBootDeviceGen2(vmName string, controllerType string, controllerNumber uint, controllerLocation uint) error {
|
||||
|
||||
script := `param ([string] $vmName, [string] $controllerType, [int] $controllerNumber, [int] $controllerLocation)`
|
||||
|
||||
switch {
|
||||
|
||||
case controllerType == "CD":
|
||||
// for CDs we have to use Get-VMDvdDrive to find the device
|
||||
script += `
|
||||
$vmDevice = Hyper-V\Get-VMDvdDrive -VMName $vmName -ControllerNumber $controllerNumber -ControllerLocation $controllerLocation -ErrorAction SilentlyContinue`
|
||||
|
||||
case controllerType == "NET":
|
||||
// for "NET" device, we select the first network adapter on the VM
|
||||
script += `
|
||||
$vmDevice = Hyper-V\Get-VMNetworkAdapter -VMName $vmName -ErrorAction SilentlyContinue | Select-Object -First 1`
|
||||
|
||||
default:
|
||||
script += `
|
||||
$vmDevice = @(Hyper-V\Get-VMIdeController -VMName $vmName -ErrorAction SilentlyContinue) +
|
||||
@(Hyper-V\Get-VMScsiController -VMName $vmName -ErrorAction SilentlyContinue) |
|
||||
Select-Object -ExpandProperty Drives |
|
||||
Where-Object { $_.ControllerType -eq $controllerType } |
|
||||
Where-Object { ($_.ControllerNumber -eq $controllerNumber) -and ($_.ControllerLocation -eq $controllerLocation) }
|
||||
`
|
||||
|
||||
}
|
||||
|
||||
script += `
|
||||
if ($vmDevice -eq $null) { throw 'unable to find boot device' }
|
||||
Hyper-V\Set-VMFirmware -VMName $vmName -FirstBootDevice $vmDevice
|
||||
`
|
||||
|
||||
var ps powershell.PowerShellCmd
|
||||
err := ps.Run(script, vmName, controllerType, strconv.FormatInt(int64(controllerNumber), 10), strconv.FormatInt(int64(controllerLocation), 10))
|
||||
return err
|
||||
}
|
||||
|
||||
func SetFirstBootDevice(vmName string, controllerType string, controllerNumber uint, controllerLocation uint, generation uint) error {
|
||||
|
||||
if generation == 1 {
|
||||
return SetFirstBootDeviceGen1(vmName, controllerType)
|
||||
} else {
|
||||
return SetFirstBootDeviceGen2(vmName, controllerType, controllerNumber, controllerLocation)
|
||||
}
|
||||
}
|
||||
|
||||
func DeleteDvdDrive(vmName string, controllerNumber uint, controllerLocation uint) error {
|
||||
var script = `
|
||||
param([string]$vmName,[int]$controllerNumber,[int]$controllerLocation)
|
||||
|
@ -872,9 +941,9 @@ if ($disks.Length -eq 0) {
|
|||
foreach ($disk in $disks) {
|
||||
Write-Output "Compacting disk: $(Split-Path $disk -leaf)"
|
||||
|
||||
$sizeBefore = $disk.Length
|
||||
$sizeBefore = (Get-Item -Path $disk).Length
|
||||
Optimize-VHD -Path $disk -Mode Full
|
||||
$sizeAfter = $disk.Length
|
||||
$sizeAfter = (Get-Item -Path $disk).Length
|
||||
|
||||
# Calculate the percentage change in disk size
|
||||
if ($sizeAfter -gt 0) { # Protect against division by zero
|
||||
|
|
|
@ -112,7 +112,9 @@ func IsPowershellAvailable() (bool, string, error) {
|
|||
|
||||
func (ps *PowerShellCmd) getPowerShellPath() (string, error) {
|
||||
powershellAvailable, path, err := IsPowershellAvailable()
|
||||
|
||||
if err != nil {
|
||||
log.Fatalf("IsPowershellAvailable: %v", err)
|
||||
}
|
||||
if !powershellAvailable {
|
||||
log.Fatal("Cannot find PowerShell in the path")
|
||||
return "", err
|
||||
|
|
|
@ -5,7 +5,6 @@ package shell_local
|
|||
import (
|
||||
"errors"
|
||||
"fmt"
|
||||
// "log"
|
||||
"os"
|
||||
"path/filepath"
|
||||
"runtime"
|
||||
|
|
|
@ -1,8 +1,9 @@
|
|||
package fix
|
||||
|
||||
import (
|
||||
"github.com/mitchellh/mapstructure"
|
||||
"strings"
|
||||
|
||||
"github.com/mitchellh/mapstructure"
|
||||
)
|
||||
|
||||
type FixerAmazonTemporarySecurityCIDRs struct{}
|
||||
|
|
4
go.mod
4
go.mod
|
@ -14,7 +14,7 @@ require (
|
|||
github.com/NaverCloudPlatform/ncloud-sdk-go-v2 v1.1.0
|
||||
github.com/PuerkitoBio/goquery v1.5.0 // indirect
|
||||
github.com/StackExchange/wmi v0.0.0-20190523213315-cbe66965904d // indirect
|
||||
github.com/Telmate/proxmox-api-go v0.0.0-20200116224409-320525bf3340
|
||||
github.com/Telmate/proxmox-api-go v0.0.0-20200225212220-a29566462efd
|
||||
github.com/abdullin/seq v0.0.0-20160510034733-d5467c17e7af // indirect
|
||||
github.com/aliyun/alibaba-cloud-sdk-go v0.0.0-20190418113227-25233c783f4e
|
||||
github.com/aliyun/aliyun-oss-go-sdk v0.0.0-20170113022742-e6dbea820a9f
|
||||
|
@ -144,7 +144,7 @@ require (
|
|||
github.com/stretchr/objx v0.2.0 // indirect
|
||||
github.com/stretchr/testify v1.4.0
|
||||
github.com/temoto/robotstxt v1.1.1 // indirect
|
||||
github.com/tencentcloud/tencentcloud-sdk-go v3.0.97+incompatible
|
||||
github.com/tencentcloud/tencentcloud-sdk-go v3.0.121+incompatible
|
||||
github.com/ucloud/ucloud-sdk-go v0.12.0
|
||||
github.com/ufilesdk-dev/ufile-gosdk v0.0.0-20190830075812-b4dbc4ef43a6
|
||||
github.com/ugorji/go v0.0.0-20151218193438-646ae4a518c1
|
||||
|
|
4
go.sum
4
go.sum
|
@ -43,6 +43,8 @@ github.com/StackExchange/wmi v0.0.0-20190523213315-cbe66965904d h1:G0m3OIz70MZUW
|
|||
github.com/StackExchange/wmi v0.0.0-20190523213315-cbe66965904d/go.mod h1:3eOhrUMpNV+6aFIbp5/iudMxNCF27Vw2OZgy4xEx0Fg=
|
||||
github.com/Telmate/proxmox-api-go v0.0.0-20200116224409-320525bf3340 h1:bOjy6c07dpipWm11dL92FbtmXGnDywOm2uKzG4CePuY=
|
||||
github.com/Telmate/proxmox-api-go v0.0.0-20200116224409-320525bf3340/go.mod h1:OGWyIMJ87/k/GCz8CGiWB2HOXsOVDM6Lpe/nFPkC4IQ=
|
||||
github.com/Telmate/proxmox-api-go v0.0.0-20200225212220-a29566462efd h1:Moss3RtB00h4omKW+leNGIGIfmHcnkZPTJ5d0A0fY14=
|
||||
github.com/Telmate/proxmox-api-go v0.0.0-20200225212220-a29566462efd/go.mod h1:OGWyIMJ87/k/GCz8CGiWB2HOXsOVDM6Lpe/nFPkC4IQ=
|
||||
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=
|
||||
|
@ -465,6 +467,8 @@ github.com/temoto/robotstxt v1.1.1 h1:Gh8RCs8ouX3hRSxxK7B1mO5RFByQ4CmJZDwgom++Ja
|
|||
github.com/temoto/robotstxt v1.1.1/go.mod h1:+1AmkuG3IYkh1kv0d2qEB9Le88ehNO0zwOr3ujewlOo=
|
||||
github.com/tencentcloud/tencentcloud-sdk-go v3.0.97+incompatible h1:y2gZtLpcWqFzSFbQSKwv1gL+NocPRM0ktGh7Dlb8U7s=
|
||||
github.com/tencentcloud/tencentcloud-sdk-go v3.0.97+incompatible/go.mod h1:0PfYow01SHPMhKY31xa+EFz2RStxIqj6JFAJS+IkCi4=
|
||||
github.com/tencentcloud/tencentcloud-sdk-go v3.0.121+incompatible h1:/5EEPgCRsWdGgZsM7YRLhYE9gpE9+kUGoIfGCrFYU0s=
|
||||
github.com/tencentcloud/tencentcloud-sdk-go v3.0.121+incompatible/go.mod h1:0PfYow01SHPMhKY31xa+EFz2RStxIqj6JFAJS+IkCi4=
|
||||
github.com/ucloud/ucloud-sdk-go v0.12.0 h1:VCFN3jWg/G4wvwjG6qG5AhFuAT1JdmGvY6+4WHbuJcw=
|
||||
github.com/ucloud/ucloud-sdk-go v0.12.0/go.mod h1:lM6fpI8y6iwACtlbHUav823/uKPdXsNBlnBpRF2fj3c=
|
||||
github.com/ufilesdk-dev/ufile-gosdk v0.0.0-20190830075812-b4dbc4ef43a6 h1:FAWNiqocJ04wC4Znj7Ax4PGWstZijayO6ifuHHvb+vI=
|
||||
|
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue