Add ISO builder which creates an empty VM
This commit is contained in:
parent
f909669670
commit
df40ffbe8d
4
build.sh
4
build.sh
|
@ -11,3 +11,7 @@ rm -f bin/*
|
||||||
GOOS=darwin go build -o bin/packer-builder-vsphere-clone.macos ./clone
|
GOOS=darwin go build -o bin/packer-builder-vsphere-clone.macos ./clone
|
||||||
GOOS=linux go build -o bin/packer-builder-vsphere-clone.linux ./clone
|
GOOS=linux go build -o bin/packer-builder-vsphere-clone.linux ./clone
|
||||||
GOOS=windows go build -o bin/packer-builder-vsphere-clone.exe ./clone
|
GOOS=windows go build -o bin/packer-builder-vsphere-clone.exe ./clone
|
||||||
|
|
||||||
|
GOOS=darwin go build -o bin/packer-builder-vsphere-iso.macos ./iso
|
||||||
|
GOOS=linux go build -o bin/packer-builder-vsphere-iso.linux ./iso
|
||||||
|
GOOS=windows go build -o bin/packer-builder-vsphere-iso.exe ./iso
|
||||||
|
|
|
@ -1,8 +1,6 @@
|
||||||
package clone
|
package clone
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"errors"
|
|
||||||
|
|
||||||
packerCommon "github.com/hashicorp/packer/common"
|
packerCommon "github.com/hashicorp/packer/common"
|
||||||
"github.com/hashicorp/packer/helper/communicator"
|
"github.com/hashicorp/packer/helper/communicator"
|
||||||
"github.com/hashicorp/packer/packer"
|
"github.com/hashicorp/packer/packer"
|
||||||
|
@ -75,18 +73,8 @@ func (b *Builder) Run(ui packer.Ui, hook packer.Hook, cache packer.Cache) (packe
|
||||||
b.runner = packerCommon.NewRunner(steps, b.config.PackerConfig, ui)
|
b.runner = packerCommon.NewRunner(steps, b.config.PackerConfig, ui)
|
||||||
b.runner.Run(state)
|
b.runner.Run(state)
|
||||||
|
|
||||||
// If there was an error, return that
|
if err := common.CheckRunStatus(state); err != nil {
|
||||||
if rawErr, ok := state.GetOk("error"); ok {
|
return nil, err
|
||||||
return nil, rawErr.(error)
|
|
||||||
}
|
|
||||||
|
|
||||||
// If we were interrupted or cancelled, then just exit.
|
|
||||||
if _, ok := state.GetOk(multistep.StateCancelled); ok {
|
|
||||||
return nil, errors.New("Build was cancelled.")
|
|
||||||
}
|
|
||||||
|
|
||||||
if _, ok := state.GetOk(multistep.StateHalted); ok {
|
|
||||||
return nil, errors.New("Build was halted.")
|
|
||||||
}
|
}
|
||||||
|
|
||||||
artifact := &common.Artifact{
|
artifact := &common.Artifact{
|
||||||
|
|
|
@ -1,21 +1,20 @@
|
||||||
package clone
|
package clone
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"encoding/json"
|
|
||||||
"fmt"
|
|
||||||
builderT "github.com/hashicorp/packer/helper/builder/testing"
|
builderT "github.com/hashicorp/packer/helper/builder/testing"
|
||||||
|
commonT "github.com/jetbrains-infra/packer-builder-vsphere/common/testing"
|
||||||
|
|
||||||
"github.com/hashicorp/packer/packer"
|
"github.com/hashicorp/packer/packer"
|
||||||
"github.com/jetbrains-infra/packer-builder-vsphere/common"
|
|
||||||
"github.com/jetbrains-infra/packer-builder-vsphere/driver"
|
"github.com/jetbrains-infra/packer-builder-vsphere/driver"
|
||||||
"math/rand"
|
|
||||||
"testing"
|
"testing"
|
||||||
|
"github.com/jetbrains-infra/packer-builder-vsphere/common"
|
||||||
)
|
)
|
||||||
|
|
||||||
func TestBuilderAcc_default(t *testing.T) {
|
func TestBuilderAcc_default(t *testing.T) {
|
||||||
config := defaultConfig()
|
config := defaultConfig()
|
||||||
builderT.Test(t, builderT.TestCase{
|
builderT.Test(t, builderT.TestCase{
|
||||||
Builder: &Builder{},
|
Builder: &Builder{},
|
||||||
Template: renderConfig(config),
|
Template: commonT.RenderConfig(config),
|
||||||
Check: checkDefault(t, config["vm_name"].(string), config["host"].(string), "datastore1"),
|
Check: checkDefault(t, config["vm_name"].(string), config["host"].(string), "datastore1"),
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
@ -33,7 +32,7 @@ func defaultConfig() map[string]interface{} {
|
||||||
"ssh_username": "root",
|
"ssh_username": "root",
|
||||||
"ssh_password": "jetbrains",
|
"ssh_password": "jetbrains",
|
||||||
}
|
}
|
||||||
config["vm_name"] = fmt.Sprintf("test-%v", rand.Intn(1000))
|
config["vm_name"] = commonT.NewVMName()
|
||||||
return config
|
return config
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -100,7 +99,7 @@ func TestBuilderAcc_artifact(t *testing.T) {
|
||||||
config := defaultConfig()
|
config := defaultConfig()
|
||||||
builderT.Test(t, builderT.TestCase{
|
builderT.Test(t, builderT.TestCase{
|
||||||
Builder: &Builder{},
|
Builder: &Builder{},
|
||||||
Template: renderConfig(config),
|
Template: commonT.RenderConfig(config),
|
||||||
Check: checkArtifact(t),
|
Check: checkArtifact(t),
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
@ -133,7 +132,7 @@ func folderConfig() string {
|
||||||
config := defaultConfig()
|
config := defaultConfig()
|
||||||
config["folder"] = "folder1/folder2"
|
config["folder"] = "folder1/folder2"
|
||||||
config["linked_clone"] = true // speed up
|
config["linked_clone"] = true // speed up
|
||||||
return renderConfig(config)
|
return commonT.RenderConfig(config)
|
||||||
}
|
}
|
||||||
|
|
||||||
func checkFolder(t *testing.T, folder string) builderT.TestCheckFunc {
|
func checkFolder(t *testing.T, folder string) builderT.TestCheckFunc {
|
||||||
|
@ -171,7 +170,7 @@ func resourcePoolConfig() string {
|
||||||
config := defaultConfig()
|
config := defaultConfig()
|
||||||
config["resource_pool"] = "pool1/pool2"
|
config["resource_pool"] = "pool1/pool2"
|
||||||
config["linked_clone"] = true // speed up
|
config["linked_clone"] = true // speed up
|
||||||
return renderConfig(config)
|
return commonT.RenderConfig(config)
|
||||||
}
|
}
|
||||||
|
|
||||||
func checkResourcePool(t *testing.T, pool string) builderT.TestCheckFunc {
|
func checkResourcePool(t *testing.T, pool string) builderT.TestCheckFunc {
|
||||||
|
@ -208,7 +207,7 @@ func TestBuilderAcc_datastore(t *testing.T) {
|
||||||
func datastoreConfig() string {
|
func datastoreConfig() string {
|
||||||
config := defaultConfig()
|
config := defaultConfig()
|
||||||
config["template"] = "alpine-host4" // on esxi-4.vsphere65.test
|
config["template"] = "alpine-host4" // on esxi-4.vsphere65.test
|
||||||
return renderConfig(config)
|
return commonT.RenderConfig(config)
|
||||||
}
|
}
|
||||||
|
|
||||||
func checkDatastore(t *testing.T, name string) builderT.TestCheckFunc {
|
func checkDatastore(t *testing.T, name string) builderT.TestCheckFunc {
|
||||||
|
@ -251,7 +250,7 @@ func TestBuilderAcc_multipleDatastores(t *testing.T) {
|
||||||
func multipleDatastoresConfig() string {
|
func multipleDatastoresConfig() string {
|
||||||
config := defaultConfig()
|
config := defaultConfig()
|
||||||
config["host"] = "esxi-4.vsphere65.test" // host with 2 datastores
|
config["host"] = "esxi-4.vsphere65.test" // host with 2 datastores
|
||||||
return renderConfig(config)
|
return commonT.RenderConfig(config)
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestBuilderAcc_linkedClone(t *testing.T) {
|
func TestBuilderAcc_linkedClone(t *testing.T) {
|
||||||
|
@ -265,7 +264,7 @@ func TestBuilderAcc_linkedClone(t *testing.T) {
|
||||||
func linkedCloneConfig() string {
|
func linkedCloneConfig() string {
|
||||||
config := defaultConfig()
|
config := defaultConfig()
|
||||||
config["linked_clone"] = true
|
config["linked_clone"] = true
|
||||||
return renderConfig(config)
|
return commonT.RenderConfig(config)
|
||||||
}
|
}
|
||||||
|
|
||||||
func checkLinkedClone(t *testing.T) builderT.TestCheckFunc {
|
func checkLinkedClone(t *testing.T) builderT.TestCheckFunc {
|
||||||
|
@ -303,7 +302,7 @@ func hardwareConfig() string {
|
||||||
config["RAM_reservation"] = 1024
|
config["RAM_reservation"] = 1024
|
||||||
config["linked_clone"] = true // speed up
|
config["linked_clone"] = true // speed up
|
||||||
|
|
||||||
return renderConfig(config)
|
return commonT.RenderConfig(config)
|
||||||
}
|
}
|
||||||
|
|
||||||
func checkHardware(t *testing.T) builderT.TestCheckFunc {
|
func checkHardware(t *testing.T) builderT.TestCheckFunc {
|
||||||
|
@ -358,7 +357,7 @@ func RAMReservationConfig() string {
|
||||||
config["RAM_reserve_all"] = true
|
config["RAM_reserve_all"] = true
|
||||||
config["linked_clone"] = true // speed up
|
config["linked_clone"] = true // speed up
|
||||||
|
|
||||||
return renderConfig(config)
|
return commonT.RenderConfig(config)
|
||||||
}
|
}
|
||||||
|
|
||||||
func checkRAMReservation(t *testing.T) builderT.TestCheckFunc {
|
func checkRAMReservation(t *testing.T) builderT.TestCheckFunc {
|
||||||
|
@ -391,7 +390,7 @@ func sshKeyConfig() string {
|
||||||
config["ssh_password"] = ""
|
config["ssh_password"] = ""
|
||||||
config["ssh_private_key_file"] = "../test-key.pem"
|
config["ssh_private_key_file"] = "../test-key.pem"
|
||||||
config["linked_clone"] = true // speed up
|
config["linked_clone"] = true // speed up
|
||||||
return renderConfig(config)
|
return commonT.RenderConfig(config)
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestBuilderAcc_snapshot(t *testing.T) {
|
func TestBuilderAcc_snapshot(t *testing.T) {
|
||||||
|
@ -405,7 +404,7 @@ func TestBuilderAcc_snapshot(t *testing.T) {
|
||||||
func snapshotConfig() string {
|
func snapshotConfig() string {
|
||||||
config := defaultConfig()
|
config := defaultConfig()
|
||||||
config["create_snapshot"] = true
|
config["create_snapshot"] = true
|
||||||
return renderConfig(config)
|
return commonT.RenderConfig(config)
|
||||||
}
|
}
|
||||||
|
|
||||||
func checkSnapshot(t *testing.T) builderT.TestCheckFunc {
|
func checkSnapshot(t *testing.T) builderT.TestCheckFunc {
|
||||||
|
@ -439,7 +438,7 @@ func templateConfig() string {
|
||||||
config := defaultConfig()
|
config := defaultConfig()
|
||||||
config["convert_to_template"] = true
|
config["convert_to_template"] = true
|
||||||
config["linked_clone"] = true // speed up
|
config["linked_clone"] = true // speed up
|
||||||
return renderConfig(config)
|
return commonT.RenderConfig(config)
|
||||||
}
|
}
|
||||||
|
|
||||||
func checkTemplate(t *testing.T) builderT.TestCheckFunc {
|
func checkTemplate(t *testing.T) builderT.TestCheckFunc {
|
||||||
|
@ -460,22 +459,6 @@ func checkTemplate(t *testing.T) builderT.TestCheckFunc {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
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 {
|
func testConn(t *testing.T) *driver.Driver {
|
||||||
d, err := driver.NewDriver(&driver.ConnectConfig{
|
d, err := driver.NewDriver(&driver.ConnectConfig{
|
||||||
VCenterServer: "vcenter.vsphere65.test",
|
VCenterServer: "vcenter.vsphere65.test",
|
||||||
|
|
|
@ -3,7 +3,6 @@ package clone
|
||||||
import (
|
import (
|
||||||
packerCommon "github.com/hashicorp/packer/common"
|
packerCommon "github.com/hashicorp/packer/common"
|
||||||
"github.com/hashicorp/packer/helper/communicator"
|
"github.com/hashicorp/packer/helper/communicator"
|
||||||
"github.com/hashicorp/packer/helper/config"
|
|
||||||
"github.com/hashicorp/packer/packer"
|
"github.com/hashicorp/packer/packer"
|
||||||
"github.com/hashicorp/packer/template/interpolate"
|
"github.com/hashicorp/packer/template/interpolate"
|
||||||
"github.com/jetbrains-infra/packer-builder-vsphere/common"
|
"github.com/jetbrains-infra/packer-builder-vsphere/common"
|
||||||
|
@ -13,7 +12,7 @@ type Config struct {
|
||||||
packerCommon.PackerConfig `mapstructure:",squash"`
|
packerCommon.PackerConfig `mapstructure:",squash"`
|
||||||
common.ConnectConfig `mapstructure:",squash"`
|
common.ConnectConfig `mapstructure:",squash"`
|
||||||
CloneConfig `mapstructure:",squash"`
|
CloneConfig `mapstructure:",squash"`
|
||||||
HardwareConfig `mapstructure:",squash"`
|
common.HardwareConfig `mapstructure:",squash"`
|
||||||
Comm communicator.Config `mapstructure:",squash"`
|
Comm communicator.Config `mapstructure:",squash"`
|
||||||
common.ShutdownConfig `mapstructure:",squash"`
|
common.ShutdownConfig `mapstructure:",squash"`
|
||||||
CreateSnapshot bool `mapstructure:"create_snapshot"`
|
CreateSnapshot bool `mapstructure:"create_snapshot"`
|
||||||
|
@ -24,14 +23,8 @@ type Config struct {
|
||||||
|
|
||||||
func NewConfig(raws ...interface{}) (*Config, []string, error) {
|
func NewConfig(raws ...interface{}) (*Config, []string, error) {
|
||||||
c := new(Config)
|
c := new(Config)
|
||||||
{
|
if err := common.DecodeConfig(c, &c.ctx, raws...); err != nil {
|
||||||
err := config.Decode(c, &config.DecodeOpts{
|
return nil, nil, err
|
||||||
Interpolate: true,
|
|
||||||
InterpolateContext: &c.ctx,
|
|
||||||
}, raws...)
|
|
||||||
if err != nil {
|
|
||||||
return nil, nil, err
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
errs := new(packer.MultiError)
|
errs := new(packer.MultiError)
|
||||||
|
|
|
@ -1,42 +1,21 @@
|
||||||
package clone
|
package clone
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"github.com/mitchellh/multistep"
|
|
||||||
"github.com/hashicorp/packer/packer"
|
"github.com/hashicorp/packer/packer"
|
||||||
"fmt"
|
"github.com/jetbrains-infra/packer-builder-vsphere/common"
|
||||||
"github.com/jetbrains-infra/packer-builder-vsphere/driver"
|
"github.com/jetbrains-infra/packer-builder-vsphere/driver"
|
||||||
|
"github.com/mitchellh/multistep"
|
||||||
)
|
)
|
||||||
|
|
||||||
type HardwareConfig struct {
|
|
||||||
CPUs int32 `mapstructure:"CPUs"`
|
|
||||||
CPUReservation int64 `mapstructure:"CPU_reservation"`
|
|
||||||
CPULimit int64 `mapstructure:"CPU_limit"`
|
|
||||||
RAM int64 `mapstructure:"RAM"`
|
|
||||||
RAMReservation int64 `mapstructure:"RAM_reservation"`
|
|
||||||
RAMReserveAll bool `mapstructure:"RAM_reserve_all"`
|
|
||||||
DiskSize int64 `mapstructure:"disk_size"`
|
|
||||||
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 {
|
type StepConfigureHardware struct {
|
||||||
config *HardwareConfig
|
config *common.HardwareConfig
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s *StepConfigureHardware) Run(state multistep.StateBag) multistep.StepAction {
|
func (s *StepConfigureHardware) Run(state multistep.StateBag) multistep.StepAction {
|
||||||
ui := state.Get("ui").(packer.Ui)
|
ui := state.Get("ui").(packer.Ui)
|
||||||
vm := state.Get("vm").(*driver.VirtualMachine)
|
vm := state.Get("vm").(*driver.VirtualMachine)
|
||||||
|
|
||||||
if *s.config != (HardwareConfig{}) {
|
if *s.config != (common.HardwareConfig{}) {
|
||||||
ui.Say("Customizing hardware parameters...")
|
ui.Say("Customizing hardware parameters...")
|
||||||
|
|
||||||
err := vm.Configure(&driver.HardwareConfig{
|
err := vm.Configure(&driver.HardwareConfig{
|
||||||
|
|
|
@ -0,0 +1,24 @@
|
||||||
|
package common
|
||||||
|
|
||||||
|
import (
|
||||||
|
"github.com/mitchellh/multistep"
|
||||||
|
"errors"
|
||||||
|
)
|
||||||
|
|
||||||
|
func CheckRunStatus(state *multistep.BasicStateBag) error {
|
||||||
|
// If there was an error, return that
|
||||||
|
if rawErr, ok := state.GetOk("error"); ok {
|
||||||
|
return rawErr.(error)
|
||||||
|
}
|
||||||
|
|
||||||
|
// If we were interrupted or cancelled, then just exit.
|
||||||
|
if _, ok := state.GetOk(multistep.StateCancelled); ok {
|
||||||
|
return errors.New("Build was cancelled.")
|
||||||
|
}
|
||||||
|
|
||||||
|
if _, ok := state.GetOk(multistep.StateHalted); ok {
|
||||||
|
return errors.New("Build was halted.")
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
|
@ -0,0 +1,14 @@
|
||||||
|
package common
|
||||||
|
|
||||||
|
import (
|
||||||
|
"github.com/hashicorp/packer/helper/config"
|
||||||
|
"github.com/hashicorp/packer/template/interpolate"
|
||||||
|
)
|
||||||
|
|
||||||
|
func DecodeConfig(cfg interface{}, ctx *interpolate.Context, raws ...interface{}) error {
|
||||||
|
err := config.Decode(cfg, &config.DecodeOpts{
|
||||||
|
Interpolate: true,
|
||||||
|
InterpolateContext: ctx,
|
||||||
|
}, raws...)
|
||||||
|
return err
|
||||||
|
}
|
|
@ -0,0 +1,39 @@
|
||||||
|
package common
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
"github.com/jetbrains-infra/packer-builder-vsphere/driver"
|
||||||
|
)
|
||||||
|
|
||||||
|
type HardwareConfig struct {
|
||||||
|
CPUs int32 `mapstructure:"CPUs"`
|
||||||
|
CPUReservation int64 `mapstructure:"CPU_reservation"`
|
||||||
|
CPULimit int64 `mapstructure:"CPU_limit"`
|
||||||
|
RAM int64 `mapstructure:"RAM"`
|
||||||
|
RAMReservation int64 `mapstructure:"RAM_reservation"`
|
||||||
|
RAMReserveAll bool `mapstructure:"RAM_reserve_all"`
|
||||||
|
DiskSize int64 `mapstructure:"disk_size"`
|
||||||
|
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
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *HardwareConfig) ToDriverHardwareConfig() driver.HardwareConfig {
|
||||||
|
return driver.HardwareConfig{
|
||||||
|
CPUs: c.CPUs,
|
||||||
|
CPUReservation: c.CPUReservation,
|
||||||
|
CPULimit: c.CPULimit,
|
||||||
|
RAM: c.RAM,
|
||||||
|
RAMReservation: c.RAMReservation,
|
||||||
|
RAMReserveAll: c.RAMReserveAll,
|
||||||
|
DiskSize: c.DiskSize,
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,29 @@
|
||||||
|
package testing
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
"math/rand"
|
||||||
|
"time"
|
||||||
|
"encoding/json"
|
||||||
|
)
|
||||||
|
|
||||||
|
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)
|
||||||
|
}
|
|
@ -0,0 +1,70 @@
|
||||||
|
package iso
|
||||||
|
|
||||||
|
import (
|
||||||
|
packerCommon "github.com/hashicorp/packer/common"
|
||||||
|
"github.com/hashicorp/packer/packer"
|
||||||
|
"github.com/jetbrains-infra/packer-builder-vsphere/common"
|
||||||
|
"github.com/jetbrains-infra/packer-builder-vsphere/driver"
|
||||||
|
"github.com/mitchellh/multistep"
|
||||||
|
)
|
||||||
|
|
||||||
|
type Builder struct {
|
||||||
|
config *Config
|
||||||
|
runner multistep.Runner
|
||||||
|
}
|
||||||
|
|
||||||
|
func (b *Builder) Prepare(raws ...interface{}) ([]string, error) {
|
||||||
|
c, warnings, errs := NewConfig(raws...)
|
||||||
|
if errs != nil {
|
||||||
|
return warnings, errs
|
||||||
|
}
|
||||||
|
b.config = c
|
||||||
|
|
||||||
|
return warnings, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (b *Builder) Run(ui packer.Ui, hook packer.Hook, cache packer.Cache) (packer.Artifact, error) {
|
||||||
|
state := new(multistep.BasicStateBag)
|
||||||
|
state.Put("comm", &b.config.Comm)
|
||||||
|
state.Put("hook", hook)
|
||||||
|
state.Put("ui", ui)
|
||||||
|
|
||||||
|
steps := []multistep.Step{}
|
||||||
|
|
||||||
|
steps = append(steps,
|
||||||
|
&common.StepConnect{
|
||||||
|
Config: &b.config.ConnectConfig,
|
||||||
|
},
|
||||||
|
&StepCreateVM{
|
||||||
|
config: &b.config.CreateConfig,
|
||||||
|
},
|
||||||
|
)
|
||||||
|
|
||||||
|
if b.config.CDRomConfig.ISOPath != "" {
|
||||||
|
steps = append(steps,
|
||||||
|
&StepAddCDRom{
|
||||||
|
config: &b.config.CDRomConfig,
|
||||||
|
},
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Run!
|
||||||
|
b.runner = packerCommon.NewRunner(steps, b.config.PackerConfig, ui)
|
||||||
|
b.runner.Run(state)
|
||||||
|
|
||||||
|
if err := common.CheckRunStatus(state); err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
artifact := &common.Artifact{
|
||||||
|
Name: b.config.VMName,
|
||||||
|
VM: state.Get("vm").(*driver.VirtualMachine),
|
||||||
|
}
|
||||||
|
return artifact, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (b *Builder) Cancel() {
|
||||||
|
if b.runner != nil {
|
||||||
|
b.runner.Cancel()
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,33 @@
|
||||||
|
package iso
|
||||||
|
|
||||||
|
import (
|
||||||
|
builderT "github.com/hashicorp/packer/helper/builder/testing"
|
||||||
|
commonT "github.com/jetbrains-infra/packer-builder-vsphere/common/testing"
|
||||||
|
"testing"
|
||||||
|
)
|
||||||
|
|
||||||
|
func TestBuilderAcc_default(t *testing.T) {
|
||||||
|
config := defaultConfig()
|
||||||
|
builderT.Test(t, builderT.TestCase{
|
||||||
|
Builder: &Builder{},
|
||||||
|
Template: commonT.RenderConfig(config),
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
func defaultConfig() map[string]interface{} {
|
||||||
|
config := map[string]interface{}{
|
||||||
|
"vcenter_server": "vcenter.vsphere65.test",
|
||||||
|
"username": "root",
|
||||||
|
"password": "jetbrains",
|
||||||
|
"insecure_connection": true,
|
||||||
|
|
||||||
|
"host": "esxi-1.vsphere65.test",
|
||||||
|
|
||||||
|
"ssh_username": "root",
|
||||||
|
"ssh_password": "jetbrains",
|
||||||
|
|
||||||
|
"vm_name": commonT.NewVMName(),
|
||||||
|
}
|
||||||
|
|
||||||
|
return config
|
||||||
|
}
|
|
@ -0,0 +1,43 @@
|
||||||
|
package iso
|
||||||
|
|
||||||
|
import (
|
||||||
|
packerCommon "github.com/hashicorp/packer/common"
|
||||||
|
"github.com/hashicorp/packer/helper/communicator"
|
||||||
|
"github.com/hashicorp/packer/packer"
|
||||||
|
"github.com/hashicorp/packer/template/interpolate"
|
||||||
|
"github.com/jetbrains-infra/packer-builder-vsphere/common"
|
||||||
|
)
|
||||||
|
|
||||||
|
type Config struct {
|
||||||
|
packerCommon.PackerConfig `mapstructure:",squash"`
|
||||||
|
common.ConnectConfig `mapstructure:",squash"`
|
||||||
|
Comm communicator.Config `mapstructure:",squash"`
|
||||||
|
common.ShutdownConfig `mapstructure:",squash"`
|
||||||
|
CreateSnapshot bool `mapstructure:"create_snapshot"`
|
||||||
|
ConvertToTemplate bool `mapstructure:"convert_to_template"`
|
||||||
|
|
||||||
|
CreateConfig `mapstructure:",squash"`
|
||||||
|
CDRomConfig `mapstructure:",squash"`
|
||||||
|
|
||||||
|
ctx interpolate.Context
|
||||||
|
}
|
||||||
|
|
||||||
|
func NewConfig(raws ...interface{}) (*Config, []string, error) {
|
||||||
|
c := new(Config)
|
||||||
|
if err := common.DecodeConfig(c, &c.ctx, raws...); err != nil {
|
||||||
|
return nil, nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
errs := new(packer.MultiError)
|
||||||
|
errs = packer.MultiErrorAppend(errs, c.Comm.Prepare(&c.ctx)...)
|
||||||
|
errs = packer.MultiErrorAppend(errs, c.ConnectConfig.Prepare()...)
|
||||||
|
errs = packer.MultiErrorAppend(errs, c.HardwareConfig.Prepare()...)
|
||||||
|
errs = packer.MultiErrorAppend(errs, c.ShutdownConfig.Prepare()...)
|
||||||
|
errs = packer.MultiErrorAppend(errs, c.CreateConfig.Prepare()...)
|
||||||
|
|
||||||
|
if len(errs.Errors) > 0 {
|
||||||
|
return nil, nil, errs
|
||||||
|
}
|
||||||
|
|
||||||
|
return c, nil, nil
|
||||||
|
}
|
|
@ -0,0 +1,40 @@
|
||||||
|
package iso
|
||||||
|
|
||||||
|
import (
|
||||||
|
"github.com/hashicorp/packer/packer"
|
||||||
|
"github.com/jetbrains-infra/packer-builder-vsphere/driver"
|
||||||
|
"github.com/mitchellh/multistep"
|
||||||
|
)
|
||||||
|
|
||||||
|
type CDRomConfig struct {
|
||||||
|
ISOPath string `mapstructure:"iso_path"`
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *CDRomConfig) Prepare() []error {
|
||||||
|
var errs []error
|
||||||
|
|
||||||
|
return errs
|
||||||
|
}
|
||||||
|
|
||||||
|
type StepAddCDRom struct {
|
||||||
|
config *CDRomConfig
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *StepAddCDRom) Run(state multistep.StateBag) multistep.StepAction {
|
||||||
|
ui := state.Get("ui").(packer.Ui)
|
||||||
|
|
||||||
|
ui.Say("Adding CDRom ...")
|
||||||
|
|
||||||
|
vm := state.Get("vm").(*driver.VirtualMachine)
|
||||||
|
err := vm.AddCdrom(s.config.ISOPath)
|
||||||
|
if err != nil {
|
||||||
|
state.Put("error", err)
|
||||||
|
return multistep.ActionHalt
|
||||||
|
}
|
||||||
|
|
||||||
|
return multistep.ActionContinue
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *StepAddCDRom) Cleanup(state multistep.StateBag) {
|
||||||
|
// nothing
|
||||||
|
}
|
|
@ -0,0 +1,110 @@
|
||||||
|
package iso
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
"github.com/hashicorp/packer/packer"
|
||||||
|
"github.com/jetbrains-infra/packer-builder-vsphere/common"
|
||||||
|
"github.com/jetbrains-infra/packer-builder-vsphere/driver"
|
||||||
|
"github.com/mitchellh/multistep"
|
||||||
|
)
|
||||||
|
|
||||||
|
type CreateConfig struct {
|
||||||
|
common.HardwareConfig `mapstructure:",squash"`
|
||||||
|
|
||||||
|
DiskThinProvisioned bool `mapstructure:"disk_thin_provisioned"`
|
||||||
|
DiskControllerType string `mapstructure:"disk_controller_type"`
|
||||||
|
|
||||||
|
VMName string `mapstructure:"vm_name"`
|
||||||
|
Folder string `mapstructure:"folder"`
|
||||||
|
Host string `mapstructure:"host"`
|
||||||
|
ResourcePool string `mapstructure:"resource_pool"`
|
||||||
|
Datastore string `mapstructure:"datastore"`
|
||||||
|
GuestOSType string `mapstructure:"guest_os_type"`
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *CreateConfig) Prepare() []error {
|
||||||
|
var errs []error
|
||||||
|
|
||||||
|
// needed to avoid changing the original config in case of errors
|
||||||
|
tmp := *c
|
||||||
|
|
||||||
|
// do recursive calls
|
||||||
|
errs = append(errs, tmp.HardwareConfig.Prepare()...)
|
||||||
|
|
||||||
|
// check for errors
|
||||||
|
if tmp.VMName == "" {
|
||||||
|
errs = append(errs, fmt.Errorf("Target VM name is required"))
|
||||||
|
}
|
||||||
|
if tmp.Host == "" {
|
||||||
|
errs = append(errs, fmt.Errorf("vSphere host is required"))
|
||||||
|
}
|
||||||
|
|
||||||
|
if len(errs) > 0 {
|
||||||
|
return errs
|
||||||
|
}
|
||||||
|
|
||||||
|
// set default values
|
||||||
|
if tmp.GuestOSType == "" {
|
||||||
|
tmp.GuestOSType = "otherGuest"
|
||||||
|
}
|
||||||
|
|
||||||
|
// change the original config
|
||||||
|
*c = tmp
|
||||||
|
|
||||||
|
return []error{}
|
||||||
|
}
|
||||||
|
|
||||||
|
type StepCreateVM struct {
|
||||||
|
config *CreateConfig
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *StepCreateVM) Run(state multistep.StateBag) multistep.StepAction {
|
||||||
|
ui := state.Get("ui").(packer.Ui)
|
||||||
|
d := state.Get("driver").(*driver.Driver)
|
||||||
|
|
||||||
|
ui.Say("Creating VM...")
|
||||||
|
|
||||||
|
vm, err := d.CreateVM(&driver.CreateConfig{
|
||||||
|
HardwareConfig: s.config.HardwareConfig.ToDriverHardwareConfig(),
|
||||||
|
|
||||||
|
DiskThinProvisioned: s.config.DiskThinProvisioned,
|
||||||
|
DiskControllerType: s.config.DiskControllerType,
|
||||||
|
Name: s.config.VMName,
|
||||||
|
Folder: s.config.Folder,
|
||||||
|
Host: s.config.Host,
|
||||||
|
ResourcePool: s.config.ResourcePool,
|
||||||
|
Datastore: s.config.Datastore,
|
||||||
|
GuestOS: s.config.GuestOSType,
|
||||||
|
})
|
||||||
|
|
||||||
|
if err != nil {
|
||||||
|
state.Put("error", 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,4 @@
|
||||||
|
#!/bin/sh
|
||||||
|
|
||||||
|
export PACKER_ACC=1
|
||||||
|
go test -v "$@"
|
Loading…
Reference in New Issue