Merge pull request #19 from jetbrains-infra/tests

Tests
This commit is contained in:
Michael Kuzmin 2017-07-01 16:11:12 +03:00 committed by GitHub
commit 17f7a973d6
8 changed files with 179 additions and 103 deletions

View File

@ -1,9 +1,15 @@
package main package main
import (
"github.com/vmware/govmomi/object"
"context"
)
const BuilderId = "jetbrains.vsphere" const BuilderId = "jetbrains.vsphere"
type Artifact struct { type Artifact struct {
VMName string `json:"vm_name"` VMName string
Conn *object.VirtualMachine
} }
func (a *Artifact) BuilderId() string { func (a *Artifact) BuilderId() string {
@ -27,5 +33,15 @@ func (a *Artifact) State(name string) interface{} {
} }
func (a *Artifact) Destroy() error { func (a *Artifact) Destroy() error {
ctx := context.TODO()
task, err := a.Conn.Destroy(ctx)
if err != nil {
return err
}
_, err = task.WaitForResult(ctx, nil)
if err != nil {
return err
}
return nil return nil
} }

View File

@ -10,6 +10,11 @@ import (
"github.com/hashicorp/packer/helper/communicator" "github.com/hashicorp/packer/helper/communicator"
gossh "golang.org/x/crypto/ssh" gossh "golang.org/x/crypto/ssh"
"github.com/hashicorp/packer/communicator/ssh" "github.com/hashicorp/packer/communicator/ssh"
"github.com/vmware/govmomi"
"context"
"net/url"
"github.com/vmware/govmomi/find"
"github.com/vmware/govmomi/object"
) )
type Builder struct { type Builder struct {
@ -28,16 +33,39 @@ func (b *Builder) Prepare(raws ...interface{}) ([]string, error) {
} }
func (b *Builder) Run(ui packer.Ui, hook packer.Hook, cache packer.Cache) (packer.Artifact, error) { func (b *Builder) Run(ui packer.Ui, hook packer.Hook, cache packer.Cache) (packer.Artifact, error) {
// Set up the state.
state := new(multistep.BasicStateBag) state := new(multistep.BasicStateBag)
state.Put("hook", hook) state.Put("hook", hook)
state.Put("ui", ui) state.Put("ui", ui)
ctx := context.TODO()
state.Put("ctx", ctx)
vcenter_url, err := url.Parse(b.config.Url)
if err != nil {
return nil, err
}
vcenter_url.User = url.UserPassword(b.config.Username, b.config.Password)
client, err := govmomi.NewClient(ctx, vcenter_url,true)
if err != nil {
return nil, err
}
state.Put("client", client)
finder := find.NewFinder(client.Client, false)
dc, err := finder.DatacenterOrDefault(ctx, b.config.DCName)
if err != nil {
return nil, err
}
finder.SetDatacenter(dc)
state.Put("finder", finder)
state.Put("dc", dc)
vmSrc, err := finder.VirtualMachine(ctx, b.config.Template)
if err != nil {
return nil, err
}
state.Put("vmSrc", vmSrc)
// Build the steps.
steps := []multistep.Step{ steps := []multistep.Step{
&StepSetup{
config: b.config,
},
&StepCloneVM{ &StepCloneVM{
config: b.config, config: b.config,
}, },
@ -94,9 +122,9 @@ func (b *Builder) Run(ui packer.Ui, hook packer.Hook, cache packer.Cache) (packe
return nil, errors.New("Build was halted.") return nil, errors.New("Build was halted.")
} }
// No errors, must've worked
artifact := &Artifact{ artifact := &Artifact{
VMName: b.config.VMName, VMName: b.config.VMName,
Conn: state.Get("vm").(*object.VirtualMachine),
} }
return artifact, nil return artifact, nil
} }

32
builder_acc_test.go Normal file
View File

@ -0,0 +1,32 @@
package main
import (
"testing"
builderT "github.com/hashicorp/packer/helper/builder/testing"
)
func TestBuilderAcc_basic(t *testing.T) {
builderT.Test(t, builderT.TestCase{
Builder: &Builder{},
Template: testBuilderAccBasic,
})
}
const testBuilderAccBasic = `
{
"builders": [{
"type": "test",
"url": "https://vcenter.vsphere5.test/sdk",
"username": "root",
"password": "jetbrains",
"template": "basic",
"vm_name": "test1",
"host": "esxi-1.vsphere5.test",
"ssh_username": "jetbrains",
"ssh_password": "jetbrains"
}]
}
`

14
builder_test.go Normal file
View File

@ -0,0 +1,14 @@
package main
import (
"github.com/hashicorp/packer/packer"
"testing"
)
func TestBuilder_ImplementsBuilder(t *testing.T) {
var raw interface{}
raw = &Builder{}
if _, ok := raw.(packer.Builder); !ok {
t.Fatalf("Builder should be a builder")
}
}

View File

@ -57,6 +57,7 @@ func NewConfig(raws ...interface{}) (*Config, []string, error) {
// Accumulate any errors // Accumulate any errors
errs := new(packer.MultiError) errs := new(packer.MultiError)
var warnings []string
// Prepare config(s) // Prepare config(s)
errs = packer.MultiErrorAppend(errs, c.Config.Prepare(&c.ctx)...) errs = packer.MultiErrorAppend(errs, c.Config.Prepare(&c.ctx)...)
@ -100,12 +101,9 @@ func NewConfig(raws ...interface{}) (*Config, []string, error) {
errs = packer.MultiErrorAppend(errs, fmt.Errorf("Failed parsing shutdown_timeout: %s", err)) errs = packer.MultiErrorAppend(errs, fmt.Errorf("Failed parsing shutdown_timeout: %s", err))
} }
//if c.Datastore == "" {
// Warnings // warnings = append(warnings, "Datastore is not specified, will try to find the default one")
var warnings []string //}
if c.Datastore == "" {
warnings = append(warnings, "Datastore is not specified, will try to find the default one")
}
if len(errs.Errors) > 0 { if len(errs.Errors) > 0 {
return nil, warnings, errs return nil, warnings, errs

67
config_test.go Normal file
View File

@ -0,0 +1,67 @@
package main
import (
"testing"
"time"
)
func TestMinimalConfig(t *testing.T) {
_, warns, errs := NewConfig(minimalConfig())
testConfigOk(t, warns, errs)
}
func TestInvalidCpu(t *testing.T) {
raw := minimalConfig()
raw["cpus"] = "string"
_, warns, errs := NewConfig(raw)
testConfigErr(t, warns, errs)
}
func TestInvalidRam(t *testing.T) {
raw := minimalConfig()
raw["RAM"] = "string"
_, warns, errs := NewConfig(raw)
testConfigErr(t, warns, errs)
}
func TestTimeout(t *testing.T) {
raw := minimalConfig()
raw["shutdown_timeout"] = "3m"
conf, warns, err := NewConfig(raw)
testConfigOk(t, warns, err)
if conf.ShutdownTimeout != 3 * time.Minute {
t.Fatalf("shutdown_timeout sourld equal 3 minutes")
}
}
func minimalConfig() map[string]interface{} {
return map[string]interface{}{
"url": "https://vcenter.domain.local/sdk",
"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.Fatalf("bad: %#v", warns)
}
if err != nil {
t.Fatalf("bad: %s", err)
}
}
func testConfigErr(t *testing.T, warns []string, err error) {
if len(warns) > 0 {
t.Fatalf("bad: %#v", warns)
}
if err == nil {
t.Fatal("should error")
}
}

View File

@ -1,7 +1,6 @@
package main package main
import ( import (
"github.com/vmware/govmomi"
"context" "context"
"github.com/mitchellh/multistep" "github.com/mitchellh/multistep"
"github.com/vmware/govmomi/vim25/types" "github.com/vmware/govmomi/vim25/types"
@ -14,28 +13,25 @@ import (
) )
type CloneParameters struct { type CloneParameters struct {
client *govmomi.Client ctx context.Context
folder *object.Folder vmSrc *object.VirtualMachine
vmName string
folder *object.Folder
resourcePool *object.ResourcePool resourcePool *object.ResourcePool
datastore *object.Datastore datastore *object.Datastore
vmSrc *object.VirtualMachine
ctx context.Context
vmName string
linkedClone bool linkedClone bool
} }
type StepCloneVM struct{ type StepCloneVM struct {
config *Config config *Config
success bool success bool
} }
func (s *StepCloneVM) Run(state multistep.StateBag) multistep.StepAction { func (s *StepCloneVM) Run(state multistep.StateBag) multistep.StepAction {
client := state.Get("client").(*govmomi.Client)
ctx := state.Get("ctx").(context.Context) ctx := state.Get("ctx").(context.Context)
finder := state.Get("finder").(*find.Finder) finder := state.Get("finder").(*find.Finder)
dc := state.Get("dc").(*object.Datacenter) dc := state.Get("dc").(*object.Datacenter)
vmSrc := state.Get("vmSrc").(*object.VirtualMachine) vmSrc := state.Get("vmSrc").(*object.VirtualMachine)
ui := state.Get("ui").(packer.Ui) ui := state.Get("ui").(packer.Ui)
ui.Say("start cloning...") ui.Say("start cloning...")
@ -64,13 +60,12 @@ func (s *StepCloneVM) Run(state multistep.StateBag) multistep.StepAction {
} }
vm, err := cloneVM(&CloneParameters{ vm, err := cloneVM(&CloneParameters{
client: client, ctx: ctx,
folder: folder, vmSrc: vmSrc,
vmName: s.config.VMName,
folder: folder,
resourcePool: pool, resourcePool: pool,
datastore: datastore, datastore: datastore,
vmSrc: vmSrc,
ctx: ctx,
vmName: s.config.VMName,
linkedClone: s.config.LinkedClone, linkedClone: s.config.LinkedClone,
}) })
if err != nil { if err != nil {
@ -79,7 +74,6 @@ func (s *StepCloneVM) Run(state multistep.StateBag) multistep.StepAction {
} }
state.Put("vm", vm) state.Put("vm", vm)
state.Put("ctx", ctx)
s.success = true s.success = true
return multistep.ActionContinue return multistep.ActionContinue
} }
@ -158,6 +152,6 @@ func cloneVM(params *CloneParameters) (vm *object.VirtualMachine, err error) {
return return
} }
vm = object.NewVirtualMachine(params.client.Client, info.Result.(types.ManagedObjectReference)) vm = object.NewVirtualMachine(params.vmSrc.Client(), info.Result.(types.ManagedObjectReference))
return vm, nil return vm, nil
} }

View File

@ -1,73 +0,0 @@
package main
import (
"github.com/mitchellh/multistep"
"github.com/hashicorp/packer/packer"
"github.com/vmware/govmomi/find"
"fmt"
"github.com/vmware/govmomi"
"context"
"net/url"
)
type StepSetup struct{
config *Config
}
func (s *StepSetup) Run(state multistep.StateBag) multistep.StepAction {
ui := state.Get("ui").(packer.Ui)
ui.Say("setup...")
// Prepare entities: client (authentification), finder, folder, virtual machine
client, ctx, err := createClient(s.config.Url, s.config.Username, s.config.Password)
if err != nil {
state.Put("error", err)
return multistep.ActionHalt
}
// Set up finder
finder := find.NewFinder(client.Client, false)
dc, err := finder.DatacenterOrDefault(ctx, s.config.DCName)
if err != nil {
state.Put("error", err)
return multistep.ActionHalt
}
finder.SetDatacenter(dc)
// Get source VM
vmSrc, err := finder.VirtualMachine(ctx, s.config.Template)
if err != nil {
state.Put("error", err)
return multistep.ActionHalt
}
state.Put("client", client)
state.Put("ctx", ctx)
state.Put("finder", finder)
state.Put("dc", dc)
state.Put("vmSrc", vmSrc)
return multistep.ActionContinue
}
func (s *StepSetup) Cleanup(state multistep.StateBag) {}
func createClient(URL, username, password string) (*govmomi.Client, context.Context, error) {
// create context
ctx := context.TODO() // an empty, default context (for those, who is unsure)
// create a client
// (connected to the specified URL,
// logged in with the username-password)
u, err := url.Parse(URL) // create a URL object from string
if err != nil {
return nil, nil, err
}
u.User = url.UserPassword(username, password) // set username and password for automatical authentification
fmt.Println(u.String())
client, err := govmomi.NewClient(ctx, u,true) // creating a client (logs in with given uname&pswd)
if err != nil {
return nil, nil, err
}
return client, ctx, nil
}