Compare commits
435 Commits
Author | SHA1 | Date | |
---|---|---|---|
|
756f4d7e1a | ||
|
4894eca5d3 | ||
|
d111a01612 | ||
|
fc6694679c | ||
|
36658a7875 | ||
|
deba45ef3a | ||
|
1e9c3eb66a | ||
|
7077d1e374 | ||
|
54738803d5 | ||
|
0ff8fa1e77 | ||
|
4b07607b11 | ||
|
bf1383d1e8 | ||
|
13c2a1d78a | ||
|
2a36fae810 | ||
|
c411df4b02 | ||
|
cc03e4a10c | ||
|
7147cb4592 | ||
|
1b20fbac99 | ||
|
6c9ee9dc94 | ||
|
b38a3b67bc | ||
|
a776a46e1e | ||
|
d11786a294 | ||
|
7a9e710071 | ||
|
88e56ca729 | ||
|
81c1ea03ee | ||
|
5a826dfbce | ||
|
bc21b3cfac | ||
|
e62c1a1373 | ||
|
30498d8258 | ||
|
e460d5bd58 | ||
|
eac40a8976 | ||
|
5dc6c311f0 | ||
|
af169dfed0 | ||
|
3a92c96509 | ||
|
ac530c5628 | ||
|
dc743791d9 | ||
|
3b40f051bb | ||
|
8f73b0d338 | ||
|
6bf1ad468c | ||
|
e64dd4897f | ||
|
23fdf2286d | ||
|
3a07cb9e0a | ||
|
84a3a2389a | ||
|
5194dec899 | ||
|
9d57d14f3e | ||
|
a9e5f30a55 | ||
|
4438cdce28 | ||
|
757529e20b | ||
|
8fe24bddcd | ||
|
6af6320f8a | ||
|
3cd902b9b6 | ||
|
2379ca3afc | ||
|
58fc1930bc | ||
|
02280e60e4 | ||
|
524ef18847 | ||
|
dac47c649b | ||
|
8ef30c26e4 | ||
|
609ef7317e | ||
|
7841481ee0 | ||
|
0b9d7be27e | ||
|
767809c80f | ||
|
785b62ad24 | ||
|
5759ecea09 | ||
|
4646bde57c | ||
|
a7ec448ce6 | ||
|
bea09bb3ed | ||
|
83b1052bb4 | ||
|
de1a62d88c | ||
|
ea70f903c7 | ||
|
5c090071a9 | ||
|
f6a317cb41 | ||
|
2fe14d14ac | ||
|
7c4b652c17 | ||
|
461367ad1c | ||
|
0bb70e8957 | ||
|
17c58e8d24 | ||
|
de46bb8b2f | ||
|
bb64b86619 | ||
|
3f0a2186bf | ||
|
cda48bd66e | ||
|
3c5fc29b36 | ||
|
70fd83e803 | ||
|
d8294d3ccd | ||
|
ffc0ffb5cd | ||
|
416c734853 | ||
|
b55b096836 | ||
|
6c808da038 | ||
|
6b0b4baf2f | ||
|
c49d4ebf12 | ||
|
880d12c66f | ||
|
9cc7249127 | ||
|
b4d83eb533 | ||
|
a267698fad | ||
|
5d0cff3fc6 | ||
|
479e16a2c1 | ||
|
d15616c1d2 | ||
|
dcb839e131 | ||
|
8c58db3504 | ||
|
6eeea7dc64 | ||
|
52cd604a9b | ||
|
73e173370a | ||
|
4f90bf5f17 | ||
|
0ccc53b8b5 | ||
|
55e3b071e3 | ||
|
752bd52f61 | ||
|
f72f00341c | ||
|
69bae007b6 | ||
|
88e52b581f | ||
|
f7a8fa4959 | ||
|
e9f55883e3 | ||
|
4df2465c06 | ||
|
8a823a13eb | ||
|
79c6b508a4 | ||
|
4b531de0ff | ||
|
f7de8aace4 | ||
|
1e35a0da41 | ||
|
88e403f87c | ||
|
6fc83b9e4b | ||
|
7b40fd4424 | ||
|
c6147b13c9 | ||
|
c5e5a86607 | ||
|
38ef07516e | ||
|
ac90dec7ea | ||
|
f4a161e806 | ||
|
5525b73b73 | ||
|
8b8bd284f5 | ||
|
509b6b3c25 | ||
|
7a4a1d0b9b | ||
|
d9cb44c847 | ||
|
aa4ea9d5c8 | ||
|
31683d2758 | ||
|
ac62f77e36 | ||
|
6005f12d3d | ||
|
77801d5798 | ||
|
e607da2c40 | ||
|
e32a2027e7 | ||
|
320e8de9e0 | ||
|
05d31ebf90 | ||
|
7789f3feb3 | ||
|
b2a1659117 | ||
|
659c607e85 | ||
|
36af56d37d | ||
|
207656b4d1 | ||
|
4c183b3bd3 | ||
|
edbaaffa88 | ||
|
276a88d965 | ||
|
3729341cec | ||
|
8b7b733723 | ||
|
b41d0a5c29 | ||
|
e1980005f3 | ||
|
1743373022 | ||
|
3b9ae65e98 | ||
|
ab7b302bd6 | ||
|
03a10455c6 | ||
|
4cccce21e0 | ||
|
672de99182 | ||
|
93fd8f8631 | ||
|
c89f97d49a | ||
|
78871142dc | ||
|
53ad50100a | ||
|
87fe87896a | ||
|
da63294ed4 | ||
|
8a32e10dc3 | ||
|
81c219f5fb | ||
|
b06e8e4c22 | ||
|
9156283acd | ||
|
0d9c90bfb3 | ||
|
5f97c22606 | ||
|
fb4d888419 | ||
|
dc747b585c | ||
|
de46204943 | ||
|
846ee51b46 | ||
|
efa6074a16 | ||
|
5dcec23317 | ||
|
76cc31fe6c | ||
|
ab6bbcea17 | ||
|
e9eae458a5 | ||
|
dbe697b852 | ||
|
ebbd6180b9 | ||
|
5db3c95954 | ||
|
393edf4050 | ||
|
f566af1fae | ||
|
02d6278161 | ||
|
649093bae2 | ||
|
9dded619c1 | ||
|
ff088d432f | ||
|
8189a3f53c | ||
|
f3c88bc352 | ||
|
3d6c58d35c | ||
|
8b81550553 | ||
|
6fd58d98e9 | ||
|
990069d2fd | ||
|
d192d78cd9 | ||
|
a054887730 | ||
|
2efce1b15b | ||
|
29b2cc790b | ||
|
8a95bb4c21 | ||
|
92ab62aca0 | ||
|
34779f1b85 | ||
|
b403283198 | ||
|
4ba3117ab9 | ||
|
cd705b51f9 | ||
|
7f0fdc9f59 | ||
|
d2e851bdfe | ||
|
5533ec97a5 | ||
|
a472fd2b02 | ||
|
8835d6069f | ||
|
e4b9d11d85 | ||
|
fd4034839f | ||
|
5a484e08d0 | ||
|
cefc156ea4 | ||
|
30f276e4da | ||
|
aec4d8e24c | ||
|
20da456f23 | ||
|
64fb82b190 | ||
|
fb1d23ca8e | ||
|
dbff7f1ada | ||
|
06436ebde5 | ||
|
00934875d1 | ||
|
43b251dfaf | ||
|
ed54475a60 | ||
|
a7026bb1fe | ||
|
33cca1ae7d | ||
|
870a6d1475 | ||
|
ea75b25c5e | ||
|
794e5834eb | ||
|
4f96ba4bad | ||
|
f854587116 | ||
|
ce98892ce2 | ||
|
63821d62e0 | ||
|
51ed1d7c5a | ||
|
f71febe2e1 | ||
|
705baaa476 | ||
|
a0c3a73803 | ||
|
4632737bdc | ||
|
b906dc41fb | ||
|
0180b47615 | ||
|
d7b80bac74 | ||
|
0eb7ab3f39 | ||
|
0d4d8a6006 | ||
|
657145a852 | ||
|
68ad75e139 | ||
|
5414483285 | ||
|
000bba48b3 | ||
|
9d066f31d1 | ||
|
2266bc8af7 | ||
|
b380fa3a13 | ||
|
945160857e | ||
|
567ecb14b1 | ||
|
ca10207b4a | ||
|
06c5188c9f | ||
|
d4d97c18f7 | ||
|
fbb343a371 | ||
|
f584aa6a5c | ||
|
906728b229 | ||
|
8526cde728 | ||
|
3d078501fd | ||
|
1a562a2190 | ||
|
4869b524d8 | ||
|
a69d19895b | ||
|
f2d5671812 | ||
|
a5a2b491ab | ||
|
04120e891a | ||
|
fba3a9c71e | ||
|
c3a255d4e9 | ||
|
ee25c7b42b | ||
|
ae4cfe532a | ||
|
0b9fe164da | ||
|
1e5b636649 | ||
|
03cabedd06 | ||
|
e0a4a0b514 | ||
|
3f14bb9924 | ||
|
cdb9300ac1 | ||
|
eeb66603eb | ||
|
404e5a0c4a | ||
|
b2a858fe0f | ||
|
70adca2259 | ||
|
d258626c85 | ||
|
1221794f0a | ||
|
aff02b798a | ||
|
c7ac4898ed | ||
|
5e1f8571d6 | ||
|
10270c90a0 | ||
|
5a206f2d2c | ||
|
191ac47af3 | ||
|
5e44cd6434 | ||
|
4e5b3bee63 | ||
|
3a7e051b80 | ||
|
bb008cd2c9 | ||
|
3531171bc4 | ||
|
b51f3e3ae0 | ||
|
ee5af0d185 | ||
|
2a9073838e | ||
|
92efcc520b | ||
|
a21eb6c932 | ||
|
b90edcc3c2 | ||
|
f79980553f | ||
|
fe921b417d | ||
|
d1de461cdb | ||
|
2183cb5869 | ||
|
2bad924ff0 | ||
|
ad1a44fbf7 | ||
|
f6978fb5ae | ||
|
b1f0013d8c | ||
|
923079e683 | ||
|
c64f6973c4 | ||
|
059707af53 | ||
|
a850d3d4f0 | ||
|
addd20360c | ||
|
6ca049e86c | ||
|
636def983b | ||
|
aa0bbcae25 | ||
|
2369b76a42 | ||
|
75290051a1 | ||
|
351fdbba9d | ||
|
274d86cdc6 | ||
|
437b4a3037 | ||
|
87d91752f3 | ||
|
e4f94fcc21 | ||
|
116eff17bc | ||
|
52fa3fe1b5 | ||
|
db43b42ef7 | ||
|
82d2a412ab | ||
|
945e6d6a3d | ||
|
0ab61566e2 | ||
|
a135a74946 | ||
|
a0e9c97c89 | ||
|
d63518195d | ||
|
37433bc022 | ||
|
388d48cfde | ||
|
0855aab927 | ||
|
7f52133218 | ||
|
4b8ec79cad | ||
|
35c4ccc7c2 | ||
|
c27273524a | ||
|
fed2a44f04 | ||
|
7dbd2d8f68 | ||
|
fc40d56b73 | ||
|
fc6c922f47 | ||
|
422028bb3c | ||
|
0407989de8 | ||
|
c2e3cc9756 | ||
|
dffeca1181 | ||
|
6ef9b70500 | ||
|
176c4ad7a3 | ||
|
6ffa99d624 | ||
|
d1e6bb66a9 | ||
|
10469ac3e0 | ||
|
36f10c7dc1 | ||
|
2591ae8226 | ||
|
1539688cb8 | ||
|
72fb12ab28 | ||
|
1cf49992fa | ||
|
22cf62c614 | ||
|
16f8c25d84 | ||
|
d2b7f3a7d8 | ||
|
27819cd51f | ||
|
d619e751e3 | ||
|
f0149fa495 | ||
|
fc03c2010f | ||
|
0cb3aff4e8 | ||
|
201a9be4a8 | ||
|
cd1875019d | ||
|
462082bee8 | ||
|
396a8de131 | ||
|
4efd1029a2 | ||
|
2ce67b0465 | ||
|
152bdee7f2 | ||
|
4c4ba3026b | ||
|
216eb2aab5 | ||
|
29f79d24de | ||
|
30c4c8001f | ||
|
b9a7feb225 | ||
|
2e1f94e831 | ||
|
987a5ac55e | ||
|
e308c0759f | ||
|
313f5a7354 | ||
|
5cb0a0955b | ||
|
31acbe6508 | ||
|
f101aa8511 | ||
|
b67b96c82a | ||
|
2a4ce52eaa | ||
|
b73cbc1230 | ||
|
b7081f238f | ||
|
dfa9d63097 | ||
|
5e87812ecf | ||
|
c392b5499e | ||
|
03e3a7ad85 | ||
|
c27e845d1a | ||
|
6df01a0f40 | ||
|
41c5930cde | ||
|
c2c133c605 | ||
|
85e3b00e5a | ||
|
971ab83376 | ||
|
13ca6ef95b | ||
|
322817e827 | ||
|
783eee0a39 | ||
|
6180bd1ca5 | ||
|
b433754c83 | ||
|
602255e9c1 | ||
|
97ca6d5cab | ||
|
649798b4ac | ||
|
a9fb6532ae | ||
|
25a2200f50 | ||
|
4cd0fcf288 | ||
|
ca80e009db | ||
|
490ff260c2 | ||
|
97df6fd283 | ||
|
1450fd0568 | ||
|
acd16bb984 | ||
|
c75b4555cc | ||
|
ba7c5ddcd2 | ||
|
651274e508 | ||
|
94dcf160df | ||
|
8a41cc693a | ||
|
70e57a2d66 | ||
|
c88168f4ba | ||
|
efbc365b6d | ||
|
cadf3a5d6a | ||
|
1dd963278f | ||
|
d9757f2c38 | ||
|
6071be1572 | ||
|
50403a3ed6 | ||
|
1de826270c | ||
|
ee0d6d38e0 | ||
|
e6e8855da4 | ||
|
bf92b3abff | ||
|
91c1326bc4 | ||
|
2dbe047d45 | ||
|
538966204c | ||
|
fd35d7c984 | ||
|
68ccc25108 | ||
|
e69f739d19 | ||
|
1daab4ef1a | ||
|
74cfea407d |
6
.github/dependabot.yml
vendored
Normal file
6
.github/dependabot.yml
vendored
Normal file
@ -0,0 +1,6 @@
|
||||
version: 2
|
||||
updates:
|
||||
- package-ecosystem: "gomod" # See documentation for possible values
|
||||
directory: "/" # Location of package manifests
|
||||
schedule:
|
||||
interval: "daily"
|
14
.github/release.yml
vendored
Normal file
14
.github/release.yml
vendored
Normal file
@ -0,0 +1,14 @@
|
||||
changelog:
|
||||
categories:
|
||||
- title: Breaking Changes 🛠
|
||||
labels:
|
||||
- breaking-change
|
||||
- title: New Features 🎉
|
||||
labels:
|
||||
- enhancement
|
||||
- title: Bug fixes
|
||||
labels:
|
||||
- bug
|
||||
- title: Other Changes
|
||||
labels:
|
||||
- "*"
|
24
.github/workflows/go-test.yml
vendored
Normal file
24
.github/workflows/go-test.yml
vendored
Normal file
@ -0,0 +1,24 @@
|
||||
name: go-test
|
||||
on:
|
||||
push:
|
||||
branches:
|
||||
- 'main'
|
||||
pull_request:
|
||||
|
||||
permissions:
|
||||
contents: read
|
||||
|
||||
jobs:
|
||||
go-tests:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- name: Checkout
|
||||
uses: actions/checkout@v2
|
||||
- name: Unshallow
|
||||
run: git fetch --prune --unshallow
|
||||
- name: Set up Go
|
||||
uses: actions/setup-go@v2
|
||||
with:
|
||||
go-version: "1.20"
|
||||
- name: Run go tests
|
||||
run: go test -race -count 1 ./... -timeout=3m
|
46
.github/workflows/release.yml
vendored
Normal file
46
.github/workflows/release.yml
vendored
Normal file
@ -0,0 +1,46 @@
|
||||
# This GitHub action can publish assets for release when a tag is created.
|
||||
# Currently its setup to run on any tag that matches the pattern "v*" (ie. v0.1.0).
|
||||
#
|
||||
# This uses an action (hashicorp/ghaction-import-gpg) that assumes you set your
|
||||
# private key in the `GPG_PRIVATE_KEY` secret and passphrase in the `GPG_PASSPHRASE`
|
||||
# secret. If you would rather own your own GPG handling, please fork this action
|
||||
# or use an alternative one for key handling.
|
||||
#
|
||||
# You will need to pass the `--batch` flag to `gpg` in your signing step
|
||||
# in `goreleaser` to indicate this is being used in a non-interactive mode.
|
||||
#
|
||||
name: release
|
||||
on:
|
||||
push:
|
||||
tags:
|
||||
- 'v*'
|
||||
jobs:
|
||||
goreleaser:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- name: Checkout
|
||||
uses: actions/checkout@v2
|
||||
- name: Unshallow
|
||||
run: git fetch --prune --unshallow
|
||||
- name: Set up Go
|
||||
uses: actions/setup-go@v2
|
||||
with:
|
||||
go-version: "1.20"
|
||||
- name: Describe plugin
|
||||
id: plugin_describe
|
||||
run: echo "::set-output name=api_version::$(go run . describe | jq -r '.api_version')"
|
||||
- name: Import GPG key
|
||||
id: import_gpg
|
||||
uses: crazy-max/ghaction-import-gpg@v5.0.0
|
||||
with:
|
||||
gpg_private_key: ${{ secrets.GPG_PRIVATE_KEY }}
|
||||
passphrase: ${{ secrets.GPG_PASSPHRASE }}
|
||||
- name: Run GoReleaser
|
||||
uses: goreleaser/goreleaser-action@f82d6c1c344bcacabba2c841718984797f664a6b # v4.2.0
|
||||
with:
|
||||
version: latest
|
||||
args: release --clean
|
||||
env:
|
||||
GPG_FINGERPRINT: ${{ steps.import_gpg.outputs.fingerprint }}
|
||||
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
||||
API_VERSION: ${{ steps.plugin_describe.outputs.api_version }}
|
8
.gitignore
vendored
8
.gitignore
vendored
@ -1,2 +1,10 @@
|
||||
*.swp
|
||||
*~
|
||||
bin
|
||||
pkg
|
||||
crash.log
|
||||
packer_cache/*
|
||||
builder-*
|
||||
dist/*
|
||||
vendor/*
|
||||
packer-plugin-xenserver
|
1
.go-version
Normal file
1
.go-version
Normal file
@ -0,0 +1 @@
|
||||
1.20.11
|
74
.goreleaser.yml
Normal file
74
.goreleaser.yml
Normal file
@ -0,0 +1,74 @@
|
||||
version: 2
|
||||
# This is an example goreleaser.yaml file with some sane defaults.
|
||||
# Make sure to check the documentation at http://goreleaser.com
|
||||
env:
|
||||
- CGO_ENABLED=0
|
||||
before:
|
||||
hooks:
|
||||
- go test ./...
|
||||
# As part of the release doc files are included as a separate deliverable for
|
||||
# consumption by Packer.io. To include a separate docs.zip uncomment the following command.
|
||||
#- make ci-release-docs
|
||||
# Check plugin compatibility with required version of the Packer SDK
|
||||
- make plugin-check
|
||||
builds:
|
||||
# A separated build to run the packer-plugins-check only once for a linux_amd64 binary
|
||||
-
|
||||
id: plugin-check
|
||||
mod_timestamp: '{{ .CommitTimestamp }}'
|
||||
flags:
|
||||
- -trimpath #removes all file system paths from the compiled executable
|
||||
ldflags:
|
||||
- '-s -w -X {{ .ModulePath }}/version.Version={{.Version}} -X {{ .ModulePath }}/version.VersionPrerelease= '
|
||||
goos:
|
||||
- linux
|
||||
goarch:
|
||||
- amd64
|
||||
binary: '{{ .ProjectName }}_v{{ .Version }}_{{ .Env.API_VERSION }}_{{ .Os }}_{{ .Arch }}'
|
||||
-
|
||||
mod_timestamp: '{{ .CommitTimestamp }}'
|
||||
flags:
|
||||
- -trimpath #removes all file system paths from the compiled executable
|
||||
ldflags:
|
||||
- '-s -w -X {{ .ModulePath }}/version.Version={{.Version}} -X {{ .ModulePath }}/version.VersionPrerelease= '
|
||||
goos:
|
||||
- freebsd
|
||||
- windows
|
||||
- linux
|
||||
- darwin
|
||||
goarch:
|
||||
- amd64
|
||||
- '386'
|
||||
- arm
|
||||
- arm64
|
||||
ignore:
|
||||
- goos: darwin
|
||||
goarch: '386'
|
||||
- goos: linux
|
||||
goarch: amd64
|
||||
binary: '{{ .ProjectName }}_v{{ .Version }}_{{ .Env.API_VERSION }}_{{ .Os }}_{{ .Arch }}'
|
||||
archives:
|
||||
- format: zip
|
||||
files:
|
||||
- none*
|
||||
name_template: '{{ .ProjectName }}_v{{ .Version }}_{{ .Env.API_VERSION }}_{{ .Os }}_{{ .Arch }}'
|
||||
checksum:
|
||||
name_template: '{{ .ProjectName }}_v{{ .Version }}_SHA256SUMS'
|
||||
algorithm: sha256
|
||||
signs:
|
||||
- artifacts: checksum
|
||||
args:
|
||||
# if you are using this is in a GitHub action or some other automated pipeline, you
|
||||
# need to pass the batch flag to indicate its not interactive.
|
||||
- "--batch"
|
||||
- "--local-user"
|
||||
- "{{ .Env.GPG_FINGERPRINT }}"
|
||||
- "--output"
|
||||
- "${signature}"
|
||||
- "--detach-sign"
|
||||
- "${artifact}"
|
||||
release:
|
||||
draft: false
|
||||
|
||||
changelog:
|
||||
disable: true
|
34
GNUmakefile
Normal file
34
GNUmakefile
Normal file
@ -0,0 +1,34 @@
|
||||
NAME=xenserver
|
||||
BINARY=packer-plugin-${NAME}
|
||||
|
||||
COUNT?=1
|
||||
TEST?=$(shell go list ./...)
|
||||
HASHICORP_PACKER_PLUGIN_SDK_VERSION?=$(shell go list -m github.com/hashicorp/packer-plugin-sdk | cut -d " " -f2)
|
||||
|
||||
.PHONY: dev
|
||||
|
||||
build:
|
||||
@go build -o ${BINARY}
|
||||
|
||||
dev: build
|
||||
@mkdir -p ~/.packer.d/plugins/
|
||||
@mv ${BINARY} ~/.packer.d/plugins/${BINARY}
|
||||
|
||||
test:
|
||||
@go test -race -count $(COUNT) $(TEST) -timeout=3m
|
||||
|
||||
install-packer-sdc: ## Install packer sofware development command
|
||||
@go install github.com/hashicorp/packer-plugin-sdk/cmd/packer-sdc@${HASHICORP_PACKER_PLUGIN_SDK_VERSION}
|
||||
|
||||
ci-release-docs: install-packer-sdc
|
||||
@packer-sdc renderdocs -src docs -partials docs-partials/ -dst docs/
|
||||
@/bin/sh -c "[ -d docs ] && zip -r docs.zip docs/"
|
||||
|
||||
plugin-check: install-packer-sdc build
|
||||
@packer-sdc plugin-check ${BINARY}
|
||||
|
||||
testacc: dev
|
||||
@PACKER_ACC=1 go test -count $(COUNT) -v $(TEST) -timeout=120m
|
||||
|
||||
generate: install-packer-sdc
|
||||
@go generate ./...
|
160
README.md
160
README.md
@ -2,134 +2,86 @@
|
||||
|
||||
This builder plugin extends packer.io to support building images for XenServer.
|
||||
|
||||
You can check out packer [here](https://packer.io).
|
||||
This is a fork of the original builder since the original project was abandoned and no longer compiled with recent versions of Go or worked with Xenserver 7.6 and later.
|
||||
|
||||
It improves the original project in the following ways:
|
||||
1. Developed alongside the [Xenorchestra terraform provider](https://github.com/ddelnano/terraform-provider-xenorchestra) to ensure the hashicorp ecosystem is interoperable.
|
||||
2. Reimplements how the boot commands are sent over VNC to be compatible with later versions of Xenserver (Citrix hypervisor) and XCP
|
||||
|
||||
## Dependencies
|
||||
* Packer >= 0.7.2 (https://packer.io)
|
||||
* XenServer > 6.2 (http://xenserver.org)
|
||||
* Golang (tested with 1.2.1)
|
||||
## Status
|
||||
|
||||
At the time of this writing the packer builder has been verified to work with Xenserver 7.6 and can launch VMs with the packer output through the xenorchestra terraform provider.
|
||||
|
||||
In order for this integration to work you must also configure NATing in Dom0.
|
||||
The following list contains things that are incomplete but will be worked on soon:
|
||||
|
||||
You can do this by executing the following in Dom0:
|
||||
- The documentation is still in an inconsistent state with upstream
|
||||
- XVA builder is untested
|
||||
- Lots of dead code to remove from upstream
|
||||
|
||||
```shell
|
||||
# Install netcat
|
||||
yum install --enablerepo=base,extras --disablerepo=citrix -y nc
|
||||
# Setup NAT - NB, this _disable the firewall_ - be careful!
|
||||
echo 1 > /proc/sys/net/ipv4/ip_forward
|
||||
/sbin/iptables -F INPUT
|
||||
## Using the builder
|
||||
|
||||
/sbin/iptables -t nat -A POSTROUTING -o xenbr0 -j MASQUERADE
|
||||
/sbin/iptables -A INPUT -i xenbr0 -p tcp -m tcp --dport 53 -j ACCEPT
|
||||
/sbin/iptables -A INPUT -i xenbr0 -p udp -m udp --dport 53 -j ACCEPT
|
||||
/sbin/iptables -A FORWARD -i xenbr0 -o xenapi -m state --state RELATED,ESTABLISHED -j ACCEPT
|
||||
/sbin/iptables -A FORWARD -i xenapi -o xenbr0 -j ACCEPT
|
||||
The packer builder can be installed via `packer init` as long as the packer template includes the following in it's `pkr.hcl` file
|
||||
```
|
||||
(Borrowed from: jonludlam/vagrant-xenserver)
|
||||
|
||||
|
||||
## Install Go
|
||||
|
||||
Follow these instructions and install golang on your system:
|
||||
* https://golang.org/doc/install
|
||||
|
||||
## Install Packer
|
||||
|
||||
Visit https://packer.io and install the latest version of packer. Once the
|
||||
install has completed, setup an environment variable 'PACKERPATH' pointing
|
||||
to the installation location. E.g.
|
||||
|
||||
```shell
|
||||
export PACKERPATH=/usr/local/packer
|
||||
packer {
|
||||
required_plugins {
|
||||
xenserver= {
|
||||
version = ">= v0.6.0"
|
||||
source = "github.com/ddelnano/xenserver"
|
||||
}
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
The following command will install the packer plugin using the Ubuntu example provided in this repository.
|
||||
|
||||
```
|
||||
packer init examples/ubuntu/ubuntu-2004.pkr.hcl
|
||||
```
|
||||
|
||||
If you are using an older version of packer or are still using json templates you will need to download the relevant release from the project's [releases page](https://github.com/ddelnano/packer-builder-xenserver/releases) and copy the binary to `~/.packer.d/plugins/packer-builder-xenserver-iso`.
|
||||
|
||||
## Developing the builder
|
||||
|
||||
### Dependencies
|
||||
* Packer >= v1.7.1 (https://packer.io)
|
||||
* XenServer / Citrix Hypervisor > 7.6
|
||||
* Golang 1.20
|
||||
|
||||
## Compile the plugin
|
||||
|
||||
Once you have installed Packer, you must compile this plugin and install the
|
||||
resulting binary.
|
||||
|
||||
```shell
|
||||
cd $GOROOT
|
||||
mkdir -p src/github.com/rdobson/
|
||||
cd src/github.com/rdobson
|
||||
git clone https://github.com/rdobson/packer-builder-xenserver.git
|
||||
cd packer-builder-xenserver
|
||||
./build.sh
|
||||
Documentation for Plugins directory: [Official Docs](https://developer.hashicorp.com/packer/docs/configure#packer-s-plugin-directory)
|
||||
|
||||
```
|
||||
|
||||
If the build is successful, you should now have a 'packer-builder-xenserver' binary
|
||||
in your $PACKERPATH directory and you are ready to get going with packer.
|
||||
|
||||
## Centos 6.4 Example
|
||||
|
||||
Once you've setup the above, you are good to go with an example.
|
||||
|
||||
To get you started, there is an example config file which you can use `examples/centos-6.4.conf`:
|
||||
### Linux/MacOS
|
||||
|
||||
```shell
|
||||
{
|
||||
"builders": [{
|
||||
"type": "xenserver",
|
||||
"username": "root",
|
||||
"password": "hostpassword",
|
||||
"host_ip": "10.81.2.105",
|
||||
"instance_name": "packer-centos-6-4",
|
||||
"instance_memory": "2048000000",
|
||||
"root_disk_size": "5000000000",
|
||||
"iso_name": "CentOS-6.4-x86_64-minimal.iso",
|
||||
"http_directory": "http",
|
||||
"local_ip": "10.80.3.223",
|
||||
"ssh_username": "root",
|
||||
"ssh_password": "vmpassword",
|
||||
"boot_command":
|
||||
[
|
||||
"<tab><wait>",
|
||||
" ks=http://{{ .HTTPIP }}:{{ .HTTPPort }}/centos6-ks.cfg<enter>"
|
||||
]
|
||||
}]
|
||||
}
|
||||
```
|
||||
Currently it is not (easily) possible to take care of the ISO download and upload,
|
||||
so you will need to attach an ISO SR to the XenServer host (NFS/CIFS) with the
|
||||
ISO you want to use for installation. You will then need to specify the name
|
||||
in the config file (this must be unique).
|
||||
go build -o packer-plugin-xenserver
|
||||
|
||||
|
||||
An explanation of what these parameters are doing:
|
||||
* `type` - this specifies the builder. This must be 'xenserver'.
|
||||
* `username` - this is the username for the XenServer host being used.
|
||||
* `password` - this is the password for the XenServer host being used.
|
||||
* `host_ip` - this is the IP for the XenServer host being used.
|
||||
* `instance_name` - this is the name that should be given to the created VM.
|
||||
* `instance_memory` - this is the static memory configuration for the VM.
|
||||
* `root_disk_size` - this is the size of the disk the VM should be created with.
|
||||
* `iso_name` - this is the name of the ISO visible on a ISO SR connected to the XenServer host.
|
||||
* `http_directory` - the path to a local directory to serve up over http.
|
||||
* `local_ip` - the IP on the machine you are running packer that your XenServer can connect too.
|
||||
* `ssh_username` - the username set by the installer for the instance.
|
||||
* `ssh_password` - the password set by the installer for the instance.
|
||||
* `boot_command` - a set of commands to be sent to the instance over VNC.
|
||||
|
||||
|
||||
Note, the `http_directory` and `local_ip` parameters are only required if you
|
||||
want packer to serve up files over HTTP. In this example, the templated variables
|
||||
`{{ .HTTPIP }}` and `{{ .HTTPPort }}` will be substituted for the `local_ip` and
|
||||
the port that packer starts it's HTTP service on.
|
||||
|
||||
Once you've updated the config file with your own parameters, you can use packer
|
||||
to build this VM with the following:
|
||||
|
||||
```
|
||||
packer build centos-6.4.conf
|
||||
# Add the plugin to the location packer expects it to be installed in
|
||||
mkdir -p ~/.packer.d/plugins/
|
||||
cp packer-plugin-xenserver ~/.packer.d/plugins
|
||||
```
|
||||
|
||||
### Windows (Powershell)
|
||||
|
||||
```powershell
|
||||
go build -o packer-plugin-xenserver
|
||||
|
||||
mkdir "%APPDATA%\packer.d\plugins"
|
||||
cp packer-plugin-xenserver "%APPDATA%\packer.d\plugins"
|
||||
```
|
||||
|
||||
# Documentation
|
||||
|
||||
For complete documentation on configuration commands, see [the
|
||||
xenserver-iso docs](docs/builders/iso/xenserver-iso.html.markdown)
|
||||
|
||||
## Support
|
||||
|
||||
You can discuss any issues you have or feature requests in [Discord](https://discord.gg/ZpNq8ez).
|
||||
|
||||
If you'd like to support my effort on the project, please consider buying me a coffee
|
||||
|
||||
[!["Buy Me A Coffee"](https://www.buymeacoffee.com/assets/img/custom_images/orange_img.png)](https://www.buymeacoffee.com/ddelnano)
|
||||
|
@ -1,57 +0,0 @@
|
||||
package xenserver
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"os"
|
||||
"github.com/mitchellh/packer/packer"
|
||||
"path/filepath"
|
||||
)
|
||||
|
||||
|
||||
type LocalArtifact struct {
|
||||
dir string
|
||||
f []string
|
||||
}
|
||||
|
||||
|
||||
func NewArtifact(dir string) (packer.Artifact, error) {
|
||||
files := make([]string, 0, 1)
|
||||
visit := func(path string, info os.FileInfo, err error) error {
|
||||
if !info.IsDir() {
|
||||
files = append(files, path)
|
||||
}
|
||||
return err
|
||||
}
|
||||
if err := filepath.Walk(dir, visit); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return &LocalArtifact{
|
||||
dir: dir,
|
||||
f: files,
|
||||
}, nil
|
||||
}
|
||||
|
||||
func (*LocalArtifact) BuilderId() string {
|
||||
return BuilderId
|
||||
}
|
||||
|
||||
func (a *LocalArtifact) Files() []string {
|
||||
return a.f
|
||||
}
|
||||
|
||||
func (*LocalArtifact) Id() string {
|
||||
return "VM"
|
||||
}
|
||||
|
||||
func (a *LocalArtifact) String() string {
|
||||
return fmt.Sprintf("VM files in directory: %s", a.dir)
|
||||
}
|
||||
|
||||
func (a *LocalArtifact) State(name string) interface{} {
|
||||
return nil
|
||||
}
|
||||
|
||||
func (a *LocalArtifact) Destroy() error {
|
||||
return os.RemoveAll(a.dir)
|
||||
}
|
@ -1,387 +0,0 @@
|
||||
package xenserver
|
||||
|
||||
import (
|
||||
"github.com/mitchellh/multistep"
|
||||
"github.com/mitchellh/packer/packer"
|
||||
"github.com/mitchellh/packer/common"
|
||||
"fmt"
|
||||
"log"
|
||||
"errors"
|
||||
"time"
|
||||
"os"
|
||||
commonssh "github.com/mitchellh/packer/common/ssh"
|
||||
)
|
||||
|
||||
|
||||
// Set the unique ID for this builder
|
||||
const BuilderId = "packer.xenserver"
|
||||
|
||||
|
||||
type config struct {
|
||||
common.PackerConfig `mapstructure:",squash"`
|
||||
|
||||
Username string `mapstructure:"username"`
|
||||
Password string `mapstructure:"password"`
|
||||
HostIp string `mapstructure:"host_ip"`
|
||||
IsoUrl string `mapstructure:"iso_url"`
|
||||
|
||||
InstanceName string `mapstructure:"instance_name"`
|
||||
InstanceMemory string `mapstructure:"instance_memory"`
|
||||
RootDiskSize string `mapstructure:"root_disk_size"`
|
||||
CloneTemplate string `mapstructure:"clone_template"`
|
||||
IsoName string `mapstructure:"iso_name"`
|
||||
SrName string `mapstructure:"sr_name"`
|
||||
NetworkName string `mapstructure:"network_name"`
|
||||
|
||||
HostPortMin uint `mapstructure:"host_port_min"`
|
||||
HostPortMax uint `mapstructure:"host_port_max"`
|
||||
|
||||
BootCommand []string `mapstructure:"boot_command"`
|
||||
RawBootWait string `mapstructure:"boot_wait"`
|
||||
|
||||
BootWait time.Duration ``
|
||||
sshWaitTimeout time.Duration ``
|
||||
|
||||
ISOChecksum string `mapstructure:"iso_checksum"`
|
||||
ISOChecksumType string `mapstructure:"iso_checksum_type"`
|
||||
ISOUrls []string `mapstructure:"iso_urls"`
|
||||
ISOUrl string `mapstructure:"iso_url"`
|
||||
|
||||
HTTPDir string `mapstructure:"http_directory"`
|
||||
HTTPPortMin uint `mapstructure:"http_port_min"`
|
||||
HTTPPortMax uint `mapstructure:"http_port_max"`
|
||||
|
||||
LocalIp string `mapstructure:"local_ip"`
|
||||
PlatformArgs map[string]string `mapstructure:"platform_args"`
|
||||
|
||||
RawSSHWaitTimeout string `mapstructure:"ssh_wait_timeout"`
|
||||
|
||||
SSHPassword string `mapstructure:"ssh_password"`
|
||||
SSHUser string `mapstructure:"ssh_username"`
|
||||
SSHKeyPath string `mapstructure:"ssh_key_path"`
|
||||
|
||||
OutputDir string `mapstructure:"output_directory"`
|
||||
|
||||
tpl *packer.ConfigTemplate
|
||||
}
|
||||
|
||||
|
||||
type Builder struct {
|
||||
config config
|
||||
runner multistep.Runner
|
||||
}
|
||||
|
||||
|
||||
func (self *Builder) Prepare (raws ...interface{}) (params []string, retErr error) {
|
||||
|
||||
md, err := common.DecodeConfig(&self.config, raws...)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
errs := common.CheckUnusedConfig(md)
|
||||
if errs == nil {
|
||||
errs = &packer.MultiError{}
|
||||
}
|
||||
|
||||
self.config.tpl, err = packer.NewConfigTemplate()
|
||||
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
// Set default vaules
|
||||
|
||||
if self.config.HostPortMin == 0 {
|
||||
self.config.HostPortMin = 5900
|
||||
}
|
||||
|
||||
if self.config.HostPortMax == 0 {
|
||||
self.config.HostPortMax = 6000
|
||||
}
|
||||
|
||||
if self.config.RawBootWait == "" {
|
||||
self.config.RawBootWait = "5s"
|
||||
}
|
||||
|
||||
if self.config.HTTPPortMin == 0 {
|
||||
self.config.HTTPPortMin = 8000
|
||||
}
|
||||
|
||||
if self.config.HTTPPortMax == 0 {
|
||||
self.config.HTTPPortMax = 9000
|
||||
}
|
||||
|
||||
if self.config.RawSSHWaitTimeout == "" {
|
||||
self.config.RawSSHWaitTimeout = "200m"
|
||||
}
|
||||
|
||||
if self.config.OutputDir == "" {
|
||||
self.config.OutputDir = fmt.Sprintf("output-%s", self.config.PackerBuildName)
|
||||
}
|
||||
|
||||
templates := map[string]*string {
|
||||
"username": &self.config.Username,
|
||||
"password": &self.config.Password,
|
||||
"host_ip": &self.config.HostIp,
|
||||
"iso_url": &self.config.IsoUrl,
|
||||
"instance_name": &self.config.InstanceName,
|
||||
"instance_memory": &self.config.InstanceMemory,
|
||||
"root_disk_size": &self.config.RootDiskSize,
|
||||
"clone_template": &self.config.CloneTemplate,
|
||||
"iso_name": &self.config.IsoName,
|
||||
"sr_name": &self.config.SrName,
|
||||
"network_name": &self.config.NetworkName,
|
||||
"boot_wait": &self.config.RawBootWait,
|
||||
"iso_checksum": &self.config.ISOChecksum,
|
||||
"iso_checksum_type": &self.config.ISOChecksumType,
|
||||
"http_directory": &self.config.HTTPDir,
|
||||
"local_ip": &self.config.LocalIp,
|
||||
"ssh_wait_timeout": &self.config.RawSSHWaitTimeout,
|
||||
"ssh_username": &self.config.SSHUser,
|
||||
"ssh_password": &self.config.SSHPassword,
|
||||
"ssh_key_path": &self.config.SSHKeyPath,
|
||||
"output_directory": &self.config.OutputDir,
|
||||
}
|
||||
|
||||
|
||||
for n, ptr := range templates {
|
||||
var err error
|
||||
*ptr, err = self.config.tpl.Process(*ptr, nil)
|
||||
if err != nil {
|
||||
errs = packer.MultiErrorAppend(errs, fmt.Errorf("Error processing %s: %s", n, err))
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
if self.config.IsoUrl == "" {
|
||||
errs = packer.MultiErrorAppend(
|
||||
errs, errors.New("a iso url must be specified"))
|
||||
}
|
||||
*/
|
||||
|
||||
self.config.BootWait, err = time.ParseDuration(self.config.RawBootWait)
|
||||
if err != nil {
|
||||
errs = packer.MultiErrorAppend(
|
||||
errs, errors.New("Failed to parse boot_wait."))
|
||||
}
|
||||
|
||||
self.config.sshWaitTimeout, err = time.ParseDuration(self.config.RawSSHWaitTimeout)
|
||||
if err != nil {
|
||||
errs = packer.MultiErrorAppend(
|
||||
errs, fmt.Errorf("Failed to parse ssh_wait_timeout: %s", err))
|
||||
}
|
||||
|
||||
for i, command := range self.config.BootCommand {
|
||||
if err := self.config.tpl.Validate(command); err != nil {
|
||||
errs = packer.MultiErrorAppend(errs,
|
||||
fmt.Errorf("Error processing boot_command[%d]: %s", i, err))
|
||||
}
|
||||
}
|
||||
|
||||
if self.config.SSHUser == "" {
|
||||
errs = packer.MultiErrorAppend(
|
||||
errs, errors.New("An ssh_username must be specified."))
|
||||
}
|
||||
|
||||
if self.config.SSHKeyPath != "" {
|
||||
if _, err := os.Stat(self.config.SSHKeyPath); err != nil {
|
||||
errs = packer.MultiErrorAppend(
|
||||
errs, fmt.Errorf("ssh_key_path is invalid: %s", err))
|
||||
} else if _, err := commonssh.FileSigner(self.config.SSHKeyPath); err != nil {
|
||||
errs = packer.MultiErrorAppend(
|
||||
errs, fmt.Errorf("ssh_key_path is invalid: %s", err))
|
||||
}
|
||||
}
|
||||
|
||||
if self.config.Username == "" {
|
||||
errs = packer.MultiErrorAppend(
|
||||
errs, errors.New("A username for the xenserver host must be specified."))
|
||||
}
|
||||
|
||||
if self.config.Password == "" {
|
||||
errs = packer.MultiErrorAppend(
|
||||
errs, errors.New("A password for the xenserver host must be specified."))
|
||||
}
|
||||
|
||||
if self.config.HostIp == "" {
|
||||
errs = packer.MultiErrorAppend(
|
||||
errs, errors.New("An ip for the xenserver host must be specified."))
|
||||
}
|
||||
|
||||
if self.config.InstanceName == "" {
|
||||
errs = packer.MultiErrorAppend(
|
||||
errs, errors.New("An insatnce name must be specified."))
|
||||
}
|
||||
|
||||
if self.config.InstanceMemory == "" {
|
||||
self.config.InstanceMemory = "1024000000"
|
||||
}
|
||||
|
||||
if self.config.RootDiskSize == "" {
|
||||
errs = packer.MultiErrorAppend(
|
||||
errs, errors.New("A root disk size must be specified."))
|
||||
}
|
||||
|
||||
if self.config.CloneTemplate == "" {
|
||||
self.config.CloneTemplate = "Other install media"
|
||||
}
|
||||
|
||||
/*
|
||||
if self.config.LocalIp == "" {
|
||||
errs = packer.MultiErrorAppend(
|
||||
errs, errors.New("A local IP visible to XenServer's mangement interface is required to serve files."))
|
||||
}
|
||||
*/
|
||||
|
||||
if len(self.config.PlatformArgs) == 0 {
|
||||
pargs := make(map[string]string)
|
||||
pargs["viridian"] = "false"
|
||||
pargs["nx"] = "true"
|
||||
pargs["pae"] = "true"
|
||||
pargs["apic"] = "true"
|
||||
pargs["timeoffset"] = "0"
|
||||
pargs["acpi"] = "1"
|
||||
self.config.PlatformArgs = pargs
|
||||
}
|
||||
|
||||
if self.config.HTTPPortMin > self.config.HTTPPortMax {
|
||||
errs = packer.MultiErrorAppend(
|
||||
errs, errors.New("the HTTP min port must be less than the max"))
|
||||
}
|
||||
|
||||
if self.config.HostPortMin > self.config.HostPortMax {
|
||||
errs = packer.MultiErrorAppend(
|
||||
errs, errors.New("the host min port must be less than the max"))
|
||||
}
|
||||
/*
|
||||
if self.config.ISOChecksumType == "" {
|
||||
errs = packer.MultiErrorAppend(
|
||||
errs, errors.New("The iso_checksum_type must be specified."))
|
||||
} else {
|
||||
self.config.ISOChecksumType = strings.ToLower(self.config.ISOChecksumType)
|
||||
if self.config.ISOChecksumType != "none" {
|
||||
if self.config.ISOChecksum == "" {
|
||||
errs = packer.MultiErrorAppend(
|
||||
errs, errors.New("Due to the file size being large, an iso_checksum is required."))
|
||||
} else {
|
||||
self.config.ISOChecksum = strings.ToLower(self.config.ISOChecksum)
|
||||
}
|
||||
|
||||
if hash := common.HashForType(self.config.ISOChecksumType); hash == nil {
|
||||
errs = packer.MultiErrorAppend(
|
||||
errs, fmt.Errorf("Unsupported checksum type: %s", self.config.ISOChecksumType))
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
if self.config.ISOUrl == "" {
|
||||
errs = packer.MultiErrorAppend(
|
||||
errs, errors.New("A ISO URL must be specfied."))
|
||||
} else {
|
||||
self.config.ISOUrls = []string{self.config.ISOUrl}
|
||||
}
|
||||
|
||||
for i, url := range self.config.ISOUrls {
|
||||
self.config.ISOUrls[i], err = common.DownloadableURL(url)
|
||||
if err != nil {
|
||||
errs = packer.MultiErrorAppend(
|
||||
errs, fmt.Errorf("Failed to parse the iso_url (%d): %s", i, err))
|
||||
}
|
||||
}
|
||||
*/
|
||||
if len(errs.Errors) > 0 {
|
||||
retErr = errors.New(errs.Error())
|
||||
}
|
||||
|
||||
return nil, retErr
|
||||
|
||||
}
|
||||
|
||||
func (self *Builder) Run(ui packer.Ui, hook packer.Hook, cache packer.Cache) (packer.Artifact, error) {
|
||||
//Setup XAPI client
|
||||
client := NewXenAPIClient(self.config.HostIp, self.config.Username, self.config.Password)
|
||||
|
||||
err := client.Login()
|
||||
if err != nil {
|
||||
return nil, err.(error)
|
||||
}
|
||||
ui.Say("XAPI client session established")
|
||||
|
||||
client.GetHosts()
|
||||
|
||||
//Share state between the other steps using a statebag
|
||||
state := new(multistep.BasicStateBag)
|
||||
state.Put("cache", cache)
|
||||
state.Put("client", client)
|
||||
state.Put("config", self.config)
|
||||
state.Put("hook", hook)
|
||||
state.Put("ui", ui)
|
||||
|
||||
|
||||
//Build the steps
|
||||
steps := []multistep.Step{
|
||||
/*
|
||||
&common.StepDownload{
|
||||
Checksum: self.config.ISOChecksum,
|
||||
ChecksumType: self.config.ISOChecksumType,
|
||||
Description: "ISO",
|
||||
ResultKey: "iso_path",
|
||||
Url: self.config.ISOUrls,
|
||||
},
|
||||
*/
|
||||
new(stepPrepareOutputDir),
|
||||
new(stepHTTPServer),
|
||||
//new(stepUploadIso),
|
||||
new(stepCreateInstance),
|
||||
new(stepStartVmPaused),
|
||||
new(stepGetVNCPort),
|
||||
&stepForwardPortOverSSH{
|
||||
RemotePort: instanceVNCPort,
|
||||
RemoteDest: instanceVNCIP,
|
||||
HostPortMin: self.config.HostPortMin,
|
||||
HostPortMax: self.config.HostPortMax,
|
||||
ResultKey: "local_vnc_port",
|
||||
},
|
||||
new(stepBootWait),
|
||||
new(stepTypeBootCommand),
|
||||
new(stepWait),
|
||||
new(stepStartOnHIMN),
|
||||
&stepForwardPortOverSSH{
|
||||
RemotePort: himnSSHPort,
|
||||
RemoteDest: himnSSHIP,
|
||||
HostPortMin: self.config.HostPortMin,
|
||||
HostPortMax: self.config.HostPortMax,
|
||||
ResultKey: "local_ssh_port",
|
||||
},
|
||||
&common.StepConnectSSH{
|
||||
SSHAddress: sshLocalAddress,
|
||||
SSHConfig: sshConfig,
|
||||
SSHWaitTimeout: self.config.sshWaitTimeout,
|
||||
},
|
||||
new(common.StepProvision),
|
||||
new(stepShutdownAndExport),
|
||||
}
|
||||
|
||||
self.runner = &multistep.BasicRunner{Steps: steps}
|
||||
self.runner.Run(state)
|
||||
|
||||
artifact, _ := NewArtifact(self.config.OutputDir)
|
||||
|
||||
if rawErr, ok := state.GetOk("error"); ok {
|
||||
return nil, rawErr.(error)
|
||||
}
|
||||
|
||||
return artifact, nil
|
||||
}
|
||||
|
||||
|
||||
func (self *Builder) Cancel() {
|
||||
if self.runner != nil {
|
||||
log.Println("Cancelling the step runner...")
|
||||
self.runner.Cancel()
|
||||
}
|
||||
fmt.Println("Cancelling the builder")
|
||||
}
|
||||
|
@ -1,761 +0,0 @@
|
||||
package xenserver
|
||||
|
||||
import (
|
||||
"github.com/nilshell/xmlrpc"
|
||||
"log"
|
||||
"fmt"
|
||||
"errors"
|
||||
)
|
||||
|
||||
func check(err error) {
|
||||
if err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
type XenAPIClient struct {
|
||||
Session interface{}
|
||||
Host string
|
||||
Url string
|
||||
Username string
|
||||
Password string
|
||||
RPC *xmlrpc.Client
|
||||
}
|
||||
|
||||
|
||||
type APIResult struct {
|
||||
Status string
|
||||
Value interface{}
|
||||
ErrorDescription string
|
||||
}
|
||||
|
||||
type VM struct {
|
||||
Ref string
|
||||
Client *XenAPIClient
|
||||
}
|
||||
|
||||
type SR struct {
|
||||
Ref string
|
||||
Client *XenAPIClient
|
||||
}
|
||||
|
||||
type VDI struct {
|
||||
Ref string
|
||||
Client *XenAPIClient
|
||||
}
|
||||
|
||||
type Network struct {
|
||||
Ref string
|
||||
Client *XenAPIClient
|
||||
}
|
||||
|
||||
type VBD struct {
|
||||
Ref string
|
||||
Client *XenAPIClient
|
||||
}
|
||||
|
||||
type VIF struct {
|
||||
Ref string
|
||||
Client *XenAPIClient
|
||||
}
|
||||
|
||||
type PIF struct {
|
||||
Ref string
|
||||
Client *XenAPIClient
|
||||
}
|
||||
|
||||
type Pool struct {
|
||||
Ref string
|
||||
Client *XenAPIClient
|
||||
}
|
||||
|
||||
func (c *XenAPIClient) RPCCall (result interface{}, method string, params []interface{}) (err error) {
|
||||
fmt.Println(params)
|
||||
p := new(xmlrpc.Params)
|
||||
p.Params = params
|
||||
err = c.RPC.Call(method, *p, result)
|
||||
return err
|
||||
}
|
||||
|
||||
|
||||
func (client *XenAPIClient) Login () (err error) {
|
||||
//Do loging call
|
||||
result := xmlrpc.Struct{}
|
||||
|
||||
params := make([]interface{}, 2)
|
||||
params[0] = client.Username
|
||||
params[1] = client.Password
|
||||
|
||||
err = client.RPCCall(&result, "session.login_with_password", params)
|
||||
client.Session = result["Value"]
|
||||
return err
|
||||
}
|
||||
|
||||
func (client *XenAPIClient) APICall (result *APIResult, method string, params ...interface{}) (err error) {
|
||||
if client.Session == nil {
|
||||
fmt.Println("Error: no session")
|
||||
return fmt.Errorf("No session. Unable to make call")
|
||||
}
|
||||
|
||||
//Make a params slice which will include the session
|
||||
p := make([]interface{}, len(params) + 1)
|
||||
p[0] = client.Session
|
||||
|
||||
if params != nil {
|
||||
for idx, element := range params {
|
||||
p[idx+1] = element
|
||||
}
|
||||
}
|
||||
|
||||
res := xmlrpc.Struct{}
|
||||
|
||||
err = client.RPCCall(&res, method, p)
|
||||
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
result.Status = res["Status"].(string)
|
||||
|
||||
if result.Status != "Success" {
|
||||
fmt.Println("Encountered an API error: ", result.Status)
|
||||
fmt.Println(res["ErrorDescription"])
|
||||
log.Fatal(res["ErrorDescription"])
|
||||
return errors.New("API Error occurred")
|
||||
} else {
|
||||
result.Value = res["Value"]
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
|
||||
func (client *XenAPIClient) GetHosts () (err error) {
|
||||
result := APIResult{}
|
||||
_ = client.APICall(&result, "host.get_all")
|
||||
hosts := result.Value
|
||||
fmt.Println(hosts)
|
||||
return nil
|
||||
}
|
||||
|
||||
|
||||
func (client *XenAPIClient) GetPools () (pools []*Pool, err error) {
|
||||
pools = make([]*Pool, 0)
|
||||
result := APIResult{}
|
||||
err = client.APICall(&result, "pool.get_all")
|
||||
if err != nil {
|
||||
return pools, err
|
||||
}
|
||||
|
||||
for _, elem := range result.Value.([]interface{}) {
|
||||
pool := new(Pool)
|
||||
pool.Ref = elem.(string)
|
||||
pool.Client = client
|
||||
pools = append(pools, pool)
|
||||
}
|
||||
|
||||
return pools, nil
|
||||
}
|
||||
|
||||
|
||||
func (client *XenAPIClient) GetDefaultSR () (sr *SR, err error) {
|
||||
pools, err := client.GetPools()
|
||||
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
pool_rec, err := pools[0].GetRecord()
|
||||
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
if pool_rec["default_SR"] == "" {
|
||||
return nil, errors.New("No default_SR specified for the pool.")
|
||||
}
|
||||
|
||||
sr = new(SR)
|
||||
sr.Ref = pool_rec["default_SR"].(string)
|
||||
sr.Client = client
|
||||
|
||||
return sr, nil
|
||||
}
|
||||
|
||||
|
||||
func (client *XenAPIClient) GetVMByUuid (vm_uuid string) (vm *VM, err error) {
|
||||
vm = new(VM)
|
||||
result := APIResult{}
|
||||
err = client.APICall(&result, "VM.get_by_uuid", vm_uuid)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
vm.Ref = result.Value.(string)
|
||||
vm.Client = client
|
||||
return
|
||||
}
|
||||
|
||||
|
||||
func (client *XenAPIClient) GetVMByNameLabel (name_label string) (vms []*VM, err error) {
|
||||
vms = make([]*VM, 0)
|
||||
result := APIResult{}
|
||||
err = client.APICall(&result, "VM.get_by_name_label", name_label)
|
||||
if err != nil {
|
||||
return vms, err
|
||||
}
|
||||
|
||||
for _, elem := range result.Value.([]interface{}) {
|
||||
vm := new(VM)
|
||||
vm.Ref = elem.(string)
|
||||
vm.Client = client
|
||||
vms = append(vms, vm)
|
||||
}
|
||||
|
||||
return vms, nil
|
||||
}
|
||||
|
||||
|
||||
func (client *XenAPIClient) GetSRByNameLabel (name_label string) (srs []*SR, err error) {
|
||||
srs = make([]*SR, 0)
|
||||
result := APIResult{}
|
||||
err = client.APICall(&result, "SR.get_by_name_label", name_label)
|
||||
if err != nil {
|
||||
return srs, err
|
||||
}
|
||||
|
||||
for _, elem := range result.Value.([]interface{}) {
|
||||
sr := new(SR)
|
||||
sr.Ref = elem.(string)
|
||||
sr.Client = client
|
||||
srs = append(srs, sr)
|
||||
}
|
||||
|
||||
return srs, nil
|
||||
}
|
||||
|
||||
func (client *XenAPIClient) GetNetworkByUuid (network_uuid string) (network *Network, err error) {
|
||||
network = new(Network)
|
||||
result := APIResult{}
|
||||
err = client.APICall(&result, "network.get_by_uuid", network_uuid)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
network.Ref = result.Value.(string)
|
||||
network.Client = client
|
||||
return
|
||||
}
|
||||
|
||||
|
||||
func (client *XenAPIClient) GetNetworkByNameLabel (name_label string) (networks []*Network, err error) {
|
||||
networks = make([]*Network, 0)
|
||||
result := APIResult{}
|
||||
err = client.APICall(&result, "network.get_by_name_label", name_label)
|
||||
if err != nil {
|
||||
return networks, err
|
||||
}
|
||||
|
||||
for _, elem := range result.Value.([]interface{}) {
|
||||
network := new(Network)
|
||||
network.Ref = elem.(string)
|
||||
network.Client = client
|
||||
networks = append(networks, network)
|
||||
}
|
||||
|
||||
return networks, nil
|
||||
}
|
||||
|
||||
func (client *XenAPIClient) GetVdiByNameLabel (name_label string) (vdis []*VDI, err error) {
|
||||
vdis = make([]*VDI, 0)
|
||||
result := APIResult{}
|
||||
err = client.APICall(&result, "VDI.get_by_name_label", name_label)
|
||||
if err != nil {
|
||||
return vdis, err
|
||||
}
|
||||
|
||||
for _, elem := range result.Value.([]interface{}) {
|
||||
vdi := new(VDI)
|
||||
vdi.Ref = elem.(string)
|
||||
vdi.Client = client
|
||||
vdis = append(vdis, vdi)
|
||||
}
|
||||
|
||||
return vdis, nil
|
||||
}
|
||||
|
||||
|
||||
func (client *XenAPIClient) GetSRByUuid (sr_uuid string) (sr *SR, err error) {
|
||||
sr = new(SR)
|
||||
result := APIResult{}
|
||||
err = client.APICall(&result, "SR.get_by_uuid", sr_uuid)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
sr.Ref = result.Value.(string)
|
||||
sr.Client = client
|
||||
return
|
||||
}
|
||||
|
||||
func (client *XenAPIClient) GetVdiByUuid (vdi_uuid string) (vdi *VDI, err error) {
|
||||
vdi = new(VDI)
|
||||
result := APIResult{}
|
||||
err = client.APICall(&result, "VDI.get_by_uuid", vdi_uuid)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
vdi.Ref = result.Value.(string)
|
||||
vdi.Client = client
|
||||
return
|
||||
}
|
||||
|
||||
func (client *XenAPIClient) GetPIFs() (pifs []*PIF, err error) {
|
||||
pifs = make([]*PIF, 0)
|
||||
result := APIResult{}
|
||||
err = client.APICall(&result, "PIF.get_all")
|
||||
if err != nil {
|
||||
return pifs, err
|
||||
}
|
||||
for _, elem := range result.Value.([]interface{}) {
|
||||
pif := new(PIF)
|
||||
pif.Ref = elem.(string)
|
||||
pif.Client = client
|
||||
pifs = append(pifs, pif)
|
||||
}
|
||||
|
||||
return pifs, nil
|
||||
}
|
||||
|
||||
// VM associated functions
|
||||
|
||||
func (self *VM) Clone (label string) (new_instance *VM, err error) {
|
||||
new_instance = new(VM)
|
||||
|
||||
result := APIResult{}
|
||||
err = self.Client.APICall(&result, "VM.clone", self.Ref, label)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
new_instance.Ref = result.Value.(string)
|
||||
new_instance.Client = self.Client
|
||||
return
|
||||
}
|
||||
|
||||
func (self *VM) Start(paused, force bool) (err error) {
|
||||
result := APIResult{}
|
||||
err = self.Client.APICall(&result, "VM.start", self.Ref, paused, force)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
func (self *VM) CleanShutdown() (err error) {
|
||||
result := APIResult{}
|
||||
err = self.Client.APICall(&result, "VM.clean_shutdown", self.Ref)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
func (self *VM) Unpause () (err error) {
|
||||
result := APIResult{}
|
||||
err = self.Client.APICall(&result, "VM.unpause", self.Ref)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
func (self *VM) SetPVBootloader(pv_bootloader, pv_args string) (err error) {
|
||||
result := APIResult{}
|
||||
err = self.Client.APICall(&result, "VM.set_PV_bootloader", self.Ref, pv_bootloader)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
result = APIResult{}
|
||||
err = self.Client.APICall(&result, "VM.set_PV_bootloader_args", self.Ref, pv_args)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
func (self *VM) GetDomainId() (domid string, err error) {
|
||||
result := APIResult{}
|
||||
err = self.Client.APICall(&result, "VM.get_domid", self.Ref)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
domid = result.Value.(string)
|
||||
return domid, nil
|
||||
}
|
||||
|
||||
func (self *VM) GetPowerState() (state string, err error) {
|
||||
result := APIResult{}
|
||||
err = self.Client.APICall(&result, "VM.get_power_state", self.Ref)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
state = result.Value.(string)
|
||||
return state, nil
|
||||
}
|
||||
|
||||
func (self *VM) GetUuid() (uuid string, err error) {
|
||||
result := APIResult{}
|
||||
err = self.Client.APICall(&result, "VM.get_uuid", self.Ref)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
uuid = result.Value.(string)
|
||||
return uuid, nil
|
||||
}
|
||||
|
||||
func (self *VM) GetVBDs() (vbds []VBD, err error) {
|
||||
vbds = make([]VBD, 0)
|
||||
result := APIResult{}
|
||||
err = self.Client.APICall(&result, "VM.get_VBDs", self.Ref)
|
||||
if err != nil {
|
||||
return vbds, err
|
||||
}
|
||||
for _, elem := range result.Value.([]interface{}) {
|
||||
vbd := VBD{}
|
||||
vbd.Ref = elem.(string)
|
||||
vbd.Client = self.Client
|
||||
vbds = append(vbds, vbd)
|
||||
}
|
||||
|
||||
return vbds, nil
|
||||
}
|
||||
|
||||
|
||||
func (self *VM) GetVIFs() (vifs []VIF, err error) {
|
||||
vifs = make([]VIF, 0)
|
||||
result := APIResult{}
|
||||
err = self.Client.APICall(&result, "VM.get_VIFs", self.Ref)
|
||||
if err != nil {
|
||||
return vifs, err
|
||||
}
|
||||
for _, elem := range result.Value.([]interface{}) {
|
||||
vif := VIF{}
|
||||
vif.Ref = elem.(string)
|
||||
vif.Client = self.Client
|
||||
vifs = append(vifs, vif)
|
||||
}
|
||||
|
||||
return vifs, nil
|
||||
}
|
||||
|
||||
func (self *VM) GetDisks() (vdis []*VDI, err error) {
|
||||
// Return just data disks (non-isos)
|
||||
vdis = make([]*VDI, 0)
|
||||
vbds, err := self.GetVBDs()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
for _, vbd := range vbds {
|
||||
rec, err := vbd.GetRecord()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if rec["type"] == "Disk" {
|
||||
|
||||
vdi, err := vbd.GetVDI()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
vdis = append(vdis, vdi)
|
||||
|
||||
}
|
||||
}
|
||||
return vdis, nil
|
||||
}
|
||||
|
||||
func (self *VM) GetGuestMetricsRef() (ref string, err error) {
|
||||
result := APIResult{}
|
||||
err = self.Client.APICall(&result, "VM.get_guest_metrics", self.Ref)
|
||||
if err != nil {
|
||||
return "", nil
|
||||
}
|
||||
ref = result.Value.(string)
|
||||
return ref, err
|
||||
}
|
||||
|
||||
func (self *VM) GetGuestMetrics() (metrics map[string]interface{}, err error) {
|
||||
metrics = make(map[string]interface{})
|
||||
metrics_ref, err := self.GetGuestMetricsRef()
|
||||
if err != nil {
|
||||
return metrics, err
|
||||
}
|
||||
|
||||
result := APIResult{}
|
||||
err = self.Client.APICall(&result, "VM_guest_metrics.get_record", metrics_ref)
|
||||
if err != nil {
|
||||
return metrics, nil
|
||||
}
|
||||
for k, v := range result.Value.(xmlrpc.Struct) {
|
||||
metrics[k] = v
|
||||
}
|
||||
return metrics, nil
|
||||
}
|
||||
|
||||
func (self *VM) SetStaticMemoryRange(min, max string) (err error) {
|
||||
result := APIResult{}
|
||||
err = self.Client.APICall(&result, "VM.set_memory_limits", self.Ref, min, max, min, max)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
func (self *VM) ConnectVdi (vdi *VDI, iso bool) (err error) {
|
||||
|
||||
// 1. Create a VBD
|
||||
|
||||
vbd_rec := make(xmlrpc.Struct)
|
||||
vbd_rec["VM"] = self.Ref
|
||||
vbd_rec["VDI"] = vdi.Ref
|
||||
vbd_rec["userdevice"] = "autodetect"
|
||||
vbd_rec["unpluggable"] = false
|
||||
vbd_rec["empty"] = false
|
||||
vbd_rec["other_config"] = make(xmlrpc.Struct)
|
||||
vbd_rec["qos_algorithm_type"] = ""
|
||||
vbd_rec["qos_algorithm_params"] = make(xmlrpc.Struct)
|
||||
|
||||
if iso {
|
||||
vbd_rec["mode"] = "RO"
|
||||
vbd_rec["bootable"] = true
|
||||
vbd_rec["type"] = "CD"
|
||||
} else {
|
||||
vbd_rec["mode"] = "RW"
|
||||
vbd_rec["bootable"] = false
|
||||
vbd_rec["type"] = "Disk"
|
||||
}
|
||||
|
||||
result := APIResult{}
|
||||
err = self.Client.APICall(&result, "VBD.create", vbd_rec)
|
||||
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
vbd_ref := result.Value.(string)
|
||||
fmt.Println("VBD Ref:", vbd_ref)
|
||||
|
||||
result = APIResult{}
|
||||
err = self.Client.APICall(&result, "VBD.get_uuid", vbd_ref)
|
||||
|
||||
fmt.Println("VBD UUID: ", result.Value.(string))
|
||||
/*
|
||||
// 2. Plug VBD (Non need - the VM hasn't booted.
|
||||
// @todo - check VM state
|
||||
result = APIResult{}
|
||||
err = self.Client.APICall(&result, "VBD.plug", vbd_ref)
|
||||
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
*/
|
||||
return
|
||||
}
|
||||
|
||||
func (self *VM) SetPlatform(params map[string]string) (err error) {
|
||||
result := APIResult{}
|
||||
platform_rec := make(xmlrpc.Struct)
|
||||
for key, value := range params {
|
||||
platform_rec[key] = value
|
||||
}
|
||||
|
||||
err = self.Client.APICall(&result, "VM.set_platform", self.Ref, platform_rec)
|
||||
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
|
||||
func (self *VM) ConnectNetwork (network *Network, device string) (vif *VIF, err error) {
|
||||
// Create the VIF
|
||||
|
||||
vif_rec := make(xmlrpc.Struct)
|
||||
vif_rec["network"] = network.Ref
|
||||
vif_rec["VM"] = self.Ref
|
||||
vif_rec["MAC"] = ""
|
||||
vif_rec["device"] = device
|
||||
vif_rec["MTU"] = "1504"
|
||||
vif_rec["other_config"] = make(xmlrpc.Struct)
|
||||
vif_rec["qos_algorithm_type"] = ""
|
||||
vif_rec["qos_algorithm_params"] = make(xmlrpc.Struct)
|
||||
|
||||
result := APIResult{}
|
||||
err = self.Client.APICall(&result, "VIF.create", vif_rec)
|
||||
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
vif = new(VIF)
|
||||
vif.Ref = result.Value.(string)
|
||||
vif.Client = self.Client
|
||||
|
||||
return vif, nil
|
||||
}
|
||||
|
||||
// Setters
|
||||
|
||||
func (self *VM) SetIsATemplate (is_a_template bool) (err error) {
|
||||
result := APIResult{}
|
||||
err = self.Client.APICall(&result, "VM.set_is_a_template", self.Ref, is_a_template)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
// SR associated functions
|
||||
|
||||
func (self *SR) CreateVdi (name_label, size string) (vdi *VDI, err error) {
|
||||
vdi = new(VDI)
|
||||
|
||||
vdi_rec := make(xmlrpc.Struct)
|
||||
vdi_rec["name_label"] = name_label
|
||||
vdi_rec["SR"] = self.Ref
|
||||
vdi_rec["virtual_size"] = size
|
||||
vdi_rec["type"] = "user"
|
||||
vdi_rec["sharable"] = false
|
||||
vdi_rec["read_only"] = false
|
||||
|
||||
oc := make(xmlrpc.Struct)
|
||||
oc["temp"] = "temp"
|
||||
vdi_rec["other_config"] = oc
|
||||
|
||||
|
||||
result := APIResult{}
|
||||
err = self.Client.APICall(&result, "VDI.create", vdi_rec)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
vdi.Ref = result.Value.(string)
|
||||
vdi.Client = self.Client
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
// Network associated functions
|
||||
|
||||
func (self *Network) GetAssignedIPs () (ip_map map[string]string, err error) {
|
||||
ip_map = make(map[string]string, 0)
|
||||
result := APIResult{}
|
||||
err = self.Client.APICall(&result, "network.get_assigned_ips", self.Ref)
|
||||
if err != nil {
|
||||
return ip_map, err
|
||||
}
|
||||
for k, v := range result.Value.(xmlrpc.Struct) {
|
||||
ip_map[k] = v.(string)
|
||||
}
|
||||
return ip_map, nil
|
||||
}
|
||||
|
||||
// PIF associated functions
|
||||
|
||||
func (self *PIF) GetRecord () (record map[string]interface{}, err error) {
|
||||
record = make(map[string]interface{})
|
||||
result := APIResult{}
|
||||
err = self.Client.APICall(&result, "PIF.get_record", self.Ref)
|
||||
if err != nil {
|
||||
return record, err
|
||||
}
|
||||
for k, v := range result.Value.(xmlrpc.Struct) {
|
||||
record[k] = v
|
||||
}
|
||||
return record, nil
|
||||
}
|
||||
|
||||
// Pool associated functions
|
||||
|
||||
func (self *Pool) GetRecord () (record map[string]interface{}, err error) {
|
||||
record = make(map[string]interface{})
|
||||
result := APIResult{}
|
||||
err = self.Client.APICall(&result, "pool.get_record", self.Ref)
|
||||
if err != nil {
|
||||
return record, err
|
||||
}
|
||||
for k, v := range result.Value.(xmlrpc.Struct) {
|
||||
record[k] = v
|
||||
}
|
||||
return record, nil
|
||||
}
|
||||
|
||||
|
||||
// VBD associated functions
|
||||
func (self *VBD) GetRecord () (record map[string]interface{}, err error) {
|
||||
record = make(map[string]interface{})
|
||||
result := APIResult{}
|
||||
err = self.Client.APICall(&result, "VBD.get_record", self.Ref)
|
||||
if err != nil {
|
||||
return record, err
|
||||
}
|
||||
for k, v := range result.Value.(xmlrpc.Struct) {
|
||||
record[k] = v
|
||||
}
|
||||
return record, nil
|
||||
}
|
||||
|
||||
func (self *VBD) GetVDI () (vdi *VDI, err error) {
|
||||
vbd_rec, err := self.GetRecord()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
vdi = new(VDI)
|
||||
vdi.Ref = vbd_rec["VDI"].(string)
|
||||
vdi.Client = self.Client
|
||||
|
||||
return vdi, nil
|
||||
}
|
||||
|
||||
func (self *VBD) Eject () (err error) {
|
||||
result := APIResult{}
|
||||
err = self.Client.APICall(&result, "VBD.eject", self.Ref)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// VIF associated functions
|
||||
|
||||
func (self *VIF) Destroy () (err error) {
|
||||
result := APIResult{}
|
||||
err = self.Client.APICall(&result, "VIF.destroy", self.Ref)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// VDI associated functions
|
||||
|
||||
func (self *VDI) GetUuid () (vdi_uuid string, err error) {
|
||||
result := APIResult{}
|
||||
err = self.Client.APICall(&result, "VDI.get_uuid", self.Ref)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
vdi_uuid = result.Value.(string)
|
||||
return vdi_uuid, nil
|
||||
}
|
||||
|
||||
|
||||
// Client Initiator
|
||||
|
||||
func NewXenAPIClient (host, username, password string) (client XenAPIClient) {
|
||||
client.Host = host
|
||||
client.Url = "http://" + host
|
||||
client.Username = username
|
||||
client.Password = password
|
||||
client.RPC, _ = xmlrpc.NewClient(client.Url, nil)
|
||||
return
|
||||
}
|
58
builder/xenserver/common/artifact.go
Normal file
58
builder/xenserver/common/artifact.go
Normal file
@ -0,0 +1,58 @@
|
||||
package common
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"github.com/hashicorp/packer-plugin-sdk/packer"
|
||||
"os"
|
||||
"path/filepath"
|
||||
)
|
||||
|
||||
// This is the common builder ID to all of these artifacts.
|
||||
const BuilderId = "packer.xenserver"
|
||||
|
||||
type LocalArtifact struct {
|
||||
dir string
|
||||
f []string
|
||||
}
|
||||
|
||||
func NewArtifact(dir string) (packer.Artifact, error) {
|
||||
files := make([]string, 0, 1)
|
||||
visit := func(path string, info os.FileInfo, err error) error {
|
||||
if !info.IsDir() {
|
||||
files = append(files, path)
|
||||
}
|
||||
return err
|
||||
}
|
||||
if err := filepath.Walk(dir, visit); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return &LocalArtifact{
|
||||
dir: dir,
|
||||
f: files,
|
||||
}, nil
|
||||
}
|
||||
|
||||
func (*LocalArtifact) BuilderId() string {
|
||||
return BuilderId
|
||||
}
|
||||
|
||||
func (a *LocalArtifact) Files() []string {
|
||||
return a.f
|
||||
}
|
||||
|
||||
func (*LocalArtifact) Id() string {
|
||||
return "VM"
|
||||
}
|
||||
|
||||
func (a *LocalArtifact) String() string {
|
||||
return fmt.Sprintf("VM files in directory: %s", a.dir)
|
||||
}
|
||||
|
||||
func (a *LocalArtifact) State(name string) interface{} {
|
||||
return nil
|
||||
}
|
||||
|
||||
func (a *LocalArtifact) Destroy() error {
|
||||
return os.RemoveAll(a.dir)
|
||||
}
|
985
builder/xenserver/common/client.go
Normal file
985
builder/xenserver/common/client.go
Normal file
@ -0,0 +1,985 @@
|
||||
package common
|
||||
|
||||
import (
|
||||
"encoding/xml"
|
||||
"errors"
|
||||
"fmt"
|
||||
"log"
|
||||
|
||||
xmlrpc "github.com/amfranz/go-xmlrpc-client"
|
||||
xenapi "github.com/terra-farm/go-xen-api-client"
|
||||
)
|
||||
|
||||
type XenAPIClient struct {
|
||||
Session interface{}
|
||||
Host string
|
||||
Url string
|
||||
Username string
|
||||
Password string
|
||||
RPC *xmlrpc.Client
|
||||
}
|
||||
|
||||
type APIResult struct {
|
||||
Status string
|
||||
Value interface{}
|
||||
ErrorDescription string
|
||||
}
|
||||
|
||||
type XenAPIObject struct {
|
||||
Ref string
|
||||
Client *XenAPIClient
|
||||
}
|
||||
|
||||
type Host XenAPIObject
|
||||
type VM XenAPIObject
|
||||
type SR XenAPIObject
|
||||
type VDI XenAPIObject
|
||||
type Network XenAPIObject
|
||||
type VBD XenAPIObject
|
||||
type VIF XenAPIObject
|
||||
type PIF XenAPIObject
|
||||
type Pool XenAPIObject
|
||||
type Task XenAPIObject
|
||||
|
||||
type VDIType int
|
||||
|
||||
const (
|
||||
_ VDIType = iota
|
||||
Disk
|
||||
CD
|
||||
Floppy
|
||||
)
|
||||
|
||||
type TaskStatusType int
|
||||
|
||||
const (
|
||||
_ TaskStatusType = iota
|
||||
Pending
|
||||
Success
|
||||
Failure
|
||||
Cancelling
|
||||
Cancelled
|
||||
)
|
||||
|
||||
func (c *XenAPIClient) RPCCall(result interface{}, method string, params []interface{}) (err error) {
|
||||
fmt.Println(params)
|
||||
p := xmlrpc.Params{Params: params}
|
||||
err = c.RPC.Call(method, p, result)
|
||||
return err
|
||||
}
|
||||
|
||||
func (client *XenAPIClient) Login() (err error) {
|
||||
//Do loging call
|
||||
result := xmlrpc.Struct{}
|
||||
|
||||
params := make([]interface{}, 2)
|
||||
params[0] = client.Username
|
||||
params[1] = client.Password
|
||||
|
||||
err = client.RPCCall(&result, "session.login_with_password", params)
|
||||
client.Session = result["Value"]
|
||||
return err
|
||||
}
|
||||
|
||||
func (client *XenAPIClient) APICall(result *APIResult, method string, params ...interface{}) (err error) {
|
||||
if client.Session == nil {
|
||||
fmt.Println("Error: no session")
|
||||
return fmt.Errorf("No session. Unable to make call")
|
||||
}
|
||||
|
||||
//Make a params slice which will include the session
|
||||
p := make([]interface{}, len(params)+1)
|
||||
p[0] = client.Session
|
||||
|
||||
if params != nil {
|
||||
for idx, element := range params {
|
||||
p[idx+1] = element
|
||||
}
|
||||
}
|
||||
|
||||
res := xmlrpc.Struct{}
|
||||
|
||||
err = client.RPCCall(&res, method, p)
|
||||
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
result.Status = res["Status"].(string)
|
||||
|
||||
if result.Status != "Success" {
|
||||
fmt.Println("Encountered an API error: ", result.Status)
|
||||
fmt.Println(res["ErrorDescription"])
|
||||
return fmt.Errorf("API Error: %s", res["ErrorDescription"])
|
||||
} else {
|
||||
result.Value = res["Value"]
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
func (client *XenAPIClient) GetHosts() (hosts []*Host, err error) {
|
||||
hosts = make([]*Host, 0)
|
||||
result := APIResult{}
|
||||
_ = client.APICall(&result, "host.get_all")
|
||||
for _, elem := range result.Value.([]interface{}) {
|
||||
host := new(Host)
|
||||
host.Ref = elem.(string)
|
||||
host.Client = client
|
||||
hosts = append(hosts, host)
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
func (client *XenAPIClient) GetPools() (pools []*Pool, err error) {
|
||||
pools = make([]*Pool, 0)
|
||||
result := APIResult{}
|
||||
err = client.APICall(&result, "pool.get_all")
|
||||
if err != nil {
|
||||
return pools, err
|
||||
}
|
||||
|
||||
for _, elem := range result.Value.([]interface{}) {
|
||||
pool := new(Pool)
|
||||
pool.Ref = elem.(string)
|
||||
pool.Client = client
|
||||
pools = append(pools, pool)
|
||||
}
|
||||
|
||||
return pools, nil
|
||||
}
|
||||
|
||||
func (client *XenAPIClient) GetDefaultSR() (sr *SR, err error) {
|
||||
pools, err := client.GetPools()
|
||||
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
pool_rec, err := pools[0].GetRecord()
|
||||
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
if pool_rec["default_SR"] == "" {
|
||||
return nil, errors.New("No default_SR specified for the pool.")
|
||||
}
|
||||
|
||||
sr = new(SR)
|
||||
sr.Ref = pool_rec["default_SR"].(string)
|
||||
sr.Client = client
|
||||
|
||||
return sr, nil
|
||||
}
|
||||
|
||||
func (client *XenAPIClient) GetVMByUuid(vm_uuid string) (vm *VM, err error) {
|
||||
vm = new(VM)
|
||||
result := APIResult{}
|
||||
err = client.APICall(&result, "VM.get_by_uuid", vm_uuid)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
vm.Ref = result.Value.(string)
|
||||
vm.Client = client
|
||||
return
|
||||
}
|
||||
|
||||
func (client *XenAPIClient) GetVMByNameLabel(name_label string) (vms []*VM, err error) {
|
||||
vms = make([]*VM, 0)
|
||||
result := APIResult{}
|
||||
err = client.APICall(&result, "VM.get_by_name_label", name_label)
|
||||
if err != nil {
|
||||
return vms, err
|
||||
}
|
||||
|
||||
for _, elem := range result.Value.([]interface{}) {
|
||||
vm := new(VM)
|
||||
vm.Ref = elem.(string)
|
||||
vm.Client = client
|
||||
vms = append(vms, vm)
|
||||
}
|
||||
|
||||
return vms, nil
|
||||
}
|
||||
|
||||
func (client *XenAPIClient) GetNetworkByUuid(network_uuid string) (network *Network, err error) {
|
||||
network = new(Network)
|
||||
result := APIResult{}
|
||||
err = client.APICall(&result, "network.get_by_uuid", network_uuid)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
network.Ref = result.Value.(string)
|
||||
network.Client = client
|
||||
return
|
||||
}
|
||||
|
||||
func (client *XenAPIClient) GetNetworkByNameLabel(name_label string) (networks []*Network, err error) {
|
||||
networks = make([]*Network, 0)
|
||||
result := APIResult{}
|
||||
err = client.APICall(&result, "network.get_by_name_label", name_label)
|
||||
if err != nil {
|
||||
return networks, err
|
||||
}
|
||||
|
||||
for _, elem := range result.Value.([]interface{}) {
|
||||
network := new(Network)
|
||||
network.Ref = elem.(string)
|
||||
network.Client = client
|
||||
networks = append(networks, network)
|
||||
}
|
||||
|
||||
return networks, nil
|
||||
}
|
||||
|
||||
func (client *XenAPIClient) GetVdiByNameLabel(name_label string) (vdis []*VDI, err error) {
|
||||
vdis = make([]*VDI, 0)
|
||||
result := APIResult{}
|
||||
err = client.APICall(&result, "VDI.get_by_name_label", name_label)
|
||||
if err != nil {
|
||||
return vdis, err
|
||||
}
|
||||
|
||||
for _, elem := range result.Value.([]interface{}) {
|
||||
vdi := new(VDI)
|
||||
vdi.Ref = elem.(string)
|
||||
vdi.Client = client
|
||||
vdis = append(vdis, vdi)
|
||||
}
|
||||
|
||||
return vdis, nil
|
||||
}
|
||||
|
||||
func (client *XenAPIClient) GetVdiByUuid(vdi_uuid string) (vdi *VDI, err error) {
|
||||
vdi = new(VDI)
|
||||
result := APIResult{}
|
||||
err = client.APICall(&result, "VDI.get_by_uuid", vdi_uuid)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
vdi.Ref = result.Value.(string)
|
||||
vdi.Client = client
|
||||
return
|
||||
}
|
||||
|
||||
func (client *XenAPIClient) GetPIFs() (pifs []*PIF, err error) {
|
||||
pifs = make([]*PIF, 0)
|
||||
result := APIResult{}
|
||||
err = client.APICall(&result, "PIF.get_all")
|
||||
if err != nil {
|
||||
return pifs, err
|
||||
}
|
||||
for _, elem := range result.Value.([]interface{}) {
|
||||
pif := new(PIF)
|
||||
pif.Ref = elem.(string)
|
||||
pif.Client = client
|
||||
pifs = append(pifs, pif)
|
||||
}
|
||||
|
||||
return pifs, nil
|
||||
}
|
||||
|
||||
// Host associated functions
|
||||
|
||||
func (self *Host) GetSoftwareVersion() (versions map[string]interface{}, err error) {
|
||||
versions = make(map[string]interface{})
|
||||
|
||||
result := APIResult{}
|
||||
err = self.Client.APICall(&result, "host.get_software_version", self.Ref)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
for k, v := range result.Value.(xmlrpc.Struct) {
|
||||
versions[k] = v.(string)
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
func (self *Host) CallPlugin(plugin, function string, args map[string]string) (res string, err error) {
|
||||
|
||||
args_rec := make(xmlrpc.Struct)
|
||||
for key, value := range args {
|
||||
args_rec[key] = value
|
||||
}
|
||||
|
||||
result := APIResult{}
|
||||
err = self.Client.APICall(&result, "host.call_plugin", self.Ref, plugin, function, args_rec)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
|
||||
// The plugin should return a string value
|
||||
res = result.Value.(string)
|
||||
return
|
||||
}
|
||||
|
||||
// VM associated functions
|
||||
|
||||
func (self *VM) Clone(label string) (new_instance *VM, err error) {
|
||||
new_instance = new(VM)
|
||||
|
||||
result := APIResult{}
|
||||
err = self.Client.APICall(&result, "VM.clone", self.Ref, label)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
new_instance.Ref = result.Value.(string)
|
||||
new_instance.Client = self.Client
|
||||
return
|
||||
}
|
||||
|
||||
func (self *VM) Destroy() (err error) {
|
||||
result := APIResult{}
|
||||
err = self.Client.APICall(&result, "VM.destroy", self.Ref)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
func (self *VM) Start(paused, force bool) (err error) {
|
||||
result := APIResult{}
|
||||
err = self.Client.APICall(&result, "VM.start", self.Ref, paused, force)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
func (self *VM) CleanShutdown() (err error) {
|
||||
result := APIResult{}
|
||||
err = self.Client.APICall(&result, "VM.clean_shutdown", self.Ref)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
func Unpause(c *Connection, vmRef xenapi.VMRef) (err error) {
|
||||
err = c.client.VM.Unpause(c.session, vmRef)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
func (self *VM) SetHVMBoot(policy, bootOrder string) (err error) {
|
||||
result := APIResult{}
|
||||
err = self.Client.APICall(&result, "VM.set_HVM_boot_policy", self.Ref, policy)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
result = APIResult{}
|
||||
params := make(xmlrpc.Struct)
|
||||
params["order"] = bootOrder
|
||||
err = self.Client.APICall(&result, "VM.set_HVM_boot_params", self.Ref, params)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
func (self *VM) SetPVBootloader(pv_bootloader, pv_args string) (err error) {
|
||||
result := APIResult{}
|
||||
err = self.Client.APICall(&result, "VM.set_PV_bootloader", self.Ref, pv_bootloader)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
result = APIResult{}
|
||||
err = self.Client.APICall(&result, "VM.set_PV_bootloader_args", self.Ref, pv_args)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
func (self *VM) GetDomainId() (domid string, err error) {
|
||||
result := APIResult{}
|
||||
err = self.Client.APICall(&result, "VM.get_domid", self.Ref)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
domid = result.Value.(string)
|
||||
return domid, nil
|
||||
}
|
||||
|
||||
func (self *VM) GetPowerState() (state string, err error) {
|
||||
result := APIResult{}
|
||||
err = self.Client.APICall(&result, "VM.get_power_state", self.Ref)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
state = result.Value.(string)
|
||||
return state, nil
|
||||
}
|
||||
|
||||
func (self *VM) GetUuid() (uuid string, err error) {
|
||||
result := APIResult{}
|
||||
err = self.Client.APICall(&result, "VM.get_uuid", self.Ref)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
uuid = result.Value.(string)
|
||||
return uuid, nil
|
||||
}
|
||||
|
||||
func (self *VM) GetVBDs() (vbds []VBD, err error) {
|
||||
vbds = make([]VBD, 0)
|
||||
result := APIResult{}
|
||||
err = self.Client.APICall(&result, "VM.get_VBDs", self.Ref)
|
||||
if err != nil {
|
||||
return vbds, err
|
||||
}
|
||||
for _, elem := range result.Value.([]interface{}) {
|
||||
vbd := VBD{}
|
||||
vbd.Ref = elem.(string)
|
||||
vbd.Client = self.Client
|
||||
vbds = append(vbds, vbd)
|
||||
}
|
||||
|
||||
return vbds, nil
|
||||
}
|
||||
|
||||
func GetDisks(c *Connection, vmRef xenapi.VMRef) (vdis []xenapi.VDIRef, err error) {
|
||||
// Return just data disks (non-isos)
|
||||
vdis = make([]xenapi.VDIRef, 0)
|
||||
vbds, err := c.client.VM.GetVBDs(c.session, vmRef)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
for _, vbd := range vbds {
|
||||
rec, err := c.client.VBD.GetRecord(c.session, vbd)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if rec.Type == "Disk" {
|
||||
|
||||
vdi, err := c.client.VBD.GetVDI(c.session, vbd)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
vdis = append(vdis, vdi)
|
||||
|
||||
}
|
||||
}
|
||||
return vdis, nil
|
||||
}
|
||||
|
||||
func (self *VM) GetGuestMetricsRef() (ref string, err error) {
|
||||
result := APIResult{}
|
||||
err = self.Client.APICall(&result, "VM.get_guest_metrics", self.Ref)
|
||||
if err != nil {
|
||||
return "", nil
|
||||
}
|
||||
ref = result.Value.(string)
|
||||
return ref, err
|
||||
}
|
||||
|
||||
func (self *VM) GetGuestMetrics() (metrics map[string]interface{}, err error) {
|
||||
metrics_ref, err := self.GetGuestMetricsRef()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if metrics_ref == "OpaqueRef:NULL" {
|
||||
return nil, nil
|
||||
}
|
||||
|
||||
result := APIResult{}
|
||||
err = self.Client.APICall(&result, "VM_guest_metrics.get_record", metrics_ref)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return result.Value.(xmlrpc.Struct), nil
|
||||
}
|
||||
|
||||
func (self *VM) SetStaticMemoryRange(min, max uint) (err error) {
|
||||
result := APIResult{}
|
||||
strMin := fmt.Sprintf("%d", min)
|
||||
strMax := fmt.Sprintf("%d", max)
|
||||
err = self.Client.APICall(&result, "VM.set_memory_limits", self.Ref, strMin, strMax, strMin, strMax)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
func ConnectVdi(c *Connection, vmRef xenapi.VMRef, vdiRef xenapi.VDIRef, vbdType xenapi.VbdType) (err error) {
|
||||
|
||||
var mode xenapi.VbdMode
|
||||
var unpluggable bool
|
||||
var bootable bool
|
||||
var t xenapi.VbdType
|
||||
switch vbdType {
|
||||
case xenapi.VbdTypeCD:
|
||||
mode = xenapi.VbdModeRO
|
||||
bootable = true
|
||||
unpluggable = false
|
||||
t = xenapi.VbdTypeCD
|
||||
case xenapi.VbdTypeDisk:
|
||||
mode = xenapi.VbdModeRW
|
||||
bootable = false
|
||||
unpluggable = false
|
||||
t = xenapi.VbdTypeDisk
|
||||
case xenapi.VbdTypeFloppy:
|
||||
mode = xenapi.VbdModeRW
|
||||
bootable = false
|
||||
unpluggable = true
|
||||
t = xenapi.VbdTypeFloppy
|
||||
}
|
||||
|
||||
vbd_ref, err := c.client.VBD.Create(c.session, xenapi.VBDRecord{
|
||||
VM: xenapi.VMRef(vmRef),
|
||||
VDI: xenapi.VDIRef(vdiRef),
|
||||
Userdevice: "autodetect",
|
||||
Empty: false,
|
||||
// OtherConfig: map[string]interface{{}},
|
||||
QosAlgorithmType: "",
|
||||
// QosAlgorithmParams: map[string]interface{{}},
|
||||
Mode: mode,
|
||||
Unpluggable: unpluggable,
|
||||
Bootable: bootable,
|
||||
Type: t,
|
||||
})
|
||||
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
fmt.Println("VBD Ref:", vbd_ref)
|
||||
|
||||
uuid, err := c.client.VBD.GetUUID(c.session, vbd_ref)
|
||||
|
||||
fmt.Println("VBD UUID: ", uuid)
|
||||
/*
|
||||
// 2. Plug VBD (Non need - the VM hasn't booted.
|
||||
// @todo - check VM state
|
||||
result = APIResult{}
|
||||
err = self.Client.APICall(&result, "VBD.plug", vbd_ref)
|
||||
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
*/
|
||||
return
|
||||
}
|
||||
|
||||
func DisconnectVdi(c *Connection, vmRef xenapi.VMRef, vdi xenapi.VDIRef) error {
|
||||
vbds, err := c.client.VM.GetVBDs(c.session, vmRef)
|
||||
if err != nil {
|
||||
return fmt.Errorf("Unable to get VM VBDs: %s", err.Error())
|
||||
}
|
||||
|
||||
for _, vbd := range vbds {
|
||||
rec, err := c.client.VBD.GetRecord(c.session, vbd)
|
||||
if err != nil {
|
||||
return fmt.Errorf("Could not get record for VBD '%s': %s", vbd, err.Error())
|
||||
}
|
||||
recVdi := rec.VDI
|
||||
if recVdi == vdi {
|
||||
_ = c.client.VBD.Unplug(c.session, vbd)
|
||||
err = c.client.VBD.Destroy(c.session, vbd)
|
||||
if err != nil {
|
||||
return fmt.Errorf("Could not destroy VBD '%s': %s", vbd, err.Error())
|
||||
}
|
||||
|
||||
return nil
|
||||
} else {
|
||||
log.Printf("Could not find VDI record in VBD '%s'", vbd)
|
||||
}
|
||||
}
|
||||
|
||||
return fmt.Errorf("Could not find VBD for VDI '%s'", vdi)
|
||||
}
|
||||
|
||||
func (self *VM) SetPlatform(params map[string]string) (err error) {
|
||||
result := APIResult{}
|
||||
platform_rec := make(xmlrpc.Struct)
|
||||
for key, value := range params {
|
||||
platform_rec[key] = value
|
||||
}
|
||||
|
||||
err = self.Client.APICall(&result, "VM.set_platform", self.Ref, platform_rec)
|
||||
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
func ConnectNetwork(c *Connection, networkRef xenapi.NetworkRef, vmRef xenapi.VMRef, device string) (*xenapi.VIFRef, error) {
|
||||
vif, err := c.client.VIF.Create(c.session, xenapi.VIFRecord{
|
||||
Network: networkRef,
|
||||
VM: vmRef,
|
||||
Device: device,
|
||||
LockingMode: xenapi.VifLockingModeNetworkDefault,
|
||||
})
|
||||
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
log.Printf("Created the following VIF: %s", vif)
|
||||
|
||||
return &vif, nil
|
||||
}
|
||||
|
||||
func AddVMTags(c *Connection, vmRef xenapi.VMRef, tags []string) error {
|
||||
for _, tag := range tags {
|
||||
log.Printf("Adding tag %s to VM %s\n", tag, vmRef)
|
||||
err := c.GetClient().VM.AddTags(c.session, vmRef, tag)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// Setters
|
||||
|
||||
func (self *VM) SetIsATemplate(is_a_template bool) (err error) {
|
||||
result := APIResult{}
|
||||
err = self.Client.APICall(&result, "VM.set_is_a_template", self.Ref, is_a_template)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
// SR associated functions
|
||||
|
||||
func (self *SR) CreateVdi(name_label string, size int64) (vdi *VDI, err error) {
|
||||
vdi = new(VDI)
|
||||
|
||||
vdi_rec := make(xmlrpc.Struct)
|
||||
vdi_rec["name_label"] = name_label
|
||||
vdi_rec["SR"] = self.Ref
|
||||
vdi_rec["virtual_size"] = fmt.Sprintf("%d", size)
|
||||
vdi_rec["type"] = "user"
|
||||
vdi_rec["sharable"] = false
|
||||
vdi_rec["read_only"] = false
|
||||
|
||||
oc := make(xmlrpc.Struct)
|
||||
oc["temp"] = "temp"
|
||||
vdi_rec["other_config"] = oc
|
||||
|
||||
result := APIResult{}
|
||||
err = self.Client.APICall(&result, "VDI.create", vdi_rec)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
vdi.Ref = result.Value.(string)
|
||||
vdi.Client = self.Client
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
// Network associated functions
|
||||
|
||||
func (self *Network) GetAssignedIPs() (ip_map map[string]string, err error) {
|
||||
ip_map = make(map[string]string, 0)
|
||||
result := APIResult{}
|
||||
err = self.Client.APICall(&result, "network.get_assigned_ips", self.Ref)
|
||||
if err != nil {
|
||||
return ip_map, err
|
||||
}
|
||||
for k, v := range result.Value.(xmlrpc.Struct) {
|
||||
ip_map[k] = v.(string)
|
||||
}
|
||||
return ip_map, nil
|
||||
}
|
||||
|
||||
// PIF associated functions
|
||||
|
||||
func (self *PIF) GetRecord() (record map[string]interface{}, err error) {
|
||||
record = make(map[string]interface{})
|
||||
result := APIResult{}
|
||||
err = self.Client.APICall(&result, "PIF.get_record", self.Ref)
|
||||
if err != nil {
|
||||
return record, err
|
||||
}
|
||||
for k, v := range result.Value.(xmlrpc.Struct) {
|
||||
record[k] = v
|
||||
}
|
||||
return record, nil
|
||||
}
|
||||
|
||||
// Pool associated functions
|
||||
|
||||
func (self *Pool) GetRecord() (record map[string]interface{}, err error) {
|
||||
record = make(map[string]interface{})
|
||||
result := APIResult{}
|
||||
err = self.Client.APICall(&result, "pool.get_record", self.Ref)
|
||||
if err != nil {
|
||||
return record, err
|
||||
}
|
||||
for k, v := range result.Value.(xmlrpc.Struct) {
|
||||
record[k] = v
|
||||
}
|
||||
return record, nil
|
||||
}
|
||||
|
||||
// VBD associated functions
|
||||
func (self *VBD) GetRecord() (record map[string]interface{}, err error) {
|
||||
record = make(map[string]interface{})
|
||||
result := APIResult{}
|
||||
err = self.Client.APICall(&result, "VBD.get_record", self.Ref)
|
||||
if err != nil {
|
||||
return record, err
|
||||
}
|
||||
for k, v := range result.Value.(xmlrpc.Struct) {
|
||||
record[k] = v
|
||||
}
|
||||
return record, nil
|
||||
}
|
||||
|
||||
func (self *VBD) GetVDI() (vdi *VDI, err error) {
|
||||
vbd_rec, err := self.GetRecord()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
vdi = new(VDI)
|
||||
vdi.Ref = vbd_rec["VDI"].(string)
|
||||
vdi.Client = self.Client
|
||||
|
||||
return vdi, nil
|
||||
}
|
||||
|
||||
func (self *VBD) Eject() (err error) {
|
||||
result := APIResult{}
|
||||
err = self.Client.APICall(&result, "VBD.eject", self.Ref)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (self *VBD) Unplug() (err error) {
|
||||
result := APIResult{}
|
||||
err = self.Client.APICall(&result, "VBD.unplug", self.Ref)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (self *VBD) Destroy() (err error) {
|
||||
result := APIResult{}
|
||||
err = self.Client.APICall(&result, "VBD.destroy", self.Ref)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// VIF associated functions
|
||||
|
||||
func (self *VIF) Destroy() (err error) {
|
||||
result := APIResult{}
|
||||
err = self.Client.APICall(&result, "VIF.destroy", self.Ref)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// VDI associated functions
|
||||
|
||||
func (self *VDI) GetUuid() (vdi_uuid string, err error) {
|
||||
result := APIResult{}
|
||||
err = self.Client.APICall(&result, "VDI.get_uuid", self.Ref)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
vdi_uuid = result.Value.(string)
|
||||
return vdi_uuid, nil
|
||||
}
|
||||
|
||||
func (self *VDI) GetVBDs() (vbds []VBD, err error) {
|
||||
vbds = make([]VBD, 0)
|
||||
result := APIResult{}
|
||||
err = self.Client.APICall(&result, "VDI.get_VBDs", self.Ref)
|
||||
if err != nil {
|
||||
return vbds, err
|
||||
}
|
||||
for _, elem := range result.Value.([]interface{}) {
|
||||
vbd := VBD{}
|
||||
vbd.Ref = elem.(string)
|
||||
vbd.Client = self.Client
|
||||
vbds = append(vbds, vbd)
|
||||
}
|
||||
|
||||
return vbds, nil
|
||||
}
|
||||
|
||||
func (self *VDI) Destroy() (err error) {
|
||||
result := APIResult{}
|
||||
err = self.Client.APICall(&result, "VDI.destroy", self.Ref)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
// Expose a VDI using the Transfer VM
|
||||
// (Legacy VHD export)
|
||||
|
||||
type TransferRecord struct {
|
||||
UrlFull string `xml:"url_full,attr"`
|
||||
}
|
||||
|
||||
func Expose(c *Connection, vdiRef xenapi.VDIRef, format string) (url string, err error) {
|
||||
|
||||
hosts, err := c.client.Host.GetAll(c.session)
|
||||
|
||||
if err != nil {
|
||||
err = errors.New(fmt.Sprintf("Could not retrieve hosts in the pool: %s", err.Error()))
|
||||
return "", err
|
||||
}
|
||||
host := hosts[0]
|
||||
|
||||
if err != nil {
|
||||
err = errors.New(fmt.Sprintf("Failed to get VDI uuid for %s: %s", vdiRef, err.Error()))
|
||||
return "", err
|
||||
}
|
||||
|
||||
args := make(map[string]string)
|
||||
args["transfer_mode"] = "http"
|
||||
args["vdi_uuid"] = string(vdiRef)
|
||||
args["expose_vhd"] = "true"
|
||||
args["network_uuid"] = "management"
|
||||
args["timeout_minutes"] = "5"
|
||||
|
||||
handle, err := c.client.Host.CallPlugin(c.session, host, "transfer", "expose", args)
|
||||
|
||||
if err != nil {
|
||||
err = errors.New(fmt.Sprintf("Error whilst exposing VDI %s: %s", vdiRef, err.Error()))
|
||||
return "", err
|
||||
}
|
||||
|
||||
args = make(map[string]string)
|
||||
args["record_handle"] = handle
|
||||
record_xml, err := c.client.Host.CallPlugin(c.session, host, "transfer", "get_record", args)
|
||||
|
||||
if err != nil {
|
||||
err = errors.New(fmt.Sprintf("Unable to retrieve transfer record for VDI %s: %s", vdiRef, err.Error()))
|
||||
return "", err
|
||||
}
|
||||
|
||||
var record TransferRecord
|
||||
xml.Unmarshal([]byte(record_xml), &record)
|
||||
|
||||
if record.UrlFull == "" {
|
||||
return "", errors.New(fmt.Sprintf("Error: did not parse XML properly: '%s'", record_xml))
|
||||
}
|
||||
|
||||
// Handles either raw or VHD formats
|
||||
|
||||
switch format {
|
||||
case "vhd":
|
||||
url = fmt.Sprintf("%s.vhd", record.UrlFull)
|
||||
|
||||
case "raw":
|
||||
url = record.UrlFull
|
||||
}
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
func Unexpose(c *Connection, vdiRef xenapi.VDIRef) (err error) {
|
||||
|
||||
disk_uuid, err := c.client.VDI.GetUUID(c.session, vdiRef)
|
||||
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
hosts, err := c.client.Host.GetAll(c.session)
|
||||
|
||||
if err != nil {
|
||||
err = errors.New(fmt.Sprintf("Could not retrieve hosts in the pool: %s", err.Error()))
|
||||
return err
|
||||
}
|
||||
|
||||
host := hosts[0]
|
||||
|
||||
args := make(map[string]string)
|
||||
args["vdi_uuid"] = disk_uuid
|
||||
|
||||
result, err := c.client.Host.CallPlugin(c.session, host, "transfer", "unexpose", args)
|
||||
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
log.Println(fmt.Sprintf("Unexpose result: %s", result))
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// Task associated functions
|
||||
|
||||
// func (self *Task) GetResult() (object *XenAPIObject, err error) {
|
||||
// result := APIResult{}
|
||||
// err = self.Client.APICall(&result, "task.get_result", self.Ref)
|
||||
// if err != nil {
|
||||
// return
|
||||
// }
|
||||
// switch ref := result.Value.(type) {
|
||||
// case string:
|
||||
// // @fixme: xapi currently sends us an xmlrpc-encoded string via xmlrpc.
|
||||
// // This seems to be a bug in xapi. Remove this workaround when it's fixed
|
||||
// re := regexp.MustCompile("^<value><array><data><value>([^<]*)</value>.*</data></array></value>$")
|
||||
// match := re.FindStringSubmatch(ref)
|
||||
// if match == nil {
|
||||
// object = nil
|
||||
// } else {
|
||||
// object = &XenAPIObject{
|
||||
// Ref: match[1],
|
||||
// Client: self.Client,
|
||||
// }
|
||||
// }
|
||||
// case nil:
|
||||
// object = nil
|
||||
// default:
|
||||
// err = fmt.Errorf("task.get_result: unknown value type %T (expected string or nil)", ref)
|
||||
// }
|
||||
// return
|
||||
// }
|
||||
|
||||
// Client Initiator
|
||||
type Connection struct {
|
||||
client *xenapi.Client
|
||||
session xenapi.SessionRef
|
||||
Host string
|
||||
Username string
|
||||
Password string
|
||||
}
|
||||
|
||||
func (c Connection) GetSession() string {
|
||||
return string(c.session)
|
||||
}
|
||||
|
||||
func NewXenAPIClient(host, username, password string) (*Connection, error) {
|
||||
client, err := xenapi.NewClient("https://"+host, nil)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
session, err := client.Session.LoginWithPassword(username, password, "1.0", "packer")
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return &Connection{client, session, host, username, password}, nil
|
||||
}
|
||||
|
||||
func (c *Connection) GetClient() *xenapi.Client {
|
||||
return c.client
|
||||
}
|
||||
|
||||
func (c *Connection) GetSessionRef() xenapi.SessionRef {
|
||||
return c.session
|
||||
}
|
304
builder/xenserver/common/common_config.go
Normal file
304
builder/xenserver/common/common_config.go
Normal file
@ -0,0 +1,304 @@
|
||||
package common
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"fmt"
|
||||
"os"
|
||||
"time"
|
||||
|
||||
"github.com/hashicorp/packer-plugin-sdk/common"
|
||||
"github.com/hashicorp/packer-plugin-sdk/multistep"
|
||||
"github.com/hashicorp/packer-plugin-sdk/template/interpolate"
|
||||
xenapi "github.com/terra-farm/go-xen-api-client"
|
||||
)
|
||||
|
||||
type CommonConfig struct {
|
||||
Username string `mapstructure:"remote_username"`
|
||||
Password string `mapstructure:"remote_password"`
|
||||
HostIp string `mapstructure:"remote_host"`
|
||||
HostSshPort uint `mapstructure:"remote_ssh_port"`
|
||||
|
||||
VMName string `mapstructure:"vm_name"`
|
||||
VMDescription string `mapstructure:"vm_description"`
|
||||
SrName string `mapstructure:"sr_name"`
|
||||
SrISOName string `mapstructure:"sr_iso_name" required:"false"`
|
||||
FloppyFiles []string `mapstructure:"floppy_files"`
|
||||
NetworkNames []string `mapstructure:"network_names"`
|
||||
ExportNetworkNames []string `mapstructure:"export_network_names"`
|
||||
VMTags []string `mapstructure:"vm_tags"`
|
||||
|
||||
HostPortMin uint `mapstructure:"host_port_min"`
|
||||
HostPortMax uint `mapstructure:"host_port_max"`
|
||||
|
||||
BootCommand []string `mapstructure:"boot_command"`
|
||||
ShutdownCommand string `mapstructure:"shutdown_command"`
|
||||
|
||||
RawBootWait string `mapstructure:"boot_wait"`
|
||||
BootWait time.Duration
|
||||
|
||||
ToolsIsoName string `mapstructure:"tools_iso_name"`
|
||||
|
||||
HTTPDir string `mapstructure:"http_directory"`
|
||||
HTTPPortMin uint `mapstructure:"http_port_min"`
|
||||
HTTPPortMax uint `mapstructure:"http_port_max"`
|
||||
|
||||
// SSHHostPortMin uint `mapstructure:"ssh_host_port_min"`
|
||||
// SSHHostPortMax uint `mapstructure:"ssh_host_port_max"`
|
||||
SSHKeyPath string `mapstructure:"ssh_key_path"`
|
||||
SSHPassword string `mapstructure:"ssh_password"`
|
||||
SSHPort uint `mapstructure:"ssh_port"`
|
||||
SSHUser string `mapstructure:"ssh_username"`
|
||||
SSHConfig `mapstructure:",squash"`
|
||||
|
||||
RawSSHWaitTimeout string `mapstructure:"ssh_wait_timeout"`
|
||||
SSHWaitTimeout time.Duration
|
||||
|
||||
OutputDir string `mapstructure:"output_directory"`
|
||||
Format string `mapstructure:"format"`
|
||||
KeepVM string `mapstructure:"keep_vm"`
|
||||
IPGetter string `mapstructure:"ip_getter"`
|
||||
}
|
||||
|
||||
func (c *CommonConfig) Prepare(ctx *interpolate.Context, pc *common.PackerConfig) []error {
|
||||
var err error
|
||||
var errs []error
|
||||
|
||||
// Set default values
|
||||
|
||||
if c.HostSshPort == 0 {
|
||||
c.HostSshPort = 22
|
||||
}
|
||||
|
||||
if c.HostPortMin == 0 {
|
||||
c.HostPortMin = 5900
|
||||
}
|
||||
|
||||
if c.HostPortMax == 0 {
|
||||
c.HostPortMax = 6000
|
||||
}
|
||||
|
||||
if c.RawBootWait == "" {
|
||||
c.RawBootWait = "5s"
|
||||
}
|
||||
|
||||
if c.HTTPPortMin == 0 {
|
||||
c.HTTPPortMin = 8000
|
||||
}
|
||||
|
||||
if c.HTTPPortMax == 0 {
|
||||
c.HTTPPortMax = 9000
|
||||
}
|
||||
|
||||
if c.RawSSHWaitTimeout == "" {
|
||||
c.RawSSHWaitTimeout = "200m"
|
||||
}
|
||||
|
||||
if c.FloppyFiles == nil {
|
||||
c.FloppyFiles = make([]string, 0)
|
||||
}
|
||||
|
||||
/*
|
||||
if c.SSHHostPortMin == 0 {
|
||||
c.SSHHostPortMin = 2222
|
||||
}
|
||||
|
||||
if c.SSHHostPortMax == 0 {
|
||||
c.SSHHostPortMax = 4444
|
||||
}
|
||||
*/
|
||||
|
||||
if c.SSHPort == 0 {
|
||||
c.SSHPort = 22
|
||||
}
|
||||
|
||||
if c.RawSSHWaitTimeout == "" {
|
||||
c.RawSSHWaitTimeout = "20m"
|
||||
}
|
||||
|
||||
if c.OutputDir == "" {
|
||||
c.OutputDir = fmt.Sprintf("output-%s", pc.PackerBuildName)
|
||||
}
|
||||
|
||||
if c.VMName == "" {
|
||||
c.VMName = fmt.Sprintf("packer-%s-{{timestamp}}", pc.PackerBuildName)
|
||||
}
|
||||
|
||||
if c.Format == "" {
|
||||
c.Format = "xva"
|
||||
}
|
||||
|
||||
if c.KeepVM == "" {
|
||||
c.KeepVM = "never"
|
||||
}
|
||||
|
||||
if c.IPGetter == "" {
|
||||
c.IPGetter = "auto"
|
||||
}
|
||||
|
||||
// Validation
|
||||
|
||||
if c.Username == "" {
|
||||
errs = append(errs, errors.New("remote_username must be specified."))
|
||||
}
|
||||
|
||||
if c.Password == "" {
|
||||
errs = append(errs, errors.New("remote_password must be specified."))
|
||||
}
|
||||
|
||||
if c.HostIp == "" {
|
||||
errs = append(errs, errors.New("remote_host must be specified."))
|
||||
}
|
||||
|
||||
if c.HostPortMin > c.HostPortMax {
|
||||
errs = append(errs, errors.New("the host min port must be less than the max"))
|
||||
}
|
||||
|
||||
if c.HTTPPortMin > c.HTTPPortMax {
|
||||
errs = append(errs, errors.New("the HTTP min port must be less than the max"))
|
||||
}
|
||||
|
||||
c.BootWait, err = time.ParseDuration(c.RawBootWait)
|
||||
if err != nil {
|
||||
errs = append(errs, fmt.Errorf("Failed to parse boot_wait: %s", err))
|
||||
}
|
||||
|
||||
if c.SSHKeyPath != "" {
|
||||
if _, err := os.Stat(c.SSHKeyPath); err != nil {
|
||||
errs = append(errs, fmt.Errorf("ssh_key_path is invalid: %s", err))
|
||||
} else if _, err := FileSigner(c.SSHKeyPath); err != nil {
|
||||
errs = append(errs, fmt.Errorf("ssh_key_path is invalid: %s", err))
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
if c.SSHHostPortMin > c.SSHHostPortMax {
|
||||
errs = append(errs,
|
||||
errors.New("ssh_host_port_min must be less than ssh_host_port_max"))
|
||||
}
|
||||
*/
|
||||
|
||||
if c.SSHUser == "" {
|
||||
errs = append(errs, errors.New("An ssh_username must be specified."))
|
||||
}
|
||||
|
||||
c.SSHWaitTimeout, err = time.ParseDuration(c.RawSSHWaitTimeout)
|
||||
if err != nil {
|
||||
errs = append(errs, fmt.Errorf("Failed to parse ssh_wait_timeout: %s", err))
|
||||
}
|
||||
|
||||
switch c.Format {
|
||||
case "xva", "xva_compressed", "vdi_raw", "vdi_vhd", "none":
|
||||
default:
|
||||
errs = append(errs, errors.New("format must be one of 'xva', 'vdi_raw', 'vdi_vhd', 'none'"))
|
||||
}
|
||||
|
||||
switch c.KeepVM {
|
||||
case "always", "never", "on_success":
|
||||
default:
|
||||
errs = append(errs, errors.New("keep_vm must be one of 'always', 'never', 'on_success'"))
|
||||
}
|
||||
|
||||
switch c.IPGetter {
|
||||
case "auto", "tools", "http":
|
||||
default:
|
||||
errs = append(errs, errors.New("ip_getter must be one of 'auto', 'tools', 'http'"))
|
||||
}
|
||||
|
||||
return errs
|
||||
}
|
||||
|
||||
// steps should check config.ShouldKeepVM first before cleaning up the VM
|
||||
func (c CommonConfig) ShouldKeepVM(state multistep.StateBag) bool {
|
||||
switch c.KeepVM {
|
||||
case "always":
|
||||
return true
|
||||
case "never":
|
||||
return false
|
||||
case "on_success":
|
||||
// only keep instance if build was successful
|
||||
_, cancelled := state.GetOk(multistep.StateCancelled)
|
||||
_, halted := state.GetOk(multistep.StateHalted)
|
||||
return !(cancelled || halted)
|
||||
default:
|
||||
panic(fmt.Sprintf("Unknown keep_vm value '%s'", c.KeepVM))
|
||||
}
|
||||
}
|
||||
|
||||
func (config CommonConfig) GetSR(c *Connection) (xenapi.SRRef, error) {
|
||||
if config.SrName == "" {
|
||||
return getDefaultSR(c)
|
||||
} else {
|
||||
var srRef xenapi.SRRef
|
||||
|
||||
// Use the provided name label to find the SR to use
|
||||
srs, err := c.GetClient().SR.GetByNameLabel(c.session, config.SrName)
|
||||
|
||||
if err != nil {
|
||||
return srRef, err
|
||||
}
|
||||
|
||||
switch {
|
||||
case len(srs) == 0:
|
||||
return srRef, fmt.Errorf("Couldn't find a SR with the specified name-label '%s'", config.SrName)
|
||||
case len(srs) > 1:
|
||||
return srRef, fmt.Errorf("Found more than one SR with the name '%s'. The name must be unique", config.SrName)
|
||||
}
|
||||
|
||||
return srs[0], nil
|
||||
}
|
||||
}
|
||||
|
||||
func (config CommonConfig) GetISOSR(c *Connection) (xenapi.SRRef, error) {
|
||||
var srRef xenapi.SRRef
|
||||
if config.SrISOName == "" {
|
||||
return getDefaultSR(c)
|
||||
|
||||
} else {
|
||||
// Use the provided name label to find the SR to use
|
||||
srs, err := c.GetClient().SR.GetByNameLabel(c.session, config.SrISOName)
|
||||
|
||||
if err != nil {
|
||||
return srRef, err
|
||||
}
|
||||
|
||||
switch {
|
||||
case len(srs) == 0:
|
||||
return srRef, fmt.Errorf("Couldn't find a SR with the specified name-label '%s'", config.SrISOName)
|
||||
case len(srs) > 1:
|
||||
return srRef, fmt.Errorf("Found more than one SR with the name '%s'. The name must be unique", config.SrISOName)
|
||||
}
|
||||
|
||||
return srs[0], nil
|
||||
}
|
||||
}
|
||||
|
||||
func getDefaultSR(c *Connection) (xenapi.SRRef, error) {
|
||||
var srRef xenapi.SRRef
|
||||
client := c.GetClient()
|
||||
hostRef, err := client.Session.GetThisHost(c.session, c.session)
|
||||
|
||||
if err != nil {
|
||||
return srRef, err
|
||||
}
|
||||
|
||||
// The current version of the go-xen-api-client does not fully support XenAPI version 8.2
|
||||
// In particular, some values for the pool `allowed_operations` are not recognised, resulting
|
||||
// in a parse error when retrieving pool records. As a workaround, we only fetch pool refs.
|
||||
pool_refs, err := client.Pool.GetAll(c.session)
|
||||
|
||||
if err != nil {
|
||||
return srRef, err
|
||||
}
|
||||
|
||||
for _, pool_ref := range pool_refs {
|
||||
pool_master, err := client.Pool.GetMaster(c.session, pool_ref)
|
||||
if err != nil {
|
||||
return srRef, err
|
||||
}
|
||||
if pool_master == hostRef {
|
||||
return client.Pool.GetDefaultSR(c.session, pool_ref)
|
||||
}
|
||||
}
|
||||
|
||||
return srRef, errors.New(fmt.Sprintf("failed to find default SR on host '%s'", hostRef))
|
||||
}
|
45
builder/xenserver/common/config.go
Normal file
45
builder/xenserver/common/config.go
Normal file
@ -0,0 +1,45 @@
|
||||
//go:generate packer-sdc mapstructure-to-hcl2 -type Config
|
||||
package common
|
||||
|
||||
import (
|
||||
"time"
|
||||
|
||||
"github.com/hashicorp/packer-plugin-sdk/common"
|
||||
"github.com/hashicorp/packer-plugin-sdk/communicator"
|
||||
"github.com/hashicorp/packer-plugin-sdk/template/interpolate"
|
||||
)
|
||||
|
||||
type Config struct {
|
||||
common.PackerConfig `mapstructure:",squash"`
|
||||
CommonConfig `mapstructure:",squash"`
|
||||
Comm communicator.Config `mapstructure:",squash"`
|
||||
|
||||
VCPUsMax uint `mapstructure:"vcpus_max"`
|
||||
VCPUsAtStartup uint `mapstructure:"vcpus_atstartup"`
|
||||
VMMemory uint `mapstructure:"vm_memory"`
|
||||
DiskName string `mapstructure:"disk_name"`
|
||||
DiskSize uint `mapstructure:"disk_size"`
|
||||
CloneTemplate string `mapstructure:"clone_template"`
|
||||
VMOtherConfig map[string]string `mapstructure:"vm_other_config"`
|
||||
VMTags []string `mapstructure:"vm_tags"`
|
||||
|
||||
ISOChecksum string `mapstructure:"iso_checksum"`
|
||||
ISOUrls []string `mapstructure:"iso_urls"`
|
||||
ISOUrl string `mapstructure:"iso_url"`
|
||||
ISOName string `mapstructure:"iso_name"`
|
||||
|
||||
PlatformArgs map[string]string `mapstructure:"platform_args"`
|
||||
|
||||
RawInstallTimeout string `mapstructure:"install_timeout"`
|
||||
InstallTimeout time.Duration ``
|
||||
SourcePath string `mapstructure:"source_path"`
|
||||
|
||||
Firmware string `mapstructure:"firmware"`
|
||||
SkipSetTemplate bool `mapstructure:"skip_set_template"`
|
||||
|
||||
ctx interpolate.Context
|
||||
}
|
||||
|
||||
func (c Config) GetInterpContext() *interpolate.Context {
|
||||
return &c.ctx
|
||||
}
|
233
builder/xenserver/common/config.hcl2spec.go
Normal file
233
builder/xenserver/common/config.hcl2spec.go
Normal file
@ -0,0 +1,233 @@
|
||||
// Code generated by "packer-sdc mapstructure-to-hcl2"; DO NOT EDIT.
|
||||
|
||||
package common
|
||||
|
||||
import (
|
||||
"github.com/hashicorp/hcl/v2/hcldec"
|
||||
"github.com/zclconf/go-cty/cty"
|
||||
)
|
||||
|
||||
// FlatConfig is an auto-generated flat version of Config.
|
||||
// Where the contents of a field with a `mapstructure:,squash` tag are bubbled up.
|
||||
type FlatConfig struct {
|
||||
PackerBuildName *string `mapstructure:"packer_build_name" cty:"packer_build_name" hcl:"packer_build_name"`
|
||||
PackerBuilderType *string `mapstructure:"packer_builder_type" cty:"packer_builder_type" hcl:"packer_builder_type"`
|
||||
PackerCoreVersion *string `mapstructure:"packer_core_version" cty:"packer_core_version" hcl:"packer_core_version"`
|
||||
PackerDebug *bool `mapstructure:"packer_debug" cty:"packer_debug" hcl:"packer_debug"`
|
||||
PackerForce *bool `mapstructure:"packer_force" cty:"packer_force" hcl:"packer_force"`
|
||||
PackerOnError *string `mapstructure:"packer_on_error" cty:"packer_on_error" hcl:"packer_on_error"`
|
||||
PackerUserVars map[string]string `mapstructure:"packer_user_variables" cty:"packer_user_variables" hcl:"packer_user_variables"`
|
||||
PackerSensitiveVars []string `mapstructure:"packer_sensitive_variables" cty:"packer_sensitive_variables" hcl:"packer_sensitive_variables"`
|
||||
Username *string `mapstructure:"remote_username" cty:"remote_username" hcl:"remote_username"`
|
||||
Password *string `mapstructure:"remote_password" cty:"remote_password" hcl:"remote_password"`
|
||||
HostIp *string `mapstructure:"remote_host" cty:"remote_host" hcl:"remote_host"`
|
||||
HostSshPort *uint `mapstructure:"remote_ssh_port" cty:"remote_ssh_port" hcl:"remote_ssh_port"`
|
||||
VMName *string `mapstructure:"vm_name" cty:"vm_name" hcl:"vm_name"`
|
||||
VMDescription *string `mapstructure:"vm_description" cty:"vm_description" hcl:"vm_description"`
|
||||
SrName *string `mapstructure:"sr_name" cty:"sr_name" hcl:"sr_name"`
|
||||
SrISOName *string `mapstructure:"sr_iso_name" required:"false" cty:"sr_iso_name" hcl:"sr_iso_name"`
|
||||
FloppyFiles []string `mapstructure:"floppy_files" cty:"floppy_files" hcl:"floppy_files"`
|
||||
NetworkNames []string `mapstructure:"network_names" cty:"network_names" hcl:"network_names"`
|
||||
ExportNetworkNames []string `mapstructure:"export_network_names" cty:"export_network_names" hcl:"export_network_names"`
|
||||
VMTags []string `mapstructure:"vm_tags" cty:"vm_tags" hcl:"vm_tags"`
|
||||
HostPortMin *uint `mapstructure:"host_port_min" cty:"host_port_min" hcl:"host_port_min"`
|
||||
HostPortMax *uint `mapstructure:"host_port_max" cty:"host_port_max" hcl:"host_port_max"`
|
||||
BootCommand []string `mapstructure:"boot_command" cty:"boot_command" hcl:"boot_command"`
|
||||
ShutdownCommand *string `mapstructure:"shutdown_command" cty:"shutdown_command" hcl:"shutdown_command"`
|
||||
RawBootWait *string `mapstructure:"boot_wait" cty:"boot_wait" hcl:"boot_wait"`
|
||||
ToolsIsoName *string `mapstructure:"tools_iso_name" cty:"tools_iso_name" hcl:"tools_iso_name"`
|
||||
HTTPDir *string `mapstructure:"http_directory" cty:"http_directory" hcl:"http_directory"`
|
||||
HTTPPortMin *uint `mapstructure:"http_port_min" cty:"http_port_min" hcl:"http_port_min"`
|
||||
HTTPPortMax *uint `mapstructure:"http_port_max" cty:"http_port_max" hcl:"http_port_max"`
|
||||
SSHKeyPath *string `mapstructure:"ssh_key_path" cty:"ssh_key_path" hcl:"ssh_key_path"`
|
||||
SSHPassword *string `mapstructure:"ssh_password" cty:"ssh_password" hcl:"ssh_password"`
|
||||
SSHPort *uint `mapstructure:"ssh_port" cty:"ssh_port" hcl:"ssh_port"`
|
||||
SSHUser *string `mapstructure:"ssh_username" cty:"ssh_username" hcl:"ssh_username"`
|
||||
Type *string `mapstructure:"communicator" cty:"communicator" hcl:"communicator"`
|
||||
PauseBeforeConnect *string `mapstructure:"pause_before_connecting" cty:"pause_before_connecting" hcl:"pause_before_connecting"`
|
||||
SSHHost *string `mapstructure:"ssh_host" cty:"ssh_host" hcl:"ssh_host"`
|
||||
SSHKeyPairName *string `mapstructure:"ssh_keypair_name" undocumented:"true" cty:"ssh_keypair_name" hcl:"ssh_keypair_name"`
|
||||
SSHTemporaryKeyPairName *string `mapstructure:"temporary_key_pair_name" undocumented:"true" cty:"temporary_key_pair_name" hcl:"temporary_key_pair_name"`
|
||||
SSHTemporaryKeyPairType *string `mapstructure:"temporary_key_pair_type" cty:"temporary_key_pair_type" hcl:"temporary_key_pair_type"`
|
||||
SSHTemporaryKeyPairBits *int `mapstructure:"temporary_key_pair_bits" cty:"temporary_key_pair_bits" hcl:"temporary_key_pair_bits"`
|
||||
SSHCiphers []string `mapstructure:"ssh_ciphers" cty:"ssh_ciphers" hcl:"ssh_ciphers"`
|
||||
SSHClearAuthorizedKeys *bool `mapstructure:"ssh_clear_authorized_keys" cty:"ssh_clear_authorized_keys" hcl:"ssh_clear_authorized_keys"`
|
||||
SSHKEXAlgos []string `mapstructure:"ssh_key_exchange_algorithms" cty:"ssh_key_exchange_algorithms" hcl:"ssh_key_exchange_algorithms"`
|
||||
SSHPrivateKeyFile *string `mapstructure:"ssh_private_key_file" undocumented:"true" cty:"ssh_private_key_file" hcl:"ssh_private_key_file"`
|
||||
SSHCertificateFile *string `mapstructure:"ssh_certificate_file" cty:"ssh_certificate_file" hcl:"ssh_certificate_file"`
|
||||
SSHPty *bool `mapstructure:"ssh_pty" cty:"ssh_pty" hcl:"ssh_pty"`
|
||||
SSHTimeout *string `mapstructure:"ssh_timeout" cty:"ssh_timeout" hcl:"ssh_timeout"`
|
||||
SSHWaitTimeout *string `mapstructure:"ssh_wait_timeout" undocumented:"true" cty:"ssh_wait_timeout" hcl:"ssh_wait_timeout"`
|
||||
SSHAgentAuth *bool `mapstructure:"ssh_agent_auth" undocumented:"true" cty:"ssh_agent_auth" hcl:"ssh_agent_auth"`
|
||||
SSHDisableAgentForwarding *bool `mapstructure:"ssh_disable_agent_forwarding" cty:"ssh_disable_agent_forwarding" hcl:"ssh_disable_agent_forwarding"`
|
||||
SSHHandshakeAttempts *int `mapstructure:"ssh_handshake_attempts" cty:"ssh_handshake_attempts" hcl:"ssh_handshake_attempts"`
|
||||
SSHBastionHost *string `mapstructure:"ssh_bastion_host" cty:"ssh_bastion_host" hcl:"ssh_bastion_host"`
|
||||
SSHBastionPort *int `mapstructure:"ssh_bastion_port" cty:"ssh_bastion_port" hcl:"ssh_bastion_port"`
|
||||
SSHBastionAgentAuth *bool `mapstructure:"ssh_bastion_agent_auth" cty:"ssh_bastion_agent_auth" hcl:"ssh_bastion_agent_auth"`
|
||||
SSHBastionUsername *string `mapstructure:"ssh_bastion_username" cty:"ssh_bastion_username" hcl:"ssh_bastion_username"`
|
||||
SSHBastionPassword *string `mapstructure:"ssh_bastion_password" cty:"ssh_bastion_password" hcl:"ssh_bastion_password"`
|
||||
SSHBastionInteractive *bool `mapstructure:"ssh_bastion_interactive" cty:"ssh_bastion_interactive" hcl:"ssh_bastion_interactive"`
|
||||
SSHBastionPrivateKeyFile *string `mapstructure:"ssh_bastion_private_key_file" cty:"ssh_bastion_private_key_file" hcl:"ssh_bastion_private_key_file"`
|
||||
SSHBastionCertificateFile *string `mapstructure:"ssh_bastion_certificate_file" cty:"ssh_bastion_certificate_file" hcl:"ssh_bastion_certificate_file"`
|
||||
SSHFileTransferMethod *string `mapstructure:"ssh_file_transfer_method" cty:"ssh_file_transfer_method" hcl:"ssh_file_transfer_method"`
|
||||
SSHProxyHost *string `mapstructure:"ssh_proxy_host" cty:"ssh_proxy_host" hcl:"ssh_proxy_host"`
|
||||
SSHProxyPort *int `mapstructure:"ssh_proxy_port" cty:"ssh_proxy_port" hcl:"ssh_proxy_port"`
|
||||
SSHProxyUsername *string `mapstructure:"ssh_proxy_username" cty:"ssh_proxy_username" hcl:"ssh_proxy_username"`
|
||||
SSHProxyPassword *string `mapstructure:"ssh_proxy_password" cty:"ssh_proxy_password" hcl:"ssh_proxy_password"`
|
||||
SSHKeepAliveInterval *string `mapstructure:"ssh_keep_alive_interval" cty:"ssh_keep_alive_interval" hcl:"ssh_keep_alive_interval"`
|
||||
SSHReadWriteTimeout *string `mapstructure:"ssh_read_write_timeout" cty:"ssh_read_write_timeout" hcl:"ssh_read_write_timeout"`
|
||||
SSHRemoteTunnels []string `mapstructure:"ssh_remote_tunnels" cty:"ssh_remote_tunnels" hcl:"ssh_remote_tunnels"`
|
||||
SSHLocalTunnels []string `mapstructure:"ssh_local_tunnels" cty:"ssh_local_tunnels" hcl:"ssh_local_tunnels"`
|
||||
SSHPublicKey []byte `mapstructure:"ssh_public_key" undocumented:"true" cty:"ssh_public_key" hcl:"ssh_public_key"`
|
||||
SSHPrivateKey []byte `mapstructure:"ssh_private_key" undocumented:"true" cty:"ssh_private_key" hcl:"ssh_private_key"`
|
||||
WinRMUser *string `mapstructure:"winrm_username" cty:"winrm_username" hcl:"winrm_username"`
|
||||
WinRMPassword *string `mapstructure:"winrm_password" cty:"winrm_password" hcl:"winrm_password"`
|
||||
WinRMHost *string `mapstructure:"winrm_host" cty:"winrm_host" hcl:"winrm_host"`
|
||||
WinRMNoProxy *bool `mapstructure:"winrm_no_proxy" cty:"winrm_no_proxy" hcl:"winrm_no_proxy"`
|
||||
WinRMPort *int `mapstructure:"winrm_port" cty:"winrm_port" hcl:"winrm_port"`
|
||||
WinRMTimeout *string `mapstructure:"winrm_timeout" cty:"winrm_timeout" hcl:"winrm_timeout"`
|
||||
WinRMUseSSL *bool `mapstructure:"winrm_use_ssl" cty:"winrm_use_ssl" hcl:"winrm_use_ssl"`
|
||||
WinRMInsecure *bool `mapstructure:"winrm_insecure" cty:"winrm_insecure" hcl:"winrm_insecure"`
|
||||
WinRMUseNTLM *bool `mapstructure:"winrm_use_ntlm" cty:"winrm_use_ntlm" hcl:"winrm_use_ntlm"`
|
||||
SSHHostPortMin *uint `mapstructure:"ssh_host_port_min" cty:"ssh_host_port_min" hcl:"ssh_host_port_min"`
|
||||
SSHHostPortMax *uint `mapstructure:"ssh_host_port_max" cty:"ssh_host_port_max" hcl:"ssh_host_port_max"`
|
||||
SSHSkipNatMapping *bool `mapstructure:"ssh_skip_nat_mapping" cty:"ssh_skip_nat_mapping" hcl:"ssh_skip_nat_mapping"`
|
||||
OutputDir *string `mapstructure:"output_directory" cty:"output_directory" hcl:"output_directory"`
|
||||
Format *string `mapstructure:"format" cty:"format" hcl:"format"`
|
||||
KeepVM *string `mapstructure:"keep_vm" cty:"keep_vm" hcl:"keep_vm"`
|
||||
IPGetter *string `mapstructure:"ip_getter" cty:"ip_getter" hcl:"ip_getter"`
|
||||
VCPUsMax *uint `mapstructure:"vcpus_max" cty:"vcpus_max" hcl:"vcpus_max"`
|
||||
VCPUsAtStartup *uint `mapstructure:"vcpus_atstartup" cty:"vcpus_atstartup" hcl:"vcpus_atstartup"`
|
||||
VMMemory *uint `mapstructure:"vm_memory" cty:"vm_memory" hcl:"vm_memory"`
|
||||
DiskName *string `mapstructure:"disk_name" cty:"disk_name" hcl:"disk_name"`
|
||||
DiskSize *uint `mapstructure:"disk_size" cty:"disk_size" hcl:"disk_size"`
|
||||
CloneTemplate *string `mapstructure:"clone_template" cty:"clone_template" hcl:"clone_template"`
|
||||
VMOtherConfig map[string]string `mapstructure:"vm_other_config" cty:"vm_other_config" hcl:"vm_other_config"`
|
||||
ISOChecksum *string `mapstructure:"iso_checksum" cty:"iso_checksum" hcl:"iso_checksum"`
|
||||
ISOUrls []string `mapstructure:"iso_urls" cty:"iso_urls" hcl:"iso_urls"`
|
||||
ISOUrl *string `mapstructure:"iso_url" cty:"iso_url" hcl:"iso_url"`
|
||||
ISOName *string `mapstructure:"iso_name" cty:"iso_name" hcl:"iso_name"`
|
||||
PlatformArgs map[string]string `mapstructure:"platform_args" cty:"platform_args" hcl:"platform_args"`
|
||||
RawInstallTimeout *string `mapstructure:"install_timeout" cty:"install_timeout" hcl:"install_timeout"`
|
||||
SourcePath *string `mapstructure:"source_path" cty:"source_path" hcl:"source_path"`
|
||||
Firmware *string `mapstructure:"firmware" cty:"firmware" hcl:"firmware"`
|
||||
SkipSetTemplate *bool `mapstructure:"skip_set_template" cty:"skip_set_template" hcl:"skip_set_template"`
|
||||
}
|
||||
|
||||
// FlatMapstructure returns a new FlatConfig.
|
||||
// FlatConfig is an auto-generated flat version of Config.
|
||||
// Where the contents a fields with a `mapstructure:,squash` tag are bubbled up.
|
||||
func (*Config) FlatMapstructure() interface{ HCL2Spec() map[string]hcldec.Spec } {
|
||||
return new(FlatConfig)
|
||||
}
|
||||
|
||||
// HCL2Spec returns the hcl spec of a Config.
|
||||
// This spec is used by HCL to read the fields of Config.
|
||||
// The decoded values from this spec will then be applied to a FlatConfig.
|
||||
func (*FlatConfig) HCL2Spec() map[string]hcldec.Spec {
|
||||
s := map[string]hcldec.Spec{
|
||||
"packer_build_name": &hcldec.AttrSpec{Name: "packer_build_name", Type: cty.String, Required: false},
|
||||
"packer_builder_type": &hcldec.AttrSpec{Name: "packer_builder_type", Type: cty.String, Required: false},
|
||||
"packer_core_version": &hcldec.AttrSpec{Name: "packer_core_version", Type: cty.String, Required: false},
|
||||
"packer_debug": &hcldec.AttrSpec{Name: "packer_debug", Type: cty.Bool, Required: false},
|
||||
"packer_force": &hcldec.AttrSpec{Name: "packer_force", Type: cty.Bool, Required: false},
|
||||
"packer_on_error": &hcldec.AttrSpec{Name: "packer_on_error", Type: cty.String, Required: false},
|
||||
"packer_user_variables": &hcldec.AttrSpec{Name: "packer_user_variables", Type: cty.Map(cty.String), Required: false},
|
||||
"packer_sensitive_variables": &hcldec.AttrSpec{Name: "packer_sensitive_variables", Type: cty.List(cty.String), Required: false},
|
||||
"remote_username": &hcldec.AttrSpec{Name: "remote_username", Type: cty.String, Required: false},
|
||||
"remote_password": &hcldec.AttrSpec{Name: "remote_password", Type: cty.String, Required: false},
|
||||
"remote_host": &hcldec.AttrSpec{Name: "remote_host", Type: cty.String, Required: false},
|
||||
"remote_ssh_port": &hcldec.AttrSpec{Name: "remote_ssh_port", Type: cty.Number, Required: false},
|
||||
"vm_name": &hcldec.AttrSpec{Name: "vm_name", Type: cty.String, Required: false},
|
||||
"vm_description": &hcldec.AttrSpec{Name: "vm_description", Type: cty.String, Required: false},
|
||||
"sr_name": &hcldec.AttrSpec{Name: "sr_name", Type: cty.String, Required: false},
|
||||
"sr_iso_name": &hcldec.AttrSpec{Name: "sr_iso_name", Type: cty.String, Required: false},
|
||||
"floppy_files": &hcldec.AttrSpec{Name: "floppy_files", Type: cty.List(cty.String), Required: false},
|
||||
"network_names": &hcldec.AttrSpec{Name: "network_names", Type: cty.List(cty.String), Required: false},
|
||||
"export_network_names": &hcldec.AttrSpec{Name: "export_network_names", Type: cty.List(cty.String), Required: false},
|
||||
"vm_tags": &hcldec.AttrSpec{Name: "vm_tags", Type: cty.List(cty.String), Required: false},
|
||||
"host_port_min": &hcldec.AttrSpec{Name: "host_port_min", Type: cty.Number, Required: false},
|
||||
"host_port_max": &hcldec.AttrSpec{Name: "host_port_max", Type: cty.Number, Required: false},
|
||||
"boot_command": &hcldec.AttrSpec{Name: "boot_command", Type: cty.List(cty.String), Required: false},
|
||||
"shutdown_command": &hcldec.AttrSpec{Name: "shutdown_command", Type: cty.String, Required: false},
|
||||
"boot_wait": &hcldec.AttrSpec{Name: "boot_wait", Type: cty.String, Required: false},
|
||||
"tools_iso_name": &hcldec.AttrSpec{Name: "tools_iso_name", Type: cty.String, Required: false},
|
||||
"http_directory": &hcldec.AttrSpec{Name: "http_directory", Type: cty.String, Required: false},
|
||||
"http_port_min": &hcldec.AttrSpec{Name: "http_port_min", Type: cty.Number, Required: false},
|
||||
"http_port_max": &hcldec.AttrSpec{Name: "http_port_max", Type: cty.Number, Required: false},
|
||||
"ssh_key_path": &hcldec.AttrSpec{Name: "ssh_key_path", Type: cty.String, Required: false},
|
||||
"ssh_password": &hcldec.AttrSpec{Name: "ssh_password", Type: cty.String, Required: false},
|
||||
"ssh_port": &hcldec.AttrSpec{Name: "ssh_port", Type: cty.Number, Required: false},
|
||||
"ssh_username": &hcldec.AttrSpec{Name: "ssh_username", 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},
|
||||
"ssh_keypair_name": &hcldec.AttrSpec{Name: "ssh_keypair_name", Type: cty.String, Required: false},
|
||||
"temporary_key_pair_name": &hcldec.AttrSpec{Name: "temporary_key_pair_name", Type: cty.String, Required: false},
|
||||
"temporary_key_pair_type": &hcldec.AttrSpec{Name: "temporary_key_pair_type", Type: cty.String, Required: false},
|
||||
"temporary_key_pair_bits": &hcldec.AttrSpec{Name: "temporary_key_pair_bits", Type: cty.Number, Required: false},
|
||||
"ssh_ciphers": &hcldec.AttrSpec{Name: "ssh_ciphers", Type: cty.List(cty.String), Required: false},
|
||||
"ssh_clear_authorized_keys": &hcldec.AttrSpec{Name: "ssh_clear_authorized_keys", Type: cty.Bool, Required: false},
|
||||
"ssh_key_exchange_algorithms": &hcldec.AttrSpec{Name: "ssh_key_exchange_algorithms", Type: cty.List(cty.String), Required: false},
|
||||
"ssh_private_key_file": &hcldec.AttrSpec{Name: "ssh_private_key_file", Type: cty.String, Required: false},
|
||||
"ssh_certificate_file": &hcldec.AttrSpec{Name: "ssh_certificate_file", Type: cty.String, Required: false},
|
||||
"ssh_pty": &hcldec.AttrSpec{Name: "ssh_pty", Type: cty.Bool, Required: false},
|
||||
"ssh_timeout": &hcldec.AttrSpec{Name: "ssh_timeout", Type: cty.String, Required: false},
|
||||
"ssh_wait_timeout": &hcldec.AttrSpec{Name: "ssh_wait_timeout", Type: cty.String, Required: false},
|
||||
"ssh_agent_auth": &hcldec.AttrSpec{Name: "ssh_agent_auth", Type: cty.Bool, Required: false},
|
||||
"ssh_disable_agent_forwarding": &hcldec.AttrSpec{Name: "ssh_disable_agent_forwarding", Type: cty.Bool, Required: false},
|
||||
"ssh_handshake_attempts": &hcldec.AttrSpec{Name: "ssh_handshake_attempts", Type: cty.Number, Required: false},
|
||||
"ssh_bastion_host": &hcldec.AttrSpec{Name: "ssh_bastion_host", Type: cty.String, Required: false},
|
||||
"ssh_bastion_port": &hcldec.AttrSpec{Name: "ssh_bastion_port", Type: cty.Number, Required: false},
|
||||
"ssh_bastion_agent_auth": &hcldec.AttrSpec{Name: "ssh_bastion_agent_auth", Type: cty.Bool, Required: false},
|
||||
"ssh_bastion_username": &hcldec.AttrSpec{Name: "ssh_bastion_username", Type: cty.String, Required: false},
|
||||
"ssh_bastion_password": &hcldec.AttrSpec{Name: "ssh_bastion_password", Type: cty.String, Required: false},
|
||||
"ssh_bastion_interactive": &hcldec.AttrSpec{Name: "ssh_bastion_interactive", Type: cty.Bool, Required: false},
|
||||
"ssh_bastion_private_key_file": &hcldec.AttrSpec{Name: "ssh_bastion_private_key_file", Type: cty.String, Required: false},
|
||||
"ssh_bastion_certificate_file": &hcldec.AttrSpec{Name: "ssh_bastion_certificate_file", Type: cty.String, Required: false},
|
||||
"ssh_file_transfer_method": &hcldec.AttrSpec{Name: "ssh_file_transfer_method", Type: cty.String, Required: false},
|
||||
"ssh_proxy_host": &hcldec.AttrSpec{Name: "ssh_proxy_host", Type: cty.String, Required: false},
|
||||
"ssh_proxy_port": &hcldec.AttrSpec{Name: "ssh_proxy_port", Type: cty.Number, Required: false},
|
||||
"ssh_proxy_username": &hcldec.AttrSpec{Name: "ssh_proxy_username", Type: cty.String, Required: false},
|
||||
"ssh_proxy_password": &hcldec.AttrSpec{Name: "ssh_proxy_password", Type: cty.String, Required: false},
|
||||
"ssh_keep_alive_interval": &hcldec.AttrSpec{Name: "ssh_keep_alive_interval", Type: cty.String, Required: false},
|
||||
"ssh_read_write_timeout": &hcldec.AttrSpec{Name: "ssh_read_write_timeout", Type: cty.String, Required: false},
|
||||
"ssh_remote_tunnels": &hcldec.AttrSpec{Name: "ssh_remote_tunnels", Type: cty.List(cty.String), Required: false},
|
||||
"ssh_local_tunnels": &hcldec.AttrSpec{Name: "ssh_local_tunnels", Type: cty.List(cty.String), Required: false},
|
||||
"ssh_public_key": &hcldec.AttrSpec{Name: "ssh_public_key", Type: cty.List(cty.Number), Required: false},
|
||||
"ssh_private_key": &hcldec.AttrSpec{Name: "ssh_private_key", Type: cty.List(cty.Number), Required: false},
|
||||
"winrm_username": &hcldec.AttrSpec{Name: "winrm_username", Type: cty.String, Required: false},
|
||||
"winrm_password": &hcldec.AttrSpec{Name: "winrm_password", Type: cty.String, Required: false},
|
||||
"winrm_host": &hcldec.AttrSpec{Name: "winrm_host", Type: cty.String, Required: false},
|
||||
"winrm_no_proxy": &hcldec.AttrSpec{Name: "winrm_no_proxy", Type: cty.Bool, Required: false},
|
||||
"winrm_port": &hcldec.AttrSpec{Name: "winrm_port", Type: cty.Number, Required: false},
|
||||
"winrm_timeout": &hcldec.AttrSpec{Name: "winrm_timeout", Type: cty.String, Required: false},
|
||||
"winrm_use_ssl": &hcldec.AttrSpec{Name: "winrm_use_ssl", Type: cty.Bool, Required: false},
|
||||
"winrm_insecure": &hcldec.AttrSpec{Name: "winrm_insecure", Type: cty.Bool, Required: false},
|
||||
"winrm_use_ntlm": &hcldec.AttrSpec{Name: "winrm_use_ntlm", Type: cty.Bool, Required: false},
|
||||
"ssh_host_port_min": &hcldec.AttrSpec{Name: "ssh_host_port_min", Type: cty.Number, Required: false},
|
||||
"ssh_host_port_max": &hcldec.AttrSpec{Name: "ssh_host_port_max", Type: cty.Number, Required: false},
|
||||
"ssh_skip_nat_mapping": &hcldec.AttrSpec{Name: "ssh_skip_nat_mapping", Type: cty.Bool, Required: false},
|
||||
"output_directory": &hcldec.AttrSpec{Name: "output_directory", Type: cty.String, Required: false},
|
||||
"format": &hcldec.AttrSpec{Name: "format", Type: cty.String, Required: false},
|
||||
"keep_vm": &hcldec.AttrSpec{Name: "keep_vm", Type: cty.String, Required: false},
|
||||
"ip_getter": &hcldec.AttrSpec{Name: "ip_getter", Type: cty.String, Required: false},
|
||||
"vcpus_max": &hcldec.AttrSpec{Name: "vcpus_max", Type: cty.Number, Required: false},
|
||||
"vcpus_atstartup": &hcldec.AttrSpec{Name: "vcpus_atstartup", Type: cty.Number, Required: false},
|
||||
"vm_memory": &hcldec.AttrSpec{Name: "vm_memory", Type: cty.Number, Required: false},
|
||||
"disk_name": &hcldec.AttrSpec{Name: "disk_name", Type: cty.String, Required: false},
|
||||
"disk_size": &hcldec.AttrSpec{Name: "disk_size", Type: cty.Number, Required: false},
|
||||
"clone_template": &hcldec.AttrSpec{Name: "clone_template", Type: cty.String, Required: false},
|
||||
"vm_other_config": &hcldec.AttrSpec{Name: "vm_other_config", Type: cty.Map(cty.String), Required: false},
|
||||
"iso_checksum": &hcldec.AttrSpec{Name: "iso_checksum", Type: cty.String, Required: false},
|
||||
"iso_urls": &hcldec.AttrSpec{Name: "iso_urls", Type: cty.List(cty.String), Required: false},
|
||||
"iso_url": &hcldec.AttrSpec{Name: "iso_url", Type: cty.String, Required: false},
|
||||
"iso_name": &hcldec.AttrSpec{Name: "iso_name", Type: cty.String, Required: false},
|
||||
"platform_args": &hcldec.AttrSpec{Name: "platform_args", Type: cty.Map(cty.String), Required: false},
|
||||
"install_timeout": &hcldec.AttrSpec{Name: "install_timeout", Type: cty.String, Required: false},
|
||||
"source_path": &hcldec.AttrSpec{Name: "source_path", Type: cty.String, Required: false},
|
||||
"firmware": &hcldec.AttrSpec{Name: "firmware", Type: cty.String, Required: false},
|
||||
"skip_set_template": &hcldec.AttrSpec{Name: "skip_set_template", Type: cty.Bool, Required: false},
|
||||
}
|
||||
return s
|
||||
}
|
25
builder/xenserver/common/find_port.go
Normal file
25
builder/xenserver/common/find_port.go
Normal file
@ -0,0 +1,25 @@
|
||||
package common
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"log"
|
||||
"net"
|
||||
)
|
||||
|
||||
// FindPort finds and starts listening on a port in the range [portMin, portMax]
|
||||
// returns the listener and the port number on success, or nil, 0 on failure
|
||||
func FindPort(portMin uint, portMax uint) (net.Listener, uint) {
|
||||
log.Printf("Looking for an available port between %d and %d", portMin, portMax)
|
||||
|
||||
for port := portMin; port <= portMax; port++ {
|
||||
log.Printf("Trying port: %d", port)
|
||||
l, err := net.Listen("tcp", fmt.Sprintf(":%d", port))
|
||||
if err == nil {
|
||||
return l, port
|
||||
} else {
|
||||
log.Printf("Port %d unavailable: %s", port, err.Error())
|
||||
}
|
||||
}
|
||||
|
||||
return nil, 0
|
||||
}
|
127
builder/xenserver/common/http_upload.go
Normal file
127
builder/xenserver/common/http_upload.go
Normal file
@ -0,0 +1,127 @@
|
||||
package common
|
||||
|
||||
import (
|
||||
"crypto/tls"
|
||||
"fmt"
|
||||
"log"
|
||||
"net/http"
|
||||
"net/url"
|
||||
"os"
|
||||
"time"
|
||||
|
||||
"github.com/hashicorp/packer-plugin-sdk/multistep"
|
||||
"github.com/hashicorp/packer-plugin-sdk/packer"
|
||||
xsclient "github.com/terra-farm/go-xen-api-client"
|
||||
)
|
||||
|
||||
func appendQuery(urlstring, k, v string) (string, error) {
|
||||
u, err := url.Parse(urlstring)
|
||||
if err != nil {
|
||||
return "", fmt.Errorf("Unable to parse URL '%s': %s", urlstring, err.Error())
|
||||
}
|
||||
m := u.Query()
|
||||
m.Add(k, v)
|
||||
u.RawQuery = m.Encode()
|
||||
return u.String(), err
|
||||
}
|
||||
|
||||
func HTTPUpload(import_url string, fh *os.File, state multistep.StateBag) (result string, err error) {
|
||||
ui := state.Get("ui").(packer.Ui)
|
||||
c := state.Get("client").(*Connection)
|
||||
|
||||
task, err := c.client.Task.Create(c.session, "packer-task", "Packer task")
|
||||
if err != nil {
|
||||
err = fmt.Errorf("Unable to create task: %s", err.Error())
|
||||
return
|
||||
}
|
||||
defer c.client.Task.Destroy(c.session, task)
|
||||
|
||||
import_task_url, err := appendQuery(import_url, "task_id", string(task))
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
// Get file length
|
||||
fstat, err := fh.Stat()
|
||||
if err != nil {
|
||||
err = fmt.Errorf("Unable to stat '%s': %s", fh.Name(), err.Error())
|
||||
return
|
||||
}
|
||||
fileLength := fstat.Size()
|
||||
|
||||
// Define a new transport which allows self-signed certs
|
||||
tr := &http.Transport{
|
||||
TLSClientConfig: &tls.Config{InsecureSkipVerify: true},
|
||||
}
|
||||
|
||||
// Create a client
|
||||
httpClient := &http.Client{Transport: tr}
|
||||
|
||||
// Create request and download file
|
||||
request, err := http.NewRequest("PUT", import_task_url, fh)
|
||||
request.ContentLength = fileLength
|
||||
|
||||
ui.Say(fmt.Sprintf("PUT '%s'", import_task_url))
|
||||
|
||||
resp, err := httpClient.Do(request) // Do closes fh for us, according to docs
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
if resp.StatusCode != 200 {
|
||||
err = fmt.Errorf("PUT request got non-200 status code: %s", resp.Status)
|
||||
return
|
||||
}
|
||||
|
||||
logIteration := 0
|
||||
err = InterruptibleWait{
|
||||
Predicate: func() (bool, error) {
|
||||
status, err := c.client.Task.GetStatus(c.session, task)
|
||||
if err != nil {
|
||||
return false, fmt.Errorf("Failed to get task status: %s", err.Error())
|
||||
}
|
||||
switch status {
|
||||
case xsclient.TaskStatusTypePending:
|
||||
progress, err := c.client.Task.GetProgress(c.session, task)
|
||||
if err != nil {
|
||||
return false, fmt.Errorf("Failed to get progress: %s", err.Error())
|
||||
}
|
||||
logIteration = logIteration + 1
|
||||
if logIteration%5 == 0 {
|
||||
log.Printf("Upload %.0f%% complete", progress*100)
|
||||
}
|
||||
return false, nil
|
||||
case xsclient.TaskStatusTypeSuccess:
|
||||
return true, nil
|
||||
case xsclient.TaskStatusTypeFailure:
|
||||
errorInfo, err := c.client.Task.GetErrorInfo(c.session, task)
|
||||
if err != nil {
|
||||
errorInfo = []string{fmt.Sprintf("furthermore, failed to get error info: %s", err.Error())}
|
||||
}
|
||||
return false, fmt.Errorf("Task failed: %s", errorInfo)
|
||||
case xsclient.TaskStatusTypeCancelling, xsclient.TaskStatusTypeCancelled:
|
||||
return false, fmt.Errorf("Task cancelled")
|
||||
default:
|
||||
return false, fmt.Errorf("Unknown task status %v", status)
|
||||
}
|
||||
},
|
||||
PredicateInterval: 1 * time.Second,
|
||||
Timeout: 24 * time.Hour,
|
||||
}.Wait(state)
|
||||
|
||||
resp.Body.Close()
|
||||
|
||||
if err != nil {
|
||||
err = fmt.Errorf("Error uploading: %s", err.Error())
|
||||
return
|
||||
}
|
||||
|
||||
result, err = c.client.Task.GetResult(c.session, task)
|
||||
if err != nil {
|
||||
err = fmt.Errorf("Error getting result: %s", err.Error())
|
||||
return
|
||||
}
|
||||
|
||||
log.Printf("Upload complete")
|
||||
return
|
||||
}
|
86
builder/xenserver/common/interruptible_wait.go
Normal file
86
builder/xenserver/common/interruptible_wait.go
Normal file
@ -0,0 +1,86 @@
|
||||
package common
|
||||
|
||||
import (
|
||||
"github.com/hashicorp/packer-plugin-sdk/multistep"
|
||||
"time"
|
||||
)
|
||||
|
||||
type InterruptibleWait struct {
|
||||
Timeout time.Duration
|
||||
|
||||
// optional:
|
||||
Predicate func() (result bool, err error)
|
||||
PredicateInterval time.Duration
|
||||
}
|
||||
|
||||
type TimeoutError struct{}
|
||||
|
||||
func (err TimeoutError) Error() string {
|
||||
return "Timed out"
|
||||
}
|
||||
|
||||
type InterruptedError struct{}
|
||||
|
||||
func (err InterruptedError) Error() string {
|
||||
return "Interrupted"
|
||||
}
|
||||
|
||||
type PredicateResult struct {
|
||||
complete bool
|
||||
err error
|
||||
}
|
||||
|
||||
/* Wait waits for up to Timeout duration, checking an optional Predicate every PredicateInterval duration.
|
||||
The first run of Predicate is immediately after Wait is called.
|
||||
If the command is interrupted by the user, then an InterruptedError is returned.
|
||||
If Predicate is not nil, a timeout leads to TimeoutError being returned, and a successful Predicate run leads to nil being returned.
|
||||
If Predicate is nil, a timeout is not an error, and nil is returned.
|
||||
*/
|
||||
func (wait InterruptibleWait) Wait(state multistep.StateBag) error {
|
||||
predicateResult := make(chan PredicateResult, 1)
|
||||
stopWaiting := make(chan struct{})
|
||||
defer close(stopWaiting)
|
||||
|
||||
if wait.Predicate != nil {
|
||||
go func() {
|
||||
for {
|
||||
if complete, err := wait.Predicate(); err != nil || complete {
|
||||
predicateResult <- PredicateResult{complete, err}
|
||||
return
|
||||
}
|
||||
|
||||
select {
|
||||
case <-time.After(wait.PredicateInterval):
|
||||
continue
|
||||
case <-stopWaiting:
|
||||
return
|
||||
}
|
||||
}
|
||||
}()
|
||||
}
|
||||
|
||||
timeout := time.After(wait.Timeout)
|
||||
for {
|
||||
// wait for either install to complete/error,
|
||||
// an interrupt to come through, or a timeout to occur
|
||||
|
||||
if _, ok := state.GetOk(multistep.StateCancelled); ok {
|
||||
return InterruptedError{}
|
||||
}
|
||||
|
||||
select {
|
||||
case result := <-predicateResult:
|
||||
return result.err
|
||||
|
||||
case <-time.After(1 * time.Second):
|
||||
continue
|
||||
|
||||
case <-timeout:
|
||||
if wait.Predicate != nil {
|
||||
return TimeoutError{}
|
||||
} else {
|
||||
return nil
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
217
builder/xenserver/common/ssh.go
Normal file
217
builder/xenserver/common/ssh.go
Normal file
@ -0,0 +1,217 @@
|
||||
package common
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"encoding/pem"
|
||||
"fmt"
|
||||
"io"
|
||||
"io/ioutil"
|
||||
"log"
|
||||
"net"
|
||||
"os"
|
||||
"strings"
|
||||
|
||||
"github.com/hashicorp/packer-plugin-sdk/multistep"
|
||||
gossh "golang.org/x/crypto/ssh"
|
||||
)
|
||||
|
||||
func SSHAddress(state multistep.StateBag) (string, error) {
|
||||
sshIP := state.Get("ssh_address").(string)
|
||||
sshHostPort := state.Get("ssh_port").(uint)
|
||||
return fmt.Sprintf("%s:%d", sshIP, sshHostPort), nil
|
||||
}
|
||||
|
||||
func SSHLocalAddress(state multistep.StateBag) (string, error) {
|
||||
sshLocalPort, ok := state.Get("local_ssh_port").(uint)
|
||||
if !ok {
|
||||
return "", fmt.Errorf("SSH port forwarding hasn't been set up yet")
|
||||
}
|
||||
conn_str := fmt.Sprintf("%s:%d", "127.0.0.1", sshLocalPort)
|
||||
return conn_str, nil
|
||||
}
|
||||
|
||||
func SSHPort(state multistep.StateBag) (int, error) {
|
||||
sshHostPort := state.Get("local_ssh_port").(uint)
|
||||
return int(sshHostPort), nil
|
||||
}
|
||||
|
||||
func CommHost(state multistep.StateBag) (string, error) {
|
||||
return "127.0.0.1", nil
|
||||
}
|
||||
|
||||
func SSHConfigFunc(config SSHConfig) func(multistep.StateBag) (*gossh.ClientConfig, error) {
|
||||
return func(state multistep.StateBag) (*gossh.ClientConfig, error) {
|
||||
config := state.Get("commonconfig").(CommonConfig)
|
||||
auth := []gossh.AuthMethod{
|
||||
gossh.Password(config.SSHPassword),
|
||||
}
|
||||
|
||||
if config.SSHKeyPath != "" {
|
||||
signer, err := FileSigner(config.SSHKeyPath)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
auth = append(auth, gossh.PublicKeys(signer))
|
||||
}
|
||||
|
||||
return &gossh.ClientConfig{
|
||||
User: config.SSHUser,
|
||||
Auth: auth,
|
||||
HostKeyCallback: gossh.InsecureIgnoreHostKey(),
|
||||
}, nil
|
||||
}
|
||||
}
|
||||
|
||||
func doExecuteSSHCmd(cmd, target string, config *gossh.ClientConfig) (stdout string, err error) {
|
||||
client, err := gossh.Dial("tcp", target, config)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
|
||||
//Create session
|
||||
session, err := client.NewSession()
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
|
||||
defer session.Close()
|
||||
|
||||
var b bytes.Buffer
|
||||
session.Stdout = &b
|
||||
if err := session.Run(cmd); err != nil {
|
||||
return "", err
|
||||
}
|
||||
|
||||
return strings.Trim(b.String(), "\n"), nil
|
||||
}
|
||||
|
||||
func ExecuteHostSSHCmd(state multistep.StateBag, cmd string) (stdout string, err error) {
|
||||
config := state.Get("commonconfig").(CommonConfig)
|
||||
sshAddress, _ := SSHAddress(state)
|
||||
// Setup connection config
|
||||
sshConfig := &gossh.ClientConfig{
|
||||
User: config.Username,
|
||||
Auth: []gossh.AuthMethod{
|
||||
gossh.Password(config.Password),
|
||||
},
|
||||
HostKeyCallback: gossh.InsecureIgnoreHostKey(),
|
||||
}
|
||||
return doExecuteSSHCmd(cmd, sshAddress, sshConfig)
|
||||
}
|
||||
|
||||
func ExecuteGuestSSHCmd(state multistep.StateBag, cmd string) (stdout string, err error) {
|
||||
config := state.Get("commonconfig").(CommonConfig)
|
||||
localAddress, err := SSHLocalAddress(state)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
sshConfig, err := SSHConfigFunc(config.SSHConfig)(state)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
return doExecuteSSHCmd(cmd, localAddress, sshConfig)
|
||||
}
|
||||
|
||||
func forward(local_conn net.Conn, config *gossh.ClientConfig, server string, server_ssh_port int, remote_dest string, remote_port uint) error {
|
||||
defer local_conn.Close()
|
||||
|
||||
ssh_client_conn, err := gossh.Dial("tcp", fmt.Sprintf("%s:%d", server, server_ssh_port), config)
|
||||
if err != nil {
|
||||
log.Printf("local ssh.Dial error: %s", err)
|
||||
return err
|
||||
}
|
||||
defer ssh_client_conn.Close()
|
||||
|
||||
remote_loc := fmt.Sprintf("%s:%d", remote_dest, remote_port)
|
||||
ssh_conn, err := ssh_client_conn.Dial("tcp", remote_loc)
|
||||
if err != nil {
|
||||
log.Printf("ssh.Dial error: %s", err)
|
||||
return err
|
||||
}
|
||||
defer ssh_conn.Close()
|
||||
|
||||
txDone := make(chan struct{})
|
||||
rxDone := make(chan struct{})
|
||||
|
||||
go func() {
|
||||
_, err = io.Copy(ssh_conn, local_conn)
|
||||
if err != nil {
|
||||
log.Printf("io.copy failed: %v", err)
|
||||
}
|
||||
close(txDone)
|
||||
}()
|
||||
|
||||
go func() {
|
||||
_, err = io.Copy(local_conn, ssh_conn)
|
||||
if err != nil {
|
||||
log.Printf("io.copy failed: %v", err)
|
||||
}
|
||||
close(rxDone)
|
||||
}()
|
||||
|
||||
<-txDone
|
||||
<-rxDone
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func ssh_port_forward(local_listener net.Listener, remote_port int, remote_dest, host string, host_ssh_port int, username, password string) error {
|
||||
|
||||
config := &gossh.ClientConfig{
|
||||
User: username,
|
||||
Auth: []gossh.AuthMethod{
|
||||
gossh.Password(password),
|
||||
},
|
||||
HostKeyCallback: gossh.InsecureIgnoreHostKey(),
|
||||
}
|
||||
|
||||
for {
|
||||
local_connection, err := local_listener.Accept()
|
||||
|
||||
if err != nil {
|
||||
log.Printf("Local accept failed: %s", err)
|
||||
return err
|
||||
}
|
||||
|
||||
// Forward to a remote port
|
||||
go forward(local_connection, config, host, host_ssh_port, remote_dest, uint(remote_port))
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// FileSigner returns an gossh.Signer for a key file.
|
||||
func FileSigner(path string) (gossh.Signer, error) {
|
||||
f, err := os.Open(path)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
defer f.Close()
|
||||
|
||||
keyBytes, err := ioutil.ReadAll(f)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
// We parse the private key on our own first so that we can
|
||||
// show a nicer error if the private key has a password.
|
||||
block, _ := pem.Decode(keyBytes)
|
||||
if block == nil {
|
||||
return nil, fmt.Errorf(
|
||||
"Failed to read key '%s': no key found", path)
|
||||
}
|
||||
if block.Headers["Proc-Type"] == "4,ENCRYPTED" {
|
||||
return nil, fmt.Errorf(
|
||||
"Failed to read key '%s': password protected keys are\n"+
|
||||
"not supported. Please decrypt the key prior to use.", path)
|
||||
}
|
||||
|
||||
signer, err := gossh.ParsePrivateKey(keyBytes)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("Error setting up SSH config: %s", err)
|
||||
}
|
||||
|
||||
return signer, nil
|
||||
}
|
48
builder/xenserver/common/ssh_config.go
Normal file
48
builder/xenserver/common/ssh_config.go
Normal file
@ -0,0 +1,48 @@
|
||||
package common
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"time"
|
||||
|
||||
"github.com/hashicorp/packer-plugin-sdk/communicator"
|
||||
"github.com/hashicorp/packer-plugin-sdk/template/interpolate"
|
||||
)
|
||||
|
||||
type SSHConfig struct {
|
||||
Comm communicator.Config `mapstructure:",squash"`
|
||||
|
||||
SSHHostPortMin uint `mapstructure:"ssh_host_port_min"`
|
||||
SSHHostPortMax uint `mapstructure:"ssh_host_port_max"`
|
||||
SSHSkipNatMapping bool `mapstructure:"ssh_skip_nat_mapping"`
|
||||
|
||||
// These are deprecated, but we keep them around for BC
|
||||
// TODO(@mitchellh): remove
|
||||
SSHKeyPath string `mapstructure:"ssh_key_path"`
|
||||
SSHWaitTimeout time.Duration `mapstructure:"ssh_wait_timeout"`
|
||||
}
|
||||
|
||||
func (c *SSHConfig) Prepare(ctx *interpolate.Context) []error {
|
||||
if c.SSHHostPortMin == 0 {
|
||||
c.SSHHostPortMin = 2222
|
||||
}
|
||||
|
||||
if c.SSHHostPortMax == 0 {
|
||||
c.SSHHostPortMax = 4444
|
||||
}
|
||||
|
||||
// TODO: backwards compatibility, write fixer instead
|
||||
if c.SSHKeyPath != "" {
|
||||
c.Comm.SSHPrivateKeyFile = c.SSHKeyPath
|
||||
}
|
||||
if c.SSHWaitTimeout != 0 {
|
||||
c.Comm.SSHTimeout = c.SSHWaitTimeout
|
||||
}
|
||||
|
||||
errs := c.Comm.Prepare(ctx)
|
||||
if c.SSHHostPortMin > c.SSHHostPortMax {
|
||||
errs = append(errs,
|
||||
errors.New("ssh_host_port_min must be less than ssh_host_port_max"))
|
||||
}
|
||||
|
||||
return errs
|
||||
}
|
84
builder/xenserver/common/step_attach_vdi.go
Normal file
84
builder/xenserver/common/step_attach_vdi.go
Normal file
@ -0,0 +1,84 @@
|
||||
package common
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"log"
|
||||
|
||||
"github.com/hashicorp/packer-plugin-sdk/multistep"
|
||||
"github.com/hashicorp/packer-plugin-sdk/packer"
|
||||
xsclient "github.com/terra-farm/go-xen-api-client"
|
||||
)
|
||||
|
||||
type StepAttachVdi struct {
|
||||
VdiUuidKey string
|
||||
VdiType xsclient.VbdType
|
||||
|
||||
vdi xsclient.VDIRef
|
||||
}
|
||||
|
||||
func (self *StepAttachVdi) Run(ctx context.Context, state multistep.StateBag) multistep.StepAction {
|
||||
ui := state.Get("ui").(packer.Ui)
|
||||
c := state.Get("client").(*Connection)
|
||||
|
||||
log.Printf("Running attach vdi for key %s\n", self.VdiUuidKey)
|
||||
var vdiUuid string
|
||||
if vdiUuidRaw, ok := state.GetOk(self.VdiUuidKey); ok {
|
||||
vdiUuid = vdiUuidRaw.(string)
|
||||
} else {
|
||||
log.Printf("Skipping attach of '%s'", self.VdiUuidKey)
|
||||
return multistep.ActionContinue
|
||||
}
|
||||
|
||||
var err error
|
||||
self.vdi, err = c.client.VDI.GetByUUID(c.session, vdiUuid)
|
||||
if err != nil {
|
||||
ui.Error(fmt.Sprintf("Unable to get VDI from UUID '%s': %s", vdiUuid, err.Error()))
|
||||
return multistep.ActionHalt
|
||||
}
|
||||
|
||||
uuid := state.Get("instance_uuid").(string)
|
||||
instance, err := c.client.VM.GetByUUID(c.session, uuid)
|
||||
if err != nil {
|
||||
ui.Error(fmt.Sprintf("Unable to get VM from UUID '%s': %s", uuid, err.Error()))
|
||||
return multistep.ActionHalt
|
||||
}
|
||||
|
||||
err = ConnectVdi(c, instance, self.vdi, self.VdiType)
|
||||
if err != nil {
|
||||
ui.Error(fmt.Sprintf("Error attaching VDI '%s': '%s'", vdiUuid, err.Error()))
|
||||
return multistep.ActionHalt
|
||||
}
|
||||
|
||||
log.Printf("Attached VDI '%s'", vdiUuid)
|
||||
|
||||
return multistep.ActionContinue
|
||||
}
|
||||
|
||||
func (self *StepAttachVdi) Cleanup(state multistep.StateBag) {
|
||||
config := state.Get("commonconfig").(CommonConfig)
|
||||
c := state.Get("client").(*Connection)
|
||||
if config.ShouldKeepVM(state) {
|
||||
return
|
||||
}
|
||||
|
||||
if self.vdi == "" {
|
||||
return
|
||||
}
|
||||
|
||||
uuid := state.Get("instance_uuid").(string)
|
||||
vmRef, err := c.client.VM.GetByUUID(c.session, uuid)
|
||||
if err != nil {
|
||||
log.Printf("Unable to get VM from UUID '%s': %s", uuid, err.Error())
|
||||
return
|
||||
}
|
||||
|
||||
vdiUuid := state.Get(self.VdiUuidKey).(string)
|
||||
|
||||
err = DisconnectVdi(c, vmRef, self.vdi)
|
||||
if err != nil {
|
||||
log.Printf("Unable to disconnect VDI '%s': %s", vdiUuid, err.Error())
|
||||
return
|
||||
}
|
||||
log.Printf("Detached VDI '%s'", vdiUuid)
|
||||
}
|
33
builder/xenserver/common/step_boot_wait.go
Normal file
33
builder/xenserver/common/step_boot_wait.go
Normal file
@ -0,0 +1,33 @@
|
||||
package common
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
|
||||
"github.com/hashicorp/packer-plugin-sdk/multistep"
|
||||
"github.com/hashicorp/packer-plugin-sdk/packer"
|
||||
)
|
||||
|
||||
type StepBootWait struct{}
|
||||
|
||||
func (self *StepBootWait) Run(ctx context.Context, state multistep.StateBag) multistep.StepAction {
|
||||
c := state.Get("client").(*Connection)
|
||||
config := state.Get("commonconfig").(CommonConfig)
|
||||
ui := state.Get("ui").(packer.Ui)
|
||||
|
||||
instance, _ := c.client.VM.GetByUUID(c.session, state.Get("instance_uuid").(string))
|
||||
ui.Say("Unpausing VM " + state.Get("instance_uuid").(string))
|
||||
Unpause(c, instance)
|
||||
|
||||
if int64(config.BootWait) > 0 {
|
||||
ui.Say(fmt.Sprintf("Waiting %s for boot...", config.BootWait))
|
||||
err := InterruptibleWait{Timeout: config.BootWait}.Wait(state)
|
||||
if err != nil {
|
||||
ui.Error(err.Error())
|
||||
return multistep.ActionHalt
|
||||
}
|
||||
}
|
||||
return multistep.ActionContinue
|
||||
}
|
||||
|
||||
func (self *StepBootWait) Cleanup(state multistep.StateBag) {}
|
262
builder/xenserver/common/step_create_instance.go
Normal file
262
builder/xenserver/common/step_create_instance.go
Normal file
@ -0,0 +1,262 @@
|
||||
package common
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"log"
|
||||
|
||||
"github.com/hashicorp/packer-plugin-sdk/multistep"
|
||||
"github.com/hashicorp/packer-plugin-sdk/packer"
|
||||
xenapi "github.com/terra-farm/go-xen-api-client"
|
||||
xsclient "github.com/terra-farm/go-xen-api-client"
|
||||
)
|
||||
|
||||
type StepCreateInstance struct {
|
||||
// The XVA builder assumes it will boot an instance with an OS installed on its disks
|
||||
// while the ISO builder needs packer to create a disk for an OS to be installed on.
|
||||
AssumePreInstalledOS bool
|
||||
|
||||
instance *xsclient.VMRef
|
||||
vdi *xsclient.VDIRef
|
||||
}
|
||||
|
||||
func (self *StepCreateInstance) Run(ctx context.Context, state multistep.StateBag) multistep.StepAction {
|
||||
|
||||
c := state.Get("client").(*Connection)
|
||||
config := state.Get("config").(Config)
|
||||
ui := state.Get("ui").(packer.Ui)
|
||||
|
||||
ui.Say("Step: Create Instance")
|
||||
|
||||
// Get the template to clone from
|
||||
|
||||
vms, err := c.GetClient().VM.GetByNameLabel(c.GetSessionRef(), config.CloneTemplate)
|
||||
|
||||
switch {
|
||||
case len(vms) == 0:
|
||||
ui.Error(fmt.Sprintf("Couldn't find a template with the name-label '%s'. Aborting.", config.CloneTemplate))
|
||||
return multistep.ActionHalt
|
||||
case len(vms) > 1:
|
||||
ui.Error(fmt.Sprintf("Found more than one template with the name '%s'. The name must be unique. Aborting.", config.CloneTemplate))
|
||||
return multistep.ActionHalt
|
||||
}
|
||||
|
||||
template := vms[0]
|
||||
|
||||
// Clone that VM template
|
||||
instance, err := c.GetClient().VM.Clone(c.GetSessionRef(), template, config.VMName)
|
||||
if err != nil {
|
||||
ui.Error(fmt.Sprintf("Error cloning VM: %s", err.Error()))
|
||||
return multistep.ActionHalt
|
||||
}
|
||||
self.instance = &instance
|
||||
|
||||
err = c.GetClient().VM.SetIsATemplate(c.GetSessionRef(), instance, false)
|
||||
if err != nil {
|
||||
ui.Error(fmt.Sprintf("Error setting is_a_template=false: %s", err.Error()))
|
||||
return multistep.ActionHalt
|
||||
}
|
||||
|
||||
err = c.GetClient().VM.SetVCPUsMax(c.GetSessionRef(), instance, int(config.VCPUsMax))
|
||||
if err != nil {
|
||||
ui.Error(fmt.Sprintf("Error setting VM VCPUs Max=%d: %s", config.VCPUsMax, err.Error()))
|
||||
return multistep.ActionHalt
|
||||
}
|
||||
|
||||
err = c.GetClient().VM.SetVCPUsAtStartup(c.GetSessionRef(), instance, int(config.VCPUsAtStartup))
|
||||
if err != nil {
|
||||
ui.Error(fmt.Sprintf("Error setting VM VCPUs At Startup=%d: %s", config.VCPUsAtStartup, err.Error()))
|
||||
return multistep.ActionHalt
|
||||
}
|
||||
|
||||
memory := int(config.VMMemory * 1024 * 1024)
|
||||
err = c.GetClient().VM.SetMemoryLimits(c.GetSessionRef(), instance, memory, memory, memory, memory)
|
||||
if err != nil {
|
||||
ui.Error(fmt.Sprintf("Error setting VM memory=%d: %s", memory, err.Error()))
|
||||
return multistep.ActionHalt
|
||||
}
|
||||
|
||||
err = c.GetClient().VM.SetPlatform(c.GetSessionRef(), instance, config.PlatformArgs)
|
||||
if err != nil {
|
||||
ui.Error(fmt.Sprintf("Error setting VM platform: %s", err.Error()))
|
||||
return multistep.ActionHalt
|
||||
}
|
||||
|
||||
err = c.GetClient().VM.SetNameDescription(c.GetSessionRef(), instance, config.VMDescription)
|
||||
if err != nil {
|
||||
ui.Error(fmt.Sprintf("Error setting VM description: %s", err.Error()))
|
||||
return multistep.ActionHalt
|
||||
}
|
||||
|
||||
if len(config.VMOtherConfig) != 0 {
|
||||
vm_other_config, err := c.GetClient().VM.GetOtherConfig(c.GetSessionRef(), instance)
|
||||
if err != nil {
|
||||
ui.Error(fmt.Sprintf("Error getting VM other-config: %s", err.Error()))
|
||||
return multistep.ActionHalt
|
||||
}
|
||||
for key, value := range config.VMOtherConfig {
|
||||
vm_other_config[key] = value
|
||||
}
|
||||
err = c.GetClient().VM.SetOtherConfig(c.GetSessionRef(), instance, vm_other_config)
|
||||
if err != nil {
|
||||
ui.Error(fmt.Sprintf("Error setting VM other-config: %s", err.Error()))
|
||||
return multistep.ActionHalt
|
||||
}
|
||||
}
|
||||
|
||||
if !self.AssumePreInstalledOS {
|
||||
err = c.GetClient().VM.RemoveFromOtherConfig(c.GetSessionRef(), instance, "disks")
|
||||
if err != nil {
|
||||
ui.Error(fmt.Sprintf("Error removing disks from VM other-config: %s", err.Error()))
|
||||
return multistep.ActionHalt
|
||||
}
|
||||
|
||||
// Create VDI for the instance
|
||||
sr, err := config.GetSR(c)
|
||||
|
||||
if err != nil {
|
||||
ui.Error(fmt.Sprintf("Unable to get SR: %s", err.Error()))
|
||||
return multistep.ActionHalt
|
||||
}
|
||||
|
||||
ui.Say(fmt.Sprintf("Using the following SR for the VM: %s", sr))
|
||||
|
||||
vdi, err := c.GetClient().VDI.Create(c.GetSessionRef(), xenapi.VDIRecord{
|
||||
NameLabel: config.DiskName,
|
||||
VirtualSize: int(config.DiskSize * 1024 * 1024),
|
||||
Type: "user",
|
||||
Sharable: false,
|
||||
ReadOnly: false,
|
||||
SR: sr,
|
||||
OtherConfig: map[string]string{
|
||||
"temp": "temp",
|
||||
},
|
||||
})
|
||||
if err != nil {
|
||||
ui.Error(fmt.Sprintf("Unable to create packer disk VDI: %s", err.Error()))
|
||||
return multistep.ActionHalt
|
||||
}
|
||||
self.vdi = &vdi
|
||||
|
||||
err = ConnectVdi(c, instance, vdi, xsclient.VbdTypeDisk)
|
||||
if err != nil {
|
||||
ui.Error(fmt.Sprintf("Unable to connect packer disk VDI: %s", err.Error()))
|
||||
return multistep.ActionHalt
|
||||
}
|
||||
}
|
||||
|
||||
// Connect Network
|
||||
|
||||
var network xsclient.NetworkRef
|
||||
|
||||
if len(config.NetworkNames) == 0 {
|
||||
// No network has be specified. Use the management interface
|
||||
log.Println("No network name given, attempting to use management interface")
|
||||
pifs, err := c.GetClient().PIF.GetAll(c.GetSessionRef())
|
||||
|
||||
if err != nil {
|
||||
ui.Error(fmt.Sprintf("Error getting PIFs: %s", err.Error()))
|
||||
return multistep.ActionHalt
|
||||
}
|
||||
|
||||
for _, pif := range pifs {
|
||||
pif_rec, err := c.GetClient().PIF.GetRecord(c.GetSessionRef(), pif)
|
||||
|
||||
if err != nil {
|
||||
ui.Error(fmt.Sprintf("Error getting PIF record: %s", err.Error()))
|
||||
return multistep.ActionHalt
|
||||
}
|
||||
|
||||
if pif_rec.Management {
|
||||
network = pif_rec.Network
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
if string(network) == "" {
|
||||
ui.Error("Error: couldn't find management network. Aborting.")
|
||||
return multistep.ActionHalt
|
||||
}
|
||||
|
||||
log.Printf("Creating VIF on network '%s' on VM '%s'\n", network, instance)
|
||||
_, err = ConnectNetwork(c, network, instance, "0")
|
||||
|
||||
if err != nil {
|
||||
ui.Error(fmt.Sprintf("Failed to create VIF with error: %v", err))
|
||||
return multistep.ActionHalt
|
||||
}
|
||||
|
||||
} else {
|
||||
log.Printf("Using provided network names: %v\n", config.NetworkNames)
|
||||
// Look up each network by it's name label
|
||||
for i, networkNameLabel := range config.NetworkNames {
|
||||
networks, err := c.GetClient().Network.GetByNameLabel(c.GetSessionRef(), networkNameLabel)
|
||||
|
||||
if err != nil {
|
||||
ui.Error(fmt.Sprintf("Error occured getting Network by name-label: %s", err.Error()))
|
||||
return multistep.ActionHalt
|
||||
}
|
||||
|
||||
switch {
|
||||
case len(networks) == 0:
|
||||
ui.Error(fmt.Sprintf("Couldn't find a network with the specified name-label '%s'. Aborting.", networkNameLabel))
|
||||
return multistep.ActionHalt
|
||||
case len(networks) > 1:
|
||||
ui.Error(fmt.Sprintf("Found more than one network with the name '%s'. The name must be unique. Aborting.", networkNameLabel))
|
||||
return multistep.ActionHalt
|
||||
}
|
||||
|
||||
//we need the VIF index string
|
||||
vifIndexString := fmt.Sprintf("%d", i)
|
||||
_, err = ConnectNetwork(c, networks[0], instance, vifIndexString)
|
||||
|
||||
if err != nil {
|
||||
ui.Say(fmt.Sprintf("Failed to connect VIF with error: %v", err.Error()))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
err = AddVMTags(c, instance, config.VMTags)
|
||||
if err != nil {
|
||||
ui.Error(fmt.Sprintf("Failed to add tags: %s", err.Error()))
|
||||
return multistep.ActionHalt
|
||||
}
|
||||
|
||||
instanceId, err := c.GetClient().VM.GetUUID(c.GetSessionRef(), instance)
|
||||
if err != nil {
|
||||
ui.Error(fmt.Sprintf("Unable to get VM UUID: %s", err.Error()))
|
||||
return multistep.ActionHalt
|
||||
}
|
||||
|
||||
state.Put("instance_uuid", instanceId)
|
||||
ui.Say(fmt.Sprintf("Created instance '%s'", instanceId))
|
||||
|
||||
return multistep.ActionContinue
|
||||
}
|
||||
|
||||
func (self *StepCreateInstance) Cleanup(state multistep.StateBag) {
|
||||
config := state.Get("config").(Config)
|
||||
if config.ShouldKeepVM(state) {
|
||||
return
|
||||
}
|
||||
|
||||
ui := state.Get("ui").(packer.Ui)
|
||||
c := state.Get("client").(*Connection)
|
||||
|
||||
if self.instance != nil {
|
||||
ui.Say("Destroying VM")
|
||||
_ = c.GetClient().VM.HardShutdown(c.GetSessionRef(), *self.instance) // redundant, just in case
|
||||
err := c.GetClient().VM.Destroy(c.GetSessionRef(), *self.instance)
|
||||
if err != nil {
|
||||
ui.Error(err.Error())
|
||||
}
|
||||
}
|
||||
|
||||
if self.vdi != nil {
|
||||
ui.Say("Destroying VDI")
|
||||
err := c.GetClient().VDI.Destroy(c.GetSessionRef(), *self.vdi)
|
||||
if err != nil {
|
||||
ui.Error(err.Error())
|
||||
}
|
||||
}
|
||||
}
|
53
builder/xenserver/common/step_detach_vdi.go
Normal file
53
builder/xenserver/common/step_detach_vdi.go
Normal file
@ -0,0 +1,53 @@
|
||||
package common
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"log"
|
||||
|
||||
"github.com/hashicorp/packer-plugin-sdk/multistep"
|
||||
"github.com/hashicorp/packer-plugin-sdk/packer"
|
||||
)
|
||||
|
||||
type StepDetachVdi struct {
|
||||
VdiUuidKey string
|
||||
}
|
||||
|
||||
func (self *StepDetachVdi) Run(ctx context.Context, state multistep.StateBag) multistep.StepAction {
|
||||
ui := state.Get("ui").(packer.Ui)
|
||||
c := state.Get("client").(*Connection)
|
||||
|
||||
var vdiUuid string
|
||||
if vdiUuidRaw, ok := state.GetOk(self.VdiUuidKey); ok {
|
||||
vdiUuid = vdiUuidRaw.(string)
|
||||
} else {
|
||||
log.Printf("Skipping detach of '%s'", self.VdiUuidKey)
|
||||
return multistep.ActionContinue
|
||||
}
|
||||
|
||||
vdi, err := c.client.VDI.GetByUUID(c.session, vdiUuid)
|
||||
if err != nil {
|
||||
ui.Error(fmt.Sprintf("Unable to get VDI from UUID '%s': %s", vdiUuid, err.Error()))
|
||||
return multistep.ActionHalt
|
||||
}
|
||||
|
||||
uuid := state.Get("instance_uuid").(string)
|
||||
instance, err := c.client.VM.GetByUUID(c.session, uuid)
|
||||
if err != nil {
|
||||
ui.Error(fmt.Sprintf("Unable to get VM from UUID '%s': %s", uuid, err.Error()))
|
||||
return multistep.ActionHalt
|
||||
}
|
||||
|
||||
err = DisconnectVdi(c, instance, vdi)
|
||||
if err != nil {
|
||||
ui.Error(fmt.Sprintf("Unable to detach VDI '%s': %s", vdiUuid, err.Error()))
|
||||
//return multistep.ActionHalt
|
||||
return multistep.ActionContinue
|
||||
}
|
||||
|
||||
log.Printf("Detached VDI '%s'", vdiUuid)
|
||||
|
||||
return multistep.ActionContinue
|
||||
}
|
||||
|
||||
func (self *StepDetachVdi) Cleanup(state multistep.StateBag) {}
|
278
builder/xenserver/common/step_export.go
Normal file
278
builder/xenserver/common/step_export.go
Normal file
@ -0,0 +1,278 @@
|
||||
package common
|
||||
|
||||
import (
|
||||
"context"
|
||||
"crypto/tls"
|
||||
"fmt"
|
||||
"io"
|
||||
"net/http"
|
||||
"os"
|
||||
"os/exec"
|
||||
|
||||
"github.com/hashicorp/packer-plugin-sdk/multistep"
|
||||
"github.com/hashicorp/packer-plugin-sdk/packer"
|
||||
)
|
||||
|
||||
type StepExport struct{}
|
||||
|
||||
func downloadFile(url, filename string, ui packer.Ui) (err error) {
|
||||
|
||||
// Create the file
|
||||
fh, err := os.Create(filename)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
defer fh.Close()
|
||||
|
||||
// Define a new transport which allows self-signed certs
|
||||
tr := &http.Transport{
|
||||
TLSClientConfig: &tls.Config{InsecureSkipVerify: true},
|
||||
}
|
||||
|
||||
// Create a client
|
||||
client := &http.Client{Transport: tr}
|
||||
|
||||
// Create request and download file
|
||||
|
||||
resp, err := client.Get(url)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
defer resp.Body.Close()
|
||||
|
||||
var progress uint
|
||||
var total uint
|
||||
var percentage uint
|
||||
var marker_len uint
|
||||
|
||||
progress = uint(0)
|
||||
total = uint(resp.ContentLength)
|
||||
percentage = uint(0)
|
||||
marker_len = uint(5)
|
||||
|
||||
var buffer [4096]byte
|
||||
for {
|
||||
n, err := resp.Body.Read(buffer[:])
|
||||
if err != nil && err != io.EOF {
|
||||
return err
|
||||
}
|
||||
|
||||
progress += uint(n)
|
||||
|
||||
if _, write_err := fh.Write(buffer[:n]); write_err != nil {
|
||||
return write_err
|
||||
}
|
||||
|
||||
if err == io.EOF {
|
||||
break
|
||||
}
|
||||
|
||||
// Increment percentage in multiples of marker_len
|
||||
cur_percentage := ((progress * 100 / total) / marker_len) * marker_len
|
||||
if cur_percentage > percentage {
|
||||
percentage = cur_percentage
|
||||
ui.Message(fmt.Sprintf("Downloading... %d%%", percentage))
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (StepExport) Run(ctx context.Context, state multistep.StateBag) multistep.StepAction {
|
||||
config := state.Get("commonconfig").(CommonConfig)
|
||||
ui := state.Get("ui").(packer.Ui)
|
||||
c := state.Get("client").(*Connection)
|
||||
instance_uuid := state.Get("instance_uuid").(string)
|
||||
suffix := ".vhd"
|
||||
extrauri := "&format=vhd"
|
||||
|
||||
instance, err := c.client.VM.GetByUUID(c.session, instance_uuid)
|
||||
if err != nil {
|
||||
ui.Error(fmt.Sprintf("Could not get VM with UUID '%s': %s", instance_uuid, err.Error()))
|
||||
return multistep.ActionHalt
|
||||
}
|
||||
|
||||
if len(config.ExportNetworkNames) > 0 {
|
||||
vifs, err := c.client.VM.GetVIFs(c.session, instance)
|
||||
if err != nil {
|
||||
ui.Error(fmt.Sprintf("Error occured getting VIFs: %s", err.Error()))
|
||||
return multistep.ActionHalt
|
||||
}
|
||||
|
||||
for _, vif := range vifs {
|
||||
err := c.client.VIF.Destroy(c.session, vif)
|
||||
if err != nil {
|
||||
ui.Error(fmt.Sprintf("Destroy vif fail: '%s': %s", vif, err.Error()))
|
||||
return multistep.ActionHalt
|
||||
}
|
||||
}
|
||||
for i, networkNameLabel := range config.ExportNetworkNames {
|
||||
networks, err := c.client.Network.GetByNameLabel(c.session, networkNameLabel)
|
||||
|
||||
if err != nil {
|
||||
ui.Error(fmt.Sprintf("Error occured getting Network by name-label: %s", err.Error()))
|
||||
return multistep.ActionHalt
|
||||
}
|
||||
|
||||
switch {
|
||||
case len(networks) == 0:
|
||||
ui.Error(fmt.Sprintf("Couldn't find a network with the specified name-label '%s'. Aborting.", networkNameLabel))
|
||||
return multistep.ActionHalt
|
||||
case len(networks) > 1:
|
||||
ui.Error(fmt.Sprintf("Found more than one network with the name '%s'. The name must be unique. Aborting.", networkNameLabel))
|
||||
return multistep.ActionHalt
|
||||
}
|
||||
|
||||
//we need the VIF index string
|
||||
vifIndexString := fmt.Sprintf("%d", i)
|
||||
_, err = ConnectNetwork(c, networks[0], instance, vifIndexString)
|
||||
|
||||
if err != nil {
|
||||
ui.Say(err.Error())
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
ui.Say("Step: export artifact")
|
||||
|
||||
compress_option_xe := "compress=false"
|
||||
compress_option_url := ""
|
||||
|
||||
switch config.Format {
|
||||
case "none":
|
||||
ui.Say("Skipping export")
|
||||
return multistep.ActionContinue
|
||||
|
||||
case "xva_compressed":
|
||||
compress_option_xe = "compress=true"
|
||||
compress_option_url = "use_compression=true&"
|
||||
fallthrough
|
||||
case "xva":
|
||||
// export the VM
|
||||
|
||||
export_filename := fmt.Sprintf("%s/%s.xva", config.OutputDir, config.VMName)
|
||||
|
||||
use_xe := os.Getenv("USE_XE") == "1"
|
||||
if xe, e := exec.LookPath("xe"); e == nil && use_xe {
|
||||
cmd := exec.Command(
|
||||
xe,
|
||||
"-s", c.Host,
|
||||
"-p", "443",
|
||||
"-u", c.Username,
|
||||
"-pw", c.Password,
|
||||
"vm-export",
|
||||
"vm="+instance_uuid,
|
||||
compress_option_xe,
|
||||
"filename="+export_filename,
|
||||
)
|
||||
|
||||
ui.Say(fmt.Sprintf("Getting XVA %+v %+v", cmd.Path, cmd.Args))
|
||||
|
||||
err = cmd.Run()
|
||||
} else {
|
||||
export_url := fmt.Sprintf("https://%s/export?%suuid=%s&session_id=%s",
|
||||
c.Host,
|
||||
compress_option_url,
|
||||
instance_uuid,
|
||||
c.GetSession(),
|
||||
)
|
||||
|
||||
ui.Say("Getting XVA " + export_url)
|
||||
err = downloadFile(export_url, export_filename, ui)
|
||||
}
|
||||
|
||||
if err != nil {
|
||||
ui.Error(fmt.Sprintf("Could not download XVA: %s", err.Error()))
|
||||
return multistep.ActionHalt
|
||||
}
|
||||
|
||||
case "vdi_raw":
|
||||
suffix = ".raw"
|
||||
extrauri = ""
|
||||
fallthrough
|
||||
case "vdi_vhd":
|
||||
// export the disks
|
||||
|
||||
disks, err := GetDisks(c, instance)
|
||||
if err != nil {
|
||||
ui.Error(fmt.Sprintf("Could not get VM disks: %s", err.Error()))
|
||||
return multistep.ActionHalt
|
||||
}
|
||||
for _, disk := range disks {
|
||||
disk_uuid, err := c.client.VDI.GetUUID(c.session, disk)
|
||||
if err != nil {
|
||||
ui.Error(fmt.Sprintf("Could not get disk with UUID '%s': %s", disk_uuid, err.Error()))
|
||||
return multistep.ActionHalt
|
||||
}
|
||||
|
||||
// Work out XenServer version
|
||||
hosts, err := c.client.Host.GetAll(c.session)
|
||||
|
||||
if err != nil {
|
||||
ui.Error(fmt.Sprintf("Could not retrieve hosts in the pool: %s", err.Error()))
|
||||
return multistep.ActionHalt
|
||||
}
|
||||
host := hosts[0]
|
||||
host_software_versions, err := c.client.Host.GetSoftwareVersion(c.session, host)
|
||||
xs_version := host_software_versions["product_version"]
|
||||
|
||||
if err != nil {
|
||||
ui.Error(fmt.Sprintf("Could not get the software version: %s", err.Error()))
|
||||
return multistep.ActionHalt
|
||||
}
|
||||
|
||||
var disk_export_url string
|
||||
|
||||
// @todo: check for 6.5 SP1
|
||||
if xs_version <= "6.5.0" && config.Format == "vdi_vhd" {
|
||||
// Export the VHD using a Transfer VM
|
||||
|
||||
disk_export_url, err = Expose(c, disk, "vhd")
|
||||
|
||||
if err != nil {
|
||||
ui.Error(fmt.Sprintf("Failed to expose disk %s: %s", disk_uuid, err.Error()))
|
||||
return multistep.ActionHalt
|
||||
}
|
||||
|
||||
} else {
|
||||
|
||||
// Use the preferred direct export from XAPI
|
||||
// Basic auth in URL request is required as session token is not
|
||||
// accepted for some reason.
|
||||
// @todo: raise with XAPI team.
|
||||
disk_export_url = fmt.Sprintf("https://%s:%s@%s/export_raw_vdi?vdi=%s%s",
|
||||
c.Username,
|
||||
c.Password,
|
||||
c.Host,
|
||||
disk_uuid,
|
||||
extrauri)
|
||||
|
||||
}
|
||||
|
||||
disk_export_filename := fmt.Sprintf("%s/%s%s", config.OutputDir, disk_uuid, suffix)
|
||||
|
||||
ui.Say("Getting VDI " + disk_export_url)
|
||||
err = downloadFile(disk_export_url, disk_export_filename, ui)
|
||||
if err != nil {
|
||||
ui.Error(fmt.Sprintf("Could not download VDI: %s", err.Error()))
|
||||
return multistep.ActionHalt
|
||||
}
|
||||
|
||||
// Call unexpose in case a TVM was used. The call is harmless
|
||||
// if that is not the case.
|
||||
Unexpose(c, disk)
|
||||
|
||||
}
|
||||
|
||||
default:
|
||||
panic(fmt.Sprintf("Unknown export format '%s'", config.Format))
|
||||
}
|
||||
|
||||
ui.Say("Download completed: " + config.OutputDir)
|
||||
|
||||
return multistep.ActionContinue
|
||||
}
|
||||
|
||||
func (StepExport) Cleanup(state multistep.StateBag) {}
|
44
builder/xenserver/common/step_find_or_upload_vdi.go
Normal file
44
builder/xenserver/common/step_find_or_upload_vdi.go
Normal file
@ -0,0 +1,44 @@
|
||||
package common
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
|
||||
"github.com/hashicorp/packer-plugin-sdk/multistep"
|
||||
"github.com/hashicorp/packer-plugin-sdk/packer"
|
||||
)
|
||||
|
||||
type StepFindOrUploadVdi struct {
|
||||
StepUploadVdi
|
||||
}
|
||||
|
||||
func (self *StepFindOrUploadVdi) Run(ctx context.Context, state multistep.StateBag) multistep.StepAction {
|
||||
ui := state.Get("ui").(packer.Ui)
|
||||
c := state.Get("client").(*Connection)
|
||||
vdiName := self.VdiNameFunc()
|
||||
|
||||
ui.Say(fmt.Sprintf("Attemping to find VDI '%s'", vdiName))
|
||||
|
||||
vdis, err := c.client.VDI.GetByNameLabel(c.session, vdiName)
|
||||
if err != nil {
|
||||
ui.Error(fmt.Sprintf("Failed to find VDI '%s' by name label: %s", vdiName, err.Error()))
|
||||
return multistep.ActionHalt
|
||||
}
|
||||
|
||||
if len(vdis) > 1 {
|
||||
ui.Error(fmt.Sprintf("Found more than one VDI with name '%s'. Name must be unique", vdiName))
|
||||
return multistep.ActionHalt
|
||||
} else if len(vdis) == 1 {
|
||||
|
||||
vdi := vdis[0]
|
||||
|
||||
vdiUuid, err := c.client.VDI.GetUUID(c.session, vdi)
|
||||
if err != nil {
|
||||
ui.Error(fmt.Sprintf("Unable to get UUID of VDI '%s': %s", vdiName, err.Error()))
|
||||
return multistep.ActionHalt
|
||||
}
|
||||
state.Put(self.VdiUuidKey, vdiUuid)
|
||||
return multistep.ActionContinue
|
||||
}
|
||||
return self.uploadVdi(ctx, state)
|
||||
}
|
49
builder/xenserver/common/step_find_vdi.go
Normal file
49
builder/xenserver/common/step_find_vdi.go
Normal file
@ -0,0 +1,49 @@
|
||||
package common
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
|
||||
"github.com/hashicorp/packer-plugin-sdk/multistep"
|
||||
"github.com/hashicorp/packer-plugin-sdk/packer"
|
||||
)
|
||||
|
||||
type StepFindVdi struct {
|
||||
VdiName string
|
||||
ImagePathFunc func() string
|
||||
VdiUuidKey string
|
||||
}
|
||||
|
||||
func (self *StepFindVdi) Run(ctx context.Context, state multistep.StateBag) multistep.StepAction {
|
||||
ui := state.Get("ui").(packer.Ui)
|
||||
c := state.Get("client").(*Connection)
|
||||
|
||||
// Ignore if VdiName is not specified
|
||||
if self.VdiName == "" {
|
||||
return multistep.ActionContinue
|
||||
}
|
||||
|
||||
vdis, err := c.client.VDI.GetByNameLabel(c.session, self.VdiName)
|
||||
|
||||
switch {
|
||||
case len(vdis) == 0:
|
||||
ui.Error(fmt.Sprintf("Couldn't find a VDI named '%s'", self.VdiName))
|
||||
return multistep.ActionHalt
|
||||
case len(vdis) > 1:
|
||||
ui.Error(fmt.Sprintf("Found more than one VDI with name '%s'. Name must be unique", self.VdiName))
|
||||
return multistep.ActionHalt
|
||||
}
|
||||
|
||||
vdi := vdis[0]
|
||||
|
||||
vdiUuid, err := c.client.VDI.GetUUID(c.session, vdi)
|
||||
if err != nil {
|
||||
ui.Error(fmt.Sprintf("Unable to get UUID of VDI '%s': %s", self.VdiName, err.Error()))
|
||||
return multistep.ActionHalt
|
||||
}
|
||||
state.Put(self.VdiUuidKey, vdiUuid)
|
||||
|
||||
return multistep.ActionContinue
|
||||
}
|
||||
|
||||
func (self *StepFindVdi) Cleanup(state multistep.StateBag) {}
|
51
builder/xenserver/common/step_forward_port_over_ssh.go
Normal file
51
builder/xenserver/common/step_forward_port_over_ssh.go
Normal file
@ -0,0 +1,51 @@
|
||||
package common
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
|
||||
"github.com/hashicorp/packer-plugin-sdk/multistep"
|
||||
"github.com/hashicorp/packer-plugin-sdk/packer"
|
||||
)
|
||||
|
||||
type StepForwardPortOverSSH struct {
|
||||
RemotePort func(state multistep.StateBag) (int, error)
|
||||
RemoteDest func(state multistep.StateBag) (string, error)
|
||||
|
||||
HostPortMin uint
|
||||
HostPortMax uint
|
||||
|
||||
ResultKey string
|
||||
}
|
||||
|
||||
func (self *StepForwardPortOverSSH) Run(ctx context.Context, state multistep.StateBag) multistep.StepAction {
|
||||
|
||||
config := state.Get("commonconfig").(CommonConfig)
|
||||
ui := state.Get("ui").(packer.Ui)
|
||||
|
||||
// Find a free local port:
|
||||
|
||||
l, sshHostPort := FindPort(self.HostPortMin, self.HostPortMax)
|
||||
|
||||
if l == nil || sshHostPort == 0 {
|
||||
ui.Error("Error: unable to find free host port. Try providing a larger range [host_port_min, host_port_max]")
|
||||
return multistep.ActionHalt
|
||||
}
|
||||
|
||||
ui.Say(fmt.Sprintf("Creating a local port forward over SSH on local port %d", sshHostPort))
|
||||
|
||||
hostAddress, _ := state.Get("ssh_address").(string)
|
||||
hostSshPort, _ := state.Get("ssh_port").(int)
|
||||
remotePort, _ := self.RemotePort(state)
|
||||
remoteDest, _ := self.RemoteDest(state)
|
||||
|
||||
go ssh_port_forward(l, remotePort, remoteDest, hostAddress, hostSshPort, config.Username, config.Password)
|
||||
ui.Say(fmt.Sprintf("Port forward setup. %d ---> %s:%d on %s", sshHostPort, remoteDest, remotePort, hostAddress))
|
||||
|
||||
// Provide the local port to future steps.
|
||||
state.Put(self.ResultKey, sshHostPort)
|
||||
|
||||
return multistep.ActionContinue
|
||||
}
|
||||
|
||||
func (self *StepForwardPortOverSSH) Cleanup(state multistep.StateBag) {}
|
51
builder/xenserver/common/step_get_vnc_port.go
Normal file
51
builder/xenserver/common/step_get_vnc_port.go
Normal file
@ -0,0 +1,51 @@
|
||||
package common
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"strconv"
|
||||
|
||||
"github.com/hashicorp/packer-plugin-sdk/multistep"
|
||||
"github.com/hashicorp/packer-plugin-sdk/packer"
|
||||
)
|
||||
|
||||
type StepGetVNCPort struct{}
|
||||
|
||||
func (self *StepGetVNCPort) Run(state multistep.StateBag) multistep.StepAction {
|
||||
ui := state.Get("ui").(packer.Ui)
|
||||
|
||||
ui.Say("Step: forward the instances VNC port over SSH")
|
||||
|
||||
domid := state.Get("domid").(int)
|
||||
cmd := fmt.Sprintf("xenstore-read /local/domain/%d/console/vnc-port", domid)
|
||||
|
||||
remote_vncport, err := ExecuteHostSSHCmd(state, cmd)
|
||||
if err != nil {
|
||||
ui.Error(fmt.Sprintf("Unable to get VNC port (is the VM running?): %s", err.Error()))
|
||||
return multistep.ActionHalt
|
||||
}
|
||||
|
||||
remote_port, err := strconv.ParseUint(remote_vncport, 10, 16)
|
||||
|
||||
if err != nil {
|
||||
ui.Error(fmt.Sprintf("Unable to convert '%s' to an int", remote_vncport))
|
||||
ui.Error(err.Error())
|
||||
return multistep.ActionHalt
|
||||
}
|
||||
|
||||
state.Put("instance_vnc_port", uint(remote_port))
|
||||
|
||||
return multistep.ActionContinue
|
||||
}
|
||||
|
||||
func (self *StepGetVNCPort) Cleanup(state multistep.StateBag) {
|
||||
}
|
||||
|
||||
func InstanceVNCPort(state multistep.StateBag) (uint, error) {
|
||||
vncPort := state.Get("instance_vnc_port").(uint)
|
||||
return vncPort, nil
|
||||
}
|
||||
|
||||
func InstanceVNCIP(state multistep.StateBag) (string, error) {
|
||||
// The port is in Dom0, so we want to forward from localhost
|
||||
return "127.0.0.1", nil
|
||||
}
|
96
builder/xenserver/common/step_http_server.go
Normal file
96
builder/xenserver/common/step_http_server.go
Normal file
@ -0,0 +1,96 @@
|
||||
package common
|
||||
|
||||
// Taken from hashicorp/packer/builder/qemu/step_http_server.go
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"log"
|
||||
"net"
|
||||
"net/http"
|
||||
|
||||
"github.com/hashicorp/packer-plugin-sdk/multistep"
|
||||
"github.com/hashicorp/packer-plugin-sdk/packer"
|
||||
)
|
||||
|
||||
// This step creates and runs the HTTP server that is serving files from the
|
||||
// directory specified by the 'http_directory` configuration parameter in the
|
||||
// template.
|
||||
//
|
||||
// Uses:
|
||||
// config *config
|
||||
// ui packer.Ui
|
||||
//
|
||||
// Produces:
|
||||
// http_port int - The port the HTTP server started on.
|
||||
type StepHTTPServer struct {
|
||||
Chan chan<- string
|
||||
|
||||
l net.Listener
|
||||
}
|
||||
|
||||
type IPSnooper struct {
|
||||
ch chan<- string
|
||||
handler http.Handler
|
||||
}
|
||||
|
||||
func (snooper IPSnooper) ServeHTTP(resp http.ResponseWriter, req *http.Request) {
|
||||
log.Printf("HTTP: %s %s %s", req.RemoteAddr, req.Method, req.URL)
|
||||
ip, _, err := net.SplitHostPort(req.RemoteAddr)
|
||||
if err == nil && ip != "" {
|
||||
select {
|
||||
case snooper.ch <- ip:
|
||||
log.Printf("Remembering remote address '%s'", ip)
|
||||
default:
|
||||
// if ch is already full, don't block waiting to send the address, just drop it
|
||||
}
|
||||
}
|
||||
snooper.handler.ServeHTTP(resp, req)
|
||||
}
|
||||
|
||||
func (s *StepHTTPServer) Run(ctx context.Context, state multistep.StateBag) multistep.StepAction {
|
||||
config := state.Get("commonconfig").(CommonConfig)
|
||||
ui := state.Get("ui").(packer.Ui)
|
||||
|
||||
var httpPort uint = 0
|
||||
if config.HTTPDir == "" {
|
||||
// the packer provision steps assert this type is an int
|
||||
// so this cannot be a uint like the rest of the code
|
||||
state.Put("http_port", int(httpPort))
|
||||
return multistep.ActionContinue
|
||||
}
|
||||
|
||||
s.l, httpPort = FindPort(config.HTTPPortMin, config.HTTPPortMax)
|
||||
|
||||
if s.l == nil || httpPort == 0 {
|
||||
ui.Error("Error: unable to find free HTTP server port. Try providing a larger range [http_port_min, http_port_max]")
|
||||
return multistep.ActionHalt
|
||||
}
|
||||
|
||||
ui.Say(fmt.Sprintf("Starting HTTP server on port %d", httpPort))
|
||||
|
||||
// Start the HTTP server and run it in the background
|
||||
fileServer := http.FileServer(http.Dir(config.HTTPDir))
|
||||
server := &http.Server{
|
||||
Addr: fmt.Sprintf(":%d", httpPort),
|
||||
Handler: IPSnooper{
|
||||
ch: s.Chan,
|
||||
handler: fileServer,
|
||||
},
|
||||
}
|
||||
go server.Serve(s.l)
|
||||
|
||||
// Save the address into the state so it can be accessed in the future
|
||||
// the packer provision steps assert this type is an int
|
||||
// so this cannot be a uint like the rest of the code
|
||||
state.Put("http_port", int(httpPort))
|
||||
|
||||
return multistep.ActionContinue
|
||||
}
|
||||
|
||||
func (s *StepHTTPServer) Cleanup(multistep.StateBag) {
|
||||
if s.l != nil {
|
||||
// Close the listener so that the HTTP server stops
|
||||
s.l.Close()
|
||||
}
|
||||
}
|
@ -1,27 +1,31 @@
|
||||
package xenserver
|
||||
package common
|
||||
|
||||
/* Taken from https://raw.githubusercontent.com/mitchellh/packer/master/builder/qemu/step_prepare_output_dir.go */
|
||||
/* Taken from https://raw.githubusercontent.com/hashicorp/packer/master/builder/qemu/step_prepare_output_dir.go */
|
||||
|
||||
import (
|
||||
"github.com/mitchellh/multistep"
|
||||
"github.com/mitchellh/packer/packer"
|
||||
"context"
|
||||
"log"
|
||||
"os"
|
||||
"time"
|
||||
|
||||
"github.com/hashicorp/packer-plugin-sdk/multistep"
|
||||
"github.com/hashicorp/packer-plugin-sdk/packer"
|
||||
)
|
||||
|
||||
type stepPrepareOutputDir struct{}
|
||||
type StepPrepareOutputDir struct {
|
||||
Force bool
|
||||
Path string
|
||||
}
|
||||
|
||||
func (stepPrepareOutputDir) Run(state multistep.StateBag) multistep.StepAction {
|
||||
config := state.Get("config").(config)
|
||||
func (self *StepPrepareOutputDir) Run(ctx context.Context, state multistep.StateBag) multistep.StepAction {
|
||||
ui := state.Get("ui").(packer.Ui)
|
||||
|
||||
if _, err := os.Stat(config.OutputDir); err == nil && config.PackerForce {
|
||||
if _, err := os.Stat(self.Path); err == nil && self.Force {
|
||||
ui.Say("Deleting previous output directory...")
|
||||
os.RemoveAll(config.OutputDir)
|
||||
os.RemoveAll(self.Path)
|
||||
}
|
||||
|
||||
if err := os.MkdirAll(config.OutputDir, 0755); err != nil {
|
||||
if err := os.MkdirAll(self.Path, 0755); err != nil {
|
||||
state.Put("error", err)
|
||||
return multistep.ActionHalt
|
||||
}
|
||||
@ -29,17 +33,16 @@ func (stepPrepareOutputDir) Run(state multistep.StateBag) multistep.StepAction {
|
||||
return multistep.ActionContinue
|
||||
}
|
||||
|
||||
func (stepPrepareOutputDir) Cleanup(state multistep.StateBag) {
|
||||
func (self *StepPrepareOutputDir) Cleanup(state multistep.StateBag) {
|
||||
_, cancelled := state.GetOk(multistep.StateCancelled)
|
||||
_, halted := state.GetOk(multistep.StateHalted)
|
||||
|
||||
if cancelled || halted {
|
||||
config := state.Get("config").(config)
|
||||
ui := state.Get("ui").(packer.Ui)
|
||||
|
||||
ui.Say("Deleting output directory...")
|
||||
for i := 0; i < 5; i++ {
|
||||
err := os.RemoveAll(config.OutputDir)
|
||||
err := os.RemoveAll(self.Path)
|
||||
if err == nil {
|
||||
break
|
||||
}
|
47
builder/xenserver/common/step_set_vm_host_ssh_address.go
Normal file
47
builder/xenserver/common/step_set_vm_host_ssh_address.go
Normal file
@ -0,0 +1,47 @@
|
||||
package common
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
|
||||
"github.com/hashicorp/packer-plugin-sdk/multistep"
|
||||
"github.com/hashicorp/packer-plugin-sdk/packer"
|
||||
)
|
||||
|
||||
type StepSetVmHostSshAddress struct{}
|
||||
|
||||
func (self *StepSetVmHostSshAddress) Run(ctx context.Context, state multistep.StateBag) multistep.StepAction {
|
||||
|
||||
c := state.Get("client").(*Connection)
|
||||
config := state.Get("config").(Config)
|
||||
ui := state.Get("ui").(packer.Ui)
|
||||
|
||||
ui.Say("Step: Set SSH address to VM host IP")
|
||||
|
||||
uuid := state.Get("instance_uuid").(string)
|
||||
instance, err := c.client.VM.GetByUUID(c.session, uuid)
|
||||
if err != nil {
|
||||
ui.Error(fmt.Sprintf("Unable to get VM from UUID '%s': %s", uuid, err.Error()))
|
||||
return multistep.ActionHalt
|
||||
}
|
||||
|
||||
host, err := c.client.VM.GetResidentOn(c.session, instance)
|
||||
if err != nil {
|
||||
ui.Error(fmt.Sprintf("Unable to get VM Host for VM '%s': %s", uuid, err.Error()))
|
||||
}
|
||||
|
||||
address, err := c.client.Host.GetAddress(c.session, host)
|
||||
if err != nil {
|
||||
ui.Error(fmt.Sprintf("Unable to get address from VM Host: %s", err.Error()))
|
||||
}
|
||||
|
||||
state.Put("ssh_address", address)
|
||||
ui.Say(fmt.Sprintf("Set host SSH address to '%s'.", address))
|
||||
|
||||
state.Put("ssh_port", config.HostSshPort)
|
||||
ui.Say(fmt.Sprintf("Set host SSH port to %d.", config.HostSshPort))
|
||||
|
||||
return multistep.ActionContinue
|
||||
}
|
||||
|
||||
func (self *StepSetVmHostSshAddress) Cleanup(state multistep.StateBag) {}
|
35
builder/xenserver/common/step_set_vm_to_template.go
Normal file
35
builder/xenserver/common/step_set_vm_to_template.go
Normal file
@ -0,0 +1,35 @@
|
||||
package common
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
|
||||
"github.com/hashicorp/packer-plugin-sdk/multistep"
|
||||
"github.com/hashicorp/packer-plugin-sdk/packer"
|
||||
)
|
||||
|
||||
type StepSetVmToTemplate struct{}
|
||||
|
||||
func (StepSetVmToTemplate) Run(ctx context.Context, state multistep.StateBag) multistep.StepAction {
|
||||
ui := state.Get("ui").(packer.Ui)
|
||||
c := state.Get("client").(*Connection)
|
||||
instance_uuid := state.Get("instance_uuid").(string)
|
||||
|
||||
instance, err := c.client.VM.GetByUUID(c.session, instance_uuid)
|
||||
if err != nil {
|
||||
ui.Error(fmt.Sprintf("Could not get VM with UUID '%s': %s", instance_uuid, err.Error()))
|
||||
return multistep.ActionHalt
|
||||
}
|
||||
|
||||
err = c.client.VM.SetIsATemplate(c.session, instance, true)
|
||||
|
||||
if err != nil {
|
||||
ui.Error(fmt.Sprintf("failed to set VM '%s' as a template with error: %v", instance_uuid, err))
|
||||
return multistep.ActionHalt
|
||||
}
|
||||
|
||||
ui.Message("Successfully set VM as a template")
|
||||
return multistep.ActionContinue
|
||||
}
|
||||
|
||||
func (StepSetVmToTemplate) Cleanup(state multistep.StateBag) {}
|
82
builder/xenserver/common/step_shutdown.go
Normal file
82
builder/xenserver/common/step_shutdown.go
Normal file
@ -0,0 +1,82 @@
|
||||
package common
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"time"
|
||||
|
||||
"github.com/hashicorp/packer-plugin-sdk/multistep"
|
||||
"github.com/hashicorp/packer-plugin-sdk/packer"
|
||||
xenapi "github.com/terra-farm/go-xen-api-client"
|
||||
)
|
||||
|
||||
type StepShutdown struct{}
|
||||
|
||||
func (StepShutdown) Run(ctx context.Context, state multistep.StateBag) multistep.StepAction {
|
||||
config := state.Get("commonconfig").(CommonConfig)
|
||||
ui := state.Get("ui").(packer.Ui)
|
||||
c := state.Get("client").(*Connection)
|
||||
instance_uuid := state.Get("instance_uuid").(string)
|
||||
|
||||
instance, err := c.client.VM.GetByUUID(c.session, instance_uuid)
|
||||
if err != nil {
|
||||
ui.Error(fmt.Sprintf("Could not get VM with UUID '%s': %s", instance_uuid, err.Error()))
|
||||
return multistep.ActionHalt
|
||||
}
|
||||
|
||||
ui.Say("Step: Shutting down VM")
|
||||
|
||||
// Shutdown the VM
|
||||
success := func() bool {
|
||||
if config.ShutdownCommand != "" {
|
||||
ui.Message("Executing shutdown command...")
|
||||
|
||||
_, err := ExecuteGuestSSHCmd(state, config.ShutdownCommand)
|
||||
if err != nil {
|
||||
ui.Error(fmt.Sprintf("Shutdown command failed: %s", err.Error()))
|
||||
return false
|
||||
}
|
||||
|
||||
ui.Message("Waiting for VM to enter Halted state...")
|
||||
|
||||
err = InterruptibleWait{
|
||||
Predicate: func() (bool, error) {
|
||||
power_state, err := c.client.VM.GetPowerState(c.session, instance)
|
||||
return power_state == xenapi.VMPowerStateHalted, err
|
||||
},
|
||||
PredicateInterval: 5 * time.Second,
|
||||
Timeout: 300 * time.Second,
|
||||
}.Wait(state)
|
||||
|
||||
if err != nil {
|
||||
ui.Error(fmt.Sprintf("Error waiting for VM to halt: %s", err.Error()))
|
||||
return false
|
||||
}
|
||||
|
||||
} else {
|
||||
ui.Message("Attempting to cleanly shutdown the VM...")
|
||||
|
||||
err = c.client.VM.CleanShutdown(c.session, instance)
|
||||
if err != nil {
|
||||
ui.Error(fmt.Sprintf("Could not shut down VM: %s", err.Error()))
|
||||
return false
|
||||
}
|
||||
|
||||
}
|
||||
return true
|
||||
}()
|
||||
|
||||
if !success {
|
||||
ui.Say("WARNING: Forcing hard shutdown of the VM...")
|
||||
err = c.client.VM.HardShutdown(c.session, instance)
|
||||
if err != nil {
|
||||
ui.Error(fmt.Sprintf("Could not hard shut down VM -- giving up: %s", err.Error()))
|
||||
return multistep.ActionHalt
|
||||
}
|
||||
}
|
||||
|
||||
ui.Message("Successfully shut down VM")
|
||||
return multistep.ActionContinue
|
||||
}
|
||||
|
||||
func (StepShutdown) Cleanup(state multistep.StateBag) {}
|
137
builder/xenserver/common/step_start_on_himn.go
Normal file
137
builder/xenserver/common/step_start_on_himn.go
Normal file
@ -0,0 +1,137 @@
|
||||
package common
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"log"
|
||||
"time"
|
||||
|
||||
"github.com/hashicorp/packer-plugin-sdk/multistep"
|
||||
"github.com/hashicorp/packer-plugin-sdk/packer"
|
||||
gossh "golang.org/x/crypto/ssh"
|
||||
)
|
||||
|
||||
type StepStartOnHIMN struct{}
|
||||
|
||||
/*
|
||||
* This step starts the installed guest on the Host Internal Management Network
|
||||
* as there exists an API to obtain the IP allocated to the VM by XAPI.
|
||||
* This in turn will allow Packer to SSH into the VM, provided NATing has been
|
||||
* enabled on the host.
|
||||
*
|
||||
*/
|
||||
|
||||
func (self *StepStartOnHIMN) Run(state multistep.StateBag) multistep.StepAction {
|
||||
|
||||
ui := state.Get("ui").(packer.Ui)
|
||||
c := state.Get("client").(*Connection)
|
||||
|
||||
ui.Say("Step: Start VM on the Host Internal Mangement Network")
|
||||
|
||||
uuid := state.Get("instance_uuid").(string)
|
||||
instance, err := c.client.VM.GetByUUID(c.session, uuid)
|
||||
if err != nil {
|
||||
ui.Error(fmt.Sprintf("Unable to get VM from UUID '%s': %s", uuid, err.Error()))
|
||||
return multistep.ActionHalt
|
||||
}
|
||||
|
||||
// Find the HIMN Ref
|
||||
networks, err := c.client.Network.GetByNameLabel(c.session, "Host internal management network")
|
||||
if err != nil || len(networks) == 0 {
|
||||
ui.Error("Unable to find a host internal management network")
|
||||
ui.Error(err.Error())
|
||||
return multistep.ActionHalt
|
||||
}
|
||||
|
||||
himn := networks[0]
|
||||
|
||||
// Create a VIF for the HIMN
|
||||
himn_vif, err := ConnectNetwork(c, himn, instance, "0")
|
||||
if err != nil {
|
||||
ui.Error("Error creating VIF")
|
||||
ui.Error(err.Error())
|
||||
return multistep.ActionHalt
|
||||
}
|
||||
|
||||
// Start the VM
|
||||
c.client.VM.Start(c.session, instance, false, false)
|
||||
|
||||
var himn_iface_ip string = ""
|
||||
|
||||
// Obtain the allocated IP
|
||||
err = InterruptibleWait{
|
||||
Predicate: func() (found bool, err error) {
|
||||
ips, err := c.client.Network.GetAssignedIps(c.session, himn)
|
||||
if err != nil {
|
||||
return false, fmt.Errorf("Can't get assigned IPs: %s", err.Error())
|
||||
}
|
||||
log.Printf("IPs: %s", ips)
|
||||
log.Printf("Ref: %s", instance)
|
||||
|
||||
//Check for instance.Ref in map
|
||||
if vm_ip, ok := ips[*himn_vif]; ok && vm_ip != "" {
|
||||
ui.Say("Found the VM's IP: " + vm_ip)
|
||||
himn_iface_ip = vm_ip
|
||||
return true, nil
|
||||
}
|
||||
|
||||
ui.Say("Wait for IP address...")
|
||||
return false, nil
|
||||
},
|
||||
PredicateInterval: 10 * time.Second,
|
||||
Timeout: 100 * time.Second,
|
||||
}.Wait(state)
|
||||
|
||||
if err != nil {
|
||||
ui.Error(fmt.Sprintf("Unable to find an IP on the Host-internal management interface: %s", err.Error()))
|
||||
return multistep.ActionHalt
|
||||
}
|
||||
|
||||
state.Put("himn_ssh_address", himn_iface_ip)
|
||||
ui.Say("Stored VM's IP " + himn_iface_ip)
|
||||
|
||||
// Wait for the VM to boot, and check we can ping this interface
|
||||
|
||||
ping_cmd := fmt.Sprintf("ping -c 1 %s", himn_iface_ip)
|
||||
|
||||
err = InterruptibleWait{
|
||||
Predicate: func() (success bool, err error) {
|
||||
ui.Message(fmt.Sprintf("Attempting to ping interface: %s", ping_cmd))
|
||||
_, err = ExecuteHostSSHCmd(state, ping_cmd)
|
||||
|
||||
switch err.(type) {
|
||||
case nil:
|
||||
// ping succeeded
|
||||
return true, nil
|
||||
case *gossh.ExitError:
|
||||
// ping failed, try again
|
||||
return false, nil
|
||||
default:
|
||||
// unknown error
|
||||
return false, err
|
||||
}
|
||||
},
|
||||
PredicateInterval: 10 * time.Second,
|
||||
Timeout: 300 * time.Second,
|
||||
}.Wait(state)
|
||||
|
||||
if err != nil {
|
||||
ui.Error(fmt.Sprintf("Unable to ping interface. (Has the VM not booted?): %s", err.Error()))
|
||||
return multistep.ActionHalt
|
||||
}
|
||||
|
||||
ui.Message("Ping success! Continuing...")
|
||||
return multistep.ActionContinue
|
||||
|
||||
}
|
||||
|
||||
func (self *StepStartOnHIMN) Cleanup(state multistep.StateBag) {}
|
||||
|
||||
func HimnSSHIP(state multistep.StateBag) (string, error) {
|
||||
ip := state.Get("himn_ssh_address").(string)
|
||||
return ip, nil
|
||||
}
|
||||
|
||||
func HimnSSHPort(state multistep.StateBag) (uint, error) {
|
||||
config := state.Get("commonconfig").(CommonConfig)
|
||||
return config.SSHPort, nil
|
||||
}
|
58
builder/xenserver/common/step_start_vm_paused.go
Normal file
58
builder/xenserver/common/step_start_vm_paused.go
Normal file
@ -0,0 +1,58 @@
|
||||
package common
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
|
||||
"github.com/hashicorp/packer-plugin-sdk/multistep"
|
||||
"github.com/hashicorp/packer-plugin-sdk/packer"
|
||||
)
|
||||
|
||||
type StepStartVmPaused struct {
|
||||
VmCleanup
|
||||
}
|
||||
|
||||
func (self *StepStartVmPaused) Run(ctx context.Context, state multistep.StateBag) multistep.StepAction {
|
||||
|
||||
c := state.Get("client").(*Connection)
|
||||
ui := state.Get("ui").(packer.Ui)
|
||||
config := state.Get("config").(Config)
|
||||
|
||||
ui.Say("Step: Start VM Paused")
|
||||
|
||||
uuid := state.Get("instance_uuid").(string)
|
||||
instance, err := c.client.VM.GetByUUID(c.session, uuid)
|
||||
if err != nil {
|
||||
ui.Error(fmt.Sprintf("Unable to get VM from UUID '%s': %s", uuid, err.Error()))
|
||||
return multistep.ActionHalt
|
||||
}
|
||||
|
||||
// note that here "cd" means boot from hard drive ('c') first, then CDROM ('d')
|
||||
err = c.client.VM.SetHVMBootPolicy(c.session, instance, "BIOS order")
|
||||
|
||||
if err != nil {
|
||||
ui.Error(fmt.Sprintf("Unable to set HVM boot params: %s", err.Error()))
|
||||
return multistep.ActionHalt
|
||||
}
|
||||
|
||||
err = c.client.VM.SetHVMBootParams(c.session, instance, map[string]string{"order": "cd", "firmware": config.Firmware})
|
||||
if err != nil {
|
||||
ui.Error(fmt.Sprintf("Unable to set HVM boot params: %s", err.Error()))
|
||||
return multistep.ActionHalt
|
||||
}
|
||||
|
||||
err = c.client.VM.Start(c.session, instance, true, false)
|
||||
if err != nil {
|
||||
ui.Error(fmt.Sprintf("Unable to start VM with UUID '%s': %s", uuid, err.Error()))
|
||||
return multistep.ActionHalt
|
||||
}
|
||||
|
||||
domid, err := c.client.VM.GetDomid(c.session, instance)
|
||||
if err != nil {
|
||||
ui.Error(fmt.Sprintf("Unable to get domid of VM with UUID '%s': %s", uuid, err.Error()))
|
||||
return multistep.ActionHalt
|
||||
}
|
||||
state.Put("domid", domid)
|
||||
|
||||
return multistep.ActionContinue
|
||||
}
|
271
builder/xenserver/common/step_type_boot_command.go
Normal file
271
builder/xenserver/common/step_type_boot_command.go
Normal file
@ -0,0 +1,271 @@
|
||||
package common
|
||||
|
||||
/* Heavily borrowed from builder/quemu/step_type_boot_command.go */
|
||||
|
||||
import (
|
||||
"context"
|
||||
"crypto/tls"
|
||||
"fmt"
|
||||
"io"
|
||||
"log"
|
||||
"net"
|
||||
"strings"
|
||||
"time"
|
||||
"unicode"
|
||||
"unicode/utf8"
|
||||
|
||||
"github.com/hashicorp/packer-plugin-sdk/multistep"
|
||||
"github.com/hashicorp/packer-plugin-sdk/packer"
|
||||
"github.com/hashicorp/packer-plugin-sdk/template/interpolate"
|
||||
"github.com/mitchellh/go-vnc"
|
||||
)
|
||||
|
||||
const KeyLeftShift uint = 0xFFE1
|
||||
|
||||
type bootCommandTemplateData struct {
|
||||
Name string
|
||||
HTTPIP string
|
||||
HTTPPort uint
|
||||
}
|
||||
|
||||
type StepTypeBootCommand struct {
|
||||
Ctx interpolate.Context
|
||||
}
|
||||
|
||||
func (step *StepTypeBootCommand) Run(ctx context.Context, state multistep.StateBag) multistep.StepAction {
|
||||
config := state.Get("config").(Config)
|
||||
ui := state.Get("ui").(packer.Ui)
|
||||
c := state.Get("client").(*Connection)
|
||||
httpPort := state.Get("http_port").(int)
|
||||
|
||||
// skip this step if we have nothing to type
|
||||
if len(config.BootCommand) == 0 {
|
||||
return multistep.ActionContinue
|
||||
}
|
||||
|
||||
vmRef, err := c.client.VM.GetByNameLabel(c.session, config.VMName)
|
||||
|
||||
if err != nil {
|
||||
state.Put("error", err)
|
||||
ui.Error(err.Error())
|
||||
return multistep.ActionHalt
|
||||
}
|
||||
|
||||
if len(vmRef) != 1 {
|
||||
ui.Error(fmt.Sprintf("expected to find a single VM, instead found '%d'. Ensure the VM name is unique", len(vmRef)))
|
||||
}
|
||||
|
||||
consoles, err := c.client.VM.GetConsoles(c.session, vmRef[0])
|
||||
if err != nil {
|
||||
state.Put("error", err)
|
||||
ui.Error(err.Error())
|
||||
return multistep.ActionHalt
|
||||
}
|
||||
|
||||
if len(consoles) != 1 {
|
||||
ui.Error(fmt.Sprintf("expected to find a VM console, instead found '%d'. Ensure there is only one console", len(consoles)))
|
||||
return multistep.ActionHalt
|
||||
}
|
||||
|
||||
location, err := c.client.Console.GetLocation(c.session, consoles[0])
|
||||
if err != nil {
|
||||
ui.Error(err.Error())
|
||||
return multistep.ActionHalt
|
||||
}
|
||||
locationPieces := strings.SplitAfter(location, "/")
|
||||
consoleHost := strings.TrimSuffix(locationPieces[2], "/")
|
||||
ui.Say("Connecting to VNC over XAPI...")
|
||||
log.Printf("Connecting to host: %s", consoleHost)
|
||||
conn, err := net.Dial("tcp", fmt.Sprintf("%s:443", consoleHost))
|
||||
|
||||
if err != nil {
|
||||
err := fmt.Errorf("Error connecting to VNC: %s", err)
|
||||
state.Put("error", err)
|
||||
ui.Error(err.Error())
|
||||
return multistep.ActionHalt
|
||||
}
|
||||
|
||||
defer conn.Close()
|
||||
|
||||
tlsConfig := &tls.Config{
|
||||
InsecureSkipVerify: true,
|
||||
}
|
||||
tlsConn := tls.Client(conn, tlsConfig)
|
||||
|
||||
consoleLocation := strings.TrimSpace(fmt.Sprintf("/%s", locationPieces[len(locationPieces)-1]))
|
||||
httpReq := fmt.Sprintf("CONNECT %s HTTP/1.0\r\nHost: %s\r\nCookie: session_id=%s\r\n\r\n", consoleLocation, consoleHost, c.session)
|
||||
fmt.Printf("Sending the follow http req: %v", httpReq)
|
||||
|
||||
ui.Message(fmt.Sprintf("Making HTTP request to initiate VNC connection: %s", httpReq))
|
||||
_, err = io.WriteString(tlsConn, httpReq)
|
||||
|
||||
if err != nil {
|
||||
err := fmt.Errorf("failed to start vnc session: %v", err)
|
||||
state.Put("error", err)
|
||||
ui.Error(err.Error())
|
||||
return multistep.ActionHalt
|
||||
}
|
||||
|
||||
buffer := make([]byte, 10000)
|
||||
_, err = tlsConn.Read(buffer)
|
||||
if err != nil && err != io.EOF {
|
||||
err := fmt.Errorf("failed to read vnc session response: %v", err)
|
||||
state.Put("error", err)
|
||||
ui.Error(err.Error())
|
||||
return multistep.ActionHalt
|
||||
}
|
||||
|
||||
ui.Message(fmt.Sprintf("Received response: %s", string(buffer)))
|
||||
|
||||
vncClient, err := vnc.Client(tlsConn, &vnc.ClientConfig{Exclusive: !config.PackerDebug})
|
||||
|
||||
if err != nil {
|
||||
err := fmt.Errorf("Error establishing VNC session: %s", err)
|
||||
state.Put("error", err)
|
||||
ui.Error(err.Error())
|
||||
return multistep.ActionHalt
|
||||
}
|
||||
|
||||
defer vncClient.Close()
|
||||
|
||||
log.Printf("Connected to the VNC console: %s", vncClient.DesktopName)
|
||||
|
||||
// find local ip
|
||||
envVar, err := ExecuteHostSSHCmd(state, "echo $SSH_CLIENT")
|
||||
if err != nil {
|
||||
ui.Error(fmt.Sprintf("Error detecting local IP: %s", err))
|
||||
return multistep.ActionHalt
|
||||
}
|
||||
if envVar == "" {
|
||||
ui.Error("Error detecting local IP: $SSH_CLIENT was empty")
|
||||
return multistep.ActionHalt
|
||||
}
|
||||
localIp := strings.Split(envVar, " ")[0]
|
||||
ui.Message(fmt.Sprintf("Found local IP: %s", localIp))
|
||||
|
||||
step.Ctx.Data = &bootCommandTemplateData{
|
||||
config.VMName,
|
||||
localIp,
|
||||
uint(httpPort),
|
||||
}
|
||||
|
||||
ui.Say("Typing boot commands over VNC...")
|
||||
for _, command := range config.BootCommand {
|
||||
|
||||
command, err := interpolate.Render(command, &step.Ctx)
|
||||
if err != nil {
|
||||
err := fmt.Errorf("Error preparing boot command: %s", err)
|
||||
state.Put("error", err)
|
||||
ui.Error(err.Error())
|
||||
return multistep.ActionHalt
|
||||
}
|
||||
|
||||
// Check for interrupts
|
||||
if _, ok := state.GetOk(multistep.StateCancelled); ok {
|
||||
return multistep.ActionHalt
|
||||
}
|
||||
|
||||
vncSendString(vncClient, command)
|
||||
}
|
||||
|
||||
return multistep.ActionContinue
|
||||
}
|
||||
|
||||
func (step *StepTypeBootCommand) Cleanup(multistep.StateBag) {}
|
||||
|
||||
// Taken from qemu's builder plugin - not an exported function.
|
||||
func vncSendString(c *vnc.ClientConn, original string) {
|
||||
// Scancodes reference: https://github.com/qemu/qemu/blob/master/ui/vnc_keysym.h
|
||||
special := make(map[string]uint32)
|
||||
special["<bs>"] = 0xFF08
|
||||
special["<del>"] = 0xFFFF
|
||||
special["<enter>"] = 0xFF0D
|
||||
special["<esc>"] = 0xFF1B
|
||||
special["<f1>"] = 0xFFBE
|
||||
special["<f2>"] = 0xFFBF
|
||||
special["<f3>"] = 0xFFC0
|
||||
special["<f4>"] = 0xFFC1
|
||||
special["<f5>"] = 0xFFC2
|
||||
special["<f6>"] = 0xFFC3
|
||||
special["<f7>"] = 0xFFC4
|
||||
special["<f8>"] = 0xFFC5
|
||||
special["<f9>"] = 0xFFC6
|
||||
special["<f10>"] = 0xFFC7
|
||||
special["<f11>"] = 0xFFC8
|
||||
special["<f12>"] = 0xFFC9
|
||||
special["<return>"] = 0xFF0D
|
||||
special["<tab>"] = 0xFF09
|
||||
special["<up>"] = 0xFF52
|
||||
special["<down>"] = 0xFF54
|
||||
special["<left>"] = 0xFF51
|
||||
special["<right>"] = 0xFF53
|
||||
special["<spacebar>"] = 0x020
|
||||
special["<insert>"] = 0xFF63
|
||||
special["<home>"] = 0xFF50
|
||||
special["<end>"] = 0xFF57
|
||||
special["<pageUp>"] = 0xFF55
|
||||
special["<pageDown>"] = 0xFF56
|
||||
|
||||
shiftedChars := "~!@#$%^&*()_+{}|:\"<>?"
|
||||
|
||||
// TODO(mitchellh): Ripe for optimizations of some point, perhaps.
|
||||
for len(original) > 0 {
|
||||
var keyCode uint32
|
||||
keyShift := false
|
||||
|
||||
if strings.HasPrefix(original, "<wait>") {
|
||||
log.Printf("Special code '<wait>' found, sleeping one second")
|
||||
time.Sleep(1 * time.Second)
|
||||
original = original[len("<wait>"):]
|
||||
continue
|
||||
}
|
||||
|
||||
if strings.HasPrefix(original, "<wait5>") {
|
||||
log.Printf("Special code '<wait5>' found, sleeping 5 seconds")
|
||||
time.Sleep(5 * time.Second)
|
||||
original = original[len("<wait5>"):]
|
||||
continue
|
||||
}
|
||||
|
||||
if strings.HasPrefix(original, "<wait10>") {
|
||||
log.Printf("Special code '<wait10>' found, sleeping 10 seconds")
|
||||
time.Sleep(10 * time.Second)
|
||||
original = original[len("<wait10>"):]
|
||||
continue
|
||||
}
|
||||
|
||||
for specialCode, specialValue := range special {
|
||||
if strings.HasPrefix(original, specialCode) {
|
||||
log.Printf("Special code '%s' found, replacing with: %d", specialCode, specialValue)
|
||||
keyCode = specialValue
|
||||
original = original[len(specialCode):]
|
||||
break
|
||||
}
|
||||
}
|
||||
|
||||
if keyCode == 0 {
|
||||
r, size := utf8.DecodeRuneInString(original)
|
||||
original = original[size:]
|
||||
keyCode = uint32(r)
|
||||
keyShift = unicode.IsUpper(r) || strings.ContainsRune(shiftedChars, r)
|
||||
|
||||
log.Printf("Sending char '%c', code %d, shift %v", r, keyCode, keyShift)
|
||||
}
|
||||
|
||||
if keyShift {
|
||||
c.KeyEvent(uint32(KeyLeftShift), true)
|
||||
}
|
||||
|
||||
c.KeyEvent(keyCode, true)
|
||||
time.Sleep(time.Second / 10)
|
||||
c.KeyEvent(keyCode, false)
|
||||
time.Sleep(time.Second / 10)
|
||||
|
||||
if keyShift {
|
||||
c.KeyEvent(uint32(KeyLeftShift), false)
|
||||
}
|
||||
|
||||
// no matter what, wait a small period
|
||||
time.Sleep(50 * time.Millisecond)
|
||||
}
|
||||
}
|
147
builder/xenserver/common/step_upload_vdi.go
Normal file
147
builder/xenserver/common/step_upload_vdi.go
Normal file
@ -0,0 +1,147 @@
|
||||
package common
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"log"
|
||||
"os"
|
||||
"time"
|
||||
|
||||
"github.com/hashicorp/packer-plugin-sdk/multistep"
|
||||
"github.com/hashicorp/packer-plugin-sdk/packer"
|
||||
xenapi "github.com/terra-farm/go-xen-api-client"
|
||||
)
|
||||
|
||||
type StepUploadVdi struct {
|
||||
VdiNameFunc func() string
|
||||
ImagePathFunc func() string
|
||||
VdiUuidKey string
|
||||
}
|
||||
|
||||
func (self *StepUploadVdi) uploadVdi(ctx context.Context, state multistep.StateBag) multistep.StepAction {
|
||||
config := state.Get("commonconfig").(CommonConfig)
|
||||
ui := state.Get("ui").(packer.Ui)
|
||||
c := state.Get("client").(*Connection)
|
||||
|
||||
imagePath := self.ImagePathFunc()
|
||||
vdiName := self.VdiNameFunc()
|
||||
if imagePath == "" {
|
||||
// skip if no disk image to attach
|
||||
return multistep.ActionContinue
|
||||
}
|
||||
|
||||
ui.Say(fmt.Sprintf("Step: Upload VDI '%s'", vdiName))
|
||||
|
||||
// Create VDI for the image
|
||||
sr, err := config.GetISOSR(c)
|
||||
ui.Say(fmt.Sprintf("Step: Found SR for upload '%v'", sr))
|
||||
|
||||
if err != nil {
|
||||
ui.Error(fmt.Sprintf("Unable to get SR: %v", err))
|
||||
return multistep.ActionHalt
|
||||
}
|
||||
|
||||
// Open the file for reading (NB: HTTPUpload closes the file for us)
|
||||
fh, err := os.Open(imagePath)
|
||||
if err != nil {
|
||||
ui.Error(fmt.Sprintf("Unable to open disk image '%s': %s", imagePath, err.Error()))
|
||||
return multistep.ActionHalt
|
||||
}
|
||||
|
||||
// Get file length
|
||||
fstat, err := fh.Stat()
|
||||
if err != nil {
|
||||
ui.Error(fmt.Sprintf("Unable to stat disk image '%s': %s", imagePath, err.Error()))
|
||||
return multistep.ActionHalt
|
||||
}
|
||||
fileLength := fstat.Size()
|
||||
|
||||
// Create the VDI
|
||||
// vdi, err := sr.CreateVdi(vdiName, fileLength)
|
||||
vdi, err := c.client.VDI.Create(c.session, xenapi.VDIRecord{
|
||||
NameLabel: vdiName,
|
||||
VirtualSize: int(fileLength),
|
||||
Type: "user",
|
||||
Sharable: false,
|
||||
ReadOnly: false,
|
||||
SR: sr,
|
||||
OtherConfig: map[string]string{
|
||||
"temp": "temp",
|
||||
},
|
||||
})
|
||||
if err != nil {
|
||||
ui.Error(fmt.Sprintf("Unable to create VDI '%s': %s", vdiName, err.Error()))
|
||||
return multistep.ActionHalt
|
||||
}
|
||||
|
||||
vdiUuid, err := c.client.VDI.GetUUID(c.session, vdi)
|
||||
if err != nil {
|
||||
ui.Error(fmt.Sprintf("Unable to get UUID of VDI '%s': %s", vdiName, err.Error()))
|
||||
return multistep.ActionHalt
|
||||
}
|
||||
state.Put(self.VdiUuidKey, vdiUuid)
|
||||
|
||||
_, err = HTTPUpload(fmt.Sprintf("https://%s/import_raw_vdi?vdi=%s&session_id=%s",
|
||||
c.Host,
|
||||
vdi,
|
||||
c.GetSession(),
|
||||
), fh, state)
|
||||
if err != nil {
|
||||
ui.Error(fmt.Sprintf("Unable to upload VDI: %s", err.Error()))
|
||||
return multistep.ActionHalt
|
||||
}
|
||||
|
||||
return multistep.ActionContinue
|
||||
}
|
||||
|
||||
func (self *StepUploadVdi) Run(ctx context.Context, state multistep.StateBag) multistep.StepAction {
|
||||
return self.uploadVdi(ctx, state)
|
||||
}
|
||||
|
||||
func (self *StepUploadVdi) Cleanup(state multistep.StateBag) {
|
||||
config := state.Get("commonconfig").(CommonConfig)
|
||||
ui := state.Get("ui").(packer.Ui)
|
||||
c := state.Get("client").(*Connection)
|
||||
|
||||
vdiName := self.VdiNameFunc()
|
||||
|
||||
if config.ShouldKeepVM(state) {
|
||||
return
|
||||
}
|
||||
|
||||
vdiUuidRaw, ok := state.GetOk(self.VdiUuidKey)
|
||||
if !ok {
|
||||
// VDI doesn't exist
|
||||
return
|
||||
}
|
||||
|
||||
vdiUuid := vdiUuidRaw.(string)
|
||||
if vdiUuid == "" {
|
||||
// VDI already cleaned up
|
||||
return
|
||||
}
|
||||
|
||||
vdi, err := c.client.VDI.GetByUUID(c.session, vdiUuid)
|
||||
if err != nil {
|
||||
ui.Error(fmt.Sprintf("Can't get VDI '%s': %s", vdiUuid, err.Error()))
|
||||
return
|
||||
}
|
||||
|
||||
// an interrupted import_raw_vdi takes a while to release the VDI
|
||||
// so try several times
|
||||
for i := 0; i < 3; i++ {
|
||||
log.Printf("Trying to destroy VDI...")
|
||||
err = c.client.VDI.Destroy(c.session, vdi)
|
||||
if err == nil {
|
||||
break
|
||||
}
|
||||
time.Sleep(1 * time.Second)
|
||||
}
|
||||
if err != nil {
|
||||
ui.Error(fmt.Sprintf("Can't destroy VDI '%s': %s", vdiUuid, err.Error()))
|
||||
return
|
||||
}
|
||||
ui.Say(fmt.Sprintf("Destroyed VDI '%s'", vdiName))
|
||||
|
||||
state.Put(self.VdiUuidKey, "")
|
||||
}
|
96
builder/xenserver/common/step_wait_for_ip.go
Normal file
96
builder/xenserver/common/step_wait_for_ip.go
Normal file
@ -0,0 +1,96 @@
|
||||
package common
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"time"
|
||||
|
||||
"github.com/hashicorp/packer-plugin-sdk/multistep"
|
||||
"github.com/hashicorp/packer-plugin-sdk/packer"
|
||||
)
|
||||
|
||||
type StepWaitForIP struct {
|
||||
VmCleanup
|
||||
Chan <-chan string
|
||||
Timeout time.Duration
|
||||
}
|
||||
|
||||
func (self *StepWaitForIP) Run(ctx context.Context, state multistep.StateBag) multistep.StepAction {
|
||||
ui := state.Get("ui").(packer.Ui)
|
||||
c := state.Get("client").(*Connection)
|
||||
config := state.Get("commonconfig").(CommonConfig)
|
||||
|
||||
ui.Say("Step: Wait for VM's IP to become known to us.")
|
||||
|
||||
uuid := state.Get("instance_uuid").(string)
|
||||
instance, err := c.client.VM.GetByUUID(c.session, uuid)
|
||||
if err != nil {
|
||||
ui.Error(fmt.Sprintf("Unable to get VM from UUID '%s': %s", uuid, err.Error()))
|
||||
return multistep.ActionHalt
|
||||
}
|
||||
|
||||
var ip string
|
||||
err = InterruptibleWait{
|
||||
Timeout: self.Timeout,
|
||||
PredicateInterval: 5 * time.Second,
|
||||
Predicate: func() (result bool, err error) {
|
||||
|
||||
if config.IPGetter == "auto" || config.IPGetter == "http" {
|
||||
|
||||
// Snoop IP from HTTP fetch
|
||||
select {
|
||||
case ip = <-self.Chan:
|
||||
ui.Message(fmt.Sprintf("Got IP '%s' from HTTP request", ip))
|
||||
return true, nil
|
||||
default:
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
if config.IPGetter == "auto" || config.IPGetter == "tools" {
|
||||
|
||||
// Look for PV IP
|
||||
m, err := c.client.VM.GetGuestMetrics(c.session, instance)
|
||||
if err != nil {
|
||||
return false, err
|
||||
}
|
||||
if m != "" {
|
||||
metrics, err := c.client.VMGuestMetrics.GetRecord(c.session, m)
|
||||
if err != nil {
|
||||
return false, err
|
||||
}
|
||||
networks := metrics.Networks
|
||||
var ok bool
|
||||
if ip, ok = networks["0/ip"]; ok {
|
||||
if ip != "" {
|
||||
ui.Message(fmt.Sprintf("Got IP '%s' from XenServer tools", ip))
|
||||
return true, nil
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
return false, nil
|
||||
},
|
||||
}.Wait(state)
|
||||
if err != nil {
|
||||
ui.Error(fmt.Sprintf("Could not get IP address of VM: %s", err.Error()))
|
||||
// @todo: give advice on what went wrong (no HTTP server? no PV drivers?)
|
||||
return multistep.ActionHalt
|
||||
}
|
||||
|
||||
ui.Say(fmt.Sprintf("Got IP address '%s'", ip))
|
||||
state.Put("instance_ssh_address", ip)
|
||||
|
||||
return multistep.ActionContinue
|
||||
}
|
||||
|
||||
func InstanceSSHIP(state multistep.StateBag) (string, error) {
|
||||
ip := state.Get("instance_ssh_address").(string)
|
||||
return ip, nil
|
||||
}
|
||||
|
||||
func InstanceSSHPort(state multistep.StateBag) (int, error) {
|
||||
return 22, nil
|
||||
}
|
31
builder/xenserver/common/vm_cleanup.go
Normal file
31
builder/xenserver/common/vm_cleanup.go
Normal file
@ -0,0 +1,31 @@
|
||||
package common
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"log"
|
||||
|
||||
"github.com/hashicorp/packer-plugin-sdk/multistep"
|
||||
)
|
||||
|
||||
type VmCleanup struct{}
|
||||
|
||||
func (self *VmCleanup) Cleanup(state multistep.StateBag) {
|
||||
config := state.Get("commonconfig").(CommonConfig)
|
||||
c := state.Get("client").(*Connection)
|
||||
|
||||
if config.ShouldKeepVM(state) {
|
||||
return
|
||||
}
|
||||
|
||||
uuid := state.Get("instance_uuid").(string)
|
||||
instance, err := c.client.VM.GetByUUID(c.session, uuid)
|
||||
if err != nil {
|
||||
log.Printf(fmt.Sprintf("Unable to get VM from UUID '%s': %s", uuid, err.Error()))
|
||||
return
|
||||
}
|
||||
|
||||
err = c.client.VM.HardShutdown(c.session, instance)
|
||||
if err != nil {
|
||||
log.Printf(fmt.Sprintf("Unable to force shutdown VM '%s': %s", uuid, err.Error()))
|
||||
}
|
||||
}
|
337
builder/xenserver/iso/builder.go
Normal file
337
builder/xenserver/iso/builder.go
Normal file
@ -0,0 +1,337 @@
|
||||
package iso
|
||||
|
||||
import (
|
||||
"context"
|
||||
"errors"
|
||||
"fmt"
|
||||
"path"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
"github.com/hashicorp/hcl/v2/hcldec"
|
||||
"github.com/hashicorp/packer-plugin-sdk/communicator"
|
||||
"github.com/hashicorp/packer-plugin-sdk/multistep"
|
||||
commonsteps "github.com/hashicorp/packer-plugin-sdk/multistep/commonsteps"
|
||||
"github.com/hashicorp/packer-plugin-sdk/packer"
|
||||
hconfig "github.com/hashicorp/packer-plugin-sdk/template/config"
|
||||
"github.com/hashicorp/packer-plugin-sdk/template/interpolate"
|
||||
xsclient "github.com/terra-farm/go-xen-api-client"
|
||||
xscommon "github.com/xenserver/packer-builder-xenserver/builder/xenserver/common"
|
||||
)
|
||||
|
||||
type Builder struct {
|
||||
config xscommon.Config
|
||||
runner multistep.Runner
|
||||
}
|
||||
|
||||
func (self *Builder) ConfigSpec() hcldec.ObjectSpec { return self.config.FlatMapstructure().HCL2Spec() }
|
||||
|
||||
func (self *Builder) Prepare(raws ...interface{}) (params []string, warns []string, retErr error) {
|
||||
|
||||
var errs *packer.MultiError
|
||||
|
||||
err := hconfig.Decode(&self.config, &hconfig.DecodeOpts{
|
||||
Interpolate: true,
|
||||
InterpolateFilter: &interpolate.RenderFilter{
|
||||
Exclude: []string{
|
||||
"boot_command",
|
||||
},
|
||||
},
|
||||
}, raws...)
|
||||
|
||||
if err != nil {
|
||||
errs = packer.MultiErrorAppend(errs, err)
|
||||
}
|
||||
|
||||
errs = packer.MultiErrorAppend(
|
||||
errs, self.config.CommonConfig.Prepare(self.config.GetInterpContext(), &self.config.PackerConfig)...)
|
||||
errs = packer.MultiErrorAppend(errs, self.config.SSHConfig.Prepare(self.config.GetInterpContext())...)
|
||||
|
||||
// Set default values
|
||||
|
||||
if self.config.RawInstallTimeout == "" {
|
||||
self.config.RawInstallTimeout = "200m"
|
||||
}
|
||||
|
||||
if self.config.DiskName == "" {
|
||||
self.config.DiskName = "Packer-disk"
|
||||
}
|
||||
|
||||
if self.config.DiskSize == 0 {
|
||||
self.config.DiskSize = 40000
|
||||
}
|
||||
|
||||
if self.config.VCPUsMax == 0 {
|
||||
self.config.VCPUsMax = 1
|
||||
}
|
||||
|
||||
if self.config.VCPUsAtStartup == 0 {
|
||||
self.config.VCPUsAtStartup = 1
|
||||
}
|
||||
|
||||
if self.config.VCPUsAtStartup > self.config.VCPUsMax {
|
||||
self.config.VCPUsAtStartup = self.config.VCPUsMax
|
||||
}
|
||||
|
||||
if self.config.VMMemory == 0 {
|
||||
self.config.VMMemory = 1024
|
||||
}
|
||||
|
||||
if self.config.CloneTemplate == "" {
|
||||
self.config.CloneTemplate = "Other install media"
|
||||
}
|
||||
|
||||
if self.config.Firmware == "" {
|
||||
self.config.Firmware = "bios"
|
||||
}
|
||||
|
||||
if len(self.config.PlatformArgs) == 0 {
|
||||
pargs := make(map[string]string)
|
||||
pargs["viridian"] = "false"
|
||||
pargs["nx"] = "true"
|
||||
pargs["pae"] = "true"
|
||||
pargs["apic"] = "true"
|
||||
pargs["timeoffset"] = "0"
|
||||
pargs["acpi"] = "1"
|
||||
self.config.PlatformArgs = pargs
|
||||
}
|
||||
|
||||
// Template substitution
|
||||
|
||||
templates := map[string]*string{
|
||||
"clone_template": &self.config.CloneTemplate,
|
||||
"iso_checksum": &self.config.ISOChecksum,
|
||||
"iso_url": &self.config.ISOUrl,
|
||||
"iso_name": &self.config.ISOName,
|
||||
"install_timeout": &self.config.RawInstallTimeout,
|
||||
}
|
||||
for i := range self.config.ISOUrls {
|
||||
templates[fmt.Sprintf("iso_urls[%d]", i)] = &self.config.ISOUrls[i]
|
||||
}
|
||||
|
||||
// Validation
|
||||
|
||||
self.config.InstallTimeout, err = time.ParseDuration(self.config.RawInstallTimeout)
|
||||
if err != nil {
|
||||
errs = packer.MultiErrorAppend(
|
||||
errs, fmt.Errorf("Failed to parse install_timeout: %s", err))
|
||||
}
|
||||
|
||||
if self.config.ISOName == "" {
|
||||
// If ISO name is not specified, assume a URL and checksum has been provided.
|
||||
self.config.ISOChecksum = strings.ToLower(self.config.ISOChecksum)
|
||||
|
||||
if len(self.config.ISOUrls) == 0 {
|
||||
if self.config.ISOUrl == "" {
|
||||
errs = packer.MultiErrorAppend(
|
||||
errs, errors.New("One of iso_url or iso_urls must be specified."))
|
||||
} else {
|
||||
self.config.ISOUrls = []string{self.config.ISOUrl}
|
||||
}
|
||||
|
||||
} else if self.config.ISOUrl != "" {
|
||||
errs = packer.MultiErrorAppend(
|
||||
errs, errors.New("Only one of iso_url or iso_urls may be specified."))
|
||||
}
|
||||
|
||||
//The SDK can validate the ISO checksum and other sanity checks on the url.
|
||||
iso_config := commonsteps.ISOConfig{
|
||||
ISOChecksum: self.config.ISOChecksum,
|
||||
ISOUrls: self.config.ISOUrls,
|
||||
}
|
||||
|
||||
_, iso_errs := iso_config.Prepare(nil)
|
||||
if iso_errs != nil {
|
||||
for _, this_err := range iso_errs {
|
||||
errs = packer.MultiErrorAppend(errs, this_err)
|
||||
}
|
||||
}
|
||||
|
||||
} else {
|
||||
|
||||
// An ISO name has been provided. It should be attached from an available SR.
|
||||
|
||||
}
|
||||
|
||||
if len(errs.Errors) > 0 {
|
||||
retErr = errors.New(errs.Error())
|
||||
}
|
||||
|
||||
return nil, nil, retErr
|
||||
|
||||
}
|
||||
|
||||
func (self *Builder) Run(ctx context.Context, ui packer.Ui, hook packer.Hook) (packer.Artifact, error) {
|
||||
c, err := xscommon.NewXenAPIClient(self.config.HostIp, self.config.Username, self.config.Password)
|
||||
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
ui.Say("XAPI client session established")
|
||||
|
||||
c.GetClient().Host.GetAll(c.GetSessionRef())
|
||||
|
||||
//Share state between the other steps using a statebag
|
||||
state := new(multistep.BasicStateBag)
|
||||
state.Put("client", c)
|
||||
state.Put("config", self.config)
|
||||
state.Put("commonconfig", self.config.CommonConfig)
|
||||
state.Put("hook", hook)
|
||||
state.Put("ui", ui)
|
||||
|
||||
httpReqChan := make(chan string, 1)
|
||||
|
||||
//Build the steps
|
||||
download_steps := []multistep.Step{
|
||||
&commonsteps.StepDownload{
|
||||
Checksum: self.config.ISOChecksum,
|
||||
Description: "ISO",
|
||||
ResultKey: "iso_path",
|
||||
Url: self.config.ISOUrls,
|
||||
},
|
||||
}
|
||||
steps := []multistep.Step{
|
||||
&xscommon.StepPrepareOutputDir{
|
||||
Force: self.config.PackerForce,
|
||||
Path: self.config.OutputDir,
|
||||
},
|
||||
&commonsteps.StepCreateFloppy{
|
||||
Files: self.config.FloppyFiles,
|
||||
Label: "cidata",
|
||||
},
|
||||
&xscommon.StepHTTPServer{
|
||||
Chan: httpReqChan,
|
||||
},
|
||||
&xscommon.StepUploadVdi{
|
||||
VdiNameFunc: func() string {
|
||||
return "Packer-floppy-disk"
|
||||
},
|
||||
ImagePathFunc: func() string {
|
||||
if floppyPath, ok := state.GetOk("floppy_path"); ok {
|
||||
return floppyPath.(string)
|
||||
}
|
||||
return ""
|
||||
},
|
||||
VdiUuidKey: "floppy_vdi_uuid",
|
||||
},
|
||||
&xscommon.StepFindOrUploadVdi{
|
||||
xscommon.StepUploadVdi{
|
||||
VdiNameFunc: func() string {
|
||||
if len(self.config.ISOUrls) > 0 {
|
||||
return path.Base(self.config.ISOUrls[0])
|
||||
}
|
||||
return ""
|
||||
},
|
||||
ImagePathFunc: func() string {
|
||||
if isoPath, ok := state.GetOk("iso_path"); ok {
|
||||
return isoPath.(string)
|
||||
}
|
||||
return ""
|
||||
},
|
||||
VdiUuidKey: "iso_vdi_uuid",
|
||||
},
|
||||
},
|
||||
&xscommon.StepFindVdi{
|
||||
VdiName: self.config.ToolsIsoName,
|
||||
VdiUuidKey: "tools_vdi_uuid",
|
||||
},
|
||||
&xscommon.StepFindVdi{
|
||||
VdiName: self.config.ISOName,
|
||||
VdiUuidKey: "isoname_vdi_uuid",
|
||||
},
|
||||
&xscommon.StepCreateInstance{
|
||||
AssumePreInstalledOS: false,
|
||||
},
|
||||
&xscommon.StepAttachVdi{
|
||||
VdiUuidKey: "floppy_vdi_uuid",
|
||||
VdiType: xsclient.VbdTypeFloppy,
|
||||
},
|
||||
&xscommon.StepAttachVdi{
|
||||
VdiUuidKey: "iso_vdi_uuid",
|
||||
VdiType: xsclient.VbdTypeCD,
|
||||
},
|
||||
&xscommon.StepAttachVdi{
|
||||
VdiUuidKey: "isoname_vdi_uuid",
|
||||
VdiType: xsclient.VbdTypeCD,
|
||||
},
|
||||
&xscommon.StepAttachVdi{
|
||||
VdiUuidKey: "tools_vdi_uuid",
|
||||
VdiType: xsclient.VbdTypeCD,
|
||||
},
|
||||
new(xscommon.StepStartVmPaused),
|
||||
new(xscommon.StepSetVmHostSshAddress),
|
||||
// &xscommon.StepForwardPortOverSSH{
|
||||
// RemotePort: xscommon.InstanceVNCPort,
|
||||
// RemoteDest: xscommon.InstanceVNCIP,
|
||||
// HostPortMin: self.config.HostPortMin,
|
||||
// HostPortMax: self.config.HostPortMax,
|
||||
// ResultKey: "local_vnc_port",
|
||||
// },
|
||||
new(xscommon.StepBootWait),
|
||||
&xscommon.StepTypeBootCommand{
|
||||
Ctx: *self.config.GetInterpContext(),
|
||||
},
|
||||
&xscommon.StepWaitForIP{
|
||||
Chan: httpReqChan,
|
||||
Timeout: self.config.InstallTimeout, // @todo change this
|
||||
},
|
||||
&xscommon.StepForwardPortOverSSH{
|
||||
RemotePort: xscommon.InstanceSSHPort,
|
||||
RemoteDest: xscommon.InstanceSSHIP,
|
||||
HostPortMin: self.config.HostPortMin,
|
||||
HostPortMax: self.config.HostPortMax,
|
||||
ResultKey: "local_ssh_port",
|
||||
},
|
||||
&communicator.StepConnect{
|
||||
Config: &self.config.SSHConfig.Comm,
|
||||
Host: xscommon.InstanceSSHIP,
|
||||
SSHConfig: self.config.Comm.SSHConfigFunc(),
|
||||
SSHPort: xscommon.InstanceSSHPort,
|
||||
},
|
||||
new(commonsteps.StepProvision),
|
||||
new(xscommon.StepShutdown),
|
||||
}
|
||||
|
||||
if !self.config.SkipSetTemplate {
|
||||
steps = append(steps,
|
||||
new(xscommon.StepSetVmToTemplate))
|
||||
}
|
||||
|
||||
steps = append(steps,
|
||||
&xscommon.StepDetachVdi{
|
||||
VdiUuidKey: "iso_vdi_uuid",
|
||||
},
|
||||
&xscommon.StepDetachVdi{
|
||||
VdiUuidKey: "isoname_vdi_uuid",
|
||||
},
|
||||
&xscommon.StepDetachVdi{
|
||||
VdiUuidKey: "tools_vdi_uuid",
|
||||
},
|
||||
&xscommon.StepDetachVdi{
|
||||
VdiUuidKey: "floppy_vdi_uuid",
|
||||
},
|
||||
new(xscommon.StepExport))
|
||||
|
||||
if self.config.ISOName == "" {
|
||||
steps = append(download_steps, steps...)
|
||||
}
|
||||
|
||||
self.runner = &multistep.BasicRunner{Steps: steps}
|
||||
self.runner.Run(ctx, state)
|
||||
|
||||
if rawErr, ok := state.GetOk("error"); ok {
|
||||
return nil, rawErr.(error)
|
||||
}
|
||||
|
||||
// If we were interrupted or cancelled, then just exit.
|
||||
if _, ok := state.GetOk(multistep.StateCancelled); ok {
|
||||
return nil, errors.New("Build was cancelled.")
|
||||
}
|
||||
if _, ok := state.GetOk(multistep.StateHalted); ok {
|
||||
return nil, errors.New("Build was halted.")
|
||||
}
|
||||
|
||||
artifact, _ := xscommon.NewArtifact(self.config.OutputDir)
|
||||
|
||||
return artifact, nil
|
||||
}
|
303
builder/xenserver/iso/builder_test.go
Normal file
303
builder/xenserver/iso/builder_test.go
Normal file
@ -0,0 +1,303 @@
|
||||
package iso
|
||||
|
||||
import (
|
||||
"reflect"
|
||||
"testing"
|
||||
|
||||
"github.com/hashicorp/packer-plugin-sdk/common"
|
||||
"github.com/hashicorp/packer-plugin-sdk/packer"
|
||||
)
|
||||
|
||||
func testConfig() map[string]interface{} {
|
||||
return map[string]interface{}{
|
||||
"remote_host": "localhost",
|
||||
"remote_username": "admin",
|
||||
"remote_password": "admin",
|
||||
"vm_name": "foo",
|
||||
"iso_checksum": "md5:A221725EE181A44C67E25BD6A2516742",
|
||||
"iso_url": "http://www.google.com/",
|
||||
"shutdown_command": "yes",
|
||||
"ssh_username": "foo",
|
||||
|
||||
common.BuildNameConfigKey: "foo",
|
||||
}
|
||||
}
|
||||
|
||||
func TestBuilder_ImplementsBuilder(t *testing.T) {
|
||||
var raw interface{}
|
||||
raw = &Builder{}
|
||||
if _, ok := raw.(packer.Builder); !ok {
|
||||
t.Error("Builder must implement builder.")
|
||||
}
|
||||
}
|
||||
|
||||
func TestBuilderPrepare_Defaults(t *testing.T) {
|
||||
var b Builder
|
||||
config := testConfig()
|
||||
_, warns, err := b.Prepare(config)
|
||||
if len(warns) > 0 {
|
||||
t.Fatalf("bad: %#v", warns)
|
||||
}
|
||||
if err != nil {
|
||||
t.Fatalf("should not have error: %s", err)
|
||||
}
|
||||
|
||||
if b.config.ToolsIsoName != "" {
|
||||
t.Errorf("bad tools ISO name: %s", b.config.ToolsIsoName)
|
||||
}
|
||||
|
||||
if b.config.CloneTemplate != "Other install media" {
|
||||
t.Errorf("bad clone template: %s", b.config.CloneTemplate)
|
||||
}
|
||||
|
||||
if b.config.VMName == "" {
|
||||
t.Errorf("bad vm name: %s", b.config.VMName)
|
||||
}
|
||||
|
||||
if b.config.Format != "xva" {
|
||||
t.Errorf("bad format: %s", b.config.Format)
|
||||
}
|
||||
|
||||
if b.config.KeepVM != "never" {
|
||||
t.Errorf("bad keep instance: %s", b.config.KeepVM)
|
||||
}
|
||||
|
||||
if b.config.HostSshPort != 22 {
|
||||
t.Errorf("bad ssh port: %d", b.config.HostSshPort)
|
||||
}
|
||||
}
|
||||
|
||||
func TestBuilderPrepare_DiskSize(t *testing.T) {
|
||||
var b Builder
|
||||
config := testConfig()
|
||||
|
||||
delete(config, "disk_size")
|
||||
_, warns, err := b.Prepare(config)
|
||||
if len(warns) > 0 {
|
||||
t.Fatalf("bad: %#v", warns)
|
||||
}
|
||||
if err != nil {
|
||||
t.Fatalf("bad err: %s", err)
|
||||
}
|
||||
|
||||
if b.config.DiskSize != 40000 {
|
||||
t.Fatalf("bad size: %d", b.config.DiskSize)
|
||||
}
|
||||
|
||||
config["disk_size"] = 60000
|
||||
b = Builder{}
|
||||
_, warns, err = b.Prepare(config)
|
||||
if len(warns) > 0 {
|
||||
t.Fatalf("bad: %#v", warns)
|
||||
}
|
||||
if err != nil {
|
||||
t.Fatalf("should not have error: %s", err)
|
||||
}
|
||||
|
||||
if b.config.DiskSize != 60000 {
|
||||
t.Fatalf("bad size: %d", b.config.DiskSize)
|
||||
}
|
||||
}
|
||||
|
||||
func TestBuilderPrepare_Format(t *testing.T) {
|
||||
var b Builder
|
||||
config := testConfig()
|
||||
|
||||
// Bad
|
||||
config["format"] = "foo"
|
||||
_, warns, err := b.Prepare(config)
|
||||
if len(warns) > 0 {
|
||||
t.Fatalf("bad: %#v", warns)
|
||||
}
|
||||
if err == nil {
|
||||
t.Fatal("should have error")
|
||||
}
|
||||
|
||||
// Good
|
||||
config["format"] = "vdi_raw"
|
||||
b = Builder{}
|
||||
_, warns, err = b.Prepare(config)
|
||||
if len(warns) > 0 {
|
||||
t.Fatalf("bad: %#v", warns)
|
||||
}
|
||||
if err != nil {
|
||||
t.Fatalf("should not have error: %s", err)
|
||||
}
|
||||
}
|
||||
|
||||
func TestBuilderPrepare_HTTPPort(t *testing.T) {
|
||||
var b Builder
|
||||
config := testConfig()
|
||||
|
||||
// Bad
|
||||
config["http_port_min"] = 1000
|
||||
config["http_port_max"] = 500
|
||||
_, warns, err := b.Prepare(config)
|
||||
if len(warns) > 0 {
|
||||
t.Fatalf("bad: %#v", warns)
|
||||
}
|
||||
if err == nil {
|
||||
t.Fatal("should have error")
|
||||
}
|
||||
|
||||
// Bad
|
||||
config["http_port_min"] = -500
|
||||
b = Builder{}
|
||||
_, warns, err = b.Prepare(config)
|
||||
if len(warns) > 0 {
|
||||
t.Fatalf("bad: %#v", warns)
|
||||
}
|
||||
if err == nil {
|
||||
t.Fatal("should have error")
|
||||
}
|
||||
|
||||
// Good
|
||||
config["http_port_min"] = 500
|
||||
config["http_port_max"] = 1000
|
||||
b = Builder{}
|
||||
_, warns, err = b.Prepare(config)
|
||||
if len(warns) > 0 {
|
||||
t.Fatalf("bad: %#v", warns)
|
||||
}
|
||||
if err != nil {
|
||||
t.Fatalf("should not have error: %s", err)
|
||||
}
|
||||
}
|
||||
|
||||
func TestBuilderPrepare_InvalidKey(t *testing.T) {
|
||||
var b Builder
|
||||
config := testConfig()
|
||||
|
||||
// Add a random key
|
||||
config["i_should_not_be_valid"] = true
|
||||
_, warns, err := b.Prepare(config)
|
||||
if len(warns) > 0 {
|
||||
t.Fatalf("bad: %#v", warns)
|
||||
}
|
||||
if err == nil {
|
||||
t.Fatal("should have error")
|
||||
}
|
||||
}
|
||||
|
||||
func TestBuilderPrepare_ISOChecksum(t *testing.T) {
|
||||
var b Builder
|
||||
config := testConfig()
|
||||
|
||||
// Test good
|
||||
|
||||
b = Builder{}
|
||||
_, warns, err := b.Prepare(config)
|
||||
if len(warns) > 0 {
|
||||
t.Fatalf("bad: %#v", warns)
|
||||
}
|
||||
if err != nil {
|
||||
t.Fatalf("should not have error: %s", err)
|
||||
}
|
||||
|
||||
// Test bad
|
||||
config["iso_checksum"] = ""
|
||||
_, warns, err = b.Prepare(config)
|
||||
if len(warns) > 0 {
|
||||
t.Fatalf("bad: %#v", warns)
|
||||
}
|
||||
if err == nil {
|
||||
t.Fatal("should have error")
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
func TestBuilderPrepare_ISOUrl(t *testing.T) {
|
||||
var b Builder
|
||||
config := testConfig()
|
||||
delete(config, "iso_url")
|
||||
delete(config, "iso_urls")
|
||||
|
||||
// Test both epty
|
||||
config["iso_url"] = ""
|
||||
b = Builder{}
|
||||
_, warns, err := b.Prepare(config)
|
||||
if len(warns) > 0 {
|
||||
t.Fatalf("bad: %#v", warns)
|
||||
}
|
||||
if err == nil {
|
||||
t.Fatal("should have error")
|
||||
}
|
||||
|
||||
// Test iso_url set
|
||||
config["iso_url"] = "http://www.packer.io"
|
||||
b = Builder{}
|
||||
_, warns, err = b.Prepare(config)
|
||||
if len(warns) > 0 {
|
||||
t.Fatalf("bad: %#v", warns)
|
||||
}
|
||||
if err != nil {
|
||||
t.Errorf("should not have error: %s", err)
|
||||
}
|
||||
|
||||
expected := []string{"http://www.packer.io"}
|
||||
if !reflect.DeepEqual(b.config.ISOUrls, expected) {
|
||||
t.Fatalf("bad: %#v", b.config.ISOUrls)
|
||||
}
|
||||
|
||||
// Test both set
|
||||
config["iso_url"] = "http://www.packer.io"
|
||||
config["iso_urls"] = []string{"http://www.packer.io"}
|
||||
b = Builder{}
|
||||
_, warns, err = b.Prepare(config)
|
||||
if len(warns) > 0 {
|
||||
t.Fatalf("bad: %#v", warns)
|
||||
}
|
||||
if err == nil {
|
||||
t.Fatal("should have error")
|
||||
}
|
||||
|
||||
// Test just iso_urls set
|
||||
delete(config, "iso_url")
|
||||
config["iso_urls"] = []string{
|
||||
"http://www.packer.io",
|
||||
"http://www.hashicorp.com",
|
||||
}
|
||||
|
||||
b = Builder{}
|
||||
_, warns, err = b.Prepare(config)
|
||||
if len(warns) > 0 {
|
||||
t.Fatalf("bad: %#v", warns)
|
||||
}
|
||||
if err != nil {
|
||||
t.Errorf("should not have error: %s", err)
|
||||
}
|
||||
|
||||
expected = []string{
|
||||
"http://www.packer.io",
|
||||
"http://www.hashicorp.com",
|
||||
}
|
||||
if !reflect.DeepEqual(b.config.ISOUrls, expected) {
|
||||
t.Fatalf("bad: %#v", b.config.ISOUrls)
|
||||
}
|
||||
}
|
||||
|
||||
func TestBuilderPrepare_KeepVM(t *testing.T) {
|
||||
var b Builder
|
||||
config := testConfig()
|
||||
|
||||
// Bad
|
||||
config["keep_vm"] = "foo"
|
||||
_, warns, err := b.Prepare(config)
|
||||
if len(warns) > 0 {
|
||||
t.Fatalf("bad: %#v", warns)
|
||||
}
|
||||
if err == nil {
|
||||
t.Fatal("should have error")
|
||||
}
|
||||
|
||||
// Good
|
||||
config["keep_vm"] = "always"
|
||||
b = Builder{}
|
||||
_, warns, err = b.Prepare(config)
|
||||
if len(warns) > 0 {
|
||||
t.Fatalf("bad: %#v", warns)
|
||||
}
|
||||
if err != nil {
|
||||
t.Fatalf("should not have error: %s", err)
|
||||
}
|
||||
}
|
@ -1,152 +0,0 @@
|
||||
package xenserver
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
gossh "code.google.com/p/go.crypto/ssh"
|
||||
"github.com/mitchellh/multistep"
|
||||
commonssh "github.com/mitchellh/packer/common/ssh"
|
||||
"github.com/mitchellh/packer/communicator/ssh"
|
||||
"strings"
|
||||
"log"
|
||||
"bytes"
|
||||
"net"
|
||||
"io"
|
||||
)
|
||||
|
||||
func sshAddress(state multistep.StateBag) (string, error) {
|
||||
sshIP := state.Get("ssh_address").(string)
|
||||
sshHostPort := 22
|
||||
return fmt.Sprintf("%s:%d", sshIP, sshHostPort), nil
|
||||
}
|
||||
|
||||
func sshLocalAddress(state multistep.StateBag) (string, error) {
|
||||
sshLocalPort := state.Get("local_ssh_port").(uint)
|
||||
conn_str := fmt.Sprintf("%s:%d", "127.0.0.1", sshLocalPort)
|
||||
log.Printf("sshLocalAddress: %s", conn_str)
|
||||
return conn_str, nil
|
||||
}
|
||||
|
||||
func sshConfig(state multistep.StateBag) (*gossh.ClientConfig, error) {
|
||||
config := state.Get("config").(config)
|
||||
|
||||
auth := []gossh.AuthMethod{
|
||||
gossh.Password(config.SSHPassword),
|
||||
gossh.KeyboardInteractive(
|
||||
ssh.PasswordKeyboardInteractive(config.SSHPassword)),
|
||||
}
|
||||
|
||||
if config.SSHKeyPath != "" {
|
||||
signer, err := commonssh.FileSigner(config.SSHKeyPath)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
auth = append(auth, gossh.PublicKeys(signer))
|
||||
}
|
||||
|
||||
return &gossh.ClientConfig{
|
||||
User: config.SSHUser,
|
||||
Auth: auth,
|
||||
}, nil
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
func execute_ssh_cmd (cmd, host, port, username, password string) (stdout string, err error) {
|
||||
// Setup connection config
|
||||
config := &gossh.ClientConfig {
|
||||
User: username,
|
||||
Auth: []gossh.AuthMethod {
|
||||
gossh.Password(password),
|
||||
},
|
||||
}
|
||||
|
||||
client, err := gossh.Dial("tcp", host + ":" + port, config)
|
||||
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
|
||||
//Create session
|
||||
session, err := client.NewSession()
|
||||
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
|
||||
defer session.Close()
|
||||
|
||||
var b bytes.Buffer
|
||||
session.Stdout = &b
|
||||
if err := session.Run(cmd); err != nil {
|
||||
return "", err
|
||||
}
|
||||
|
||||
session.Close()
|
||||
return strings.Trim(b.String(), "\n"), nil
|
||||
}
|
||||
|
||||
func forward(local_conn net.Conn, config *gossh.ClientConfig, server, remote_dest string, remote_port uint) {
|
||||
ssh_client_conn, err := gossh.Dial("tcp", server + ":22", config)
|
||||
if err != nil {
|
||||
log.Fatalf("local ssh.Dial error: %s", err)
|
||||
}
|
||||
|
||||
remote_loc := fmt.Sprintf("%s:%d", remote_dest, remote_port)
|
||||
ssh_conn, err := ssh_client_conn.Dial("tcp", remote_loc)
|
||||
if err != nil {
|
||||
log.Fatalf("ssh.Dial error: %s", err)
|
||||
}
|
||||
|
||||
go func() {
|
||||
_, err = io.Copy(ssh_conn, local_conn)
|
||||
if err != nil {
|
||||
log.Fatalf("io.copy failed: %v", err)
|
||||
}
|
||||
}()
|
||||
|
||||
go func() {
|
||||
_, err = io.Copy(local_conn, ssh_conn)
|
||||
if err != nil {
|
||||
log.Fatalf("io.copy failed: %v", err)
|
||||
}
|
||||
}()
|
||||
}
|
||||
|
||||
func ssh_port_forward(local_port uint, remote_port uint, remote_dest, host, username, password string) (err error) {
|
||||
|
||||
config := &gossh.ClientConfig {
|
||||
User: username,
|
||||
Auth: []gossh.AuthMethod{
|
||||
gossh.Password(password),
|
||||
},
|
||||
}
|
||||
|
||||
// Listen on a local port
|
||||
local_listener, err := net.Listen("tcp",
|
||||
fmt.Sprintf("%s:%d",
|
||||
"127.0.0.1",
|
||||
local_port))
|
||||
if err != nil {
|
||||
log.Fatalf("Local listen failed: %s", err)
|
||||
return err
|
||||
}
|
||||
|
||||
for {
|
||||
local_connection, err := local_listener.Accept()
|
||||
|
||||
if err != nil {
|
||||
log.Fatalf("Local accept failed: %s", err)
|
||||
return err
|
||||
}
|
||||
|
||||
|
||||
// Forward to a remote port
|
||||
go forward(local_connection, config, host, remote_dest, remote_port)
|
||||
}
|
||||
|
||||
|
||||
|
||||
return nil
|
||||
}
|
@ -1,30 +0,0 @@
|
||||
package xenserver
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"github.com/mitchellh/multistep"
|
||||
"github.com/mitchellh/packer/packer"
|
||||
"time"
|
||||
)
|
||||
|
||||
|
||||
type stepBootWait struct{}
|
||||
|
||||
func (self *stepBootWait) Run(state multistep.StateBag) multistep.StepAction {
|
||||
client := state.Get("client").(XenAPIClient)
|
||||
config := state.Get("config").(config)
|
||||
ui := state.Get("ui").(packer.Ui)
|
||||
|
||||
instance, _ := client.GetVMByUuid(state.Get("instance_uuid").(string))
|
||||
ui.Say("Unpausing VM " + state.Get("instance_uuid").(string))
|
||||
instance.Unpause()
|
||||
|
||||
if int64(config.BootWait) > 0 {
|
||||
ui.Say(fmt.Sprintf("Waiting %s for boot...", config.BootWait))
|
||||
time.Sleep(config.BootWait)
|
||||
}
|
||||
return multistep.ActionContinue
|
||||
}
|
||||
|
||||
func (self *stepBootWait) Cleanup(state multistep.StateBag) {}
|
||||
|
@ -1,198 +0,0 @@
|
||||
package xenserver
|
||||
|
||||
|
||||
import (
|
||||
"github.com/mitchellh/multistep"
|
||||
"github.com/mitchellh/packer/packer"
|
||||
"fmt"
|
||||
"log"
|
||||
)
|
||||
|
||||
type stepCreateInstance struct {
|
||||
InstanceId string
|
||||
}
|
||||
|
||||
func (self *stepCreateInstance) Run(state multistep.StateBag) multistep.StepAction {
|
||||
|
||||
client := state.Get("client").(XenAPIClient)
|
||||
config := state.Get("config").(config)
|
||||
ui := state.Get("ui").(packer.Ui)
|
||||
|
||||
ui.Say("Step: Create Instance")
|
||||
|
||||
|
||||
// Get the template to clone from
|
||||
|
||||
vms, err := client.GetVMByNameLabel(config.CloneTemplate)
|
||||
|
||||
switch {
|
||||
case len(vms) == 0:
|
||||
log.Fatal(fmt.Sprintf("Couldn't find a template with the name-label '%s'. Aborting.", config.CloneTemplate))
|
||||
return multistep.ActionHalt
|
||||
case len(vms) > 1:
|
||||
log.Fatal(fmt.Sprintf("Found more than one template with the name '%s'. The name must be unique. Aborting.", config.CloneTemplate))
|
||||
return multistep.ActionHalt
|
||||
}
|
||||
|
||||
template := vms[0]
|
||||
|
||||
// Clone that VM template
|
||||
instance, _ := template.Clone(config.InstanceName)
|
||||
instance.SetIsATemplate(false)
|
||||
instance.SetStaticMemoryRange(config.InstanceMemory, config.InstanceMemory)
|
||||
instance.SetPlatform(config.PlatformArgs)
|
||||
|
||||
// Create VDI for the instance
|
||||
var sr *SR
|
||||
|
||||
if config.SrName == "" {
|
||||
// Find the default SR
|
||||
default_sr, err := client.GetDefaultSR()
|
||||
sr = default_sr
|
||||
|
||||
if err != nil {
|
||||
log.Fatal(fmt.Sprintf("Error getting default SR: %s", err.Error()))
|
||||
return multistep.ActionHalt
|
||||
}
|
||||
|
||||
|
||||
} else {
|
||||
// Use the provided name label to find the SR to use
|
||||
srs, err := client.GetSRByNameLabel(config.SrName)
|
||||
|
||||
if err != nil {
|
||||
log.Fatal(fmt.Sprintf("Error getting default SR: %s", err.Error()))
|
||||
return multistep.ActionHalt
|
||||
}
|
||||
|
||||
switch {
|
||||
case len(srs) == 0:
|
||||
log.Fatal(fmt.Sprintf("Couldn't find a SR with the specified name-label '%s'. Aborting.", config.SrName))
|
||||
return multistep.ActionHalt
|
||||
case len(srs) > 1:
|
||||
log.Fatal(fmt.Sprintf("Found more than one SR with the name '%s'. The name must be unique. Aborting.", config.SrName))
|
||||
return multistep.ActionHalt
|
||||
}
|
||||
|
||||
sr = srs[0]
|
||||
}
|
||||
|
||||
vdi, _ := sr.CreateVdi("Packer-disk", config.RootDiskSize)
|
||||
|
||||
instance.ConnectVdi(vdi, false)
|
||||
|
||||
// Connect Network
|
||||
|
||||
var network *Network
|
||||
|
||||
if config.NetworkName == "" {
|
||||
// No network has be specified. Use the management interface
|
||||
network = new(Network)
|
||||
network.Ref = ""
|
||||
network.Client = &client
|
||||
|
||||
pifs, err := client.GetPIFs()
|
||||
|
||||
if err != nil {
|
||||
log.Fatal(fmt.Sprintf("Error getting PIFs %s", err.Error()))
|
||||
return multistep.ActionHalt
|
||||
}
|
||||
|
||||
for _, pif := range pifs {
|
||||
pif_rec, err := pif.GetRecord()
|
||||
|
||||
if err != nil {
|
||||
log.Fatal(fmt.Sprintf("Error getting PIF record: %s", err.Error()))
|
||||
return multistep.ActionHalt
|
||||
}
|
||||
|
||||
if pif_rec["management"].(bool) {
|
||||
network.Ref = pif_rec["network"].(string)
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
if network.Ref == "" {
|
||||
log.Fatal("Error: couldn't find management network. Aborting.")
|
||||
return multistep.ActionHalt
|
||||
}
|
||||
|
||||
} else {
|
||||
// Look up the network by it's name label
|
||||
|
||||
networks, err := client.GetNetworkByNameLabel(config.NetworkName)
|
||||
|
||||
if err != nil {
|
||||
log.Fatal(fmt.Sprintf("Error occured getting Network by name-label: %s", err.Error()))
|
||||
return multistep.ActionHalt
|
||||
}
|
||||
|
||||
switch {
|
||||
case len(networks) == 0:
|
||||
log.Fatal(fmt.Sprintf("Couldn't find a network with the specified name-label '%s'. Aborting.", config.NetworkName))
|
||||
return multistep.ActionHalt
|
||||
case len(networks) > 1:
|
||||
log.Fatal(fmt.Sprintf("Found more than one SR with the name '%s'. The name must be unique. Aborting.", config.NetworkName))
|
||||
return multistep.ActionHalt
|
||||
}
|
||||
|
||||
network = networks[0]
|
||||
}
|
||||
|
||||
if err != nil {
|
||||
ui.Say(err.Error())
|
||||
}
|
||||
_, err = instance.ConnectNetwork(network, "0")
|
||||
|
||||
if err != nil {
|
||||
ui.Say(err.Error())
|
||||
}
|
||||
|
||||
// Connect the ISO
|
||||
//iso_vdi_uuid := state.Get("iso_vdi_uuid").(string)
|
||||
|
||||
|
||||
isos, err := client.GetVdiByNameLabel(config.IsoName)
|
||||
|
||||
|
||||
switch {
|
||||
case len(isos) == 0:
|
||||
log.Fatal(fmt.Sprintf("Couldn't find an ISO named '%s'. Aborting", config.IsoName))
|
||||
return multistep.ActionHalt
|
||||
case len(isos) > 1:
|
||||
log.Fatal(fmt.Sprintf("Found more than one VDI with name '%s'. Name must be unique. Aborting.", config.IsoName))
|
||||
return multistep.ActionHalt
|
||||
}
|
||||
|
||||
iso := isos[0]
|
||||
|
||||
//iso, _ := client.GetVdiByUuid(config.IsoUuid)
|
||||
//ui.Say("Using VDI: " + iso_vdi_uuid)
|
||||
//iso, _ := client.GetVdiByUuid(iso_vdi_uuid)
|
||||
instance.ConnectVdi(iso, true)
|
||||
|
||||
// Stash the VM reference
|
||||
self.InstanceId, _ = instance.GetUuid()
|
||||
state.Put("instance_uuid", self.InstanceId)
|
||||
state.Put("instance", instance)
|
||||
ui.Say(fmt.Sprintf("Created instance '%s'", self.InstanceId))
|
||||
|
||||
return multistep.ActionContinue
|
||||
}
|
||||
|
||||
|
||||
func (self *stepCreateInstance) Cleanup(state multistep.StateBag) {
|
||||
|
||||
// client := state.Get("client").(*XenAPIClient)
|
||||
// config := state.Get("config").(config)
|
||||
// ui := state.Get("ui").(packer.Ui)
|
||||
|
||||
// If instance hasn't been created, we have nothing to do.
|
||||
if self.InstanceId == "" {
|
||||
return
|
||||
}
|
||||
|
||||
// @todo: destroy the created instance.
|
||||
|
||||
return
|
||||
}
|
@ -1,73 +0,0 @@
|
||||
package xenserver
|
||||
|
||||
import (
|
||||
"github.com/mitchellh/multistep"
|
||||
"github.com/mitchellh/packer/packer"
|
||||
"log"
|
||||
"net"
|
||||
"fmt"
|
||||
)
|
||||
|
||||
type stepForwardPortOverSSH struct {
|
||||
|
||||
RemotePort func (state multistep.StateBag) (uint, error)
|
||||
RemoteDest func (state multistep.StateBag) (string, error)
|
||||
|
||||
HostPortMin uint
|
||||
HostPortMax uint
|
||||
|
||||
ResultKey string
|
||||
}
|
||||
|
||||
|
||||
func (self *stepForwardPortOverSSH) Run(state multistep.StateBag) multistep.StepAction {
|
||||
|
||||
config := state.Get("config").(config)
|
||||
ui := state.Get("ui").(packer.Ui)
|
||||
|
||||
// Find a free local port:
|
||||
|
||||
log.Printf("Looking for an available port between %d and %d",
|
||||
self.HostPortMin,
|
||||
self.HostPortMax)
|
||||
|
||||
|
||||
var sshHostPort uint
|
||||
var foundPort bool
|
||||
|
||||
foundPort = false
|
||||
|
||||
for i := self.HostPortMin; i < self.HostPortMax; i++ {
|
||||
sshHostPort = i
|
||||
log.Printf("Trying port: %d", sshHostPort)
|
||||
l, err := net.Listen("tcp", fmt.Sprintf("127.0.0.1:%d", sshHostPort))
|
||||
if err == nil {
|
||||
l.Close()
|
||||
foundPort = true
|
||||
break
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
if !foundPort {
|
||||
log.Fatal("Error: unable to find free host port. Try providing a larger range")
|
||||
return multistep.ActionHalt
|
||||
}
|
||||
|
||||
|
||||
ui.Say(fmt.Sprintf("Creating a local port forward over SSH on local port %d", sshHostPort))
|
||||
|
||||
remotePort, _ := self.RemotePort(state)
|
||||
remoteDest, _ := self.RemoteDest(state)
|
||||
|
||||
|
||||
go ssh_port_forward(sshHostPort, remotePort, remoteDest, config.HostIp, config.Username, config.Password)
|
||||
ui.Say(fmt.Sprintf("Port forward setup. %d ---> %s:%d on %s", sshHostPort, remoteDest, remotePort, config.HostIp))
|
||||
|
||||
// Provide the local port to future steps.
|
||||
state.Put(self.ResultKey, sshHostPort)
|
||||
|
||||
return multistep.ActionContinue
|
||||
}
|
||||
|
||||
func (self *stepForwardPortOverSSH) Cleanup(state multistep.StateBag) {}
|
@ -1,52 +0,0 @@
|
||||
package xenserver
|
||||
|
||||
|
||||
import (
|
||||
"github.com/mitchellh/multistep"
|
||||
"github.com/mitchellh/packer/packer"
|
||||
"fmt"
|
||||
"strconv"
|
||||
"log"
|
||||
)
|
||||
|
||||
type stepGetVNCPort struct {}
|
||||
|
||||
|
||||
func (self *stepGetVNCPort) Run(state multistep.StateBag) multistep.StepAction {
|
||||
|
||||
config := state.Get("config").(config)
|
||||
ui := state.Get("ui").(packer.Ui)
|
||||
|
||||
ui.Say("Step: forward the instances VNC port over SSH")
|
||||
|
||||
domid := state.Get("domid").(string)
|
||||
cmd := fmt.Sprintf("xenstore-read /local/domain/%s/console/vnc-port", domid)
|
||||
|
||||
remote_vncport, _ := execute_ssh_cmd(cmd, config.HostIp, "22", config.Username, config.Password)
|
||||
|
||||
remote_port, err := strconv.ParseUint(remote_vncport, 10, 16)
|
||||
|
||||
if err != nil {
|
||||
log.Fatal(err.Error())
|
||||
log.Fatal(fmt.Sprintf("Unable to convert '%s' to an int", remote_vncport))
|
||||
return multistep.ActionHalt
|
||||
}
|
||||
|
||||
state.Put("instance_vnc_port", uint(remote_port))
|
||||
|
||||
return multistep.ActionContinue
|
||||
}
|
||||
|
||||
|
||||
func (self *stepGetVNCPort) Cleanup(state multistep.StateBag) {
|
||||
}
|
||||
|
||||
func instanceVNCPort (state multistep.StateBag) (uint, error) {
|
||||
vncPort := state.Get("instance_vnc_port").(uint)
|
||||
return vncPort, nil
|
||||
}
|
||||
|
||||
func instanceVNCIP (state multistep.StateBag) (string, error) {
|
||||
// The port is in Dom0, so we want to forward from localhost
|
||||
return "127.0.0.1", nil
|
||||
}
|
@ -1,78 +0,0 @@
|
||||
package xenserver
|
||||
|
||||
// Taken from mitchellh/packer/builder/qemu/step_http_server.go
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"github.com/mitchellh/multistep"
|
||||
"github.com/mitchellh/packer/packer"
|
||||
"log"
|
||||
"math/rand"
|
||||
"net"
|
||||
"net/http"
|
||||
)
|
||||
|
||||
// This step creates and runs the HTTP server that is serving files from the
|
||||
// directory specified by the 'http_directory` configuration parameter in the
|
||||
// template.
|
||||
//
|
||||
// Uses:
|
||||
// config *config
|
||||
// ui packer.Ui
|
||||
//
|
||||
// Produces:
|
||||
// http_port int - The port the HTTP server started on.
|
||||
type stepHTTPServer struct {
|
||||
l net.Listener
|
||||
}
|
||||
|
||||
func (s *stepHTTPServer) Run(state multistep.StateBag) multistep.StepAction {
|
||||
config := state.Get("config").(config)
|
||||
ui := state.Get("ui").(packer.Ui)
|
||||
|
||||
var httpPort uint = 0
|
||||
if config.HTTPDir == "" {
|
||||
state.Put("http_port", httpPort)
|
||||
return multistep.ActionContinue
|
||||
}
|
||||
|
||||
// Find an available TCP port for our HTTP server
|
||||
var httpAddr string
|
||||
portRange := int(config.HTTPPortMax - config.HTTPPortMin)
|
||||
for {
|
||||
var err error
|
||||
var offset uint = 0
|
||||
|
||||
if portRange > 0 {
|
||||
// Intn will panic if portRange == 0, so we do a check.
|
||||
offset = uint(rand.Intn(portRange))
|
||||
}
|
||||
|
||||
httpPort = offset + config.HTTPPortMin
|
||||
httpAddr = fmt.Sprintf(":%d", httpPort)
|
||||
log.Printf("Trying port: %d", httpPort)
|
||||
s.l, err = net.Listen("tcp", httpAddr)
|
||||
if err == nil {
|
||||
break
|
||||
}
|
||||
}
|
||||
|
||||
ui.Say(fmt.Sprintf("Starting HTTP server on port %d", httpPort))
|
||||
|
||||
// Start the HTTP server and run it in the background
|
||||
fileServer := http.FileServer(http.Dir(config.HTTPDir))
|
||||
server := &http.Server{Addr: httpAddr, Handler: fileServer}
|
||||
go server.Serve(s.l)
|
||||
|
||||
// Save the address into the state so it can be accessed in the future
|
||||
state.Put("http_port", httpPort)
|
||||
|
||||
return multistep.ActionContinue
|
||||
}
|
||||
|
||||
func (s *stepHTTPServer) Cleanup(multistep.StateBag) {
|
||||
if s.l != nil {
|
||||
// Close the listener so that the HTTP server stops
|
||||
s.l.Close()
|
||||
}
|
||||
}
|
@ -1,100 +0,0 @@
|
||||
package xenserver
|
||||
|
||||
/* Taken from https://raw.githubusercontent.com/mitchellh/packer/master/builder/qemu/step_prepare_output_dir.go */
|
||||
|
||||
import (
|
||||
"github.com/mitchellh/multistep"
|
||||
"github.com/mitchellh/packer/packer"
|
||||
"os"
|
||||
"fmt"
|
||||
"net/http"
|
||||
"crypto/tls"
|
||||
"io"
|
||||
)
|
||||
|
||||
type stepShutdownAndExport struct{}
|
||||
|
||||
func downloadFile(url, filename string) (err error) {
|
||||
|
||||
// Create the file
|
||||
fh, err := os.Create(filename)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// Define a new transport which allows self-signed certs
|
||||
tr := &http.Transport{
|
||||
TLSClientConfig: &tls.Config{InsecureSkipVerify: true},
|
||||
}
|
||||
|
||||
// Create a client
|
||||
client := &http.Client{Transport: tr}
|
||||
|
||||
// Create request and download file
|
||||
|
||||
resp, err := client.Get(url)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
defer resp.Body.Close()
|
||||
io.Copy(fh, resp.Body)
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
|
||||
func (stepShutdownAndExport) Run(state multistep.StateBag) multistep.StepAction {
|
||||
config := state.Get("config").(config)
|
||||
ui := state.Get("ui").(packer.Ui)
|
||||
client := state.Get("client").(XenAPIClient)
|
||||
instance_uuid := state.Get("instance_uuid").(string)
|
||||
|
||||
instance, _ := client.GetVMByUuid(instance_uuid)
|
||||
|
||||
ui.Say("Step: Shutdown and export VPX")
|
||||
|
||||
// Shutdown the VM
|
||||
ui.Say("Shutting down the VM...")
|
||||
instance.CleanShutdown()
|
||||
|
||||
//Export the VM
|
||||
|
||||
export_url := fmt.Sprintf("https://%s/export?vm=%s&session_id=%s",
|
||||
client.Host,
|
||||
instance.Ref,
|
||||
client.Session.(string),
|
||||
)
|
||||
|
||||
export_filename := fmt.Sprintf("%s/%s.xva", config.OutputDir, config.InstanceName)
|
||||
ui.Say("Getting metadata " + export_url)
|
||||
downloadFile(export_url, export_filename)
|
||||
|
||||
disks, _ := instance.GetDisks()
|
||||
for _, disk := range disks {
|
||||
disk_uuid, _ := disk.GetUuid()
|
||||
|
||||
// Basic auth in URL request is required as session token is not
|
||||
// accepted for some reason.
|
||||
// @todo: raise with XAPI team.
|
||||
disk_export_url := fmt.Sprintf("https://%s:%s@%s/export_raw_vdi?vdi=%s",
|
||||
client.Username,
|
||||
client.Password,
|
||||
client.Host,
|
||||
disk_uuid,
|
||||
)
|
||||
|
||||
ui.Say("Getting " + disk_export_url)
|
||||
disk_export_filename := fmt.Sprintf("%s/%s.raw", config.OutputDir, disk_uuid)
|
||||
ui.Say("Downloading " + disk_uuid)
|
||||
downloadFile(disk_export_url, disk_export_filename)
|
||||
}
|
||||
|
||||
|
||||
ui.Say("Download complteded: " + config.OutputDir)
|
||||
|
||||
return multistep.ActionContinue
|
||||
}
|
||||
|
||||
func (stepShutdownAndExport) Cleanup(state multistep.StateBag) {
|
||||
}
|
@ -1,123 +0,0 @@
|
||||
package xenserver
|
||||
|
||||
import (
|
||||
"github.com/mitchellh/multistep"
|
||||
"github.com/mitchellh/packer/packer"
|
||||
"time"
|
||||
"log"
|
||||
"fmt"
|
||||
)
|
||||
|
||||
type stepStartOnHIMN struct{}
|
||||
|
||||
|
||||
/*
|
||||
* This step starts the installed guest on the Host Internal Management Network
|
||||
* as there exists an API to obtain the IP allocated to the VM by XAPI.
|
||||
* This in turn will allow Packer to SSH into the VM, provided NATing has been
|
||||
* enabled on the host.
|
||||
*
|
||||
*/
|
||||
|
||||
func (self *stepStartOnHIMN) Run(state multistep.StateBag) multistep.StepAction {
|
||||
|
||||
ui := state.Get("ui").(packer.Ui)
|
||||
client := state.Get("client").(XenAPIClient)
|
||||
config := state.Get("config").(config)
|
||||
|
||||
ui.Say("Step: Start VM on the Host Internal Mangement Network")
|
||||
|
||||
instance := state.Get("instance").(*VM)
|
||||
|
||||
// Find the HIMN Ref
|
||||
networks, err := client.GetNetworkByNameLabel("Host internal management network")
|
||||
if err != nil || len(networks) == 0 {
|
||||
log.Fatal("Unable to find a host internal management network")
|
||||
log.Fatal(err.Error())
|
||||
return multistep.ActionHalt
|
||||
}
|
||||
|
||||
|
||||
himn := networks[0]
|
||||
|
||||
// Create a VIF for the HIMN
|
||||
himn_vif, err := instance.ConnectNetwork(himn, "0")
|
||||
if err != nil {
|
||||
log.Fatal("Error creating VIF")
|
||||
log.Fatal(err.Error())
|
||||
return multistep.ActionHalt
|
||||
}
|
||||
|
||||
// Start the VM
|
||||
instance.Start(false, false)
|
||||
|
||||
|
||||
var himn_iface_ip string = ""
|
||||
|
||||
// Obtain the allocated IP
|
||||
for i:=0; i < 10; i++ {
|
||||
ips, _ := himn.GetAssignedIPs()
|
||||
log.Printf("IPs: %s", ips)
|
||||
log.Printf("Ref: %s", instance.Ref)
|
||||
|
||||
//Check for instance.Ref in map
|
||||
if vm_ip, ok := ips[himn_vif.Ref]; ok {
|
||||
ui.Say("Found the VM's IP " + vm_ip)
|
||||
himn_iface_ip = vm_ip
|
||||
break
|
||||
}
|
||||
|
||||
ui.Say("Wait for IP address...")
|
||||
time.Sleep(10*time.Second)
|
||||
|
||||
}
|
||||
|
||||
if himn_iface_ip != "" {
|
||||
state.Put("himn_ssh_address", himn_iface_ip)
|
||||
ui.Say("Stored VM's IP " + himn_iface_ip)
|
||||
} else {
|
||||
log.Fatal("Unable to find an IP on the Host-internal management interface")
|
||||
return multistep.ActionHalt
|
||||
}
|
||||
|
||||
|
||||
// Wait for the VM to boot, and check we can ping this interface
|
||||
|
||||
ping_cmd := fmt.Sprintf("ping -c 1 %s", himn_iface_ip)
|
||||
|
||||
err = nil
|
||||
for i:=0; i < 30; i++ {
|
||||
ui.Message(fmt.Sprintf("Attempting to ping interface: %s", ping_cmd))
|
||||
_, err := execute_ssh_cmd(ping_cmd, config.HostIp, "22", config.Username, config.Password)
|
||||
|
||||
if err == nil {
|
||||
ui.Message("Ping success! Continuing...")
|
||||
break
|
||||
}
|
||||
|
||||
time.Sleep(10 * time.Second)
|
||||
}
|
||||
|
||||
if err != nil {
|
||||
log.Fatal("Unable to ping interface. Something is wrong. Has the VM not booted?")
|
||||
log.Fatal(err.Error())
|
||||
return multistep.ActionHalt
|
||||
}
|
||||
|
||||
|
||||
return multistep.ActionContinue
|
||||
|
||||
}
|
||||
|
||||
func (self *stepStartOnHIMN) Cleanup(state multistep.StateBag) {}
|
||||
|
||||
|
||||
func himnSSHIP (state multistep.StateBag) (string, error) {
|
||||
ip := state.Get("himn_ssh_address").(string)
|
||||
return ip, nil
|
||||
}
|
||||
|
||||
|
||||
func himnSSHPort (state multistep.StateBag) (uint, error) {
|
||||
return 22, nil
|
||||
}
|
@ -1,30 +0,0 @@
|
||||
package xenserver
|
||||
|
||||
|
||||
import (
|
||||
"github.com/mitchellh/multistep"
|
||||
"github.com/mitchellh/packer/packer"
|
||||
)
|
||||
|
||||
type stepStartVmPaused struct {}
|
||||
|
||||
func (self *stepStartVmPaused) Run(state multistep.StateBag) multistep.StepAction {
|
||||
|
||||
client := state.Get("client").(XenAPIClient)
|
||||
ui := state.Get("ui").(packer.Ui)
|
||||
|
||||
ui.Say("Step: Start VM Paused")
|
||||
|
||||
instance, _ := client.GetVMByUuid(state.Get("instance_uuid").(string))
|
||||
|
||||
instance.Start(true, false)
|
||||
|
||||
domid, _ := instance.GetDomainId()
|
||||
state.Put("domid", domid)
|
||||
|
||||
return multistep.ActionContinue
|
||||
}
|
||||
|
||||
|
||||
func (self *stepStartVmPaused) Cleanup(state multistep.StateBag) {
|
||||
}
|
@ -1,186 +0,0 @@
|
||||
package xenserver
|
||||
|
||||
/* Heavily borrowed from builder/quemu/step_type_boot_command.go */
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"github.com/mitchellh/go-vnc"
|
||||
"github.com/mitchellh/multistep"
|
||||
"github.com/mitchellh/packer/packer"
|
||||
"log"
|
||||
"net"
|
||||
"strings"
|
||||
"time"
|
||||
"unicode"
|
||||
"unicode/utf8"
|
||||
)
|
||||
|
||||
const KeyLeftShift uint = 0xFFE1
|
||||
|
||||
type bootCommandTemplateData struct {
|
||||
HTTPIP string
|
||||
HTTPPort uint
|
||||
}
|
||||
|
||||
|
||||
type stepTypeBootCommand struct{}
|
||||
|
||||
func (self *stepTypeBootCommand) Run (state multistep.StateBag) multistep.StepAction {
|
||||
config := state.Get("config").(config)
|
||||
ui := state.Get("ui").(packer.Ui)
|
||||
vnc_port := state.Get("local_vnc_port").(uint)
|
||||
http_port := state.Get("http_port").(uint)
|
||||
|
||||
// Connect to the local VNC port as we have set up a SSH port forward
|
||||
ui.Say("Connecting to the VM over VNC")
|
||||
ui.Message(fmt.Sprintf("Using local port: %d", vnc_port))
|
||||
net_conn, err := net.Dial("tcp", fmt.Sprintf("127.0.0.1:%d", vnc_port))
|
||||
|
||||
if err != nil {
|
||||
err := fmt.Errorf("Error connecting to VNC: %s", err)
|
||||
state.Put("error", err)
|
||||
ui.Error(err.Error())
|
||||
return multistep.ActionHalt
|
||||
}
|
||||
|
||||
defer net_conn.Close()
|
||||
|
||||
c, err := vnc.Client(net_conn, &vnc.ClientConfig{Exclusive: true})
|
||||
|
||||
if err != nil {
|
||||
err := fmt.Errorf("Error establishing VNC session: %s", err)
|
||||
state.Put("error", err)
|
||||
ui.Error(err.Error())
|
||||
return multistep.ActionHalt
|
||||
}
|
||||
|
||||
defer c.Close()
|
||||
|
||||
log.Printf("Connected to the VNC console: %s", c.DesktopName)
|
||||
|
||||
// @todo - include http port/ip so kickstarter files can be grabbed
|
||||
tplData := &bootCommandTemplateData {
|
||||
config.LocalIp,
|
||||
http_port,
|
||||
}
|
||||
|
||||
ui.Say("About to type boot commands over VNC...")
|
||||
for _, command := range config.BootCommand {
|
||||
|
||||
command, err := config.tpl.Process(command, tplData)
|
||||
if err != nil {
|
||||
err := fmt.Errorf("Error preparing boot command: %s", err)
|
||||
state.Put("error", err)
|
||||
ui.Error(err.Error())
|
||||
return multistep.ActionHalt
|
||||
}
|
||||
|
||||
// Check for interrupts
|
||||
if _, ok := state.GetOk(multistep.StateCancelled); ok {
|
||||
return multistep.ActionHalt
|
||||
}
|
||||
|
||||
vncSendString(c, command)
|
||||
}
|
||||
|
||||
ui.Say("Finished typing.")
|
||||
|
||||
return multistep.ActionContinue
|
||||
}
|
||||
|
||||
func (self *stepTypeBootCommand) Cleanup (multistep.StateBag) {}
|
||||
|
||||
// Taken from qemu's builder plugin - not an exported function.
|
||||
func vncSendString(c *vnc.ClientConn, original string) {
|
||||
// Scancodes reference: https://github.com/qemu/qemu/blob/master/ui/vnc_keysym.h
|
||||
special := make(map[string]uint32)
|
||||
special["<bs>"] = 0xFF08
|
||||
special["<del>"] = 0xFFFF
|
||||
special["<enter>"] = 0xFF0D
|
||||
special["<esc>"] = 0xFF1B
|
||||
special["<f1>"] = 0xFFBE
|
||||
special["<f2>"] = 0xFFBF
|
||||
special["<f3>"] = 0xFFC0
|
||||
special["<f4>"] = 0xFFC1
|
||||
special["<f5>"] = 0xFFC2
|
||||
special["<f6>"] = 0xFFC3
|
||||
special["<f7>"] = 0xFFC4
|
||||
special["<f8>"] = 0xFFC5
|
||||
special["<f9>"] = 0xFFC6
|
||||
special["<f10>"] = 0xFFC7
|
||||
special["<f11>"] = 0xFFC8
|
||||
special["<f12>"] = 0xFFC9
|
||||
special["<return>"] = 0xFF0D
|
||||
special["<tab>"] = 0xFF09
|
||||
special["<up>"] = 0xFF52
|
||||
special["<down>"] = 0xFF54
|
||||
special["<left>"] = 0xFF51
|
||||
special["<right>"] = 0xFF53
|
||||
special["<spacebar>"] = 0x020
|
||||
special["<insert>"] = 0xFF63
|
||||
special["<home>"] = 0xFF50
|
||||
special["<end>"] = 0xFF57
|
||||
special["<pageUp>"] = 0xFF55
|
||||
special["<pageDown>"] = 0xFF56
|
||||
|
||||
shiftedChars := "~!@#$%^&*()_+{}|:\"<>?"
|
||||
|
||||
// TODO(mitchellh): Ripe for optimizations of some point, perhaps.
|
||||
for len(original) > 0 {
|
||||
var keyCode uint32
|
||||
keyShift := false
|
||||
|
||||
if strings.HasPrefix(original, "<wait>") {
|
||||
log.Printf("Special code '<wait>' found, sleeping one second")
|
||||
time.Sleep(1 * time.Second)
|
||||
original = original[len("<wait>"):]
|
||||
continue
|
||||
}
|
||||
|
||||
if strings.HasPrefix(original, "<wait5>") {
|
||||
log.Printf("Special code '<wait5>' found, sleeping 5 seconds")
|
||||
time.Sleep(5 * time.Second)
|
||||
original = original[len("<wait5>"):]
|
||||
continue
|
||||
}
|
||||
|
||||
if strings.HasPrefix(original, "<wait10>") {
|
||||
log.Printf("Special code '<wait10>' found, sleeping 10 seconds")
|
||||
time.Sleep(10 * time.Second)
|
||||
original = original[len("<wait10>"):]
|
||||
continue
|
||||
}
|
||||
|
||||
for specialCode, specialValue := range special {
|
||||
if strings.HasPrefix(original, specialCode) {
|
||||
log.Printf("Special code '%s' found, replacing with: %d", specialCode, specialValue)
|
||||
keyCode = specialValue
|
||||
original = original[len(specialCode):]
|
||||
break
|
||||
}
|
||||
}
|
||||
|
||||
if keyCode == 0 {
|
||||
r, size := utf8.DecodeRuneInString(original)
|
||||
original = original[size:]
|
||||
keyCode = uint32(r)
|
||||
keyShift = unicode.IsUpper(r) || strings.ContainsRune(shiftedChars, r)
|
||||
|
||||
log.Printf("Sending char '%c', code %d, shift %v", r, keyCode, keyShift)
|
||||
}
|
||||
|
||||
if keyShift {
|
||||
c.KeyEvent(uint32(KeyLeftShift), true)
|
||||
}
|
||||
|
||||
c.KeyEvent(keyCode, true)
|
||||
c.KeyEvent(keyCode, false)
|
||||
|
||||
if keyShift {
|
||||
c.KeyEvent(uint32(KeyLeftShift), false)
|
||||
}
|
||||
|
||||
// qemu is picky, so no matter what, wait a small period
|
||||
time.Sleep(100 * time.Millisecond)
|
||||
}
|
||||
}
|
@ -1,81 +0,0 @@
|
||||
package xenserver
|
||||
|
||||
|
||||
import (
|
||||
"github.com/mitchellh/multistep"
|
||||
"github.com/mitchellh/packer/packer"
|
||||
"os"
|
||||
"strconv"
|
||||
"os/exec"
|
||||
"log"
|
||||
)
|
||||
|
||||
type stepUploadIso struct {}
|
||||
|
||||
func (self *stepUploadIso) Run(state multistep.StateBag) multistep.StepAction {
|
||||
|
||||
client := state.Get("client").(XenAPIClient)
|
||||
config := state.Get("config").(config)
|
||||
ui := state.Get("ui").(packer.Ui)
|
||||
|
||||
ui.Say("Step: Upload ISO to server")
|
||||
iso_path := state.Get("iso_path").(string)
|
||||
|
||||
// Determine the ISO's filesize
|
||||
file, err := os.Open(iso_path)
|
||||
if err != nil {
|
||||
ui.Error(err.Error())
|
||||
return multistep.ActionHalt
|
||||
}
|
||||
stat, err := file.Stat()
|
||||
|
||||
if err != nil {
|
||||
ui.Error(err.Error())
|
||||
return multistep.ActionHalt
|
||||
}
|
||||
|
||||
iso_filesize := stat.Size()
|
||||
|
||||
// Create a VDI with the write size
|
||||
srs, err := client.GetSRByNameLabel(config.SrName)
|
||||
|
||||
sr := srs[0]
|
||||
|
||||
if err != nil {
|
||||
ui.Error(err.Error())
|
||||
return multistep.ActionHalt
|
||||
}
|
||||
|
||||
filesize_str := strconv.FormatInt(iso_filesize, 10)
|
||||
log.Printf("Filesize of the ISO is %d", filesize_str)
|
||||
vdi, err := sr.CreateVdi("Packer Gen " + stat.Name(), filesize_str)
|
||||
|
||||
if err != nil {
|
||||
ui.Error(err.Error())
|
||||
return multistep.ActionHalt
|
||||
}
|
||||
|
||||
// Upload the ISO to this VDI
|
||||
vdi_uuid, _ := vdi.GetUuid()
|
||||
host_url := "https://" + client.Host
|
||||
log.Printf("Host URL: %s", host_url)
|
||||
ui.Say("Uploading ISO to " + vdi_uuid)
|
||||
out, err := exec.Command("/usr/bin/importdisk.py", host_url, client.Username, client.Password, vdi_uuid, iso_path).CombinedOutput()
|
||||
log.Printf("Output: %s", out)
|
||||
|
||||
if err != nil {
|
||||
log.Printf("%s", err)
|
||||
ui.Error(err.Error())
|
||||
return multistep.ActionHalt
|
||||
}
|
||||
|
||||
// Stash the vdi uuid to be used in preference
|
||||
state.Put("iso_vdi_uuid", vdi_uuid)
|
||||
|
||||
|
||||
return multistep.ActionContinue
|
||||
}
|
||||
|
||||
|
||||
func (self *stepUploadIso) Cleanup(state multistep.StateBag) {
|
||||
}
|
@ -1,66 +0,0 @@
|
||||
package xenserver
|
||||
|
||||
import (
|
||||
"github.com/mitchellh/multistep"
|
||||
"github.com/mitchellh/packer/packer"
|
||||
"time"
|
||||
"reflect"
|
||||
)
|
||||
|
||||
|
||||
type stepWait struct{}
|
||||
|
||||
func (self *stepWait) Run(state multistep.StateBag) multistep.StepAction {
|
||||
ui := state.Get("ui").(packer.Ui)
|
||||
client := state.Get("client").(XenAPIClient)
|
||||
|
||||
ui.Say("Step: Wait for install to complete.")
|
||||
|
||||
|
||||
//Expect install to be configured to shutdown on completion
|
||||
|
||||
instance_id := state.Get("instance_uuid").(string)
|
||||
instance, _ := client.GetVMByUuid(instance_id)
|
||||
|
||||
for {
|
||||
time.Sleep(30 * time.Second)
|
||||
ui.Say("Waiting for VM install...")
|
||||
|
||||
power_state, _ := instance.GetPowerState()
|
||||
if power_state == "Halted" {
|
||||
ui.Say("Install has completed. Moving on.")
|
||||
break
|
||||
}
|
||||
}
|
||||
|
||||
// Eject ISO from drive
|
||||
vbds, _ := instance.GetVBDs()
|
||||
for _, vbd := range vbds {
|
||||
rec, _ := vbd.GetRecord()
|
||||
|
||||
// Hack - should encapsulate this in the client really
|
||||
// This is needed because we can't guarentee the type
|
||||
// returned by the xmlrpc lib will be string
|
||||
switch reflect.TypeOf(rec["type"]).Kind() {
|
||||
case reflect.String:
|
||||
if rec["type"].(string) == "CD" {
|
||||
ui.Say("Ejecting CD...")
|
||||
vbd.Eject()
|
||||
}
|
||||
default:
|
||||
break
|
||||
}
|
||||
}
|
||||
|
||||
// Destroy all connected VIFs
|
||||
vifs, _ := instance.GetVIFs()
|
||||
for _, vif := range vifs {
|
||||
ui.Message("Destroying VIF " + vif.Ref)
|
||||
vif.Destroy()
|
||||
}
|
||||
|
||||
return multistep.ActionContinue
|
||||
}
|
||||
|
||||
func (self *stepWait) Cleanup(state multistep.StateBag) {}
|
||||
|
209
builder/xenserver/xva/builder.go
Normal file
209
builder/xenserver/xva/builder.go
Normal file
@ -0,0 +1,209 @@
|
||||
package xva
|
||||
|
||||
import (
|
||||
"context"
|
||||
"errors"
|
||||
"time"
|
||||
|
||||
"github.com/hashicorp/hcl/v2/hcldec"
|
||||
"github.com/hashicorp/packer-plugin-sdk/communicator"
|
||||
"github.com/hashicorp/packer-plugin-sdk/multistep"
|
||||
commonsteps "github.com/hashicorp/packer-plugin-sdk/multistep/commonsteps"
|
||||
"github.com/hashicorp/packer-plugin-sdk/packer"
|
||||
hconfig "github.com/hashicorp/packer-plugin-sdk/template/config"
|
||||
"github.com/hashicorp/packer-plugin-sdk/template/interpolate"
|
||||
xsclient "github.com/terra-farm/go-xen-api-client"
|
||||
xscommon "github.com/xenserver/packer-builder-xenserver/builder/xenserver/common"
|
||||
)
|
||||
|
||||
type Builder struct {
|
||||
config xscommon.Config
|
||||
runner multistep.Runner
|
||||
}
|
||||
|
||||
func (self *Builder) ConfigSpec() hcldec.ObjectSpec { return self.config.FlatMapstructure().HCL2Spec() }
|
||||
|
||||
func (self *Builder) Prepare(raws ...interface{}) (params []string, warns []string, retErr error) {
|
||||
|
||||
var errs *packer.MultiError
|
||||
|
||||
err := hconfig.Decode(&self.config, &hconfig.DecodeOpts{
|
||||
Interpolate: true,
|
||||
InterpolateFilter: &interpolate.RenderFilter{
|
||||
Exclude: []string{
|
||||
"boot_command",
|
||||
},
|
||||
},
|
||||
}, raws...)
|
||||
|
||||
if err != nil {
|
||||
errs = packer.MultiErrorAppend(errs, err)
|
||||
}
|
||||
|
||||
errs = packer.MultiErrorAppend(
|
||||
errs, self.config.CommonConfig.Prepare(self.config.GetInterpContext(), &self.config.PackerConfig)...)
|
||||
errs = packer.MultiErrorAppend(errs, self.config.SSHConfig.Prepare(self.config.GetInterpContext())...)
|
||||
|
||||
// Set default values
|
||||
if self.config.VCPUsMax == 0 {
|
||||
self.config.VCPUsMax = 1
|
||||
}
|
||||
|
||||
if self.config.VCPUsAtStartup == 0 {
|
||||
self.config.VCPUsAtStartup = 1
|
||||
}
|
||||
|
||||
if self.config.VCPUsAtStartup > self.config.VCPUsMax {
|
||||
self.config.VCPUsAtStartup = self.config.VCPUsMax
|
||||
}
|
||||
|
||||
if self.config.VMMemory == 0 {
|
||||
self.config.VMMemory = 1024
|
||||
}
|
||||
|
||||
if len(self.config.PlatformArgs) == 0 {
|
||||
pargs := make(map[string]string)
|
||||
pargs["viridian"] = "false"
|
||||
pargs["nx"] = "true"
|
||||
pargs["pae"] = "true"
|
||||
pargs["apic"] = "true"
|
||||
pargs["timeoffset"] = "0"
|
||||
pargs["acpi"] = "1"
|
||||
self.config.PlatformArgs = pargs
|
||||
}
|
||||
|
||||
// Validation
|
||||
|
||||
if self.config.SourcePath == "" && self.config.CloneTemplate == "" {
|
||||
errs = packer.MultiErrorAppend(
|
||||
errs, errors.New("Either source_path or clone_template must be specified"))
|
||||
} else if self.config.SourcePath != "" && self.config.CloneTemplate != "" {
|
||||
errs = packer.MultiErrorAppend(
|
||||
errs, errors.New("Only one of source_path and clone_template must be specified"))
|
||||
}
|
||||
|
||||
if len(errs.Errors) > 0 {
|
||||
retErr = errors.New(errs.Error())
|
||||
}
|
||||
|
||||
return nil, nil, retErr
|
||||
|
||||
}
|
||||
|
||||
func (self *Builder) Run(ctx context.Context, ui packer.Ui, hook packer.Hook) (packer.Artifact, error) {
|
||||
//Setup XAPI client
|
||||
c, err := xscommon.NewXenAPIClient(self.config.HostIp, self.config.Username, self.config.Password)
|
||||
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
ui.Say("XAPI client session established")
|
||||
|
||||
c.GetClient().Host.GetAll(c.GetSessionRef())
|
||||
|
||||
//Share state between the other steps using a statebag
|
||||
state := new(multistep.BasicStateBag)
|
||||
state.Put("client", c)
|
||||
state.Put("config", self.config)
|
||||
state.Put("commonconfig", self.config.CommonConfig)
|
||||
state.Put("hook", hook)
|
||||
state.Put("ui", ui)
|
||||
|
||||
httpReqChan := make(chan string, 1)
|
||||
|
||||
//Build the steps
|
||||
steps := []multistep.Step{
|
||||
&xscommon.StepPrepareOutputDir{
|
||||
Force: self.config.PackerForce,
|
||||
Path: self.config.OutputDir,
|
||||
},
|
||||
&commonsteps.StepCreateFloppy{
|
||||
Files: self.config.FloppyFiles,
|
||||
Label: "cidata",
|
||||
},
|
||||
&xscommon.StepHTTPServer{
|
||||
Chan: httpReqChan,
|
||||
},
|
||||
&xscommon.StepUploadVdi{
|
||||
VdiNameFunc: func() string {
|
||||
return "Packer-floppy-disk"
|
||||
},
|
||||
ImagePathFunc: func() string {
|
||||
if floppyPath, ok := state.GetOk("floppy_path"); ok {
|
||||
return floppyPath.(string)
|
||||
}
|
||||
return ""
|
||||
},
|
||||
VdiUuidKey: "floppy_vdi_uuid",
|
||||
},
|
||||
&xscommon.StepFindVdi{
|
||||
VdiName: self.config.ToolsIsoName,
|
||||
VdiUuidKey: "tools_vdi_uuid",
|
||||
},
|
||||
&xscommon.StepCreateInstance{
|
||||
AssumePreInstalledOS: true,
|
||||
},
|
||||
new(stepImportInstance),
|
||||
&xscommon.StepAttachVdi{
|
||||
VdiUuidKey: "floppy_vdi_uuid",
|
||||
VdiType: xsclient.VbdTypeFloppy,
|
||||
},
|
||||
&xscommon.StepAttachVdi{
|
||||
VdiUuidKey: "tools_vdi_uuid",
|
||||
VdiType: xsclient.VbdTypeCD,
|
||||
},
|
||||
new(xscommon.StepStartVmPaused),
|
||||
new(xscommon.StepSetVmHostSshAddress),
|
||||
new(xscommon.StepBootWait),
|
||||
&xscommon.StepTypeBootCommand{
|
||||
Ctx: *self.config.GetInterpContext(),
|
||||
},
|
||||
&xscommon.StepWaitForIP{
|
||||
Chan: httpReqChan,
|
||||
Timeout: 300 * time.Minute, /*self.config.InstallTimeout*/ // @todo change this
|
||||
},
|
||||
&xscommon.StepForwardPortOverSSH{
|
||||
RemotePort: xscommon.InstanceSSHPort,
|
||||
RemoteDest: xscommon.InstanceSSHIP,
|
||||
HostPortMin: self.config.HostPortMin,
|
||||
HostPortMax: self.config.HostPortMax,
|
||||
ResultKey: "local_ssh_port",
|
||||
},
|
||||
&communicator.StepConnect{
|
||||
Config: &self.config.SSHConfig.Comm,
|
||||
Host: xscommon.InstanceSSHIP,
|
||||
SSHConfig: self.config.Comm.SSHConfigFunc(),
|
||||
SSHPort: xscommon.InstanceSSHPort,
|
||||
},
|
||||
new(commonsteps.StepProvision),
|
||||
new(xscommon.StepShutdown),
|
||||
new(xscommon.StepSetVmToTemplate),
|
||||
&xscommon.StepDetachVdi{
|
||||
VdiUuidKey: "tools_vdi_uuid",
|
||||
},
|
||||
&xscommon.StepDetachVdi{
|
||||
VdiUuidKey: "floppy_vdi_uuid",
|
||||
},
|
||||
new(xscommon.StepExport),
|
||||
}
|
||||
|
||||
self.runner = &multistep.BasicRunner{Steps: steps}
|
||||
self.runner.Run(ctx, state)
|
||||
|
||||
if rawErr, ok := state.GetOk("error"); ok {
|
||||
return nil, rawErr.(error)
|
||||
}
|
||||
|
||||
// If we were interrupted or cancelled, then just exit.
|
||||
if _, ok := state.GetOk(multistep.StateCancelled); ok {
|
||||
return nil, errors.New("Build was cancelled.")
|
||||
}
|
||||
if _, ok := state.GetOk(multistep.StateHalted); ok {
|
||||
return nil, errors.New("Build was halted.")
|
||||
}
|
||||
|
||||
artifact, _ := xscommon.NewArtifact(self.config.OutputDir)
|
||||
|
||||
return artifact, nil
|
||||
}
|
194
builder/xenserver/xva/builder_test.go
Normal file
194
builder/xenserver/xva/builder_test.go
Normal file
@ -0,0 +1,194 @@
|
||||
package xva
|
||||
|
||||
import (
|
||||
"testing"
|
||||
|
||||
"github.com/hashicorp/packer-plugin-sdk/common"
|
||||
"github.com/hashicorp/packer-plugin-sdk/packer"
|
||||
)
|
||||
|
||||
func testConfig() map[string]interface{} {
|
||||
return map[string]interface{}{
|
||||
"remote_host": "localhost",
|
||||
"remote_username": "admin",
|
||||
"remote_password": "admin",
|
||||
"vm_name": "foo",
|
||||
"shutdown_command": "yes",
|
||||
"ssh_username": "foo",
|
||||
"source_path": ".",
|
||||
|
||||
common.BuildNameConfigKey: "foo",
|
||||
}
|
||||
}
|
||||
|
||||
func TestBuilder_ImplementsBuilder(t *testing.T) {
|
||||
var raw interface{}
|
||||
raw = &Builder{}
|
||||
if _, ok := raw.(packer.Builder); !ok {
|
||||
t.Error("Builder must implement builder.")
|
||||
}
|
||||
}
|
||||
|
||||
func TestBuilderPrepare_Defaults(t *testing.T) {
|
||||
var b Builder
|
||||
config := testConfig()
|
||||
_, warns, err := b.Prepare(config)
|
||||
if len(warns) > 0 {
|
||||
t.Fatalf("bad: %#v", warns)
|
||||
}
|
||||
if err != nil {
|
||||
t.Fatalf("should not have error: %s", err)
|
||||
}
|
||||
|
||||
if b.config.ToolsIsoName != "" {
|
||||
t.Errorf("bad tools ISO name: %s", b.config.ToolsIsoName)
|
||||
}
|
||||
|
||||
if b.config.VMName == "" {
|
||||
t.Errorf("bad vm name: %s", b.config.VMName)
|
||||
}
|
||||
|
||||
if b.config.Format != "xva" {
|
||||
t.Errorf("bad format: %s", b.config.Format)
|
||||
}
|
||||
|
||||
if b.config.KeepVM != "never" {
|
||||
t.Errorf("bad keep instance: %s", b.config.KeepVM)
|
||||
}
|
||||
|
||||
if b.config.HostSshPort != 22 {
|
||||
t.Errorf("bad ssh port: %d", b.config.HostSshPort)
|
||||
}
|
||||
}
|
||||
|
||||
func TestBuilderPrepare_Format(t *testing.T) {
|
||||
var b Builder
|
||||
config := testConfig()
|
||||
|
||||
// Bad
|
||||
config["format"] = "foo"
|
||||
_, warns, err := b.Prepare(config)
|
||||
if len(warns) > 0 {
|
||||
t.Fatalf("bad: %#v", warns)
|
||||
}
|
||||
if err == nil {
|
||||
t.Fatal("should have error")
|
||||
}
|
||||
|
||||
// Good
|
||||
config["format"] = "vdi_raw"
|
||||
b = Builder{}
|
||||
_, warns, err = b.Prepare(config)
|
||||
if len(warns) > 0 {
|
||||
t.Fatalf("bad: %#v", warns)
|
||||
}
|
||||
if err != nil {
|
||||
t.Fatalf("should not have error: %s", err)
|
||||
}
|
||||
}
|
||||
|
||||
func TestBuilderPrepare_HTTPPort(t *testing.T) {
|
||||
var b Builder
|
||||
config := testConfig()
|
||||
|
||||
// Bad
|
||||
config["http_port_min"] = 1000
|
||||
config["http_port_max"] = 500
|
||||
_, warns, err := b.Prepare(config)
|
||||
if len(warns) > 0 {
|
||||
t.Fatalf("bad: %#v", warns)
|
||||
}
|
||||
if err == nil {
|
||||
t.Fatal("should have error")
|
||||
}
|
||||
|
||||
// Bad
|
||||
config["http_port_min"] = -500
|
||||
b = Builder{}
|
||||
_, warns, err = b.Prepare(config)
|
||||
if len(warns) > 0 {
|
||||
t.Fatalf("bad: %#v", warns)
|
||||
}
|
||||
if err == nil {
|
||||
t.Fatal("should have error")
|
||||
}
|
||||
|
||||
// Good
|
||||
config["http_port_min"] = 500
|
||||
config["http_port_max"] = 1000
|
||||
b = Builder{}
|
||||
_, warns, err = b.Prepare(config)
|
||||
if len(warns) > 0 {
|
||||
t.Fatalf("bad: %#v", warns)
|
||||
}
|
||||
if err != nil {
|
||||
t.Fatalf("should not have error: %s", err)
|
||||
}
|
||||
}
|
||||
|
||||
func TestBuilderPrepare_InvalidKey(t *testing.T) {
|
||||
var b Builder
|
||||
config := testConfig()
|
||||
|
||||
// Add a random key
|
||||
config["i_should_not_be_valid"] = true
|
||||
_, warns, err := b.Prepare(config)
|
||||
if len(warns) > 0 {
|
||||
t.Fatalf("bad: %#v", warns)
|
||||
}
|
||||
if err == nil {
|
||||
t.Fatal("should have error")
|
||||
}
|
||||
}
|
||||
|
||||
func TestBuilderPrepare_KeepVM(t *testing.T) {
|
||||
var b Builder
|
||||
config := testConfig()
|
||||
|
||||
// Bad
|
||||
config["keep_vm"] = "foo"
|
||||
_, warns, err := b.Prepare(config)
|
||||
if len(warns) > 0 {
|
||||
t.Fatalf("bad: %#v", warns)
|
||||
}
|
||||
if err == nil {
|
||||
t.Fatal("should have error")
|
||||
}
|
||||
|
||||
// Good
|
||||
config["keep_vm"] = "always"
|
||||
b = Builder{}
|
||||
_, warns, err = b.Prepare(config)
|
||||
if len(warns) > 0 {
|
||||
t.Fatalf("bad: %#v", warns)
|
||||
}
|
||||
if err != nil {
|
||||
t.Fatalf("should not have error: %s", err)
|
||||
}
|
||||
}
|
||||
|
||||
func TestBuilderPrepare_SourcePath(t *testing.T) {
|
||||
var b Builder
|
||||
config := testConfig()
|
||||
|
||||
// Bad
|
||||
config["source_path"] = ""
|
||||
_, warns, err := b.Prepare(config)
|
||||
if len(warns) > 0 {
|
||||
t.Fatalf("bad: %#v", warns)
|
||||
}
|
||||
if err == nil {
|
||||
t.Fatal("should have error")
|
||||
}
|
||||
|
||||
// Good
|
||||
config["source_path"] = "."
|
||||
b = Builder{}
|
||||
_, warns, err = b.Prepare(config)
|
||||
if len(warns) > 0 {
|
||||
t.Fatalf("bad: %#v", warns)
|
||||
}
|
||||
if err != nil {
|
||||
t.Fatalf("should not have error: %s", err)
|
||||
}
|
||||
}
|
126
builder/xenserver/xva/step_import_instance.go
Normal file
126
builder/xenserver/xva/step_import_instance.go
Normal file
@ -0,0 +1,126 @@
|
||||
package xva
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"log"
|
||||
"os"
|
||||
|
||||
"github.com/hashicorp/packer-plugin-sdk/multistep"
|
||||
"github.com/hashicorp/packer-plugin-sdk/packer"
|
||||
xsclient "github.com/terra-farm/go-xen-api-client"
|
||||
xscommon "github.com/xenserver/packer-builder-xenserver/builder/xenserver/common"
|
||||
)
|
||||
|
||||
type stepImportInstance struct {
|
||||
instance xsclient.VMRef
|
||||
vdi xsclient.VDIRef
|
||||
}
|
||||
|
||||
func (self *stepImportInstance) Run(ctx context.Context, state multistep.StateBag) multistep.StepAction {
|
||||
|
||||
c := state.Get("client").(*xscommon.Connection)
|
||||
config := state.Get("config").(xscommon.Config)
|
||||
ui := state.Get("ui").(packer.Ui)
|
||||
|
||||
ui.Say("Step: Import Instance")
|
||||
|
||||
if config.SourcePath == "" {
|
||||
log.Println("Skipping importing instance - no `source_path` configured.")
|
||||
return multistep.ActionContinue
|
||||
}
|
||||
|
||||
// find the SR
|
||||
srs, err := c.GetClient().SR.GetAll(c.GetSessionRef())
|
||||
sr := srs[0]
|
||||
if err != nil {
|
||||
ui.Error(fmt.Sprintf("Unable to get SR: %s", err.Error()))
|
||||
return multistep.ActionHalt
|
||||
}
|
||||
|
||||
// Open the file for reading (NB: httpUpload closes the file for us)
|
||||
fh, err := os.Open(config.SourcePath)
|
||||
if err != nil {
|
||||
ui.Error(fmt.Sprintf("Unable to open XVA '%s': %s", config.SourcePath, err.Error()))
|
||||
return multistep.ActionHalt
|
||||
}
|
||||
|
||||
result, err := xscommon.HTTPUpload(fmt.Sprintf("https://%s/import?session_id=%s&sr_id=%s",
|
||||
c.Host,
|
||||
c.GetSession(),
|
||||
sr,
|
||||
), fh, state)
|
||||
if err != nil {
|
||||
ui.Error(fmt.Sprintf("Unable to upload VDI: %s", err.Error()))
|
||||
return multistep.ActionHalt
|
||||
}
|
||||
if result == "" {
|
||||
ui.Error("XAPI did not reply with an instance reference")
|
||||
return multistep.ActionHalt
|
||||
}
|
||||
|
||||
instance := xsclient.VMRef(result)
|
||||
|
||||
instanceId, err := c.GetClient().VM.GetUUID(c.GetSessionRef(), instance)
|
||||
if err != nil {
|
||||
ui.Error(fmt.Sprintf("Unable to get VM UUID: %s", err.Error()))
|
||||
return multistep.ActionHalt
|
||||
}
|
||||
state.Put("instance_uuid", instanceId)
|
||||
|
||||
err = c.GetClient().VM.SetVCPUsMax(c.GetSessionRef(), instance, int(config.VCPUsMax))
|
||||
if err != nil {
|
||||
ui.Error(fmt.Sprintf("Error setting VM VCPUs Max=%d: %s", config.VCPUsMax, err.Error()))
|
||||
return multistep.ActionHalt
|
||||
}
|
||||
|
||||
err = c.GetClient().VM.SetVCPUsAtStartup(c.GetSessionRef(), instance, int(config.VCPUsAtStartup))
|
||||
if err != nil {
|
||||
ui.Error(fmt.Sprintf("Error setting VM VCPUs At Startup=%d: %s", config.VCPUsAtStartup, err.Error()))
|
||||
return multistep.ActionHalt
|
||||
}
|
||||
|
||||
err = c.GetClient().VM.SetNameDescription(c.GetSessionRef(), instance, config.VMDescription)
|
||||
if err != nil {
|
||||
ui.Error(fmt.Sprintf("Error setting VM description: %s", err.Error()))
|
||||
return multistep.ActionHalt
|
||||
}
|
||||
|
||||
err = xscommon.AddVMTags(c, instance, config.VMTags)
|
||||
if err != nil {
|
||||
ui.Error(fmt.Sprintf("Failed to add tags: %s", err.Error()))
|
||||
return multistep.ActionHalt
|
||||
}
|
||||
|
||||
ui.Say(fmt.Sprintf("Imported instance '%s'", instanceId))
|
||||
|
||||
return multistep.ActionContinue
|
||||
}
|
||||
|
||||
func (self *stepImportInstance) Cleanup(state multistep.StateBag) {
|
||||
/*
|
||||
config := state.Get("config").(config)
|
||||
if config.ShouldKeepVM(state) {
|
||||
return
|
||||
}
|
||||
|
||||
ui := state.Get("ui").(packer.Ui)
|
||||
|
||||
if self.instance != nil {
|
||||
ui.Say("Destroying VM")
|
||||
_ = self.instance.HardShutdown() // redundant, just in case
|
||||
err := self.instance.Destroy()
|
||||
if err != nil {
|
||||
ui.Error(err.Error())
|
||||
}
|
||||
}
|
||||
|
||||
if self.vdi != nil {
|
||||
ui.Say("Destroying VDI")
|
||||
err := self.vdi.Destroy()
|
||||
if err != nil {
|
||||
ui.Error(err.Error())
|
||||
}
|
||||
}
|
||||
*/
|
||||
}
|
305
docs/builders/iso/xenserver-iso.html.markdown
Normal file
305
docs/builders/iso/xenserver-iso.html.markdown
Normal file
@ -0,0 +1,305 @@
|
||||
---
|
||||
layout: "docs"
|
||||
page_title: "XenServer Builder (from an ISO)"
|
||||
description: |-
|
||||
The XenServer Packer builder is able to create XenServer virtual machines and export them either as an XVA or a VDI and create VM templates starting from an ISO image.
|
||||
---
|
||||
|
||||
# XenServer Builder (from an ISO)
|
||||
|
||||
Type: `xenserver-iso`
|
||||
|
||||
The XenServer Packer builder is able to create [XenServer](https://www.xenserver.org/)
|
||||
virtual machines and export them either as an XVA or a VDI and create VM templates starting from an ISO image.
|
||||
|
||||
The builder builds a virtual machine by creating a new virtual machine
|
||||
from scratch, booting it, installing an OS, provisioning software within
|
||||
the OS, then shutting it down. The result of the XenServer builder is a
|
||||
directory containing all the files necessary to run the virtual machine
|
||||
portably.
|
||||
|
||||
## Configuration Reference
|
||||
|
||||
There are many configuration options available for the XenServer builder.
|
||||
They are organized below into two categories: required and optional. Within
|
||||
each category, the available options are alphabetized and described.
|
||||
|
||||
### Required:
|
||||
|
||||
* `iso_checksum` (string) - The checksum for the OS ISO file. Because ISO
|
||||
files are so large, this is required and Packer will verify it prior
|
||||
to booting a virtual machine with the ISO attached. The type of
|
||||
the checksum is specified within the checksum field as a prefix, ex:
|
||||
"md5:{$checksum}". The type of the checksum can also be omitted and
|
||||
Packer will try to infer it based on string length. Valid values are
|
||||
"none", "{$checksum}", "md5:{$checksum}", "sha1:{$checksum}",
|
||||
"sha256:{$checksum}", "sha512:{$checksum}" or "file:{$path}". Here is
|
||||
an example list of valid checksum values:
|
||||
* `md5:090992ba9fd140077b0661cb75f7ce13`
|
||||
* `090992ba9fd140077b0661cb75f7ce13`
|
||||
* `sha1:ebfb681885ddf1234c18094a45bbeafd91467911`
|
||||
* `ebfb681885ddf1234c18094a45bbeafd91467911`
|
||||
* `sha256:ed363350696a726b7932db864dda019bd2017365c9e299627830f06954643f93`
|
||||
* `ed363350696a726b7932db864dda019bd2017365c9e299627830f06954643f93`
|
||||
* `file:http://releases.ubuntu.com/20.04/SHA256SUMS`
|
||||
* `file:file://./local/path/file.sum`
|
||||
* `file:./local/path/file.sum`
|
||||
* `none`
|
||||
* Although the checksum will not be verified when it is set to "none",
|
||||
this is not recommended since these files can be very large and
|
||||
corruption does happen from time to time.
|
||||
|
||||
|
||||
* `iso_url` (string) - A URL to the ISO containing the installation image.
|
||||
This URL can be either an HTTP URL or a file URL (or path to a file).
|
||||
If this is an HTTP URL, Packer will download it and cache it between
|
||||
runs.
|
||||
|
||||
* `remote_host` (string) - The host of the Xenserver / XCP-ng pool primary. Typically, these will be specified through
|
||||
environment variables as seen in the [examples](../../../examples).
|
||||
|
||||
* `remote_ssh_port` (integer) - The port that SSH will be listening on in the Xenserver / XCP-ng pool primary. By default this is 22.
|
||||
|
||||
* `remote_username` (string) - The XenServer username used to access the remote machine.
|
||||
|
||||
* `remote_password` (string) - The XenServer password for access to the remote machine.
|
||||
|
||||
* `ssh_username` (string) - The username to use to SSH into the machine
|
||||
once the OS is installed.
|
||||
|
||||
### Optional:
|
||||
|
||||
* `boot_command` (array of strings) - This is an array of commands to type
|
||||
when the virtual machine is first booted. The goal of these commands should
|
||||
be to type just enough to initialize the operating system installer. Special
|
||||
keys can be typed as well, and are covered in the section below on the boot
|
||||
command. If this is not specified, it is assumed the installer will start
|
||||
itself. See the [Ubuntu](../../../examples/ubuntu) and [centos](../../../examples/centos) examples to see how these
|
||||
are used to launch autoinstall and kickstart respectively.
|
||||
|
||||
* `boot_wait` (string) - The time to wait after booting the initial virtual
|
||||
machine before typing the `boot_command`. The value of this should be
|
||||
a duration. Examples are `5s` and `1m30s` which will cause Packer to wait
|
||||
five seconds and one minute 30 seconds, respectively. If this isn't specified,
|
||||
the default is 10 seconds.
|
||||
|
||||
* `clone_template` (string) - The template to clone. Defaults to "Other install
|
||||
media", this is "other", but you can get _dramatic_ performance improvements
|
||||
by setting this to the proper value. To view all available values for this
|
||||
run `xe template-list`. Setting the correct value hints to XenServer how to
|
||||
optimize the virtual hardware to work best with that operating system.
|
||||
|
||||
* `disk_name` (string) - The name of the hard disk to create for the VM.
|
||||
By default, the name is "Packer-disk".
|
||||
|
||||
* `disk_size` (integer) - The size, in megabytes, of the hard disk to create
|
||||
for the VM. By default, this is 40000 (about 40 GB).
|
||||
|
||||
* `firmware` (string) - Whether to use `bios` or `uefi` as the boot firmware
|
||||
for the resulting VM. Defaults to `bios`.
|
||||
|
||||
* `floppy_files` (array of strings) - A list of files to place onto a floppy
|
||||
disk that is attached when the VM is booted. This is most useful
|
||||
for unattended Windows installs, which look for an `Autounattend.xml` file
|
||||
on removable media. By default, no floppy will be attached. All files
|
||||
listed in this setting get placed into the root directory of the floppy
|
||||
and the floppy is attached as the first floppy device. Currently, no
|
||||
support exists for creating sub-directories on the floppy. Wildcard
|
||||
characters (\*, ?, and []) are allowed. Directory names are also allowed,
|
||||
which will add all the files found in the directory to the floppy.
|
||||
|
||||
* `format` (string) - Either "xva", "xva_compressed", "vdi_raw" or "none", this specifies the
|
||||
output format of the exported virtual machine. This defaults to "xva". Set to
|
||||
"vdi_raw" to export just the raw disk image. Set to "none" to export nothing;
|
||||
this is only useful with "keep_vm" set to "always" or "on_success".
|
||||
|
||||
* `http_directory` (string) - Path to a directory to serve using an HTTP
|
||||
server. The files in this directory will be available over HTTP which will
|
||||
be requestable from the virtual machine. This is useful for hosting
|
||||
kickstart files and so on. By default, this is `""`, which means no HTTP
|
||||
server will be started. The address and port of the HTTP server will be
|
||||
available as variables in `boot_command`. This is covered in more detail
|
||||
below.
|
||||
|
||||
* `http_port_min` and `http_port_max` (integer) - These are the minimum and
|
||||
maximum port to use for the HTTP server started to serve the `http_directory`.
|
||||
Because Packer often runs in parallel, Packer will choose a randomly available
|
||||
port in this range to run the HTTP server. If you want to force the HTTP
|
||||
server to be on one port, make this minimum and maximum port the same.
|
||||
By default, the values are 8000 and 9000, respectively.
|
||||
|
||||
* `install_timeout` (string) - The amount of time to wait after booting the VM
|
||||
for the installer to shut itself down.
|
||||
If it doesn't shut down in this time, it is an error. By default, the timeout
|
||||
is "200m", or over three hours.
|
||||
|
||||
* `iso_urls` (array of strings) - Multiple URLs for the ISO to download.
|
||||
Packer will try these in order. If anything goes wrong attempting to download
|
||||
or while downloading a single URL, it will move on to the next. All URLs
|
||||
must point to the same file (same checksum). By default, this is empty
|
||||
and `iso_url` is used. Only one of `iso_url` or `iso_urls` can be specified.
|
||||
|
||||
* `tools_iso_name` (string) - Choose the tools iso you want to use.
|
||||
Usually "guest-tools.iso", or "xs-tools.iso". Not setting this variable causes no tools-related
|
||||
ISO to be attached.
|
||||
|
||||
* `keep_vm` (string) - Determine when to keep the VM and when to clean it up. This
|
||||
can be `always`, `never` or `on_success`. The default is `never`, and Packer
|
||||
always deletes the VM regardless of whether the process succeeded and an artifact
|
||||
was produced. `always` asks Packer to leave the VM at the end of the process
|
||||
regardless of success. `on_success` requests that the VM only be cleaned up if an
|
||||
artifact was produced. The latter is useful for debugging templates that fail.
|
||||
|
||||
* `ip_getter` (string) - Defines the method by which the IP of the guest machine is
|
||||
identified. Options are: `auto`, `tools`, `http`. The default is `auto`, which will
|
||||
attempt both methods. `tools` requires that the guest tools be installed and functional
|
||||
inside the quest machine.
|
||||
|
||||
* `skip_set_template` (bool) - If you want to get the full XVA, to be able to import the VM directly
|
||||
instead of using the output template, you can set this to `true`.
|
||||
|
||||
* `network_names` (array of strings) - A list of networks identified by their name label which
|
||||
will be used for the VM during creation. The first network will correspond to the VM's
|
||||
first network interface (VIF), the second will correspond to the second VIF and so on.
|
||||
|
||||
* `export_network_names` (array of strings) - A list of networks identified by their name label which
|
||||
will be attached to the export. The first network will correspond to the VM's
|
||||
first network interface (VIF), the second will correspond to the second VIF and so on.
|
||||
|
||||
* `output_directory` (string) - This is the path to the directory where the
|
||||
resulting virtual machine will be created. This may be relative or absolute.
|
||||
If relative, the path is relative to the working directory when `packer`
|
||||
is executed. This directory must not exist or be empty prior to running the builder.
|
||||
By default, this is "output-BUILDNAME" where "BUILDNAME" is the name
|
||||
of the build.
|
||||
|
||||
* `platform_args` (object of key/value strings) - The platform args.
|
||||
Defaults to
|
||||
|
||||
```json
|
||||
{
|
||||
"viridian": "false",
|
||||
"nx": "true",
|
||||
"pae": "true",
|
||||
"apic": "true",
|
||||
"timeoffset": "0",
|
||||
"acpi": "1",
|
||||
"cores-per-socket": "1"
|
||||
}
|
||||
```
|
||||
|
||||
* `shutdown_command` (string) - The command to use to gracefully shut down
|
||||
the machine once all the provisioning is done. If this is omitted, packer
|
||||
will shut down the VM gracefully through the Xen api's vm shutdown command. Unless
|
||||
you have special requirements this should typically be left to its default.
|
||||
|
||||
* `sr_name` (string) - The SR to use for storing the disk for the VM that Packer
|
||||
creates. By default, the default SR of the system will be used.
|
||||
|
||||
* `sr_iso_name` (string) - The SR to use for uploading the provided ISO.
|
||||
By default, the default SR of the system will be used.
|
||||
|
||||
* `ssh_host_port_min` and `ssh_host_port_max` (integer) - The minimum and
|
||||
maximum port to use for the SSH port on the host machine which is forwarded
|
||||
to the SSH port on the guest machine. Because Packer often runs in parallel,
|
||||
Packer will choose a randomly available port in this range to use as the
|
||||
host port.
|
||||
|
||||
* `ssh_key_path` (string) - Path to a private key to use for authenticating
|
||||
with SSH. By default, this is not set (key-based auth won't be used).
|
||||
The associated public key is expected to already be configured on the
|
||||
VM being prepared by some other process (kickstart, etc.).
|
||||
|
||||
* `ssh_password` (string) - The password for `ssh_username` to use to
|
||||
authenticate with SSH. By default, this is the empty string.
|
||||
|
||||
* `ssh_port` (integer) - The port that SSH will be listening on in the guest
|
||||
virtual machine. By default, this is `22`.
|
||||
|
||||
* `ssh_wait_timeout` (string) - The duration to wait for SSH to become
|
||||
available. By default, this is `20m`, or 20 minutes. **Note**: that this should
|
||||
be quite long since the timer begins as soon as the virtual machine is booted.
|
||||
|
||||
* `tools_iso_name` (string) - The name of the XenServer Tools ISO. Defaults to
|
||||
`xs-tools.iso`.
|
||||
|
||||
* `vm_description` (string) - The description of the new virtual
|
||||
machine. By default, this is an empty string.
|
||||
|
||||
* `vm_name` (string) - This is the name of the new virtual
|
||||
machine, without the file extension. By default, this is
|
||||
`packer-BUILDNAME-TIMESTAMP`, where "BUILDNAME" is the name of the build.
|
||||
|
||||
* `vcpus_max` (integer) - The maximum number of VCPUs for the VM.
|
||||
By default, this is `1`.
|
||||
|
||||
* `vcpus_atstartup` (integer) - The number of startup VCPUs for the VM.
|
||||
By default, this is `1`.
|
||||
|
||||
* `vm_memory` (integer) - The size, in megabytes, of the amount of memory to
|
||||
allocate for the VM. By default, this is `1024` (1 GB).
|
||||
|
||||
* `vm_tags` (array of strings) - A list of tags to add to the VM
|
||||
|
||||
## Differences with other Packer builders
|
||||
|
||||
Currently, the XenServer builder has some quirks when compared with other Packer builders.
|
||||
|
||||
The builder currently only works remotely.
|
||||
|
||||
The installer is expected to shut down the VM to indicate that it has completed. This is in contrast to other builders,
|
||||
which instead detect completion by a successful SSH connection. The reason for this difference is that currently the
|
||||
builder has no way of knowing what the IP address of the VM is without starting it on the HIMN.
|
||||
|
||||
## Boot Command
|
||||
|
||||
The `boot_command` configuration is very important: it specifies the keys
|
||||
to type when the virtual machine is first booted in order to start the
|
||||
OS installer. This command is typed after `boot_wait`, which gives the
|
||||
virtual machine some time to actually load the ISO.
|
||||
|
||||
As documented above, the `boot_command` is an array of strings. The
|
||||
strings are all typed in sequence. It is an array only to improve readability
|
||||
within the template.
|
||||
|
||||
The boot command is "typed" character by character over a VNC connection
|
||||
to the machine, simulating a human actually typing on the keyboard. There are
|
||||
a set of special keys available. If these are in your boot command, they
|
||||
will be replaced by the proper key:
|
||||
|
||||
* `<bs>` - Backspace
|
||||
|
||||
* `<del>` - Delete
|
||||
|
||||
* `<enter>` and `<return>` - Simulates an actual "enter" or "return" keypress.
|
||||
|
||||
* `<esc>` - Simulates pressing the escape key.
|
||||
|
||||
* `<tab>` - Simulates pressing the tab key.
|
||||
|
||||
* `<f1>` - `<f12>` - Simulates pressing a function key.
|
||||
|
||||
* `<up>` `<down>` `<left>` `<right>` - Simulates pressing an arrow key.
|
||||
|
||||
* `<spacebar>` - Simulates pressing the spacebar.
|
||||
|
||||
* `<insert>` - Simulates pressing the insert key.
|
||||
|
||||
* `<home>` `<end>` - Simulates pressing the home and end keys.
|
||||
|
||||
* `<pageUp>` `<pageDown>` - Simulates pressing the page up and page down keys.
|
||||
|
||||
* `<wait>` `<wait5>` `<wait10>` - Adds a 1, 5 or 10 second pause before sending any additional keys. This
|
||||
is useful if you have to generally wait for the UI to update before typing more.
|
||||
|
||||
In addition to the special keys, each command to type is treated as a
|
||||
configuration template.
|
||||
|
||||
The available variables are:
|
||||
|
||||
* `HTTPIP` and `HTTPPort` - The IP and port, respectively of an HTTP server
|
||||
that is started serving the directory specified by the `http_directory`
|
||||
configuration parameter. If `http_directory` isn't specified, these will be
|
||||
blank!
|
||||
|
||||
See the [examples](../../../examples) for working boot commands.
|
36
examples/README.md
Normal file
36
examples/README.md
Normal file
@ -0,0 +1,36 @@
|
||||
## Examples
|
||||
|
||||
In order for new users to get up and running with the packer builder, a few examples of building a machine image with popular distros have been created.
|
||||
|
||||
In order to see an exhaustive list of configuration options for the packer builder please see the [following documentation](../docs/builders/xenserver-iso.html.markdown). This doc will focus on the details relevant to the particular distro.
|
||||
|
||||
### Running the examples
|
||||
|
||||
In order to run the examples you will need to perform the following steps:
|
||||
1. Export those vars:
|
||||
```
|
||||
PKR_VAR_remote_host
|
||||
PKR_VAR_remote_password
|
||||
PKR_VAR_remote_username
|
||||
PKR_VAR_sr_name
|
||||
PKR_VAR_sr_iso_name
|
||||
```
|
||||
`PKR_VAR_remote_host` must be the resource pool primary, aka the master.
|
||||
|
||||
2. Run `packer init path/to/defenition.pkr.hcl` to download the xenserver plugin
|
||||
|
||||
2. Run `packer build path/to/defenition.pkr.hcl`
|
||||
so for example:
|
||||
`packer build examples/centos/centos8-netinstall.pkr.hcl`
|
||||
|
||||
### Ubuntu
|
||||
|
||||
The Ubuntu example uses the [autoinstall tool](https://ubuntu.com/server/docs/install/autoinstallhttps://ubuntu.com/server/docs/install/autoinstall) to configure the VM template. Please see the [autoinstall docs](https://ubuntu.com/server/docs/install/autoinstall-reference) for an exhaustive list of what is supported.
|
||||
|
||||
Packer will create a http server to serve the files as specified from the `http_directory` specified in the builder configuration. This is where the [user-data](http/ubuntu-2004/user-data) and [meta-data](http/ubuntu-2004/meta-data) for autoinstall must be present.
|
||||
|
||||
### Centos
|
||||
|
||||
The Centos examples use kickstart files to configure the VM template. Please see the [kickstart documentation](https://access.redhat.com/documentation/en-us/red_hat_enterprise_linux/7/html/installation_guide/sect-kickstart-syntax) for the options that are supported.
|
||||
|
||||
Packer will create a http server to serve the files as specified from the `http_directory` specified in the builder configuration. This is where the [kickstart config](http/centos8/ks-centos8.cfg) file must be present.
|
@ -1,21 +0,0 @@
|
||||
|
||||
"builders": [{
|
||||
"type": "xenserver",
|
||||
"username": "root",
|
||||
"password": "hostpassword",
|
||||
"host_ip": "10.81.2.105",
|
||||
"instance_name": "packer-centos-6-4",
|
||||
"instance_memory": "2048000000",
|
||||
"root_disk_size": "40000000000",
|
||||
"iso_name": "CentOS-6.4-x86_64-minimal.iso",
|
||||
"http_directory": "http",
|
||||
"local_ip": "10.80.3.223",
|
||||
"ssh_username": "root",
|
||||
"ssh_password": "vmpassword",
|
||||
"boot_command":
|
||||
[
|
||||
"<tab><wait>",
|
||||
" ks=http://{{ .HTTPIP }}:{{ .HTTPPort }}/centos6-ks.cfg<enter>"
|
||||
]
|
||||
}]
|
||||
}
|
80
examples/centos/centos8-local.pkr.hcl
Normal file
80
examples/centos/centos8-local.pkr.hcl
Normal file
@ -0,0 +1,80 @@
|
||||
packer {
|
||||
required_plugins {
|
||||
xenserver= {
|
||||
version = ">= v0.6.0"
|
||||
source = "github.com/ddelnano/xenserver"
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
variable "remote_host" {
|
||||
type = string
|
||||
description = "The ip or fqdn of your XenServer. This will be pulled from the env var 'PKR_VAR_remote_host'"
|
||||
sensitive = true
|
||||
default = null
|
||||
}
|
||||
|
||||
variable "remote_password" {
|
||||
type = string
|
||||
description = "The password used to interact with your XenServer. This will be pulled from the env var 'PKR_VAR_remote_password'"
|
||||
sensitive = true
|
||||
default = null
|
||||
}
|
||||
|
||||
variable "remote_username" {
|
||||
type = string
|
||||
description = "The username used to interact with your XenServer. This will be pulled from the env var 'PKR_VAR_remote_username'"
|
||||
sensitive = true
|
||||
default = null
|
||||
|
||||
}
|
||||
|
||||
variable "sr_iso_name" {
|
||||
type = string
|
||||
default = ""
|
||||
description = "The ISO-SR to packer will use"
|
||||
|
||||
}
|
||||
|
||||
variable "sr_name" {
|
||||
type = string
|
||||
default = ""
|
||||
description = "The name of the SR to packer will use"
|
||||
}
|
||||
|
||||
locals {
|
||||
timestamp = regex_replace(timestamp(), "[- TZ:]", "")
|
||||
}
|
||||
|
||||
source "xenserver-iso" "centos8-local" {
|
||||
iso_checksum = "sha1:aaf9d4b3071c16dbbda01dfe06085e5d0fdac76df323e3bbe87cce4318052247"
|
||||
iso_url = "http://mirrors.ocf.berkeley.edu/centos/8.3.2011/isos/x86_64/CentOS-8.3.2011-x86_64-dvd1.iso"
|
||||
|
||||
sr_iso_name = var.sr_iso_name
|
||||
sr_name = var.sr_name
|
||||
tools_iso_name = "guest-tools.iso"
|
||||
|
||||
remote_host = var.remote_host
|
||||
remote_password = var.remote_password
|
||||
remote_username = var.remote_username
|
||||
|
||||
vm_name = "packer-centos8-local-${local.timestamp}"
|
||||
vm_description = "Build started: ${local.timestamp}\n This was installed from the dvd"
|
||||
vm_memory = 4096
|
||||
disk_size = 4096
|
||||
|
||||
http_directory = "examples/http/centos8"
|
||||
boot_command = ["<tab> text ks=http://{{ .HTTPIP }}:{{ .HTTPPort }}/ks-centos8-local.cfg<enter><wait>"]
|
||||
boot_wait = "10s"
|
||||
|
||||
ssh_username = "root"
|
||||
ssh_password = "centos"
|
||||
ssh_wait_timeout = "10000s"
|
||||
|
||||
output_directory = "packer-centos8-local"
|
||||
keep_vm = "always"
|
||||
}
|
||||
|
||||
build {
|
||||
sources = ["xenserver-iso.centos8-local"]
|
||||
}
|
80
examples/centos/centos8-netinstall.pkr.hcl
Normal file
80
examples/centos/centos8-netinstall.pkr.hcl
Normal file
@ -0,0 +1,80 @@
|
||||
packer {
|
||||
required_plugins {
|
||||
xenserver= {
|
||||
version = ">= v0.6.0"
|
||||
source = "github.com/ddelnano/xenserver"
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
variable "remote_host" {
|
||||
type = string
|
||||
description = "The ip or fqdn of your XenServer. This will be pulled from the env var 'PKR_VAR_remote_host'"
|
||||
sensitive = true
|
||||
default = null
|
||||
}
|
||||
|
||||
variable "remote_password" {
|
||||
type = string
|
||||
description = "The password used to interact with your XenServer. This will be pulled from the env var 'PKR_VAR_remote_password'"
|
||||
sensitive = true
|
||||
default = null
|
||||
}
|
||||
|
||||
variable "remote_username" {
|
||||
type = string
|
||||
description = "The username used to interact with your XenServer. This will be pulled from the env var 'PKR_VAR_remote_username'"
|
||||
sensitive = true
|
||||
default = null
|
||||
|
||||
}
|
||||
|
||||
variable "sr_iso_name" {
|
||||
type = string
|
||||
default = ""
|
||||
description = "The ISO-SR to packer will use"
|
||||
|
||||
}
|
||||
|
||||
variable "sr_name" {
|
||||
type = string
|
||||
default = ""
|
||||
description = "The name of the SR to packer will use"
|
||||
}
|
||||
|
||||
locals {
|
||||
timestamp = regex_replace(timestamp(), "[- TZ:]", "")
|
||||
}
|
||||
|
||||
source "xenserver-iso" "centos8-netinstall" {
|
||||
iso_checksum = "sha1:07a8e59c42cc086ec4c49bdce4fae5a17b077dea"
|
||||
iso_url = "http://mirrors.ocf.berkeley.edu/centos/8.3.2011/isos/x86_64/CentOS-8.3.2011-x86_64-boot.iso"
|
||||
|
||||
sr_iso_name = var.sr_iso_name
|
||||
sr_name = var.sr_name
|
||||
tools_iso_name = "guest-tools.iso"
|
||||
|
||||
remote_host = var.remote_host
|
||||
remote_password = var.remote_password
|
||||
remote_username = var.remote_username
|
||||
|
||||
vm_name = "packer-centos8-netinstall-${local.timestamp}"
|
||||
vm_description = "Build started: ${local.timestamp}\n This was installed with an external repository"
|
||||
vm_memory = 4096
|
||||
disk_size = 4096
|
||||
|
||||
http_directory = "examples/http/centos8"
|
||||
boot_command = ["<tab> text ks=http://{{ .HTTPIP }}:{{ .HTTPPort }}/ks-centos8-netinstall.cfg<enter><wait>"]
|
||||
boot_wait = "10s"
|
||||
|
||||
ssh_username = "root"
|
||||
ssh_password = "centos"
|
||||
ssh_wait_timeout = "10000s"
|
||||
|
||||
output_directory = "packer-centos8-netinstall"
|
||||
keep_vm = "always"
|
||||
}
|
||||
|
||||
build {
|
||||
sources = ["xenserver-iso.centos8-netinstall"]
|
||||
}
|
@ -1,49 +0,0 @@
|
||||
install
|
||||
cdrom
|
||||
lang en_US.UTF-8
|
||||
keyboard us
|
||||
unsupported_hardware
|
||||
network --bootproto=dhcp
|
||||
rootpw --iscrypted $1$DIlig7gp$FuhFdeHj.R1VrEzZsI4uo0
|
||||
firewall --disabled
|
||||
authconfig --enableshadow --passalgo=sha512
|
||||
selinux --permissive
|
||||
timezone UTC
|
||||
bootloader --location=mbr
|
||||
text
|
||||
skipx
|
||||
zerombr
|
||||
clearpart --all --initlabel
|
||||
autopart
|
||||
auth --useshadow --enablemd5
|
||||
firstboot --disabled
|
||||
#reboot
|
||||
poweroff
|
||||
|
||||
%packages --ignoremissing
|
||||
@Base
|
||||
@Core
|
||||
@Development Tools
|
||||
openssl-devel
|
||||
readline-devel
|
||||
zlib-devel
|
||||
kernel-devel
|
||||
vim
|
||||
wget
|
||||
%end
|
||||
|
||||
%post
|
||||
yum -y update
|
||||
|
||||
# update root certs
|
||||
wget -O/etc/pki/tls/certs/ca-bundle.crt http://curl.haxx.se/ca/cacert.pem
|
||||
|
||||
# vagrant
|
||||
groupadd vagrant -g 999
|
||||
useradd vagrant -g vagrant -G wheel -u 900 -s /bin/bash
|
||||
echo "vagrant" | passwd --stdin vagrant
|
||||
|
||||
# sudo
|
||||
echo "vagrant ALL=(ALL) NOPASSWD: ALL" >> /etc/sudoers
|
||||
sed -i "s/^.*requiretty/#Defaults requiretty/" /etc/sudoers
|
||||
%end
|
47
examples/http/centos8/ks-centos8-local.cfg
Normal file
47
examples/http/centos8/ks-centos8-local.cfg
Normal file
@ -0,0 +1,47 @@
|
||||
eula --agreed
|
||||
lang en-US.UTF-8
|
||||
keyboard --vckeymap='de' --xlayouts='de'
|
||||
timezone Europe/Berlin
|
||||
|
||||
cdrom
|
||||
|
||||
text
|
||||
skipx
|
||||
firstboot --disable
|
||||
|
||||
rootpw --plaintext centos
|
||||
|
||||
firewall --enabled --ssh
|
||||
selinux --enforcing
|
||||
|
||||
# Installation logging level
|
||||
logging --level=info
|
||||
|
||||
network --bootproto=dhcp --device=eth0 --onboot=on
|
||||
|
||||
# System bootloader configuration
|
||||
bootloader --location=mbr
|
||||
zerombr
|
||||
clearpart --all
|
||||
|
||||
# Disk partitioning information
|
||||
part / --asprimary --fstype="ext4" --size=1024 --grow
|
||||
|
||||
|
||||
%addon com_redhat_kdump --disable
|
||||
%end
|
||||
|
||||
%packages --ignoremissing --excludedocs
|
||||
openssh-clients
|
||||
sudo
|
||||
|
||||
# unnecessary firmware
|
||||
-aic94xx-firmware*
|
||||
-alsa-*
|
||||
-ivtv-*
|
||||
-iwl*firmware
|
||||
%end
|
||||
|
||||
# Reboot after installation
|
||||
reboot --eject
|
||||
|
46
examples/http/centos8/ks-centos8-netinstall.cfg
Normal file
46
examples/http/centos8/ks-centos8-netinstall.cfg
Normal file
@ -0,0 +1,46 @@
|
||||
eula --agreed
|
||||
lang en-US.UTF-8
|
||||
keyboard --vckeymap='de' --xlayouts='de'
|
||||
timezone Europe/Berlin
|
||||
|
||||
text
|
||||
skipx
|
||||
firstboot --disable
|
||||
|
||||
url --url="http://mirror.centos.org/centos/8.3.2011/BaseOS/x86_64/os/"
|
||||
rootpw --plaintext centos
|
||||
|
||||
firewall --enabled --ssh
|
||||
selinux --enforcing
|
||||
|
||||
# Installation logging level
|
||||
logging --level=info
|
||||
|
||||
network --bootproto=dhcp --device=eth0 --onboot=on
|
||||
|
||||
# System bootloader configuration
|
||||
bootloader --location=mbr
|
||||
zerombr
|
||||
clearpart --all
|
||||
|
||||
# Disk partitioning information
|
||||
part / --asprimary --fstype="ext4" --size=1024 --grow
|
||||
|
||||
|
||||
%addon com_redhat_kdump --disable
|
||||
%end
|
||||
|
||||
%packages --ignoremissing --excludedocs
|
||||
openssh-clients
|
||||
sudo
|
||||
|
||||
# unnecessary firmware
|
||||
-aic94xx-firmware*
|
||||
-alsa-*
|
||||
-ivtv-*
|
||||
-iwl*firmware
|
||||
%end
|
||||
|
||||
# Reboot after installation
|
||||
reboot --eject
|
||||
|
0
examples/http/ubuntu-2004/meta-data
Normal file
0
examples/http/ubuntu-2004/meta-data
Normal file
22
examples/http/ubuntu-2004/user-data
Normal file
22
examples/http/ubuntu-2004/user-data
Normal file
@ -0,0 +1,22 @@
|
||||
#cloud-config
|
||||
|
||||
# hack for cloud-init per:
|
||||
# https://github.com/leakespeake/packer/blob/3f3e361751b4be9326b66771d96f2519bc8f885e/builders/vmware/vsphere-iso/ubuntu-server-20-04/hcl2/http/ubuntu-server-subiquity/user-data
|
||||
runcmd:
|
||||
# to enable true auto-install for Ubuntu 20.04 with cloud-init nocloud (eliminates "Continue with autoinstall?" prompt)
|
||||
- [eval, 'echo $(cat /proc/cmdline) "autoinstall" > /root/cmdline']
|
||||
- [eval, 'mount -n --bind -o ro /root/cmdline /proc/cmdline']
|
||||
- [eval, 'snap restart subiquity.subiquity-service']
|
||||
|
||||
autoinstall:
|
||||
version: 1
|
||||
identity:
|
||||
hostname: ubuntu-server
|
||||
# This is the crypted pass of 'ubuntu'
|
||||
password: "$6$exDY1mhS4KUYCE/2$zmn9ToZwTKLhCw.b4/b.ZRTIZM30JZ4QrOQ2aOXJ8yk96xpcCof0kxKwuX1kqLG/ygbJ1f8wxED22bTL4F46P0"
|
||||
username: testuser
|
||||
packages:
|
||||
- xe-guest-utilities
|
||||
ssh:
|
||||
install-server: yes
|
||||
allow-pw: yes
|
111
examples/ubuntu/ubuntu-2004.pkr.hcl
Normal file
111
examples/ubuntu/ubuntu-2004.pkr.hcl
Normal file
@ -0,0 +1,111 @@
|
||||
packer {
|
||||
required_plugins {
|
||||
xenserver= {
|
||||
version = ">= v0.5.2"
|
||||
source = "github.com/ddelnano/xenserver"
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
# The ubuntu_version value determines what Ubuntu iso URL and sha256 hash we lookup. Updating
|
||||
# this will allow a new version to be pulled in.
|
||||
data "null" "ubuntu_version" {
|
||||
input = "20.04"
|
||||
}
|
||||
|
||||
locals {
|
||||
timestamp = regex_replace(timestamp(), "[- TZ:]", "")
|
||||
ubuntu_version = data.null.ubuntu_version.output
|
||||
|
||||
# Update this map to support future releases. At this time, the Ubuntu
|
||||
# jammy template is not available yet.
|
||||
ubuntu_template_name = {
|
||||
20.04 = "Ubuntu Focal Fossa 20.04"
|
||||
}
|
||||
}
|
||||
|
||||
# TODO(ddelnano): Update this to use a local once https://github.com/hashicorp/packer/issues/11011
|
||||
# is fixed.
|
||||
data "http" "ubuntu_sha_and_release" {
|
||||
url = "https://releases.ubuntu.com/${data.null.ubuntu_version.output}/SHA256SUMS"
|
||||
}
|
||||
|
||||
local "ubuntu_sha256" {
|
||||
expression = regex("([A-Za-z0-9]+)[\\s\\*]+ubuntu-.*server", data.http.ubuntu_sha_and_release.body)
|
||||
}
|
||||
|
||||
local "ubuntu_url_path" {
|
||||
expression = regex("[A-Za-z0-9]+[\\s\\*]+ubuntu-${local.ubuntu_version}.(\\d+)-live-server-amd64.iso", data.http.ubuntu_sha_and_release.body)
|
||||
}
|
||||
|
||||
variable "remote_host" {
|
||||
type = string
|
||||
description = "The ip or fqdn of your XenServer. This will be pulled from the env var 'PKR_VAR_XAPI_HOST'"
|
||||
sensitive = true
|
||||
default = null
|
||||
}
|
||||
|
||||
variable "remote_password" {
|
||||
type = string
|
||||
description = "The password used to interact with your XenServer. This will be pulled from the env var 'PKR_VAR_XAPI_PASSWORD'"
|
||||
sensitive = true
|
||||
default = null
|
||||
}
|
||||
|
||||
variable "remote_username" {
|
||||
type = string
|
||||
description = "The username used to interact with your XenServer. This will be pulled from the env var 'PKR_VAR_XAPI_USERNAME'"
|
||||
sensitive = true
|
||||
default = null
|
||||
|
||||
}
|
||||
|
||||
variable "sr_iso_name" {
|
||||
type = string
|
||||
default = ""
|
||||
description = "The ISO-SR to packer will use"
|
||||
|
||||
}
|
||||
|
||||
variable "sr_name" {
|
||||
type = string
|
||||
default = ""
|
||||
description = "The name of the SR to packer will use"
|
||||
}
|
||||
|
||||
source "xenserver-iso" "ubuntu-2004" {
|
||||
iso_checksum = "sha256:${local.ubuntu_sha256.0}"
|
||||
iso_url = "https://releases.ubuntu.com/${local.ubuntu_version}/ubuntu-${local.ubuntu_version}.${local.ubuntu_url_path.0}-live-server-amd64.iso"
|
||||
|
||||
sr_iso_name = var.sr_iso_name
|
||||
sr_name = var.sr_name
|
||||
tools_iso_name = "guest-tools.iso"
|
||||
|
||||
remote_host = var.remote_host
|
||||
remote_password = var.remote_password
|
||||
remote_username = var.remote_username
|
||||
|
||||
# Change this to match the ISO of ubuntu you are using in the iso_url variable
|
||||
clone_template = local.ubuntu_template_name[data.null.ubuntu_version.output]
|
||||
vm_name = "packer-ubuntu-${data.null.ubuntu_version.output}-${local.timestamp}"
|
||||
vm_description = "Build started: ${local.timestamp}"
|
||||
vm_memory = 4096
|
||||
disk_size = 30720
|
||||
|
||||
floppy_files = [
|
||||
"examples/http/ubuntu-2004/meta-data",
|
||||
"examples/http/ubuntu-2004/user-data",
|
||||
]
|
||||
|
||||
ssh_username = "testuser"
|
||||
ssh_password = "ubuntu"
|
||||
ssh_wait_timeout = "60000s"
|
||||
ssh_handshake_attempts = 10000
|
||||
|
||||
output_directory = "packer-ubuntu-2004-iso"
|
||||
keep_vm = "always"
|
||||
}
|
||||
|
||||
build {
|
||||
sources = ["xenserver-iso.ubuntu-2004"]
|
||||
}
|
104
go.mod
Normal file
104
go.mod
Normal file
@ -0,0 +1,104 @@
|
||||
module github.com/xenserver/packer-builder-xenserver
|
||||
|
||||
go 1.20
|
||||
|
||||
require (
|
||||
github.com/amfranz/go-xmlrpc-client v0.0.0-20190612172737-76858463955d
|
||||
github.com/hashicorp/hcl/v2 v2.20.1
|
||||
github.com/hashicorp/packer-plugin-sdk v0.3.0
|
||||
github.com/mitchellh/go-vnc v0.0.0-20150629162542-723ed9867aed
|
||||
github.com/terra-farm/go-xen-api-client v0.0.2
|
||||
github.com/zclconf/go-cty v1.14.4
|
||||
golang.org/x/crypto v0.24.0
|
||||
)
|
||||
|
||||
require (
|
||||
cloud.google.com/go v0.110.0 // indirect
|
||||
cloud.google.com/go/compute v1.19.1 // indirect
|
||||
cloud.google.com/go/compute/metadata v0.2.3 // indirect
|
||||
cloud.google.com/go/iam v0.13.0 // indirect
|
||||
cloud.google.com/go/storage v1.28.1 // indirect
|
||||
github.com/Azure/go-ntlmssp v0.0.0-20200615164410-66371956d46c // indirect
|
||||
github.com/ChrisTrenkamp/goxpath v0.0.0-20210404020558-97928f7e12b6 // indirect
|
||||
github.com/agext/levenshtein v1.2.3 // indirect
|
||||
github.com/apparentlymart/go-textseg/v13 v13.0.0 // indirect
|
||||
github.com/apparentlymart/go-textseg/v15 v15.0.0 // indirect
|
||||
github.com/armon/go-metrics v0.3.9 // indirect
|
||||
github.com/aws/aws-sdk-go v1.40.34 // indirect
|
||||
github.com/bgentry/go-netrc v0.0.0-20140422174119-9fd32a8b3d3d // indirect
|
||||
github.com/cenkalti/backoff/v3 v3.2.2 // indirect
|
||||
github.com/dylanmei/iso8601 v0.1.0 // indirect
|
||||
github.com/fatih/color v1.12.0 // indirect
|
||||
github.com/gofrs/flock v0.8.1 // indirect
|
||||
github.com/gofrs/uuid v4.0.0+incompatible // indirect
|
||||
github.com/golang/groupcache v0.0.0-20210331224755-41bb18bfe9da // indirect
|
||||
github.com/golang/protobuf v1.5.3 // indirect
|
||||
github.com/golang/snappy v0.0.4 // indirect
|
||||
github.com/google/go-cmp v0.6.0 // indirect
|
||||
github.com/google/uuid v1.3.0 // indirect
|
||||
github.com/googleapis/enterprise-certificate-proxy v0.2.3 // indirect
|
||||
github.com/googleapis/gax-go/v2 v2.7.1 // indirect
|
||||
github.com/hashicorp/consul/api v1.10.1 // indirect
|
||||
github.com/hashicorp/errwrap v1.1.0 // indirect
|
||||
github.com/hashicorp/go-cleanhttp v0.5.2 // indirect
|
||||
github.com/hashicorp/go-getter/gcs/v2 v2.1.0 // indirect
|
||||
github.com/hashicorp/go-getter/s3/v2 v2.1.0 // indirect
|
||||
github.com/hashicorp/go-getter/v2 v2.1.0 // indirect
|
||||
github.com/hashicorp/go-hclog v0.16.2 // indirect
|
||||
github.com/hashicorp/go-immutable-radix v1.3.1 // indirect
|
||||
github.com/hashicorp/go-multierror v1.1.1 // indirect
|
||||
github.com/hashicorp/go-retryablehttp v0.7.0 // indirect
|
||||
github.com/hashicorp/go-rootcerts v1.0.2 // indirect
|
||||
github.com/hashicorp/go-safetemp v1.0.0 // indirect
|
||||
github.com/hashicorp/go-sockaddr v1.0.2 // indirect
|
||||
github.com/hashicorp/go-version v1.3.0 // indirect
|
||||
github.com/hashicorp/golang-lru v0.5.4 // indirect
|
||||
github.com/hashicorp/hcl v1.0.0 // indirect
|
||||
github.com/hashicorp/serf v0.9.5 // indirect
|
||||
github.com/hashicorp/vault/api v1.1.1 // indirect
|
||||
github.com/hashicorp/vault/sdk v0.2.1 // indirect
|
||||
github.com/hashicorp/yamux v0.0.0-20210826001029-26ff87cf9493 // indirect
|
||||
github.com/jehiah/go-strftime v0.0.0-20171201141054-1d33003b3869 // indirect
|
||||
github.com/jmespath/go-jmespath v0.4.0 // indirect
|
||||
github.com/kr/fs v0.1.0 // indirect
|
||||
github.com/masterzen/simplexml v0.0.0-20190410153822-31eea3082786 // indirect
|
||||
github.com/masterzen/winrm v0.0.0-20210623064412-3b76017826b0 // indirect
|
||||
github.com/mattn/go-colorable v0.1.8 // indirect
|
||||
github.com/mattn/go-isatty v0.0.13 // indirect
|
||||
github.com/mitchellh/go-fs v0.0.0-20180402235330-b7b9ca407fff // indirect
|
||||
github.com/mitchellh/go-homedir v1.1.0 // indirect
|
||||
github.com/mitchellh/go-testing-interface v1.14.1 // indirect
|
||||
github.com/mitchellh/go-wordwrap v1.0.1 // indirect
|
||||
github.com/mitchellh/iochan v1.0.0 // indirect
|
||||
github.com/mitchellh/mapstructure v1.4.1 // indirect
|
||||
github.com/mitchellh/reflectwalk v1.0.0 // indirect
|
||||
github.com/niemeyer/pretty v0.0.0-20200227124842-a10e7caefd8e // indirect
|
||||
github.com/nu7hatch/gouuid v0.0.0-20131221200532-179d4d0c4d8d // indirect
|
||||
github.com/packer-community/winrmcp v0.0.0-20180921211025-c76d91c1e7db // indirect
|
||||
github.com/pierrec/lz4 v2.6.1+incompatible // indirect
|
||||
github.com/pkg/sftp v1.13.2 // indirect
|
||||
github.com/ryanuber/go-glob v1.0.0 // indirect
|
||||
github.com/ugorji/go/codec v1.2.6 // indirect
|
||||
github.com/ulikunitz/xz v0.5.10 // indirect
|
||||
go.opencensus.io v0.24.0 // indirect
|
||||
golang.org/x/mod v0.17.0 // indirect
|
||||
golang.org/x/net v0.25.0 // indirect
|
||||
golang.org/x/oauth2 v0.7.0 // indirect
|
||||
golang.org/x/sync v0.7.0 // indirect
|
||||
golang.org/x/sys v0.21.0 // indirect
|
||||
golang.org/x/term v0.21.0 // indirect
|
||||
golang.org/x/text v0.16.0 // indirect
|
||||
golang.org/x/time v0.0.0-20210723032227-1f47c861a9ac // indirect
|
||||
golang.org/x/tools v0.21.1-0.20240508182429-e35e4ccd0d2d // indirect
|
||||
golang.org/x/xerrors v0.0.0-20220907171357-04be3eba64a2 // indirect
|
||||
google.golang.org/api v0.114.0 // indirect
|
||||
google.golang.org/appengine v1.6.7 // indirect
|
||||
google.golang.org/genproto v0.0.0-20230410155749-daa745c078e1 // indirect
|
||||
google.golang.org/grpc v1.56.3 // indirect
|
||||
google.golang.org/protobuf v1.33.0 // indirect
|
||||
gopkg.in/check.v1 v1.0.0-20200227125254-8fa46927fb4f // indirect
|
||||
gopkg.in/square/go-jose.v2 v2.6.0 // indirect
|
||||
gopkg.in/yaml.v2 v2.4.0 // indirect
|
||||
)
|
||||
|
||||
replace github.com/zclconf/go-cty => github.com/nywilken/go-cty v1.13.3 // added by packer-sdc fix as noted in github.com/hashicorp/packer-plugin-sdk/issues/187
|
772
go.sum
Normal file
772
go.sum
Normal file
@ -0,0 +1,772 @@
|
||||
bazil.org/fuse v0.0.0-20160811212531-371fbbdaa898/go.mod h1:Xbm+BRKSBEpa4q4hTSxohYNQpsxXPbPry4JJWOB3LB8=
|
||||
cloud.google.com/go v0.26.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw=
|
||||
cloud.google.com/go v0.34.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw=
|
||||
cloud.google.com/go v0.38.0/go.mod h1:990N+gfupTy94rShfmMCWGDn0LpTmnzTp2qbd1dvSRU=
|
||||
cloud.google.com/go v0.44.1/go.mod h1:iSa0KzasP4Uvy3f1mN/7PiObzGgflwredwwASm/v6AU=
|
||||
cloud.google.com/go v0.44.2/go.mod h1:60680Gw3Yr4ikxnPRS/oxxkBccT6SA1yMk63TGekxKY=
|
||||
cloud.google.com/go v0.45.1/go.mod h1:RpBamKRgapWJb87xiFSdk4g1CME7QZg3uwTez+TSTjc=
|
||||
cloud.google.com/go v0.46.3/go.mod h1:a6bKKbmY7er1mI7TEI4lsAkts/mkhTSZK8w33B4RAg0=
|
||||
cloud.google.com/go v0.50.0/go.mod h1:r9sluTvynVuxRIOHXQEHMFffphuXHOMZMycpNR5e6To=
|
||||
cloud.google.com/go v0.52.0/go.mod h1:pXajvRH/6o3+F9jDHZWQ5PbGhn+o8w9qiu/CffaVdO4=
|
||||
cloud.google.com/go v0.53.0/go.mod h1:fp/UouUEsRkN6ryDKNW/Upv/JBKnv6WDthjR6+vze6M=
|
||||
cloud.google.com/go v0.110.0 h1:Zc8gqp3+a9/Eyph2KDmcGaPtbKRIoqq4YTlL4NMD0Ys=
|
||||
cloud.google.com/go v0.110.0/go.mod h1:SJnCLqQ0FCFGSZMUNUf84MV3Aia54kn7pi8st7tMzaY=
|
||||
cloud.google.com/go/bigquery v1.0.1/go.mod h1:i/xbL2UlR5RvWAURpBYZTtm/cXjCha9lbfbpx4poX+o=
|
||||
cloud.google.com/go/bigquery v1.3.0/go.mod h1:PjpwJnslEMmckchkHFfq+HTD2DmtT67aNFKH1/VBDHE=
|
||||
cloud.google.com/go/bigquery v1.4.0/go.mod h1:S8dzgnTigyfTmLBfrtrhyYhwRxG72rYxvftPBK2Dvzc=
|
||||
cloud.google.com/go/compute v1.19.1 h1:am86mquDUgjGNWxiGn+5PGLbmgiWXlE/yNWpIpNvuXY=
|
||||
cloud.google.com/go/compute v1.19.1/go.mod h1:6ylj3a05WF8leseCdIf77NK0g1ey+nj5IKd5/kvShxE=
|
||||
cloud.google.com/go/compute/metadata v0.2.3 h1:mg4jlk7mCAj6xXp9UJ4fjI9VUI5rubuGBW5aJ7UnBMY=
|
||||
cloud.google.com/go/compute/metadata v0.2.3/go.mod h1:VAV5nSsACxMJvgaAuX6Pk2AawlZn8kiOGuCv6gTkwuA=
|
||||
cloud.google.com/go/datastore v1.0.0/go.mod h1:LXYbyblFSglQ5pkeyhO+Qmw7ukd3C+pD7TKLgZqpHYE=
|
||||
cloud.google.com/go/datastore v1.1.0/go.mod h1:umbIZjpQpHh4hmRpGhH4tLFup+FVzqBi1b3c64qFpCk=
|
||||
cloud.google.com/go/iam v0.13.0 h1:+CmB+K0J/33d0zSQ9SlFWUeCCEn5XJA0ZMZ3pHE9u8k=
|
||||
cloud.google.com/go/iam v0.13.0/go.mod h1:ljOg+rcNfzZ5d6f1nAUJ8ZIxOaZUVoS14bKCtaLZ/D0=
|
||||
cloud.google.com/go/longrunning v0.4.1 h1:v+yFJOfKC3yZdY6ZUI933pIYdhyhV8S3NpWrXWmg7jM=
|
||||
cloud.google.com/go/pubsub v1.0.1/go.mod h1:R0Gpsv3s54REJCy4fxDixWD93lHJMoZTyQ2kNxGRt3I=
|
||||
cloud.google.com/go/pubsub v1.1.0/go.mod h1:EwwdRX2sKPjnvnqCa270oGRyludottCI76h+R3AArQw=
|
||||
cloud.google.com/go/pubsub v1.2.0/go.mod h1:jhfEVHT8odbXTkndysNHCcx0awwzvfOlguIAii9o8iA=
|
||||
cloud.google.com/go/storage v1.0.0/go.mod h1:IhtSnM/ZTZV8YYJWCY8RULGVqBDmpoyjwiyrjsg+URw=
|
||||
cloud.google.com/go/storage v1.5.0/go.mod h1:tpKbwo567HUNpVclU5sGELwQWBDZ8gh0ZeosJ0Rtdos=
|
||||
cloud.google.com/go/storage v1.6.0/go.mod h1:N7U0C8pVQ/+NIKOBQyamJIeKQKkZ+mxpohlUTyfDhBk=
|
||||
cloud.google.com/go/storage v1.28.1 h1:F5QDG5ChchaAVQhINh24U99OWHURqrW8OmQcGKXcbgI=
|
||||
cloud.google.com/go/storage v1.28.1/go.mod h1:Qnisd4CqDdo6BGs2AD5LLnEsmSQ80wQ5ogcBBKhU86Y=
|
||||
dmitri.shuralyov.com/gpu/mtl v0.0.0-20190408044501-666a987793e9/go.mod h1:H6x//7gZCb22OMCxBHrMx7a5I7Hp++hsVxbQ4BYO7hU=
|
||||
github.com/Azure/go-ansiterm v0.0.0-20170929234023-d6e3b3328b78/go.mod h1:LmzpDX56iTiv29bbRTIsUNlaFfuhWRQBWjQdVyAevI8=
|
||||
github.com/Azure/go-ntlmssp v0.0.0-20180810175552-4a21cbd618b4/go.mod h1:chxPXzSsl7ZWRAuOIE23GDNzjWuZquvFlgA8xmpunjU=
|
||||
github.com/Azure/go-ntlmssp v0.0.0-20200615164410-66371956d46c h1:/IBSNwUN8+eKzUzbJPqhK839ygXJ82sde8x3ogr6R28=
|
||||
github.com/Azure/go-ntlmssp v0.0.0-20200615164410-66371956d46c/go.mod h1:chxPXzSsl7ZWRAuOIE23GDNzjWuZquvFlgA8xmpunjU=
|
||||
github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU=
|
||||
github.com/BurntSushi/xgb v0.0.0-20160522181843-27f122750802/go.mod h1:IVnqGOEym/WlBOVXweHU+Q+/VP0lqqI8lqeDx9IjBqo=
|
||||
github.com/ChrisTrenkamp/goxpath v0.0.0-20170922090931-c385f95c6022/go.mod h1:nuWgzSkT5PnyOd+272uUmV0dnAnAn42Mk7PiQC5VzN4=
|
||||
github.com/ChrisTrenkamp/goxpath v0.0.0-20210404020558-97928f7e12b6 h1:w0E0fgc1YafGEh5cROhlROMWXiNoZqApk2PDN0M1+Ns=
|
||||
github.com/ChrisTrenkamp/goxpath v0.0.0-20210404020558-97928f7e12b6/go.mod h1:nuWgzSkT5PnyOd+272uUmV0dnAnAn42Mk7PiQC5VzN4=
|
||||
github.com/DataDog/datadog-go v3.2.0+incompatible/go.mod h1:LButxg5PwREeZtORoXG3tL4fMGNddJ+vMq1mwgfaqoQ=
|
||||
github.com/Microsoft/go-winio v0.4.15-0.20190919025122-fc70bd9a86b5/go.mod h1:tTuCMEN+UleMWgg9dVx4Hu52b1bJo+59jBh3ajtinzw=
|
||||
github.com/Microsoft/hcsshim v0.8.9/go.mod h1:5692vkUqntj1idxauYlpoINNKeqCiG6Sg38RRsjT5y8=
|
||||
github.com/agext/levenshtein v1.2.3 h1:YB2fHEn0UJagG8T1rrWknE3ZQzWM06O8AMAatNn7lmo=
|
||||
github.com/agext/levenshtein v1.2.3/go.mod h1:JEDfjyjHDjOF/1e4FlBE/PkbqA9OfWu2ki2W0IB5558=
|
||||
github.com/alecthomas/template v0.0.0-20160405071501-a0175ee3bccc/go.mod h1:LOuyumcjzFXgccqObfd/Ljyb9UuFJ6TxHnclSeseNhc=
|
||||
github.com/alecthomas/template v0.0.0-20190718012654-fb15b899a751/go.mod h1:LOuyumcjzFXgccqObfd/Ljyb9UuFJ6TxHnclSeseNhc=
|
||||
github.com/alecthomas/units v0.0.0-20151022065526-2efee857e7cf/go.mod h1:ybxpYRFXyAe+OPACYpWeL0wqObRcbAqCMya13uyzqw0=
|
||||
github.com/alecthomas/units v0.0.0-20190717042225-c3de453c63f4/go.mod h1:ybxpYRFXyAe+OPACYpWeL0wqObRcbAqCMya13uyzqw0=
|
||||
github.com/amfranz/go-xmlrpc-client v0.0.0-20190612172737-76858463955d h1:39lR6Kg+GsvDpLMD2Mb7gkjXmmLexqfr7SPy4iQWDTE=
|
||||
github.com/amfranz/go-xmlrpc-client v0.0.0-20190612172737-76858463955d/go.mod h1:2NlXXRCkTbr/vZtUjcHKhbrESE4a3CDqVrgOROB16dg=
|
||||
github.com/antchfx/xpath v1.1.11 h1:WOFtK8TVAjLm3lbgqeP0arlHpvCEeTANeWZ/csPpJkQ=
|
||||
github.com/antchfx/xquery v0.0.0-20180515051857-ad5b8c7a47b0 h1:JaCC8jz0zdMLk2m+qCCVLLLM/PL93p84w4pK3aJWj60=
|
||||
github.com/apparentlymart/go-textseg/v13 v13.0.0 h1:Y+KvPE1NYz0xl601PVImeQfFyEy6iT90AvPUL1NNfNw=
|
||||
github.com/apparentlymart/go-textseg/v13 v13.0.0/go.mod h1:ZK2fH7c4NqDTLtiYLvIkEghdlcqw7yxLeM89kiTRPUo=
|
||||
github.com/apparentlymart/go-textseg/v15 v15.0.0 h1:uYvfpb3DyLSCGWnctWKGj857c6ew1u1fNQOlOtuGxQY=
|
||||
github.com/apparentlymart/go-textseg/v15 v15.0.0/go.mod h1:K8XmNZdhEBkdlyDdvbmmsvpAG721bKi0joRfFdHIWJ4=
|
||||
github.com/armon/circbuf v0.0.0-20150827004946-bbbad097214e/go.mod h1:3U/XgcO3hCbHZ8TKRvWD2dDTCfh9M9ya+I9JpbB7O8o=
|
||||
github.com/armon/go-metrics v0.0.0-20180917152333-f0300d1749da/go.mod h1:Q73ZrmVTwzkszR9V5SSuryQ31EELlFMUz1kKyl939pY=
|
||||
github.com/armon/go-metrics v0.3.0/go.mod h1:zXjbSimjXTd7vOpY8B0/2LpvNvDoXBuplAD+gJD3GYs=
|
||||
github.com/armon/go-metrics v0.3.3/go.mod h1:4O98XIr/9W0sxpJ8UaYkvjk10Iff7SnFrb4QAOwNTFc=
|
||||
github.com/armon/go-metrics v0.3.9 h1:O2sNqxBdvq8Eq5xmzljcYzAORli6RWCvEym4cJf9m18=
|
||||
github.com/armon/go-metrics v0.3.9/go.mod h1:4O98XIr/9W0sxpJ8UaYkvjk10Iff7SnFrb4QAOwNTFc=
|
||||
github.com/armon/go-radix v0.0.0-20180808171621-7fddfc383310/go.mod h1:ufUuZ+zHj4x4TnLV4JWEpy2hxWSpsRywHrMgIH9cCH8=
|
||||
github.com/armon/go-radix v1.0.0/go.mod h1:ufUuZ+zHj4x4TnLV4JWEpy2hxWSpsRywHrMgIH9cCH8=
|
||||
github.com/aws/aws-sdk-go v1.25.37/go.mod h1:KmX6BPdI08NWTb3/sm4ZGu5ShLoqVDhKgpiN924inxo=
|
||||
github.com/aws/aws-sdk-go v1.30.8/go.mod h1:5zCpMtNQVjRREroY7sYe8lOMRSxkhG6MZveU8YkpAk0=
|
||||
github.com/aws/aws-sdk-go v1.30.27/go.mod h1:5zCpMtNQVjRREroY7sYe8lOMRSxkhG6MZveU8YkpAk0=
|
||||
github.com/aws/aws-sdk-go v1.40.34 h1:SBYmodndE2d4AYucuuJnOXk4MD1SFbucoIdpwKVKeSA=
|
||||
github.com/aws/aws-sdk-go v1.40.34/go.mod h1:585smgzpB/KqRA+K3y/NL/oYRqQvpNJYvLm+LY1U59Q=
|
||||
github.com/beorn7/perks v0.0.0-20180321164747-3a771d992973/go.mod h1:Dwedo/Wpr24TaqPxmxbtue+5NUziq4I4S80YR8gNf3Q=
|
||||
github.com/beorn7/perks v1.0.0/go.mod h1:KWe93zE9D1o94FZ5RNwFwVgaQK1VOXiVxmqh+CedLV8=
|
||||
github.com/beorn7/perks v1.0.1/go.mod h1:G2ZrVWU2WbWT9wwq4/hrbKbnv/1ERSJQ0ibhJ6rlkpw=
|
||||
github.com/bgentry/go-netrc v0.0.0-20140422174119-9fd32a8b3d3d h1:xDfNPAt8lFiC1UJrqV3uuy861HCTo708pDMbjHHdCas=
|
||||
github.com/bgentry/go-netrc v0.0.0-20140422174119-9fd32a8b3d3d/go.mod h1:6QX/PXZ00z/TKoufEY6K/a0k6AhaJrQKdFe6OfVXsa4=
|
||||
github.com/bgentry/speakeasy v0.1.0/go.mod h1:+zsyZBPWlz7T6j88CTgSN5bM796AkVf0kBD4zp0CCIs=
|
||||
github.com/cenkalti/backoff/v3 v3.0.0/go.mod h1:cIeZDE3IrqwwJl6VUwCN6trj1oXrTS4rc0ij+ULvLYs=
|
||||
github.com/cenkalti/backoff/v3 v3.2.2 h1:cfUAAO3yvKMYKPrvhDuHSwQnhZNk/RMHKdZqKTxfm6M=
|
||||
github.com/cenkalti/backoff/v3 v3.2.2/go.mod h1:cIeZDE3IrqwwJl6VUwCN6trj1oXrTS4rc0ij+ULvLYs=
|
||||
github.com/census-instrumentation/opencensus-proto v0.2.1/go.mod h1:f6KPmirojxKA12rnyqOA5BBL4O983OfeGPqjHWSTneU=
|
||||
github.com/cespare/xxhash/v2 v2.1.1/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs=
|
||||
github.com/chzyer/logex v1.1.10/go.mod h1:+Ywpsq7O8HXn0nuIou7OrIPyXbp3wmkHB+jjWRnGsAI=
|
||||
github.com/chzyer/readline v0.0.0-20180603132655-2972be24d48e/go.mod h1:nSuG5e5PlCu98SY8svDHJxuZscDgtXS6KTTbou5AhLI=
|
||||
github.com/chzyer/test v0.0.0-20180213035817-a1ea475d72b1/go.mod h1:Q3SI9o4m/ZMnBNeIyt5eFwwo7qiLfzFZmjNmxjkiQlU=
|
||||
github.com/circonus-labs/circonus-gometrics v2.3.1+incompatible/go.mod h1:nmEj6Dob7S7YxXgwXpfOuvO54S+tGdZdw9fuRZt25Ag=
|
||||
github.com/circonus-labs/circonusllhist v0.1.3/go.mod h1:kMXHVDlOchFAehlya5ePtbp5jckzBHf4XRpQvBOLI+I=
|
||||
github.com/client9/misspell v0.3.4/go.mod h1:qj6jICC3Q7zFZvVWo7KLAzC3yx5G7kyvSDkc90ppPyw=
|
||||
github.com/cncf/udpa/go v0.0.0-20191209042840-269d4d468f6f/go.mod h1:M8M6+tZqaGXZJjfX53e64911xZQV5JYwmTeXPW+k8Sc=
|
||||
github.com/containerd/cgroups v0.0.0-20190919134610-bf292b21730f/go.mod h1:OApqhQ4XNSNC13gXIwDjhOQxjWa/NxkwZXJ1EvqT0ko=
|
||||
github.com/containerd/console v0.0.0-20180822173158-c12b1e7919c1/go.mod h1:Tj/on1eG8kiEhd0+fhSDzsPAFESxzBBvdyEgyryXffw=
|
||||
github.com/containerd/containerd v1.3.2/go.mod h1:bC6axHOhabU15QhwfG7w5PipXdVtMXFTttgp+kVtyUA=
|
||||
github.com/containerd/containerd v1.3.4/go.mod h1:bC6axHOhabU15QhwfG7w5PipXdVtMXFTttgp+kVtyUA=
|
||||
github.com/containerd/continuity v0.0.0-20190426062206-aaeac12a7ffc/go.mod h1:GL3xCUCBDV3CZiTSEKksMWbLE66hEyuu9qyDOOqM47Y=
|
||||
github.com/containerd/continuity v0.0.0-20200709052629-daa8e1ccc0bc/go.mod h1:cECdGN1O8G9bgKTlLhuPJimka6Xb/Gg7vYzCTNVxhvo=
|
||||
github.com/containerd/fifo v0.0.0-20190226154929-a9fb20d87448/go.mod h1:ODA38xgv3Kuk8dQz2ZQXpnv/UZZUHUCL7pnLehbXgQI=
|
||||
github.com/containerd/go-runc v0.0.0-20180907222934-5a6d9f37cfa3/go.mod h1:IV7qH3hrUgRmyYrtgEeGWJfWbgcHL9CSRruz2Vqcph0=
|
||||
github.com/containerd/ttrpc v0.0.0-20190828154514-0e0f228740de/go.mod h1:PvCDdDGpgqzQIzDW1TphrGLssLDZp2GuS+X5DkEJB8o=
|
||||
github.com/containerd/typeurl v0.0.0-20180627222232-a93fcdb778cd/go.mod h1:Cm3kwCdlkCfMSHURc+r6fwoGH6/F1hH3S4sg0rLFWPc=
|
||||
github.com/coreos/go-systemd v0.0.0-20190321100706-95778dfbb74e/go.mod h1:F5haX7vjVVG0kc13fIWeqUViNPyEJxv/OmvnBo0Yme4=
|
||||
github.com/creack/pty v1.1.9/go.mod h1:oKZEueFk5CKHvIhNR5MUki03XCEU+Q6VDXinZuGJ33E=
|
||||
github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
|
||||
github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c=
|
||||
github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
|
||||
github.com/docker/distribution v2.7.1+incompatible/go.mod h1:J2gT2udsDAN96Uj4KfcMRqY0/ypR+oyYUYmja8H+y+w=
|
||||
github.com/docker/docker v1.4.2-0.20200319182547-c7ad2b866182/go.mod h1:eEKB0N0r5NX/I1kEveEz05bcu8tLC/8azJZsviup8Sk=
|
||||
github.com/docker/go-connections v0.4.0/go.mod h1:Gbd7IOopHjR8Iph03tsViu4nIes5XhDvyHbTtUxmeec=
|
||||
github.com/docker/go-units v0.4.0/go.mod h1:fgPhTUdO+D/Jk86RDLlptpiXQzgHJF7gydDDbaIK4Dk=
|
||||
github.com/dustin/go-humanize v0.0.0-20171111073723-bb3d318650d4/go.mod h1:HtrtbFcZ19U5GC7JDqmcUSB87Iq5E25KnS6fMYU6eOk=
|
||||
github.com/dylanmei/iso8601 v0.1.0 h1:812NGQDBcqquTfH5Yeo7lwR0nzx/cKdsmf3qMjPURUI=
|
||||
github.com/dylanmei/iso8601 v0.1.0/go.mod h1:w9KhXSgIyROl1DefbMYIE7UVSIvELTbMrCfx+QkYnoQ=
|
||||
github.com/dylanmei/winrmtest v0.0.0-20170819153634-c2fbb09e6c08 h1:0bp6/GrNOrTDtSXe9YYGCwf8jp5Fb/b+4a6MTRm4qzY=
|
||||
github.com/envoyproxy/go-control-plane v0.9.0/go.mod h1:YTl/9mNaCwkRvm6d1a2C3ymFceY/DCBVvsKhRF0iEA4=
|
||||
github.com/envoyproxy/go-control-plane v0.9.1-0.20191026205805-5f8ba28d4473/go.mod h1:YTl/9mNaCwkRvm6d1a2C3ymFceY/DCBVvsKhRF0iEA4=
|
||||
github.com/envoyproxy/go-control-plane v0.9.4/go.mod h1:6rpuAdCZL397s3pYoYcLgu1mIlRU8Am5FuJP05cCM98=
|
||||
github.com/envoyproxy/protoc-gen-validate v0.1.0/go.mod h1:iSmxcyjqTsJpI2R4NaDN7+kN2VEUnK/pcBlmesArF7c=
|
||||
github.com/fatih/color v1.7.0/go.mod h1:Zm6kSWBoL9eyXnKyktHP6abPY2pDugNf5KwzbycvMj4=
|
||||
github.com/fatih/color v1.9.0/go.mod h1:eQcE1qtQxscV5RaZvpXrrb8Drkc3/DdQ+uUYCNjL+zU=
|
||||
github.com/fatih/color v1.12.0 h1:mRhaKNwANqRgUBGKmnI5ZxEk7QXmjQeCcuYFMX2bfcc=
|
||||
github.com/fatih/color v1.12.0/go.mod h1:ELkj/draVOlAH/xkhN6mQ50Qd0MPOk5AAr3maGEBuJM=
|
||||
github.com/fatih/structs v1.1.0/go.mod h1:9NiDSp5zOcgEDl+j00MP/WkGVPOlPRLejGD8Ga6PJ7M=
|
||||
github.com/frankban/quicktest v1.10.0 h1:Gfh+GAJZOAoKZsIZeZbdn2JF10kN1XHNvjsvQK8gVkE=
|
||||
github.com/frankban/quicktest v1.10.0/go.mod h1:ui7WezCLWMWxVWr1GETZY3smRy0G4KWq9vcPtJmFl7Y=
|
||||
github.com/fsnotify/fsnotify v1.4.7/go.mod h1:jwhsz4b93w/PPRr/qN1Yymfu8t87LnFCMoQvtojpjFo=
|
||||
github.com/go-asn1-ber/asn1-ber v1.3.1/go.mod h1:hEBeB/ic+5LoWskz+yKT7vGhhPYkProFKoKdwZRWMe0=
|
||||
github.com/go-gl/glfw v0.0.0-20190409004039-e6da0acd62b1/go.mod h1:vR7hzQXu2zJy9AVAgeJqvqgH9Q5CA+iKCZ2gyEVpxRU=
|
||||
github.com/go-gl/glfw/v3.3/glfw v0.0.0-20191125211704-12ad95a8df72/go.mod h1:tQ2UAYgL5IevRw8kRxooKSPJfGvJ9fJQFa0TUsXzTg8=
|
||||
github.com/go-gl/glfw/v3.3/glfw v0.0.0-20200222043503-6f7a984d4dc4/go.mod h1:tQ2UAYgL5IevRw8kRxooKSPJfGvJ9fJQFa0TUsXzTg8=
|
||||
github.com/go-kit/kit v0.8.0/go.mod h1:xBxKIO96dXMWWy0MnWVtmwkA9/13aqxPnvrjFYMA2as=
|
||||
github.com/go-kit/kit v0.9.0/go.mod h1:xBxKIO96dXMWWy0MnWVtmwkA9/13aqxPnvrjFYMA2as=
|
||||
github.com/go-ldap/ldap/v3 v3.1.3/go.mod h1:3rbOH3jRS2u6jg2rJnKAMLE/xQyCKIveG2Sa/Cohzb8=
|
||||
github.com/go-ldap/ldap/v3 v3.1.10/go.mod h1:5Zun81jBTabRaI8lzN7E1JjyEl1g6zI6u9pd8luAK4Q=
|
||||
github.com/go-logfmt/logfmt v0.3.0/go.mod h1:Qt1PoO58o5twSAckw1HlFXLmHsOX5/0LbT9GBnD5lWE=
|
||||
github.com/go-logfmt/logfmt v0.4.0/go.mod h1:3RMwSq7FuexP4Kalkev3ejPJsZTpXXBr9+V4qmtdjCk=
|
||||
github.com/go-sql-driver/mysql v1.5.0/go.mod h1:DCzpHaOWr8IXmIStZouvnhqoel9Qv2LBy8hT2VhHyBg=
|
||||
github.com/go-stack/stack v1.8.0/go.mod h1:v0f6uXyyMGvRgIKkXu+yp6POWl0qKG85gN/melR3HDY=
|
||||
github.com/go-test/deep v1.0.2-0.20181118220953-042da051cf31/go.mod h1:wGDj63lr65AM2AQyKZd/NYHGb0R+1RLqB8NKt3aSFNA=
|
||||
github.com/go-test/deep v1.0.2/go.mod h1:wGDj63lr65AM2AQyKZd/NYHGb0R+1RLqB8NKt3aSFNA=
|
||||
github.com/go-test/deep v1.0.3 h1:ZrJSEWsXzPOxaZnFteGEfooLba+ju3FYIbOrS+rQd68=
|
||||
github.com/godbus/dbus v0.0.0-20190422162347-ade71ed3457e/go.mod h1:bBOAhwG1umN6/6ZUMtDFBMQR8jRg9O75tm9K00oMsK4=
|
||||
github.com/gofrs/flock v0.8.1 h1:+gYjHKf32LDeiEEFhQaotPbLuUXjY5ZqxKgXy7n59aw=
|
||||
github.com/gofrs/flock v0.8.1/go.mod h1:F1TvTiK9OcQqauNUHlbJvyl9Qa1QvF/gOUDKA14jxHU=
|
||||
github.com/gofrs/uuid v3.2.0+incompatible/go.mod h1:b2aQJv3Z4Fp6yNu3cdSllBxTCLRxnplIgP/c0N/04lM=
|
||||
github.com/gofrs/uuid v4.0.0+incompatible h1:1SD/1F5pU8p29ybwgQSwpQk+mwdRrXCYuPhW6m+TnJw=
|
||||
github.com/gofrs/uuid v4.0.0+incompatible/go.mod h1:b2aQJv3Z4Fp6yNu3cdSllBxTCLRxnplIgP/c0N/04lM=
|
||||
github.com/gogo/protobuf v1.1.1/go.mod h1:r8qH/GZQm5c6nD/R0oafs1akxWv10x8SbQlK7atdtwQ=
|
||||
github.com/gogo/protobuf v1.2.1/go.mod h1:hp+jE20tsWTFYpLwKvXlhS1hjn+gTNwPg2I6zVXpSg4=
|
||||
github.com/gogo/protobuf v1.3.1/go.mod h1:SlYgWuQ5SjCEi6WLHjHCa1yvBfUnHcTbrrZtXPKa29o=
|
||||
github.com/golang/glog v0.0.0-20160126235308-23def4e6c14b/go.mod h1:SBH7ygxi8pfUlaOkMMuAQtPIUF8ecWP5IEl/CR7VP2Q=
|
||||
github.com/golang/groupcache v0.0.0-20190702054246-869f871628b6/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc=
|
||||
github.com/golang/groupcache v0.0.0-20191227052852-215e87163ea7/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc=
|
||||
github.com/golang/groupcache v0.0.0-20200121045136-8c9f03a8e57e/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc=
|
||||
github.com/golang/groupcache v0.0.0-20210331224755-41bb18bfe9da h1:oI5xCqsCo564l8iNU+DwB5epxmsaqB+rhGL0m5jtYqE=
|
||||
github.com/golang/groupcache v0.0.0-20210331224755-41bb18bfe9da/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc=
|
||||
github.com/golang/mock v1.1.1/go.mod h1:oTYuIxOrZwtPieC+H1uAHpcLFnEyAGVDL/k47Jfbm0A=
|
||||
github.com/golang/mock v1.2.0/go.mod h1:oTYuIxOrZwtPieC+H1uAHpcLFnEyAGVDL/k47Jfbm0A=
|
||||
github.com/golang/mock v1.3.1/go.mod h1:sBzyDLLjw3U8JLTeZvSv8jJB+tU5PVekmnlKIyFUx0Y=
|
||||
github.com/golang/mock v1.4.0/go.mod h1:UOMv5ysSaYNkG+OFQykRIcU/QvvxJf3p21QfJ2Bt3cw=
|
||||
github.com/golang/protobuf v1.2.0/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U=
|
||||
github.com/golang/protobuf v1.3.1/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U=
|
||||
github.com/golang/protobuf v1.3.2/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U=
|
||||
github.com/golang/protobuf v1.3.3/go.mod h1:vzj43D7+SQXF/4pzW/hwtAqwc6iTitCiVSaWz5lYuqw=
|
||||
github.com/golang/protobuf v1.4.0-rc.1/go.mod h1:ceaxUfeHdC40wWswd/P6IGgMaK3YpKi5j83Wpe3EHw8=
|
||||
github.com/golang/protobuf v1.4.0-rc.1.0.20200221234624-67d41d38c208/go.mod h1:xKAWHe0F5eneWXFV3EuXVDTCmh+JuBKY0li0aMyXATA=
|
||||
github.com/golang/protobuf v1.4.0-rc.2/go.mod h1:LlEzMj4AhA7rCAGe4KMBDvJI+AwstrUpVNzEA03Pprs=
|
||||
github.com/golang/protobuf v1.4.0-rc.4.0.20200313231945-b860323f09d0/go.mod h1:WU3c8KckQ9AFe+yFwt9sWVRKCVIyN9cPHBJSNnbL67w=
|
||||
github.com/golang/protobuf v1.4.0/go.mod h1:jodUvKwWbYaEsadDk5Fwe5c77LiNKVO9IDvqG2KuDX0=
|
||||
github.com/golang/protobuf v1.4.1/go.mod h1:U8fpvMrcmy5pZrNK1lt4xCsGvpyWQ/VVv6QDs8UjoX8=
|
||||
github.com/golang/protobuf v1.4.2/go.mod h1:oDoupMAO8OvCJWAcko0GGGIgR6R6ocIYbsSw735rRwI=
|
||||
github.com/golang/protobuf v1.4.3/go.mod h1:oDoupMAO8OvCJWAcko0GGGIgR6R6ocIYbsSw735rRwI=
|
||||
github.com/golang/protobuf v1.5.0/go.mod h1:FsONVRAS9T7sI+LIUmWTfcYkHO4aIWwzhcaSAoJOfIk=
|
||||
github.com/golang/protobuf v1.5.3 h1:KhyjKVUg7Usr/dYsdSqoFveMYd5ko72D+zANwlG1mmg=
|
||||
github.com/golang/protobuf v1.5.3/go.mod h1:XVQd3VNwM+JqD3oG2Ue2ip4fOMUkwXdXDdiuN0vRsmY=
|
||||
github.com/golang/snappy v0.0.1/go.mod h1:/XxbfmMg8lxefKM7IXC3fBNl/7bRcc72aCRzEWrmP2Q=
|
||||
github.com/golang/snappy v0.0.4 h1:yAGX7huGHXlcLOEtBnF4w7FQwA26wojNCwOYAEhLjQM=
|
||||
github.com/golang/snappy v0.0.4/go.mod h1:/XxbfmMg8lxefKM7IXC3fBNl/7bRcc72aCRzEWrmP2Q=
|
||||
github.com/google/btree v0.0.0-20180813153112-4030bb1f1f0c/go.mod h1:lNA+9X1NB3Zf8V7Ke586lFgjr2dZNuvo3lPJSGZ5JPQ=
|
||||
github.com/google/btree v1.0.0 h1:0udJVsspx3VBr5FwtLhQQtuAsVc79tTq0ocGIPAU6qo=
|
||||
github.com/google/btree v1.0.0/go.mod h1:lNA+9X1NB3Zf8V7Ke586lFgjr2dZNuvo3lPJSGZ5JPQ=
|
||||
github.com/google/go-cmp v0.2.0/go.mod h1:oXzfMopK8JAjlY9xF4vHSVASa0yLyX7SntLO5aqRK0M=
|
||||
github.com/google/go-cmp v0.3.0/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU=
|
||||
github.com/google/go-cmp v0.3.1/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU=
|
||||
github.com/google/go-cmp v0.4.0/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
|
||||
github.com/google/go-cmp v0.5.0/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
|
||||
github.com/google/go-cmp v0.5.3/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
|
||||
github.com/google/go-cmp v0.5.5/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
|
||||
github.com/google/go-cmp v0.6.0 h1:ofyhxvXcZhMsU5ulbFiLKl/XBFqE1GSq7atu8tAmTRI=
|
||||
github.com/google/go-cmp v0.6.0/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY=
|
||||
github.com/google/gofuzz v1.0.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg=
|
||||
github.com/google/martian v2.1.0+incompatible h1:/CP5g8u/VJHijgedC/Legn3BAbAaWPgecwXBIDzw5no=
|
||||
github.com/google/martian v2.1.0+incompatible/go.mod h1:9I4somxYTbIHy5NJKHRl3wXiIaQGbYVAs8BPL6v8lEs=
|
||||
github.com/google/martian/v3 v3.3.2 h1:IqNFLAmvJOgVlpdEBiQbDc2EwKW77amAycfTuWKdfvw=
|
||||
github.com/google/pprof v0.0.0-20181206194817-3ea8567a2e57/go.mod h1:zfwlbNMJ+OItoe0UupaVj+oy1omPYYDuagoSzA8v9mc=
|
||||
github.com/google/pprof v0.0.0-20190515194954-54271f7e092f/go.mod h1:zfwlbNMJ+OItoe0UupaVj+oy1omPYYDuagoSzA8v9mc=
|
||||
github.com/google/pprof v0.0.0-20191218002539-d4f498aebedc/go.mod h1:ZgVRPoUq/hfqzAqh7sHMqb3I9Rq5C59dIz2SbBwJ4eM=
|
||||
github.com/google/pprof v0.0.0-20200212024743-f11f1df84d12/go.mod h1:ZgVRPoUq/hfqzAqh7sHMqb3I9Rq5C59dIz2SbBwJ4eM=
|
||||
github.com/google/renameio v0.1.0/go.mod h1:KWCgfxg9yswjAJkECMjeO8J8rahYeXnNhOm40UhjYkI=
|
||||
github.com/google/uuid v1.1.2/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
|
||||
github.com/google/uuid v1.3.0 h1:t6JiXgmwXMjEs8VusXIJk2BXHsn+wx8BZdTaoZ5fu7I=
|
||||
github.com/google/uuid v1.3.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
|
||||
github.com/googleapis/enterprise-certificate-proxy v0.2.3 h1:yk9/cqRKtT9wXZSsRH9aurXEpJX+U6FLtpYTdC3R06k=
|
||||
github.com/googleapis/enterprise-certificate-proxy v0.2.3/go.mod h1:AwSRAtLfXpU5Nm3pW+v7rGDHp09LsPtGY9MduiEsR9k=
|
||||
github.com/googleapis/gax-go/v2 v2.0.4/go.mod h1:0Wqv26UfaUD9n4G6kQubkQ+KchISgw+vpHVxEJEs9eg=
|
||||
github.com/googleapis/gax-go/v2 v2.0.5/go.mod h1:DWXyrwAJ9X0FpwwEdw+IPEYBICEFu5mhpdKc/us6bOk=
|
||||
github.com/googleapis/gax-go/v2 v2.7.1 h1:gF4c0zjUP2H/s/hEGyLA3I0fA2ZWjzYiONAD6cvPr8A=
|
||||
github.com/googleapis/gax-go/v2 v2.7.1/go.mod h1:4orTrqY6hXxxaUL4LHIPl6lGo8vAE38/qKbhSAKP6QI=
|
||||
github.com/gorilla/mux v1.7.4/go.mod h1:DVbg23sWSpFRCP0SfiEN6jmj59UnW/n46BH5rLB71So=
|
||||
github.com/hashicorp/consul/api v1.10.1 h1:MwZJp86nlnL+6+W1Zly4JUuVn9YHhMggBirMpHGD7kw=
|
||||
github.com/hashicorp/consul/api v1.10.1/go.mod h1:XjsvQN+RJGWI2TWy1/kqaE16HrR2J/FWgkYjdZQsX9M=
|
||||
github.com/hashicorp/consul/sdk v0.8.0 h1:OJtKBtEjboEZvG6AOUdh4Z1Zbyu0WcxQ0qatRrZHTVU=
|
||||
github.com/hashicorp/consul/sdk v0.8.0/go.mod h1:GBvyrGALthsZObzUGsfgHZQDXjg4lOjagTIwIR1vPms=
|
||||
github.com/hashicorp/errwrap v1.0.0/go.mod h1:YH+1FKiLXxHSkmPseP+kNlulaMuP3n2brvKWEqk/Jc4=
|
||||
github.com/hashicorp/errwrap v1.1.0 h1:OxrOeh75EUXMY8TBjag2fzXGZ40LB6IKw45YeGUDY2I=
|
||||
github.com/hashicorp/errwrap v1.1.0/go.mod h1:YH+1FKiLXxHSkmPseP+kNlulaMuP3n2brvKWEqk/Jc4=
|
||||
github.com/hashicorp/go-cleanhttp v0.5.0/go.mod h1:JpRdi6/HCYpAwUzNwuwqhbovhLtngrth3wmdIIUrZ80=
|
||||
github.com/hashicorp/go-cleanhttp v0.5.1/go.mod h1:JpRdi6/HCYpAwUzNwuwqhbovhLtngrth3wmdIIUrZ80=
|
||||
github.com/hashicorp/go-cleanhttp v0.5.2 h1:035FKYIWjmULyFRBKPs8TBQoi0x6d9G4xc9neXJWAZQ=
|
||||
github.com/hashicorp/go-cleanhttp v0.5.2/go.mod h1:kO/YDlP8L1346E6Sodw+PrpBSV4/SoxCXGY6BqNFT48=
|
||||
github.com/hashicorp/go-getter/gcs/v2 v2.1.0 h1:1S1hvWgHrhUihP/Y4FVbjCWwE7EwxpksKoRcC7g+Hgs=
|
||||
github.com/hashicorp/go-getter/gcs/v2 v2.1.0/go.mod h1:dVyTnX1BynHAjbumB4Pk14GoJ+v3VbDUJtbI7G0oOlU=
|
||||
github.com/hashicorp/go-getter/s3/v2 v2.1.0 h1:8uwuP97zEQ7y7H4bLzRqiN4T8vmpXeJthigqSEjX+08=
|
||||
github.com/hashicorp/go-getter/s3/v2 v2.1.0/go.mod h1:rwzJPQaBuc5riYOucPx84DOE74xIhKENOWgBjK3XVEs=
|
||||
github.com/hashicorp/go-getter/v2 v2.1.0 h1:MsLbi7yFKGFPVmpK+un4/k5HFry0tqvo9JppsCmIutU=
|
||||
github.com/hashicorp/go-getter/v2 v2.1.0/go.mod h1:w65fE5glbccYjndAuj1kA5lnVBGZYEaH0e5qA1kpIks=
|
||||
github.com/hashicorp/go-hclog v0.0.0-20180709165350-ff2cf002a8dd/go.mod h1:9bjs9uLqI8l75knNv3lV1kA55veR+WUPSiKIWcQHudI=
|
||||
github.com/hashicorp/go-hclog v0.9.2/go.mod h1:5CU+agLiy3J7N7QjHK5d05KxGsuXiQLrjA0H7acj2lQ=
|
||||
github.com/hashicorp/go-hclog v0.12.0/go.mod h1:whpDNt7SSdeAju8AWKIWsul05p54N/39EeqMAyrmvFQ=
|
||||
github.com/hashicorp/go-hclog v0.14.1/go.mod h1:whpDNt7SSdeAju8AWKIWsul05p54N/39EeqMAyrmvFQ=
|
||||
github.com/hashicorp/go-hclog v0.16.1/go.mod h1:whpDNt7SSdeAju8AWKIWsul05p54N/39EeqMAyrmvFQ=
|
||||
github.com/hashicorp/go-hclog v0.16.2 h1:K4ev2ib4LdQETX5cSZBG0DVLk1jwGqSPXBjdah3veNs=
|
||||
github.com/hashicorp/go-hclog v0.16.2/go.mod h1:whpDNt7SSdeAju8AWKIWsul05p54N/39EeqMAyrmvFQ=
|
||||
github.com/hashicorp/go-immutable-radix v1.0.0/go.mod h1:0y9vanUI8NX6FsYoO3zeMjhV/C5i9g4Q3DwcSNZ4P60=
|
||||
github.com/hashicorp/go-immutable-radix v1.1.0/go.mod h1:0y9vanUI8NX6FsYoO3zeMjhV/C5i9g4Q3DwcSNZ4P60=
|
||||
github.com/hashicorp/go-immutable-radix v1.3.1 h1:DKHmCUm2hRBK510BaiZlwvpD40f8bJFeZnpfm2KLowc=
|
||||
github.com/hashicorp/go-immutable-radix v1.3.1/go.mod h1:0y9vanUI8NX6FsYoO3zeMjhV/C5i9g4Q3DwcSNZ4P60=
|
||||
github.com/hashicorp/go-kms-wrapping/entropy v0.1.0/go.mod h1:d1g9WGtAunDNpek8jUIEJnBlbgKS1N2Q61QkHiZyR1g=
|
||||
github.com/hashicorp/go-msgpack v0.5.3/go.mod h1:ahLV/dePpqEmjfWmKiqvPkv/twdG7iPBM1vqhUKIvfM=
|
||||
github.com/hashicorp/go-msgpack v0.5.5 h1:i9R9JSrqIz0QVLz3sz+i3YJdT7TTSLcfLLzJi9aZTuI=
|
||||
github.com/hashicorp/go-multierror v1.0.0/go.mod h1:dHtQlpGsu+cZNNAkkCN/P3hoUDHhCYQXV3UM06sGGrk=
|
||||
github.com/hashicorp/go-multierror v1.1.0/go.mod h1:spPvp8C1qA32ftKqdAHm4hHTbPw+vmowP0z+KUhOZdA=
|
||||
github.com/hashicorp/go-multierror v1.1.1 h1:H5DkEtf6CXdFp0N0Em5UCwQpXMWke8IA0+lD48awMYo=
|
||||
github.com/hashicorp/go-multierror v1.1.1/go.mod h1:iw975J/qwKPdAO1clOe2L8331t/9/fmwbPZ6JB6eMoM=
|
||||
github.com/hashicorp/go-plugin v1.0.1/go.mod h1:++UyYGoz3o5w9ZzAdZxtQKrWWP+iqPBn3cQptSMzBuY=
|
||||
github.com/hashicorp/go-retryablehttp v0.5.3/go.mod h1:9B5zBasrRhHXnJnui7y6sL7es7NDiJgTc6Er0maI1Xs=
|
||||
github.com/hashicorp/go-retryablehttp v0.6.2/go.mod h1:gEx6HMUGxYYhJScX7W1Il64m6cc2C1mDaW3NQ9sY1FY=
|
||||
github.com/hashicorp/go-retryablehttp v0.6.6/go.mod h1:vAew36LZh98gCBJNLH42IQ1ER/9wtLZZ8meHqQvEYWY=
|
||||
github.com/hashicorp/go-retryablehttp v0.7.0 h1:eu1EI/mbirUgP5C8hVsTNaGZreBDlYiwC1FZWkvQPQ4=
|
||||
github.com/hashicorp/go-retryablehttp v0.7.0/go.mod h1:vAew36LZh98gCBJNLH42IQ1ER/9wtLZZ8meHqQvEYWY=
|
||||
github.com/hashicorp/go-rootcerts v1.0.1/go.mod h1:pqUvnprVnM5bf7AOirdbb01K4ccR319Vf4pU3K5EGc8=
|
||||
github.com/hashicorp/go-rootcerts v1.0.2 h1:jzhAVGtqPKbwpyCPELlgNWhE1znq+qwJtW5Oi2viEzc=
|
||||
github.com/hashicorp/go-rootcerts v1.0.2/go.mod h1:pqUvnprVnM5bf7AOirdbb01K4ccR319Vf4pU3K5EGc8=
|
||||
github.com/hashicorp/go-safetemp v1.0.0 h1:2HR189eFNrjHQyENnQMMpCiBAsRxzbTMIgBhEyExpmo=
|
||||
github.com/hashicorp/go-safetemp v1.0.0/go.mod h1:oaerMy3BhqiTbVye6QuFhFtIceqFoDHxNAB65b+Rj1I=
|
||||
github.com/hashicorp/go-sockaddr v1.0.0/go.mod h1:7Xibr9yA9JjQq1JpNB2Vw7kxv8xerXegt+ozgdvDeDU=
|
||||
github.com/hashicorp/go-sockaddr v1.0.2 h1:ztczhD1jLxIRjVejw8gFomI1BQZOe2WoVOu0SyteCQc=
|
||||
github.com/hashicorp/go-sockaddr v1.0.2/go.mod h1:rB4wwRAUzs07qva3c5SdrY/NEtAUjGlgmH/UkBUC97A=
|
||||
github.com/hashicorp/go-syslog v1.0.0/go.mod h1:qPfqrKkXGihmCqbJM2mZgkZGvKG1dFdvsLplgctolz4=
|
||||
github.com/hashicorp/go-uuid v1.0.0/go.mod h1:6SBZvOh/SIDV7/2o3Jml5SYk/TvGqwFJ/bN7x4byOro=
|
||||
github.com/hashicorp/go-uuid v1.0.1/go.mod h1:6SBZvOh/SIDV7/2o3Jml5SYk/TvGqwFJ/bN7x4byOro=
|
||||
github.com/hashicorp/go-uuid v1.0.2 h1:cfejS+Tpcp13yd5nYHWDI6qVCny6wyX2Mt5SGur2IGE=
|
||||
github.com/hashicorp/go-uuid v1.0.2/go.mod h1:6SBZvOh/SIDV7/2o3Jml5SYk/TvGqwFJ/bN7x4byOro=
|
||||
github.com/hashicorp/go-version v1.1.0/go.mod h1:fltr4n8CU8Ke44wwGCBoEymUuxUHl09ZGVZPK5anwXA=
|
||||
github.com/hashicorp/go-version v1.2.0/go.mod h1:fltr4n8CU8Ke44wwGCBoEymUuxUHl09ZGVZPK5anwXA=
|
||||
github.com/hashicorp/go-version v1.3.0 h1:McDWVJIU/y+u1BRV06dPaLfLCaT7fUTJLp5r04x7iNw=
|
||||
github.com/hashicorp/go-version v1.3.0/go.mod h1:fltr4n8CU8Ke44wwGCBoEymUuxUHl09ZGVZPK5anwXA=
|
||||
github.com/hashicorp/golang-lru v0.5.0/go.mod h1:/m3WP610KZHVQ1SGc6re/UDhFvYD7pJ4Ao+sR/qLZy8=
|
||||
github.com/hashicorp/golang-lru v0.5.1/go.mod h1:/m3WP610KZHVQ1SGc6re/UDhFvYD7pJ4Ao+sR/qLZy8=
|
||||
github.com/hashicorp/golang-lru v0.5.3/go.mod h1:iADmTwqILo4mZ8BN3D2Q6+9jd8WM5uGBxy+E8yxSoD4=
|
||||
github.com/hashicorp/golang-lru v0.5.4 h1:YDjusn29QI/Das2iO9M0BHnIbxPeyuCHsjMW+lJfyTc=
|
||||
github.com/hashicorp/golang-lru v0.5.4/go.mod h1:iADmTwqILo4mZ8BN3D2Q6+9jd8WM5uGBxy+E8yxSoD4=
|
||||
github.com/hashicorp/hcl v1.0.0 h1:0Anlzjpi4vEasTeNFn2mLJgTSwt0+6sfsiTG8qcWGx4=
|
||||
github.com/hashicorp/hcl v1.0.0/go.mod h1:E5yfLk+7swimpb2L/Alb/PJmXilQ/rhwaUYs4T20WEQ=
|
||||
github.com/hashicorp/hcl/v2 v2.20.1 h1:M6hgdyz7HYt1UN9e61j+qKJBqR3orTWbI1HKBJEdxtc=
|
||||
github.com/hashicorp/hcl/v2 v2.20.1/go.mod h1:TZDqQ4kNKCbh1iJp99FdPiUaVDDUPivbqxZulxDYqL4=
|
||||
github.com/hashicorp/logutils v1.0.0/go.mod h1:QIAnNjmIWmVIIkWDTG1z5v++HQmx9WQRO+LraFDTW64=
|
||||
github.com/hashicorp/mdns v1.0.1/go.mod h1:4gW7WsVCke5TE7EPeYliwHlRUyBtfCwuFwuMg2DmyNY=
|
||||
github.com/hashicorp/memberlist v0.2.2 h1:5+RffWKwqJ71YPu9mWsF7ZOscZmwfasdA8kbdC7AO2g=
|
||||
github.com/hashicorp/memberlist v0.2.2/go.mod h1:MS2lj3INKhZjWNqd3N0m3J+Jxf3DAOnAH9VT3Sh9MUE=
|
||||
github.com/hashicorp/packer-plugin-sdk v0.3.0 h1:G4Uze/85X3n6c+8DawHdxptOZ0vHOeJ2LAAhBFLjYmg=
|
||||
github.com/hashicorp/packer-plugin-sdk v0.3.0/go.mod h1:bqpbL7w5Ee2QWrUyAsZI/MdCYpw15ls4mxgn9Ei2DZc=
|
||||
github.com/hashicorp/serf v0.9.5 h1:EBWvyu9tcRszt3Bxp3KNssBMP1KuHWyO51lz9+786iM=
|
||||
github.com/hashicorp/serf v0.9.5/go.mod h1:UWDWwZeL5cuWDJdl0C6wrvrUwEqtQ4ZKBKKENpqIUyk=
|
||||
github.com/hashicorp/vault/api v1.0.5-0.20200519221902-385fac77e20f/go.mod h1:euTFbi2YJgwcju3imEt919lhJKF68nN1cQPq3aA+kBE=
|
||||
github.com/hashicorp/vault/api v1.1.1 h1:907ld+Z9cALyvbZK2qUX9cLwvSaEQsMVQB3x2KE8+AI=
|
||||
github.com/hashicorp/vault/api v1.1.1/go.mod h1:29UXcn/1cLOPHQNMWA7bCz2By4PSd0VKPAydKXS5yN0=
|
||||
github.com/hashicorp/vault/sdk v0.1.14-0.20200519221530-14615acda45f/go.mod h1:WX57W2PwkrOPQ6rVQk+dy5/htHIaB4aBM70EwKThu10=
|
||||
github.com/hashicorp/vault/sdk v0.2.1 h1:S4O6Iv/dyKlE9AUTXGa7VOvZmsCvg36toPKgV4f2P4M=
|
||||
github.com/hashicorp/vault/sdk v0.2.1/go.mod h1:WfUiO1vYzfBkz1TmoE4ZGU7HD0T0Cl/rZwaxjBkgN4U=
|
||||
github.com/hashicorp/yamux v0.0.0-20180604194846-3520598351bb/go.mod h1:+NfK9FKeTrX5uv1uIXGdwYDTeHna2qgaIlx54MXqjAM=
|
||||
github.com/hashicorp/yamux v0.0.0-20210826001029-26ff87cf9493 h1:brI5vBRUlAlM34VFmnLPwjnCL/FxAJp9XvOdX6Zt+XE=
|
||||
github.com/hashicorp/yamux v0.0.0-20210826001029-26ff87cf9493/go.mod h1:CtWFDAQgb7dxtzFs4tWbplKIe2jSi3+5vKbgIO0SLnQ=
|
||||
github.com/hpcloud/tail v1.0.0/go.mod h1:ab1qPbhIpdTxEkNHXyeSf5vhxWSCs/tWer42PpOxQnU=
|
||||
github.com/ianlancetaylor/demangle v0.0.0-20181102032728-5e5cf60278f6/go.mod h1:aSSvb/t6k1mPoxDqO4vJh6VOCGPwU4O0C2/Eqndh1Sc=
|
||||
github.com/inconshreveable/mousetrap v1.0.0/go.mod h1:PxqpIevigyE2G7u3NXJIT2ANytuPF1OarO4DADm73n8=
|
||||
github.com/jehiah/go-strftime v0.0.0-20171201141054-1d33003b3869 h1:IPJ3dvxmJ4uczJe5YQdrYB16oTJlGSC/OyZDqUk9xX4=
|
||||
github.com/jehiah/go-strftime v0.0.0-20171201141054-1d33003b3869/go.mod h1:cJ6Cj7dQo+O6GJNiMx+Pa94qKj+TG8ONdKHgMNIyyag=
|
||||
github.com/jmespath/go-jmespath v0.0.0-20180206201540-c2b33e8439af/go.mod h1:Nht3zPeWKUH0NzdCt2Blrr5ys8VGpn0CEB0cQHVjt7k=
|
||||
github.com/jmespath/go-jmespath v0.3.0/go.mod h1:9QtRXoHjLGCJ5IBSaohpXITPlowMeeYCZ7fLUTSywik=
|
||||
github.com/jmespath/go-jmespath v0.4.0 h1:BEgLn5cpjn8UN1mAw4NjwDrS35OdebyEtFe+9YPoQUg=
|
||||
github.com/jmespath/go-jmespath v0.4.0/go.mod h1:T8mJZnbsbmF+m6zOOFylbeCJqk5+pHWvzYPziyZiYoo=
|
||||
github.com/jmespath/go-jmespath/internal/testify v1.5.1 h1:shLQSRRSCCPj3f2gpwzGwWFoC7ycTf1rcQZHOlsJ6N8=
|
||||
github.com/jmespath/go-jmespath/internal/testify v1.5.1/go.mod h1:L3OGu8Wl2/fWfCI6z80xFu9LTZmf1ZRjMHUOPmWr69U=
|
||||
github.com/json-iterator/go v1.1.6/go.mod h1:+SdeFBvtyEkXs7REEP0seUULqWtbJapLOCVDaaPEHmU=
|
||||
github.com/json-iterator/go v1.1.9/go.mod h1:KdQUCv79m/52Kvf8AW2vK1V8akMuk1QjK/uOdHXbAo4=
|
||||
github.com/jstemmer/go-junit-report v0.0.0-20190106144839-af01ea7f8024/go.mod h1:6v2b51hI/fHJwM22ozAgKL4VKDeJcHhJFhtBdhmNjmU=
|
||||
github.com/jstemmer/go-junit-report v0.9.1/go.mod h1:Brl9GWCQeLvo8nXZwPNNblvFj/XSXhF0NWZEnDohbsk=
|
||||
github.com/julienschmidt/httprouter v1.2.0/go.mod h1:SYymIcj16QtmaHHD7aYtjjsJG7VTCxuUUipMqKk8s4w=
|
||||
github.com/kisielk/errcheck v1.1.0/go.mod h1:EZBBE59ingxPouuu3KfxchcWSUPOHkagtvWXihfKN4Q=
|
||||
github.com/kisielk/errcheck v1.2.0/go.mod h1:/BMXB+zMLi60iA8Vv6Ksmxu/1UDYcXs4uQLJ+jE2L00=
|
||||
github.com/kisielk/gotool v1.0.0/go.mod h1:XhKaO+MFFWcvkIS/tQcRk01m1F5IRFswLeQ+oQHNcck=
|
||||
github.com/konsorten/go-windows-terminal-sequences v1.0.1/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ=
|
||||
github.com/konsorten/go-windows-terminal-sequences v1.0.3 h1:CE8S1cTafDpPvMhIxNJKvHsGVBgn1xWYf1NbHQhywc8=
|
||||
github.com/konsorten/go-windows-terminal-sequences v1.0.3/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ=
|
||||
github.com/kr/fs v0.1.0 h1:Jskdu9ieNAYnjxsi0LbQp1ulIKZV1LAFgK1tWhpZgl8=
|
||||
github.com/kr/fs v0.1.0/go.mod h1:FFnZGqtBN9Gxj7eW1uZ42v5BccTP0vu6NEaFoC2HwRg=
|
||||
github.com/kr/logfmt v0.0.0-20140226030751-b84e30acd515/go.mod h1:+0opPa2QZZtGFBFZlji/RkVcI2GknAs/DXo4wKdlNEc=
|
||||
github.com/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORNo=
|
||||
github.com/kr/pretty v0.2.0 h1:s5hAObm+yFO5uHYt5dYjxi2rXrsnmRpJx4OYvIWUaQs=
|
||||
github.com/kr/pretty v0.2.0/go.mod h1:ipq/a2n7PKx3OHsz4KJII5eveXtPO4qwEXGdVfWzfnI=
|
||||
github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ=
|
||||
github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI=
|
||||
github.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY=
|
||||
github.com/kr/text v0.2.0/go.mod h1:eLer722TekiGuMkidMxC/pM04lWEeraHUUmBw8l2grE=
|
||||
github.com/masterzen/simplexml v0.0.0-20160608183007-4572e39b1ab9/go.mod h1:kCEbxUJlNDEBNbdQMkPSp6yaKcRXVI6f4ddk8Riv4bc=
|
||||
github.com/masterzen/simplexml v0.0.0-20190410153822-31eea3082786 h1:2ZKn+w/BJeL43sCxI2jhPLRv73oVVOjEKZjKkflyqxg=
|
||||
github.com/masterzen/simplexml v0.0.0-20190410153822-31eea3082786/go.mod h1:kCEbxUJlNDEBNbdQMkPSp6yaKcRXVI6f4ddk8Riv4bc=
|
||||
github.com/masterzen/winrm v0.0.0-20210623064412-3b76017826b0 h1:KqYuDbSr8I2X8H65InN8SafDEa0UaLRy6WEmxDqd0F0=
|
||||
github.com/masterzen/winrm v0.0.0-20210623064412-3b76017826b0/go.mod h1:l31LCh9VvG43RJ83A5JLkFPjuz48cZAxBSLQLaIn1p8=
|
||||
github.com/mattn/go-colorable v0.0.9/go.mod h1:9vuHe8Xs5qXnSaW/c/ABM9alt+Vo+STaOChaDxuIBZU=
|
||||
github.com/mattn/go-colorable v0.1.4/go.mod h1:U0ppj6V5qS13XJ6of8GYAs25YV2eR4EVcfRqFIhoBtE=
|
||||
github.com/mattn/go-colorable v0.1.6/go.mod h1:u6P/XSegPjTcexA+o6vUJrdnUu04hMope9wVRipJSqc=
|
||||
github.com/mattn/go-colorable v0.1.8 h1:c1ghPdyEDarC70ftn0y+A/Ee++9zz8ljHG1b13eJ0s8=
|
||||
github.com/mattn/go-colorable v0.1.8/go.mod h1:u6P/XSegPjTcexA+o6vUJrdnUu04hMope9wVRipJSqc=
|
||||
github.com/mattn/go-isatty v0.0.3/go.mod h1:M+lRXTBqGeGNdLjl/ufCoiOlB5xdOkqRJdNxMWT7Zi4=
|
||||
github.com/mattn/go-isatty v0.0.8/go.mod h1:Iq45c/XA43vh69/j3iqttzPXn0bhXyGjM0Hdxcsrc5s=
|
||||
github.com/mattn/go-isatty v0.0.10/go.mod h1:qgIWMr58cqv1PHHyhnkY9lrL7etaEgOFcMEpPG5Rm84=
|
||||
github.com/mattn/go-isatty v0.0.11/go.mod h1:PhnuNfih5lzO57/f3n+odYbM4JtupLOxQOAqxQCu2WE=
|
||||
github.com/mattn/go-isatty v0.0.12/go.mod h1:cbi8OIDigv2wuxKPP5vlRcQ1OAZbq2CE4Kysco4FUpU=
|
||||
github.com/mattn/go-isatty v0.0.13 h1:qdl+GuBjcsKKDco5BsxPJlId98mSWNKqYA+Co0SC1yA=
|
||||
github.com/mattn/go-isatty v0.0.13/go.mod h1:cbi8OIDigv2wuxKPP5vlRcQ1OAZbq2CE4Kysco4FUpU=
|
||||
github.com/matttproud/golang_protobuf_extensions v1.0.1/go.mod h1:D8He9yQNgCq6Z5Ld7szi9bcBfOoFv/3dc6xSMkL2PC0=
|
||||
github.com/miekg/dns v1.0.14/go.mod h1:W1PPwlIAgtquWBMBEV9nkV9Cazfe8ScdGz/Lj7v3Nrg=
|
||||
github.com/miekg/dns v1.1.26 h1:gPxPSwALAeHJSjarOs00QjVdV9QoBvc1D2ujQUr5BzU=
|
||||
github.com/miekg/dns v1.1.26/go.mod h1:bPDLeHnStXmXAq1m/Ch/hvfNHr14JKNPMBo3VZKjuso=
|
||||
github.com/mitchellh/cli v1.0.0/go.mod h1:hNIlj7HEI86fIcpObd7a0FcrxTWetlwJDGcceTlRvqc=
|
||||
github.com/mitchellh/cli v1.1.0/go.mod h1:xcISNoH86gajksDmfB23e/pu+B+GeFRMYmoHXxx3xhI=
|
||||
github.com/mitchellh/copystructure v1.0.0/go.mod h1:SNtv71yrdKgLRyLFxmLdkAbkKEFWgYaq1OVrnRcwhnw=
|
||||
github.com/mitchellh/go-fs v0.0.0-20180402235330-b7b9ca407fff h1:bFJ74ac7ZK/jyislqiWdzrnENesFt43sNEBRh1xk/+g=
|
||||
github.com/mitchellh/go-fs v0.0.0-20180402235330-b7b9ca407fff/go.mod h1:g7SZj7ABpStq3tM4zqHiVEG5un/DZ1+qJJKO7qx1EvU=
|
||||
github.com/mitchellh/go-homedir v1.0.0/go.mod h1:SfyaCUpYCn1Vlf4IUYiD9fPX4A5wJrkLzIz1N1q0pr0=
|
||||
github.com/mitchellh/go-homedir v1.1.0 h1:lukF9ziXFxDFPkA1vsr5zpc1XuPDn/wFntq5mG+4E0Y=
|
||||
github.com/mitchellh/go-homedir v1.1.0/go.mod h1:SfyaCUpYCn1Vlf4IUYiD9fPX4A5wJrkLzIz1N1q0pr0=
|
||||
github.com/mitchellh/go-testing-interface v0.0.0-20171004221916-a61a99592b77/go.mod h1:kRemZodwjscx+RGhAo8eIhFbs2+BFgRtFPeD/KE+zxI=
|
||||
github.com/mitchellh/go-testing-interface v1.0.0/go.mod h1:kRemZodwjscx+RGhAo8eIhFbs2+BFgRtFPeD/KE+zxI=
|
||||
github.com/mitchellh/go-testing-interface v1.14.1 h1:jrgshOhYAUVNMAJiKbEu7EqAwgJJ2JqpQmpLJOu07cU=
|
||||
github.com/mitchellh/go-testing-interface v1.14.1/go.mod h1:gfgS7OtZj6MA4U1UrDRp04twqAjfvlZyCfX3sDjEym8=
|
||||
github.com/mitchellh/go-vnc v0.0.0-20150629162542-723ed9867aed h1:FI2NIv6fpef6BQl2u3IZX/Cj20tfypRF4yd+uaHOMtI=
|
||||
github.com/mitchellh/go-vnc v0.0.0-20150629162542-723ed9867aed/go.mod h1:3rdaFaCv4AyBgu5ALFM0+tSuHrBh6v692nyQe3ikrq0=
|
||||
github.com/mitchellh/go-wordwrap v1.0.0/go.mod h1:ZXFpozHsX6DPmq2I0TCekCxypsnAUbP2oI0UX1GXzOo=
|
||||
github.com/mitchellh/go-wordwrap v1.0.1 h1:TLuKupo69TCn6TQSyGxwI1EblZZEsQ0vMlAFQflz0v0=
|
||||
github.com/mitchellh/go-wordwrap v1.0.1/go.mod h1:R62XHJLzvMFRBbcrT7m7WgmE1eOyTSsCt+hzestvNj0=
|
||||
github.com/mitchellh/iochan v1.0.0 h1:C+X3KsSTLFVBr/tK1eYN/vs4rJcvsiLU338UhYPJWeY=
|
||||
github.com/mitchellh/iochan v1.0.0/go.mod h1:JwYml1nuB7xOzsp52dPpHFffvOCDupsG0QubkSMEySY=
|
||||
github.com/mitchellh/mapstructure v0.0.0-20160808181253-ca63d7c062ee/go.mod h1:FVVH3fgwuzCH5S8UJGiWEs2h04kUh9fWfEaFds41c1Y=
|
||||
github.com/mitchellh/mapstructure v1.1.2/go.mod h1:FVVH3fgwuzCH5S8UJGiWEs2h04kUh9fWfEaFds41c1Y=
|
||||
github.com/mitchellh/mapstructure v1.3.2/go.mod h1:bFUtVrKA4DC2yAKiSyO/QUcy7e+RRV2QTWOzhPopBRo=
|
||||
github.com/mitchellh/mapstructure v1.4.1 h1:CpVNEelQCZBooIPDn+AR3NpivK/TIKU8bDxdASFVQag=
|
||||
github.com/mitchellh/mapstructure v1.4.1/go.mod h1:bFUtVrKA4DC2yAKiSyO/QUcy7e+RRV2QTWOzhPopBRo=
|
||||
github.com/mitchellh/reflectwalk v1.0.0 h1:9D+8oIskB4VJBN5SFlmc27fSlIBZaov1Wpk/IfikLNY=
|
||||
github.com/mitchellh/reflectwalk v1.0.0/go.mod h1:mSTlrgnPZtwu0c4WaC2kGObEpuNDbx0jmZXqmk4esnw=
|
||||
github.com/modern-go/concurrent v0.0.0-20180228061459-e0a39a4cb421/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q=
|
||||
github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q=
|
||||
github.com/modern-go/reflect2 v0.0.0-20180701023420-4b7aa43c6742/go.mod h1:bx2lNnkwVCuqBIxFjflWJWanXIb3RllmbCylyMrvgv0=
|
||||
github.com/modern-go/reflect2 v1.0.1/go.mod h1:bx2lNnkwVCuqBIxFjflWJWanXIb3RllmbCylyMrvgv0=
|
||||
github.com/morikuni/aec v1.0.0/go.mod h1:BbKIizmSmc5MMPqRYbxO4ZU0S0+P200+tUnFx7PXmsc=
|
||||
github.com/mwitkow/go-conntrack v0.0.0-20161129095857-cc309e4a2223/go.mod h1:qRWi+5nqEBWmkhHvq77mSJWrCKwh8bxhgT7d/eI7P4U=
|
||||
github.com/niemeyer/pretty v0.0.0-20200227124842-a10e7caefd8e h1:fD57ERR4JtEqsWbfPhv4DMiApHyliiK5xCTNVSPiaAs=
|
||||
github.com/niemeyer/pretty v0.0.0-20200227124842-a10e7caefd8e/go.mod h1:zD1mROLANZcx1PVRCS0qkT7pwLkGfwJo4zjcN/Tysno=
|
||||
github.com/nu7hatch/gouuid v0.0.0-20131221200532-179d4d0c4d8d h1:VhgPp6v9qf9Agr/56bj7Y/xa04UccTW04VP0Qed4vnQ=
|
||||
github.com/nu7hatch/gouuid v0.0.0-20131221200532-179d4d0c4d8d/go.mod h1:YUTz3bUH2ZwIWBy3CJBeOBEugqcmXREj14T+iG/4k4U=
|
||||
github.com/nywilken/go-cty v1.13.3 h1:03U99oXf3j3g9xgqAE3YGpixCjM8Mg09KZ0Ji9LzX0o=
|
||||
github.com/nywilken/go-cty v1.13.3/go.mod h1:YKQzy/7pZ7iq2jNFzy5go57xdxdWoLLpaEp4u238AE0=
|
||||
github.com/oklog/run v1.0.0/go.mod h1:dlhp/R75TPv97u0XWUtDeV/lRKWPKSdTuV0TZvrmrQA=
|
||||
github.com/onsi/ginkgo v1.6.0/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE=
|
||||
github.com/onsi/ginkgo v1.10.1/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE=
|
||||
github.com/onsi/gomega v1.7.0/go.mod h1:ex+gbHU/CVuBBDIJjb2X0qEXbFg53c61hWP/1CpauHY=
|
||||
github.com/opencontainers/go-digest v0.0.0-20180430190053-c9281466c8b2/go.mod h1:cMLVZDEM3+U2I4VmLI6N8jQYUd2OVphdqWwCJHrFt2s=
|
||||
github.com/opencontainers/go-digest v1.0.0-rc1/go.mod h1:cMLVZDEM3+U2I4VmLI6N8jQYUd2OVphdqWwCJHrFt2s=
|
||||
github.com/opencontainers/go-digest v1.0.0/go.mod h1:0JzlMkj0TRzQZfJkVvzbP0HBR3IKzErnv2BNG4W4MAM=
|
||||
github.com/opencontainers/image-spec v1.0.1/go.mod h1:BtxoFyWECRxE4U/7sNtV5W15zMzWCbyJoFRP3s7yZA0=
|
||||
github.com/opencontainers/runc v0.0.0-20190115041553-12f6a991201f/go.mod h1:qT5XzbpPznkRYVz/mWwUaVBUv2rmF59PVA73FjuZG0U=
|
||||
github.com/opencontainers/runc v0.1.1/go.mod h1:qT5XzbpPznkRYVz/mWwUaVBUv2rmF59PVA73FjuZG0U=
|
||||
github.com/opencontainers/runtime-spec v0.1.2-0.20190507144316-5b71a03e2700/go.mod h1:jwyrGlmzljRJv/Fgzds9SsS/C5hL+LL3ko9hs6T5lQ0=
|
||||
github.com/packer-community/winrmcp v0.0.0-20180921211025-c76d91c1e7db h1:9uViuKtx1jrlXLBW/pMnhOfzn3iSEdLase/But/IZRU=
|
||||
github.com/packer-community/winrmcp v0.0.0-20180921211025-c76d91c1e7db/go.mod h1:f6Izs6JvFTdnRbziASagjZ2vmf55NSIkC/weStxCHqk=
|
||||
github.com/pascaldekloe/goe v0.0.0-20180627143212-57f6aae5913c/go.mod h1:lzWF7FIEvWOWxwDKqyGYQf6ZUaNfKdP144TG7ZOy1lc=
|
||||
github.com/pascaldekloe/goe v0.1.0 h1:cBOtyMzM9HTpWjXfbbunk26uA6nG3a8n06Wieeh0MwY=
|
||||
github.com/pascaldekloe/goe v0.1.0/go.mod h1:lzWF7FIEvWOWxwDKqyGYQf6ZUaNfKdP144TG7ZOy1lc=
|
||||
github.com/pierrec/lz4 v2.0.5+incompatible/go.mod h1:pdkljMzZIN41W+lC3N2tnIh5sFi+IEE17M5jbnwPHcY=
|
||||
github.com/pierrec/lz4 v2.5.2+incompatible/go.mod h1:pdkljMzZIN41W+lC3N2tnIh5sFi+IEE17M5jbnwPHcY=
|
||||
github.com/pierrec/lz4 v2.6.1+incompatible h1:9UY3+iC23yxF0UfGaYrGplQ+79Rg+h/q9FV9ix19jjM=
|
||||
github.com/pierrec/lz4 v2.6.1+incompatible/go.mod h1:pdkljMzZIN41W+lC3N2tnIh5sFi+IEE17M5jbnwPHcY=
|
||||
github.com/pkg/errors v0.8.0/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
|
||||
github.com/pkg/errors v0.8.1-0.20171018195549-f15c970de5b7/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
|
||||
github.com/pkg/errors v0.8.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
|
||||
github.com/pkg/errors v0.9.1 h1:FEBLx1zS214owpjy7qsBeixbURkuhQAwrK5UwLGTwt4=
|
||||
github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
|
||||
github.com/pkg/sftp v1.13.2 h1:taJnKntsWgU+qae21Rx52lIwndAdKrj0mfUNQsz1z4Q=
|
||||
github.com/pkg/sftp v1.13.2/go.mod h1:LzqnAvaD5TWeNBsZpfKxSYn1MbjWwOsCIAFFJbpIsK8=
|
||||
github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
|
||||
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
|
||||
github.com/posener/complete v1.1.1/go.mod h1:em0nMJCgc9GFtwrmVmEMR/ZL6WyhyjMBndrE9hABlRI=
|
||||
github.com/posener/complete v1.2.3/go.mod h1:WZIdtGGp+qx0sLrYKtIRAruyNpv6hFCicSgv7Sy7s/s=
|
||||
github.com/prometheus/client_golang v0.9.1/go.mod h1:7SWBe2y4D6OKWSNQJUaRYU/AaXPKyh/dDVn+NZz0KFw=
|
||||
github.com/prometheus/client_golang v0.9.2/go.mod h1:OsXs2jCmiKlQ1lTBmv21f2mNfw4xf/QclQDMrYNZzcM=
|
||||
github.com/prometheus/client_golang v1.0.0/go.mod h1:db9x61etRT2tGnBNRi70OPL5FsnadC4Ky3P0J6CfImo=
|
||||
github.com/prometheus/client_golang v1.4.0/go.mod h1:e9GMxYsXl05ICDXkRhurwBS4Q3OK1iX/F2sw+iXX5zU=
|
||||
github.com/prometheus/client_model v0.0.0-20180712105110-5c3871d89910/go.mod h1:MbSGuTsp3dbXC40dX6PRTWyKYBIrTGTE9sqQNg2J8bo=
|
||||
github.com/prometheus/client_model v0.0.0-20190129233127-fd36f4220a90/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA=
|
||||
github.com/prometheus/client_model v0.0.0-20190812154241-14fe0d1b01d4/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA=
|
||||
github.com/prometheus/client_model v0.2.0/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA=
|
||||
github.com/prometheus/common v0.0.0-20181126121408-4724e9255275/go.mod h1:daVV7qP5qjZbuso7PdcryaAu0sAZbrN9i7WWcTMWvro=
|
||||
github.com/prometheus/common v0.4.1/go.mod h1:TNfzLD0ON7rHzMJeJkieUDPYmFC7Snx/y86RQel1bk4=
|
||||
github.com/prometheus/common v0.9.1/go.mod h1:yhUN8i9wzaXS3w1O07YhxHEBxD+W35wd8bs7vj7HSQ4=
|
||||
github.com/prometheus/procfs v0.0.0-20180125133057-cb4147076ac7/go.mod h1:c3At6R/oaqEKCNdg8wHV1ftS6bRYblBhIjjI8uT2IGk=
|
||||
github.com/prometheus/procfs v0.0.0-20181005140218-185b4288413d/go.mod h1:c3At6R/oaqEKCNdg8wHV1ftS6bRYblBhIjjI8uT2IGk=
|
||||
github.com/prometheus/procfs v0.0.0-20181204211112-1dc9a6cbc91a/go.mod h1:c3At6R/oaqEKCNdg8wHV1ftS6bRYblBhIjjI8uT2IGk=
|
||||
github.com/prometheus/procfs v0.0.2/go.mod h1:TjEm7ze935MbeOT/UhFTIMYKhuLP4wbCsTZCD3I8kEA=
|
||||
github.com/prometheus/procfs v0.0.8/go.mod h1:7Qr8sr6344vo1JqZ6HhLceV9o3AJ1Ff+GxbHq6oeK9A=
|
||||
github.com/rogpeppe/go-internal v1.3.0/go.mod h1:M8bDsm7K2OlrFYOpmOWEs/qY81heoFRclV5y23lUDJ4=
|
||||
github.com/ryanuber/columnize v0.0.0-20160712163229-9b3edd62028f/go.mod h1:sm1tb6uqfes/u+d4ooFouqFdy9/2g9QGwK3SQygK0Ts=
|
||||
github.com/ryanuber/columnize v2.1.0+incompatible/go.mod h1:sm1tb6uqfes/u+d4ooFouqFdy9/2g9QGwK3SQygK0Ts=
|
||||
github.com/ryanuber/go-glob v1.0.0 h1:iQh3xXAumdQ+4Ufa5b25cRpC5TYKlno6hsv6Cb3pkBk=
|
||||
github.com/ryanuber/go-glob v1.0.0/go.mod h1:807d1WSdnB0XRJzKNil9Om6lcp/3a0v4qIHxIXzX/Yc=
|
||||
github.com/satori/go.uuid v1.2.0 h1:0uYX9dsZ2yD7q2RtLRtPSdGDWzjeM3TbMJP9utgA0ww=
|
||||
github.com/sean-/seed v0.0.0-20170313163322-e2103e2c3529 h1:nn5Wsu0esKSJiIVhscUtVbo7ada43DJhG55ua/hjS5I=
|
||||
github.com/sean-/seed v0.0.0-20170313163322-e2103e2c3529/go.mod h1:DxrIzT+xaE7yg65j358z/aeFdxmN0P9QXhEzd20vsDc=
|
||||
github.com/sirupsen/logrus v1.0.4-0.20170822132746-89742aefa4b2/go.mod h1:pMByvHTf9Beacp5x1UXfOR9xyW/9antXMhjMPG0dEzc=
|
||||
github.com/sirupsen/logrus v1.2.0/go.mod h1:LxeOpSwHxABJmUn/MG1IvRgCAasNZTLOkJPxbbu5VWo=
|
||||
github.com/sirupsen/logrus v1.4.1/go.mod h1:ni0Sbl8bgC9z8RoU9G6nDWqqs/fq4eDPysMBDgk/93Q=
|
||||
github.com/sirupsen/logrus v1.4.2/go.mod h1:tLMulIdttU9McNUspp0xgXVQah82FyeX6MwdIuYE2rE=
|
||||
github.com/sirupsen/logrus v1.6.0 h1:UBcNElsrwanuuMsnGSlYmtmgbb23qDR5dG+6X6Oo89I=
|
||||
github.com/sirupsen/logrus v1.6.0/go.mod h1:7uNnSEd1DgxDLC74fIahvMZmmYsHGZGEOFrfsX/uA88=
|
||||
github.com/spf13/cobra v0.0.2-0.20171109065643-2da4a54c5cee/go.mod h1:1l0Ry5zgKvJasoi3XT1TypsSe7PqH0Sj9dhYf7v3XqQ=
|
||||
github.com/spf13/pflag v1.0.1-0.20171106142849-4c012f6dcd95/go.mod h1:DYY7MBk1bdzusC3SYhjObp+wFpr4gzcvqqNjLnInEg4=
|
||||
github.com/spf13/pflag v1.0.3/go.mod h1:DYY7MBk1bdzusC3SYhjObp+wFpr4gzcvqqNjLnInEg4=
|
||||
github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
|
||||
github.com/stretchr/objx v0.1.1/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
|
||||
github.com/stretchr/objx v0.4.0/go.mod h1:YvHI0jy2hoMjB+UWwv71VJQ9isScKT/TqJzVSSt89Yw=
|
||||
github.com/stretchr/objx v0.5.0 h1:1zr/of2m5FGMsad5YfcqgdqdWrIhu+EBEJRhR1U7z/c=
|
||||
github.com/stretchr/objx v0.5.0/go.mod h1:Yh+to48EsGEfYuaHDzXPcE3xhTkx73EhmCGUpEOglKo=
|
||||
github.com/stretchr/testify v1.2.2/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs=
|
||||
github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI=
|
||||
github.com/stretchr/testify v1.4.0/go.mod h1:j7eGeouHqKxXV5pUuKE4zz7dFj8WfuZ+81PSLYec5m4=
|
||||
github.com/stretchr/testify v1.5.1/go.mod h1:5W2xD1RspED5o8YsWQXVCued0rvSQ+mT+I5cxcmMvtA=
|
||||
github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
|
||||
github.com/stretchr/testify v1.7.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
|
||||
github.com/stretchr/testify v1.8.0/go.mod h1:yNjHg4UonilssWZ8iaSj1OCr/vHnekPRkoO+kdMU+MU=
|
||||
github.com/stretchr/testify v1.8.1 h1:w7B6lhMri9wdJUVmEZPGGhZzrYTPvgJArz7wNPgYKsk=
|
||||
github.com/stretchr/testify v1.8.1/go.mod h1:w2LPCIKwWwSfY2zedu0+kehJoqGctiVI29o6fzry7u4=
|
||||
github.com/terra-farm/go-xen-api-client v0.0.2 h1:EREW0N6XqU935b005yCwbPjm8F6582j2rI5C4ew3ZTQ=
|
||||
github.com/terra-farm/go-xen-api-client v0.0.2/go.mod h1:L7+Ea6rxzK7AL4DhcEPDQxdLKb4wKq0sYbxHS2ag9YE=
|
||||
github.com/tv42/httpunix v0.0.0-20150427012821-b75d8614f926/go.mod h1:9ESjWnEqriFuLhtthL60Sar/7RFoluCcXsuvEwTV5KM=
|
||||
github.com/ugorji/go v1.2.6/go.mod h1:anCg0y61KIhDlPZmnH+so+RQbysYVyDko0IMgJv0Nn0=
|
||||
github.com/ugorji/go/codec v1.2.6 h1:7kbGefxLoDBuYXOms4yD7223OpNMMPNPZxXk5TvFcyQ=
|
||||
github.com/ugorji/go/codec v1.2.6/go.mod h1:V6TCNZ4PHqoHGFZuSG1W8nrCzzdgA2DozYxWFFpvxTw=
|
||||
github.com/ulikunitz/xz v0.5.8/go.mod h1:nbz6k7qbPmH4IRqmfOplQw/tblSgqTqBwxkY0oWt/14=
|
||||
github.com/ulikunitz/xz v0.5.10 h1:t92gobL9l3HE202wg3rlk19F6X+JOxl9BBrCCMYEYd8=
|
||||
github.com/ulikunitz/xz v0.5.10/go.mod h1:nbz6k7qbPmH4IRqmfOplQw/tblSgqTqBwxkY0oWt/14=
|
||||
github.com/urfave/cli v0.0.0-20171014202726-7bc6a0acffa5/go.mod h1:70zkFmudgCuE/ngEzBv17Jvp/497gISqfk5gWijbERA=
|
||||
github.com/zclconf/go-cty-debug v0.0.0-20191215020915-b22d67c1ba0b h1:FosyBZYxY34Wul7O/MSKey3txpPYyCqVO5ZyceuQJEI=
|
||||
go.opencensus.io v0.21.0/go.mod h1:mSImk1erAIZhrmZN+AvHh14ztQfjbGwt4TtuofqLduU=
|
||||
go.opencensus.io v0.22.0/go.mod h1:+kGneAE2xo2IficOXnaByMWTGM9T73dGwxeWcUqIpI8=
|
||||
go.opencensus.io v0.22.2/go.mod h1:yxeiOL68Rb0Xd1ddK5vPZ/oVn4vY4Ynel7k9FzqtOIw=
|
||||
go.opencensus.io v0.22.3/go.mod h1:yxeiOL68Rb0Xd1ddK5vPZ/oVn4vY4Ynel7k9FzqtOIw=
|
||||
go.opencensus.io v0.24.0 h1:y73uSU6J157QMP2kn2r30vwW1A2W2WFwSCGnAVxeaD0=
|
||||
go.opencensus.io v0.24.0/go.mod h1:vNK8G9p7aAivkbmorf4v+7Hgx+Zs0yY+0fOtgBfjQKo=
|
||||
go.uber.org/atomic v1.6.0/go.mod h1:sABNBOSYdrvTF6hTgEIbc7YasKWGhgEQZyfxyTvoXHQ=
|
||||
golang.org/x/crypto v0.0.0-20171113213409-9f005a07e0d3/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4=
|
||||
golang.org/x/crypto v0.0.0-20180904163835-0709b304e793/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4=
|
||||
golang.org/x/crypto v0.0.0-20181029021203-45a5f77698d3/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4=
|
||||
golang.org/x/crypto v0.0.0-20190222235706-ffb98f73852f/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4=
|
||||
golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=
|
||||
golang.org/x/crypto v0.0.0-20190418165655-df01cb2cc480/go.mod h1:WFFai1msRO1wXaEeE5yQxYXgSfI8pQAWXbQop6sCtWE=
|
||||
golang.org/x/crypto v0.0.0-20190510104115-cbcb75029529/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
|
||||
golang.org/x/crypto v0.0.0-20190605123033-f99c8df09eb5/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
|
||||
golang.org/x/crypto v0.0.0-20190923035154-9ee001bba392/go.mod h1:/lpIB1dKB+9EgE3H3cr1v9wB50oz8l4C4h62xy7jSTY=
|
||||
golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
|
||||
golang.org/x/crypto v0.0.0-20200604202706-70a84ac30bf9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=
|
||||
golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=
|
||||
golang.org/x/crypto v0.0.0-20210421170649-83a5a9bb288b/go.mod h1:T9bdIzuCu7OtxOm1hfPfRQxPLYneinmdGuTeoZ9dtd4=
|
||||
golang.org/x/crypto v0.24.0 h1:mnl8DM0o513X8fdIkmyFE/5hTYxbwYOjDS/+rK6qpRI=
|
||||
golang.org/x/crypto v0.24.0/go.mod h1:Z1PMYSOR5nyMcyAVAIQSKCDwalqy85Aqn1x3Ws4L5DM=
|
||||
golang.org/x/exp v0.0.0-20190121172915-509febef88a4/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA=
|
||||
golang.org/x/exp v0.0.0-20190306152737-a1d7652674e8/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA=
|
||||
golang.org/x/exp v0.0.0-20190510132918-efd6b22b2522/go.mod h1:ZjyILWgesfNpC6sMxTJOJm9Kp84zZh5NQWvqDGG3Qr8=
|
||||
golang.org/x/exp v0.0.0-20190829153037-c13cbed26979/go.mod h1:86+5VVa7VpoJ4kLfm080zCjGlMRFzhUhsZKEZO7MGek=
|
||||
golang.org/x/exp v0.0.0-20191030013958-a1ab85dbe136/go.mod h1:JXzH8nQsPlswgeRAPE3MuO9GYsAcnJvJ4vnMwN/5qkY=
|
||||
golang.org/x/exp v0.0.0-20191129062945-2f5052295587/go.mod h1:2RIsYlXP63K8oxa1u096TMicItID8zy7Y6sNkU49FU4=
|
||||
golang.org/x/exp v0.0.0-20191227195350-da58074b4299/go.mod h1:2RIsYlXP63K8oxa1u096TMicItID8zy7Y6sNkU49FU4=
|
||||
golang.org/x/exp v0.0.0-20200119233911-0405dc783f0a/go.mod h1:2RIsYlXP63K8oxa1u096TMicItID8zy7Y6sNkU49FU4=
|
||||
golang.org/x/exp v0.0.0-20200207192155-f17229e696bd/go.mod h1:J/WKrq2StrnmMY6+EHIKF9dgMWnmCNThgcyBT1FY9mM=
|
||||
golang.org/x/exp v0.0.0-20200224162631-6cc2880d07d6/go.mod h1:3jZMyOhIsHpP37uCMkUooju7aAi5cS1Q23tOzKc+0MU=
|
||||
golang.org/x/image v0.0.0-20190227222117-0694c2d4d067/go.mod h1:kZ7UVZpmo3dzQBMxlp+ypCbDeSB+sBbTgSJuh5dn5js=
|
||||
golang.org/x/image v0.0.0-20190802002840-cff245a6509b/go.mod h1:FeLwcggjj3mMvU+oOTbSwawSJRM1uh48EjtB4UJZlP0=
|
||||
golang.org/x/lint v0.0.0-20181026193005-c67002cb31c3/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE=
|
||||
golang.org/x/lint v0.0.0-20190227174305-5b3e6a55c961/go.mod h1:wehouNa3lNwaWXcvxsM5YxQ5yQlVC4a0KAMCusXpPoU=
|
||||
golang.org/x/lint v0.0.0-20190301231843-5614ed5bae6f/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE=
|
||||
golang.org/x/lint v0.0.0-20190313153728-d0100b6bd8b3/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc=
|
||||
golang.org/x/lint v0.0.0-20190409202823-959b441ac422/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc=
|
||||
golang.org/x/lint v0.0.0-20190909230951-414d861bb4ac/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc=
|
||||
golang.org/x/lint v0.0.0-20190930215403-16217165b5de/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc=
|
||||
golang.org/x/lint v0.0.0-20191125180803-fdd1cda4f05f/go.mod h1:5qLYkcX4OjUUV8bRuDixDT3tpyyb+LUpUlRWLxfhWrs=
|
||||
golang.org/x/lint v0.0.0-20200130185559-910be7a94367/go.mod h1:3xt1FjdF8hUf6vQPIChWIBhFzV8gjjsPE/fR3IyQdNY=
|
||||
golang.org/x/mobile v0.0.0-20190312151609-d3739f865fa6/go.mod h1:z+o9i4GpDbdi3rU15maQ/Ox0txvL9dWGYEHz965HBQE=
|
||||
golang.org/x/mobile v0.0.0-20190719004257-d2bd2a29d028/go.mod h1:E/iHnbuqvinMTCcRqshq8CkpyQDoeVncDDYHnLhea+o=
|
||||
golang.org/x/mod v0.0.0-20190513183733-4bf6d317e70e/go.mod h1:mXi4GBBbnImb6dmsKGUJ2LatrhH/nqhxcFungHvyanc=
|
||||
golang.org/x/mod v0.1.0/go.mod h1:0QHyrYULN0/3qlju5TqG8bIK38QM8yzMo5ekMj3DlcY=
|
||||
golang.org/x/mod v0.1.1-0.20191105210325-c90efee705ee/go.mod h1:QqPTAvyqsEbceGzBzNggFXnrqF1CaUcvgkdR5Ot7KZg=
|
||||
golang.org/x/mod v0.1.1-0.20191107180719-034126e5016b/go.mod h1:QqPTAvyqsEbceGzBzNggFXnrqF1CaUcvgkdR5Ot7KZg=
|
||||
golang.org/x/mod v0.2.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA=
|
||||
golang.org/x/mod v0.17.0 h1:zY54UmvipHiNd+pm+m0x9KhZ9hl1/7QNMyxXbc6ICqA=
|
||||
golang.org/x/mod v0.17.0/go.mod h1:hTbmBsO62+eylJbnUtE2MGJUyE7QWk4xUqPFrRgJ+7c=
|
||||
golang.org/x/net v0.0.0-20180724234803-3673e40ba225/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
|
||||
golang.org/x/net v0.0.0-20180826012351-8a410e7b638d/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
|
||||
golang.org/x/net v0.0.0-20180906233101-161cd47e91fd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
|
||||
golang.org/x/net v0.0.0-20181023162649-9b4f9f5ad519/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
|
||||
golang.org/x/net v0.0.0-20181114220301-adae6a3d119a/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
|
||||
golang.org/x/net v0.0.0-20181201002055-351d144fa1fc/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
|
||||
golang.org/x/net v0.0.0-20190108225652-1e06a53dbb7e/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
|
||||
golang.org/x/net v0.0.0-20190213061140-3a22650c66bd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
|
||||
golang.org/x/net v0.0.0-20190311183353-d8887717615a/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
|
||||
golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
|
||||
golang.org/x/net v0.0.0-20190501004415-9ce7a6920f09/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
|
||||
golang.org/x/net v0.0.0-20190503192946-f4e77d36d62c/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
|
||||
golang.org/x/net v0.0.0-20190603091049-60506f45cf65/go.mod h1:HSz+uSET+XFnRR8LxR5pz3Of3rY3CfYBVs4xY44aLks=
|
||||
golang.org/x/net v0.0.0-20190613194153-d28f0bde5980/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
|
||||
golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
|
||||
golang.org/x/net v0.0.0-20190724013045-ca1201d0de80/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
|
||||
golang.org/x/net v0.0.0-20190813141303-74dc4d7220e7/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
|
||||
golang.org/x/net v0.0.0-20190923162816-aa69164e4478/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
|
||||
golang.org/x/net v0.0.0-20191004110552-13f9640d40b9/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
|
||||
golang.org/x/net v0.0.0-20191209160850-c0dbc17a3553/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
|
||||
golang.org/x/net v0.0.0-20200114155413-6afb5195e5aa/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
|
||||
golang.org/x/net v0.0.0-20200202094626-16171245cfb2/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
|
||||
golang.org/x/net v0.0.0-20200222125558-5a598a2470a0/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
|
||||
golang.org/x/net v0.0.0-20200602114024-627f9648deb9/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A=
|
||||
golang.org/x/net v0.0.0-20201110031124-69a78807bb2b/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU=
|
||||
golang.org/x/net v0.0.0-20210226172049-e18ecbb05110/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg=
|
||||
golang.org/x/net v0.0.0-20210614182718-04defd469f4e/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y=
|
||||
golang.org/x/net v0.25.0 h1:d/OCCoBEUq33pjydKrGQhw7IlUPI2Oylr+8qLx49kac=
|
||||
golang.org/x/net v0.25.0/go.mod h1:JkAGAh7GEvH74S6FOH42FLoXpXbE/aqXSrIQjXgsiwM=
|
||||
golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U=
|
||||
golang.org/x/oauth2 v0.0.0-20190226205417-e64efc72b421/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw=
|
||||
golang.org/x/oauth2 v0.0.0-20190604053449-0f29369cfe45/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw=
|
||||
golang.org/x/oauth2 v0.0.0-20191202225959-858c2ad4c8b6/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw=
|
||||
golang.org/x/oauth2 v0.0.0-20200107190931-bf48bf16ab8d/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw=
|
||||
golang.org/x/oauth2 v0.7.0 h1:qe6s0zUXlPX80/dITx3440hWZ7GwMwgDDyrSGTPJG/g=
|
||||
golang.org/x/oauth2 v0.7.0/go.mod h1:hPLQkd9LyjfXTiRohC/41GhcFqxisoUQ99sCUOHO9x4=
|
||||
golang.org/x/sync v0.0.0-20180314180146-1d60e4601c6f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||
golang.org/x/sync v0.0.0-20181108010431-42b317875d0f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||
golang.org/x/sync v0.0.0-20181221193216-37e7f081c4d4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||
golang.org/x/sync v0.0.0-20190227155943-e225da77a7e6/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||
golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||
golang.org/x/sync v0.0.0-20190911185100-cd5d95a43a6e/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||
golang.org/x/sync v0.7.0 h1:YsImfSBoP9QPYL0xyKJPq0gcaJdG3rInoqxTWbfQu9M=
|
||||
golang.org/x/sync v0.7.0/go.mod h1:Czt+wKu1gCyEFDUtn0jG5QVvpJ6rzVqr5aXyt9drQfk=
|
||||
golang.org/x/sys v0.0.0-20180823144017-11551d06cbcc/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
||||
golang.org/x/sys v0.0.0-20180830151530-49385e6e1522/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
||||
golang.org/x/sys v0.0.0-20180905080454-ebe1bf3edb33/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
||||
golang.org/x/sys v0.0.0-20180909124046-d0be0721c37e/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
||||
golang.org/x/sys v0.0.0-20181026203630-95b1ffbd15a5/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
||||
golang.org/x/sys v0.0.0-20181116152217-5ac8a444bdc5/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
||||
golang.org/x/sys v0.0.0-20190129075346-302c3dd5f1cc/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
||||
golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
||||
golang.org/x/sys v0.0.0-20190222072716-a9d3bda3a223/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
||||
golang.org/x/sys v0.0.0-20190312061237-fead79001313/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20190403152447-81d4e9dc473e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20190422165155-953cdadca894/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20190502145724-3ef323f4f1fd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20190507160741-ecd444e8653b/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20190514135907-3a4b5fb9f71f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20190606165138-5da285871e9c/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20190624142023-c5567b49c5d0/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20190726091711-fc99dfbffb4e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20190916202348-b4ddaad3f8a3/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20190922100055-0a153f010e69/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20190924154521-2837fb4f24fe/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20191001151750-bb3f8db39f24/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20191008105621-543471e840be/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20191026070338-33540a1f6037/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20191204072324-ce4227a45e2e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20191228213918-04cbcbbfeed8/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20200113162924-86b910548bc1/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20200116001909-b77594299b42/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20200122134326-e047566fdf82/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20200124204421-9fbb57f87de9/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20200202164722-d101bd2416d5/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20200212091648-12a6c2dcc1e4/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20200223170610-d5e6a3e2c0ae/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20200323222414-85ca7c5b95cd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20200602225109-6fdc65e7d980/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20200930185726-fdedc70b468f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20210423082822-04245dca01da/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20210423185535-09eb48e85fd7/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.21.0 h1:rF+pYz3DAGSQAxAu1CbC7catZg4ebC4UIeIhKxBZvws=
|
||||
golang.org/x/sys v0.21.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
|
||||
golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo=
|
||||
golang.org/x/term v0.21.0 h1:WVXCp+/EBEHOj53Rvu+7KiT/iElMrO8ACK16SMZ3jaA=
|
||||
golang.org/x/term v0.21.0/go.mod h1:ooXLefLobQVslOqselCNF4SxFAaoS6KujMbsGzSDmX0=
|
||||
golang.org/x/text v0.0.0-20170915032832-14c0d48ead0c/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
|
||||
golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
|
||||
golang.org/x/text v0.3.1-0.20180807135948-17ff2d5776d2/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
|
||||
golang.org/x/text v0.3.2/go.mod h1:bEr9sfX3Q8Zfm5fL9x+3itogRgK3+ptLWKqgva+5dAk=
|
||||
golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
|
||||
golang.org/x/text v0.3.6/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
|
||||
golang.org/x/text v0.16.0 h1:a94ExnEXNtEwYLGJSIUxnWoxoRz/ZcCsV63ROupILh4=
|
||||
golang.org/x/text v0.16.0/go.mod h1:GhwF1Be+LQoKShO3cGOHzqOgRrGaYc9AvblQOmPVHnI=
|
||||
golang.org/x/time v0.0.0-20181108054448-85acf8d2951c/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=
|
||||
golang.org/x/time v0.0.0-20190308202827-9d24e82272b4/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=
|
||||
golang.org/x/time v0.0.0-20191024005414-555d28b269f0/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=
|
||||
golang.org/x/time v0.0.0-20200416051211-89c76fbcd5d1/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=
|
||||
golang.org/x/time v0.0.0-20210723032227-1f47c861a9ac h1:7zkz7BUtwNFFqcowJ+RIgu2MaV/MapERkDIy+mwPyjs=
|
||||
golang.org/x/time v0.0.0-20210723032227-1f47c861a9ac/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=
|
||||
golang.org/x/tools v0.0.0-20180221164845-07fd8470d635/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
|
||||
golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
|
||||
golang.org/x/tools v0.0.0-20181030221726-6c7e314b6563/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
|
||||
golang.org/x/tools v0.0.0-20190114222345-bf090417da8b/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
|
||||
golang.org/x/tools v0.0.0-20190226205152-f727befe758c/go.mod h1:9Yl7xja0Znq3iFh3HoIrodX9oNMXvdceNzlUR8zjMvY=
|
||||
golang.org/x/tools v0.0.0-20190311212946-11955173bddd/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs=
|
||||
golang.org/x/tools v0.0.0-20190312151545-0bb0c0a6e846/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs=
|
||||
golang.org/x/tools v0.0.0-20190312170243-e65039ee4138/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs=
|
||||
golang.org/x/tools v0.0.0-20190425150028-36563e24a262/go.mod h1:RgjU9mgBXZiqYHBnxXauZ1Gv1EHHAz9KjViQ78xBX0Q=
|
||||
golang.org/x/tools v0.0.0-20190506145303-2d16b83fe98c/go.mod h1:RgjU9mgBXZiqYHBnxXauZ1Gv1EHHAz9KjViQ78xBX0Q=
|
||||
golang.org/x/tools v0.0.0-20190524140312-2c0ae7006135/go.mod h1:RgjU9mgBXZiqYHBnxXauZ1Gv1EHHAz9KjViQ78xBX0Q=
|
||||
golang.org/x/tools v0.0.0-20190606124116-d0a3d012864b/go.mod h1:/rFqwRUd4F7ZHNgwSSTFct+R/Kf4OFW1sUzUTQQTgfc=
|
||||
golang.org/x/tools v0.0.0-20190621195816-6e04913cbbac/go.mod h1:/rFqwRUd4F7ZHNgwSSTFct+R/Kf4OFW1sUzUTQQTgfc=
|
||||
golang.org/x/tools v0.0.0-20190624222133-a101b041ded4/go.mod h1:/rFqwRUd4F7ZHNgwSSTFct+R/Kf4OFW1sUzUTQQTgfc=
|
||||
golang.org/x/tools v0.0.0-20190628153133-6cdbf07be9d0/go.mod h1:/rFqwRUd4F7ZHNgwSSTFct+R/Kf4OFW1sUzUTQQTgfc=
|
||||
golang.org/x/tools v0.0.0-20190816200558-6889da9d5479/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
|
||||
golang.org/x/tools v0.0.0-20190907020128-2ca718005c18/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
|
||||
golang.org/x/tools v0.0.0-20190911174233-4f2ddba30aff/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
|
||||
golang.org/x/tools v0.0.0-20191012152004-8de300cfc20a/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
|
||||
golang.org/x/tools v0.0.0-20191029041327-9cc4af7d6b2c/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
|
||||
golang.org/x/tools v0.0.0-20191113191852-77e3bb0ad9e7/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
|
||||
golang.org/x/tools v0.0.0-20191115202509-3a792d9c32b2/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
|
||||
golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
|
||||
golang.org/x/tools v0.0.0-20191125144606-a911d9008d1f/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
|
||||
golang.org/x/tools v0.0.0-20191130070609-6e064ea0cf2d/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
|
||||
golang.org/x/tools v0.0.0-20191216173652-a0e659d51361/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28=
|
||||
golang.org/x/tools v0.0.0-20191227053925-7b8e75db28f4/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28=
|
||||
golang.org/x/tools v0.0.0-20200117161641-43d50277825c/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28=
|
||||
golang.org/x/tools v0.0.0-20200122220014-bf1340f18c4a/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28=
|
||||
golang.org/x/tools v0.0.0-20200130002326-2f3ba24bd6e7/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28=
|
||||
golang.org/x/tools v0.0.0-20200204074204-1cc6d1ef6c74/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28=
|
||||
golang.org/x/tools v0.0.0-20200207183749-b753a1ba74fa/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28=
|
||||
golang.org/x/tools v0.0.0-20200212150539-ea181f53ac56/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28=
|
||||
golang.org/x/tools v0.0.0-20200224181240-023911ca70b2/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28=
|
||||
golang.org/x/tools v0.21.1-0.20240508182429-e35e4ccd0d2d h1:vU5i/LfpvrRCpgM/VPfJLg5KjxD3E+hfT1SH+d9zLwg=
|
||||
golang.org/x/tools v0.21.1-0.20240508182429-e35e4ccd0d2d/go.mod h1:aiJjzUbINMkxbQROHiO6hDPo2LHcIPhhQsa9DLh0yGk=
|
||||
golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
|
||||
golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
|
||||
golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
|
||||
golang.org/x/xerrors v0.0.0-20220907171357-04be3eba64a2 h1:H2TDz8ibqkAF6YGhCdN3jS9O0/s90v0rJh3X/OLHEUk=
|
||||
golang.org/x/xerrors v0.0.0-20220907171357-04be3eba64a2/go.mod h1:K8+ghG5WaK9qNqU5K3HdILfMLy1f3aNYFI/wnl100a8=
|
||||
google.golang.org/api v0.4.0/go.mod h1:8k5glujaEP+g9n7WNsDg8QP6cUVNI86fCNMcbazEtwE=
|
||||
google.golang.org/api v0.7.0/go.mod h1:WtwebWUNSVBH/HAw79HIFXZNqEvBhG+Ra+ax0hx3E3M=
|
||||
google.golang.org/api v0.8.0/go.mod h1:o4eAsZoiT+ibD93RtjEohWalFOjRDx6CVaqeizhEnKg=
|
||||
google.golang.org/api v0.9.0/go.mod h1:o4eAsZoiT+ibD93RtjEohWalFOjRDx6CVaqeizhEnKg=
|
||||
google.golang.org/api v0.13.0/go.mod h1:iLdEw5Ide6rF15KTC1Kkl0iskquN2gFfn9o9XIsbkAI=
|
||||
google.golang.org/api v0.14.0/go.mod h1:iLdEw5Ide6rF15KTC1Kkl0iskquN2gFfn9o9XIsbkAI=
|
||||
google.golang.org/api v0.15.0/go.mod h1:iLdEw5Ide6rF15KTC1Kkl0iskquN2gFfn9o9XIsbkAI=
|
||||
google.golang.org/api v0.17.0/go.mod h1:BwFmGc8tA3vsd7r/7kR8DY7iEEGSU04BFxCo5jP/sfE=
|
||||
google.golang.org/api v0.18.0/go.mod h1:BwFmGc8tA3vsd7r/7kR8DY7iEEGSU04BFxCo5jP/sfE=
|
||||
google.golang.org/api v0.21.0/go.mod h1:BwFmGc8tA3vsd7r/7kR8DY7iEEGSU04BFxCo5jP/sfE=
|
||||
google.golang.org/api v0.114.0 h1:1xQPji6cO2E2vLiI+C/XiFAnsn1WV3mjaEwGLhi3grE=
|
||||
google.golang.org/api v0.114.0/go.mod h1:ifYI2ZsFK6/uGddGfAD5BMxlnkBqCmqHSDUVi45N5Yg=
|
||||
google.golang.org/appengine v1.1.0/go.mod h1:EbEs0AVv82hx2wNQdGPgUI5lhzA/G0D9YwlJXL52JkM=
|
||||
google.golang.org/appengine v1.4.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4=
|
||||
google.golang.org/appengine v1.5.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4=
|
||||
google.golang.org/appengine v1.6.1/go.mod h1:i06prIuMbXzDqacNJfV5OdTW448YApPu5ww/cMBSeb0=
|
||||
google.golang.org/appengine v1.6.5/go.mod h1:8WjMMxjGQR8xUklV/ARdw2HLXBOI7O7uCIDZVag1xfc=
|
||||
google.golang.org/appengine v1.6.7 h1:FZR1q0exgwxzPzp/aF+VccGrSfxfPpkBqjIIEq3ru6c=
|
||||
google.golang.org/appengine v1.6.7/go.mod h1:8WjMMxjGQR8xUklV/ARdw2HLXBOI7O7uCIDZVag1xfc=
|
||||
google.golang.org/genproto v0.0.0-20180817151627-c66870c02cf8/go.mod h1:JiN7NxoALGmiZfu7CAH4rXhgtRTLTxftemlI0sWmxmc=
|
||||
google.golang.org/genproto v0.0.0-20190307195333-5fe7a883aa19/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE=
|
||||
google.golang.org/genproto v0.0.0-20190418145605-e7d98fc518a7/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE=
|
||||
google.golang.org/genproto v0.0.0-20190425155659-357c62f0e4bb/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE=
|
||||
google.golang.org/genproto v0.0.0-20190502173448-54afdca5d873/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE=
|
||||
google.golang.org/genproto v0.0.0-20190801165951-fa694d86fc64/go.mod h1:DMBHOl98Agz4BDEuKkezgsaosCRResVns1a3J2ZsMNc=
|
||||
google.golang.org/genproto v0.0.0-20190819201941-24fa4b261c55/go.mod h1:DMBHOl98Agz4BDEuKkezgsaosCRResVns1a3J2ZsMNc=
|
||||
google.golang.org/genproto v0.0.0-20190911173649-1774047e7e51/go.mod h1:IbNlFCBrqXvoKpeg0TB2l7cyZUmoaFKYIwrEpbDKLA8=
|
||||
google.golang.org/genproto v0.0.0-20191108220845-16a3f7862a1a/go.mod h1:n3cpQtvxv34hfy77yVDNjmbRyujviMdxYliBSkLhpCc=
|
||||
google.golang.org/genproto v0.0.0-20191115194625-c23dd37a84c9/go.mod h1:n3cpQtvxv34hfy77yVDNjmbRyujviMdxYliBSkLhpCc=
|
||||
google.golang.org/genproto v0.0.0-20191216164720-4f79533eabd1/go.mod h1:n3cpQtvxv34hfy77yVDNjmbRyujviMdxYliBSkLhpCc=
|
||||
google.golang.org/genproto v0.0.0-20191230161307-f3c370f40bfb/go.mod h1:n3cpQtvxv34hfy77yVDNjmbRyujviMdxYliBSkLhpCc=
|
||||
google.golang.org/genproto v0.0.0-20200115191322-ca5a22157cba/go.mod h1:n3cpQtvxv34hfy77yVDNjmbRyujviMdxYliBSkLhpCc=
|
||||
google.golang.org/genproto v0.0.0-20200122232147-0452cf42e150/go.mod h1:n3cpQtvxv34hfy77yVDNjmbRyujviMdxYliBSkLhpCc=
|
||||
google.golang.org/genproto v0.0.0-20200204135345-fa8e72b47b90/go.mod h1:GmwEX6Z4W5gMy59cAlVYjN9JhxgbQH6Gn+gFDQe2lzA=
|
||||
google.golang.org/genproto v0.0.0-20200212174721-66ed5ce911ce/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c=
|
||||
google.golang.org/genproto v0.0.0-20200224152610-e50cd9704f63/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c=
|
||||
google.golang.org/genproto v0.0.0-20200526211855-cb27e3aa2013/go.mod h1:NbSheEEYHJ7i3ixzK3sjbqSGDJWnxyFXZblF3eUsNvo=
|
||||
google.golang.org/genproto v0.0.0-20230410155749-daa745c078e1 h1:KpwkzHKEF7B9Zxg18WzOa7djJ+Ha5DzthMyZYQfEn2A=
|
||||
google.golang.org/genproto v0.0.0-20230410155749-daa745c078e1/go.mod h1:nKE/iIaLqn2bQwXBg8f1g2Ylh6r5MN5CmZvuzZCgsCU=
|
||||
google.golang.org/grpc v1.14.0/go.mod h1:yo6s7OP7yaDglbqo1J04qKzAhqBH6lvTonzMVmEdcZw=
|
||||
google.golang.org/grpc v1.19.0/go.mod h1:mqu4LbDTu4XGKhr4mRzUsmM4RtVoemTSY81AxZiDr8c=
|
||||
google.golang.org/grpc v1.20.1/go.mod h1:10oTOabMzJvdu6/UiuZezV6QK5dSlG84ov/aaiqXj38=
|
||||
google.golang.org/grpc v1.21.1/go.mod h1:oYelfM1adQP15Ek0mdvEgi9Df8B9CZIaU1084ijfRaM=
|
||||
google.golang.org/grpc v1.22.0/go.mod h1:Y5yQAOtifL1yxbo5wqy6BxZv8vAUGQwXBOALyacEbxg=
|
||||
google.golang.org/grpc v1.23.0/go.mod h1:Y5yQAOtifL1yxbo5wqy6BxZv8vAUGQwXBOALyacEbxg=
|
||||
google.golang.org/grpc v1.23.1/go.mod h1:Y5yQAOtifL1yxbo5wqy6BxZv8vAUGQwXBOALyacEbxg=
|
||||
google.golang.org/grpc v1.25.1/go.mod h1:c3i+UQWmh7LiEpx4sFZnkU36qjEYZ0imhYfXVyQciAY=
|
||||
google.golang.org/grpc v1.26.0/go.mod h1:qbnxyOmOxrQa7FizSgH+ReBfzJrCY1pSN7KXBS8abTk=
|
||||
google.golang.org/grpc v1.27.0/go.mod h1:qbnxyOmOxrQa7FizSgH+ReBfzJrCY1pSN7KXBS8abTk=
|
||||
google.golang.org/grpc v1.27.1/go.mod h1:qbnxyOmOxrQa7FizSgH+ReBfzJrCY1pSN7KXBS8abTk=
|
||||
google.golang.org/grpc v1.29.1/go.mod h1:itym6AZVZYACWQqET3MqgPpjcuV5QH3BxFS3IjizoKk=
|
||||
google.golang.org/grpc v1.33.2/go.mod h1:JMHMWHQWaTccqQQlmk3MJZS+GWXOdAesneDmEnv2fbc=
|
||||
google.golang.org/grpc v1.56.3 h1:8I4C0Yq1EjstUzUJzpcRVbuYA2mODtEmpWiQoN/b2nc=
|
||||
google.golang.org/grpc v1.56.3/go.mod h1:I9bI3vqKfayGqPUAwGdOSu7kt6oIJLixfffKrpXqQ9s=
|
||||
google.golang.org/protobuf v0.0.0-20200109180630-ec00e32a8dfd/go.mod h1:DFci5gLYBciE7Vtevhsrf46CRTquxDuWsQurQQe4oz8=
|
||||
google.golang.org/protobuf v0.0.0-20200221191635-4d8936d0db64/go.mod h1:kwYJMbMJ01Woi6D6+Kah6886xMZcty6N08ah7+eCXa0=
|
||||
google.golang.org/protobuf v0.0.0-20200228230310-ab0ca4ff8a60/go.mod h1:cfTl7dwQJ+fmap5saPgwCLgHXTUD7jkjRqWcaiX5VyM=
|
||||
google.golang.org/protobuf v1.20.1-0.20200309200217-e05f789c0967/go.mod h1:A+miEFZTKqfCUM6K7xSMQL9OKL/b6hQv+e19PK+JZNE=
|
||||
google.golang.org/protobuf v1.21.0/go.mod h1:47Nbq4nVaFHyn7ilMalzfO3qCViNmqZ2kzikPIcrTAo=
|
||||
google.golang.org/protobuf v1.22.0/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2l/sGQquU=
|
||||
google.golang.org/protobuf v1.23.0/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2l/sGQquU=
|
||||
google.golang.org/protobuf v1.23.1-0.20200526195155-81db48ad09cc/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2l/sGQquU=
|
||||
google.golang.org/protobuf v1.25.0/go.mod h1:9JNX74DMeImyA3h4bdi1ymwjUzf21/xIlbajtzgsN7c=
|
||||
google.golang.org/protobuf v1.26.0-rc.1/go.mod h1:jlhhOSvTdKEhbULTjvd4ARK9grFBp09yW+WbY/TyQbw=
|
||||
google.golang.org/protobuf v1.26.0/go.mod h1:9q0QmTI4eRPtz6boOQmLYwt+qCgq0jsYwAQnmE0givc=
|
||||
google.golang.org/protobuf v1.33.0 h1:uNO2rsAINq/JlFpSdYEKIZ0uKD/R9cpdv0T+yoGwGmI=
|
||||
google.golang.org/protobuf v1.33.0/go.mod h1:c6P6GXX6sHbq/GpV6MGZEdwhWPcYBgnhAHhKbcUYpos=
|
||||
gopkg.in/airbrake/gobrake.v2 v2.0.9/go.mod h1:/h5ZAUhDkGaJfjzjKLSjv6zCL6O0LLBxU4K+aSYdM/U=
|
||||
gopkg.in/alecthomas/kingpin.v2 v2.2.6/go.mod h1:FMv+mEhP44yOT+4EoQTLFTRgOQ1FBLkstjWtayDeSgw=
|
||||
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
|
||||
gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
|
||||
gopkg.in/check.v1 v1.0.0-20190902080502-41f04d3bba15/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
|
||||
gopkg.in/check.v1 v1.0.0-20200227125254-8fa46927fb4f h1:BLraFXnmrev5lT+xlilqcH8XK9/i0At2xKjWk4p6zsU=
|
||||
gopkg.in/check.v1 v1.0.0-20200227125254-8fa46927fb4f/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
|
||||
gopkg.in/errgo.v2 v2.1.0/go.mod h1:hNsd1EY+bozCKY1Ytp96fpM3vjJbqLJn88ws8XvfDNI=
|
||||
gopkg.in/fsnotify.v1 v1.4.7/go.mod h1:Tz8NjZHkW78fSQdbUxIjBTcgA1z1m8ZHf0WmKUhAMys=
|
||||
gopkg.in/gemnasium/logrus-airbrake-hook.v2 v2.1.2/go.mod h1:Xk6kEKp8OKb+X14hQBKWaSkCsqBpgog8nAV2xsGOxlo=
|
||||
gopkg.in/square/go-jose.v2 v2.3.1/go.mod h1:M9dMgbHiYLoDGQrXy7OpJDJWiKiU//h+vD76mk0e1AI=
|
||||
gopkg.in/square/go-jose.v2 v2.5.1/go.mod h1:M9dMgbHiYLoDGQrXy7OpJDJWiKiU//h+vD76mk0e1AI=
|
||||
gopkg.in/square/go-jose.v2 v2.6.0 h1:NGk74WTnPKBNUhNzQX7PYcTLUjoq7mzKk2OKbvwk2iI=
|
||||
gopkg.in/square/go-jose.v2 v2.6.0/go.mod h1:M9dMgbHiYLoDGQrXy7OpJDJWiKiU//h+vD76mk0e1AI=
|
||||
gopkg.in/tomb.v1 v1.0.0-20141024135613-dd632973f1e7/go.mod h1:dt/ZhP58zS4L8KSrWDmTeBkI65Dw0HsyUHuEVlX15mw=
|
||||
gopkg.in/yaml.v2 v2.2.1/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
|
||||
gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
|
||||
gopkg.in/yaml.v2 v2.2.4/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
|
||||
gopkg.in/yaml.v2 v2.2.5/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
|
||||
gopkg.in/yaml.v2 v2.2.8/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
|
||||
gopkg.in/yaml.v2 v2.4.0 h1:D8xgwECY7CYvx+Y2n4sBz93Jn9JRvxdiyyo8CTfuKaY=
|
||||
gopkg.in/yaml.v2 v2.4.0/go.mod h1:RDklbk79AGWmwhnvt/jBztapEOGDOx6ZbXqjP6csGnQ=
|
||||
gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
|
||||
gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA=
|
||||
gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
|
||||
gotest.tools v2.2.0+incompatible/go.mod h1:DsYFclhRJ6vuDpmuTbkuFWG+y2sxOXAzmJt81HFBacw=
|
||||
gotest.tools/v3 v3.0.2/go.mod h1:3SzNCllyD9/Y+b5r9JIKQ474KzkZyqLqEfYqMsX94Bk=
|
||||
honnef.co/go/tools v0.0.0-20190102054323-c2f93a96b099/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4=
|
||||
honnef.co/go/tools v0.0.0-20190106161140-3f1c8253044a/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4=
|
||||
honnef.co/go/tools v0.0.0-20190418001031-e561f6794a2a/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4=
|
||||
honnef.co/go/tools v0.0.0-20190523083050-ea95bdfd59fc/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4=
|
||||
honnef.co/go/tools v0.0.1-2019.2.3/go.mod h1:a3bituU0lyd329TUQxRnasdCoJDkEUEAqEt0JzvZhAg=
|
||||
honnef.co/go/tools v0.0.1-2020.1.3/go.mod h1:X/FiERA/W4tHapMX5mGpAtMSVEeEUOyHaw9vFzvIQ3k=
|
||||
rsc.io/binaryregexp v0.2.0/go.mod h1:qTv7/COck+e2FymRvadv62gMdZztPaShugOCi3I+8D8=
|
||||
rsc.io/quote/v3 v3.1.0/go.mod h1:yEA65RcK8LyAZtP9Kv3t0HmxON59tX3rD+tICJqUlj0=
|
||||
rsc.io/sampler v1.3.0/go.mod h1:T1hPZKmBbMNahiBKFy5HrXp6adAjACjK9JXDnKaTXpA=
|
25
main.go
25
main.go
@ -1,15 +1,24 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"github.com/rdobson/packer-builder-xenserver/builder/xenserver"
|
||||
"github.com/mitchellh/packer/packer/plugin"
|
||||
"fmt"
|
||||
"os"
|
||||
|
||||
"github.com/xenserver/packer-builder-xenserver/builder/xenserver/iso"
|
||||
"github.com/xenserver/packer-builder-xenserver/builder/xenserver/xva"
|
||||
"github.com/xenserver/packer-builder-xenserver/version"
|
||||
|
||||
"github.com/hashicorp/packer-plugin-sdk/plugin"
|
||||
)
|
||||
|
||||
func main() {
|
||||
server, err := plugin.Server()
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
server.RegisterBuilder(new(xenserver.Builder))
|
||||
server.Serve()
|
||||
pps := plugin.NewSet()
|
||||
pps.RegisterBuilder("iso", new(iso.Builder))
|
||||
pps.RegisterBuilder("xva", new(xva.Builder))
|
||||
pps.SetVersion(version.PluginVersion)
|
||||
err := pps.Run()
|
||||
if err != nil {
|
||||
fmt.Fprintln(os.Stderr, err.Error())
|
||||
os.Exit(1)
|
||||
}
|
||||
}
|
||||
|
19
version/version.go
Normal file
19
version/version.go
Normal file
@ -0,0 +1,19 @@
|
||||
package version
|
||||
|
||||
import (
|
||||
"github.com/hashicorp/packer-plugin-sdk/version"
|
||||
)
|
||||
|
||||
var (
|
||||
// Version is the main version number that is being run at the moment.
|
||||
Version = "v0.6.0"
|
||||
|
||||
// VersionPrerelease is A pre-release marker for the Version. If this is ""
|
||||
// (empty string) then it means that it is a final release. Otherwise, this
|
||||
// is a pre-release such as "dev" (in development), "beta", "rc1", etc.
|
||||
VersionPrerelease = "dev"
|
||||
|
||||
// PluginVersion is used by the plugin set to allow Packer to recognize
|
||||
// what version this plugin is.
|
||||
PluginVersion = version.InitializePluginVersion(Version, VersionPrerelease)
|
||||
)
|
Loading…
Reference in New Issue
Block a user