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"
|
||||
vmwareisobuilder "github.com/hashicorp/packer/builder/vmware/iso"
|
||||
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"
|
||||
alicloudimportpostprocessor "github.com/hashicorp/packer/post-processor/alicloud-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),
|
||||
"vmware-iso": new(vmwareisobuilder.Builder),
|
||||
"vmware-vmx": new(vmwarevmxbuilder.Builder),
|
||||
"vsphere-clone": new(vsphereclonebuilder.Builder),
|
||||
"vsphere-iso": new(vsphereisobuilder.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/storage v1.4.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/Azure/azure-sdk-for-go v30.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/test v0.0.0-20180213035817-a1ea475d72b1 // 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/digitalocean/go-libvirt v0.0.0-20190626172931-4d226dd6c437 // indirect
|
||||
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/dylanmei/iso8601 v0.1.0 // indirect
|
||||
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/fatih/camelcase 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/google/go-cmp v0.3.1
|
||||
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/uuid v1.0.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/hcl v1.0.0 // indirect
|
||||
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/vault v1.1.0
|
||||
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/pgzip v0.0.0-20151221113845-47f36e165cec
|
||||
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/masterzen/azure-sdk-for-go v0.0.0-20161014135628-ee4f0065d00c // 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-homedir v1.0.0
|
||||
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/mapstructure v0.0.0-20180111000720-b4575eea38cc
|
||||
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/nu7hatch/gouuid v0.0.0-20131221200532-179d4d0c4d8d // indirect
|
||||
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/outscale/osc-go v0.0.1
|
||||
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/posener/complete v1.1.1
|
||||
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/rogpeppe/go-internal v1.5.0 // indirect
|
||||
github.com/ryanuber/go-glob v0.0.0-20170128012129-256dc444b735 // indirect
|
||||
github.com/saintfish/chardet v0.0.0-20120816061221-3af4cd4741ca // 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/ugorji/go v0.0.0-20151218193438-646ae4a518c1
|
||||
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/yandex-cloud/go-genproto v0.0.0-20190916101622-7617782d381e
|
||||
github.com/yandex-cloud/go-sdk v0.0.0-20190916101744-c781afa45829
|
||||
|
@ -160,16 +153,14 @@ require (
|
|||
go.opencensus.io v0.22.2 // indirect
|
||||
golang.org/x/crypto v0.0.0-20191202143827-86a70503ff7e
|
||||
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/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/oauth2 v0.0.0-20191202225959-858c2ad4c8b6
|
||||
golang.org/x/sync v0.0.0-20190911185100-cd5d95a43a6e
|
||||
golang.org/x/sys v0.0.0-20191128015809-6d18c012aee9
|
||||
golang.org/x/time v0.0.0-20191024005414-555d28b269f0 // indirect
|
||||
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/appengine v1.6.5 // 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/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.3.0 h1:sAbMqjY1PEQKZBWfbu6Y6bsupJ9c4QdHnzg/VvYTLcE=
|
||||
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/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/storage v1.0.0/go.mod h1:IhtSnM/ZTZV8YYJWCY8RULGVqBDmpoyjwiyrjsg+URw=
|
||||
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/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-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/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/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/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/go.mod h1:chxPXzSsl7ZWRAuOIE23GDNzjWuZquvFlgA8xmpunjU=
|
||||
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/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/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/go.mod h1:OGWyIMJ87/k/GCz8CGiWB2HOXsOVDM6Lpe/nFPkC4IQ=
|
||||
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/xquery v0.0.0-20170730121040-eb8c3c172607 h1:BFFG6KP8ASFBg2ptWsJn8p8RDufBjBDKIxLU7BTYGOM=
|
||||
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/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-textseg v1.0.0 h1:rRmlIsPEEhUTIKQb7T++Nz/A5Q6C9IuX2wFoYVvnCs0=
|
||||
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/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/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/go.mod h1:S/7n9copUssQ56c7aAgHqftWO4LTf4xY6CGWt8Bc+3M=
|
||||
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/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/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.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c=
|
||||
github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
|
||||
github.com/davecgh/go-xdr v0.0.0-20161123171359-e6a2ba005892/go.mod h1:CTDl0pzVzE5DEzZhPfvhY/9sPFMQIxaJ9VAMs9AagrE=
|
||||
github.com/dgrijalva/jwt-go v3.2.0+incompatible h1:7qlOGliEKZXTDg6OTjfoBKDXWrumCAMpl/TFQ4/5kLM=
|
||||
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=
|
||||
|
@ -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/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.1/go.mod h1:G1fbsNGAFpC1aaERrShZQVdUV2ZuZuv6FCl2v9JNSxQ=
|
||||
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/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/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-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-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-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-test/deep v1.0.3 h1:ZrJSEWsXzPOxaZnFteGEfooLba+ju3FYIbOrS+rQd68=
|
||||
github.com/go-test/deep v1.0.3/go.mod h1:wGDj63lr65AM2AQyKZd/NYHGb0R+1RLqB8NKt3aSFNA=
|
||||
github.com/gobwas/glob v0.2.3 h1:A4xDbljILXROh+kObIiy5kIaPYD8e96x1tgBhUI5J+Y=
|
||||
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/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-20191105193234-27840fff0d09/go.mod h1:zfwlbNMJ+OItoe0UupaVj+oy1omPYYDuagoSzA8v9mc=
|
||||
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/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/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
|
||||
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/hashicorp/consul v1.4.0 h1:PQTW4xCuAExEiSbhrsFsikzbW5gVBoi74BjUvYFyKHw=
|
||||
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/go.mod h1:YH+1FKiLXxHSkmPseP+kNlulaMuP3n2brvKWEqk/Jc4=
|
||||
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-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-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/go.mod h1:dHtQlpGsu+cZNNAkkCN/P3hoUDHhCYQXV3UM06sGGrk=
|
||||
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.1 h1:fv1ep09latC32wFoVwnqcnKJGnMSdBanPczbHAYm1BE=
|
||||
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/go.mod h1:fltr4n8CU8Ke44wwGCBoEymUuxUHl09ZGVZPK5anwXA=
|
||||
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/v2 v2.0.0 h1:efQznTz+ydmQXq3BOnRa3AXzvCeTq1P4dKj/z5GLlY8=
|
||||
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/mdns v1.0.0/go.mod h1:tL+uN++7HEJ6SQLQ2/p+z2pH24WQKWjBPkE0mNTz8vQ=
|
||||
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/hpcloud/tail v1.0.0 h1:nfCOvKYfkgYP8hkirhJocXT2+zOD8yUNjXaWfTlyFKI=
|
||||
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/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/go.mod h1:UrKjuULIWLjHFlG6aSPunArE5QX57LftMmStAZJBEX8=
|
||||
github.com/jehiah/go-strftime v0.0.0-20171201141054-1d33003b3869 h1:IPJ3dvxmJ4uczJe5YQdrYB16oTJlGSC/OyZDqUk9xX4=
|
||||
github.com/jehiah/go-strftime v0.0.0-20171201141054-1d33003b3869/go.mod h1:cJ6Cj7dQo+O6GJNiMx+Pa94qKj+TG8ONdKHgMNIyyag=
|
||||
github.com/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-20180206201540-c2b33e8439af h1:pmfjZENx5imkbgOkpRUYLnmbU7UEFbjtDA2hxJ1ichM=
|
||||
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/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORNo=
|
||||
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/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI=
|
||||
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-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-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/go.mod h1:XPvLUNfbS4fJH25nqRHfWLMa1ONC8Amw+mIA639KxkE=
|
||||
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/go.mod h1:ZXFpozHsX6DPmq2I0TCekCxypsnAUbP2oI0UX1GXzOo=
|
||||
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/go.mod h1:JwYml1nuB7xOzsp52dPpHFffvOCDupsG0QubkSMEySY=
|
||||
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/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-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/go.mod h1:SAEjPB4voP88qmWJXI7mA5m15uNlEnuHLx4Eu2mPGpQ=
|
||||
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.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/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=
|
||||
|
@ -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/sean-/seed v0.0.0-20170313163322-e2103e2c3529 h1:nn5Wsu0esKSJiIVhscUtVbo7ada43DJhG55ua/hjS5I=
|
||||
github.com/sean-/seed v0.0.0-20170313163322-e2103e2c3529/go.mod h1:DxrIzT+xaE7yg65j358z/aeFdxmN0P9QXhEzd20vsDc=
|
||||
github.com/sergi/go-diff v1.0.0 h1:Kpca3qRNrduNnOQeazBd0ysaKrUJiIuISHxogkT9RPQ=
|
||||
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/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.1 h1:2vfRuCMp5sSVIDSqO8oNnWJq7mPa6KVP3iPIwFBuy8A=
|
||||
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/testify v1.2.2 h1:bSDNvY7ZPG5RlJ8otE/7V6gMiyenm9RtJ7IUVIAoJ1w=
|
||||
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/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/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/go.mod h1:lM6fpI8y6iwACtlbHUav823/uKPdXsNBlnBpRF2fj3c=
|
||||
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/vmihailenco/msgpack v3.3.3+incompatible h1:wapg9xDUZDzGCNFlwc5SqI1rvcciqcxEHac4CYj89xI=
|
||||
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.0.0-20170707011325-c2105a174311/go.mod h1:URlwyTFZX72RmxtxuaFL2Uj3fD1JTvZdx59bHWk6aFU=
|
||||
github.com/vmware/govmomi v0.21.0 h1:jc8uMuxpcV2xMAA/cnEDlnsIjvqcMra5Y8onh/U3VuY=
|
||||
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/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/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/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/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/go.mod h1:xnAOWiHeOqg2nWS62VtQ7pbOu17FtxJNW8RLEih+O3s=
|
||||
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-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-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/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=
|
||||
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/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-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-20190227174305-5b3e6a55c961/go.mod h1:wehouNa3lNwaWXcvxsM5YxQ5yQlVC4a0KAMCusXpPoU=
|
||||
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/mobile v0.0.0-20190312151609-d3739f865fa6/go.mod h1:z+o9i4GpDbdi3rU15maQ/Ox0txvL9dWGYEHz965HBQE=
|
||||
golang.org/x/mobile v0.0.0-20190719004257-d2bd2a29d028/go.mod h1:E/iHnbuqvinMTCcRqshq8CkpyQDoeVncDDYHnLhea+o=
|
||||
golang.org/x/mobile v0.0.0-20191130191448-5c0e7e404af8 h1:9w7mvrikkrG9zFfEJfuFe08FVKrg8Yi0ePhOdGAKpUw=
|
||||
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.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-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-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-20190603091049-60506f45cf65/go.mod h1:HSz+uSET+XFnRR8LxR5pz3Of3rY3CfYBVs4xY44aLks=
|
||||
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/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/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
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/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-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
|
||||
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.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 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-20190902080502-41f04d3bba15 h1:YR8cESwS4TdDjEe65xsg0ogRM/Nc3DYOhEAlW+xobZo=
|
||||
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/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-20190106161140-3f1c8253044a/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.1-2019.2.3 h1:3JgtbtFHMiCmsznwGVTUWbgGov+pVqnlf1dEJTNAXeM=
|
||||
honnef.co/go/tools v0.0.1-2019.2.3/go.mod h1:a3bituU0lyd329TUQxRnasdCoJDkEUEAqEt0JzvZhAg=
|
||||
|
|
|
@ -3,6 +3,7 @@ package config
|
|||
import (
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"log"
|
||||
"reflect"
|
||||
"sort"
|
||||
"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
|
||||
// for build-specific variables. Pull these out to add to interpolation
|
||||
// context.
|
||||
if len(raws) == 0 {
|
||||
return nil, raws
|
||||
}
|
||||
|
||||
// Internally, our tests may cause this to be read as a map[string]string
|
||||
placeholderData := raws[len(raws)-1]
|
||||
|
@ -201,6 +205,7 @@ func DetectContext(raws ...interface{}) (*interpolate.Context, error) {
|
|||
|
||||
for _, r := range raws {
|
||||
if err := mapstructure.Decode(r, &s); err != nil {
|
||||
log.Printf("Error detecting context: %s", 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
|
||||
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>
|
||||
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> <bruce.downs@jivesoftware.com>
|
||||
Bruce Downs <bruceadowns@gmail.com> <bruce.downs@autodesk.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 <cblomart@gmail.com>
|
||||
David Stark <dave@davidstark.name> <david.stark@bskyb.com>
|
||||
Eric Gray <egray@vmware.com> <ericgray@users.noreply.github.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>
|
||||
Jeremy Canady <jcanady@jackhenry.com> <jcanady@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> tkak <takaaki.frkw@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>
|
||||
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:
|
||||
- 1.8
|
||||
# Set the version of Go.
|
||||
language: go
|
||||
go: 1.12
|
||||
|
||||
before_install:
|
||||
- make vendor
|
||||
# Always set the project's Go import path to ensure that forked
|
||||
# builds get cloned to the correct location.
|
||||
go_import_path: github.com/vmware/govmomi
|
||||
|
||||
script:
|
||||
- make check test
|
||||
# Ensure all the jobs know where the temp directory is.
|
||||
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
|
||||
|
||||
### 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)
|
||||
|
||||
* WaitOptions.MaxWaitSeconds is now optional
|
||||
|
|
|
@ -3,34 +3,55 @@
|
|||
# This script is generated by contributors.sh
|
||||
#
|
||||
|
||||
Abhijeet Kasurde <akasurde@redhat.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>
|
||||
amandahla <amanda.andrade@serpro.gov.br>
|
||||
Amanda H. L. de Andrade <amanda.andrade@serpro.gov.br>
|
||||
Amit Bathla <abathla@.vmware.com>
|
||||
amit bezalel <amit.bezalel@hpe.com>
|
||||
Andrew <AndrewDi@users.noreply.github.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>
|
||||
Arran Walker <arran.walker@zopa.com>
|
||||
Aryeh Weinreb <aryehweinreb@gmail.com>
|
||||
Austin Parker <aparker@apprenda.com>
|
||||
Balu Dontu <bdontu@vmware.com>
|
||||
bastienbc <bastien.barbe.creuly@gmail.com>
|
||||
Benjamin Peterson <benjamin@python.org>
|
||||
Bob Killen <killen.bob@gmail.com>
|
||||
Brad Fitzpatrick <bradfitz@golang.org>
|
||||
Bruce Downs <bruceadowns@gmail.com>
|
||||
Cédric Blomart <cblomart@gmail.com>
|
||||
Chris Marchesi <chrism@vancluevertech.com>
|
||||
Christian Höltje <docwhat@gerf.org>
|
||||
Clint Greenwood <cgreenwood@vmware.com>
|
||||
CuiHaozhi <cuihaozhi@chinacloud.com.cn>
|
||||
Danny Lockard <danny.lockard@banno.com>
|
||||
Dave Smith-Uchida <dsmithuchida@vmware.com>
|
||||
Dave Tucker <dave@dtucker.co.uk>
|
||||
Davide Agnello <dagnello@hp.com>
|
||||
David Stark <dave@davidstark.name>
|
||||
Davinder Kumar <davinderk@vmware.com>
|
||||
Deric Crago <deric.crago@gmail.com>
|
||||
Doug MacEachern <dougm@vmware.com>
|
||||
Eloy Coto <eloy.coto@gmail.com>
|
||||
Eric Gray <egray@vmware.com>
|
||||
Eric Yutao <eric.yutao@gmail.com>
|
||||
Erik Hollensbe <github@hollensbe.org>
|
||||
Ethan Kaley <ethan.kaley@emc.com>
|
||||
Fabio Rapposelli <fabio@vmware.com>
|
||||
Faiyaz Ahmed <ahmedf@vmware.com>
|
||||
forkbomber <forkbomber@users.noreply.github.com>
|
||||
freebsdly <qinhuajun@outlook.com>
|
||||
Gavin Gray <gavin@infinio.com>
|
||||
Gavrie Philipson <gavrie.philipson@elastifile.com>
|
||||
George Hicken <ghicken@vmware.com>
|
||||
|
@ -38,24 +59,65 @@ Gerrit Renker <Gerrit.Renker@ctl.io>
|
|||
gthombare <gthombare@vmware.com>
|
||||
Hasan Mahmood <mahmoodh@vmware.com>
|
||||
Henrik Hodne <henrik@travis-ci.com>
|
||||
hui luo <luoh@vmware.com>
|
||||
Isaac Rodman <isaac@eyz.us>
|
||||
Ivan Porto Carrero <icarrero@vmware.com>
|
||||
James King <james.king@emc.com>
|
||||
Jason Kincl <jkincl@gmail.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>
|
||||
maplain <fangyuanl@vmware.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>
|
||||
Michal Jankowski <mjankowski@vmware.com>
|
||||
mingwei <mingwei@smartx.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>
|
||||
prydin <prydin@vmware.com>
|
||||
rHermes <teodor_spaeren@riseup.net>
|
||||
Rowan Jacobs <rojacobs@pivotal.io>
|
||||
runner.mei <runner.mei@gmail.com>
|
||||
S.Çağlar Onur <conur@vmware.com>
|
||||
Sergey Ignatov <sergey.ignatov@jetbrains.com>
|
||||
Sten Feldman <exile@chamber.ee>
|
||||
Stepan Mazurov <smazurov@gmail.com>
|
||||
Steve Purcell <steve@sanityinc.com>
|
||||
Takaaki Furukawa <takaaki.frkw@gmail.com>
|
||||
Tamas Eger <tamas.eger@bitrise.io>
|
||||
tanishi <tanishi503@gmail.com>
|
||||
Ted Zlatanov <tzz@lifelogs.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>
|
||||
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>
|
||||
ykakarap <yuva2811@gmail.com>
|
||||
Yuya Kusakabe <yuya.kusakabe@gmail.com>
|
||||
Zacharias Taubert <zacharias.taubert@gmail.com>
|
||||
Zach Tucker <ztucker@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
|
||||
|
||||
|
@ -6,19 +7,26 @@ check: goimports govet
|
|||
|
||||
goimports:
|
||||
@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 '^$$'
|
||||
|
||||
govet:
|
||||
@echo checking go vet...
|
||||
@go tool vet -structtags=false -methods=false $$(find . -mindepth 1 -maxdepth 1 -type d -not -name vendor)
|
||||
|
||||
test:
|
||||
go test -v $(TEST_OPTS) ./...
|
||||
@$(GO) vet -structtag=false -methods=false $(pkgs)
|
||||
|
||||
install:
|
||||
go install -v github.com/vmware/govmomi/govc
|
||||
go install -v github.com/vmware/govmomi/vcsim
|
||||
$(MAKE) -C govc install
|
||||
$(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
|
||||
./govc/usage.sh > ./govc/USAGE.md
|
||||
|
|
|
@ -5,14 +5,19 @@
|
|||
|
||||
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
|
||||
|
||||
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
|
||||
leave a note and we'll include it in this compatibility list.
|
||||
It may work with versions 5.5 and 5.1, but neither are officially supported.
|
||||
|
||||
## 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
|
||||
[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
|
||||
Merges to this repository will trigger builds in both Travis and [Drone][drone].
|
||||
## Installation
|
||||
|
||||
To build locally with Drone:
|
||||
- Ensure that you have Docker 1.6 or higher installed.
|
||||
- Install the [Drone command line tools][dronecli].
|
||||
- Run `drone exec` from within the root directory of the govmomi repository.
|
||||
```sh
|
||||
go get -u github.com/vmware/govmomi
|
||||
```
|
||||
|
||||
## 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 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)
|
||||
|
||||
* [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)
|
||||
|
||||
|
@ -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)
|
||||
|
||||
* [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
|
||||
|
||||
* [rbvmomi](https://github.com/vmware/rbvmomi)
|
||||
|
@ -75,4 +93,4 @@ Refer to the [CHANGELOG](CHANGELOG.md) for version to version changes.
|
|||
|
||||
## 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 (
|
||||
"context"
|
||||
"crypto/tls"
|
||||
"net/url"
|
||||
|
||||
"github.com/vmware/govmomi/property"
|
||||
|
@ -99,41 +98,11 @@ func NewClient(ctx context.Context, u *url.URL, insecure bool) (*Client, error)
|
|||
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.
|
||||
func (c *Client) Login(ctx context.Context, u *url.Userinfo) error {
|
||||
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.
|
||||
func (c *Client) Logout(ctx context.Context) error {
|
||||
// Close any idle connections after logging out.
|
||||
|
|
|
@ -38,16 +38,26 @@ type Finder struct {
|
|||
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{
|
||||
client: client,
|
||||
si: object.NewSearchIndex(client),
|
||||
r: recurser{
|
||||
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
|
||||
}
|
||||
|
||||
|
@ -253,7 +263,7 @@ func (f *Finder) managedObjectList(ctx context.Context, path string, tl bool, in
|
|||
fn = f.dcReference
|
||||
}
|
||||
|
||||
if len(path) == 0 {
|
||||
if path == "" {
|
||||
path = "."
|
||||
}
|
||||
|
||||
|
@ -625,6 +635,15 @@ func (f *Finder) ClusterComputeResourceList(ctx context.Context, path string) ([
|
|||
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) {
|
||||
ccrs, err := f.ClusterComputeResourceList(ctx, path)
|
||||
if err != nil {
|
||||
|
@ -638,6 +657,18 @@ func (f *Finder) ClusterComputeResource(ctx context.Context, path string) (*obje
|
|||
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) {
|
||||
s := &spec{
|
||||
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) {
|
||||
hs, err := f.HostSystem(ctx, "*/*")
|
||||
hs, err := f.HostSystem(ctx, "*")
|
||||
if err != nil {
|
||||
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())
|
||||
|
||||
// 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
|
||||
}
|
||||
|
||||
|
|
|
@ -190,7 +190,7 @@ func (r recurser) List(ctx context.Context, s *spec, root list.Element, parts []
|
|||
}
|
||||
|
||||
if !matched {
|
||||
matched = strings.HasSuffix(e.Path, path.Join(all...))
|
||||
matched = strings.HasSuffix(e.Path, "/"+path.Join(all...))
|
||||
if matched {
|
||||
// name contains a '/'
|
||||
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