Merge pull request #8480 from hashicorp/merge-vsphere-builder
Merge the vSphere builder
This commit is contained in:
commit
fac320d290
|
@ -0,0 +1,99 @@
|
||||||
|
package clone
|
||||||
|
|
||||||
|
import (
|
||||||
|
"context"
|
||||||
|
"github.com/hashicorp/hcl/v2/hcldec"
|
||||||
|
"github.com/hashicorp/packer/builder/vsphere/common"
|
||||||
|
"github.com/hashicorp/packer/builder/vsphere/driver"
|
||||||
|
packerCommon "github.com/hashicorp/packer/common"
|
||||||
|
"github.com/hashicorp/packer/helper/communicator"
|
||||||
|
"github.com/hashicorp/packer/helper/multistep"
|
||||||
|
"github.com/hashicorp/packer/packer"
|
||||||
|
)
|
||||||
|
|
||||||
|
type Builder struct {
|
||||||
|
config Config
|
||||||
|
runner multistep.Runner
|
||||||
|
}
|
||||||
|
|
||||||
|
func (b *Builder) ConfigSpec() hcldec.ObjectSpec { return b.config.FlatMapstructure().HCL2Spec() }
|
||||||
|
|
||||||
|
func (b *Builder) Prepare(raws ...interface{}) ([]string, []string, error) {
|
||||||
|
warnings, errs := b.config.Prepare(raws...)
|
||||||
|
if errs != nil {
|
||||||
|
return nil, warnings, errs
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil, warnings, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (b *Builder) Run(ctx context.Context, ui packer.Ui, hook packer.Hook) (packer.Artifact, error) {
|
||||||
|
state := new(multistep.BasicStateBag)
|
||||||
|
state.Put("hook", hook)
|
||||||
|
state.Put("ui", ui)
|
||||||
|
|
||||||
|
var steps []multistep.Step
|
||||||
|
|
||||||
|
steps = append(steps,
|
||||||
|
&common.StepConnect{
|
||||||
|
Config: &b.config.ConnectConfig,
|
||||||
|
},
|
||||||
|
&StepCloneVM{
|
||||||
|
Config: &b.config.CloneConfig,
|
||||||
|
Location: &b.config.LocationConfig,
|
||||||
|
Force: b.config.PackerConfig.PackerForce,
|
||||||
|
},
|
||||||
|
&common.StepConfigureHardware{
|
||||||
|
Config: &b.config.HardwareConfig,
|
||||||
|
},
|
||||||
|
&common.StepConfigParams{
|
||||||
|
Config: &b.config.ConfigParamsConfig,
|
||||||
|
},
|
||||||
|
)
|
||||||
|
|
||||||
|
if b.config.Comm.Type != "none" {
|
||||||
|
steps = append(steps,
|
||||||
|
&common.StepRun{
|
||||||
|
Config: &b.config.RunConfig,
|
||||||
|
SetOrder: false,
|
||||||
|
},
|
||||||
|
&common.StepWaitForIp{
|
||||||
|
Config: &b.config.WaitIpConfig,
|
||||||
|
},
|
||||||
|
&communicator.StepConnect{
|
||||||
|
Config: &b.config.Comm,
|
||||||
|
Host: common.CommHost(b.config.Comm.SSHHost),
|
||||||
|
SSHConfig: b.config.Comm.SSHConfigFunc(),
|
||||||
|
},
|
||||||
|
&packerCommon.StepProvision{},
|
||||||
|
&common.StepShutdown{
|
||||||
|
Config: &b.config.ShutdownConfig,
|
||||||
|
},
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
steps = append(steps,
|
||||||
|
&common.StepCreateSnapshot{
|
||||||
|
CreateSnapshot: b.config.CreateSnapshot,
|
||||||
|
},
|
||||||
|
&common.StepConvertToTemplate{
|
||||||
|
ConvertToTemplate: b.config.ConvertToTemplate,
|
||||||
|
},
|
||||||
|
)
|
||||||
|
|
||||||
|
b.runner = packerCommon.NewRunner(steps, b.config.PackerConfig, ui)
|
||||||
|
b.runner.Run(ctx, state)
|
||||||
|
|
||||||
|
if rawErr, ok := state.GetOk("error"); ok {
|
||||||
|
return nil, rawErr.(error)
|
||||||
|
}
|
||||||
|
|
||||||
|
if _, ok := state.GetOk("vm"); !ok {
|
||||||
|
return nil, nil
|
||||||
|
}
|
||||||
|
artifact := &common.Artifact{
|
||||||
|
Name: b.config.VMName,
|
||||||
|
VM: state.Get("vm").(*driver.VirtualMachine),
|
||||||
|
}
|
||||||
|
return artifact, nil
|
||||||
|
}
|
|
@ -0,0 +1,733 @@
|
||||||
|
package clone
|
||||||
|
|
||||||
|
import (
|
||||||
|
"github.com/hashicorp/packer/builder/vsphere/common"
|
||||||
|
commonT "github.com/hashicorp/packer/builder/vsphere/common/testing"
|
||||||
|
builderT "github.com/hashicorp/packer/helper/builder/testing"
|
||||||
|
"github.com/hashicorp/packer/packer"
|
||||||
|
"github.com/vmware/govmomi/vim25/types"
|
||||||
|
"os"
|
||||||
|
"testing"
|
||||||
|
)
|
||||||
|
|
||||||
|
func TestCloneBuilderAcc_default(t *testing.T) {
|
||||||
|
t.Skip("Acceptance tests not configured yet.")
|
||||||
|
config := defaultConfig()
|
||||||
|
builderT.Test(t, builderT.TestCase{
|
||||||
|
Builder: &Builder{},
|
||||||
|
Template: commonT.RenderConfig(config),
|
||||||
|
Check: checkDefault(t, config["vm_name"].(string), config["host"].(string), "datastore1"),
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
func defaultConfig() map[string]interface{} {
|
||||||
|
username := os.Getenv("VSPHERE_USERNAME")
|
||||||
|
if username == "" {
|
||||||
|
username = "root"
|
||||||
|
}
|
||||||
|
password := os.Getenv("VSPHERE_PASSWORD")
|
||||||
|
if password == "" {
|
||||||
|
password = "jetbrains"
|
||||||
|
}
|
||||||
|
|
||||||
|
config := map[string]interface{}{
|
||||||
|
"vcenter_server": "vcenter.vsphere65.test",
|
||||||
|
"username": username,
|
||||||
|
"password": password,
|
||||||
|
"insecure_connection": true,
|
||||||
|
|
||||||
|
"template": "alpine",
|
||||||
|
"host": "esxi-1.vsphere65.test",
|
||||||
|
|
||||||
|
"linked_clone": true, // speed up
|
||||||
|
"communicator": "none",
|
||||||
|
}
|
||||||
|
config["vm_name"] = commonT.NewVMName()
|
||||||
|
return config
|
||||||
|
}
|
||||||
|
|
||||||
|
func checkDefault(t *testing.T, name string, host string, datastore string) builderT.TestCheckFunc {
|
||||||
|
return func(artifacts []packer.Artifact) error {
|
||||||
|
d := commonT.TestConn(t)
|
||||||
|
vm := commonT.GetVM(t, d, artifacts)
|
||||||
|
|
||||||
|
vmInfo, err := vm.Info("name", "parent", "runtime.host", "resourcePool", "datastore")
|
||||||
|
if err != nil {
|
||||||
|
t.Fatalf("Cannot read VM properties: %v", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
if vmInfo.Name != name {
|
||||||
|
t.Errorf("Invalid VM name: expected '%v', got '%v'", name, vmInfo.Name)
|
||||||
|
}
|
||||||
|
|
||||||
|
f := d.NewFolder(vmInfo.Parent)
|
||||||
|
folderPath, err := f.Path()
|
||||||
|
if err != nil {
|
||||||
|
t.Fatalf("Cannot read folder name: %v", err)
|
||||||
|
}
|
||||||
|
if folderPath != "" {
|
||||||
|
t.Errorf("Invalid folder: expected '/', got '%v'", folderPath)
|
||||||
|
}
|
||||||
|
|
||||||
|
h := d.NewHost(vmInfo.Runtime.Host)
|
||||||
|
hostInfo, err := h.Info("name")
|
||||||
|
if err != nil {
|
||||||
|
t.Fatal("Cannot read host properties: ", err)
|
||||||
|
}
|
||||||
|
if hostInfo.Name != host {
|
||||||
|
t.Errorf("Invalid host name: expected '%v', got '%v'", host, hostInfo.Name)
|
||||||
|
}
|
||||||
|
|
||||||
|
p := d.NewResourcePool(vmInfo.ResourcePool)
|
||||||
|
poolPath, err := p.Path()
|
||||||
|
if err != nil {
|
||||||
|
t.Fatalf("Cannot read resource pool name: %v", err)
|
||||||
|
}
|
||||||
|
if poolPath != "" {
|
||||||
|
t.Errorf("Invalid resource pool: expected '/', got '%v'", poolPath)
|
||||||
|
}
|
||||||
|
|
||||||
|
dsr := vmInfo.Datastore[0].Reference()
|
||||||
|
ds := d.NewDatastore(&dsr)
|
||||||
|
dsInfo, err := ds.Info("name")
|
||||||
|
if err != nil {
|
||||||
|
t.Fatal("Cannot read datastore properties: ", err)
|
||||||
|
}
|
||||||
|
if dsInfo.Name != datastore {
|
||||||
|
t.Errorf("Invalid datastore name: expected '%v', got '%v'", datastore, dsInfo.Name)
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestCloneBuilderAcc_artifact(t *testing.T) {
|
||||||
|
t.Skip("Acceptance tests not configured yet.")
|
||||||
|
config := defaultConfig()
|
||||||
|
builderT.Test(t, builderT.TestCase{
|
||||||
|
Builder: &Builder{},
|
||||||
|
Template: commonT.RenderConfig(config),
|
||||||
|
Check: checkArtifact(t),
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
func checkArtifact(t *testing.T) builderT.TestCheckFunc {
|
||||||
|
return func(artifacts []packer.Artifact) error {
|
||||||
|
if len(artifacts) > 1 {
|
||||||
|
t.Fatal("more than 1 artifact")
|
||||||
|
}
|
||||||
|
|
||||||
|
artifactRaw := artifacts[0]
|
||||||
|
_, ok := artifactRaw.(*common.Artifact)
|
||||||
|
if !ok {
|
||||||
|
t.Fatalf("unknown artifact: %#v", artifactRaw)
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestCloneBuilderAcc_folder(t *testing.T) {
|
||||||
|
t.Skip("Acceptance tests not configured yet.")
|
||||||
|
builderT.Test(t, builderT.TestCase{
|
||||||
|
Builder: &Builder{},
|
||||||
|
Template: folderConfig(),
|
||||||
|
Check: checkFolder(t, "folder1/folder2"),
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
func folderConfig() string {
|
||||||
|
config := defaultConfig()
|
||||||
|
config["folder"] = "folder1/folder2"
|
||||||
|
return commonT.RenderConfig(config)
|
||||||
|
}
|
||||||
|
|
||||||
|
func checkFolder(t *testing.T, folder string) builderT.TestCheckFunc {
|
||||||
|
return func(artifacts []packer.Artifact) error {
|
||||||
|
d := commonT.TestConn(t)
|
||||||
|
vm := commonT.GetVM(t, d, artifacts)
|
||||||
|
|
||||||
|
vmInfo, err := vm.Info("parent")
|
||||||
|
if err != nil {
|
||||||
|
t.Fatalf("Cannot read VM properties: %v", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
f := d.NewFolder(vmInfo.Parent)
|
||||||
|
path, err := f.Path()
|
||||||
|
if err != nil {
|
||||||
|
t.Fatalf("Cannot read folder name: %v", err)
|
||||||
|
}
|
||||||
|
if path != folder {
|
||||||
|
t.Errorf("Wrong folder. expected: %v, got: %v", folder, path)
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestCloneBuilderAcc_resourcePool(t *testing.T) {
|
||||||
|
t.Skip("Acceptance tests not configured yet.")
|
||||||
|
builderT.Test(t, builderT.TestCase{
|
||||||
|
Builder: &Builder{},
|
||||||
|
Template: resourcePoolConfig(),
|
||||||
|
Check: checkResourcePool(t, "pool1/pool2"),
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
func resourcePoolConfig() string {
|
||||||
|
config := defaultConfig()
|
||||||
|
config["resource_pool"] = "pool1/pool2"
|
||||||
|
return commonT.RenderConfig(config)
|
||||||
|
}
|
||||||
|
|
||||||
|
func checkResourcePool(t *testing.T, pool string) builderT.TestCheckFunc {
|
||||||
|
return func(artifacts []packer.Artifact) error {
|
||||||
|
d := commonT.TestConn(t)
|
||||||
|
vm := commonT.GetVM(t, d, artifacts)
|
||||||
|
|
||||||
|
vmInfo, err := vm.Info("resourcePool")
|
||||||
|
if err != nil {
|
||||||
|
t.Fatalf("Cannot read VM properties: %v", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
p := d.NewResourcePool(vmInfo.ResourcePool)
|
||||||
|
path, err := p.Path()
|
||||||
|
if err != nil {
|
||||||
|
t.Fatalf("Cannot read resource pool name: %v", err)
|
||||||
|
}
|
||||||
|
if path != pool {
|
||||||
|
t.Errorf("Wrong folder. expected: %v, got: %v", pool, path)
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestCloneBuilderAcc_datastore(t *testing.T) {
|
||||||
|
t.Skip("Acceptance tests not configured yet.")
|
||||||
|
builderT.Test(t, builderT.TestCase{
|
||||||
|
Builder: &Builder{},
|
||||||
|
Template: datastoreConfig(),
|
||||||
|
Check: checkDatastore(t, "datastore1"), // on esxi-1.vsphere65.test
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
func datastoreConfig() string {
|
||||||
|
config := defaultConfig()
|
||||||
|
config["template"] = "alpine-host4" // on esxi-4.vsphere65.test
|
||||||
|
config["linked_clone"] = false
|
||||||
|
return commonT.RenderConfig(config)
|
||||||
|
}
|
||||||
|
|
||||||
|
func checkDatastore(t *testing.T, name string) builderT.TestCheckFunc {
|
||||||
|
return func(artifacts []packer.Artifact) error {
|
||||||
|
d := commonT.TestConn(t)
|
||||||
|
vm := commonT.GetVM(t, d, artifacts)
|
||||||
|
|
||||||
|
vmInfo, err := vm.Info("datastore")
|
||||||
|
if err != nil {
|
||||||
|
t.Fatalf("Cannot read VM properties: %v", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
n := len(vmInfo.Datastore)
|
||||||
|
if n != 1 {
|
||||||
|
t.Fatalf("VM should have 1 datastore, got %v", n)
|
||||||
|
}
|
||||||
|
|
||||||
|
ds := d.NewDatastore(&vmInfo.Datastore[0])
|
||||||
|
info, err := ds.Info("name")
|
||||||
|
if err != nil {
|
||||||
|
t.Fatalf("Cannot read datastore properties: %v", err)
|
||||||
|
}
|
||||||
|
if info.Name != name {
|
||||||
|
t.Errorf("Wrong datastore. expected: %v, got: %v", name, info.Name)
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestCloneBuilderAcc_multipleDatastores(t *testing.T) {
|
||||||
|
t.Skip("test must fail")
|
||||||
|
|
||||||
|
builderT.Test(t, builderT.TestCase{
|
||||||
|
Builder: &Builder{},
|
||||||
|
Template: multipleDatastoresConfig(),
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
func multipleDatastoresConfig() string {
|
||||||
|
config := defaultConfig()
|
||||||
|
config["host"] = "esxi-4.vsphere65.test" // host with 2 datastores
|
||||||
|
config["linked_clone"] = false
|
||||||
|
return commonT.RenderConfig(config)
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestCloneBuilderAcc_fullClone(t *testing.T) {
|
||||||
|
t.Skip("Acceptance tests not configured yet.")
|
||||||
|
builderT.Test(t, builderT.TestCase{
|
||||||
|
Builder: &Builder{},
|
||||||
|
Template: fullCloneConfig(),
|
||||||
|
Check: checkFullClone(t),
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
func fullCloneConfig() string {
|
||||||
|
config := defaultConfig()
|
||||||
|
config["linked_clone"] = false
|
||||||
|
return commonT.RenderConfig(config)
|
||||||
|
}
|
||||||
|
|
||||||
|
func checkFullClone(t *testing.T) builderT.TestCheckFunc {
|
||||||
|
return func(artifacts []packer.Artifact) error {
|
||||||
|
d := commonT.TestConn(t)
|
||||||
|
vm := commonT.GetVM(t, d, artifacts)
|
||||||
|
|
||||||
|
vmInfo, err := vm.Info("layoutEx.disk")
|
||||||
|
if err != nil {
|
||||||
|
t.Fatalf("Cannot read VM properties: %v", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
if len(vmInfo.LayoutEx.Disk[0].Chain) != 1 {
|
||||||
|
t.Error("Not a full clone")
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestCloneBuilderAcc_linkedClone(t *testing.T) {
|
||||||
|
t.Skip("Acceptance tests not configured yet.")
|
||||||
|
builderT.Test(t, builderT.TestCase{
|
||||||
|
Builder: &Builder{},
|
||||||
|
Template: linkedCloneConfig(),
|
||||||
|
Check: checkLinkedClone(t),
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
func linkedCloneConfig() string {
|
||||||
|
config := defaultConfig()
|
||||||
|
config["linked_clone"] = true
|
||||||
|
return commonT.RenderConfig(config)
|
||||||
|
}
|
||||||
|
|
||||||
|
func checkLinkedClone(t *testing.T) builderT.TestCheckFunc {
|
||||||
|
return func(artifacts []packer.Artifact) error {
|
||||||
|
d := commonT.TestConn(t)
|
||||||
|
vm := commonT.GetVM(t, d, artifacts)
|
||||||
|
|
||||||
|
vmInfo, err := vm.Info("layoutEx.disk")
|
||||||
|
if err != nil {
|
||||||
|
t.Fatalf("Cannot read VM properties: %v", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
if len(vmInfo.LayoutEx.Disk[0].Chain) != 2 {
|
||||||
|
t.Error("Not a linked clone")
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestCloneBuilderAcc_network(t *testing.T) {
|
||||||
|
t.Skip("Acceptance tests not configured yet.")
|
||||||
|
builderT.Test(t, builderT.TestCase{
|
||||||
|
Builder: &Builder{},
|
||||||
|
Template: networkConfig(),
|
||||||
|
Check: checkNetwork(t, "VM Network 2"),
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
func networkConfig() string {
|
||||||
|
config := defaultConfig()
|
||||||
|
config["template"] = "alpine-host4"
|
||||||
|
config["host"] = "esxi-4.vsphere65.test"
|
||||||
|
config["datastore"] = "datastore4"
|
||||||
|
config["network"] = "VM Network 2"
|
||||||
|
return commonT.RenderConfig(config)
|
||||||
|
}
|
||||||
|
|
||||||
|
func checkNetwork(t *testing.T, name string) builderT.TestCheckFunc {
|
||||||
|
return func(artifacts []packer.Artifact) error {
|
||||||
|
d := commonT.TestConn(t)
|
||||||
|
vm := commonT.GetVM(t, d, artifacts)
|
||||||
|
|
||||||
|
vmInfo, err := vm.Info("network")
|
||||||
|
if err != nil {
|
||||||
|
t.Fatalf("Cannot read VM properties: %v", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
n := len(vmInfo.Network)
|
||||||
|
if n != 1 {
|
||||||
|
t.Fatalf("VM should have 1 network, got %v", n)
|
||||||
|
}
|
||||||
|
|
||||||
|
ds := d.NewNetwork(&vmInfo.Network[0])
|
||||||
|
info, err := ds.Info("name")
|
||||||
|
if err != nil {
|
||||||
|
t.Fatalf("Cannot read network properties: %v", err)
|
||||||
|
}
|
||||||
|
if info.Name != name {
|
||||||
|
t.Errorf("Wrong network. expected: %v, got: %v", name, info.Name)
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestCloneBuilderAcc_hardware(t *testing.T) {
|
||||||
|
t.Skip("Acceptance tests not configured yet.")
|
||||||
|
builderT.Test(t, builderT.TestCase{
|
||||||
|
Builder: &Builder{},
|
||||||
|
Template: hardwareConfig(),
|
||||||
|
Check: checkHardware(t),
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
func hardwareConfig() string {
|
||||||
|
config := defaultConfig()
|
||||||
|
config["CPUs"] = 2
|
||||||
|
config["cpu_cores"] = 2
|
||||||
|
config["CPU_reservation"] = 1000
|
||||||
|
config["CPU_limit"] = 1500
|
||||||
|
config["RAM"] = 2048
|
||||||
|
config["RAM_reservation"] = 1024
|
||||||
|
config["CPU_hot_plug"] = true
|
||||||
|
config["RAM_hot_plug"] = true
|
||||||
|
config["video_ram"] = 8192
|
||||||
|
|
||||||
|
return commonT.RenderConfig(config)
|
||||||
|
}
|
||||||
|
|
||||||
|
func checkHardware(t *testing.T) builderT.TestCheckFunc {
|
||||||
|
return func(artifacts []packer.Artifact) error {
|
||||||
|
d := commonT.TestConn(t)
|
||||||
|
|
||||||
|
vm := commonT.GetVM(t, d, artifacts)
|
||||||
|
vmInfo, err := vm.Info("config")
|
||||||
|
if err != nil {
|
||||||
|
t.Fatalf("Cannot read VM properties: %v", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
cpuSockets := vmInfo.Config.Hardware.NumCPU
|
||||||
|
if cpuSockets != 2 {
|
||||||
|
t.Errorf("VM should have 2 CPU sockets, got %v", cpuSockets)
|
||||||
|
}
|
||||||
|
|
||||||
|
cpuCores := vmInfo.Config.Hardware.NumCoresPerSocket
|
||||||
|
if cpuCores != 2 {
|
||||||
|
t.Errorf("VM should have 2 CPU cores per socket, got %v", cpuCores)
|
||||||
|
}
|
||||||
|
|
||||||
|
cpuReservation := *vmInfo.Config.CpuAllocation.Reservation
|
||||||
|
if cpuReservation != 1000 {
|
||||||
|
t.Errorf("VM should have CPU reservation for 1000 Mhz, got %v", cpuReservation)
|
||||||
|
}
|
||||||
|
|
||||||
|
cpuLimit := *vmInfo.Config.CpuAllocation.Limit
|
||||||
|
if cpuLimit != 1500 {
|
||||||
|
t.Errorf("VM should have CPU reservation for 1500 Mhz, got %v", cpuLimit)
|
||||||
|
}
|
||||||
|
|
||||||
|
ram := vmInfo.Config.Hardware.MemoryMB
|
||||||
|
if ram != 2048 {
|
||||||
|
t.Errorf("VM should have 2048 MB of RAM, got %v", ram)
|
||||||
|
}
|
||||||
|
|
||||||
|
ramReservation := *vmInfo.Config.MemoryAllocation.Reservation
|
||||||
|
if ramReservation != 1024 {
|
||||||
|
t.Errorf("VM should have RAM reservation for 1024 MB, got %v", ramReservation)
|
||||||
|
}
|
||||||
|
|
||||||
|
cpuHotAdd := vmInfo.Config.CpuHotAddEnabled
|
||||||
|
if !*cpuHotAdd {
|
||||||
|
t.Errorf("VM should have CPU hot add enabled, got %v", cpuHotAdd)
|
||||||
|
}
|
||||||
|
|
||||||
|
memoryHotAdd := vmInfo.Config.MemoryHotAddEnabled
|
||||||
|
if !*memoryHotAdd {
|
||||||
|
t.Errorf("VM should have Memory hot add enabled, got %v", memoryHotAdd)
|
||||||
|
}
|
||||||
|
|
||||||
|
l, err := vm.Devices()
|
||||||
|
if err != nil {
|
||||||
|
t.Fatalf("Cannot read VM devices: %v", err)
|
||||||
|
}
|
||||||
|
v := l.SelectByType((*types.VirtualMachineVideoCard)(nil))
|
||||||
|
if len(v) != 1 {
|
||||||
|
t.Errorf("VM should have one video card")
|
||||||
|
}
|
||||||
|
if v[0].(*types.VirtualMachineVideoCard).VideoRamSizeInKB != 8192 {
|
||||||
|
t.Errorf("Video RAM should be equal 8192")
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestCloneBuilderAcc_RAMReservation(t *testing.T) {
|
||||||
|
t.Skip("Acceptance tests not configured yet.")
|
||||||
|
builderT.Test(t, builderT.TestCase{
|
||||||
|
Builder: &Builder{},
|
||||||
|
Template: RAMReservationConfig(),
|
||||||
|
Check: checkRAMReservation(t),
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
func RAMReservationConfig() string {
|
||||||
|
config := defaultConfig()
|
||||||
|
config["RAM_reserve_all"] = true
|
||||||
|
|
||||||
|
return commonT.RenderConfig(config)
|
||||||
|
}
|
||||||
|
|
||||||
|
func checkRAMReservation(t *testing.T) builderT.TestCheckFunc {
|
||||||
|
return func(artifacts []packer.Artifact) error {
|
||||||
|
d := commonT.TestConn(t)
|
||||||
|
|
||||||
|
vm := commonT.GetVM(t, d, artifacts)
|
||||||
|
vmInfo, err := vm.Info("config")
|
||||||
|
if err != nil {
|
||||||
|
t.Fatalf("Cannot read VM properties: %v", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
if *vmInfo.Config.MemoryReservationLockedToMax != true {
|
||||||
|
t.Errorf("VM should have all RAM reserved")
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestCloneBuilderAcc_sshPassword(t *testing.T) {
|
||||||
|
t.Skip("Acceptance tests not configured yet.")
|
||||||
|
builderT.Test(t, builderT.TestCase{
|
||||||
|
Builder: &Builder{},
|
||||||
|
Template: sshPasswordConfig(),
|
||||||
|
Check: checkDefaultBootOrder(t),
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
func sshPasswordConfig() string {
|
||||||
|
config := defaultConfig()
|
||||||
|
config["communicator"] = "ssh"
|
||||||
|
config["ssh_username"] = "root"
|
||||||
|
config["ssh_password"] = "jetbrains"
|
||||||
|
return commonT.RenderConfig(config)
|
||||||
|
}
|
||||||
|
|
||||||
|
func checkDefaultBootOrder(t *testing.T) builderT.TestCheckFunc {
|
||||||
|
return func(artifacts []packer.Artifact) error {
|
||||||
|
d := commonT.TestConn(t)
|
||||||
|
vm := commonT.GetVM(t, d, artifacts)
|
||||||
|
|
||||||
|
vmInfo, err := vm.Info("config.bootOptions")
|
||||||
|
if err != nil {
|
||||||
|
t.Fatalf("Cannot read VM properties: %v", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
order := vmInfo.Config.BootOptions.BootOrder
|
||||||
|
if order != nil {
|
||||||
|
t.Errorf("Boot order must be empty")
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestCloneBuilderAcc_sshKey(t *testing.T) {
|
||||||
|
t.Skip("Acceptance tests not configured yet.")
|
||||||
|
builderT.Test(t, builderT.TestCase{
|
||||||
|
Builder: &Builder{},
|
||||||
|
Template: sshKeyConfig(),
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
func sshKeyConfig() string {
|
||||||
|
config := defaultConfig()
|
||||||
|
config["communicator"] = "ssh"
|
||||||
|
config["ssh_username"] = "root"
|
||||||
|
config["ssh_private_key_file"] = "../test/test-key.pem"
|
||||||
|
return commonT.RenderConfig(config)
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestCloneBuilderAcc_snapshot(t *testing.T) {
|
||||||
|
t.Skip("Acceptance tests not configured yet.")
|
||||||
|
builderT.Test(t, builderT.TestCase{
|
||||||
|
Builder: &Builder{},
|
||||||
|
Template: snapshotConfig(),
|
||||||
|
Check: checkSnapshot(t),
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
func snapshotConfig() string {
|
||||||
|
config := defaultConfig()
|
||||||
|
config["linked_clone"] = false
|
||||||
|
config["create_snapshot"] = true
|
||||||
|
return commonT.RenderConfig(config)
|
||||||
|
}
|
||||||
|
|
||||||
|
func checkSnapshot(t *testing.T) builderT.TestCheckFunc {
|
||||||
|
return func(artifacts []packer.Artifact) error {
|
||||||
|
d := commonT.TestConn(t)
|
||||||
|
|
||||||
|
vm := commonT.GetVM(t, d, artifacts)
|
||||||
|
vmInfo, err := vm.Info("layoutEx.disk")
|
||||||
|
if err != nil {
|
||||||
|
t.Fatalf("Cannot read VM properties: %v", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
layers := len(vmInfo.LayoutEx.Disk[0].Chain)
|
||||||
|
if layers != 2 {
|
||||||
|
t.Errorf("VM should have a single snapshot. expected 2 disk layers, got %v", layers)
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestCloneBuilderAcc_template(t *testing.T) {
|
||||||
|
t.Skip("Acceptance tests not configured yet.")
|
||||||
|
builderT.Test(t, builderT.TestCase{
|
||||||
|
Builder: &Builder{},
|
||||||
|
Template: templateConfig(),
|
||||||
|
Check: checkTemplate(t),
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
func templateConfig() string {
|
||||||
|
config := defaultConfig()
|
||||||
|
config["convert_to_template"] = true
|
||||||
|
return commonT.RenderConfig(config)
|
||||||
|
}
|
||||||
|
|
||||||
|
func checkTemplate(t *testing.T) builderT.TestCheckFunc {
|
||||||
|
return func(artifacts []packer.Artifact) error {
|
||||||
|
d := commonT.TestConn(t)
|
||||||
|
|
||||||
|
vm := commonT.GetVM(t, d, artifacts)
|
||||||
|
vmInfo, err := vm.Info("config.template")
|
||||||
|
if err != nil {
|
||||||
|
t.Fatalf("Cannot read VM properties: %v", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
if vmInfo.Config.Template != true {
|
||||||
|
t.Error("Not a template")
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestCloneBuilderAcc_bootOrder(t *testing.T) {
|
||||||
|
t.Skip("Acceptance tests not configured yet.")
|
||||||
|
builderT.Test(t, builderT.TestCase{
|
||||||
|
Builder: &Builder{},
|
||||||
|
Template: bootOrderConfig(),
|
||||||
|
Check: checkBootOrder(t),
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
func bootOrderConfig() string {
|
||||||
|
config := defaultConfig()
|
||||||
|
config["communicator"] = "ssh"
|
||||||
|
config["ssh_username"] = "root"
|
||||||
|
config["ssh_password"] = "jetbrains"
|
||||||
|
|
||||||
|
config["boot_order"] = "disk,cdrom,floppy"
|
||||||
|
|
||||||
|
return commonT.RenderConfig(config)
|
||||||
|
}
|
||||||
|
|
||||||
|
func checkBootOrder(t *testing.T) builderT.TestCheckFunc {
|
||||||
|
return func(artifacts []packer.Artifact) error {
|
||||||
|
d := commonT.TestConn(t)
|
||||||
|
vm := commonT.GetVM(t, d, artifacts)
|
||||||
|
|
||||||
|
vmInfo, err := vm.Info("config.bootOptions")
|
||||||
|
if err != nil {
|
||||||
|
t.Fatalf("Cannot read VM properties: %v", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
order := vmInfo.Config.BootOptions.BootOrder
|
||||||
|
if order == nil {
|
||||||
|
t.Errorf("Boot order must not be empty")
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestCloneBuilderAcc_notes(t *testing.T) {
|
||||||
|
t.Skip("Acceptance tests not configured yet.")
|
||||||
|
builderT.Test(t, builderT.TestCase{
|
||||||
|
Builder: &Builder{},
|
||||||
|
Template: notesConfig(),
|
||||||
|
Check: checkNotes(t),
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
func notesConfig() string {
|
||||||
|
config := defaultConfig()
|
||||||
|
config["notes"] = "test"
|
||||||
|
|
||||||
|
return commonT.RenderConfig(config)
|
||||||
|
}
|
||||||
|
|
||||||
|
func checkNotes(t *testing.T) builderT.TestCheckFunc {
|
||||||
|
return func(artifacts []packer.Artifact) error {
|
||||||
|
d := commonT.TestConn(t)
|
||||||
|
vm := commonT.GetVM(t, d, artifacts)
|
||||||
|
|
||||||
|
vmInfo, err := vm.Info("config.annotation")
|
||||||
|
if err != nil {
|
||||||
|
t.Fatalf("Cannot read VM properties: %v", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
notes := vmInfo.Config.Annotation
|
||||||
|
if notes != "test" {
|
||||||
|
t.Errorf("notest should be 'test'")
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestCloneBuilderAcc_windows(t *testing.T) {
|
||||||
|
t.Skip("Acceptance tests not configured yet.")
|
||||||
|
t.Skip("test is too slow")
|
||||||
|
config := windowsConfig()
|
||||||
|
builderT.Test(t, builderT.TestCase{
|
||||||
|
Builder: &Builder{},
|
||||||
|
Template: commonT.RenderConfig(config),
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
func windowsConfig() map[string]interface{} {
|
||||||
|
username := os.Getenv("VSPHERE_USERNAME")
|
||||||
|
if username == "" {
|
||||||
|
username = "root"
|
||||||
|
}
|
||||||
|
password := os.Getenv("VSPHERE_PASSWORD")
|
||||||
|
if password == "" {
|
||||||
|
password = "jetbrains"
|
||||||
|
}
|
||||||
|
|
||||||
|
config := map[string]interface{}{
|
||||||
|
"vcenter_server": "vcenter.vsphere65.test",
|
||||||
|
"username": username,
|
||||||
|
"password": password,
|
||||||
|
"insecure_connection": true,
|
||||||
|
|
||||||
|
"vm_name": commonT.NewVMName(),
|
||||||
|
"template": "windows",
|
||||||
|
"host": "esxi-1.vsphere65.test",
|
||||||
|
"linked_clone": true, // speed up
|
||||||
|
|
||||||
|
"communicator": "winrm",
|
||||||
|
"winrm_username": "jetbrains",
|
||||||
|
"winrm_password": "jetbrains",
|
||||||
|
}
|
||||||
|
|
||||||
|
return config
|
||||||
|
}
|
|
@ -0,0 +1,14 @@
|
||||||
|
package clone
|
||||||
|
|
||||||
|
import (
|
||||||
|
"github.com/hashicorp/packer/packer"
|
||||||
|
"testing"
|
||||||
|
)
|
||||||
|
|
||||||
|
func TestCloneBuilder_ImplementsBuilder(t *testing.T) {
|
||||||
|
var raw interface{}
|
||||||
|
raw = &Builder{}
|
||||||
|
if _, ok := raw.(packer.Builder); !ok {
|
||||||
|
t.Fatalf("Builder should be a builder")
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,62 @@
|
||||||
|
//go:generate struct-markdown
|
||||||
|
//go:generate mapstructure-to-hcl2 -type Config
|
||||||
|
|
||||||
|
package clone
|
||||||
|
|
||||||
|
import (
|
||||||
|
"github.com/hashicorp/packer/builder/vsphere/common"
|
||||||
|
packerCommon "github.com/hashicorp/packer/common"
|
||||||
|
"github.com/hashicorp/packer/helper/communicator"
|
||||||
|
"github.com/hashicorp/packer/helper/config"
|
||||||
|
"github.com/hashicorp/packer/packer"
|
||||||
|
"github.com/hashicorp/packer/template/interpolate"
|
||||||
|
)
|
||||||
|
|
||||||
|
type Config struct {
|
||||||
|
packerCommon.PackerConfig `mapstructure:",squash"`
|
||||||
|
|
||||||
|
common.ConnectConfig `mapstructure:",squash"`
|
||||||
|
CloneConfig `mapstructure:",squash"`
|
||||||
|
common.LocationConfig `mapstructure:",squash"`
|
||||||
|
common.HardwareConfig `mapstructure:",squash"`
|
||||||
|
common.ConfigParamsConfig `mapstructure:",squash"`
|
||||||
|
|
||||||
|
common.RunConfig `mapstructure:",squash"`
|
||||||
|
common.WaitIpConfig `mapstructure:",squash"`
|
||||||
|
Comm communicator.Config `mapstructure:",squash"`
|
||||||
|
common.ShutdownConfig `mapstructure:",squash"`
|
||||||
|
|
||||||
|
// Create a snapshot when set to `true`, so the VM can be used as a base
|
||||||
|
// for linked clones. Defaults to `false`.
|
||||||
|
CreateSnapshot bool `mapstructure:"create_snapshot"`
|
||||||
|
// Convert VM to a template. Defaults to `false`.
|
||||||
|
ConvertToTemplate bool `mapstructure:"convert_to_template"`
|
||||||
|
|
||||||
|
ctx interpolate.Context
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *Config) Prepare(raws ...interface{}) ([]string, error) {
|
||||||
|
err := config.Decode(c, &config.DecodeOpts{
|
||||||
|
Interpolate: true,
|
||||||
|
InterpolateContext: &c.ctx,
|
||||||
|
}, raws...)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
errs := new(packer.MultiError)
|
||||||
|
errs = packer.MultiErrorAppend(errs, c.ConnectConfig.Prepare()...)
|
||||||
|
errs = packer.MultiErrorAppend(errs, c.CloneConfig.Prepare()...)
|
||||||
|
errs = packer.MultiErrorAppend(errs, c.LocationConfig.Prepare()...)
|
||||||
|
errs = packer.MultiErrorAppend(errs, c.HardwareConfig.Prepare()...)
|
||||||
|
|
||||||
|
errs = packer.MultiErrorAppend(errs, c.WaitIpConfig.Prepare()...)
|
||||||
|
errs = packer.MultiErrorAppend(errs, c.Comm.Prepare(&c.ctx)...)
|
||||||
|
errs = packer.MultiErrorAppend(errs, c.ShutdownConfig.Prepare()...)
|
||||||
|
|
||||||
|
if len(errs.Errors) > 0 {
|
||||||
|
return nil, errs
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil, nil
|
||||||
|
}
|
|
@ -0,0 +1,192 @@
|
||||||
|
// Code generated by "mapstructure-to-hcl2 -type Config"; DO NOT EDIT.
|
||||||
|
package clone
|
||||||
|
|
||||||
|
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"`
|
||||||
|
PackerBuilderType *string `mapstructure:"packer_builder_type" cty:"packer_builder_type"`
|
||||||
|
PackerDebug *bool `mapstructure:"packer_debug" cty:"packer_debug"`
|
||||||
|
PackerForce *bool `mapstructure:"packer_force" cty:"packer_force"`
|
||||||
|
PackerOnError *string `mapstructure:"packer_on_error" cty:"packer_on_error"`
|
||||||
|
PackerUserVars map[string]string `mapstructure:"packer_user_variables" cty:"packer_user_variables"`
|
||||||
|
PackerSensitiveVars []string `mapstructure:"packer_sensitive_variables" cty:"packer_sensitive_variables"`
|
||||||
|
VCenterServer *string `mapstructure:"vcenter_server" cty:"vcenter_server"`
|
||||||
|
Username *string `mapstructure:"username" cty:"username"`
|
||||||
|
Password *string `mapstructure:"password" cty:"password"`
|
||||||
|
InsecureConnection *bool `mapstructure:"insecure_connection" cty:"insecure_connection"`
|
||||||
|
Datacenter *string `mapstructure:"datacenter" cty:"datacenter"`
|
||||||
|
Template *string `mapstructure:"template" cty:"template"`
|
||||||
|
DiskSize *int64 `mapstructure:"disk_size" cty:"disk_size"`
|
||||||
|
LinkedClone *bool `mapstructure:"linked_clone" cty:"linked_clone"`
|
||||||
|
Network *string `mapstructure:"network" cty:"network"`
|
||||||
|
Notes *string `mapstructure:"notes" cty:"notes"`
|
||||||
|
VMName *string `mapstructure:"vm_name" cty:"vm_name"`
|
||||||
|
Folder *string `mapstructure:"folder" cty:"folder"`
|
||||||
|
Cluster *string `mapstructure:"cluster" cty:"cluster"`
|
||||||
|
Host *string `mapstructure:"host" cty:"host"`
|
||||||
|
ResourcePool *string `mapstructure:"resource_pool" cty:"resource_pool"`
|
||||||
|
Datastore *string `mapstructure:"datastore" cty:"datastore"`
|
||||||
|
CPUs *int32 `mapstructure:"CPUs" cty:"CPUs"`
|
||||||
|
CpuCores *int32 `mapstructure:"cpu_cores" cty:"cpu_cores"`
|
||||||
|
CPUReservation *int64 `mapstructure:"CPU_reservation" cty:"CPU_reservation"`
|
||||||
|
CPULimit *int64 `mapstructure:"CPU_limit" cty:"CPU_limit"`
|
||||||
|
CpuHotAddEnabled *bool `mapstructure:"CPU_hot_plug" cty:"CPU_hot_plug"`
|
||||||
|
RAM *int64 `mapstructure:"RAM" cty:"RAM"`
|
||||||
|
RAMReservation *int64 `mapstructure:"RAM_reservation" cty:"RAM_reservation"`
|
||||||
|
RAMReserveAll *bool `mapstructure:"RAM_reserve_all" cty:"RAM_reserve_all"`
|
||||||
|
MemoryHotAddEnabled *bool `mapstructure:"RAM_hot_plug" cty:"RAM_hot_plug"`
|
||||||
|
VideoRAM *int64 `mapstructure:"video_ram" cty:"video_ram"`
|
||||||
|
NestedHV *bool `mapstructure:"NestedHV" cty:"NestedHV"`
|
||||||
|
ConfigParams map[string]string `mapstructure:"configuration_parameters" cty:"configuration_parameters"`
|
||||||
|
BootOrder *string `mapstructure:"boot_order" cty:"boot_order"`
|
||||||
|
WaitTimeout *string `mapstructure:"ip_wait_timeout" cty:"ip_wait_timeout"`
|
||||||
|
SettleTimeout *string `mapstructure:"ip_settle_timeout" cty:"ip_settle_timeout"`
|
||||||
|
Type *string `mapstructure:"communicator" cty:"communicator"`
|
||||||
|
PauseBeforeConnect *string `mapstructure:"pause_before_connecting" cty:"pause_before_connecting"`
|
||||||
|
SSHHost *string `mapstructure:"ssh_host" cty:"ssh_host"`
|
||||||
|
SSHPort *int `mapstructure:"ssh_port" cty:"ssh_port"`
|
||||||
|
SSHUsername *string `mapstructure:"ssh_username" cty:"ssh_username"`
|
||||||
|
SSHPassword *string `mapstructure:"ssh_password" cty:"ssh_password"`
|
||||||
|
SSHKeyPairName *string `mapstructure:"ssh_keypair_name" cty:"ssh_keypair_name"`
|
||||||
|
SSHTemporaryKeyPairName *string `mapstructure:"temporary_key_pair_name" cty:"temporary_key_pair_name"`
|
||||||
|
SSHClearAuthorizedKeys *bool `mapstructure:"ssh_clear_authorized_keys" cty:"ssh_clear_authorized_keys"`
|
||||||
|
SSHPrivateKeyFile *string `mapstructure:"ssh_private_key_file" cty:"ssh_private_key_file"`
|
||||||
|
SSHPty *bool `mapstructure:"ssh_pty" cty:"ssh_pty"`
|
||||||
|
SSHTimeout *string `mapstructure:"ssh_timeout" cty:"ssh_timeout"`
|
||||||
|
SSHAgentAuth *bool `mapstructure:"ssh_agent_auth" cty:"ssh_agent_auth"`
|
||||||
|
SSHDisableAgentForwarding *bool `mapstructure:"ssh_disable_agent_forwarding" cty:"ssh_disable_agent_forwarding"`
|
||||||
|
SSHHandshakeAttempts *int `mapstructure:"ssh_handshake_attempts" cty:"ssh_handshake_attempts"`
|
||||||
|
SSHBastionHost *string `mapstructure:"ssh_bastion_host" cty:"ssh_bastion_host"`
|
||||||
|
SSHBastionPort *int `mapstructure:"ssh_bastion_port" cty:"ssh_bastion_port"`
|
||||||
|
SSHBastionAgentAuth *bool `mapstructure:"ssh_bastion_agent_auth" cty:"ssh_bastion_agent_auth"`
|
||||||
|
SSHBastionUsername *string `mapstructure:"ssh_bastion_username" cty:"ssh_bastion_username"`
|
||||||
|
SSHBastionPassword *string `mapstructure:"ssh_bastion_password" cty:"ssh_bastion_password"`
|
||||||
|
SSHBastionPrivateKeyFile *string `mapstructure:"ssh_bastion_private_key_file" cty:"ssh_bastion_private_key_file"`
|
||||||
|
SSHFileTransferMethod *string `mapstructure:"ssh_file_transfer_method" cty:"ssh_file_transfer_method"`
|
||||||
|
SSHProxyHost *string `mapstructure:"ssh_proxy_host" cty:"ssh_proxy_host"`
|
||||||
|
SSHProxyPort *int `mapstructure:"ssh_proxy_port" cty:"ssh_proxy_port"`
|
||||||
|
SSHProxyUsername *string `mapstructure:"ssh_proxy_username" cty:"ssh_proxy_username"`
|
||||||
|
SSHProxyPassword *string `mapstructure:"ssh_proxy_password" cty:"ssh_proxy_password"`
|
||||||
|
SSHKeepAliveInterval *string `mapstructure:"ssh_keep_alive_interval" cty:"ssh_keep_alive_interval"`
|
||||||
|
SSHReadWriteTimeout *string `mapstructure:"ssh_read_write_timeout" cty:"ssh_read_write_timeout"`
|
||||||
|
SSHRemoteTunnels []string `mapstructure:"ssh_remote_tunnels" cty:"ssh_remote_tunnels"`
|
||||||
|
SSHLocalTunnels []string `mapstructure:"ssh_local_tunnels" cty:"ssh_local_tunnels"`
|
||||||
|
SSHPublicKey []byte `mapstructure:"ssh_public_key" cty:"ssh_public_key"`
|
||||||
|
SSHPrivateKey []byte `mapstructure:"ssh_private_key" cty:"ssh_private_key"`
|
||||||
|
WinRMUser *string `mapstructure:"winrm_username" cty:"winrm_username"`
|
||||||
|
WinRMPassword *string `mapstructure:"winrm_password" cty:"winrm_password"`
|
||||||
|
WinRMHost *string `mapstructure:"winrm_host" cty:"winrm_host"`
|
||||||
|
WinRMPort *int `mapstructure:"winrm_port" cty:"winrm_port"`
|
||||||
|
WinRMTimeout *string `mapstructure:"winrm_timeout" cty:"winrm_timeout"`
|
||||||
|
WinRMUseSSL *bool `mapstructure:"winrm_use_ssl" cty:"winrm_use_ssl"`
|
||||||
|
WinRMInsecure *bool `mapstructure:"winrm_insecure" cty:"winrm_insecure"`
|
||||||
|
WinRMUseNTLM *bool `mapstructure:"winrm_use_ntlm" cty:"winrm_use_ntlm"`
|
||||||
|
Command *string `mapstructure:"shutdown_command" cty:"shutdown_command"`
|
||||||
|
Timeout *string `mapstructure:"shutdown_timeout" cty:"shutdown_timeout"`
|
||||||
|
CreateSnapshot *bool `mapstructure:"create_snapshot" cty:"create_snapshot"`
|
||||||
|
ConvertToTemplate *bool `mapstructure:"convert_to_template" cty:"convert_to_template"`
|
||||||
|
}
|
||||||
|
|
||||||
|
// FlatMapstructure returns a new FlatConfig.
|
||||||
|
// FlatConfig is an auto-generated flat version of Config.
|
||||||
|
// Where the contents a fields with a `mapstructure:,squash` tag are bubbled up.
|
||||||
|
func (*Config) FlatMapstructure() interface{ HCL2Spec() map[string]hcldec.Spec } {
|
||||||
|
return new(FlatConfig)
|
||||||
|
}
|
||||||
|
|
||||||
|
// HCL2Spec returns the hcl spec of a Config.
|
||||||
|
// This spec is used by HCL to read the fields of Config.
|
||||||
|
// The decoded values from this spec will then be applied to a FlatConfig.
|
||||||
|
func (*FlatConfig) HCL2Spec() map[string]hcldec.Spec {
|
||||||
|
s := map[string]hcldec.Spec{
|
||||||
|
"packer_build_name": &hcldec.AttrSpec{Name: "packer_build_name", Type: cty.String, Required: false},
|
||||||
|
"packer_builder_type": &hcldec.AttrSpec{Name: "packer_builder_type", Type: cty.String, Required: false},
|
||||||
|
"packer_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.BlockAttrsSpec{TypeName: "packer_user_variables", ElementType: cty.String, Required: false},
|
||||||
|
"packer_sensitive_variables": &hcldec.AttrSpec{Name: "packer_sensitive_variables", Type: cty.List(cty.String), Required: false},
|
||||||
|
"vcenter_server": &hcldec.AttrSpec{Name: "vcenter_server", Type: cty.String, Required: false},
|
||||||
|
"username": &hcldec.AttrSpec{Name: "username", Type: cty.String, Required: false},
|
||||||
|
"password": &hcldec.AttrSpec{Name: "password", Type: cty.String, Required: false},
|
||||||
|
"insecure_connection": &hcldec.AttrSpec{Name: "insecure_connection", Type: cty.Bool, Required: false},
|
||||||
|
"datacenter": &hcldec.AttrSpec{Name: "datacenter", Type: cty.String, Required: false},
|
||||||
|
"template": &hcldec.AttrSpec{Name: "template", Type: cty.String, Required: false},
|
||||||
|
"disk_size": &hcldec.AttrSpec{Name: "disk_size", Type: cty.Number, Required: false},
|
||||||
|
"linked_clone": &hcldec.AttrSpec{Name: "linked_clone", Type: cty.Bool, Required: false},
|
||||||
|
"network": &hcldec.AttrSpec{Name: "network", Type: cty.String, Required: false},
|
||||||
|
"notes": &hcldec.AttrSpec{Name: "notes", Type: cty.String, Required: false},
|
||||||
|
"vm_name": &hcldec.AttrSpec{Name: "vm_name", Type: cty.String, Required: false},
|
||||||
|
"folder": &hcldec.AttrSpec{Name: "folder", Type: cty.String, Required: false},
|
||||||
|
"cluster": &hcldec.AttrSpec{Name: "cluster", Type: cty.String, Required: false},
|
||||||
|
"host": &hcldec.AttrSpec{Name: "host", Type: cty.String, Required: false},
|
||||||
|
"resource_pool": &hcldec.AttrSpec{Name: "resource_pool", Type: cty.String, Required: false},
|
||||||
|
"datastore": &hcldec.AttrSpec{Name: "datastore", Type: cty.String, Required: false},
|
||||||
|
"CPUs": &hcldec.AttrSpec{Name: "CPUs", Type: cty.Number, Required: false},
|
||||||
|
"cpu_cores": &hcldec.AttrSpec{Name: "cpu_cores", Type: cty.Number, Required: false},
|
||||||
|
"CPU_reservation": &hcldec.AttrSpec{Name: "CPU_reservation", Type: cty.Number, Required: false},
|
||||||
|
"CPU_limit": &hcldec.AttrSpec{Name: "CPU_limit", Type: cty.Number, Required: false},
|
||||||
|
"CPU_hot_plug": &hcldec.AttrSpec{Name: "CPU_hot_plug", Type: cty.Bool, Required: false},
|
||||||
|
"RAM": &hcldec.AttrSpec{Name: "RAM", Type: cty.Number, Required: false},
|
||||||
|
"RAM_reservation": &hcldec.AttrSpec{Name: "RAM_reservation", Type: cty.Number, Required: false},
|
||||||
|
"RAM_reserve_all": &hcldec.AttrSpec{Name: "RAM_reserve_all", Type: cty.Bool, Required: false},
|
||||||
|
"RAM_hot_plug": &hcldec.AttrSpec{Name: "RAM_hot_plug", Type: cty.Bool, Required: false},
|
||||||
|
"video_ram": &hcldec.AttrSpec{Name: "video_ram", Type: cty.Number, Required: false},
|
||||||
|
"NestedHV": &hcldec.AttrSpec{Name: "NestedHV", Type: cty.Bool, Required: false},
|
||||||
|
"configuration_parameters": &hcldec.BlockAttrsSpec{TypeName: "configuration_parameters", ElementType: cty.String, Required: false},
|
||||||
|
"boot_order": &hcldec.AttrSpec{Name: "boot_order", Type: cty.String, Required: false},
|
||||||
|
"ip_wait_timeout": &hcldec.AttrSpec{Name: "ip_wait_timeout", Type: cty.String, Required: false},
|
||||||
|
"ip_settle_timeout": &hcldec.AttrSpec{Name: "ip_settle_timeout", 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},
|
||||||
|
"ssh_clear_authorized_keys": &hcldec.AttrSpec{Name: "ssh_clear_authorized_keys", Type: cty.Bool, Required: false},
|
||||||
|
"ssh_private_key_file": &hcldec.AttrSpec{Name: "ssh_private_key_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_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_private_key_file": &hcldec.AttrSpec{Name: "ssh_bastion_private_key_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_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},
|
||||||
|
"shutdown_command": &hcldec.AttrSpec{Name: "shutdown_command", Type: cty.String, Required: false},
|
||||||
|
"shutdown_timeout": &hcldec.AttrSpec{Name: "shutdown_timeout", Type: cty.String, Required: false},
|
||||||
|
"create_snapshot": &hcldec.AttrSpec{Name: "create_snapshot", Type: cty.Bool, Required: false},
|
||||||
|
"convert_to_template": &hcldec.AttrSpec{Name: "convert_to_template", Type: cty.Bool, Required: false},
|
||||||
|
}
|
||||||
|
return s
|
||||||
|
}
|
|
@ -0,0 +1,74 @@
|
||||||
|
package clone
|
||||||
|
|
||||||
|
import (
|
||||||
|
"testing"
|
||||||
|
"time"
|
||||||
|
)
|
||||||
|
|
||||||
|
func TestCloneConfig_MinimalConfig(t *testing.T) {
|
||||||
|
c := new(Config)
|
||||||
|
warns, errs := c.Prepare(minimalConfig())
|
||||||
|
testConfigOk(t, warns, errs)
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestCloneConfig_MandatoryParameters(t *testing.T) {
|
||||||
|
params := []string{"vcenter_server", "username", "password", "template", "vm_name", "host"}
|
||||||
|
for _, param := range params {
|
||||||
|
raw := minimalConfig()
|
||||||
|
raw[param] = ""
|
||||||
|
c := new(Config)
|
||||||
|
warns, err := c.Prepare(raw)
|
||||||
|
testConfigErr(t, param, warns, err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestCloneConfig_Timeout(t *testing.T) {
|
||||||
|
raw := minimalConfig()
|
||||||
|
raw["shutdown_timeout"] = "3m"
|
||||||
|
conf := new(Config)
|
||||||
|
warns, err := conf.Prepare(raw)
|
||||||
|
testConfigOk(t, warns, err)
|
||||||
|
if conf.ShutdownConfig.Timeout != 3*time.Minute {
|
||||||
|
t.Fatalf("shutdown_timeout sould be equal 3 minutes, got %v", conf.ShutdownConfig.Timeout)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestCloneConfig_RAMReservation(t *testing.T) {
|
||||||
|
raw := minimalConfig()
|
||||||
|
raw["RAM_reservation"] = 1000
|
||||||
|
raw["RAM_reserve_all"] = true
|
||||||
|
c := new(Config)
|
||||||
|
warns, err := c.Prepare(raw)
|
||||||
|
testConfigErr(t, "RAM_reservation", warns, err)
|
||||||
|
}
|
||||||
|
|
||||||
|
func minimalConfig() map[string]interface{} {
|
||||||
|
return map[string]interface{}{
|
||||||
|
"vcenter_server": "vcenter.domain.local",
|
||||||
|
"username": "root",
|
||||||
|
"password": "vmware",
|
||||||
|
"template": "ubuntu",
|
||||||
|
"vm_name": "vm1",
|
||||||
|
"host": "esxi1.domain.local",
|
||||||
|
"ssh_username": "root",
|
||||||
|
"ssh_password": "secret",
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func testConfigOk(t *testing.T, warns []string, err error) {
|
||||||
|
if len(warns) > 0 {
|
||||||
|
t.Errorf("Should be no warnings: %#v", warns)
|
||||||
|
}
|
||||||
|
if err != nil {
|
||||||
|
t.Errorf("Unexpected error: %s", err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func testConfigErr(t *testing.T, context string, warns []string, err error) {
|
||||||
|
if len(warns) > 0 {
|
||||||
|
t.Errorf("Should be no warnings: %#v", warns)
|
||||||
|
}
|
||||||
|
if err == nil {
|
||||||
|
t.Error("An error is not raised for", context)
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,124 @@
|
||||||
|
//go:generate struct-markdown
|
||||||
|
//go:generate mapstructure-to-hcl2 -type CloneConfig
|
||||||
|
|
||||||
|
package clone
|
||||||
|
|
||||||
|
import (
|
||||||
|
"context"
|
||||||
|
"fmt"
|
||||||
|
"github.com/hashicorp/packer/builder/vsphere/common"
|
||||||
|
"github.com/hashicorp/packer/builder/vsphere/driver"
|
||||||
|
"github.com/hashicorp/packer/helper/multistep"
|
||||||
|
"github.com/hashicorp/packer/packer"
|
||||||
|
)
|
||||||
|
|
||||||
|
type CloneConfig struct {
|
||||||
|
// Name of source VM. Path is optional.
|
||||||
|
Template string `mapstructure:"template"`
|
||||||
|
// The size of the disk in MB.
|
||||||
|
DiskSize int64 `mapstructure:"disk_size"`
|
||||||
|
// Create VM as a linked clone from latest snapshot. Defaults to `false`.
|
||||||
|
LinkedClone bool `mapstructure:"linked_clone"`
|
||||||
|
// Set network VM will be connected to.
|
||||||
|
Network string `mapstructure:"network"`
|
||||||
|
// VM notes.
|
||||||
|
Notes string `mapstructure:"notes"`
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *CloneConfig) Prepare() []error {
|
||||||
|
var errs []error
|
||||||
|
|
||||||
|
if c.Template == "" {
|
||||||
|
errs = append(errs, fmt.Errorf("'template' is required"))
|
||||||
|
}
|
||||||
|
|
||||||
|
if c.LinkedClone == true && c.DiskSize != 0 {
|
||||||
|
errs = append(errs, fmt.Errorf("'linked_clone' and 'disk_size' cannot be used together"))
|
||||||
|
}
|
||||||
|
|
||||||
|
return errs
|
||||||
|
}
|
||||||
|
|
||||||
|
type StepCloneVM struct {
|
||||||
|
Config *CloneConfig
|
||||||
|
Location *common.LocationConfig
|
||||||
|
Force bool
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *StepCloneVM) Run(ctx context.Context, state multistep.StateBag) multistep.StepAction {
|
||||||
|
ui := state.Get("ui").(packer.Ui)
|
||||||
|
d := state.Get("driver").(*driver.Driver)
|
||||||
|
|
||||||
|
vm, err := d.FindVM(s.Location.VMName)
|
||||||
|
|
||||||
|
if s.Force == false && err == nil {
|
||||||
|
state.Put("error", fmt.Errorf("%s already exists, you can use -force flag to destroy it", s.Location.VMName))
|
||||||
|
return multistep.ActionHalt
|
||||||
|
} else if s.Force == true && err == nil {
|
||||||
|
ui.Say(fmt.Sprintf("the vm/template %s already exists, but deleting it due to -force flag", s.Location.VMName))
|
||||||
|
err := vm.Destroy()
|
||||||
|
if err != nil {
|
||||||
|
state.Put("error", fmt.Errorf("error destroying %s: %v", s.Location.VMName, err))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
ui.Say("Cloning VM...")
|
||||||
|
template, err := d.FindVM(s.Config.Template)
|
||||||
|
if err != nil {
|
||||||
|
state.Put("error", err)
|
||||||
|
return multistep.ActionHalt
|
||||||
|
}
|
||||||
|
|
||||||
|
vm, err = template.Clone(ctx, &driver.CloneConfig{
|
||||||
|
Name: s.Location.VMName,
|
||||||
|
Folder: s.Location.Folder,
|
||||||
|
Cluster: s.Location.Cluster,
|
||||||
|
Host: s.Location.Host,
|
||||||
|
ResourcePool: s.Location.ResourcePool,
|
||||||
|
Datastore: s.Location.Datastore,
|
||||||
|
LinkedClone: s.Config.LinkedClone,
|
||||||
|
Network: s.Config.Network,
|
||||||
|
Annotation: s.Config.Notes,
|
||||||
|
})
|
||||||
|
if err != nil {
|
||||||
|
state.Put("error", err)
|
||||||
|
return multistep.ActionHalt
|
||||||
|
}
|
||||||
|
if vm == nil {
|
||||||
|
return multistep.ActionHalt
|
||||||
|
}
|
||||||
|
state.Put("vm", vm)
|
||||||
|
|
||||||
|
if s.Config.DiskSize > 0 {
|
||||||
|
err = vm.ResizeDisk(s.Config.DiskSize)
|
||||||
|
if err != nil {
|
||||||
|
state.Put("error", err)
|
||||||
|
return multistep.ActionHalt
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return multistep.ActionContinue
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *StepCloneVM) Cleanup(state multistep.StateBag) {
|
||||||
|
_, cancelled := state.GetOk(multistep.StateCancelled)
|
||||||
|
_, halted := state.GetOk(multistep.StateHalted)
|
||||||
|
if !cancelled && !halted {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
ui := state.Get("ui").(packer.Ui)
|
||||||
|
|
||||||
|
st := state.Get("vm")
|
||||||
|
if st == nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
vm := st.(*driver.VirtualMachine)
|
||||||
|
|
||||||
|
ui.Say("Destroying VM...")
|
||||||
|
|
||||||
|
err := vm.Destroy()
|
||||||
|
if err != nil {
|
||||||
|
ui.Error(err.Error())
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,38 @@
|
||||||
|
// Code generated by "mapstructure-to-hcl2 -type CloneConfig"; DO NOT EDIT.
|
||||||
|
package clone
|
||||||
|
|
||||||
|
import (
|
||||||
|
"github.com/hashicorp/hcl/v2/hcldec"
|
||||||
|
"github.com/zclconf/go-cty/cty"
|
||||||
|
)
|
||||||
|
|
||||||
|
// FlatCloneConfig is an auto-generated flat version of CloneConfig.
|
||||||
|
// Where the contents of a field with a `mapstructure:,squash` tag are bubbled up.
|
||||||
|
type FlatCloneConfig struct {
|
||||||
|
Template *string `mapstructure:"template" cty:"template"`
|
||||||
|
DiskSize *int64 `mapstructure:"disk_size" cty:"disk_size"`
|
||||||
|
LinkedClone *bool `mapstructure:"linked_clone" cty:"linked_clone"`
|
||||||
|
Network *string `mapstructure:"network" cty:"network"`
|
||||||
|
Notes *string `mapstructure:"notes" cty:"notes"`
|
||||||
|
}
|
||||||
|
|
||||||
|
// FlatMapstructure returns a new FlatCloneConfig.
|
||||||
|
// FlatCloneConfig is an auto-generated flat version of CloneConfig.
|
||||||
|
// Where the contents a fields with a `mapstructure:,squash` tag are bubbled up.
|
||||||
|
func (*CloneConfig) FlatMapstructure() interface{ HCL2Spec() map[string]hcldec.Spec } {
|
||||||
|
return new(FlatCloneConfig)
|
||||||
|
}
|
||||||
|
|
||||||
|
// HCL2Spec returns the hcl spec of a CloneConfig.
|
||||||
|
// This spec is used by HCL to read the fields of CloneConfig.
|
||||||
|
// The decoded values from this spec will then be applied to a FlatCloneConfig.
|
||||||
|
func (*FlatCloneConfig) HCL2Spec() map[string]hcldec.Spec {
|
||||||
|
s := map[string]hcldec.Spec{
|
||||||
|
"template": &hcldec.AttrSpec{Name: "template", Type: cty.String, Required: false},
|
||||||
|
"disk_size": &hcldec.AttrSpec{Name: "disk_size", Type: cty.Number, Required: false},
|
||||||
|
"linked_clone": &hcldec.AttrSpec{Name: "linked_clone", Type: cty.Bool, Required: false},
|
||||||
|
"network": &hcldec.AttrSpec{Name: "network", Type: cty.String, Required: false},
|
||||||
|
"notes": &hcldec.AttrSpec{Name: "notes", Type: cty.String, Required: false},
|
||||||
|
}
|
||||||
|
return s
|
||||||
|
}
|
|
@ -0,0 +1,36 @@
|
||||||
|
package common
|
||||||
|
|
||||||
|
import (
|
||||||
|
"github.com/hashicorp/packer/builder/vsphere/driver"
|
||||||
|
)
|
||||||
|
|
||||||
|
const BuilderId = "jetbrains.vsphere"
|
||||||
|
|
||||||
|
type Artifact struct {
|
||||||
|
Name string
|
||||||
|
VM *driver.VirtualMachine
|
||||||
|
}
|
||||||
|
|
||||||
|
func (a *Artifact) BuilderId() string {
|
||||||
|
return BuilderId
|
||||||
|
}
|
||||||
|
|
||||||
|
func (a *Artifact) Files() []string {
|
||||||
|
return []string{}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (a *Artifact) Id() string {
|
||||||
|
return a.Name
|
||||||
|
}
|
||||||
|
|
||||||
|
func (a *Artifact) String() string {
|
||||||
|
return a.Name
|
||||||
|
}
|
||||||
|
|
||||||
|
func (a *Artifact) State(name string) interface{} {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (a *Artifact) Destroy() error {
|
||||||
|
return a.VM.Destroy()
|
||||||
|
}
|
|
@ -0,0 +1,39 @@
|
||||||
|
//go:generate struct-markdown
|
||||||
|
//go:generate mapstructure-to-hcl2 -type LocationConfig
|
||||||
|
|
||||||
|
package common
|
||||||
|
|
||||||
|
import "fmt"
|
||||||
|
|
||||||
|
type LocationConfig struct {
|
||||||
|
// Name of the new VM to create.
|
||||||
|
VMName string `mapstructure:"vm_name"`
|
||||||
|
// VM folder to create the VM in.
|
||||||
|
Folder string `mapstructure:"folder"`
|
||||||
|
// ESXi cluster where target VM is created. See
|
||||||
|
// [Working with Clusters](#working-with-clusters).
|
||||||
|
Cluster string `mapstructure:"cluster"`
|
||||||
|
// ESXi host where target VM is created. A full path must be specified if
|
||||||
|
// the host is in a folder. For example `folder/host`. See the
|
||||||
|
// `Specifying Clusters and Hosts` section above for more details.
|
||||||
|
Host string `mapstructure:"host"`
|
||||||
|
// VMWare resource pool. Defaults to the root resource pool of the
|
||||||
|
// `host` or `cluster`.
|
||||||
|
ResourcePool string `mapstructure:"resource_pool"`
|
||||||
|
// VMWare datastore. Required if `host` is a cluster, or if `host` has
|
||||||
|
// multiple datastores.
|
||||||
|
Datastore string `mapstructure:"datastore"`
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *LocationConfig) Prepare() []error {
|
||||||
|
var errs []error
|
||||||
|
|
||||||
|
if c.VMName == "" {
|
||||||
|
errs = append(errs, fmt.Errorf("'vm_name' is required"))
|
||||||
|
}
|
||||||
|
if c.Cluster == "" && c.Host == "" {
|
||||||
|
errs = append(errs, fmt.Errorf("'host' or 'cluster' is required"))
|
||||||
|
}
|
||||||
|
|
||||||
|
return errs
|
||||||
|
}
|
|
@ -0,0 +1,40 @@
|
||||||
|
// Code generated by "mapstructure-to-hcl2 -type LocationConfig"; DO NOT EDIT.
|
||||||
|
package common
|
||||||
|
|
||||||
|
import (
|
||||||
|
"github.com/hashicorp/hcl/v2/hcldec"
|
||||||
|
"github.com/zclconf/go-cty/cty"
|
||||||
|
)
|
||||||
|
|
||||||
|
// FlatLocationConfig is an auto-generated flat version of LocationConfig.
|
||||||
|
// Where the contents of a field with a `mapstructure:,squash` tag are bubbled up.
|
||||||
|
type FlatLocationConfig struct {
|
||||||
|
VMName *string `mapstructure:"vm_name" cty:"vm_name"`
|
||||||
|
Folder *string `mapstructure:"folder" cty:"folder"`
|
||||||
|
Cluster *string `mapstructure:"cluster" cty:"cluster"`
|
||||||
|
Host *string `mapstructure:"host" cty:"host"`
|
||||||
|
ResourcePool *string `mapstructure:"resource_pool" cty:"resource_pool"`
|
||||||
|
Datastore *string `mapstructure:"datastore" cty:"datastore"`
|
||||||
|
}
|
||||||
|
|
||||||
|
// FlatMapstructure returns a new FlatLocationConfig.
|
||||||
|
// FlatLocationConfig is an auto-generated flat version of LocationConfig.
|
||||||
|
// Where the contents a fields with a `mapstructure:,squash` tag are bubbled up.
|
||||||
|
func (*LocationConfig) FlatMapstructure() interface{ HCL2Spec() map[string]hcldec.Spec } {
|
||||||
|
return new(FlatLocationConfig)
|
||||||
|
}
|
||||||
|
|
||||||
|
// HCL2Spec returns the hcl spec of a LocationConfig.
|
||||||
|
// This spec is used by HCL to read the fields of LocationConfig.
|
||||||
|
// The decoded values from this spec will then be applied to a FlatLocationConfig.
|
||||||
|
func (*FlatLocationConfig) HCL2Spec() map[string]hcldec.Spec {
|
||||||
|
s := map[string]hcldec.Spec{
|
||||||
|
"vm_name": &hcldec.AttrSpec{Name: "vm_name", Type: cty.String, Required: false},
|
||||||
|
"folder": &hcldec.AttrSpec{Name: "folder", Type: cty.String, Required: false},
|
||||||
|
"cluster": &hcldec.AttrSpec{Name: "cluster", Type: cty.String, Required: false},
|
||||||
|
"host": &hcldec.AttrSpec{Name: "host", Type: cty.String, Required: false},
|
||||||
|
"resource_pool": &hcldec.AttrSpec{Name: "resource_pool", Type: cty.String, Required: false},
|
||||||
|
"datastore": &hcldec.AttrSpec{Name: "datastore", Type: cty.String, Required: false},
|
||||||
|
}
|
||||||
|
return s
|
||||||
|
}
|
|
@ -0,0 +1,15 @@
|
||||||
|
package common
|
||||||
|
|
||||||
|
import (
|
||||||
|
"github.com/hashicorp/packer/helper/multistep"
|
||||||
|
)
|
||||||
|
|
||||||
|
func CommHost(host string) func(multistep.StateBag) (string, error) {
|
||||||
|
return func(state multistep.StateBag) (string, error) {
|
||||||
|
if host != "" {
|
||||||
|
return host, nil
|
||||||
|
} else {
|
||||||
|
return state.Get("ip").(string), nil
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,38 @@
|
||||||
|
//go:generate struct-markdown
|
||||||
|
//go:generate mapstructure-to-hcl2 -type ConfigParamsConfig
|
||||||
|
|
||||||
|
package common
|
||||||
|
|
||||||
|
import (
|
||||||
|
"context"
|
||||||
|
"fmt"
|
||||||
|
"github.com/hashicorp/packer/builder/vsphere/driver"
|
||||||
|
"github.com/hashicorp/packer/helper/multistep"
|
||||||
|
"github.com/hashicorp/packer/packer"
|
||||||
|
)
|
||||||
|
|
||||||
|
type ConfigParamsConfig struct {
|
||||||
|
// Custom parameters.
|
||||||
|
ConfigParams map[string]string `mapstructure:"configuration_parameters"`
|
||||||
|
}
|
||||||
|
|
||||||
|
type StepConfigParams struct {
|
||||||
|
Config *ConfigParamsConfig
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *StepConfigParams) Run(_ context.Context, state multistep.StateBag) multistep.StepAction {
|
||||||
|
ui := state.Get("ui").(packer.Ui)
|
||||||
|
vm := state.Get("vm").(*driver.VirtualMachine)
|
||||||
|
|
||||||
|
if s.Config.ConfigParams != nil {
|
||||||
|
ui.Say("Adding configuration parameters...")
|
||||||
|
if err := vm.AddConfigParams(s.Config.ConfigParams); err != nil {
|
||||||
|
state.Put("error", fmt.Errorf("error adding configuration parameters: %v", err))
|
||||||
|
return multistep.ActionHalt
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return multistep.ActionContinue
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *StepConfigParams) Cleanup(state multistep.StateBag) {}
|
|
@ -0,0 +1,30 @@
|
||||||
|
// Code generated by "mapstructure-to-hcl2 -type ConfigParamsConfig"; DO NOT EDIT.
|
||||||
|
package common
|
||||||
|
|
||||||
|
import (
|
||||||
|
"github.com/hashicorp/hcl/v2/hcldec"
|
||||||
|
"github.com/zclconf/go-cty/cty"
|
||||||
|
)
|
||||||
|
|
||||||
|
// FlatConfigParamsConfig is an auto-generated flat version of ConfigParamsConfig.
|
||||||
|
// Where the contents of a field with a `mapstructure:,squash` tag are bubbled up.
|
||||||
|
type FlatConfigParamsConfig struct {
|
||||||
|
ConfigParams map[string]string `mapstructure:"configuration_parameters" cty:"configuration_parameters"`
|
||||||
|
}
|
||||||
|
|
||||||
|
// FlatMapstructure returns a new FlatConfigParamsConfig.
|
||||||
|
// FlatConfigParamsConfig is an auto-generated flat version of ConfigParamsConfig.
|
||||||
|
// Where the contents a fields with a `mapstructure:,squash` tag are bubbled up.
|
||||||
|
func (*ConfigParamsConfig) FlatMapstructure() interface{ HCL2Spec() map[string]hcldec.Spec } {
|
||||||
|
return new(FlatConfigParamsConfig)
|
||||||
|
}
|
||||||
|
|
||||||
|
// HCL2Spec returns the hcl spec of a ConfigParamsConfig.
|
||||||
|
// This spec is used by HCL to read the fields of ConfigParamsConfig.
|
||||||
|
// The decoded values from this spec will then be applied to a FlatConfigParamsConfig.
|
||||||
|
func (*FlatConfigParamsConfig) HCL2Spec() map[string]hcldec.Spec {
|
||||||
|
s := map[string]hcldec.Spec{
|
||||||
|
"configuration_parameters": &hcldec.BlockAttrsSpec{TypeName: "configuration_parameters", ElementType: cty.String, Required: false},
|
||||||
|
}
|
||||||
|
return s
|
||||||
|
}
|
|
@ -0,0 +1,63 @@
|
||||||
|
//go:generate struct-markdown
|
||||||
|
//go:generate mapstructure-to-hcl2 -type ConnectConfig
|
||||||
|
|
||||||
|
package common
|
||||||
|
|
||||||
|
import (
|
||||||
|
"context"
|
||||||
|
"fmt"
|
||||||
|
"github.com/hashicorp/packer/builder/vsphere/driver"
|
||||||
|
"github.com/hashicorp/packer/helper/multistep"
|
||||||
|
)
|
||||||
|
|
||||||
|
type ConnectConfig struct {
|
||||||
|
// vCenter server hostname.
|
||||||
|
VCenterServer string `mapstructure:"vcenter_server"`
|
||||||
|
// vSphere username.
|
||||||
|
Username string `mapstructure:"username"`
|
||||||
|
// vSphere password.
|
||||||
|
Password string `mapstructure:"password"`
|
||||||
|
// Do not validate vCenter server's TLS certificate. Defaults to `false`.
|
||||||
|
InsecureConnection bool `mapstructure:"insecure_connection"`
|
||||||
|
// VMware datacenter name. Required if there is more than one datacenter in vCenter.
|
||||||
|
Datacenter string `mapstructure:"datacenter"`
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *ConnectConfig) Prepare() []error {
|
||||||
|
var errs []error
|
||||||
|
|
||||||
|
if c.VCenterServer == "" {
|
||||||
|
errs = append(errs, fmt.Errorf("'vcenter_server' is required"))
|
||||||
|
}
|
||||||
|
if c.Username == "" {
|
||||||
|
errs = append(errs, fmt.Errorf("'username' is required"))
|
||||||
|
}
|
||||||
|
if c.Password == "" {
|
||||||
|
errs = append(errs, fmt.Errorf("'password' is required"))
|
||||||
|
}
|
||||||
|
|
||||||
|
return errs
|
||||||
|
}
|
||||||
|
|
||||||
|
type StepConnect struct {
|
||||||
|
Config *ConnectConfig
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *StepConnect) Run(_ context.Context, state multistep.StateBag) multistep.StepAction {
|
||||||
|
d, err := driver.NewDriver(&driver.ConnectConfig{
|
||||||
|
VCenterServer: s.Config.VCenterServer,
|
||||||
|
Username: s.Config.Username,
|
||||||
|
Password: s.Config.Password,
|
||||||
|
InsecureConnection: s.Config.InsecureConnection,
|
||||||
|
Datacenter: s.Config.Datacenter,
|
||||||
|
})
|
||||||
|
if err != nil {
|
||||||
|
state.Put("error", err)
|
||||||
|
return multistep.ActionHalt
|
||||||
|
}
|
||||||
|
state.Put("driver", d)
|
||||||
|
|
||||||
|
return multistep.ActionContinue
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *StepConnect) Cleanup(multistep.StateBag) {}
|
|
@ -0,0 +1,38 @@
|
||||||
|
// Code generated by "mapstructure-to-hcl2 -type ConnectConfig"; DO NOT EDIT.
|
||||||
|
package common
|
||||||
|
|
||||||
|
import (
|
||||||
|
"github.com/hashicorp/hcl/v2/hcldec"
|
||||||
|
"github.com/zclconf/go-cty/cty"
|
||||||
|
)
|
||||||
|
|
||||||
|
// FlatConnectConfig is an auto-generated flat version of ConnectConfig.
|
||||||
|
// Where the contents of a field with a `mapstructure:,squash` tag are bubbled up.
|
||||||
|
type FlatConnectConfig struct {
|
||||||
|
VCenterServer *string `mapstructure:"vcenter_server" cty:"vcenter_server"`
|
||||||
|
Username *string `mapstructure:"username" cty:"username"`
|
||||||
|
Password *string `mapstructure:"password" cty:"password"`
|
||||||
|
InsecureConnection *bool `mapstructure:"insecure_connection" cty:"insecure_connection"`
|
||||||
|
Datacenter *string `mapstructure:"datacenter" cty:"datacenter"`
|
||||||
|
}
|
||||||
|
|
||||||
|
// FlatMapstructure returns a new FlatConnectConfig.
|
||||||
|
// FlatConnectConfig is an auto-generated flat version of ConnectConfig.
|
||||||
|
// Where the contents a fields with a `mapstructure:,squash` tag are bubbled up.
|
||||||
|
func (*ConnectConfig) FlatMapstructure() interface{ HCL2Spec() map[string]hcldec.Spec } {
|
||||||
|
return new(FlatConnectConfig)
|
||||||
|
}
|
||||||
|
|
||||||
|
// HCL2Spec returns the hcl spec of a ConnectConfig.
|
||||||
|
// This spec is used by HCL to read the fields of ConnectConfig.
|
||||||
|
// The decoded values from this spec will then be applied to a FlatConnectConfig.
|
||||||
|
func (*FlatConnectConfig) HCL2Spec() map[string]hcldec.Spec {
|
||||||
|
s := map[string]hcldec.Spec{
|
||||||
|
"vcenter_server": &hcldec.AttrSpec{Name: "vcenter_server", Type: cty.String, Required: false},
|
||||||
|
"username": &hcldec.AttrSpec{Name: "username", Type: cty.String, Required: false},
|
||||||
|
"password": &hcldec.AttrSpec{Name: "password", Type: cty.String, Required: false},
|
||||||
|
"insecure_connection": &hcldec.AttrSpec{Name: "insecure_connection", Type: cty.Bool, Required: false},
|
||||||
|
"datacenter": &hcldec.AttrSpec{Name: "datacenter", Type: cty.String, Required: false},
|
||||||
|
}
|
||||||
|
return s
|
||||||
|
}
|
|
@ -0,0 +1,83 @@
|
||||||
|
//go:generate struct-markdown
|
||||||
|
//go:generate mapstructure-to-hcl2 -type HardwareConfig
|
||||||
|
|
||||||
|
package common
|
||||||
|
|
||||||
|
import (
|
||||||
|
"context"
|
||||||
|
"fmt"
|
||||||
|
"github.com/hashicorp/packer/builder/vsphere/driver"
|
||||||
|
"github.com/hashicorp/packer/helper/multistep"
|
||||||
|
"github.com/hashicorp/packer/packer"
|
||||||
|
)
|
||||||
|
|
||||||
|
type HardwareConfig struct {
|
||||||
|
// Number of CPU sockets.
|
||||||
|
CPUs int32 `mapstructure:"CPUs"`
|
||||||
|
// Number of CPU cores per socket.
|
||||||
|
CpuCores int32 `mapstructure:"cpu_cores"`
|
||||||
|
// Amount of reserved CPU resources in MHz.
|
||||||
|
CPUReservation int64 `mapstructure:"CPU_reservation"`
|
||||||
|
// Upper limit of available CPU resources in MHz.
|
||||||
|
CPULimit int64 `mapstructure:"CPU_limit"`
|
||||||
|
// Enable CPU hot plug setting for virtual machine. Defaults to `false`.
|
||||||
|
CpuHotAddEnabled bool `mapstructure:"CPU_hot_plug"`
|
||||||
|
// Amount of RAM in MB.
|
||||||
|
RAM int64 `mapstructure:"RAM"`
|
||||||
|
// Amount of reserved RAM in MB.
|
||||||
|
RAMReservation int64 `mapstructure:"RAM_reservation"`
|
||||||
|
// Reserve all available RAM. Defaults to `false`. Cannot be used together
|
||||||
|
// with `RAM_reservation`.
|
||||||
|
RAMReserveAll bool `mapstructure:"RAM_reserve_all"`
|
||||||
|
// Enable RAM hot plug setting for virtual machine. Defaults to `false`.
|
||||||
|
MemoryHotAddEnabled bool `mapstructure:"RAM_hot_plug"`
|
||||||
|
// Amount of video memory in MB.
|
||||||
|
VideoRAM int64 `mapstructure:"video_ram"`
|
||||||
|
// Enable nested hardware virtualization for VM. Defaults to `false`.
|
||||||
|
NestedHV bool `mapstructure:"NestedHV"`
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *HardwareConfig) Prepare() []error {
|
||||||
|
var errs []error
|
||||||
|
|
||||||
|
if c.RAMReservation > 0 && c.RAMReserveAll != false {
|
||||||
|
errs = append(errs, fmt.Errorf("'RAM_reservation' and 'RAM_reserve_all' cannot be used together"))
|
||||||
|
}
|
||||||
|
|
||||||
|
return errs
|
||||||
|
}
|
||||||
|
|
||||||
|
type StepConfigureHardware struct {
|
||||||
|
Config *HardwareConfig
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *StepConfigureHardware) Run(_ context.Context, state multistep.StateBag) multistep.StepAction {
|
||||||
|
ui := state.Get("ui").(packer.Ui)
|
||||||
|
vm := state.Get("vm").(*driver.VirtualMachine)
|
||||||
|
|
||||||
|
if *s.Config != (HardwareConfig{}) {
|
||||||
|
ui.Say("Customizing hardware...")
|
||||||
|
|
||||||
|
err := vm.Configure(&driver.HardwareConfig{
|
||||||
|
CPUs: s.Config.CPUs,
|
||||||
|
CpuCores: s.Config.CpuCores,
|
||||||
|
CPUReservation: s.Config.CPUReservation,
|
||||||
|
CPULimit: s.Config.CPULimit,
|
||||||
|
RAM: s.Config.RAM,
|
||||||
|
RAMReservation: s.Config.RAMReservation,
|
||||||
|
RAMReserveAll: s.Config.RAMReserveAll,
|
||||||
|
NestedHV: s.Config.NestedHV,
|
||||||
|
CpuHotAddEnabled: s.Config.CpuHotAddEnabled,
|
||||||
|
MemoryHotAddEnabled: s.Config.MemoryHotAddEnabled,
|
||||||
|
VideoRAM: s.Config.VideoRAM,
|
||||||
|
})
|
||||||
|
if err != nil {
|
||||||
|
state.Put("error", err)
|
||||||
|
return multistep.ActionHalt
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return multistep.ActionContinue
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *StepConfigureHardware) Cleanup(multistep.StateBag) {}
|
|
@ -0,0 +1,50 @@
|
||||||
|
// Code generated by "mapstructure-to-hcl2 -type HardwareConfig"; DO NOT EDIT.
|
||||||
|
package common
|
||||||
|
|
||||||
|
import (
|
||||||
|
"github.com/hashicorp/hcl/v2/hcldec"
|
||||||
|
"github.com/zclconf/go-cty/cty"
|
||||||
|
)
|
||||||
|
|
||||||
|
// FlatHardwareConfig is an auto-generated flat version of HardwareConfig.
|
||||||
|
// Where the contents of a field with a `mapstructure:,squash` tag are bubbled up.
|
||||||
|
type FlatHardwareConfig struct {
|
||||||
|
CPUs *int32 `mapstructure:"CPUs" cty:"CPUs"`
|
||||||
|
CpuCores *int32 `mapstructure:"cpu_cores" cty:"cpu_cores"`
|
||||||
|
CPUReservation *int64 `mapstructure:"CPU_reservation" cty:"CPU_reservation"`
|
||||||
|
CPULimit *int64 `mapstructure:"CPU_limit" cty:"CPU_limit"`
|
||||||
|
CpuHotAddEnabled *bool `mapstructure:"CPU_hot_plug" cty:"CPU_hot_plug"`
|
||||||
|
RAM *int64 `mapstructure:"RAM" cty:"RAM"`
|
||||||
|
RAMReservation *int64 `mapstructure:"RAM_reservation" cty:"RAM_reservation"`
|
||||||
|
RAMReserveAll *bool `mapstructure:"RAM_reserve_all" cty:"RAM_reserve_all"`
|
||||||
|
MemoryHotAddEnabled *bool `mapstructure:"RAM_hot_plug" cty:"RAM_hot_plug"`
|
||||||
|
VideoRAM *int64 `mapstructure:"video_ram" cty:"video_ram"`
|
||||||
|
NestedHV *bool `mapstructure:"NestedHV" cty:"NestedHV"`
|
||||||
|
}
|
||||||
|
|
||||||
|
// FlatMapstructure returns a new FlatHardwareConfig.
|
||||||
|
// FlatHardwareConfig is an auto-generated flat version of HardwareConfig.
|
||||||
|
// Where the contents a fields with a `mapstructure:,squash` tag are bubbled up.
|
||||||
|
func (*HardwareConfig) FlatMapstructure() interface{ HCL2Spec() map[string]hcldec.Spec } {
|
||||||
|
return new(FlatHardwareConfig)
|
||||||
|
}
|
||||||
|
|
||||||
|
// HCL2Spec returns the hcl spec of a HardwareConfig.
|
||||||
|
// This spec is used by HCL to read the fields of HardwareConfig.
|
||||||
|
// The decoded values from this spec will then be applied to a FlatHardwareConfig.
|
||||||
|
func (*FlatHardwareConfig) HCL2Spec() map[string]hcldec.Spec {
|
||||||
|
s := map[string]hcldec.Spec{
|
||||||
|
"CPUs": &hcldec.AttrSpec{Name: "CPUs", Type: cty.Number, Required: false},
|
||||||
|
"cpu_cores": &hcldec.AttrSpec{Name: "cpu_cores", Type: cty.Number, Required: false},
|
||||||
|
"CPU_reservation": &hcldec.AttrSpec{Name: "CPU_reservation", Type: cty.Number, Required: false},
|
||||||
|
"CPU_limit": &hcldec.AttrSpec{Name: "CPU_limit", Type: cty.Number, Required: false},
|
||||||
|
"CPU_hot_plug": &hcldec.AttrSpec{Name: "CPU_hot_plug", Type: cty.Bool, Required: false},
|
||||||
|
"RAM": &hcldec.AttrSpec{Name: "RAM", Type: cty.Number, Required: false},
|
||||||
|
"RAM_reservation": &hcldec.AttrSpec{Name: "RAM_reservation", Type: cty.Number, Required: false},
|
||||||
|
"RAM_reserve_all": &hcldec.AttrSpec{Name: "RAM_reserve_all", Type: cty.Bool, Required: false},
|
||||||
|
"RAM_hot_plug": &hcldec.AttrSpec{Name: "RAM_hot_plug", Type: cty.Bool, Required: false},
|
||||||
|
"video_ram": &hcldec.AttrSpec{Name: "video_ram", Type: cty.Number, Required: false},
|
||||||
|
"NestedHV": &hcldec.AttrSpec{Name: "NestedHV", Type: cty.Bool, Required: false},
|
||||||
|
}
|
||||||
|
return s
|
||||||
|
}
|
|
@ -0,0 +1,79 @@
|
||||||
|
//go:generate struct-markdown
|
||||||
|
//go:generate mapstructure-to-hcl2 -type RunConfig
|
||||||
|
|
||||||
|
package common
|
||||||
|
|
||||||
|
import (
|
||||||
|
"context"
|
||||||
|
"github.com/hashicorp/packer/builder/vsphere/driver"
|
||||||
|
"github.com/hashicorp/packer/helper/multistep"
|
||||||
|
"github.com/hashicorp/packer/packer"
|
||||||
|
"strings"
|
||||||
|
)
|
||||||
|
|
||||||
|
type RunConfig struct {
|
||||||
|
// Priority of boot devices. Defaults to `disk,cdrom`
|
||||||
|
BootOrder string `mapstructure:"boot_order"` // example: "floppy,cdrom,ethernet,disk"
|
||||||
|
}
|
||||||
|
|
||||||
|
type StepRun struct {
|
||||||
|
Config *RunConfig
|
||||||
|
SetOrder bool
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *StepRun) Run(_ context.Context, state multistep.StateBag) multistep.StepAction {
|
||||||
|
ui := state.Get("ui").(packer.Ui)
|
||||||
|
vm := state.Get("vm").(*driver.VirtualMachine)
|
||||||
|
|
||||||
|
if s.Config.BootOrder != "" {
|
||||||
|
ui.Say("Set boot order...")
|
||||||
|
order := strings.Split(s.Config.BootOrder, ",")
|
||||||
|
if err := vm.SetBootOrder(order); err != nil {
|
||||||
|
state.Put("error", err)
|
||||||
|
return multistep.ActionHalt
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
if s.SetOrder {
|
||||||
|
ui.Say("Set boot order temporary...")
|
||||||
|
if err := vm.SetBootOrder([]string{"disk", "cdrom"}); err != nil {
|
||||||
|
state.Put("error", err)
|
||||||
|
return multistep.ActionHalt
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
ui.Say("Power on VM...")
|
||||||
|
err := vm.PowerOn()
|
||||||
|
if err != nil {
|
||||||
|
state.Put("error", err)
|
||||||
|
return multistep.ActionHalt
|
||||||
|
}
|
||||||
|
|
||||||
|
return multistep.ActionContinue
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *StepRun) Cleanup(state multistep.StateBag) {
|
||||||
|
ui := state.Get("ui").(packer.Ui)
|
||||||
|
vm := state.Get("vm").(*driver.VirtualMachine)
|
||||||
|
|
||||||
|
if s.Config.BootOrder == "" && s.SetOrder {
|
||||||
|
ui.Say("Clear boot order...")
|
||||||
|
if err := vm.SetBootOrder([]string{"-"}); err != nil {
|
||||||
|
state.Put("error", err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
_, cancelled := state.GetOk(multistep.StateCancelled)
|
||||||
|
_, halted := state.GetOk(multistep.StateHalted)
|
||||||
|
if !cancelled && !halted {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
ui.Say("Power off VM...")
|
||||||
|
|
||||||
|
err := vm.PowerOff()
|
||||||
|
if err != nil {
|
||||||
|
ui.Error(err.Error())
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,30 @@
|
||||||
|
// Code generated by "mapstructure-to-hcl2 -type RunConfig"; DO NOT EDIT.
|
||||||
|
package common
|
||||||
|
|
||||||
|
import (
|
||||||
|
"github.com/hashicorp/hcl/v2/hcldec"
|
||||||
|
"github.com/zclconf/go-cty/cty"
|
||||||
|
)
|
||||||
|
|
||||||
|
// FlatRunConfig is an auto-generated flat version of RunConfig.
|
||||||
|
// Where the contents of a field with a `mapstructure:,squash` tag are bubbled up.
|
||||||
|
type FlatRunConfig struct {
|
||||||
|
BootOrder *string `mapstructure:"boot_order" cty:"boot_order"`
|
||||||
|
}
|
||||||
|
|
||||||
|
// FlatMapstructure returns a new FlatRunConfig.
|
||||||
|
// FlatRunConfig is an auto-generated flat version of RunConfig.
|
||||||
|
// Where the contents a fields with a `mapstructure:,squash` tag are bubbled up.
|
||||||
|
func (*RunConfig) FlatMapstructure() interface{ HCL2Spec() map[string]hcldec.Spec } {
|
||||||
|
return new(FlatRunConfig)
|
||||||
|
}
|
||||||
|
|
||||||
|
// HCL2Spec returns the hcl spec of a RunConfig.
|
||||||
|
// This spec is used by HCL to read the fields of RunConfig.
|
||||||
|
// The decoded values from this spec will then be applied to a FlatRunConfig.
|
||||||
|
func (*FlatRunConfig) HCL2Spec() map[string]hcldec.Spec {
|
||||||
|
s := map[string]hcldec.Spec{
|
||||||
|
"boot_order": &hcldec.AttrSpec{Name: "boot_order", Type: cty.String, Required: false},
|
||||||
|
}
|
||||||
|
return s
|
||||||
|
}
|
|
@ -0,0 +1,80 @@
|
||||||
|
//go:generate struct-markdown
|
||||||
|
//go:generate mapstructure-to-hcl2 -type ShutdownConfig
|
||||||
|
|
||||||
|
package common
|
||||||
|
|
||||||
|
import (
|
||||||
|
"bytes"
|
||||||
|
"context"
|
||||||
|
"fmt"
|
||||||
|
"github.com/hashicorp/packer/builder/vsphere/driver"
|
||||||
|
"github.com/hashicorp/packer/helper/multistep"
|
||||||
|
"github.com/hashicorp/packer/packer"
|
||||||
|
"log"
|
||||||
|
"time"
|
||||||
|
)
|
||||||
|
|
||||||
|
type ShutdownConfig struct {
|
||||||
|
// Specify a VM guest shutdown command. VMware guest tools are used by
|
||||||
|
// default.
|
||||||
|
Command string `mapstructure:"shutdown_command"`
|
||||||
|
// Amount of time to wait for graceful VM shutdown. Examples 45s and 10m.
|
||||||
|
// Defaults to 5m(5 minutes).
|
||||||
|
Timeout time.Duration `mapstructure:"shutdown_timeout"`
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *ShutdownConfig) Prepare() []error {
|
||||||
|
var errs []error
|
||||||
|
|
||||||
|
if c.Timeout == 0 {
|
||||||
|
c.Timeout = 5 * time.Minute
|
||||||
|
}
|
||||||
|
|
||||||
|
return errs
|
||||||
|
}
|
||||||
|
|
||||||
|
type StepShutdown struct {
|
||||||
|
Config *ShutdownConfig
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *StepShutdown) Run(ctx context.Context, state multistep.StateBag) multistep.StepAction {
|
||||||
|
ui := state.Get("ui").(packer.Ui)
|
||||||
|
comm := state.Get("communicator").(packer.Communicator)
|
||||||
|
vm := state.Get("vm").(*driver.VirtualMachine)
|
||||||
|
|
||||||
|
if s.Config.Command != "" {
|
||||||
|
ui.Say("Executing shutdown command...")
|
||||||
|
log.Printf("Shutdown command: %s", s.Config.Command)
|
||||||
|
|
||||||
|
var stdout, stderr bytes.Buffer
|
||||||
|
cmd := &packer.RemoteCmd{
|
||||||
|
Command: s.Config.Command,
|
||||||
|
Stdout: &stdout,
|
||||||
|
Stderr: &stderr,
|
||||||
|
}
|
||||||
|
err := comm.Start(ctx, cmd)
|
||||||
|
if err != nil {
|
||||||
|
state.Put("error", fmt.Errorf("Failed to send shutdown command: %s", err))
|
||||||
|
return multistep.ActionHalt
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
ui.Say("Shut down VM...")
|
||||||
|
|
||||||
|
err := vm.StartShutdown()
|
||||||
|
if err != nil {
|
||||||
|
state.Put("error", fmt.Errorf("Cannot shut down VM: %v", err))
|
||||||
|
return multistep.ActionHalt
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
log.Printf("Waiting max %s for shutdown to complete", s.Config.Timeout)
|
||||||
|
err := vm.WaitForShutdown(ctx, s.Config.Timeout)
|
||||||
|
if err != nil {
|
||||||
|
state.Put("error", err)
|
||||||
|
return multistep.ActionHalt
|
||||||
|
}
|
||||||
|
|
||||||
|
return multistep.ActionContinue
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *StepShutdown) Cleanup(state multistep.StateBag) {}
|
|
@ -0,0 +1,32 @@
|
||||||
|
// Code generated by "mapstructure-to-hcl2 -type ShutdownConfig"; DO NOT EDIT.
|
||||||
|
package common
|
||||||
|
|
||||||
|
import (
|
||||||
|
"github.com/hashicorp/hcl/v2/hcldec"
|
||||||
|
"github.com/zclconf/go-cty/cty"
|
||||||
|
)
|
||||||
|
|
||||||
|
// FlatShutdownConfig is an auto-generated flat version of ShutdownConfig.
|
||||||
|
// Where the contents of a field with a `mapstructure:,squash` tag are bubbled up.
|
||||||
|
type FlatShutdownConfig struct {
|
||||||
|
Command *string `mapstructure:"shutdown_command" cty:"shutdown_command"`
|
||||||
|
Timeout *string `mapstructure:"shutdown_timeout" cty:"shutdown_timeout"`
|
||||||
|
}
|
||||||
|
|
||||||
|
// FlatMapstructure returns a new FlatShutdownConfig.
|
||||||
|
// FlatShutdownConfig is an auto-generated flat version of ShutdownConfig.
|
||||||
|
// Where the contents a fields with a `mapstructure:,squash` tag are bubbled up.
|
||||||
|
func (*ShutdownConfig) FlatMapstructure() interface{ HCL2Spec() map[string]hcldec.Spec } {
|
||||||
|
return new(FlatShutdownConfig)
|
||||||
|
}
|
||||||
|
|
||||||
|
// HCL2Spec returns the hcl spec of a ShutdownConfig.
|
||||||
|
// This spec is used by HCL to read the fields of ShutdownConfig.
|
||||||
|
// The decoded values from this spec will then be applied to a FlatShutdownConfig.
|
||||||
|
func (*FlatShutdownConfig) HCL2Spec() map[string]hcldec.Spec {
|
||||||
|
s := map[string]hcldec.Spec{
|
||||||
|
"shutdown_command": &hcldec.AttrSpec{Name: "shutdown_command", Type: cty.String, Required: false},
|
||||||
|
"shutdown_timeout": &hcldec.AttrSpec{Name: "shutdown_timeout", Type: cty.String, Required: false},
|
||||||
|
}
|
||||||
|
return s
|
||||||
|
}
|
|
@ -0,0 +1,31 @@
|
||||||
|
package common
|
||||||
|
|
||||||
|
import (
|
||||||
|
"context"
|
||||||
|
"github.com/hashicorp/packer/builder/vsphere/driver"
|
||||||
|
"github.com/hashicorp/packer/helper/multistep"
|
||||||
|
"github.com/hashicorp/packer/packer"
|
||||||
|
)
|
||||||
|
|
||||||
|
type StepCreateSnapshot struct {
|
||||||
|
CreateSnapshot bool
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *StepCreateSnapshot) Run(_ context.Context, state multistep.StateBag) multistep.StepAction {
|
||||||
|
ui := state.Get("ui").(packer.Ui)
|
||||||
|
vm := state.Get("vm").(*driver.VirtualMachine)
|
||||||
|
|
||||||
|
if s.CreateSnapshot {
|
||||||
|
ui.Say("Creating snapshot...")
|
||||||
|
|
||||||
|
err := vm.CreateSnapshot("Created by Packer")
|
||||||
|
if err != nil {
|
||||||
|
state.Put("error", err)
|
||||||
|
return multistep.ActionHalt
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return multistep.ActionContinue
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *StepCreateSnapshot) Cleanup(state multistep.StateBag) {}
|
|
@ -0,0 +1,30 @@
|
||||||
|
package common
|
||||||
|
|
||||||
|
import (
|
||||||
|
"context"
|
||||||
|
"github.com/hashicorp/packer/builder/vsphere/driver"
|
||||||
|
"github.com/hashicorp/packer/helper/multistep"
|
||||||
|
"github.com/hashicorp/packer/packer"
|
||||||
|
)
|
||||||
|
|
||||||
|
type StepConvertToTemplate struct {
|
||||||
|
ConvertToTemplate bool
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *StepConvertToTemplate) Run(_ context.Context, state multistep.StateBag) multistep.StepAction {
|
||||||
|
ui := state.Get("ui").(packer.Ui)
|
||||||
|
vm := state.Get("vm").(*driver.VirtualMachine)
|
||||||
|
|
||||||
|
if s.ConvertToTemplate {
|
||||||
|
ui.Say("Convert VM into template...")
|
||||||
|
err := vm.ConvertToTemplate()
|
||||||
|
if err != nil {
|
||||||
|
state.Put("error", err)
|
||||||
|
return multistep.ActionHalt
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return multistep.ActionContinue
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *StepConvertToTemplate) Cleanup(state multistep.StateBag) {}
|
|
@ -0,0 +1,142 @@
|
||||||
|
//go:generate struct-markdown
|
||||||
|
//go:generate mapstructure-to-hcl2 -type WaitIpConfig
|
||||||
|
|
||||||
|
package common
|
||||||
|
|
||||||
|
import (
|
||||||
|
"context"
|
||||||
|
"fmt"
|
||||||
|
"github.com/hashicorp/packer/builder/vsphere/driver"
|
||||||
|
"github.com/hashicorp/packer/helper/multistep"
|
||||||
|
"github.com/hashicorp/packer/packer"
|
||||||
|
"log"
|
||||||
|
"time"
|
||||||
|
)
|
||||||
|
|
||||||
|
type WaitIpConfig struct {
|
||||||
|
// Amount of time to wait for VM's IP, similar to 'ssh_timeout'.
|
||||||
|
// Defaults to 30m (30 minutes). See the Goang
|
||||||
|
// [ParseDuration](https://golang.org/pkg/time/#ParseDuration) documentation
|
||||||
|
// for full details.
|
||||||
|
WaitTimeout time.Duration `mapstructure:"ip_wait_timeout"`
|
||||||
|
// Amount of time to wait for VM's IP to settle down, sometimes VM may
|
||||||
|
// report incorrect IP initially, then its recommended to set that
|
||||||
|
// parameter to apx. 2 minutes. Examples 45s and 10m. Defaults to
|
||||||
|
// 5s(5 seconds). See the Golang
|
||||||
|
// [ParseDuration](https://golang.org/pkg/time/#ParseDuration) documentation
|
||||||
|
// for full details.
|
||||||
|
SettleTimeout time.Duration `mapstructure:"ip_settle_timeout"`
|
||||||
|
|
||||||
|
// WaitTimeout is a total timeout, so even if VM changes IP frequently and it doesn't settle down we will end waiting.
|
||||||
|
}
|
||||||
|
|
||||||
|
type StepWaitForIp struct {
|
||||||
|
Config *WaitIpConfig
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *WaitIpConfig) Prepare() []error {
|
||||||
|
var errs []error
|
||||||
|
|
||||||
|
if c.SettleTimeout == 0 {
|
||||||
|
c.SettleTimeout = 5 * time.Second
|
||||||
|
}
|
||||||
|
if c.WaitTimeout == 0 {
|
||||||
|
c.WaitTimeout = 30 * time.Minute
|
||||||
|
}
|
||||||
|
|
||||||
|
return errs
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *StepWaitForIp) Run(ctx context.Context, state multistep.StateBag) multistep.StepAction {
|
||||||
|
ui := state.Get("ui").(packer.Ui)
|
||||||
|
vm := state.Get("vm").(*driver.VirtualMachine)
|
||||||
|
|
||||||
|
var ip string
|
||||||
|
var err error
|
||||||
|
|
||||||
|
sub, cancel := context.WithCancel(ctx)
|
||||||
|
waitDone := make(chan bool, 1)
|
||||||
|
defer func() {
|
||||||
|
cancel()
|
||||||
|
}()
|
||||||
|
|
||||||
|
go func() {
|
||||||
|
ui.Say("Waiting for IP...")
|
||||||
|
ip, err = doGetIp(vm, sub, s.Config)
|
||||||
|
waitDone <- true
|
||||||
|
}()
|
||||||
|
|
||||||
|
log.Printf("[INFO] Waiting for IP, up to total timeout: %s, settle timeout: %s", s.Config.WaitTimeout, s.Config.SettleTimeout)
|
||||||
|
timeout := time.After(s.Config.WaitTimeout)
|
||||||
|
for {
|
||||||
|
select {
|
||||||
|
case <-timeout:
|
||||||
|
err := fmt.Errorf("Timeout waiting for IP.")
|
||||||
|
state.Put("error", err)
|
||||||
|
ui.Error(err.Error())
|
||||||
|
cancel()
|
||||||
|
return multistep.ActionHalt
|
||||||
|
case <-ctx.Done():
|
||||||
|
cancel()
|
||||||
|
log.Println("[WARN] Interrupt detected, quitting waiting for IP.")
|
||||||
|
return multistep.ActionHalt
|
||||||
|
case <-waitDone:
|
||||||
|
if err != nil {
|
||||||
|
state.Put("error", err)
|
||||||
|
return multistep.ActionHalt
|
||||||
|
}
|
||||||
|
state.Put("ip", ip)
|
||||||
|
ui.Say(fmt.Sprintf("IP address: %v", ip))
|
||||||
|
return multistep.ActionContinue
|
||||||
|
case <-time.After(1 * time.Second):
|
||||||
|
if _, ok := state.GetOk(multistep.StateCancelled); ok {
|
||||||
|
return multistep.ActionHalt
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func doGetIp(vm *driver.VirtualMachine, ctx context.Context, c *WaitIpConfig) (string, error) {
|
||||||
|
var prevIp = ""
|
||||||
|
var stopTime time.Time
|
||||||
|
var interval time.Duration
|
||||||
|
if c.SettleTimeout.Seconds() >= 120 {
|
||||||
|
interval = 30 * time.Second
|
||||||
|
} else if c.SettleTimeout.Seconds() >= 60 {
|
||||||
|
interval = 15 * time.Second
|
||||||
|
} else if c.SettleTimeout.Seconds() >= 10 {
|
||||||
|
interval = 5 * time.Second
|
||||||
|
} else {
|
||||||
|
interval = 1 * time.Second
|
||||||
|
}
|
||||||
|
loop:
|
||||||
|
ip, err := vm.WaitForIP(ctx)
|
||||||
|
if err != nil {
|
||||||
|
return "", err
|
||||||
|
}
|
||||||
|
if prevIp == "" || prevIp != ip {
|
||||||
|
if prevIp == "" {
|
||||||
|
log.Printf("VM IP aquired: %s", ip)
|
||||||
|
} else {
|
||||||
|
log.Printf("VM IP changed from %s to %s", prevIp, ip)
|
||||||
|
}
|
||||||
|
prevIp = ip
|
||||||
|
stopTime = time.Now().Add(c.SettleTimeout)
|
||||||
|
goto loop
|
||||||
|
} else {
|
||||||
|
log.Printf("VM IP is still the same: %s", prevIp)
|
||||||
|
if time.Now().After(stopTime) {
|
||||||
|
log.Printf("VM IP seems stable enough: %s", ip)
|
||||||
|
return ip, nil
|
||||||
|
}
|
||||||
|
select {
|
||||||
|
case <-ctx.Done():
|
||||||
|
return "", fmt.Errorf("IP wait cancelled")
|
||||||
|
case <-time.After(interval):
|
||||||
|
goto loop
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *StepWaitForIp) Cleanup(state multistep.StateBag) {}
|
|
@ -0,0 +1,32 @@
|
||||||
|
// Code generated by "mapstructure-to-hcl2 -type WaitIpConfig"; DO NOT EDIT.
|
||||||
|
package common
|
||||||
|
|
||||||
|
import (
|
||||||
|
"github.com/hashicorp/hcl/v2/hcldec"
|
||||||
|
"github.com/zclconf/go-cty/cty"
|
||||||
|
)
|
||||||
|
|
||||||
|
// FlatWaitIpConfig is an auto-generated flat version of WaitIpConfig.
|
||||||
|
// Where the contents of a field with a `mapstructure:,squash` tag are bubbled up.
|
||||||
|
type FlatWaitIpConfig struct {
|
||||||
|
WaitTimeout *string `mapstructure:"ip_wait_timeout" cty:"ip_wait_timeout"`
|
||||||
|
SettleTimeout *string `mapstructure:"ip_settle_timeout" cty:"ip_settle_timeout"`
|
||||||
|
}
|
||||||
|
|
||||||
|
// FlatMapstructure returns a new FlatWaitIpConfig.
|
||||||
|
// FlatWaitIpConfig is an auto-generated flat version of WaitIpConfig.
|
||||||
|
// Where the contents a fields with a `mapstructure:,squash` tag are bubbled up.
|
||||||
|
func (*WaitIpConfig) FlatMapstructure() interface{ HCL2Spec() map[string]hcldec.Spec } {
|
||||||
|
return new(FlatWaitIpConfig)
|
||||||
|
}
|
||||||
|
|
||||||
|
// HCL2Spec returns the hcl spec of a WaitIpConfig.
|
||||||
|
// This spec is used by HCL to read the fields of WaitIpConfig.
|
||||||
|
// The decoded values from this spec will then be applied to a FlatWaitIpConfig.
|
||||||
|
func (*FlatWaitIpConfig) HCL2Spec() map[string]hcldec.Spec {
|
||||||
|
s := map[string]hcldec.Spec{
|
||||||
|
"ip_wait_timeout": &hcldec.AttrSpec{Name: "ip_wait_timeout", Type: cty.String, Required: false},
|
||||||
|
"ip_settle_timeout": &hcldec.AttrSpec{Name: "ip_settle_timeout", Type: cty.String, Required: false},
|
||||||
|
}
|
||||||
|
return s
|
||||||
|
}
|
|
@ -0,0 +1,68 @@
|
||||||
|
package testing
|
||||||
|
|
||||||
|
import (
|
||||||
|
"encoding/json"
|
||||||
|
"fmt"
|
||||||
|
"github.com/hashicorp/packer/builder/vsphere/common"
|
||||||
|
"github.com/hashicorp/packer/builder/vsphere/driver"
|
||||||
|
"github.com/hashicorp/packer/packer"
|
||||||
|
"math/rand"
|
||||||
|
"os"
|
||||||
|
"testing"
|
||||||
|
"time"
|
||||||
|
)
|
||||||
|
|
||||||
|
func NewVMName() string {
|
||||||
|
rand.Seed(time.Now().UnixNano())
|
||||||
|
return fmt.Sprintf("test-%v", rand.Intn(1000))
|
||||||
|
}
|
||||||
|
|
||||||
|
func RenderConfig(config map[string]interface{}) string {
|
||||||
|
t := map[string][]map[string]interface{}{
|
||||||
|
"builders": {
|
||||||
|
map[string]interface{}{
|
||||||
|
"type": "test",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
}
|
||||||
|
for k, v := range config {
|
||||||
|
t["builders"][0][k] = v
|
||||||
|
}
|
||||||
|
|
||||||
|
j, _ := json.Marshal(t)
|
||||||
|
return string(j)
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestConn(t *testing.T) *driver.Driver {
|
||||||
|
username := os.Getenv("VSPHERE_USERNAME")
|
||||||
|
if username == "" {
|
||||||
|
username = "root"
|
||||||
|
}
|
||||||
|
password := os.Getenv("VSPHERE_PASSWORD")
|
||||||
|
if password == "" {
|
||||||
|
password = "jetbrains"
|
||||||
|
}
|
||||||
|
|
||||||
|
d, err := driver.NewDriver(&driver.ConnectConfig{
|
||||||
|
VCenterServer: "vcenter.vsphere65.test",
|
||||||
|
Username: username,
|
||||||
|
Password: password,
|
||||||
|
InsecureConnection: true,
|
||||||
|
})
|
||||||
|
if err != nil {
|
||||||
|
t.Fatal("Cannot connect: ", err)
|
||||||
|
}
|
||||||
|
return d
|
||||||
|
}
|
||||||
|
|
||||||
|
func GetVM(t *testing.T, d *driver.Driver, artifacts []packer.Artifact) *driver.VirtualMachine {
|
||||||
|
artifactRaw := artifacts[0]
|
||||||
|
artifact, _ := artifactRaw.(*common.Artifact)
|
||||||
|
|
||||||
|
vm, err := d.FindVM(artifact.Name)
|
||||||
|
if err != nil {
|
||||||
|
t.Fatalf("Cannot find VM: %v", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
return vm
|
||||||
|
}
|
|
@ -0,0 +1,129 @@
|
||||||
|
package driver
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
"github.com/vmware/govmomi/object"
|
||||||
|
"github.com/vmware/govmomi/vim25/mo"
|
||||||
|
"github.com/vmware/govmomi/vim25/soap"
|
||||||
|
"github.com/vmware/govmomi/vim25/types"
|
||||||
|
)
|
||||||
|
|
||||||
|
type Datastore struct {
|
||||||
|
ds *object.Datastore
|
||||||
|
driver *Driver
|
||||||
|
}
|
||||||
|
|
||||||
|
func (d *Driver) NewDatastore(ref *types.ManagedObjectReference) *Datastore {
|
||||||
|
return &Datastore{
|
||||||
|
ds: object.NewDatastore(d.client.Client, *ref),
|
||||||
|
driver: d,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// If name is an empty string, then resolve host's one
|
||||||
|
func (d *Driver) FindDatastore(name string, host string) (*Datastore, error) {
|
||||||
|
if name == "" {
|
||||||
|
h, err := d.FindHost(host)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
i, err := h.Info("datastore")
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
if len(i.Datastore) > 1 {
|
||||||
|
return nil, fmt.Errorf("Host has multiple datastores. Specify it explicitly")
|
||||||
|
}
|
||||||
|
|
||||||
|
ds := d.NewDatastore(&i.Datastore[0])
|
||||||
|
inf, err := ds.Info("name")
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
name = inf.Name
|
||||||
|
}
|
||||||
|
|
||||||
|
ds, err := d.finder.Datastore(d.ctx, name)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
return &Datastore{
|
||||||
|
ds: ds,
|
||||||
|
driver: d,
|
||||||
|
}, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (ds *Datastore) Info(params ...string) (*mo.Datastore, error) {
|
||||||
|
var p []string
|
||||||
|
if len(params) == 0 {
|
||||||
|
p = []string{"*"}
|
||||||
|
} else {
|
||||||
|
p = params
|
||||||
|
}
|
||||||
|
var info mo.Datastore
|
||||||
|
err := ds.ds.Properties(ds.driver.ctx, ds.ds.Reference(), p, &info)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
return &info, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (ds *Datastore) FileExists(path string) bool {
|
||||||
|
_, err := ds.ds.Stat(ds.driver.ctx, path)
|
||||||
|
return err == nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (ds *Datastore) Name() string {
|
||||||
|
return ds.ds.Name()
|
||||||
|
}
|
||||||
|
|
||||||
|
func (ds *Datastore) ResolvePath(path string) string {
|
||||||
|
return ds.ds.Path(path)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (ds *Datastore) UploadFile(src, dst string, host string) error {
|
||||||
|
p := soap.DefaultUpload
|
||||||
|
ctx := ds.driver.ctx
|
||||||
|
|
||||||
|
if host != "" {
|
||||||
|
h, err := ds.driver.FindHost(host)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
ctx = ds.ds.HostContext(ctx, h.host)
|
||||||
|
}
|
||||||
|
|
||||||
|
return ds.ds.UploadFile(ctx, src, dst, &p)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (ds *Datastore) Delete(path string) error {
|
||||||
|
dc, err := ds.driver.finder.Datacenter(ds.driver.ctx, ds.ds.DatacenterPath)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
fm := ds.ds.NewFileManager(dc, false)
|
||||||
|
return fm.Delete(ds.driver.ctx, path)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (ds *Datastore) MakeDirectory(path string) error {
|
||||||
|
dc, err := ds.driver.finder.Datacenter(ds.driver.ctx, ds.ds.DatacenterPath)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
fm := ds.ds.NewFileManager(dc, false)
|
||||||
|
return fm.FileManager.MakeDirectory(ds.driver.ctx, path, dc, true)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Cuts out the datastore prefix
|
||||||
|
// Example: "[datastore1] file.ext" --> "file.ext"
|
||||||
|
func RemoveDatastorePrefix(path string) string {
|
||||||
|
res := object.DatastorePath{}
|
||||||
|
if hadPrefix := res.FromString(path); hadPrefix {
|
||||||
|
return res.Path
|
||||||
|
} else {
|
||||||
|
return path
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,96 @@
|
||||||
|
package driver
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
"io/ioutil"
|
||||||
|
"testing"
|
||||||
|
"time"
|
||||||
|
)
|
||||||
|
|
||||||
|
func TestDatastoreAcc(t *testing.T) {
|
||||||
|
t.Skip("Acceptance tests not configured yet.")
|
||||||
|
d := newTestDriver(t)
|
||||||
|
ds, err := d.FindDatastore("datastore1", "")
|
||||||
|
if err != nil {
|
||||||
|
t.Fatalf("Cannot find the default datastore '%v': %v", "datastore1", err)
|
||||||
|
}
|
||||||
|
info, err := ds.Info("name")
|
||||||
|
if err != nil {
|
||||||
|
t.Fatalf("Cannot read datastore properties: %v", err)
|
||||||
|
}
|
||||||
|
if info.Name != "datastore1" {
|
||||||
|
t.Errorf("Wrong datastore. expected: 'datastore1', got: '%v'", info.Name)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestFileUpload(t *testing.T) {
|
||||||
|
t.Skip("Acceptance tests not configured yet.")
|
||||||
|
dsName := "datastore1"
|
||||||
|
hostName := "esxi-1.vsphere65.test"
|
||||||
|
|
||||||
|
fileName := fmt.Sprintf("test-%v", time.Now().Unix())
|
||||||
|
tmpFile, err := ioutil.TempFile("", fileName)
|
||||||
|
if err != nil {
|
||||||
|
t.Fatalf("Error creating temp file")
|
||||||
|
}
|
||||||
|
err = tmpFile.Close()
|
||||||
|
if err != nil {
|
||||||
|
t.Fatalf("Error creating temp file")
|
||||||
|
}
|
||||||
|
|
||||||
|
d := newTestDriver(t)
|
||||||
|
ds, err := d.FindDatastore(dsName, hostName)
|
||||||
|
if err != nil {
|
||||||
|
t.Fatalf("Cannot find datastore '%v': %v", dsName, err)
|
||||||
|
}
|
||||||
|
|
||||||
|
err = ds.UploadFile(tmpFile.Name(), fileName, hostName)
|
||||||
|
if err != nil {
|
||||||
|
t.Fatalf("Cannot upload file: %v", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
if ds.FileExists(fileName) != true {
|
||||||
|
t.Fatalf("Cannot find file")
|
||||||
|
}
|
||||||
|
|
||||||
|
err = ds.Delete(fileName)
|
||||||
|
if err != nil {
|
||||||
|
t.Fatalf("Cannot delete file: %v", err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestFileUploadDRS(t *testing.T) {
|
||||||
|
t.Skip("Acceptance tests not configured yet.")
|
||||||
|
dsName := "datastore3"
|
||||||
|
hostName := ""
|
||||||
|
|
||||||
|
fileName := fmt.Sprintf("test-%v", time.Now().Unix())
|
||||||
|
tmpFile, err := ioutil.TempFile("", fileName)
|
||||||
|
if err != nil {
|
||||||
|
t.Fatalf("Error creating temp file")
|
||||||
|
}
|
||||||
|
err = tmpFile.Close()
|
||||||
|
if err != nil {
|
||||||
|
t.Fatalf("Error creating temp file")
|
||||||
|
}
|
||||||
|
|
||||||
|
d := newTestDriver(t)
|
||||||
|
ds, err := d.FindDatastore(dsName, hostName)
|
||||||
|
if err != nil {
|
||||||
|
t.Fatalf("Cannot find datastore '%v': %v", dsName, err)
|
||||||
|
}
|
||||||
|
|
||||||
|
err = ds.UploadFile(tmpFile.Name(), fileName, hostName)
|
||||||
|
if err != nil {
|
||||||
|
t.Fatalf("Cannot upload file: %v", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
if ds.FileExists(fileName) != true {
|
||||||
|
t.Fatalf("Cannot find file")
|
||||||
|
}
|
||||||
|
|
||||||
|
err = ds.Delete(fileName)
|
||||||
|
if err != nil {
|
||||||
|
t.Fatalf("Cannot delete file: %v", err)
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,72 @@
|
||||||
|
package driver
|
||||||
|
|
||||||
|
import (
|
||||||
|
"context"
|
||||||
|
"fmt"
|
||||||
|
"github.com/vmware/govmomi"
|
||||||
|
"github.com/vmware/govmomi/find"
|
||||||
|
"github.com/vmware/govmomi/object"
|
||||||
|
"github.com/vmware/govmomi/session"
|
||||||
|
"github.com/vmware/govmomi/vim25"
|
||||||
|
"github.com/vmware/govmomi/vim25/soap"
|
||||||
|
"net/url"
|
||||||
|
"time"
|
||||||
|
)
|
||||||
|
|
||||||
|
type Driver struct {
|
||||||
|
ctx context.Context
|
||||||
|
client *govmomi.Client
|
||||||
|
finder *find.Finder
|
||||||
|
datacenter *object.Datacenter
|
||||||
|
}
|
||||||
|
|
||||||
|
type ConnectConfig struct {
|
||||||
|
VCenterServer string
|
||||||
|
Username string
|
||||||
|
Password string
|
||||||
|
InsecureConnection bool
|
||||||
|
Datacenter string
|
||||||
|
}
|
||||||
|
|
||||||
|
func NewDriver(config *ConnectConfig) (*Driver, error) {
|
||||||
|
ctx := context.TODO()
|
||||||
|
|
||||||
|
vcenterUrl, err := url.Parse(fmt.Sprintf("https://%v/sdk", config.VCenterServer))
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
credentials := url.UserPassword(config.Username, config.Password)
|
||||||
|
vcenterUrl.User = credentials
|
||||||
|
|
||||||
|
soapClient := soap.NewClient(vcenterUrl, config.InsecureConnection)
|
||||||
|
vimClient, err := vim25.NewClient(ctx, soapClient)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
vimClient.RoundTripper = session.KeepAlive(vimClient.RoundTripper, 10*time.Minute)
|
||||||
|
client := &govmomi.Client{
|
||||||
|
Client: vimClient,
|
||||||
|
SessionManager: session.NewManager(vimClient),
|
||||||
|
}
|
||||||
|
|
||||||
|
err = client.SessionManager.Login(ctx, credentials)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
finder := find.NewFinder(client.Client, false)
|
||||||
|
datacenter, err := finder.DatacenterOrDefault(ctx, config.Datacenter)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
finder.SetDatacenter(datacenter)
|
||||||
|
|
||||||
|
d := Driver{
|
||||||
|
ctx: ctx,
|
||||||
|
client: client,
|
||||||
|
datacenter: datacenter,
|
||||||
|
finder: finder,
|
||||||
|
}
|
||||||
|
return &d, nil
|
||||||
|
}
|
|
@ -0,0 +1,39 @@
|
||||||
|
package driver
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
"math/rand"
|
||||||
|
"os"
|
||||||
|
"testing"
|
||||||
|
"time"
|
||||||
|
)
|
||||||
|
|
||||||
|
// Defines whether acceptance tests should be run
|
||||||
|
const TestHostName = "esxi-1.vsphere65.test"
|
||||||
|
|
||||||
|
func newTestDriver(t *testing.T) *Driver {
|
||||||
|
username := os.Getenv("VSPHERE_USERNAME")
|
||||||
|
if username == "" {
|
||||||
|
username = "root"
|
||||||
|
}
|
||||||
|
password := os.Getenv("VSPHERE_PASSWORD")
|
||||||
|
if password == "" {
|
||||||
|
password = "jetbrains"
|
||||||
|
}
|
||||||
|
|
||||||
|
d, err := NewDriver(&ConnectConfig{
|
||||||
|
VCenterServer: "vcenter.vsphere65.test",
|
||||||
|
Username: username,
|
||||||
|
Password: password,
|
||||||
|
InsecureConnection: true,
|
||||||
|
})
|
||||||
|
if err != nil {
|
||||||
|
t.Fatalf("Cannot connect: %v", err)
|
||||||
|
}
|
||||||
|
return d
|
||||||
|
}
|
||||||
|
|
||||||
|
func newVMName() string {
|
||||||
|
rand.Seed(time.Now().UTC().UnixNano())
|
||||||
|
return fmt.Sprintf("test-%v", rand.Intn(1000))
|
||||||
|
}
|
|
@ -0,0 +1,67 @@
|
||||||
|
package driver
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
"github.com/vmware/govmomi/object"
|
||||||
|
"github.com/vmware/govmomi/vim25/mo"
|
||||||
|
"github.com/vmware/govmomi/vim25/types"
|
||||||
|
)
|
||||||
|
|
||||||
|
type Folder struct {
|
||||||
|
driver *Driver
|
||||||
|
folder *object.Folder
|
||||||
|
}
|
||||||
|
|
||||||
|
func (d *Driver) NewFolder(ref *types.ManagedObjectReference) *Folder {
|
||||||
|
return &Folder{
|
||||||
|
folder: object.NewFolder(d.client.Client, *ref),
|
||||||
|
driver: d,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (d *Driver) FindFolder(name string) (*Folder, error) {
|
||||||
|
f, err := d.finder.Folder(d.ctx, fmt.Sprintf("/%v/vm/%v", d.datacenter.Name(), name))
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
return &Folder{
|
||||||
|
folder: f,
|
||||||
|
driver: d,
|
||||||
|
}, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (f *Folder) Info(params ...string) (*mo.Folder, error) {
|
||||||
|
var p []string
|
||||||
|
if len(params) == 0 {
|
||||||
|
p = []string{"*"}
|
||||||
|
} else {
|
||||||
|
p = params
|
||||||
|
}
|
||||||
|
var info mo.Folder
|
||||||
|
err := f.folder.Properties(f.driver.ctx, f.folder.Reference(), p, &info)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
return &info, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (f *Folder) Path() (string, error) {
|
||||||
|
info, err := f.Info("name", "parent")
|
||||||
|
if err != nil {
|
||||||
|
return "", err
|
||||||
|
}
|
||||||
|
if info.Parent.Type == "Datacenter" {
|
||||||
|
return "", nil
|
||||||
|
} else {
|
||||||
|
parent := f.driver.NewFolder(info.Parent)
|
||||||
|
path, err := parent.Path()
|
||||||
|
if err != nil {
|
||||||
|
return "", err
|
||||||
|
}
|
||||||
|
if path == "" {
|
||||||
|
return info.Name, nil
|
||||||
|
} else {
|
||||||
|
return fmt.Sprintf("%v/%v", path, info.Name), nil
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,19 @@
|
||||||
|
package driver
|
||||||
|
|
||||||
|
import "testing"
|
||||||
|
|
||||||
|
func TestFolderAcc(t *testing.T) {
|
||||||
|
t.Skip("Acceptance tests not configured yet.")
|
||||||
|
d := newTestDriver(t)
|
||||||
|
f, err := d.FindFolder("folder1/folder2")
|
||||||
|
if err != nil {
|
||||||
|
t.Fatalf("Cannot find the default folder '%v': %v", "folder1/folder2", err)
|
||||||
|
}
|
||||||
|
path, err := f.Path()
|
||||||
|
if err != nil {
|
||||||
|
t.Fatalf("Cannot read folder name: %v", err)
|
||||||
|
}
|
||||||
|
if path != "folder1/folder2" {
|
||||||
|
t.Errorf("Wrong folder. expected: 'folder1/folder2', got: '%v'", path)
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,45 @@
|
||||||
|
package driver
|
||||||
|
|
||||||
|
import (
|
||||||
|
"github.com/vmware/govmomi/object"
|
||||||
|
"github.com/vmware/govmomi/vim25/mo"
|
||||||
|
"github.com/vmware/govmomi/vim25/types"
|
||||||
|
)
|
||||||
|
|
||||||
|
type Host struct {
|
||||||
|
driver *Driver
|
||||||
|
host *object.HostSystem
|
||||||
|
}
|
||||||
|
|
||||||
|
func (d *Driver) NewHost(ref *types.ManagedObjectReference) *Host {
|
||||||
|
return &Host{
|
||||||
|
host: object.NewHostSystem(d.client.Client, *ref),
|
||||||
|
driver: d,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (d *Driver) FindHost(name string) (*Host, error) {
|
||||||
|
h, err := d.finder.HostSystem(d.ctx, name)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
return &Host{
|
||||||
|
host: h,
|
||||||
|
driver: d,
|
||||||
|
}, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (h *Host) Info(params ...string) (*mo.HostSystem, error) {
|
||||||
|
var p []string
|
||||||
|
if len(params) == 0 {
|
||||||
|
p = []string{"*"}
|
||||||
|
} else {
|
||||||
|
p = params
|
||||||
|
}
|
||||||
|
var info mo.HostSystem
|
||||||
|
err := h.host.Properties(h.driver.ctx, h.host.Reference(), p, &info)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
return &info, nil
|
||||||
|
}
|
|
@ -0,0 +1,22 @@
|
||||||
|
package driver
|
||||||
|
|
||||||
|
import (
|
||||||
|
"testing"
|
||||||
|
)
|
||||||
|
|
||||||
|
func TestHostAcc(t *testing.T) {
|
||||||
|
t.Skip("Acceptance tests not configured yet.")
|
||||||
|
d := newTestDriver(t)
|
||||||
|
host, err := d.FindHost(TestHostName)
|
||||||
|
if err != nil {
|
||||||
|
t.Fatalf("Cannot find the default host '%v': %v", "datastore1", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
info, err := host.Info("name")
|
||||||
|
if err != nil {
|
||||||
|
t.Fatalf("Cannot read host properties: %v", err)
|
||||||
|
}
|
||||||
|
if info.Name != TestHostName {
|
||||||
|
t.Errorf("Wrong host name: expected '%v', got: '%v'", TestHostName, info.Name)
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,45 @@
|
||||||
|
package driver
|
||||||
|
|
||||||
|
import (
|
||||||
|
"github.com/vmware/govmomi/object"
|
||||||
|
"github.com/vmware/govmomi/vim25/mo"
|
||||||
|
"github.com/vmware/govmomi/vim25/types"
|
||||||
|
)
|
||||||
|
|
||||||
|
type Network struct {
|
||||||
|
driver *Driver
|
||||||
|
network *object.Network
|
||||||
|
}
|
||||||
|
|
||||||
|
func (d *Driver) NewNetwork(ref *types.ManagedObjectReference) *Network {
|
||||||
|
return &Network{
|
||||||
|
network: object.NewNetwork(d.client.Client, *ref),
|
||||||
|
driver: d,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (d *Driver) FindNetwork(name string) (*Network, error) {
|
||||||
|
n, err := d.finder.Network(d.ctx, name)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
return &Network{
|
||||||
|
network: n.(*object.Network),
|
||||||
|
driver: d,
|
||||||
|
}, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (n *Network) Info(params ...string) (*mo.Network, error) {
|
||||||
|
var p []string
|
||||||
|
if len(params) == 0 {
|
||||||
|
p = []string{"*"}
|
||||||
|
} else {
|
||||||
|
p = params
|
||||||
|
}
|
||||||
|
var info mo.Network
|
||||||
|
err := n.network.Properties(n.driver.ctx, n.network.Reference(), p, &info)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
return &info, nil
|
||||||
|
}
|
|
@ -0,0 +1,74 @@
|
||||||
|
package driver
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
"github.com/vmware/govmomi/object"
|
||||||
|
"github.com/vmware/govmomi/vim25/mo"
|
||||||
|
"github.com/vmware/govmomi/vim25/types"
|
||||||
|
)
|
||||||
|
|
||||||
|
type ResourcePool struct {
|
||||||
|
pool *object.ResourcePool
|
||||||
|
driver *Driver
|
||||||
|
}
|
||||||
|
|
||||||
|
func (d *Driver) NewResourcePool(ref *types.ManagedObjectReference) *ResourcePool {
|
||||||
|
return &ResourcePool{
|
||||||
|
pool: object.NewResourcePool(d.client.Client, *ref),
|
||||||
|
driver: d,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (d *Driver) FindResourcePool(cluster string, host string, name string) (*ResourcePool, error) {
|
||||||
|
var res string
|
||||||
|
if cluster != "" {
|
||||||
|
res = cluster
|
||||||
|
} else {
|
||||||
|
res = host
|
||||||
|
}
|
||||||
|
|
||||||
|
p, err := d.finder.ResourcePool(d.ctx, fmt.Sprintf("%v/Resources/%v", res, name))
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
return &ResourcePool{
|
||||||
|
pool: p,
|
||||||
|
driver: d,
|
||||||
|
}, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (p *ResourcePool) Info(params ...string) (*mo.ResourcePool, error) {
|
||||||
|
var params2 []string
|
||||||
|
if len(params) == 0 {
|
||||||
|
params2 = []string{"*"}
|
||||||
|
} else {
|
||||||
|
params2 = params
|
||||||
|
}
|
||||||
|
var info mo.ResourcePool
|
||||||
|
err := p.pool.Properties(p.driver.ctx, p.pool.Reference(), params2, &info)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
return &info, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (p *ResourcePool) Path() (string, error) {
|
||||||
|
poolInfo, err := p.Info("name", "parent")
|
||||||
|
if err != nil {
|
||||||
|
return "", err
|
||||||
|
}
|
||||||
|
if poolInfo.Parent.Type == "ComputeResource" {
|
||||||
|
return "", nil
|
||||||
|
} else {
|
||||||
|
parent := p.driver.NewResourcePool(poolInfo.Parent)
|
||||||
|
parentPath, err := parent.Path()
|
||||||
|
if err != nil {
|
||||||
|
return "", err
|
||||||
|
}
|
||||||
|
if parentPath == "" {
|
||||||
|
return poolInfo.Name, nil
|
||||||
|
} else {
|
||||||
|
return fmt.Sprintf("%v/%v", parentPath, poolInfo.Name), nil
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,20 @@
|
||||||
|
package driver
|
||||||
|
|
||||||
|
import "testing"
|
||||||
|
|
||||||
|
func TestResourcePoolAcc(t *testing.T) {
|
||||||
|
t.Skip("Acceptance tests not configured yet.")
|
||||||
|
d := newTestDriver(t)
|
||||||
|
p, err := d.FindResourcePool("", "esxi-1.vsphere65.test", "pool1/pool2")
|
||||||
|
if err != nil {
|
||||||
|
t.Fatalf("Cannot find the default resource pool '%v': %v", "pool1/pool2", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
path, err := p.Path()
|
||||||
|
if err != nil {
|
||||||
|
t.Fatalf("Cannot read resource pool name: %v", err)
|
||||||
|
}
|
||||||
|
if path != "pool1/pool2" {
|
||||||
|
t.Errorf("Wrong folder. expected: 'pool1/pool2', got: '%v'", path)
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,665 @@
|
||||||
|
package driver
|
||||||
|
|
||||||
|
import (
|
||||||
|
"context"
|
||||||
|
"errors"
|
||||||
|
"fmt"
|
||||||
|
"github.com/vmware/govmomi/object"
|
||||||
|
"github.com/vmware/govmomi/vim25/mo"
|
||||||
|
"github.com/vmware/govmomi/vim25/types"
|
||||||
|
"log"
|
||||||
|
"strings"
|
||||||
|
"time"
|
||||||
|
)
|
||||||
|
|
||||||
|
type VirtualMachine struct {
|
||||||
|
vm *object.VirtualMachine
|
||||||
|
driver *Driver
|
||||||
|
}
|
||||||
|
|
||||||
|
type CloneConfig struct {
|
||||||
|
Name string
|
||||||
|
Folder string
|
||||||
|
Cluster string
|
||||||
|
Host string
|
||||||
|
ResourcePool string
|
||||||
|
Datastore string
|
||||||
|
LinkedClone bool
|
||||||
|
Network string
|
||||||
|
Annotation string
|
||||||
|
}
|
||||||
|
|
||||||
|
type HardwareConfig struct {
|
||||||
|
CPUs int32
|
||||||
|
CpuCores int32
|
||||||
|
CPUReservation int64
|
||||||
|
CPULimit int64
|
||||||
|
RAM int64
|
||||||
|
RAMReservation int64
|
||||||
|
RAMReserveAll bool
|
||||||
|
NestedHV bool
|
||||||
|
CpuHotAddEnabled bool
|
||||||
|
MemoryHotAddEnabled bool
|
||||||
|
VideoRAM int64
|
||||||
|
}
|
||||||
|
|
||||||
|
type CreateConfig struct {
|
||||||
|
DiskThinProvisioned bool
|
||||||
|
DiskControllerType string // example: "scsi", "pvscsi"
|
||||||
|
DiskSize int64
|
||||||
|
|
||||||
|
Annotation string
|
||||||
|
Name string
|
||||||
|
Folder string
|
||||||
|
Cluster string
|
||||||
|
Host string
|
||||||
|
ResourcePool string
|
||||||
|
Datastore string
|
||||||
|
GuestOS string // example: otherGuest
|
||||||
|
Network string // "" for default network
|
||||||
|
NetworkCard string // example: vmxnet3
|
||||||
|
USBController bool
|
||||||
|
Version uint // example: 10
|
||||||
|
Firmware string // efi or bios
|
||||||
|
}
|
||||||
|
|
||||||
|
func (d *Driver) NewVM(ref *types.ManagedObjectReference) *VirtualMachine {
|
||||||
|
return &VirtualMachine{
|
||||||
|
vm: object.NewVirtualMachine(d.client.Client, *ref),
|
||||||
|
driver: d,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (d *Driver) FindVM(name string) (*VirtualMachine, error) {
|
||||||
|
vm, err := d.finder.VirtualMachine(d.ctx, name)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
return &VirtualMachine{
|
||||||
|
vm: vm,
|
||||||
|
driver: d,
|
||||||
|
}, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (d *Driver) CreateVM(config *CreateConfig) (*VirtualMachine, error) {
|
||||||
|
createSpec := types.VirtualMachineConfigSpec{
|
||||||
|
Name: config.Name,
|
||||||
|
Annotation: config.Annotation,
|
||||||
|
GuestId: config.GuestOS,
|
||||||
|
}
|
||||||
|
if config.Version != 0 {
|
||||||
|
createSpec.Version = fmt.Sprintf("%s%d", "vmx-", config.Version)
|
||||||
|
}
|
||||||
|
if config.Firmware != "" {
|
||||||
|
createSpec.Firmware = config.Firmware
|
||||||
|
}
|
||||||
|
|
||||||
|
folder, err := d.FindFolder(config.Folder)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
resourcePool, err := d.FindResourcePool(config.Cluster, config.Host, config.ResourcePool)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
var host *object.HostSystem
|
||||||
|
if config.Cluster != "" && config.Host != "" {
|
||||||
|
h, err := d.FindHost(config.Host)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
host = h.host
|
||||||
|
}
|
||||||
|
|
||||||
|
datastore, err := d.FindDatastore(config.Datastore, config.Host)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
devices := object.VirtualDeviceList{}
|
||||||
|
|
||||||
|
devices, err = addDisk(d, devices, config)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
devices, err = addNetwork(d, devices, config)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
if config.USBController {
|
||||||
|
t := true
|
||||||
|
usb := &types.VirtualUSBController{
|
||||||
|
EhciEnabled: &t,
|
||||||
|
}
|
||||||
|
devices = append(devices, usb)
|
||||||
|
}
|
||||||
|
|
||||||
|
createSpec.DeviceChange, err = devices.ConfigSpec(types.VirtualDeviceConfigSpecOperationAdd)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
createSpec.Files = &types.VirtualMachineFileInfo{
|
||||||
|
VmPathName: fmt.Sprintf("[%s]", datastore.Name()),
|
||||||
|
}
|
||||||
|
|
||||||
|
task, err := folder.folder.CreateVM(d.ctx, createSpec, resourcePool.pool, host)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
taskInfo, err := task.WaitForResult(d.ctx, nil)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
vmRef := taskInfo.Result.(types.ManagedObjectReference)
|
||||||
|
|
||||||
|
return d.NewVM(&vmRef), nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (vm *VirtualMachine) Info(params ...string) (*mo.VirtualMachine, error) {
|
||||||
|
var p []string
|
||||||
|
if len(params) == 0 {
|
||||||
|
p = []string{"*"}
|
||||||
|
} else {
|
||||||
|
p = params
|
||||||
|
}
|
||||||
|
var info mo.VirtualMachine
|
||||||
|
err := vm.vm.Properties(vm.driver.ctx, vm.vm.Reference(), p, &info)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
return &info, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (vm *VirtualMachine) Devices() (object.VirtualDeviceList, error) {
|
||||||
|
vmInfo, err := vm.Info("config.hardware.device")
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
return vmInfo.Config.Hardware.Device, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (vm *VirtualMachine) Clone(ctx context.Context, config *CloneConfig) (*VirtualMachine, error) {
|
||||||
|
folder, err := vm.driver.FindFolder(config.Folder)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
var relocateSpec types.VirtualMachineRelocateSpec
|
||||||
|
|
||||||
|
pool, err := vm.driver.FindResourcePool(config.Cluster, config.Host, config.ResourcePool)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
poolRef := pool.pool.Reference()
|
||||||
|
relocateSpec.Pool = &poolRef
|
||||||
|
|
||||||
|
datastore, err := vm.driver.FindDatastore(config.Datastore, config.Host)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
datastoreRef := datastore.ds.Reference()
|
||||||
|
relocateSpec.Datastore = &datastoreRef
|
||||||
|
|
||||||
|
var cloneSpec types.VirtualMachineCloneSpec
|
||||||
|
cloneSpec.Location = relocateSpec
|
||||||
|
cloneSpec.PowerOn = false
|
||||||
|
|
||||||
|
if config.LinkedClone == true {
|
||||||
|
cloneSpec.Location.DiskMoveType = "createNewChildDiskBacking"
|
||||||
|
|
||||||
|
tpl, err := vm.Info("snapshot")
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
if tpl.Snapshot == nil {
|
||||||
|
err = errors.New("`linked_clone=true`, but template has no snapshots")
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
cloneSpec.Snapshot = tpl.Snapshot.CurrentSnapshot
|
||||||
|
}
|
||||||
|
|
||||||
|
var configSpec types.VirtualMachineConfigSpec
|
||||||
|
cloneSpec.Config = &configSpec
|
||||||
|
|
||||||
|
if config.Annotation != "" {
|
||||||
|
configSpec.Annotation = config.Annotation
|
||||||
|
}
|
||||||
|
|
||||||
|
if config.Network != "" {
|
||||||
|
net, err := vm.driver.FindNetwork(config.Network)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
backing, err := net.network.EthernetCardBackingInfo(ctx)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
devices, err := vm.vm.Device(ctx)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
adapter, err := findNetworkAdapter(devices)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
adapter.GetVirtualEthernetCard().Backing = backing
|
||||||
|
|
||||||
|
config := &types.VirtualDeviceConfigSpec{
|
||||||
|
Device: adapter.(types.BaseVirtualDevice),
|
||||||
|
Operation: types.VirtualDeviceConfigSpecOperationEdit,
|
||||||
|
}
|
||||||
|
|
||||||
|
configSpec.DeviceChange = append(configSpec.DeviceChange, config)
|
||||||
|
}
|
||||||
|
|
||||||
|
task, err := vm.vm.Clone(vm.driver.ctx, folder.folder, config.Name, cloneSpec)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
info, err := task.WaitForResult(ctx, nil)
|
||||||
|
if err != nil {
|
||||||
|
if ctx.Err() == context.Canceled {
|
||||||
|
err = task.Cancel(context.TODO())
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
vmRef := info.Result.(types.ManagedObjectReference)
|
||||||
|
created := vm.driver.NewVM(&vmRef)
|
||||||
|
return created, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (vm *VirtualMachine) Destroy() error {
|
||||||
|
task, err := vm.vm.Destroy(vm.driver.ctx)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
_, err = task.WaitForResult(vm.driver.ctx, nil)
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
func (vm *VirtualMachine) Configure(config *HardwareConfig) error {
|
||||||
|
var confSpec types.VirtualMachineConfigSpec
|
||||||
|
confSpec.NumCPUs = config.CPUs
|
||||||
|
confSpec.NumCoresPerSocket = config.CpuCores
|
||||||
|
confSpec.MemoryMB = config.RAM
|
||||||
|
|
||||||
|
var cpuSpec types.ResourceAllocationInfo
|
||||||
|
cpuSpec.Reservation = &config.CPUReservation
|
||||||
|
if config.CPULimit != 0 {
|
||||||
|
cpuSpec.Limit = &config.CPULimit
|
||||||
|
}
|
||||||
|
confSpec.CpuAllocation = &cpuSpec
|
||||||
|
|
||||||
|
var ramSpec types.ResourceAllocationInfo
|
||||||
|
ramSpec.Reservation = &config.RAMReservation
|
||||||
|
confSpec.MemoryAllocation = &ramSpec
|
||||||
|
|
||||||
|
confSpec.MemoryReservationLockedToMax = &config.RAMReserveAll
|
||||||
|
confSpec.NestedHVEnabled = &config.NestedHV
|
||||||
|
|
||||||
|
confSpec.CpuHotAddEnabled = &config.CpuHotAddEnabled
|
||||||
|
confSpec.MemoryHotAddEnabled = &config.MemoryHotAddEnabled
|
||||||
|
|
||||||
|
if config.VideoRAM != 0 {
|
||||||
|
devices, err := vm.vm.Device(vm.driver.ctx)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
l := devices.SelectByType((*types.VirtualMachineVideoCard)(nil))
|
||||||
|
if len(l) != 1 {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
card := l[0].(*types.VirtualMachineVideoCard)
|
||||||
|
|
||||||
|
card.VideoRamSizeInKB = config.VideoRAM
|
||||||
|
|
||||||
|
spec := &types.VirtualDeviceConfigSpec{
|
||||||
|
Device: card,
|
||||||
|
Operation: types.VirtualDeviceConfigSpecOperationEdit,
|
||||||
|
}
|
||||||
|
confSpec.DeviceChange = append(confSpec.DeviceChange, spec)
|
||||||
|
}
|
||||||
|
|
||||||
|
task, err := vm.vm.Reconfigure(vm.driver.ctx, confSpec)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
_, err = task.WaitForResult(vm.driver.ctx, nil)
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
func (vm *VirtualMachine) ResizeDisk(diskSize int64) error {
|
||||||
|
var confSpec types.VirtualMachineConfigSpec
|
||||||
|
|
||||||
|
devices, err := vm.vm.Device(vm.driver.ctx)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
disk, err := findDisk(devices)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
disk.CapacityInKB = diskSize * 1024
|
||||||
|
|
||||||
|
confSpec.DeviceChange = []types.BaseVirtualDeviceConfigSpec{
|
||||||
|
&types.VirtualDeviceConfigSpec{
|
||||||
|
Device: disk,
|
||||||
|
Operation: types.VirtualDeviceConfigSpecOperationEdit,
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
task, err := vm.vm.Reconfigure(vm.driver.ctx, confSpec)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
_, err = task.WaitForResult(vm.driver.ctx, nil)
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
func findDisk(devices object.VirtualDeviceList) (*types.VirtualDisk, error) {
|
||||||
|
var disks []*types.VirtualDisk
|
||||||
|
for _, device := range devices {
|
||||||
|
switch d := device.(type) {
|
||||||
|
case *types.VirtualDisk:
|
||||||
|
disks = append(disks, d)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
switch len(disks) {
|
||||||
|
case 0:
|
||||||
|
return nil, errors.New("VM has no disks")
|
||||||
|
case 1:
|
||||||
|
return disks[0], nil
|
||||||
|
}
|
||||||
|
return nil, errors.New("VM has multiple disks")
|
||||||
|
}
|
||||||
|
|
||||||
|
func (vm *VirtualMachine) PowerOn() error {
|
||||||
|
task, err := vm.vm.PowerOn(vm.driver.ctx)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
_, err = task.WaitForResult(vm.driver.ctx, nil)
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
func (vm *VirtualMachine) WaitForIP(ctx context.Context) (string, error) {
|
||||||
|
return vm.vm.WaitForIP(ctx)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (vm *VirtualMachine) PowerOff() error {
|
||||||
|
state, err := vm.vm.PowerState(vm.driver.ctx)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
if state == types.VirtualMachinePowerStatePoweredOff {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
task, err := vm.vm.PowerOff(vm.driver.ctx)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
_, err = task.WaitForResult(vm.driver.ctx, nil)
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
func (vm *VirtualMachine) StartShutdown() error {
|
||||||
|
err := vm.vm.ShutdownGuest(vm.driver.ctx)
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
func (vm *VirtualMachine) WaitForShutdown(ctx context.Context, timeout time.Duration) error {
|
||||||
|
shutdownTimer := time.After(timeout)
|
||||||
|
for {
|
||||||
|
powerState, err := vm.vm.PowerState(vm.driver.ctx)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
if powerState == "poweredOff" {
|
||||||
|
break
|
||||||
|
}
|
||||||
|
|
||||||
|
select {
|
||||||
|
case <-shutdownTimer:
|
||||||
|
err := errors.New("Timeout while waiting for machine to shut down.")
|
||||||
|
return err
|
||||||
|
case <-ctx.Done():
|
||||||
|
return nil
|
||||||
|
default:
|
||||||
|
time.Sleep(1 * time.Second)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (vm *VirtualMachine) CreateSnapshot(name string) error {
|
||||||
|
task, err := vm.vm.CreateSnapshot(vm.driver.ctx, name, "", false, false)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
_, err = task.WaitForResult(vm.driver.ctx, nil)
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
func (vm *VirtualMachine) ConvertToTemplate() error {
|
||||||
|
return vm.vm.MarkAsTemplate(vm.driver.ctx)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (vm *VirtualMachine) GetDir() (string, error) {
|
||||||
|
vmInfo, err := vm.Info("name", "layoutEx.file")
|
||||||
|
if err != nil {
|
||||||
|
return "", err
|
||||||
|
}
|
||||||
|
|
||||||
|
vmxName := fmt.Sprintf("/%s.vmx", vmInfo.Name)
|
||||||
|
for _, file := range vmInfo.LayoutEx.File {
|
||||||
|
if strings.HasSuffix(file.Name, vmxName) {
|
||||||
|
return RemoveDatastorePrefix(file.Name[:len(file.Name)-len(vmxName)]), nil
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return "", fmt.Errorf("cannot find '%s'", vmxName)
|
||||||
|
}
|
||||||
|
|
||||||
|
func addDisk(_ *Driver, devices object.VirtualDeviceList, config *CreateConfig) (object.VirtualDeviceList, error) {
|
||||||
|
device, err := devices.CreateSCSIController(config.DiskControllerType)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
devices = append(devices, device)
|
||||||
|
controller, err := devices.FindDiskController(devices.Name(device))
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
disk := &types.VirtualDisk{
|
||||||
|
VirtualDevice: types.VirtualDevice{
|
||||||
|
Key: devices.NewKey(),
|
||||||
|
Backing: &types.VirtualDiskFlatVer2BackingInfo{
|
||||||
|
DiskMode: string(types.VirtualDiskModePersistent),
|
||||||
|
ThinProvisioned: types.NewBool(config.DiskThinProvisioned),
|
||||||
|
},
|
||||||
|
},
|
||||||
|
CapacityInKB: config.DiskSize * 1024,
|
||||||
|
}
|
||||||
|
|
||||||
|
devices.AssignController(disk, controller)
|
||||||
|
devices = append(devices, disk)
|
||||||
|
|
||||||
|
return devices, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func addNetwork(d *Driver, devices object.VirtualDeviceList, config *CreateConfig) (object.VirtualDeviceList, error) {
|
||||||
|
var network object.NetworkReference
|
||||||
|
if config.Network == "" {
|
||||||
|
h, err := d.FindHost(config.Host)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
i, err := h.Info("network")
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
if len(i.Network) > 1 {
|
||||||
|
return nil, fmt.Errorf("Host has multiple networks. Specify it explicitly")
|
||||||
|
}
|
||||||
|
|
||||||
|
network = object.NewNetwork(d.client.Client, i.Network[0])
|
||||||
|
} else {
|
||||||
|
var err error
|
||||||
|
network, err = d.finder.Network(d.ctx, config.Network)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
backing, err := network.EthernetCardBackingInfo(d.ctx)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
device, err := object.EthernetCardTypes().CreateEthernetCard(config.NetworkCard, backing)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
return append(devices, device), nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (vm *VirtualMachine) AddCdrom(controllerType string, isoPath string) error {
|
||||||
|
devices, err := vm.vm.Device(vm.driver.ctx)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
var controller *types.VirtualController
|
||||||
|
if controllerType == "sata" {
|
||||||
|
c, err := vm.FindSATAController()
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
controller = c.GetVirtualController()
|
||||||
|
} else {
|
||||||
|
c, err := devices.FindIDEController("")
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
controller = c.GetVirtualController()
|
||||||
|
}
|
||||||
|
|
||||||
|
cdrom, err := vm.CreateCdrom(controller)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
if isoPath != "" {
|
||||||
|
devices.InsertIso(cdrom, isoPath)
|
||||||
|
}
|
||||||
|
|
||||||
|
log.Printf("Creating CD-ROM on controller '%v' with iso '%v'", controller, isoPath)
|
||||||
|
return vm.addDevice(cdrom)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (vm *VirtualMachine) AddFloppy(imgPath string) error {
|
||||||
|
devices, err := vm.vm.Device(vm.driver.ctx)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
floppy, err := devices.CreateFloppy()
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
if imgPath != "" {
|
||||||
|
floppy = devices.InsertImg(floppy, imgPath)
|
||||||
|
}
|
||||||
|
|
||||||
|
return vm.addDevice(floppy)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (vm *VirtualMachine) SetBootOrder(order []string) error {
|
||||||
|
devices, err := vm.vm.Device(vm.driver.ctx)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
bootOptions := types.VirtualMachineBootOptions{
|
||||||
|
BootOrder: devices.BootOrder(order),
|
||||||
|
}
|
||||||
|
|
||||||
|
return vm.vm.SetBootOptions(vm.driver.ctx, &bootOptions)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (vm *VirtualMachine) RemoveDevice(keepFiles bool, device ...types.BaseVirtualDevice) error {
|
||||||
|
return vm.vm.RemoveDevice(vm.driver.ctx, keepFiles, device...)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (vm *VirtualMachine) addDevice(device types.BaseVirtualDevice) error {
|
||||||
|
newDevices := object.VirtualDeviceList{device}
|
||||||
|
confSpec := types.VirtualMachineConfigSpec{}
|
||||||
|
var err error
|
||||||
|
confSpec.DeviceChange, err = newDevices.ConfigSpec(types.VirtualDeviceConfigSpecOperationAdd)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
task, err := vm.vm.Reconfigure(vm.driver.ctx, confSpec)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
_, err = task.WaitForResult(vm.driver.ctx, nil)
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
func (vm *VirtualMachine) AddConfigParams(params map[string]string) error {
|
||||||
|
var confSpec types.VirtualMachineConfigSpec
|
||||||
|
|
||||||
|
var ov []types.BaseOptionValue
|
||||||
|
for k, v := range params {
|
||||||
|
o := types.OptionValue{
|
||||||
|
Key: k,
|
||||||
|
Value: v,
|
||||||
|
}
|
||||||
|
ov = append(ov, &o)
|
||||||
|
}
|
||||||
|
confSpec.ExtraConfig = ov
|
||||||
|
|
||||||
|
task, err := vm.vm.Reconfigure(vm.driver.ctx, confSpec)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
_, err = task.WaitForResult(vm.driver.ctx, nil)
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
func findNetworkAdapter(l object.VirtualDeviceList) (types.BaseVirtualEthernetCard, error) {
|
||||||
|
c := l.SelectByType((*types.VirtualEthernetCard)(nil))
|
||||||
|
if len(c) == 0 {
|
||||||
|
return nil, errors.New("no network adapter device found")
|
||||||
|
}
|
||||||
|
|
||||||
|
return c[0].(types.BaseVirtualEthernetCard), nil
|
||||||
|
}
|
|
@ -0,0 +1,71 @@
|
||||||
|
package driver
|
||||||
|
|
||||||
|
import (
|
||||||
|
"errors"
|
||||||
|
"github.com/vmware/govmomi/vim25/types"
|
||||||
|
)
|
||||||
|
|
||||||
|
var (
|
||||||
|
ErrNoSataController = errors.New("no available SATA controller")
|
||||||
|
)
|
||||||
|
|
||||||
|
func (vm *VirtualMachine) AddSATAController() error {
|
||||||
|
sata := &types.VirtualAHCIController{}
|
||||||
|
return vm.addDevice(sata)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (vm *VirtualMachine) FindSATAController() (*types.VirtualAHCIController, error) {
|
||||||
|
l, err := vm.Devices()
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
c := l.PickController((*types.VirtualAHCIController)(nil))
|
||||||
|
if c == nil {
|
||||||
|
return nil, ErrNoSataController
|
||||||
|
}
|
||||||
|
|
||||||
|
return c.(*types.VirtualAHCIController), nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (vm *VirtualMachine) CreateCdrom(c *types.VirtualController) (*types.VirtualCdrom, error) {
|
||||||
|
l, err := vm.Devices()
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
device := &types.VirtualCdrom{}
|
||||||
|
|
||||||
|
l.AssignController(device, c)
|
||||||
|
|
||||||
|
device.Backing = &types.VirtualCdromAtapiBackingInfo{
|
||||||
|
VirtualDeviceDeviceBackingInfo: types.VirtualDeviceDeviceBackingInfo{},
|
||||||
|
}
|
||||||
|
|
||||||
|
device.Connectable = &types.VirtualDeviceConnectInfo{
|
||||||
|
AllowGuestControl: true,
|
||||||
|
Connected: true,
|
||||||
|
StartConnected: true,
|
||||||
|
}
|
||||||
|
|
||||||
|
return device, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (vm *VirtualMachine) EjectCdroms() error {
|
||||||
|
devices, err := vm.Devices()
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
cdroms := devices.SelectByType((*types.VirtualCdrom)(nil))
|
||||||
|
for _, cd := range cdroms {
|
||||||
|
c := cd.(*types.VirtualCdrom)
|
||||||
|
c.Backing = &types.VirtualCdromRemotePassthroughBackingInfo{}
|
||||||
|
c.Connectable = &types.VirtualDeviceConnectInfo{}
|
||||||
|
err := vm.vm.EditDevice(vm.driver.ctx, c)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
|
@ -0,0 +1,312 @@
|
||||||
|
package driver
|
||||||
|
|
||||||
|
import (
|
||||||
|
"context"
|
||||||
|
"log"
|
||||||
|
"net"
|
||||||
|
"testing"
|
||||||
|
"time"
|
||||||
|
)
|
||||||
|
|
||||||
|
func TestVMAcc_clone(t *testing.T) {
|
||||||
|
t.Skip("Acceptance tests not configured yet.")
|
||||||
|
testCases := []struct {
|
||||||
|
name string
|
||||||
|
config *CloneConfig
|
||||||
|
checkFunction func(*testing.T, *VirtualMachine, *CloneConfig)
|
||||||
|
}{
|
||||||
|
{"Default", &CloneConfig{}, cloneDefaultCheck},
|
||||||
|
{"LinkedClone", &CloneConfig{LinkedClone: true}, cloneLinkedCloneCheck},
|
||||||
|
{"Folder", &CloneConfig{LinkedClone: true, Folder: "folder1/folder2"}, cloneFolderCheck},
|
||||||
|
{"ResourcePool", &CloneConfig{LinkedClone: true, ResourcePool: "pool1/pool2"}, cloneResourcePoolCheck},
|
||||||
|
{"Configure", &CloneConfig{LinkedClone: true}, configureCheck},
|
||||||
|
{"Configure_RAMReserveAll", &CloneConfig{LinkedClone: true}, configureRAMReserveAllCheck},
|
||||||
|
{"StartAndStop", &CloneConfig{LinkedClone: true}, startAndStopCheck},
|
||||||
|
{"Template", &CloneConfig{LinkedClone: true}, templateCheck},
|
||||||
|
{"Snapshot", &CloneConfig{}, snapshotCheck},
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, tc := range testCases {
|
||||||
|
t.Run(tc.name, func(t *testing.T) {
|
||||||
|
tc.config.Host = TestHostName
|
||||||
|
tc.config.Name = newVMName()
|
||||||
|
|
||||||
|
templateName := "alpine"
|
||||||
|
d := newTestDriver(t)
|
||||||
|
|
||||||
|
template, err := d.FindVM(templateName) // Don't destroy this VM!
|
||||||
|
if err != nil {
|
||||||
|
t.Fatalf("Cannot find template vm '%v': %v", templateName, err)
|
||||||
|
}
|
||||||
|
|
||||||
|
log.Printf("[DEBUG] Clonning VM")
|
||||||
|
vm, err := template.Clone(context.TODO(), tc.config)
|
||||||
|
if err != nil {
|
||||||
|
t.Fatalf("Cannot clone vm '%v': %v", templateName, err)
|
||||||
|
}
|
||||||
|
|
||||||
|
defer destroyVM(t, vm, tc.config.Name)
|
||||||
|
|
||||||
|
log.Printf("[DEBUG] Running check function")
|
||||||
|
tc.checkFunction(t, vm, tc.config)
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func cloneDefaultCheck(t *testing.T, vm *VirtualMachine, config *CloneConfig) {
|
||||||
|
d := vm.driver
|
||||||
|
|
||||||
|
// Check that the clone can be found by its name
|
||||||
|
if _, err := d.FindVM(config.Name); err != nil {
|
||||||
|
t.Errorf("Cannot find created vm '%v': %v", config.Name, err)
|
||||||
|
}
|
||||||
|
|
||||||
|
vmInfo, err := vm.Info("name", "parent", "runtime.host", "resourcePool", "datastore", "layoutEx.disk")
|
||||||
|
if err != nil {
|
||||||
|
t.Fatalf("Cannot read VM properties: %v", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
if vmInfo.Name != config.Name {
|
||||||
|
t.Errorf("Invalid VM name: expected '%v', got '%v'", config.Name, vmInfo.Name)
|
||||||
|
}
|
||||||
|
|
||||||
|
f := d.NewFolder(vmInfo.Parent)
|
||||||
|
folderPath, err := f.Path()
|
||||||
|
if err != nil {
|
||||||
|
t.Fatalf("Cannot read folder name: %v", err)
|
||||||
|
}
|
||||||
|
if folderPath != "" {
|
||||||
|
t.Errorf("Invalid folder: expected '/', got '%v'", folderPath)
|
||||||
|
}
|
||||||
|
|
||||||
|
h := d.NewHost(vmInfo.Runtime.Host)
|
||||||
|
hostInfo, err := h.Info("name")
|
||||||
|
if err != nil {
|
||||||
|
t.Fatal("Cannot read host properties: ", err)
|
||||||
|
}
|
||||||
|
if hostInfo.Name != TestHostName {
|
||||||
|
t.Errorf("Invalid host name: expected '%v', got '%v'", TestHostName, hostInfo.Name)
|
||||||
|
}
|
||||||
|
|
||||||
|
p := d.NewResourcePool(vmInfo.ResourcePool)
|
||||||
|
poolPath, err := p.Path()
|
||||||
|
if err != nil {
|
||||||
|
t.Fatalf("Cannot read resource pool name: %v", err)
|
||||||
|
}
|
||||||
|
if poolPath != "" {
|
||||||
|
t.Errorf("Invalid resource pool: expected '/', got '%v'", poolPath)
|
||||||
|
}
|
||||||
|
|
||||||
|
dsr := vmInfo.Datastore[0].Reference()
|
||||||
|
ds := d.NewDatastore(&dsr)
|
||||||
|
dsInfo, err := ds.Info("name")
|
||||||
|
if err != nil {
|
||||||
|
t.Fatal("Cannot read datastore properties: ", err)
|
||||||
|
}
|
||||||
|
if dsInfo.Name != "datastore1" {
|
||||||
|
t.Errorf("Invalid datastore name: expected '%v', got '%v'", "datastore1", dsInfo.Name)
|
||||||
|
}
|
||||||
|
|
||||||
|
if len(vmInfo.LayoutEx.Disk[0].Chain) != 1 {
|
||||||
|
t.Error("Not a full clone")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func configureCheck(t *testing.T, vm *VirtualMachine, _ *CloneConfig) {
|
||||||
|
log.Printf("[DEBUG] Configuring the vm")
|
||||||
|
hwConfig := &HardwareConfig{
|
||||||
|
CPUs: 2,
|
||||||
|
CPUReservation: 1000,
|
||||||
|
CPULimit: 1500,
|
||||||
|
RAM: 2048,
|
||||||
|
RAMReservation: 1024,
|
||||||
|
MemoryHotAddEnabled: true,
|
||||||
|
CpuHotAddEnabled: true,
|
||||||
|
}
|
||||||
|
err := vm.Configure(hwConfig)
|
||||||
|
if err != nil {
|
||||||
|
t.Fatalf("Failed to configure VM: %v", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
log.Printf("[DEBUG] Running checks")
|
||||||
|
vmInfo, err := vm.Info("config")
|
||||||
|
if err != nil {
|
||||||
|
t.Fatalf("Cannot read VM properties: %v", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
cpuSockets := vmInfo.Config.Hardware.NumCPU
|
||||||
|
if cpuSockets != hwConfig.CPUs {
|
||||||
|
t.Errorf("VM should have %v CPU sockets, got %v", hwConfig.CPUs, cpuSockets)
|
||||||
|
}
|
||||||
|
|
||||||
|
cpuReservation := *vmInfo.Config.CpuAllocation.Reservation
|
||||||
|
if cpuReservation != hwConfig.CPUReservation {
|
||||||
|
t.Errorf("VM should have CPU reservation for %v Mhz, got %v", hwConfig.CPUReservation, cpuReservation)
|
||||||
|
}
|
||||||
|
|
||||||
|
cpuLimit := *vmInfo.Config.CpuAllocation.Limit
|
||||||
|
if cpuLimit != hwConfig.CPULimit {
|
||||||
|
t.Errorf("VM should have CPU reservation for %v Mhz, got %v", hwConfig.CPULimit, cpuLimit)
|
||||||
|
}
|
||||||
|
|
||||||
|
ram := vmInfo.Config.Hardware.MemoryMB
|
||||||
|
if int64(ram) != hwConfig.RAM {
|
||||||
|
t.Errorf("VM should have %v MB of RAM, got %v", hwConfig.RAM, ram)
|
||||||
|
}
|
||||||
|
|
||||||
|
ramReservation := *vmInfo.Config.MemoryAllocation.Reservation
|
||||||
|
if ramReservation != hwConfig.RAMReservation {
|
||||||
|
t.Errorf("VM should have RAM reservation for %v MB, got %v", hwConfig.RAMReservation, ramReservation)
|
||||||
|
}
|
||||||
|
|
||||||
|
cpuHotAdd := vmInfo.Config.CpuHotAddEnabled
|
||||||
|
if *cpuHotAdd != hwConfig.CpuHotAddEnabled {
|
||||||
|
t.Errorf("VM should have CPU hot add set to %v, got %v", hwConfig.CpuHotAddEnabled, cpuHotAdd)
|
||||||
|
}
|
||||||
|
|
||||||
|
memoryHotAdd := vmInfo.Config.MemoryHotAddEnabled
|
||||||
|
if *memoryHotAdd != hwConfig.MemoryHotAddEnabled {
|
||||||
|
t.Errorf("VM should have Memroy hot add set to %v, got %v", hwConfig.MemoryHotAddEnabled, memoryHotAdd)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func configureRAMReserveAllCheck(t *testing.T, vm *VirtualMachine, _ *CloneConfig) {
|
||||||
|
log.Printf("[DEBUG] Configuring the vm")
|
||||||
|
err := vm.Configure(&HardwareConfig{RAMReserveAll: true})
|
||||||
|
if err != nil {
|
||||||
|
t.Fatalf("Failed to configure VM: %v", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
log.Printf("[DEBUG] Running checks")
|
||||||
|
vmInfo, err := vm.Info("config")
|
||||||
|
if err != nil {
|
||||||
|
t.Fatalf("Cannot read VM properties: %v", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
if *vmInfo.Config.MemoryReservationLockedToMax != true {
|
||||||
|
t.Errorf("VM should have all RAM reserved")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func cloneLinkedCloneCheck(t *testing.T, vm *VirtualMachine, _ *CloneConfig) {
|
||||||
|
vmInfo, err := vm.Info("layoutEx.disk")
|
||||||
|
if err != nil {
|
||||||
|
t.Fatalf("Cannot read VM properties: %v", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
if len(vmInfo.LayoutEx.Disk[0].Chain) != 2 {
|
||||||
|
t.Error("Not a linked clone")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func cloneFolderCheck(t *testing.T, vm *VirtualMachine, config *CloneConfig) {
|
||||||
|
vmInfo, err := vm.Info("parent")
|
||||||
|
if err != nil {
|
||||||
|
t.Fatalf("Cannot read VM properties: %v", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
f := vm.driver.NewFolder(vmInfo.Parent)
|
||||||
|
path, err := f.Path()
|
||||||
|
if err != nil {
|
||||||
|
t.Fatalf("Cannot read folder name: %v", err)
|
||||||
|
}
|
||||||
|
if path != config.Folder {
|
||||||
|
t.Errorf("Wrong folder. expected: %v, got: %v", config.Folder, path)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func cloneResourcePoolCheck(t *testing.T, vm *VirtualMachine, config *CloneConfig) {
|
||||||
|
vmInfo, err := vm.Info("resourcePool")
|
||||||
|
if err != nil {
|
||||||
|
t.Fatalf("Cannot read VM properties: %v", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
p := vm.driver.NewResourcePool(vmInfo.ResourcePool)
|
||||||
|
path, err := p.Path()
|
||||||
|
if err != nil {
|
||||||
|
t.Fatalf("Cannot read resource pool name: %v", err)
|
||||||
|
}
|
||||||
|
if path != config.ResourcePool {
|
||||||
|
t.Errorf("Wrong folder. expected: %v, got: %v", config.ResourcePool, path)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func startAndStopCheck(t *testing.T, vm *VirtualMachine, config *CloneConfig) {
|
||||||
|
stopper := startVM(t, vm, config.Name)
|
||||||
|
defer stopper()
|
||||||
|
|
||||||
|
switch ip, err := vm.WaitForIP(context.TODO()); {
|
||||||
|
case err != nil:
|
||||||
|
t.Errorf("Cannot obtain IP address from created vm '%v': %v", config.Name, err)
|
||||||
|
case net.ParseIP(ip) == nil:
|
||||||
|
t.Errorf("'%v' is not a valid ip address", ip)
|
||||||
|
}
|
||||||
|
|
||||||
|
err := vm.StartShutdown()
|
||||||
|
if err != nil {
|
||||||
|
t.Fatalf("Failed to initiate guest shutdown: %v", err)
|
||||||
|
}
|
||||||
|
log.Printf("[DEBUG] Waiting max 1m0s for shutdown to complete")
|
||||||
|
err = vm.WaitForShutdown(context.TODO(), 1*time.Minute)
|
||||||
|
if err != nil {
|
||||||
|
t.Fatalf("Failed to wait for giest shutdown: %v", err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func snapshotCheck(t *testing.T, vm *VirtualMachine, config *CloneConfig) {
|
||||||
|
stopper := startVM(t, vm, config.Name)
|
||||||
|
defer stopper()
|
||||||
|
|
||||||
|
err := vm.CreateSnapshot("test-snapshot")
|
||||||
|
if err != nil {
|
||||||
|
t.Fatalf("Failed to create snapshot: %v", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
vmInfo, err := vm.Info("layoutEx.disk")
|
||||||
|
if err != nil {
|
||||||
|
t.Fatalf("Cannot read VM properties: %v", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
layers := len(vmInfo.LayoutEx.Disk[0].Chain)
|
||||||
|
if layers != 2 {
|
||||||
|
t.Errorf("VM should have a single snapshot. expected 2 disk layers, got %v", layers)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func templateCheck(t *testing.T, vm *VirtualMachine, _ *CloneConfig) {
|
||||||
|
err := vm.ConvertToTemplate()
|
||||||
|
if err != nil {
|
||||||
|
t.Fatalf("Failed to convert to template: %v", err)
|
||||||
|
}
|
||||||
|
vmInfo, err := vm.Info("config.template")
|
||||||
|
if err != nil {
|
||||||
|
t.Errorf("Cannot read VM properties: %v", err)
|
||||||
|
} else if !vmInfo.Config.Template {
|
||||||
|
t.Error("Not a template")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func startVM(t *testing.T, vm *VirtualMachine, vmName string) (stopper func()) {
|
||||||
|
log.Printf("[DEBUG] Starting the vm")
|
||||||
|
if err := vm.PowerOn(); err != nil {
|
||||||
|
t.Fatalf("Cannot start vm '%v': %v", vmName, err)
|
||||||
|
}
|
||||||
|
return func() {
|
||||||
|
log.Printf("[DEBUG] Powering off the vm")
|
||||||
|
if err := vm.PowerOff(); err != nil {
|
||||||
|
t.Errorf("Cannot power off started vm '%v': %v", vmName, err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func destroyVM(t *testing.T, vm *VirtualMachine, vmName string) {
|
||||||
|
log.Printf("[DEBUG] Deleting the VM")
|
||||||
|
if err := vm.Destroy(); err != nil {
|
||||||
|
t.Errorf("!!! ERROR DELETING VM '%v': %v!!!", vmName, err)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Check that the clone is no longer exists
|
||||||
|
if _, err := vm.driver.FindVM(vmName); err == nil {
|
||||||
|
t.Errorf("!!! STILL CAN FIND VM '%v'. IT MIGHT NOT HAVE BEEN DELETED !!!", vmName)
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,92 @@
|
||||||
|
package driver
|
||||||
|
|
||||||
|
import (
|
||||||
|
"log"
|
||||||
|
"testing"
|
||||||
|
)
|
||||||
|
|
||||||
|
func TestVMAcc_create(t *testing.T) {
|
||||||
|
t.Skip("Acceptance tests not configured yet.")
|
||||||
|
testCases := []struct {
|
||||||
|
name string
|
||||||
|
config *CreateConfig
|
||||||
|
checkFunction func(*testing.T, *VirtualMachine, *CreateConfig)
|
||||||
|
}{
|
||||||
|
{"MinimalConfiguration", &CreateConfig{}, createDefaultCheck},
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, tc := range testCases {
|
||||||
|
t.Run(tc.name, func(t *testing.T) {
|
||||||
|
tc.config.Host = TestHostName
|
||||||
|
tc.config.Name = newVMName()
|
||||||
|
|
||||||
|
d := newTestDriver(t)
|
||||||
|
|
||||||
|
log.Printf("[DEBUG] Creating VM")
|
||||||
|
vm, err := d.CreateVM(tc.config)
|
||||||
|
if err != nil {
|
||||||
|
t.Fatalf("Cannot create VM: %v", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
defer destroyVM(t, vm, tc.config.Name)
|
||||||
|
|
||||||
|
log.Printf("[DEBUG] Running check function")
|
||||||
|
tc.checkFunction(t, vm, tc.config)
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func createDefaultCheck(t *testing.T, vm *VirtualMachine, config *CreateConfig) {
|
||||||
|
d := vm.driver
|
||||||
|
|
||||||
|
// Check that the clone can be found by its name
|
||||||
|
if _, err := d.FindVM(config.Name); err != nil {
|
||||||
|
t.Errorf("Cannot find created vm '%v': %v", config.Name, err)
|
||||||
|
}
|
||||||
|
|
||||||
|
vmInfo, err := vm.Info("name", "parent", "runtime.host", "resourcePool", "datastore", "layoutEx.disk")
|
||||||
|
if err != nil {
|
||||||
|
t.Fatalf("Cannot read VM properties: %v", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
if vmInfo.Name != config.Name {
|
||||||
|
t.Errorf("Invalid VM name: expected '%v', got '%v'", config.Name, vmInfo.Name)
|
||||||
|
}
|
||||||
|
|
||||||
|
f := d.NewFolder(vmInfo.Parent)
|
||||||
|
folderPath, err := f.Path()
|
||||||
|
if err != nil {
|
||||||
|
t.Fatalf("Cannot read folder name: %v", err)
|
||||||
|
}
|
||||||
|
if folderPath != "" {
|
||||||
|
t.Errorf("Invalid folder: expected '/', got '%v'", folderPath)
|
||||||
|
}
|
||||||
|
|
||||||
|
h := d.NewHost(vmInfo.Runtime.Host)
|
||||||
|
hostInfo, err := h.Info("name")
|
||||||
|
if err != nil {
|
||||||
|
t.Fatal("Cannot read host properties: ", err)
|
||||||
|
}
|
||||||
|
if hostInfo.Name != TestHostName {
|
||||||
|
t.Errorf("Invalid host name: expected '%v', got '%v'", TestHostName, hostInfo.Name)
|
||||||
|
}
|
||||||
|
|
||||||
|
p := d.NewResourcePool(vmInfo.ResourcePool)
|
||||||
|
poolPath, err := p.Path()
|
||||||
|
if err != nil {
|
||||||
|
t.Fatalf("Cannot read resource pool name: %v", err)
|
||||||
|
}
|
||||||
|
if poolPath != "" {
|
||||||
|
t.Errorf("Invalid resource pool: expected '/', got '%v'", poolPath)
|
||||||
|
}
|
||||||
|
|
||||||
|
dsr := vmInfo.Datastore[0].Reference()
|
||||||
|
ds := d.NewDatastore(&dsr)
|
||||||
|
dsInfo, err := ds.Info("name")
|
||||||
|
if err != nil {
|
||||||
|
t.Fatal("Cannot read datastore properties: ", err)
|
||||||
|
}
|
||||||
|
if dsInfo.Name != "datastore1" {
|
||||||
|
t.Errorf("Invalid datastore name: expected 'datastore1', got '%v'", dsInfo.Name)
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,82 @@
|
||||||
|
package driver
|
||||||
|
|
||||||
|
import (
|
||||||
|
"github.com/vmware/govmomi/vim25/methods"
|
||||||
|
"github.com/vmware/govmomi/vim25/types"
|
||||||
|
"golang.org/x/mobile/event/key"
|
||||||
|
"strings"
|
||||||
|
"unicode"
|
||||||
|
)
|
||||||
|
|
||||||
|
type KeyInput struct {
|
||||||
|
Message string
|
||||||
|
Scancode key.Code
|
||||||
|
Alt bool
|
||||||
|
Ctrl bool
|
||||||
|
Shift bool
|
||||||
|
}
|
||||||
|
|
||||||
|
var scancodeMap = make(map[rune]key.Code)
|
||||||
|
|
||||||
|
func init() {
|
||||||
|
scancodeIndex := make(map[string]key.Code)
|
||||||
|
scancodeIndex["abcdefghijklmnopqrstuvwxyz"] = key.CodeA
|
||||||
|
scancodeIndex["ABCDEFGHIJKLMNOPQRSTUVWXYZ"] = key.CodeA
|
||||||
|
scancodeIndex["1234567890"] = key.Code1
|
||||||
|
scancodeIndex["!@#$%^&*()"] = key.Code1
|
||||||
|
scancodeIndex[" "] = key.CodeSpacebar
|
||||||
|
scancodeIndex["-=[]\\"] = key.CodeHyphenMinus
|
||||||
|
scancodeIndex["_+{}|"] = key.CodeHyphenMinus
|
||||||
|
scancodeIndex[";'`,./"] = key.CodeSemicolon
|
||||||
|
scancodeIndex[":\"~<>?"] = key.CodeSemicolon
|
||||||
|
|
||||||
|
for chars, start := range scancodeIndex {
|
||||||
|
for i, r := range chars {
|
||||||
|
scancodeMap[r] = start + key.Code(i)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const shiftedChars = "!@#$%^&*()_+{}|:\"~<>?"
|
||||||
|
|
||||||
|
func (vm *VirtualMachine) TypeOnKeyboard(input KeyInput) (int32, error) {
|
||||||
|
var spec types.UsbScanCodeSpec
|
||||||
|
|
||||||
|
for _, r := range input.Message {
|
||||||
|
scancode := scancodeMap[r]
|
||||||
|
shift := input.Shift || unicode.IsUpper(r) || strings.ContainsRune(shiftedChars, r)
|
||||||
|
|
||||||
|
spec.KeyEvents = append(spec.KeyEvents, types.UsbScanCodeSpecKeyEvent{
|
||||||
|
// https://github.com/lamw/vghetto-scripts/blob/f74bc8ba20064f46592bcce5a873b161a7fa3d72/powershell/VMKeystrokes.ps1#L130
|
||||||
|
UsbHidCode: int32(scancode)<<16 | 7,
|
||||||
|
Modifiers: &types.UsbScanCodeSpecModifierType{
|
||||||
|
LeftControl: &input.Ctrl,
|
||||||
|
LeftAlt: &input.Alt,
|
||||||
|
LeftShift: &shift,
|
||||||
|
},
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
if input.Scancode != 0 {
|
||||||
|
spec.KeyEvents = append(spec.KeyEvents, types.UsbScanCodeSpecKeyEvent{
|
||||||
|
UsbHidCode: int32(input.Scancode)<<16 | 7,
|
||||||
|
Modifiers: &types.UsbScanCodeSpecModifierType{
|
||||||
|
LeftControl: &input.Ctrl,
|
||||||
|
LeftAlt: &input.Alt,
|
||||||
|
LeftShift: &input.Shift,
|
||||||
|
},
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
req := &types.PutUsbScanCodes{
|
||||||
|
This: vm.vm.Reference(),
|
||||||
|
Spec: spec,
|
||||||
|
}
|
||||||
|
|
||||||
|
resp, err := methods.PutUsbScanCodes(vm.driver.ctx, vm.driver.client.RoundTripper, req)
|
||||||
|
if err != nil {
|
||||||
|
return 0, err
|
||||||
|
}
|
||||||
|
|
||||||
|
return resp.Returnval, nil
|
||||||
|
}
|
|
@ -0,0 +1,62 @@
|
||||||
|
{
|
||||||
|
"builders": [
|
||||||
|
{
|
||||||
|
"type": "vsphere-iso",
|
||||||
|
|
||||||
|
"vcenter_server": "vcenter.vsphere65.test",
|
||||||
|
"username": "root",
|
||||||
|
"password": "jetbrains",
|
||||||
|
"insecure_connection": true,
|
||||||
|
|
||||||
|
"vm_name": "alpine-{{timestamp}}",
|
||||||
|
"host": "esxi-1.vsphere65.test",
|
||||||
|
|
||||||
|
"CPUs": 1,
|
||||||
|
"RAM": 512,
|
||||||
|
"RAM_reserve_all": true,
|
||||||
|
"disk_controller_type": "pvscsi",
|
||||||
|
"disk_size": 1024,
|
||||||
|
"disk_thin_provisioned": true,
|
||||||
|
"network_card": "vmxnet3",
|
||||||
|
|
||||||
|
"guest_os_type": "other3xLinux64Guest",
|
||||||
|
|
||||||
|
"iso_paths": [
|
||||||
|
"[datastore1] ISO/alpine-standard-3.8.2-x86_64.iso"
|
||||||
|
],
|
||||||
|
"floppy_files": [
|
||||||
|
"{{template_dir}}/answerfile",
|
||||||
|
"{{template_dir}}/setup.sh"
|
||||||
|
],
|
||||||
|
|
||||||
|
"boot_wait": "15s",
|
||||||
|
"boot_command": [
|
||||||
|
"root<enter><wait>",
|
||||||
|
"mount -t vfat /dev/fd0 /media/floppy<enter><wait>",
|
||||||
|
"setup-alpine -f /media/floppy/answerfile<enter>",
|
||||||
|
"<wait5>",
|
||||||
|
"jetbrains<enter>",
|
||||||
|
"jetbrains<enter>",
|
||||||
|
"<wait5>",
|
||||||
|
"y<enter>",
|
||||||
|
"<wait10><wait10><wait10><wait10>",
|
||||||
|
"reboot<enter>",
|
||||||
|
"<wait10><wait10>",
|
||||||
|
"root<enter>",
|
||||||
|
"jetbrains<enter><wait>",
|
||||||
|
"mount -t vfat /dev/fd0 /media/floppy<enter><wait>",
|
||||||
|
"/media/floppy/SETUP.SH<enter>"
|
||||||
|
],
|
||||||
|
|
||||||
|
"ssh_username": "root",
|
||||||
|
"ssh_password": "jetbrains"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
|
||||||
|
"provisioners": [
|
||||||
|
{
|
||||||
|
"type": "shell",
|
||||||
|
"inline": ["ls /"]
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
|
@ -0,0 +1,15 @@
|
||||||
|
KEYMAPOPTS="us us"
|
||||||
|
HOSTNAMEOPTS="-n alpine"
|
||||||
|
INTERFACESOPTS="auto lo
|
||||||
|
iface lo inet loopback
|
||||||
|
|
||||||
|
auto eth0
|
||||||
|
iface eth0 inet dhcp
|
||||||
|
hostname alpine
|
||||||
|
"
|
||||||
|
TIMEZONEOPTS="-z UTC"
|
||||||
|
PROXYOPTS="none"
|
||||||
|
APKREPOSOPTS="http://mirror.yandex.ru/mirrors/alpine/v3.8/main"
|
||||||
|
SSHDOPTS="-c openssh"
|
||||||
|
NTPOPTS="-c none"
|
||||||
|
DISKOPTS="-m sys /dev/sda"
|
|
@ -0,0 +1,19 @@
|
||||||
|
#!/bin/sh
|
||||||
|
|
||||||
|
set -ex
|
||||||
|
|
||||||
|
apk add libressl
|
||||||
|
apk add open-vm-tools
|
||||||
|
rc-update add open-vm-tools
|
||||||
|
/etc/init.d/open-vm-tools start
|
||||||
|
|
||||||
|
cat >/usr/local/bin/shutdown <<EOF
|
||||||
|
#!/bin/sh
|
||||||
|
poweroff
|
||||||
|
EOF
|
||||||
|
chmod +x /usr/local/bin/shutdown
|
||||||
|
|
||||||
|
sed -i "/#PermitRootLogin/c\PermitRootLogin yes" /etc/ssh/sshd_config
|
||||||
|
mkdir ~/.ssh
|
||||||
|
wget https://raw.githubusercontent.com/jetbrains-infra/packer-builder-vsphere/master/test/test-key.pub -O ~/.ssh/authorized_keys
|
||||||
|
/etc/init.d/sshd restart
|
|
@ -0,0 +1,18 @@
|
||||||
|
{
|
||||||
|
"builders": [
|
||||||
|
{
|
||||||
|
"type": "vsphere-clone",
|
||||||
|
|
||||||
|
"vcenter_server": "vcenter.vsphere65.test",
|
||||||
|
"username": "root",
|
||||||
|
"password": "jetbrains",
|
||||||
|
"insecure_connection": "true",
|
||||||
|
|
||||||
|
"template": "alpine",
|
||||||
|
"vm_name": "alpine-clone-{{timestamp}}",
|
||||||
|
"host": "esxi-1.vsphere65.test",
|
||||||
|
|
||||||
|
"communicator": "none"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
|
@ -0,0 +1,25 @@
|
||||||
|
package main
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
"github.com/hashicorp/packer/builder/vsphere/driver"
|
||||||
|
)
|
||||||
|
|
||||||
|
func main() {
|
||||||
|
d, err := driver.NewDriver(&driver.ConnectConfig{
|
||||||
|
VCenterServer: "vcenter.vsphere65.test",
|
||||||
|
Username: "root",
|
||||||
|
Password: "jetbrains",
|
||||||
|
InsecureConnection: true,
|
||||||
|
})
|
||||||
|
if err != nil {
|
||||||
|
panic(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
ds, err := d.FindDatastore("", "esxi-1.vsphere65.test")
|
||||||
|
if err != nil {
|
||||||
|
panic(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
fmt.Println(ds.Name())
|
||||||
|
}
|
|
@ -0,0 +1,51 @@
|
||||||
|
{
|
||||||
|
"builders": [
|
||||||
|
{
|
||||||
|
"type": "vsphere-iso",
|
||||||
|
|
||||||
|
"vcenter_server": "vcenter.vsphere65.test",
|
||||||
|
"username": "root",
|
||||||
|
"password": "jetbrains",
|
||||||
|
"insecure_connection": "true",
|
||||||
|
|
||||||
|
"vm_name": "macos-packer",
|
||||||
|
"host": "esxi-mac.vsphere65.test",
|
||||||
|
|
||||||
|
"guest_os_type": "darwin16_64Guest",
|
||||||
|
|
||||||
|
"CPUs": 1,
|
||||||
|
"RAM": 4096,
|
||||||
|
|
||||||
|
"disk_size": 32768,
|
||||||
|
"disk_thin_provisioned": true,
|
||||||
|
|
||||||
|
"network_card": "e1000e",
|
||||||
|
"usb_controller": true,
|
||||||
|
|
||||||
|
"configuration_parameters": {
|
||||||
|
"ich7m.present": "TRUE",
|
||||||
|
"smc.present": "TRUE"
|
||||||
|
},
|
||||||
|
"cdrom_type": "sata",
|
||||||
|
|
||||||
|
"iso_paths": [
|
||||||
|
"[datastore-mac] ISO/macOS 10.13.3.iso",
|
||||||
|
"[datastore-mac] ISO/VMware Tools/10.2.0/darwin.iso"
|
||||||
|
],
|
||||||
|
|
||||||
|
"iso_urls": ["{{template_dir}}/setup/out/setup.iso"],
|
||||||
|
"iso_checksum_type": "sha256",
|
||||||
|
"iso_checksum_url": "file:///{{template_dir}}/setup/out/sha256sums",
|
||||||
|
|
||||||
|
"boot_wait": "4m",
|
||||||
|
"boot_command": [
|
||||||
|
"<enter><wait5>",
|
||||||
|
"<leftCtrlOn><f2><leftCtrlOff>u<enter>t<enter><wait5>",
|
||||||
|
"/Volumes/setup/setup.sh<enter>"
|
||||||
|
],
|
||||||
|
|
||||||
|
"ssh_username": "jetbrains",
|
||||||
|
"ssh_password": "jetbrains"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
|
@ -0,0 +1 @@
|
||||||
|
out/
|
|
@ -0,0 +1,15 @@
|
||||||
|
#!/bin/sh
|
||||||
|
set -eux
|
||||||
|
|
||||||
|
# Based on
|
||||||
|
# https://gist.github.com/agentsim/00cc38c693e7d0e1b36a2080870d955b#gistcomment-2304505
|
||||||
|
|
||||||
|
mkdir -p out
|
||||||
|
|
||||||
|
hdiutil create -o out/HighSierra.cdr -size 5530m -layout SPUD -fs HFS+J
|
||||||
|
hdiutil attach out/HighSierra.cdr.dmg -noverify -mountpoint /Volumes/install_build
|
||||||
|
sudo /Applications/Install\ macOS\ High\ Sierra.app/Contents/Resources/createinstallmedia --volume /Volumes/install_build --nointeraction
|
||||||
|
hdiutil detach /Volumes/Install\ macOS\ High\ Sierra
|
||||||
|
hdiutil convert out/HighSierra.cdr.dmg -format UDTO -o out/HighSierra.iso
|
||||||
|
mv out/HighSierra.iso.cdr out/HighSierra.iso
|
||||||
|
rm out/HighSierra.cdr.dmg
|
|
@ -0,0 +1,27 @@
|
||||||
|
#!/bin/sh
|
||||||
|
set -eux
|
||||||
|
|
||||||
|
mkdir -p out/pkgroot
|
||||||
|
rm -rf /out/pkgroot/*
|
||||||
|
|
||||||
|
mkdir -p out/scripts
|
||||||
|
rm -rf /out/scripts/*
|
||||||
|
cp postinstall out/scripts/
|
||||||
|
|
||||||
|
pkgbuild \
|
||||||
|
--identifier io.packer.install \
|
||||||
|
--root out/pkgroot \
|
||||||
|
--scripts out/scripts \
|
||||||
|
out/postinstall.pkg
|
||||||
|
|
||||||
|
mkdir -p out/iso
|
||||||
|
rm -rf out/iso/*
|
||||||
|
cp setup.sh out/iso/
|
||||||
|
chmod +x out/iso/setup.sh
|
||||||
|
|
||||||
|
productbuild --package out/postinstall.pkg out/iso/postinstall.pkg
|
||||||
|
|
||||||
|
rm -f out/setup.iso
|
||||||
|
hdiutil makehybrid -iso -joliet -default-volume-name setup -o out/setup.iso out/iso
|
||||||
|
cd out
|
||||||
|
shasum -a 256 setup.iso >sha256sums
|
|
@ -0,0 +1,25 @@
|
||||||
|
#!/bin/sh
|
||||||
|
set -eux
|
||||||
|
# debug output in /var/log/install.log
|
||||||
|
|
||||||
|
# Create user account
|
||||||
|
USERNAME=jetbrains
|
||||||
|
PASSWORD=jetbrains
|
||||||
|
dscl . -create "/Users/${USERNAME}"
|
||||||
|
dscl . -create "/Users/${USERNAME}" UserShell /bin/bash
|
||||||
|
dscl . -create "/Users/${USERNAME}" RealName "${USERNAME}"
|
||||||
|
dscl . -create "/Users/${USERNAME}" UniqueID 510
|
||||||
|
dscl . -create "/Users/${USERNAME}" PrimaryGroupID 20
|
||||||
|
dscl . -create "/Users/${USERNAME}" NFSHomeDirectory "/Users/${USERNAME}"
|
||||||
|
dscl . -passwd "/Users/${USERNAME}" "${PASSWORD}"
|
||||||
|
dscl . -append /Groups/admin GroupMembership "${USERNAME}"
|
||||||
|
createhomedir -c
|
||||||
|
|
||||||
|
# Start VMware Tools daemon explicitly
|
||||||
|
launchctl load /Library/LaunchDaemons/com.vmware.launchd.tools.plist
|
||||||
|
|
||||||
|
# Enable SSH
|
||||||
|
systemsetup -setremotelogin on
|
||||||
|
|
||||||
|
# Disable the welcome screen
|
||||||
|
touch "$3/private/var/db/.AppleSetupDone"
|
|
@ -0,0 +1,13 @@
|
||||||
|
#!/bin/sh
|
||||||
|
set -eux
|
||||||
|
|
||||||
|
# Format partition
|
||||||
|
diskutil eraseDisk JHFS+ Disk disk0
|
||||||
|
|
||||||
|
# Packages are installed in reversed order - why?
|
||||||
|
"/Volumes/Image Volume/Install macOS High Sierra.app/Contents/Resources/startosinstall" \
|
||||||
|
--volume /Volumes/Disk \
|
||||||
|
--converttoapfs no \
|
||||||
|
--agreetolicense \
|
||||||
|
--installpackage "/Volumes/setup/postinstall.pkg" \
|
||||||
|
--installpackage "/Volumes/VMware Tools/Install VMware Tools.app/Contents/Resources/VMware Tools.pkg"
|
|
@ -0,0 +1,16 @@
|
||||||
|
d-i passwd/user-fullname string jetbrains
|
||||||
|
d-i passwd/username string jetbrains
|
||||||
|
d-i passwd/user-password password jetbrains
|
||||||
|
d-i passwd/user-password-again password jetbrains
|
||||||
|
d-i user-setup/allow-password-weak boolean true
|
||||||
|
|
||||||
|
d-i partman-auto/disk string /dev/sda
|
||||||
|
d-i partman-auto/method string regular
|
||||||
|
d-i partman-partitioning/confirm_write_new_label boolean true
|
||||||
|
d-i partman/choose_partition select finish
|
||||||
|
d-i partman/confirm boolean true
|
||||||
|
d-i partman/confirm_nooverwrite boolean true
|
||||||
|
|
||||||
|
d-i pkgsel/include string open-vm-tools openssh-server
|
||||||
|
|
||||||
|
d-i finish-install/reboot_in_progress note
|
|
@ -0,0 +1,62 @@
|
||||||
|
{
|
||||||
|
"builders": [
|
||||||
|
{
|
||||||
|
"type": "vsphere-iso",
|
||||||
|
|
||||||
|
"vcenter_server": "vcenter.vsphere65.test",
|
||||||
|
"username": "root",
|
||||||
|
"password": "jetbrains",
|
||||||
|
"insecure_connection": "true",
|
||||||
|
|
||||||
|
"vm_name": "example-ubuntu",
|
||||||
|
"host": "esxi-1.vsphere65.test",
|
||||||
|
|
||||||
|
"guest_os_type": "ubuntu64Guest",
|
||||||
|
|
||||||
|
"ssh_username": "jetbrains",
|
||||||
|
"ssh_password": "jetbrains",
|
||||||
|
|
||||||
|
"CPUs": 1,
|
||||||
|
"RAM": 1024,
|
||||||
|
"RAM_reserve_all": true,
|
||||||
|
|
||||||
|
"disk_controller_type": "pvscsi",
|
||||||
|
"disk_size": 32768,
|
||||||
|
"disk_thin_provisioned": true,
|
||||||
|
|
||||||
|
"network_card": "vmxnet3",
|
||||||
|
|
||||||
|
"iso_paths": [
|
||||||
|
"[datastore1] ISO/ubuntu-16.04.3-server-amd64.iso"
|
||||||
|
],
|
||||||
|
"floppy_files": [
|
||||||
|
"{{template_dir}}/preseed.cfg"
|
||||||
|
],
|
||||||
|
"boot_command": [
|
||||||
|
"<enter><wait><f6><wait><esc><wait>",
|
||||||
|
"<bs><bs><bs><bs><bs><bs><bs><bs><bs><bs>",
|
||||||
|
"<bs><bs><bs><bs><bs><bs><bs><bs><bs><bs>",
|
||||||
|
"<bs><bs><bs><bs><bs><bs><bs><bs><bs><bs>",
|
||||||
|
"<bs><bs><bs><bs><bs><bs><bs><bs><bs><bs>",
|
||||||
|
"<bs><bs><bs><bs><bs><bs><bs><bs><bs><bs>",
|
||||||
|
"<bs><bs><bs><bs><bs><bs><bs><bs><bs><bs>",
|
||||||
|
"<bs><bs><bs><bs><bs><bs><bs><bs><bs><bs>",
|
||||||
|
"<bs><bs><bs><bs><bs><bs><bs><bs><bs><bs>",
|
||||||
|
"<bs><bs><bs>",
|
||||||
|
"/install/vmlinuz",
|
||||||
|
" initrd=/install/initrd.gz",
|
||||||
|
" priority=critical",
|
||||||
|
" locale=en_US",
|
||||||
|
" file=/media/preseed.cfg",
|
||||||
|
"<enter>"
|
||||||
|
]
|
||||||
|
}
|
||||||
|
],
|
||||||
|
|
||||||
|
"provisioners": [
|
||||||
|
{
|
||||||
|
"type": "shell",
|
||||||
|
"inline": ["ls /"]
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
|
@ -0,0 +1,2 @@
|
||||||
|
*.cmd text eol=crlf
|
||||||
|
*.ps1 text eol=crlf
|
|
@ -0,0 +1,122 @@
|
||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<unattend xmlns="urn:schemas-microsoft-com:unattend">
|
||||||
|
<settings pass="windowsPE">
|
||||||
|
<component name="Microsoft-Windows-International-Core-WinPE" processorArchitecture="amd64" publicKeyToken="31bf3856ad364e35" language="neutral" versionScope="nonSxS" xmlns:wcm="http://schemas.microsoft.com/WMIConfig/2002/State" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
|
||||||
|
<UILanguage>en-US</UILanguage>
|
||||||
|
</component>
|
||||||
|
|
||||||
|
<component name="Microsoft-Windows-PnpCustomizationsWinPE" processorArchitecture="amd64" publicKeyToken="31bf3856ad364e35" language="neutral" versionScope="nonSxS" xmlns:wcm="http://schemas.microsoft.com/WMIConfig/2002/State" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
|
||||||
|
<DriverPaths>
|
||||||
|
<PathAndCredentials wcm:action="add" wcm:keyValue="A">
|
||||||
|
<!-- pvscsi-Windows8.flp -->
|
||||||
|
<Path>B:\</Path>
|
||||||
|
</PathAndCredentials>
|
||||||
|
</DriverPaths>
|
||||||
|
</component>
|
||||||
|
|
||||||
|
<component name="Microsoft-Windows-Setup" processorArchitecture="amd64" publicKeyToken="31bf3856ad364e35" language="neutral" versionScope="nonSxS" xmlns:wcm="http://schemas.microsoft.com/WMIConfig/2002/State" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
|
||||||
|
<UserData>
|
||||||
|
<AcceptEula>true</AcceptEula>
|
||||||
|
|
||||||
|
<!-- Retail image requires a key
|
||||||
|
<ProductKey>
|
||||||
|
<Key>XXXXX-XXXXX-XXXXX-XXXXX-XXXXX</Key>
|
||||||
|
</ProductKey>
|
||||||
|
-->
|
||||||
|
</UserData>
|
||||||
|
|
||||||
|
<ImageInstall>
|
||||||
|
<OSImage>
|
||||||
|
<InstallFrom>
|
||||||
|
<MetaData wcm:action="add">
|
||||||
|
<Key>/IMAGE/NAME</Key>
|
||||||
|
<Value>Windows 10 Pro</Value>
|
||||||
|
</MetaData>
|
||||||
|
</InstallFrom>
|
||||||
|
<InstallToAvailablePartition>true</InstallToAvailablePartition>
|
||||||
|
</OSImage>
|
||||||
|
</ImageInstall>
|
||||||
|
|
||||||
|
<DiskConfiguration>
|
||||||
|
<Disk wcm:action="add">
|
||||||
|
<DiskID>0</DiskID>
|
||||||
|
<CreatePartitions>
|
||||||
|
<CreatePartition wcm:action="add">
|
||||||
|
<Order>1</Order>
|
||||||
|
<Extend>true</Extend>
|
||||||
|
<Type>Primary</Type>
|
||||||
|
</CreatePartition>
|
||||||
|
</CreatePartitions>
|
||||||
|
</Disk>
|
||||||
|
</DiskConfiguration>
|
||||||
|
</component>
|
||||||
|
</settings>
|
||||||
|
|
||||||
|
<settings pass="offlineServicing">
|
||||||
|
<component name="Microsoft-Windows-LUA-Settings" processorArchitecture="amd64" publicKeyToken="31bf3856ad364e35" language="neutral" versionScope="nonSxS" xmlns:wcm="http://schemas.microsoft.com/WMIConfig/2002/State" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
|
||||||
|
<!-- Disable user account control -->
|
||||||
|
<EnableLUA>false</EnableLUA>
|
||||||
|
</component>
|
||||||
|
</settings>
|
||||||
|
|
||||||
|
<settings pass="specialize">
|
||||||
|
<component name="Microsoft-Windows-Deployment" processorArchitecture="amd64" publicKeyToken="31bf3856ad364e35" language="neutral" versionScope="nonSxS" xmlns:wcm="http://schemas.microsoft.com/WMIConfig/2002/State" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
|
||||||
|
<RunSynchronous>
|
||||||
|
<RunSynchronousCommand wcm:action="add">
|
||||||
|
<Order>1</Order>
|
||||||
|
<!-- Install VMware Tools from windows.iso -->
|
||||||
|
<Path>a:\vmtools.cmd</Path>
|
||||||
|
<WillReboot>Always</WillReboot>
|
||||||
|
</RunSynchronousCommand>
|
||||||
|
</RunSynchronous>
|
||||||
|
</component>
|
||||||
|
</settings>
|
||||||
|
|
||||||
|
<settings pass="oobeSystem">
|
||||||
|
<component name="Microsoft-Windows-International-Core" processorArchitecture="amd64" publicKeyToken="31bf3856ad364e35" language="neutral" versionScope="nonSxS" xmlns:wcm="http://schemas.microsoft.com/WMIConfig/2002/State" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
|
||||||
|
<SystemLocale>en-US</SystemLocale>
|
||||||
|
</component>
|
||||||
|
|
||||||
|
<component name="Microsoft-Windows-Shell-Setup" processorArchitecture="amd64" publicKeyToken="31bf3856ad364e35" language="neutral" versionScope="nonSxS" xmlns:wcm="http://schemas.microsoft.com/WMIConfig/2002/State" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
|
||||||
|
<OOBE>
|
||||||
|
<!-- Privacy settings -->
|
||||||
|
<ProtectYourPC>3</ProtectYourPC>
|
||||||
|
</OOBE>
|
||||||
|
|
||||||
|
<!--
|
||||||
|
<TimeZone>Russian Standard Time</TimeZone>
|
||||||
|
-->
|
||||||
|
|
||||||
|
<UserAccounts>
|
||||||
|
<LocalAccounts>
|
||||||
|
<LocalAccount wcm:action="add">
|
||||||
|
<Name>jetbrains</Name>
|
||||||
|
<Password>
|
||||||
|
<Value>jetbrains</Value>
|
||||||
|
<PlainText>true</PlainText>
|
||||||
|
</Password>
|
||||||
|
<Group>Administrators</Group>
|
||||||
|
</LocalAccount>
|
||||||
|
</LocalAccounts>
|
||||||
|
</UserAccounts>
|
||||||
|
|
||||||
|
<AutoLogon>
|
||||||
|
<Enabled>true</Enabled>
|
||||||
|
<Username>jetbrains</Username>
|
||||||
|
<Password>
|
||||||
|
<Value>jetbrains</Value>
|
||||||
|
<PlainText>true</PlainText>
|
||||||
|
</Password>
|
||||||
|
<LogonCount>1</LogonCount>
|
||||||
|
</AutoLogon>
|
||||||
|
<FirstLogonCommands>
|
||||||
|
<SynchronousCommand wcm:action="add">
|
||||||
|
<Order>1</Order>
|
||||||
|
<!-- Enable WinRM service -->
|
||||||
|
<CommandLine>powershell -ExecutionPolicy Bypass -File a:\setup.ps1</CommandLine>
|
||||||
|
<RequiresUserInput>true</RequiresUserInput>
|
||||||
|
</SynchronousCommand>
|
||||||
|
</FirstLogonCommands>
|
||||||
|
</component>
|
||||||
|
</settings>
|
||||||
|
</unattend>
|
|
@ -0,0 +1,15 @@
|
||||||
|
$ErrorActionPreference = "Stop"
|
||||||
|
|
||||||
|
# Switch network connection to private mode
|
||||||
|
# Required for WinRM firewall rules
|
||||||
|
$profile = Get-NetConnectionProfile
|
||||||
|
Set-NetConnectionProfile -Name $profile.Name -NetworkCategory Private
|
||||||
|
|
||||||
|
# Enable WinRM service
|
||||||
|
winrm quickconfig -quiet
|
||||||
|
winrm set winrm/config/service '@{AllowUnencrypted="true"}'
|
||||||
|
winrm set winrm/config/service/auth '@{Basic="true"}'
|
||||||
|
|
||||||
|
# Reset auto logon count
|
||||||
|
# https://docs.microsoft.com/en-us/windows-hardware/customize/desktop/unattend/microsoft-windows-shell-setup-autologon-logoncount#logoncount-known-issue
|
||||||
|
Set-ItemProperty -Path 'HKLM:\SOFTWARE\Microsoft\Windows NT\CurrentVersion\Winlogon' -Name AutoLogonCount -Value 0
|
|
@ -0,0 +1,2 @@
|
||||||
|
@rem Silent mode, basic UI, no reboot
|
||||||
|
e:\setup64 /s /v "/qb REBOOT=R"
|
|
@ -0,0 +1,48 @@
|
||||||
|
{
|
||||||
|
"builders": [
|
||||||
|
{
|
||||||
|
"type": "vsphere-iso",
|
||||||
|
|
||||||
|
"vcenter_server": "vcenter.vsphere65.test",
|
||||||
|
"username": "root",
|
||||||
|
"password": "jetbrains",
|
||||||
|
"insecure_connection": "true",
|
||||||
|
|
||||||
|
"vm_name": "example-windows",
|
||||||
|
"host": "esxi-1.vsphere65.test",
|
||||||
|
|
||||||
|
"guest_os_type": "windows9_64Guest",
|
||||||
|
|
||||||
|
"communicator": "winrm",
|
||||||
|
"winrm_username": "jetbrains",
|
||||||
|
"winrm_password": "jetbrains",
|
||||||
|
|
||||||
|
"CPUs": 1,
|
||||||
|
"RAM": 4096,
|
||||||
|
"RAM_reserve_all": true,
|
||||||
|
|
||||||
|
"disk_controller_type": "pvscsi",
|
||||||
|
"disk_size": 32768,
|
||||||
|
"disk_thin_provisioned": true,
|
||||||
|
|
||||||
|
"network_card": "vmxnet3",
|
||||||
|
|
||||||
|
"iso_paths": [
|
||||||
|
"[datastore1] ISO/en_windows_10_multi-edition_vl_version_1709_updated_dec_2017_x64_dvd_100406172.iso",
|
||||||
|
"[datastore1] ISO/VMware Tools/10.2.0/windows.iso"
|
||||||
|
],
|
||||||
|
|
||||||
|
"floppy_files": [
|
||||||
|
"{{template_dir}}/setup/"
|
||||||
|
],
|
||||||
|
"floppy_img_path": "[datastore1] ISO/VMware Tools/10.2.0/pvscsi-Windows8.flp"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
|
||||||
|
"provisioners": [
|
||||||
|
{
|
||||||
|
"type": "windows-shell",
|
||||||
|
"inline": ["dir c:\\"]
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
|
@ -0,0 +1,148 @@
|
||||||
|
package iso
|
||||||
|
|
||||||
|
import (
|
||||||
|
"context"
|
||||||
|
|
||||||
|
"github.com/hashicorp/hcl/v2/hcldec"
|
||||||
|
"github.com/hashicorp/packer/builder/vsphere/common"
|
||||||
|
"github.com/hashicorp/packer/builder/vsphere/driver"
|
||||||
|
packerCommon "github.com/hashicorp/packer/common"
|
||||||
|
"github.com/hashicorp/packer/helper/communicator"
|
||||||
|
"github.com/hashicorp/packer/helper/multistep"
|
||||||
|
"github.com/hashicorp/packer/packer"
|
||||||
|
)
|
||||||
|
|
||||||
|
type Builder struct {
|
||||||
|
config Config
|
||||||
|
runner multistep.Runner
|
||||||
|
}
|
||||||
|
|
||||||
|
func (b *Builder) ConfigSpec() hcldec.ObjectSpec { return b.config.FlatMapstructure().HCL2Spec() }
|
||||||
|
|
||||||
|
func (b *Builder) Prepare(raws ...interface{}) ([]string, []string, error) {
|
||||||
|
warnings, errs := b.config.Prepare(raws...)
|
||||||
|
if errs != nil {
|
||||||
|
return nil, warnings, errs
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil, warnings, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (b *Builder) Run(ctx context.Context, ui packer.Ui, hook packer.Hook) (packer.Artifact, error) {
|
||||||
|
state := new(multistep.BasicStateBag)
|
||||||
|
state.Put("hook", hook)
|
||||||
|
state.Put("ui", ui)
|
||||||
|
|
||||||
|
var steps []multistep.Step
|
||||||
|
|
||||||
|
steps = append(steps,
|
||||||
|
&common.StepConnect{
|
||||||
|
Config: &b.config.ConnectConfig,
|
||||||
|
},
|
||||||
|
)
|
||||||
|
|
||||||
|
if b.config.ISOUrls != nil {
|
||||||
|
steps = append(steps,
|
||||||
|
&packerCommon.StepDownload{
|
||||||
|
Checksum: b.config.ISOChecksum,
|
||||||
|
ChecksumType: b.config.ISOChecksumType,
|
||||||
|
Description: "ISO",
|
||||||
|
Extension: b.config.TargetExtension,
|
||||||
|
ResultKey: "iso_path",
|
||||||
|
TargetPath: b.config.TargetPath,
|
||||||
|
Url: b.config.ISOUrls,
|
||||||
|
},
|
||||||
|
&StepRemoteUpload{
|
||||||
|
Datastore: b.config.Datastore,
|
||||||
|
Host: b.config.Host,
|
||||||
|
},
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
steps = append(steps,
|
||||||
|
&StepCreateVM{
|
||||||
|
Config: &b.config.CreateConfig,
|
||||||
|
Location: &b.config.LocationConfig,
|
||||||
|
Force: b.config.PackerConfig.PackerForce,
|
||||||
|
},
|
||||||
|
&common.StepConfigureHardware{
|
||||||
|
Config: &b.config.HardwareConfig,
|
||||||
|
},
|
||||||
|
&StepAddCDRom{
|
||||||
|
Config: &b.config.CDRomConfig,
|
||||||
|
},
|
||||||
|
&common.StepConfigParams{
|
||||||
|
Config: &b.config.ConfigParamsConfig,
|
||||||
|
},
|
||||||
|
)
|
||||||
|
|
||||||
|
if b.config.Comm.Type != "none" {
|
||||||
|
steps = append(steps,
|
||||||
|
&packerCommon.StepCreateFloppy{
|
||||||
|
Files: b.config.FloppyFiles,
|
||||||
|
Directories: b.config.FloppyDirectories,
|
||||||
|
},
|
||||||
|
&StepAddFloppy{
|
||||||
|
Config: &b.config.FloppyConfig,
|
||||||
|
Datastore: b.config.Datastore,
|
||||||
|
Host: b.config.Host,
|
||||||
|
},
|
||||||
|
&packerCommon.StepHTTPServer{
|
||||||
|
HTTPDir: b.config.HTTPDir,
|
||||||
|
HTTPPortMin: b.config.HTTPPortMin,
|
||||||
|
HTTPPortMax: b.config.HTTPPortMax,
|
||||||
|
},
|
||||||
|
&common.StepRun{
|
||||||
|
Config: &b.config.RunConfig,
|
||||||
|
SetOrder: true,
|
||||||
|
},
|
||||||
|
&StepBootCommand{
|
||||||
|
Config: &b.config.BootConfig,
|
||||||
|
Ctx: b.config.ctx,
|
||||||
|
VMName: b.config.VMName,
|
||||||
|
},
|
||||||
|
&common.StepWaitForIp{
|
||||||
|
Config: &b.config.WaitIpConfig,
|
||||||
|
},
|
||||||
|
&communicator.StepConnect{
|
||||||
|
Config: &b.config.Comm,
|
||||||
|
Host: common.CommHost(b.config.Comm.SSHHost),
|
||||||
|
SSHConfig: b.config.Comm.SSHConfigFunc(),
|
||||||
|
},
|
||||||
|
&packerCommon.StepProvision{},
|
||||||
|
&common.StepShutdown{
|
||||||
|
Config: &b.config.ShutdownConfig,
|
||||||
|
},
|
||||||
|
&StepRemoveFloppy{
|
||||||
|
Datastore: b.config.Datastore,
|
||||||
|
Host: b.config.Host,
|
||||||
|
},
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
steps = append(steps,
|
||||||
|
&StepRemoveCDRom{},
|
||||||
|
&common.StepCreateSnapshot{
|
||||||
|
CreateSnapshot: b.config.CreateSnapshot,
|
||||||
|
},
|
||||||
|
&common.StepConvertToTemplate{
|
||||||
|
ConvertToTemplate: b.config.ConvertToTemplate,
|
||||||
|
},
|
||||||
|
)
|
||||||
|
|
||||||
|
b.runner = packerCommon.NewRunner(steps, b.config.PackerConfig, ui)
|
||||||
|
b.runner.Run(ctx, state)
|
||||||
|
|
||||||
|
if rawErr, ok := state.GetOk("error"); ok {
|
||||||
|
return nil, rawErr.(error)
|
||||||
|
}
|
||||||
|
|
||||||
|
if _, ok := state.GetOk("vm"); !ok {
|
||||||
|
return nil, nil
|
||||||
|
}
|
||||||
|
artifact := &common.Artifact{
|
||||||
|
Name: b.config.VMName,
|
||||||
|
VM: state.Get("vm").(*driver.VirtualMachine),
|
||||||
|
}
|
||||||
|
return artifact, nil
|
||||||
|
}
|
|
@ -0,0 +1,555 @@
|
||||||
|
package iso
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
commonT "github.com/hashicorp/packer/builder/vsphere/common/testing"
|
||||||
|
builderT "github.com/hashicorp/packer/helper/builder/testing"
|
||||||
|
"github.com/hashicorp/packer/packer"
|
||||||
|
"github.com/vmware/govmomi/vim25/types"
|
||||||
|
"io/ioutil"
|
||||||
|
"os"
|
||||||
|
"testing"
|
||||||
|
)
|
||||||
|
|
||||||
|
func TestISOBuilderAcc_default(t *testing.T) {
|
||||||
|
config := defaultConfig()
|
||||||
|
builderT.Test(t, builderT.TestCase{
|
||||||
|
Builder: &Builder{},
|
||||||
|
Template: commonT.RenderConfig(config),
|
||||||
|
Check: checkDefault(t, config["vm_name"].(string), config["host"].(string), "datastore1"),
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
func defaultConfig() map[string]interface{} {
|
||||||
|
username := os.Getenv("VSPHERE_USERNAME")
|
||||||
|
if username == "" {
|
||||||
|
username = "root"
|
||||||
|
}
|
||||||
|
password := os.Getenv("VSPHERE_PASSWORD")
|
||||||
|
if password == "" {
|
||||||
|
password = "jetbrains"
|
||||||
|
}
|
||||||
|
|
||||||
|
config := map[string]interface{}{
|
||||||
|
"vcenter_server": "vcenter.vsphere65.test",
|
||||||
|
"username": username,
|
||||||
|
"password": password,
|
||||||
|
"insecure_connection": true,
|
||||||
|
|
||||||
|
"host": "esxi-1.vsphere65.test",
|
||||||
|
|
||||||
|
"ssh_username": "root",
|
||||||
|
"ssh_password": "jetbrains",
|
||||||
|
|
||||||
|
"vm_name": commonT.NewVMName(),
|
||||||
|
"disk_size": 2048,
|
||||||
|
|
||||||
|
"communicator": "none", // do not start the VM without any bootable devices
|
||||||
|
}
|
||||||
|
|
||||||
|
return config
|
||||||
|
}
|
||||||
|
|
||||||
|
func checkDefault(t *testing.T, name string, host string, datastore string) builderT.TestCheckFunc {
|
||||||
|
return func(artifacts []packer.Artifact) error {
|
||||||
|
d := commonT.TestConn(t)
|
||||||
|
vm := commonT.GetVM(t, d, artifacts)
|
||||||
|
|
||||||
|
vmInfo, err := vm.Info("name", "parent", "runtime.host", "resourcePool", "datastore", "layoutEx.disk", "config.firmware")
|
||||||
|
if err != nil {
|
||||||
|
t.Fatalf("Cannot read VM properties: %v", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
if vmInfo.Name != name {
|
||||||
|
t.Errorf("Invalid VM name: expected '%v', got '%v'", name, vmInfo.Name)
|
||||||
|
}
|
||||||
|
|
||||||
|
f := d.NewFolder(vmInfo.Parent)
|
||||||
|
folderPath, err := f.Path()
|
||||||
|
if err != nil {
|
||||||
|
t.Fatalf("Cannot read folder name: %v", err)
|
||||||
|
}
|
||||||
|
if folderPath != "" {
|
||||||
|
t.Errorf("Invalid folder: expected '/', got '%v'", folderPath)
|
||||||
|
}
|
||||||
|
|
||||||
|
h := d.NewHost(vmInfo.Runtime.Host)
|
||||||
|
hostInfo, err := h.Info("name")
|
||||||
|
if err != nil {
|
||||||
|
t.Fatal("Cannot read host properties: ", err)
|
||||||
|
}
|
||||||
|
if hostInfo.Name != host {
|
||||||
|
t.Errorf("Invalid host name: expected '%v', got '%v'", host, hostInfo.Name)
|
||||||
|
}
|
||||||
|
|
||||||
|
p := d.NewResourcePool(vmInfo.ResourcePool)
|
||||||
|
poolPath, err := p.Path()
|
||||||
|
if err != nil {
|
||||||
|
t.Fatalf("Cannot read resource pool name: %v", err)
|
||||||
|
}
|
||||||
|
if poolPath != "" {
|
||||||
|
t.Errorf("Invalid resource pool: expected '/', got '%v'", poolPath)
|
||||||
|
}
|
||||||
|
|
||||||
|
dsr := vmInfo.Datastore[0].Reference()
|
||||||
|
ds := d.NewDatastore(&dsr)
|
||||||
|
dsInfo, err := ds.Info("name")
|
||||||
|
if err != nil {
|
||||||
|
t.Fatal("Cannot read datastore properties: ", err)
|
||||||
|
}
|
||||||
|
if dsInfo.Name != datastore {
|
||||||
|
t.Errorf("Invalid datastore name: expected '%v', got '%v'", datastore, dsInfo.Name)
|
||||||
|
}
|
||||||
|
|
||||||
|
fw := vmInfo.Config.Firmware
|
||||||
|
if fw != "bios" {
|
||||||
|
t.Errorf("Invalid firmware: expected 'bios', got '%v'", fw)
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestISOBuilderAcc_notes(t *testing.T) {
|
||||||
|
builderT.Test(t, builderT.TestCase{
|
||||||
|
Builder: &Builder{},
|
||||||
|
Template: notesConfig(),
|
||||||
|
Check: checkNotes(t),
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
func notesConfig() string {
|
||||||
|
config := defaultConfig()
|
||||||
|
config["notes"] = "test"
|
||||||
|
|
||||||
|
return commonT.RenderConfig(config)
|
||||||
|
}
|
||||||
|
|
||||||
|
func checkNotes(t *testing.T) builderT.TestCheckFunc {
|
||||||
|
return func(artifacts []packer.Artifact) error {
|
||||||
|
d := commonT.TestConn(t)
|
||||||
|
vm := commonT.GetVM(t, d, artifacts)
|
||||||
|
|
||||||
|
vmInfo, err := vm.Info("config.annotation")
|
||||||
|
if err != nil {
|
||||||
|
t.Fatalf("Cannot read VM properties: %v", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
notes := vmInfo.Config.Annotation
|
||||||
|
if notes != "test" {
|
||||||
|
t.Errorf("notes should be 'test'")
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestISOBuilderAcc_hardware(t *testing.T) {
|
||||||
|
builderT.Test(t, builderT.TestCase{
|
||||||
|
Builder: &Builder{},
|
||||||
|
Template: hardwareConfig(),
|
||||||
|
Check: checkHardware(t),
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
func hardwareConfig() string {
|
||||||
|
config := defaultConfig()
|
||||||
|
config["CPUs"] = 2
|
||||||
|
config["cpu_cores"] = 2
|
||||||
|
config["CPU_reservation"] = 1000
|
||||||
|
config["CPU_limit"] = 1500
|
||||||
|
config["RAM"] = 2048
|
||||||
|
config["RAM_reservation"] = 1024
|
||||||
|
config["NestedHV"] = true
|
||||||
|
config["firmware"] = "efi"
|
||||||
|
config["video_ram"] = 8192
|
||||||
|
|
||||||
|
return commonT.RenderConfig(config)
|
||||||
|
}
|
||||||
|
|
||||||
|
func checkHardware(t *testing.T) builderT.TestCheckFunc {
|
||||||
|
return func(artifacts []packer.Artifact) error {
|
||||||
|
d := commonT.TestConn(t)
|
||||||
|
|
||||||
|
vm := commonT.GetVM(t, d, artifacts)
|
||||||
|
vmInfo, err := vm.Info("config")
|
||||||
|
if err != nil {
|
||||||
|
t.Fatalf("Cannot read VM properties: %v", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
cpuSockets := vmInfo.Config.Hardware.NumCPU
|
||||||
|
if cpuSockets != 2 {
|
||||||
|
t.Errorf("VM should have 2 CPU sockets, got %v", cpuSockets)
|
||||||
|
}
|
||||||
|
|
||||||
|
cpuCores := vmInfo.Config.Hardware.NumCoresPerSocket
|
||||||
|
if cpuCores != 2 {
|
||||||
|
t.Errorf("VM should have 2 CPU cores per socket, got %v", cpuCores)
|
||||||
|
}
|
||||||
|
|
||||||
|
cpuReservation := *vmInfo.Config.CpuAllocation.Reservation
|
||||||
|
if cpuReservation != 1000 {
|
||||||
|
t.Errorf("VM should have CPU reservation for 1000 Mhz, got %v", cpuReservation)
|
||||||
|
}
|
||||||
|
|
||||||
|
cpuLimit := *vmInfo.Config.CpuAllocation.Limit
|
||||||
|
if cpuLimit != 1500 {
|
||||||
|
t.Errorf("VM should have CPU reservation for 1500 Mhz, got %v", cpuLimit)
|
||||||
|
}
|
||||||
|
|
||||||
|
ram := vmInfo.Config.Hardware.MemoryMB
|
||||||
|
if ram != 2048 {
|
||||||
|
t.Errorf("VM should have 2048 MB of RAM, got %v", ram)
|
||||||
|
}
|
||||||
|
|
||||||
|
ramReservation := *vmInfo.Config.MemoryAllocation.Reservation
|
||||||
|
if ramReservation != 1024 {
|
||||||
|
t.Errorf("VM should have RAM reservation for 1024 MB, got %v", ramReservation)
|
||||||
|
}
|
||||||
|
|
||||||
|
nestedHV := vmInfo.Config.NestedHVEnabled
|
||||||
|
if !*nestedHV {
|
||||||
|
t.Errorf("VM should have NestedHV enabled, got %v", nestedHV)
|
||||||
|
}
|
||||||
|
|
||||||
|
fw := vmInfo.Config.Firmware
|
||||||
|
if fw != "efi" {
|
||||||
|
t.Errorf("Invalid firmware: expected 'efi', got '%v'", fw)
|
||||||
|
}
|
||||||
|
|
||||||
|
l, err := vm.Devices()
|
||||||
|
if err != nil {
|
||||||
|
t.Fatalf("Cannot read VM devices: %v", err)
|
||||||
|
}
|
||||||
|
c := l.PickController((*types.VirtualIDEController)(nil))
|
||||||
|
if c == nil {
|
||||||
|
t.Errorf("VM should have IDE controller")
|
||||||
|
}
|
||||||
|
s := l.PickController((*types.VirtualAHCIController)(nil))
|
||||||
|
if s != nil {
|
||||||
|
t.Errorf("VM should have no SATA controllers")
|
||||||
|
}
|
||||||
|
|
||||||
|
v := l.SelectByType((*types.VirtualMachineVideoCard)(nil))
|
||||||
|
if len(v) != 1 {
|
||||||
|
t.Errorf("VM should have one video card")
|
||||||
|
}
|
||||||
|
if v[0].(*types.VirtualMachineVideoCard).VideoRamSizeInKB != 8192 {
|
||||||
|
t.Errorf("Video RAM should be equal 8192")
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestISOBuilderAcc_limit(t *testing.T) {
|
||||||
|
builderT.Test(t, builderT.TestCase{
|
||||||
|
Builder: &Builder{},
|
||||||
|
Template: limitConfig(),
|
||||||
|
Check: checkLimit(t),
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
func limitConfig() string {
|
||||||
|
config := defaultConfig()
|
||||||
|
config["CPUs"] = 1 // hardware is customized, but CPU limit is not specified explicitly
|
||||||
|
|
||||||
|
return commonT.RenderConfig(config)
|
||||||
|
}
|
||||||
|
|
||||||
|
func checkLimit(t *testing.T) builderT.TestCheckFunc {
|
||||||
|
return func(artifacts []packer.Artifact) error {
|
||||||
|
d := commonT.TestConn(t)
|
||||||
|
|
||||||
|
vm := commonT.GetVM(t, d, artifacts)
|
||||||
|
vmInfo, err := vm.Info("config.cpuAllocation")
|
||||||
|
if err != nil {
|
||||||
|
t.Fatalf("Cannot read VM properties: %v", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
limit := *vmInfo.Config.CpuAllocation.Limit
|
||||||
|
if limit != -1 { // must be unlimited
|
||||||
|
t.Errorf("Invalid CPU limit: expected '%v', got '%v'", -1, limit)
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestISOBuilderAcc_sata(t *testing.T) {
|
||||||
|
builderT.Test(t, builderT.TestCase{
|
||||||
|
Builder: &Builder{},
|
||||||
|
Template: sataConfig(),
|
||||||
|
Check: checkSata(t),
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
func sataConfig() string {
|
||||||
|
config := defaultConfig()
|
||||||
|
config["cdrom_type"] = "sata"
|
||||||
|
|
||||||
|
return commonT.RenderConfig(config)
|
||||||
|
}
|
||||||
|
|
||||||
|
func checkSata(t *testing.T) builderT.TestCheckFunc {
|
||||||
|
return func(artifacts []packer.Artifact) error {
|
||||||
|
d := commonT.TestConn(t)
|
||||||
|
|
||||||
|
vm := commonT.GetVM(t, d, artifacts)
|
||||||
|
|
||||||
|
l, err := vm.Devices()
|
||||||
|
if err != nil {
|
||||||
|
t.Fatalf("Cannot read VM devices: %v", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
c := l.PickController((*types.VirtualAHCIController)(nil))
|
||||||
|
if c == nil {
|
||||||
|
t.Errorf("VM has no SATA controllers")
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestISOBuilderAcc_cdrom(t *testing.T) {
|
||||||
|
builderT.Test(t, builderT.TestCase{
|
||||||
|
Builder: &Builder{},
|
||||||
|
Template: cdromConfig(),
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
func cdromConfig() string {
|
||||||
|
config := defaultConfig()
|
||||||
|
config["iso_paths"] = []string{
|
||||||
|
"[datastore1] test0.iso",
|
||||||
|
"[datastore1] test1.iso",
|
||||||
|
}
|
||||||
|
return commonT.RenderConfig(config)
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestISOBuilderAcc_networkCard(t *testing.T) {
|
||||||
|
builderT.Test(t, builderT.TestCase{
|
||||||
|
Builder: &Builder{},
|
||||||
|
Template: networkCardConfig(),
|
||||||
|
Check: checkNetworkCard(t),
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
func networkCardConfig() string {
|
||||||
|
config := defaultConfig()
|
||||||
|
config["network_card"] = "vmxnet3"
|
||||||
|
return commonT.RenderConfig(config)
|
||||||
|
}
|
||||||
|
|
||||||
|
func checkNetworkCard(t *testing.T) builderT.TestCheckFunc {
|
||||||
|
return func(artifacts []packer.Artifact) error {
|
||||||
|
d := commonT.TestConn(t)
|
||||||
|
|
||||||
|
vm := commonT.GetVM(t, d, artifacts)
|
||||||
|
devices, err := vm.Devices()
|
||||||
|
if err != nil {
|
||||||
|
t.Fatalf("Cannot read VM properties: %v", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
netCards := devices.SelectByType((*types.VirtualEthernetCard)(nil))
|
||||||
|
if len(netCards) == 0 {
|
||||||
|
t.Fatalf("Cannot find the network card")
|
||||||
|
}
|
||||||
|
if len(netCards) > 1 {
|
||||||
|
t.Fatalf("Found several network catds")
|
||||||
|
}
|
||||||
|
if _, ok := netCards[0].(*types.VirtualVmxnet3); !ok {
|
||||||
|
t.Errorf("The network card type is not the expected one (vmxnet3)")
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestISOBuilderAcc_createFloppy(t *testing.T) {
|
||||||
|
tmpFile, err := ioutil.TempFile("", "packer-vsphere-iso-test")
|
||||||
|
if err != nil {
|
||||||
|
t.Fatalf("Error creating temp file: %v", err)
|
||||||
|
}
|
||||||
|
_, err = fmt.Fprint(tmpFile, "Hello, World!")
|
||||||
|
if err != nil {
|
||||||
|
t.Fatalf("Error creating temp file: %v", err)
|
||||||
|
}
|
||||||
|
err = tmpFile.Close()
|
||||||
|
if err != nil {
|
||||||
|
t.Fatalf("Error creating temp file: %v", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
builderT.Test(t, builderT.TestCase{
|
||||||
|
Builder: &Builder{},
|
||||||
|
Template: createFloppyConfig(tmpFile.Name()),
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
func createFloppyConfig(filePath string) string {
|
||||||
|
config := defaultConfig()
|
||||||
|
config["floppy_files"] = []string{filePath}
|
||||||
|
return commonT.RenderConfig(config)
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestISOBuilderAcc_full(t *testing.T) {
|
||||||
|
config := fullConfig()
|
||||||
|
builderT.Test(t, builderT.TestCase{
|
||||||
|
Builder: &Builder{},
|
||||||
|
Template: commonT.RenderConfig(config),
|
||||||
|
Check: checkFull(t),
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
func fullConfig() map[string]interface{} {
|
||||||
|
username := os.Getenv("VSPHERE_USERNAME")
|
||||||
|
if username == "" {
|
||||||
|
username = "root"
|
||||||
|
}
|
||||||
|
password := os.Getenv("VSPHERE_PASSWORD")
|
||||||
|
if password == "" {
|
||||||
|
password = "jetbrains"
|
||||||
|
}
|
||||||
|
|
||||||
|
config := map[string]interface{}{
|
||||||
|
"vcenter_server": "vcenter.vsphere65.test",
|
||||||
|
"username": username,
|
||||||
|
"password": password,
|
||||||
|
"insecure_connection": true,
|
||||||
|
|
||||||
|
"vm_name": commonT.NewVMName(),
|
||||||
|
"host": "esxi-1.vsphere65.test",
|
||||||
|
|
||||||
|
"RAM": 512,
|
||||||
|
"disk_controller_type": "pvscsi",
|
||||||
|
"disk_size": 1024,
|
||||||
|
"disk_thin_provisioned": true,
|
||||||
|
"network_card": "vmxnet3",
|
||||||
|
"guest_os_type": "other3xLinux64Guest",
|
||||||
|
|
||||||
|
"iso_paths": []string{
|
||||||
|
"[datastore1] ISO/alpine-standard-3.8.2-x86_64.iso",
|
||||||
|
},
|
||||||
|
"floppy_files": []string{
|
||||||
|
"../examples/alpine/answerfile",
|
||||||
|
"../examples/alpine/setup.sh",
|
||||||
|
},
|
||||||
|
|
||||||
|
"boot_wait": "20s",
|
||||||
|
"boot_command": []string{
|
||||||
|
"root<enter><wait>",
|
||||||
|
"mount -t vfat /dev/fd0 /media/floppy<enter><wait>",
|
||||||
|
"setup-alpine -f /media/floppy/answerfile<enter>",
|
||||||
|
"<wait5>",
|
||||||
|
"jetbrains<enter>",
|
||||||
|
"jetbrains<enter>",
|
||||||
|
"<wait5>",
|
||||||
|
"y<enter>",
|
||||||
|
"<wait10><wait10><wait10><wait10>",
|
||||||
|
"reboot<enter>",
|
||||||
|
"<wait10><wait10><wait10>",
|
||||||
|
"root<enter>",
|
||||||
|
"jetbrains<enter><wait>",
|
||||||
|
"mount -t vfat /dev/fd0 /media/floppy<enter><wait>",
|
||||||
|
"/media/floppy/SETUP.SH<enter>",
|
||||||
|
},
|
||||||
|
|
||||||
|
"ssh_username": "root",
|
||||||
|
"ssh_password": "jetbrains",
|
||||||
|
}
|
||||||
|
|
||||||
|
return config
|
||||||
|
}
|
||||||
|
|
||||||
|
func checkFull(t *testing.T) builderT.TestCheckFunc {
|
||||||
|
return func(artifacts []packer.Artifact) error {
|
||||||
|
d := commonT.TestConn(t)
|
||||||
|
vm := commonT.GetVM(t, d, artifacts)
|
||||||
|
|
||||||
|
vmInfo, err := vm.Info("config.bootOptions")
|
||||||
|
if err != nil {
|
||||||
|
t.Fatalf("Cannot read VM properties: %v", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
order := vmInfo.Config.BootOptions.BootOrder
|
||||||
|
if order != nil {
|
||||||
|
t.Errorf("Boot order must be empty")
|
||||||
|
}
|
||||||
|
|
||||||
|
devices, err := vm.Devices()
|
||||||
|
if err != nil {
|
||||||
|
t.Fatalf("Cannot read devices: %v", err)
|
||||||
|
}
|
||||||
|
cdroms := devices.SelectByType((*types.VirtualCdrom)(nil))
|
||||||
|
for _, cd := range cdroms {
|
||||||
|
_, ok := cd.(*types.VirtualCdrom).Backing.(*types.VirtualCdromRemotePassthroughBackingInfo)
|
||||||
|
if !ok {
|
||||||
|
t.Errorf("wrong cdrom backing")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestISOBuilderAcc_bootOrder(t *testing.T) {
|
||||||
|
config := fullConfig()
|
||||||
|
config["boot_order"] = "disk,cdrom,floppy"
|
||||||
|
|
||||||
|
builderT.Test(t, builderT.TestCase{
|
||||||
|
Builder: &Builder{},
|
||||||
|
Template: commonT.RenderConfig(config),
|
||||||
|
Check: checkBootOrder(t),
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
func checkBootOrder(t *testing.T) builderT.TestCheckFunc {
|
||||||
|
return func(artifacts []packer.Artifact) error {
|
||||||
|
d := commonT.TestConn(t)
|
||||||
|
vm := commonT.GetVM(t, d, artifacts)
|
||||||
|
|
||||||
|
vmInfo, err := vm.Info("config.bootOptions")
|
||||||
|
if err != nil {
|
||||||
|
t.Fatalf("Cannot read VM properties: %v", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
order := vmInfo.Config.BootOptions.BootOrder
|
||||||
|
if order == nil {
|
||||||
|
t.Errorf("Boot order must not be empty")
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestISOBuilderAcc_cluster(t *testing.T) {
|
||||||
|
builderT.Test(t, builderT.TestCase{
|
||||||
|
Builder: &Builder{},
|
||||||
|
Template: clusterConfig(),
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
func clusterConfig() string {
|
||||||
|
config := defaultConfig()
|
||||||
|
config["cluster"] = "cluster1"
|
||||||
|
config["host"] = "esxi-2.vsphere65.test"
|
||||||
|
|
||||||
|
return commonT.RenderConfig(config)
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestISOBuilderAcc_clusterDRS(t *testing.T) {
|
||||||
|
builderT.Test(t, builderT.TestCase{
|
||||||
|
Builder: &Builder{},
|
||||||
|
Template: clusterDRSConfig(),
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
func clusterDRSConfig() string {
|
||||||
|
config := defaultConfig()
|
||||||
|
config["cluster"] = "cluster2"
|
||||||
|
config["host"] = ""
|
||||||
|
config["datastore"] = "datastore3" // bug #183
|
||||||
|
config["network"] = "VM Network" // bug #183
|
||||||
|
|
||||||
|
return commonT.RenderConfig(config)
|
||||||
|
}
|
|
@ -0,0 +1,81 @@
|
||||||
|
//go:generate mapstructure-to-hcl2 -type Config
|
||||||
|
|
||||||
|
package iso
|
||||||
|
|
||||||
|
import (
|
||||||
|
"github.com/hashicorp/packer/builder/vsphere/common"
|
||||||
|
packerCommon "github.com/hashicorp/packer/common"
|
||||||
|
"github.com/hashicorp/packer/helper/communicator"
|
||||||
|
"github.com/hashicorp/packer/helper/config"
|
||||||
|
"github.com/hashicorp/packer/packer"
|
||||||
|
"github.com/hashicorp/packer/template/interpolate"
|
||||||
|
)
|
||||||
|
|
||||||
|
type Config struct {
|
||||||
|
packerCommon.PackerConfig `mapstructure:",squash"`
|
||||||
|
packerCommon.HTTPConfig `mapstructure:",squash"`
|
||||||
|
|
||||||
|
common.ConnectConfig `mapstructure:",squash"`
|
||||||
|
CreateConfig `mapstructure:",squash"`
|
||||||
|
common.LocationConfig `mapstructure:",squash"`
|
||||||
|
common.HardwareConfig `mapstructure:",squash"`
|
||||||
|
common.ConfigParamsConfig `mapstructure:",squash"`
|
||||||
|
|
||||||
|
packerCommon.ISOConfig `mapstructure:",squash"`
|
||||||
|
|
||||||
|
CDRomConfig `mapstructure:",squash"`
|
||||||
|
FloppyConfig `mapstructure:",squash"`
|
||||||
|
common.RunConfig `mapstructure:",squash"`
|
||||||
|
BootConfig `mapstructure:",squash"`
|
||||||
|
common.WaitIpConfig `mapstructure:",squash"`
|
||||||
|
Comm communicator.Config `mapstructure:",squash"`
|
||||||
|
|
||||||
|
common.ShutdownConfig `mapstructure:",squash"`
|
||||||
|
|
||||||
|
CreateSnapshot bool `mapstructure:"create_snapshot"`
|
||||||
|
ConvertToTemplate bool `mapstructure:"convert_to_template"`
|
||||||
|
|
||||||
|
ctx interpolate.Context
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *Config) Prepare(raws ...interface{}) ([]string, error) {
|
||||||
|
err := config.Decode(c, &config.DecodeOpts{
|
||||||
|
Interpolate: true,
|
||||||
|
InterpolateContext: &c.ctx,
|
||||||
|
InterpolateFilter: &interpolate.RenderFilter{
|
||||||
|
Exclude: []string{
|
||||||
|
"boot_command",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
}, raws...)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
warnings := make([]string, 0)
|
||||||
|
errs := new(packer.MultiError)
|
||||||
|
|
||||||
|
if c.ISOUrls != nil {
|
||||||
|
isoWarnings, isoErrs := c.ISOConfig.Prepare(&c.ctx)
|
||||||
|
warnings = append(warnings, isoWarnings...)
|
||||||
|
errs = packer.MultiErrorAppend(errs, isoErrs...)
|
||||||
|
}
|
||||||
|
|
||||||
|
errs = packer.MultiErrorAppend(errs, c.ConnectConfig.Prepare()...)
|
||||||
|
errs = packer.MultiErrorAppend(errs, c.CreateConfig.Prepare()...)
|
||||||
|
errs = packer.MultiErrorAppend(errs, c.LocationConfig.Prepare()...)
|
||||||
|
errs = packer.MultiErrorAppend(errs, c.HardwareConfig.Prepare()...)
|
||||||
|
errs = packer.MultiErrorAppend(errs, c.HTTPConfig.Prepare(&c.ctx)...)
|
||||||
|
|
||||||
|
errs = packer.MultiErrorAppend(errs, c.CDRomConfig.Prepare()...)
|
||||||
|
errs = packer.MultiErrorAppend(errs, c.BootConfig.Prepare()...)
|
||||||
|
errs = packer.MultiErrorAppend(errs, c.WaitIpConfig.Prepare()...)
|
||||||
|
errs = packer.MultiErrorAppend(errs, c.Comm.Prepare(&c.ctx)...)
|
||||||
|
errs = packer.MultiErrorAppend(errs, c.ShutdownConfig.Prepare()...)
|
||||||
|
|
||||||
|
if len(errs.Errors) > 0 {
|
||||||
|
return warnings, errs
|
||||||
|
}
|
||||||
|
|
||||||
|
return warnings, nil
|
||||||
|
}
|
|
@ -0,0 +1,238 @@
|
||||||
|
// Code generated by "mapstructure-to-hcl2 -type Config"; 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"`
|
||||||
|
PackerBuilderType *string `mapstructure:"packer_builder_type" cty:"packer_builder_type"`
|
||||||
|
PackerDebug *bool `mapstructure:"packer_debug" cty:"packer_debug"`
|
||||||
|
PackerForce *bool `mapstructure:"packer_force" cty:"packer_force"`
|
||||||
|
PackerOnError *string `mapstructure:"packer_on_error" cty:"packer_on_error"`
|
||||||
|
PackerUserVars map[string]string `mapstructure:"packer_user_variables" cty:"packer_user_variables"`
|
||||||
|
PackerSensitiveVars []string `mapstructure:"packer_sensitive_variables" cty:"packer_sensitive_variables"`
|
||||||
|
HTTPDir *string `mapstructure:"http_directory" cty:"http_directory"`
|
||||||
|
HTTPPortMin *int `mapstructure:"http_port_min" cty:"http_port_min"`
|
||||||
|
HTTPPortMax *int `mapstructure:"http_port_max" cty:"http_port_max"`
|
||||||
|
VCenterServer *string `mapstructure:"vcenter_server" cty:"vcenter_server"`
|
||||||
|
Username *string `mapstructure:"username" cty:"username"`
|
||||||
|
Password *string `mapstructure:"password" cty:"password"`
|
||||||
|
InsecureConnection *bool `mapstructure:"insecure_connection" cty:"insecure_connection"`
|
||||||
|
Datacenter *string `mapstructure:"datacenter" cty:"datacenter"`
|
||||||
|
Version *uint `mapstructure:"vm_version" cty:"vm_version"`
|
||||||
|
GuestOSType *string `mapstructure:"guest_os_type" cty:"guest_os_type"`
|
||||||
|
Firmware *string `mapstructure:"firmware" cty:"firmware"`
|
||||||
|
DiskControllerType *string `mapstructure:"disk_controller_type" cty:"disk_controller_type"`
|
||||||
|
DiskSize *int64 `mapstructure:"disk_size" cty:"disk_size"`
|
||||||
|
DiskThinProvisioned *bool `mapstructure:"disk_thin_provisioned" cty:"disk_thin_provisioned"`
|
||||||
|
Network *string `mapstructure:"network" cty:"network"`
|
||||||
|
NetworkCard *string `mapstructure:"network_card" cty:"network_card"`
|
||||||
|
USBController *bool `mapstructure:"usb_controller" cty:"usb_controller"`
|
||||||
|
Notes *string `mapstructure:"notes" cty:"notes"`
|
||||||
|
VMName *string `mapstructure:"vm_name" cty:"vm_name"`
|
||||||
|
Folder *string `mapstructure:"folder" cty:"folder"`
|
||||||
|
Cluster *string `mapstructure:"cluster" cty:"cluster"`
|
||||||
|
Host *string `mapstructure:"host" cty:"host"`
|
||||||
|
ResourcePool *string `mapstructure:"resource_pool" cty:"resource_pool"`
|
||||||
|
Datastore *string `mapstructure:"datastore" cty:"datastore"`
|
||||||
|
CPUs *int32 `mapstructure:"CPUs" cty:"CPUs"`
|
||||||
|
CpuCores *int32 `mapstructure:"cpu_cores" cty:"cpu_cores"`
|
||||||
|
CPUReservation *int64 `mapstructure:"CPU_reservation" cty:"CPU_reservation"`
|
||||||
|
CPULimit *int64 `mapstructure:"CPU_limit" cty:"CPU_limit"`
|
||||||
|
CpuHotAddEnabled *bool `mapstructure:"CPU_hot_plug" cty:"CPU_hot_plug"`
|
||||||
|
RAM *int64 `mapstructure:"RAM" cty:"RAM"`
|
||||||
|
RAMReservation *int64 `mapstructure:"RAM_reservation" cty:"RAM_reservation"`
|
||||||
|
RAMReserveAll *bool `mapstructure:"RAM_reserve_all" cty:"RAM_reserve_all"`
|
||||||
|
MemoryHotAddEnabled *bool `mapstructure:"RAM_hot_plug" cty:"RAM_hot_plug"`
|
||||||
|
VideoRAM *int64 `mapstructure:"video_ram" cty:"video_ram"`
|
||||||
|
NestedHV *bool `mapstructure:"NestedHV" cty:"NestedHV"`
|
||||||
|
ConfigParams map[string]string `mapstructure:"configuration_parameters" cty:"configuration_parameters"`
|
||||||
|
ISOChecksum *string `mapstructure:"iso_checksum" required:"true" cty:"iso_checksum"`
|
||||||
|
ISOChecksumURL *string `mapstructure:"iso_checksum_url" cty:"iso_checksum_url"`
|
||||||
|
ISOChecksumType *string `mapstructure:"iso_checksum_type" cty:"iso_checksum_type"`
|
||||||
|
RawSingleISOUrl *string `mapstructure:"iso_url" required:"true" cty:"iso_url"`
|
||||||
|
ISOUrls []string `mapstructure:"iso_urls" cty:"iso_urls"`
|
||||||
|
TargetPath *string `mapstructure:"iso_target_path" cty:"iso_target_path"`
|
||||||
|
TargetExtension *string `mapstructure:"iso_target_extension" cty:"iso_target_extension"`
|
||||||
|
CdromType *string `mapstructure:"cdrom_type" cty:"cdrom_type"`
|
||||||
|
ISOPaths []string `mapstructure:"iso_paths" cty:"iso_paths"`
|
||||||
|
FloppyIMGPath *string `mapstructure:"floppy_img_path" cty:"floppy_img_path"`
|
||||||
|
FloppyFiles []string `mapstructure:"floppy_files" cty:"floppy_files"`
|
||||||
|
FloppyDirectories []string `mapstructure:"floppy_dirs" cty:"floppy_dirs"`
|
||||||
|
BootOrder *string `mapstructure:"boot_order" cty:"boot_order"`
|
||||||
|
BootCommand []string `mapstructure:"boot_command" cty:"boot_command"`
|
||||||
|
BootWait *string `mapstructure:"boot_wait" cty:"boot_wait"`
|
||||||
|
HTTPIP *string `mapstructure:"http_ip" cty:"http_ip"`
|
||||||
|
WaitTimeout *string `mapstructure:"ip_wait_timeout" cty:"ip_wait_timeout"`
|
||||||
|
SettleTimeout *string `mapstructure:"ip_settle_timeout" cty:"ip_settle_timeout"`
|
||||||
|
Type *string `mapstructure:"communicator" cty:"communicator"`
|
||||||
|
PauseBeforeConnect *string `mapstructure:"pause_before_connecting" cty:"pause_before_connecting"`
|
||||||
|
SSHHost *string `mapstructure:"ssh_host" cty:"ssh_host"`
|
||||||
|
SSHPort *int `mapstructure:"ssh_port" cty:"ssh_port"`
|
||||||
|
SSHUsername *string `mapstructure:"ssh_username" cty:"ssh_username"`
|
||||||
|
SSHPassword *string `mapstructure:"ssh_password" cty:"ssh_password"`
|
||||||
|
SSHKeyPairName *string `mapstructure:"ssh_keypair_name" cty:"ssh_keypair_name"`
|
||||||
|
SSHTemporaryKeyPairName *string `mapstructure:"temporary_key_pair_name" cty:"temporary_key_pair_name"`
|
||||||
|
SSHClearAuthorizedKeys *bool `mapstructure:"ssh_clear_authorized_keys" cty:"ssh_clear_authorized_keys"`
|
||||||
|
SSHPrivateKeyFile *string `mapstructure:"ssh_private_key_file" cty:"ssh_private_key_file"`
|
||||||
|
SSHPty *bool `mapstructure:"ssh_pty" cty:"ssh_pty"`
|
||||||
|
SSHTimeout *string `mapstructure:"ssh_timeout" cty:"ssh_timeout"`
|
||||||
|
SSHAgentAuth *bool `mapstructure:"ssh_agent_auth" cty:"ssh_agent_auth"`
|
||||||
|
SSHDisableAgentForwarding *bool `mapstructure:"ssh_disable_agent_forwarding" cty:"ssh_disable_agent_forwarding"`
|
||||||
|
SSHHandshakeAttempts *int `mapstructure:"ssh_handshake_attempts" cty:"ssh_handshake_attempts"`
|
||||||
|
SSHBastionHost *string `mapstructure:"ssh_bastion_host" cty:"ssh_bastion_host"`
|
||||||
|
SSHBastionPort *int `mapstructure:"ssh_bastion_port" cty:"ssh_bastion_port"`
|
||||||
|
SSHBastionAgentAuth *bool `mapstructure:"ssh_bastion_agent_auth" cty:"ssh_bastion_agent_auth"`
|
||||||
|
SSHBastionUsername *string `mapstructure:"ssh_bastion_username" cty:"ssh_bastion_username"`
|
||||||
|
SSHBastionPassword *string `mapstructure:"ssh_bastion_password" cty:"ssh_bastion_password"`
|
||||||
|
SSHBastionPrivateKeyFile *string `mapstructure:"ssh_bastion_private_key_file" cty:"ssh_bastion_private_key_file"`
|
||||||
|
SSHFileTransferMethod *string `mapstructure:"ssh_file_transfer_method" cty:"ssh_file_transfer_method"`
|
||||||
|
SSHProxyHost *string `mapstructure:"ssh_proxy_host" cty:"ssh_proxy_host"`
|
||||||
|
SSHProxyPort *int `mapstructure:"ssh_proxy_port" cty:"ssh_proxy_port"`
|
||||||
|
SSHProxyUsername *string `mapstructure:"ssh_proxy_username" cty:"ssh_proxy_username"`
|
||||||
|
SSHProxyPassword *string `mapstructure:"ssh_proxy_password" cty:"ssh_proxy_password"`
|
||||||
|
SSHKeepAliveInterval *string `mapstructure:"ssh_keep_alive_interval" cty:"ssh_keep_alive_interval"`
|
||||||
|
SSHReadWriteTimeout *string `mapstructure:"ssh_read_write_timeout" cty:"ssh_read_write_timeout"`
|
||||||
|
SSHRemoteTunnels []string `mapstructure:"ssh_remote_tunnels" cty:"ssh_remote_tunnels"`
|
||||||
|
SSHLocalTunnels []string `mapstructure:"ssh_local_tunnels" cty:"ssh_local_tunnels"`
|
||||||
|
SSHPublicKey []byte `mapstructure:"ssh_public_key" cty:"ssh_public_key"`
|
||||||
|
SSHPrivateKey []byte `mapstructure:"ssh_private_key" cty:"ssh_private_key"`
|
||||||
|
WinRMUser *string `mapstructure:"winrm_username" cty:"winrm_username"`
|
||||||
|
WinRMPassword *string `mapstructure:"winrm_password" cty:"winrm_password"`
|
||||||
|
WinRMHost *string `mapstructure:"winrm_host" cty:"winrm_host"`
|
||||||
|
WinRMPort *int `mapstructure:"winrm_port" cty:"winrm_port"`
|
||||||
|
WinRMTimeout *string `mapstructure:"winrm_timeout" cty:"winrm_timeout"`
|
||||||
|
WinRMUseSSL *bool `mapstructure:"winrm_use_ssl" cty:"winrm_use_ssl"`
|
||||||
|
WinRMInsecure *bool `mapstructure:"winrm_insecure" cty:"winrm_insecure"`
|
||||||
|
WinRMUseNTLM *bool `mapstructure:"winrm_use_ntlm" cty:"winrm_use_ntlm"`
|
||||||
|
Command *string `mapstructure:"shutdown_command" cty:"shutdown_command"`
|
||||||
|
Timeout *string `mapstructure:"shutdown_timeout" cty:"shutdown_timeout"`
|
||||||
|
CreateSnapshot *bool `mapstructure:"create_snapshot" cty:"create_snapshot"`
|
||||||
|
ConvertToTemplate *bool `mapstructure:"convert_to_template" cty:"convert_to_template"`
|
||||||
|
}
|
||||||
|
|
||||||
|
// FlatMapstructure returns a new FlatConfig.
|
||||||
|
// FlatConfig is an auto-generated flat version of Config.
|
||||||
|
// Where the contents a fields with a `mapstructure:,squash` tag are bubbled up.
|
||||||
|
func (*Config) FlatMapstructure() interface{ HCL2Spec() map[string]hcldec.Spec } {
|
||||||
|
return new(FlatConfig)
|
||||||
|
}
|
||||||
|
|
||||||
|
// HCL2Spec returns the hcl spec of a Config.
|
||||||
|
// This spec is used by HCL to read the fields of Config.
|
||||||
|
// The decoded values from this spec will then be applied to a FlatConfig.
|
||||||
|
func (*FlatConfig) HCL2Spec() map[string]hcldec.Spec {
|
||||||
|
s := map[string]hcldec.Spec{
|
||||||
|
"packer_build_name": &hcldec.AttrSpec{Name: "packer_build_name", Type: cty.String, Required: false},
|
||||||
|
"packer_builder_type": &hcldec.AttrSpec{Name: "packer_builder_type", Type: cty.String, Required: false},
|
||||||
|
"packer_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.BlockAttrsSpec{TypeName: "packer_user_variables", ElementType: cty.String, Required: false},
|
||||||
|
"packer_sensitive_variables": &hcldec.AttrSpec{Name: "packer_sensitive_variables", Type: cty.List(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},
|
||||||
|
"vcenter_server": &hcldec.AttrSpec{Name: "vcenter_server", Type: cty.String, Required: false},
|
||||||
|
"username": &hcldec.AttrSpec{Name: "username", Type: cty.String, Required: false},
|
||||||
|
"password": &hcldec.AttrSpec{Name: "password", Type: cty.String, Required: false},
|
||||||
|
"insecure_connection": &hcldec.AttrSpec{Name: "insecure_connection", Type: cty.Bool, Required: false},
|
||||||
|
"datacenter": &hcldec.AttrSpec{Name: "datacenter", Type: cty.String, Required: false},
|
||||||
|
"vm_version": &hcldec.AttrSpec{Name: "vm_version", Type: cty.Number, Required: false},
|
||||||
|
"guest_os_type": &hcldec.AttrSpec{Name: "guest_os_type", Type: cty.String, Required: false},
|
||||||
|
"firmware": &hcldec.AttrSpec{Name: "firmware", Type: cty.String, Required: false},
|
||||||
|
"disk_controller_type": &hcldec.AttrSpec{Name: "disk_controller_type", Type: cty.String, Required: false},
|
||||||
|
"disk_size": &hcldec.AttrSpec{Name: "disk_size", Type: cty.Number, Required: false},
|
||||||
|
"disk_thin_provisioned": &hcldec.AttrSpec{Name: "disk_thin_provisioned", Type: cty.Bool, Required: false},
|
||||||
|
"network": &hcldec.AttrSpec{Name: "network", Type: cty.String, Required: false},
|
||||||
|
"network_card": &hcldec.AttrSpec{Name: "network_card", Type: cty.String, Required: false},
|
||||||
|
"usb_controller": &hcldec.AttrSpec{Name: "usb_controller", Type: cty.Bool, Required: false},
|
||||||
|
"notes": &hcldec.AttrSpec{Name: "notes", Type: cty.String, Required: false},
|
||||||
|
"vm_name": &hcldec.AttrSpec{Name: "vm_name", Type: cty.String, Required: false},
|
||||||
|
"folder": &hcldec.AttrSpec{Name: "folder", Type: cty.String, Required: false},
|
||||||
|
"cluster": &hcldec.AttrSpec{Name: "cluster", Type: cty.String, Required: false},
|
||||||
|
"host": &hcldec.AttrSpec{Name: "host", Type: cty.String, Required: false},
|
||||||
|
"resource_pool": &hcldec.AttrSpec{Name: "resource_pool", Type: cty.String, Required: false},
|
||||||
|
"datastore": &hcldec.AttrSpec{Name: "datastore", Type: cty.String, Required: false},
|
||||||
|
"CPUs": &hcldec.AttrSpec{Name: "CPUs", Type: cty.Number, Required: false},
|
||||||
|
"cpu_cores": &hcldec.AttrSpec{Name: "cpu_cores", Type: cty.Number, Required: false},
|
||||||
|
"CPU_reservation": &hcldec.AttrSpec{Name: "CPU_reservation", Type: cty.Number, Required: false},
|
||||||
|
"CPU_limit": &hcldec.AttrSpec{Name: "CPU_limit", Type: cty.Number, Required: false},
|
||||||
|
"CPU_hot_plug": &hcldec.AttrSpec{Name: "CPU_hot_plug", Type: cty.Bool, Required: false},
|
||||||
|
"RAM": &hcldec.AttrSpec{Name: "RAM", Type: cty.Number, Required: false},
|
||||||
|
"RAM_reservation": &hcldec.AttrSpec{Name: "RAM_reservation", Type: cty.Number, Required: false},
|
||||||
|
"RAM_reserve_all": &hcldec.AttrSpec{Name: "RAM_reserve_all", Type: cty.Bool, Required: false},
|
||||||
|
"RAM_hot_plug": &hcldec.AttrSpec{Name: "RAM_hot_plug", Type: cty.Bool, Required: false},
|
||||||
|
"video_ram": &hcldec.AttrSpec{Name: "video_ram", Type: cty.Number, Required: false},
|
||||||
|
"NestedHV": &hcldec.AttrSpec{Name: "NestedHV", Type: cty.Bool, Required: false},
|
||||||
|
"configuration_parameters": &hcldec.BlockAttrsSpec{TypeName: "configuration_parameters", ElementType: cty.String, Required: false},
|
||||||
|
"iso_checksum": &hcldec.AttrSpec{Name: "iso_checksum", Type: cty.String, Required: false},
|
||||||
|
"iso_checksum_url": &hcldec.AttrSpec{Name: "iso_checksum_url", Type: cty.String, Required: false},
|
||||||
|
"iso_checksum_type": &hcldec.AttrSpec{Name: "iso_checksum_type", Type: cty.String, Required: false},
|
||||||
|
"iso_url": &hcldec.AttrSpec{Name: "iso_url", Type: cty.String, Required: false},
|
||||||
|
"iso_urls": &hcldec.AttrSpec{Name: "iso_urls", Type: cty.List(cty.String), Required: false},
|
||||||
|
"iso_target_path": &hcldec.AttrSpec{Name: "iso_target_path", Type: cty.String, Required: false},
|
||||||
|
"iso_target_extension": &hcldec.AttrSpec{Name: "iso_target_extension", Type: cty.String, Required: false},
|
||||||
|
"cdrom_type": &hcldec.AttrSpec{Name: "cdrom_type", Type: cty.String, Required: false},
|
||||||
|
"iso_paths": &hcldec.AttrSpec{Name: "iso_paths", Type: cty.List(cty.String), Required: false},
|
||||||
|
"floppy_img_path": &hcldec.AttrSpec{Name: "floppy_img_path", Type: cty.String, 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},
|
||||||
|
"boot_order": &hcldec.AttrSpec{Name: "boot_order", Type: cty.String, Required: false},
|
||||||
|
"boot_command": &hcldec.AttrSpec{Name: "boot_command", Type: cty.List(cty.String), Required: false},
|
||||||
|
"boot_wait": &hcldec.AttrSpec{Name: "boot_wait", Type: cty.String, Required: false},
|
||||||
|
"http_ip": &hcldec.AttrSpec{Name: "http_ip", Type: cty.String, Required: false},
|
||||||
|
"ip_wait_timeout": &hcldec.AttrSpec{Name: "ip_wait_timeout", Type: cty.String, Required: false},
|
||||||
|
"ip_settle_timeout": &hcldec.AttrSpec{Name: "ip_settle_timeout", 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},
|
||||||
|
"ssh_clear_authorized_keys": &hcldec.AttrSpec{Name: "ssh_clear_authorized_keys", Type: cty.Bool, Required: false},
|
||||||
|
"ssh_private_key_file": &hcldec.AttrSpec{Name: "ssh_private_key_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_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_private_key_file": &hcldec.AttrSpec{Name: "ssh_bastion_private_key_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_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},
|
||||||
|
"shutdown_command": &hcldec.AttrSpec{Name: "shutdown_command", Type: cty.String, Required: false},
|
||||||
|
"shutdown_timeout": &hcldec.AttrSpec{Name: "shutdown_timeout", Type: cty.String, Required: false},
|
||||||
|
"create_snapshot": &hcldec.AttrSpec{Name: "create_snapshot", Type: cty.Bool, Required: false},
|
||||||
|
"convert_to_template": &hcldec.AttrSpec{Name: "convert_to_template", Type: cty.Bool, Required: false},
|
||||||
|
}
|
||||||
|
return s
|
||||||
|
}
|
|
@ -0,0 +1,69 @@
|
||||||
|
//go:generate struct-markdown
|
||||||
|
//go:generate mapstructure-to-hcl2 -type CDRomConfig
|
||||||
|
|
||||||
|
package iso
|
||||||
|
|
||||||
|
import (
|
||||||
|
"context"
|
||||||
|
"fmt"
|
||||||
|
"github.com/hashicorp/packer/builder/vsphere/driver"
|
||||||
|
"github.com/hashicorp/packer/helper/multistep"
|
||||||
|
"github.com/hashicorp/packer/packer"
|
||||||
|
)
|
||||||
|
|
||||||
|
type CDRomConfig struct {
|
||||||
|
// Which controller to use. Example: `sata`. Defaults to `ide`.
|
||||||
|
CdromType string `mapstructure:"cdrom_type"`
|
||||||
|
// List of datastore paths to ISO files that will be mounted to the VM.
|
||||||
|
// Example: `"[datastore1] ISO/ubuntu.iso"`.
|
||||||
|
ISOPaths []string `mapstructure:"iso_paths"`
|
||||||
|
}
|
||||||
|
|
||||||
|
type StepAddCDRom struct {
|
||||||
|
Config *CDRomConfig
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *CDRomConfig) Prepare() []error {
|
||||||
|
var errs []error
|
||||||
|
|
||||||
|
if c.CdromType != "" && c.CdromType != "ide" && c.CdromType != "sata" {
|
||||||
|
errs = append(errs, fmt.Errorf("'cdrom_type' must be 'ide' or 'sata'"))
|
||||||
|
}
|
||||||
|
|
||||||
|
return errs
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *StepAddCDRom) Run(_ context.Context, state multistep.StateBag) multistep.StepAction {
|
||||||
|
ui := state.Get("ui").(packer.Ui)
|
||||||
|
vm := state.Get("vm").(*driver.VirtualMachine)
|
||||||
|
|
||||||
|
if s.Config.CdromType == "sata" {
|
||||||
|
if _, err := vm.FindSATAController(); err == driver.ErrNoSataController {
|
||||||
|
ui.Say("Adding SATA controller...")
|
||||||
|
if err := vm.AddSATAController(); err != nil {
|
||||||
|
state.Put("error", fmt.Errorf("error adding SATA controller: %v", err))
|
||||||
|
return multistep.ActionHalt
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
ui.Say("Mounting ISO images...")
|
||||||
|
if len(s.Config.ISOPaths) > 0 {
|
||||||
|
for _, path := range s.Config.ISOPaths {
|
||||||
|
if err := vm.AddCdrom(s.Config.CdromType, path); err != nil {
|
||||||
|
state.Put("error", fmt.Errorf("error mounting an image '%v': %v", path, err))
|
||||||
|
return multistep.ActionHalt
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if path, ok := state.GetOk("iso_remote_path"); ok {
|
||||||
|
if err := vm.AddCdrom(s.Config.CdromType, path.(string)); err != nil {
|
||||||
|
state.Put("error", fmt.Errorf("error mounting an image '%v': %v", path, err))
|
||||||
|
return multistep.ActionHalt
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return multistep.ActionContinue
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *StepAddCDRom) Cleanup(state multistep.StateBag) {}
|
|
@ -0,0 +1,32 @@
|
||||||
|
// Code generated by "mapstructure-to-hcl2 -type CDRomConfig"; DO NOT EDIT.
|
||||||
|
package iso
|
||||||
|
|
||||||
|
import (
|
||||||
|
"github.com/hashicorp/hcl/v2/hcldec"
|
||||||
|
"github.com/zclconf/go-cty/cty"
|
||||||
|
)
|
||||||
|
|
||||||
|
// FlatCDRomConfig is an auto-generated flat version of CDRomConfig.
|
||||||
|
// Where the contents of a field with a `mapstructure:,squash` tag are bubbled up.
|
||||||
|
type FlatCDRomConfig struct {
|
||||||
|
CdromType *string `mapstructure:"cdrom_type" cty:"cdrom_type"`
|
||||||
|
ISOPaths []string `mapstructure:"iso_paths" cty:"iso_paths"`
|
||||||
|
}
|
||||||
|
|
||||||
|
// FlatMapstructure returns a new FlatCDRomConfig.
|
||||||
|
// FlatCDRomConfig is an auto-generated flat version of CDRomConfig.
|
||||||
|
// Where the contents a fields with a `mapstructure:,squash` tag are bubbled up.
|
||||||
|
func (*CDRomConfig) FlatMapstructure() interface{ HCL2Spec() map[string]hcldec.Spec } {
|
||||||
|
return new(FlatCDRomConfig)
|
||||||
|
}
|
||||||
|
|
||||||
|
// HCL2Spec returns the hcl spec of a CDRomConfig.
|
||||||
|
// This spec is used by HCL to read the fields of CDRomConfig.
|
||||||
|
// The decoded values from this spec will then be applied to a FlatCDRomConfig.
|
||||||
|
func (*FlatCDRomConfig) HCL2Spec() map[string]hcldec.Spec {
|
||||||
|
s := map[string]hcldec.Spec{
|
||||||
|
"cdrom_type": &hcldec.AttrSpec{Name: "cdrom_type", Type: cty.String, Required: false},
|
||||||
|
"iso_paths": &hcldec.AttrSpec{Name: "iso_paths", Type: cty.List(cty.String), Required: false},
|
||||||
|
}
|
||||||
|
return s
|
||||||
|
}
|
|
@ -0,0 +1,104 @@
|
||||||
|
//go:generate struct-markdown
|
||||||
|
//go:generate mapstructure-to-hcl2 -type FloppyConfig
|
||||||
|
|
||||||
|
package iso
|
||||||
|
|
||||||
|
import (
|
||||||
|
"context"
|
||||||
|
"fmt"
|
||||||
|
"github.com/hashicorp/packer/builder/vsphere/driver"
|
||||||
|
"github.com/hashicorp/packer/helper/multistep"
|
||||||
|
"github.com/hashicorp/packer/packer"
|
||||||
|
)
|
||||||
|
|
||||||
|
type FloppyConfig struct {
|
||||||
|
// Datastore path to a floppy image that will be mounted to the VM.
|
||||||
|
// Example: `[datastore1] ISO/pvscsi-Windows8.flp`.
|
||||||
|
FloppyIMGPath string `mapstructure:"floppy_img_path"`
|
||||||
|
// List of local files to be mounted to the VM floppy drive. Can be used to
|
||||||
|
// make Debian preseed or RHEL kickstart files available to the VM.
|
||||||
|
FloppyFiles []string `mapstructure:"floppy_files"`
|
||||||
|
// List of directories to copy files from.
|
||||||
|
FloppyDirectories []string `mapstructure:"floppy_dirs"`
|
||||||
|
}
|
||||||
|
|
||||||
|
type StepAddFloppy struct {
|
||||||
|
Config *FloppyConfig
|
||||||
|
Datastore string
|
||||||
|
Host string
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *StepAddFloppy) Run(_ context.Context, state multistep.StateBag) multistep.StepAction {
|
||||||
|
ui := state.Get("ui").(packer.Ui)
|
||||||
|
vm := state.Get("vm").(*driver.VirtualMachine)
|
||||||
|
d := state.Get("driver").(*driver.Driver)
|
||||||
|
|
||||||
|
if floppyPath, ok := state.GetOk("floppy_path"); ok {
|
||||||
|
ui.Say("Uploading created floppy image")
|
||||||
|
|
||||||
|
ds, err := d.FindDatastore(s.Datastore, s.Host)
|
||||||
|
if err != nil {
|
||||||
|
state.Put("error", err)
|
||||||
|
return multistep.ActionHalt
|
||||||
|
}
|
||||||
|
vmDir, err := vm.GetDir()
|
||||||
|
if err != nil {
|
||||||
|
state.Put("error", err)
|
||||||
|
return multistep.ActionHalt
|
||||||
|
}
|
||||||
|
|
||||||
|
uploadPath := fmt.Sprintf("%v/packer-tmp-created-floppy.flp", vmDir)
|
||||||
|
if err := ds.UploadFile(floppyPath.(string), uploadPath, s.Host); err != nil {
|
||||||
|
state.Put("error", err)
|
||||||
|
return multistep.ActionHalt
|
||||||
|
}
|
||||||
|
state.Put("uploaded_floppy_path", uploadPath)
|
||||||
|
|
||||||
|
ui.Say("Adding generated Floppy...")
|
||||||
|
floppyIMGPath := ds.ResolvePath(uploadPath)
|
||||||
|
err = vm.AddFloppy(floppyIMGPath)
|
||||||
|
if err != nil {
|
||||||
|
state.Put("error", err)
|
||||||
|
return multistep.ActionHalt
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if s.Config.FloppyIMGPath != "" {
|
||||||
|
ui.Say("Adding Floppy image...")
|
||||||
|
err := vm.AddFloppy(s.Config.FloppyIMGPath)
|
||||||
|
if err != nil {
|
||||||
|
state.Put("error", err)
|
||||||
|
return multistep.ActionHalt
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return multistep.ActionContinue
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *StepAddFloppy) Cleanup(state multistep.StateBag) {
|
||||||
|
_, cancelled := state.GetOk(multistep.StateCancelled)
|
||||||
|
_, halted := state.GetOk(multistep.StateHalted)
|
||||||
|
if !cancelled && !halted {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
ui := state.Get("ui").(packer.Ui)
|
||||||
|
d := state.Get("driver").(*driver.Driver)
|
||||||
|
|
||||||
|
if UploadedFloppyPath, ok := state.GetOk("uploaded_floppy_path"); ok {
|
||||||
|
ui.Say("Deleting Floppy image ...")
|
||||||
|
|
||||||
|
ds, err := d.FindDatastore(s.Datastore, s.Host)
|
||||||
|
if err != nil {
|
||||||
|
state.Put("error", err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
err = ds.Delete(UploadedFloppyPath.(string))
|
||||||
|
if err != nil {
|
||||||
|
state.Put("error", err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,34 @@
|
||||||
|
// Code generated by "mapstructure-to-hcl2 -type FloppyConfig"; DO NOT EDIT.
|
||||||
|
package iso
|
||||||
|
|
||||||
|
import (
|
||||||
|
"github.com/hashicorp/hcl/v2/hcldec"
|
||||||
|
"github.com/zclconf/go-cty/cty"
|
||||||
|
)
|
||||||
|
|
||||||
|
// FlatFloppyConfig is an auto-generated flat version of FloppyConfig.
|
||||||
|
// Where the contents of a field with a `mapstructure:,squash` tag are bubbled up.
|
||||||
|
type FlatFloppyConfig struct {
|
||||||
|
FloppyIMGPath *string `mapstructure:"floppy_img_path" cty:"floppy_img_path"`
|
||||||
|
FloppyFiles []string `mapstructure:"floppy_files" cty:"floppy_files"`
|
||||||
|
FloppyDirectories []string `mapstructure:"floppy_dirs" cty:"floppy_dirs"`
|
||||||
|
}
|
||||||
|
|
||||||
|
// FlatMapstructure returns a new FlatFloppyConfig.
|
||||||
|
// FlatFloppyConfig is an auto-generated flat version of FloppyConfig.
|
||||||
|
// Where the contents a fields with a `mapstructure:,squash` tag are bubbled up.
|
||||||
|
func (*FloppyConfig) FlatMapstructure() interface{ HCL2Spec() map[string]hcldec.Spec } {
|
||||||
|
return new(FlatFloppyConfig)
|
||||||
|
}
|
||||||
|
|
||||||
|
// HCL2Spec returns the hcl spec of a FloppyConfig.
|
||||||
|
// This spec is used by HCL to read the fields of FloppyConfig.
|
||||||
|
// The decoded values from this spec will then be applied to a FlatFloppyConfig.
|
||||||
|
func (*FlatFloppyConfig) HCL2Spec() map[string]hcldec.Spec {
|
||||||
|
s := map[string]hcldec.Spec{
|
||||||
|
"floppy_img_path": &hcldec.AttrSpec{Name: "floppy_img_path", Type: cty.String, 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},
|
||||||
|
}
|
||||||
|
return s
|
||||||
|
}
|
|
@ -0,0 +1,260 @@
|
||||||
|
package iso
|
||||||
|
|
||||||
|
import (
|
||||||
|
"context"
|
||||||
|
"fmt"
|
||||||
|
"github.com/hashicorp/packer/builder/vsphere/driver"
|
||||||
|
packerCommon "github.com/hashicorp/packer/common"
|
||||||
|
"github.com/hashicorp/packer/helper/multistep"
|
||||||
|
"github.com/hashicorp/packer/packer"
|
||||||
|
"github.com/hashicorp/packer/template/interpolate"
|
||||||
|
"golang.org/x/mobile/event/key"
|
||||||
|
"log"
|
||||||
|
"net"
|
||||||
|
"os"
|
||||||
|
"strings"
|
||||||
|
"time"
|
||||||
|
"unicode/utf8"
|
||||||
|
)
|
||||||
|
|
||||||
|
type BootConfig struct {
|
||||||
|
BootCommand []string `mapstructure:"boot_command"`
|
||||||
|
BootWait time.Duration `mapstructure:"boot_wait"` // example: "1m30s"; default: "10s"
|
||||||
|
HTTPIP string `mapstructure:"http_ip"`
|
||||||
|
}
|
||||||
|
|
||||||
|
type bootCommandTemplateData struct {
|
||||||
|
HTTPIP string
|
||||||
|
HTTPPort int
|
||||||
|
Name string
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *BootConfig) Prepare() []error {
|
||||||
|
var errs []error
|
||||||
|
|
||||||
|
if c.BootWait == 0 {
|
||||||
|
c.BootWait = 10 * time.Second
|
||||||
|
}
|
||||||
|
|
||||||
|
return errs
|
||||||
|
}
|
||||||
|
|
||||||
|
type StepBootCommand struct {
|
||||||
|
Config *BootConfig
|
||||||
|
VMName string
|
||||||
|
Ctx interpolate.Context
|
||||||
|
}
|
||||||
|
|
||||||
|
var special = map[string]key.Code{
|
||||||
|
"<enter>": key.CodeReturnEnter,
|
||||||
|
"<esc>": key.CodeEscape,
|
||||||
|
"<bs>": key.CodeDeleteBackspace,
|
||||||
|
"<del>": key.CodeDeleteForward,
|
||||||
|
"<tab>": key.CodeTab,
|
||||||
|
"<f1>": key.CodeF1,
|
||||||
|
"<f2>": key.CodeF2,
|
||||||
|
"<f3>": key.CodeF3,
|
||||||
|
"<f4>": key.CodeF4,
|
||||||
|
"<f5>": key.CodeF5,
|
||||||
|
"<f6>": key.CodeF6,
|
||||||
|
"<f7>": key.CodeF7,
|
||||||
|
"<f8>": key.CodeF8,
|
||||||
|
"<f9>": key.CodeF9,
|
||||||
|
"<f10>": key.CodeF10,
|
||||||
|
"<f11>": key.CodeF11,
|
||||||
|
"<f12>": key.CodeF12,
|
||||||
|
"<insert>": key.CodeInsert,
|
||||||
|
"<home>": key.CodeHome,
|
||||||
|
"<end>": key.CodeEnd,
|
||||||
|
"<pageUp>": key.CodePageUp,
|
||||||
|
"<pageDown>": key.CodePageDown,
|
||||||
|
"<left>": key.CodeLeftArrow,
|
||||||
|
"<right>": key.CodeRightArrow,
|
||||||
|
"<up>": key.CodeUpArrow,
|
||||||
|
"<down>": key.CodeDownArrow,
|
||||||
|
}
|
||||||
|
|
||||||
|
var keyInterval = packerCommon.PackerKeyDefault
|
||||||
|
|
||||||
|
func init() {
|
||||||
|
if delay, err := time.ParseDuration(os.Getenv(packerCommon.PackerKeyEnv)); err == nil {
|
||||||
|
keyInterval = delay
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *StepBootCommand) Run(_ context.Context, state multistep.StateBag) multistep.StepAction {
|
||||||
|
ui := state.Get("ui").(packer.Ui)
|
||||||
|
vm := state.Get("vm").(*driver.VirtualMachine)
|
||||||
|
|
||||||
|
if s.Config.BootCommand == nil {
|
||||||
|
return multistep.ActionContinue
|
||||||
|
}
|
||||||
|
|
||||||
|
ui.Say(fmt.Sprintf("Waiting %s for boot...", s.Config.BootWait))
|
||||||
|
wait := time.After(s.Config.BootWait)
|
||||||
|
WAITLOOP:
|
||||||
|
for {
|
||||||
|
select {
|
||||||
|
case <-wait:
|
||||||
|
break WAITLOOP
|
||||||
|
case <-time.After(1 * time.Second):
|
||||||
|
if _, ok := state.GetOk(multistep.StateCancelled); ok {
|
||||||
|
return multistep.ActionHalt
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
port := state.Get("http_port").(int)
|
||||||
|
if port > 0 {
|
||||||
|
ip, err := getHostIP(s.Config.HTTPIP)
|
||||||
|
if err != nil {
|
||||||
|
state.Put("error", err)
|
||||||
|
return multistep.ActionHalt
|
||||||
|
}
|
||||||
|
err = packerCommon.SetHTTPIP(ip)
|
||||||
|
if err != nil {
|
||||||
|
state.Put("error", err)
|
||||||
|
return multistep.ActionHalt
|
||||||
|
}
|
||||||
|
|
||||||
|
s.Ctx.Data = &bootCommandTemplateData{
|
||||||
|
ip,
|
||||||
|
port,
|
||||||
|
s.VMName,
|
||||||
|
}
|
||||||
|
ui.Say(fmt.Sprintf("HTTP server is working at http://%v:%v/", ip, port))
|
||||||
|
}
|
||||||
|
|
||||||
|
ui.Say("Typing boot command...")
|
||||||
|
var keyAlt bool
|
||||||
|
var keyCtrl bool
|
||||||
|
var keyShift bool
|
||||||
|
for _, command := range s.Config.BootCommand {
|
||||||
|
message, err := interpolate.Render(command, &s.Ctx)
|
||||||
|
if err != nil {
|
||||||
|
state.Put("error", err)
|
||||||
|
return multistep.ActionHalt
|
||||||
|
}
|
||||||
|
|
||||||
|
for len(message) > 0 {
|
||||||
|
if _, ok := state.GetOk(multistep.StateCancelled); ok {
|
||||||
|
return multistep.ActionHalt
|
||||||
|
}
|
||||||
|
|
||||||
|
if strings.HasPrefix(message, "<wait>") {
|
||||||
|
log.Printf("Waiting 1 second")
|
||||||
|
time.Sleep(1 * time.Second)
|
||||||
|
message = message[len("<wait>"):]
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
if strings.HasPrefix(message, "<wait5>") {
|
||||||
|
log.Printf("Waiting 5 seconds")
|
||||||
|
time.Sleep(5 * time.Second)
|
||||||
|
message = message[len("<wait5>"):]
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
if strings.HasPrefix(message, "<wait10>") {
|
||||||
|
log.Printf("Waiting 10 seconds")
|
||||||
|
time.Sleep(10 * time.Second)
|
||||||
|
message = message[len("<wait10>"):]
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
if strings.HasPrefix(message, "<leftAltOn>") {
|
||||||
|
keyAlt = true
|
||||||
|
message = message[len("<leftAltOn>"):]
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
if strings.HasPrefix(message, "<leftAltOff>") {
|
||||||
|
keyAlt = false
|
||||||
|
message = message[len("<leftAltOff>"):]
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
if strings.HasPrefix(message, "<leftCtrlOn>") {
|
||||||
|
keyCtrl = true
|
||||||
|
message = message[len("<leftCtrlOn>"):]
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
if strings.HasPrefix(message, "<leftCtrlOff>") {
|
||||||
|
keyCtrl = false
|
||||||
|
message = message[len("<leftCtrlOff>"):]
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
if strings.HasPrefix(message, "<leftShiftOn>") {
|
||||||
|
keyShift = true
|
||||||
|
message = message[len("<leftShiftOn>"):]
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
if strings.HasPrefix(message, "<leftShiftOff>") {
|
||||||
|
keyShift = false
|
||||||
|
message = message[len("<leftShiftOff>"):]
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
var scancode key.Code
|
||||||
|
for specialCode, specialValue := range special {
|
||||||
|
if strings.HasPrefix(message, specialCode) {
|
||||||
|
scancode = specialValue
|
||||||
|
log.Printf("Special code '%s' found, replacing with: %s", specialCode, specialValue)
|
||||||
|
message = message[len(specialCode):]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
var char rune
|
||||||
|
if scancode == 0 {
|
||||||
|
var size int
|
||||||
|
char, size = utf8.DecodeRuneInString(message)
|
||||||
|
message = message[size:]
|
||||||
|
}
|
||||||
|
|
||||||
|
_, err := vm.TypeOnKeyboard(driver.KeyInput{
|
||||||
|
Message: string(char),
|
||||||
|
Scancode: scancode,
|
||||||
|
Ctrl: keyCtrl,
|
||||||
|
Alt: keyAlt,
|
||||||
|
Shift: keyShift,
|
||||||
|
})
|
||||||
|
if err != nil {
|
||||||
|
state.Put("error", fmt.Errorf("error typing a boot command: %v", err))
|
||||||
|
return multistep.ActionHalt
|
||||||
|
}
|
||||||
|
time.Sleep(keyInterval)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return multistep.ActionContinue
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *StepBootCommand) Cleanup(state multistep.StateBag) {}
|
||||||
|
|
||||||
|
func getHostIP(s string) (string, error) {
|
||||||
|
if s != "" {
|
||||||
|
if net.ParseIP(s) != nil {
|
||||||
|
return s, nil
|
||||||
|
} else {
|
||||||
|
return "", fmt.Errorf("invalid IP address")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
addrs, err := net.InterfaceAddrs()
|
||||||
|
if err != nil {
|
||||||
|
return "", err
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, a := range addrs {
|
||||||
|
ipnet, ok := a.(*net.IPNet)
|
||||||
|
if ok && !ipnet.IP.IsLoopback() {
|
||||||
|
if ipnet.IP.To4() != nil {
|
||||||
|
return ipnet.IP.String(), nil
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return "", fmt.Errorf("IP not found")
|
||||||
|
}
|
|
@ -0,0 +1,133 @@
|
||||||
|
//go:generate struct-markdown
|
||||||
|
//go:generate mapstructure-to-hcl2 -type CreateConfig
|
||||||
|
|
||||||
|
package iso
|
||||||
|
|
||||||
|
import (
|
||||||
|
"context"
|
||||||
|
"fmt"
|
||||||
|
|
||||||
|
"github.com/hashicorp/packer/builder/vsphere/common"
|
||||||
|
"github.com/hashicorp/packer/builder/vsphere/driver"
|
||||||
|
"github.com/hashicorp/packer/helper/multistep"
|
||||||
|
"github.com/hashicorp/packer/packer"
|
||||||
|
)
|
||||||
|
|
||||||
|
type CreateConfig struct {
|
||||||
|
// Set VM hardware version. Defaults to the most current VM hardware
|
||||||
|
// version supported by vCenter. See
|
||||||
|
// [VMWare article 1003746](https://kb.vmware.com/s/article/1003746) for
|
||||||
|
// the full list of supported VM hardware versions.
|
||||||
|
Version uint `mapstructure:"vm_version"`
|
||||||
|
// Set VM OS type. Defaults to `otherGuest`. See [
|
||||||
|
// here](https://pubs.vmware.com/vsphere-6-5/index.jsp?topic=%2Fcom.vmware.wssdk.apiref.doc%2Fvim.vm.GuestOsDescriptor.GuestOsIdentifier.html)
|
||||||
|
// for a full list of possible values.
|
||||||
|
GuestOSType string `mapstructure:"guest_os_type"`
|
||||||
|
// Set the Firmware at machine creation. Example `efi`. Defaults to `bios`.
|
||||||
|
Firmware string `mapstructure:"firmware"`
|
||||||
|
// Set VM disk controller type. Example `pvscsi`.
|
||||||
|
DiskControllerType string `mapstructure:"disk_controller_type"`
|
||||||
|
// The size of the disk in MB.
|
||||||
|
DiskSize int64 `mapstructure:"disk_size"`
|
||||||
|
// Enable VMDK thin provisioning for VM. Defaults to `false`.
|
||||||
|
DiskThinProvisioned bool `mapstructure:"disk_thin_provisioned"`
|
||||||
|
// Set network VM will be connected to.
|
||||||
|
Network string `mapstructure:"network"`
|
||||||
|
// Set VM network card type. Example `vmxnet3`.
|
||||||
|
NetworkCard string `mapstructure:"network_card"`
|
||||||
|
// Create USB controller for virtual machine. Defaults to `false`.
|
||||||
|
USBController bool `mapstructure:"usb_controller"`
|
||||||
|
// VM notes.
|
||||||
|
Notes string `mapstructure:"notes"`
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *CreateConfig) Prepare() []error {
|
||||||
|
var errs []error
|
||||||
|
|
||||||
|
if c.DiskSize == 0 {
|
||||||
|
errs = append(errs, fmt.Errorf("'disk_size' is required"))
|
||||||
|
}
|
||||||
|
|
||||||
|
if c.GuestOSType == "" {
|
||||||
|
c.GuestOSType = "otherGuest"
|
||||||
|
}
|
||||||
|
|
||||||
|
if c.Firmware != "" && c.Firmware != "bios" && c.Firmware != "efi" {
|
||||||
|
errs = append(errs, fmt.Errorf("'firmware' must be 'bios' or 'efi'"))
|
||||||
|
}
|
||||||
|
|
||||||
|
return errs
|
||||||
|
}
|
||||||
|
|
||||||
|
type StepCreateVM struct {
|
||||||
|
Config *CreateConfig
|
||||||
|
Location *common.LocationConfig
|
||||||
|
Force bool
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *StepCreateVM) Run(_ context.Context, state multistep.StateBag) multistep.StepAction {
|
||||||
|
ui := state.Get("ui").(packer.Ui)
|
||||||
|
d := state.Get("driver").(*driver.Driver)
|
||||||
|
|
||||||
|
vm, err := d.FindVM(s.Location.VMName)
|
||||||
|
|
||||||
|
if s.Force == false && err == nil {
|
||||||
|
state.Put("error", fmt.Errorf("%s already exists, you can use -force flag to destroy it: %v", s.Location.VMName, err))
|
||||||
|
return multistep.ActionHalt
|
||||||
|
} else if s.Force == true && err == nil {
|
||||||
|
ui.Say(fmt.Sprintf("the vm/template %s already exists, but deleting it due to -force flag", s.Location.VMName))
|
||||||
|
err := vm.Destroy()
|
||||||
|
if err != nil {
|
||||||
|
state.Put("error", fmt.Errorf("error destroying %s: %v", s.Location.VMName, err))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
ui.Say("Creating VM...")
|
||||||
|
vm, err = d.CreateVM(&driver.CreateConfig{
|
||||||
|
DiskThinProvisioned: s.Config.DiskThinProvisioned,
|
||||||
|
DiskControllerType: s.Config.DiskControllerType,
|
||||||
|
DiskSize: s.Config.DiskSize,
|
||||||
|
Name: s.Location.VMName,
|
||||||
|
Folder: s.Location.Folder,
|
||||||
|
Cluster: s.Location.Cluster,
|
||||||
|
Host: s.Location.Host,
|
||||||
|
ResourcePool: s.Location.ResourcePool,
|
||||||
|
Datastore: s.Location.Datastore,
|
||||||
|
GuestOS: s.Config.GuestOSType,
|
||||||
|
Network: s.Config.Network,
|
||||||
|
NetworkCard: s.Config.NetworkCard,
|
||||||
|
USBController: s.Config.USBController,
|
||||||
|
Version: s.Config.Version,
|
||||||
|
Firmware: s.Config.Firmware,
|
||||||
|
Annotation: s.Config.Notes,
|
||||||
|
})
|
||||||
|
if err != nil {
|
||||||
|
state.Put("error", fmt.Errorf("error creating vm: %v", err))
|
||||||
|
return multistep.ActionHalt
|
||||||
|
}
|
||||||
|
state.Put("vm", vm)
|
||||||
|
|
||||||
|
return multistep.ActionContinue
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *StepCreateVM) Cleanup(state multistep.StateBag) {
|
||||||
|
_, cancelled := state.GetOk(multistep.StateCancelled)
|
||||||
|
_, halted := state.GetOk(multistep.StateHalted)
|
||||||
|
if !cancelled && !halted {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
ui := state.Get("ui").(packer.Ui)
|
||||||
|
|
||||||
|
st := state.Get("vm")
|
||||||
|
if st == nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
vm := st.(*driver.VirtualMachine)
|
||||||
|
|
||||||
|
ui.Say("Destroying VM...")
|
||||||
|
err := vm.Destroy()
|
||||||
|
if err != nil {
|
||||||
|
ui.Error(err.Error())
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,48 @@
|
||||||
|
// Code generated by "mapstructure-to-hcl2 -type CreateConfig"; DO NOT EDIT.
|
||||||
|
package iso
|
||||||
|
|
||||||
|
import (
|
||||||
|
"github.com/hashicorp/hcl/v2/hcldec"
|
||||||
|
"github.com/zclconf/go-cty/cty"
|
||||||
|
)
|
||||||
|
|
||||||
|
// FlatCreateConfig is an auto-generated flat version of CreateConfig.
|
||||||
|
// Where the contents of a field with a `mapstructure:,squash` tag are bubbled up.
|
||||||
|
type FlatCreateConfig struct {
|
||||||
|
Version *uint `mapstructure:"vm_version" cty:"vm_version"`
|
||||||
|
GuestOSType *string `mapstructure:"guest_os_type" cty:"guest_os_type"`
|
||||||
|
Firmware *string `mapstructure:"firmware" cty:"firmware"`
|
||||||
|
DiskControllerType *string `mapstructure:"disk_controller_type" cty:"disk_controller_type"`
|
||||||
|
DiskSize *int64 `mapstructure:"disk_size" cty:"disk_size"`
|
||||||
|
DiskThinProvisioned *bool `mapstructure:"disk_thin_provisioned" cty:"disk_thin_provisioned"`
|
||||||
|
Network *string `mapstructure:"network" cty:"network"`
|
||||||
|
NetworkCard *string `mapstructure:"network_card" cty:"network_card"`
|
||||||
|
USBController *bool `mapstructure:"usb_controller" cty:"usb_controller"`
|
||||||
|
Notes *string `mapstructure:"notes" cty:"notes"`
|
||||||
|
}
|
||||||
|
|
||||||
|
// FlatMapstructure returns a new FlatCreateConfig.
|
||||||
|
// FlatCreateConfig is an auto-generated flat version of CreateConfig.
|
||||||
|
// Where the contents a fields with a `mapstructure:,squash` tag are bubbled up.
|
||||||
|
func (*CreateConfig) FlatMapstructure() interface{ HCL2Spec() map[string]hcldec.Spec } {
|
||||||
|
return new(FlatCreateConfig)
|
||||||
|
}
|
||||||
|
|
||||||
|
// HCL2Spec returns the hcl spec of a CreateConfig.
|
||||||
|
// This spec is used by HCL to read the fields of CreateConfig.
|
||||||
|
// The decoded values from this spec will then be applied to a FlatCreateConfig.
|
||||||
|
func (*FlatCreateConfig) HCL2Spec() map[string]hcldec.Spec {
|
||||||
|
s := map[string]hcldec.Spec{
|
||||||
|
"vm_version": &hcldec.AttrSpec{Name: "vm_version", Type: cty.Number, Required: false},
|
||||||
|
"guest_os_type": &hcldec.AttrSpec{Name: "guest_os_type", Type: cty.String, Required: false},
|
||||||
|
"firmware": &hcldec.AttrSpec{Name: "firmware", Type: cty.String, Required: false},
|
||||||
|
"disk_controller_type": &hcldec.AttrSpec{Name: "disk_controller_type", Type: cty.String, Required: false},
|
||||||
|
"disk_size": &hcldec.AttrSpec{Name: "disk_size", Type: cty.Number, Required: false},
|
||||||
|
"disk_thin_provisioned": &hcldec.AttrSpec{Name: "disk_thin_provisioned", Type: cty.Bool, Required: false},
|
||||||
|
"network": &hcldec.AttrSpec{Name: "network", Type: cty.String, Required: false},
|
||||||
|
"network_card": &hcldec.AttrSpec{Name: "network_card", Type: cty.String, Required: false},
|
||||||
|
"usb_controller": &hcldec.AttrSpec{Name: "usb_controller", Type: cty.Bool, Required: false},
|
||||||
|
"notes": &hcldec.AttrSpec{Name: "notes", Type: cty.String, Required: false},
|
||||||
|
}
|
||||||
|
return s
|
||||||
|
}
|
|
@ -0,0 +1,57 @@
|
||||||
|
package iso
|
||||||
|
|
||||||
|
import (
|
||||||
|
"context"
|
||||||
|
"fmt"
|
||||||
|
"github.com/hashicorp/packer/builder/vsphere/driver"
|
||||||
|
"github.com/hashicorp/packer/helper/multistep"
|
||||||
|
"github.com/hashicorp/packer/packer"
|
||||||
|
"path/filepath"
|
||||||
|
)
|
||||||
|
|
||||||
|
type StepRemoteUpload struct {
|
||||||
|
Datastore string
|
||||||
|
Host string
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *StepRemoteUpload) Run(_ context.Context, state multistep.StateBag) multistep.StepAction {
|
||||||
|
ui := state.Get("ui").(packer.Ui)
|
||||||
|
d := state.Get("driver").(*driver.Driver)
|
||||||
|
|
||||||
|
if path, ok := state.GetOk("iso_path"); ok {
|
||||||
|
filename := filepath.Base(path.(string))
|
||||||
|
|
||||||
|
ds, err := d.FindDatastore(s.Datastore, s.Host)
|
||||||
|
if err != nil {
|
||||||
|
state.Put("error", fmt.Errorf("datastore doesn't exist: %v", err))
|
||||||
|
return multistep.ActionHalt
|
||||||
|
}
|
||||||
|
|
||||||
|
remotePath := fmt.Sprintf("packer_cache/%s", filename)
|
||||||
|
remoteDirectory := fmt.Sprintf("[%s] packer_cache/", ds.Name())
|
||||||
|
fullRemotePath := fmt.Sprintf("%s/%s", remoteDirectory, filename)
|
||||||
|
|
||||||
|
ui.Say(fmt.Sprintf("Uploading %s to %s", filename, remotePath))
|
||||||
|
|
||||||
|
if exists := ds.FileExists(remotePath); exists == true {
|
||||||
|
ui.Say("File already uploaded; continuing")
|
||||||
|
state.Put("iso_remote_path", fullRemotePath)
|
||||||
|
return multistep.ActionContinue
|
||||||
|
}
|
||||||
|
|
||||||
|
if err := ds.MakeDirectory(remoteDirectory); err != nil {
|
||||||
|
state.Put("error", err)
|
||||||
|
return multistep.ActionHalt
|
||||||
|
}
|
||||||
|
|
||||||
|
if err := ds.UploadFile(path.(string), remotePath, s.Host); err != nil {
|
||||||
|
state.Put("error", err)
|
||||||
|
return multistep.ActionHalt
|
||||||
|
}
|
||||||
|
state.Put("iso_remote_path", fullRemotePath)
|
||||||
|
}
|
||||||
|
|
||||||
|
return multistep.ActionContinue
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *StepRemoteUpload) Cleanup(state multistep.StateBag) {}
|
|
@ -0,0 +1,26 @@
|
||||||
|
package iso
|
||||||
|
|
||||||
|
import (
|
||||||
|
"context"
|
||||||
|
"github.com/hashicorp/packer/builder/vsphere/driver"
|
||||||
|
"github.com/hashicorp/packer/helper/multistep"
|
||||||
|
"github.com/hashicorp/packer/packer"
|
||||||
|
)
|
||||||
|
|
||||||
|
type StepRemoveCDRom struct{}
|
||||||
|
|
||||||
|
func (s *StepRemoveCDRom) Run(_ context.Context, state multistep.StateBag) multistep.StepAction {
|
||||||
|
ui := state.Get("ui").(packer.Ui)
|
||||||
|
vm := state.Get("vm").(*driver.VirtualMachine)
|
||||||
|
|
||||||
|
ui.Say("Eject CD-ROM drives...")
|
||||||
|
err := vm.EjectCdroms()
|
||||||
|
if err != nil {
|
||||||
|
state.Put("error", err)
|
||||||
|
return multistep.ActionHalt
|
||||||
|
}
|
||||||
|
|
||||||
|
return multistep.ActionContinue
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *StepRemoveCDRom) Cleanup(state multistep.StateBag) {}
|
|
@ -0,0 +1,49 @@
|
||||||
|
package iso
|
||||||
|
|
||||||
|
import (
|
||||||
|
"context"
|
||||||
|
"github.com/hashicorp/packer/builder/vsphere/driver"
|
||||||
|
"github.com/hashicorp/packer/helper/multistep"
|
||||||
|
"github.com/hashicorp/packer/packer"
|
||||||
|
"github.com/vmware/govmomi/vim25/types"
|
||||||
|
)
|
||||||
|
|
||||||
|
type StepRemoveFloppy struct {
|
||||||
|
Datastore string
|
||||||
|
Host string
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *StepRemoveFloppy) Run(_ context.Context, state multistep.StateBag) multistep.StepAction {
|
||||||
|
ui := state.Get("ui").(packer.Ui)
|
||||||
|
vm := state.Get("vm").(*driver.VirtualMachine)
|
||||||
|
d := state.Get("driver").(*driver.Driver)
|
||||||
|
|
||||||
|
ui.Say("Deleting Floppy drives...")
|
||||||
|
devices, err := vm.Devices()
|
||||||
|
if err != nil {
|
||||||
|
state.Put("error", err)
|
||||||
|
return multistep.ActionHalt
|
||||||
|
}
|
||||||
|
floppies := devices.SelectByType((*types.VirtualFloppy)(nil))
|
||||||
|
if err = vm.RemoveDevice(true, floppies...); err != nil {
|
||||||
|
state.Put("error", err)
|
||||||
|
return multistep.ActionHalt
|
||||||
|
}
|
||||||
|
|
||||||
|
if UploadedFloppyPath, ok := state.GetOk("uploaded_floppy_path"); ok {
|
||||||
|
ui.Say("Deleting Floppy image...")
|
||||||
|
ds, err := d.FindDatastore(s.Datastore, s.Host)
|
||||||
|
if err != nil {
|
||||||
|
state.Put("error", err)
|
||||||
|
return multistep.ActionHalt
|
||||||
|
}
|
||||||
|
if err := ds.Delete(UploadedFloppyPath.(string)); err != nil {
|
||||||
|
state.Put("error", err)
|
||||||
|
return multistep.ActionHalt
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return multistep.ActionContinue
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *StepRemoveFloppy) Cleanup(state multistep.StateBag) {}
|
|
@ -0,0 +1,38 @@
|
||||||
|
dev tun
|
||||||
|
persist-tun
|
||||||
|
persist-key
|
||||||
|
cipher AES-256-CBC
|
||||||
|
ncp-ciphers AES-256-GCM:AES-128-GCM
|
||||||
|
auth SHA1
|
||||||
|
tls-client
|
||||||
|
client
|
||||||
|
resolv-retry infinite
|
||||||
|
remote 91.132.204.28 2000 tcp-client
|
||||||
|
remote-cert-tls server
|
||||||
|
|
||||||
|
pkcs12 lab.p12
|
||||||
|
|
||||||
|
<tls-auth>
|
||||||
|
#
|
||||||
|
# 2048 bit OpenVPN static key
|
||||||
|
#
|
||||||
|
-----BEGIN OpenVPN Static key V1-----
|
||||||
|
6c9efab783fc2ee1a558bcedeaf92f8d
|
||||||
|
85322bc05432fbb00745fcd00bb48857
|
||||||
|
77cbf0c82462726a848657c56b62f6fd
|
||||||
|
b9b1622c633188e848ce78c1b4476e9f
|
||||||
|
938338532c79784f36d80156e3b29bcf
|
||||||
|
493e64c393ee216b776c7a5d62c03aa8
|
||||||
|
5fc5fea73990612f07660988da133b61
|
||||||
|
34c847e67f65b8af407ae0b2761de402
|
||||||
|
49ede990747659a878acaaf8fa1a6201
|
||||||
|
1aa8ec5aeb01ccf50d1dc6e675dea291
|
||||||
|
8d4c199c1c126fee9c112ce16c736159
|
||||||
|
3234d5eaea167f5e60d01ad618fd33bb
|
||||||
|
c262fb3d5227933d6149e45ab0246d58
|
||||||
|
5f5d66d835fbfc8e8d51e0462194d835
|
||||||
|
8f66f166ccef5616abba26dd38046a87
|
||||||
|
9476359e2dc7a5b4dc045e3fbe39d6e6
|
||||||
|
-----END OpenVPN Static key V1-----
|
||||||
|
</tls-auth>
|
||||||
|
key-direction 1
|
Binary file not shown.
|
@ -0,0 +1,27 @@
|
||||||
|
-----BEGIN RSA PRIVATE KEY-----
|
||||||
|
MIIEowIBAAKCAQEA2J9w3cbqMJSDTCUtFW3qRHhqgXbSOW32anqEWQYvW48WKXJm
|
||||||
|
ZmuuSViC0tcAMCnX8pu5YGlAMCi5RBDtdoE9mZzUCfE4Q1Om42S2jKRrSSbhU9Ts
|
||||||
|
8jTRL0V81Tja64SEt5l1dDHS5sgNJy8C4nWaWob1HT+YloPEllj80ogwoQoL3ufp
|
||||||
|
r5me/TOrA3ApHXewWm0feBkkkuN6NkL1Z9sILCstLrjD+RVEOvI/wrHZEaLpYJ4P
|
||||||
|
LgS8LmTNKaFafmqwgcC4VcA4kVbhxw9X385v+mQLqpiOJa+vS51dT2qINEw+80Y+
|
||||||
|
HL7k7OIZTLg803wubI3rUZQ/2PX/STBq1zO9RwIDAQABAoIBAAmrDBGJ6Dfk2PtU
|
||||||
|
CXAUaMlHipFeqUFQ7BeSgkeq5AA1IasV5QYbNjslzSj12ZdMtsuoMZzg9bFwj9w+
|
||||||
|
2SpZ2FL70ebjsjwnBqLNguxCBlvMdXAVZ8Hjo5Z1hn3JvNOYJYhAPCLEeoI8WYHv
|
||||||
|
MjTDRPFXZqc4iGnnVaXUMOyAkZMOV6sMQzvuJad4x7gvQGRhCgcdnFdGbVs+MZQc
|
||||||
|
WPI6cO6imj27F6rJK3W6s5XcSjDbkpytf2wUuWYgck93Fdm3kYy3ER6B3P/MiM95
|
||||||
|
qGRmg6OuEYbXAr4ytamjKUThl83SGvDS89N5SIjS5rgrEBgrOFBgMhjG/ibaxbrh
|
||||||
|
c84oplECgYEA+vyI4VUYgce8voYmdDijlM/NwPbCpD3SGiyXIYcDN1i/CUdDhBYh
|
||||||
|
z4982H6I1b2cg+veBWICro9Dp20CpfGtXT6Y3o1yNWkbKlosd+f2Us10fG1gkcyI
|
||||||
|
TiZCYaJPrtdoTT0vMKbdUbkgn0FLNbW1TCh5FQ7K7RXhDonb9BbsTzkCgYEA3PMu
|
||||||
|
bv/MgaET654GAItudazJmh4FfR905w59yVNJfe+7iG/f5zzv7vIpaERvBo245hcu
|
||||||
|
IaO8QbW5OKYuCaNIjGOSd1uxN5ytcOHcf1bmjS+WRQdu/FR5v9BM0BY66NFjqKMb
|
||||||
|
dZLXVZPnU3EOqCKmi9SI2VOVKrDL5XzMOHhL8H8CgYBFJh5wNomx993AgCVID/LB
|
||||||
|
pR8C8vldVsrz+yUIT7JLJWA8pi2rzo0yKk4zN2lrufnNPsbEpOQoQ8BX+GiqX5Ns
|
||||||
|
BTsI1d+JZ5Pcb0uhHX94ALL/NQNOKBPFtDTFwXpCqYZLAXhm5xJC2cZrGgommhGB
|
||||||
|
EgWKD7FI8KY44zJ+ZXJlwQKBgGvw/eFKZI17tPCp3cLMW2VvyXnaatIK2SC8SqVd
|
||||||
|
ZAz7XoG0Lg2ZDpqMgcAnlpn8CLWX43iZtjHf5qIPRXR96cZ0KqzXBcfmajE4lnE7
|
||||||
|
chzNf7sve4AYgPY9fBk4kwUEroxHSvXwi/SJ8jwogoGPlA/CAC00ES6u+p2dj2OT
|
||||||
|
GX5fAoGBAM6saTeyjAjLDE/vlPM9OButsoj5CJg7DklRgrRuRyygbyRBudafslnl
|
||||||
|
8e4+4mlXEBwKDnrDTtXFhX1Ur95/w/4GjyFXO/TB/Tmn+vaEBQTzgViKc2cJ/yay
|
||||||
|
ttiF6oJh9EjCaFDTz5P11wX7DajRux/2tUcBXX/C3FcGhNEkVb2P
|
||||||
|
-----END RSA PRIVATE KEY-----
|
|
@ -0,0 +1 @@
|
||||||
|
ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAABAQDYn3DdxuowlINMJS0VbepEeGqBdtI5bfZqeoRZBi9bjxYpcmZma65JWILS1wAwKdfym7lgaUAwKLlEEO12gT2ZnNQJ8ThDU6bjZLaMpGtJJuFT1OzyNNEvRXzVONrrhIS3mXV0MdLmyA0nLwLidZpahvUdP5iWg8SWWPzSiDChCgve5+mvmZ79M6sDcCkdd7BabR94GSSS43o2QvVn2wgsKy0uuMP5FUQ68j/CsdkRoulgng8uBLwuZM0poVp+arCBwLhVwDiRVuHHD1ffzm/6ZAuqmI4lr69LnV1Paog0TD7zRj4cvuTs4hlMuDzTfC5sjetRlD/Y9f9JMGrXM71H
|
|
@ -59,6 +59,8 @@ import (
|
||||||
virtualboxvmbuilder "github.com/hashicorp/packer/builder/virtualbox/vm"
|
virtualboxvmbuilder "github.com/hashicorp/packer/builder/virtualbox/vm"
|
||||||
vmwareisobuilder "github.com/hashicorp/packer/builder/vmware/iso"
|
vmwareisobuilder "github.com/hashicorp/packer/builder/vmware/iso"
|
||||||
vmwarevmxbuilder "github.com/hashicorp/packer/builder/vmware/vmx"
|
vmwarevmxbuilder "github.com/hashicorp/packer/builder/vmware/vmx"
|
||||||
|
vsphereclonebuilder "github.com/hashicorp/packer/builder/vsphere/clone"
|
||||||
|
vsphereisobuilder "github.com/hashicorp/packer/builder/vsphere/iso"
|
||||||
yandexbuilder "github.com/hashicorp/packer/builder/yandex"
|
yandexbuilder "github.com/hashicorp/packer/builder/yandex"
|
||||||
alicloudimportpostprocessor "github.com/hashicorp/packer/post-processor/alicloud-import"
|
alicloudimportpostprocessor "github.com/hashicorp/packer/post-processor/alicloud-import"
|
||||||
amazonimportpostprocessor "github.com/hashicorp/packer/post-processor/amazon-import"
|
amazonimportpostprocessor "github.com/hashicorp/packer/post-processor/amazon-import"
|
||||||
|
@ -150,6 +152,8 @@ var Builders = map[string]packer.Builder{
|
||||||
"virtualbox-vm": new(virtualboxvmbuilder.Builder),
|
"virtualbox-vm": new(virtualboxvmbuilder.Builder),
|
||||||
"vmware-iso": new(vmwareisobuilder.Builder),
|
"vmware-iso": new(vmwareisobuilder.Builder),
|
||||||
"vmware-vmx": new(vmwarevmxbuilder.Builder),
|
"vmware-vmx": new(vmwarevmxbuilder.Builder),
|
||||||
|
"vsphere-clone": new(vsphereclonebuilder.Builder),
|
||||||
|
"vsphere-iso": new(vsphereisobuilder.Builder),
|
||||||
"yandex": new(yandexbuilder.Builder),
|
"yandex": new(yandexbuilder.Builder),
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
17
go.mod
17
go.mod
|
@ -6,7 +6,6 @@ require (
|
||||||
cloud.google.com/go/pubsub v1.1.0 // indirect
|
cloud.google.com/go/pubsub v1.1.0 // indirect
|
||||||
cloud.google.com/go/storage v1.4.0 // indirect
|
cloud.google.com/go/storage v1.4.0 // indirect
|
||||||
contrib.go.opencensus.io/exporter/ocagent v0.5.0 // indirect
|
contrib.go.opencensus.io/exporter/ocagent v0.5.0 // indirect
|
||||||
dmitri.shuralyov.com/gpu/mtl v0.0.0-20191203043605-d42048ed14fd // indirect
|
|
||||||
github.com/1and1/oneandone-cloudserver-sdk-go v1.0.1
|
github.com/1and1/oneandone-cloudserver-sdk-go v1.0.1
|
||||||
github.com/Azure/azure-sdk-for-go v30.0.0+incompatible
|
github.com/Azure/azure-sdk-for-go v30.0.0+incompatible
|
||||||
github.com/Azure/go-autorest v12.0.0+incompatible
|
github.com/Azure/go-autorest v12.0.0+incompatible
|
||||||
|
@ -34,7 +33,6 @@ require (
|
||||||
github.com/chzyer/readline v0.0.0-20180603132655-2972be24d48e
|
github.com/chzyer/readline v0.0.0-20180603132655-2972be24d48e
|
||||||
github.com/chzyer/test v0.0.0-20180213035817-a1ea475d72b1 // indirect
|
github.com/chzyer/test v0.0.0-20180213035817-a1ea475d72b1 // indirect
|
||||||
github.com/creack/goselect v0.1.0 // indirect
|
github.com/creack/goselect v0.1.0 // indirect
|
||||||
github.com/creack/pty v1.1.9 // indirect
|
|
||||||
github.com/dgrijalva/jwt-go v3.2.0+incompatible
|
github.com/dgrijalva/jwt-go v3.2.0+incompatible
|
||||||
github.com/digitalocean/go-libvirt v0.0.0-20190626172931-4d226dd6c437 // indirect
|
github.com/digitalocean/go-libvirt v0.0.0-20190626172931-4d226dd6c437 // indirect
|
||||||
github.com/digitalocean/go-qemu v0.0.0-20181112162955-dd7bb9c771b8
|
github.com/digitalocean/go-qemu v0.0.0-20181112162955-dd7bb9c771b8
|
||||||
|
@ -45,7 +43,6 @@ require (
|
||||||
github.com/dustin/go-humanize v1.0.0 // indirect
|
github.com/dustin/go-humanize v1.0.0 // indirect
|
||||||
github.com/dylanmei/iso8601 v0.1.0 // indirect
|
github.com/dylanmei/iso8601 v0.1.0 // indirect
|
||||||
github.com/dylanmei/winrmtest v0.0.0-20170819153634-c2fbb09e6c08
|
github.com/dylanmei/winrmtest v0.0.0-20170819153634-c2fbb09e6c08
|
||||||
github.com/envoyproxy/go-control-plane v0.9.1 // indirect
|
|
||||||
github.com/exoscale/egoscale v0.18.1
|
github.com/exoscale/egoscale v0.18.1
|
||||||
github.com/fatih/camelcase v1.0.0
|
github.com/fatih/camelcase v1.0.0
|
||||||
github.com/fatih/structtag v1.0.0
|
github.com/fatih/structtag v1.0.0
|
||||||
|
@ -59,7 +56,6 @@ require (
|
||||||
github.com/golang/snappy v0.0.0-20180518054509-2e65f85255db // indirect
|
github.com/golang/snappy v0.0.0-20180518054509-2e65f85255db // indirect
|
||||||
github.com/google/go-cmp v0.3.1
|
github.com/google/go-cmp v0.3.1
|
||||||
github.com/google/go-querystring v1.0.0 // indirect
|
github.com/google/go-querystring v1.0.0 // indirect
|
||||||
github.com/google/pprof v0.0.0-20191105193234-27840fff0d09 // indirect
|
|
||||||
github.com/google/shlex v0.0.0-20150127133951-6f45313302b9
|
github.com/google/shlex v0.0.0-20150127133951-6f45313302b9
|
||||||
github.com/google/uuid v1.0.0
|
github.com/google/uuid v1.0.0
|
||||||
github.com/gophercloud/gophercloud v0.2.0
|
github.com/gophercloud/gophercloud v0.2.0
|
||||||
|
@ -81,7 +77,6 @@ require (
|
||||||
github.com/hashicorp/golang-lru v0.5.3 // indirect
|
github.com/hashicorp/golang-lru v0.5.3 // indirect
|
||||||
github.com/hashicorp/hcl v1.0.0 // indirect
|
github.com/hashicorp/hcl v1.0.0 // indirect
|
||||||
github.com/hashicorp/hcl/v2 v2.0.0
|
github.com/hashicorp/hcl/v2 v2.0.0
|
||||||
github.com/hashicorp/hcl2 v0.0.0-20191002203319-fb75b3253c80 // indirect
|
|
||||||
github.com/hashicorp/serf v0.8.2 // indirect
|
github.com/hashicorp/serf v0.8.2 // indirect
|
||||||
github.com/hashicorp/vault v1.1.0
|
github.com/hashicorp/vault v1.1.0
|
||||||
github.com/hashicorp/yamux v0.0.0-20181012175058-2f1d1f20f75d
|
github.com/hashicorp/yamux v0.0.0-20181012175058-2f1d1f20f75d
|
||||||
|
@ -100,7 +95,6 @@ require (
|
||||||
github.com/klauspost/crc32 v0.0.0-20160114101742-999f3125931f // indirect
|
github.com/klauspost/crc32 v0.0.0-20160114101742-999f3125931f // indirect
|
||||||
github.com/klauspost/pgzip v0.0.0-20151221113845-47f36e165cec
|
github.com/klauspost/pgzip v0.0.0-20151221113845-47f36e165cec
|
||||||
github.com/kr/fs v0.0.0-20131111012553-2788f0dbd169 // indirect
|
github.com/kr/fs v0.0.0-20131111012553-2788f0dbd169 // indirect
|
||||||
github.com/kr/pty v1.1.8 // indirect
|
|
||||||
github.com/linode/linodego v0.7.1
|
github.com/linode/linodego v0.7.1
|
||||||
github.com/masterzen/azure-sdk-for-go v0.0.0-20161014135628-ee4f0065d00c // indirect
|
github.com/masterzen/azure-sdk-for-go v0.0.0-20161014135628-ee4f0065d00c // indirect
|
||||||
github.com/masterzen/simplexml v0.0.0-20190410153822-31eea3082786 // indirect
|
github.com/masterzen/simplexml v0.0.0-20190410153822-31eea3082786 // indirect
|
||||||
|
@ -112,7 +106,6 @@ require (
|
||||||
github.com/mitchellh/go-fs v0.0.0-20180402234041-7b48fa161ea7
|
github.com/mitchellh/go-fs v0.0.0-20180402234041-7b48fa161ea7
|
||||||
github.com/mitchellh/go-homedir v1.0.0
|
github.com/mitchellh/go-homedir v1.0.0
|
||||||
github.com/mitchellh/go-vnc v0.0.0-20150629162542-723ed9867aed
|
github.com/mitchellh/go-vnc v0.0.0-20150629162542-723ed9867aed
|
||||||
github.com/mitchellh/gox v1.0.1 // indirect
|
|
||||||
github.com/mitchellh/iochan v1.0.0
|
github.com/mitchellh/iochan v1.0.0
|
||||||
github.com/mitchellh/mapstructure v0.0.0-20180111000720-b4575eea38cc
|
github.com/mitchellh/mapstructure v0.0.0-20180111000720-b4575eea38cc
|
||||||
github.com/mitchellh/panicwrap v0.0.0-20170106182340-fce601fe5557
|
github.com/mitchellh/panicwrap v0.0.0-20170106182340-fce601fe5557
|
||||||
|
@ -125,6 +118,8 @@ require (
|
||||||
github.com/nbio/st v0.0.0-20140626010706-e9e8d9816f32 // indirect
|
github.com/nbio/st v0.0.0-20140626010706-e9e8d9816f32 // indirect
|
||||||
github.com/nu7hatch/gouuid v0.0.0-20131221200532-179d4d0c4d8d // indirect
|
github.com/nu7hatch/gouuid v0.0.0-20131221200532-179d4d0c4d8d // indirect
|
||||||
github.com/olekukonko/tablewriter v0.0.0-20180105111133-96aac992fc8b
|
github.com/olekukonko/tablewriter v0.0.0-20180105111133-96aac992fc8b
|
||||||
|
github.com/onsi/ginkgo v1.7.0 // indirect
|
||||||
|
github.com/onsi/gomega v1.4.3 // indirect
|
||||||
github.com/oracle/oci-go-sdk v1.8.0
|
github.com/oracle/oci-go-sdk v1.8.0
|
||||||
github.com/outscale/osc-go v0.0.1
|
github.com/outscale/osc-go v0.0.1
|
||||||
github.com/packer-community/winrmcp v0.0.0-20180921204643-0fd363d6159a
|
github.com/packer-community/winrmcp v0.0.0-20180921204643-0fd363d6159a
|
||||||
|
@ -133,9 +128,7 @@ require (
|
||||||
github.com/pkg/sftp v0.0.0-20160118190721-e84cc8c755ca
|
github.com/pkg/sftp v0.0.0-20160118190721-e84cc8c755ca
|
||||||
github.com/posener/complete v1.1.1
|
github.com/posener/complete v1.1.1
|
||||||
github.com/profitbricks/profitbricks-sdk-go v4.0.2+incompatible
|
github.com/profitbricks/profitbricks-sdk-go v4.0.2+incompatible
|
||||||
github.com/prometheus/client_model v0.0.0-20191202183732-d1d2010b5bee // indirect
|
|
||||||
github.com/renstrom/fuzzysearch v0.0.0-20160331204855-2d205ac6ec17 // indirect
|
github.com/renstrom/fuzzysearch v0.0.0-20160331204855-2d205ac6ec17 // indirect
|
||||||
github.com/rogpeppe/go-internal v1.5.0 // indirect
|
|
||||||
github.com/ryanuber/go-glob v0.0.0-20170128012129-256dc444b735 // indirect
|
github.com/ryanuber/go-glob v0.0.0-20170128012129-256dc444b735 // indirect
|
||||||
github.com/saintfish/chardet v0.0.0-20120816061221-3af4cd4741ca // indirect
|
github.com/saintfish/chardet v0.0.0-20120816061221-3af4cd4741ca // indirect
|
||||||
github.com/satori/go.uuid v1.2.0 // indirect
|
github.com/satori/go.uuid v1.2.0 // indirect
|
||||||
|
@ -152,7 +145,7 @@ require (
|
||||||
github.com/ufilesdk-dev/ufile-gosdk v0.0.0-20190830075812-b4dbc4ef43a6
|
github.com/ufilesdk-dev/ufile-gosdk v0.0.0-20190830075812-b4dbc4ef43a6
|
||||||
github.com/ugorji/go v0.0.0-20151218193438-646ae4a518c1
|
github.com/ugorji/go v0.0.0-20151218193438-646ae4a518c1
|
||||||
github.com/ulikunitz/xz v0.5.5
|
github.com/ulikunitz/xz v0.5.5
|
||||||
github.com/vmware/govmomi v0.0.0-20170707011325-c2105a174311
|
github.com/vmware/govmomi v0.21.0
|
||||||
github.com/xanzy/go-cloudstack v0.0.0-20190526095453-42f262b63ed0
|
github.com/xanzy/go-cloudstack v0.0.0-20190526095453-42f262b63ed0
|
||||||
github.com/yandex-cloud/go-genproto v0.0.0-20190916101622-7617782d381e
|
github.com/yandex-cloud/go-genproto v0.0.0-20190916101622-7617782d381e
|
||||||
github.com/yandex-cloud/go-sdk v0.0.0-20190916101744-c781afa45829
|
github.com/yandex-cloud/go-sdk v0.0.0-20190916101744-c781afa45829
|
||||||
|
@ -160,16 +153,14 @@ require (
|
||||||
go.opencensus.io v0.22.2 // indirect
|
go.opencensus.io v0.22.2 // indirect
|
||||||
golang.org/x/crypto v0.0.0-20191202143827-86a70503ff7e
|
golang.org/x/crypto v0.0.0-20191202143827-86a70503ff7e
|
||||||
golang.org/x/exp v0.0.0-20191129062945-2f5052295587 // indirect
|
golang.org/x/exp v0.0.0-20191129062945-2f5052295587 // indirect
|
||||||
golang.org/x/image v0.0.0-20191009234506-e7c1f5e7dbb8 // indirect
|
|
||||||
golang.org/x/lint v0.0.0-20191125180803-fdd1cda4f05f // indirect
|
golang.org/x/lint v0.0.0-20191125180803-fdd1cda4f05f // indirect
|
||||||
golang.org/x/mobile v0.0.0-20191130191448-5c0e7e404af8 // indirect
|
golang.org/x/mobile v0.0.0-20191130191448-5c0e7e404af8
|
||||||
golang.org/x/net v0.0.0-20191126235420-ef20fe5d7933
|
golang.org/x/net v0.0.0-20191126235420-ef20fe5d7933
|
||||||
golang.org/x/oauth2 v0.0.0-20191202225959-858c2ad4c8b6
|
golang.org/x/oauth2 v0.0.0-20191202225959-858c2ad4c8b6
|
||||||
golang.org/x/sync v0.0.0-20190911185100-cd5d95a43a6e
|
golang.org/x/sync v0.0.0-20190911185100-cd5d95a43a6e
|
||||||
golang.org/x/sys v0.0.0-20191128015809-6d18c012aee9
|
golang.org/x/sys v0.0.0-20191128015809-6d18c012aee9
|
||||||
golang.org/x/time v0.0.0-20191024005414-555d28b269f0 // indirect
|
golang.org/x/time v0.0.0-20191024005414-555d28b269f0 // indirect
|
||||||
golang.org/x/tools v0.0.0-20191203051722-db047d72ee39
|
golang.org/x/tools v0.0.0-20191203051722-db047d72ee39
|
||||||
golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898 // indirect
|
|
||||||
google.golang.org/api v0.14.0
|
google.golang.org/api v0.14.0
|
||||||
google.golang.org/appengine v1.6.5 // indirect
|
google.golang.org/appengine v1.6.5 // indirect
|
||||||
google.golang.org/genproto v0.0.0-20191115221424-83cc0476cb11 // indirect
|
google.golang.org/genproto v0.0.0-20191115221424-83cc0476cb11 // indirect
|
||||||
|
|
59
go.sum
59
go.sum
|
@ -9,9 +9,12 @@ cloud.google.com/go v0.46.3/go.mod h1:a6bKKbmY7er1mI7TEI4lsAkts/mkhTSZK8w33B4RAg
|
||||||
cloud.google.com/go v0.49.0 h1:CH+lkubJzcPYB1Ggupcq0+k8Ni2ILdG2lYjDIgavDBQ=
|
cloud.google.com/go v0.49.0 h1:CH+lkubJzcPYB1Ggupcq0+k8Ni2ILdG2lYjDIgavDBQ=
|
||||||
cloud.google.com/go v0.49.0/go.mod h1:hGvAdzcWNbyuxS3nWhD7H2cIJxjRRTRLQVB0bdputVY=
|
cloud.google.com/go v0.49.0/go.mod h1:hGvAdzcWNbyuxS3nWhD7H2cIJxjRRTRLQVB0bdputVY=
|
||||||
cloud.google.com/go/bigquery v1.0.1/go.mod h1:i/xbL2UlR5RvWAURpBYZTtm/cXjCha9lbfbpx4poX+o=
|
cloud.google.com/go/bigquery v1.0.1/go.mod h1:i/xbL2UlR5RvWAURpBYZTtm/cXjCha9lbfbpx4poX+o=
|
||||||
|
cloud.google.com/go/bigquery v1.3.0 h1:sAbMqjY1PEQKZBWfbu6Y6bsupJ9c4QdHnzg/VvYTLcE=
|
||||||
cloud.google.com/go/bigquery v1.3.0/go.mod h1:PjpwJnslEMmckchkHFfq+HTD2DmtT67aNFKH1/VBDHE=
|
cloud.google.com/go/bigquery v1.3.0/go.mod h1:PjpwJnslEMmckchkHFfq+HTD2DmtT67aNFKH1/VBDHE=
|
||||||
|
cloud.google.com/go/datastore v1.0.0 h1:Kt+gOPPp2LEPWp8CSfxhsM8ik9CcyE/gYu+0r+RnZvM=
|
||||||
cloud.google.com/go/datastore v1.0.0/go.mod h1:LXYbyblFSglQ5pkeyhO+Qmw7ukd3C+pD7TKLgZqpHYE=
|
cloud.google.com/go/datastore v1.0.0/go.mod h1:LXYbyblFSglQ5pkeyhO+Qmw7ukd3C+pD7TKLgZqpHYE=
|
||||||
cloud.google.com/go/pubsub v1.0.1/go.mod h1:R0Gpsv3s54REJCy4fxDixWD93lHJMoZTyQ2kNxGRt3I=
|
cloud.google.com/go/pubsub v1.0.1/go.mod h1:R0Gpsv3s54REJCy4fxDixWD93lHJMoZTyQ2kNxGRt3I=
|
||||||
|
cloud.google.com/go/pubsub v1.1.0 h1:9/vpR43S4aJaROxqQHQ3nH9lfyKKV0dC3vOmnw8ebQQ=
|
||||||
cloud.google.com/go/pubsub v1.1.0/go.mod h1:EwwdRX2sKPjnvnqCa270oGRyludottCI76h+R3AArQw=
|
cloud.google.com/go/pubsub v1.1.0/go.mod h1:EwwdRX2sKPjnvnqCa270oGRyludottCI76h+R3AArQw=
|
||||||
cloud.google.com/go/storage v1.0.0/go.mod h1:IhtSnM/ZTZV8YYJWCY8RULGVqBDmpoyjwiyrjsg+URw=
|
cloud.google.com/go/storage v1.0.0/go.mod h1:IhtSnM/ZTZV8YYJWCY8RULGVqBDmpoyjwiyrjsg+URw=
|
||||||
cloud.google.com/go/storage v1.4.0 h1:KDdqY5VTXBTqpSbctVTt0mVvfanP6JZzNzLE0qNY100=
|
cloud.google.com/go/storage v1.4.0 h1:KDdqY5VTXBTqpSbctVTt0mVvfanP6JZzNzLE0qNY100=
|
||||||
|
@ -19,15 +22,12 @@ cloud.google.com/go/storage v1.4.0/go.mod h1:ZusYJWlOshgSBGbt6K3GnB3MT3H1xs2id9+
|
||||||
contrib.go.opencensus.io/exporter/ocagent v0.5.0 h1:TKXjQSRS0/cCDrP7KvkgU6SmILtF/yV2TOs/02K/WZQ=
|
contrib.go.opencensus.io/exporter/ocagent v0.5.0 h1:TKXjQSRS0/cCDrP7KvkgU6SmILtF/yV2TOs/02K/WZQ=
|
||||||
contrib.go.opencensus.io/exporter/ocagent v0.5.0/go.mod h1:ImxhfLRpxoYiSq891pBrLVhN+qmP8BTVvdH2YLs7Gl0=
|
contrib.go.opencensus.io/exporter/ocagent v0.5.0/go.mod h1:ImxhfLRpxoYiSq891pBrLVhN+qmP8BTVvdH2YLs7Gl0=
|
||||||
dmitri.shuralyov.com/gpu/mtl v0.0.0-20190408044501-666a987793e9/go.mod h1:H6x//7gZCb22OMCxBHrMx7a5I7Hp++hsVxbQ4BYO7hU=
|
dmitri.shuralyov.com/gpu/mtl v0.0.0-20190408044501-666a987793e9/go.mod h1:H6x//7gZCb22OMCxBHrMx7a5I7Hp++hsVxbQ4BYO7hU=
|
||||||
dmitri.shuralyov.com/gpu/mtl v0.0.0-20191203043605-d42048ed14fd/go.mod h1:H6x//7gZCb22OMCxBHrMx7a5I7Hp++hsVxbQ4BYO7hU=
|
|
||||||
github.com/1and1/oneandone-cloudserver-sdk-go v1.0.1 h1:RMTyvS5bjvSWiUcfqfr/E2pxHEMrALvU+E12n6biymg=
|
github.com/1and1/oneandone-cloudserver-sdk-go v1.0.1 h1:RMTyvS5bjvSWiUcfqfr/E2pxHEMrALvU+E12n6biymg=
|
||||||
github.com/1and1/oneandone-cloudserver-sdk-go v1.0.1/go.mod h1:61apmbkVJH4kg+38ftT+/l0XxdUCVnHggqcOTqZRSEE=
|
github.com/1and1/oneandone-cloudserver-sdk-go v1.0.1/go.mod h1:61apmbkVJH4kg+38ftT+/l0XxdUCVnHggqcOTqZRSEE=
|
||||||
github.com/Azure/azure-sdk-for-go v30.0.0+incompatible h1:6o1Yzl7wTBYg+xw0pY4qnalaPmEQolubEEdepo1/kmI=
|
github.com/Azure/azure-sdk-for-go v30.0.0+incompatible h1:6o1Yzl7wTBYg+xw0pY4qnalaPmEQolubEEdepo1/kmI=
|
||||||
github.com/Azure/azure-sdk-for-go v30.0.0+incompatible/go.mod h1:9XXNKU+eRnpl9moKnB4QOLf1HestfXbmab5FXxiDBjc=
|
github.com/Azure/azure-sdk-for-go v30.0.0+incompatible/go.mod h1:9XXNKU+eRnpl9moKnB4QOLf1HestfXbmab5FXxiDBjc=
|
||||||
github.com/Azure/go-autorest v12.0.0+incompatible h1:N+VqClcomLGD/sHb3smbSYYtNMgKpVV3Cd5r5i8z6bQ=
|
github.com/Azure/go-autorest v12.0.0+incompatible h1:N+VqClcomLGD/sHb3smbSYYtNMgKpVV3Cd5r5i8z6bQ=
|
||||||
github.com/Azure/go-autorest v12.0.0+incompatible/go.mod h1:r+4oMnoxhatjLLJ6zxSWATqVooLgysK6ZNox3g/xq24=
|
github.com/Azure/go-autorest v12.0.0+incompatible/go.mod h1:r+4oMnoxhatjLLJ6zxSWATqVooLgysK6ZNox3g/xq24=
|
||||||
github.com/Azure/go-ntlmssp v0.0.0-20180810175552-4a21cbd618b4 h1:pSm8mp0T2OH2CPmPDPtwHPr3VAQaOwVF/JbllOPP4xA=
|
|
||||||
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 h1:3FwiePtHk5YJrooV799oo5jIfsgRdES25VdngJM03dU=
|
||||||
github.com/Azure/go-ntlmssp v0.0.0-20191115201650-bad6df29494a/go.mod h1:chxPXzSsl7ZWRAuOIE23GDNzjWuZquvFlgA8xmpunjU=
|
github.com/Azure/go-ntlmssp v0.0.0-20191115201650-bad6df29494a/go.mod h1:chxPXzSsl7ZWRAuOIE23GDNzjWuZquvFlgA8xmpunjU=
|
||||||
github.com/BurntSushi/toml v0.3.1 h1:WXkYYl6Yr3qBf1K79EBnL4mak0OimBfB0XUf9Vl28OQ=
|
github.com/BurntSushi/toml v0.3.1 h1:WXkYYl6Yr3qBf1K79EBnL4mak0OimBfB0XUf9Vl28OQ=
|
||||||
|
@ -41,8 +41,6 @@ github.com/PuerkitoBio/goquery v1.5.0 h1:uGvmFXOA73IKluu/F84Xd1tt/z07GYm8X49XKHP
|
||||||
github.com/PuerkitoBio/goquery v1.5.0/go.mod h1:qD2PgZ9lccMbQlc7eEOjaeRlFQON7xY8kdmcsrnKqMg=
|
github.com/PuerkitoBio/goquery v1.5.0/go.mod h1:qD2PgZ9lccMbQlc7eEOjaeRlFQON7xY8kdmcsrnKqMg=
|
||||||
github.com/StackExchange/wmi v0.0.0-20190523213315-cbe66965904d h1:G0m3OIz70MZUWq3EgK3CesDbo8upS2Vm9/P3FtgI+Jk=
|
github.com/StackExchange/wmi v0.0.0-20190523213315-cbe66965904d h1:G0m3OIz70MZUWq3EgK3CesDbo8upS2Vm9/P3FtgI+Jk=
|
||||||
github.com/StackExchange/wmi v0.0.0-20190523213315-cbe66965904d/go.mod h1:3eOhrUMpNV+6aFIbp5/iudMxNCF27Vw2OZgy4xEx0Fg=
|
github.com/StackExchange/wmi v0.0.0-20190523213315-cbe66965904d/go.mod h1:3eOhrUMpNV+6aFIbp5/iudMxNCF27Vw2OZgy4xEx0Fg=
|
||||||
github.com/Telmate/proxmox-api-go v0.0.0-20190815172943-ef9222844e60 h1:iEmbIRk4brAP3wevhCr5MGAqxHUbbIDHvE+6D1/7pRA=
|
|
||||||
github.com/Telmate/proxmox-api-go v0.0.0-20190815172943-ef9222844e60/go.mod h1:OGWyIMJ87/k/GCz8CGiWB2HOXsOVDM6Lpe/nFPkC4IQ=
|
|
||||||
github.com/Telmate/proxmox-api-go v0.0.0-20191015171801-b0c2796b9fcf h1:rVT2xsBm03Jp0r0yfGm5AMlqp0mZmxTTiNnSrc9S+Hs=
|
github.com/Telmate/proxmox-api-go v0.0.0-20191015171801-b0c2796b9fcf h1:rVT2xsBm03Jp0r0yfGm5AMlqp0mZmxTTiNnSrc9S+Hs=
|
||||||
github.com/Telmate/proxmox-api-go v0.0.0-20191015171801-b0c2796b9fcf/go.mod h1:OGWyIMJ87/k/GCz8CGiWB2HOXsOVDM6Lpe/nFPkC4IQ=
|
github.com/Telmate/proxmox-api-go v0.0.0-20191015171801-b0c2796b9fcf/go.mod h1:OGWyIMJ87/k/GCz8CGiWB2HOXsOVDM6Lpe/nFPkC4IQ=
|
||||||
github.com/abdullin/seq v0.0.0-20160510034733-d5467c17e7af h1:DBNMBMuMiWYu0b+8KMJuWmfCkcxl09JwdlqwDZZ6U14=
|
github.com/abdullin/seq v0.0.0-20160510034733-d5467c17e7af h1:DBNMBMuMiWYu0b+8KMJuWmfCkcxl09JwdlqwDZZ6U14=
|
||||||
|
@ -63,10 +61,9 @@ github.com/antchfx/xpath v0.0.0-20170728053731-b5c552e1acbd h1:S3Fr6QnkpW9VRjiEY
|
||||||
github.com/antchfx/xpath v0.0.0-20170728053731-b5c552e1acbd/go.mod h1:Yee4kTMuNiPYJ7nSNorELQMr1J33uOpXDMByNYhvtNk=
|
github.com/antchfx/xpath v0.0.0-20170728053731-b5c552e1acbd/go.mod h1:Yee4kTMuNiPYJ7nSNorELQMr1J33uOpXDMByNYhvtNk=
|
||||||
github.com/antchfx/xquery v0.0.0-20170730121040-eb8c3c172607 h1:BFFG6KP8ASFBg2ptWsJn8p8RDufBjBDKIxLU7BTYGOM=
|
github.com/antchfx/xquery v0.0.0-20170730121040-eb8c3c172607 h1:BFFG6KP8ASFBg2ptWsJn8p8RDufBjBDKIxLU7BTYGOM=
|
||||||
github.com/antchfx/xquery v0.0.0-20170730121040-eb8c3c172607/go.mod h1:LzD22aAzDP8/dyiCKFp31He4m2GPjl0AFyzDtZzUu9M=
|
github.com/antchfx/xquery v0.0.0-20170730121040-eb8c3c172607/go.mod h1:LzD22aAzDP8/dyiCKFp31He4m2GPjl0AFyzDtZzUu9M=
|
||||||
github.com/antihax/optional v0.0.0-20180407024304-ca021399b1a6 h1:uZuxRZCz65cG1o6K/xUqImNcYKtmk9ylqaH0itMSvzA=
|
|
||||||
github.com/antihax/optional v0.0.0-20180407024304-ca021399b1a6/go.mod h1:V8iCPQYkqmusNa815XgQio277wI47sdRh1dUOLdyC6Q=
|
|
||||||
github.com/antihax/optional v1.0.0 h1:xK2lYat7ZLaVVcIuj82J8kIro4V6kDe0AUDFboUCwcg=
|
github.com/antihax/optional v1.0.0 h1:xK2lYat7ZLaVVcIuj82J8kIro4V6kDe0AUDFboUCwcg=
|
||||||
github.com/antihax/optional v1.0.0/go.mod h1:uupD/76wgC+ih3iEmQUL+0Ugr19nfwCT1kdvxnR2qWY=
|
github.com/antihax/optional v1.0.0/go.mod h1:uupD/76wgC+ih3iEmQUL+0Ugr19nfwCT1kdvxnR2qWY=
|
||||||
|
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-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 h1:rRmlIsPEEhUTIKQb7T++Nz/A5Q6C9IuX2wFoYVvnCs0=
|
||||||
github.com/apparentlymart/go-textseg v1.0.0/go.mod h1:z96Txxhf3xSFMPmb5X/1W05FF/Nj9VFpLOpjS5yuumk=
|
github.com/apparentlymart/go-textseg v1.0.0/go.mod h1:z96Txxhf3xSFMPmb5X/1W05FF/Nj9VFpLOpjS5yuumk=
|
||||||
|
@ -90,7 +87,6 @@ github.com/bgentry/speakeasy v0.1.0 h1:ByYyxL9InA1OWqxJqqp2A5pYHUrCiAL6K3J+LKSsQ
|
||||||
github.com/bgentry/speakeasy v0.1.0/go.mod h1:+zsyZBPWlz7T6j88CTgSN5bM796AkVf0kBD4zp0CCIs=
|
github.com/bgentry/speakeasy v0.1.0/go.mod h1:+zsyZBPWlz7T6j88CTgSN5bM796AkVf0kBD4zp0CCIs=
|
||||||
github.com/biogo/hts v0.0.0-20160420073057-50da7d4131a3 h1:3b+p838vN4sc37brz9W2HDphtSwZFcXZwFLyzm5Vk28=
|
github.com/biogo/hts v0.0.0-20160420073057-50da7d4131a3 h1:3b+p838vN4sc37brz9W2HDphtSwZFcXZwFLyzm5Vk28=
|
||||||
github.com/biogo/hts v0.0.0-20160420073057-50da7d4131a3/go.mod h1:YOY5xnRf7Jz2SZCLSKgVfyqNzbRgyTznM3HyDqQMxcU=
|
github.com/biogo/hts v0.0.0-20160420073057-50da7d4131a3/go.mod h1:YOY5xnRf7Jz2SZCLSKgVfyqNzbRgyTznM3HyDqQMxcU=
|
||||||
github.com/bsm/go-vlq v0.0.0-20150828105119-ec6e8d4f5f4e/go.mod h1:N+BjUcTjSxc2mtRGSCPsat1kze3CUtvJN3/jTXlp29k=
|
|
||||||
github.com/c2h5oh/datasize v0.0.0-20171227191756-4eba002a5eae h1:2Zmk+8cNvAGuY8AyvZuWpUdpQUAXwfom4ReVMe/CTIo=
|
github.com/c2h5oh/datasize v0.0.0-20171227191756-4eba002a5eae h1:2Zmk+8cNvAGuY8AyvZuWpUdpQUAXwfom4ReVMe/CTIo=
|
||||||
github.com/c2h5oh/datasize v0.0.0-20171227191756-4eba002a5eae/go.mod h1:S/7n9copUssQ56c7aAgHqftWO4LTf4xY6CGWt8Bc+3M=
|
github.com/c2h5oh/datasize v0.0.0-20171227191756-4eba002a5eae/go.mod h1:S/7n9copUssQ56c7aAgHqftWO4LTf4xY6CGWt8Bc+3M=
|
||||||
github.com/census-instrumentation/opencensus-proto v0.2.0 h1:LzQXZOgg4CQfE6bFvXGM30YZL1WW/M337pXml+GrcZ4=
|
github.com/census-instrumentation/opencensus-proto v0.2.0 h1:LzQXZOgg4CQfE6bFvXGM30YZL1WW/M337pXml+GrcZ4=
|
||||||
|
@ -108,11 +104,10 @@ github.com/chzyer/test v0.0.0-20180213035817-a1ea475d72b1/go.mod h1:Q3SI9o4m/ZMn
|
||||||
github.com/client9/misspell v0.3.4/go.mod h1:qj6jICC3Q7zFZvVWo7KLAzC3yx5G7kyvSDkc90ppPyw=
|
github.com/client9/misspell v0.3.4/go.mod h1:qj6jICC3Q7zFZvVWo7KLAzC3yx5G7kyvSDkc90ppPyw=
|
||||||
github.com/creack/goselect v0.1.0 h1:4QiXIhcpSQF50XGaBsFzesjwX/1qOY5bOveQPmN9CXY=
|
github.com/creack/goselect v0.1.0 h1:4QiXIhcpSQF50XGaBsFzesjwX/1qOY5bOveQPmN9CXY=
|
||||||
github.com/creack/goselect v0.1.0/go.mod h1:gHrIcH/9UZDn2qgeTUeW5K9eZsVYCH6/60J/FHysWyE=
|
github.com/creack/goselect v0.1.0/go.mod h1:gHrIcH/9UZDn2qgeTUeW5K9eZsVYCH6/60J/FHysWyE=
|
||||||
github.com/creack/pty v1.1.7/go.mod h1:lj5s0c3V2DBrqTV7llrYr5NG6My20zk30Fl46Y7DoTY=
|
|
||||||
github.com/creack/pty v1.1.9/go.mod h1:oKZEueFk5CKHvIhNR5MUki03XCEU+Q6VDXinZuGJ33E=
|
|
||||||
github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
|
github.com/davecgh/go-spew v1.1.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 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c=
|
||||||
github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
|
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 h1:7qlOGliEKZXTDg6OTjfoBKDXWrumCAMpl/TFQ4/5kLM=
|
github.com/dgrijalva/jwt-go v3.2.0+incompatible h1:7qlOGliEKZXTDg6OTjfoBKDXWrumCAMpl/TFQ4/5kLM=
|
||||||
github.com/dgrijalva/jwt-go v3.2.0+incompatible/go.mod h1:E3ru+11k8xSBh+hMPgOLZmtrrCbhqsmaPHjLKYnJCaQ=
|
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 h1:phR13shVFOIpa1pnLBmewI9p16NEladLPvVylLPeexo=
|
github.com/digitalocean/go-libvirt v0.0.0-20190626172931-4d226dd6c437 h1:phR13shVFOIpa1pnLBmewI9p16NEladLPvVylLPeexo=
|
||||||
|
@ -134,7 +129,6 @@ github.com/dylanmei/iso8601 v0.1.0/go.mod h1:w9KhXSgIyROl1DefbMYIE7UVSIvELTbMrCf
|
||||||
github.com/dylanmei/winrmtest v0.0.0-20170819153634-c2fbb09e6c08 h1:0bp6/GrNOrTDtSXe9YYGCwf8jp5Fb/b+4a6MTRm4qzY=
|
github.com/dylanmei/winrmtest v0.0.0-20170819153634-c2fbb09e6c08 h1:0bp6/GrNOrTDtSXe9YYGCwf8jp5Fb/b+4a6MTRm4qzY=
|
||||||
github.com/dylanmei/winrmtest v0.0.0-20170819153634-c2fbb09e6c08/go.mod h1:VBVDFSBXCIW8JaHQpI8lldSKfYaLMzP9oyq6IJ4fhzY=
|
github.com/dylanmei/winrmtest v0.0.0-20170819153634-c2fbb09e6c08/go.mod h1:VBVDFSBXCIW8JaHQpI8lldSKfYaLMzP9oyq6IJ4fhzY=
|
||||||
github.com/envoyproxy/go-control-plane v0.9.0/go.mod h1:YTl/9mNaCwkRvm6d1a2C3ymFceY/DCBVvsKhRF0iEA4=
|
github.com/envoyproxy/go-control-plane v0.9.0/go.mod h1:YTl/9mNaCwkRvm6d1a2C3ymFceY/DCBVvsKhRF0iEA4=
|
||||||
github.com/envoyproxy/go-control-plane v0.9.1/go.mod h1:G1fbsNGAFpC1aaERrShZQVdUV2ZuZuv6FCl2v9JNSxQ=
|
|
||||||
github.com/envoyproxy/protoc-gen-validate v0.1.0/go.mod h1:iSmxcyjqTsJpI2R4NaDN7+kN2VEUnK/pcBlmesArF7c=
|
github.com/envoyproxy/protoc-gen-validate v0.1.0/go.mod h1:iSmxcyjqTsJpI2R4NaDN7+kN2VEUnK/pcBlmesArF7c=
|
||||||
github.com/exoscale/egoscale v0.18.1 h1:1FNZVk8jHUx0AvWhOZxLEDNlacTU0chMXUUNkm9EZaI=
|
github.com/exoscale/egoscale v0.18.1 h1:1FNZVk8jHUx0AvWhOZxLEDNlacTU0chMXUUNkm9EZaI=
|
||||||
github.com/exoscale/egoscale v0.18.1/go.mod h1:Z7OOdzzTOz1Q1PjQXumlz9Wn/CddH0zSYdCF3rnBKXE=
|
github.com/exoscale/egoscale v0.18.1/go.mod h1:Z7OOdzzTOz1Q1PjQXumlz9Wn/CddH0zSYdCF3rnBKXE=
|
||||||
|
@ -149,12 +143,12 @@ github.com/fsnotify/fsnotify v1.4.7/go.mod h1:jwhsz4b93w/PPRr/qN1Yymfu8t87LnFCMo
|
||||||
github.com/ghodss/yaml v1.0.0 h1:wQHKEahhL6wmXdzwWG11gIVCkOv05bNOh+Rxn0yngAk=
|
github.com/ghodss/yaml v1.0.0 h1:wQHKEahhL6wmXdzwWG11gIVCkOv05bNOh+Rxn0yngAk=
|
||||||
github.com/ghodss/yaml v1.0.0/go.mod h1:4dBDuWmgqj2HViK6kFavaiC9ZROes6MMH2rRYeMEF04=
|
github.com/ghodss/yaml v1.0.0/go.mod h1:4dBDuWmgqj2HViK6kFavaiC9ZROes6MMH2rRYeMEF04=
|
||||||
github.com/go-gl/glfw v0.0.0-20190409004039-e6da0acd62b1/go.mod h1:vR7hzQXu2zJy9AVAgeJqvqgH9Q5CA+iKCZ2gyEVpxRU=
|
github.com/go-gl/glfw v0.0.0-20190409004039-e6da0acd62b1/go.mod h1:vR7hzQXu2zJy9AVAgeJqvqgH9Q5CA+iKCZ2gyEVpxRU=
|
||||||
github.com/go-gl/glfw v0.0.0-20191125211704-12ad95a8df72/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-20191125211704-12ad95a8df72/go.mod h1:tQ2UAYgL5IevRw8kRxooKSPJfGvJ9fJQFa0TUsXzTg8=
|
||||||
github.com/go-ini/ini v1.25.4 h1:Mujh4R/dH6YL8bxuISne3xX2+qcQ9p0IxKAP6ExWoUo=
|
github.com/go-ini/ini v1.25.4 h1:Mujh4R/dH6YL8bxuISne3xX2+qcQ9p0IxKAP6ExWoUo=
|
||||||
github.com/go-ini/ini v1.25.4/go.mod h1:ByCAeIL28uOIIG0E3PJtZPDL8WnHpFKFOtgjp+3Ies8=
|
github.com/go-ini/ini v1.25.4/go.mod h1:ByCAeIL28uOIIG0E3PJtZPDL8WnHpFKFOtgjp+3Ies8=
|
||||||
github.com/go-ole/go-ole v1.2.4 h1:nNBDSCOigTSiarFpYE9J/KtEA1IOW4CNeqT9TQDqCxI=
|
github.com/go-ole/go-ole v1.2.4 h1:nNBDSCOigTSiarFpYE9J/KtEA1IOW4CNeqT9TQDqCxI=
|
||||||
github.com/go-ole/go-ole v1.2.4/go.mod h1:XCwSNxSkXRo4vlyPy93sltvi/qJq0jqQhjqQNIwKuxM=
|
github.com/go-ole/go-ole v1.2.4/go.mod h1:XCwSNxSkXRo4vlyPy93sltvi/qJq0jqQhjqQNIwKuxM=
|
||||||
|
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/go-test/deep v1.0.3/go.mod h1:wGDj63lr65AM2AQyKZd/NYHGb0R+1RLqB8NKt3aSFNA=
|
||||||
github.com/gobwas/glob v0.2.3 h1:A4xDbljILXROh+kObIiy5kIaPYD8e96x1tgBhUI5J+Y=
|
github.com/gobwas/glob v0.2.3 h1:A4xDbljILXROh+kObIiy5kIaPYD8e96x1tgBhUI5J+Y=
|
||||||
github.com/gobwas/glob v0.2.3/go.mod h1:d3Ez4x06l9bZtSvzIay5+Yzi0fmZzPgnTbPcKjJAkT8=
|
github.com/gobwas/glob v0.2.3/go.mod h1:d3Ez4x06l9bZtSvzIay5+Yzi0fmZzPgnTbPcKjJAkT8=
|
||||||
|
@ -200,10 +194,10 @@ github.com/google/martian v2.1.0+incompatible h1:/CP5g8u/VJHijgedC/Legn3BAbAaWPg
|
||||||
github.com/google/martian v2.1.0+incompatible/go.mod h1:9I4somxYTbIHy5NJKHRl3wXiIaQGbYVAs8BPL6v8lEs=
|
github.com/google/martian v2.1.0+incompatible/go.mod h1:9I4somxYTbIHy5NJKHRl3wXiIaQGbYVAs8BPL6v8lEs=
|
||||||
github.com/google/pprof v0.0.0-20181206194817-3ea8567a2e57/go.mod h1:zfwlbNMJ+OItoe0UupaVj+oy1omPYYDuagoSzA8v9mc=
|
github.com/google/pprof v0.0.0-20181206194817-3ea8567a2e57/go.mod h1:zfwlbNMJ+OItoe0UupaVj+oy1omPYYDuagoSzA8v9mc=
|
||||||
github.com/google/pprof v0.0.0-20190515194954-54271f7e092f/go.mod h1:zfwlbNMJ+OItoe0UupaVj+oy1omPYYDuagoSzA8v9mc=
|
github.com/google/pprof v0.0.0-20190515194954-54271f7e092f/go.mod h1:zfwlbNMJ+OItoe0UupaVj+oy1omPYYDuagoSzA8v9mc=
|
||||||
github.com/google/pprof v0.0.0-20191105193234-27840fff0d09/go.mod h1:zfwlbNMJ+OItoe0UupaVj+oy1omPYYDuagoSzA8v9mc=
|
|
||||||
github.com/google/renameio v0.1.0/go.mod h1:KWCgfxg9yswjAJkECMjeO8J8rahYeXnNhOm40UhjYkI=
|
github.com/google/renameio v0.1.0/go.mod h1:KWCgfxg9yswjAJkECMjeO8J8rahYeXnNhOm40UhjYkI=
|
||||||
github.com/google/shlex v0.0.0-20150127133951-6f45313302b9 h1:JM174NTeGNJ2m/oLH3UOWOvWQQKd+BoL3hcSCUWFLt0=
|
github.com/google/shlex v0.0.0-20150127133951-6f45313302b9 h1:JM174NTeGNJ2m/oLH3UOWOvWQQKd+BoL3hcSCUWFLt0=
|
||||||
github.com/google/shlex v0.0.0-20150127133951-6f45313302b9/go.mod h1:RpwtwJQFrIEPstU94h88MWPXP2ektJZ8cZ0YntAmXiE=
|
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.0.0 h1:b4Gk+7WdP/d3HZH8EJsZpvV7EtDOgaZLtnaNGIu1adA=
|
github.com/google/uuid v1.0.0 h1:b4Gk+7WdP/d3HZH8EJsZpvV7EtDOgaZLtnaNGIu1adA=
|
||||||
github.com/google/uuid v1.0.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
|
github.com/google/uuid v1.0.0/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.4/go.mod h1:0Wqv26UfaUD9n4G6kQubkQ+KchISgw+vpHVxEJEs9eg=
|
||||||
|
@ -223,7 +217,6 @@ github.com/grpc-ecosystem/grpc-gateway v1.8.5 h1:2+KSC78XiO6Qy0hIjfc1OD9H+hsaJdJ
|
||||||
github.com/grpc-ecosystem/grpc-gateway v1.8.5/go.mod h1:vNeuVxBJEsws4ogUvrchl83t/GYV9WGTSLVdBhOQFDY=
|
github.com/grpc-ecosystem/grpc-gateway v1.8.5/go.mod h1:vNeuVxBJEsws4ogUvrchl83t/GYV9WGTSLVdBhOQFDY=
|
||||||
github.com/hashicorp/consul v1.4.0 h1:PQTW4xCuAExEiSbhrsFsikzbW5gVBoi74BjUvYFyKHw=
|
github.com/hashicorp/consul v1.4.0 h1:PQTW4xCuAExEiSbhrsFsikzbW5gVBoi74BjUvYFyKHw=
|
||||||
github.com/hashicorp/consul v1.4.0/go.mod h1:mFrjN1mfidgJfYP1xrJCF+AfRhr6Eaqhb2+sfyn/OOI=
|
github.com/hashicorp/consul v1.4.0/go.mod h1:mFrjN1mfidgJfYP1xrJCF+AfRhr6Eaqhb2+sfyn/OOI=
|
||||||
github.com/hashicorp/errwrap v0.0.0-20180715044906-d6c0cd880357/go.mod h1:YH+1FKiLXxHSkmPseP+kNlulaMuP3n2brvKWEqk/Jc4=
|
|
||||||
github.com/hashicorp/errwrap v1.0.0 h1:hLrqtEDnRye3+sgx6z4qVLNuviH3MR5aQ0ykNJa/UYA=
|
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/errwrap v1.0.0/go.mod h1:YH+1FKiLXxHSkmPseP+kNlulaMuP3n2brvKWEqk/Jc4=
|
||||||
github.com/hashicorp/go-checkpoint v0.0.0-20171009173528-1545e56e46de h1:XDCSythtg8aWSRSO29uwhgh7b127fWr+m5SemqjSUL8=
|
github.com/hashicorp/go-checkpoint v0.0.0-20171009173528-1545e56e46de h1:XDCSythtg8aWSRSO29uwhgh7b127fWr+m5SemqjSUL8=
|
||||||
|
@ -236,7 +229,6 @@ github.com/hashicorp/go-immutable-radix v1.0.0 h1:AKDB1HM5PWEA7i4nhcpwOrO2byshxB
|
||||||
github.com/hashicorp/go-immutable-radix v1.0.0/go.mod h1:0y9vanUI8NX6FsYoO3zeMjhV/C5i9g4Q3DwcSNZ4P60=
|
github.com/hashicorp/go-immutable-radix v1.0.0/go.mod h1:0y9vanUI8NX6FsYoO3zeMjhV/C5i9g4Q3DwcSNZ4P60=
|
||||||
github.com/hashicorp/go-msgpack v0.5.3 h1:zKjpN5BK/P5lMYrLmBHdBULWbJ0XpYR+7NGzqkZzoD4=
|
github.com/hashicorp/go-msgpack v0.5.3 h1:zKjpN5BK/P5lMYrLmBHdBULWbJ0XpYR+7NGzqkZzoD4=
|
||||||
github.com/hashicorp/go-msgpack v0.5.3/go.mod h1:ahLV/dePpqEmjfWmKiqvPkv/twdG7iPBM1vqhUKIvfM=
|
github.com/hashicorp/go-msgpack v0.5.3/go.mod h1:ahLV/dePpqEmjfWmKiqvPkv/twdG7iPBM1vqhUKIvfM=
|
||||||
github.com/hashicorp/go-multierror v0.0.0-20180717150148-3d5d8f294aa0/go.mod h1:JMRHfdO9jKNzS/+BTlxCjKNQHg/jZAft8U7LloJvN7I=
|
|
||||||
github.com/hashicorp/go-multierror v1.0.0 h1:iVjPR7a6H0tWELX5NxNe7bYopibicUzc7uPribsnS6o=
|
github.com/hashicorp/go-multierror v1.0.0 h1:iVjPR7a6H0tWELX5NxNe7bYopibicUzc7uPribsnS6o=
|
||||||
github.com/hashicorp/go-multierror v1.0.0/go.mod h1:dHtQlpGsu+cZNNAkkCN/P3hoUDHhCYQXV3UM06sGGrk=
|
github.com/hashicorp/go-multierror v1.0.0/go.mod h1:dHtQlpGsu+cZNNAkkCN/P3hoUDHhCYQXV3UM06sGGrk=
|
||||||
github.com/hashicorp/go-oracle-terraform v0.0.0-20181016190316-007121241b79 h1:RKu7yAXZTaQsxj1K9GDsh+QVw0+Wu1SWHxtbFN0n+hE=
|
github.com/hashicorp/go-oracle-terraform v0.0.0-20181016190316-007121241b79 h1:RKu7yAXZTaQsxj1K9GDsh+QVw0+Wu1SWHxtbFN0n+hE=
|
||||||
|
@ -253,7 +245,6 @@ github.com/hashicorp/go-syslog v1.0.0/go.mod h1:qPfqrKkXGihmCqbJM2mZgkZGvKG1dFdv
|
||||||
github.com/hashicorp/go-uuid v1.0.0/go.mod h1:6SBZvOh/SIDV7/2o3Jml5SYk/TvGqwFJ/bN7x4byOro=
|
github.com/hashicorp/go-uuid v1.0.0/go.mod h1:6SBZvOh/SIDV7/2o3Jml5SYk/TvGqwFJ/bN7x4byOro=
|
||||||
github.com/hashicorp/go-uuid v1.0.1 h1:fv1ep09latC32wFoVwnqcnKJGnMSdBanPczbHAYm1BE=
|
github.com/hashicorp/go-uuid v1.0.1 h1:fv1ep09latC32wFoVwnqcnKJGnMSdBanPczbHAYm1BE=
|
||||||
github.com/hashicorp/go-uuid v1.0.1/go.mod h1:6SBZvOh/SIDV7/2o3Jml5SYk/TvGqwFJ/bN7x4byOro=
|
github.com/hashicorp/go-uuid v1.0.1/go.mod h1:6SBZvOh/SIDV7/2o3Jml5SYk/TvGqwFJ/bN7x4byOro=
|
||||||
github.com/hashicorp/go-version v1.0.0/go.mod h1:fltr4n8CU8Ke44wwGCBoEymUuxUHl09ZGVZPK5anwXA=
|
|
||||||
github.com/hashicorp/go-version v1.1.0 h1:bPIoEKD27tNdebFGGxxYwcL4nepeY4j1QP23PFRGzg0=
|
github.com/hashicorp/go-version v1.1.0 h1:bPIoEKD27tNdebFGGxxYwcL4nepeY4j1QP23PFRGzg0=
|
||||||
github.com/hashicorp/go-version v1.1.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 h1:3vNe/fWF5CBgRIguda1meWhsZHy3m8gCJ5wx+dIzX/E=
|
||||||
|
@ -269,8 +260,6 @@ 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 v1.0.0/go.mod h1:E5yfLk+7swimpb2L/Alb/PJmXilQ/rhwaUYs4T20WEQ=
|
||||||
github.com/hashicorp/hcl/v2 v2.0.0 h1:efQznTz+ydmQXq3BOnRa3AXzvCeTq1P4dKj/z5GLlY8=
|
github.com/hashicorp/hcl/v2 v2.0.0 h1:efQznTz+ydmQXq3BOnRa3AXzvCeTq1P4dKj/z5GLlY8=
|
||||||
github.com/hashicorp/hcl/v2 v2.0.0/go.mod h1:oVVDG71tEinNGYCxinCYadcmKU9bglqW9pV3txagJ90=
|
github.com/hashicorp/hcl/v2 v2.0.0/go.mod h1:oVVDG71tEinNGYCxinCYadcmKU9bglqW9pV3txagJ90=
|
||||||
github.com/hashicorp/hcl2 v0.0.0-20191002203319-fb75b3253c80 h1:PFfGModn55JA0oBsvFghhj0v93me+Ctr3uHC/UmFAls=
|
|
||||||
github.com/hashicorp/hcl2 v0.0.0-20191002203319-fb75b3253c80/go.mod h1:Cxv+IJLuBiEhQ7pBYGEuORa0nr4U994pE8mYLuFd7v0=
|
|
||||||
github.com/hashicorp/logutils v1.0.0/go.mod h1:QIAnNjmIWmVIIkWDTG1z5v++HQmx9WQRO+LraFDTW64=
|
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.0/go.mod h1:tL+uN++7HEJ6SQLQ2/p+z2pH24WQKWjBPkE0mNTz8vQ=
|
||||||
github.com/hashicorp/memberlist v0.1.3 h1:EmmoJme1matNzb+hMpDuR/0sbJSUisxyqBGG676r31M=
|
github.com/hashicorp/memberlist v0.1.3 h1:EmmoJme1matNzb+hMpDuR/0sbJSUisxyqBGG676r31M=
|
||||||
|
@ -285,17 +274,12 @@ github.com/hetznercloud/hcloud-go v1.15.1 h1:G8Q+xyAqQ5IUY7yq4HKZgkabFa0S/VXJXq3
|
||||||
github.com/hetznercloud/hcloud-go v1.15.1/go.mod h1:8lR3yHBHZWy2uGcUi9Ibt4UOoop2wrVdERJgCtxsF3Q=
|
github.com/hetznercloud/hcloud-go v1.15.1/go.mod h1:8lR3yHBHZWy2uGcUi9Ibt4UOoop2wrVdERJgCtxsF3Q=
|
||||||
github.com/hpcloud/tail v1.0.0 h1:nfCOvKYfkgYP8hkirhJocXT2+zOD8yUNjXaWfTlyFKI=
|
github.com/hpcloud/tail v1.0.0 h1:nfCOvKYfkgYP8hkirhJocXT2+zOD8yUNjXaWfTlyFKI=
|
||||||
github.com/hpcloud/tail v1.0.0/go.mod h1:ab1qPbhIpdTxEkNHXyeSf5vhxWSCs/tWer42PpOxQnU=
|
github.com/hpcloud/tail v1.0.0/go.mod h1:ab1qPbhIpdTxEkNHXyeSf5vhxWSCs/tWer42PpOxQnU=
|
||||||
github.com/hyperonecom/h1-client-go v0.0.0-20190122232013-cf38e8387775 h1:MIteIoIQ5nFoOmwEHPDsqng8d0dtKj3lCnQCwGvtxXc=
|
|
||||||
github.com/hyperonecom/h1-client-go v0.0.0-20190122232013-cf38e8387775/go.mod h1:R9rU87RxxmcD3DkspW9JqGBXiJyg5MA+WNCtJrBtnXs=
|
|
||||||
github.com/hyperonecom/h1-client-go v0.0.0-20190913175216-d19ba8b5e876 h1:7GEvE+OUG2Vhl/fgSns4Wg2kTUJtDyhcS57ZSVpjB1Q=
|
|
||||||
github.com/hyperonecom/h1-client-go v0.0.0-20190913175216-d19ba8b5e876/go.mod h1:BwyQSLvalkGOjxkAgoWgTocPoN+ZDUodCruMhtIrwQs=
|
|
||||||
github.com/hyperonecom/h1-client-go v0.0.0-20191203060043-b46280e4c4a4 h1:mSmyzhwBeQt2TlHbsXYLona9pwjWAvYGwQJ2Cq/k3VE=
|
github.com/hyperonecom/h1-client-go v0.0.0-20191203060043-b46280e4c4a4 h1:mSmyzhwBeQt2TlHbsXYLona9pwjWAvYGwQJ2Cq/k3VE=
|
||||||
github.com/hyperonecom/h1-client-go v0.0.0-20191203060043-b46280e4c4a4/go.mod h1:yNUVHSleURKSaYUKq4Wx0i/vjCen2aq7CvPyHd/Vj2Q=
|
github.com/hyperonecom/h1-client-go v0.0.0-20191203060043-b46280e4c4a4/go.mod h1:yNUVHSleURKSaYUKq4Wx0i/vjCen2aq7CvPyHd/Vj2Q=
|
||||||
github.com/jdcloud-api/jdcloud-sdk-go v1.9.1-0.20190605102154-3d81a50ca961 h1:a2/K4HRhg31A5vafiz5yYiGMjaCxwRpyjJStfVquKds=
|
github.com/jdcloud-api/jdcloud-sdk-go v1.9.1-0.20190605102154-3d81a50ca961 h1:a2/K4HRhg31A5vafiz5yYiGMjaCxwRpyjJStfVquKds=
|
||||||
github.com/jdcloud-api/jdcloud-sdk-go v1.9.1-0.20190605102154-3d81a50ca961/go.mod h1:UrKjuULIWLjHFlG6aSPunArE5QX57LftMmStAZJBEX8=
|
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 h1:IPJ3dvxmJ4uczJe5YQdrYB16oTJlGSC/OyZDqUk9xX4=
|
||||||
github.com/jehiah/go-strftime v0.0.0-20171201141054-1d33003b3869/go.mod h1:cJ6Cj7dQo+O6GJNiMx+Pa94qKj+TG8ONdKHgMNIyyag=
|
github.com/jehiah/go-strftime v0.0.0-20171201141054-1d33003b3869/go.mod h1:cJ6Cj7dQo+O6GJNiMx+Pa94qKj+TG8ONdKHgMNIyyag=
|
||||||
github.com/jessevdk/go-flags v1.4.0/go.mod h1:4FA24M0QyGHXBuZZK/XkWh8h0e1EYbRYJSGM75WSRxI=
|
|
||||||
github.com/jmespath/go-jmespath v0.0.0-20160202185014-0b12d6b521d8/go.mod h1:Nht3zPeWKUH0NzdCt2Blrr5ys8VGpn0CEB0cQHVjt7k=
|
github.com/jmespath/go-jmespath v0.0.0-20160202185014-0b12d6b521d8/go.mod h1:Nht3zPeWKUH0NzdCt2Blrr5ys8VGpn0CEB0cQHVjt7k=
|
||||||
github.com/jmespath/go-jmespath v0.0.0-20180206201540-c2b33e8439af h1:pmfjZENx5imkbgOkpRUYLnmbU7UEFbjtDA2hxJ1ichM=
|
github.com/jmespath/go-jmespath v0.0.0-20180206201540-c2b33e8439af h1:pmfjZENx5imkbgOkpRUYLnmbU7UEFbjtDA2hxJ1ichM=
|
||||||
github.com/jmespath/go-jmespath v0.0.0-20180206201540-c2b33e8439af/go.mod h1:Nht3zPeWKUH0NzdCt2Blrr5ys8VGpn0CEB0cQHVjt7k=
|
github.com/jmespath/go-jmespath v0.0.0-20180206201540-c2b33e8439af/go.mod h1:Nht3zPeWKUH0NzdCt2Blrr5ys8VGpn0CEB0cQHVjt7k=
|
||||||
|
@ -329,7 +313,6 @@ github.com/kr/fs v0.0.0-20131111012553-2788f0dbd169/go.mod h1:glhvuHOU9Hy7/8Pwwd
|
||||||
github.com/kr/pretty v0.1.0 h1:L/CwN0zerZDmRFUapSPitk6f+Q3+0za1rQkzVuMiMFI=
|
github.com/kr/pretty v0.1.0 h1:L/CwN0zerZDmRFUapSPitk6f+Q3+0za1rQkzVuMiMFI=
|
||||||
github.com/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORNo=
|
github.com/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORNo=
|
||||||
github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ=
|
github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ=
|
||||||
github.com/kr/pty v1.1.8/go.mod h1:O1sed60cT9XZ5uDucP5qwvh+TE3NnUj51EiZO/lmSfw=
|
|
||||||
github.com/kr/text v0.1.0 h1:45sCR5RtlFHMR4UwH9sdQ5TC8v0qDQCHnXt+kaKSTVE=
|
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/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 h1:MtvEpTB6LX3vkb4ax0b5D2DHbNAUsen0Gx5wZoq3lV4=
|
||||||
|
@ -351,8 +334,6 @@ github.com/mattn/go-isatty v0.0.10 h1:qxFzApOv4WsAL965uUPIsXzAKCZxN2p9UqdhFS4ZW1
|
||||||
github.com/mattn/go-isatty v0.0.10/go.mod h1:qgIWMr58cqv1PHHyhnkY9lrL7etaEgOFcMEpPG5Rm84=
|
github.com/mattn/go-isatty v0.0.10/go.mod h1:qgIWMr58cqv1PHHyhnkY9lrL7etaEgOFcMEpPG5Rm84=
|
||||||
github.com/mattn/go-runewidth v0.0.4 h1:2BvfKmzob6Bmd4YsL0zygOqfdFnK7GR4QL06Do4/p7Y=
|
github.com/mattn/go-runewidth v0.0.4 h1:2BvfKmzob6Bmd4YsL0zygOqfdFnK7GR4QL06Do4/p7Y=
|
||||||
github.com/mattn/go-runewidth v0.0.4/go.mod h1:LwmH8dsx7+W8Uxz3IHJYH5QSwggIsqBzpuz5H//U1FU=
|
github.com/mattn/go-runewidth v0.0.4/go.mod h1:LwmH8dsx7+W8Uxz3IHJYH5QSwggIsqBzpuz5H//U1FU=
|
||||||
github.com/mattn/go-tty v0.0.0-20190424173100-523744f04859 h1:smQbSzmT3EHl4EUwtFwFGmGIpiYgIiiPeVv1uguIQEE=
|
|
||||||
github.com/mattn/go-tty v0.0.0-20190424173100-523744f04859/go.mod h1:XPvLUNfbS4fJH25nqRHfWLMa1ONC8Amw+mIA639KxkE=
|
|
||||||
github.com/mattn/go-tty v0.0.0-20191112051231-74040eebce08 h1:8YAWbq7rJqfbc6IaAvA2eCQuOQvf6Bs4vHKcOyWw//E=
|
github.com/mattn/go-tty v0.0.0-20191112051231-74040eebce08 h1:8YAWbq7rJqfbc6IaAvA2eCQuOQvf6Bs4vHKcOyWw//E=
|
||||||
github.com/mattn/go-tty v0.0.0-20191112051231-74040eebce08/go.mod h1:XPvLUNfbS4fJH25nqRHfWLMa1ONC8Amw+mIA639KxkE=
|
github.com/mattn/go-tty v0.0.0-20191112051231-74040eebce08/go.mod h1:XPvLUNfbS4fJH25nqRHfWLMa1ONC8Amw+mIA639KxkE=
|
||||||
github.com/miekg/dns v1.0.14/go.mod h1:W1PPwlIAgtquWBMBEV9nkV9Cazfe8ScdGz/Lj7v3Nrg=
|
github.com/miekg/dns v1.0.14/go.mod h1:W1PPwlIAgtquWBMBEV9nkV9Cazfe8ScdGz/Lj7v3Nrg=
|
||||||
|
@ -371,8 +352,6 @@ github.com/mitchellh/go-vnc v0.0.0-20150629162542-723ed9867aed/go.mod h1:3rdaFaC
|
||||||
github.com/mitchellh/go-wordwrap v0.0.0-20150314170334-ad45545899c7 h1:DpOJ2HYzCv8LZP15IdmG+YdwD2luVPHITV96TkirNBM=
|
github.com/mitchellh/go-wordwrap v0.0.0-20150314170334-ad45545899c7 h1:DpOJ2HYzCv8LZP15IdmG+YdwD2luVPHITV96TkirNBM=
|
||||||
github.com/mitchellh/go-wordwrap v0.0.0-20150314170334-ad45545899c7/go.mod h1:ZXFpozHsX6DPmq2I0TCekCxypsnAUbP2oI0UX1GXzOo=
|
github.com/mitchellh/go-wordwrap v0.0.0-20150314170334-ad45545899c7/go.mod h1:ZXFpozHsX6DPmq2I0TCekCxypsnAUbP2oI0UX1GXzOo=
|
||||||
github.com/mitchellh/gox v0.4.0/go.mod h1:Sd9lOJ0+aimLBi73mGofS1ycjY8lL3uZM3JPS42BGNg=
|
github.com/mitchellh/gox v0.4.0/go.mod h1:Sd9lOJ0+aimLBi73mGofS1ycjY8lL3uZM3JPS42BGNg=
|
||||||
github.com/mitchellh/gox v1.0.1 h1:x0jD3dcHk9a9xPSDN6YEL4xL6Qz0dvNYm8yZqui5chI=
|
|
||||||
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 h1:C+X3KsSTLFVBr/tK1eYN/vs4rJcvsiLU338UhYPJWeY=
|
||||||
github.com/mitchellh/iochan v1.0.0/go.mod h1:JwYml1nuB7xOzsp52dPpHFffvOCDupsG0QubkSMEySY=
|
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 v0.0.0-20160808181253-ca63d7c062ee/go.mod h1:FVVH3fgwuzCH5S8UJGiWEs2h04kUh9fWfEaFds41c1Y=
|
||||||
|
@ -427,12 +406,10 @@ github.com/posener/complete v1.1.1/go.mod h1:em0nMJCgc9GFtwrmVmEMR/ZL6WyhyjMBndr
|
||||||
github.com/profitbricks/profitbricks-sdk-go v4.0.2+incompatible h1:ZoVHH6voxW9Onzo6z2yLtocVoN6mBocyDoqoyAMHokE=
|
github.com/profitbricks/profitbricks-sdk-go v4.0.2+incompatible h1:ZoVHH6voxW9Onzo6z2yLtocVoN6mBocyDoqoyAMHokE=
|
||||||
github.com/profitbricks/profitbricks-sdk-go v4.0.2+incompatible/go.mod h1:T3/WrziK7fYH3C8ilAFAHe99R452/IzIG3YYkqaOFeQ=
|
github.com/profitbricks/profitbricks-sdk-go v4.0.2+incompatible/go.mod h1:T3/WrziK7fYH3C8ilAFAHe99R452/IzIG3YYkqaOFeQ=
|
||||||
github.com/prometheus/client_model v0.0.0-20190812154241-14fe0d1b01d4/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA=
|
github.com/prometheus/client_model v0.0.0-20190812154241-14fe0d1b01d4/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA=
|
||||||
github.com/prometheus/client_model v0.0.0-20191202183732-d1d2010b5bee/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA=
|
|
||||||
github.com/renstrom/fuzzysearch v0.0.0-20160331204855-2d205ac6ec17 h1:4qPms2txLWMLXKzqlnYSulKRS4cS9aYgPtAEpUelQok=
|
github.com/renstrom/fuzzysearch v0.0.0-20160331204855-2d205ac6ec17 h1:4qPms2txLWMLXKzqlnYSulKRS4cS9aYgPtAEpUelQok=
|
||||||
github.com/renstrom/fuzzysearch v0.0.0-20160331204855-2d205ac6ec17/go.mod h1:SAEjPB4voP88qmWJXI7mA5m15uNlEnuHLx4Eu2mPGpQ=
|
github.com/renstrom/fuzzysearch v0.0.0-20160331204855-2d205ac6ec17/go.mod h1:SAEjPB4voP88qmWJXI7mA5m15uNlEnuHLx4Eu2mPGpQ=
|
||||||
github.com/rogpeppe/fastuuid v0.0.0-20150106093220-6724a57986af/go.mod h1:XWv6SoW27p1b0cqNHllgS5HIMJraePCO15w5zCzIWYg=
|
github.com/rogpeppe/fastuuid v0.0.0-20150106093220-6724a57986af/go.mod h1:XWv6SoW27p1b0cqNHllgS5HIMJraePCO15w5zCzIWYg=
|
||||||
github.com/rogpeppe/go-internal v1.3.0/go.mod h1:M8bDsm7K2OlrFYOpmOWEs/qY81heoFRclV5y23lUDJ4=
|
github.com/rogpeppe/go-internal v1.3.0/go.mod h1:M8bDsm7K2OlrFYOpmOWEs/qY81heoFRclV5y23lUDJ4=
|
||||||
github.com/rogpeppe/go-internal v1.5.0/go.mod h1:xXDCJY+GAPziupqXw64V24skbSoqbTEfhy4qGm1nDQc=
|
|
||||||
github.com/ryanuber/columnize v0.0.0-20160712163229-9b3edd62028f/go.mod h1:sm1tb6uqfes/u+d4ooFouqFdy9/2g9QGwK3SQygK0Ts=
|
github.com/ryanuber/columnize v0.0.0-20160712163229-9b3edd62028f/go.mod h1:sm1tb6uqfes/u+d4ooFouqFdy9/2g9QGwK3SQygK0Ts=
|
||||||
github.com/ryanuber/go-glob v0.0.0-20170128012129-256dc444b735 h1:7YvPJVmEeFHR1Tj9sZEYsmarJEQfMVYpd/Vyy/A8dqE=
|
github.com/ryanuber/go-glob v0.0.0-20170128012129-256dc444b735 h1:7YvPJVmEeFHR1Tj9sZEYsmarJEQfMVYpd/Vyy/A8dqE=
|
||||||
github.com/ryanuber/go-glob v0.0.0-20170128012129-256dc444b735/go.mod h1:807d1WSdnB0XRJzKNil9Om6lcp/3a0v4qIHxIXzX/Yc=
|
github.com/ryanuber/go-glob v0.0.0-20170128012129-256dc444b735/go.mod h1:807d1WSdnB0XRJzKNil9Om6lcp/3a0v4qIHxIXzX/Yc=
|
||||||
|
@ -444,6 +421,7 @@ github.com/scaleway/scaleway-cli v0.0.0-20180921094345-7b12c9699d70 h1:DaqC32ZwO
|
||||||
github.com/scaleway/scaleway-cli v0.0.0-20180921094345-7b12c9699d70/go.mod h1:XjlXWPd6VONhsRSEuzGkV8mzRpH7ou1cdLV7IKJk96s=
|
github.com/scaleway/scaleway-cli v0.0.0-20180921094345-7b12c9699d70/go.mod h1:XjlXWPd6VONhsRSEuzGkV8mzRpH7ou1cdLV7IKJk96s=
|
||||||
github.com/sean-/seed v0.0.0-20170313163322-e2103e2c3529 h1:nn5Wsu0esKSJiIVhscUtVbo7ada43DJhG55ua/hjS5I=
|
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/sean-/seed v0.0.0-20170313163322-e2103e2c3529/go.mod h1:DxrIzT+xaE7yg65j358z/aeFdxmN0P9QXhEzd20vsDc=
|
||||||
|
github.com/sergi/go-diff v1.0.0 h1:Kpca3qRNrduNnOQeazBd0ysaKrUJiIuISHxogkT9RPQ=
|
||||||
github.com/sergi/go-diff v1.0.0/go.mod h1:0CfEIISq7TuYL3j771MWULgwwjU+GofnZX9QAmXWZgo=
|
github.com/sergi/go-diff v1.0.0/go.mod h1:0CfEIISq7TuYL3j771MWULgwwjU+GofnZX9QAmXWZgo=
|
||||||
github.com/shirou/gopsutil v2.18.12+incompatible h1:1eaJvGomDnH74/5cF4CTmTbLHAriGFsTZppLXDX93OM=
|
github.com/shirou/gopsutil v2.18.12+incompatible h1:1eaJvGomDnH74/5cF4CTmTbLHAriGFsTZppLXDX93OM=
|
||||||
github.com/shirou/gopsutil v2.18.12+incompatible/go.mod h1:5b4v6he4MtMOwMlS0TUMTu2PcXUg8+E1lC7eC3UO/RA=
|
github.com/shirou/gopsutil v2.18.12+incompatible/go.mod h1:5b4v6he4MtMOwMlS0TUMTu2PcXUg8+E1lC7eC3UO/RA=
|
||||||
|
@ -461,6 +439,7 @@ github.com/spf13/pflag v1.0.2/go.mod h1:DYY7MBk1bdzusC3SYhjObp+wFpr4gzcvqqNjLnIn
|
||||||
github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
|
github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
|
||||||
github.com/stretchr/objx v0.1.1 h1:2vfRuCMp5sSVIDSqO8oNnWJq7mPa6KVP3iPIwFBuy8A=
|
github.com/stretchr/objx v0.1.1 h1:2vfRuCMp5sSVIDSqO8oNnWJq7mPa6KVP3iPIwFBuy8A=
|
||||||
github.com/stretchr/objx v0.1.1/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 h1:Hbg2NidpLE8veEBkEZTL3CvlkUIVzuU9jDplZO54c48=
|
||||||
github.com/stretchr/objx v0.2.0/go.mod h1:qt09Ya8vawLte6SNmTgCsAVtYtaKzEcn8ATUoHMkEqE=
|
github.com/stretchr/objx v0.2.0/go.mod h1:qt09Ya8vawLte6SNmTgCsAVtYtaKzEcn8ATUoHMkEqE=
|
||||||
github.com/stretchr/testify v1.2.2 h1:bSDNvY7ZPG5RlJ8otE/7V6gMiyenm9RtJ7IUVIAoJ1w=
|
github.com/stretchr/testify v1.2.2 h1:bSDNvY7ZPG5RlJ8otE/7V6gMiyenm9RtJ7IUVIAoJ1w=
|
||||||
github.com/stretchr/testify v1.2.2/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs=
|
github.com/stretchr/testify v1.2.2/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs=
|
||||||
|
@ -472,8 +451,6 @@ github.com/temoto/robotstxt v1.1.1 h1:Gh8RCs8ouX3hRSxxK7B1mO5RFByQ4CmJZDwgom++Ja
|
||||||
github.com/temoto/robotstxt v1.1.1/go.mod h1:+1AmkuG3IYkh1kv0d2qEB9Le88ehNO0zwOr3ujewlOo=
|
github.com/temoto/robotstxt v1.1.1/go.mod h1:+1AmkuG3IYkh1kv0d2qEB9Le88ehNO0zwOr3ujewlOo=
|
||||||
github.com/tencentcloud/tencentcloud-sdk-go v3.0.97+incompatible h1:y2gZtLpcWqFzSFbQSKwv1gL+NocPRM0ktGh7Dlb8U7s=
|
github.com/tencentcloud/tencentcloud-sdk-go v3.0.97+incompatible h1:y2gZtLpcWqFzSFbQSKwv1gL+NocPRM0ktGh7Dlb8U7s=
|
||||||
github.com/tencentcloud/tencentcloud-sdk-go v3.0.97+incompatible/go.mod h1:0PfYow01SHPMhKY31xa+EFz2RStxIqj6JFAJS+IkCi4=
|
github.com/tencentcloud/tencentcloud-sdk-go v3.0.97+incompatible/go.mod h1:0PfYow01SHPMhKY31xa+EFz2RStxIqj6JFAJS+IkCi4=
|
||||||
github.com/ucloud/ucloud-sdk-go v0.8.7 h1:BmXOb5RivI0Uu4oZRpjI6SQ9/y7n/H9wxTGR1txIE8o=
|
|
||||||
github.com/ucloud/ucloud-sdk-go v0.8.7/go.mod h1:lM6fpI8y6iwACtlbHUav823/uKPdXsNBlnBpRF2fj3c=
|
|
||||||
github.com/ucloud/ucloud-sdk-go v0.12.0 h1:VCFN3jWg/G4wvwjG6qG5AhFuAT1JdmGvY6+4WHbuJcw=
|
github.com/ucloud/ucloud-sdk-go v0.12.0 h1:VCFN3jWg/G4wvwjG6qG5AhFuAT1JdmGvY6+4WHbuJcw=
|
||||||
github.com/ucloud/ucloud-sdk-go v0.12.0/go.mod h1:lM6fpI8y6iwACtlbHUav823/uKPdXsNBlnBpRF2fj3c=
|
github.com/ucloud/ucloud-sdk-go v0.12.0/go.mod h1:lM6fpI8y6iwACtlbHUav823/uKPdXsNBlnBpRF2fj3c=
|
||||||
github.com/ufilesdk-dev/ufile-gosdk v0.0.0-20190830075812-b4dbc4ef43a6 h1:FAWNiqocJ04wC4Znj7Ax4PGWstZijayO6ifuHHvb+vI=
|
github.com/ufilesdk-dev/ufile-gosdk v0.0.0-20190830075812-b4dbc4ef43a6 h1:FAWNiqocJ04wC4Znj7Ax4PGWstZijayO6ifuHHvb+vI=
|
||||||
|
@ -484,19 +461,17 @@ 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.5/go.mod h1:2bypXElzHzzJZwzH67Y6wb67pO62Rzfn7BSiF4ABRW8=
|
||||||
github.com/vmihailenco/msgpack v3.3.3+incompatible h1:wapg9xDUZDzGCNFlwc5SqI1rvcciqcxEHac4CYj89xI=
|
github.com/vmihailenco/msgpack v3.3.3+incompatible h1:wapg9xDUZDzGCNFlwc5SqI1rvcciqcxEHac4CYj89xI=
|
||||||
github.com/vmihailenco/msgpack v3.3.3+incompatible/go.mod h1:fy3FlTQTDXWkZ7Bh6AcGMlsjHatGryHQYUTf1ShIgkk=
|
github.com/vmihailenco/msgpack v3.3.3+incompatible/go.mod h1:fy3FlTQTDXWkZ7Bh6AcGMlsjHatGryHQYUTf1ShIgkk=
|
||||||
github.com/vmware/govmomi v0.0.0-20170707011325-c2105a174311 h1:s5pyxd5S6wRs2WpEE0xRfWUF46Wbz44h203KnbX0ecI=
|
github.com/vmware/govmomi v0.21.0 h1:jc8uMuxpcV2xMAA/cnEDlnsIjvqcMra5Y8onh/U3VuY=
|
||||||
github.com/vmware/govmomi v0.0.0-20170707011325-c2105a174311/go.mod h1:URlwyTFZX72RmxtxuaFL2Uj3fD1JTvZdx59bHWk6aFU=
|
github.com/vmware/govmomi v0.21.0/go.mod h1:zbnFoBQ9GIjs2RVETy8CNEpb+L+Lwkjs3XZUL0B3/m0=
|
||||||
|
github.com/vmware/vmw-guestinfo v0.0.0-20170707015358-25eff159a728/go.mod h1:x9oS4Wk2s2u4tS29nEaDLdzvuHdB19CvSGJjPgkZJNk=
|
||||||
github.com/xanzy/go-cloudstack v0.0.0-20190526095453-42f262b63ed0 h1:NJrcIkdzq0C3I8ypAZwFE9RHtGbfp+mJvqIcoFATZuk=
|
github.com/xanzy/go-cloudstack v0.0.0-20190526095453-42f262b63ed0 h1:NJrcIkdzq0C3I8ypAZwFE9RHtGbfp+mJvqIcoFATZuk=
|
||||||
github.com/xanzy/go-cloudstack v0.0.0-20190526095453-42f262b63ed0/go.mod h1:sBh287mCRwCz6zyXHMmw7sSZGPohVpnx+o+OY4M+i3A=
|
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-20190916101622-7617782d381e h1:hzwq5GUKP0aQzDja1XP4sBYyOmnezs/RVtzP+xiLbfI=
|
github.com/yandex-cloud/go-genproto v0.0.0-20190916101622-7617782d381e h1:hzwq5GUKP0aQzDja1XP4sBYyOmnezs/RVtzP+xiLbfI=
|
||||||
github.com/yandex-cloud/go-genproto v0.0.0-20190916101622-7617782d381e/go.mod h1:HEUYX/p8966tMUHHT+TsS0hF/Ca/NYwqprC5WXSDMfE=
|
github.com/yandex-cloud/go-genproto v0.0.0-20190916101622-7617782d381e/go.mod h1:HEUYX/p8966tMUHHT+TsS0hF/Ca/NYwqprC5WXSDMfE=
|
||||||
github.com/yandex-cloud/go-sdk v0.0.0-20190916101744-c781afa45829 h1:2FGwbx03GpP1Ulzg/L46tSoKh9t4yg8BhMKQl/Ff1x8=
|
github.com/yandex-cloud/go-sdk v0.0.0-20190916101744-c781afa45829 h1:2FGwbx03GpP1Ulzg/L46tSoKh9t4yg8BhMKQl/Ff1x8=
|
||||||
github.com/yandex-cloud/go-sdk v0.0.0-20190916101744-c781afa45829/go.mod h1:Eml0jFLU4VVHgIN8zPHMuNwZXVzUMILyO6lQZSfz854=
|
github.com/yandex-cloud/go-sdk v0.0.0-20190916101744-c781afa45829/go.mod h1:Eml0jFLU4VVHgIN8zPHMuNwZXVzUMILyO6lQZSfz854=
|
||||||
github.com/zclconf/go-cty v1.0.0/go.mod h1:xnAOWiHeOqg2nWS62VtQ7pbOu17FtxJNW8RLEih+O3s=
|
|
||||||
github.com/zclconf/go-cty v1.1.0 h1:uJwc9HiBOCpoKIObTQaLR+tsEXx1HBHnOsOOpcdhZgw=
|
github.com/zclconf/go-cty v1.1.0 h1:uJwc9HiBOCpoKIObTQaLR+tsEXx1HBHnOsOOpcdhZgw=
|
||||||
github.com/zclconf/go-cty v1.1.0/go.mod h1:xnAOWiHeOqg2nWS62VtQ7pbOu17FtxJNW8RLEih+O3s=
|
github.com/zclconf/go-cty v1.1.0/go.mod h1:xnAOWiHeOqg2nWS62VtQ7pbOu17FtxJNW8RLEih+O3s=
|
||||||
github.com/zclconf/go-cty v1.1.1 h1:Shl2p9Dat0cqJfXu0DZa+cOTRPhXQjK8IYWD6GVfiqo=
|
|
||||||
github.com/zclconf/go-cty v1.1.1/go.mod h1:xnAOWiHeOqg2nWS62VtQ7pbOu17FtxJNW8RLEih+O3s=
|
|
||||||
github.com/zclconf/go-cty v1.1.2-0.20191126233707-f0f7fd24c4af h1:4arg31xOP/qIUV1YVbCWJtChPGzwGzgmlucVbddUq+Y=
|
github.com/zclconf/go-cty v1.1.2-0.20191126233707-f0f7fd24c4af h1:4arg31xOP/qIUV1YVbCWJtChPGzwGzgmlucVbddUq+Y=
|
||||||
github.com/zclconf/go-cty v1.1.2-0.20191126233707-f0f7fd24c4af/go.mod h1:xnAOWiHeOqg2nWS62VtQ7pbOu17FtxJNW8RLEih+O3s=
|
github.com/zclconf/go-cty v1.1.2-0.20191126233707-f0f7fd24c4af/go.mod h1:xnAOWiHeOqg2nWS62VtQ7pbOu17FtxJNW8RLEih+O3s=
|
||||||
go.opencensus.io v0.21.0 h1:mU6zScU4U1YAFPHEHYk+3JC4SY7JxgkqS10ZOSyksNg=
|
go.opencensus.io v0.21.0 h1:mU6zScU4U1YAFPHEHYk+3JC4SY7JxgkqS10ZOSyksNg=
|
||||||
|
@ -518,8 +493,6 @@ golang.org/x/crypto v0.0.0-20190426145343-a29dc8fdc734/go.mod h1:yigFU9vqHzYiE8U
|
||||||
golang.org/x/crypto v0.0.0-20190510104115-cbcb75029529/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
|
golang.org/x/crypto v0.0.0-20190510104115-cbcb75029529/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
|
||||||
golang.org/x/crypto v0.0.0-20190605123033-f99c8df09eb5 h1:58fnuSXlxZmFdJyvtTFVmVhcMLU6v5fEb/ok4wyqtNU=
|
golang.org/x/crypto v0.0.0-20190605123033-f99c8df09eb5 h1:58fnuSXlxZmFdJyvtTFVmVhcMLU6v5fEb/ok4wyqtNU=
|
||||||
golang.org/x/crypto v0.0.0-20190605123033-f99c8df09eb5/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
|
golang.org/x/crypto v0.0.0-20190605123033-f99c8df09eb5/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
|
||||||
golang.org/x/crypto v0.0.0-20191112222119-e1110fd1c708 h1:pXVtWnwHkrWD9ru3sDxY/qFK/bfc0egRovX91EjWjf4=
|
|
||||||
golang.org/x/crypto v0.0.0-20191112222119-e1110fd1c708/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=
|
|
||||||
golang.org/x/crypto v0.0.0-20191202143827-86a70503ff7e h1:egKlR8l7Nu9vHGWbcUV8lqR4987UfUbBd7GbhqGzNYU=
|
golang.org/x/crypto v0.0.0-20191202143827-86a70503ff7e h1:egKlR8l7Nu9vHGWbcUV8lqR4987UfUbBd7GbhqGzNYU=
|
||||||
golang.org/x/crypto v0.0.0-20191202143827-86a70503ff7e/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=
|
golang.org/x/crypto v0.0.0-20191202143827-86a70503ff7e/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=
|
||||||
golang.org/x/exp v0.0.0-20190121172915-509febef88a4/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA=
|
golang.org/x/exp v0.0.0-20190121172915-509febef88a4/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA=
|
||||||
|
@ -532,7 +505,6 @@ golang.org/x/exp v0.0.0-20191129062945-2f5052295587 h1:5Uz0rkjCFu9BC9gCRN7EkwVvh
|
||||||
golang.org/x/exp v0.0.0-20191129062945-2f5052295587/go.mod h1:2RIsYlXP63K8oxa1u096TMicItID8zy7Y6sNkU49FU4=
|
golang.org/x/exp v0.0.0-20191129062945-2f5052295587/go.mod h1:2RIsYlXP63K8oxa1u096TMicItID8zy7Y6sNkU49FU4=
|
||||||
golang.org/x/image v0.0.0-20190227222117-0694c2d4d067/go.mod h1:kZ7UVZpmo3dzQBMxlp+ypCbDeSB+sBbTgSJuh5dn5js=
|
golang.org/x/image v0.0.0-20190227222117-0694c2d4d067/go.mod h1:kZ7UVZpmo3dzQBMxlp+ypCbDeSB+sBbTgSJuh5dn5js=
|
||||||
golang.org/x/image v0.0.0-20190802002840-cff245a6509b/go.mod h1:FeLwcggjj3mMvU+oOTbSwawSJRM1uh48EjtB4UJZlP0=
|
golang.org/x/image v0.0.0-20190802002840-cff245a6509b/go.mod h1:FeLwcggjj3mMvU+oOTbSwawSJRM1uh48EjtB4UJZlP0=
|
||||||
golang.org/x/image v0.0.0-20191009234506-e7c1f5e7dbb8/go.mod h1:FeLwcggjj3mMvU+oOTbSwawSJRM1uh48EjtB4UJZlP0=
|
|
||||||
golang.org/x/lint v0.0.0-20181026193005-c67002cb31c3/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE=
|
golang.org/x/lint v0.0.0-20181026193005-c67002cb31c3/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE=
|
||||||
golang.org/x/lint v0.0.0-20190227174305-5b3e6a55c961/go.mod h1:wehouNa3lNwaWXcvxsM5YxQ5yQlVC4a0KAMCusXpPoU=
|
golang.org/x/lint v0.0.0-20190227174305-5b3e6a55c961/go.mod h1:wehouNa3lNwaWXcvxsM5YxQ5yQlVC4a0KAMCusXpPoU=
|
||||||
golang.org/x/lint v0.0.0-20190301231843-5614ed5bae6f/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE=
|
golang.org/x/lint v0.0.0-20190301231843-5614ed5bae6f/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE=
|
||||||
|
@ -544,6 +516,7 @@ golang.org/x/lint v0.0.0-20191125180803-fdd1cda4f05f h1:J5lckAjkw6qYlOZNj90mLYNT
|
||||||
golang.org/x/lint v0.0.0-20191125180803-fdd1cda4f05f/go.mod h1:5qLYkcX4OjUUV8bRuDixDT3tpyyb+LUpUlRWLxfhWrs=
|
golang.org/x/lint v0.0.0-20191125180803-fdd1cda4f05f/go.mod h1:5qLYkcX4OjUUV8bRuDixDT3tpyyb+LUpUlRWLxfhWrs=
|
||||||
golang.org/x/mobile v0.0.0-20190312151609-d3739f865fa6/go.mod h1:z+o9i4GpDbdi3rU15maQ/Ox0txvL9dWGYEHz965HBQE=
|
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-20190719004257-d2bd2a29d028/go.mod h1:E/iHnbuqvinMTCcRqshq8CkpyQDoeVncDDYHnLhea+o=
|
||||||
|
golang.org/x/mobile v0.0.0-20191130191448-5c0e7e404af8 h1:9w7mvrikkrG9zFfEJfuFe08FVKrg8Yi0ePhOdGAKpUw=
|
||||||
golang.org/x/mobile v0.0.0-20191130191448-5c0e7e404af8/go.mod h1:p895TfNkDgPEmEQrNiOtIl3j98d/tGU95djDj7NfyjQ=
|
golang.org/x/mobile v0.0.0-20191130191448-5c0e7e404af8/go.mod h1:p895TfNkDgPEmEQrNiOtIl3j98d/tGU95djDj7NfyjQ=
|
||||||
golang.org/x/mod v0.0.0-20190513183733-4bf6d317e70e/go.mod h1:mXi4GBBbnImb6dmsKGUJ2LatrhH/nqhxcFungHvyanc=
|
golang.org/x/mod v0.0.0-20190513183733-4bf6d317e70e/go.mod h1:mXi4GBBbnImb6dmsKGUJ2LatrhH/nqhxcFungHvyanc=
|
||||||
golang.org/x/mod v0.1.0/go.mod h1:0QHyrYULN0/3qlju5TqG8bIK38QM8yzMo5ekMj3DlcY=
|
golang.org/x/mod v0.1.0/go.mod h1:0QHyrYULN0/3qlju5TqG8bIK38QM8yzMo5ekMj3DlcY=
|
||||||
|
@ -564,7 +537,6 @@ golang.org/x/net v0.0.0-20190311183353-d8887717615a h1:oWX7TPOiFAMXLq8o0ikBYfCJV
|
||||||
golang.org/x/net v0.0.0-20190311183353-d8887717615a/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
|
golang.org/x/net v0.0.0-20190311183353-d8887717615a/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
|
||||||
golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
|
golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
|
||||||
golang.org/x/net v0.0.0-20190501004415-9ce7a6920f09/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
|
golang.org/x/net v0.0.0-20190501004415-9ce7a6920f09/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
|
||||||
golang.org/x/net v0.0.0-20190502183928-7f726cade0ab/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
|
|
||||||
golang.org/x/net v0.0.0-20190503192946-f4e77d36d62c/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
|
golang.org/x/net v0.0.0-20190503192946-f4e77d36d62c/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
|
||||||
golang.org/x/net v0.0.0-20190603091049-60506f45cf65/go.mod h1:HSz+uSET+XFnRR8LxR5pz3Of3rY3CfYBVs4xY44aLks=
|
golang.org/x/net v0.0.0-20190603091049-60506f45cf65/go.mod h1:HSz+uSET+XFnRR8LxR5pz3Of3rY3CfYBVs4xY44aLks=
|
||||||
golang.org/x/net v0.0.0-20190620200207-3b0461eec859 h1:R/3boaszxrf1GEUWTVDzSKVwLmSJpwZ1yqXm8j0v2QI=
|
golang.org/x/net v0.0.0-20190620200207-3b0461eec859 h1:R/3boaszxrf1GEUWTVDzSKVwLmSJpwZ1yqXm8j0v2QI=
|
||||||
|
@ -609,8 +581,6 @@ golang.org/x/sys v0.0.0-20190606165138-5da285871e9c/go.mod h1:h1NjWce9XRLGQEsW7w
|
||||||
golang.org/x/sys v0.0.0-20190624142023-c5567b49c5d0 h1:HyfiK1WMnHj5FXFXatD+Qs1A/xC2Run6RzeW1SyHxpc=
|
golang.org/x/sys v0.0.0-20190624142023-c5567b49c5d0 h1:HyfiK1WMnHj5FXFXatD+Qs1A/xC2Run6RzeW1SyHxpc=
|
||||||
golang.org/x/sys v0.0.0-20190624142023-c5567b49c5d0/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
golang.org/x/sys v0.0.0-20190624142023-c5567b49c5d0/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||||
golang.org/x/sys v0.0.0-20191008105621-543471e840be/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
golang.org/x/sys v0.0.0-20191008105621-543471e840be/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||||
golang.org/x/sys v0.0.0-20191110163157-d32e6e3b99c4 h1:Hynbrlo6LbYI3H1IqXpkVDOcX/3HiPdhVEuyj5a59RM=
|
|
||||||
golang.org/x/sys v0.0.0-20191110163157-d32e6e3b99c4/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
|
||||||
golang.org/x/sys v0.0.0-20191128015809-6d18c012aee9 h1:ZBzSG/7F4eNKz2L3GE9o300RX0Az1Bw5HF7PDraD+qU=
|
golang.org/x/sys v0.0.0-20191128015809-6d18c012aee9 h1:ZBzSG/7F4eNKz2L3GE9o300RX0Az1Bw5HF7PDraD+qU=
|
||||||
golang.org/x/sys v0.0.0-20191128015809-6d18c012aee9/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
golang.org/x/sys v0.0.0-20191128015809-6d18c012aee9/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||||
golang.org/x/text v0.3.0 h1:g61tztE5qeGQ89tm6NTjjM9VPIm088od1l6aSorWRWg=
|
golang.org/x/text v0.3.0 h1:g61tztE5qeGQ89tm6NTjjM9VPIm088od1l6aSorWRWg=
|
||||||
|
@ -650,7 +620,6 @@ golang.org/x/tools v0.0.0-20191125144606-a911d9008d1f/go.mod h1:b+2E5dAYhXwXZwtn
|
||||||
golang.org/x/tools v0.0.0-20191203051722-db047d72ee39 h1:zARK4PTmTfx1BC6iKP21qIRjz0nFzFj4ZAlbUy6Q6pM=
|
golang.org/x/tools v0.0.0-20191203051722-db047d72ee39 h1:zARK4PTmTfx1BC6iKP21qIRjz0nFzFj4ZAlbUy6Q6pM=
|
||||||
golang.org/x/tools v0.0.0-20191203051722-db047d72ee39/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
|
golang.org/x/tools v0.0.0-20191203051722-db047d72ee39/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
|
||||||
golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
|
golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
|
||||||
golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
|
|
||||||
google.golang.org/api v0.4.0 h1:KKgc1aqhV8wDPbDzlDtpvyjZFY3vjz85FP7p4wcQUyI=
|
google.golang.org/api v0.4.0 h1:KKgc1aqhV8wDPbDzlDtpvyjZFY3vjz85FP7p4wcQUyI=
|
||||||
google.golang.org/api v0.4.0/go.mod h1:8k5glujaEP+g9n7WNsDg8QP6cUVNI86fCNMcbazEtwE=
|
google.golang.org/api v0.4.0/go.mod h1:8k5glujaEP+g9n7WNsDg8QP6cUVNI86fCNMcbazEtwE=
|
||||||
google.golang.org/api v0.7.0/go.mod h1:WtwebWUNSVBH/HAw79HIFXZNqEvBhG+Ra+ax0hx3E3M=
|
google.golang.org/api v0.7.0/go.mod h1:WtwebWUNSVBH/HAw79HIFXZNqEvBhG+Ra+ax0hx3E3M=
|
||||||
|
@ -694,6 +663,7 @@ gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405 h1:yhCVgyC4o1eVCa2tZl7eS0r+
|
||||||
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
|
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
|
||||||
gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127 h1:qIbj1fsPNlZgppZ+VLlY7N33q108Sa+fhmuc+sWQYwY=
|
gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127 h1:qIbj1fsPNlZgppZ+VLlY7N33q108Sa+fhmuc+sWQYwY=
|
||||||
gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
|
gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
|
||||||
|
gopkg.in/check.v1 v1.0.0-20190902080502-41f04d3bba15 h1:YR8cESwS4TdDjEe65xsg0ogRM/Nc3DYOhEAlW+xobZo=
|
||||||
gopkg.in/check.v1 v1.0.0-20190902080502-41f04d3bba15/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
|
gopkg.in/check.v1 v1.0.0-20190902080502-41f04d3bba15/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
|
||||||
gopkg.in/cheggaaa/pb.v1 v1.0.27 h1:kJdccidYzt3CaHD1crCFTS1hxyhSi059NhOFUf03YFo=
|
gopkg.in/cheggaaa/pb.v1 v1.0.27 h1:kJdccidYzt3CaHD1crCFTS1hxyhSi059NhOFUf03YFo=
|
||||||
gopkg.in/cheggaaa/pb.v1 v1.0.27/go.mod h1:V/YB90LKu/1FcN3WVnfiiE5oMCibMjukxqG/qStrOgw=
|
gopkg.in/cheggaaa/pb.v1 v1.0.27/go.mod h1:V/YB90LKu/1FcN3WVnfiiE5oMCibMjukxqG/qStrOgw=
|
||||||
|
@ -720,7 +690,6 @@ gopkg.in/yaml.v2 v2.2.7/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
|
||||||
honnef.co/go/tools v0.0.0-20190102054323-c2f93a96b099/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4=
|
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-20190106161140-3f1c8253044a/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4=
|
||||||
honnef.co/go/tools v0.0.0-20190418001031-e561f6794a2a/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4=
|
honnef.co/go/tools v0.0.0-20190418001031-e561f6794a2a/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4=
|
||||||
howett.net/plist v0.0.0-20181124034731-591f970eefbb/go.mod h1:vMygbs4qMhSZSc4lCUl2OEE+rDiIIJAIdR4m7MiMcm0=
|
|
||||||
honnef.co/go/tools v0.0.0-20190523083050-ea95bdfd59fc/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4=
|
honnef.co/go/tools v0.0.0-20190523083050-ea95bdfd59fc/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4=
|
||||||
honnef.co/go/tools v0.0.1-2019.2.3 h1:3JgtbtFHMiCmsznwGVTUWbgGov+pVqnlf1dEJTNAXeM=
|
honnef.co/go/tools v0.0.1-2019.2.3 h1:3JgtbtFHMiCmsznwGVTUWbgGov+pVqnlf1dEJTNAXeM=
|
||||||
honnef.co/go/tools v0.0.1-2019.2.3/go.mod h1:a3bituU0lyd329TUQxRnasdCoJDkEUEAqEt0JzvZhAg=
|
honnef.co/go/tools v0.0.1-2019.2.3/go.mod h1:a3bituU0lyd329TUQxRnasdCoJDkEUEAqEt0JzvZhAg=
|
||||||
|
|
|
@ -3,6 +3,7 @@ package config
|
||||||
import (
|
import (
|
||||||
"encoding/json"
|
"encoding/json"
|
||||||
"fmt"
|
"fmt"
|
||||||
|
"log"
|
||||||
"reflect"
|
"reflect"
|
||||||
"sort"
|
"sort"
|
||||||
"strings"
|
"strings"
|
||||||
|
@ -158,6 +159,9 @@ func DetectContextData(raws ...interface{}) (map[interface{}]interface{}, []inte
|
||||||
// In provisioners, the last value pulled from raws is the placeholder data
|
// In provisioners, the last value pulled from raws is the placeholder data
|
||||||
// for build-specific variables. Pull these out to add to interpolation
|
// for build-specific variables. Pull these out to add to interpolation
|
||||||
// context.
|
// context.
|
||||||
|
if len(raws) == 0 {
|
||||||
|
return nil, raws
|
||||||
|
}
|
||||||
|
|
||||||
// Internally, our tests may cause this to be read as a map[string]string
|
// Internally, our tests may cause this to be read as a map[string]string
|
||||||
placeholderData := raws[len(raws)-1]
|
placeholderData := raws[len(raws)-1]
|
||||||
|
@ -201,6 +205,7 @@ func DetectContext(raws ...interface{}) (*interpolate.Context, error) {
|
||||||
|
|
||||||
for _, r := range raws {
|
for _, r := range raws {
|
||||||
if err := mapstructure.Decode(r, &s); err != nil {
|
if err := mapstructure.Decode(r, &s); err != nil {
|
||||||
|
log.Printf("Error detecting context: %s", err)
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -1 +0,0 @@
|
||||||
eyJhbGciOiJSU0EtT0FFUCIsImVuYyI6IkExMjhHQ00ifQ.kK6pryC8R-O1R0Gj9ydLvQuIZlcYLGze23WdW7xbpiEEKdz6nweJrMm7ysy8lgu1tM47JVo19p2_b26bNKSQshCUOETvd7Hb2UMZOjnyUnqdyAAyoi6UkIquXfUUbHTNS0iMxwSxxW9KMp2GXNq8-o6T8xQZTDirBJFKKd8ZNUasTaoa5j8U9IfdR1aCavTBuOhvk8IVs-jSbY5TVJMJiE0IOPXois7aRJ6uAiANQBk9VKLegEcZD_qAewecXHDsHi-u0jbmg3o3PPaJaK_Qv5dsPlR2M-E2kE3AGUn0-zn5zYRngoAZ8WZr2O4GvLdltJKq9i2z7jOrdOzzRcDRow.96qvwl_E1Hj15u7Q.hWs-jQ8FsqQFD7pE9N-UEP1BWQ9rsJIcCaPvQRIp8Fukm_vvlw9YEaEq0ERLrsUWsJWpd1ca8_h8x7xD6f_d5YppwRqRHIeGIsdBOTMhNs0lG8ikkQXLat-UroCpy8EC17nuUtDE2E2Kdxrk4Cdd6Bk-dKk0Ta4w3Ud0YBKa.P8zrO7xizgv0i98eVWWzEg
|
|
|
@ -1,17 +0,0 @@
|
||||||
clone:
|
|
||||||
tags: true
|
|
||||||
path: github.com/vmware/govmomi
|
|
||||||
build:
|
|
||||||
image: golang:1.7
|
|
||||||
pull: true
|
|
||||||
environment:
|
|
||||||
- GOVC_TEST_URL=$$GOVC_TEST_URL
|
|
||||||
- GOVC_INSECURE=1
|
|
||||||
- VCA=1
|
|
||||||
commands:
|
|
||||||
- make all install
|
|
||||||
- git clone https://github.com/sstephenson/bats.git /tmp/bats
|
|
||||||
- /tmp/bats/install.sh /usr/local
|
|
||||||
- apt-get -qq update && apt-get install -yqq uuid-runtime bsdmainutils jq
|
|
||||||
- govc/test/images/update.sh
|
|
||||||
- bats govc/test
|
|
|
@ -1 +1,6 @@
|
||||||
secrets.yml
|
secrets.yml
|
||||||
|
dist/
|
||||||
|
.idea/
|
||||||
|
|
||||||
|
# Ignore editor temp files
|
||||||
|
*~
|
||||||
|
|
|
@ -0,0 +1,118 @@
|
||||||
|
---
|
||||||
|
project_name: govmomi
|
||||||
|
builds:
|
||||||
|
- id: govc
|
||||||
|
goos:
|
||||||
|
- linux
|
||||||
|
- darwin
|
||||||
|
- windows
|
||||||
|
- freebsd
|
||||||
|
goarch:
|
||||||
|
- amd64
|
||||||
|
- 386
|
||||||
|
- arm64
|
||||||
|
env:
|
||||||
|
- CGO_ENABLED=0
|
||||||
|
main: ./govc/main.go
|
||||||
|
binary: govc
|
||||||
|
flags: -compiler gc
|
||||||
|
ldflags: -X github.com/vmware/govmomi/govc/flags.GitVersion={{.Version}}
|
||||||
|
- id: vcsim
|
||||||
|
goos:
|
||||||
|
- linux
|
||||||
|
- darwin
|
||||||
|
- windows
|
||||||
|
- freebsd
|
||||||
|
goarch:
|
||||||
|
- amd64
|
||||||
|
- 386
|
||||||
|
- arm64
|
||||||
|
env:
|
||||||
|
- CGO_ENABLED=0
|
||||||
|
main: ./vcsim/main.go
|
||||||
|
binary: vcsim
|
||||||
|
flags: -compiler gc
|
||||||
|
ldflags: -X github.com/vmware/govmomi/vcsim/flags.GitVersion={{.Version}}
|
||||||
|
archives:
|
||||||
|
- id: govcbuild
|
||||||
|
builds: ['govc']
|
||||||
|
name_template: 'govc_{{ .Os }}_{{ .Arch }}'
|
||||||
|
format: gz
|
||||||
|
format_overrides:
|
||||||
|
- goos: windows
|
||||||
|
format: zip
|
||||||
|
files:
|
||||||
|
- none*
|
||||||
|
- id: vcsimbuild
|
||||||
|
builds: ['vcsim']
|
||||||
|
name_template: 'vcsim_{{ .Os }}_{{ .Arch }}'
|
||||||
|
format: gz
|
||||||
|
format_overrides:
|
||||||
|
- goos: windows
|
||||||
|
format: zip
|
||||||
|
files:
|
||||||
|
- none*
|
||||||
|
checksum:
|
||||||
|
name_template: '{{ .ProjectName }}_{{ .Version }}_checksums.txt'
|
||||||
|
changelog:
|
||||||
|
sort: asc
|
||||||
|
filters:
|
||||||
|
exclude:
|
||||||
|
- '^docs:'
|
||||||
|
- '^test:'
|
||||||
|
- Merge pull request
|
||||||
|
- Merge branch
|
||||||
|
brews:
|
||||||
|
- name: govc
|
||||||
|
ids:
|
||||||
|
- govc
|
||||||
|
github:
|
||||||
|
owner: govmomi
|
||||||
|
name: homebrew-tap
|
||||||
|
commit_author:
|
||||||
|
name: Alfred the Narwhal
|
||||||
|
email: cna-alfred@vmware.com
|
||||||
|
folder: Formula
|
||||||
|
homepage: "https://github.com/vmware/govmomi/blob/master/govc/README.md"
|
||||||
|
description: "govc is a vSphere CLI built on top of govmomi."
|
||||||
|
test: |
|
||||||
|
system "#{bin}/govc version"
|
||||||
|
install: |
|
||||||
|
bin.install "govc"
|
||||||
|
- name: vcsim
|
||||||
|
ids:
|
||||||
|
- vcsim
|
||||||
|
github:
|
||||||
|
owner: govmomi
|
||||||
|
name: homebrew-tap
|
||||||
|
commit_author:
|
||||||
|
name: Alfred the Narwhal
|
||||||
|
email: cna-alfred@vmware.com
|
||||||
|
folder: Formula
|
||||||
|
homepage: "https://github.com/vmware/govmomi/blob/master/vcsim/README.md"
|
||||||
|
description: "vcsim is a vSphere API simulator built on top of govmomi."
|
||||||
|
test: |
|
||||||
|
system "#{bin}/vcsim -h"
|
||||||
|
install: |
|
||||||
|
bin.install "vcsim"
|
||||||
|
dockers:
|
||||||
|
- image_templates:
|
||||||
|
- "vmware/govc:{{ .Tag }}"
|
||||||
|
- "vmware/govc:v{{ .Major }}"
|
||||||
|
- "vmware/govc:v{{ .Major }}.{{ .Minor }}"
|
||||||
|
- "vmware/govc:latest"
|
||||||
|
goos: linux
|
||||||
|
goarch: amd64
|
||||||
|
dockerfile: Dockerfile.govc
|
||||||
|
binaries:
|
||||||
|
- govc
|
||||||
|
- image_templates:
|
||||||
|
- "vmware/vcsim:{{ .Tag }}"
|
||||||
|
- "vmware/vcsim:v{{ .Major }}"
|
||||||
|
- "vmware/vcsim:v{{ .Major }}.{{ .Minor }}"
|
||||||
|
- "vmware/vcsim:latest"
|
||||||
|
goos: linux
|
||||||
|
goarch: amd64
|
||||||
|
dockerfile: Dockerfile.vcsim
|
||||||
|
binaries:
|
||||||
|
- vcsim
|
|
@ -1,17 +1,29 @@
|
||||||
|
Amanda H. L. de Andrade <amanda.andrade@serpro.gov.br> amandahla <amanda.andrade@serpro.gov.br>
|
||||||
|
Amanda H. L. de Andrade <amanda.andrade@serpro.gov.br> Amanda Hager Lopes de Andrade Katz <amanda.katz@serpro.gov.br>
|
||||||
Amit Bathla <abathla@.vmware.com> <abathla@promb-1s-dhcp216.eng.vmware.com>
|
Amit Bathla <abathla@.vmware.com> <abathla@promb-1s-dhcp216.eng.vmware.com>
|
||||||
|
Andrew Kutz <akutz@vmware.com> akutz <akutz@vmware.com>
|
||||||
|
Andrew Kutz <akutz@vmware.com> <sakutz@gmail.com>
|
||||||
|
Andrew Kutz <akutz@vmware.com> Andrew Kutz <101085+akutz@users.noreply.github.com>
|
||||||
Bruce Downs <bruceadowns@gmail.com> <bdowns@vmware.com>
|
Bruce Downs <bruceadowns@gmail.com> <bdowns@vmware.com>
|
||||||
Bruce Downs <bruceadowns@gmail.com> <bruce.downs@jivesoftware.com>
|
Bruce Downs <bruceadowns@gmail.com> <bruce.downs@jivesoftware.com>
|
||||||
|
Bruce Downs <bruceadowns@gmail.com> <bruce.downs@autodesk.com>
|
||||||
Clint Greenwood <cgreenwood@vmware.com> <clint.greenwood@gmail.com>
|
Clint Greenwood <cgreenwood@vmware.com> <clint.greenwood@gmail.com>
|
||||||
Cédric Blomart <cblomart@gmail.com> <cedric.blomart@minfin.fed.be>
|
Cédric Blomart <cblomart@gmail.com> <cedric.blomart@minfin.fed.be>
|
||||||
Cédric Blomart <cblomart@gmail.com> cedric <cblomart@gmail.com>
|
Cédric Blomart <cblomart@gmail.com> cedric <cblomart@gmail.com>
|
||||||
David Stark <dave@davidstark.name> <david.stark@bskyb.com>
|
David Stark <dave@davidstark.name> <david.stark@bskyb.com>
|
||||||
Eric Gray <egray@vmware.com> <ericgray@users.noreply.github.com>
|
Eric Gray <egray@vmware.com> <ericgray@users.noreply.github.com>
|
||||||
Eric Yutao <eric.yutao@gmail.com> eric <eric.yutao@gmail.com>
|
Eric Yutao <eric.yutao@gmail.com> eric <eric.yutao@gmail.com>
|
||||||
|
Fabio Rapposelli <fabio@vmware.com> <fabio@rapposelli.org>
|
||||||
Henrik Hodne <henrik@travis-ci.com> <henrik@hodne.io>
|
Henrik Hodne <henrik@travis-ci.com> <henrik@hodne.io>
|
||||||
Jeremy Canady <jcanady@jackhenry.com> <jcanady@gmail.com>
|
Jeremy Canady <jcanady@jackhenry.com> <jcanady@gmail.com>
|
||||||
Pieter Noordhuis <pnoordhuis@vmware.com> <pcnoordhuis@gmail.com>
|
Pieter Noordhuis <pnoordhuis@vmware.com> <pcnoordhuis@gmail.com>
|
||||||
Takaaki Furukawa <takaaki.frkw@gmail.com> takaaki.furukawa <takaaki.furukawa@mail.rakuten.com>
|
Takaaki Furukawa <takaaki.frkw@gmail.com> takaaki.furukawa <takaaki.furukawa@mail.rakuten.com>
|
||||||
Takaaki Furukawa <takaaki.frkw@gmail.com> tkak <takaaki.frkw@gmail.com>
|
Takaaki Furukawa <takaaki.frkw@gmail.com> tkak <takaaki.frkw@gmail.com>
|
||||||
Vadim Egorov <vegorov@vmware.com> <egorovv@gmail.com>
|
Vadim Egorov <vegorov@vmware.com> <egorovv@gmail.com>
|
||||||
|
Anfernee Yongkun Gui <agui@vmware.com> <anfernee.gui@gmail.com>
|
||||||
|
Anfernee Yongkun Gui <agui@vmware.com> Yongkun Anfernee Gui <agui@vmware.com>
|
||||||
Zach Tucker <ztucker@vmware.com> <jzt@users.noreply.github.com>
|
Zach Tucker <ztucker@vmware.com> <jzt@users.noreply.github.com>
|
||||||
Zee Yang <zeey@vmware.com> <zee.yang@gmail.com>
|
Zee Yang <zeey@vmware.com> <zee.yang@gmail.com>
|
||||||
|
Jiatong Wang <wjiatong@vmware.com> jiatongw <wjiatong@vmware.com>
|
||||||
|
Uwe Bessle <Uwe.Bessle@iteratec.de> Uwe Bessle <u.bessle.extern@eos-ts.com>
|
||||||
|
Uwe Bessle <Uwe.Bessle@iteratec.de> Uwe Bessle <uwe.bessle@web.de>
|
||||||
|
|
|
@ -1,12 +1,95 @@
|
||||||
sudo: false
|
# Use the newer Travis-CI build templates based on the
|
||||||
|
# Ubuntu Linux distribution "Xenial Xerus" release.
|
||||||
|
os: linux
|
||||||
|
dist: xenial
|
||||||
|
|
||||||
language: go
|
# Disable sudo for all builds by default. This ensures all jobs use
|
||||||
|
# Travis-CI's containerized build environment unless specified otherwise.
|
||||||
|
# The container builds have *much* shorter queue times than the VM-based
|
||||||
|
# build environment on which the sudo builds depend.
|
||||||
|
sudo: false
|
||||||
|
services: false
|
||||||
|
|
||||||
go:
|
# Set the version of Go.
|
||||||
- 1.8
|
language: go
|
||||||
|
go: 1.12
|
||||||
|
|
||||||
before_install:
|
# Always set the project's Go import path to ensure that forked
|
||||||
- make vendor
|
# builds get cloned to the correct location.
|
||||||
|
go_import_path: github.com/vmware/govmomi
|
||||||
|
|
||||||
script:
|
# Ensure all the jobs know where the temp directory is.
|
||||||
- make check test
|
env:
|
||||||
|
global: TMPDIR=/tmp
|
||||||
|
|
||||||
|
jobs:
|
||||||
|
include:
|
||||||
|
|
||||||
|
# The "lint" stage runs the various linters against the project.
|
||||||
|
- &lint-stage
|
||||||
|
stage: lint
|
||||||
|
env: LINTER=govet
|
||||||
|
install: true
|
||||||
|
script: make "${LINTER}"
|
||||||
|
|
||||||
|
- <<: *lint-stage
|
||||||
|
env: LINTER=goimports
|
||||||
|
|
||||||
|
# The "build" stage verifies the program can be built against the
|
||||||
|
# various GOOS and GOARCH combinations found in the Go releaser
|
||||||
|
# config file, ".goreleaser.yml".
|
||||||
|
- &build-stage
|
||||||
|
stage: build
|
||||||
|
env: GOOS=linux GOARCH=amd64
|
||||||
|
install: true
|
||||||
|
script: make install
|
||||||
|
|
||||||
|
- <<: *build-stage
|
||||||
|
env: GOOS=linux GOARCH=386
|
||||||
|
|
||||||
|
- <<: *build-stage
|
||||||
|
env: GOOS=darwin GOARCH=amd64
|
||||||
|
- <<: *build-stage
|
||||||
|
env: GOOS=darwin GOARCH=386
|
||||||
|
|
||||||
|
- <<: *build-stage
|
||||||
|
env: GOOS=freebsd GOARCH=amd64
|
||||||
|
- <<: *build-stage
|
||||||
|
env: GOOS=freebsd GOARCH=386
|
||||||
|
|
||||||
|
- <<: *build-stage
|
||||||
|
env: GOOS=windows GOARCH=amd64
|
||||||
|
- <<: *build-stage
|
||||||
|
env: GOOS=windows GOARCH=386
|
||||||
|
|
||||||
|
# The test stage executes the test target.
|
||||||
|
- stage: test
|
||||||
|
install: true
|
||||||
|
script: make test
|
||||||
|
|
||||||
|
# The deploy stage deploys the build artifacts using goreleaser.
|
||||||
|
#
|
||||||
|
# This stage will only be activated when there is an annotated tag present
|
||||||
|
# or when the text "/ci-deploy" is present in the commit message. However,
|
||||||
|
# the "deploy" phase of the build will still only be executed on non-PR
|
||||||
|
# builds as that restriction is baked into Travis-CI.
|
||||||
|
#
|
||||||
|
# Finally, this stage requires the Travis-CI VM infrastructure in order to
|
||||||
|
# leverage Docker. This will increase the amount of time the jobs sit
|
||||||
|
# in the queue, waiting to be built. However, it's a necessity as Travis-CI
|
||||||
|
# only allows the use of Docker with VM builds.
|
||||||
|
- stage: deploy
|
||||||
|
if: tag IS present OR commit_message =~ /\/ci-deploy/
|
||||||
|
sudo: required
|
||||||
|
services: docker
|
||||||
|
install: true
|
||||||
|
script: make install
|
||||||
|
after_success: docker login -u="${DOCKER_USERNAME}" -p="${DOCKER_PASSWORD}"
|
||||||
|
deploy:
|
||||||
|
- provider: script
|
||||||
|
skip_cleanup: true
|
||||||
|
script: curl -sL http://git.io/goreleaser | bash
|
||||||
|
addons:
|
||||||
|
apt:
|
||||||
|
update: true
|
||||||
|
packages: xmlstarlet
|
||||||
|
|
|
@ -1,5 +1,81 @@
|
||||||
# changelog
|
# changelog
|
||||||
|
|
||||||
|
### 0.21.0 (2019-07-24)
|
||||||
|
|
||||||
|
* Add vsan package
|
||||||
|
|
||||||
|
* Add vslm (FCD) global catalog support
|
||||||
|
|
||||||
|
* Add content library support
|
||||||
|
|
||||||
|
### 0.20.0 (2019-02-06)
|
||||||
|
|
||||||
|
* Add vslm package for managing First Class Disks
|
||||||
|
|
||||||
|
* Add LoginByToken to session KeepAliveHandler
|
||||||
|
|
||||||
|
### 0.19.0 (2018-09-30)
|
||||||
|
|
||||||
|
* New vapi/rest and and vapi/tags packages
|
||||||
|
|
||||||
|
* Allowing the use of STS for exchanging tokens
|
||||||
|
|
||||||
|
* Add object.VirtualMachine.UUID method
|
||||||
|
|
||||||
|
* SetRootCAs on the soap.Client returns an error for invalid certificates
|
||||||
|
|
||||||
|
* Add ClusterComputeResource.MoveInto method
|
||||||
|
|
||||||
|
### 0.18.0 (2018-05-24)
|
||||||
|
|
||||||
|
* Add VirtualDiskManager wrapper to set UUID
|
||||||
|
|
||||||
|
* Add vmxnet2, pcnet32 and sriov to VirtualDeviceList.EthernetCardTypes
|
||||||
|
|
||||||
|
* Add new vSphere 6.7 APIs
|
||||||
|
|
||||||
|
* Decrease LoginExtensionByCertificate tunnel usage
|
||||||
|
|
||||||
|
* SAML token authentication support via SessionManager.LoginByToken
|
||||||
|
|
||||||
|
* New SSO admin client for managing users
|
||||||
|
|
||||||
|
* New STS client for issuing and renewing SAML tokens
|
||||||
|
|
||||||
|
* New Lookup Service client for discovering endpoints such as STS and ssoadmin
|
||||||
|
|
||||||
|
* Switch from gvt to go dep for managing dependencies
|
||||||
|
|
||||||
|
### 0.17.1 (2018-03-19)
|
||||||
|
|
||||||
|
* vcsim: add Destroy method for Folder and Datacenter types
|
||||||
|
|
||||||
|
* In progress.Reader emit final report on EOF.
|
||||||
|
|
||||||
|
* vcsim: add EventManager.QueryEvents
|
||||||
|
|
||||||
|
### 0.17.0 (2018-02-28)
|
||||||
|
|
||||||
|
* Add HostStorageSystem.AttachScsiLun method
|
||||||
|
|
||||||
|
* Avoid possible panic in Datastore.Stat (#969)
|
||||||
|
|
||||||
|
* Destroy event history collectors (#962)
|
||||||
|
|
||||||
|
* Add VirtualDiskManager.CreateChildDisk method
|
||||||
|
|
||||||
|
### 0.16.0 (2017-11-08)
|
||||||
|
|
||||||
|
* Add support for SOAP request operation ID header
|
||||||
|
|
||||||
|
* Moved ovf helpers from govc import.ovf command to ovf and nfc packages
|
||||||
|
|
||||||
|
* Added guest/toolbox (client) package
|
||||||
|
|
||||||
|
* Added toolbox package and toolbox command
|
||||||
|
|
||||||
|
* Added simulator package and vcsim command
|
||||||
|
|
||||||
### 0.15.0 (2017-06-19)
|
### 0.15.0 (2017-06-19)
|
||||||
|
|
||||||
* WaitOptions.MaxWaitSeconds is now optional
|
* WaitOptions.MaxWaitSeconds is now optional
|
||||||
|
|
|
@ -3,34 +3,55 @@
|
||||||
# This script is generated by contributors.sh
|
# This script is generated by contributors.sh
|
||||||
#
|
#
|
||||||
|
|
||||||
|
Abhijeet Kasurde <akasurde@redhat.com>
|
||||||
abrarshivani <abrarshivani@users.noreply.github.com>
|
abrarshivani <abrarshivani@users.noreply.github.com>
|
||||||
|
Adam Shannon <adamkshannon@gmail.com>
|
||||||
|
Alessandro Cortiana <alessandro.cortiana@gmail.com>
|
||||||
|
Alex Bozhenko <alexbozhenko@fb.com>
|
||||||
|
Alex Ellis (VMware) <alexellis2@gmail.com>
|
||||||
|
Alex <puzo2002@gmail.com>
|
||||||
Alvaro Miranda <kikitux@gmail.com>
|
Alvaro Miranda <kikitux@gmail.com>
|
||||||
amandahla <amanda.andrade@serpro.gov.br>
|
Amanda H. L. de Andrade <amanda.andrade@serpro.gov.br>
|
||||||
Amit Bathla <abathla@.vmware.com>
|
Amit Bathla <abathla@.vmware.com>
|
||||||
|
amit bezalel <amit.bezalel@hpe.com>
|
||||||
|
Andrew <AndrewDi@users.noreply.github.com>
|
||||||
Andrew Chin <andrew@andrewtchin.com>
|
Andrew Chin <andrew@andrewtchin.com>
|
||||||
|
Andrew Kutz <akutz@vmware.com>
|
||||||
|
Andrey Klimentyev <andrey.klimentyev@flant.com>
|
||||||
|
Anfernee Yongkun Gui <agui@vmware.com>
|
||||||
|
angystardust <angystardust@users.noreply.github.com>
|
||||||
aniketGslab <aniket.shinde@gslab.com>
|
aniketGslab <aniket.shinde@gslab.com>
|
||||||
Arran Walker <arran.walker@zopa.com>
|
Arran Walker <arran.walker@zopa.com>
|
||||||
Aryeh Weinreb <aryehweinreb@gmail.com>
|
Aryeh Weinreb <aryehweinreb@gmail.com>
|
||||||
Austin Parker <aparker@apprenda.com>
|
Austin Parker <aparker@apprenda.com>
|
||||||
Balu Dontu <bdontu@vmware.com>
|
Balu Dontu <bdontu@vmware.com>
|
||||||
bastienbc <bastien.barbe.creuly@gmail.com>
|
bastienbc <bastien.barbe.creuly@gmail.com>
|
||||||
|
Benjamin Peterson <benjamin@python.org>
|
||||||
Bob Killen <killen.bob@gmail.com>
|
Bob Killen <killen.bob@gmail.com>
|
||||||
Brad Fitzpatrick <bradfitz@golang.org>
|
Brad Fitzpatrick <bradfitz@golang.org>
|
||||||
Bruce Downs <bruceadowns@gmail.com>
|
Bruce Downs <bruceadowns@gmail.com>
|
||||||
Cédric Blomart <cblomart@gmail.com>
|
Cédric Blomart <cblomart@gmail.com>
|
||||||
|
Chris Marchesi <chrism@vancluevertech.com>
|
||||||
Christian Höltje <docwhat@gerf.org>
|
Christian Höltje <docwhat@gerf.org>
|
||||||
Clint Greenwood <cgreenwood@vmware.com>
|
Clint Greenwood <cgreenwood@vmware.com>
|
||||||
|
CuiHaozhi <cuihaozhi@chinacloud.com.cn>
|
||||||
Danny Lockard <danny.lockard@banno.com>
|
Danny Lockard <danny.lockard@banno.com>
|
||||||
|
Dave Smith-Uchida <dsmithuchida@vmware.com>
|
||||||
Dave Tucker <dave@dtucker.co.uk>
|
Dave Tucker <dave@dtucker.co.uk>
|
||||||
Davide Agnello <dagnello@hp.com>
|
Davide Agnello <dagnello@hp.com>
|
||||||
David Stark <dave@davidstark.name>
|
David Stark <dave@davidstark.name>
|
||||||
|
Davinder Kumar <davinderk@vmware.com>
|
||||||
|
Deric Crago <deric.crago@gmail.com>
|
||||||
Doug MacEachern <dougm@vmware.com>
|
Doug MacEachern <dougm@vmware.com>
|
||||||
Eloy Coto <eloy.coto@gmail.com>
|
Eloy Coto <eloy.coto@gmail.com>
|
||||||
Eric Gray <egray@vmware.com>
|
Eric Gray <egray@vmware.com>
|
||||||
Eric Yutao <eric.yutao@gmail.com>
|
Eric Yutao <eric.yutao@gmail.com>
|
||||||
|
Erik Hollensbe <github@hollensbe.org>
|
||||||
|
Ethan Kaley <ethan.kaley@emc.com>
|
||||||
Fabio Rapposelli <fabio@vmware.com>
|
Fabio Rapposelli <fabio@vmware.com>
|
||||||
Faiyaz Ahmed <ahmedf@vmware.com>
|
Faiyaz Ahmed <ahmedf@vmware.com>
|
||||||
forkbomber <forkbomber@users.noreply.github.com>
|
forkbomber <forkbomber@users.noreply.github.com>
|
||||||
|
freebsdly <qinhuajun@outlook.com>
|
||||||
Gavin Gray <gavin@infinio.com>
|
Gavin Gray <gavin@infinio.com>
|
||||||
Gavrie Philipson <gavrie.philipson@elastifile.com>
|
Gavrie Philipson <gavrie.philipson@elastifile.com>
|
||||||
George Hicken <ghicken@vmware.com>
|
George Hicken <ghicken@vmware.com>
|
||||||
|
@ -38,24 +59,65 @@ Gerrit Renker <Gerrit.Renker@ctl.io>
|
||||||
gthombare <gthombare@vmware.com>
|
gthombare <gthombare@vmware.com>
|
||||||
Hasan Mahmood <mahmoodh@vmware.com>
|
Hasan Mahmood <mahmoodh@vmware.com>
|
||||||
Henrik Hodne <henrik@travis-ci.com>
|
Henrik Hodne <henrik@travis-ci.com>
|
||||||
|
hui luo <luoh@vmware.com>
|
||||||
Isaac Rodman <isaac@eyz.us>
|
Isaac Rodman <isaac@eyz.us>
|
||||||
Ivan Porto Carrero <icarrero@vmware.com>
|
Ivan Porto Carrero <icarrero@vmware.com>
|
||||||
|
James King <james.king@emc.com>
|
||||||
Jason Kincl <jkincl@gmail.com>
|
Jason Kincl <jkincl@gmail.com>
|
||||||
Jeremy Canady <jcanady@jackhenry.com>
|
Jeremy Canady <jcanady@jackhenry.com>
|
||||||
|
jeremy-clerc <jeremy@clerc.io>
|
||||||
|
Jiatong Wang <wjiatong@vmware.com>
|
||||||
|
João Pereira <joaodrp@gmail.com>
|
||||||
|
Jonas Ausevicius <jonas.ausevicius@virtustream.com>
|
||||||
|
Jorge Sevilla <jorge.sevilla@rstor.io>
|
||||||
|
kayrus <kay.diam@gmail.com>
|
||||||
|
Kevin George <georgek@vmware.com>
|
||||||
|
leslie-qiwa <leslie.qiwa@gmail.com>
|
||||||
Louie Jiang <jiangl@vmware.com>
|
Louie Jiang <jiangl@vmware.com>
|
||||||
|
maplain <fangyuanl@vmware.com>
|
||||||
Marc Carmier <mcarmier@gmail.com>
|
Marc Carmier <mcarmier@gmail.com>
|
||||||
|
Maria Ntalla <maria.ntalla@gmail.com>
|
||||||
|
Marin Atanasov Nikolov <mnikolov@vmware.com>
|
||||||
|
Mario Trangoni <mjtrangoni@gmail.com>
|
||||||
|
Mark Peek <markpeek@vmware.com>
|
||||||
|
Matt Clay <matt@mystile.com>
|
||||||
|
Matthew Cosgrove <matthew.cosgrove@dell.com>
|
||||||
|
Matt Moriarity <matt@mattmoriarity.com>
|
||||||
Mevan Samaratunga <mevansam@gmail.com>
|
Mevan Samaratunga <mevansam@gmail.com>
|
||||||
|
Michal Jankowski <mjankowski@vmware.com>
|
||||||
|
mingwei <mingwei@smartx.com>
|
||||||
Nicolas Lamirault <nicolas.lamirault@gmail.com>
|
Nicolas Lamirault <nicolas.lamirault@gmail.com>
|
||||||
|
Omar Kohl <omarkohl@gmail.com>
|
||||||
|
Parham Alvani <parham.alvani@gmail.com>
|
||||||
|
Pierre Gronlier <pierre.gronlier@corp.ovh.com>
|
||||||
Pieter Noordhuis <pnoordhuis@vmware.com>
|
Pieter Noordhuis <pnoordhuis@vmware.com>
|
||||||
|
prydin <prydin@vmware.com>
|
||||||
|
rHermes <teodor_spaeren@riseup.net>
|
||||||
|
Rowan Jacobs <rojacobs@pivotal.io>
|
||||||
runner.mei <runner.mei@gmail.com>
|
runner.mei <runner.mei@gmail.com>
|
||||||
S.Çağlar Onur <conur@vmware.com>
|
S.Çağlar Onur <conur@vmware.com>
|
||||||
Sergey Ignatov <sergey.ignatov@jetbrains.com>
|
Sergey Ignatov <sergey.ignatov@jetbrains.com>
|
||||||
|
Sten Feldman <exile@chamber.ee>
|
||||||
|
Stepan Mazurov <smazurov@gmail.com>
|
||||||
Steve Purcell <steve@sanityinc.com>
|
Steve Purcell <steve@sanityinc.com>
|
||||||
Takaaki Furukawa <takaaki.frkw@gmail.com>
|
Takaaki Furukawa <takaaki.frkw@gmail.com>
|
||||||
|
Tamas Eger <tamas.eger@bitrise.io>
|
||||||
|
tanishi <tanishi503@gmail.com>
|
||||||
Ted Zlatanov <tzz@lifelogs.com>
|
Ted Zlatanov <tzz@lifelogs.com>
|
||||||
Thibaut Ackermann <thibaut.ackermann@alcatel-lucent.com>
|
Thibaut Ackermann <thibaut.ackermann@alcatel-lucent.com>
|
||||||
|
Tim McNamara <tim.mcnamara@canonical.com>
|
||||||
|
Tjeu Kayim <15987676+TjeuKayim@users.noreply.github.com>
|
||||||
|
Trevor Dawe <trevor.dawe@gmail.com>
|
||||||
|
Uwe Bessle <Uwe.Bessle@iteratec.de>
|
||||||
Vadim Egorov <vegorov@vmware.com>
|
Vadim Egorov <vegorov@vmware.com>
|
||||||
|
Vikram Krishnamurthy <vikramkrishnamu@vmware.com>
|
||||||
|
Volodymyr Bobyr <pupsua@gmail.com>
|
||||||
|
William Lam <info.virtuallyghetto@gmail.com>
|
||||||
|
Witold Krecicki <wpk@culm.net>
|
||||||
Yang Yang <yangy@vmware.com>
|
Yang Yang <yangy@vmware.com>
|
||||||
|
ykakarap <yuva2811@gmail.com>
|
||||||
Yuya Kusakabe <yuya.kusakabe@gmail.com>
|
Yuya Kusakabe <yuya.kusakabe@gmail.com>
|
||||||
|
Zacharias Taubert <zacharias.taubert@gmail.com>
|
||||||
Zach Tucker <ztucker@vmware.com>
|
Zach Tucker <ztucker@vmware.com>
|
||||||
Zee Yang <zeey@vmware.com>
|
Zee Yang <zeey@vmware.com>
|
||||||
|
zyuxin <zyuxin@vmware.com>
|
||||||
|
|
|
@ -0,0 +1,4 @@
|
||||||
|
FROM scratch
|
||||||
|
LABEL maintainer="fabio@vmware.com"
|
||||||
|
COPY govc /
|
||||||
|
ENTRYPOINT [ "/govc" ]
|
|
@ -0,0 +1,4 @@
|
||||||
|
FROM scratch
|
||||||
|
LABEL maintainer="fabio@vmware.com"
|
||||||
|
COPY vcsim /
|
||||||
|
ENTRYPOINT [ "/vcsim" ]
|
|
@ -0,0 +1,60 @@
|
||||||
|
# This file is autogenerated, do not edit; changes may be undone by the next 'dep ensure'.
|
||||||
|
|
||||||
|
|
||||||
|
[[projects]]
|
||||||
|
branch = "improvements"
|
||||||
|
digest = "1:b183578c34fabccaf65f1a57d2efeec2086abdce1446978d69ab3a0016cb750c"
|
||||||
|
name = "github.com/davecgh/go-xdr"
|
||||||
|
packages = ["xdr2"]
|
||||||
|
pruneopts = "NUT"
|
||||||
|
revision = "4930550ba2e22f87187498acfd78348b15f4e7a8"
|
||||||
|
source = "https://github.com/rasky/go-xdr"
|
||||||
|
|
||||||
|
[[projects]]
|
||||||
|
digest = "1:1ab18cf8c2084968d6dca0dd46fbda9efba08664ecd7957b63c7ca57bb2455df"
|
||||||
|
name = "github.com/google/uuid"
|
||||||
|
packages = ["."]
|
||||||
|
pruneopts = "NUT"
|
||||||
|
revision = "6a5e28554805e78ea6141142aba763936c4761c0"
|
||||||
|
|
||||||
|
[[projects]]
|
||||||
|
branch = "govmomi"
|
||||||
|
digest = "1:f49ed6cb2129e9a3ce9dde5037cb243b5849c0ec0c7973b9d1e987872d8b8cc6"
|
||||||
|
name = "github.com/kr/pretty"
|
||||||
|
packages = ["."]
|
||||||
|
pruneopts = "NUT"
|
||||||
|
revision = "2ee9d7453c02ef7fa518a83ae23644eb8872186a"
|
||||||
|
source = "https://github.com/dougm/pretty"
|
||||||
|
|
||||||
|
[[projects]]
|
||||||
|
branch = "master"
|
||||||
|
digest = "1:c3a7836b5904db0f8b609595b619916a6831cb35b8b714aec39f96d00c6155d8"
|
||||||
|
name = "github.com/kr/text"
|
||||||
|
packages = ["."]
|
||||||
|
pruneopts = "NUT"
|
||||||
|
revision = "7cafcd837844e784b526369c9bce262804aebc60"
|
||||||
|
|
||||||
|
[[projects]]
|
||||||
|
branch = "master"
|
||||||
|
digest = "1:4bea31865971675c482ed875caeabe7d2182dcb47d52900b7da5236d66dc9970"
|
||||||
|
name = "github.com/vmware/vmw-guestinfo"
|
||||||
|
packages = [
|
||||||
|
"bdoor",
|
||||||
|
"message",
|
||||||
|
"vmcheck",
|
||||||
|
]
|
||||||
|
pruneopts = "NUT"
|
||||||
|
revision = "25eff159a728be87e103a0b8045e08273f4dbec4"
|
||||||
|
|
||||||
|
[solve-meta]
|
||||||
|
analyzer-name = "dep"
|
||||||
|
analyzer-version = 1
|
||||||
|
input-imports = [
|
||||||
|
"github.com/davecgh/go-xdr/xdr2",
|
||||||
|
"github.com/google/uuid",
|
||||||
|
"github.com/kr/pretty",
|
||||||
|
"github.com/vmware/vmw-guestinfo/message",
|
||||||
|
"github.com/vmware/vmw-guestinfo/vmcheck",
|
||||||
|
]
|
||||||
|
solver-name = "gps-cdcl"
|
||||||
|
solver-version = 1
|
|
@ -0,0 +1,19 @@
|
||||||
|
# Refer to https://github.com/golang/dep/blob/master/docs/Gopkg.toml.md
|
||||||
|
# for detailed Gopkg.toml documentation.
|
||||||
|
#
|
||||||
|
# Refer to https://github.com/toml-lang/toml for detailed TOML docs.
|
||||||
|
|
||||||
|
[prune]
|
||||||
|
non-go = true
|
||||||
|
go-tests = true
|
||||||
|
unused-packages = true
|
||||||
|
|
||||||
|
[[constraint]]
|
||||||
|
branch = "improvements"
|
||||||
|
name = "github.com/davecgh/go-xdr"
|
||||||
|
source = "https://github.com/rasky/go-xdr"
|
||||||
|
|
||||||
|
[[constraint]]
|
||||||
|
branch = "govmomi"
|
||||||
|
name = "github.com/kr/pretty"
|
||||||
|
source = "https://github.com/dougm/pretty"
|
|
@ -1,4 +1,5 @@
|
||||||
.PHONY: test
|
GO ?= go
|
||||||
|
pkgs = $(shell $(GO) list ./... | grep -v 'github.com/vmware/govmomi/vim25/xml')
|
||||||
|
|
||||||
all: check test
|
all: check test
|
||||||
|
|
||||||
|
@ -6,19 +7,26 @@ check: goimports govet
|
||||||
|
|
||||||
goimports:
|
goimports:
|
||||||
@echo checking go imports...
|
@echo checking go imports...
|
||||||
@go get golang.org/x/tools/cmd/goimports
|
@command -v goimports >/dev/null 2>&1 || $(GO) get golang.org/x/tools/cmd/goimports
|
||||||
@! goimports -d . 2>&1 | egrep -v '^$$'
|
@! goimports -d . 2>&1 | egrep -v '^$$'
|
||||||
|
|
||||||
govet:
|
govet:
|
||||||
@echo checking go vet...
|
@echo checking go vet...
|
||||||
@go tool vet -structtags=false -methods=false $$(find . -mindepth 1 -maxdepth 1 -type d -not -name vendor)
|
@$(GO) vet -structtag=false -methods=false $(pkgs)
|
||||||
|
|
||||||
test:
|
|
||||||
go test -v $(TEST_OPTS) ./...
|
|
||||||
|
|
||||||
install:
|
install:
|
||||||
go install -v github.com/vmware/govmomi/govc
|
$(MAKE) -C govc install
|
||||||
go install -v github.com/vmware/govmomi/vcsim
|
$(MAKE) -C vcsim install
|
||||||
|
|
||||||
|
go-test:
|
||||||
|
GORACE=history_size=5 $(GO) test -timeout 5m -count 1 -race -v $(TEST_OPTS) ./...
|
||||||
|
|
||||||
|
govc-test: install
|
||||||
|
./govc/test/images/update.sh
|
||||||
|
(cd govc/test && ./vendor/github.com/sstephenson/bats/libexec/bats -t .)
|
||||||
|
|
||||||
|
.PHONY: test
|
||||||
|
test: go-test govc-test
|
||||||
|
|
||||||
doc: install
|
doc: install
|
||||||
./govc/usage.sh > ./govc/USAGE.md
|
./govc/usage.sh > ./govc/USAGE.md
|
||||||
|
|
|
@ -5,14 +5,19 @@
|
||||||
|
|
||||||
A Go library for interacting with VMware vSphere APIs (ESXi and/or vCenter).
|
A Go library for interacting with VMware vSphere APIs (ESXi and/or vCenter).
|
||||||
|
|
||||||
For `govc`, a CLI built on top of govmomi, check out the [govc](./govc) directory and [USAGE](./govc/USAGE.md) document.
|
In addition to the vSphere API client, this repository includes:
|
||||||
|
|
||||||
|
* [govc](./govc) - vSphere CLI
|
||||||
|
|
||||||
|
* [vcsim](./vcsim) - vSphere API mock framework
|
||||||
|
|
||||||
|
* [toolbox](./toolbox) - VM guest tools framework
|
||||||
|
|
||||||
## Compatibility
|
## Compatibility
|
||||||
|
|
||||||
This library is built for and tested against ESXi and vCenter 5.5, 6.0 and 6.5.
|
This library is built for and tested against ESXi and vCenter 6.0, 6.5 and 6.7.
|
||||||
|
|
||||||
If you're able to use it against older versions of ESXi and/or vCenter, please
|
It may work with versions 5.5 and 5.1, but neither are officially supported.
|
||||||
leave a note and we'll include it in this compatibility list.
|
|
||||||
|
|
||||||
## Documentation
|
## Documentation
|
||||||
|
|
||||||
|
@ -25,17 +30,12 @@ See [godoc.org][godoc] for documentation.
|
||||||
|
|
||||||
[apiref]:http://pubs.vmware.com/vsphere-6-5/index.jsp#com.vmware.wssdk.apiref.doc/right-pane.html
|
[apiref]:http://pubs.vmware.com/vsphere-6-5/index.jsp#com.vmware.wssdk.apiref.doc/right-pane.html
|
||||||
[godoc]:http://godoc.org/github.com/vmware/govmomi
|
[godoc]:http://godoc.org/github.com/vmware/govmomi
|
||||||
[drone]:https://drone.io
|
|
||||||
[dronesrc]:https://github.com/drone/drone
|
|
||||||
[dronecli]:http://readme.drone.io/devs/cli/
|
|
||||||
|
|
||||||
#### Building with CI
|
## Installation
|
||||||
Merges to this repository will trigger builds in both Travis and [Drone][drone].
|
|
||||||
|
|
||||||
To build locally with Drone:
|
```sh
|
||||||
- Ensure that you have Docker 1.6 or higher installed.
|
go get -u github.com/vmware/govmomi
|
||||||
- Install the [Drone command line tools][dronecli].
|
```
|
||||||
- Run `drone exec` from within the root directory of the govmomi repository.
|
|
||||||
|
|
||||||
## Discussion
|
## Discussion
|
||||||
|
|
||||||
|
@ -53,9 +53,21 @@ Refer to the [CHANGELOG](CHANGELOG.md) for version to version changes.
|
||||||
|
|
||||||
* [Docker Machine](https://github.com/docker/machine/tree/master/drivers/vmwarevsphere)
|
* [Docker Machine](https://github.com/docker/machine/tree/master/drivers/vmwarevsphere)
|
||||||
|
|
||||||
|
* [Docker InfraKit](https://github.com/docker/infrakit/tree/master/pkg/provider/vsphere)
|
||||||
|
|
||||||
|
* [Docker LinuxKit](https://github.com/linuxkit/linuxkit/tree/master/src/cmd/linuxkit)
|
||||||
|
|
||||||
* [Kubernetes](https://github.com/kubernetes/kubernetes/tree/master/pkg/cloudprovider/providers/vsphere)
|
* [Kubernetes](https://github.com/kubernetes/kubernetes/tree/master/pkg/cloudprovider/providers/vsphere)
|
||||||
|
|
||||||
* [Terraform](https://github.com/hashicorp/terraform/tree/master/builtin/providers/vsphere)
|
* [Kubernetes Cloud Provider](https://github.com/kubernetes/cloud-provider-vsphere)
|
||||||
|
|
||||||
|
* [Kubernetes Cluster API](https://github.com/kubernetes-sigs/cluster-api-provider-vsphere)
|
||||||
|
|
||||||
|
* [Kubernetes kops](https://github.com/kubernetes/kops/tree/master/upup/pkg/fi/cloudup/vsphere)
|
||||||
|
|
||||||
|
* [Terraform](https://github.com/terraform-providers/terraform-provider-vsphere)
|
||||||
|
|
||||||
|
* [Packer](https://github.com/jetbrains-infra/packer-builder-vsphere)
|
||||||
|
|
||||||
* [VMware VIC Engine](https://github.com/vmware/vic)
|
* [VMware VIC Engine](https://github.com/vmware/vic)
|
||||||
|
|
||||||
|
@ -67,6 +79,12 @@ Refer to the [CHANGELOG](CHANGELOG.md) for version to version changes.
|
||||||
|
|
||||||
* [Libretto](https://github.com/apcera/libretto/tree/master/virtualmachine/vsphere)
|
* [Libretto](https://github.com/apcera/libretto/tree/master/virtualmachine/vsphere)
|
||||||
|
|
||||||
|
* [Telegraf](https://github.com/influxdata/telegraf/tree/master/plugins/inputs/vsphere)
|
||||||
|
|
||||||
|
* [Open Storage](https://github.com/libopenstorage/openstorage/tree/master/pkg/storageops/vsphere)
|
||||||
|
|
||||||
|
* [Juju](https://github.com/juju/juju)
|
||||||
|
|
||||||
## Related projects
|
## Related projects
|
||||||
|
|
||||||
* [rbvmomi](https://github.com/vmware/rbvmomi)
|
* [rbvmomi](https://github.com/vmware/rbvmomi)
|
||||||
|
@ -75,4 +93,4 @@ Refer to the [CHANGELOG](CHANGELOG.md) for version to version changes.
|
||||||
|
|
||||||
## License
|
## License
|
||||||
|
|
||||||
govmomi is available under the [Apache 2 license](LICENSE).
|
govmomi is available under the [Apache 2 license](LICENSE.txt).
|
||||||
|
|
|
@ -58,7 +58,6 @@ package govmomi
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"context"
|
"context"
|
||||||
"crypto/tls"
|
|
||||||
"net/url"
|
"net/url"
|
||||||
|
|
||||||
"github.com/vmware/govmomi/property"
|
"github.com/vmware/govmomi/property"
|
||||||
|
@ -99,41 +98,11 @@ func NewClient(ctx context.Context, u *url.URL, insecure bool) (*Client, error)
|
||||||
return c, nil
|
return c, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// NewClientWithCertificate creates a new client from a URL. The client authenticates with the
|
|
||||||
// server with the certificate before returning if the URL contains user information.
|
|
||||||
func NewClientWithCertificate(ctx context.Context, u *url.URL, insecure bool, cert tls.Certificate) (*Client, error) {
|
|
||||||
soapClient := soap.NewClient(u, insecure)
|
|
||||||
soapClient.SetCertificate(cert)
|
|
||||||
vimClient, err := vim25.NewClient(ctx, soapClient)
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
|
|
||||||
c := &Client{
|
|
||||||
Client: vimClient,
|
|
||||||
SessionManager: session.NewManager(vimClient),
|
|
||||||
}
|
|
||||||
|
|
||||||
if u.User != nil {
|
|
||||||
err = c.LoginExtensionByCertificate(ctx, u.User.Username(), "")
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return c, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// Login dispatches to the SessionManager.
|
// Login dispatches to the SessionManager.
|
||||||
func (c *Client) Login(ctx context.Context, u *url.Userinfo) error {
|
func (c *Client) Login(ctx context.Context, u *url.Userinfo) error {
|
||||||
return c.SessionManager.Login(ctx, u)
|
return c.SessionManager.Login(ctx, u)
|
||||||
}
|
}
|
||||||
|
|
||||||
// Login dispatches to the SessionManager.
|
|
||||||
func (c *Client) LoginExtensionByCertificate(ctx context.Context, key string, locale string) error {
|
|
||||||
return c.SessionManager.LoginExtensionByCertificate(ctx, key, locale)
|
|
||||||
}
|
|
||||||
|
|
||||||
// Logout dispatches to the SessionManager.
|
// Logout dispatches to the SessionManager.
|
||||||
func (c *Client) Logout(ctx context.Context) error {
|
func (c *Client) Logout(ctx context.Context) error {
|
||||||
// Close any idle connections after logging out.
|
// Close any idle connections after logging out.
|
||||||
|
|
|
@ -38,16 +38,26 @@ type Finder struct {
|
||||||
folders *object.DatacenterFolders
|
folders *object.DatacenterFolders
|
||||||
}
|
}
|
||||||
|
|
||||||
func NewFinder(client *vim25.Client, all bool) *Finder {
|
func NewFinder(client *vim25.Client, all ...bool) *Finder {
|
||||||
|
props := false
|
||||||
|
if len(all) == 1 {
|
||||||
|
props = all[0]
|
||||||
|
}
|
||||||
|
|
||||||
f := &Finder{
|
f := &Finder{
|
||||||
client: client,
|
client: client,
|
||||||
si: object.NewSearchIndex(client),
|
si: object.NewSearchIndex(client),
|
||||||
r: recurser{
|
r: recurser{
|
||||||
Collector: property.DefaultCollector(client),
|
Collector: property.DefaultCollector(client),
|
||||||
All: all,
|
All: props,
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if len(all) == 0 {
|
||||||
|
// attempt to avoid SetDatacenter() requirement
|
||||||
|
f.dc, _ = f.DefaultDatacenter(context.Background())
|
||||||
|
}
|
||||||
|
|
||||||
return f
|
return f
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -253,7 +263,7 @@ func (f *Finder) managedObjectList(ctx context.Context, path string, tl bool, in
|
||||||
fn = f.dcReference
|
fn = f.dcReference
|
||||||
}
|
}
|
||||||
|
|
||||||
if len(path) == 0 {
|
if path == "" {
|
||||||
path = "."
|
path = "."
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -625,6 +635,15 @@ func (f *Finder) ClusterComputeResourceList(ctx context.Context, path string) ([
|
||||||
return ccrs, nil
|
return ccrs, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (f *Finder) DefaultClusterComputeResource(ctx context.Context) (*object.ClusterComputeResource, error) {
|
||||||
|
cr, err := f.ClusterComputeResource(ctx, "*")
|
||||||
|
if err != nil {
|
||||||
|
return nil, toDefaultError(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
return cr, nil
|
||||||
|
}
|
||||||
|
|
||||||
func (f *Finder) ClusterComputeResource(ctx context.Context, path string) (*object.ClusterComputeResource, error) {
|
func (f *Finder) ClusterComputeResource(ctx context.Context, path string) (*object.ClusterComputeResource, error) {
|
||||||
ccrs, err := f.ClusterComputeResourceList(ctx, path)
|
ccrs, err := f.ClusterComputeResourceList(ctx, path)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
@ -638,6 +657,18 @@ func (f *Finder) ClusterComputeResource(ctx context.Context, path string) (*obje
|
||||||
return ccrs[0], nil
|
return ccrs[0], nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (f *Finder) ClusterComputeResourceOrDefault(ctx context.Context, path string) (*object.ClusterComputeResource, error) {
|
||||||
|
if path != "" {
|
||||||
|
cr, err := f.ClusterComputeResource(ctx, path)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
return cr, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
return f.DefaultClusterComputeResource(ctx)
|
||||||
|
}
|
||||||
|
|
||||||
func (f *Finder) HostSystemList(ctx context.Context, path string) ([]*object.HostSystem, error) {
|
func (f *Finder) HostSystemList(ctx context.Context, path string) ([]*object.HostSystem, error) {
|
||||||
s := &spec{
|
s := &spec{
|
||||||
Relative: f.hostFolder,
|
Relative: f.hostFolder,
|
||||||
|
@ -695,7 +726,7 @@ func (f *Finder) HostSystem(ctx context.Context, path string) (*object.HostSyste
|
||||||
}
|
}
|
||||||
|
|
||||||
func (f *Finder) DefaultHostSystem(ctx context.Context) (*object.HostSystem, error) {
|
func (f *Finder) DefaultHostSystem(ctx context.Context) (*object.HostSystem, error) {
|
||||||
hs, err := f.HostSystem(ctx, "*/*")
|
hs, err := f.HostSystem(ctx, "*")
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, toDefaultError(err)
|
return nil, toDefaultError(err)
|
||||||
}
|
}
|
||||||
|
@ -885,6 +916,12 @@ func (f *Finder) DefaultFolder(ctx context.Context) (*object.Folder, error) {
|
||||||
}
|
}
|
||||||
folder := object.NewFolder(f.client, ref.Reference())
|
folder := object.NewFolder(f.client, ref.Reference())
|
||||||
|
|
||||||
|
// Set the InventoryPath of the newly created folder object
|
||||||
|
// The default foler becomes the datacenter's "vm" folder.
|
||||||
|
// The "vm" folder always exists for a datacenter. It cannot be
|
||||||
|
// removed or replaced
|
||||||
|
folder.SetInventoryPath(path.Join(f.dc.InventoryPath, "vm"))
|
||||||
|
|
||||||
return folder, nil
|
return folder, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -190,7 +190,7 @@ func (r recurser) List(ctx context.Context, s *spec, root list.Element, parts []
|
||||||
}
|
}
|
||||||
|
|
||||||
if !matched {
|
if !matched {
|
||||||
matched = strings.HasSuffix(e.Path, path.Join(all...))
|
matched = strings.HasSuffix(e.Path, "/"+path.Join(all...))
|
||||||
if matched {
|
if matched {
|
||||||
// name contains a '/'
|
// name contains a '/'
|
||||||
out = append(out, e)
|
out = append(out, e)
|
||||||
|
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue