Compare commits
46 Commits
main
...
v0.3.3-dev
Author | SHA1 | Date | |
---|---|---|---|
|
d43ee760aa | ||
|
e65b7ab96a | ||
|
7bc4d2b8d4 | ||
|
1241e64d93 | ||
|
e671f722cb | ||
|
6cf22f2d5f | ||
|
bc3ca5736d | ||
|
7ab84891ae | ||
|
58745db224 | ||
|
d334a085ed | ||
|
d9f981e4f3 | ||
|
f24ca7f84d | ||
|
cdd65fb230 | ||
|
4bc24960fb | ||
|
5a9caff902 | ||
|
494aa02283 | ||
|
8e4ed13171 | ||
|
727e02c292 | ||
|
38714450d7 | ||
|
f049f32908 | ||
|
991ced778f | ||
|
13c25063af | ||
|
a67141b2b5 | ||
|
cd8e37013d | ||
|
76d884b65d | ||
|
84f5023702 | ||
|
702dffcea0 | ||
|
233e3500b8 | ||
|
cfe413b101 | ||
|
23ba68a2b1 | ||
|
faad0e166b | ||
|
893ff925fb | ||
|
a89a5369dd | ||
|
f60e1b6a7f | ||
|
3cb985d134 | ||
|
ade8d0d819 | ||
|
35efd42e55 | ||
|
9139994fdd | ||
|
189cd46360 | ||
|
0df631cda7 | ||
|
ccb8147f31 | ||
|
cc25ed3495 | ||
|
6e84e6d371 | ||
|
64e4fd2a4d | ||
|
2929200bdb | ||
|
e8feeffb3f |
6
.gitignore
vendored
6
.gitignore
vendored
@ -7,4 +7,8 @@ packer_cache/*
|
||||
builder-*
|
||||
dist/*
|
||||
vendor/*
|
||||
packer-plugin-xenserver
|
||||
packer-plugin-xenserver
|
||||
.idea
|
||||
|
||||
docs-rendered
|
||||
docs-partials
|
@ -18,7 +18,7 @@ builds:
|
||||
post:
|
||||
# This will check plugin compatibility against latest version of Packer
|
||||
- cmd: |
|
||||
go install github.com/hashicorp/packer/cmd/packer-plugins-check@latest &&
|
||||
go install github.com/hashicorp/packer/cmd/packer-plugins-check@v1.7.4 &&
|
||||
packer-plugins-check -load={{ .Name }}
|
||||
dir: "{{ dir .Path}}"
|
||||
flags:
|
||||
|
32
GNUmakefile
Normal file
32
GNUmakefile
Normal file
@ -0,0 +1,32 @@
|
||||
NAME=xenserver
|
||||
BINARY=packer-plugin-${NAME}
|
||||
|
||||
COUNT?=1
|
||||
TEST?=$(shell go list ./...)
|
||||
|
||||
.PHONY: dev
|
||||
|
||||
build:
|
||||
@go build -o ${BINARY}
|
||||
|
||||
dev: build
|
||||
@mkdir -p ~/.packer.d/plugins/
|
||||
@mv ${BINARY} ~/.packer.d/plugins/${BINARY}
|
||||
|
||||
generate:
|
||||
@go install github.com/hashicorp/packer-plugin-sdk/cmd/packer-sdc@latest
|
||||
@go generate -v ./...
|
||||
|
||||
ci-release-docs:
|
||||
@go install github.com/hashicorp/packer-plugin-sdk/cmd/packer-sdc@latest
|
||||
@packer-sdc renderdocs -src docs-src -partials docs-partials/ -dst docs/
|
||||
@/bin/sh -c "[ -d docs ] && zip -r docs.zip docs/"
|
||||
|
||||
run-example: dev
|
||||
@packer build ./example
|
||||
|
||||
test:
|
||||
@go test -count $(COUNT) $(TEST) -timeout=3m
|
||||
|
||||
testacc: dev
|
||||
@PACKER_ACC=1 go test -count $(COUNT) -v $(TEST) -timeout=120m
|
@ -56,7 +56,7 @@ $ go build -o packer-plugin-xenserver
|
||||
|
||||
# Add the builder to the location packer expects it to be installed in
|
||||
$ mkdir -p ~/.packer.d/plugins/
|
||||
$ cp builder-xenserver-iso ~/.packer.d/plugins/packer-builder-xenserver-iso
|
||||
$ cp packer-plugin-xenserver ~/.packer.d/plugins/packer-plugin-xenserver
|
||||
```
|
||||
|
||||
# Documentation
|
||||
|
@ -1,4 +1,4 @@
|
||||
package common
|
||||
package artifact
|
||||
|
||||
import (
|
||||
"fmt"
|
@ -1,974 +0,0 @@
|
||||
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
|
||||
}
|
||||
|
||||
// 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
|
||||
}
|
@ -1,289 +0,0 @@
|
||||
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"`
|
||||
|
||||
VMName string `mapstructure:"vm_name"`
|
||||
VMDescription string `mapstructure:"vm_description"`
|
||||
SrName string `mapstructure:"sr_name"`
|
||||
SrISOName string `mapstructure:"sr_iso_name"`
|
||||
FloppyFiles []string `mapstructure:"floppy_files"`
|
||||
NetworkNames []string `mapstructure:"network_names"`
|
||||
ExportNetworkNames []string `mapstructure:"export_network_names"`
|
||||
|
||||
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.HostPortMin == 0 {
|
||||
c.HostPortMin = 5900
|
||||
}
|
||||
|
||||
if c.HostPortMax == 0 {
|
||||
c.HostPortMax = 6000
|
||||
}
|
||||
|
||||
if c.RawBootWait == "" {
|
||||
c.RawBootWait = "5s"
|
||||
}
|
||||
|
||||
if c.ToolsIsoName == "" {
|
||||
c.ToolsIsoName = "xs-tools.iso"
|
||||
}
|
||||
|
||||
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) {
|
||||
var srRef xenapi.SRRef
|
||||
if config.SrName == "" {
|
||||
hostRef, err := c.GetClient().Session.GetThisHost(c.session, c.session)
|
||||
|
||||
if err != nil {
|
||||
return srRef, err
|
||||
}
|
||||
|
||||
pools, err := c.GetClient().Pool.GetAllRecords(c.session)
|
||||
|
||||
if err != nil {
|
||||
return srRef, err
|
||||
}
|
||||
|
||||
for _, pool := range pools {
|
||||
if pool.Master == hostRef {
|
||||
return pool.DefaultSR, nil
|
||||
}
|
||||
}
|
||||
|
||||
return srRef, errors.New(fmt.Sprintf("failed to find default SR on host '%s'", hostRef))
|
||||
|
||||
} else {
|
||||
// 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 srRef, errors.New("sr_iso_name must be specified in the packer configuration")
|
||||
|
||||
} else {
|
||||
// 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
|
||||
}
|
||||
}
|
@ -1,41 +0,0 @@
|
||||
//go:generate 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"`
|
||||
DiskSize uint `mapstructure:"disk_size"`
|
||||
CloneTemplate string `mapstructure:"clone_template"`
|
||||
VMOtherConfig map[string]string `mapstructure:"vm_other_config"`
|
||||
|
||||
ISOChecksum string `mapstructure:"iso_checksum"`
|
||||
ISOChecksumType string `mapstructure:"iso_checksum_type"`
|
||||
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"`
|
||||
|
||||
ctx interpolate.Context
|
||||
}
|
||||
|
||||
func (c Config) GetInterpContext() *interpolate.Context {
|
||||
return &c.ctx
|
||||
}
|
74
builder/xenserver/common/config/comm_config.go
Normal file
74
builder/xenserver/common/config/comm_config.go
Normal file
@ -0,0 +1,74 @@
|
||||
//go:generate packer-sdc struct-markdown
|
||||
|
||||
package config
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"github.com/hashicorp/packer-plugin-sdk/communicator"
|
||||
"github.com/hashicorp/packer-plugin-sdk/template/interpolate"
|
||||
)
|
||||
|
||||
// Based upon https://github.com/hashicorp/packer-plugin-qemu/blob/a2121bb95d84288a1df5d7fbce94985a7cdfb793/builder/qemu/comm_config.go
|
||||
|
||||
type CommConfig struct {
|
||||
Comm communicator.Config `mapstructure:",squash"`
|
||||
|
||||
// Defaults to false. When enabled, Packer
|
||||
// does not setup forwarded port mapping for communicator (SSH or WinRM) requests and uses ssh_port or winrm_port
|
||||
// on the host to communicate to the virtual machine.
|
||||
SkipNatMapping bool `mapstructure:"skip_nat_mapping" required:"false"`
|
||||
|
||||
// These are deprecated, but we keep them around for backwards compatibility
|
||||
// TODO: remove later
|
||||
|
||||
/*
|
||||
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.).
|
||||
*/
|
||||
SSHKeyPath string `mapstructure:"ssh_key_path" undocumented:"true"`
|
||||
SSHSkipNatMapping bool `mapstructure:"ssh_skip_nat_mapping" undocumented:"true"`
|
||||
HostPortMin int `mapstructure:"host_port_min" required:"false" undocumented:"true"`
|
||||
HostPortMax int `mapstructure:"host_port_max" required:"false" undocumented:"true"`
|
||||
}
|
||||
|
||||
func (c *CommConfig) Prepare(ctx *interpolate.Context) (warnings []string, errs []error) {
|
||||
|
||||
const removedHostPortFmt = "%s is deprecated because free ports are selected automatically. " +
|
||||
"Please remove %s from your template. " +
|
||||
"In future versions of Packer, inclusion of %s will error your builds."
|
||||
|
||||
// Backwards compatibility
|
||||
if c.HostPortMin != 0 {
|
||||
warnings = append(warnings, fmt.Sprintf(removedHostPortFmt, "host_port_min", "host_port_min", "host_port_min"))
|
||||
}
|
||||
|
||||
// Backwards compatibility
|
||||
if c.HostPortMax != 0 {
|
||||
warnings = append(warnings, fmt.Sprintf(removedHostPortFmt, "host_port_max", "host_port_max", "host_port_max"))
|
||||
}
|
||||
|
||||
// Backwards compatibility
|
||||
if c.SSHKeyPath != "" {
|
||||
warnings = append(warnings, "ssh_key_path is deprecated and is being replaced by ssh_private_key_file. "+
|
||||
"Please, update your template to use ssh_private_key_file. In future versions of Packer, inclusion of ssh_key_path will error your builds.")
|
||||
c.Comm.SSHPrivateKeyFile = c.SSHKeyPath
|
||||
}
|
||||
|
||||
// Backwards compatibility
|
||||
if c.SSHSkipNatMapping {
|
||||
warnings = append(warnings, "ssh_skip_nat_mapping is deprecated and is being replaced by skip_nat_mapping. "+
|
||||
"Please, update your template to use skip_nat_mapping. In future versions of Packer, inclusion of ssh_skip_nat_mapping will error your builds.")
|
||||
|
||||
c.SkipNatMapping = c.SSHSkipNatMapping
|
||||
}
|
||||
|
||||
if c.Comm.SSHHost == "" && c.SkipNatMapping {
|
||||
c.Comm.SSHHost = "127.0.0.1"
|
||||
c.Comm.WinRMHost = "127.0.0.1"
|
||||
}
|
||||
|
||||
errs = c.Comm.Prepare(ctx)
|
||||
|
||||
return
|
||||
}
|
294
builder/xenserver/common/config/common_config.go
Normal file
294
builder/xenserver/common/config/common_config.go
Normal file
@ -0,0 +1,294 @@
|
||||
//go:generate packer-sdc struct-markdown
|
||||
|
||||
package config
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"fmt"
|
||||
"github.com/hashicorp/packer-plugin-sdk/bootcommand"
|
||||
"github.com/hashicorp/packer-plugin-sdk/common"
|
||||
"github.com/hashicorp/packer-plugin-sdk/multistep"
|
||||
"github.com/hashicorp/packer-plugin-sdk/multistep/commonsteps"
|
||||
packersdk "github.com/hashicorp/packer-plugin-sdk/packer"
|
||||
"github.com/hashicorp/packer-plugin-sdk/shutdowncommand"
|
||||
hconfig "github.com/hashicorp/packer-plugin-sdk/template/config"
|
||||
"github.com/hashicorp/packer-plugin-sdk/template/interpolate"
|
||||
xenapi "github.com/terra-farm/go-xen-api-client"
|
||||
"github.com/xenserver/packer-builder-xenserver/builder/xenserver/common/xen"
|
||||
)
|
||||
|
||||
type CommonConfig struct {
|
||||
common.PackerConfig `mapstructure:",squash"`
|
||||
bootcommand.VNCConfig `mapstructure:",squash"`
|
||||
commonsteps.HTTPConfig `mapstructure:",squash"`
|
||||
|
||||
XenConfig `mapstructure:",squash"`
|
||||
|
||||
commonsteps.FloppyConfig `mapstructure:",squash"`
|
||||
|
||||
/*
|
||||
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.
|
||||
*/
|
||||
VMName string `mapstructure:"vm_name"`
|
||||
|
||||
/*
|
||||
The description of the new virtual machine. By default, this is the empty string.
|
||||
*/
|
||||
VMDescription string `mapstructure:"vm_description"`
|
||||
SrName string `mapstructure:"sr_name"`
|
||||
SrISOName string `mapstructure:"sr_iso_name"`
|
||||
|
||||
/*
|
||||
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 corespond to the second VIF and so on.
|
||||
*/
|
||||
NetworkNames []string `mapstructure:"network_names"`
|
||||
ExportNetworkNames []string `mapstructure:"export_network_names"`
|
||||
|
||||
/*
|
||||
The platform args. Defaults to
|
||||
```javascript
|
||||
{
|
||||
"viridian": "false",
|
||||
"nx": "true",
|
||||
"pae": "true",
|
||||
"apic": "true",
|
||||
"timeoffset": "0",
|
||||
"acpi": "1",
|
||||
"cores-per-socket": "1"
|
||||
}
|
||||
```
|
||||
*/
|
||||
PlatformArgs map[string]string `mapstructure:"platform_args"`
|
||||
|
||||
shutdowncommand.ShutdownConfig `mapstructure:",squash"`
|
||||
|
||||
/*
|
||||
The name of the XenServer Tools ISO. Defaults to "xs-tools.iso".
|
||||
*/
|
||||
ToolsIsoName string `mapstructure:"tools_iso_name"`
|
||||
|
||||
CommConfig `mapstructure:",squash" `
|
||||
|
||||
/*
|
||||
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.
|
||||
*/
|
||||
OutputDir string `mapstructure:"output_directory"`
|
||||
|
||||
/*
|
||||
Either "xva", "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".
|
||||
*/
|
||||
Format string `mapstructure:"format"`
|
||||
|
||||
/*
|
||||
Determine when to keep the VM and when to clean it up. This can be "always", "never" or "on_success".
|
||||
By default this 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.
|
||||
*/
|
||||
KeepVM string `mapstructure:"keep_vm"`
|
||||
IPGetter string `mapstructure:"ip_getter"`
|
||||
|
||||
Firmware string `mapstructure:"firmware"`
|
||||
HardwareConfig `mapstructure:",squash"`
|
||||
|
||||
ctx interpolate.Context
|
||||
}
|
||||
|
||||
func (c *CommonConfig) GetInterpContext() *interpolate.Context {
|
||||
return &c.ctx
|
||||
}
|
||||
|
||||
func (c *CommonConfig) Prepare(upper interface{}, raws ...interface{}) ([]string, []string, error) {
|
||||
|
||||
err := hconfig.Decode(upper, &hconfig.DecodeOpts{
|
||||
Interpolate: true,
|
||||
InterpolateFilter: &interpolate.RenderFilter{
|
||||
Exclude: []string{
|
||||
"boot_command",
|
||||
},
|
||||
},
|
||||
}, raws...)
|
||||
|
||||
if err != nil {
|
||||
return nil, nil, err
|
||||
}
|
||||
|
||||
var errs *packersdk.MultiError
|
||||
var warnings []string
|
||||
|
||||
// Set default values
|
||||
|
||||
if c.Firmware == "" {
|
||||
c.Firmware = "bios"
|
||||
}
|
||||
|
||||
if c.ToolsIsoName == "" {
|
||||
c.ToolsIsoName = "xs-tools.iso"
|
||||
}
|
||||
|
||||
if c.OutputDir == "" {
|
||||
c.OutputDir = fmt.Sprintf("output-%s", c.PackerConfig.PackerBuildName)
|
||||
}
|
||||
|
||||
if c.VMName == "" {
|
||||
c.VMName = fmt.Sprintf("packer-%s-{{timestamp}}", c.PackerConfig.PackerBuildName)
|
||||
}
|
||||
|
||||
if c.Format == "" {
|
||||
c.Format = "xva"
|
||||
}
|
||||
|
||||
if c.KeepVM == "" {
|
||||
c.KeepVM = "never"
|
||||
}
|
||||
|
||||
if c.IPGetter == "" {
|
||||
c.IPGetter = "auto"
|
||||
}
|
||||
|
||||
if len(c.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"
|
||||
c.PlatformArgs = pargs
|
||||
}
|
||||
|
||||
// Validation
|
||||
|
||||
if c.HTTPPortMin > c.HTTPPortMax {
|
||||
errs = packersdk.MultiErrorAppend(errs, errors.New("the HTTP min port must be less than the max"))
|
||||
}
|
||||
|
||||
switch c.Format {
|
||||
case "xva", "xva_compressed", "vdi_raw", "vdi_vhd", "none":
|
||||
default:
|
||||
errs = packersdk.MultiErrorAppend(errs, errors.New("format must be one of 'xva', 'vdi_raw', 'vdi_vhd', 'none'"))
|
||||
}
|
||||
|
||||
switch c.KeepVM {
|
||||
case "always", "never", "on_success":
|
||||
default:
|
||||
errs = packersdk.MultiErrorAppend(errs, errors.New("keep_vm must be one of 'always', 'never', 'on_success'"))
|
||||
}
|
||||
|
||||
switch c.IPGetter {
|
||||
case "auto", "tools", "http":
|
||||
default:
|
||||
errs = packersdk.MultiErrorAppend(errs, errors.New("ip_getter must be one of 'auto', 'tools', 'http'"))
|
||||
}
|
||||
|
||||
innerWarnings, es := c.CommConfig.Prepare(&c.ctx)
|
||||
errs = packersdk.MultiErrorAppend(errs, es...)
|
||||
warnings = append(warnings, innerWarnings...)
|
||||
|
||||
innerWarnings, es = c.XenConfig.Prepare(&c.ctx)
|
||||
errs = packersdk.MultiErrorAppend(errs, es...)
|
||||
warnings = append(warnings, innerWarnings...)
|
||||
|
||||
innerWarnings, es = c.HardwareConfig.Prepare(&c.ctx)
|
||||
errs = packersdk.MultiErrorAppend(errs, es...)
|
||||
warnings = append(warnings, innerWarnings...)
|
||||
|
||||
errs = packersdk.MultiErrorAppend(errs, c.VNCConfig.Prepare(&c.ctx)...)
|
||||
errs = packersdk.MultiErrorAppend(errs, c.HTTPConfig.Prepare(&c.ctx)...)
|
||||
errs = packersdk.MultiErrorAppend(errs, c.ShutdownConfig.Prepare(&c.ctx)...)
|
||||
errs = packersdk.MultiErrorAppend(errs, c.FloppyConfig.Prepare(&c.ctx)...)
|
||||
|
||||
return nil, warnings, 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 *xen.Connection) (xenapi.SRRef, error) {
|
||||
var srRef xenapi.SRRef
|
||||
if config.SrName == "" {
|
||||
hostRef, err := c.GetClient().Session.GetThisHost(c.GetSessionRef(), c.GetSessionRef())
|
||||
|
||||
if err != nil {
|
||||
return srRef, err
|
||||
}
|
||||
|
||||
pools, err := c.GetClient().Pool.GetAllRecords(c.GetSessionRef())
|
||||
|
||||
if err != nil {
|
||||
return srRef, err
|
||||
}
|
||||
|
||||
for _, pool := range pools {
|
||||
if pool.Master == hostRef {
|
||||
return pool.DefaultSR, nil
|
||||
}
|
||||
}
|
||||
|
||||
return srRef, errors.New(fmt.Sprintf("failed to find default SR on host '%s'", hostRef))
|
||||
|
||||
} else {
|
||||
// Use the provided name label to find the SR to use
|
||||
srs, err := c.GetClient().SR.GetByNameLabel(c.GetSessionRef(), 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 *xen.Connection) (xenapi.SRRef, error) {
|
||||
var srRef xenapi.SRRef
|
||||
if config.SrISOName == "" {
|
||||
return srRef, errors.New("sr_iso_name must be specified in the packer configuration")
|
||||
|
||||
} else {
|
||||
// Use the provided name label to find the SR to use
|
||||
srs, err := c.GetClient().SR.GetByNameLabel(c.GetSessionRef(), 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
|
||||
}
|
||||
}
|
48
builder/xenserver/common/config/hardware_config.go
Normal file
48
builder/xenserver/common/config/hardware_config.go
Normal file
@ -0,0 +1,48 @@
|
||||
//go:generate packer-sdc struct-markdown
|
||||
|
||||
package config
|
||||
|
||||
import (
|
||||
"github.com/hashicorp/packer-plugin-sdk/template/interpolate"
|
||||
)
|
||||
|
||||
type HardwareConfig struct {
|
||||
/*
|
||||
The maximum number of VCPUs for the VM. By default this is 1.
|
||||
*/
|
||||
VCPUsMax uint `mapstructure:"vcpus_max"`
|
||||
|
||||
/*
|
||||
The number of startup VCPUs for the VM. By default this is 1.
|
||||
*/
|
||||
VCPUsAtStartup uint `mapstructure:"vcpus_atstartup"`
|
||||
|
||||
/*
|
||||
The size, in megabytes, of the amount of memory to allocate for the VM. By default, this is 1024 (1 GB).
|
||||
*/
|
||||
VMMemory uint `mapstructure:"vm_memory"`
|
||||
}
|
||||
|
||||
func (c *HardwareConfig) Prepare(ctx *interpolate.Context) (warnings []string, errs []error) {
|
||||
// Default values
|
||||
|
||||
if c.VCPUsMax == 0 {
|
||||
c.VCPUsMax = 1
|
||||
}
|
||||
|
||||
if c.VCPUsAtStartup == 0 {
|
||||
c.VCPUsAtStartup = 1
|
||||
}
|
||||
|
||||
if c.VCPUsAtStartup > c.VCPUsMax {
|
||||
c.VCPUsAtStartup = c.VCPUsMax
|
||||
}
|
||||
|
||||
if c.VMMemory == 0 {
|
||||
c.VMMemory = 1024
|
||||
}
|
||||
|
||||
// Validation
|
||||
|
||||
return
|
||||
}
|
63
builder/xenserver/common/config/xen_config.go
Normal file
63
builder/xenserver/common/config/xen_config.go
Normal file
@ -0,0 +1,63 @@
|
||||
package config
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"github.com/hashicorp/packer-plugin-sdk/template/interpolate"
|
||||
)
|
||||
|
||||
type XenConfig struct {
|
||||
/*
|
||||
The XenServer username used to access the remote machine.
|
||||
*/
|
||||
Username string `mapstructure:"remote_username" required:"true"`
|
||||
|
||||
/*
|
||||
The XenServer password for access to the remote machine.
|
||||
*/
|
||||
Password string `mapstructure:"remote_password" required:"true"`
|
||||
|
||||
/*
|
||||
The host of the Xenserver / XCP-ng pool primary.
|
||||
Typically these will be specified through environment variables as seen in the
|
||||
[examples](../../examples/centos8.json).
|
||||
*/
|
||||
HostIp string `mapstructure:"remote_host" required:"true"`
|
||||
|
||||
/*
|
||||
The port of the Xenserver API. Defaults to 443.
|
||||
*/
|
||||
HostPort int `mapstructure:"remote_port"`
|
||||
|
||||
/*
|
||||
The ssh port of the Xenserver pool primary. Defaults to 22.
|
||||
*/
|
||||
HostSSHPort int `mapstructure:"remote_ssh_port"`
|
||||
}
|
||||
|
||||
func (c *XenConfig) Prepare(ctx *interpolate.Context) (warnings []string, errs []error) {
|
||||
// Default values
|
||||
|
||||
if c.HostPort == 0 {
|
||||
c.HostPort = 443
|
||||
}
|
||||
|
||||
if c.HostSSHPort == 0 {
|
||||
c.HostSSHPort = 22
|
||||
}
|
||||
|
||||
// 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."))
|
||||
}
|
||||
|
||||
return
|
||||
}
|
@ -1,25 +0,0 @@
|
||||
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
|
||||
}
|
8
builder/xenserver/common/proxy/forwarding.go
Normal file
8
builder/xenserver/common/proxy/forwarding.go
Normal file
@ -0,0 +1,8 @@
|
||||
package proxy
|
||||
|
||||
type ProxyForwarding interface {
|
||||
GetServiceHost() string
|
||||
GetServicePort() int
|
||||
Start() error
|
||||
Close() error
|
||||
}
|
22
builder/xenserver/common/proxy/forwarding_direct.go
Normal file
22
builder/xenserver/common/proxy/forwarding_direct.go
Normal file
@ -0,0 +1,22 @@
|
||||
package proxy
|
||||
|
||||
type noNatProxyForwarding struct {
|
||||
TargetHost string
|
||||
TargetPort int
|
||||
}
|
||||
|
||||
func (self *noNatProxyForwarding) GetServiceHost() string {
|
||||
return self.TargetHost
|
||||
}
|
||||
|
||||
func (self *noNatProxyForwarding) GetServicePort() int {
|
||||
return self.TargetPort
|
||||
}
|
||||
|
||||
func (self *noNatProxyForwarding) Close() error {
|
||||
return nil
|
||||
}
|
||||
|
||||
func (self *noNatProxyForwarding) Start() error {
|
||||
return nil
|
||||
}
|
128
builder/xenserver/common/proxy/forwarding_real.go
Normal file
128
builder/xenserver/common/proxy/forwarding_real.go
Normal file
@ -0,0 +1,128 @@
|
||||
package proxy
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"io"
|
||||
"log"
|
||||
"net"
|
||||
"strconv"
|
||||
)
|
||||
|
||||
type realProxyForwarding struct {
|
||||
Proxy XenProxy
|
||||
TargetHost string
|
||||
TargetPort int
|
||||
ConnectionWrapper func(net.Conn) (net.Conn, error)
|
||||
|
||||
listener net.Listener
|
||||
}
|
||||
|
||||
func (self *realProxyForwarding) GetServiceHost() string {
|
||||
return self.listener.Addr().(*net.TCPAddr).IP.String()
|
||||
}
|
||||
|
||||
func (self *realProxyForwarding) GetServicePort() int {
|
||||
return self.listener.Addr().(*net.TCPAddr).Port
|
||||
}
|
||||
|
||||
func (self *realProxyForwarding) Close() error {
|
||||
if self.listener != nil {
|
||||
return self.listener.Close()
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func identityWrapper(conn net.Conn) (net.Conn, error) {
|
||||
return conn, nil
|
||||
}
|
||||
|
||||
func (self *realProxyForwarding) Start() error {
|
||||
if self.ConnectionWrapper == nil {
|
||||
self.ConnectionWrapper = identityWrapper
|
||||
}
|
||||
|
||||
var err error
|
||||
self.listener, err = net.Listen("tcp", "127.0.0.1:0")
|
||||
|
||||
if err != nil {
|
||||
return fmt.Errorf("could not create port forward listener: %w", err)
|
||||
}
|
||||
|
||||
clientAddr := net.JoinHostPort(self.GetServiceHost(), strconv.Itoa(self.GetServicePort()))
|
||||
targetAddr := net.JoinHostPort(self.TargetHost, strconv.Itoa(self.TargetPort))
|
||||
log.Printf("Starting forwarding %s -> %s", clientAddr, targetAddr)
|
||||
|
||||
go func() {
|
||||
for {
|
||||
accept, err := self.listener.Accept()
|
||||
if err != nil {
|
||||
log.Printf("Stopped forwarding %s -> %s", clientAddr, targetAddr)
|
||||
return
|
||||
}
|
||||
|
||||
go self.handleConnection(accept)
|
||||
}
|
||||
}()
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (self *realProxyForwarding) serviceForwardedConnection(clientConn net.Conn, targetConn net.Conn) {
|
||||
txDone := make(chan struct{})
|
||||
rxDone := make(chan struct{})
|
||||
|
||||
go func() {
|
||||
_, err := io.Copy(targetConn, clientConn)
|
||||
|
||||
log.Printf("[FORWARD] proxy client closed connection")
|
||||
|
||||
// Close conn so that other copy operation unblocks
|
||||
targetConn.Close()
|
||||
close(txDone)
|
||||
|
||||
if err != nil {
|
||||
log.Printf("[FORWARD] Error conn <- accept: %v", err)
|
||||
return
|
||||
}
|
||||
}()
|
||||
|
||||
go func() {
|
||||
_, err := io.Copy(clientConn, targetConn)
|
||||
|
||||
log.Printf("[FORWARD] proxy target closed connection")
|
||||
|
||||
// Close accept so that other copy operation unblocks
|
||||
clientConn.Close()
|
||||
close(rxDone)
|
||||
|
||||
if err != nil {
|
||||
log.Printf("[FORWARD] Error accept <- conn: %v", err)
|
||||
return
|
||||
}
|
||||
}()
|
||||
|
||||
<-txDone
|
||||
<-rxDone
|
||||
}
|
||||
|
||||
func (self *realProxyForwarding) handleConnection(clientConn net.Conn) {
|
||||
defer clientConn.Close()
|
||||
|
||||
rawTargetConnection, err := self.Proxy.Connect(self.TargetHost, self.TargetPort)
|
||||
if err != nil {
|
||||
log.Printf("[FORWARD] Connect proxy Error: %v", err)
|
||||
return
|
||||
}
|
||||
defer rawTargetConnection.Close()
|
||||
|
||||
targetConn, err := self.ConnectionWrapper(rawTargetConnection)
|
||||
|
||||
if err != nil {
|
||||
log.Printf("[FORWARD] wrap proxy connection error: %v", err)
|
||||
return
|
||||
}
|
||||
|
||||
defer targetConn.Close()
|
||||
|
||||
self.serviceForwardedConnection(clientConn, targetConn)
|
||||
}
|
25
builder/xenserver/common/proxy/proxy.go
Normal file
25
builder/xenserver/common/proxy/proxy.go
Normal file
@ -0,0 +1,25 @@
|
||||
package proxy
|
||||
|
||||
import (
|
||||
"context"
|
||||
"net"
|
||||
)
|
||||
|
||||
type XenProxy interface {
|
||||
Close() error
|
||||
Start() error
|
||||
ConnectWithAddr(address string) (net.Conn, error)
|
||||
Connect(host string, port int) (net.Conn, error)
|
||||
CreateForwarding(host string, port int) ProxyForwarding
|
||||
CreateWrapperForwarding(host string, port int, wrapper func(rawConn net.Conn) (net.Conn, error)) ProxyForwarding
|
||||
}
|
||||
|
||||
func CreateProxy(skipNatForwarding bool, dialer func(ctx context.Context, network, addr string) (net.Conn, error)) XenProxy {
|
||||
if skipNatForwarding {
|
||||
return &noNatXenProxy{}
|
||||
}
|
||||
|
||||
return &realXenProxy{
|
||||
Dialer: dialer,
|
||||
}
|
||||
}
|
45
builder/xenserver/common/proxy/proxy_disabled.go
Normal file
45
builder/xenserver/common/proxy/proxy_disabled.go
Normal file
@ -0,0 +1,45 @@
|
||||
package proxy
|
||||
|
||||
import (
|
||||
"net"
|
||||
"strconv"
|
||||
)
|
||||
|
||||
type noNatXenProxy struct{}
|
||||
|
||||
func (proxy *noNatXenProxy) Close() error {
|
||||
return nil
|
||||
}
|
||||
|
||||
func (proxy *noNatXenProxy) Start() error {
|
||||
return nil
|
||||
}
|
||||
|
||||
func (proxy *noNatXenProxy) ConnectWithAddr(address string) (net.Conn, error) {
|
||||
c, err := net.Dial("tcp", address)
|
||||
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return c, nil
|
||||
}
|
||||
|
||||
func (proxy *noNatXenProxy) Connect(host string, port int) (net.Conn, error) {
|
||||
return proxy.ConnectWithAddr(net.JoinHostPort(host, strconv.Itoa(port)))
|
||||
}
|
||||
|
||||
func (proxy *noNatXenProxy) CreateForwarding(host string, port int) ProxyForwarding {
|
||||
return &noNatProxyForwarding{
|
||||
TargetHost: host,
|
||||
TargetPort: port,
|
||||
}
|
||||
}
|
||||
|
||||
func (proxy *noNatXenProxy) CreateWrapperForwarding(host string, port int,
|
||||
_ func(rawConn net.Conn) (net.Conn, error)) ProxyForwarding {
|
||||
return &noNatProxyForwarding{
|
||||
TargetHost: host,
|
||||
TargetPort: port,
|
||||
}
|
||||
}
|
88
builder/xenserver/common/proxy/proxy_socks.go
Normal file
88
builder/xenserver/common/proxy/proxy_socks.go
Normal file
@ -0,0 +1,88 @@
|
||||
package proxy
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"github.com/armon/go-socks5"
|
||||
proxyClient "golang.org/x/net/proxy"
|
||||
"log"
|
||||
"net"
|
||||
"strconv"
|
||||
)
|
||||
|
||||
type realXenProxy struct {
|
||||
Dialer func(ctx context.Context, network, addr string) (net.Conn, error)
|
||||
|
||||
socksListener net.Listener
|
||||
}
|
||||
|
||||
func (proxy *realXenProxy) Addr() string {
|
||||
return proxy.socksListener.Addr().String()
|
||||
}
|
||||
|
||||
func (proxy *realXenProxy) Close() error {
|
||||
if proxy.socksListener != nil {
|
||||
return proxy.socksListener.Close()
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (proxy *realXenProxy) Start() error {
|
||||
socksConfig := &socks5.Config{
|
||||
Dial: proxy.Dialer,
|
||||
}
|
||||
server, err := socks5.New(socksConfig)
|
||||
if err != nil {
|
||||
return fmt.Errorf("could not setup socks server: %w", err)
|
||||
}
|
||||
|
||||
proxy.socksListener, err = net.Listen("tcp", "127.0.0.1:0")
|
||||
if err != nil {
|
||||
return fmt.Errorf("error creating socks listener: %s", err)
|
||||
}
|
||||
|
||||
go func() {
|
||||
err := server.Serve(proxy.socksListener)
|
||||
if err != nil {
|
||||
log.Printf("error in proxy server: %v", err)
|
||||
}
|
||||
}()
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (proxy *realXenProxy) ConnectWithAddr(address string) (net.Conn, error) {
|
||||
dialer, err := proxyClient.SOCKS5("tcp", proxy.Addr(), nil, proxyClient.Direct)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("unable to connect to proxy: %s", err)
|
||||
}
|
||||
|
||||
c, err := dialer.Dial("tcp", address)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return c, nil
|
||||
}
|
||||
|
||||
func (proxy *realXenProxy) Connect(host string, port int) (net.Conn, error) {
|
||||
return proxy.ConnectWithAddr(net.JoinHostPort(host, strconv.Itoa(port)))
|
||||
}
|
||||
|
||||
func (proxy *realXenProxy) CreateForwarding(host string, port int) ProxyForwarding {
|
||||
return &realProxyForwarding{
|
||||
Proxy: proxy,
|
||||
TargetHost: host,
|
||||
TargetPort: port,
|
||||
}
|
||||
}
|
||||
|
||||
func (proxy *realXenProxy) CreateWrapperForwarding(host string, port int,
|
||||
wrapper func(rawConn net.Conn) (net.Conn, error)) ProxyForwarding {
|
||||
return &realProxyForwarding{
|
||||
Proxy: proxy,
|
||||
TargetHost: host,
|
||||
TargetPort: port,
|
||||
ConnectionWrapper: wrapper,
|
||||
}
|
||||
}
|
@ -1,217 +0,0 @@
|
||||
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 := 22
|
||||
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, remote_dest string, remote_port uint) error {
|
||||
defer local_conn.Close()
|
||||
|
||||
ssh_client_conn, err := gossh.Dial("tcp", server+":22", 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, 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, 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
|
||||
}
|
139
builder/xenserver/common/ssh/ssh.go
Normal file
139
builder/xenserver/common/ssh/ssh.go
Normal file
@ -0,0 +1,139 @@
|
||||
package ssh
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"context"
|
||||
"fmt"
|
||||
"github.com/hashicorp/packer-plugin-sdk/multistep"
|
||||
config2 "github.com/xenserver/packer-builder-xenserver/builder/xenserver/common/config"
|
||||
"github.com/xenserver/packer-builder-xenserver/builder/xenserver/common/proxy"
|
||||
"golang.org/x/crypto/ssh"
|
||||
"log"
|
||||
"net"
|
||||
"strconv"
|
||||
"strings"
|
||||
"time"
|
||||
)
|
||||
|
||||
func doExecuteSSHCmd(cmd string, client *ssh.Client) (stdout string, err error) {
|
||||
//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 ExecuteSSHCmd(host string, port int, username, password, cmd string) (stdout string, err error) {
|
||||
sshClient, err := ConnectSSH(host, port, username, password)
|
||||
if err != nil {
|
||||
return "", fmt.Errorf("Could not connect to ssh")
|
||||
}
|
||||
|
||||
defer sshClient.Close()
|
||||
|
||||
return doExecuteSSHCmd(cmd, sshClient)
|
||||
}
|
||||
|
||||
func ExecuteApiHostSSHCmd(state multistep.StateBag, cmd string) (stdout string, err error) {
|
||||
config := state.Get("commonconfig").(config2.CommonConfig)
|
||||
return ExecuteSSHCmd(config.HostIp, config.HostSSHPort, config.Username, config.Password, cmd)
|
||||
}
|
||||
|
||||
func ExecuteHostSSHCmd(state multistep.StateBag, cmd string) (stdout string, err error) {
|
||||
config := state.Get("commonconfig").(config2.CommonConfig)
|
||||
xenProxy := state.Get("xen_proxy").(proxy.XenProxy)
|
||||
|
||||
host := state.Get("vm_host_address").(string)
|
||||
|
||||
sshClient, err := ConnectSSHWithProxy(xenProxy, host, 22, config.Username, config.Password)
|
||||
if err != nil {
|
||||
return "", fmt.Errorf("could not connect to ssh proxy: %w", err)
|
||||
}
|
||||
|
||||
defer sshClient.Close()
|
||||
|
||||
return doExecuteSSHCmd(cmd, sshClient)
|
||||
}
|
||||
|
||||
func ConnectSSH(host string, port int, username string, password string) (*ssh.Client, error) {
|
||||
address := net.JoinHostPort(host, strconv.Itoa(port))
|
||||
log.Printf("Connecting with ssh to %s", address)
|
||||
|
||||
config := &ssh.ClientConfig{
|
||||
User: username,
|
||||
Auth: []ssh.AuthMethod{
|
||||
ssh.Password(password),
|
||||
},
|
||||
HostKeyCallback: ssh.InsecureIgnoreHostKey(),
|
||||
}
|
||||
|
||||
client, err := ssh.Dial("tcp", address, config)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("could not connect to ssh server: %w", err)
|
||||
}
|
||||
return client, err
|
||||
}
|
||||
|
||||
func ConnectSSHWithProxy(proxy proxy.XenProxy, host string, port int, username string, password string) (*ssh.Client, error) {
|
||||
connection, err := proxy.Connect(host, port)
|
||||
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("could not connect to target server: %w", err)
|
||||
}
|
||||
|
||||
config := &ssh.ClientConfig{
|
||||
User: username,
|
||||
Auth: []ssh.AuthMethod{
|
||||
ssh.Password(password),
|
||||
},
|
||||
HostKeyCallback: ssh.InsecureIgnoreHostKey(),
|
||||
}
|
||||
|
||||
connectionEstablished := make(chan struct{}, 1)
|
||||
var sshConn ssh.Conn
|
||||
var sshChan <-chan ssh.NewChannel
|
||||
var req <-chan *ssh.Request
|
||||
|
||||
go func() {
|
||||
sshConn, sshChan, req, err = ssh.NewClientConn(connection, host, config)
|
||||
close(connectionEstablished)
|
||||
}()
|
||||
|
||||
timeout := time.Minute
|
||||
|
||||
select {
|
||||
case <-connectionEstablished:
|
||||
// We don't need to do anything here. We just want select to block until
|
||||
// we connect or timeout.
|
||||
case <-time.After(timeout):
|
||||
if sshConn != nil {
|
||||
sshConn.Close()
|
||||
}
|
||||
|
||||
if connection != nil {
|
||||
connection.Close()
|
||||
}
|
||||
|
||||
return nil, fmt.Errorf("timeout connecting to ssh server: %w", err)
|
||||
}
|
||||
|
||||
sshClient := ssh.NewClient(sshConn, sshChan, req)
|
||||
|
||||
return sshClient, nil
|
||||
}
|
||||
|
||||
func SSHDialer(client *ssh.Client) func(ctx context.Context, network, addr string) (net.Conn, error) {
|
||||
return func(ctx context.Context, network, addr string) (net.Conn, error) {
|
||||
return client.Dial("tcp", addr)
|
||||
}
|
||||
}
|
@ -1,48 +0,0 @@
|
||||
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
|
||||
}
|
@ -1,50 +0,0 @@
|
||||
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)
|
||||
remotePort, _ := self.RemotePort(state)
|
||||
remoteDest, _ := self.RemoteDest(state)
|
||||
|
||||
go ssh_port_forward(l, remotePort, remoteDest, hostAddress, 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) {}
|
@ -1,51 +0,0 @@
|
||||
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
|
||||
}
|
@ -1,96 +0,0 @@
|
||||
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,272 +0,0 @@
|
||||
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 (self *StepTypeBootCommand) 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)
|
||||
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(fmt.Sprintf("Connecting to the VM console VNC over xapi via %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.Say(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.Say(fmt.Sprintf("Received response: %s", string(buffer)))
|
||||
|
||||
vncClient, err := vnc.Client(tlsConn, &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 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))
|
||||
|
||||
self.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, &self.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)
|
||||
}
|
||||
|
||||
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)
|
||||
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)
|
||||
}
|
||||
}
|
@ -1,8 +1,10 @@
|
||||
package common
|
||||
package steps
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
config2 "github.com/xenserver/packer-builder-xenserver/builder/xenserver/common/config"
|
||||
"github.com/xenserver/packer-builder-xenserver/builder/xenserver/common/xen"
|
||||
"log"
|
||||
|
||||
"github.com/hashicorp/packer-plugin-sdk/multistep"
|
||||
@ -19,7 +21,7 @@ type StepAttachVdi struct {
|
||||
|
||||
func (self *StepAttachVdi) Run(ctx context.Context, state multistep.StateBag) multistep.StepAction {
|
||||
ui := state.Get("ui").(packer.Ui)
|
||||
c := state.Get("client").(*Connection)
|
||||
c := state.Get("client").(*xen.Connection)
|
||||
|
||||
log.Printf("Running attach vdi for key %s\n", self.VdiUuidKey)
|
||||
var vdiUuid string
|
||||
@ -31,20 +33,20 @@ func (self *StepAttachVdi) Run(ctx context.Context, state multistep.StateBag) mu
|
||||
}
|
||||
|
||||
var err error
|
||||
self.vdi, err = c.client.VDI.GetByUUID(c.session, vdiUuid)
|
||||
self.vdi, err = c.GetClient().VDI.GetByUUID(c.GetSessionRef(), 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)
|
||||
instance, err := c.GetClient().VM.GetByUUID(c.GetSessionRef(), 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)
|
||||
err = xen.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
|
||||
@ -56,8 +58,8 @@ func (self *StepAttachVdi) Run(ctx context.Context, state multistep.StateBag) mu
|
||||
}
|
||||
|
||||
func (self *StepAttachVdi) Cleanup(state multistep.StateBag) {
|
||||
config := state.Get("commonconfig").(CommonConfig)
|
||||
c := state.Get("client").(*Connection)
|
||||
config := state.Get("commonconfig").(config2.CommonConfig)
|
||||
c := state.Get("client").(*xen.Connection)
|
||||
if config.ShouldKeepVM(state) {
|
||||
return
|
||||
}
|
||||
@ -67,7 +69,7 @@ func (self *StepAttachVdi) Cleanup(state multistep.StateBag) {
|
||||
}
|
||||
|
||||
uuid := state.Get("instance_uuid").(string)
|
||||
vmRef, err := c.client.VM.GetByUUID(c.session, uuid)
|
||||
vmRef, err := c.GetClient().VM.GetByUUID(c.GetSessionRef(), uuid)
|
||||
if err != nil {
|
||||
log.Printf("Unable to get VM from UUID '%s': %s", uuid, err.Error())
|
||||
return
|
||||
@ -75,7 +77,7 @@ func (self *StepAttachVdi) Cleanup(state multistep.StateBag) {
|
||||
|
||||
vdiUuid := state.Get(self.VdiUuidKey).(string)
|
||||
|
||||
err = DisconnectVdi(c, vmRef, self.vdi)
|
||||
err = xen.DisconnectVdi(c, vmRef, self.vdi)
|
||||
if err != nil {
|
||||
log.Printf("Unable to disconnect VDI '%s': %s", vdiUuid, err.Error())
|
||||
return
|
@ -1,8 +1,11 @@
|
||||
package common
|
||||
package steps
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
config2 "github.com/xenserver/packer-builder-xenserver/builder/xenserver/common/config"
|
||||
"github.com/xenserver/packer-builder-xenserver/builder/xenserver/common/util"
|
||||
"github.com/xenserver/packer-builder-xenserver/builder/xenserver/common/xen"
|
||||
|
||||
"github.com/hashicorp/packer-plugin-sdk/multistep"
|
||||
"github.com/hashicorp/packer-plugin-sdk/packer"
|
||||
@ -11,17 +14,17 @@ import (
|
||||
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)
|
||||
c := state.Get("client").(*xen.Connection)
|
||||
config := state.Get("commonconfig").(config2.CommonConfig)
|
||||
ui := state.Get("ui").(packer.Ui)
|
||||
|
||||
instance, _ := c.client.VM.GetByUUID(c.session, state.Get("instance_uuid").(string))
|
||||
instance, _ := c.GetClient().VM.GetByUUID(c.GetSessionRef(), state.Get("instance_uuid").(string))
|
||||
ui.Say("Unpausing VM " + state.Get("instance_uuid").(string))
|
||||
Unpause(c, instance)
|
||||
xen.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)
|
||||
err := util.InterruptibleWait{Timeout: config.BootWait}.Wait(state)
|
||||
if err != nil {
|
||||
ui.Error(err.Error())
|
||||
return multistep.ActionHalt
|
103
builder/xenserver/common/steps/step_create_forwarding.go
Normal file
103
builder/xenserver/common/steps/step_create_forwarding.go
Normal file
@ -0,0 +1,103 @@
|
||||
package steps
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"github.com/hashicorp/packer-plugin-sdk/multistep"
|
||||
"github.com/hashicorp/packer-plugin-sdk/packer"
|
||||
"github.com/xenserver/packer-builder-xenserver/builder/xenserver/common/proxy"
|
||||
)
|
||||
|
||||
type ForwardTarget struct {
|
||||
Host func(multistep.StateBag) (string, error)
|
||||
Port func(multistep.StateBag) (int, error)
|
||||
Key string
|
||||
|
||||
forwarding proxy.ProxyForwarding
|
||||
}
|
||||
|
||||
type forwardingInfo struct {
|
||||
host string
|
||||
port int
|
||||
}
|
||||
|
||||
type StepCreateForwarding struct {
|
||||
Targets []ForwardTarget
|
||||
}
|
||||
|
||||
func (self *StepCreateForwarding) close() {
|
||||
for _, target := range self.Targets {
|
||||
if target.forwarding != nil {
|
||||
target.forwarding.Close()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func (self *StepCreateForwarding) Run(_ context.Context, state multistep.StateBag) multistep.StepAction {
|
||||
ui := state.Get("ui").(packer.Ui)
|
||||
xenProxy := state.Get("xen_proxy").(proxy.XenProxy)
|
||||
|
||||
for _, target := range self.Targets {
|
||||
host, err := target.Host(state)
|
||||
|
||||
if err != nil {
|
||||
self.close()
|
||||
err := fmt.Errorf("could not get host for forwarding: %w", err)
|
||||
state.Put("error", err)
|
||||
ui.Error(err.Error())
|
||||
return multistep.ActionHalt
|
||||
}
|
||||
|
||||
port, err := target.Port(state)
|
||||
|
||||
if err != nil {
|
||||
self.close()
|
||||
err := fmt.Errorf("could not get port for forwarding: %w", err)
|
||||
state.Put("error", err)
|
||||
ui.Error(err.Error())
|
||||
return multistep.ActionHalt
|
||||
}
|
||||
|
||||
forwarding := xenProxy.CreateForwarding(host, port)
|
||||
err = forwarding.Start()
|
||||
|
||||
if err != nil {
|
||||
self.close()
|
||||
err := fmt.Errorf("could not create forwarding: %w", err)
|
||||
state.Put("error", err)
|
||||
ui.Error(err.Error())
|
||||
return multistep.ActionHalt
|
||||
}
|
||||
|
||||
info := forwardingInfo{
|
||||
host: forwarding.GetServiceHost(),
|
||||
port: forwarding.GetServicePort(),
|
||||
}
|
||||
|
||||
state.Put(target.Key, info)
|
||||
}
|
||||
|
||||
return multistep.ActionContinue
|
||||
}
|
||||
|
||||
func (self *StepCreateForwarding) Cleanup(_ multistep.StateBag) {
|
||||
self.close()
|
||||
}
|
||||
|
||||
func GetForwardedHost(state multistep.StateBag, key string) (string, error) {
|
||||
address, ok := state.GetOk(key)
|
||||
if !ok {
|
||||
return "", fmt.Errorf("key '%s' does not exist", key)
|
||||
}
|
||||
|
||||
return address.(forwardingInfo).host, nil
|
||||
}
|
||||
|
||||
func GetForwardedPort(state multistep.StateBag, key string) (int, error) {
|
||||
address, ok := state.GetOk(key)
|
||||
if !ok {
|
||||
return 0, fmt.Errorf("key '%s' does not exist", key)
|
||||
}
|
||||
|
||||
return address.(forwardingInfo).port, nil
|
||||
}
|
65
builder/xenserver/common/steps/step_create_proxy.go
Normal file
65
builder/xenserver/common/steps/step_create_proxy.go
Normal file
@ -0,0 +1,65 @@
|
||||
package steps
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
config2 "github.com/xenserver/packer-builder-xenserver/builder/xenserver/common/config"
|
||||
"github.com/xenserver/packer-builder-xenserver/builder/xenserver/common/proxy"
|
||||
ssh2 "github.com/xenserver/packer-builder-xenserver/builder/xenserver/common/ssh"
|
||||
"golang.org/x/crypto/ssh"
|
||||
"log"
|
||||
|
||||
"github.com/hashicorp/packer-plugin-sdk/multistep"
|
||||
"github.com/hashicorp/packer-plugin-sdk/packer"
|
||||
)
|
||||
|
||||
type StepCreateProxy struct {
|
||||
sshClient *ssh.Client
|
||||
proxyServer proxy.XenProxy
|
||||
}
|
||||
|
||||
func (self *StepCreateProxy) Run(_ context.Context, state multistep.StateBag) multistep.StepAction {
|
||||
config := state.Get("commonconfig").(config2.CommonConfig)
|
||||
ui := state.Get("ui").(packer.Ui)
|
||||
|
||||
var err error
|
||||
self.sshClient, err = ssh2.ConnectSSH(config.HostIp, config.HostSSHPort, config.Username, config.Password)
|
||||
if err != nil {
|
||||
err := fmt.Errorf("error connecting to hypervisor with ssh: %s", err)
|
||||
state.Put("error", err)
|
||||
ui.Error(err.Error())
|
||||
return multistep.ActionHalt
|
||||
}
|
||||
|
||||
self.proxyServer = proxy.CreateProxy(config.SkipNatMapping, ssh2.SSHDialer(self.sshClient))
|
||||
|
||||
err = self.proxyServer.Start()
|
||||
if err != nil {
|
||||
err := fmt.Errorf("error creating socks proxy server: %s", err)
|
||||
state.Put("error", err)
|
||||
ui.Error(err.Error())
|
||||
return multistep.ActionHalt
|
||||
}
|
||||
|
||||
state.Put("xen_proxy", self.proxyServer)
|
||||
|
||||
return multistep.ActionContinue
|
||||
}
|
||||
|
||||
func (self *StepCreateProxy) Cleanup(_ multistep.StateBag) {
|
||||
if self.proxyServer != nil {
|
||||
err := self.proxyServer.Close()
|
||||
if err != nil {
|
||||
log.Printf("error cleaning up proxy server: %v", err)
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
if self.sshClient != nil {
|
||||
err := self.sshClient.Close()
|
||||
if err != nil {
|
||||
log.Printf("error cleaning up ssh client: %v", err)
|
||||
return
|
||||
}
|
||||
}
|
||||
}
|
@ -1,8 +1,9 @@
|
||||
package common
|
||||
package steps
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"github.com/xenserver/packer-builder-xenserver/builder/xenserver/common/xen"
|
||||
"log"
|
||||
|
||||
"github.com/hashicorp/packer-plugin-sdk/multistep"
|
||||
@ -15,7 +16,7 @@ type StepDetachVdi struct {
|
||||
|
||||
func (self *StepDetachVdi) Run(ctx context.Context, state multistep.StateBag) multistep.StepAction {
|
||||
ui := state.Get("ui").(packer.Ui)
|
||||
c := state.Get("client").(*Connection)
|
||||
c := state.Get("client").(*xen.Connection)
|
||||
|
||||
var vdiUuid string
|
||||
if vdiUuidRaw, ok := state.GetOk(self.VdiUuidKey); ok {
|
||||
@ -25,20 +26,20 @@ func (self *StepDetachVdi) Run(ctx context.Context, state multistep.StateBag) mu
|
||||
return multistep.ActionContinue
|
||||
}
|
||||
|
||||
vdi, err := c.client.VDI.GetByUUID(c.session, vdiUuid)
|
||||
vdi, err := c.GetClient().VDI.GetByUUID(c.GetSessionRef(), 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)
|
||||
instance, err := c.GetClient().VM.GetByUUID(c.GetSessionRef(), 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)
|
||||
err = xen.DisconnectVdi(c, instance, vdi)
|
||||
if err != nil {
|
||||
ui.Error(fmt.Sprintf("Unable to detach VDI '%s': %s", vdiUuid, err.Error()))
|
||||
//return multistep.ActionHalt
|
@ -1,13 +1,17 @@
|
||||
package common
|
||||
package steps
|
||||
|
||||
import (
|
||||
"context"
|
||||
"crypto/tls"
|
||||
"fmt"
|
||||
config2 "github.com/xenserver/packer-builder-xenserver/builder/xenserver/common/config"
|
||||
"github.com/xenserver/packer-builder-xenserver/builder/xenserver/common/xen"
|
||||
"io"
|
||||
"net"
|
||||
"net/http"
|
||||
"os"
|
||||
"os/exec"
|
||||
"strconv"
|
||||
|
||||
"github.com/hashicorp/packer-plugin-sdk/multistep"
|
||||
"github.com/hashicorp/packer-plugin-sdk/packer"
|
||||
@ -81,35 +85,35 @@ func downloadFile(url, filename string, ui packer.Ui) (err error) {
|
||||
}
|
||||
|
||||
func (StepExport) Run(ctx context.Context, state multistep.StateBag) multistep.StepAction {
|
||||
config := state.Get("commonconfig").(CommonConfig)
|
||||
config := state.Get("commonconfig").(config2.CommonConfig)
|
||||
ui := state.Get("ui").(packer.Ui)
|
||||
c := state.Get("client").(*Connection)
|
||||
c := state.Get("client").(*xen.Connection)
|
||||
instance_uuid := state.Get("instance_uuid").(string)
|
||||
suffix := ".vhd"
|
||||
extrauri := "&format=vhd"
|
||||
|
||||
instance, err := c.client.VM.GetByUUID(c.session, instance_uuid)
|
||||
instance, err := c.GetClient().VM.GetByUUID(c.GetSessionRef(), 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)
|
||||
vifs, err := c.GetClient().VM.GetVIFs(c.GetSessionRef(), 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)
|
||||
err := c.GetClient().VIF.Destroy(c.GetSessionRef(), 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)
|
||||
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()))
|
||||
@ -127,7 +131,7 @@ func (StepExport) Run(ctx context.Context, state multistep.StateBag) multistep.S
|
||||
|
||||
//we need the VIF index string
|
||||
vifIndexString := fmt.Sprintf("%d", i)
|
||||
_, err = ConnectNetwork(c, networks[0], instance, vifIndexString)
|
||||
_, err = xen.ConnectNetwork(c, networks[0], instance, vifIndexString)
|
||||
|
||||
if err != nil {
|
||||
ui.Say(err.Error())
|
||||
@ -159,7 +163,7 @@ func (StepExport) Run(ctx context.Context, state multistep.StateBag) multistep.S
|
||||
cmd := exec.Command(
|
||||
xe,
|
||||
"-s", c.Host,
|
||||
"-p", "443",
|
||||
"-p", strconv.Itoa(c.Port),
|
||||
"-u", c.Username,
|
||||
"-pw", c.Password,
|
||||
"vm-export",
|
||||
@ -173,7 +177,7 @@ func (StepExport) Run(ctx context.Context, state multistep.StateBag) multistep.S
|
||||
err = cmd.Run()
|
||||
} else {
|
||||
export_url := fmt.Sprintf("https://%s/export?%suuid=%s&session_id=%s",
|
||||
c.Host,
|
||||
net.JoinHostPort(c.Host, strconv.Itoa(c.Port)),
|
||||
compress_option_url,
|
||||
instance_uuid,
|
||||
c.GetSession(),
|
||||
@ -195,27 +199,27 @@ func (StepExport) Run(ctx context.Context, state multistep.StateBag) multistep.S
|
||||
case "vdi_vhd":
|
||||
// export the disks
|
||||
|
||||
disks, err := GetDisks(c, instance)
|
||||
disks, err := xen.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)
|
||||
disk_uuid, err := c.GetClient().VDI.GetUUID(c.GetSessionRef(), 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)
|
||||
hosts, err := c.GetClient().Host.GetAll(c.GetSessionRef())
|
||||
|
||||
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)
|
||||
host_software_versions, err := c.GetClient().Host.GetSoftwareVersion(c.GetSessionRef(), host)
|
||||
xs_version := host_software_versions["product_version"]
|
||||
|
||||
if err != nil {
|
||||
@ -229,7 +233,7 @@ func (StepExport) Run(ctx context.Context, state multistep.StateBag) multistep.S
|
||||
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")
|
||||
disk_export_url, err = xen.Expose(c, disk, "vhd")
|
||||
|
||||
if err != nil {
|
||||
ui.Error(fmt.Sprintf("Failed to expose disk %s: %s", disk_uuid, err.Error()))
|
||||
@ -245,7 +249,7 @@ func (StepExport) Run(ctx context.Context, state multistep.StateBag) multistep.S
|
||||
disk_export_url = fmt.Sprintf("https://%s:%s@%s/export_raw_vdi?vdi=%s%s",
|
||||
c.Username,
|
||||
c.Password,
|
||||
c.Host,
|
||||
net.JoinHostPort(c.Host, strconv.Itoa(c.Port)),
|
||||
disk_uuid,
|
||||
extrauri)
|
||||
|
||||
@ -262,7 +266,7 @@ func (StepExport) Run(ctx context.Context, state multistep.StateBag) multistep.S
|
||||
|
||||
// Call unexpose in case a TVM was used. The call is harmless
|
||||
// if that is not the case.
|
||||
Unexpose(c, disk)
|
||||
xen.Unexpose(c, disk)
|
||||
|
||||
}
|
||||
|
@ -1,8 +1,9 @@
|
||||
package common
|
||||
package steps
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"github.com/xenserver/packer-builder-xenserver/builder/xenserver/common/xen"
|
||||
|
||||
"github.com/hashicorp/packer-plugin-sdk/multistep"
|
||||
"github.com/hashicorp/packer-plugin-sdk/packer"
|
||||
@ -16,14 +17,14 @@ type StepFindVdi struct {
|
||||
|
||||
func (self *StepFindVdi) Run(ctx context.Context, state multistep.StateBag) multistep.StepAction {
|
||||
ui := state.Get("ui").(packer.Ui)
|
||||
c := state.Get("client").(*Connection)
|
||||
c := state.Get("client").(*xen.Connection)
|
||||
|
||||
// Ignore if VdiName is not specified
|
||||
if self.VdiName == "" {
|
||||
return multistep.ActionContinue
|
||||
}
|
||||
|
||||
vdis, err := c.client.VDI.GetByNameLabel(c.session, self.VdiName)
|
||||
vdis, err := c.GetClient().VDI.GetByNameLabel(c.GetSessionRef(), self.VdiName)
|
||||
|
||||
switch {
|
||||
case len(vdis) == 0:
|
||||
@ -36,7 +37,7 @@ func (self *StepFindVdi) Run(ctx context.Context, state multistep.StateBag) mult
|
||||
|
||||
vdi := vdis[0]
|
||||
|
||||
vdiUuid, err := c.client.VDI.GetUUID(c.session, vdi)
|
||||
vdiUuid, err := c.GetClient().VDI.GetUUID(c.GetSessionRef(), vdi)
|
||||
if err != nil {
|
||||
ui.Error(fmt.Sprintf("Unable to get UUID of VDI '%s': %s", self.VdiName, err.Error()))
|
||||
return multistep.ActionHalt
|
85
builder/xenserver/common/steps/step_get_vnc_port.go
Normal file
85
builder/xenserver/common/steps/step_get_vnc_port.go
Normal file
@ -0,0 +1,85 @@
|
||||
package steps
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"github.com/hashicorp/packer-plugin-sdk/multistep"
|
||||
"github.com/hashicorp/packer-plugin-sdk/packer"
|
||||
config2 "github.com/xenserver/packer-builder-xenserver/builder/xenserver/common/config"
|
||||
"github.com/xenserver/packer-builder-xenserver/builder/xenserver/common/proxy"
|
||||
"github.com/xenserver/packer-builder-xenserver/builder/xenserver/common/vnc"
|
||||
"github.com/xenserver/packer-builder-xenserver/builder/xenserver/common/xen"
|
||||
"net"
|
||||
"strconv"
|
||||
)
|
||||
|
||||
type StepGetVNCPort struct {
|
||||
forwarding proxy.ProxyForwarding
|
||||
}
|
||||
|
||||
func (self *StepGetVNCPort) Run(ctx context.Context, state multistep.StateBag) multistep.StepAction {
|
||||
ui := state.Get("ui").(packer.Ui)
|
||||
xenClient := state.Get("client").(*xen.Connection)
|
||||
xenProxy := state.Get("xen_proxy").(proxy.XenProxy)
|
||||
config := state.Get("commonconfig").(config2.CommonConfig)
|
||||
|
||||
if config.VNCConfig.DisableVNC {
|
||||
return multistep.ActionContinue
|
||||
}
|
||||
|
||||
ui.Say("Step: forward the instances VNC")
|
||||
|
||||
location, err := vnc.GetVNCConsoleLocation(state)
|
||||
if err != nil {
|
||||
state.Put("error", err)
|
||||
ui.Error(err.Error())
|
||||
return multistep.ActionHalt
|
||||
}
|
||||
|
||||
target, err := vnc.GetTcpAddressFromURL(location)
|
||||
if err != nil {
|
||||
state.Put("error", err)
|
||||
ui.Error(err.Error())
|
||||
return multistep.ActionHalt
|
||||
}
|
||||
|
||||
host, portText, err := net.SplitHostPort(target)
|
||||
|
||||
if err != nil {
|
||||
state.Put("error", err)
|
||||
ui.Error(err.Error())
|
||||
return multistep.ActionHalt
|
||||
}
|
||||
|
||||
port, err := strconv.Atoi(portText)
|
||||
|
||||
if err != nil {
|
||||
state.Put("error", err)
|
||||
ui.Error(err.Error())
|
||||
return multistep.ActionHalt
|
||||
}
|
||||
|
||||
self.forwarding = xenProxy.CreateWrapperForwarding(host, port, func(rawConn net.Conn) (net.Conn, error) {
|
||||
return vnc.InitializeVNCConnection(location, string(xenClient.GetSessionRef()), rawConn)
|
||||
})
|
||||
|
||||
err = self.forwarding.Start()
|
||||
|
||||
if err != nil {
|
||||
self.forwarding.Close()
|
||||
state.Put("error", err)
|
||||
ui.Error(err.Error())
|
||||
return multistep.ActionHalt
|
||||
}
|
||||
|
||||
vncUrl := net.JoinHostPort(self.forwarding.GetServiceHost(), strconv.Itoa(self.forwarding.GetServicePort()))
|
||||
ui.Say(fmt.Sprintf("VNC available on vnc://%s", vncUrl))
|
||||
|
||||
return multistep.ActionContinue
|
||||
}
|
||||
|
||||
func (self *StepGetVNCPort) Cleanup(state multistep.StateBag) {
|
||||
if self.forwarding != nil {
|
||||
self.forwarding.Close()
|
||||
}
|
||||
}
|
39
builder/xenserver/common/steps/step_http_ip_discover.go
Normal file
39
builder/xenserver/common/steps/step_http_ip_discover.go
Normal file
@ -0,0 +1,39 @@
|
||||
package steps
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"github.com/xenserver/packer-builder-xenserver/builder/xenserver/common/ssh"
|
||||
"strings"
|
||||
|
||||
"github.com/hashicorp/packer-plugin-sdk/multistep"
|
||||
packersdk "github.com/hashicorp/packer-plugin-sdk/packer"
|
||||
)
|
||||
|
||||
// Step to discover the http ip
|
||||
// which guests use to reach the vm host
|
||||
// To make sure the IP is set before boot command and http server steps
|
||||
type StepHTTPIPDiscover struct{}
|
||||
|
||||
func (s *StepHTTPIPDiscover) Run(ctx context.Context, state multistep.StateBag) multistep.StepAction {
|
||||
ui := state.Get("ui").(packersdk.Ui)
|
||||
|
||||
// find local ip
|
||||
envVar, err := ssh.ExecuteApiHostSSHCmd(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
|
||||
}
|
||||
hostIP := strings.Split(envVar, " ")[0]
|
||||
ui.Message(fmt.Sprintf("Found local IP: %s", hostIP))
|
||||
|
||||
state.Put("http_ip", hostIP)
|
||||
|
||||
return multistep.ActionContinue
|
||||
}
|
||||
|
||||
func (s *StepHTTPIPDiscover) Cleanup(state multistep.StateBag) {}
|
@ -1,4 +1,4 @@
|
||||
package common
|
||||
package steps
|
||||
|
||||
/* Taken from https://raw.githubusercontent.com/hashicorp/packer/master/builder/qemu/step_prepare_output_dir.go */
|
||||
|
@ -1,8 +1,9 @@
|
||||
package common
|
||||
package steps
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"github.com/xenserver/packer-builder-xenserver/builder/xenserver/common/xen"
|
||||
|
||||
"github.com/hashicorp/packer-plugin-sdk/multistep"
|
||||
"github.com/hashicorp/packer-plugin-sdk/packer"
|
||||
@ -12,29 +13,29 @@ type StepSetVmHostSshAddress struct{}
|
||||
|
||||
func (self *StepSetVmHostSshAddress) Run(ctx context.Context, state multistep.StateBag) multistep.StepAction {
|
||||
|
||||
c := state.Get("client").(*Connection)
|
||||
c := state.Get("client").(*xen.Connection)
|
||||
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)
|
||||
instance, err := c.GetClient().VM.GetByUUID(c.GetSessionRef(), 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)
|
||||
host, err := c.GetClient().VM.GetResidentOn(c.GetSessionRef(), 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)
|
||||
address, err := c.GetClient().Host.GetAddress(c.GetSessionRef(), host)
|
||||
if err != nil {
|
||||
ui.Error(fmt.Sprintf("Unable to get address from VM Host: %s", err.Error()))
|
||||
}
|
||||
|
||||
state.Put("ssh_address", address)
|
||||
state.Put("vm_host_address", address)
|
||||
ui.Say(fmt.Sprintf("Set host SSH address to '%s'.", address))
|
||||
|
||||
return multistep.ActionContinue
|
@ -1,8 +1,9 @@
|
||||
package common
|
||||
package steps
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"github.com/xenserver/packer-builder-xenserver/builder/xenserver/common/xen"
|
||||
|
||||
"github.com/hashicorp/packer-plugin-sdk/multistep"
|
||||
"github.com/hashicorp/packer-plugin-sdk/packer"
|
||||
@ -12,16 +13,16 @@ 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)
|
||||
c := state.Get("client").(*xen.Connection)
|
||||
instance_uuid := state.Get("instance_uuid").(string)
|
||||
|
||||
instance, err := c.client.VM.GetByUUID(c.session, instance_uuid)
|
||||
instance, err := c.GetClient().VM.GetByUUID(c.GetSessionRef(), 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)
|
||||
err = c.GetClient().VM.SetIsATemplate(c.GetSessionRef(), instance, true)
|
||||
|
||||
if err != nil {
|
||||
ui.Error(fmt.Sprintf("failed to set VM '%s' as a template with error: %v", instance_uuid, err))
|
@ -1,8 +1,12 @@
|
||||
package common
|
||||
package steps
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
config2 "github.com/xenserver/packer-builder-xenserver/builder/xenserver/common/config"
|
||||
"github.com/xenserver/packer-builder-xenserver/builder/xenserver/common/util"
|
||||
"github.com/xenserver/packer-builder-xenserver/builder/xenserver/common/xen"
|
||||
"log"
|
||||
"time"
|
||||
|
||||
"github.com/hashicorp/packer-plugin-sdk/multistep"
|
||||
@ -13,12 +17,12 @@ import (
|
||||
type StepShutdown struct{}
|
||||
|
||||
func (StepShutdown) Run(ctx context.Context, state multistep.StateBag) multistep.StepAction {
|
||||
config := state.Get("commonconfig").(CommonConfig)
|
||||
config := state.Get("commonconfig").(config2.CommonConfig)
|
||||
ui := state.Get("ui").(packer.Ui)
|
||||
c := state.Get("client").(*Connection)
|
||||
c := state.Get("client").(*xen.Connection)
|
||||
instance_uuid := state.Get("instance_uuid").(string)
|
||||
|
||||
instance, err := c.client.VM.GetByUUID(c.session, instance_uuid)
|
||||
instance, err := c.GetClient().VM.GetByUUID(c.GetSessionRef(), instance_uuid)
|
||||
if err != nil {
|
||||
ui.Error(fmt.Sprintf("Could not get VM with UUID '%s': %s", instance_uuid, err.Error()))
|
||||
return multistep.ActionHalt
|
||||
@ -29,23 +33,26 @@ func (StepShutdown) Run(ctx context.Context, state multistep.StateBag) multistep
|
||||
// Shutdown the VM
|
||||
success := func() bool {
|
||||
if config.ShutdownCommand != "" {
|
||||
ui.Message("Executing shutdown command...")
|
||||
comm := state.Get("communicator").(packer.Communicator)
|
||||
ui.Say("Gracefully halting virtual machine...")
|
||||
log.Printf("Executing shutdown command: %s", config.ShutdownCommand)
|
||||
|
||||
_, err := ExecuteGuestSSHCmd(state, config.ShutdownCommand)
|
||||
if err != nil {
|
||||
ui.Error(fmt.Sprintf("Shutdown command failed: %s", err.Error()))
|
||||
cmd := &packer.RemoteCmd{Command: config.ShutdownCommand}
|
||||
if err := cmd.RunWithUi(ctx, comm, ui); err != nil {
|
||||
ui.Error(fmt.Sprintf("Failed to send shutdown command: %s", err.Error()))
|
||||
return false
|
||||
}
|
||||
|
||||
ui.Message("Waiting for VM to enter Halted state...")
|
||||
ui.Message(fmt.Sprintf("Waiting for VM to enter Halted state... Timeout after %s",
|
||||
config.ShutdownTimeout.String()))
|
||||
|
||||
err = InterruptibleWait{
|
||||
err = util.InterruptibleWait{
|
||||
Predicate: func() (bool, error) {
|
||||
power_state, err := c.client.VM.GetPowerState(c.session, instance)
|
||||
power_state, err := c.GetClient().VM.GetPowerState(c.GetSessionRef(), instance)
|
||||
return power_state == xenapi.VMPowerStateHalted, err
|
||||
},
|
||||
PredicateInterval: 5 * time.Second,
|
||||
Timeout: 300 * time.Second,
|
||||
Timeout: config.ShutdownTimeout,
|
||||
}.Wait(state)
|
||||
|
||||
if err != nil {
|
||||
@ -56,7 +63,7 @@ func (StepShutdown) Run(ctx context.Context, state multistep.StateBag) multistep
|
||||
} else {
|
||||
ui.Message("Attempting to cleanly shutdown the VM...")
|
||||
|
||||
err = c.client.VM.CleanShutdown(c.session, instance)
|
||||
err = c.GetClient().VM.CleanShutdown(c.GetSessionRef(), instance)
|
||||
if err != nil {
|
||||
ui.Error(fmt.Sprintf("Could not shut down VM: %s", err.Error()))
|
||||
return false
|
||||
@ -68,7 +75,7 @@ func (StepShutdown) Run(ctx context.Context, state multistep.StateBag) multistep
|
||||
|
||||
if !success {
|
||||
ui.Say("WARNING: Forcing hard shutdown of the VM...")
|
||||
err = c.client.VM.HardShutdown(c.session, instance)
|
||||
err = c.GetClient().VM.HardShutdown(c.GetSessionRef(), instance)
|
||||
if err != nil {
|
||||
ui.Error(fmt.Sprintf("Could not hard shut down VM -- giving up: %s", err.Error()))
|
||||
return multistep.ActionHalt
|
@ -1,7 +1,11 @@
|
||||
package common
|
||||
package steps
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
config2 "github.com/xenserver/packer-builder-xenserver/builder/xenserver/common/config"
|
||||
"github.com/xenserver/packer-builder-xenserver/builder/xenserver/common/ssh"
|
||||
"github.com/xenserver/packer-builder-xenserver/builder/xenserver/common/util"
|
||||
"github.com/xenserver/packer-builder-xenserver/builder/xenserver/common/xen"
|
||||
"log"
|
||||
"time"
|
||||
|
||||
@ -10,6 +14,7 @@ import (
|
||||
gossh "golang.org/x/crypto/ssh"
|
||||
)
|
||||
|
||||
// TODO Seems to be unused
|
||||
type StepStartOnHIMN struct{}
|
||||
|
||||
/*
|
||||
@ -23,19 +28,19 @@ type StepStartOnHIMN struct{}
|
||||
func (self *StepStartOnHIMN) Run(state multistep.StateBag) multistep.StepAction {
|
||||
|
||||
ui := state.Get("ui").(packer.Ui)
|
||||
c := state.Get("client").(*Connection)
|
||||
c := state.Get("client").(*xen.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)
|
||||
instance, err := c.GetClient().VM.GetByUUID(c.GetSessionRef(), 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")
|
||||
networks, err := c.GetClient().Network.GetByNameLabel(c.GetSessionRef(), "Host internal management network")
|
||||
if err != nil || len(networks) == 0 {
|
||||
ui.Error("Unable to find a host internal management network")
|
||||
ui.Error(err.Error())
|
||||
@ -45,7 +50,7 @@ func (self *StepStartOnHIMN) Run(state multistep.StateBag) multistep.StepAction
|
||||
himn := networks[0]
|
||||
|
||||
// Create a VIF for the HIMN
|
||||
himn_vif, err := ConnectNetwork(c, himn, instance, "0")
|
||||
himn_vif, err := xen.ConnectNetwork(c, himn, instance, "0")
|
||||
if err != nil {
|
||||
ui.Error("Error creating VIF")
|
||||
ui.Error(err.Error())
|
||||
@ -53,14 +58,14 @@ func (self *StepStartOnHIMN) Run(state multistep.StateBag) multistep.StepAction
|
||||
}
|
||||
|
||||
// Start the VM
|
||||
c.client.VM.Start(c.session, instance, false, false)
|
||||
c.GetClient().VM.Start(c.GetSessionRef(), instance, false, false)
|
||||
|
||||
var himn_iface_ip string = ""
|
||||
|
||||
// Obtain the allocated IP
|
||||
err = InterruptibleWait{
|
||||
err = util.InterruptibleWait{
|
||||
Predicate: func() (found bool, err error) {
|
||||
ips, err := c.client.Network.GetAssignedIps(c.session, himn)
|
||||
ips, err := c.GetClient().Network.GetAssignedIps(c.GetSessionRef(), himn)
|
||||
if err != nil {
|
||||
return false, fmt.Errorf("Can't get assigned IPs: %s", err.Error())
|
||||
}
|
||||
@ -93,10 +98,10 @@ func (self *StepStartOnHIMN) Run(state multistep.StateBag) multistep.StepAction
|
||||
|
||||
ping_cmd := fmt.Sprintf("ping -c 1 %s", himn_iface_ip)
|
||||
|
||||
err = InterruptibleWait{
|
||||
err = util.InterruptibleWait{
|
||||
Predicate: func() (success bool, err error) {
|
||||
ui.Message(fmt.Sprintf("Attempting to ping interface: %s", ping_cmd))
|
||||
_, err = ExecuteHostSSHCmd(state, ping_cmd)
|
||||
_, err = ssh.ExecuteHostSSHCmd(state, ping_cmd)
|
||||
|
||||
switch err.(type) {
|
||||
case nil:
|
||||
@ -131,7 +136,7 @@ func HimnSSHIP(state multistep.StateBag) (string, error) {
|
||||
return ip, nil
|
||||
}
|
||||
|
||||
func HimnSSHPort(state multistep.StateBag) (uint, error) {
|
||||
config := state.Get("commonconfig").(CommonConfig)
|
||||
return config.SSHPort, nil
|
||||
func HimnSSHPort(state multistep.StateBag) (int, error) {
|
||||
config := state.Get("commonconfig").(config2.CommonConfig)
|
||||
return config.Comm.SSHPort, nil
|
||||
}
|
@ -1,8 +1,10 @@
|
||||
package common
|
||||
package steps
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
config2 "github.com/xenserver/packer-builder-xenserver/builder/xenserver/common/config"
|
||||
"github.com/xenserver/packer-builder-xenserver/builder/xenserver/common/xen"
|
||||
|
||||
"github.com/hashicorp/packer-plugin-sdk/multistep"
|
||||
"github.com/hashicorp/packer-plugin-sdk/packer"
|
||||
@ -14,39 +16,40 @@ type StepStartVmPaused struct {
|
||||
|
||||
func (self *StepStartVmPaused) Run(ctx context.Context, state multistep.StateBag) multistep.StepAction {
|
||||
|
||||
c := state.Get("client").(*Connection)
|
||||
c := state.Get("client").(*xen.Connection)
|
||||
ui := state.Get("ui").(packer.Ui)
|
||||
config := state.Get("config").(config2.CommonConfig)
|
||||
|
||||
ui.Say("Step: Start VM Paused")
|
||||
|
||||
uuid := state.Get("instance_uuid").(string)
|
||||
instance, err := c.client.VM.GetByUUID(c.session, uuid)
|
||||
instance, err := c.GetClient().VM.GetByUUID(c.GetSessionRef(), 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")
|
||||
err = c.GetClient().VM.SetHVMBootPolicy(c.GetSessionRef(), 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"})
|
||||
err = c.GetClient().VM.SetHVMBootParams(c.GetSessionRef(), 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)
|
||||
err = c.GetClient().VM.Start(c.GetSessionRef(), 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)
|
||||
domid, err := c.GetClient().VM.GetDomid(c.GetSessionRef(), instance)
|
||||
if err != nil {
|
||||
ui.Error(fmt.Sprintf("Unable to get domid of VM with UUID '%s': %s", uuid, err.Error()))
|
||||
return multistep.ActionHalt
|
110
builder/xenserver/common/steps/step_type_boot_command.go
Normal file
110
builder/xenserver/common/steps/step_type_boot_command.go
Normal file
@ -0,0 +1,110 @@
|
||||
package steps
|
||||
|
||||
/*
|
||||
Heavily borrowed from builder/quemu/step_type_boot_command.go
|
||||
*/
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"github.com/hashicorp/packer-plugin-sdk/bootcommand"
|
||||
"github.com/hashicorp/packer-plugin-sdk/multistep"
|
||||
"github.com/hashicorp/packer-plugin-sdk/packer"
|
||||
"github.com/hashicorp/packer-plugin-sdk/template/interpolate"
|
||||
config2 "github.com/xenserver/packer-builder-xenserver/builder/xenserver/common/config"
|
||||
"github.com/xenserver/packer-builder-xenserver/builder/xenserver/common/vnc"
|
||||
"log"
|
||||
)
|
||||
|
||||
type bootCommandTemplateData struct {
|
||||
Name string
|
||||
HTTPIP string
|
||||
HTTPPort uint
|
||||
}
|
||||
|
||||
type StepTypeBootCommand struct {
|
||||
Ctx interpolate.Context
|
||||
}
|
||||
|
||||
func (self *StepTypeBootCommand) Run(ctx context.Context, state multistep.StateBag) multistep.StepAction {
|
||||
config := state.Get("commonconfig").(config2.CommonConfig)
|
||||
ui := state.Get("ui").(packer.Ui)
|
||||
httpPort := state.Get("http_port").(int)
|
||||
|
||||
if config.VNCConfig.DisableVNC {
|
||||
return multistep.ActionContinue
|
||||
}
|
||||
|
||||
// skip this step if we have nothing to type
|
||||
if len(config.BootCommand) == 0 {
|
||||
return multistep.ActionContinue
|
||||
}
|
||||
|
||||
var httpIP string
|
||||
if config.HTTPAddress != "0.0.0.0" {
|
||||
httpIP = config.HTTPAddress
|
||||
} else {
|
||||
httpIP = state.Get("http_ip").(string)
|
||||
}
|
||||
|
||||
location, err := vnc.GetVNCConsoleLocation(state)
|
||||
if err != nil {
|
||||
state.Put("error", err)
|
||||
ui.Error(err.Error())
|
||||
return multistep.ActionHalt
|
||||
}
|
||||
|
||||
ui.Say(fmt.Sprintf("Connecting to the VM console VNC over xapi via %s", location))
|
||||
|
||||
vncClient, err := vnc.CreateVNCClient(state, location)
|
||||
|
||||
if err != nil {
|
||||
ui.Error(err.Error())
|
||||
return multistep.ActionHalt
|
||||
}
|
||||
|
||||
defer vncClient.Close()
|
||||
|
||||
log.Printf("Connected to the VNC console: %s", vncClient.DesktopName)
|
||||
|
||||
self.Ctx.Data = &bootCommandTemplateData{
|
||||
config.VMName,
|
||||
httpIP,
|
||||
uint(httpPort),
|
||||
}
|
||||
|
||||
vncDriver := bootcommand.NewVNCDriver(vncClient, config.VNCConfig.BootKeyInterval)
|
||||
|
||||
ui.Say("Typing boot commands over VNC...")
|
||||
|
||||
command, err := interpolate.Render(config.VNCConfig.FlatBootCommand(), &self.Ctx)
|
||||
|
||||
if err != nil {
|
||||
err := fmt.Errorf("Error preparing boot command: %s", err)
|
||||
state.Put("error", err)
|
||||
ui.Error(err.Error())
|
||||
return multistep.ActionHalt
|
||||
}
|
||||
|
||||
seq, err := bootcommand.GenerateExpressionSequence(command)
|
||||
|
||||
if err != nil {
|
||||
err := fmt.Errorf("Error generating boot command: %s", err)
|
||||
state.Put("error", err)
|
||||
ui.Error(err.Error())
|
||||
return multistep.ActionHalt
|
||||
}
|
||||
|
||||
if err := seq.Do(ctx, vncDriver); err != nil {
|
||||
err := fmt.Errorf("Error running boot command: %s", err)
|
||||
state.Put("error", err)
|
||||
ui.Error(err.Error())
|
||||
return multistep.ActionHalt
|
||||
}
|
||||
|
||||
ui.Say("Finished typing.")
|
||||
|
||||
return multistep.ActionContinue
|
||||
}
|
||||
|
||||
func (self *StepTypeBootCommand) Cleanup(multistep.StateBag) {}
|
@ -1,10 +1,14 @@
|
||||
package common
|
||||
package steps
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
config2 "github.com/xenserver/packer-builder-xenserver/builder/xenserver/common/config"
|
||||
"github.com/xenserver/packer-builder-xenserver/builder/xenserver/common/xen"
|
||||
"log"
|
||||
"net"
|
||||
"os"
|
||||
"strconv"
|
||||
"time"
|
||||
|
||||
"github.com/hashicorp/packer-plugin-sdk/multistep"
|
||||
@ -19,9 +23,9 @@ type StepUploadVdi struct {
|
||||
}
|
||||
|
||||
func (self *StepUploadVdi) Run(ctx context.Context, state multistep.StateBag) multistep.StepAction {
|
||||
config := state.Get("commonconfig").(CommonConfig)
|
||||
config := state.Get("commonconfig").(config2.CommonConfig)
|
||||
ui := state.Get("ui").(packer.Ui)
|
||||
c := state.Get("client").(*Connection)
|
||||
c := state.Get("client").(*xen.Connection)
|
||||
|
||||
imagePath := self.ImagePathFunc()
|
||||
vdiName := self.VdiNameFunc()
|
||||
@ -33,7 +37,7 @@ func (self *StepUploadVdi) Run(ctx context.Context, state multistep.StateBag) mu
|
||||
ui.Say(fmt.Sprintf("Step: Upload VDI '%s'", vdiName))
|
||||
|
||||
// Create VDI for the image
|
||||
srs, err := c.client.SR.GetAll(c.session)
|
||||
srs, err := c.GetClient().SR.GetAll(c.GetSessionRef())
|
||||
ui.Say(fmt.Sprintf("Step: Found SRs '%v'", srs))
|
||||
|
||||
sr, err := config.GetISOSR(c)
|
||||
@ -60,7 +64,7 @@ func (self *StepUploadVdi) Run(ctx context.Context, state multistep.StateBag) mu
|
||||
|
||||
// Create the VDI
|
||||
// vdi, err := sr.CreateVdi(vdiName, fileLength)
|
||||
vdi, err := c.client.VDI.Create(c.session, xenapi.VDIRecord{
|
||||
vdi, err := c.GetClient().VDI.Create(c.GetSessionRef(), xenapi.VDIRecord{
|
||||
NameLabel: vdiName,
|
||||
VirtualSize: int(fileLength),
|
||||
Type: "user",
|
||||
@ -76,15 +80,15 @@ func (self *StepUploadVdi) Run(ctx context.Context, state multistep.StateBag) mu
|
||||
return multistep.ActionHalt
|
||||
}
|
||||
|
||||
vdiUuid, err := c.client.VDI.GetUUID(c.session, vdi)
|
||||
vdiUuid, err := c.GetClient().VDI.GetUUID(c.GetSessionRef(), 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,
|
||||
_, err = xen.HTTPUpload(fmt.Sprintf("https://%s/import_raw_vdi?vdi=%s&session_id=%s",
|
||||
net.JoinHostPort(c.Host, strconv.Itoa(c.Port)),
|
||||
vdi,
|
||||
c.GetSession(),
|
||||
), fh, state)
|
||||
@ -97,9 +101,9 @@ func (self *StepUploadVdi) Run(ctx context.Context, state multistep.StateBag) mu
|
||||
}
|
||||
|
||||
func (self *StepUploadVdi) Cleanup(state multistep.StateBag) {
|
||||
config := state.Get("commonconfig").(CommonConfig)
|
||||
config := state.Get("commonconfig").(config2.CommonConfig)
|
||||
ui := state.Get("ui").(packer.Ui)
|
||||
c := state.Get("client").(*Connection)
|
||||
c := state.Get("client").(*xen.Connection)
|
||||
|
||||
vdiName := self.VdiNameFunc()
|
||||
|
||||
@ -119,7 +123,7 @@ func (self *StepUploadVdi) Cleanup(state multistep.StateBag) {
|
||||
return
|
||||
}
|
||||
|
||||
vdi, err := c.client.VDI.GetByUUID(c.session, vdiUuid)
|
||||
vdi, err := c.GetClient().VDI.GetByUUID(c.GetSessionRef(), vdiUuid)
|
||||
if err != nil {
|
||||
ui.Error(fmt.Sprintf("Can't get VDI '%s': %s", vdiUuid, err.Error()))
|
||||
return
|
||||
@ -129,7 +133,7 @@ func (self *StepUploadVdi) Cleanup(state multistep.StateBag) {
|
||||
// so try several times
|
||||
for i := 0; i < 3; i++ {
|
||||
log.Printf("Trying to destroy VDI...")
|
||||
err = c.client.VDI.Destroy(c.session, vdi)
|
||||
err = c.GetClient().VDI.Destroy(c.GetSessionRef(), vdi)
|
||||
if err == nil {
|
||||
break
|
||||
}
|
@ -1,8 +1,11 @@
|
||||
package common
|
||||
package steps
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
config2 "github.com/xenserver/packer-builder-xenserver/builder/xenserver/common/config"
|
||||
"github.com/xenserver/packer-builder-xenserver/builder/xenserver/common/util"
|
||||
"github.com/xenserver/packer-builder-xenserver/builder/xenserver/common/xen"
|
||||
"time"
|
||||
|
||||
"github.com/hashicorp/packer-plugin-sdk/multistep"
|
||||
@ -17,20 +20,26 @@ type StepWaitForIP struct {
|
||||
|
||||
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)
|
||||
c := state.Get("client").(*xen.Connection)
|
||||
config := state.Get("commonconfig").(config2.CommonConfig)
|
||||
|
||||
// Respect static configuration
|
||||
if config.Comm.Host() != "" {
|
||||
state.Put("instance_ssh_address", config.Comm.Host())
|
||||
return multistep.ActionContinue
|
||||
}
|
||||
|
||||
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)
|
||||
instance, err := c.GetClient().VM.GetByUUID(c.GetSessionRef(), 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{
|
||||
err = util.InterruptibleWait{
|
||||
Timeout: self.Timeout,
|
||||
PredicateInterval: 5 * time.Second,
|
||||
Predicate: func() (result bool, err error) {
|
||||
@ -50,12 +59,12 @@ func (self *StepWaitForIP) Run(ctx context.Context, state multistep.StateBag) mu
|
||||
if config.IPGetter == "auto" || config.IPGetter == "tools" {
|
||||
|
||||
// Look for PV IP
|
||||
m, err := c.client.VM.GetGuestMetrics(c.session, instance)
|
||||
m, err := c.GetClient().VM.GetGuestMetrics(c.GetSessionRef(), instance)
|
||||
if err != nil {
|
||||
return false, err
|
||||
}
|
||||
if m != "" {
|
||||
metrics, err := c.client.VMGuestMetrics.GetRecord(c.session, m)
|
||||
metrics, err := c.GetClient().VMGuestMetrics.GetRecord(c.GetSessionRef(), m)
|
||||
if err != nil {
|
||||
return false, err
|
||||
}
|
||||
@ -85,11 +94,12 @@ func (self *StepWaitForIP) Run(ctx context.Context, state multistep.StateBag) mu
|
||||
return multistep.ActionContinue
|
||||
}
|
||||
|
||||
func InstanceSSHIP(state multistep.StateBag) (string, error) {
|
||||
func InstanceCommIP(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
|
||||
func InstanceCommPort(state multistep.StateBag) (int, error) {
|
||||
config := state.Get("commonconfig").(config2.CommonConfig)
|
||||
return config.Comm.Port(), nil
|
||||
}
|
@ -1,7 +1,9 @@
|
||||
package common
|
||||
package steps
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
config2 "github.com/xenserver/packer-builder-xenserver/builder/xenserver/common/config"
|
||||
"github.com/xenserver/packer-builder-xenserver/builder/xenserver/common/xen"
|
||||
"log"
|
||||
|
||||
"github.com/hashicorp/packer-plugin-sdk/multistep"
|
||||
@ -10,21 +12,21 @@ import (
|
||||
type VmCleanup struct{}
|
||||
|
||||
func (self *VmCleanup) Cleanup(state multistep.StateBag) {
|
||||
config := state.Get("commonconfig").(CommonConfig)
|
||||
c := state.Get("client").(*Connection)
|
||||
config := state.Get("commonconfig").(config2.CommonConfig)
|
||||
c := state.Get("client").(*xen.Connection)
|
||||
|
||||
if config.ShouldKeepVM(state) {
|
||||
return
|
||||
}
|
||||
|
||||
uuid := state.Get("instance_uuid").(string)
|
||||
instance, err := c.client.VM.GetByUUID(c.session, uuid)
|
||||
instance, err := c.GetClient().VM.GetByUUID(c.GetSessionRef(), 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)
|
||||
err = c.GetClient().VM.HardShutdown(c.GetSessionRef(), instance)
|
||||
if err != nil {
|
||||
log.Printf(fmt.Sprintf("Unable to force shutdown VM '%s': %s", uuid, err.Error()))
|
||||
}
|
@ -1,4 +1,4 @@
|
||||
package common
|
||||
package util
|
||||
|
||||
import (
|
||||
"github.com/hashicorp/packer-plugin-sdk/multistep"
|
173
builder/xenserver/common/vnc/vnc.go
Normal file
173
builder/xenserver/common/vnc/vnc.go
Normal file
@ -0,0 +1,173 @@
|
||||
package vnc
|
||||
|
||||
import (
|
||||
"crypto/tls"
|
||||
"errors"
|
||||
"fmt"
|
||||
"github.com/hashicorp/packer-plugin-sdk/multistep"
|
||||
"github.com/mitchellh/go-vnc"
|
||||
config2 "github.com/xenserver/packer-builder-xenserver/builder/xenserver/common/config"
|
||||
"github.com/xenserver/packer-builder-xenserver/builder/xenserver/common/proxy"
|
||||
"github.com/xenserver/packer-builder-xenserver/builder/xenserver/common/xen"
|
||||
"io"
|
||||
"log"
|
||||
"net"
|
||||
"net/http"
|
||||
"net/url"
|
||||
"strings"
|
||||
)
|
||||
|
||||
func GetVNCConsoleLocation(state multistep.StateBag) (string, error) {
|
||||
xenClient := state.Get("client").(*xen.Connection)
|
||||
config := state.Get("commonconfig").(config2.CommonConfig)
|
||||
|
||||
vmRef, err := xenClient.GetClient().VM.GetByNameLabel(xenClient.GetSessionRef(), config.VMName)
|
||||
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
|
||||
if len(vmRef) != 1 {
|
||||
return "", fmt.Errorf("expected to find a single VM, instead found '%d'. Ensure the VM name is unique", len(vmRef))
|
||||
}
|
||||
|
||||
consoles, err := xenClient.GetClient().VM.GetConsoles(xenClient.GetSessionRef(), vmRef[0])
|
||||
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
|
||||
if len(consoles) != 1 {
|
||||
return "", fmt.Errorf("expected to find a VM console, instead found '%d'. Ensure there is only one console", len(consoles))
|
||||
}
|
||||
|
||||
location, err := xenClient.GetClient().Console.GetLocation(xenClient.GetSessionRef(), consoles[0])
|
||||
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
|
||||
return location, nil
|
||||
}
|
||||
|
||||
func CreateVNCConnection(state multistep.StateBag, location string) (net.Conn, error) {
|
||||
xenClient := state.Get("client").(*xen.Connection)
|
||||
xenProxy := state.Get("xen_proxy").(proxy.XenProxy)
|
||||
|
||||
target, err := GetTcpAddressFromURL(location)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
rawConn, err := xenProxy.ConnectWithAddr(target)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
tlsConn, err := InitializeVNCConnection(location, string(xenClient.GetSessionRef()), rawConn)
|
||||
if err != nil {
|
||||
rawConn.Close()
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return tlsConn, nil
|
||||
}
|
||||
|
||||
func CreateVNCClient(state multistep.StateBag, location string) (*vnc.ClientConn, error) {
|
||||
var err error
|
||||
|
||||
connection, err := CreateVNCConnection(state, location)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
client, err := vnc.Client(connection, &vnc.ClientConfig{
|
||||
Exclusive: true,
|
||||
})
|
||||
if err != nil {
|
||||
connection.Close()
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return client, nil
|
||||
}
|
||||
|
||||
func InitializeVNCConnection(location string, xenSessionRef string, rawConn net.Conn) (*tls.Conn, error) {
|
||||
tlsConfig := &tls.Config{
|
||||
InsecureSkipVerify: true,
|
||||
}
|
||||
|
||||
tlsConnection := tls.Client(rawConn, tlsConfig)
|
||||
|
||||
request, err := http.NewRequest(http.MethodConnect, location, http.NoBody)
|
||||
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("could not connect to xen console api: %w", err)
|
||||
}
|
||||
|
||||
request.Close = false
|
||||
request.AddCookie(&http.Cookie{
|
||||
Name: "session_id",
|
||||
Value: xenSessionRef,
|
||||
})
|
||||
|
||||
err = request.Write(tlsConnection)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("could not write to tls connection: %w", err)
|
||||
}
|
||||
|
||||
// Look for \r\n\r\n sequence. Everything after the HTTP Header is for the vnc client.
|
||||
response, err := readHttpHeader(tlsConnection)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("could not read http header: %w", err)
|
||||
}
|
||||
|
||||
log.Printf("Received response: %s", response)
|
||||
return tlsConnection, nil
|
||||
}
|
||||
|
||||
func GetTcpAddressFromURL(location string) (string, error) {
|
||||
parsedUrl, err := url.Parse(location)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
|
||||
var target string
|
||||
if parsedUrl.Port() != "" {
|
||||
target = parsedUrl.Host
|
||||
} else if parsedUrl.Scheme == "http" {
|
||||
target = parsedUrl.Host + ":80"
|
||||
} else if parsedUrl.Scheme == "https" {
|
||||
target = parsedUrl.Host + ":443"
|
||||
} else {
|
||||
return "", errors.New("unsupported protocol")
|
||||
}
|
||||
return target, nil
|
||||
}
|
||||
|
||||
func readHttpHeader(tlsConn *tls.Conn) (string, error) {
|
||||
builder := strings.Builder{}
|
||||
buffer := make([]byte, 1)
|
||||
sequenceProgress := 0
|
||||
|
||||
for {
|
||||
if _, err := io.ReadFull(tlsConn, buffer); err != nil {
|
||||
return "", fmt.Errorf("failed to start vnc session: %w", err)
|
||||
}
|
||||
|
||||
builder.WriteByte(buffer[0])
|
||||
|
||||
if buffer[0] == '\n' && sequenceProgress%2 == 1 {
|
||||
sequenceProgress++
|
||||
} else if buffer[0] == '\r' && sequenceProgress%2 == 0 {
|
||||
sequenceProgress++
|
||||
} else {
|
||||
sequenceProgress = 0
|
||||
}
|
||||
|
||||
if sequenceProgress == 4 {
|
||||
break
|
||||
}
|
||||
}
|
||||
return builder.String(), nil
|
||||
}
|
284
builder/xenserver/common/xen/client.go
Normal file
284
builder/xenserver/common/xen/client.go
Normal file
@ -0,0 +1,284 @@
|
||||
package xen
|
||||
|
||||
import (
|
||||
"encoding/xml"
|
||||
"errors"
|
||||
"fmt"
|
||||
"log"
|
||||
"net"
|
||||
"strconv"
|
||||
|
||||
xenapi "github.com/terra-farm/go-xen-api-client"
|
||||
)
|
||||
|
||||
func Unpause(c *Connection, vmRef xenapi.VMRef) (err error) {
|
||||
err = c.client.VM.Unpause(c.session, vmRef)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
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 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 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
|
||||
}
|
||||
|
||||
// VDI associated functions
|
||||
// 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
|
||||
}
|
||||
|
||||
// Client Initiator
|
||||
type Connection struct {
|
||||
client *xenapi.Client
|
||||
session xenapi.SessionRef
|
||||
Host string
|
||||
Port int
|
||||
Username string
|
||||
Password string
|
||||
}
|
||||
|
||||
func (c Connection) GetSession() string {
|
||||
return string(c.session)
|
||||
}
|
||||
|
||||
func NewXenAPIClient(host string, port int, username, password string) (*Connection, error) {
|
||||
url := fmt.Sprintf("https://%s/", net.JoinHostPort(host, strconv.Itoa(port)))
|
||||
client, err := xenapi.NewClient(url, 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, port, username, password}, nil
|
||||
}
|
||||
|
||||
func (c *Connection) GetClient() *xenapi.Client {
|
||||
return c.client
|
||||
}
|
||||
|
||||
func (c *Connection) GetSessionRef() xenapi.SessionRef {
|
||||
return c.session
|
||||
}
|
@ -1,8 +1,9 @@
|
||||
package common
|
||||
package xen
|
||||
|
||||
import (
|
||||
"crypto/tls"
|
||||
"fmt"
|
||||
"github.com/xenserver/packer-builder-xenserver/builder/xenserver/common/util"
|
||||
"log"
|
||||
"net/http"
|
||||
"net/url"
|
||||
@ -29,12 +30,12 @@ func HTTPUpload(import_url string, fh *os.File, state multistep.StateBag) (resul
|
||||
ui := state.Get("ui").(packer.Ui)
|
||||
c := state.Get("client").(*Connection)
|
||||
|
||||
task, err := c.client.Task.Create(c.session, "packer-task", "Packer task")
|
||||
task, err := c.GetClient().Task.Create(c.GetSessionRef(), "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)
|
||||
defer c.GetClient().Task.Destroy(c.GetSessionRef(), task)
|
||||
|
||||
import_task_url, err := appendQuery(import_url, "task_id", string(task))
|
||||
if err != nil {
|
||||
@ -74,15 +75,15 @@ func HTTPUpload(import_url string, fh *os.File, state multistep.StateBag) (resul
|
||||
}
|
||||
|
||||
logIteration := 0
|
||||
err = InterruptibleWait{
|
||||
err = util.InterruptibleWait{
|
||||
Predicate: func() (bool, error) {
|
||||
status, err := c.client.Task.GetStatus(c.session, task)
|
||||
status, err := c.GetClient().Task.GetStatus(c.GetSessionRef(), 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)
|
||||
progress, err := c.GetClient().Task.GetProgress(c.GetSessionRef(), task)
|
||||
if err != nil {
|
||||
return false, fmt.Errorf("Failed to get progress: %s", err.Error())
|
||||
}
|
||||
@ -94,7 +95,7 @@ func HTTPUpload(import_url string, fh *os.File, state multistep.StateBag) (resul
|
||||
case xsclient.TaskStatusTypeSuccess:
|
||||
return true, nil
|
||||
case xsclient.TaskStatusTypeFailure:
|
||||
errorInfo, err := c.client.Task.GetErrorInfo(c.session, task)
|
||||
errorInfo, err := c.GetClient().Task.GetErrorInfo(c.GetSessionRef(), task)
|
||||
if err != nil {
|
||||
errorInfo = []string{fmt.Sprintf("furthermore, failed to get error info: %s", err.Error())}
|
||||
}
|
||||
@ -116,7 +117,7 @@ func HTTPUpload(import_url string, fh *os.File, state multistep.StateBag) (resul
|
||||
return
|
||||
}
|
||||
|
||||
result, err = c.client.Task.GetResult(c.session, task)
|
||||
result, err = c.GetClient().Task.GetResult(c.GetSessionRef(), task)
|
||||
if err != nil {
|
||||
err = fmt.Errorf("Error getting result: %s", err.Error())
|
||||
return
|
@ -3,159 +3,32 @@ package iso
|
||||
import (
|
||||
"context"
|
||||
"errors"
|
||||
"fmt"
|
||||
artifact2 "github.com/xenserver/packer-builder-xenserver/builder/xenserver/common/artifact"
|
||||
steps2 "github.com/xenserver/packer-builder-xenserver/builder/xenserver/common/steps"
|
||||
"github.com/xenserver/packer-builder-xenserver/builder/xenserver/common/xen"
|
||||
"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
|
||||
config 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 {
|
||||
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.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 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_checksum_type": &self.config.ISOChecksumType,
|
||||
"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.
|
||||
|
||||
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 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."))
|
||||
}
|
||||
} 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 (b *Builder) Prepare(raws ...interface{}) (params []string, warns []string, errors error) {
|
||||
return b.config.Prepare(raws...)
|
||||
}
|
||||
|
||||
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)
|
||||
c, err := xen.NewXenAPIClient(self.config.HostIp, self.config.HostPort, self.config.Username, self.config.Password)
|
||||
|
||||
if err != nil {
|
||||
return nil, err
|
||||
@ -185,17 +58,16 @@ func (self *Builder) Run(ctx context.Context, ui packer.Ui, hook packer.Hook) (p
|
||||
}
|
||||
|
||||
steps := []multistep.Step{
|
||||
&xscommon.StepPrepareOutputDir{
|
||||
&steps2.StepPrepareOutputDir{
|
||||
Force: self.config.PackerForce,
|
||||
Path: self.config.OutputDir,
|
||||
},
|
||||
&commonsteps.StepCreateFloppy{
|
||||
Files: self.config.FloppyFiles,
|
||||
Files: self.config.FloppyFiles,
|
||||
Directories: self.config.FloppyDirectories,
|
||||
Label: self.config.FloppyLabel,
|
||||
},
|
||||
&xscommon.StepHTTPServer{
|
||||
Chan: httpReqChan,
|
||||
},
|
||||
&xscommon.StepUploadVdi{
|
||||
&steps2.StepUploadVdi{
|
||||
VdiNameFunc: func() string {
|
||||
return "Packer-floppy-disk"
|
||||
},
|
||||
@ -207,7 +79,7 @@ func (self *Builder) Run(ctx context.Context, ui packer.Ui, hook packer.Hook) (p
|
||||
},
|
||||
VdiUuidKey: "floppy_vdi_uuid",
|
||||
},
|
||||
&xscommon.StepUploadVdi{
|
||||
&steps2.StepUploadVdi{
|
||||
VdiNameFunc: func() string {
|
||||
if len(self.config.ISOUrls) > 0 {
|
||||
return path.Base(self.config.ISOUrls[0])
|
||||
@ -222,77 +94,84 @@ func (self *Builder) Run(ctx context.Context, ui packer.Ui, hook packer.Hook) (p
|
||||
},
|
||||
VdiUuidKey: "iso_vdi_uuid",
|
||||
},
|
||||
&xscommon.StepFindVdi{
|
||||
&steps2.StepFindVdi{
|
||||
VdiName: self.config.ToolsIsoName,
|
||||
VdiUuidKey: "tools_vdi_uuid",
|
||||
},
|
||||
&xscommon.StepFindVdi{
|
||||
&steps2.StepFindVdi{
|
||||
VdiName: self.config.ISOName,
|
||||
VdiUuidKey: "isoname_vdi_uuid",
|
||||
},
|
||||
new(stepCreateInstance),
|
||||
&xscommon.StepAttachVdi{
|
||||
&steps2.StepAttachVdi{
|
||||
VdiUuidKey: "floppy_vdi_uuid",
|
||||
VdiType: xsclient.VbdTypeFloppy,
|
||||
},
|
||||
&xscommon.StepAttachVdi{
|
||||
&steps2.StepAttachVdi{
|
||||
VdiUuidKey: "iso_vdi_uuid",
|
||||
VdiType: xsclient.VbdTypeCD,
|
||||
},
|
||||
&xscommon.StepAttachVdi{
|
||||
&steps2.StepAttachVdi{
|
||||
VdiUuidKey: "isoname_vdi_uuid",
|
||||
VdiType: xsclient.VbdTypeCD,
|
||||
},
|
||||
&xscommon.StepAttachVdi{
|
||||
&steps2.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{
|
||||
new(steps2.StepStartVmPaused),
|
||||
new(steps2.StepSetVmHostSshAddress),
|
||||
new(steps2.StepHTTPIPDiscover),
|
||||
&steps2.StepCreateProxy{},
|
||||
commonsteps.HTTPServerFromHTTPConfig(&self.config.HTTPConfig),
|
||||
new(steps2.StepBootWait),
|
||||
&steps2.StepTypeBootCommand{
|
||||
Ctx: *self.config.GetInterpContext(),
|
||||
},
|
||||
&xscommon.StepWaitForIP{
|
||||
/*
|
||||
VNC is only available after boot command because xenserver doesn't seem to support two vnc connections at the same time
|
||||
*/
|
||||
&steps2.StepGetVNCPort{},
|
||||
&steps2.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",
|
||||
},
|
||||
&steps2.StepCreateForwarding{Targets: []steps2.ForwardTarget{
|
||||
{
|
||||
Host: steps2.InstanceCommIP,
|
||||
Port: steps2.InstanceCommPort,
|
||||
Key: "local_comm_address",
|
||||
},
|
||||
}},
|
||||
&communicator.StepConnect{
|
||||
Config: &self.config.SSHConfig.Comm,
|
||||
Host: xscommon.InstanceSSHIP,
|
||||
Config: &self.config.Comm,
|
||||
Host: func(state multistep.StateBag) (string, error) {
|
||||
return steps2.GetForwardedHost(state, "local_comm_address")
|
||||
},
|
||||
SSHConfig: self.config.Comm.SSHConfigFunc(),
|
||||
SSHPort: xscommon.InstanceSSHPort,
|
||||
SSHPort: func(state multistep.StateBag) (int, error) {
|
||||
return steps2.GetForwardedPort(state, "local_comm_address")
|
||||
},
|
||||
WinRMPort: func(state multistep.StateBag) (int, error) {
|
||||
return steps2.GetForwardedPort(state, "local_comm_address")
|
||||
},
|
||||
},
|
||||
new(commonsteps.StepProvision),
|
||||
new(xscommon.StepShutdown),
|
||||
new(xscommon.StepSetVmToTemplate),
|
||||
&xscommon.StepDetachVdi{
|
||||
new(steps2.StepShutdown),
|
||||
new(steps2.StepSetVmToTemplate),
|
||||
&steps2.StepDetachVdi{
|
||||
VdiUuidKey: "iso_vdi_uuid",
|
||||
},
|
||||
&xscommon.StepDetachVdi{
|
||||
&steps2.StepDetachVdi{
|
||||
VdiUuidKey: "isoname_vdi_uuid",
|
||||
},
|
||||
&xscommon.StepDetachVdi{
|
||||
&steps2.StepDetachVdi{
|
||||
VdiUuidKey: "tools_vdi_uuid",
|
||||
},
|
||||
&xscommon.StepDetachVdi{
|
||||
&steps2.StepDetachVdi{
|
||||
VdiUuidKey: "floppy_vdi_uuid",
|
||||
},
|
||||
new(xscommon.StepExport),
|
||||
new(steps2.StepExport),
|
||||
}
|
||||
|
||||
if self.config.ISOName == "" {
|
||||
@ -314,7 +193,7 @@ func (self *Builder) Run(ctx context.Context, ui packer.Ui, hook packer.Hook) (p
|
||||
return nil, errors.New("Build was halted.")
|
||||
}
|
||||
|
||||
artifact, _ := xscommon.NewArtifact(self.config.OutputDir)
|
||||
artifact, _ := artifact2.NewArtifact(self.config.OutputDir)
|
||||
|
||||
return artifact, nil
|
||||
}
|
||||
|
@ -1,10 +1,11 @@
|
||||
package iso
|
||||
|
||||
import (
|
||||
"github.com/hashicorp/packer-plugin-sdk/packer"
|
||||
"reflect"
|
||||
"testing"
|
||||
|
||||
"github.com/hashicorp/packer-plugin-sdk/packer"
|
||||
"github.com/hashicorp/packer-plugin-sdk/common"
|
||||
)
|
||||
|
||||
func testConfig() map[string]interface{} {
|
||||
@ -19,7 +20,7 @@ func testConfig() map[string]interface{} {
|
||||
"shutdown_command": "yes",
|
||||
"ssh_username": "foo",
|
||||
|
||||
packer.BuildNameConfigKey: "foo",
|
||||
common.BuildNameConfigKey: "foo",
|
||||
}
|
||||
}
|
||||
|
||||
|
135
builder/xenserver/iso/config.go
Normal file
135
builder/xenserver/iso/config.go
Normal file
@ -0,0 +1,135 @@
|
||||
//go:generate packer-sdc mapstructure-to-hcl2 -type Config
|
||||
//go:generate packer-sdc struct-markdown
|
||||
|
||||
package iso
|
||||
|
||||
import (
|
||||
"errors"
|
||||
packersdk "github.com/hashicorp/packer-plugin-sdk/packer"
|
||||
xscommon "github.com/xenserver/packer-builder-xenserver/builder/xenserver/common/config"
|
||||
"strings"
|
||||
"time"
|
||||
)
|
||||
|
||||
type Config struct {
|
||||
xscommon.CommonConfig `mapstructure:",squash"`
|
||||
|
||||
/*
|
||||
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.
|
||||
*/
|
||||
ISOUrl string `mapstructure:"iso_url" required:"true"`
|
||||
|
||||
/*
|
||||
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 with `iso_checksum_type`, documented below.
|
||||
*/
|
||||
ISOChecksum string `mapstructure:"iso_checksum" required:"true"`
|
||||
|
||||
/*
|
||||
The type of the checksum specified in `iso_checksum`. Valid values are "none", "md5", "sha1", "sha256", or
|
||||
"sha512" currently. While "none" will skip checksumming, this is not recommended since ISO files are
|
||||
generally large and corruption does happen from time to time.
|
||||
*/
|
||||
ISOChecksumType string `mapstructure:"iso_checksum_type" required:"true"`
|
||||
|
||||
/*
|
||||
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.
|
||||
*/
|
||||
ISOUrls []string `mapstructure:"iso_urls"`
|
||||
|
||||
ISOName string `mapstructure:"iso_name"`
|
||||
|
||||
/*
|
||||
The size, in megabytes, of the hard disk to create for the VM. By default, this is 40000 (about 40 GB).
|
||||
*/
|
||||
DiskSize uint `mapstructure:"disk_size"`
|
||||
AdditionalDisks []uint `mapstructure:"additional_disks"`
|
||||
|
||||
/*
|
||||
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.
|
||||
*/
|
||||
CloneTemplate string `mapstructure:"clone_template"`
|
||||
VMOtherConfig map[string]string `mapstructure:"vm_other_config"`
|
||||
|
||||
/*
|
||||
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.
|
||||
*/
|
||||
InstallTimeout time.Duration `mapstructure:"install_timeout"`
|
||||
}
|
||||
|
||||
func (c *Config) Prepare(raws ...interface{}) ([]string, []string, error) {
|
||||
var errs *packersdk.MultiError
|
||||
|
||||
params, warnings, merrs := c.CommonConfig.Prepare(c, raws)
|
||||
if merrs != nil {
|
||||
errs = packersdk.MultiErrorAppend(errs, merrs)
|
||||
}
|
||||
|
||||
// Set default values
|
||||
|
||||
if c.InstallTimeout == 0 {
|
||||
c.InstallTimeout = 200 * time.Minute
|
||||
}
|
||||
|
||||
if c.DiskSize == 0 {
|
||||
c.DiskSize = 40000
|
||||
}
|
||||
|
||||
if c.CloneTemplate == "" {
|
||||
c.CloneTemplate = "Other install media"
|
||||
}
|
||||
|
||||
// Validation
|
||||
|
||||
if c.ISOName == "" {
|
||||
|
||||
// If ISO name is not specified, assume a URL and checksum has been provided.
|
||||
|
||||
if c.ISOChecksumType == "" {
|
||||
errs = packersdk.MultiErrorAppend(
|
||||
errs, errors.New("The iso_checksum_type must be specified."))
|
||||
} else {
|
||||
c.ISOChecksumType = strings.ToLower(c.ISOChecksumType)
|
||||
if c.ISOChecksumType != "none" {
|
||||
if c.ISOChecksum == "" {
|
||||
errs = packersdk.MultiErrorAppend(
|
||||
errs, errors.New("Due to the file size being large, an iso_checksum is required."))
|
||||
} else {
|
||||
c.ISOChecksum = strings.ToLower(c.ISOChecksum)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if len(c.ISOUrls) == 0 {
|
||||
if c.ISOUrl == "" {
|
||||
errs = packersdk.MultiErrorAppend(
|
||||
errs, errors.New("One of iso_url or iso_urls must be specified."))
|
||||
} else {
|
||||
c.ISOUrls = []string{c.ISOUrl}
|
||||
}
|
||||
} else if c.ISOUrl != "" {
|
||||
errs = packersdk.MultiErrorAppend(
|
||||
errs, errors.New("Only one of iso_url or iso_urls may be specified."))
|
||||
}
|
||||
} else {
|
||||
|
||||
// An ISO name has been provided. It should be attached from an available SR.
|
||||
|
||||
}
|
||||
|
||||
if errs != nil && len(errs.Errors) > 0 {
|
||||
return params, warnings, errs
|
||||
}
|
||||
|
||||
return params, warnings, nil
|
||||
}
|
247
builder/xenserver/iso/config.hcl2spec.go
Normal file
247
builder/xenserver/iso/config.hcl2spec.go
Normal file
@ -0,0 +1,247 @@
|
||||
// Code generated by "packer-sdc mapstructure-to-hcl2"; DO NOT EDIT.
|
||||
|
||||
package iso
|
||||
|
||||
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"`
|
||||
BootGroupInterval *string `mapstructure:"boot_keygroup_interval" cty:"boot_keygroup_interval" hcl:"boot_keygroup_interval"`
|
||||
BootWait *string `mapstructure:"boot_wait" cty:"boot_wait" hcl:"boot_wait"`
|
||||
BootCommand []string `mapstructure:"boot_command" cty:"boot_command" hcl:"boot_command"`
|
||||
DisableVNC *bool `mapstructure:"disable_vnc" cty:"disable_vnc" hcl:"disable_vnc"`
|
||||
BootKeyInterval *string `mapstructure:"boot_key_interval" cty:"boot_key_interval" hcl:"boot_key_interval"`
|
||||
HTTPDir *string `mapstructure:"http_directory" cty:"http_directory" hcl:"http_directory"`
|
||||
HTTPContent map[string]string `mapstructure:"http_content" cty:"http_content" hcl:"http_content"`
|
||||
HTTPPortMin *int `mapstructure:"http_port_min" cty:"http_port_min" hcl:"http_port_min"`
|
||||
HTTPPortMax *int `mapstructure:"http_port_max" cty:"http_port_max" hcl:"http_port_max"`
|
||||
HTTPAddress *string `mapstructure:"http_bind_address" cty:"http_bind_address" hcl:"http_bind_address"`
|
||||
HTTPInterface *string `mapstructure:"http_interface" undocumented:"true" cty:"http_interface" hcl:"http_interface"`
|
||||
Username *string `mapstructure:"remote_username" required:"true" cty:"remote_username" hcl:"remote_username"`
|
||||
Password *string `mapstructure:"remote_password" required:"true" cty:"remote_password" hcl:"remote_password"`
|
||||
HostIp *string `mapstructure:"remote_host" required:"true" cty:"remote_host" hcl:"remote_host"`
|
||||
HostPort *int `mapstructure:"remote_port" cty:"remote_port" hcl:"remote_port"`
|
||||
HostSSHPort *int `mapstructure:"remote_ssh_port" cty:"remote_ssh_port" hcl:"remote_ssh_port"`
|
||||
FloppyFiles []string `mapstructure:"floppy_files" cty:"floppy_files" hcl:"floppy_files"`
|
||||
FloppyDirectories []string `mapstructure:"floppy_dirs" cty:"floppy_dirs" hcl:"floppy_dirs"`
|
||||
FloppyLabel *string `mapstructure:"floppy_label" cty:"floppy_label" hcl:"floppy_label"`
|
||||
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" cty:"sr_iso_name" hcl:"sr_iso_name"`
|
||||
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"`
|
||||
PlatformArgs map[string]string `mapstructure:"platform_args" cty:"platform_args" hcl:"platform_args"`
|
||||
ShutdownCommand *string `mapstructure:"shutdown_command" required:"false" cty:"shutdown_command" hcl:"shutdown_command"`
|
||||
ShutdownTimeout *string `mapstructure:"shutdown_timeout" required:"false" cty:"shutdown_timeout" hcl:"shutdown_timeout"`
|
||||
ToolsIsoName *string `mapstructure:"tools_iso_name" cty:"tools_iso_name" hcl:"tools_iso_name"`
|
||||
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"`
|
||||
SSHPort *int `mapstructure:"ssh_port" cty:"ssh_port" hcl:"ssh_port"`
|
||||
SSHUsername *string `mapstructure:"ssh_username" cty:"ssh_username" hcl:"ssh_username"`
|
||||
SSHPassword *string `mapstructure:"ssh_password" cty:"ssh_password" hcl:"ssh_password"`
|
||||
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"`
|
||||
SkipNatMapping *bool `mapstructure:"skip_nat_mapping" required:"false" cty:"skip_nat_mapping" hcl:"skip_nat_mapping"`
|
||||
SSHKeyPath *string `mapstructure:"ssh_key_path" undocumented:"true" cty:"ssh_key_path" hcl:"ssh_key_path"`
|
||||
SSHSkipNatMapping *bool `mapstructure:"ssh_skip_nat_mapping" undocumented:"true" cty:"ssh_skip_nat_mapping" hcl:"ssh_skip_nat_mapping"`
|
||||
HostPortMin *int `mapstructure:"host_port_min" required:"false" undocumented:"true" cty:"host_port_min" hcl:"host_port_min"`
|
||||
HostPortMax *int `mapstructure:"host_port_max" required:"false" undocumented:"true" cty:"host_port_max" hcl:"host_port_max"`
|
||||
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"`
|
||||
Firmware *string `mapstructure:"firmware" cty:"firmware" hcl:"firmware"`
|
||||
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"`
|
||||
ISOUrl *string `mapstructure:"iso_url" required:"true" cty:"iso_url" hcl:"iso_url"`
|
||||
ISOChecksum *string `mapstructure:"iso_checksum" required:"true" cty:"iso_checksum" hcl:"iso_checksum"`
|
||||
ISOChecksumType *string `mapstructure:"iso_checksum_type" required:"true" cty:"iso_checksum_type" hcl:"iso_checksum_type"`
|
||||
ISOUrls []string `mapstructure:"iso_urls" cty:"iso_urls" hcl:"iso_urls"`
|
||||
ISOName *string `mapstructure:"iso_name" cty:"iso_name" hcl:"iso_name"`
|
||||
DiskSize *uint `mapstructure:"disk_size" cty:"disk_size" hcl:"disk_size"`
|
||||
AdditionalDisks []uint `mapstructure:"additional_disks" cty:"additional_disks" hcl:"additional_disks"`
|
||||
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"`
|
||||
InstallTimeout *string `mapstructure:"install_timeout" cty:"install_timeout" hcl:"install_timeout"`
|
||||
}
|
||||
|
||||
// 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},
|
||||
"boot_keygroup_interval": &hcldec.AttrSpec{Name: "boot_keygroup_interval", Type: cty.String, Required: false},
|
||||
"boot_wait": &hcldec.AttrSpec{Name: "boot_wait", Type: cty.String, Required: false},
|
||||
"boot_command": &hcldec.AttrSpec{Name: "boot_command", Type: cty.List(cty.String), Required: false},
|
||||
"disable_vnc": &hcldec.AttrSpec{Name: "disable_vnc", Type: cty.Bool, Required: false},
|
||||
"boot_key_interval": &hcldec.AttrSpec{Name: "boot_key_interval", Type: cty.String, Required: false},
|
||||
"http_directory": &hcldec.AttrSpec{Name: "http_directory", Type: cty.String, Required: false},
|
||||
"http_content": &hcldec.AttrSpec{Name: "http_content", Type: cty.Map(cty.String), Required: false},
|
||||
"http_port_min": &hcldec.AttrSpec{Name: "http_port_min", Type: cty.Number, Required: false},
|
||||
"http_port_max": &hcldec.AttrSpec{Name: "http_port_max", Type: cty.Number, Required: false},
|
||||
"http_bind_address": &hcldec.AttrSpec{Name: "http_bind_address", Type: cty.String, Required: false},
|
||||
"http_interface": &hcldec.AttrSpec{Name: "http_interface", Type: 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_port": &hcldec.AttrSpec{Name: "remote_port", Type: cty.Number, Required: false},
|
||||
"remote_ssh_port": &hcldec.AttrSpec{Name: "remote_ssh_port", Type: cty.Number, Required: false},
|
||||
"floppy_files": &hcldec.AttrSpec{Name: "floppy_files", Type: cty.List(cty.String), Required: false},
|
||||
"floppy_dirs": &hcldec.AttrSpec{Name: "floppy_dirs", Type: cty.List(cty.String), Required: false},
|
||||
"floppy_label": &hcldec.AttrSpec{Name: "floppy_label", Type: cty.String, 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},
|
||||
"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},
|
||||
"platform_args": &hcldec.AttrSpec{Name: "platform_args", Type: cty.Map(cty.String), Required: false},
|
||||
"shutdown_command": &hcldec.AttrSpec{Name: "shutdown_command", Type: cty.String, Required: false},
|
||||
"shutdown_timeout": &hcldec.AttrSpec{Name: "shutdown_timeout", Type: cty.String, Required: false},
|
||||
"tools_iso_name": &hcldec.AttrSpec{Name: "tools_iso_name", 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_port": &hcldec.AttrSpec{Name: "ssh_port", Type: cty.Number, Required: false},
|
||||
"ssh_username": &hcldec.AttrSpec{Name: "ssh_username", Type: cty.String, Required: false},
|
||||
"ssh_password": &hcldec.AttrSpec{Name: "ssh_password", 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},
|
||||
"skip_nat_mapping": &hcldec.AttrSpec{Name: "skip_nat_mapping", Type: cty.Bool, Required: false},
|
||||
"ssh_key_path": &hcldec.AttrSpec{Name: "ssh_key_path", Type: cty.String, Required: false},
|
||||
"ssh_skip_nat_mapping": &hcldec.AttrSpec{Name: "ssh_skip_nat_mapping", Type: cty.Bool, 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},
|
||||
"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},
|
||||
"firmware": &hcldec.AttrSpec{Name: "firmware", 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},
|
||||
"iso_url": &hcldec.AttrSpec{Name: "iso_url", Type: cty.String, Required: false},
|
||||
"iso_checksum": &hcldec.AttrSpec{Name: "iso_checksum", Type: cty.String, Required: false},
|
||||
"iso_checksum_type": &hcldec.AttrSpec{Name: "iso_checksum_type", Type: cty.String, Required: false},
|
||||
"iso_urls": &hcldec.AttrSpec{Name: "iso_urls", Type: cty.List(cty.String), Required: false},
|
||||
"iso_name": &hcldec.AttrSpec{Name: "iso_name", Type: cty.String, Required: false},
|
||||
"disk_size": &hcldec.AttrSpec{Name: "disk_size", Type: cty.Number, Required: false},
|
||||
"additional_disks": &hcldec.AttrSpec{Name: "additional_disks", Type: cty.List(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},
|
||||
"install_timeout": &hcldec.AttrSpec{Name: "install_timeout", Type: cty.String, Required: false},
|
||||
}
|
||||
return s
|
||||
}
|
@ -3,24 +3,24 @@ package iso
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"github.com/xenserver/packer-builder-xenserver/builder/xenserver/common/xen"
|
||||
"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"
|
||||
xscommon "github.com/xenserver/packer-builder-xenserver/builder/xenserver/common"
|
||||
)
|
||||
|
||||
type stepCreateInstance struct {
|
||||
instance *xsclient.VMRef
|
||||
vdi *xsclient.VDIRef
|
||||
vdis []*xsclient.VDIRef
|
||||
}
|
||||
|
||||
func (self *stepCreateInstance) Run(ctx context.Context, state multistep.StateBag) multistep.StepAction {
|
||||
|
||||
c := state.Get("client").(*xscommon.Connection)
|
||||
config := state.Get("config").(xscommon.Config)
|
||||
c := state.Get("client").(*xen.Connection)
|
||||
config := state.Get("config").(Config)
|
||||
ui := state.Get("ui").(packer.Ui)
|
||||
|
||||
ui.Say("Step: Create Instance")
|
||||
@ -101,7 +101,7 @@ func (self *stepCreateInstance) Run(ctx context.Context, state multistep.StateBa
|
||||
}
|
||||
}
|
||||
|
||||
// Create VDI for the instance
|
||||
// Create VDI for the instancearray append
|
||||
sr, err := config.GetSR(c)
|
||||
|
||||
if err != nil {
|
||||
@ -111,27 +111,36 @@ func (self *stepCreateInstance) Run(ctx context.Context, state multistep.StateBa
|
||||
|
||||
ui.Say(fmt.Sprintf("Using the following SR for the VM: %s", sr))
|
||||
|
||||
vdi, err := c.GetClient().VDI.Create(c.GetSessionRef(), xenapi.VDIRecord{
|
||||
NameLabel: "Packer-disk",
|
||||
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
|
||||
disk_sizes := make([]uint, len(config.AdditionalDisks)+1)
|
||||
copy(disk_sizes[1:], config.AdditionalDisks)
|
||||
disk_sizes[0] = config.DiskSize
|
||||
|
||||
err = xscommon.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
|
||||
self.vdis = make([]*xsclient.VDIRef, len(config.AdditionalDisks)+1)
|
||||
|
||||
for i, disk_size := range disk_sizes {
|
||||
vdi, err := c.GetClient().VDI.Create(c.GetSessionRef(), xenapi.VDIRecord{
|
||||
NameLabel: fmt.Sprintf("Packer-disk-%d", i),
|
||||
VirtualSize: int(disk_size * 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.vdis[i] = &vdi
|
||||
|
||||
err = xen.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
|
||||
@ -168,7 +177,7 @@ func (self *stepCreateInstance) Run(ctx context.Context, state multistep.StateBa
|
||||
}
|
||||
|
||||
log.Printf("Creating VIF on network '%s' on VM '%s'\n", network, instance)
|
||||
_, err = xscommon.ConnectNetwork(c, network, instance, "0")
|
||||
_, err = xen.ConnectNetwork(c, network, instance, "0")
|
||||
|
||||
if err != nil {
|
||||
ui.Error(fmt.Sprintf("Failed to create VIF with error: %v", err))
|
||||
@ -197,7 +206,7 @@ func (self *stepCreateInstance) Run(ctx context.Context, state multistep.StateBa
|
||||
|
||||
//we need the VIF index string
|
||||
vifIndexString := fmt.Sprintf("%d", i)
|
||||
_, err = xscommon.ConnectNetwork(c, networks[0], instance, vifIndexString)
|
||||
_, err = xen.ConnectNetwork(c, networks[0], instance, vifIndexString)
|
||||
|
||||
if err != nil {
|
||||
ui.Say(fmt.Sprintf("Failed to connect VIF with error: %v", err.Error()))
|
||||
@ -218,13 +227,13 @@ func (self *stepCreateInstance) Run(ctx context.Context, state multistep.StateBa
|
||||
}
|
||||
|
||||
func (self *stepCreateInstance) Cleanup(state multistep.StateBag) {
|
||||
config := state.Get("config").(xscommon.Config)
|
||||
config := state.Get("config").(Config)
|
||||
if config.ShouldKeepVM(state) {
|
||||
return
|
||||
}
|
||||
|
||||
ui := state.Get("ui").(packer.Ui)
|
||||
c := state.Get("client").(*xscommon.Connection)
|
||||
c := state.Get("client").(*xen.Connection)
|
||||
|
||||
if self.instance != nil {
|
||||
ui.Say("Destroying VM")
|
||||
@ -235,11 +244,13 @@ func (self *stepCreateInstance) Cleanup(state multistep.StateBag) {
|
||||
}
|
||||
}
|
||||
|
||||
if self.vdi != nil {
|
||||
ui.Say("Destroying VDI")
|
||||
err := c.GetClient().VDI.Destroy(c.GetSessionRef(), *self.vdi)
|
||||
if err != nil {
|
||||
ui.Error(err.Error())
|
||||
for i, vdi := range self.vdis {
|
||||
if vdi != nil {
|
||||
ui.Say(fmt.Sprintf("Destroying VDI %d", i))
|
||||
err := c.GetClient().VDI.Destroy(c.GetSessionRef(), *vdi)
|
||||
if err != nil {
|
||||
ui.Error(err.Error())
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -3,7 +3,9 @@ package xva
|
||||
import (
|
||||
"context"
|
||||
"errors"
|
||||
"fmt"
|
||||
artifact2 "github.com/xenserver/packer-builder-xenserver/builder/xenserver/common/artifact"
|
||||
steps2 "github.com/xenserver/packer-builder-xenserver/builder/xenserver/common/steps"
|
||||
"github.com/xenserver/packer-builder-xenserver/builder/xenserver/common/xen"
|
||||
"time"
|
||||
|
||||
"github.com/hashicorp/hcl/v2/hcldec"
|
||||
@ -11,84 +13,23 @@ import (
|
||||
"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
|
||||
config 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 {
|
||||
packer.MultiErrorAppend(errs, err)
|
||||
}
|
||||
|
||||
errs = packer.MultiErrorAppend(
|
||||
errs, self.config.CommonConfig.Prepare(self.config.GetInterpContext(), &self.config.PackerConfig)...)
|
||||
|
||||
// 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 == "" {
|
||||
errs = packer.MultiErrorAppend(errs, fmt.Errorf("A source_path must be specified"))
|
||||
}
|
||||
|
||||
if len(errs.Errors) > 0 {
|
||||
retErr = errors.New(errs.Error())
|
||||
}
|
||||
|
||||
return nil, nil, retErr
|
||||
|
||||
func (b *Builder) Prepare(raws ...interface{}) (params []string, warns []string, errors error) {
|
||||
return b.config.Prepare(raws...)
|
||||
}
|
||||
|
||||
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)
|
||||
c, err := xen.NewXenAPIClient(self.config.HostIp, self.config.HostPort, self.config.Username, self.config.Password)
|
||||
|
||||
if err != nil {
|
||||
return nil, err
|
||||
@ -110,15 +51,16 @@ func (self *Builder) Run(ctx context.Context, ui packer.Ui, hook packer.Hook) (p
|
||||
|
||||
//Build the steps
|
||||
steps := []multistep.Step{
|
||||
&xscommon.StepPrepareOutputDir{
|
||||
&steps2.StepPrepareOutputDir{
|
||||
Force: self.config.PackerForce,
|
||||
Path: self.config.OutputDir,
|
||||
},
|
||||
&commonsteps.StepCreateFloppy{
|
||||
Files: self.config.FloppyFiles,
|
||||
Files: self.config.FloppyFiles,
|
||||
Directories: self.config.FloppyDirectories,
|
||||
Label: self.config.FloppyLabel,
|
||||
},
|
||||
new(xscommon.StepHTTPServer),
|
||||
&xscommon.StepUploadVdi{
|
||||
&steps2.StepUploadVdi{
|
||||
VdiNameFunc: func() string {
|
||||
return "Packer-floppy-disk"
|
||||
},
|
||||
@ -130,44 +72,65 @@ func (self *Builder) Run(ctx context.Context, ui packer.Ui, hook packer.Hook) (p
|
||||
},
|
||||
VdiUuidKey: "floppy_vdi_uuid",
|
||||
},
|
||||
&xscommon.StepFindVdi{
|
||||
&steps2.StepFindVdi{
|
||||
VdiName: self.config.ToolsIsoName,
|
||||
VdiUuidKey: "tools_vdi_uuid",
|
||||
},
|
||||
new(stepImportInstance),
|
||||
&xscommon.StepAttachVdi{
|
||||
&steps2.StepAttachVdi{
|
||||
VdiUuidKey: "floppy_vdi_uuid",
|
||||
VdiType: xsclient.VbdTypeFloppy,
|
||||
},
|
||||
&xscommon.StepAttachVdi{
|
||||
&steps2.StepAttachVdi{
|
||||
VdiUuidKey: "tools_vdi_uuid",
|
||||
VdiType: xsclient.VbdTypeCD,
|
||||
},
|
||||
new(xscommon.StepStartVmPaused),
|
||||
new(xscommon.StepSetVmHostSshAddress),
|
||||
new(xscommon.StepBootWait),
|
||||
&xscommon.StepTypeBootCommand{
|
||||
new(steps2.StepStartVmPaused),
|
||||
new(steps2.StepSetVmHostSshAddress),
|
||||
new(steps2.StepHTTPIPDiscover),
|
||||
&steps2.StepCreateProxy{},
|
||||
commonsteps.HTTPServerFromHTTPConfig(&self.config.HTTPConfig),
|
||||
new(steps2.StepBootWait),
|
||||
&steps2.StepTypeBootCommand{
|
||||
Ctx: *self.config.GetInterpContext(),
|
||||
},
|
||||
&xscommon.StepWaitForIP{
|
||||
/*
|
||||
VNC is only available after boot command because xenserver doesn't seem to support two vnc connections at the same time
|
||||
*/
|
||||
&steps2.StepGetVNCPort{},
|
||||
&steps2.StepWaitForIP{
|
||||
Chan: httpReqChan,
|
||||
Timeout: 300 * time.Minute, /*self.config.InstallTimeout*/ // @todo change this
|
||||
},
|
||||
&steps2.StepCreateForwarding{Targets: []steps2.ForwardTarget{
|
||||
{
|
||||
Host: steps2.InstanceCommIP,
|
||||
Port: steps2.InstanceCommPort,
|
||||
Key: "local_comm_address",
|
||||
},
|
||||
}},
|
||||
&communicator.StepConnect{
|
||||
Config: &self.config.SSHConfig.Comm,
|
||||
Host: xscommon.CommHost,
|
||||
SSHConfig: xscommon.SSHConfigFunc(self.config.CommonConfig.SSHConfig),
|
||||
SSHPort: xscommon.SSHPort,
|
||||
Config: &self.config.Comm,
|
||||
Host: func(state multistep.StateBag) (string, error) {
|
||||
return steps2.GetForwardedHost(state, "local_comm_address")
|
||||
},
|
||||
SSHConfig: self.config.Comm.SSHConfigFunc(),
|
||||
SSHPort: func(state multistep.StateBag) (int, error) {
|
||||
return steps2.GetForwardedPort(state, "local_comm_address")
|
||||
},
|
||||
WinRMPort: func(state multistep.StateBag) (int, error) {
|
||||
return steps2.GetForwardedPort(state, "local_comm_address")
|
||||
},
|
||||
},
|
||||
new(commonsteps.StepProvision),
|
||||
new(xscommon.StepShutdown),
|
||||
&xscommon.StepDetachVdi{
|
||||
new(steps2.StepShutdown),
|
||||
&steps2.StepDetachVdi{
|
||||
VdiUuidKey: "floppy_vdi_uuid",
|
||||
},
|
||||
&xscommon.StepDetachVdi{
|
||||
&steps2.StepDetachVdi{
|
||||
VdiUuidKey: "tools_vdi_uuid",
|
||||
},
|
||||
new(xscommon.StepExport),
|
||||
new(steps2.StepExport),
|
||||
}
|
||||
|
||||
self.runner = &multistep.BasicRunner{Steps: steps}
|
||||
@ -185,7 +148,7 @@ func (self *Builder) Run(ctx context.Context, ui packer.Ui, hook packer.Hook) (p
|
||||
return nil, errors.New("Build was halted.")
|
||||
}
|
||||
|
||||
artifact, _ := xscommon.NewArtifact(self.config.OutputDir)
|
||||
artifact, _ := artifact2.NewArtifact(self.config.OutputDir)
|
||||
|
||||
return artifact, nil
|
||||
}
|
||||
|
@ -1,9 +1,10 @@
|
||||
package xva
|
||||
|
||||
import (
|
||||
"github.com/hashicorp/packer-plugin-sdk/packer"
|
||||
"testing"
|
||||
|
||||
"github.com/hashicorp/packer-plugin-sdk/packer"
|
||||
"github.com/hashicorp/packer-plugin-sdk/common"
|
||||
)
|
||||
|
||||
func testConfig() map[string]interface{} {
|
||||
@ -16,7 +17,7 @@ func testConfig() map[string]interface{} {
|
||||
"ssh_username": "foo",
|
||||
"source_path": ".",
|
||||
|
||||
packer.BuildNameConfigKey: "foo",
|
||||
common.BuildNameConfigKey: "foo",
|
||||
}
|
||||
}
|
||||
|
||||
|
35
builder/xenserver/xva/config.go
Normal file
35
builder/xenserver/xva/config.go
Normal file
@ -0,0 +1,35 @@
|
||||
//go:generate packer-sdc mapstructure-to-hcl2 -type Config
|
||||
//go:generate packer-sdc struct-markdown
|
||||
|
||||
package xva
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
packersdk "github.com/hashicorp/packer-plugin-sdk/packer"
|
||||
xscommon "github.com/xenserver/packer-builder-xenserver/builder/xenserver/common/config"
|
||||
)
|
||||
|
||||
type Config struct {
|
||||
xscommon.CommonConfig `mapstructure:",squash"`
|
||||
|
||||
SourcePath string `mapstructure:"source_path"`
|
||||
}
|
||||
|
||||
func (c *Config) Prepare(raws ...interface{}) ([]string, []string, error) {
|
||||
var errs *packersdk.MultiError
|
||||
params, warnings, merrs := c.CommonConfig.Prepare(c, raws)
|
||||
if merrs != nil {
|
||||
errs = packersdk.MultiErrorAppend(errs, merrs)
|
||||
}
|
||||
|
||||
// Validation
|
||||
|
||||
if c.SourcePath == "" {
|
||||
errs = packersdk.MultiErrorAppend(errs, fmt.Errorf("A source_path must be specified"))
|
||||
}
|
||||
|
||||
if errs != nil && len(errs.Errors) > 0 {
|
||||
return params, warnings, errs
|
||||
}
|
||||
return params, warnings, nil
|
||||
}
|
@ -1,6 +1,6 @@
|
||||
// Code generated by "mapstructure-to-hcl2 -type Config"; DO NOT EDIT.
|
||||
// Code generated by "packer-sdc mapstructure-to-hcl2"; DO NOT EDIT.
|
||||
|
||||
package common
|
||||
package xva
|
||||
|
||||
import (
|
||||
"github.com/hashicorp/hcl/v2/hcldec"
|
||||
@ -12,37 +12,47 @@ import (
|
||||
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"`
|
||||
BootGroupInterval *string `mapstructure:"boot_keygroup_interval" cty:"boot_keygroup_interval" hcl:"boot_keygroup_interval"`
|
||||
BootWait *string `mapstructure:"boot_wait" cty:"boot_wait" hcl:"boot_wait"`
|
||||
BootCommand []string `mapstructure:"boot_command" cty:"boot_command" hcl:"boot_command"`
|
||||
DisableVNC *bool `mapstructure:"disable_vnc" cty:"disable_vnc" hcl:"disable_vnc"`
|
||||
BootKeyInterval *string `mapstructure:"boot_key_interval" cty:"boot_key_interval" hcl:"boot_key_interval"`
|
||||
HTTPDir *string `mapstructure:"http_directory" cty:"http_directory" hcl:"http_directory"`
|
||||
HTTPContent map[string]string `mapstructure:"http_content" cty:"http_content" hcl:"http_content"`
|
||||
HTTPPortMin *int `mapstructure:"http_port_min" cty:"http_port_min" hcl:"http_port_min"`
|
||||
HTTPPortMax *int `mapstructure:"http_port_max" cty:"http_port_max" hcl:"http_port_max"`
|
||||
HTTPAddress *string `mapstructure:"http_bind_address" cty:"http_bind_address" hcl:"http_bind_address"`
|
||||
HTTPInterface *string `mapstructure:"http_interface" undocumented:"true" cty:"http_interface" hcl:"http_interface"`
|
||||
Username *string `mapstructure:"remote_username" required:"true" cty:"remote_username" hcl:"remote_username"`
|
||||
Password *string `mapstructure:"remote_password" required:"true" cty:"remote_password" hcl:"remote_password"`
|
||||
HostIp *string `mapstructure:"remote_host" required:"true" cty:"remote_host" hcl:"remote_host"`
|
||||
HostPort *int `mapstructure:"remote_port" cty:"remote_port" hcl:"remote_port"`
|
||||
HostSSHPort *int `mapstructure:"remote_ssh_port" cty:"remote_ssh_port" hcl:"remote_ssh_port"`
|
||||
FloppyFiles []string `mapstructure:"floppy_files" cty:"floppy_files" hcl:"floppy_files"`
|
||||
FloppyDirectories []string `mapstructure:"floppy_dirs" cty:"floppy_dirs" hcl:"floppy_dirs"`
|
||||
FloppyLabel *string `mapstructure:"floppy_label" cty:"floppy_label" hcl:"floppy_label"`
|
||||
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" 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"`
|
||||
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"`
|
||||
PlatformArgs map[string]string `mapstructure:"platform_args" cty:"platform_args" hcl:"platform_args"`
|
||||
ShutdownCommand *string `mapstructure:"shutdown_command" required:"false" cty:"shutdown_command" hcl:"shutdown_command"`
|
||||
ShutdownTimeout *string `mapstructure:"shutdown_timeout" required:"false" cty:"shutdown_timeout" hcl:"shutdown_timeout"`
|
||||
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"`
|
||||
SSHPort *int `mapstructure:"ssh_port" cty:"ssh_port" hcl:"ssh_port"`
|
||||
SSHUsername *string `mapstructure:"ssh_username" cty:"ssh_username" hcl:"ssh_username"`
|
||||
SSHPassword *string `mapstructure:"ssh_password" cty:"ssh_password" hcl:"ssh_password"`
|
||||
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"`
|
||||
@ -86,26 +96,19 @@ type FlatConfig struct {
|
||||
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"`
|
||||
SkipNatMapping *bool `mapstructure:"skip_nat_mapping" required:"false" cty:"skip_nat_mapping" hcl:"skip_nat_mapping"`
|
||||
SSHKeyPath *string `mapstructure:"ssh_key_path" undocumented:"true" cty:"ssh_key_path" hcl:"ssh_key_path"`
|
||||
SSHSkipNatMapping *bool `mapstructure:"ssh_skip_nat_mapping" undocumented:"true" cty:"ssh_skip_nat_mapping" hcl:"ssh_skip_nat_mapping"`
|
||||
HostPortMin *int `mapstructure:"host_port_min" required:"false" undocumented:"true" cty:"host_port_min" hcl:"host_port_min"`
|
||||
HostPortMax *int `mapstructure:"host_port_max" required:"false" undocumented:"true" cty:"host_port_max" hcl:"host_port_max"`
|
||||
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"`
|
||||
Firmware *string `mapstructure:"firmware" cty:"firmware" hcl:"firmware"`
|
||||
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"`
|
||||
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"`
|
||||
ISOChecksumType *string `mapstructure:"iso_checksum_type" cty:"iso_checksum_type" hcl:"iso_checksum_type"`
|
||||
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"`
|
||||
}
|
||||
|
||||
@ -123,37 +126,47 @@ 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},
|
||||
"boot_keygroup_interval": &hcldec.AttrSpec{Name: "boot_keygroup_interval", Type: cty.String, Required: false},
|
||||
"boot_wait": &hcldec.AttrSpec{Name: "boot_wait", Type: cty.String, Required: false},
|
||||
"boot_command": &hcldec.AttrSpec{Name: "boot_command", Type: cty.List(cty.String), Required: false},
|
||||
"disable_vnc": &hcldec.AttrSpec{Name: "disable_vnc", Type: cty.Bool, Required: false},
|
||||
"boot_key_interval": &hcldec.AttrSpec{Name: "boot_key_interval", Type: cty.String, Required: false},
|
||||
"http_directory": &hcldec.AttrSpec{Name: "http_directory", Type: cty.String, Required: false},
|
||||
"http_content": &hcldec.AttrSpec{Name: "http_content", Type: cty.Map(cty.String), Required: false},
|
||||
"http_port_min": &hcldec.AttrSpec{Name: "http_port_min", Type: cty.Number, Required: false},
|
||||
"http_port_max": &hcldec.AttrSpec{Name: "http_port_max", Type: cty.Number, Required: false},
|
||||
"http_bind_address": &hcldec.AttrSpec{Name: "http_bind_address", Type: cty.String, Required: false},
|
||||
"http_interface": &hcldec.AttrSpec{Name: "http_interface", Type: 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_port": &hcldec.AttrSpec{Name: "remote_port", Type: cty.Number, Required: false},
|
||||
"remote_ssh_port": &hcldec.AttrSpec{Name: "remote_ssh_port", Type: cty.Number, Required: false},
|
||||
"floppy_files": &hcldec.AttrSpec{Name: "floppy_files", Type: cty.List(cty.String), Required: false},
|
||||
"floppy_dirs": &hcldec.AttrSpec{Name: "floppy_dirs", Type: cty.List(cty.String), Required: false},
|
||||
"floppy_label": &hcldec.AttrSpec{Name: "floppy_label", Type: cty.String, 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},
|
||||
"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},
|
||||
"platform_args": &hcldec.AttrSpec{Name: "platform_args", Type: cty.Map(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},
|
||||
"shutdown_timeout": &hcldec.AttrSpec{Name: "shutdown_timeout", 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_port": &hcldec.AttrSpec{Name: "ssh_port", Type: cty.Number, Required: false},
|
||||
"ssh_username": &hcldec.AttrSpec{Name: "ssh_username", Type: cty.String, Required: false},
|
||||
"ssh_password": &hcldec.AttrSpec{Name: "ssh_password", 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},
|
||||
@ -197,26 +210,19 @@ func (*FlatConfig) HCL2Spec() map[string]hcldec.Spec {
|
||||
"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},
|
||||
"skip_nat_mapping": &hcldec.AttrSpec{Name: "skip_nat_mapping", Type: cty.Bool, Required: false},
|
||||
"ssh_key_path": &hcldec.AttrSpec{Name: "ssh_key_path", Type: cty.String, Required: false},
|
||||
"ssh_skip_nat_mapping": &hcldec.AttrSpec{Name: "ssh_skip_nat_mapping", Type: cty.Bool, 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},
|
||||
"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},
|
||||
"firmware": &hcldec.AttrSpec{Name: "firmware", 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_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_checksum_type": &hcldec.AttrSpec{Name: "iso_checksum_type", 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},
|
||||
}
|
||||
return s
|
@ -3,12 +3,14 @@ package xva
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"github.com/xenserver/packer-builder-xenserver/builder/xenserver/common/xen"
|
||||
"net"
|
||||
"os"
|
||||
"strconv"
|
||||
|
||||
"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 {
|
||||
@ -18,8 +20,8 @@ type stepImportInstance struct {
|
||||
|
||||
func (self *stepImportInstance) Run(ctx context.Context, state multistep.StateBag) multistep.StepAction {
|
||||
|
||||
c := state.Get("client").(*xscommon.Connection)
|
||||
config := state.Get("config").(xscommon.Config)
|
||||
c := state.Get("client").(*xen.Connection)
|
||||
config := state.Get("config").(Config)
|
||||
ui := state.Get("ui").(packer.Ui)
|
||||
|
||||
ui.Say("Step: Import Instance")
|
||||
@ -39,8 +41,8 @@ func (self *stepImportInstance) Run(ctx context.Context, state multistep.StateBa
|
||||
return multistep.ActionHalt
|
||||
}
|
||||
|
||||
result, err := xscommon.HTTPUpload(fmt.Sprintf("https://%s/import?session_id=%s&sr_id=%s",
|
||||
c.Host,
|
||||
result, err := xen.HTTPUpload(fmt.Sprintf("https://%s/import?session_id=%s&sr_id=%s",
|
||||
net.JoinHostPort(c.Host, strconv.Itoa(c.Port)),
|
||||
c.GetSession(),
|
||||
sr,
|
||||
), fh, state)
|
||||
|
94
docs-src/builders/iso/xenserver-iso.mdx
Normal file
94
docs-src/builders/iso/xenserver-iso.mdx
Normal file
@ -0,0 +1,94 @@
|
||||
---
|
||||
modeline: |
|
||||
vim: set ft=pandoc:
|
||||
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.
|
||||
page_title: "XenServer Builder (from an ISO)"
|
||||
nav_title: "XenServer Builder (from an ISO)"
|
||||
---
|
||||
|
||||
# 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 builder. In addition to
|
||||
the items listed here, you will want to look at the general configuration
|
||||
references for [HTTP](#http-directory-configuration),
|
||||
[Floppy](#floppy-configuration),
|
||||
[Boot](#boot-configuration),
|
||||
[Shutdown](#shutdown-configuration),
|
||||
[Communicator](#communicator-configuration)
|
||||
configuration references, which are
|
||||
necessary for this build to succeed and can be found further down the page.
|
||||
|
||||
### Required:
|
||||
@include 'builder/xenserver/iso/Config-required.mdx'
|
||||
|
||||
### Optional:
|
||||
|
||||
@include 'builder/xenserver/iso/Config-not-required.mdx'
|
||||
@include 'builder/xenserver/common/config/CommonConfig-not-required.mdx'
|
||||
@include 'builder/xenserver/common/config/HardwareConfig-not-required.mdx'
|
||||
|
||||
## Http directory configuration
|
||||
|
||||
@include 'packer-plugin-sdk/multistep/commonsteps/HTTPConfig.mdx'
|
||||
|
||||
### Optional:
|
||||
|
||||
@include 'packer-plugin-sdk/multistep/commonsteps/HTTPConfig-not-required.mdx'
|
||||
|
||||
## Floppy configuration
|
||||
|
||||
@include 'packer-plugin-sdk/multistep/commonsteps/FloppyConfig.mdx'
|
||||
|
||||
### Optional:
|
||||
|
||||
@include 'packer-plugin-sdk/multistep/commonsteps/FloppyConfig-not-required.mdx'
|
||||
|
||||
## Shutdown configuration
|
||||
|
||||
### Optional:
|
||||
|
||||
@include 'packer-plugin-sdk/shutdowncommand/ShutdownConfig-not-required.mdx'
|
||||
|
||||
## Communicator configuration
|
||||
|
||||
### Optional common fields:
|
||||
|
||||
@include 'packer-plugin-sdk/communicator/Config-not-required.mdx'
|
||||
|
||||
@include 'builder/xenserver/common/config/CommConfig-not-required.mdx'
|
||||
|
||||
### Optional SSH fields:
|
||||
|
||||
@include 'packer-plugin-sdk/communicator/SSH-not-required.mdx'
|
||||
|
||||
@include 'packer-plugin-sdk/communicator/SSH-Private-Key-File-not-required.mdx'
|
||||
|
||||
### Optional WinRM fields:
|
||||
|
||||
@include 'packer-plugin-sdk/communicator/WinRM-not-required.mdx'
|
||||
|
||||
## Boot Configuration
|
||||
|
||||
@include 'packer-plugin-sdk/bootcommand/VNCConfig.mdx'
|
||||
|
||||
@include 'packer-plugin-sdk/bootcommand/BootConfig.mdx'
|
||||
|
||||
### Optional:
|
||||
|
||||
@include 'packer-plugin-sdk/bootcommand/VNCConfig-not-required.mdx'
|
||||
|
||||
@include 'packer-plugin-sdk/bootcommand/BootConfig-not-required.mdx'
|
@ -1,255 +0,0 @@
|
||||
---
|
||||
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 with `iso_checksum_type`, documented below.
|
||||
|
||||
* `iso_checksum_type` (string) - The type of the checksum specified in
|
||||
`iso_checksum`. Valid values are "none", "md5", "sha1", "sha256", or
|
||||
"sha512" currently. While "none" will skip checksumming, this is not
|
||||
recommended since ISO files are generally 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/centos8.json).
|
||||
|
||||
* `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-2004.json) and [centos](../../examples/centos8.json) 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_size` (integer) - The size, in megabytes, of the hard disk to create
|
||||
for the VM. By default, this is 40000 (about 40 GB).
|
||||
|
||||
* `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", "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 that 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.
|
||||
|
||||
* `keep_vm` (string) - Determine when to keep the VM and when to clean it up. This
|
||||
can be "always", "never" or "on_success". By default this 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.
|
||||
|
||||
* `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 corespond 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
|
||||
```javascript
|
||||
{
|
||||
"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.
|
||||
|
||||
* `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 the 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).
|
||||
|
||||
## 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 for character over a VNC connection
|
||||
to the machine, simulating a human actually typing 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.
|
10
go.mod
10
go.mod
@ -4,12 +4,14 @@ go 1.16
|
||||
|
||||
require (
|
||||
github.com/amfranz/go-xmlrpc-client v0.0.0-20190612172737-76858463955d
|
||||
github.com/hashicorp/hcl/v2 v2.8.0
|
||||
github.com/hashicorp/packer-plugin-sdk v0.1.0
|
||||
github.com/armon/go-socks5 v0.0.0-20160902184237-e75332964ef5
|
||||
github.com/hashicorp/hcl/v2 v2.10.0
|
||||
github.com/hashicorp/packer-plugin-sdk v0.2.3
|
||||
github.com/mitchellh/go-vnc v0.0.0-20150629162542-723ed9867aed
|
||||
github.com/niemeyer/pretty v0.0.0-20200227124842-a10e7caefd8e // indirect
|
||||
github.com/terra-farm/go-xen-api-client v0.0.1
|
||||
github.com/zclconf/go-cty v1.7.0
|
||||
golang.org/x/crypto v0.0.0-20201208171446-5f87f3452ae9
|
||||
github.com/zclconf/go-cty v1.8.3
|
||||
golang.org/x/crypto v0.0.0-20201221181555-eec23a3978ad
|
||||
golang.org/x/net v0.0.0-20210119194325-5f4716e94777
|
||||
gopkg.in/check.v1 v1.0.0-20200227125254-8fa46927fb4f // indirect
|
||||
)
|
||||
|
259
go.sum
259
go.sum
@ -34,28 +34,6 @@ cloud.google.com/go/storage v1.8.0/go.mod h1:Wv1Oy7z6Yz3DshWRJFhqM/UCfaWIRTdp0RX
|
||||
cloud.google.com/go/storage v1.10.0 h1:STgFzyU5/8miMl0//zKh2aQeTyeaUH3WN9bSUiJ09bA=
|
||||
cloud.google.com/go/storage v1.10.0/go.mod h1:FLPqc6j+Ki4BU591ie1oL6qBQGu2Bl/tZ9ullr3+Kg0=
|
||||
dmitri.shuralyov.com/gpu/mtl v0.0.0-20190408044501-666a987793e9/go.mod h1:H6x//7gZCb22OMCxBHrMx7a5I7Hp++hsVxbQ4BYO7hU=
|
||||
github.com/1and1/oneandone-cloudserver-sdk-go v1.0.1/go.mod h1:61apmbkVJH4kg+38ftT+/l0XxdUCVnHggqcOTqZRSEE=
|
||||
github.com/Azure/azure-sdk-for-go v40.5.0+incompatible/go.mod h1:9XXNKU+eRnpl9moKnB4QOLf1HestfXbmab5FXxiDBjc=
|
||||
github.com/Azure/go-autorest v14.2.0+incompatible/go.mod h1:r+4oMnoxhatjLLJ6zxSWATqVooLgysK6ZNox3g/xq24=
|
||||
github.com/Azure/go-autorest/autorest v0.9.0/go.mod h1:xyHB1BMZT0cuDHU7I0+g046+BFDTQ8rEZB0s4Yfa6bI=
|
||||
github.com/Azure/go-autorest/autorest v0.9.3/go.mod h1:GsRuLYvwzLjjjRoWEIyMUaYq8GNUx2nRB378IPt/1p0=
|
||||
github.com/Azure/go-autorest/autorest v0.10.0/go.mod h1:/FALq9T/kS7b5J5qsQ+RSTUdAmGFqi0vUdVNNx8q630=
|
||||
github.com/Azure/go-autorest/autorest/adal v0.5.0/go.mod h1:8Z9fGy2MpX0PvDjB1pEgQTmVqjGhiHBW7RJJEciWzS0=
|
||||
github.com/Azure/go-autorest/autorest/adal v0.8.0/go.mod h1:Z6vX6WXXuyieHAXwMj0S6HY6e6wcHn37qQMBQlvY3lc=
|
||||
github.com/Azure/go-autorest/autorest/adal v0.8.1/go.mod h1:ZjhuQClTqx435SRJ2iMlOxPYt3d2C/T/7TiQCVZSn3Q=
|
||||
github.com/Azure/go-autorest/autorest/adal v0.8.2/go.mod h1:ZjhuQClTqx435SRJ2iMlOxPYt3d2C/T/7TiQCVZSn3Q=
|
||||
github.com/Azure/go-autorest/autorest/azure/auth v0.4.2/go.mod h1:90gmfKdlmKgfjUpnCEpOJzsUEjrWDSLwHIG73tSXddM=
|
||||
github.com/Azure/go-autorest/autorest/azure/cli v0.3.1/go.mod h1:ZG5p860J94/0kI9mNJVoIoLgXcirM2gF5i2kWloofxw=
|
||||
github.com/Azure/go-autorest/autorest/date v0.1.0/go.mod h1:plvfp3oPSKwf2DNjlBjWF/7vwR+cUD/ELuzDCXwHUVA=
|
||||
github.com/Azure/go-autorest/autorest/date v0.2.0/go.mod h1:vcORJHLJEh643/Ioh9+vPmf1Ij9AEBM5FuBIXLmIy0g=
|
||||
github.com/Azure/go-autorest/autorest/mocks v0.1.0/go.mod h1:OTyCOPRA2IgIlWxVYxBee2F5Gr4kF2zd2J5cFRaIDN0=
|
||||
github.com/Azure/go-autorest/autorest/mocks v0.2.0/go.mod h1:OTyCOPRA2IgIlWxVYxBee2F5Gr4kF2zd2J5cFRaIDN0=
|
||||
github.com/Azure/go-autorest/autorest/mocks v0.3.0/go.mod h1:a8FDP3DYzQ4RYfVAxAN3SVSiiO77gL2j2ronKKP0syM=
|
||||
github.com/Azure/go-autorest/autorest/to v0.3.0/go.mod h1:MgwOyqaIuKdG4TL/2ywSsIWKAfJfgHDo8ObuUk3t5sA=
|
||||
github.com/Azure/go-autorest/autorest/validation v0.2.0/go.mod h1:3EEqHnBxQGHXRYq3HT1WyXAvT7LLY3tl70hw6tQIbjI=
|
||||
github.com/Azure/go-autorest/autorest/validation v0.3.1/go.mod h1:yhLgjC0Wda5DYXl6JAsWyUe4KVNffhoDhG0zVzUMo3E=
|
||||
github.com/Azure/go-autorest/logger v0.1.0/go.mod h1:oExouG+K6PryycPJfVSxi/koC6LSNgds39diKLz7Vrc=
|
||||
github.com/Azure/go-autorest/tracing v0.5.0/go.mod h1:r/s2XiOKccPW3HrqB+W0TQzfbtp2fGCgRFtBroKn4Dk=
|
||||
github.com/Azure/go-ntlmssp v0.0.0-20180810175552-4a21cbd618b4/go.mod h1:chxPXzSsl7ZWRAuOIE23GDNzjWuZquvFlgA8xmpunjU=
|
||||
github.com/Azure/go-ntlmssp v0.0.0-20191115201650-bad6df29494a h1:3FwiePtHk5YJrooV799oo5jIfsgRdES25VdngJM03dU=
|
||||
github.com/Azure/go-ntlmssp v0.0.0-20191115201650-bad6df29494a/go.mod h1:chxPXzSsl7ZWRAuOIE23GDNzjWuZquvFlgA8xmpunjU=
|
||||
@ -64,51 +42,36 @@ github.com/BurntSushi/xgb v0.0.0-20160522181843-27f122750802/go.mod h1:IVnqGOEym
|
||||
github.com/ChrisTrenkamp/goxpath v0.0.0-20170922090931-c385f95c6022 h1:y8Gs8CzNfDF5AZvjr+5UyGQvQEBL7pwo+v+wX6q9JI8=
|
||||
github.com/ChrisTrenkamp/goxpath v0.0.0-20170922090931-c385f95c6022/go.mod h1:nuWgzSkT5PnyOd+272uUmV0dnAnAn42Mk7PiQC5VzN4=
|
||||
github.com/DataDog/datadog-go v2.2.0+incompatible/go.mod h1:LButxg5PwREeZtORoXG3tL4fMGNddJ+vMq1mwgfaqoQ=
|
||||
github.com/NaverCloudPlatform/ncloud-sdk-go-v2 v1.1.0/go.mod h1:P+3VS0ETiQPyWOx3vB/oeC8J3qd7jnVZLYAFwWgGRt8=
|
||||
github.com/StackExchange/wmi v0.0.0-20190523213315-cbe66965904d/go.mod h1:3eOhrUMpNV+6aFIbp5/iudMxNCF27Vw2OZgy4xEx0Fg=
|
||||
github.com/Telmate/proxmox-api-go v0.0.0-20200715182505-ec97c70ba887/go.mod h1:OGWyIMJ87/k/GCz8CGiWB2HOXsOVDM6Lpe/nFPkC4IQ=
|
||||
github.com/abdullin/seq v0.0.0-20160510034733-d5467c17e7af/go.mod h1:5Jv4cbFiHJMsVxt52+i0Ha45fjshj6wxYr1r19tB9bw=
|
||||
github.com/agext/levenshtein v1.2.1 h1:QmvMAjj2aEICytGiWzmxoE0x2KZvE0fvmqMOfy2tjT8=
|
||||
github.com/agext/levenshtein v1.2.1/go.mod h1:JEDfjyjHDjOF/1e4FlBE/PkbqA9OfWu2ki2W0IB5558=
|
||||
github.com/aliyun/alibaba-cloud-sdk-go v0.0.0-20190418113227-25233c783f4e/go.mod h1:T9M45xf79ahXVelWoOBmH0y4aC1t5kXO5BxwyakgIGA=
|
||||
github.com/aliyun/aliyun-oss-go-sdk v0.0.0-20170113022742-e6dbea820a9f/go.mod h1:T/Aws4fEfogEE9v+HPhhw+CntffsBHJ8nXQCwKr0/g8=
|
||||
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 v0.0.0-20170728053731-b5c552e1acbd/go.mod h1:Yee4kTMuNiPYJ7nSNorELQMr1J33uOpXDMByNYhvtNk=
|
||||
github.com/antchfx/xpath v1.1.11 h1:WOFtK8TVAjLm3lbgqeP0arlHpvCEeTANeWZ/csPpJkQ=
|
||||
github.com/antchfx/xpath v1.1.11/go.mod h1:i54GszH55fYfBmoZXapTHN8T8tkcHfRgLyVwwqzXNcs=
|
||||
github.com/antchfx/xquery v0.0.0-20170730121040-eb8c3c172607/go.mod h1:LzD22aAzDP8/dyiCKFp31He4m2GPjl0AFyzDtZzUu9M=
|
||||
github.com/antchfx/xquery v0.0.0-20180515051857-ad5b8c7a47b0 h1:JaCC8jz0zdMLk2m+qCCVLLLM/PL93p84w4pK3aJWj60=
|
||||
github.com/antchfx/xquery v0.0.0-20180515051857-ad5b8c7a47b0/go.mod h1:LzD22aAzDP8/dyiCKFp31He4m2GPjl0AFyzDtZzUu9M=
|
||||
github.com/antihax/optional v1.0.0/go.mod h1:uupD/76wgC+ih3iEmQUL+0Ugr19nfwCT1kdvxnR2qWY=
|
||||
github.com/apparentlymart/go-cidr v1.0.1/go.mod h1:EBcsNrHc3zQeuaeCeCtQruQm+n9/YjEn/vI25Lg7Gwc=
|
||||
github.com/apparentlymart/go-dump v0.0.0-20180507223929-23540a00eaa3 h1:ZSTrOEhiM5J5RFxEaFvMZVEAM1KvT1YzbEOwB2EAGjA=
|
||||
github.com/apparentlymart/go-dump v0.0.0-20180507223929-23540a00eaa3/go.mod h1:oL81AME2rN47vu18xqj1S1jPIPuN7afo62yKTNn3XMM=
|
||||
github.com/apparentlymart/go-textseg v1.0.0 h1:rRmlIsPEEhUTIKQb7T++Nz/A5Q6C9IuX2wFoYVvnCs0=
|
||||
github.com/apparentlymart/go-textseg v1.0.0/go.mod h1:z96Txxhf3xSFMPmb5X/1W05FF/Nj9VFpLOpjS5yuumk=
|
||||
github.com/apparentlymart/go-textseg/v12 v12.0.0 h1:bNEQyAGak9tojivJNkoqWErVCQbjdL7GzRt3F8NvfJ0=
|
||||
github.com/apparentlymart/go-textseg/v12 v12.0.0/go.mod h1:S/4uRK2UtaQttw1GenVJEynmyUenKwP++x/+DdGV/Ec=
|
||||
github.com/approvals/go-approval-tests v0.0.0-20160714161514-ad96e53bea43/go.mod h1:S6puKjZ9ZeqUPBv2hEBnMZGcM2J6mOsDRQcmxkMAND0=
|
||||
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/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.0.0-20190430140413-ec5e00d3c878 h1:EFSB7Zo9Eg91v7MJPVsifUysc/wPdN+NOnVe6bWbdBM=
|
||||
github.com/armon/go-metrics v0.0.0-20190430140413-ec5e00d3c878/go.mod h1:3AMJUQhVx52RsWOnlkpikZr01T/yAVN2gn0861vByNg=
|
||||
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/armon/go-socks5 v0.0.0-20160902184237-e75332964ef5 h1:0CwZNZbxp69SHPdPJAN/hZIm0C4OItdklCFmMRWYpio=
|
||||
github.com/armon/go-socks5 v0.0.0-20160902184237-e75332964ef5/go.mod h1:wHh0iHkYZB8zMSxRWpUBQtwG5a7fFgvEO+odwuTv2gs=
|
||||
github.com/aws/aws-sdk-go v1.15.78/go.mod h1:E3/ieXAlvM0XWO57iftYVDLLvQ824smPP3ATZkfNZeM=
|
||||
github.com/aws/aws-sdk-go v1.26.3/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.31.9/go.mod h1:5zCpMtNQVjRREroY7sYe8lOMRSxkhG6MZveU8YkpAk0=
|
||||
github.com/aws/aws-sdk-go v1.36.0/go.mod h1:hcU610XS61/+aQV88ixoOzUoG7v3b31pl2zKMmprdro=
|
||||
github.com/aws/aws-sdk-go v1.36.5 h1:SouM2ar1A8f+3DYWW622sDdqkkZAO3ha4j8GQjiPLFg=
|
||||
github.com/aws/aws-sdk-go v1.36.5/go.mod h1:hcU610XS61/+aQV88ixoOzUoG7v3b31pl2zKMmprdro=
|
||||
github.com/aws/aws-sdk-go v1.38.0 h1:mqnmtdW8rGIQmp2d0WRFLua0zW0Pel0P6/vd3gJuViY=
|
||||
github.com/aws/aws-sdk-go v1.38.0/go.mod h1:hcU610XS61/+aQV88ixoOzUoG7v3b31pl2zKMmprdro=
|
||||
github.com/beorn7/perks v0.0.0-20180321164747-3a771d992973/go.mod h1:Dwedo/Wpr24TaqPxmxbtue+5NUziq4I4S80YR8gNf3Q=
|
||||
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/biogo/hts v0.0.0-20160420073057-50da7d4131a3/go.mod h1:YOY5xnRf7Jz2SZCLSKgVfyqNzbRgyTznM3HyDqQMxcU=
|
||||
github.com/bmatcuk/doublestar v1.1.5/go.mod h1:wiQtGV+rzVYxB7WIlirSN++5HPtPlXEo9MEoZQC/PmE=
|
||||
github.com/c2h5oh/datasize v0.0.0-20200112174442-28bbd4740fee/go.mod h1:S/7n9copUssQ56c7aAgHqftWO4LTf4xY6CGWt8Bc+3M=
|
||||
github.com/census-instrumentation/opencensus-proto v0.2.1/go.mod h1:f6KPmirojxKA12rnyqOA5BBL4O983OfeGPqjHWSTneU=
|
||||
github.com/cheggaaa/pb v1.0.27/go.mod h1:pQciLPpbU0oxA0h+VJYYLxO+XeDQb5pZijXscXHm81s=
|
||||
github.com/chzyer/logex v1.1.10/go.mod h1:+Ywpsq7O8HXn0nuIou7OrIPyXbp3wmkHB+jjWRnGsAI=
|
||||
@ -121,17 +84,6 @@ github.com/cncf/udpa/go v0.0.0-20191209042840-269d4d468f6f/go.mod h1:M8M6+tZqaGX
|
||||
github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
|
||||
github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c=
|
||||
github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
|
||||
github.com/davecgh/go-xdr v0.0.0-20161123171359-e6a2ba005892/go.mod h1:CTDl0pzVzE5DEzZhPfvhY/9sPFMQIxaJ9VAMs9AagrE=
|
||||
github.com/dgrijalva/jwt-go v3.2.0+incompatible/go.mod h1:E3ru+11k8xSBh+hMPgOLZmtrrCbhqsmaPHjLKYnJCaQ=
|
||||
github.com/digitalocean/go-libvirt v0.0.0-20190626172931-4d226dd6c437/go.mod h1:PRcPVAAma6zcLpFd4GZrjR/MRpood3TamjKI2m/z/Uw=
|
||||
github.com/digitalocean/go-libvirt v0.0.0-20201209184759-e2a69bcd5bd1/go.mod h1:QS1XzqZLcDniNYrN7EZefq3wIyb/M2WmJbql4ZKoc1Q=
|
||||
github.com/digitalocean/go-libvirt v0.0.0-20210108193637-3a8ae49ba8cd/go.mod h1:gtar3MgGsIO64GgphCHw1cbyxSI6qEuTIm9+izMmlfk=
|
||||
github.com/digitalocean/go-libvirt v0.0.0-20210112203132-25518eb2c840/go.mod h1:gtar3MgGsIO64GgphCHw1cbyxSI6qEuTIm9+izMmlfk=
|
||||
github.com/digitalocean/go-qemu v0.0.0-20181112162955-dd7bb9c771b8/go.mod h1:/YnlngP1PARC0SKAZx6kaAEMOp8bNTQGqS+Ka3MctNI=
|
||||
github.com/digitalocean/go-qemu v0.0.0-20201211181942-d361e7b4965f/go.mod h1:y4Eq3ZfZQFWQwVyW0qvgo5seXUIq2C7BlHsdE+xtXL4=
|
||||
github.com/digitalocean/godo v1.11.1/go.mod h1:h6faOIcZ8lWIwNQ+DN7b3CgX4Kwby5T+nbpNqkUIozU=
|
||||
github.com/dimchansky/utfbom v1.1.0/go.mod h1:rO41eb7gLfo8SF1jd9F8HplJm1Fewwi4mQvIirEdv+8=
|
||||
github.com/dnaeon/go-vcr v1.0.1/go.mod h1:aBB1+wY4s93YsC3HHjMBMrwTj2R9FHDzUr9KyGc8n1E=
|
||||
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=
|
||||
@ -140,34 +92,24 @@ github.com/envoyproxy/go-control-plane v0.9.0/go.mod h1:YTl/9mNaCwkRvm6d1a2C3ymF
|
||||
github.com/envoyproxy/go-control-plane v0.9.1-0.20191026205805-5f8ba28d4473/go.mod h1:YTl/9mNaCwkRvm6d1a2C3ymFceY/DCBVvsKhRF0iEA4=
|
||||
github.com/envoyproxy/go-control-plane v0.9.4/go.mod h1:6rpuAdCZL397s3pYoYcLgu1mIlRU8Am5FuJP05cCM98=
|
||||
github.com/envoyproxy/protoc-gen-validate v0.1.0/go.mod h1:iSmxcyjqTsJpI2R4NaDN7+kN2VEUnK/pcBlmesArF7c=
|
||||
github.com/exoscale/egoscale v0.18.1/go.mod h1:Z7OOdzzTOz1Q1PjQXumlz9Wn/CddH0zSYdCF3rnBKXE=
|
||||
github.com/fatih/camelcase v1.0.0/go.mod h1:yN2Sb0lFhZJUdVvtELVWefmrXpuZESvPmqwoZc+/fpc=
|
||||
github.com/fatih/color v1.7.0/go.mod h1:Zm6kSWBoL9eyXnKyktHP6abPY2pDugNf5KwzbycvMj4=
|
||||
github.com/fatih/color v1.9.0 h1:8xPHl4/q1VyqGIPif1F+1V3Y3lSmrq01EabUW3CoW5s=
|
||||
github.com/fatih/color v1.9.0/go.mod h1:eQcE1qtQxscV5RaZvpXrrb8Drkc3/DdQ+uUYCNjL+zU=
|
||||
github.com/fatih/structs v1.1.0/go.mod h1:9NiDSp5zOcgEDl+j00MP/WkGVPOlPRLejGD8Ga6PJ7M=
|
||||
github.com/fatih/structtag v1.0.0/go.mod h1:IKitwq45uXL/yqi5mYghiD3w9H6eTOvI9vnk8tXMphA=
|
||||
github.com/ghodss/yaml v1.0.0/go.mod h1:4dBDuWmgqj2HViK6kFavaiC9ZROes6MMH2rRYeMEF04=
|
||||
github.com/fatih/structtag v1.2.0/go.mod h1:mBJUNpUnHmRKrKlQQlmCrh5PuhftFbNv8Ys4/aAZl94=
|
||||
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-ini/ini v1.25.4/go.mod h1:ByCAeIL28uOIIG0E3PJtZPDL8WnHpFKFOtgjp+3Ies8=
|
||||
github.com/go-ldap/ldap v3.0.2+incompatible/go.mod h1:qfd9rJvER9Q0/D/Sqn1DfHRoBp40uXYvFoEVrNEPqRc=
|
||||
github.com/go-ole/go-ole v1.2.4/go.mod h1:XCwSNxSkXRo4vlyPy93sltvi/qJq0jqQhjqQNIwKuxM=
|
||||
github.com/go-ole/go-ole v1.2.5/go.mod h1:pprOEPIfldk/42T2oK7lQ4v4JSDwmV0As9GaiUsvbm0=
|
||||
github.com/go-resty/resty/v2 v2.1.1-0.20191201195748-d7b97669fe48/go.mod h1:dZGr0i9PLlaaTD4H/hoZIDjQ+r6xq8mgbRzHZf7f2J8=
|
||||
github.com/go-resty/resty/v2 v2.3.0/go.mod h1:UpN9CgLZNsv4e9XG50UU8xdI0F43UQ4HmxLBDwaroHU=
|
||||
github.com/go-sql-driver/mysql v1.5.0/go.mod h1:DCzpHaOWr8IXmIStZouvnhqoel9Qv2LBy8hT2VhHyBg=
|
||||
github.com/go-test/deep v1.0.2-0.20181118220953-042da051cf31/go.mod h1:wGDj63lr65AM2AQyKZd/NYHGb0R+1RLqB8NKt3aSFNA=
|
||||
github.com/go-test/deep v1.0.3 h1:ZrJSEWsXzPOxaZnFteGEfooLba+ju3FYIbOrS+rQd68=
|
||||
github.com/go-test/deep v1.0.3/go.mod h1:wGDj63lr65AM2AQyKZd/NYHGb0R+1RLqB8NKt3aSFNA=
|
||||
github.com/gobwas/glob v0.2.3/go.mod h1:d3Ez4x06l9bZtSvzIay5+Yzi0fmZzPgnTbPcKjJAkT8=
|
||||
github.com/gofrs/flock v0.7.3 h1:I0EKY9l8HZCXTMYC4F80vwT6KNypV9uYKP3Alm/hjmQ=
|
||||
github.com/gofrs/flock v0.7.3/go.mod h1:F1TvTiK9OcQqauNUHlbJvyl9Qa1QvF/gOUDKA14jxHU=
|
||||
github.com/gofrs/uuid v3.2.0+incompatible h1:y12jRkkFxsd7GpqdSZ+/KCs/fJbqpEXSGd4+jfEaewE=
|
||||
github.com/gofrs/uuid v3.2.0+incompatible/go.mod h1:b2aQJv3Z4Fp6yNu3cdSllBxTCLRxnplIgP/c0N/04lM=
|
||||
github.com/gogo/protobuf v1.2.1/go.mod h1:hp+jE20tsWTFYpLwKvXlhS1hjn+gTNwPg2I6zVXpSg4=
|
||||
github.com/golang-collections/collections v0.0.0-20130729185459-604e922904d3/go.mod h1:nPpo7qLxd6XL3hWJG/O60sR8ZKfMCiIoNap5GvD12KU=
|
||||
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/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=
|
||||
@ -207,12 +149,9 @@ github.com/google/go-cmp v0.4.0/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/
|
||||
github.com/google/go-cmp v0.4.1/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.1/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
|
||||
github.com/google/go-cmp v0.5.2 h1:X2ev0eStA3AbceY54o37/0PQ/UWqKEiiO2dKL5OPaFM=
|
||||
github.com/google/go-cmp v0.5.2/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
|
||||
github.com/google/go-github/v33 v33.0.0/go.mod h1:GMdDnVZY/2TsWgp/lkYnpSAh6TrzhANBBwm6k6TTEXg=
|
||||
github.com/google/go-github/v33 v33.0.1-0.20210113204525-9318e629ec69/go.mod h1:GMdDnVZY/2TsWgp/lkYnpSAh6TrzhANBBwm6k6TTEXg=
|
||||
github.com/google/go-querystring v1.0.0/go.mod h1:odCYkC5MyYFN7vkCjXpyrEuKhc/BUO6wN/zVPAxq5ck=
|
||||
github.com/google/gofuzz v1.0.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg=
|
||||
github.com/google/go-cmp v0.5.5 h1:Khx7svrCpmxxtHBq5j2mp/xVjsi8hQMfNLvJFAlrGgU=
|
||||
github.com/google/go-cmp v0.5.5/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
|
||||
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.0.0 h1:pMen7vLs8nvgEYhywH3KDWJIJTeEr2ULsVWHWYHQyBs=
|
||||
@ -227,32 +166,21 @@ github.com/google/pprof v0.0.0-20200708004538-1a94d8640e99/go.mod h1:ZgVRPoUq/hf
|
||||
github.com/google/pprof v0.0.0-20200905233945-acf8798be1f7/go.mod h1:ZgVRPoUq/hfqzAqh7sHMqb3I9Rq5C59dIz2SbBwJ4eM=
|
||||
github.com/google/renameio v0.1.0/go.mod h1:KWCgfxg9yswjAJkECMjeO8J8rahYeXnNhOm40UhjYkI=
|
||||
github.com/google/shlex v0.0.0-20150127133951-6f45313302b9/go.mod h1:RpwtwJQFrIEPstU94h88MWPXP2ektJZ8cZ0YntAmXiE=
|
||||
github.com/google/uuid v0.0.0-20170306145142-6a5e28554805/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
|
||||
github.com/google/uuid v1.1.1/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
|
||||
github.com/google/uuid v1.1.2 h1:EVhdT+1Kseyi1/pUmXKaFxYsDNy9RQYkMWRH68J/W7Y=
|
||||
github.com/google/uuid v1.1.2/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
|
||||
github.com/googleapis/gax-go/v2 v2.0.4/go.mod h1:0Wqv26UfaUD9n4G6kQubkQ+KchISgw+vpHVxEJEs9eg=
|
||||
github.com/googleapis/gax-go/v2 v2.0.5 h1:sjZBwGj9Jlw33ImPtvFviGYvseOtDM7hkSKB7+Tv3SM=
|
||||
github.com/googleapis/gax-go/v2 v2.0.5/go.mod h1:DWXyrwAJ9X0FpwwEdw+IPEYBICEFu5mhpdKc/us6bOk=
|
||||
github.com/gophercloud/gophercloud v0.6.1-0.20191122030953-d8ac278c1c9d/go.mod h1:ozGNgr9KYOVATV5jsgHl/ceCDXGuguqOZAzoQ/2vcNM=
|
||||
github.com/gophercloud/gophercloud v0.12.0/go.mod h1:gmC5oQqMDOMO1t1gq5DquX/yAU808e/4mzjjDA76+Ss=
|
||||
github.com/gophercloud/utils v0.0.0-20200508015959-b0167b94122c/go.mod h1:ehWUbLQJPqS0Ep+CxeD559hsm9pthPXadJNKwZkp43w=
|
||||
github.com/gopherjs/gopherjs v0.0.0-20181017120253-0766667cb4d1/go.mod h1:wJfORRmW1u3UXTncJ5qlYoELFm8eSnnEO6hX4iZ3EWY=
|
||||
github.com/gopherjs/gopherjs v0.0.0-20181103185306-d547d1d9531e/go.mod h1:wJfORRmW1u3UXTncJ5qlYoELFm8eSnnEO6hX4iZ3EWY=
|
||||
github.com/grpc-ecosystem/go-grpc-middleware v1.1.0/go.mod h1:f5nM7jw/oeRSadq3xCzHAvxcr8HZnzsqU6ILg/0NiiE=
|
||||
github.com/hako/durafmt v0.0.0-20200710122514-c0fb7b4da026/go.mod h1:5Scbynm8dF1XAPwIwkGPqzkM/shndPm79Jd1003hTjE=
|
||||
github.com/hashicorp/aws-sdk-go-base v0.6.0/go.mod h1:2fRjWDv3jJBeN6mVWFHV6hFTNeFBx2gpDLQaZNxUVAY=
|
||||
github.com/hashicorp/consul/api v1.4.0 h1:jfESivXnO5uLdH650JU/6AnjRoHrLhULq0FnC3Kp9EY=
|
||||
github.com/hashicorp/consul/api v1.4.0/go.mod h1:xc8u05kyMa3Wjr9eEAsIAo3dg8+LywT5E/Cl7cNS5nU=
|
||||
github.com/hashicorp/consul/sdk v0.4.0 h1:zBtCfKJZcJDBvSCkQJch4ulp59m1rATFLKwNo/LYY30=
|
||||
github.com/hashicorp/consul/sdk v0.4.0/go.mod h1:fY08Y9z5SvJqevyZNy6WWPXiG3KwBPAvlcdx16zZ0fM=
|
||||
github.com/hashicorp/errwrap v1.0.0 h1:hLrqtEDnRye3+sgx6z4qVLNuviH3MR5aQ0ykNJa/UYA=
|
||||
github.com/hashicorp/errwrap v1.0.0/go.mod h1:YH+1FKiLXxHSkmPseP+kNlulaMuP3n2brvKWEqk/Jc4=
|
||||
github.com/hashicorp/go-checkpoint v0.0.0-20171009173528-1545e56e46de/go.mod h1:xIwEieBHERyEvaeKF/TcHh1Hu+lxPM+n2vT1+g9I4m4=
|
||||
github.com/hashicorp/go-cleanhttp v0.5.0/go.mod h1:JpRdi6/HCYpAwUzNwuwqhbovhLtngrth3wmdIIUrZ80=
|
||||
github.com/hashicorp/go-cleanhttp v0.5.1 h1:dH3aiDG9Jvb5r5+bYHsikaOUIpcM0xvgMXVoDkXMzJM=
|
||||
github.com/hashicorp/go-cleanhttp v0.5.1/go.mod h1:JpRdi6/HCYpAwUzNwuwqhbovhLtngrth3wmdIIUrZ80=
|
||||
github.com/hashicorp/go-cty-funcs v0.0.0-20200930094925-2721b1e36840/go.mod h1:Abjk0jbRkDaNCzsRhOv2iDCofYpX1eVsjozoiK63qLA=
|
||||
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 v1.4.1 h1:3A2Mh8smGFcf5M+gmcv898mZdrxpseik45IpcyISLsA=
|
||||
github.com/hashicorp/go-getter v1.4.1/go.mod h1:7qxyCd8rBfcShwsvxgIguu4KbS3l8bUCwg2Umn7RjeY=
|
||||
github.com/hashicorp/go-getter/gcs/v2 v2.0.0-20200604122502-a6995fa1edad h1:QPLyAkuTS5Uf9uqJQxCTDDFgmD+gVAzSvqkGb3h+7oQ=
|
||||
@ -260,8 +188,8 @@ github.com/hashicorp/go-getter/gcs/v2 v2.0.0-20200604122502-a6995fa1edad/go.mod
|
||||
github.com/hashicorp/go-getter/s3/v2 v2.0.0-20200604122502-a6995fa1edad h1:hwk5mQRwVvZc/f+So1kHbOhW/f7P2fEcCr2D5pSk9sI=
|
||||
github.com/hashicorp/go-getter/s3/v2 v2.0.0-20200604122502-a6995fa1edad/go.mod h1:kcB6Mv+0wzYXbQjTAeD/Pb85145WcFk2EElpe02fuoE=
|
||||
github.com/hashicorp/go-getter/v2 v2.0.0-20200511090339-3107ec4af37a/go.mod h1:QJ+LwRM91JBKBLyHoKBrcmi49i9Tu/ItpgTNXWSnpGM=
|
||||
github.com/hashicorp/go-getter/v2 v2.0.0-20200604122502-a6995fa1edad h1:BfRfKjQgvwJrXF2apbeqoqeuxzDcxZIeI/eCj6HLkM4=
|
||||
github.com/hashicorp/go-getter/v2 v2.0.0-20200604122502-a6995fa1edad/go.mod h1:bM2M11foprILIk1Slmr2wBrJYsdQTym//+QMJqgm3F8=
|
||||
github.com/hashicorp/go-getter/v2 v2.0.0 h1:wamdcQazMBZK6VwUo3HAOWLkcOJBWBoXPKfmf7/S17w=
|
||||
github.com/hashicorp/go-getter/v2 v2.0.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.8.0/go.mod h1:5CU+agLiy3J7N7QjHK5d05KxGsuXiQLrjA0H7acj2lQ=
|
||||
github.com/hashicorp/go-hclog v0.12.0 h1:d4QkX8FRTYaKaCZBoXYY8zJX2BXjWxurN/GA2tkrmZM=
|
||||
@ -273,9 +201,9 @@ github.com/hashicorp/go-msgpack v0.5.3/go.mod h1:ahLV/dePpqEmjfWmKiqvPkv/twdG7iP
|
||||
github.com/hashicorp/go-msgpack v0.5.5 h1:i9R9JSrqIz0QVLz3sz+i3YJdT7TTSLcfLLzJi9aZTuI=
|
||||
github.com/hashicorp/go-msgpack v0.5.5/go.mod h1:ahLV/dePpqEmjfWmKiqvPkv/twdG7iPBM1vqhUKIvfM=
|
||||
github.com/hashicorp/go-multierror v1.0.0/go.mod h1:dHtQlpGsu+cZNNAkkCN/P3hoUDHhCYQXV3UM06sGGrk=
|
||||
github.com/hashicorp/go-multierror v1.1.0 h1:B9UzwGQJehnUY1yNrnwREHc3fGbC2xefo8g4TbElacI=
|
||||
github.com/hashicorp/go-multierror v1.1.0/go.mod h1:spPvp8C1qA32ftKqdAHm4hHTbPw+vmowP0z+KUhOZdA=
|
||||
github.com/hashicorp/go-oracle-terraform v0.0.0-20181016190316-007121241b79/go.mod h1:09jT3Y/OIsjTjQ2+3bkVNPDKqWcGIYYvjB2BEKVUdvc=
|
||||
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.5.4 h1:1BZvpawXoJCWX6pNtow9+rpEj+3itIlutiqnntI6jOE=
|
||||
@ -293,7 +221,6 @@ github.com/hashicorp/go-uuid v1.0.0/go.mod h1:6SBZvOh/SIDV7/2o3Jml5SYk/TvGqwFJ/b
|
||||
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.0.0/go.mod h1:fltr4n8CU8Ke44wwGCBoEymUuxUHl09ZGVZPK5anwXA=
|
||||
github.com/hashicorp/go-version v1.1.0/go.mod h1:fltr4n8CU8Ke44wwGCBoEymUuxUHl09ZGVZPK5anwXA=
|
||||
github.com/hashicorp/go-version v1.2.0 h1:3vNe/fWF5CBgRIguda1meWhsZHy3m8gCJ5wx+dIzX/E=
|
||||
github.com/hashicorp/go-version v1.2.0/go.mod h1:fltr4n8CU8Ke44wwGCBoEymUuxUHl09ZGVZPK5anwXA=
|
||||
@ -304,34 +231,16 @@ github.com/hashicorp/golang-lru v0.5.3 h1:YPkqC67at8FYaadspW/6uE0COsBxS2656RLEr8
|
||||
github.com/hashicorp/golang-lru v0.5.3/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.6.0/go.mod h1:bQTN5mpo+jewjJgh8jr0JUguIi7qPHUF6yIfAEN3jqY=
|
||||
github.com/hashicorp/hcl/v2 v2.8.0 h1:iHLEAsNDp3N2MtqroP1wf0nF/zB2+McHN5YCzwqIm80=
|
||||
github.com/hashicorp/hcl/v2 v2.8.0/go.mod h1:bQTN5mpo+jewjJgh8jr0JUguIi7qPHUF6yIfAEN3jqY=
|
||||
github.com/hashicorp/hcl/v2 v2.10.0 h1:1S1UnuhDGlv3gRFV4+0EdwB+znNP5HmcGbIqwnSCByg=
|
||||
github.com/hashicorp/hcl/v2 v2.10.0/go.mod h1:FwWsfWEjyV/CMj8s/gqAuiviY72rJ1/oayI9WftqcKg=
|
||||
github.com/hashicorp/logutils v1.0.0/go.mod h1:QIAnNjmIWmVIIkWDTG1z5v++HQmx9WQRO+LraFDTW64=
|
||||
github.com/hashicorp/mdns v1.0.0/go.mod h1:tL+uN++7HEJ6SQLQ2/p+z2pH24WQKWjBPkE0mNTz8vQ=
|
||||
github.com/hashicorp/mdns v1.0.1/go.mod h1:4gW7WsVCke5TE7EPeYliwHlRUyBtfCwuFwuMg2DmyNY=
|
||||
github.com/hashicorp/memberlist v0.1.3/go.mod h1:ajVTdAv/9Im8oMAAj5G31PhhMCZJV2pPBoIllUwCN7I=
|
||||
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 v1.6.6/go.mod h1:meJucaEeJro8UP1jw+KaOCpaiC4VE+itPLXY8lbIU2A=
|
||||
github.com/hashicorp/packer v1.6.7-0.20210107234516-6564ee76e807/go.mod h1:fBz288Z4of8zkpDWwL/ngG1txC36jGSXS7dnUmUaLUs=
|
||||
github.com/hashicorp/packer v1.6.7-0.20210112155033-1bc89e34714f/go.mod h1:sKsuxExP+0/WCnIwwCRFFEHKy9TuQrZcwqbidE2oSq8=
|
||||
github.com/hashicorp/packer v1.6.7-0.20210120105538-140685fb4f8b/go.mod h1:Yaw0q8brThFN2PKMWMRcuhQBuwPVDJRHHioNuMJTS7Q=
|
||||
github.com/hashicorp/packer v1.6.7-0.20210125170305-539638b0f951/go.mod h1:Z3eunaxVQ3XgQ+rW7TEH0T/PRQzCUSyCBUTkm/VL7io=
|
||||
github.com/hashicorp/packer v1.6.7-0.20210126105722-aef4ced967ec/go.mod h1:2+Vo/c/fA+TD9yFc/h9jQMFm4yG+IymQIr0OdJJOPiE=
|
||||
github.com/hashicorp/packer v1.6.7-0.20210208125835-f616955ebcb6/go.mod h1:7f5ZpTTRG53rQ58BcTADuTnpiBcB3wapuxl4sF2sGMM=
|
||||
github.com/hashicorp/packer v1.6.7-0.20210217093213-201869d627bf/go.mod h1:+EWPPcqee4h8S/y913Dnta1eJkgiqsGXBQgB75A2qV0=
|
||||
github.com/hashicorp/packer v1.7.0/go.mod h1:3KRJcwOctl2JaAGpQMI1bWQRArfWNWqcYjO6AOsVVGQ=
|
||||
github.com/hashicorp/packer-plugin-sdk v0.0.6/go.mod h1:Nvh28f+Jmpp2rcaN79bULTouNkGNDRfHckhHKTAXtyU=
|
||||
github.com/hashicorp/packer-plugin-sdk v0.0.7-0.20210111224258-fd30ebb797f0/go.mod h1:YdWTt5w6cYfaQG7IOi5iorL+3SXnz8hI0gJCi8Db/LI=
|
||||
github.com/hashicorp/packer-plugin-sdk v0.0.7-0.20210120105339-f6fd68d2570a/go.mod h1:exN0C+Pe+3zu18l4nxueNjX5cfmslxUX/m/xk4IVmZQ=
|
||||
github.com/hashicorp/packer-plugin-sdk v0.0.7-0.20210122130548-45a6ca0a9365/go.mod h1:K7VsU0lfJBDyiUrSNnS/j+zMxSRwwH9WC9QvHv32KsU=
|
||||
github.com/hashicorp/packer-plugin-sdk v0.0.10-0.20210126105622-8e1648006d93/go.mod h1:AtWQLNfpn7cgH2SmZ1PTedwqNOhiPvzcuKfH5sDvIQ0=
|
||||
github.com/hashicorp/packer-plugin-sdk v0.0.11/go.mod h1:GNb0WNs7zibb8vzUZce1As64z2AW0FEMwhe2J7/NW5I=
|
||||
github.com/hashicorp/packer-plugin-sdk v0.0.12/go.mod h1:hs82OYeufirGG6KRENMpjBWomnIlte99X6wXAPThJ5I=
|
||||
github.com/hashicorp/packer-plugin-sdk v0.0.14/go.mod h1:tNb3XzJPnjMl3QuUdKmF47B5ImerdTakalHzUAvW0aw=
|
||||
github.com/hashicorp/packer-plugin-sdk v0.1.0 h1:bZHQRGc6SffXgSkGuzZ8H3ILE0BCc0P7eBlJXRTDxbc=
|
||||
github.com/hashicorp/packer-plugin-sdk v0.1.0/go.mod h1:CFsC20uZjtER/EnTn/CSMKD0kEdkqOVev8mtOmfnZiI=
|
||||
github.com/hashicorp/packer-plugin-sdk v0.2.3 h1:Z/GEaZXweNCvjKXuHTlBffLDfah1GYwucZGjIz/eULA=
|
||||
github.com/hashicorp/packer-plugin-sdk v0.2.3/go.mod h1:MAOhxLneNh27t6N6SMyRcIR5qSE86e6yYCcEfRScwIE=
|
||||
github.com/hashicorp/serf v0.8.2/go.mod h1:6hOLApaqBFA1NXqRQAsxw9QxuDEvNxSQRwA/JwenrHc=
|
||||
github.com/hashicorp/serf v0.9.2 h1:yJoyfZXo4Pk2p/M/viW+YLibBFiIbKoP79gu7kDAFP0=
|
||||
github.com/hashicorp/serf v0.9.2/go.mod h1:UWDWwZeL5cuWDJdl0C6wrvrUwEqtQ4ZKBKKENpqIUyk=
|
||||
@ -342,61 +251,39 @@ github.com/hashicorp/vault/sdk v0.1.13/go.mod h1:B+hVj7TpuQY1Y/GPbCpffmgd+tSEwvh
|
||||
github.com/hashicorp/yamux v0.0.0-20180604194846-3520598351bb/go.mod h1:+NfK9FKeTrX5uv1uIXGdwYDTeHna2qgaIlx54MXqjAM=
|
||||
github.com/hashicorp/yamux v0.0.0-20181012175058-2f1d1f20f75d h1:kJCB4vdITiW1eC1vq2e6IsrXKrZit1bv/TDYFGMp4BQ=
|
||||
github.com/hashicorp/yamux v0.0.0-20181012175058-2f1d1f20f75d/go.mod h1:+NfK9FKeTrX5uv1uIXGdwYDTeHna2qgaIlx54MXqjAM=
|
||||
github.com/hetznercloud/hcloud-go v1.15.1/go.mod h1:8lR3yHBHZWy2uGcUi9Ibt4UOoop2wrVdERJgCtxsF3Q=
|
||||
github.com/hyperonecom/h1-client-go v0.0.0-20191203060043-b46280e4c4a4/go.mod h1:yNUVHSleURKSaYUKq4Wx0i/vjCen2aq7CvPyHd/Vj2Q=
|
||||
github.com/ianlancetaylor/demangle v0.0.0-20181102032728-5e5cf60278f6/go.mod h1:aSSvb/t6k1mPoxDqO4vJh6VOCGPwU4O0C2/Eqndh1Sc=
|
||||
github.com/jdcloud-api/jdcloud-sdk-go v1.9.1-0.20190605102154-3d81a50ca961/go.mod h1:UrKjuULIWLjHFlG6aSPunArE5QX57LftMmStAZJBEX8=
|
||||
github.com/jehiah/go-strftime v0.0.0-20171201141054-1d33003b3869 h1:IPJ3dvxmJ4uczJe5YQdrYB16oTJlGSC/OyZDqUk9xX4=
|
||||
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-20160202185014-0b12d6b521d8/go.mod h1:Nht3zPeWKUH0NzdCt2Blrr5ys8VGpn0CEB0cQHVjt7k=
|
||||
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/joyent/triton-go v0.0.0-20180628001255-830d2b111e62/go.mod h1:U+RSyWxWd04xTqnuOQxnai7XGS2PrPY2cfGoDKtMHjA=
|
||||
github.com/json-iterator/go v1.1.6/go.mod h1:+SdeFBvtyEkXs7REEP0seUULqWtbJapLOCVDaaPEHmU=
|
||||
github.com/json-iterator/go v1.1.10/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 h1:6QPYqodiu3GuPL+7mfx+NwDdp2eTkp9IfEUpgAwUN0o=
|
||||
github.com/jstemmer/go-junit-report v0.9.1/go.mod h1:Brl9GWCQeLvo8nXZwPNNblvFj/XSXhF0NWZEnDohbsk=
|
||||
github.com/jtolds/gls v4.2.1+incompatible/go.mod h1:QJZ7F/aHp+rZTRtaJ1ow/lLfFfVYBRgL+9YlvaHOwJU=
|
||||
github.com/jtolds/gls v4.20.0+incompatible/go.mod h1:QJZ7F/aHp+rZTRtaJ1ow/lLfFfVYBRgL+9YlvaHOwJU=
|
||||
github.com/kisielk/errcheck v1.1.0/go.mod h1:EZBBE59ingxPouuu3KfxchcWSUPOHkagtvWXihfKN4Q=
|
||||
github.com/kisielk/gotool v1.0.0/go.mod h1:XhKaO+MFFWcvkIS/tQcRk01m1F5IRFswLeQ+oQHNcck=
|
||||
github.com/klauspost/compress v0.0.0-20160131094358-f86d2e6d8a77/go.mod h1:RyIbtBH6LamlWaDj8nUwkbUhJ87Yi3uG0guNDohfE1A=
|
||||
github.com/klauspost/compress v1.11.6/go.mod h1:aoV0uJVorq1K+umq18yTdKaF57EivdYsUV+/s2qKfXs=
|
||||
github.com/klauspost/compress v1.11.7/go.mod h1:aoV0uJVorq1K+umq18yTdKaF57EivdYsUV+/s2qKfXs=
|
||||
github.com/klauspost/cpuid v0.0.0-20160106104451-349c67577817/go.mod h1:Pj4uuM528wm8OyEC2QMXAi2YiTZ96dNQPGgoMS4s3ek=
|
||||
github.com/klauspost/crc32 v0.0.0-20160114101742-999f3125931f/go.mod h1:+ZoRqAPRLkC4NPOvfYeR5KNOrY6TD+/sAC3HXPZgDYg=
|
||||
github.com/klauspost/crc32 v1.2.0/go.mod h1:+ZoRqAPRLkC4NPOvfYeR5KNOrY6TD+/sAC3HXPZgDYg=
|
||||
github.com/klauspost/pgzip v0.0.0-20151221113845-47f36e165cec/go.mod h1:Ch1tH69qFZu15pkjo5kYi6mth2Zzwzt50oCQKQE9RUs=
|
||||
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.0.0-20131111012553-2788f0dbd169/go.mod h1:glhvuHOU9Hy7/8PwwdtnarXqLagOX0b/TbZx2zLMqEg=
|
||||
github.com/kr/fs v0.1.0 h1:Jskdu9ieNAYnjxsi0LbQp1ulIKZV1LAFgK1tWhpZgl8=
|
||||
github.com/kr/fs v0.1.0/go.mod h1:FFnZGqtBN9Gxj7eW1uZ42v5BccTP0vu6NEaFoC2HwRg=
|
||||
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 h1:45sCR5RtlFHMR4UwH9sdQ5TC8v0qDQCHnXt+kaKSTVE=
|
||||
github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI=
|
||||
github.com/kylelemons/godebug v0.0.0-20170820004349-d65d576e9348 h1:MtvEpTB6LX3vkb4ax0b5D2DHbNAUsen0Gx5wZoq3lV4=
|
||||
github.com/kylelemons/godebug v0.0.0-20170820004349-d65d576e9348/go.mod h1:B69LEHPfb2qLo0BaaOLcbitczOKLWTsrBG9LczfCD4k=
|
||||
github.com/linode/linodego v0.14.0/go.mod h1:2ce3S00NrDqJfp4i55ZuSlT0U3cKNELNYACWBPI8Tnw=
|
||||
github.com/masterzen/simplexml v0.0.0-20160608183007-4572e39b1ab9/go.mod h1:kCEbxUJlNDEBNbdQMkPSp6yaKcRXVI6f4ddk8Riv4bc=
|
||||
github.com/masterzen/simplexml v0.0.0-20190410153822-31eea3082786 h1:2ZKn+w/BJeL43sCxI2jhPLRv73oVVOjEKZjKkflyqxg=
|
||||
github.com/masterzen/simplexml v0.0.0-20190410153822-31eea3082786/go.mod h1:kCEbxUJlNDEBNbdQMkPSp6yaKcRXVI6f4ddk8Riv4bc=
|
||||
github.com/masterzen/winrm v0.0.0-20200615185753-c42b5136ff88/go.mod h1:a2HXwefeat3evJHxFXSayvRHpYEPJYtErl4uIzfaUqY=
|
||||
github.com/masterzen/winrm v0.0.0-20201030141608-56ca5c5f2380 h1:uKhPH5dYpx3Z8ZAnaTGfGZUiHOWa5p5mdG8wZlh+tLo=
|
||||
github.com/masterzen/winrm v0.0.0-20201030141608-56ca5c5f2380/go.mod h1:a2HXwefeat3evJHxFXSayvRHpYEPJYtErl4uIzfaUqY=
|
||||
github.com/masterzen/winrm v0.0.0-20210504160029-28ed956f5227 h1:Vcl9dr3dZMIEGpwP1+QSkqFUVQVOopV1nP+I3a6r6tw=
|
||||
github.com/masterzen/winrm v0.0.0-20210504160029-28ed956f5227/go.mod h1:a2HXwefeat3evJHxFXSayvRHpYEPJYtErl4uIzfaUqY=
|
||||
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 h1:6Su7aK7lXmJ/U79bYtBjLNaha4Fs1Rg9plHpcH+vvnE=
|
||||
github.com/mattn/go-colorable v0.1.6/go.mod h1:u6P/XSegPjTcexA+o6vUJrdnUu04hMope9wVRipJSqc=
|
||||
github.com/mattn/go-colorable v0.1.8 h1:c1ghPdyEDarC70ftn0y+A/Ee++9zz8ljHG1b13eJ0s8=
|
||||
github.com/mattn/go-colorable v0.1.8/go.mod h1:u6P/XSegPjTcexA+o6vUJrdnUu04hMope9wVRipJSqc=
|
||||
github.com/mattn/go-isatty v0.0.3/go.mod h1:M+lRXTBqGeGNdLjl/ufCoiOlB5xdOkqRJdNxMWT7Zi4=
|
||||
github.com/mattn/go-isatty v0.0.4/go.mod h1:M+lRXTBqGeGNdLjl/ufCoiOlB5xdOkqRJdNxMWT7Zi4=
|
||||
github.com/mattn/go-isatty v0.0.8/go.mod h1:Iq45c/XA43vh69/j3iqttzPXn0bhXyGjM0Hdxcsrc5s=
|
||||
@ -405,8 +292,6 @@ github.com/mattn/go-isatty v0.0.11/go.mod h1:PhnuNfih5lzO57/f3n+odYbM4JtupLOxQOA
|
||||
github.com/mattn/go-isatty v0.0.12 h1:wuysRhFDzyxgEmMf5xjvJ2M9dZoWAXNNr5LSBS7uHXY=
|
||||
github.com/mattn/go-isatty v0.0.12/go.mod h1:cbi8OIDigv2wuxKPP5vlRcQ1OAZbq2CE4Kysco4FUpU=
|
||||
github.com/mattn/go-runewidth v0.0.4/go.mod h1:LwmH8dsx7+W8Uxz3IHJYH5QSwggIsqBzpuz5H//U1FU=
|
||||
github.com/mattn/go-runewidth v0.0.10/go.mod h1:RAqKPSqVFrSLVXbA8x7dzmKdmGzieGRCM46jaSJTDAk=
|
||||
github.com/mattn/go-tty v0.0.0-20191112051231-74040eebce08/go.mod h1:XPvLUNfbS4fJH25nqRHfWLMa1ONC8Amw+mIA639KxkE=
|
||||
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=
|
||||
@ -429,31 +314,19 @@ github.com/mitchellh/go-wordwrap v0.0.0-20150314170334-ad45545899c7/go.mod h1:ZX
|
||||
github.com/mitchellh/go-wordwrap v1.0.0 h1:6GlHJ/LTGMrIJbwgdqdl2eEH8o+Exx/0m8ir9Gns0u4=
|
||||
github.com/mitchellh/go-wordwrap v1.0.0/go.mod h1:ZXFpozHsX6DPmq2I0TCekCxypsnAUbP2oI0UX1GXzOo=
|
||||
github.com/mitchellh/gox v0.4.0/go.mod h1:Sd9lOJ0+aimLBi73mGofS1ycjY8lL3uZM3JPS42BGNg=
|
||||
github.com/mitchellh/gox v1.0.1/go.mod h1:ED6BioOGXMswlXa2zxfh/xdd5QhwYliBFn9V18Ap4z4=
|
||||
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.2.3/go.mod h1:bFUtVrKA4DC2yAKiSyO/QUcy7e+RRV2QTWOzhPopBRo=
|
||||
github.com/mitchellh/mapstructure v1.4.0 h1:7ks8ZkOP5/ujthUsT07rNv+nkLXCQWKNHuwzOAesEks=
|
||||
github.com/mitchellh/mapstructure v1.4.0/go.mod h1:bFUtVrKA4DC2yAKiSyO/QUcy7e+RRV2QTWOzhPopBRo=
|
||||
github.com/mitchellh/panicwrap v1.0.0/go.mod h1:pKvZHwWrZowLUzftuFq7coarnxbBXU4aQh3N0BJOeeA=
|
||||
github.com/mitchellh/prefixedio v0.0.0-20151214002211-6e6954073784/go.mod h1:kB1naBgV9ORnkiTVeyJOI1DavaJkG4oNIq0Af6ZVKUo=
|
||||
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/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/oklog/run v1.0.0/go.mod h1:dlhp/R75TPv97u0XWUtDeV/lRKWPKSdTuV0TZvrmrQA=
|
||||
github.com/olekukonko/tablewriter v0.0.0-20180105111133-96aac992fc8b/go.mod h1:vsDQFd/mU46D+Z4whnwzcISnGGzXWMclvtLoiIKAKIo=
|
||||
github.com/opentracing/opentracing-go v1.1.0/go.mod h1:UkNAQd3GIcIGf0SeVgPpRdFStlNbqXla1AfSYxPUl2o=
|
||||
github.com/oracle/oci-go-sdk v18.0.0+incompatible/go.mod h1:VQb79nF8Z2cwLkLS35ukwStZIg5F66tcBccjip/j888=
|
||||
github.com/outscale/osc-sdk-go/osc v0.0.0-20200722135656-d654809d0699/go.mod h1:5AqqNH1X8zCHescKVlpSHRzrat1KCKDXqZoQPe8fY3A=
|
||||
github.com/packer-community/winrmcp v0.0.0-20180921204643-0fd363d6159a h1:A3QMuteviunoaY/8ex+RKFqwhcZJ/Cf3fCW3IwL2wx4=
|
||||
github.com/packer-community/winrmcp v0.0.0-20180921204643-0fd363d6159a/go.mod h1:f6Izs6JvFTdnRbziASagjZ2vmf55NSIkC/weStxCHqk=
|
||||
github.com/pascaldekloe/goe v0.0.0-20180627143212-57f6aae5913c/go.mod h1:lzWF7FIEvWOWxwDKqyGYQf6ZUaNfKdP144TG7ZOy1lc=
|
||||
@ -461,7 +334,6 @@ github.com/pascaldekloe/goe v0.1.0 h1:cBOtyMzM9HTpWjXfbbunk26uA6nG3a8n06Wieeh0Mw
|
||||
github.com/pascaldekloe/goe v0.1.0/go.mod h1:lzWF7FIEvWOWxwDKqyGYQf6ZUaNfKdP144TG7ZOy1lc=
|
||||
github.com/pierrec/lz4 v2.0.5+incompatible h1:2xWsjqPFWcplujydGg4WmhC/6fZqK42wMM8aXeqhl0I=
|
||||
github.com/pierrec/lz4 v2.0.5+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/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=
|
||||
@ -471,13 +343,11 @@ github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZb
|
||||
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/profitbricks/profitbricks-sdk-go v4.0.2+incompatible/go.mod h1:T3/WrziK7fYH3C8ilAFAHe99R452/IzIG3YYkqaOFeQ=
|
||||
github.com/prometheus/client_golang v0.9.2/go.mod h1:OsXs2jCmiKlQ1lTBmv21f2mNfw4xf/QclQDMrYNZzcM=
|
||||
github.com/prometheus/client_model v0.0.0-20180712105110-5c3871d89910/go.mod h1:MbSGuTsp3dbXC40dX6PRTWyKYBIrTGTE9sqQNg2J8bo=
|
||||
github.com/prometheus/client_model v0.0.0-20190812154241-14fe0d1b01d4/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA=
|
||||
github.com/prometheus/common v0.0.0-20181126121408-4724e9255275/go.mod h1:daVV7qP5qjZbuso7PdcryaAu0sAZbrN9i7WWcTMWvro=
|
||||
github.com/prometheus/procfs v0.0.0-20181204211112-1dc9a6cbc91a/go.mod h1:c3At6R/oaqEKCNdg8wHV1ftS6bRYblBhIjjI8uT2IGk=
|
||||
github.com/rivo/uniseg v0.1.0/go.mod h1:J6wj4VEh+S6ZtnVlnTBMWIodfgj8LQOQFoIToxlJtxc=
|
||||
github.com/rogpeppe/go-internal v1.3.0/go.mod h1:M8bDsm7K2OlrFYOpmOWEs/qY81heoFRclV5y23lUDJ4=
|
||||
github.com/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=
|
||||
@ -485,71 +355,47 @@ github.com/ryanuber/go-glob v1.0.0 h1:iQh3xXAumdQ+4Ufa5b25cRpC5TYKlno6hsv6Cb3pkB
|
||||
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/satori/go.uuid v1.2.0/go.mod h1:dA0hQrYB0VpLJoorglMZABFdXlWrHn1NEOzdhQKdks0=
|
||||
github.com/scaleway/scaleway-sdk-go v1.0.0-beta.7/go.mod h1:CJJ5VAbozOl0yEw7nHB9+7BXTJbIn6h7W+f6Gau5IP8=
|
||||
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/sergi/go-diff v1.0.0/go.mod h1:0CfEIISq7TuYL3j771MWULgwwjU+GofnZX9QAmXWZgo=
|
||||
github.com/shirou/gopsutil v2.18.12+incompatible/go.mod h1:5b4v6he4MtMOwMlS0TUMTu2PcXUg8+E1lC7eC3UO/RA=
|
||||
github.com/shirou/w32 v0.0.0-20160930032740-bb4de0191aa4/go.mod h1:qsXQc7+bwAM3Q1u/4XEfrquwF8Lw7D7y5cD8CuHnfIc=
|
||||
github.com/sirupsen/logrus v1.3.0/go.mod h1:LxeOpSwHxABJmUn/MG1IvRgCAasNZTLOkJPxbbu5VWo=
|
||||
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/smartystreets/assertions v0.0.0-20180927180507-b2de0cb4f26d/go.mod h1:OnSkiWE9lh6wB0YB77sQom3nweQdgAjqCqsofrRNTgc=
|
||||
github.com/smartystreets/goconvey v0.0.0-20181108003508-044398e4856c/go.mod h1:XDJAKZRPZ1CvBcN2aX5YOUTYGHki24fSF0Iv48Ibg0s=
|
||||
github.com/smartystreets/goconvey v1.6.4/go.mod h1:syvi0/a8iFYH4r/RixwvyeAJjdLS9QV7WQ/tjFTllLA=
|
||||
github.com/spf13/pflag v1.0.2/go.mod h1:DYY7MBk1bdzusC3SYhjObp+wFpr4gzcvqqNjLnInEg4=
|
||||
github.com/spf13/pflag v1.0.5/go.mod h1:McXfInJRrz4CZXVZOBLb0bTZqETkiAhM9Iw0y3An2Bg=
|
||||
github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
|
||||
github.com/stretchr/objx v0.1.1/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
|
||||
github.com/stretchr/objx v0.2.0/go.mod h1:qt09Ya8vawLte6SNmTgCsAVtYtaKzEcn8ATUoHMkEqE=
|
||||
github.com/stretchr/testify v1.2.2/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs=
|
||||
github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI=
|
||||
github.com/stretchr/testify v1.4.0/go.mod h1:j7eGeouHqKxXV5pUuKE4zz7dFj8WfuZ+81PSLYec5m4=
|
||||
github.com/stretchr/testify v1.5.1/go.mod h1:5W2xD1RspED5o8YsWQXVCued0rvSQ+mT+I5cxcmMvtA=
|
||||
github.com/stretchr/testify v1.6.1 h1:hDPOHmpOpP40lSULcqw7IrRb/u7w6RpDC9399XyoNd0=
|
||||
github.com/stretchr/testify v1.6.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
|
||||
github.com/tencentcloud/tencentcloud-sdk-go v3.0.222+incompatible/go.mod h1:0PfYow01SHPMhKY31xa+EFz2RStxIqj6JFAJS+IkCi4=
|
||||
github.com/stretchr/testify v1.7.0 h1:nwc3DEeHmmLAfoZucVR881uASk0Mfjw8xYJ99tb5CcY=
|
||||
github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
|
||||
github.com/terra-farm/go-xen-api-client v0.0.1 h1:EigkWRuAwSktqwFwX1DV64RDWTZTTC0uwmP9W3xlLjE=
|
||||
github.com/terra-farm/go-xen-api-client v0.0.1/go.mod h1:DDTy4ADe11t55X7nHKZ92rsU6fGHDjLbGmn3+ZqvZ2w=
|
||||
github.com/tv42/httpunix v0.0.0-20150427012821-b75d8614f926/go.mod h1:9ESjWnEqriFuLhtthL60Sar/7RFoluCcXsuvEwTV5KM=
|
||||
github.com/ucloud/ucloud-sdk-go v0.16.3/go.mod h1:dyLmFHmUfgb4RZKYQP9IArlvQ2pxzFthfhwxRzOEPIw=
|
||||
github.com/ufilesdk-dev/ufile-gosdk v0.0.0-20190830075812-b4dbc4ef43a6/go.mod h1:R5FMQxkQ+QK/9Vz+jfnJP4rZIktYrRcWmuAnbOSkROI=
|
||||
github.com/ugorji/go v0.0.0-20151218193438-646ae4a518c1/go.mod h1:hnLbHMwcvSihnDhEfx2/BzKp2xb0Y+ErdfYcrs9tkJQ=
|
||||
github.com/ugorji/go v1.2.4 h1:cTciPbZ/VSOzCLKclmssnfQ/jyoVyOcJ3aoJyUV1Urc=
|
||||
github.com/ugorji/go v1.2.4/go.mod h1:EuaSCk8iZMdIspsu6HXH7X2UGKw1ezO4wCfGszGmmo4=
|
||||
github.com/ugorji/go/codec v1.2.4 h1:C5VurWRRCKjuENsbM6GYVw8W++WVW9rSxoACKIvxzz8=
|
||||
github.com/ugorji/go/codec v1.2.4/go.mod h1:bWBu1+kIRWcF8uMklKaJrR6fTWQOwAlrIzX22pHwryA=
|
||||
github.com/ulikunitz/xz v0.5.5 h1:pFrO0lVpTBXLpYw+pnLj6TbvHuyjXMfjGeCwSqCVwok=
|
||||
github.com/ulikunitz/xz v0.5.5/go.mod h1:2bypXElzHzzJZwzH67Y6wb67pO62Rzfn7BSiF4ABRW8=
|
||||
github.com/ulikunitz/xz v0.5.8 h1:ERv8V6GKqVi23rgu5cj9pVfVzJbOqAY2Ntl88O6c2nQ=
|
||||
github.com/ulikunitz/xz v0.5.8/go.mod h1:nbz6k7qbPmH4IRqmfOplQw/tblSgqTqBwxkY0oWt/14=
|
||||
github.com/vmihailenco/msgpack v3.3.3+incompatible/go.mod h1:fy3FlTQTDXWkZ7Bh6AcGMlsjHatGryHQYUTf1ShIgkk=
|
||||
github.com/vmihailenco/msgpack/v4 v4.3.12/go.mod h1:gborTTJjAo/GWTqqRjrLCn9pgNN+NXzzngzBKDPIqw4=
|
||||
github.com/vmihailenco/tagparser v0.1.1/go.mod h1:OeAg3pn3UbLjkWt+rN9oFYB6u/cQgqMEUPoW2WPyhdI=
|
||||
github.com/vmware/govmomi v0.23.1/go.mod h1:Y+Wq4lst78L85Ge/F8+ORXIWiKYqaro1vhAulACy9Lc=
|
||||
github.com/vmware/vmw-guestinfo v0.0.0-20170707015358-25eff159a728/go.mod h1:x9oS4Wk2s2u4tS29nEaDLdzvuHdB19CvSGJjPgkZJNk=
|
||||
github.com/xanzy/go-cloudstack v0.0.0-20190526095453-42f262b63ed0/go.mod h1:sBh287mCRwCz6zyXHMmw7sSZGPohVpnx+o+OY4M+i3A=
|
||||
github.com/yandex-cloud/go-genproto v0.0.0-20200915125933-33de72a328bd/go.mod h1:HEUYX/p8966tMUHHT+TsS0hF/Ca/NYwqprC5WXSDMfE=
|
||||
github.com/yandex-cloud/go-sdk v0.0.0-20200921111412-ef15ded2014c/go.mod h1:Zn/U9YKH0w8n83ezLps5eB6Jftc4gSoZWxVR8hgXgoY=
|
||||
github.com/yuin/goldmark v1.1.25/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74=
|
||||
github.com/yuin/goldmark v1.1.27/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74=
|
||||
github.com/yuin/goldmark v1.1.32/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74=
|
||||
github.com/yuin/goldmark v1.2.1/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74=
|
||||
github.com/zclconf/go-cty v1.0.0/go.mod h1:xnAOWiHeOqg2nWS62VtQ7pbOu17FtxJNW8RLEih+O3s=
|
||||
github.com/zclconf/go-cty v1.2.0/go.mod h1:hOPWgoHbaTUnI5k4D2ld+GRpFJSCe6bCM7m1q/N4PQ8=
|
||||
github.com/zclconf/go-cty v1.4.0/go.mod h1:nHzOclRkoj++EU9ZjSrZvRG0BXIWt8c7loYc0qXAFGQ=
|
||||
github.com/zclconf/go-cty v1.7.0 h1:yMqLinUwNCYkmiHjEH+luio1yGl35cjqVzjvdRg2WlY=
|
||||
github.com/zclconf/go-cty v1.7.0/go.mod h1:VDR4+I79ubFBGm1uJac1226K5yANQFHeauxPBoP54+o=
|
||||
github.com/zclconf/go-cty-yaml v1.0.1/go.mod h1:IP3Ylp0wQpYm50IHK8OZWKMu6sPJIUgKa8XhiVHura0=
|
||||
github.com/zclconf/go-cty v1.8.0/go.mod h1:vVKLxnk3puL4qRAv72AO+W99LUD4da90g3uUAzyuvAk=
|
||||
github.com/zclconf/go-cty v1.8.3 h1:48gwZXrdSADU2UW9eZKHprxAI7APZGW9XmExpJpSjT0=
|
||||
github.com/zclconf/go-cty v1.8.3/go.mod h1:vVKLxnk3puL4qRAv72AO+W99LUD4da90g3uUAzyuvAk=
|
||||
github.com/zclconf/go-cty-debug v0.0.0-20191215020915-b22d67c1ba0b/go.mod h1:ZRKQfBXbGkpdV6QMzT3rU1kSTAnfu1dO8dPKjYprgj8=
|
||||
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.22.4 h1:LYy1Hy3MJdrCdMwwzxA/dRok4ejH+RwNGbuoD9fCjto=
|
||||
go.opencensus.io v0.22.4/go.mod h1:yxeiOL68Rb0Xd1ddK5vPZ/oVn4vY4Ynel7k9FzqtOIw=
|
||||
go.uber.org/atomic v1.4.0/go.mod h1:gD2HeocX3+yG+ygLZcrzQJaqmWj9AIm7n08wl/qW/PE=
|
||||
go.uber.org/multierr v1.1.0/go.mod h1:wR5kodmAFQ0UK8QlbwjlSNy0Z68gJhDJUG5sjR94q/0=
|
||||
go.uber.org/zap v1.10.0/go.mod h1:vwi/ZaCAaUcBkycHslxD9B2zi4UTXhF60s6SWpuDF0Q=
|
||||
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=
|
||||
@ -558,12 +404,9 @@ golang.org/x/crypto v0.0.0-20190510104115-cbcb75029529/go.mod h1:yigFU9vqHzYiE8U
|
||||
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-20191202143827-86a70503ff7e/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=
|
||||
golang.org/x/crypto v0.0.0-20191206172530-e9b2fee46413/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=
|
||||
golang.org/x/crypto v0.0.0-20200422194213-44a606286825/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=
|
||||
golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=
|
||||
golang.org/x/crypto v0.0.0-20201208171446-5f87f3452ae9 h1:sYNJzB4J8toYPQTM6pAkcmBRgw9SnQKP9oXCHfgy604=
|
||||
golang.org/x/crypto v0.0.0-20201208171446-5f87f3452ae9/go.mod h1:jdWPYTVW3xRLrWPugEBEK3UY2ZEsg3UU495nc5E+M+I=
|
||||
golang.org/x/crypto v0.0.0-20201221181555-eec23a3978ad h1:DN0cp81fZ3njFcrLCytUHRSUkqBjfTo4Tx9RJTWs0EY=
|
||||
golang.org/x/crypto v0.0.0-20201221181555-eec23a3978ad/go.mod h1:jdWPYTVW3xRLrWPugEBEK3UY2ZEsg3UU495nc5E+M+I=
|
||||
golang.org/x/exp v0.0.0-20190121172915-509febef88a4/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA=
|
||||
golang.org/x/exp v0.0.0-20190306152737-a1d7652674e8/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA=
|
||||
golang.org/x/exp v0.0.0-20190510132918-efd6b22b2522/go.mod h1:ZjyILWgesfNpC6sMxTJOJm9Kp84zZh5NQWvqDGG3Qr8=
|
||||
@ -590,7 +433,7 @@ golang.org/x/lint v0.0.0-20200302205851-738671d3881b h1:Wh+f8QHJXR411sJR8/vRBTZ7
|
||||
golang.org/x/lint v0.0.0-20200302205851-738671d3881b/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/mobile v0.0.0-20191130191448-5c0e7e404af8/go.mod h1:p895TfNkDgPEmEQrNiOtIl3j98d/tGU95djDj7NfyjQ=
|
||||
golang.org/x/mobile v0.0.0-20201208152944-da85bec010a2 h1:3HADozU50HyrJ2jklLtr3xr0itFkz9u4LxCJhqKVdjI=
|
||||
golang.org/x/mobile v0.0.0-20201208152944-da85bec010a2/go.mod h1:skQtrUTUwhdJvXM/2KKJzY8pDgNr9I/FOMqDVRPBUS4=
|
||||
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=
|
||||
@ -616,14 +459,12 @@ golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLL
|
||||
golang.org/x/net v0.0.0-20190628185345-da137c7871d7/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
|
||||
golang.org/x/net v0.0.0-20190724013045-ca1201d0de80/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
|
||||
golang.org/x/net v0.0.0-20190923162816-aa69164e4478/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
|
||||
golang.org/x/net v0.0.0-20191126235420-ef20fe5d7933/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
|
||||
golang.org/x/net v0.0.0-20191209160850-c0dbc17a3553/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
|
||||
golang.org/x/net v0.0.0-20200114155413-6afb5195e5aa/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
|
||||
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-20200226121028-0de0cce0169b/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
|
||||
golang.org/x/net v0.0.0-20200301022130-244492dfa37a/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
|
||||
golang.org/x/net v0.0.0-20200320220750-118fecf932d8/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
|
||||
golang.org/x/net v0.0.0-20200324143707-d3edc9973b7e/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A=
|
||||
golang.org/x/net v0.0.0-20200501053045-e0ff5e5a1de5/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A=
|
||||
golang.org/x/net v0.0.0-20200506145744-7e3656a0809f/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A=
|
||||
@ -635,8 +476,8 @@ golang.org/x/net v0.0.0-20200822124328-c89045814202/go.mod h1:/O7V0waA8r7cgGh81R
|
||||
golang.org/x/net v0.0.0-20200904194848-62affa334b73/go.mod h1:/O7V0waA8r7cgGh81Ro3o1hOxt32SMVPicZroKQ2sZA=
|
||||
golang.org/x/net v0.0.0-20201021035429-f5854403a974/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU=
|
||||
golang.org/x/net v0.0.0-20201110031124-69a78807bb2b/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU=
|
||||
golang.org/x/net v0.0.0-20201209123823-ac852fbbde11 h1:lwlPPsmjDKK0J6eG6xDWd5XPehI0R024zxjDnw3esPA=
|
||||
golang.org/x/net v0.0.0-20201209123823-ac852fbbde11/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg=
|
||||
golang.org/x/net v0.0.0-20210119194325-5f4716e94777 h1:003p0dJM77cxMSyCPFphvZf/Y5/NXf5fzg6ufd1/Oew=
|
||||
golang.org/x/net v0.0.0-20210119194325-5f4716e94777/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg=
|
||||
golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U=
|
||||
golang.org/x/oauth2 v0.0.0-20190226205417-e64efc72b421/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw=
|
||||
golang.org/x/oauth2 v0.0.0-20190604053449-0f29369cfe45/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw=
|
||||
@ -657,7 +498,6 @@ golang.org/x/sync v0.0.0-20201207232520-09787c993a3a h1:DcqTD9SDLc+1P/r1EmRBwnVs
|
||||
golang.org/x/sync v0.0.0-20201207232520-09787c993a3a/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||
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-20181026203630-95b1ffbd15a5/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=
|
||||
@ -672,13 +512,11 @@ golang.org/x/sys v0.0.0-20190507160741-ecd444e8653b/go.mod h1:h1NjWce9XRLGQEsW7w
|
||||
golang.org/x/sys v0.0.0-20190606165138-5da285871e9c/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20190624142023-c5567b49c5d0/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20190726091711-fc99dfbffb4e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-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-20191128015809-6d18c012aee9/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20191204072324-ce4227a45e2e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20191228213918-04cbcbbfeed8/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20200113162924-86b910548bc1/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
@ -699,8 +537,9 @@ golang.org/x/sys v0.0.0-20200803210538-64077c9b5642/go.mod h1:h1NjWce9XRLGQEsW7w
|
||||
golang.org/x/sys v0.0.0-20200828194041-157a740278f4/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20200905004654-be1d3432aa8f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20200930185726-fdedc70b468f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20201119102817-f84b799fce68 h1:nxC68pudNYkKU6jWhgrqdreuFiOQWj1Fs7T3VrH4Pjw=
|
||||
golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20210319071255-635bc2c9138d h1:jbzgAvDZn8aEnytae+4ou0J0GwFZoHR0hOrTg4qH8GA=
|
||||
golang.org/x/sys v0.0.0-20210319071255-635bc2c9138d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/term v0.0.0-20201117132131-f5c789dd3221/go.mod h1:Nr5EML6q2oocZ2LXRh80K7BxOlk5/8JxuGnuhpl+muw=
|
||||
golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1 h1:v+OssWQX+hTHEmOBgwxdZxK4zHq3yOs8F9J7mk0PY8E=
|
||||
golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo=
|
||||
@ -709,20 +548,19 @@ golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
|
||||
golang.org/x/text v0.3.1-0.20180807135948-17ff2d5776d2/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
|
||||
golang.org/x/text v0.3.1-0.20181227161524-e6919f6577db/go.mod h1:bEr9sfX3Q8Zfm5fL9x+3itogRgK3+ptLWKqgva+5dAk=
|
||||
golang.org/x/text v0.3.2/go.mod h1:bEr9sfX3Q8Zfm5fL9x+3itogRgK3+ptLWKqgva+5dAk=
|
||||
golang.org/x/text v0.3.3 h1:cokOdA+Jmi5PJGXLlLllQSgYigAEfHXJAERHVMaCc2k=
|
||||
golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
|
||||
golang.org/x/text v0.3.5 h1:i6eZZ+zk0SOf0xgBpEpPD18qWcJda6q1sxt3S0kzyUQ=
|
||||
golang.org/x/text v0.3.5/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
|
||||
golang.org/x/time v0.0.0-20181108054448-85acf8d2951c/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=
|
||||
golang.org/x/time v0.0.0-20190308202827-9d24e82272b4/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=
|
||||
golang.org/x/time v0.0.0-20191024005414-555d28b269f0 h1:/5xXl8Y5W96D+TtHSlonuFqGHIWVuyCkGJLwGh9JJFs=
|
||||
golang.org/x/time v0.0.0-20191024005414-555d28b269f0/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-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-20190328211700-ab21143f2384/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=
|
||||
@ -731,7 +569,6 @@ golang.org/x/tools v0.0.0-20190621195816-6e04913cbbac/go.mod h1:/rFqwRUd4F7ZHNgw
|
||||
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-20190909214602-067311248421/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-20191113191852-77e3bb0ad9e7/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
|
||||
@ -739,7 +576,6 @@ golang.org/x/tools v0.0.0-20191115202509-3a792d9c32b2/go.mod h1:b+2E5dAYhXwXZwtn
|
||||
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-20191203134012-c197fd4bf371/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-20200117012304-6edc0a871e69/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28=
|
||||
@ -758,7 +594,6 @@ golang.org/x/tools v0.0.0-20200501065659-ab2804fb9c9d/go.mod h1:EkVYQZoAsY45+roY
|
||||
golang.org/x/tools v0.0.0-20200512131952-2bc93b1c0c88/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE=
|
||||
golang.org/x/tools v0.0.0-20200515010526-7d3b6ebf133d/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE=
|
||||
golang.org/x/tools v0.0.0-20200618134242-20370b0cb4b2/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE=
|
||||
golang.org/x/tools v0.0.0-20200711155855-7342f9734a7d/go.mod h1:njjCfa9FT2d7l9Bc6FUM5FLjQPp3cFF28FI3qnDFljA=
|
||||
golang.org/x/tools v0.0.0-20200729194436-6467de6f59a7/go.mod h1:njjCfa9FT2d7l9Bc6FUM5FLjQPp3cFF28FI3qnDFljA=
|
||||
golang.org/x/tools v0.0.0-20200804011535-6c149bb5ef0d/go.mod h1:njjCfa9FT2d7l9Bc6FUM5FLjQPp3cFF28FI3qnDFljA=
|
||||
golang.org/x/tools v0.0.0-20200825202427-b303f430e36d/go.mod h1:njjCfa9FT2d7l9Bc6FUM5FLjQPp3cFF28FI3qnDFljA=
|
||||
@ -820,7 +655,6 @@ google.golang.org/genproto v0.0.0-20200224152610-e50cd9704f63/go.mod h1:55QSHmfG
|
||||
google.golang.org/genproto v0.0.0-20200228133532-8c2c7df3a383/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c=
|
||||
google.golang.org/genproto v0.0.0-20200305110556-506484158171/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c=
|
||||
google.golang.org/genproto v0.0.0-20200312145019-da6875a35672/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c=
|
||||
google.golang.org/genproto v0.0.0-20200323114720-3f67cca34472/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c=
|
||||
google.golang.org/genproto v0.0.0-20200331122359-1ee6d9798940/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c=
|
||||
google.golang.org/genproto v0.0.0-20200430143042-b979b6f78d84/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c=
|
||||
google.golang.org/genproto v0.0.0-20200511104702-f5ebc3bea380/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c=
|
||||
@ -871,20 +705,15 @@ gopkg.in/check.v1 v1.0.0-20200227125254-8fa46927fb4f h1:BLraFXnmrev5lT+xlilqcH8X
|
||||
gopkg.in/check.v1 v1.0.0-20200227125254-8fa46927fb4f/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
|
||||
gopkg.in/cheggaaa/pb.v1 v1.0.27/go.mod h1:V/YB90LKu/1FcN3WVnfiiE5oMCibMjukxqG/qStrOgw=
|
||||
gopkg.in/errgo.v2 v2.1.0/go.mod h1:hNsd1EY+bozCKY1Ytp96fpM3vjJbqLJn88ws8XvfDNI=
|
||||
gopkg.in/ini.v1 v1.42.0/go.mod h1:pNLf8WUiyNEtQjuu5G5vTm06TEv9tsIgeAvK8hOrP4k=
|
||||
gopkg.in/ini.v1 v1.62.0/go.mod h1:pNLf8WUiyNEtQjuu5G5vTm06TEv9tsIgeAvK8hOrP4k=
|
||||
gopkg.in/jarcoal/httpmock.v1 v1.0.0-20181117152235-275e9df93516/go.mod h1:d3R+NllX3X5e0zlG1Rful3uLvsGC/Q3OHut5464DEQw=
|
||||
gopkg.in/square/go-jose.v2 v2.3.1 h1:SK5KegNXmKmqE342YYN2qPHEnUYeoMiXXl1poUlI+o4=
|
||||
gopkg.in/square/go-jose.v2 v2.3.1/go.mod h1:M9dMgbHiYLoDGQrXy7OpJDJWiKiU//h+vD76mk0e1AI=
|
||||
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.7/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
|
||||
gopkg.in/yaml.v2 v2.2.8/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
|
||||
gopkg.in/yaml.v2 v2.3.0 h1:clyUAQHOM3G0M3f5vQj7LuJrETvjVot3Z5el9nffUtU=
|
||||
gopkg.in/yaml.v2 v2.3.0/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
|
||||
gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c h1:dUUwHk2QECo/6vqA44rthZ8ie2QXMNeKRTHCNY2nXvo=
|
||||
gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
|
||||
gopkg.in/yaml.v3 v3.0.0-20210107192922-496545a6307b h1:h8qDotaEPuJATrMmW04NCwg7v22aHH28wwpauUhK9Oo=
|
||||
gopkg.in/yaml.v3 v3.0.0-20210107192922-496545a6307b/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
|
||||
honnef.co/go/tools v0.0.0-20190102054323-c2f93a96b099/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4=
|
||||
honnef.co/go/tools v0.0.0-20190106161140-3f1c8253044a/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4=
|
||||
honnef.co/go/tools v0.0.0-20190418001031-e561f6794a2a/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4=
|
||||
|
Loading…
Reference in New Issue
Block a user