Compare commits
150 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| 0e564b4883 | |||
| b9f1b3c8d4 | |||
| 66ad6f3483 | |||
| 072a71b419 | |||
| e518a0a8b7 | |||
| 68e083603d | |||
| 8a1caaa804 | |||
| 0c45bd8b4f | |||
| d4d30127a3 | |||
| c4349f5a33 | |||
| b204687de9 | |||
| 631f402d1e | |||
| 7873cabf63 | |||
| 0ccff0d5b9 | |||
| 76f13deaf4 | |||
| 4133acbdf3 | |||
| 4bdf30165a | |||
| 6d8cce501e | |||
| 74a94d8547 | |||
| 9e6c94e141 | |||
| 764b4cb50a | |||
| 9403b48a52 | |||
| 698924f246 | |||
| 7e382d0df7 | |||
| 8832b3e2ca | |||
| e1a46ec293 | |||
| 717b9047db | |||
| 1dbe5abc14 | |||
| d7230827cd | |||
| e195c627fb | |||
| f013164298 | |||
| e9d3826219 | |||
| 9b649594c6 | |||
| 8482c6c2e6 | |||
| bd3112fa7c | |||
| 23b940dbd5 | |||
| e851050774 | |||
| 382f209b07 | |||
| 59cc246942 | |||
| 25f59a1592 | |||
| 4ed896fc99 | |||
| 5281740275 | |||
| b91c4d32cc | |||
| e35a872414 | |||
| fc110e747f | |||
| db7a40d6ea | |||
| 0a2fb8c383 | |||
| d2964d59e9 | |||
| ce674ff272 | |||
| 6dbe86fcf4 | |||
| 50896d4ddf | |||
| 67c9c2a59a | |||
| 6540891ca2 | |||
| d422d81dec | |||
| f808d1d968 | |||
| b8408c1c1a | |||
| 5dfeddc3a4 | |||
| 0f9c3e2cb4 | |||
| 9c9826ee4b | |||
| 433c9e8276 | |||
| 5eead20ae4 | |||
| 0936f8bc03 | |||
| 0cfcbd66ca | |||
| b45ed4c4f1 | |||
| 60d952033e | |||
| 7d065d9030 | |||
| 66f01b6033 | |||
| 6d59cbd4b5 | |||
| 36d574c9ec | |||
| f25dc1cf74 | |||
| 66a1cdb8d2 | |||
| 5d4e9af9cd | |||
| 81a65dac4f | |||
| 17fa6a3e95 | |||
| 806e799945 | |||
| dbb884deb3 | |||
| 8e0ed66318 | |||
| efa9d94d8e | |||
| d246d0c82a | |||
| b5b190b1f0 | |||
| 7f38ce0686 | |||
| 31665dc8c3 | |||
| 591b684f08 | |||
| 723a9eba9a | |||
| 11fae17308 | |||
| 2de731453f | |||
| 0a0fbfc33b | |||
| dd8f4370c6 | |||
| 5e1e4ec701 | |||
| d7300f4635 | |||
| dde74232f2 | |||
| a8fcb2d91a | |||
| 59fa6c036c | |||
| eb35617985 | |||
| 1a72eeb772 | |||
| 5846c0cdd7 | |||
| 3f80414e6d | |||
| 52e5b7051e | |||
| f3231018ca | |||
| 9cbb49bacb | |||
| 70752e0488 | |||
| 3c213e6eaf | |||
| cebfb1c735 | |||
| 94130b5def | |||
| 79867ca26e | |||
| 6c749eecf7 | |||
| 00c812cfe8 | |||
| 1ec68cac23 | |||
| 5a87760702 | |||
| 807f39284d | |||
| 9a85fdd0a5 | |||
| c9b011145d | |||
| ac239d1188 | |||
| 9c171c1f13 | |||
| 909b9fcfc7 | |||
| cebdabe0f7 | |||
| c229607ee3 | |||
| f65198726d | |||
| 7f0de5fc8d | |||
| 9ec8b67392 | |||
| 2981fd627d | |||
| 7254b04129 | |||
| 1719633058 | |||
| 2bdca997ac | |||
| 19e7114301 | |||
| 2d0c796837 | |||
| 4b10c5a87c | |||
| 483c8ec7d0 | |||
| ff1af40c66 | |||
| 391cea13ed | |||
| 4f7670a1e6 | |||
| 23fa3107a3 | |||
| c2ad9481f6 | |||
| 675fe95882 | |||
| 78012dc56f | |||
| dc81720dc9 | |||
| d654898ebf | |||
| a6d90babbf | |||
| 61f5f867eb | |||
| 9891e75f75 | |||
| 31622b50ac | |||
| 7152732597 | |||
| 55ae803852 | |||
| e490b7651d | |||
| 90eee3829d | |||
| 1cb6f4e456 | |||
| 76680ac1c6 | |||
| 6b82bf5a93 | |||
| 9756a9858a | |||
| 4fa0f6baa3 |
@@ -36,7 +36,7 @@ commands:
|
||||
type: string
|
||||
steps:
|
||||
- checkout
|
||||
- run: GOOS=<< parameters.GOOS >> go build -ldflags="-s -w" -o ./pkg/packer_<< parameters.GOOS >>_$(go env GOARCH) .
|
||||
- run: GOOS=<< parameters.GOOS >> go build -ldflags="-s -w -X github.com/hashicorp/packer/version.GitCommit=${CIRCLE_SHA1}" -o ./pkg/packer_<< parameters.GOOS >>_$(go env GOARCH) .
|
||||
- run: zip ./pkg/packer_<< parameters.GOOS >>_$(go env GOARCH).zip ./pkg/packer_<< parameters.GOOS >>_$(go env GOARCH)
|
||||
- run: rm ./pkg/packer_<< parameters.GOOS >>_$(go env GOARCH)
|
||||
- persist_to_workspace:
|
||||
|
||||
@@ -5,4 +5,6 @@
|
||||
*.md text eol=lf
|
||||
*.ps1 text eol=lf
|
||||
*.hcl text eol=lf
|
||||
go.mod text eol=lf
|
||||
go.sum text eol=lf
|
||||
common/test-fixtures/root/* eol=lf
|
||||
|
||||
@@ -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
@@ -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
|
||||
+11
-10
@@ -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
|
||||
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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 ./...
|
||||
|
||||
|
||||
@@ -205,7 +205,7 @@ func (c *AlicloudImageConfig) Prepare(ctx *interpolate.Context) []error {
|
||||
strings.HasPrefix(c.AlicloudImageName, "https://") {
|
||||
errs = append(errs, fmt.Errorf("image_name can't start with 'http://' or 'https://'"))
|
||||
}
|
||||
reg := regexp.MustCompile("\\s+")
|
||||
reg := regexp.MustCompile(`\s+`)
|
||||
if reg.FindString(c.AlicloudImageName) != "" {
|
||||
errs = append(errs, fmt.Errorf("image_name can't include spaces"))
|
||||
}
|
||||
|
||||
@@ -10,11 +10,11 @@ package chroot
|
||||
import (
|
||||
"context"
|
||||
"errors"
|
||||
"github.com/hashicorp/packer/builder"
|
||||
"runtime"
|
||||
|
||||
"github.com/aws/aws-sdk-go/service/ec2"
|
||||
"github.com/hashicorp/hcl/v2/hcldec"
|
||||
"github.com/hashicorp/packer/builder"
|
||||
awscommon "github.com/hashicorp/packer/builder/amazon/common"
|
||||
"github.com/hashicorp/packer/common"
|
||||
"github.com/hashicorp/packer/common/chroot"
|
||||
|
||||
@@ -4,13 +4,13 @@ import (
|
||||
"bytes"
|
||||
"context"
|
||||
"fmt"
|
||||
"github.com/hashicorp/packer/builder"
|
||||
"log"
|
||||
"os"
|
||||
"path/filepath"
|
||||
"strings"
|
||||
|
||||
"github.com/aws/aws-sdk-go/service/ec2"
|
||||
"github.com/hashicorp/packer/builder"
|
||||
"github.com/hashicorp/packer/common"
|
||||
"github.com/hashicorp/packer/helper/multistep"
|
||||
"github.com/hashicorp/packer/packer"
|
||||
|
||||
@@ -3,10 +3,10 @@ package chroot
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"github.com/hashicorp/packer/builder"
|
||||
"log"
|
||||
"os"
|
||||
|
||||
"github.com/hashicorp/packer/builder"
|
||||
"github.com/hashicorp/packer/helper/multistep"
|
||||
"github.com/hashicorp/packer/packer"
|
||||
)
|
||||
|
||||
@@ -19,9 +19,9 @@ type AMIConfig struct {
|
||||
// engine](../templates/engine.html) for more info).
|
||||
AMIName string `mapstructure:"ami_name" required:"true"`
|
||||
// The description to set for the resulting
|
||||
// AMI(s). By default this description is empty. This is a template
|
||||
// engine, see Build template
|
||||
// data for more information.
|
||||
// AMI(s). By default this description is empty. This is a
|
||||
// [template engine](/docs/templates/engine.html), see [Build template
|
||||
// data](#build-template-data) for more information.
|
||||
AMIDescription string `mapstructure:"ami_description" required:"false"`
|
||||
// The type of virtualization for the AMI
|
||||
// you are building. This option is required to register HVM images. Can be
|
||||
|
||||
@@ -1,12 +1,12 @@
|
||||
package common
|
||||
|
||||
import (
|
||||
"github.com/hashicorp/packer/builder"
|
||||
"reflect"
|
||||
"testing"
|
||||
|
||||
"github.com/aws/aws-sdk-go/aws"
|
||||
"github.com/aws/aws-sdk-go/service/ec2"
|
||||
"github.com/hashicorp/packer/builder"
|
||||
"github.com/hashicorp/packer/helper/multistep"
|
||||
)
|
||||
|
||||
|
||||
@@ -265,13 +265,21 @@ type RunConfig struct {
|
||||
// because a particular availability zone does not have capacity for the
|
||||
// specific instance_type requested in instance_type.
|
||||
SpotInstanceTypes []string `mapstructure:"spot_instance_types" required:"false"`
|
||||
// The maximum hourly price to pay for a spot instance
|
||||
// to create the AMI. Spot instances are a type of instance that EC2 starts
|
||||
// when the current spot price is less than the maximum price you specify.
|
||||
// Spot price will be updated based on available spot instance capacity and
|
||||
// current spot instance requests. It may save you some costs. You can set
|
||||
// this to auto for Packer to automatically discover the best spot price or
|
||||
// to "0" to use an on demand instance (default).
|
||||
// With Spot Instances, you pay the Spot price that's in effect for the
|
||||
// time period your instances are running. Spot Instance prices are set by
|
||||
// Amazon EC2 and adjust gradually based on long-term trends in supply and
|
||||
// demand for Spot Instance capacity.
|
||||
//
|
||||
// When this field is set, it represents the maximum hourly price you are
|
||||
// willing to pay for a spot instance. If you do not set this value, it
|
||||
// defaults to a maximum price equal to the on demand price of the
|
||||
// instance. In the situation where the current Amazon-set spot price
|
||||
// exceeds the value set in this field, Packer will not launch an instance
|
||||
// and the build will error. In the situation where the Amazon-set spot
|
||||
// price is less than the value set in this field, Packer will launch and
|
||||
// you will pay the Amazon-set spot price, not this maximum value.
|
||||
// For more information, see the Amazon docs on
|
||||
// [spot pricing](https://aws.amazon.com/ec2/spot/pricing/).
|
||||
SpotPrice string `mapstructure:"spot_price" required:"false"`
|
||||
// Required if spot_price is set to
|
||||
// auto. This tells Packer what sort of AMI you're launching to find the
|
||||
|
||||
@@ -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"
|
||||
|
||||
@@ -300,7 +300,7 @@ func (s *StepRunSpotInstance) Run(ctx context.Context, state multistep.StateBag)
|
||||
if len(createOutput.Errors) > 0 {
|
||||
errString := fmt.Sprintf("Error waiting for fleet request (%s) to become ready:", *createOutput.FleetId)
|
||||
for _, outErr := range createOutput.Errors {
|
||||
errString = errString + fmt.Sprintf("%s", *outErr.ErrorMessage)
|
||||
errString = errString + aws.StringValue(outErr.ErrorMessage)
|
||||
}
|
||||
err = fmt.Errorf(errString)
|
||||
state.Put("error", err)
|
||||
|
||||
@@ -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"
|
||||
|
||||
@@ -87,6 +87,14 @@ func TestBuilderAcc_forceDeleteSnapshot(t *testing.T) {
|
||||
})
|
||||
}
|
||||
|
||||
func TestBuilderAcc_testSourceAmiFilter(t *testing.T) {
|
||||
builderT.Test(t, builderT.TestCase{
|
||||
PreCheck: func() { testAccPreCheck(t) },
|
||||
Builder: &Builder{},
|
||||
Template: testSourceAmiFilter,
|
||||
})
|
||||
}
|
||||
|
||||
func checkSnapshotsDeleted(snapshotIds []*string) builderT.TestCheckFunc {
|
||||
return func(artifacts []packer.Artifact) error {
|
||||
// Verify the snapshots are gone
|
||||
@@ -344,6 +352,29 @@ const testBuilderAccEncrypted = `
|
||||
}
|
||||
`
|
||||
|
||||
const testSourceAmiFilter = `
|
||||
{
|
||||
"builders": [{
|
||||
"type": "test",
|
||||
"ami_name": "packer-filter-test-{{timestamp}}",
|
||||
"region": "us-east-1",
|
||||
"instance_type": "t2.micro",
|
||||
"ssh_username": "ubuntu",
|
||||
"source_ami_filter": {
|
||||
"filters": {
|
||||
"name": "*ubuntu-xenial-16.04-amd64-server-*",
|
||||
"root-device-type": "ebs",
|
||||
"virtualization-type": "hvm"
|
||||
},
|
||||
"most_recent": true,
|
||||
"owners": [
|
||||
"099720109477"
|
||||
]
|
||||
}
|
||||
}]
|
||||
}
|
||||
`
|
||||
|
||||
func buildForceDeregisterConfig(val, name string) string {
|
||||
return fmt.Sprintf(testBuilderAccForceDeregister, val, name)
|
||||
}
|
||||
|
||||
@@ -9,6 +9,7 @@ import (
|
||||
"context"
|
||||
"errors"
|
||||
"fmt"
|
||||
|
||||
"github.com/aws/aws-sdk-go/service/ec2"
|
||||
"github.com/aws/aws-sdk-go/service/iam"
|
||||
"github.com/hashicorp/hcl/v2/hcldec"
|
||||
|
||||
@@ -1,9 +1,10 @@
|
||||
package ebssurrogate
|
||||
|
||||
import (
|
||||
"github.com/hashicorp/packer/builder/amazon/common"
|
||||
"testing"
|
||||
|
||||
"github.com/hashicorp/packer/builder/amazon/common"
|
||||
|
||||
"github.com/hashicorp/packer/packer"
|
||||
)
|
||||
|
||||
|
||||
@@ -9,13 +9,13 @@ import (
|
||||
"context"
|
||||
"errors"
|
||||
"fmt"
|
||||
"github.com/hashicorp/packer/builder"
|
||||
"os"
|
||||
"strings"
|
||||
|
||||
"github.com/aws/aws-sdk-go/service/ec2"
|
||||
"github.com/aws/aws-sdk-go/service/iam"
|
||||
"github.com/hashicorp/hcl/v2/hcldec"
|
||||
"github.com/hashicorp/packer/builder"
|
||||
awscommon "github.com/hashicorp/packer/builder/amazon/common"
|
||||
"github.com/hashicorp/packer/common"
|
||||
"github.com/hashicorp/packer/helper/communicator"
|
||||
|
||||
@@ -55,12 +55,12 @@ const (
|
||||
)
|
||||
|
||||
var (
|
||||
reCaptureContainerName = regexp.MustCompile("^[a-z0-9][a-z0-9\\-]{2,62}$")
|
||||
reCaptureNamePrefix = regexp.MustCompile("^[A-Za-z0-9][A-Za-z0-9_\\-\\.]{0,23}$")
|
||||
reCaptureContainerName = regexp.MustCompile(`^[a-z0-9][a-z0-9\-]{2,62}$`)
|
||||
reCaptureNamePrefix = regexp.MustCompile(`^[A-Za-z0-9][A-Za-z0-9_\-\.]{0,23}$`)
|
||||
reManagedDiskName = regexp.MustCompile(validManagedDiskName)
|
||||
reResourceGroupName = regexp.MustCompile(validResourceGroupNameRe)
|
||||
reSnapshotName = regexp.MustCompile("^[A-Za-z0-9_]{1,79}$")
|
||||
reSnapshotPrefix = regexp.MustCompile("^[A-Za-z0-9_]{1,59}$")
|
||||
reSnapshotName = regexp.MustCompile(`^[A-Za-z0-9_]{1,79}$`)
|
||||
reSnapshotPrefix = regexp.MustCompile(`^[A-Za-z0-9_]{1,59}$`)
|
||||
)
|
||||
|
||||
type PlanInformation struct {
|
||||
@@ -371,8 +371,8 @@ type Config struct {
|
||||
AllowedInboundIpAddresses []string `mapstructure:"allowed_inbound_ip_addresses"`
|
||||
|
||||
// Runtime Values
|
||||
UserName string
|
||||
Password string
|
||||
UserName string `mapstructure-to-hcl2:",skip"`
|
||||
Password string `mapstructure-to-hcl2:",skip"`
|
||||
tmpAdminPassword string
|
||||
tmpCertificatePassword string
|
||||
tmpResourceGroupName string
|
||||
|
||||
@@ -66,8 +66,6 @@ type FlatConfig struct {
|
||||
AdditionalDiskSize []int32 `mapstructure:"disk_additional_size" required:"false" cty:"disk_additional_size"`
|
||||
DiskCachingType *string `mapstructure:"disk_caching_type" required:"false" cty:"disk_caching_type"`
|
||||
AllowedInboundIpAddresses []string `mapstructure:"allowed_inbound_ip_addresses" cty:"allowed_inbound_ip_addresses"`
|
||||
UserName *string `cty:"user_name"`
|
||||
Password *string `cty:"password"`
|
||||
Type *string `mapstructure:"communicator" cty:"communicator"`
|
||||
PauseBeforeConnect *string `mapstructure:"pause_before_connecting" cty:"pause_before_connecting"`
|
||||
SSHHost *string `mapstructure:"ssh_host" cty:"ssh_host"`
|
||||
@@ -180,8 +178,6 @@ func (*FlatConfig) HCL2Spec() map[string]hcldec.Spec {
|
||||
"disk_additional_size": &hcldec.AttrSpec{Name: "disk_additional_size", Type: cty.List(cty.Number), Required: false},
|
||||
"disk_caching_type": &hcldec.AttrSpec{Name: "disk_caching_type", Type: cty.String, Required: false},
|
||||
"allowed_inbound_ip_addresses": &hcldec.AttrSpec{Name: "allowed_inbound_ip_addresses", Type: cty.List(cty.String), Required: false},
|
||||
"user_name": &hcldec.AttrSpec{Name: "user_name", Type: cty.String, Required: false},
|
||||
"password": &hcldec.AttrSpec{Name: "password", Type: cty.String, Required: false},
|
||||
"communicator": &hcldec.AttrSpec{Name: "communicator", Type: cty.String, Required: false},
|
||||
"pause_before_connecting": &hcldec.AttrSpec{Name: "pause_before_connecting", Type: cty.String, Required: false},
|
||||
"ssh_host": &hcldec.AttrSpec{Name: "ssh_host", Type: cty.String, Required: false},
|
||||
|
||||
@@ -120,7 +120,7 @@ type Config struct {
|
||||
// provisioners should connect to the local IP address of the instance.
|
||||
UseLocalIPAddress bool `mapstructure:"use_local_ip_address" required:"false"`
|
||||
// User data to launch with the instance. This is a
|
||||
// template engine see User Data bellow for
|
||||
// template engine; see "User Data" bellow for
|
||||
// more details. Packer will not automatically wait for a user script to
|
||||
// finish before shutting down the instance this must be handled in a
|
||||
// provisioner.
|
||||
|
||||
@@ -186,12 +186,9 @@ func (c *Communicator) UploadDir(dst string, src string, exclude []string) error
|
||||
|
||||
*/
|
||||
|
||||
var dockerSource string
|
||||
|
||||
dockerSource := src
|
||||
if src[len(src)-1] == '/' {
|
||||
dockerSource = fmt.Sprintf("%s.", src)
|
||||
} else {
|
||||
dockerSource = fmt.Sprintf("%s", src)
|
||||
}
|
||||
|
||||
// Make the directory, then copy into it
|
||||
|
||||
@@ -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) {
|
||||
|
||||
+22
-10
@@ -91,17 +91,29 @@ type Config struct {
|
||||
// Can be useful when using custom api_url. Defaults to public.
|
||||
PublicNetAdpService string `mapstructure:"public_netadp_service" required:"false"`
|
||||
|
||||
ChrootDisk bool `mapstructure:"chroot_disk"`
|
||||
ChrootDiskSize float32 `mapstructure:"chroot_disk_size"`
|
||||
ChrootDiskType string `mapstructure:"chroot_disk_type"`
|
||||
ChrootMountPath string `mapstructure:"chroot_mount_path"`
|
||||
ChrootMounts [][]string `mapstructure:"chroot_mounts"`
|
||||
ChrootCopyFiles []string `mapstructure:"chroot_copy_files"`
|
||||
ChrootCommandWrapper string `mapstructure:"chroot_command_wrapper"`
|
||||
ChrootDisk bool `mapstructure:"chroot_disk"`
|
||||
ChrootDiskSize float32 `mapstructure:"chroot_disk_size"`
|
||||
ChrootDiskType string `mapstructure:"chroot_disk_type"`
|
||||
ChrootMountPath string `mapstructure:"chroot_mount_path"`
|
||||
ChrootMounts [][]string `mapstructure:"chroot_mounts"`
|
||||
ChrootCopyFiles []string `mapstructure:"chroot_copy_files"`
|
||||
// How to run shell commands. This defaults to {{.Command}}. This may be
|
||||
// useful to set if you want to set environmental variables or perhaps run
|
||||
// it with sudo or so on. This is a configuration template where the
|
||||
// .Command variable is replaced with the command to be run. Defaults to
|
||||
// {{.Command}}.
|
||||
ChrootCommandWrapper string `mapstructure:"chroot_command_wrapper"`
|
||||
|
||||
MountOptions []string `mapstructure:"mount_options"`
|
||||
MountPartition string `mapstructure:"mount_partition"`
|
||||
PreMountCommands []string `mapstructure:"pre_mount_commands"`
|
||||
MountOptions []string `mapstructure:"mount_options"`
|
||||
MountPartition string `mapstructure:"mount_partition"`
|
||||
// A series of commands to execute after attaching the root volume and
|
||||
// before mounting the chroot. This is not required unless using
|
||||
// from_scratch. If so, this should include any partitioning and filesystem
|
||||
// creation commands. The path to the device is provided by {{.Device}}.
|
||||
PreMountCommands []string `mapstructure:"pre_mount_commands"`
|
||||
// As pre_mount_commands, but the commands are executed after mounting the
|
||||
// root device and before the extra mount and copy steps. The device and
|
||||
// mount path are provided by {{.Device}} and {{.MountPath}}.
|
||||
PostMountCommands []string `mapstructure:"post_mount_commands"`
|
||||
// List of SSH keys by name or id to be added
|
||||
// to the server on launch.
|
||||
|
||||
@@ -148,6 +148,21 @@ type CommonConfig struct {
|
||||
// built. When this value is set to true, the machine will start without a
|
||||
// console.
|
||||
Headless bool `mapstructure:"headless" required:"false"`
|
||||
// When configured, determines the device or device type that is given preferential
|
||||
// treatment when choosing a boot device.
|
||||
//
|
||||
// For Generation 1:
|
||||
// - `IDE`
|
||||
// - `CD` *or* `DVD`
|
||||
// - `Floppy`
|
||||
// - `NET`
|
||||
//
|
||||
// For Generation 2:
|
||||
// - `IDE:x:y`
|
||||
// - `SCSI:x:y`
|
||||
// - `CD` *or* `DVD`
|
||||
// - `NET`
|
||||
FirstBootDevice string `mapstructure:"first_boot_device" required:"false"`
|
||||
}
|
||||
|
||||
func (c *CommonConfig) Prepare(ctx *interpolate.Context, pc *common.PackerConfig) ([]error, []string) {
|
||||
@@ -268,6 +283,13 @@ func (c *CommonConfig) Prepare(ctx *interpolate.Context, pc *common.PackerConfig
|
||||
}
|
||||
}
|
||||
|
||||
if c.FirstBootDevice != "" {
|
||||
_, _, _, err := ParseBootDeviceIdentifier(c.FirstBootDevice, c.Generation)
|
||||
if err != nil {
|
||||
errs = append(errs, fmt.Errorf("first_boot_device: %s", err))
|
||||
}
|
||||
}
|
||||
|
||||
if c.EnableVirtualizationExtensions {
|
||||
if c.EnableDynamicMemory {
|
||||
warning := fmt.Sprintf("For nested virtualization, when virtualization extension is enabled, " +
|
||||
|
||||
@@ -113,6 +113,8 @@ type Driver interface {
|
||||
|
||||
SetBootDvdDrive(string, uint, uint, uint) error
|
||||
|
||||
SetFirstBootDevice(string, string, uint, uint, uint) error
|
||||
|
||||
UnmountDvdDrive(string, uint, uint) error
|
||||
|
||||
DeleteDvdDrive(string, uint, uint) error
|
||||
|
||||
@@ -236,6 +236,14 @@ type DriverMock struct {
|
||||
SetBootDvdDrive_Generation uint
|
||||
SetBootDvdDrive_Err error
|
||||
|
||||
SetFirstBootDevice_Called bool
|
||||
SetFirstBootDevice_VmName string
|
||||
SetFirstBootDevice_ControllerType string
|
||||
SetFirstBootDevice_ControllerNumber uint
|
||||
SetFirstBootDevice_ControllerLocation uint
|
||||
SetFirstBootDevice_Generation uint
|
||||
SetFirstBootDevice_Err error
|
||||
|
||||
UnmountDvdDrive_Called bool
|
||||
UnmountDvdDrive_VmName string
|
||||
UnmountDvdDrive_ControllerNumber uint
|
||||
@@ -575,6 +583,17 @@ func (d *DriverMock) SetBootDvdDrive(vmName string, controllerNumber uint, contr
|
||||
return d.SetBootDvdDrive_Err
|
||||
}
|
||||
|
||||
func (d *DriverMock) SetFirstBootDevice(vmName string, controllerType string, controllerNumber uint,
|
||||
controllerLocation uint, generation uint) error {
|
||||
d.SetFirstBootDevice_Called = true
|
||||
d.SetFirstBootDevice_VmName = vmName
|
||||
d.SetFirstBootDevice_ControllerType = controllerType
|
||||
d.SetFirstBootDevice_ControllerNumber = controllerNumber
|
||||
d.SetFirstBootDevice_ControllerLocation = controllerLocation
|
||||
d.SetFirstBootDevice_Generation = generation
|
||||
return d.SetFirstBootDevice_Err
|
||||
}
|
||||
|
||||
func (d *DriverMock) UnmountDvdDrive(vmName string, controllerNumber uint, controllerLocation uint) error {
|
||||
d.UnmountDvdDrive_Called = true
|
||||
d.UnmountDvdDrive_VmName = vmName
|
||||
|
||||
@@ -267,6 +267,11 @@ func (d *HypervPS4Driver) SetBootDvdDrive(vmName string, controllerNumber uint,
|
||||
return hyperv.SetBootDvdDrive(vmName, controllerNumber, controllerLocation, generation)
|
||||
}
|
||||
|
||||
func (d *HypervPS4Driver) SetFirstBootDevice(vmName string, controllerType string, controllerNumber uint,
|
||||
controllerLocation uint, generation uint) error {
|
||||
return hyperv.SetFirstBootDevice(vmName, controllerType, controllerNumber, controllerLocation, generation)
|
||||
}
|
||||
|
||||
func (d *HypervPS4Driver) UnmountDvdDrive(vmName string, controllerNumber uint, controllerLocation uint) error {
|
||||
return hyperv.UnmountDvdDrive(vmName, controllerNumber, controllerLocation)
|
||||
}
|
||||
|
||||
@@ -12,7 +12,8 @@ import (
|
||||
)
|
||||
|
||||
type StepMountDvdDrive struct {
|
||||
Generation uint
|
||||
Generation uint
|
||||
FirstBootDevice string
|
||||
}
|
||||
|
||||
func (s *StepMountDvdDrive) Run(ctx context.Context, state multistep.StateBag) multistep.StepAction {
|
||||
@@ -57,13 +58,24 @@ func (s *StepMountDvdDrive) Run(ctx context.Context, state multistep.StateBag) m
|
||||
|
||||
state.Put("os.dvd.properties", dvdControllerProperties)
|
||||
|
||||
ui.Say(fmt.Sprintf("Setting boot drive to os dvd drive %s ...", isoPath))
|
||||
err = driver.SetBootDvdDrive(vmName, controllerNumber, controllerLocation, s.Generation)
|
||||
if err != nil {
|
||||
err := fmt.Errorf(errorMsg, err)
|
||||
state.Put("error", err)
|
||||
ui.Error(err.Error())
|
||||
return multistep.ActionHalt
|
||||
// the "first_boot_device" setting has precedence over the legacy boot order
|
||||
// configuration, but only if its been assigned a value.
|
||||
|
||||
if s.FirstBootDevice == "" {
|
||||
|
||||
if s.Generation > 1 {
|
||||
// only print this message for Gen2, it's not a true statement for Gen1 VMs
|
||||
ui.Say(fmt.Sprintf("Setting boot drive to os dvd drive %s ...", isoPath))
|
||||
}
|
||||
|
||||
err = driver.SetBootDvdDrive(vmName, controllerNumber, controllerLocation, s.Generation)
|
||||
if err != nil {
|
||||
err := fmt.Errorf(errorMsg, err)
|
||||
state.Put("error", err)
|
||||
ui.Error(err.Error())
|
||||
return multistep.ActionHalt
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
ui.Say(fmt.Sprintf("Mounting os dvd drive %s ...", isoPath))
|
||||
|
||||
@@ -0,0 +1,160 @@
|
||||
package common
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"regexp"
|
||||
"strconv"
|
||||
"strings"
|
||||
|
||||
"github.com/hashicorp/packer/helper/multistep"
|
||||
"github.com/hashicorp/packer/packer"
|
||||
)
|
||||
|
||||
type StepSetFirstBootDevice struct {
|
||||
Generation uint
|
||||
FirstBootDevice string
|
||||
}
|
||||
|
||||
func ParseBootDeviceIdentifier(deviceIdentifier string, generation uint) (string, uint, uint, error) {
|
||||
|
||||
// all input strings are forced to upperCase for comparison, I believe this is
|
||||
// safe as all of our values are 7bit ASCII clean.
|
||||
|
||||
lookupDeviceIdentifier := strings.ToUpper(deviceIdentifier)
|
||||
|
||||
if generation == 1 {
|
||||
|
||||
// Gen1 values are a simple set of if/then/else values, which we coalesce into a map
|
||||
// here for simplicity
|
||||
|
||||
lookupTable := map[string]string{
|
||||
"FLOPPY": "FLOPPY",
|
||||
"IDE": "IDE",
|
||||
"NET": "NET",
|
||||
"CD": "CD",
|
||||
"DVD": "CD",
|
||||
}
|
||||
|
||||
controllerType, isDefined := lookupTable[lookupDeviceIdentifier]
|
||||
if !isDefined {
|
||||
|
||||
return "", 0, 0, fmt.Errorf("The value %q is not a properly formatted device group identifier.", deviceIdentifier)
|
||||
|
||||
}
|
||||
|
||||
// success
|
||||
return controllerType, 0, 0, nil
|
||||
}
|
||||
|
||||
// everything else is treated as generation 2... the first set of lookups covers
|
||||
// the simple options..
|
||||
|
||||
lookupTable := map[string]string{
|
||||
"CD": "CD",
|
||||
"DVD": "CD",
|
||||
"NET": "NET",
|
||||
}
|
||||
|
||||
controllerType, isDefined := lookupTable[lookupDeviceIdentifier]
|
||||
if isDefined {
|
||||
|
||||
// these types do not require controllerNumber or controllerLocation
|
||||
return controllerType, 0, 0, nil
|
||||
|
||||
}
|
||||
|
||||
// not a simple option, check for a controllerType:controllerNumber:controllerLocation formatted
|
||||
// device..
|
||||
|
||||
r, err := regexp.Compile(`^(IDE|SCSI):(\d+):(\d+)$`)
|
||||
if err != nil {
|
||||
return "", 0, 0, err
|
||||
}
|
||||
|
||||
controllerMatch := r.FindStringSubmatch(lookupDeviceIdentifier)
|
||||
if controllerMatch != nil {
|
||||
|
||||
var controllerLocation int64
|
||||
var controllerNumber int64
|
||||
|
||||
// NOTE: controllerNumber and controllerLocation cannot be negative, the regex expression
|
||||
// would not have matched if either number was signed
|
||||
|
||||
controllerNumber, err = strconv.ParseInt(controllerMatch[2], 10, 8)
|
||||
if err == nil {
|
||||
|
||||
controllerLocation, err = strconv.ParseInt(controllerMatch[3], 10, 8)
|
||||
if err == nil {
|
||||
|
||||
return controllerMatch[1], uint(controllerNumber), uint(controllerLocation), nil
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
return "", 0, 0, err
|
||||
|
||||
}
|
||||
|
||||
return "", 0, 0, fmt.Errorf("The value %q is not a properly formatted device identifier.", deviceIdentifier)
|
||||
}
|
||||
|
||||
func (s *StepSetFirstBootDevice) Run(ctx context.Context, state multistep.StateBag) multistep.StepAction {
|
||||
|
||||
driver := state.Get("driver").(Driver)
|
||||
ui := state.Get("ui").(packer.Ui)
|
||||
vmName := state.Get("vmName").(string)
|
||||
|
||||
if s.FirstBootDevice != "" {
|
||||
|
||||
controllerType, controllerNumber, controllerLocation, err := ParseBootDeviceIdentifier(s.FirstBootDevice, s.Generation)
|
||||
if err == nil {
|
||||
|
||||
switch {
|
||||
|
||||
case controllerType == "CD":
|
||||
{
|
||||
// the "DVD" controller is special, we only apply the setting if we actually mounted
|
||||
// an ISO and only if that was mounted as the "IsoUrl" not a secondary ISO.
|
||||
|
||||
dvdControllerState := state.Get("os.dvd.properties")
|
||||
if dvdControllerState == nil {
|
||||
|
||||
ui.Say("First Boot Device is DVD, but no primary ISO mounted. Ignoring.")
|
||||
return multistep.ActionContinue
|
||||
|
||||
}
|
||||
|
||||
ui.Say(fmt.Sprintf("Setting boot device to %q", s.FirstBootDevice))
|
||||
dvdController := dvdControllerState.(DvdControllerProperties)
|
||||
err = driver.SetFirstBootDevice(vmName, controllerType, dvdController.ControllerNumber, dvdController.ControllerLocation, s.Generation)
|
||||
|
||||
}
|
||||
|
||||
default:
|
||||
{
|
||||
// anything else, we just pass as is..
|
||||
ui.Say(fmt.Sprintf("Setting boot device to %q", s.FirstBootDevice))
|
||||
err = driver.SetFirstBootDevice(vmName, controllerType, controllerNumber, controllerLocation, s.Generation)
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
if err != nil {
|
||||
err := fmt.Errorf("Error setting first boot device: %s", err)
|
||||
state.Put("error", err)
|
||||
ui.Error(err.Error())
|
||||
return multistep.ActionHalt
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
return multistep.ActionContinue
|
||||
}
|
||||
|
||||
func (s *StepSetFirstBootDevice) Cleanup(state multistep.StateBag) {
|
||||
// do nothing
|
||||
}
|
||||
@@ -0,0 +1,170 @@
|
||||
package common
|
||||
|
||||
import (
|
||||
"context"
|
||||
"testing"
|
||||
|
||||
"github.com/hashicorp/packer/helper/multistep"
|
||||
)
|
||||
|
||||
type parseBootDeviceIdentifierTest struct {
|
||||
generation uint
|
||||
deviceIdentifier string
|
||||
controllerType string
|
||||
controllerNumber uint
|
||||
controllerLocation uint
|
||||
failInParse bool // true if ParseBootDeviceIdentifier should return an error
|
||||
haltStep bool // true if Step.Run should return Halt action
|
||||
shouldCallSet bool // true if driver.SetFirstBootDevice should have been called
|
||||
setDvdProps bool // true to set DvdDeviceProperties state
|
||||
}
|
||||
|
||||
var parseIdentifierTests = [...]parseBootDeviceIdentifierTest{
|
||||
{1, "IDE", "IDE", 0, 0, false, false, true, false},
|
||||
{1, "idE", "IDE", 0, 0, false, false, true, false},
|
||||
{1, "CD", "CD", 0, 0, false, false, false, false},
|
||||
{1, "CD", "CD", 0, 0, false, false, true, true},
|
||||
{1, "cD", "CD", 0, 0, false, false, false, false},
|
||||
{1, "DVD", "CD", 0, 0, false, false, false, false},
|
||||
{1, "DVD", "CD", 0, 0, false, false, true, true},
|
||||
{1, "Dvd", "CD", 0, 0, false, false, false, false},
|
||||
{1, "FLOPPY", "FLOPPY", 0, 0, false, false, true, false},
|
||||
{1, "FloppY", "FLOPPY", 0, 0, false, false, true, false},
|
||||
{1, "NET", "NET", 0, 0, false, false, true, false},
|
||||
{1, "net", "NET", 0, 0, false, false, true, false},
|
||||
{1, "", "", 0, 0, true, false, false, false},
|
||||
{1, "bad", "", 0, 0, true, true, false, false},
|
||||
{1, "IDE:0:0", "", 0, 0, true, true, true, false},
|
||||
{1, "SCSI:0:0", "", 0, 0, true, true, true, false},
|
||||
{2, "IDE", "", 0, 0, true, true, true, false},
|
||||
{2, "idE", "", 0, 0, true, true, true, false},
|
||||
{2, "CD", "CD", 0, 0, false, false, false, false},
|
||||
{2, "CD", "CD", 0, 0, false, false, true, true},
|
||||
{2, "cD", "CD", 0, 0, false, false, false, false},
|
||||
{2, "DVD", "CD", 0, 0, false, false, false, false},
|
||||
{2, "DVD", "CD", 0, 0, false, false, true, true},
|
||||
{2, "Dvd", "CD", 0, 0, false, false, false, false},
|
||||
{2, "FLOPPY", "", 0, 0, true, true, true, false},
|
||||
{2, "FloppY", "", 0, 0, true, true, true, false},
|
||||
{2, "NET", "NET", 0, 0, false, false, true, false},
|
||||
{2, "net", "NET", 0, 0, false, false, true, false},
|
||||
{2, "", "", 0, 0, true, false, false, false},
|
||||
{2, "bad", "", 0, 0, true, true, false, false},
|
||||
{2, "IDE:0:0", "IDE", 0, 0, false, false, true, false},
|
||||
{2, "SCSI:0:0", "SCSI", 0, 0, false, false, true, false},
|
||||
{2, "Ide:0:0", "IDE", 0, 0, false, false, true, false},
|
||||
{2, "sCsI:0:0", "SCSI", 0, 0, false, false, true, false},
|
||||
{2, "IDEscsi:0:0", "", 0, 0, true, true, false, false},
|
||||
{2, "SCSIide:0:0", "", 0, 0, true, true, false, false},
|
||||
{2, "IDE:0", "", 0, 0, true, true, false, false},
|
||||
{2, "SCSI:0", "", 0, 0, true, true, false, false},
|
||||
{2, "IDE:0:a", "", 0, 0, true, true, false, false},
|
||||
{2, "SCSI:0:a", "", 0, 0, true, true, false, false},
|
||||
{2, "IDE:0:653", "", 0, 0, true, true, false, false},
|
||||
{2, "SCSI:-10:0", "", 0, 0, true, true, false, false},
|
||||
}
|
||||
|
||||
func TestStepSetFirstBootDevice_impl(t *testing.T) {
|
||||
var _ multistep.Step = new(StepSetFirstBootDevice)
|
||||
}
|
||||
|
||||
func TestStepSetFirstBootDevice_ParseIdentifier(t *testing.T) {
|
||||
|
||||
for _, identifierTest := range parseIdentifierTests {
|
||||
|
||||
controllerType, controllerNumber, controllerLocation, err := ParseBootDeviceIdentifier(
|
||||
identifierTest.deviceIdentifier,
|
||||
identifierTest.generation)
|
||||
|
||||
if (err != nil) != identifierTest.failInParse {
|
||||
|
||||
t.Fatalf("Test %q (gen %v): failInParse: %v but err: %v", identifierTest.deviceIdentifier,
|
||||
identifierTest.generation, identifierTest.failInParse, err)
|
||||
|
||||
}
|
||||
|
||||
switch {
|
||||
|
||||
case controllerType != identifierTest.controllerType:
|
||||
t.Fatalf("Test %q (gen %v): controllerType: %q != %q", identifierTest.deviceIdentifier, identifierTest.generation,
|
||||
identifierTest.controllerType, controllerType)
|
||||
|
||||
case controllerNumber != identifierTest.controllerNumber:
|
||||
t.Fatalf("Test %q (gen %v): controllerNumber: %v != %v", identifierTest.deviceIdentifier, identifierTest.generation,
|
||||
identifierTest.controllerNumber, controllerNumber)
|
||||
|
||||
case controllerLocation != identifierTest.controllerLocation:
|
||||
t.Fatalf("Test %q (gen %v): controllerLocation: %v != %v", identifierTest.deviceIdentifier, identifierTest.generation,
|
||||
identifierTest.controllerLocation, controllerLocation)
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func TestStepSetFirstBootDevice(t *testing.T) {
|
||||
|
||||
step := new(StepSetFirstBootDevice)
|
||||
|
||||
for _, identifierTest := range parseIdentifierTests {
|
||||
|
||||
state := testState(t)
|
||||
driver := state.Get("driver").(*DriverMock)
|
||||
|
||||
// requires the vmName state value
|
||||
vmName := "foo"
|
||||
state.Put("vmName", vmName)
|
||||
|
||||
// pretend that we mounted a DVD somewhere (CD:0:0)
|
||||
if identifierTest.setDvdProps {
|
||||
var dvdControllerProperties DvdControllerProperties
|
||||
dvdControllerProperties.ControllerNumber = 0
|
||||
dvdControllerProperties.ControllerLocation = 0
|
||||
dvdControllerProperties.Existing = false
|
||||
state.Put("os.dvd.properties", dvdControllerProperties)
|
||||
}
|
||||
|
||||
step.Generation = identifierTest.generation
|
||||
step.FirstBootDevice = identifierTest.deviceIdentifier
|
||||
|
||||
action := step.Run(context.Background(), state)
|
||||
if (action != multistep.ActionContinue) != identifierTest.haltStep {
|
||||
t.Fatalf("Test %q (gen %v): Bad action: %v", identifierTest.deviceIdentifier, identifierTest.generation, action)
|
||||
}
|
||||
|
||||
if identifierTest.haltStep {
|
||||
|
||||
if _, ok := state.GetOk("error"); !ok {
|
||||
t.Fatalf("Test %q (gen %v): Should have error", identifierTest.deviceIdentifier, identifierTest.generation)
|
||||
}
|
||||
|
||||
// don't perform the remaining checks..
|
||||
continue
|
||||
|
||||
} else {
|
||||
|
||||
if _, ok := state.GetOk("error"); ok {
|
||||
t.Fatalf("Test %q (gen %v): Should NOT have error", identifierTest.deviceIdentifier, identifierTest.generation)
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
if driver.SetFirstBootDevice_Called != identifierTest.shouldCallSet {
|
||||
if identifierTest.shouldCallSet {
|
||||
t.Fatalf("Test %q (gen %v): Should have called SetFirstBootDevice", identifierTest.deviceIdentifier, identifierTest.generation)
|
||||
}
|
||||
|
||||
t.Fatalf("Test %q (gen %v): Should NOT have called SetFirstBootDevice", identifierTest.deviceIdentifier, identifierTest.generation)
|
||||
}
|
||||
|
||||
if (driver.SetFirstBootDevice_Called) &&
|
||||
((driver.SetFirstBootDevice_VmName != vmName) ||
|
||||
(driver.SetFirstBootDevice_ControllerType != identifierTest.controllerType) ||
|
||||
(driver.SetFirstBootDevice_ControllerNumber != identifierTest.controllerNumber) ||
|
||||
(driver.SetFirstBootDevice_ControllerLocation != identifierTest.controllerLocation) ||
|
||||
(driver.SetFirstBootDevice_Generation != identifierTest.generation)) {
|
||||
|
||||
t.Fatalf("Test %q (gen %v): Called SetFirstBootDevice with unexpected arguments.", identifierTest.deviceIdentifier, identifierTest.generation)
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -242,7 +242,8 @@ func (b *Builder) Run(ctx context.Context, ui packer.Ui, hook packer.Hook) (pack
|
||||
&hypervcommon.StepEnableIntegrationService{},
|
||||
|
||||
&hypervcommon.StepMountDvdDrive{
|
||||
Generation: b.config.Generation,
|
||||
Generation: b.config.Generation,
|
||||
FirstBootDevice: b.config.FirstBootDevice,
|
||||
},
|
||||
&hypervcommon.StepMountFloppydrive{
|
||||
Generation: b.config.Generation,
|
||||
@@ -264,6 +265,11 @@ func (b *Builder) Run(ctx context.Context, ui packer.Ui, hook packer.Hook) (pack
|
||||
SwitchVlanId: b.config.SwitchVlanId,
|
||||
},
|
||||
|
||||
&hypervcommon.StepSetFirstBootDevice{
|
||||
Generation: b.config.Generation,
|
||||
FirstBootDevice: b.config.FirstBootDevice,
|
||||
},
|
||||
|
||||
&hypervcommon.StepRun{
|
||||
Headless: b.config.Headless,
|
||||
SwitchName: b.config.SwitchName,
|
||||
|
||||
@@ -97,6 +97,7 @@ type FlatConfig struct {
|
||||
SkipCompaction *bool `mapstructure:"skip_compaction" required:"false" cty:"skip_compaction"`
|
||||
SkipExport *bool `mapstructure:"skip_export" required:"false" cty:"skip_export"`
|
||||
Headless *bool `mapstructure:"headless" required:"false" cty:"headless"`
|
||||
FirstBootDevice *string `mapstructure:"first_boot_device" required:"false" cty:"first_boot_device"`
|
||||
ShutdownCommand *string `mapstructure:"shutdown_command" required:"false" cty:"shutdown_command"`
|
||||
ShutdownTimeout *string `mapstructure:"shutdown_timeout" required:"false" cty:"shutdown_timeout"`
|
||||
DiskSize *uint `mapstructure:"disk_size" required:"false" cty:"disk_size"`
|
||||
@@ -205,6 +206,7 @@ func (*FlatConfig) HCL2Spec() map[string]hcldec.Spec {
|
||||
"skip_compaction": &hcldec.AttrSpec{Name: "skip_compaction", Type: cty.Bool, Required: false},
|
||||
"skip_export": &hcldec.AttrSpec{Name: "skip_export", Type: cty.Bool, Required: false},
|
||||
"headless": &hcldec.AttrSpec{Name: "headless", Type: cty.Bool, Required: false},
|
||||
"first_boot_device": &hcldec.AttrSpec{Name: "first_boot_device", Type: cty.String, Required: false},
|
||||
"shutdown_command": &hcldec.AttrSpec{Name: "shutdown_command", Type: cty.String, Required: false},
|
||||
"shutdown_timeout": &hcldec.AttrSpec{Name: "shutdown_timeout", Type: cty.String, Required: false},
|
||||
"disk_size": &hcldec.AttrSpec{Name: "disk_size", Type: cty.Number, Required: false},
|
||||
|
||||
@@ -282,7 +282,8 @@ func (b *Builder) Run(ctx context.Context, ui packer.Ui, hook packer.Hook) (pack
|
||||
&hypervcommon.StepEnableIntegrationService{},
|
||||
|
||||
&hypervcommon.StepMountDvdDrive{
|
||||
Generation: b.config.Generation,
|
||||
Generation: b.config.Generation,
|
||||
FirstBootDevice: b.config.FirstBootDevice,
|
||||
},
|
||||
&hypervcommon.StepMountFloppydrive{
|
||||
Generation: b.config.Generation,
|
||||
@@ -304,6 +305,11 @@ func (b *Builder) Run(ctx context.Context, ui packer.Ui, hook packer.Hook) (pack
|
||||
SwitchVlanId: b.config.SwitchVlanId,
|
||||
},
|
||||
|
||||
&hypervcommon.StepSetFirstBootDevice{
|
||||
Generation: b.config.Generation,
|
||||
FirstBootDevice: b.config.FirstBootDevice,
|
||||
},
|
||||
|
||||
&hypervcommon.StepRun{
|
||||
Headless: b.config.Headless,
|
||||
SwitchName: b.config.SwitchName,
|
||||
|
||||
@@ -97,6 +97,7 @@ type FlatConfig struct {
|
||||
SkipCompaction *bool `mapstructure:"skip_compaction" required:"false" cty:"skip_compaction"`
|
||||
SkipExport *bool `mapstructure:"skip_export" required:"false" cty:"skip_export"`
|
||||
Headless *bool `mapstructure:"headless" required:"false" cty:"headless"`
|
||||
FirstBootDevice *string `mapstructure:"first_boot_device" required:"false" cty:"first_boot_device"`
|
||||
ShutdownCommand *string `mapstructure:"shutdown_command" required:"false" cty:"shutdown_command"`
|
||||
ShutdownTimeout *string `mapstructure:"shutdown_timeout" required:"false" cty:"shutdown_timeout"`
|
||||
CloneFromVMCXPath *string `mapstructure:"clone_from_vmcx_path" cty:"clone_from_vmcx_path"`
|
||||
@@ -207,6 +208,7 @@ func (*FlatConfig) HCL2Spec() map[string]hcldec.Spec {
|
||||
"skip_compaction": &hcldec.AttrSpec{Name: "skip_compaction", Type: cty.Bool, Required: false},
|
||||
"skip_export": &hcldec.AttrSpec{Name: "skip_export", Type: cty.Bool, Required: false},
|
||||
"headless": &hcldec.AttrSpec{Name: "headless", Type: cty.Bool, Required: false},
|
||||
"first_boot_device": &hcldec.AttrSpec{Name: "first_boot_device", Type: cty.String, Required: false},
|
||||
"shutdown_command": &hcldec.AttrSpec{Name: "shutdown_command", Type: cty.String, Required: false},
|
||||
"shutdown_timeout": &hcldec.AttrSpec{Name: "shutdown_timeout", Type: cty.String, Required: false},
|
||||
"clone_from_vmcx_path": &hcldec.AttrSpec{Name: "clone_from_vmcx_path", Type: cty.String, Required: false},
|
||||
|
||||
@@ -28,7 +28,7 @@ type Config struct {
|
||||
ContainerName string `mapstructure:"container_name" required:"false"`
|
||||
// Allows you to specify a wrapper command, such
|
||||
// as ssh so you can execute packer builds on a remote host. Defaults to
|
||||
// Empty.
|
||||
// `{{.Command}}`; i.e. no wrapper.
|
||||
CommandWrapper string `mapstructure:"command_wrapper" required:"false"`
|
||||
// The timeout in seconds to wait for the the
|
||||
// container to start. Defaults to 20 seconds.
|
||||
|
||||
@@ -2,9 +2,9 @@ package lxc
|
||||
|
||||
import (
|
||||
"context"
|
||||
"github.com/hashicorp/packer/common"
|
||||
"log"
|
||||
|
||||
"github.com/hashicorp/packer/common"
|
||||
"github.com/hashicorp/packer/helper/multistep"
|
||||
"github.com/hashicorp/packer/packer"
|
||||
)
|
||||
|
||||
@@ -20,7 +20,8 @@ type Config struct {
|
||||
OutputImage string `mapstructure:"output_image" required:"false"`
|
||||
ContainerName string `mapstructure:"container_name"`
|
||||
// Lets you prefix all builder commands, such as
|
||||
// with ssh for a remote build host. Defaults to "".
|
||||
// with ssh for a remote build host. Defaults to "{{.Command}}"; i.e. no
|
||||
// wrapper.
|
||||
CommandWrapper string `mapstructure:"command_wrapper" required:"false"`
|
||||
// The source image to use when creating the build
|
||||
// container. This can be a (local or remote) image (name or fingerprint).
|
||||
|
||||
@@ -2,9 +2,9 @@ package lxd
|
||||
|
||||
import (
|
||||
"context"
|
||||
"github.com/hashicorp/packer/common"
|
||||
"log"
|
||||
|
||||
"github.com/hashicorp/packer/common"
|
||||
"github.com/hashicorp/packer/helper/multistep"
|
||||
"github.com/hashicorp/packer/packer"
|
||||
)
|
||||
|
||||
@@ -25,6 +25,11 @@ func (a *NullArtifact) String() string {
|
||||
}
|
||||
|
||||
func (a *NullArtifact) State(name string) interface{} {
|
||||
if name == "generated_data" {
|
||||
return map[interface{}]interface{}{
|
||||
"ID": "Null",
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
|
||||
@@ -47,6 +47,7 @@ func (b *Builder) Run(ctx context.Context, ui packer.Ui, hook packer.Hook) (pack
|
||||
state := new(multistep.BasicStateBag)
|
||||
state.Put("hook", hook)
|
||||
state.Put("ui", ui)
|
||||
state.Put("instance_id", "Null")
|
||||
|
||||
// Run!
|
||||
b.runner = common.NewRunner(steps, b.config.PackerConfig, ui)
|
||||
|
||||
@@ -2,9 +2,9 @@ package chroot
|
||||
|
||||
import (
|
||||
"context"
|
||||
"github.com/hashicorp/packer/common"
|
||||
"log"
|
||||
|
||||
"github.com/hashicorp/packer/common"
|
||||
"github.com/hashicorp/packer/helper/multistep"
|
||||
"github.com/hashicorp/packer/packer"
|
||||
)
|
||||
|
||||
@@ -1,9 +1,10 @@
|
||||
package common
|
||||
|
||||
import (
|
||||
"time"
|
||||
|
||||
"github.com/hashicorp/packer/helper/communicator"
|
||||
"github.com/hashicorp/packer/template/interpolate"
|
||||
"time"
|
||||
)
|
||||
|
||||
// SSHConfig contains the configuration for SSH communicator.
|
||||
|
||||
@@ -1,12 +1,13 @@
|
||||
package common
|
||||
|
||||
import (
|
||||
"github.com/hashicorp/packer/helper/communicator"
|
||||
"github.com/hashicorp/packer/template/interpolate"
|
||||
"io/ioutil"
|
||||
"os"
|
||||
"testing"
|
||||
"time"
|
||||
|
||||
"github.com/hashicorp/packer/helper/communicator"
|
||||
"github.com/hashicorp/packer/template/interpolate"
|
||||
)
|
||||
|
||||
func testSSHConfig() *SSHConfig {
|
||||
|
||||
@@ -8,6 +8,7 @@ import (
|
||||
"log"
|
||||
"net/url"
|
||||
"os"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
"github.com/hashicorp/packer/common"
|
||||
@@ -213,6 +214,9 @@ func (c *Config) Prepare(raws ...interface{}) ([]string, error) {
|
||||
if c.Node == "" {
|
||||
errs = packer.MultiErrorAppend(errs, errors.New("node must be specified"))
|
||||
}
|
||||
if strings.ContainsAny(c.TemplateName, " ") {
|
||||
errs = packer.MultiErrorAppend(errs, errors.New("template_name must not contain spaces"))
|
||||
}
|
||||
for idx := range c.NICs {
|
||||
if c.NICs[idx].Bridge == "" {
|
||||
errs = packer.MultiErrorAppend(errs, errors.New(fmt.Sprintf("network_adapters[%d].bridge must be specified", idx)))
|
||||
|
||||
@@ -281,6 +281,10 @@ type Config struct {
|
||||
// This will also work with WinRM, just change the port forward in
|
||||
// `qemuargs` to map to WinRM's default port of `5985` or whatever value
|
||||
// you have the service set to listen on.
|
||||
//
|
||||
// This is a template engine and allows access to the following variables:
|
||||
// `{{ .HTTPIP }}`, `{{ .HTTPPort }}`, `{{ .HTTPDir }}`,
|
||||
// `{{ .OutputDir }}`, `{{ .Name }}`, and `{{ .SSHHostPort }}`
|
||||
QemuArgs [][]string `mapstructure:"qemuargs" required:"false"`
|
||||
// The name of the Qemu binary to look for. This
|
||||
// defaults to qemu-system-x86_64, but may need to be changed for
|
||||
|
||||
@@ -189,7 +189,7 @@ func (d *QemuDriver) Version() (string, error) {
|
||||
|
||||
versionOutput := strings.TrimSpace(stdout.String())
|
||||
log.Printf("Qemu --version output: %s", versionOutput)
|
||||
versionRe := regexp.MustCompile("[\\.[0-9]+]*")
|
||||
versionRe := regexp.MustCompile(`[\.[0-9]+]*`)
|
||||
matches := versionRe.FindStringSubmatch(versionOutput)
|
||||
if len(matches) == 0 {
|
||||
return "", fmt.Errorf("No version found: %s", versionOutput)
|
||||
|
||||
@@ -18,7 +18,7 @@ func (s *stepCopyDisk) Run(ctx context.Context, state multistep.StateBag) multis
|
||||
driver := state.Get("driver").(Driver)
|
||||
isoPath := state.Get("iso_path").(string)
|
||||
ui := state.Get("ui").(packer.Ui)
|
||||
path := filepath.Join(config.OutputDir, fmt.Sprintf("%s", config.VMName))
|
||||
path := filepath.Join(config.OutputDir, config.VMName)
|
||||
|
||||
command := []string{
|
||||
"convert",
|
||||
|
||||
@@ -29,13 +29,13 @@ func (s *stepCreateDisk) Run(ctx context.Context, state multistep.StateBag) mult
|
||||
ui.Say("Creating required virtual machine disks")
|
||||
// The 'main' or 'default' disk
|
||||
diskFullPaths = append(diskFullPaths, filepath.Join(config.OutputDir, name))
|
||||
diskSizes = append(diskSizes, fmt.Sprintf("%s", config.DiskSize))
|
||||
diskSizes = append(diskSizes, config.DiskSize)
|
||||
// Additional disks
|
||||
if len(config.AdditionalDiskSize) > 0 {
|
||||
for i, diskSize := range config.AdditionalDiskSize {
|
||||
path := filepath.Join(config.OutputDir, fmt.Sprintf("%s-%d", name, i+1))
|
||||
diskFullPaths = append(diskFullPaths, path)
|
||||
size := fmt.Sprintf("%s", diskSize)
|
||||
size := diskSize
|
||||
diskSizes = append(diskSizes, size)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -2,6 +2,7 @@ package qemu
|
||||
|
||||
import (
|
||||
"context"
|
||||
|
||||
"github.com/hashicorp/packer/common"
|
||||
"github.com/hashicorp/packer/helper/multistep"
|
||||
)
|
||||
|
||||
@@ -2,9 +2,10 @@ package qemu
|
||||
|
||||
import (
|
||||
"context"
|
||||
"testing"
|
||||
|
||||
"github.com/hashicorp/packer/common"
|
||||
"github.com/hashicorp/packer/helper/multistep"
|
||||
"testing"
|
||||
)
|
||||
|
||||
func TestStepHTTPIPDiscover_Run(t *testing.T) {
|
||||
|
||||
@@ -23,7 +23,7 @@ func (s *stepResizeDisk) Run(ctx context.Context, state multistep.StateBag) mult
|
||||
"resize",
|
||||
"-f", config.Format,
|
||||
path,
|
||||
fmt.Sprintf("%s", config.DiskSize),
|
||||
config.DiskSize,
|
||||
}
|
||||
if config.DiskImage == false {
|
||||
return multistep.ActionContinue
|
||||
|
||||
@@ -8,8 +8,6 @@ import (
|
||||
"os"
|
||||
|
||||
"github.com/hashicorp/packer/template/interpolate"
|
||||
"github.com/tencentcloud/tencentcloud-sdk-go/tencentcloud/common"
|
||||
"github.com/tencentcloud/tencentcloud-sdk-go/tencentcloud/common/profile"
|
||||
cvm "github.com/tencentcloud/tencentcloud-sdk-go/tencentcloud/cvm/v20170312"
|
||||
vpc "github.com/tencentcloud/tencentcloud-sdk-go/tencentcloud/vpc/v20170312"
|
||||
)
|
||||
@@ -81,17 +79,11 @@ func (cf *TencentCloudAccessConfig) Client() (*cvm.Client, *vpc.Client, error) {
|
||||
return nil, nil, fmt.Errorf("parameter zone must be set")
|
||||
}
|
||||
|
||||
credential := common.NewCredential(cf.SecretId, cf.SecretKey)
|
||||
cpf := profile.NewClientProfile()
|
||||
cpf.HttpProfile.ReqMethod = "POST"
|
||||
cpf.HttpProfile.ReqTimeout = 300
|
||||
cpf.Language = "en-US"
|
||||
|
||||
if cvm_client, err = cvm.NewClient(credential, cf.Region, cpf); err != nil {
|
||||
if cvm_client, err = NewCvmClient(cf.SecretId, cf.SecretKey, cf.Region); err != nil {
|
||||
return nil, nil, err
|
||||
}
|
||||
|
||||
if vpc_client, err = vpc.NewClient(credential, cf.Region, cpf); err != nil {
|
||||
if vpc_client, err = NewVpcClient(cf.SecretId, cf.SecretKey, cf.Region); err != nil {
|
||||
return nil, nil, err
|
||||
}
|
||||
|
||||
|
||||
@@ -10,16 +10,18 @@ import (
|
||||
"github.com/hashicorp/packer/common/retry"
|
||||
"github.com/hashicorp/packer/helper/multistep"
|
||||
"github.com/hashicorp/packer/packer"
|
||||
"github.com/tencentcloud/tencentcloud-sdk-go/tencentcloud/common"
|
||||
"github.com/tencentcloud/tencentcloud-sdk-go/tencentcloud/common/errors"
|
||||
"github.com/tencentcloud/tencentcloud-sdk-go/tencentcloud/common/profile"
|
||||
cvm "github.com/tencentcloud/tencentcloud-sdk-go/tencentcloud/cvm/v20170312"
|
||||
vpc "github.com/tencentcloud/tencentcloud-sdk-go/tencentcloud/vpc/v20170312"
|
||||
)
|
||||
|
||||
// DefaultWaitForInterval is sleep interval when wait statue
|
||||
const DefaultWaitForInterval = 5
|
||||
|
||||
// WaitForInstance wait for instance reaches statue
|
||||
func WaitForInstance(client *cvm.Client, instanceId string, status string, timeout int) error {
|
||||
ctx := context.TODO()
|
||||
func WaitForInstance(ctx context.Context, client *cvm.Client, instanceId string, status string, timeout int) error {
|
||||
req := cvm.NewDescribeInstancesRequest()
|
||||
req.InstanceIds = []*string{&instanceId}
|
||||
|
||||
@@ -50,54 +52,86 @@ func WaitForInstance(client *cvm.Client, instanceId string, status string, timeo
|
||||
}
|
||||
|
||||
// WaitForImageReady wait for image reaches statue
|
||||
func WaitForImageReady(client *cvm.Client, imageName string, status string, timeout int) error {
|
||||
ctx := context.TODO()
|
||||
req := cvm.NewDescribeImagesRequest()
|
||||
FILTER_IMAGE_NAME := "image-name"
|
||||
req.Filters = []*cvm.Filter{
|
||||
{
|
||||
Name: &FILTER_IMAGE_NAME,
|
||||
Values: []*string{&imageName},
|
||||
},
|
||||
}
|
||||
|
||||
func WaitForImageReady(ctx context.Context, client *cvm.Client, imageName string, status string, timeout int) error {
|
||||
for {
|
||||
var resp *cvm.DescribeImagesResponse
|
||||
err := Retry(ctx, func(ctx context.Context) error {
|
||||
var e error
|
||||
resp, e = client.DescribeImages(req)
|
||||
return e
|
||||
})
|
||||
image, err := GetImageByName(ctx, client, imageName)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
find := false
|
||||
for _, image := range resp.Response.ImageSet {
|
||||
if *image.ImageName == imageName && *image.ImageState == status {
|
||||
find = true
|
||||
break
|
||||
}
|
||||
}
|
||||
if find {
|
||||
break
|
||||
|
||||
if image != nil && *image.ImageState == status {
|
||||
return nil
|
||||
}
|
||||
|
||||
time.Sleep(DefaultWaitForInterval * time.Second)
|
||||
timeout = timeout - DefaultWaitForInterval
|
||||
if timeout <= 0 {
|
||||
return fmt.Errorf("wait image(%s) status(%s) timeout", imageName, status)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return nil
|
||||
// GetImageByName get image by image name
|
||||
func GetImageByName(ctx context.Context, client *cvm.Client, imageName string) (*cvm.Image, error) {
|
||||
req := cvm.NewDescribeImagesRequest()
|
||||
req.Filters = []*cvm.Filter{
|
||||
{
|
||||
Name: common.StringPtr("image-name"),
|
||||
Values: []*string{&imageName},
|
||||
},
|
||||
}
|
||||
|
||||
var resp *cvm.DescribeImagesResponse
|
||||
err := Retry(ctx, func(ctx context.Context) error {
|
||||
var e error
|
||||
resp, e = client.DescribeImages(req)
|
||||
return e
|
||||
})
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
if *resp.Response.TotalCount > 0 {
|
||||
for _, image := range resp.Response.ImageSet {
|
||||
if *image.ImageName == imageName {
|
||||
return image, nil
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return nil, nil
|
||||
}
|
||||
|
||||
// NewCvmClient returns a new cvm client
|
||||
func NewCvmClient(secretId, secretKey, region string) (client *cvm.Client, err error) {
|
||||
cpf := profile.NewClientProfile()
|
||||
cpf.HttpProfile.ReqMethod = "POST"
|
||||
cpf.HttpProfile.ReqTimeout = 300
|
||||
cpf.Language = "en-US"
|
||||
|
||||
credential := common.NewCredential(secretId, secretKey)
|
||||
client, err = cvm.NewClient(credential, region, cpf)
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
// NewVpcClient returns a new vpc client
|
||||
func NewVpcClient(secretId, secretKey, region string) (client *vpc.Client, err error) {
|
||||
cpf := profile.NewClientProfile()
|
||||
cpf.HttpProfile.ReqMethod = "POST"
|
||||
cpf.HttpProfile.ReqTimeout = 300
|
||||
cpf.Language = "en-US"
|
||||
|
||||
credential := common.NewCredential(secretId, secretKey)
|
||||
client, err = vpc.NewClient(credential, region, cpf)
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
// CheckResourceIdFormat check resource id format
|
||||
func CheckResourceIdFormat(resource string, id string) bool {
|
||||
regex := regexp.MustCompile(fmt.Sprintf("%s-[0-9a-z]{8}$", resource))
|
||||
if !regex.MatchString(id) {
|
||||
return false
|
||||
}
|
||||
return true
|
||||
return regex.MatchString(id)
|
||||
}
|
||||
|
||||
// SSHHost returns a function that can be given to the SSH communicator
|
||||
|
||||
@@ -2,6 +2,7 @@ package cvm
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"strings"
|
||||
|
||||
"github.com/hashicorp/packer/helper/multistep"
|
||||
@@ -19,6 +20,7 @@ func (s *stepCopyImage) Run(ctx context.Context, state multistep.StateBag) multi
|
||||
return multistep.ActionContinue
|
||||
}
|
||||
|
||||
config := state.Get("config").(*Config)
|
||||
client := state.Get("cvm_client").(*cvm.Client)
|
||||
|
||||
imageId := state.Get("image").(*cvm.Image).ImageId
|
||||
@@ -43,6 +45,34 @@ func (s *stepCopyImage) Run(ctx context.Context, state multistep.StateBag) multi
|
||||
return Halt(state, err, "Failed to copy image")
|
||||
}
|
||||
|
||||
Message(state, "Waiting for image ready", "")
|
||||
tencentCloudImages := state.Get("tencentcloudimages").(map[string]string)
|
||||
|
||||
for _, region := range req.DestinationRegions {
|
||||
rc, err := NewCvmClient(config.SecretId, config.SecretKey, *region)
|
||||
if err != nil {
|
||||
return Halt(state, err, "Failed to init client")
|
||||
}
|
||||
|
||||
err = WaitForImageReady(ctx, rc, config.ImageName, "NORMAL", 1800)
|
||||
if err != nil {
|
||||
return Halt(state, err, "Failed to wait for image ready")
|
||||
}
|
||||
|
||||
image, err := GetImageByName(ctx, rc, config.ImageName)
|
||||
if err != nil {
|
||||
return Halt(state, err, "Failed to get image")
|
||||
}
|
||||
|
||||
if image == nil {
|
||||
return Halt(state, err, "Failed to wait for image ready")
|
||||
}
|
||||
|
||||
tencentCloudImages[*region] = *image.ImageId
|
||||
Message(state, fmt.Sprintf("Copy image from %s(%s) to %s(%s)", s.SourceRegion, *imageId, *region, *image.ImageId), "")
|
||||
}
|
||||
|
||||
state.Put("tencentcloudimages", tencentCloudImages)
|
||||
Message(state, "Image copied", "")
|
||||
|
||||
return multistep.ActionContinue
|
||||
|
||||
@@ -58,36 +58,22 @@ func (s *stepCreateImage) Run(ctx context.Context, state multistep.StateBag) mul
|
||||
}
|
||||
|
||||
Message(state, "Waiting for image ready", "")
|
||||
err = WaitForImageReady(client, config.ImageName, "NORMAL", 3600)
|
||||
err = WaitForImageReady(ctx, client, config.ImageName, "NORMAL", 3600)
|
||||
if err != nil {
|
||||
return Halt(state, err, "Failed to wait for image ready")
|
||||
}
|
||||
|
||||
describeReq := cvm.NewDescribeImagesRequest()
|
||||
FILTER_IMAGE_NAME := "image-name"
|
||||
describeReq.Filters = []*cvm.Filter{
|
||||
{
|
||||
Name: &FILTER_IMAGE_NAME,
|
||||
Values: []*string{&config.ImageName},
|
||||
},
|
||||
}
|
||||
|
||||
var describeResp *cvm.DescribeImagesResponse
|
||||
err = Retry(ctx, func(ctx context.Context) error {
|
||||
var e error
|
||||
describeResp, e = client.DescribeImages(describeReq)
|
||||
return e
|
||||
})
|
||||
image, err := GetImageByName(ctx, client, config.ImageName)
|
||||
if err != nil {
|
||||
return Halt(state, err, "Failed to wait for image ready")
|
||||
return Halt(state, err, "Failed to get image")
|
||||
}
|
||||
|
||||
if *describeResp.Response.TotalCount == 0 {
|
||||
if image == nil {
|
||||
return Halt(state, fmt.Errorf("No image return"), "Failed to crate image")
|
||||
}
|
||||
|
||||
s.imageId = *describeResp.Response.ImageSet[0].ImageId
|
||||
state.Put("image", describeResp.Response.ImageSet[0])
|
||||
s.imageId = *image.ImageId
|
||||
state.Put("image", image)
|
||||
Message(state, s.imageId, "Image created")
|
||||
|
||||
tencentCloudImages := make(map[string]string)
|
||||
|
||||
@@ -5,7 +5,6 @@ import (
|
||||
"fmt"
|
||||
|
||||
"github.com/hashicorp/packer/helper/multistep"
|
||||
"github.com/tencentcloud/tencentcloud-sdk-go/tencentcloud/common"
|
||||
cvm "github.com/tencentcloud/tencentcloud-sdk-go/tencentcloud/cvm/v20170312"
|
||||
)
|
||||
|
||||
@@ -18,24 +17,12 @@ func (s *stepPreValidate) Run(ctx context.Context, state multistep.StateBag) mul
|
||||
|
||||
Say(state, config.ImageName, "Trying to check image name")
|
||||
|
||||
req := cvm.NewDescribeImagesRequest()
|
||||
req.Filters = []*cvm.Filter{
|
||||
{
|
||||
Name: common.StringPtr("image-name"),
|
||||
Values: []*string{&config.ImageName},
|
||||
},
|
||||
}
|
||||
var resp *cvm.DescribeImagesResponse
|
||||
err := Retry(ctx, func(ctx context.Context) error {
|
||||
var err error
|
||||
resp, err = client.DescribeImages(req)
|
||||
return err
|
||||
})
|
||||
image, err := GetImageByName(ctx, client, config.ImageName)
|
||||
if err != nil {
|
||||
return Halt(state, err, "Failed to get images info")
|
||||
}
|
||||
|
||||
if *resp.Response.TotalCount > 0 {
|
||||
if image != nil {
|
||||
return Halt(state, fmt.Errorf("Image name %s has exists", config.ImageName), "")
|
||||
}
|
||||
|
||||
|
||||
@@ -157,7 +157,7 @@ func (s *stepRunInstance) Run(ctx context.Context, state multistep.StateBag) mul
|
||||
s.instanceId = *resp.Response.InstanceIdSet[0]
|
||||
Message(state, "Waiting for instance ready", "")
|
||||
|
||||
err = WaitForInstance(client, s.instanceId, "RUNNING", 1800)
|
||||
err = WaitForInstance(ctx, client, s.instanceId, "RUNNING", 1800)
|
||||
if err != nil {
|
||||
return Halt(state, err, "Failed to wait for instance ready")
|
||||
}
|
||||
|
||||
@@ -0,0 +1,88 @@
|
||||
package vagrant
|
||||
|
||||
// Create a mock driver so that we can test Vagrant builder steps
|
||||
type MockVagrantDriver struct {
|
||||
InitCalled bool
|
||||
AddCalled bool
|
||||
UpCalled bool
|
||||
HaltCalled bool
|
||||
SuspendCalled bool
|
||||
SSHConfigCalled bool
|
||||
DestroyCalled bool
|
||||
PackageCalled bool
|
||||
VerifyCalled bool
|
||||
VersionCalled bool
|
||||
|
||||
ReturnError error
|
||||
ReturnSSHConfig *VagrantSSHConfig
|
||||
GlobalID string
|
||||
}
|
||||
|
||||
func (d *MockVagrantDriver) Init([]string) error {
|
||||
d.InitCalled = true
|
||||
return d.ReturnError
|
||||
}
|
||||
|
||||
func (d *MockVagrantDriver) Add([]string) error {
|
||||
d.AddCalled = true
|
||||
return d.ReturnError
|
||||
}
|
||||
|
||||
func (d *MockVagrantDriver) Up([]string) (string, string, error) {
|
||||
d.UpCalled = true
|
||||
return "", "", nil
|
||||
}
|
||||
|
||||
func (d *MockVagrantDriver) Halt(string) error {
|
||||
d.HaltCalled = true
|
||||
return d.ReturnError
|
||||
}
|
||||
|
||||
func (d *MockVagrantDriver) Suspend(string) error {
|
||||
d.SuspendCalled = true
|
||||
return d.ReturnError
|
||||
}
|
||||
|
||||
func (d *MockVagrantDriver) SSHConfig(gid string) (*VagrantSSHConfig, error) {
|
||||
d.SSHConfigCalled = true
|
||||
// track the input value
|
||||
d.GlobalID = gid
|
||||
|
||||
if d.ReturnSSHConfig != nil {
|
||||
return d.ReturnSSHConfig, nil
|
||||
}
|
||||
|
||||
sshConfig := VagrantSSHConfig{
|
||||
Hostname: "127.0.0.1",
|
||||
User: "vagrant",
|
||||
Port: "2222",
|
||||
UserKnownHostsFile: "/dev/null",
|
||||
StrictHostKeyChecking: false,
|
||||
PasswordAuthentication: false,
|
||||
IdentityFile: "\"/path with spaces/insecure_private_key\"",
|
||||
IdentitiesOnly: true,
|
||||
LogLevel: "FATAL"}
|
||||
return &sshConfig, d.ReturnError
|
||||
}
|
||||
|
||||
func (d *MockVagrantDriver) Destroy(string) error {
|
||||
d.DestroyCalled = true
|
||||
return d.ReturnError
|
||||
}
|
||||
|
||||
func (d *MockVagrantDriver) Package([]string) error {
|
||||
d.PackageCalled = true
|
||||
return d.ReturnError
|
||||
}
|
||||
|
||||
func (d *MockVagrantDriver) Verify() error {
|
||||
d.VerifyCalled = true
|
||||
return d.ReturnError
|
||||
}
|
||||
|
||||
func (d *MockVagrantDriver) Version() (string, error) {
|
||||
d.VersionCalled = true
|
||||
return "", d.ReturnError
|
||||
}
|
||||
|
||||
// End of mock definition
|
||||
@@ -56,9 +56,9 @@ func (s *StepSSHConfig) Run(ctx context.Context, state multistep.StateBag) multi
|
||||
}
|
||||
log.Printf("identity file is %s", sshConfig.IdentityFile)
|
||||
log.Printf("Removing quotes from identity file")
|
||||
sshConfig.IdentityFile, err = strconv.Unquote(sshConfig.IdentityFile)
|
||||
if err != nil {
|
||||
log.Printf("Error unquoting identity file: %s", err)
|
||||
unquoted, err := strconv.Unquote(sshConfig.IdentityFile)
|
||||
if err == nil {
|
||||
sshConfig.IdentityFile = unquoted
|
||||
}
|
||||
config.Comm.SSHPrivateKeyFile = sshConfig.IdentityFile
|
||||
config.Comm.SSHUsername = sshConfig.User
|
||||
|
||||
@@ -0,0 +1,98 @@
|
||||
package vagrant
|
||||
|
||||
import (
|
||||
"context"
|
||||
"testing"
|
||||
|
||||
"github.com/hashicorp/packer/helper/multistep"
|
||||
)
|
||||
|
||||
func TestStepSSHConfig_Impl(t *testing.T) {
|
||||
var raw interface{}
|
||||
raw = new(StepSSHConfig)
|
||||
if _, ok := raw.(multistep.Step); !ok {
|
||||
t.Fatalf("initialize should be a step")
|
||||
}
|
||||
}
|
||||
|
||||
func TestPrepStepSSHConfig_GlobalID(t *testing.T) {
|
||||
driver := &MockVagrantDriver{}
|
||||
config := &Config{}
|
||||
state := new(multistep.BasicStateBag)
|
||||
state.Put("driver", driver)
|
||||
state.Put("config", config)
|
||||
|
||||
step := StepSSHConfig{
|
||||
GlobalID: "adsfadf",
|
||||
}
|
||||
_ = step.Run(context.Background(), state)
|
||||
if driver.GlobalID != "adsfadf" {
|
||||
t.Fatalf("Should have called SSHConfig with GlobalID asdfasdf")
|
||||
}
|
||||
}
|
||||
|
||||
func TestPrepStepSSHConfig_NoGlobalID(t *testing.T) {
|
||||
driver := &MockVagrantDriver{}
|
||||
config := &Config{}
|
||||
state := new(multistep.BasicStateBag)
|
||||
state.Put("driver", driver)
|
||||
state.Put("config", config)
|
||||
|
||||
step := StepSSHConfig{}
|
||||
_ = step.Run(context.Background(), state)
|
||||
if driver.GlobalID != "source" {
|
||||
t.Fatalf("Should have called SSHConfig with GlobalID source")
|
||||
}
|
||||
}
|
||||
|
||||
func TestPrepStepSSHConfig_SpacesInPath(t *testing.T) {
|
||||
driver := &MockVagrantDriver{}
|
||||
driver.ReturnSSHConfig = &VagrantSSHConfig{
|
||||
Hostname: "127.0.0.1",
|
||||
User: "vagrant",
|
||||
Port: "2222",
|
||||
UserKnownHostsFile: "/dev/null",
|
||||
StrictHostKeyChecking: false,
|
||||
PasswordAuthentication: false,
|
||||
IdentityFile: "\"/path with spaces/insecure_private_key\"",
|
||||
IdentitiesOnly: true,
|
||||
LogLevel: "FATAL"}
|
||||
|
||||
config := &Config{}
|
||||
state := new(multistep.BasicStateBag)
|
||||
state.Put("driver", driver)
|
||||
state.Put("config", config)
|
||||
|
||||
step := StepSSHConfig{}
|
||||
_ = step.Run(context.Background(), state)
|
||||
expected := "/path with spaces/insecure_private_key"
|
||||
if config.Comm.SSHPrivateKeyFile != expected {
|
||||
t.Fatalf("Bad config private key. Recieved: %s; expected: %s.", config.Comm.SSHPrivateKeyFile, expected)
|
||||
}
|
||||
}
|
||||
|
||||
func TestPrepStepSSHConfig_NoSpacesInPath(t *testing.T) {
|
||||
driver := &MockVagrantDriver{}
|
||||
driver.ReturnSSHConfig = &VagrantSSHConfig{
|
||||
Hostname: "127.0.0.1",
|
||||
User: "vagrant",
|
||||
Port: "2222",
|
||||
UserKnownHostsFile: "/dev/null",
|
||||
StrictHostKeyChecking: false,
|
||||
PasswordAuthentication: false,
|
||||
IdentityFile: "/path/without/spaces/insecure_private_key",
|
||||
IdentitiesOnly: true,
|
||||
LogLevel: "FATAL"}
|
||||
|
||||
config := &Config{}
|
||||
state := new(multistep.BasicStateBag)
|
||||
state.Put("driver", driver)
|
||||
state.Put("config", config)
|
||||
|
||||
step := StepSSHConfig{}
|
||||
_ = step.Run(context.Background(), state)
|
||||
expected := "/path/without/spaces/insecure_private_key"
|
||||
if config.Comm.SSHPrivateKeyFile != expected {
|
||||
t.Fatalf("Bad config private key. Recieved: %s; expected: %s.", config.Comm.SSHPrivateKeyFile, expected)
|
||||
}
|
||||
}
|
||||
@@ -25,6 +25,9 @@ type Driver interface {
|
||||
// Create an NVME controller
|
||||
CreateNVMeController(vm string, controller string, portcount int) error
|
||||
|
||||
// Delete all floppy controllers
|
||||
RemoveFloppyControllers(vm string) error
|
||||
|
||||
// Delete a VM by name
|
||||
Delete(string) error
|
||||
|
||||
|
||||
@@ -75,6 +75,55 @@ func (d *VBox42Driver) CreateSCSIController(vmName string, name string) error {
|
||||
return d.VBoxManage(command...)
|
||||
}
|
||||
|
||||
func (d *VBox42Driver) RemoveFloppyControllers(vmName string) error {
|
||||
var stdout bytes.Buffer
|
||||
|
||||
cmd := exec.Command(d.VBoxManagePath, "showvminfo", vmName, "--machinereadable")
|
||||
cmd.Stdout = &stdout
|
||||
if err := cmd.Run(); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
StorageControllerTypeRe := regexp.MustCompile("^storagecontrollertype([0-9]+)=\"(.+)\"$")
|
||||
StorageControllerNameRe := regexp.MustCompile("^storagecontrollername([0-9]+)=\"(.+)\"$")
|
||||
|
||||
storageControllerNames := make(map[string]string)
|
||||
storageControllerIdx := ""
|
||||
|
||||
for _, line := range strings.Split(stdout.String(), "\n") {
|
||||
// Need to trim off CR character when running in windows
|
||||
// Trimming whitespaces at this point helps to filter out empty value
|
||||
line = strings.TrimRight(line, " \r")
|
||||
|
||||
matches := StorageControllerTypeRe.FindStringSubmatch(line)
|
||||
if matches != nil {
|
||||
// Floppy controllers are of a type I82078
|
||||
if matches[2] == "I82078" {
|
||||
// VirtualBox supports only one floppy controller per VM
|
||||
storageControllerIdx = matches[1]
|
||||
}
|
||||
continue
|
||||
}
|
||||
|
||||
matches = StorageControllerNameRe.FindStringSubmatch(line)
|
||||
if matches != nil {
|
||||
storageControllerNames[matches[1]] = matches[2]
|
||||
}
|
||||
}
|
||||
|
||||
if storageControllerIdx == "" {
|
||||
return nil
|
||||
}
|
||||
|
||||
command := []string{
|
||||
"storagectl", vmName,
|
||||
"--name", storageControllerNames[storageControllerIdx],
|
||||
"--remove",
|
||||
}
|
||||
|
||||
return d.VBoxManage(command...)
|
||||
}
|
||||
|
||||
func (d *VBox42Driver) Delete(name string) error {
|
||||
return d.VBoxManage("unregistervm", name, "--delete")
|
||||
}
|
||||
|
||||
@@ -17,6 +17,9 @@ type DriverMock struct {
|
||||
CreateNVMeControllerController string
|
||||
CreateNVMeControllerErr error
|
||||
|
||||
RemoveFloppyControllersVM string
|
||||
RemoveFloppyControllersErr error
|
||||
|
||||
DeleteCalled bool
|
||||
DeleteName string
|
||||
DeleteErr error
|
||||
@@ -81,6 +84,11 @@ func (d *DriverMock) CreateNVMeController(vm string, controller string, portcoun
|
||||
return d.CreateNVMeControllerErr
|
||||
}
|
||||
|
||||
func (d *DriverMock) RemoveFloppyControllers(vm string) error {
|
||||
d.RemoveFloppyControllersVM = vm
|
||||
return d.RemoveFloppyControllersErr
|
||||
}
|
||||
|
||||
func (d *DriverMock) Delete(name string) error {
|
||||
d.DeleteCalled = true
|
||||
d.DeleteName = name
|
||||
|
||||
@@ -48,6 +48,12 @@ func (s *StepAttachFloppy) Run(ctx context.Context, state multistep.StateBag) mu
|
||||
ui := state.Get("ui").(packer.Ui)
|
||||
vmName := state.Get("vmName").(string)
|
||||
|
||||
ui.Say("Deleting any current floppy disk...")
|
||||
if err := driver.RemoveFloppyControllers(vmName); err != nil {
|
||||
state.Put("error", fmt.Errorf("Error deleting existing floppy controllers: %s", err))
|
||||
return multistep.ActionHalt
|
||||
}
|
||||
|
||||
ui.Say("Attaching floppy disk...")
|
||||
|
||||
// Create the floppy disk controller
|
||||
|
||||
@@ -38,6 +38,10 @@ func TestStepAttachFloppy(t *testing.T) {
|
||||
t.Fatal("should NOT have error")
|
||||
}
|
||||
|
||||
if driver.RemoveFloppyControllersVM == "" {
|
||||
t.Fatal("RemoveFloppyControllers was not called")
|
||||
}
|
||||
|
||||
if len(driver.VBoxManageCalls) != 2 {
|
||||
t.Fatal("not enough calls to VBoxManage")
|
||||
}
|
||||
|
||||
@@ -52,7 +52,7 @@ func (s *StepConfigureVRDP) Run(ctx context.Context, state multistep.StateBag) m
|
||||
|
||||
command := []string{
|
||||
"modifyvm", vmName,
|
||||
"--vrdeaddress", fmt.Sprintf("%s", s.VRDPBindAddress),
|
||||
"--vrdeaddress", s.VRDPBindAddress,
|
||||
"--vrdeauthtype", "null",
|
||||
"--vrde", "on",
|
||||
"--vrdeport",
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -102,7 +102,7 @@ func (s *StepExport) Run(ctx context.Context, state multistep.StateBag) multiste
|
||||
return multistep.ActionHalt
|
||||
}
|
||||
|
||||
ui.Message(fmt.Sprintf("%s", out.String()))
|
||||
ui.Message(out.String())
|
||||
|
||||
return multistep.ActionContinue
|
||||
}
|
||||
|
||||
@@ -3,10 +3,11 @@ package common
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"log"
|
||||
|
||||
"github.com/hashicorp/packer/common"
|
||||
"github.com/hashicorp/packer/helper/multistep"
|
||||
"github.com/hashicorp/packer/packer"
|
||||
"log"
|
||||
)
|
||||
|
||||
// Step to discover the http ip
|
||||
|
||||
@@ -3,9 +3,10 @@ package common
|
||||
import (
|
||||
"context"
|
||||
"errors"
|
||||
"testing"
|
||||
|
||||
"github.com/hashicorp/packer/common"
|
||||
"github.com/hashicorp/packer/helper/multistep"
|
||||
"testing"
|
||||
)
|
||||
|
||||
func TestStepHTTPIPDiscover_Run(t *testing.T) {
|
||||
|
||||
@@ -92,7 +92,9 @@ type Config struct {
|
||||
// set in the VMware VMX. By default this is other. By specifying a more
|
||||
// specific OS type, VMware may perform some optimizations or virtual hardware
|
||||
// changes to better support the operating system running in the
|
||||
// virtual machine.
|
||||
// virtual machine. Valid values differ by platform and version numbers, and may
|
||||
// not match other VMware API's representation of the guest OS names. Consult your
|
||||
// platform for valid values.
|
||||
GuestOSType string `mapstructure:"guest_os_type" required:"false"`
|
||||
// The [vmx hardware
|
||||
// version](http://kb.vmware.com/selfservice/microsites/search.do?language=en_US&cmd=displayKC&externalId=1003746)
|
||||
@@ -106,8 +108,11 @@ type Config struct {
|
||||
|
||||
VMXDiskTemplatePath string `mapstructure:"vmx_disk_template_path"`
|
||||
// Path to a [configuration template](/docs/templates/engine.html) that
|
||||
// defines the contents of the virtual machine VMX file for VMware. This is
|
||||
// for **advanced users only** as this can render the virtual machine
|
||||
// defines the contents of the virtual machine VMX file for VMware. The
|
||||
// engine has access to the template variables `{{ .DiskNumber }}` and
|
||||
// `{{ .DiskName }}`.
|
||||
//
|
||||
// This is for **advanced users only** as this can render the virtual machine
|
||||
// non-functional. See below for more information. For basic VMX
|
||||
// modifications, try `vmx_data` first.
|
||||
VMXTemplatePath string `mapstructure:"vmx_template_path" required:"false"`
|
||||
|
||||
@@ -2,6 +2,7 @@ package clone
|
||||
|
||||
import (
|
||||
"context"
|
||||
|
||||
"github.com/hashicorp/hcl/v2/hcldec"
|
||||
"github.com/hashicorp/packer/builder/vsphere/common"
|
||||
"github.com/hashicorp/packer/builder/vsphere/driver"
|
||||
|
||||
@@ -1,13 +1,14 @@
|
||||
package clone
|
||||
|
||||
import (
|
||||
"os"
|
||||
"testing"
|
||||
|
||||
"github.com/hashicorp/packer/builder/vsphere/common"
|
||||
commonT "github.com/hashicorp/packer/builder/vsphere/common/testing"
|
||||
builderT "github.com/hashicorp/packer/helper/builder/testing"
|
||||
"github.com/hashicorp/packer/packer"
|
||||
"github.com/vmware/govmomi/vim25/types"
|
||||
"os"
|
||||
"testing"
|
||||
)
|
||||
|
||||
func TestCloneBuilderAcc_default(t *testing.T) {
|
||||
|
||||
@@ -1,8 +1,9 @@
|
||||
package clone
|
||||
|
||||
import (
|
||||
"github.com/hashicorp/packer/packer"
|
||||
"testing"
|
||||
|
||||
"github.com/hashicorp/packer/packer"
|
||||
)
|
||||
|
||||
func TestCloneBuilder_ImplementsBuilder(t *testing.T) {
|
||||
|
||||
@@ -6,6 +6,7 @@ package clone
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
|
||||
"github.com/hashicorp/packer/builder/vsphere/common"
|
||||
"github.com/hashicorp/packer/builder/vsphere/driver"
|
||||
"github.com/hashicorp/packer/helper/multistep"
|
||||
|
||||
@@ -6,6 +6,7 @@ package common
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
|
||||
"github.com/hashicorp/packer/builder/vsphere/driver"
|
||||
"github.com/hashicorp/packer/helper/multistep"
|
||||
"github.com/hashicorp/packer/packer"
|
||||
|
||||
@@ -6,6 +6,7 @@ package common
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
|
||||
"github.com/hashicorp/packer/builder/vsphere/driver"
|
||||
"github.com/hashicorp/packer/helper/multistep"
|
||||
)
|
||||
|
||||
@@ -6,6 +6,7 @@ package common
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
|
||||
"github.com/hashicorp/packer/builder/vsphere/driver"
|
||||
"github.com/hashicorp/packer/helper/multistep"
|
||||
"github.com/hashicorp/packer/packer"
|
||||
|
||||
@@ -5,10 +5,11 @@ package common
|
||||
|
||||
import (
|
||||
"context"
|
||||
"strings"
|
||||
|
||||
"github.com/hashicorp/packer/builder/vsphere/driver"
|
||||
"github.com/hashicorp/packer/helper/multistep"
|
||||
"github.com/hashicorp/packer/packer"
|
||||
"strings"
|
||||
)
|
||||
|
||||
type RunConfig struct {
|
||||
|
||||
@@ -7,11 +7,12 @@ import (
|
||||
"bytes"
|
||||
"context"
|
||||
"fmt"
|
||||
"log"
|
||||
"time"
|
||||
|
||||
"github.com/hashicorp/packer/builder/vsphere/driver"
|
||||
"github.com/hashicorp/packer/helper/multistep"
|
||||
"github.com/hashicorp/packer/packer"
|
||||
"log"
|
||||
"time"
|
||||
)
|
||||
|
||||
type ShutdownConfig struct {
|
||||
|
||||
@@ -2,6 +2,7 @@ package common
|
||||
|
||||
import (
|
||||
"context"
|
||||
|
||||
"github.com/hashicorp/packer/builder/vsphere/driver"
|
||||
"github.com/hashicorp/packer/helper/multistep"
|
||||
"github.com/hashicorp/packer/packer"
|
||||
|
||||
@@ -2,6 +2,7 @@ package common
|
||||
|
||||
import (
|
||||
"context"
|
||||
|
||||
"github.com/hashicorp/packer/builder/vsphere/driver"
|
||||
"github.com/hashicorp/packer/helper/multistep"
|
||||
"github.com/hashicorp/packer/packer"
|
||||
|
||||
@@ -6,11 +6,12 @@ package common
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"log"
|
||||
"time"
|
||||
|
||||
"github.com/hashicorp/packer/builder/vsphere/driver"
|
||||
"github.com/hashicorp/packer/helper/multistep"
|
||||
"github.com/hashicorp/packer/packer"
|
||||
"log"
|
||||
"time"
|
||||
)
|
||||
|
||||
type WaitIpConfig struct {
|
||||
|
||||
@@ -3,13 +3,14 @@ package testing
|
||||
import (
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"github.com/hashicorp/packer/builder/vsphere/common"
|
||||
"github.com/hashicorp/packer/builder/vsphere/driver"
|
||||
"github.com/hashicorp/packer/packer"
|
||||
"math/rand"
|
||||
"os"
|
||||
"testing"
|
||||
"time"
|
||||
|
||||
"github.com/hashicorp/packer/builder/vsphere/common"
|
||||
"github.com/hashicorp/packer/builder/vsphere/driver"
|
||||
"github.com/hashicorp/packer/packer"
|
||||
)
|
||||
|
||||
func NewVMName() string {
|
||||
|
||||
@@ -2,6 +2,7 @@ package driver
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
|
||||
"github.com/vmware/govmomi/object"
|
||||
"github.com/vmware/govmomi/vim25/mo"
|
||||
"github.com/vmware/govmomi/vim25/soap"
|
||||
|
||||
@@ -3,14 +3,15 @@ package driver
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"net/url"
|
||||
"time"
|
||||
|
||||
"github.com/vmware/govmomi"
|
||||
"github.com/vmware/govmomi/find"
|
||||
"github.com/vmware/govmomi/object"
|
||||
"github.com/vmware/govmomi/session"
|
||||
"github.com/vmware/govmomi/vim25"
|
||||
"github.com/vmware/govmomi/vim25/soap"
|
||||
"net/url"
|
||||
"time"
|
||||
)
|
||||
|
||||
type Driver struct {
|
||||
|
||||
@@ -2,6 +2,7 @@ package driver
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
|
||||
"github.com/vmware/govmomi/object"
|
||||
"github.com/vmware/govmomi/vim25/mo"
|
||||
"github.com/vmware/govmomi/vim25/types"
|
||||
|
||||
@@ -1,6 +1,8 @@
|
||||
package driver
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
|
||||
"github.com/vmware/govmomi/object"
|
||||
"github.com/vmware/govmomi/vim25/mo"
|
||||
"github.com/vmware/govmomi/vim25/types"
|
||||
@@ -8,7 +10,7 @@ import (
|
||||
|
||||
type Network struct {
|
||||
driver *Driver
|
||||
network *object.Network
|
||||
network object.NetworkReference
|
||||
}
|
||||
|
||||
func (d *Driver) NewNetwork(ref *types.ManagedObjectReference) *Network {
|
||||
@@ -24,7 +26,7 @@ func (d *Driver) FindNetwork(name string) (*Network, error) {
|
||||
return nil, err
|
||||
}
|
||||
return &Network{
|
||||
network: n.(*object.Network),
|
||||
network: n,
|
||||
driver: d,
|
||||
}, nil
|
||||
}
|
||||
@@ -37,7 +39,13 @@ func (n *Network) Info(params ...string) (*mo.Network, error) {
|
||||
p = params
|
||||
}
|
||||
var info mo.Network
|
||||
err := n.network.Properties(n.driver.ctx, n.network.Reference(), p, &info)
|
||||
|
||||
network, ok := n.network.(*object.Network)
|
||||
if !ok {
|
||||
return nil, fmt.Errorf("unexpected %t network object type", n.network)
|
||||
}
|
||||
|
||||
err := network.Properties(n.driver.ctx, network.Reference(), p, &info)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
@@ -2,6 +2,7 @@ package driver
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
|
||||
"github.com/vmware/govmomi/object"
|
||||
"github.com/vmware/govmomi/vim25/mo"
|
||||
"github.com/vmware/govmomi/vim25/types"
|
||||
|
||||
@@ -4,12 +4,13 @@ import (
|
||||
"context"
|
||||
"errors"
|
||||
"fmt"
|
||||
"github.com/vmware/govmomi/object"
|
||||
"github.com/vmware/govmomi/vim25/mo"
|
||||
"github.com/vmware/govmomi/vim25/types"
|
||||
"log"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
"github.com/vmware/govmomi/object"
|
||||
"github.com/vmware/govmomi/vim25/mo"
|
||||
"github.com/vmware/govmomi/vim25/types"
|
||||
)
|
||||
|
||||
type VirtualMachine struct {
|
||||
@@ -43,10 +44,15 @@ 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
|
||||
DiskControllerType string // example: "scsi", "pvscsi"
|
||||
DiskSize int64
|
||||
DiskControllerType string // example: "scsi", "pvscsi"
|
||||
|
||||
Annotation string
|
||||
Name string
|
||||
@@ -56,11 +62,17 @@ 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
|
||||
Storage []Disk
|
||||
}
|
||||
|
||||
type Disk struct {
|
||||
DiskSize int64
|
||||
DiskEagerlyScrub bool
|
||||
DiskThinProvisioned bool
|
||||
}
|
||||
|
||||
func (d *Driver) NewVM(ref *types.ManagedObjectReference) *VirtualMachine {
|
||||
@@ -480,6 +492,10 @@ func (vm *VirtualMachine) GetDir() (string, error) {
|
||||
}
|
||||
|
||||
func addDisk(_ *Driver, devices object.VirtualDeviceList, config *CreateConfig) (object.VirtualDeviceList, error) {
|
||||
if len(config.Storage) == 0 {
|
||||
return nil, errors.New("no storage devices have been defined")
|
||||
}
|
||||
|
||||
device, err := devices.CreateSCSIController(config.DiskControllerType)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
@@ -490,60 +506,77 @@ func addDisk(_ *Driver, devices object.VirtualDeviceList, config *CreateConfig)
|
||||
return nil, err
|
||||
}
|
||||
|
||||
disk := &types.VirtualDisk{
|
||||
VirtualDevice: types.VirtualDevice{
|
||||
Key: devices.NewKey(),
|
||||
Backing: &types.VirtualDiskFlatVer2BackingInfo{
|
||||
DiskMode: string(types.VirtualDiskModePersistent),
|
||||
ThinProvisioned: types.NewBool(config.DiskThinProvisioned),
|
||||
for _, dc := range config.Storage {
|
||||
disk := &types.VirtualDisk{
|
||||
VirtualDevice: types.VirtualDevice{
|
||||
Key: devices.NewKey(),
|
||||
Backing: &types.VirtualDiskFlatVer2BackingInfo{
|
||||
DiskMode: string(types.VirtualDiskModePersistent),
|
||||
ThinProvisioned: types.NewBool(dc.DiskThinProvisioned),
|
||||
EagerlyScrub: types.NewBool(dc.DiskEagerlyScrub),
|
||||
},
|
||||
},
|
||||
},
|
||||
CapacityInKB: config.DiskSize * 1024,
|
||||
}
|
||||
CapacityInKB: dc.DiskSize * 1024,
|
||||
}
|
||||
|
||||
devices.AssignController(disk, controller)
|
||||
devices = append(devices, disk)
|
||||
devices.AssignController(disk, controller)
|
||||
devices = append(devices, disk)
|
||||
}
|
||||
|
||||
return devices, nil
|
||||
}
|
||||
|
||||
func addNetwork(d *Driver, devices object.VirtualDeviceList, config *CreateConfig) (object.VirtualDeviceList, error) {
|
||||
if len(config.NICs) == 0 {
|
||||
return nil, errors.New("no network adapters have been defined")
|
||||
}
|
||||
|
||||
var network object.NetworkReference
|
||||
if config.Network == "" {
|
||||
h, err := d.FindHost(config.Host)
|
||||
for _, nic := range config.NICs {
|
||||
if nic.Network == "" {
|
||||
h, err := d.FindHost(config.Host)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
i, err := h.Info("network")
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
if len(i.Network) > 1 {
|
||||
return nil, fmt.Errorf("Host has multiple networks. Specify it explicitly")
|
||||
}
|
||||
|
||||
network = object.NewNetwork(d.client.Client, i.Network[0])
|
||||
} else {
|
||||
var err error
|
||||
network, err = d.finder.Network(d.ctx, nic.Network)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
}
|
||||
|
||||
backing, err := network.EthernetCardBackingInfo(d.ctx)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
i, err := h.Info("network")
|
||||
device, err := object.EthernetCardTypes().CreateEthernetCard(nic.NetworkCard, backing)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
if len(i.Network) > 1 {
|
||||
return nil, fmt.Errorf("Host has multiple networks. Specify it explicitly")
|
||||
card := device.(types.BaseVirtualEthernetCard).GetVirtualEthernetCard()
|
||||
if nic.MacAddress != "" {
|
||||
card.AddressType = string(types.VirtualEthernetCardMacTypeManual)
|
||||
card.MacAddress = nic.MacAddress
|
||||
}
|
||||
card.UptCompatibilityEnabled = nic.Passthrough
|
||||
|
||||
network = object.NewNetwork(d.client.Client, i.Network[0])
|
||||
} else {
|
||||
var err error
|
||||
network, err = d.finder.Network(d.ctx, config.Network)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
devices = append(devices, device)
|
||||
}
|
||||
|
||||
backing, err := network.EthernetCardBackingInfo(d.ctx)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
device, err := object.EthernetCardTypes().CreateEthernetCard(config.NetworkCard, backing)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return append(devices, device), nil
|
||||
return devices, nil
|
||||
}
|
||||
|
||||
func (vm *VirtualMachine) AddCdrom(controllerType string, isoPath string) error {
|
||||
|
||||
@@ -2,6 +2,7 @@ package driver
|
||||
|
||||
import (
|
||||
"errors"
|
||||
|
||||
"github.com/vmware/govmomi/vim25/types"
|
||||
)
|
||||
|
||||
@@ -51,6 +52,23 @@ func (vm *VirtualMachine) CreateCdrom(c *types.VirtualController) (*types.Virtua
|
||||
return device, nil
|
||||
}
|
||||
|
||||
func (vm *VirtualMachine) RemoveCdroms() error {
|
||||
devices, err := vm.Devices()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
cdroms := devices.SelectByType((*types.VirtualCdrom)(nil))
|
||||
if err = vm.RemoveDevice(true, cdroms...); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
sata := devices.SelectByType((*types.VirtualAHCIController)(nil))
|
||||
if err = vm.RemoveDevice(true, sata...); err != nil {
|
||||
return err
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (vm *VirtualMachine) EjectCdroms() error {
|
||||
devices, err := vm.Devices()
|
||||
if err != nil {
|
||||
|
||||
@@ -1,11 +1,12 @@
|
||||
package driver
|
||||
|
||||
import (
|
||||
"strings"
|
||||
"unicode"
|
||||
|
||||
"github.com/vmware/govmomi/vim25/methods"
|
||||
"github.com/vmware/govmomi/vim25/types"
|
||||
"golang.org/x/mobile/event/key"
|
||||
"strings"
|
||||
"unicode"
|
||||
)
|
||||
|
||||
type KeyInput struct {
|
||||
|
||||
@@ -2,6 +2,7 @@ package main
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
|
||||
"github.com/hashicorp/packer/builder/vsphere/driver"
|
||||
)
|
||||
|
||||
|
||||
@@ -121,7 +121,9 @@ func (b *Builder) Run(ctx context.Context, ui packer.Ui, hook packer.Hook) (pack
|
||||
}
|
||||
|
||||
steps = append(steps,
|
||||
&StepRemoveCDRom{},
|
||||
&StepRemoveCDRom{
|
||||
Config: &b.config.RemoveCDRomConfig,
|
||||
},
|
||||
&common.StepCreateSnapshot{
|
||||
CreateSnapshot: b.config.CreateSnapshot,
|
||||
},
|
||||
|
||||
@@ -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) {
|
||||
|
||||
@@ -1,3 +1,4 @@
|
||||
//go:generate struct-markdown
|
||||
//go:generate mapstructure-to-hcl2 -type Config
|
||||
|
||||
package iso
|
||||
@@ -24,6 +25,7 @@ type Config struct {
|
||||
packerCommon.ISOConfig `mapstructure:",squash"`
|
||||
|
||||
CDRomConfig `mapstructure:",squash"`
|
||||
RemoveCDRomConfig `mapstructure:",squash"`
|
||||
FloppyConfig `mapstructure:",squash"`
|
||||
common.RunConfig `mapstructure:",squash"`
|
||||
BootConfig `mapstructure:",squash"`
|
||||
@@ -32,7 +34,10 @@ type Config struct {
|
||||
|
||||
common.ShutdownConfig `mapstructure:",squash"`
|
||||
|
||||
CreateSnapshot bool `mapstructure:"create_snapshot"`
|
||||
// Create a snapshot when set to `true`, so the VM can be used as a base
|
||||
// for linked clones. Defaults to `false`.
|
||||
CreateSnapshot bool `mapstructure:"create_snapshot"`
|
||||
// Convert VM to a template. Defaults to `false`.
|
||||
ConvertToTemplate bool `mapstructure:"convert_to_template"`
|
||||
|
||||
ctx interpolate.Context
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user