Merge remote-tracking branch 'origin/master' into fix_8730

This commit is contained in:
Adrien Delorme 2020-03-04 12:30:01 +01:00
commit 59cc246942
198 changed files with 4181 additions and 662 deletions

View File

@ -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:

124
.golangci.yml Normal file
View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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 ./...

View File

@ -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"

View File

@ -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"

View File

@ -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"
)

View File

@ -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

View File

@ -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"
)

View File

@ -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"

View File

@ -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"

View File

@ -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"

View File

@ -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"
)

View File

@ -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"

View File

@ -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

View File

@ -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},

View File

@ -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.

View File

@ -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) {

View File

@ -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.

View File

@ -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, " +

View File

@ -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

View File

@ -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

View File

@ -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)
}

View File

@ -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))

View File

@ -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
}

View File

@ -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)
}
}
}

View File

@ -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,

View File

@ -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},

View File

@ -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,

View File

@ -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},

View File

@ -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.

View File

@ -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"
)

View File

@ -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).

View File

@ -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"
)

View File

@ -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"
)

View File

@ -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.

View File

@ -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 {

View File

@ -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)))

View File

@ -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

View File

@ -2,6 +2,7 @@ package qemu
import (
"context"
"github.com/hashicorp/packer/common"
"github.com/hashicorp/packer/helper/multistep"
)

View File

@ -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) {

View File

@ -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
}

View File

@ -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

View File

@ -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

View File

@ -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)

View File

@ -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), "")
}

View File

@ -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")
}

View File

@ -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

View File

@ -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

View File

@ -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)
}
}

View File

@ -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

View File

@ -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

View File

@ -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) {

View File

@ -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"`

View File

@ -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"

View File

@ -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) {

View File

@ -1,8 +1,9 @@
package clone
import (
"github.com/hashicorp/packer/packer"
"testing"
"github.com/hashicorp/packer/packer"
)
func TestCloneBuilder_ImplementsBuilder(t *testing.T) {

View File

@ -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"

View File

@ -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"

View File

@ -6,6 +6,7 @@ package common
import (
"context"
"fmt"
"github.com/hashicorp/packer/builder/vsphere/driver"
"github.com/hashicorp/packer/helper/multistep"
)

View File

@ -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"

View File

@ -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 {

View File

@ -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 {

View File

@ -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"

View File

@ -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"

View File

@ -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 {

View File

@ -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 {

View File

@ -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"

View File

@ -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 {

View File

@ -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"

View File

@ -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
}

View File

@ -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"

View File

@ -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 {

View File

@ -2,6 +2,7 @@ package driver
import (
"errors"
"github.com/vmware/govmomi/vim25/types"
)

View File

@ -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 {

View File

@ -2,6 +2,7 @@ package main
import (
"fmt"
"github.com/hashicorp/packer/builder/vsphere/driver"
)

View File

@ -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) {

View File

@ -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},

View File

@ -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"

View File

@ -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"

View File

@ -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 {

View File

@ -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,

View File

@ -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
}

View File

@ -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 {

View File

@ -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"

View File

@ -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"

View File

@ -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())
}

View File

@ -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)

View File

@ -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"

View 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)

View File

@ -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)

View File

@ -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
}

View File

@ -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:

View File

@ -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

View File

@ -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

View File

@ -5,7 +5,6 @@ package shell_local
import (
"errors"
"fmt"
// "log"
"os"
"path/filepath"
"runtime"

View File

@ -1,8 +1,9 @@
package fix
import (
"github.com/mitchellh/mapstructure"
"strings"
"github.com/mitchellh/mapstructure"
)
type FixerAmazonTemporarySecurityCIDRs struct{}

4
go.mod
View File

@ -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
View File

@ -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