Compare commits

...

70 Commits

Author SHA1 Message Date
Mitchell Hashimoto caaf679f27 v0.3.6 2013-09-02 10:19:27 -07:00
Mitchell Hashimoto 659383618f Update CHANGELOG 2013-09-02 10:18:48 -07:00
Mark Peek 84b0bf6dba Merge pull request #379 from justinsb/justinsb-openstack_ipv6_support
builder/openstack: Support IPv6 access address for Openstack
2013-09-02 09:56:25 -07:00
Mitchell Hashimoto 86c43a1bc9 Merge pull request #381 from greenalto/typo
builder/vmware: fix typo
2013-09-02 08:37:56 -07:00
Greg Karékinian 6cd10286db Fix typo
s/Detatching/Detaching/
2013-09-02 12:46:47 +02:00
Mitchell Hashimoto 4e87275f87 Merge pull request #378 from justinsb/increase_ssh_timeout
builder/openstack: Increase default SSH timeout from 1m -> 5m
2013-09-01 23:49:13 -07:00
Justin Santa Barbara 3001c16eee Increase default SSH timeout for OpenStack from 1m -> 5m
Many cloud providers have a minimum charge of 1 hour, and if there are DNS problems we can hit the 1 minute timeout easily.

Waiting five minutes gives more of a margin of error.
2013-09-01 14:36:59 -07:00
Mark Peek 5f3ae970c0 builder/openstack: region is now required 2013-09-01 13:46:57 -07:00
Mark Peek 5be22de623 website: add openstack to the intro platforms list 2013-09-01 13:41:48 -07:00
Mark Peek 15132a9f17 website: fixup some underscores to not be emphasis in the AWS docs 2013-09-01 13:37:19 -07:00
Mark Peek ffaca0a226 website: document required openstack region setting 2013-09-01 13:30:36 -07:00
Mark Peek eea558d2a5 builder/openstack: don't hardcode "DFW" and make region required 2013-09-01 13:22:22 -07:00
Mitchell Hashimoto 4370726ba7 website: fix typo s/script/shell/ provisioner 2013-09-01 10:11:28 -07:00
Mitchell Hashimoto 73ac1fc82c website: page titles on provisioner docs 2013-09-01 10:02:46 -07:00
Justin Santa Barbara 375eb57077 Support IPv6 access address for Openstack
We still prefer the IPv4 address, but if there is an IPv6 address and no IPv4 address, use IPv6.
2013-08-31 22:39:30 -07:00
Mitchell Hashimoto e6881cee6a Update CHANGELOG 2013-08-31 22:06:47 -07:00
Mitchell Hashimoto d4c4581885 Merge pull request #376 from justinsb/try_parse_pkcs8
communicator/ssh: If PKCS1 parsing of the SSH key fails, try PKCS8
2013-08-31 22:05:34 -07:00
Mitchell Hashimoto 0c11cf7c88 Update CHANGELOG 2013-08-31 22:02:36 -07:00
Mitchell Hashimoto 67887c11e4 Merge pull request #375 from justinsb/avoid_nil_pointer_if_no_openstack_image
builder/openstack: If no image is generated, just return nil
2013-08-31 22:01:55 -07:00
Justin Santa Barbara f1eae75389 If no image is generated, just return nil for the Openstack provider
This mirrors what is done in other providers e.g. Amazon providers
2013-08-31 21:55:52 -07:00
Justin Santa Barbara 7681d618d6 If PKCS1 parsing of the SSH key fails, try PKCS8 2013-08-31 21:54:23 -07:00
Mitchell Hashimoto 90b4dc7860 builder/digitalocean: retry destroy a few times 2013-08-31 21:32:35 -07:00
Mitchell Hashimoto b842e53028 command/inspect: cleaner output if no required vars 2013-08-31 17:49:22 -07:00
Mitchell Hashimoto 3abacee131 command/inspect: show required variables differently 2013-08-31 17:47:38 -07:00
Mitchell Hashimoto 4e2936d270 website: document required user vars 2013-08-31 17:38:38 -07:00
Mitchell Hashimoto 570be4d581 packer: fix required var check to work properly 2013-08-31 17:36:43 -07:00
Mitchell Hashimoto ab99d424ef packer: required user variables [GH-374] 2013-08-31 17:33:17 -07:00
Mitchell Hashimoto a1b4887324 website: add docs for -debug on Amazon builder 2013-08-31 16:07:55 -07:00
Mitchell Hashimoto 23b21ff4d0 Update CHANGELOG 2013-08-31 13:07:30 -07:00
Mitchell Hashimoto 8e7c2796fc builder/amazon/instance: new multistep API 2013-08-31 13:03:13 -07:00
Mitchell Hashimoto b5606af9e2 builder/amazon/ebs: new multistep API 2013-08-31 13:00:43 -07:00
Mitchell Hashimoto 1b8646c963 builder/amazon/chroot: new multistep API 2013-08-31 12:58:55 -07:00
Mitchell Hashimoto 36698d4b6d builder/vmware: new multistep API 2013-08-31 12:50:25 -07:00
Mitchell Hashimoto 8d5f8dc423 builder/virtualbox: new multistep API 2013-08-31 12:44:58 -07:00
Mitchell Hashimoto ea5361a9ac builder/openstack: new multistep API 2013-08-31 12:37:07 -07:00
Mitchell Hashimoto a82f1c18ee builder/digitalocean: new multistep API 2013-08-31 12:25:08 -07:00
Mitchell Hashimoto 98f3bc57a0 common: Use new multistep API 2013-08-31 12:17:59 -07:00
Mitchell Hashimoto 964546e050 Update CHANGELOG 2013-08-30 23:58:03 -07:00
Mitchell Hashimoto 1719cf8006 Merge branch 'cancel-next'
This implements more robust interrupt handling by propagating cancels
throughout more core Packer components.
2013-08-30 23:56:06 -07:00
Mitchell Hashimoto 6e098d1aaf packer: implement Cancel in ProvisionHook 2013-08-30 23:39:29 -07:00
Mitchell Hashimoto 66f7f5aad5 common: detect ctrl-c in Provision 2013-08-30 23:28:31 -07:00
Mitchell Hashimoto 84001c7c76 provisioner/*: implement Cancel 2013-08-30 23:23:36 -07:00
Mitchell Hashimoto 21171db836 packer: Add Cancel method to Provisioner 2013-08-30 23:21:15 -07:00
Mitchell Hashimoto 2e90660afc packer/rpc: test concurrent cancel/run 2013-08-30 23:10:16 -07:00
Mitchell Hashimoto cd12c3e030 packer/rpc: implement hook Cancel 2013-08-30 23:03:43 -07:00
Mitchell Hashimoto 017d27d7eb packer: reset runningHook when done 2013-08-30 22:59:13 -07:00
Mitchell Hashimoto 27a6dac7fd packer: implement Cancel on DispatchHook 2013-08-30 17:26:51 -07:00
Mitchell Hashimoto 98ddf043cc packer: Add Cancel() method to hook 2013-08-30 17:03:55 -07:00
Mitchell Hashimoto dfb44a2abe packer/plugin: Interrupted() to check for i nterrupts 2013-08-30 16:39:28 -07:00
Mitchell Hashimoto dbae49f0c8 provisioner/chef-solo: code re-org 2013-08-30 16:35:57 -07:00
Mitchell Hashimoto a03c66272f packer/plugin: Count number of interrupts atomically 2013-08-30 16:29:21 -07:00
Mitchell Hashimoto 65eb05384f packer: pass tests 2013-08-30 16:07:06 -07:00
Mitchell Hashimoto c157da38c9 Update CHANGELOG 2013-08-30 16:06:12 -07:00
Mitchell Hashimoto 4c4020f723 provisioner/chef-solo: properly escape user vars for JSON
/cc @mwhooker - I moved the processing up into Prepare so that any
errors will be shown during a validate pass. Also, I escape some stuff
in keys. Tests moved as well.
2013-08-30 16:05:04 -07:00
Mitchell Hashimoto e4c22472bb Merge pull request #364 from mwhooker/362
provisioner/chef-solo: template expansion of `json`
2013-08-30 15:26:39 -07:00
Mitchell Hashimoto dc9a803efd builder/amazon: don't chmod on windows 2013-08-30 15:04:23 -07:00
Mitchell Hashimoto d1fff21045 builder/amazon: chmod debug key so it is ssh-ready 2013-08-30 15:03:29 -07:00
Mitchell Hashimoto f97b88654f builder/amazon: output DNS name if debug mode 2013-08-30 14:55:56 -07:00
Mitchell Hashimoto e613b0cdcd builder/amazon: drop private key if debug mode [GH-373] 2013-08-30 14:48:50 -07:00
Mitchell Hashimoto 7ed2498407 provisioner/salt-masterless: join paths properly 2013-08-30 14:26:29 -07:00
Mark Peek 5d8a211774 website: add tip about using apt-get/yum -y to prevent hangs 2013-08-29 22:19:13 -07:00
Mark Peek 1b91110a59 Merge pull request #370 from TFDuesing/patch-1
website: openstack s/droplet/server/
2013-08-29 19:30:05 -07:00
Thomas Duesing 729232c7c7 s/droplet/server/ 2013-08-29 21:21:40 -05:00
Mitchell Hashimoto 293db31a0f Merge pull request #369 from celkins/fix-staging-dir-2
website: Fix incorrect config key for Chef staging directory
2013-08-28 21:25:41 -07:00
Christopher Elkins 9f55695964 Fix incorrect config key for staging directory 2013-08-28 21:23:57 -07:00
Matthew Hooker a1893b0bce reuse jsonString instead of opaque result var. 2013-08-28 20:16:06 -07:00
Matthew Hooker fc595de0fd clean up and retab. 2013-08-28 17:35:43 -07:00
Matthew Hooker e21e980647 template expansion of json in chef-solo
Fixes #362
2013-08-28 17:27:22 -07:00
Mitchell Hashimoto 332d666180 packer/plugin: fix nil deref during ctrl-c of provisioner 2013-08-28 11:21:02 -07:00
Mitchell Hashimoto 87f47ba6de Up vesrion for dev 2013-08-28 11:16:28 -07:00
130 changed files with 1628 additions and 920 deletions
+30
View File
@@ -1,3 +1,33 @@
## 0.3.6 (September 2, 2013)
FEATURES:
* User variables can now be specified as "required", meaning the user
MUST specify a value. Just set the default value to "null". [GH-374]
IMPROVEMENTS:
* core: Much improved interrupt handling. For example, interrupts now
cancel much more quickly within provisioners.
* builder/amazon: In `-debug` mode, the keypair used will be saved to
the current directory so you can access the machine. [GH-373]
* builder/amazon: In `-debug` mode, the DNS is outputted.
* builder/openstack: IPv6 addresses supported for SSH. [GH-379]
* communicator/ssh: Support for private keys encrypted using PKCS8. [GH-376]
* provisioner/chef-solo: You can now use user variables in the `json`
configuration for Chef. [GH-362]
BUG FIXES:
* core: Concurrent map access is completely gone, fixing rare issues
with runtime memory corruption. [GH-307]
* core: Fix possible panic when ctrl-C during provisioner run.
* builder/digitalocean: Retry destroy a few times because DO sometimes
gives false errors.
* builder/openstack: Properly handle the case no image is made. [GH-375]
* builder/openstack: Specifying a region is now required in a template.
* provisioners/salt-masterless: Use filepath join to properly join paths.
## 0.3.5 (August 28, 2013)
FEATURES:
+8 -8
View File
@@ -167,11 +167,11 @@ func (b *Builder) Run(ui packer.Ui, hook packer.Hook, cache packer.Cache) (packe
ec2conn := ec2.New(auth, region)
// Setup the state bag and initial state for the steps
state := make(map[string]interface{})
state["config"] = &b.config
state["ec2"] = ec2conn
state["hook"] = hook
state["ui"] = ui
state := new(multistep.BasicStateBag)
state.Put("config", &b.config)
state.Put("ec2", ec2conn)
state.Put("hook", hook)
state.Put("ui", ui)
// Build the steps
steps := []multistep.Step{
@@ -216,18 +216,18 @@ func (b *Builder) Run(ui packer.Ui, hook packer.Hook, cache packer.Cache) (packe
b.runner.Run(state)
// If there was an error, return that
if rawErr, ok := state["error"]; ok {
if rawErr, ok := state.GetOk("error"); ok {
return nil, rawErr.(error)
}
// If there are no AMIs, then just return
if _, ok := state["amis"]; !ok {
if _, ok := state.GetOk("amis"); !ok {
return nil, nil
}
// Build the artifact and return it
artifact := &awscommon.Artifact{
Amis: state["amis"].(map[string]string),
Amis: state.Get("amis").(map[string]string),
BuilderIdValue: BuilderId,
Conn: ec2conn,
}
+5 -1
View File
@@ -1,6 +1,10 @@
package chroot
import (
"github.com/mitchellh/multistep"
)
// Cleanup is an interface that some steps implement for early cleanup.
type Cleanup interface {
CleanupFunc(map[string]interface{}) error
CleanupFunc(multistep.StateBag) error
}
+14 -14
View File
@@ -21,12 +21,12 @@ type StepAttachVolume struct {
volumeId string
}
func (s *StepAttachVolume) Run(state map[string]interface{}) multistep.StepAction {
ec2conn := state["ec2"].(*ec2.EC2)
device := state["device"].(string)
instance := state["instance"].(*ec2.Instance)
ui := state["ui"].(packer.Ui)
volumeId := state["volume_id"].(string)
func (s *StepAttachVolume) Run(state multistep.StateBag) multistep.StepAction {
ec2conn := state.Get("ec2").(*ec2.EC2)
device := state.Get("device").(string)
instance := state.Get("instance").(*ec2.Instance)
ui := state.Get("ui").(packer.Ui)
volumeId := state.Get("volume_id").(string)
// For the API call, it expects "sd" prefixed devices.
attachVolume := strings.Replace(device, "/xvd", "/sd", 1)
@@ -35,7 +35,7 @@ func (s *StepAttachVolume) Run(state map[string]interface{}) multistep.StepActio
_, err := ec2conn.AttachVolume(volumeId, instance.InstanceId, attachVolume)
if err != nil {
err := fmt.Errorf("Error attaching volume: %s", err)
state["error"] = err
state.Put("error", err)
ui.Error(err.Error())
return multistep.ActionHalt
}
@@ -67,29 +67,29 @@ func (s *StepAttachVolume) Run(state map[string]interface{}) multistep.StepActio
_, err = awscommon.WaitForState(&stateChange)
if err != nil {
err := fmt.Errorf("Error waiting for volume: %s", err)
state["error"] = err
state.Put("error", err)
ui.Error(err.Error())
return multistep.ActionHalt
}
state["attach_cleanup"] = s
state.Put("attach_cleanup", s)
return multistep.ActionContinue
}
func (s *StepAttachVolume) Cleanup(state map[string]interface{}) {
ui := state["ui"].(packer.Ui)
func (s *StepAttachVolume) Cleanup(state multistep.StateBag) {
ui := state.Get("ui").(packer.Ui)
if err := s.CleanupFunc(state); err != nil {
ui.Error(err.Error())
}
}
func (s *StepAttachVolume) CleanupFunc(state map[string]interface{}) error {
func (s *StepAttachVolume) CleanupFunc(state multistep.StateBag) error {
if !s.attached {
return nil
}
ec2conn := state["ec2"].(*ec2.EC2)
ui := state["ui"].(packer.Ui)
ec2conn := state.Get("ec2").(*ec2.EC2)
ui := state.Get("ui").(packer.Ui)
ui.Say("Detaching EBS volume...")
_, err := ec2conn.DetachVolume(s.volumeId)
@@ -11,10 +11,10 @@ type StepChrootProvision struct {
mounts []string
}
func (s *StepChrootProvision) Run(state map[string]interface{}) multistep.StepAction {
hook := state["hook"].(packer.Hook)
mountPath := state["mount_path"].(string)
ui := state["ui"].(packer.Ui)
func (s *StepChrootProvision) Run(state multistep.StateBag) multistep.StepAction {
hook := state.Get("hook").(packer.Hook)
mountPath := state.Get("mount_path").(string)
ui := state.Get("ui").(packer.Ui)
// Create our communicator
comm := &Communicator{
@@ -24,11 +24,11 @@ func (s *StepChrootProvision) Run(state map[string]interface{}) multistep.StepAc
// Provision
log.Println("Running the provision hook")
if err := hook.Run(packer.HookProvision, ui, comm, nil); err != nil {
state["error"] = err
state.Put("error", err)
return multistep.ActionHalt
}
return multistep.ActionContinue
}
func (s *StepChrootProvision) Cleanup(state map[string]interface{}) {}
func (s *StepChrootProvision) Cleanup(state multistep.StateBag) {}
+9 -9
View File
@@ -19,10 +19,10 @@ type StepCopyFiles struct {
files []string
}
func (s *StepCopyFiles) Run(state map[string]interface{}) multistep.StepAction {
config := state["config"].(*Config)
mountPath := state["mount_path"].(string)
ui := state["ui"].(packer.Ui)
func (s *StepCopyFiles) Run(state multistep.StateBag) multistep.StepAction {
config := state.Get("config").(*Config)
mountPath := state.Get("mount_path").(string)
ui := state.Get("ui").(packer.Ui)
s.files = make([]string, 0, len(config.CopyFiles))
if len(config.CopyFiles) > 0 {
@@ -34,7 +34,7 @@ func (s *StepCopyFiles) Run(state map[string]interface{}) multistep.StepAction {
if err := s.copySingle(chrootPath, path); err != nil {
err := fmt.Errorf("Error copying file: %s", err)
state["error"] = err
state.Put("error", err)
ui.Error(err.Error())
return multistep.ActionHalt
}
@@ -43,18 +43,18 @@ func (s *StepCopyFiles) Run(state map[string]interface{}) multistep.StepAction {
}
}
state["copy_files_cleanup"] = s
state.Put("copy_files_cleanup", s)
return multistep.ActionContinue
}
func (s *StepCopyFiles) Cleanup(state map[string]interface{}) {
ui := state["ui"].(packer.Ui)
func (s *StepCopyFiles) Cleanup(state multistep.StateBag) {
ui := state.Get("ui").(packer.Ui)
if err := s.CleanupFunc(state); err != nil {
ui.Error(err.Error())
}
}
func (s *StepCopyFiles) CleanupFunc(map[string]interface{}) error {
func (s *StepCopyFiles) CleanupFunc(multistep.StateBag) error {
if s.files != nil {
for _, file := range s.files {
log.Printf("Removing: %s", file)
+12 -12
View File
@@ -18,11 +18,11 @@ type StepCreateVolume struct {
volumeId string
}
func (s *StepCreateVolume) Run(state map[string]interface{}) multistep.StepAction {
ec2conn := state["ec2"].(*ec2.EC2)
image := state["source_image"].(*ec2.Image)
instance := state["instance"].(*ec2.Instance)
ui := state["ui"].(packer.Ui)
func (s *StepCreateVolume) Run(state multistep.StateBag) multistep.StepAction {
ec2conn := state.Get("ec2").(*ec2.EC2)
image := state.Get("source_image").(*ec2.Image)
instance := state.Get("instance").(*ec2.Instance)
ui := state.Get("ui").(packer.Ui)
// Determine the root device snapshot
log.Printf("Searching for root device of the image (%s)", image.RootDeviceName)
@@ -36,7 +36,7 @@ func (s *StepCreateVolume) Run(state map[string]interface{}) multistep.StepActio
if rootDevice == nil {
err := fmt.Errorf("Couldn't find root device!")
state["error"] = err
state.Put("error", err)
ui.Error(err.Error())
return multistep.ActionHalt
}
@@ -54,7 +54,7 @@ func (s *StepCreateVolume) Run(state map[string]interface{}) multistep.StepActio
createVolumeResp, err := ec2conn.CreateVolume(createVolume)
if err != nil {
err := fmt.Errorf("Error creating root volume: %s", err)
state["error"] = err
state.Put("error", err)
ui.Error(err.Error())
return multistep.ActionHalt
}
@@ -82,22 +82,22 @@ func (s *StepCreateVolume) Run(state map[string]interface{}) multistep.StepActio
_, err = awscommon.WaitForState(&stateChange)
if err != nil {
err := fmt.Errorf("Error waiting for volume: %s", err)
state["error"] = err
state.Put("error", err)
ui.Error(err.Error())
return multistep.ActionHalt
}
state["volume_id"] = s.volumeId
state.Put("volume_id", s.volumeId)
return multistep.ActionContinue
}
func (s *StepCreateVolume) Cleanup(state map[string]interface{}) {
func (s *StepCreateVolume) Cleanup(state multistep.StateBag) {
if s.volumeId == "" {
return
}
ec2conn := state["ec2"].(*ec2.EC2)
ui := state["ui"].(packer.Ui)
ec2conn := state.Get("ec2").(*ec2.EC2)
ui := state.Get("ui").(packer.Ui)
ui.Say("Deleting the created EBS volume...")
_, err := ec2conn.DeleteVolume(s.volumeId)
+5 -5
View File
@@ -11,8 +11,8 @@ import (
// prepare for snapshotting and creating an AMI.
type StepEarlyCleanup struct{}
func (s *StepEarlyCleanup) Run(state map[string]interface{}) multistep.StepAction {
ui := state["ui"].(packer.Ui)
func (s *StepEarlyCleanup) Run(state multistep.StateBag) multistep.StepAction {
ui := state.Get("ui").(packer.Ui)
cleanupKeys := []string{
"copy_files_cleanup",
"mount_extra_cleanup",
@@ -21,11 +21,11 @@ func (s *StepEarlyCleanup) Run(state map[string]interface{}) multistep.StepActio
}
for _, key := range cleanupKeys {
c := state[key].(Cleanup)
c := state.Get(key).(Cleanup)
log.Printf("Running cleanup func: %s", key)
if err := c.CleanupFunc(state); err != nil {
err := fmt.Errorf("Error cleaning up: %s", err)
state["error"] = err
state.Put("error", err)
ui.Error(err.Error())
return multistep.ActionHalt
}
@@ -34,4 +34,4 @@ func (s *StepEarlyCleanup) Run(state map[string]interface{}) multistep.StepActio
return multistep.ActionContinue
}
func (s *StepEarlyCleanup) Cleanup(state map[string]interface{}) {}
func (s *StepEarlyCleanup) Cleanup(state multistep.StateBag) {}
+5 -5
View File
@@ -10,14 +10,14 @@ import (
// StepEarlyUnflock unlocks the flock.
type StepEarlyUnflock struct{}
func (s *StepEarlyUnflock) Run(state map[string]interface{}) multistep.StepAction {
cleanup := state["flock_cleanup"].(Cleanup)
ui := state["ui"].(packer.Ui)
func (s *StepEarlyUnflock) Run(state multistep.StateBag) multistep.StepAction {
cleanup := state.Get("flock_cleanup").(Cleanup)
ui := state.Get("ui").(packer.Ui)
log.Println("Unlocking file lock...")
if err := cleanup.CleanupFunc(state); err != nil {
err := fmt.Errorf("Error unlocking file lock: %s", err)
state["error"] = err
state.Put("error", err)
ui.Error(err.Error())
return multistep.ActionHalt
}
@@ -25,4 +25,4 @@ func (s *StepEarlyUnflock) Run(state map[string]interface{}) multistep.StepActio
return multistep.ActionContinue
}
func (s *StepEarlyUnflock) Cleanup(state map[string]interface{}) {}
func (s *StepEarlyUnflock) Cleanup(state multistep.StateBag) {}
+8 -8
View File
@@ -17,13 +17,13 @@ type StepFlock struct {
fh *os.File
}
func (s *StepFlock) Run(state map[string]interface{}) multistep.StepAction {
ui := state["ui"].(packer.Ui)
func (s *StepFlock) Run(state multistep.StateBag) multistep.StepAction {
ui := state.Get("ui").(packer.Ui)
lockfile := "/var/lock/packer-chroot/lock"
if err := os.MkdirAll(filepath.Dir(lockfile), 0755); err != nil {
err := fmt.Errorf("Error creating lock: %s", err)
state["error"] = err
state.Put("error", err)
ui.Error(err.Error())
return multistep.ActionHalt
}
@@ -32,7 +32,7 @@ func (s *StepFlock) Run(state map[string]interface{}) multistep.StepAction {
f, err := os.Create(lockfile)
if err != nil {
err := fmt.Errorf("Error creating lock: %s", err)
state["error"] = err
state.Put("error", err)
ui.Error(err.Error())
return multistep.ActionHalt
}
@@ -40,7 +40,7 @@ func (s *StepFlock) Run(state map[string]interface{}) multistep.StepAction {
// LOCK!
if err := lockFile(f); err != nil {
err := fmt.Errorf("Error creating lock: %s", err)
state["error"] = err
state.Put("error", err)
ui.Error(err.Error())
return multistep.ActionHalt
}
@@ -49,15 +49,15 @@ func (s *StepFlock) Run(state map[string]interface{}) multistep.StepAction {
// the lock.
s.fh = f
state["flock_cleanup"] = s
state.Put("flock_cleanup", s)
return multistep.ActionContinue
}
func (s *StepFlock) Cleanup(state map[string]interface{}) {
func (s *StepFlock) Cleanup(state multistep.StateBag) {
s.CleanupFunc(state)
}
func (s *StepFlock) CleanupFunc(state map[string]interface{}) error {
func (s *StepFlock) CleanupFunc(state multistep.StateBag) error {
if s.fh == nil {
return nil
}
+8 -8
View File
@@ -12,9 +12,9 @@ import (
// StepInstanceInfo verifies that this builder is running on an EC2 instance.
type StepInstanceInfo struct{}
func (s *StepInstanceInfo) Run(state map[string]interface{}) multistep.StepAction {
ec2conn := state["ec2"].(*ec2.EC2)
ui := state["ui"].(packer.Ui)
func (s *StepInstanceInfo) Run(state multistep.StateBag) multistep.StepAction {
ec2conn := state.Get("ec2").(*ec2.EC2)
ui := state.Get("ui").(packer.Ui)
// Get our own instance ID
ui.Say("Gathering information about this EC2 instance...")
@@ -24,7 +24,7 @@ func (s *StepInstanceInfo) Run(state map[string]interface{}) multistep.StepActio
err := fmt.Errorf(
"Error retrieving the ID of the instance Packer is running on.\n" +
"Please verify Packer is running on a proper AWS EC2 instance.")
state["error"] = err
state.Put("error", err)
ui.Error(err.Error())
return multistep.ActionHalt
}
@@ -36,22 +36,22 @@ func (s *StepInstanceInfo) Run(state map[string]interface{}) multistep.StepActio
instancesResp, err := ec2conn.Instances([]string{instanceId}, ec2.NewFilter())
if err != nil {
err := fmt.Errorf("Error getting instance data: %s", err)
state["error"] = err
state.Put("error", err)
ui.Error(err.Error())
return multistep.ActionHalt
}
if len(instancesResp.Reservations) == 0 {
err := fmt.Errorf("Error getting instance data: no instance found.")
state["error"] = err
state.Put("error", err)
ui.Error(err.Error())
return multistep.ActionHalt
}
instance := &instancesResp.Reservations[0].Instances[0]
state["instance"] = instance
state.Put("instance", instance)
return multistep.ActionContinue
}
func (s *StepInstanceInfo) Cleanup(map[string]interface{}) {}
func (s *StepInstanceInfo) Cleanup(multistep.StateBag) {}
+15 -15
View File
@@ -24,10 +24,10 @@ type StepMountDevice struct {
mountPath string
}
func (s *StepMountDevice) Run(state map[string]interface{}) multistep.StepAction {
config := state["config"].(*Config)
ui := state["ui"].(packer.Ui)
device := state["device"].(string)
func (s *StepMountDevice) Run(state multistep.StateBag) multistep.StepAction {
config := state.Get("config").(*Config)
ui := state.Get("ui").(packer.Ui)
device := state.Get("device").(string)
mountPath, err := config.tpl.Process(config.MountPath, &mountPathData{
Device: filepath.Base(device),
@@ -35,7 +35,7 @@ func (s *StepMountDevice) Run(state map[string]interface{}) multistep.StepAction
if err != nil {
err := fmt.Errorf("Error preparing mount directory: %s", err)
state["error"] = err
state.Put("error", err)
ui.Error(err.Error())
return multistep.ActionHalt
}
@@ -43,7 +43,7 @@ func (s *StepMountDevice) Run(state map[string]interface{}) multistep.StepAction
mountPath, err = filepath.Abs(mountPath)
if err != nil {
err := fmt.Errorf("Error preparing mount directory: %s", err)
state["error"] = err
state.Put("error", err)
ui.Error(err.Error())
return multistep.ActionHalt
}
@@ -52,7 +52,7 @@ func (s *StepMountDevice) Run(state map[string]interface{}) multistep.StepAction
if err := os.MkdirAll(mountPath, 0755); err != nil {
err := fmt.Errorf("Error creating mount directory: %s", err)
state["error"] = err
state.Put("error", err)
ui.Error(err.Error())
return multistep.ActionHalt
}
@@ -65,33 +65,33 @@ func (s *StepMountDevice) Run(state map[string]interface{}) multistep.StepAction
if err := cmd.Run(); err != nil {
err := fmt.Errorf(
"Error mounting root volume: %s\nStderr: %s", err, stderr.String())
state["error"] = err
state.Put("error", err)
ui.Error(err.Error())
return multistep.ActionHalt
}
// Set the mount path so we remember to unmount it later
s.mountPath = mountPath
state["mount_path"] = s.mountPath
state["mount_device_cleanup"] = s
state.Put("mount_path", s.mountPath)
state.Put("mount_device_cleanup", s)
return multistep.ActionContinue
}
func (s *StepMountDevice) Cleanup(state map[string]interface{}) {
ui := state["ui"].(packer.Ui)
func (s *StepMountDevice) Cleanup(state multistep.StateBag) {
ui := state.Get("ui").(packer.Ui)
if err := s.CleanupFunc(state); err != nil {
ui.Error(err.Error())
}
}
func (s *StepMountDevice) CleanupFunc(state map[string]interface{}) error {
func (s *StepMountDevice) CleanupFunc(state multistep.StateBag) error {
if s.mountPath == "" {
return nil
}
config := state["config"].(*Config)
ui := state["ui"].(packer.Ui)
config := state.Get("config").(*Config)
ui := state.Get("ui").(packer.Ui)
ui.Say("Unmounting the root device...")
unmountCommand := fmt.Sprintf("%s %s", config.UnmountCommand, s.mountPath)
+11 -11
View File
@@ -17,10 +17,10 @@ type StepMountExtra struct {
mounts []string
}
func (s *StepMountExtra) Run(state map[string]interface{}) multistep.StepAction {
config := state["config"].(*Config)
mountPath := state["mount_path"].(string)
ui := state["ui"].(packer.Ui)
func (s *StepMountExtra) Run(state multistep.StateBag) multistep.StepAction {
config := state.Get("config").(*Config)
mountPath := state.Get("mount_path").(string)
ui := state.Get("ui").(packer.Ui)
s.mounts = make([]string, 0, len(config.ChrootMounts))
@@ -30,7 +30,7 @@ func (s *StepMountExtra) Run(state map[string]interface{}) multistep.StepAction
if err := os.MkdirAll(innerPath, 0755); err != nil {
err := fmt.Errorf("Error creating mount directory: %s", err)
state["error"] = err
state.Put("error", err)
ui.Error(err.Error())
return multistep.ActionHalt
}
@@ -53,7 +53,7 @@ func (s *StepMountExtra) Run(state map[string]interface{}) multistep.StepAction
if err := cmd.Run(); err != nil {
err := fmt.Errorf(
"Error mounting: %s\nStderr: %s", err, stderr.String())
state["error"] = err
state.Put("error", err)
ui.Error(err.Error())
return multistep.ActionHalt
}
@@ -61,12 +61,12 @@ func (s *StepMountExtra) Run(state map[string]interface{}) multistep.StepAction
s.mounts = append(s.mounts, innerPath)
}
state["mount_extra_cleanup"] = s
state.Put("mount_extra_cleanup", s)
return multistep.ActionContinue
}
func (s *StepMountExtra) Cleanup(state map[string]interface{}) {
ui := state["ui"].(packer.Ui)
func (s *StepMountExtra) Cleanup(state multistep.StateBag) {
ui := state.Get("ui").(packer.Ui)
if err := s.CleanupFunc(state); err != nil {
ui.Error(err.Error())
@@ -74,12 +74,12 @@ func (s *StepMountExtra) Cleanup(state map[string]interface{}) {
}
}
func (s *StepMountExtra) CleanupFunc(state map[string]interface{}) error {
func (s *StepMountExtra) CleanupFunc(state multistep.StateBag) error {
if s.mounts == nil {
return nil
}
config := state["config"].(*Config)
config := state.Get("config").(*Config)
for len(s.mounts) > 0 {
var path string
lastIndex := len(s.mounts) - 1
+7 -7
View File
@@ -13,9 +13,9 @@ type StepPrepareDevice struct {
mounts []string
}
func (s *StepPrepareDevice) Run(state map[string]interface{}) multistep.StepAction {
config := state["config"].(*Config)
ui := state["ui"].(packer.Ui)
func (s *StepPrepareDevice) Run(state multistep.StateBag) multistep.StepAction {
config := state.Get("config").(*Config)
ui := state.Get("ui").(packer.Ui)
device := config.DevicePath
if device == "" {
@@ -24,7 +24,7 @@ func (s *StepPrepareDevice) Run(state map[string]interface{}) multistep.StepActi
device, err = AvailableDevice()
if err != nil {
err := fmt.Errorf("Error finding available device: %s", err)
state["error"] = err
state.Put("error", err)
ui.Error(err.Error())
return multistep.ActionHalt
}
@@ -32,14 +32,14 @@ func (s *StepPrepareDevice) Run(state map[string]interface{}) multistep.StepActi
if _, err := os.Stat(device); err == nil {
err := fmt.Errorf("Device is in use: %s", device)
state["error"] = err
state.Put("error", err)
ui.Error(err.Error())
return multistep.ActionHalt
}
log.Printf("Device: %s", device)
state["device"] = device
state.Put("device", device)
return multistep.ActionContinue
}
func (s *StepPrepareDevice) Cleanup(state map[string]interface{}) {}
func (s *StepPrepareDevice) Cleanup(state multistep.StateBag) {}
+11 -11
View File
@@ -11,12 +11,12 @@ import (
// StepRegisterAMI creates the AMI.
type StepRegisterAMI struct{}
func (s *StepRegisterAMI) Run(state map[string]interface{}) multistep.StepAction {
config := state["config"].(*Config)
ec2conn := state["ec2"].(*ec2.EC2)
image := state["source_image"].(*ec2.Image)
snapshotId := state["snapshot_id"].(string)
ui := state["ui"].(packer.Ui)
func (s *StepRegisterAMI) Run(state multistep.StateBag) multistep.StepAction {
config := state.Get("config").(*Config)
ec2conn := state.Get("ec2").(*ec2.EC2)
image := state.Get("source_image").(*ec2.Image)
snapshotId := state.Get("snapshot_id").(string)
ui := state.Get("ui").(packer.Ui)
ui.Say("Registering the AMI...")
blockDevices := make([]ec2.BlockDeviceMapping, len(image.BlockDevices))
@@ -40,8 +40,8 @@ func (s *StepRegisterAMI) Run(state map[string]interface{}) multistep.StepAction
registerResp, err := ec2conn.RegisterImage(registerOpts)
if err != nil {
state["error"] = fmt.Errorf("Error registering AMI: %s", err)
ui.Error(state["error"].(error).Error())
state.Put("error", fmt.Errorf("Error registering AMI: %s", err))
ui.Error(state.Get("error").(error).Error())
return multistep.ActionHalt
}
@@ -49,13 +49,13 @@ func (s *StepRegisterAMI) Run(state map[string]interface{}) multistep.StepAction
ui.Say(fmt.Sprintf("AMI: %s", registerResp.ImageId))
amis := make(map[string]string)
amis[ec2conn.Region.Name] = registerResp.ImageId
state["amis"] = amis
state.Put("amis", amis)
// Wait for the image to become ready
ui.Say("Waiting for AMI to become ready...")
if err := awscommon.WaitForAMI(ec2conn, registerResp.ImageId); err != nil {
err := fmt.Errorf("Error waiting for AMI: %s", err)
state["error"] = err
state.Put("error", err)
ui.Error(err.Error())
return multistep.ActionHalt
}
@@ -63,4 +63,4 @@ func (s *StepRegisterAMI) Run(state map[string]interface{}) multistep.StepAction
return multistep.ActionContinue
}
func (s *StepRegisterAMI) Cleanup(state map[string]interface{}) {}
func (s *StepRegisterAMI) Cleanup(state multistep.StateBag) {}
+12 -12
View File
@@ -17,16 +17,16 @@ type StepSnapshot struct {
snapshotId string
}
func (s *StepSnapshot) Run(state map[string]interface{}) multistep.StepAction {
ec2conn := state["ec2"].(*ec2.EC2)
ui := state["ui"].(packer.Ui)
volumeId := state["volume_id"].(string)
func (s *StepSnapshot) Run(state multistep.StateBag) multistep.StepAction {
ec2conn := state.Get("ec2").(*ec2.EC2)
ui := state.Get("ui").(packer.Ui)
volumeId := state.Get("volume_id").(string)
ui.Say("Creating snapshot...")
createSnapResp, err := ec2conn.CreateSnapshot(volumeId, "")
if err != nil {
err := fmt.Errorf("Error creating snapshot: %s", err)
state["error"] = err
state.Put("error", err)
ui.Error(err.Error())
return multistep.ActionHalt
}
@@ -58,26 +58,26 @@ func (s *StepSnapshot) Run(state map[string]interface{}) multistep.StepAction {
_, err = awscommon.WaitForState(&stateChange)
if err != nil {
err := fmt.Errorf("Error waiting for snapshot: %s", err)
state["error"] = err
state.Put("error", err)
ui.Error(err.Error())
return multistep.ActionHalt
}
state["snapshot_id"] = s.snapshotId
state.Put("snapshot_id", s.snapshotId)
return multistep.ActionContinue
}
func (s *StepSnapshot) Cleanup(state map[string]interface{}) {
func (s *StepSnapshot) Cleanup(state multistep.StateBag) {
if s.snapshotId == "" {
return
}
_, cancelled := state[multistep.StateCancelled]
_, halted := state[multistep.StateHalted]
_, cancelled := state.GetOk(multistep.StateCancelled)
_, halted := state.GetOk(multistep.StateHalted)
if cancelled || halted {
ec2conn := state["ec2"].(*ec2.EC2)
ui := state["ui"].(packer.Ui)
ec2conn := state.Get("ec2").(*ec2.EC2)
ui := state.Get("ui").(packer.Ui)
ui.Say("Removing snapshot since we cancelled or halted...")
_, err := ec2conn.DeleteSnapshots([]string{s.snapshotId})
if err != nil {
@@ -14,23 +14,23 @@ import (
// source_image *ec2.Image - the source AMI info
type StepSourceAMIInfo struct{}
func (s *StepSourceAMIInfo) Run(state map[string]interface{}) multistep.StepAction {
config := state["config"].(*Config)
ec2conn := state["ec2"].(*ec2.EC2)
ui := state["ui"].(packer.Ui)
func (s *StepSourceAMIInfo) Run(state multistep.StateBag) multistep.StepAction {
config := state.Get("config").(*Config)
ec2conn := state.Get("ec2").(*ec2.EC2)
ui := state.Get("ui").(packer.Ui)
ui.Say("Inspecting the source AMI...")
imageResp, err := ec2conn.Images([]string{config.SourceAmi}, ec2.NewFilter())
if err != nil {
err := fmt.Errorf("Error querying AMI: %s", err)
state["error"] = err
state.Put("error", err)
ui.Error(err.Error())
return multistep.ActionHalt
}
if len(imageResp.Images) == 0 {
err := fmt.Errorf("Source AMI '%s' was not found!", config.SourceAmi)
state["error"] = err
state.Put("error", err)
ui.Error(err.Error())
return multistep.ActionHalt
}
@@ -40,13 +40,13 @@ func (s *StepSourceAMIInfo) Run(state map[string]interface{}) multistep.StepActi
// It must be EBS-backed otherwise the build won't work
if image.RootDeviceType != "ebs" {
err := fmt.Errorf("The root device of the source AMI must be EBS-backed.")
state["error"] = err
state.Put("error", err)
ui.Error(err.Error())
return multistep.ActionHalt
}
state["source_image"] = image
state.Put("source_image", image)
return multistep.ActionContinue
}
func (s *StepSourceAMIInfo) Cleanup(map[string]interface{}) {}
func (s *StepSourceAMIInfo) Cleanup(multistep.StateBag) {}
+2 -2
View File
@@ -26,7 +26,7 @@ type StateChangeConf struct {
Conn *ec2.EC2
Pending []string
Refresh StateRefreshFunc
StepState map[string]interface{}
StepState multistep.StateBag
Target string
}
@@ -62,7 +62,7 @@ func WaitForState(conf *StateChangeConf) (i interface{}, err error) {
}
if conf.StepState != nil {
if _, ok := conf.StepState[multistep.StateCancelled]; ok {
if _, ok := conf.StepState.GetOk(multistep.StateCancelled); ok {
return nil, errors.New("interrupted")
}
}
+8 -7
View File
@@ -5,17 +5,18 @@ import (
"errors"
"fmt"
"github.com/mitchellh/goamz/ec2"
"github.com/mitchellh/multistep"
"github.com/mitchellh/packer/communicator/ssh"
"time"
)
// SSHAddress returns a function that can be given to the SSH communicator
// for determining the SSH address based on the instance DNS name.
func SSHAddress(e *ec2.EC2, port int) func(map[string]interface{}) (string, error) {
return func(state map[string]interface{}) (string, error) {
func SSHAddress(e *ec2.EC2, port int) func(multistep.StateBag) (string, error) {
return func(state multistep.StateBag) (string, error) {
for j := 0; j < 2; j++ {
var host string
i := state["instance"].(*ec2.Instance)
i := state.Get("instance").(*ec2.Instance)
if i.DNSName != "" {
host = i.DNSName
} else if i.VpcId != "" {
@@ -35,7 +36,7 @@ func SSHAddress(e *ec2.EC2, port int) func(map[string]interface{}) (string, erro
return "", fmt.Errorf("instance not found: %s", i.InstanceId)
}
state["instance"] = &r.Reservations[0].Instances[0]
state.Put("instance", &r.Reservations[0].Instances[0])
time.Sleep(1 * time.Second)
}
@@ -46,9 +47,9 @@ func SSHAddress(e *ec2.EC2, port int) func(map[string]interface{}) (string, erro
// SSHConfig returns a function that can be used for the SSH communicator
// config for connecting to the instance created over SSH using the generated
// private key.
func SSHConfig(username string) func(map[string]interface{}) (*gossh.ClientConfig, error) {
return func(state map[string]interface{}) (*gossh.ClientConfig, error) {
privateKey := state["privateKey"].(string)
func SSHConfig(username string) func(multistep.StateBag) (*gossh.ClientConfig, error) {
return func(state multistep.StateBag) (*gossh.ClientConfig, error) {
privateKey := state.Get("privateKey").(string)
keyring := new(ssh.SimpleKeychain)
if err := keyring.AddPEMKey(privateKey); err != nil {
@@ -13,10 +13,10 @@ type StepAMIRegionCopy struct {
Tags map[string]string
}
func (s *StepAMIRegionCopy) Run(state map[string]interface{}) multistep.StepAction {
ec2conn := state["ec2"].(*ec2.EC2)
ui := state["ui"].(packer.Ui)
amis := state["amis"].(map[string]string)
func (s *StepAMIRegionCopy) Run(state multistep.StateBag) multistep.StepAction {
ec2conn := state.Get("ec2").(*ec2.EC2)
ui := state.Get("ui").(packer.Ui)
amis := state.Get("amis").(map[string]string)
ami := amis[ec2conn.Region.Name]
if len(s.Regions) == 0 {
@@ -36,7 +36,7 @@ func (s *StepAMIRegionCopy) Run(state map[string]interface{}) multistep.StepActi
if err != nil {
err := fmt.Errorf("Error Copying AMI (%s) to region (%s): %s", ami, region, err)
state["error"] = err
state.Put("error", err)
ui.Error(err.Error())
return multistep.ActionHalt
}
@@ -44,7 +44,7 @@ func (s *StepAMIRegionCopy) Run(state map[string]interface{}) multistep.StepActi
ui.Say(fmt.Sprintf("Waiting for AMI (%s) in region (%s) to become ready...", resp.ImageId, region))
if err := WaitForAMI(regionconn, resp.ImageId); err != nil {
err := fmt.Errorf("Error waiting for AMI (%s) in region (%s): %s", resp.ImageId, region, err)
state["error"] = err
state.Put("error", err)
ui.Error(err.Error())
return multistep.ActionHalt
}
@@ -62,7 +62,7 @@ func (s *StepAMIRegionCopy) Run(state map[string]interface{}) multistep.StepActi
_, err := regionconn.CreateTags([]string{resp.ImageId}, ec2Tags)
if err != nil {
err := fmt.Errorf("Error adding tags to AMI (%s): %s", resp.ImageId, err)
state["error"] = err
state.Put("error", err)
ui.Error(err.Error())
return multistep.ActionHalt
}
@@ -71,10 +71,10 @@ func (s *StepAMIRegionCopy) Run(state map[string]interface{}) multistep.StepActi
amis[region] = resp.ImageId
}
state["amis"] = amis
state.Put("amis", amis)
return multistep.ActionContinue
}
func (s *StepAMIRegionCopy) Cleanup(state map[string]interface{}) {
func (s *StepAMIRegionCopy) Cleanup(state multistep.StateBag) {
// No cleanup...
}
+6 -6
View File
@@ -11,10 +11,10 @@ type StepCreateTags struct {
Tags map[string]string
}
func (s *StepCreateTags) Run(state map[string]interface{}) multistep.StepAction {
ec2conn := state["ec2"].(*ec2.EC2)
ui := state["ui"].(packer.Ui)
amis := state["amis"].(map[string]string)
func (s *StepCreateTags) Run(state multistep.StateBag) multistep.StepAction {
ec2conn := state.Get("ec2").(*ec2.EC2)
ui := state.Get("ui").(packer.Ui)
amis := state.Get("amis").(map[string]string)
ami := amis[ec2conn.Region.Name]
if len(s.Tags) > 0 {
@@ -29,7 +29,7 @@ func (s *StepCreateTags) Run(state map[string]interface{}) multistep.StepAction
_, err := ec2conn.CreateTags([]string{ami}, ec2Tags)
if err != nil {
err := fmt.Errorf("Error adding tags to AMI (%s): %s", ami, err)
state["error"] = err
state.Put("error", err)
ui.Error(err.Error())
return multistep.ActionHalt
}
@@ -38,6 +38,6 @@ func (s *StepCreateTags) Run(state map[string]interface{}) multistep.StepAction
return multistep.ActionContinue
}
func (s *StepCreateTags) Cleanup(state map[string]interface{}) {
func (s *StepCreateTags) Cleanup(state multistep.StateBag) {
// No cleanup...
}
+40 -9
View File
@@ -8,22 +8,27 @@ import (
"github.com/mitchellh/multistep"
"github.com/mitchellh/packer/packer"
"log"
"os"
"runtime"
)
type StepKeyPair struct {
Debug bool
DebugKeyPath string
keyName string
}
func (s *StepKeyPair) Run(state map[string]interface{}) multistep.StepAction {
ec2conn := state["ec2"].(*ec2.EC2)
ui := state["ui"].(packer.Ui)
func (s *StepKeyPair) Run(state multistep.StateBag) multistep.StepAction {
ec2conn := state.Get("ec2").(*ec2.EC2)
ui := state.Get("ui").(packer.Ui)
ui.Say("Creating temporary keypair for this instance...")
keyName := fmt.Sprintf("packer %s", hex.EncodeToString(identifier.NewUUID().Raw()))
log.Printf("temporary keypair name: %s", keyName)
keyResp, err := ec2conn.CreateKeyPair(keyName)
if err != nil {
state["error"] = fmt.Errorf("Error creating temporary keypair: %s", err)
state.Put("error", fmt.Errorf("Error creating temporary keypair: %s", err))
return multistep.ActionHalt
}
@@ -31,20 +36,46 @@ func (s *StepKeyPair) Run(state map[string]interface{}) multistep.StepAction {
s.keyName = keyName
// Set some state data for use in future steps
state["keyPair"] = keyName
state["privateKey"] = keyResp.KeyMaterial
state.Put("keyPair", keyName)
state.Put("privateKey", keyResp.KeyMaterial)
// If we're in debug mode, output the private key to the working
// directory.
if s.Debug {
ui.Message(fmt.Sprintf("Saving key for debug purposes: %s", s.DebugKeyPath))
f, err := os.Create(s.DebugKeyPath)
if err != nil {
state.Put("error", fmt.Errorf("Error saving debug key: %s", err))
return multistep.ActionHalt
}
defer f.Close()
// Write the key out
if _, err := f.Write([]byte(keyResp.KeyMaterial)); err != nil {
state.Put("error", fmt.Errorf("Error saving debug key: %s", err))
return multistep.ActionHalt
}
// Chmod it so that it is SSH ready
if runtime.GOOS != "windows" {
if err := f.Chmod(0600); err != nil {
state.Put("error", fmt.Errorf("Error setting permissions of debug key: %s", err))
return multistep.ActionHalt
}
}
}
return multistep.ActionContinue
}
func (s *StepKeyPair) Cleanup(state map[string]interface{}) {
func (s *StepKeyPair) Cleanup(state multistep.StateBag) {
// If no key name is set, then we never created it, so just return
if s.keyName == "" {
return
}
ec2conn := state["ec2"].(*ec2.EC2)
ui := state["ui"].(packer.Ui)
ec2conn := state.Get("ec2").(*ec2.EC2)
ui := state.Get("ui").(packer.Ui)
ui.Say("Deleting temporary keypair...")
_, err := ec2conn.DeleteKeyPair(s.keyName)
@@ -14,10 +14,10 @@ type StepModifyAMIAttributes struct {
Description string
}
func (s *StepModifyAMIAttributes) Run(state map[string]interface{}) multistep.StepAction {
ec2conn := state["ec2"].(*ec2.EC2)
ui := state["ui"].(packer.Ui)
amis := state["amis"].(map[string]string)
func (s *StepModifyAMIAttributes) Run(state multistep.StateBag) multistep.StepAction {
ec2conn := state.Get("ec2").(*ec2.EC2)
ui := state.Get("ui").(packer.Ui)
amis := state.Get("amis").(map[string]string)
ami := amis[ec2conn.Region.Name]
// Determine if there is any work to do.
@@ -65,7 +65,7 @@ func (s *StepModifyAMIAttributes) Run(state map[string]interface{}) multistep.St
_, err := ec2conn.ModifyImageAttribute(ami, opts)
if err != nil {
err := fmt.Errorf("Error modify AMI attributes: %s", err)
state["error"] = err
state.Put("error", err)
ui.Error(err.Error())
return multistep.ActionHalt
}
@@ -74,6 +74,6 @@ func (s *StepModifyAMIAttributes) Run(state map[string]interface{}) multistep.St
return multistep.ActionContinue
}
func (s *StepModifyAMIAttributes) Cleanup(state map[string]interface{}) {
func (s *StepModifyAMIAttributes) Cleanup(state multistep.StateBag) {
// No cleanup...
}
@@ -10,6 +10,7 @@ import (
)
type StepRunSourceInstance struct {
Debug bool
ExpectedRootDevice string
InstanceType string
UserData string
@@ -22,17 +23,17 @@ type StepRunSourceInstance struct {
instance *ec2.Instance
}
func (s *StepRunSourceInstance) Run(state map[string]interface{}) multistep.StepAction {
ec2conn := state["ec2"].(*ec2.EC2)
keyName := state["keyPair"].(string)
securityGroupId := state["securityGroupId"].(string)
ui := state["ui"].(packer.Ui)
func (s *StepRunSourceInstance) Run(state multistep.StateBag) multistep.StepAction {
ec2conn := state.Get("ec2").(*ec2.EC2)
keyName := state.Get("keyPair").(string)
securityGroupId := state.Get("securityGroupId").(string)
ui := state.Get("ui").(packer.Ui)
userData := s.UserData
if s.UserDataFile != "" {
contents, err := ioutil.ReadFile(s.UserDataFile)
if err != nil {
state["error"] = fmt.Errorf("Problem reading user data file: %s", err)
state.Put("error", fmt.Errorf("Problem reading user data file: %s", err))
return multistep.ActionHalt
}
@@ -55,27 +56,27 @@ func (s *StepRunSourceInstance) Run(state map[string]interface{}) multistep.Step
ui.Say("Launching a source AWS instance...")
imageResp, err := ec2conn.Images([]string{s.SourceAMI}, ec2.NewFilter())
if err != nil {
state["error"] = fmt.Errorf("There was a problem with the source AMI: %s", err)
state.Put("error", fmt.Errorf("There was a problem with the source AMI: %s", err))
return multistep.ActionHalt
}
if len(imageResp.Images) != 1 {
state["error"] = fmt.Errorf("The source AMI '%s' could not be found.", s.SourceAMI)
state.Put("error", fmt.Errorf("The source AMI '%s' could not be found.", s.SourceAMI))
return multistep.ActionHalt
}
if s.ExpectedRootDevice != "" && imageResp.Images[0].RootDeviceType != s.ExpectedRootDevice {
state["error"] = fmt.Errorf(
state.Put("error", fmt.Errorf(
"The provided source AMI has an invalid root device type.\n"+
"Expected '%s', got '%s'.",
s.ExpectedRootDevice, imageResp.Images[0].RootDeviceType)
s.ExpectedRootDevice, imageResp.Images[0].RootDeviceType))
return multistep.ActionHalt
}
runResp, err := ec2conn.RunInstances(runOpts)
if err != nil {
err := fmt.Errorf("Error launching source instance: %s", err)
state["error"] = err
state.Put("error", err)
ui.Error(err.Error())
return multistep.ActionHalt
}
@@ -94,24 +95,35 @@ func (s *StepRunSourceInstance) Run(state map[string]interface{}) multistep.Step
latestInstance, err := WaitForState(&stateChange)
if err != nil {
err := fmt.Errorf("Error waiting for instance (%s) to become ready: %s", s.instance.InstanceId, err)
state["error"] = err
state.Put("error", err)
ui.Error(err.Error())
return multistep.ActionHalt
}
s.instance = latestInstance.(*ec2.Instance)
state["instance"] = s.instance
if s.Debug {
if s.instance.DNSName != "" {
ui.Message(fmt.Sprintf("Public DNS: %s", s.instance.DNSName))
}
if s.instance.PrivateIpAddress != "" {
ui.Message(fmt.Sprintf("Private IP: %s", s.instance.PrivateIpAddress))
}
}
state.Put("instance", s.instance)
return multistep.ActionContinue
}
func (s *StepRunSourceInstance) Cleanup(state map[string]interface{}) {
func (s *StepRunSourceInstance) Cleanup(state multistep.StateBag) {
if s.instance == nil {
return
}
ec2conn := state["ec2"].(*ec2.EC2)
ui := state["ui"].(packer.Ui)
ec2conn := state.Get("ec2").(*ec2.EC2)
ui := state.Get("ui").(packer.Ui)
ui.Say("Terminating the source AWS instance...")
if _, err := ec2conn.TerminateInstances([]string{s.instance.InstanceId}); err != nil {
+9 -9
View File
@@ -19,13 +19,13 @@ type StepSecurityGroup struct {
createdGroupId string
}
func (s *StepSecurityGroup) Run(state map[string]interface{}) multistep.StepAction {
ec2conn := state["ec2"].(*ec2.EC2)
ui := state["ui"].(packer.Ui)
func (s *StepSecurityGroup) Run(state multistep.StateBag) multistep.StepAction {
ec2conn := state.Get("ec2").(*ec2.EC2)
ui := state.Get("ui").(packer.Ui)
if s.SecurityGroupId != "" {
log.Printf("Using specified security group: %s", s.SecurityGroupId)
state["securityGroupId"] = s.SecurityGroupId
state.Put("securityGroupId", s.SecurityGroupId)
return multistep.ActionContinue
}
@@ -64,24 +64,24 @@ func (s *StepSecurityGroup) Run(state map[string]interface{}) multistep.StepActi
ui.Say("Authorizing SSH access on the temporary security group...")
if _, err := ec2conn.AuthorizeSecurityGroup(groupResp.SecurityGroup, perms); err != nil {
err := fmt.Errorf("Error creating temporary security group: %s", err)
state["error"] = err
state.Put("error", err)
ui.Error(err.Error())
return multistep.ActionHalt
}
// Set some state data for use in future steps
state["securityGroupId"] = s.createdGroupId
state.Put("securityGroupId", s.createdGroupId)
return multistep.ActionContinue
}
func (s *StepSecurityGroup) Cleanup(state map[string]interface{}) {
func (s *StepSecurityGroup) Cleanup(state multistep.StateBag) {
if s.createdGroupId == "" {
return
}
ec2conn := state["ec2"].(*ec2.EC2)
ui := state["ui"].(packer.Ui)
ec2conn := state.Get("ec2").(*ec2.EC2)
ui := state.Get("ui").(packer.Ui)
ui.Say("Deleting temporary security group...")
+14 -9
View File
@@ -6,6 +6,7 @@
package ebs
import (
"fmt"
"github.com/mitchellh/goamz/ec2"
"github.com/mitchellh/multistep"
awscommon "github.com/mitchellh/packer/builder/amazon/common"
@@ -72,21 +73,25 @@ func (b *Builder) Run(ui packer.Ui, hook packer.Hook, cache packer.Cache) (packe
ec2conn := ec2.New(auth, region)
// Setup the state bag and initial state for the steps
state := make(map[string]interface{})
state["config"] = b.config
state["ec2"] = ec2conn
state["hook"] = hook
state["ui"] = ui
state := new(multistep.BasicStateBag)
state.Put("config", b.config)
state.Put("ec2", ec2conn)
state.Put("hook", hook)
state.Put("ui", ui)
// Build the steps
steps := []multistep.Step{
&awscommon.StepKeyPair{},
&awscommon.StepKeyPair{
Debug: b.config.PackerDebug,
DebugKeyPath: fmt.Sprintf("ec2_%s.pem", b.config.PackerBuildName),
},
&awscommon.StepSecurityGroup{
SecurityGroupId: b.config.SecurityGroupId,
SSHPort: b.config.SSHPort,
VpcId: b.config.VpcId,
},
&awscommon.StepRunSourceInstance{
Debug: b.config.PackerDebug,
ExpectedRootDevice: "ebs",
InstanceType: b.config.InstanceType,
UserData: b.config.UserData,
@@ -131,18 +136,18 @@ func (b *Builder) Run(ui packer.Ui, hook packer.Hook, cache packer.Cache) (packe
b.runner.Run(state)
// If there was an error, return that
if rawErr, ok := state["error"]; ok {
if rawErr, ok := state.GetOk("error"); ok {
return nil, rawErr.(error)
}
// If there are no AMIs, then just return
if _, ok := state["amis"]; !ok {
if _, ok := state.GetOk("amis"); !ok {
return nil, nil
}
// Build the artifact and return it
artifact := &awscommon.Artifact{
Amis: state["amis"].(map[string]string),
Amis: state.Get("amis").(map[string]string),
BuilderIdValue: BuilderId,
Conn: ec2conn,
}
+9 -9
View File
@@ -10,11 +10,11 @@ import (
type stepCreateAMI struct{}
func (s *stepCreateAMI) Run(state map[string]interface{}) multistep.StepAction {
config := state["config"].(config)
ec2conn := state["ec2"].(*ec2.EC2)
instance := state["instance"].(*ec2.Instance)
ui := state["ui"].(packer.Ui)
func (s *stepCreateAMI) Run(state multistep.StateBag) multistep.StepAction {
config := state.Get("config").(config)
ec2conn := state.Get("ec2").(*ec2.EC2)
instance := state.Get("instance").(*ec2.Instance)
ui := state.Get("ui").(packer.Ui)
// Create the image
ui.Say(fmt.Sprintf("Creating the AMI: %s", config.AMIName))
@@ -27,7 +27,7 @@ func (s *stepCreateAMI) Run(state map[string]interface{}) multistep.StepAction {
createResp, err := ec2conn.CreateImage(createOpts)
if err != nil {
err := fmt.Errorf("Error creating AMI: %s", err)
state["error"] = err
state.Put("error", err)
ui.Error(err.Error())
return multistep.ActionHalt
}
@@ -36,13 +36,13 @@ func (s *stepCreateAMI) Run(state map[string]interface{}) multistep.StepAction {
ui.Say(fmt.Sprintf("AMI: %s", createResp.ImageId))
amis := make(map[string]string)
amis[ec2conn.Region.Name] = createResp.ImageId
state["amis"] = amis
state.Put("amis", amis)
// Wait for the image to become ready
ui.Say("Waiting for AMI to become ready...")
if err := awscommon.WaitForAMI(ec2conn, createResp.ImageId); err != nil {
err := fmt.Errorf("Error waiting for AMI: %s", err)
state["error"] = err
state.Put("error", err)
ui.Error(err.Error())
return multistep.ActionHalt
}
@@ -50,6 +50,6 @@ func (s *stepCreateAMI) Run(state map[string]interface{}) multistep.StepAction {
return multistep.ActionContinue
}
func (s *stepCreateAMI) Cleanup(map[string]interface{}) {
func (s *stepCreateAMI) Cleanup(multistep.StateBag) {
// No cleanup...
}
+7 -7
View File
@@ -10,17 +10,17 @@ import (
type stepStopInstance struct{}
func (s *stepStopInstance) Run(state map[string]interface{}) multistep.StepAction {
ec2conn := state["ec2"].(*ec2.EC2)
instance := state["instance"].(*ec2.Instance)
ui := state["ui"].(packer.Ui)
func (s *stepStopInstance) Run(state multistep.StateBag) multistep.StepAction {
ec2conn := state.Get("ec2").(*ec2.EC2)
instance := state.Get("instance").(*ec2.Instance)
ui := state.Get("ui").(packer.Ui)
// Stop the instance so we can create an AMI from it
ui.Say("Stopping the source instance...")
_, err := ec2conn.StopInstances(instance.InstanceId)
if err != nil {
err := fmt.Errorf("Error stopping instance: %s", err)
state["error"] = err
state.Put("error", err)
ui.Error(err.Error())
return multistep.ActionHalt
}
@@ -37,7 +37,7 @@ func (s *stepStopInstance) Run(state map[string]interface{}) multistep.StepActio
_, err = awscommon.WaitForState(&stateChange)
if err != nil {
err := fmt.Errorf("Error waiting for instance to stop: %s", err)
state["error"] = err
state.Put("error", err)
ui.Error(err.Error())
return multistep.ActionHalt
}
@@ -45,6 +45,6 @@ func (s *stepStopInstance) Run(state map[string]interface{}) multistep.StepActio
return multistep.ActionContinue
}
func (s *stepStopInstance) Cleanup(map[string]interface{}) {
func (s *stepStopInstance) Cleanup(multistep.StateBag) {
// No cleanup...
}
+13 -9
View File
@@ -176,21 +176,25 @@ func (b *Builder) Run(ui packer.Ui, hook packer.Hook, cache packer.Cache) (packe
ec2conn := ec2.New(auth, region)
// Setup the state bag and initial state for the steps
state := make(map[string]interface{})
state["config"] = &b.config
state["ec2"] = ec2conn
state["hook"] = hook
state["ui"] = ui
state := new(multistep.BasicStateBag)
state.Put("config", &b.config)
state.Put("ec2", ec2conn)
state.Put("hook", hook)
state.Put("ui", ui)
// Build the steps
steps := []multistep.Step{
&awscommon.StepKeyPair{},
&awscommon.StepKeyPair{
Debug: b.config.PackerDebug,
DebugKeyPath: fmt.Sprintf("ec2_%s.pem", b.config.PackerBuildName),
},
&awscommon.StepSecurityGroup{
SecurityGroupId: b.config.SecurityGroupId,
SSHPort: b.config.SSHPort,
VpcId: b.config.VpcId,
},
&awscommon.StepRunSourceInstance{
Debug: b.config.PackerDebug,
ExpectedRootDevice: "instance-store",
InstanceType: b.config.InstanceType,
IamInstanceProfile: b.config.IamInstanceProfile,
@@ -238,18 +242,18 @@ func (b *Builder) Run(ui packer.Ui, hook packer.Hook, cache packer.Cache) (packe
b.runner.Run(state)
// If there was an error, return that
if rawErr, ok := state["error"]; ok {
if rawErr, ok := state.GetOk("error"); ok {
return nil, rawErr.(error)
}
// If there are no AMIs, then just return
if _, ok := state["amis"]; !ok {
if _, ok := state.GetOk("amis"); !ok {
return nil, nil
}
// Build the artifact and return it
artifact := &awscommon.Artifact{
Amis: state["amis"].(map[string]string),
Amis: state.Get("amis").(map[string]string),
BuilderIdValue: BuilderId,
Conn: ec2conn,
}
+18 -18
View File
@@ -19,13 +19,13 @@ type bundleCmdData struct {
type StepBundleVolume struct{}
func (s *StepBundleVolume) Run(state map[string]interface{}) multistep.StepAction {
comm := state["communicator"].(packer.Communicator)
config := state["config"].(*Config)
instance := state["instance"].(*ec2.Instance)
ui := state["ui"].(packer.Ui)
x509RemoteCertPath := state["x509RemoteCertPath"].(string)
x509RemoteKeyPath := state["x509RemoteKeyPath"].(string)
func (s *StepBundleVolume) Run(state multistep.StateBag) multistep.StepAction {
comm := state.Get("communicator").(packer.Communicator)
config := state.Get("config").(*Config)
instance := state.Get("instance").(*ec2.Instance)
ui := state.Get("ui").(packer.Ui)
x509RemoteCertPath := state.Get("x509RemoteCertPath").(string)
x509RemoteKeyPath := state.Get("x509RemoteKeyPath").(string)
// Bundle the volume
var err error
@@ -40,7 +40,7 @@ func (s *StepBundleVolume) Run(state map[string]interface{}) multistep.StepActio
})
if err != nil {
err := fmt.Errorf("Error processing bundle volume command: %s", err)
state["error"] = err
state.Put("error", err)
ui.Error(err.Error())
return multistep.ActionHalt
}
@@ -49,26 +49,26 @@ func (s *StepBundleVolume) Run(state map[string]interface{}) multistep.StepActio
cmd := new(packer.RemoteCmd)
cmd.Command = config.BundleVolCommand
if err := cmd.StartWithUi(comm, ui); err != nil {
state["error"] = fmt.Errorf("Error bundling volume: %s", err)
ui.Error(state["error"].(error).Error())
state.Put("error", fmt.Errorf("Error bundling volume: %s", err))
ui.Error(state.Get("error").(error).Error())
return multistep.ActionHalt
}
if cmd.ExitStatus != 0 {
state["error"] = fmt.Errorf(
"Volume bundling failed. Please see the output above for more\n" +
"details on what went wrong.")
ui.Error(state["error"].(error).Error())
state.Put("error", fmt.Errorf(
"Volume bundling failed. Please see the output above for more\n"+
"details on what went wrong."))
ui.Error(state.Get("error").(error).Error())
return multistep.ActionHalt
}
// Store the manifest path
manifestName := config.BundlePrefix + ".manifest.xml"
state["manifest_name"] = manifestName
state["manifest_path"] = fmt.Sprintf(
"%s/%s", config.BundleDestination, manifestName)
state.Put("manifest_name", manifestName)
state.Put("manifest_path", fmt.Sprintf(
"%s/%s", config.BundleDestination, manifestName))
return multistep.ActionContinue
}
func (s *StepBundleVolume) Cleanup(map[string]interface{}) {}
func (s *StepBundleVolume) Cleanup(multistep.StateBag) {}
+10 -10
View File
@@ -10,11 +10,11 @@ import (
type StepRegisterAMI struct{}
func (s *StepRegisterAMI) Run(state map[string]interface{}) multistep.StepAction {
config := state["config"].(*Config)
ec2conn := state["ec2"].(*ec2.EC2)
manifestPath := state["remote_manifest_path"].(string)
ui := state["ui"].(packer.Ui)
func (s *StepRegisterAMI) Run(state multistep.StateBag) multistep.StepAction {
config := state.Get("config").(*Config)
ec2conn := state.Get("ec2").(*ec2.EC2)
manifestPath := state.Get("remote_manifest_path").(string)
ui := state.Get("ui").(packer.Ui)
ui.Say("Registering the AMI...")
registerOpts := &ec2.RegisterImage{
@@ -25,8 +25,8 @@ func (s *StepRegisterAMI) Run(state map[string]interface{}) multistep.StepAction
registerResp, err := ec2conn.RegisterImage(registerOpts)
if err != nil {
state["error"] = fmt.Errorf("Error registering AMI: %s", err)
ui.Error(state["error"].(error).Error())
state.Put("error", fmt.Errorf("Error registering AMI: %s", err))
ui.Error(state.Get("error").(error).Error())
return multistep.ActionHalt
}
@@ -34,13 +34,13 @@ func (s *StepRegisterAMI) Run(state map[string]interface{}) multistep.StepAction
ui.Say(fmt.Sprintf("AMI: %s", registerResp.ImageId))
amis := make(map[string]string)
amis[ec2conn.Region.Name] = registerResp.ImageId
state["amis"] = amis
state.Put("amis", amis)
// Wait for the image to become ready
ui.Say("Waiting for AMI to become ready...")
if err := awscommon.WaitForAMI(ec2conn, registerResp.ImageId); err != nil {
err := fmt.Errorf("Error waiting for AMI: %s", err)
state["error"] = err
state.Put("error", err)
ui.Error(err.Error())
return multistep.ActionHalt
}
@@ -48,4 +48,4 @@ func (s *StepRegisterAMI) Run(state map[string]interface{}) multistep.StepAction
return multistep.ActionContinue
}
func (s *StepRegisterAMI) Cleanup(map[string]interface{}) {}
func (s *StepRegisterAMI) Cleanup(multistep.StateBag) {}
+16 -16
View File
@@ -16,12 +16,12 @@ type uploadCmdData struct {
type StepUploadBundle struct{}
func (s *StepUploadBundle) Run(state map[string]interface{}) multistep.StepAction {
comm := state["communicator"].(packer.Communicator)
config := state["config"].(*Config)
manifestName := state["manifest_name"].(string)
manifestPath := state["manifest_path"].(string)
ui := state["ui"].(packer.Ui)
func (s *StepUploadBundle) Run(state multistep.StateBag) multistep.StepAction {
comm := state.Get("communicator").(packer.Communicator)
config := state.Get("config").(*Config)
manifestName := state.Get("manifest_name").(string)
manifestPath := state.Get("manifest_path").(string)
ui := state.Get("ui").(packer.Ui)
var err error
config.BundleUploadCommand, err = config.tpl.Process(config.BundleUploadCommand, uploadCmdData{
@@ -33,7 +33,7 @@ func (s *StepUploadBundle) Run(state map[string]interface{}) multistep.StepActio
})
if err != nil {
err := fmt.Errorf("Error processing bundle upload command: %s", err)
state["error"] = err
state.Put("error", err)
ui.Error(err.Error())
return multistep.ActionHalt
}
@@ -41,23 +41,23 @@ func (s *StepUploadBundle) Run(state map[string]interface{}) multistep.StepActio
ui.Say("Uploading the bundle...")
cmd := &packer.RemoteCmd{Command: config.BundleUploadCommand}
if err := cmd.StartWithUi(comm, ui); err != nil {
state["error"] = fmt.Errorf("Error uploading volume: %s", err)
ui.Error(state["error"].(error).Error())
state.Put("error", fmt.Errorf("Error uploading volume: %s", err))
ui.Error(state.Get("error").(error).Error())
return multistep.ActionHalt
}
if cmd.ExitStatus != 0 {
state["error"] = fmt.Errorf(
"Bundle upload failed. Please see the output above for more\n" +
"details on what went wrong.")
ui.Error(state["error"].(error).Error())
state.Put("error", fmt.Errorf(
"Bundle upload failed. Please see the output above for more\n"+
"details on what went wrong."))
ui.Error(state.Get("error").(error).Error())
return multistep.ActionHalt
}
state["remote_manifest_path"] = fmt.Sprintf(
"%s/%s", config.S3Bucket, manifestName)
state.Put("remote_manifest_path", fmt.Sprintf(
"%s/%s", config.S3Bucket, manifestName))
return multistep.ActionContinue
}
func (s *StepUploadBundle) Cleanup(state map[string]interface{}) {}
func (s *StepUploadBundle) Cleanup(state multistep.StateBag) {}
@@ -9,34 +9,34 @@ import (
type StepUploadX509Cert struct{}
func (s *StepUploadX509Cert) Run(state map[string]interface{}) multistep.StepAction {
comm := state["communicator"].(packer.Communicator)
config := state["config"].(*Config)
ui := state["ui"].(packer.Ui)
func (s *StepUploadX509Cert) Run(state multistep.StateBag) multistep.StepAction {
comm := state.Get("communicator").(packer.Communicator)
config := state.Get("config").(*Config)
ui := state.Get("ui").(packer.Ui)
x509RemoteCertPath := config.X509UploadPath + "/cert.pem"
x509RemoteKeyPath := config.X509UploadPath + "/key.pem"
ui.Say("Uploading X509 Certificate...")
if err := s.uploadSingle(comm, x509RemoteCertPath, config.X509CertPath); err != nil {
state["error"] = fmt.Errorf("Error uploading X509 cert: %s", err)
ui.Error(state["error"].(error).Error())
state.Put("error", fmt.Errorf("Error uploading X509 cert: %s", err))
ui.Error(state.Get("error").(error).Error())
return multistep.ActionHalt
}
if err := s.uploadSingle(comm, x509RemoteKeyPath, config.X509KeyPath); err != nil {
state["error"] = fmt.Errorf("Error uploading X509 cert: %s", err)
ui.Error(state["error"].(error).Error())
state.Put("error", fmt.Errorf("Error uploading X509 cert: %s", err))
ui.Error(state.Get("error").(error).Error())
return multistep.ActionHalt
}
state["x509RemoteCertPath"] = x509RemoteCertPath
state["x509RemoteKeyPath"] = x509RemoteKeyPath
state.Put("x509RemoteCertPath", x509RemoteCertPath)
state.Put("x509RemoteKeyPath", x509RemoteKeyPath)
return multistep.ActionContinue
}
func (s *StepUploadX509Cert) Cleanup(map[string]interface{}) {}
func (s *StepUploadX509Cert) Cleanup(multistep.StateBag) {}
func (s *StepUploadX509Cert) uploadSingle(comm packer.Communicator, dst, src string) error {
f, err := os.Open(src)
+9 -9
View File
@@ -189,11 +189,11 @@ func (b *Builder) Run(ui packer.Ui, hook packer.Hook, cache packer.Cache) (packe
client := DigitalOceanClient{}.New(b.config.ClientID, b.config.APIKey)
// Set up the state
state := make(map[string]interface{})
state["config"] = b.config
state["client"] = client
state["hook"] = hook
state["ui"] = ui
state := new(multistep.BasicStateBag)
state.Put("config", b.config)
state.Put("client", client)
state.Put("hook", hook)
state.Put("ui", ui)
// Build the steps
steps := []multistep.Step{
@@ -224,18 +224,18 @@ func (b *Builder) Run(ui packer.Ui, hook packer.Hook, cache packer.Cache) (packe
b.runner.Run(state)
// If there was an error, return that
if rawErr, ok := state["error"]; ok {
if rawErr, ok := state.GetOk("error"); ok {
return nil, rawErr.(error)
}
if _, ok := state["snapshot_name"]; !ok {
if _, ok := state.GetOk("snapshot_name"); !ok {
log.Println("Failed to find snapshot_name in state. Bug?")
return nil, nil
}
artifact := &Artifact{
snapshotName: state["snapshot_name"].(string),
snapshotId: state["snapshot_image_id"].(uint),
snapshotName: state.Get("snapshot_name").(string),
snapshotId: state.Get("snapshot_image_id").(uint),
client: client,
}
+7 -6
View File
@@ -3,18 +3,19 @@ package digitalocean
import (
gossh "code.google.com/p/go.crypto/ssh"
"fmt"
"github.com/mitchellh/multistep"
"github.com/mitchellh/packer/communicator/ssh"
)
func sshAddress(state map[string]interface{}) (string, error) {
config := state["config"].(config)
ipAddress := state["droplet_ip"].(string)
func sshAddress(state multistep.StateBag) (string, error) {
config := state.Get("config").(config)
ipAddress := state.Get("droplet_ip").(string)
return fmt.Sprintf("%s:%d", ipAddress, config.SSHPort), nil
}
func sshConfig(state map[string]interface{}) (*gossh.ClientConfig, error) {
config := state["config"].(config)
privateKey := state["privateKey"].(string)
func sshConfig(state multistep.StateBag) (*gossh.ClientConfig, error) {
config := state.Get("config").(config)
privateKey := state.Get("privateKey").(string)
keyring := new(ssh.SimpleKeychain)
if err := keyring.AddPEMKey(privateKey); err != nil {
+22 -14
View File
@@ -14,11 +14,11 @@ type stepCreateDroplet struct {
dropletId uint
}
func (s *stepCreateDroplet) Run(state map[string]interface{}) multistep.StepAction {
client := state["client"].(*DigitalOceanClient)
ui := state["ui"].(packer.Ui)
c := state["config"].(config)
sshKeyId := state["ssh_key_id"].(uint)
func (s *stepCreateDroplet) Run(state multistep.StateBag) multistep.StepAction {
client := state.Get("client").(*DigitalOceanClient)
ui := state.Get("ui").(packer.Ui)
c := state.Get("config").(config)
sshKeyId := state.Get("ssh_key_id").(uint)
ui.Say("Creating droplet...")
@@ -29,7 +29,7 @@ func (s *stepCreateDroplet) Run(state map[string]interface{}) multistep.StepActi
dropletId, err := client.CreateDroplet(name, c.SizeID, c.ImageID, c.RegionID, sshKeyId)
if err != nil {
err := fmt.Errorf("Error creating droplet: %s", err)
state["error"] = err
state.Put("error", err)
ui.Error(err.Error())
return multistep.ActionHalt
}
@@ -38,20 +38,20 @@ func (s *stepCreateDroplet) Run(state map[string]interface{}) multistep.StepActi
s.dropletId = dropletId
// Store the droplet id for later
state["droplet_id"] = dropletId
state.Put("droplet_id", dropletId)
return multistep.ActionContinue
}
func (s *stepCreateDroplet) Cleanup(state map[string]interface{}) {
func (s *stepCreateDroplet) Cleanup(state multistep.StateBag) {
// If the dropletid isn't there, we probably never created it
if s.dropletId == 0 {
return
}
client := state["client"].(*DigitalOceanClient)
ui := state["ui"].(packer.Ui)
c := state["config"].(config)
client := state.Get("client").(*DigitalOceanClient)
ui := state.Get("ui").(packer.Ui)
c := state.Get("config").(config)
// Destroy the droplet we just created
ui.Say("Destroying droplet...")
@@ -62,12 +62,20 @@ func (s *stepCreateDroplet) Cleanup(state map[string]interface{}) {
log.Printf("Sleeping for %v, event_delay", c.RawEventDelay)
time.Sleep(c.eventDelay)
err := client.DestroyDroplet(s.dropletId)
var err error
for i := 0; i < 5; i++ {
err = client.DestroyDroplet(s.dropletId)
if err == nil {
break
}
curlstr := fmt.Sprintf("curl '%v/droplets/%v/destroy?client_id=%v&api_key=%v'",
DIGITALOCEAN_API_URL, s.dropletId, c.ClientID, c.APIKey)
time.Sleep(2 * time.Second)
}
if err != nil {
curlstr := fmt.Sprintf("curl '%v/droplets/%v/destroy?client_id=%v&api_key=%v'",
DIGITALOCEAN_API_URL, s.dropletId, c.ClientID, c.APIKey)
ui.Error(fmt.Sprintf(
"Error destroying droplet. Please destroy it manually: %v", curlstr))
}
+10 -10
View File
@@ -18,9 +18,9 @@ type stepCreateSSHKey struct {
keyId uint
}
func (s *stepCreateSSHKey) Run(state map[string]interface{}) multistep.StepAction {
client := state["client"].(*DigitalOceanClient)
ui := state["ui"].(packer.Ui)
func (s *stepCreateSSHKey) Run(state multistep.StateBag) multistep.StepAction {
client := state.Get("client").(*DigitalOceanClient)
ui := state.Get("ui").(packer.Ui)
ui.Say("Creating temporary ssh key for droplet...")
@@ -35,7 +35,7 @@ func (s *stepCreateSSHKey) Run(state map[string]interface{}) multistep.StepActio
}
// Set the private key in the statebag for later
state["privateKey"] = string(pem.EncodeToMemory(&priv_blk))
state.Put("privateKey", string(pem.EncodeToMemory(&priv_blk)))
// Marshal the public key into SSH compatible format
pub := priv.PublicKey
@@ -48,7 +48,7 @@ func (s *stepCreateSSHKey) Run(state map[string]interface{}) multistep.StepActio
keyId, err := client.CreateKey(name, pub_sshformat)
if err != nil {
err := fmt.Errorf("Error creating temporary SSH key: %s", err)
state["error"] = err
state.Put("error", err)
ui.Error(err.Error())
return multistep.ActionHalt
}
@@ -59,20 +59,20 @@ func (s *stepCreateSSHKey) Run(state map[string]interface{}) multistep.StepActio
log.Printf("temporary ssh key name: %s", name)
// Remember some state for the future
state["ssh_key_id"] = keyId
state.Put("ssh_key_id", keyId)
return multistep.ActionContinue
}
func (s *stepCreateSSHKey) Cleanup(state map[string]interface{}) {
func (s *stepCreateSSHKey) Cleanup(state multistep.StateBag) {
// If no key name is set, then we never created it, so just return
if s.keyId == 0 {
return
}
client := state["client"].(*DigitalOceanClient)
ui := state["ui"].(packer.Ui)
c := state["config"].(config)
client := state.Get("client").(*DigitalOceanClient)
ui := state.Get("ui").(packer.Ui)
c := state.Get("config").(config)
ui.Say("Deleting temporary ssh key...")
err := client.DestroyKey(s.keyId)
+9 -9
View File
@@ -8,18 +8,18 @@ import (
type stepDropletInfo struct{}
func (s *stepDropletInfo) Run(state map[string]interface{}) multistep.StepAction {
client := state["client"].(*DigitalOceanClient)
ui := state["ui"].(packer.Ui)
c := state["config"].(config)
dropletId := state["droplet_id"].(uint)
func (s *stepDropletInfo) Run(state multistep.StateBag) multistep.StepAction {
client := state.Get("client").(*DigitalOceanClient)
ui := state.Get("ui").(packer.Ui)
c := state.Get("config").(config)
dropletId := state.Get("droplet_id").(uint)
ui.Say("Waiting for droplet to become active...")
err := waitForDropletState("active", dropletId, client, c)
if err != nil {
err := fmt.Errorf("Error waiting for droplet to become active: %s", err)
state["error"] = err
state.Put("error", err)
ui.Error(err.Error())
return multistep.ActionHalt
}
@@ -28,16 +28,16 @@ func (s *stepDropletInfo) Run(state map[string]interface{}) multistep.StepAction
ip, _, err := client.DropletStatus(dropletId)
if err != nil {
err := fmt.Errorf("Error retrieving droplet ID: %s", err)
state["error"] = err
state.Put("error", err)
ui.Error(err.Error())
return multistep.ActionHalt
}
state["droplet_ip"] = ip
state.Put("droplet_ip", ip)
return multistep.ActionContinue
}
func (s *stepDropletInfo) Cleanup(state map[string]interface{}) {
func (s *stepDropletInfo) Cleanup(state multistep.StateBag) {
// no cleanup
}
+7 -7
View File
@@ -10,11 +10,11 @@ import (
type stepPowerOff struct{}
func (s *stepPowerOff) Run(state map[string]interface{}) multistep.StepAction {
client := state["client"].(*DigitalOceanClient)
c := state["config"].(config)
ui := state["ui"].(packer.Ui)
dropletId := state["droplet_id"].(uint)
func (s *stepPowerOff) Run(state multistep.StateBag) multistep.StepAction {
client := state.Get("client").(*DigitalOceanClient)
c := state.Get("config").(config)
ui := state.Get("ui").(packer.Ui)
dropletId := state.Get("droplet_id").(uint)
// Sleep arbitrarily before sending power off request
// Otherwise we get "pending event" errors, even though there isn't
@@ -27,7 +27,7 @@ func (s *stepPowerOff) Run(state map[string]interface{}) multistep.StepAction {
if err != nil {
err := fmt.Errorf("Error powering off droplet: %s", err)
state["error"] = err
state.Put("error", err)
ui.Error(err.Error())
return multistep.ActionHalt
}
@@ -44,6 +44,6 @@ func (s *stepPowerOff) Run(state map[string]interface{}) multistep.StepAction {
return multistep.ActionContinue
}
func (s *stepPowerOff) Cleanup(state map[string]interface{}) {
func (s *stepPowerOff) Cleanup(state multistep.StateBag) {
// no cleanup
}
+8 -8
View File
@@ -10,11 +10,11 @@ import (
type stepShutdown struct{}
func (s *stepShutdown) Run(state map[string]interface{}) multistep.StepAction {
client := state["client"].(*DigitalOceanClient)
c := state["config"].(config)
ui := state["ui"].(packer.Ui)
dropletId := state["droplet_id"].(uint)
func (s *stepShutdown) Run(state multistep.StateBag) multistep.StepAction {
client := state.Get("client").(*DigitalOceanClient)
c := state.Get("config").(config)
ui := state.Get("ui").(packer.Ui)
dropletId := state.Get("droplet_id").(uint)
// Sleep arbitrarily before sending the request
// Otherwise we get "pending event" errors, even though there isn't
@@ -26,7 +26,7 @@ func (s *stepShutdown) Run(state map[string]interface{}) multistep.StepAction {
if err != nil {
err := fmt.Errorf("Error shutting down droplet: %s", err)
state["error"] = err
state.Put("error", err)
ui.Error(err.Error())
return multistep.ActionHalt
}
@@ -36,7 +36,7 @@ func (s *stepShutdown) Run(state map[string]interface{}) multistep.StepAction {
err = waitForDropletState("off", dropletId, client, c)
if err != nil {
err := fmt.Errorf("Error waiting for droplet to become 'off': %s", err)
state["error"] = err
state.Put("error", err)
ui.Error(err.Error())
return multistep.ActionHalt
}
@@ -44,6 +44,6 @@ func (s *stepShutdown) Run(state map[string]interface{}) multistep.StepAction {
return multistep.ActionContinue
}
func (s *stepShutdown) Cleanup(state map[string]interface{}) {
func (s *stepShutdown) Cleanup(state multistep.StateBag) {
// no cleanup
}
+12 -12
View File
@@ -10,17 +10,17 @@ import (
type stepSnapshot struct{}
func (s *stepSnapshot) Run(state map[string]interface{}) multistep.StepAction {
client := state["client"].(*DigitalOceanClient)
ui := state["ui"].(packer.Ui)
c := state["config"].(config)
dropletId := state["droplet_id"].(uint)
func (s *stepSnapshot) Run(state multistep.StateBag) multistep.StepAction {
client := state.Get("client").(*DigitalOceanClient)
ui := state.Get("ui").(packer.Ui)
c := state.Get("config").(config)
dropletId := state.Get("droplet_id").(uint)
ui.Say(fmt.Sprintf("Creating snapshot: %v", c.SnapshotName))
err := client.CreateSnapshot(dropletId, c.SnapshotName)
if err != nil {
err := fmt.Errorf("Error creating snapshot: %s", err)
state["error"] = err
state.Put("error", err)
ui.Error(err.Error())
return multistep.ActionHalt
}
@@ -29,7 +29,7 @@ func (s *stepSnapshot) Run(state map[string]interface{}) multistep.StepAction {
err = waitForDropletState("active", dropletId, client, c)
if err != nil {
err := fmt.Errorf("Error waiting for snapshot to complete: %s", err)
state["error"] = err
state.Put("error", err)
ui.Error(err.Error())
return multistep.ActionHalt
}
@@ -38,7 +38,7 @@ func (s *stepSnapshot) Run(state map[string]interface{}) multistep.StepAction {
images, err := client.Images()
if err != nil {
err := fmt.Errorf("Error looking up snapshot ID: %s", err)
state["error"] = err
state.Put("error", err)
ui.Error(err.Error())
return multistep.ActionHalt
}
@@ -53,19 +53,19 @@ func (s *stepSnapshot) Run(state map[string]interface{}) multistep.StepAction {
if imageId == 0 {
err := errors.New("Couldn't find snapshot to get the image ID. Bug?")
state["error"] = err
state.Put("error", err)
ui.Error(err.Error())
return multistep.ActionHalt
}
log.Printf("Snapshot image ID: %d", imageId)
state["snapshot_image_id"] = imageId
state["snapshot_name"] = c.SnapshotName
state.Put("snapshot_image_id", imageId)
state.Put("snapshot_name", c.SnapshotName)
return multistep.ActionContinue
}
func (s *stepSnapshot) Cleanup(state map[string]interface{}) {
func (s *stepSnapshot) Cleanup(state multistep.StateBag) {
// no cleanup
}
+12 -3
View File
@@ -9,9 +9,10 @@ import (
// AccessConfig is for common configuration related to openstack access
type AccessConfig struct {
Username string `mapstructure:"username"`
Password string `mapstructure:"password"`
Provider string `mapstructure:"provider"`
Username string `mapstructure:"username"`
Password string `mapstructure:"password"`
Provider string `mapstructure:"provider"`
RawRegion string `mapstructure:"region"`
}
// Auth returns a valid Auth object for access to openstack services, or
@@ -40,6 +41,10 @@ func (c *AccessConfig) Auth() (gophercloud.AccessProvider, error) {
return gophercloud.Authenticate(provider, authoptions)
}
func (c *AccessConfig) Region() string {
return c.RawRegion
}
func (c *AccessConfig) Prepare(t *packer.ConfigTemplate) []error {
if t == nil {
var err error
@@ -65,6 +70,10 @@ func (c *AccessConfig) Prepare(t *packer.ConfigTemplate) []error {
}
}
if c.RawRegion == "" {
errs = append(errs, fmt.Errorf("region must be specified"))
}
if len(errs) > 0 {
return errs
}
+14 -2
View File
@@ -8,9 +8,21 @@ func testAccessConfig() *AccessConfig {
return &AccessConfig{}
}
func TestAccessConfigPrepare_Region(t *testing.T) {
func TestAccessConfigPrepare_NoRegion(t *testing.T) {
c := testAccessConfig()
if err := c.Prepare(nil); err != nil {
if err := c.Prepare(nil); err == nil {
t.Fatalf("shouldn't have err: %s", err)
}
}
func TestAccessConfigPrepare_Region(t *testing.T) {
dfw := "DFW"
c := testAccessConfig()
c.RawRegion = dfw
if err := c.Prepare(nil); err != nil {
t.Fatalf("shouldn't have err: %s", err)
}
if dfw != c.Region() {
t.Fatalf("Regions do not match: %s %s", dfw, c.Region())
}
}
+14 -8
View File
@@ -62,21 +62,22 @@ func (b *Builder) Run(ui packer.Ui, hook packer.Hook, cache packer.Cache) (packe
}
api := &gophercloud.ApiCriteria{
Name: "cloudServersOpenStack",
Region: "DFW",
Region: b.config.AccessConfig.Region(),
VersionId: "2",
UrlChoice: gophercloud.PublicURL,
}
csp, err := gophercloud.ServersApi(auth, *api)
if err != nil {
log.Printf("Region: %s", b.config.AccessConfig.Region())
return nil, err
}
// Setup the state bag and initial state for the steps
state := make(map[string]interface{})
state["config"] = b.config
state["csp"] = csp
state["hook"] = hook
state["ui"] = ui
state := new(multistep.BasicStateBag)
state.Put("config", b.config)
state.Put("csp", csp)
state.Put("hook", hook)
state.Put("ui", ui)
// Build the steps
steps := []multistep.Step{
@@ -108,13 +109,18 @@ func (b *Builder) Run(ui packer.Ui, hook packer.Hook, cache packer.Cache) (packe
b.runner.Run(state)
// If there was an error, return that
if rawErr, ok := state["error"]; ok {
if rawErr, ok := state.GetOk("error"); ok {
return nil, rawErr.(error)
}
// If there are no images, then just return
if _, ok := state.GetOk("image"); !ok {
return nil, nil
}
// Build the artifact and return it
artifact := &Artifact{
ImageId: state["image"].(string),
ImageId: state.Get("image").(string),
BuilderIdValue: BuilderId,
Conn: csp,
}
+1
View File
@@ -10,6 +10,7 @@ func testConfig() map[string]interface{} {
"username": "foo",
"password": "bar",
"provider": "foo",
"region": "DFW",
"image_name": "foo",
"source_image": "foo",
"flavor": "foo",
+1 -1
View File
@@ -39,7 +39,7 @@ func (c *RunConfig) Prepare(t *packer.ConfigTemplate) []error {
}
if c.RawSSHTimeout == "" {
c.RawSSHTimeout = "1m"
c.RawSSHTimeout = "5m"
}
// Validation
+2 -2
View File
@@ -25,7 +25,7 @@ type StateRefreshFunc func() (result interface{}, state string, progress int, er
type StateChangeConf struct {
Pending []string
Refresh StateRefreshFunc
StepState map[string]interface{}
StepState multistep.StateBag
Target string
}
@@ -61,7 +61,7 @@ func WaitForState(conf *StateChangeConf) (i interface{}, err error) {
}
if conf.StepState != nil {
if _, ok := conf.StepState[multistep.StateCancelled]; ok {
if _, ok := conf.StepState.GetOk(multistep.StateCancelled); ok {
return nil, errors.New("interrupted")
}
}
+11 -7
View File
@@ -4,6 +4,7 @@ import (
gossh "code.google.com/p/go.crypto/ssh"
"errors"
"fmt"
"github.com/mitchellh/multistep"
"github.com/mitchellh/packer/communicator/ssh"
"github.com/rackspace/gophercloud"
"time"
@@ -11,20 +12,23 @@ import (
// SSHAddress returns a function that can be given to the SSH communicator
// for determining the SSH address based on the server AccessIPv4 setting..
func SSHAddress(csp gophercloud.CloudServersProvider, port int) func(map[string]interface{}) (string, error) {
return func(state map[string]interface{}) (string, error) {
func SSHAddress(csp gophercloud.CloudServersProvider, port int) func(multistep.StateBag) (string, error) {
return func(state multistep.StateBag) (string, error) {
for j := 0; j < 2; j++ {
s := state["server"].(*gophercloud.Server)
s := state.Get("server").(*gophercloud.Server)
if s.AccessIPv4 != "" {
return fmt.Sprintf("%s:%d", s.AccessIPv4, port), nil
}
if s.AccessIPv6 != "" {
return fmt.Sprintf("[%s]:%d", s.AccessIPv6, port), nil
}
serverState, err := csp.ServerById(s.Id)
if err != nil {
return "", err
}
state["server"] = serverState
state.Put("server", serverState)
time.Sleep(1 * time.Second)
}
@@ -35,9 +39,9 @@ func SSHAddress(csp gophercloud.CloudServersProvider, port int) func(map[string]
// SSHConfig returns a function that can be used for the SSH communicator
// config for connecting to the instance created over SSH using the generated
// private key.
func SSHConfig(username string) func(map[string]interface{}) (*gossh.ClientConfig, error) {
return func(state map[string]interface{}) (*gossh.ClientConfig, error) {
privateKey := state["privateKey"].(string)
func SSHConfig(username string) func(multistep.StateBag) (*gossh.ClientConfig, error) {
return func(state multistep.StateBag) (*gossh.ClientConfig, error) {
privateKey := state.Get("privateKey").(string)
keyring := new(ssh.SimpleKeychain)
if err := keyring.AddPEMKey(privateKey); err != nil {
+9 -9
View File
@@ -11,11 +11,11 @@ import (
type stepCreateImage struct{}
func (s *stepCreateImage) Run(state map[string]interface{}) multistep.StepAction {
csp := state["csp"].(gophercloud.CloudServersProvider)
config := state["config"].(config)
server := state["server"].(*gophercloud.Server)
ui := state["ui"].(packer.Ui)
func (s *stepCreateImage) Run(state multistep.StateBag) multistep.StepAction {
csp := state.Get("csp").(gophercloud.CloudServersProvider)
config := state.Get("config").(config)
server := state.Get("server").(*gophercloud.Server)
ui := state.Get("ui").(packer.Ui)
// Create the image
ui.Say(fmt.Sprintf("Creating the image: %s", config.ImageName))
@@ -25,20 +25,20 @@ func (s *stepCreateImage) Run(state map[string]interface{}) multistep.StepAction
imageId, err := csp.CreateImage(server.Id, createOpts)
if err != nil {
err := fmt.Errorf("Error creating image: %s", err)
state["error"] = err
state.Put("error", err)
ui.Error(err.Error())
return multistep.ActionHalt
}
// Set the Image ID in the state
ui.Say(fmt.Sprintf("Image: %s", imageId))
state["image"] = imageId
state.Put("image", imageId)
// Wait for the image to become ready
ui.Say("Waiting for image to become ready...")
if err := WaitForImage(csp, imageId); err != nil {
err := fmt.Errorf("Error waiting for image: %s", err)
state["error"] = err
state.Put("error", err)
ui.Error(err.Error())
return multistep.ActionHalt
}
@@ -46,7 +46,7 @@ func (s *stepCreateImage) Run(state map[string]interface{}) multistep.StepAction
return multistep.ActionContinue
}
func (s *stepCreateImage) Cleanup(map[string]interface{}) {
func (s *stepCreateImage) Cleanup(multistep.StateBag) {
// No cleanup...
}
+9 -9
View File
@@ -14,16 +14,16 @@ type StepKeyPair struct {
keyName string
}
func (s *StepKeyPair) Run(state map[string]interface{}) multistep.StepAction {
csp := state["csp"].(gophercloud.CloudServersProvider)
ui := state["ui"].(packer.Ui)
func (s *StepKeyPair) Run(state multistep.StateBag) multistep.StepAction {
csp := state.Get("csp").(gophercloud.CloudServersProvider)
ui := state.Get("ui").(packer.Ui)
ui.Say("Creating temporary keypair for this instance...")
keyName := fmt.Sprintf("packer %s", hex.EncodeToString(identifier.NewUUID().Raw()))
log.Printf("temporary keypair name: %s", keyName)
keyResp, err := csp.CreateKeyPair(gophercloud.NewKeyPair{Name: keyName})
if err != nil {
state["error"] = fmt.Errorf("Error creating temporary keypair: %s", err)
state.Put("error", fmt.Errorf("Error creating temporary keypair: %s", err))
return multistep.ActionHalt
}
@@ -31,20 +31,20 @@ func (s *StepKeyPair) Run(state map[string]interface{}) multistep.StepAction {
s.keyName = keyName
// Set some state data for use in future steps
state["keyPair"] = keyName
state["privateKey"] = keyResp.PrivateKey
state.Put("keyPair", keyName)
state.Put("privateKey", keyResp.PrivateKey)
return multistep.ActionContinue
}
func (s *StepKeyPair) Cleanup(state map[string]interface{}) {
func (s *StepKeyPair) Cleanup(state multistep.StateBag) {
// If no key name is set, then we never created it, so just return
if s.keyName == "" {
return
}
csp := state["csp"].(gophercloud.CloudServersProvider)
ui := state["ui"].(packer.Ui)
csp := state.Get("csp").(gophercloud.CloudServersProvider)
ui := state.Get("ui").(packer.Ui)
ui.Say("Deleting temporary keypair...")
err := csp.DeleteKeyPair(s.keyName)
+10 -10
View File
@@ -16,10 +16,10 @@ type StepRunSourceServer struct {
server *gophercloud.Server
}
func (s *StepRunSourceServer) Run(state map[string]interface{}) multistep.StepAction {
csp := state["csp"].(gophercloud.CloudServersProvider)
keyName := state["keyPair"].(string)
ui := state["ui"].(packer.Ui)
func (s *StepRunSourceServer) Run(state multistep.StateBag) multistep.StepAction {
csp := state.Get("csp").(gophercloud.CloudServersProvider)
keyName := state.Get("keyPair").(string)
ui := state.Get("ui").(packer.Ui)
// XXX - validate image and flavor is available
@@ -33,7 +33,7 @@ func (s *StepRunSourceServer) Run(state map[string]interface{}) multistep.StepAc
serverResp, err := csp.CreateServer(server)
if err != nil {
err := fmt.Errorf("Error launching source server: %s", err)
state["error"] = err
state.Put("error", err)
ui.Error(err.Error())
return multistep.ActionHalt
}
@@ -51,24 +51,24 @@ func (s *StepRunSourceServer) Run(state map[string]interface{}) multistep.StepAc
latestServer, err := WaitForState(&stateChange)
if err != nil {
err := fmt.Errorf("Error waiting for server (%s) to become ready: %s", s.server.Id, err)
state["error"] = err
state.Put("error", err)
ui.Error(err.Error())
return multistep.ActionHalt
}
s.server = latestServer.(*gophercloud.Server)
state["server"] = s.server
state.Put("server", s.server)
return multistep.ActionContinue
}
func (s *StepRunSourceServer) Cleanup(state map[string]interface{}) {
func (s *StepRunSourceServer) Cleanup(state multistep.StateBag) {
if s.server == nil {
return
}
csp := state["csp"].(gophercloud.CloudServersProvider)
ui := state["ui"].(packer.Ui)
csp := state.Get("csp").(gophercloud.CloudServersProvider)
ui := state.Get("ui").(packer.Ui)
ui.Say("Terminating the source server...")
if err := csp.DeleteServerById(s.server.Id); err != nil {
+9 -9
View File
@@ -368,12 +368,12 @@ func (b *Builder) Run(ui packer.Ui, hook packer.Hook, cache packer.Cache) (packe
}
// Setup the state bag
state := make(map[string]interface{})
state["cache"] = cache
state["config"] = &b.config
state["driver"] = driver
state["hook"] = hook
state["ui"] = ui
state := new(multistep.BasicStateBag)
state.Put("cache", cache)
state.Put("config", &b.config)
state.Put("driver", driver)
state.Put("hook", hook)
state.Put("ui", ui)
// Run
if b.config.PackerDebug {
@@ -388,16 +388,16 @@ func (b *Builder) Run(ui packer.Ui, hook packer.Hook, cache packer.Cache) (packe
b.runner.Run(state)
// If there was an error, return that
if rawErr, ok := state["error"]; ok {
if rawErr, ok := state.GetOk("error"); ok {
return nil, rawErr.(error)
}
// If we were interrupted or cancelled, then just exit.
if _, ok := state[multistep.StateCancelled]; ok {
if _, ok := state.GetOk(multistep.StateCancelled); ok {
return nil, errors.New("Build was cancelled.")
}
if _, ok := state[multistep.StateHalted]; ok {
if _, ok := state.GetOk(multistep.StateHalted); ok {
return nil, errors.New("Build was halted.")
}
+5 -4
View File
@@ -3,18 +3,19 @@ package virtualbox
import (
gossh "code.google.com/p/go.crypto/ssh"
"fmt"
"github.com/mitchellh/multistep"
"github.com/mitchellh/packer/communicator/ssh"
"io/ioutil"
"os"
)
func sshAddress(state map[string]interface{}) (string, error) {
sshHostPort := state["sshHostPort"].(uint)
func sshAddress(state multistep.StateBag) (string, error) {
sshHostPort := state.Get("sshHostPort").(uint)
return fmt.Sprintf("127.0.0.1:%d", sshHostPort), nil
}
func sshConfig(state map[string]interface{}) (*gossh.ClientConfig, error) {
config := state["config"].(*config)
func sshConfig(state multistep.StateBag) (*gossh.ClientConfig, error) {
config := state.Get("config").(*config)
auth := []gossh.ClientAuth{
gossh.ClientAuthPassword(ssh.Password(config.SSHPassword)),
+11 -11
View File
@@ -20,10 +20,10 @@ type stepAttachFloppy struct {
floppyPath string
}
func (s *stepAttachFloppy) Run(state map[string]interface{}) multistep.StepAction {
func (s *stepAttachFloppy) Run(state multistep.StateBag) multistep.StepAction {
// Determine if we even have a floppy disk to attach
var floppyPath string
if floppyPathRaw, ok := state["floppy_path"]; ok {
if floppyPathRaw, ok := state.GetOk("floppy_path"); ok {
floppyPath = floppyPathRaw.(string)
} else {
log.Println("No floppy disk, not attaching.")
@@ -35,13 +35,13 @@ func (s *stepAttachFloppy) Run(state map[string]interface{}) multistep.StepActio
// floppy.
floppyPath, err := s.copyFloppy(floppyPath)
if err != nil {
state["error"] = fmt.Errorf("Error preparing floppy: %s", err)
state.Put("error", fmt.Errorf("Error preparing floppy: %s", err))
return multistep.ActionHalt
}
driver := state["driver"].(Driver)
ui := state["ui"].(packer.Ui)
vmName := state["vmName"].(string)
driver := state.Get("driver").(Driver)
ui := state.Get("ui").(packer.Ui)
vmName := state.Get("vmName").(string)
ui.Say("Attaching floppy disk...")
@@ -52,7 +52,7 @@ func (s *stepAttachFloppy) Run(state map[string]interface{}) multistep.StepActio
"--add", "floppy",
}
if err := driver.VBoxManage(command...); err != nil {
state["error"] = fmt.Errorf("Error creating floppy controller: %s", err)
state.Put("error", fmt.Errorf("Error creating floppy controller: %s", err))
return multistep.ActionHalt
}
@@ -66,7 +66,7 @@ func (s *stepAttachFloppy) Run(state map[string]interface{}) multistep.StepActio
"--medium", floppyPath,
}
if err := driver.VBoxManage(command...); err != nil {
state["error"] = fmt.Errorf("Error attaching floppy: %s", err)
state.Put("error", fmt.Errorf("Error attaching floppy: %s", err))
return multistep.ActionHalt
}
@@ -76,7 +76,7 @@ func (s *stepAttachFloppy) Run(state map[string]interface{}) multistep.StepActio
return multistep.ActionContinue
}
func (s *stepAttachFloppy) Cleanup(state map[string]interface{}) {
func (s *stepAttachFloppy) Cleanup(state multistep.StateBag) {
if s.floppyPath == "" {
return
}
@@ -84,8 +84,8 @@ func (s *stepAttachFloppy) Cleanup(state map[string]interface{}) {
// Delete the floppy disk
defer os.Remove(s.floppyPath)
driver := state["driver"].(Driver)
vmName := state["vmName"].(string)
driver := state.Get("driver").(Driver)
vmName := state.Get("vmName").(string)
command := []string{
"storageattach", vmName,
+10 -10
View File
@@ -15,11 +15,11 @@ type stepAttachISO struct {
diskPath string
}
func (s *stepAttachISO) Run(state map[string]interface{}) multistep.StepAction {
driver := state["driver"].(Driver)
isoPath := state["iso_path"].(string)
ui := state["ui"].(packer.Ui)
vmName := state["vmName"].(string)
func (s *stepAttachISO) Run(state multistep.StateBag) multistep.StepAction {
driver := state.Get("driver").(Driver)
isoPath := state.Get("iso_path").(string)
ui := state.Get("ui").(packer.Ui)
vmName := state.Get("vmName").(string)
// Attach the disk to the controller
command := []string{
@@ -32,7 +32,7 @@ func (s *stepAttachISO) Run(state map[string]interface{}) multistep.StepAction {
}
if err := driver.VBoxManage(command...); err != nil {
err := fmt.Errorf("Error attaching ISO: %s", err)
state["error"] = err
state.Put("error", err)
ui.Error(err.Error())
return multistep.ActionHalt
}
@@ -43,14 +43,14 @@ func (s *stepAttachISO) Run(state map[string]interface{}) multistep.StepAction {
return multistep.ActionContinue
}
func (s *stepAttachISO) Cleanup(state map[string]interface{}) {
func (s *stepAttachISO) Cleanup(state multistep.StateBag) {
if s.diskPath == "" {
return
}
driver := state["driver"].(Driver)
ui := state["ui"].(packer.Ui)
vmName := state["vmName"].(string)
driver := state.Get("driver").(Driver)
ui := state.Get("ui").(packer.Ui)
vmName := state.Get("vmName").(string)
command := []string{
"storageattach", vmName,
+9 -9
View File
@@ -13,11 +13,11 @@ import (
// hard drive for the virtual machine.
type stepCreateDisk struct{}
func (s *stepCreateDisk) Run(state map[string]interface{}) multistep.StepAction {
config := state["config"].(*config)
driver := state["driver"].(Driver)
ui := state["ui"].(packer.Ui)
vmName := state["vmName"].(string)
func (s *stepCreateDisk) Run(state multistep.StateBag) multistep.StepAction {
config := state.Get("config").(*config)
driver := state.Get("driver").(Driver)
ui := state.Get("ui").(packer.Ui)
vmName := state.Get("vmName").(string)
format := "VDI"
path := filepath.Join(config.OutputDir, fmt.Sprintf("%s.%s", config.VMName, strings.ToLower(format)))
@@ -34,7 +34,7 @@ func (s *stepCreateDisk) Run(state map[string]interface{}) multistep.StepAction
err := driver.VBoxManage(command...)
if err != nil {
err := fmt.Errorf("Error creating hard drive: %s", err)
state["error"] = err
state.Put("error", err)
ui.Error(err.Error())
return multistep.ActionHalt
}
@@ -44,7 +44,7 @@ func (s *stepCreateDisk) Run(state map[string]interface{}) multistep.StepAction
err = driver.VBoxManage("storagectl", vmName, "--name", controllerName, "--add", "ide")
if err != nil {
err := fmt.Errorf("Error creating disk controller: %s", err)
state["error"] = err
state.Put("error", err)
ui.Error(err.Error())
return multistep.ActionHalt
}
@@ -60,7 +60,7 @@ func (s *stepCreateDisk) Run(state map[string]interface{}) multistep.StepAction
}
if err := driver.VBoxManage(command...); err != nil {
err := fmt.Errorf("Error attaching hard drive: %s", err)
state["error"] = err
state.Put("error", err)
ui.Error(err.Error())
return multistep.ActionHalt
}
@@ -68,4 +68,4 @@ func (s *stepCreateDisk) Run(state map[string]interface{}) multistep.StepAction
return multistep.ActionContinue
}
func (s *stepCreateDisk) Cleanup(state map[string]interface{}) {}
func (s *stepCreateDisk) Cleanup(state multistep.StateBag) {}
+9 -9
View File
@@ -14,10 +14,10 @@ type stepCreateVM struct {
vmName string
}
func (s *stepCreateVM) Run(state map[string]interface{}) multistep.StepAction {
config := state["config"].(*config)
driver := state["driver"].(Driver)
ui := state["ui"].(packer.Ui)
func (s *stepCreateVM) Run(state multistep.StateBag) multistep.StepAction {
config := state.Get("config").(*config)
driver := state.Get("driver").(Driver)
ui := state.Get("ui").(packer.Ui)
name := config.VMName
@@ -38,7 +38,7 @@ func (s *stepCreateVM) Run(state map[string]interface{}) multistep.StepAction {
err := driver.VBoxManage(command...)
if err != nil {
err := fmt.Errorf("Error creating VM: %s", err)
state["error"] = err
state.Put("error", err)
ui.Error(err.Error())
return multistep.ActionHalt
}
@@ -50,18 +50,18 @@ func (s *stepCreateVM) Run(state map[string]interface{}) multistep.StepAction {
}
// Set the final name in the state bag so others can use it
state["vmName"] = s.vmName
state.Put("vmName", s.vmName)
return multistep.ActionContinue
}
func (s *stepCreateVM) Cleanup(state map[string]interface{}) {
func (s *stepCreateVM) Cleanup(state multistep.StateBag) {
if s.vmName == "" {
return
}
driver := state["driver"].(Driver)
ui := state["ui"].(packer.Ui)
driver := state.Get("driver").(Driver)
ui := state.Get("ui").(packer.Ui)
ui.Say("Unregistering and deleting virtual machine...")
if err := driver.VBoxManage("unregistervm", s.vmName, "--delete"); err != nil {
@@ -29,16 +29,16 @@ type guestAdditionsUrlTemplate struct {
// guest_additions_path string - Path to the guest additions.
type stepDownloadGuestAdditions struct{}
func (s *stepDownloadGuestAdditions) Run(state map[string]interface{}) multistep.StepAction {
func (s *stepDownloadGuestAdditions) Run(state multistep.StateBag) multistep.StepAction {
var action multistep.StepAction
driver := state["driver"].(Driver)
ui := state["ui"].(packer.Ui)
config := state["config"].(*config)
driver := state.Get("driver").(Driver)
ui := state.Get("ui").(packer.Ui)
config := state.Get("config").(*config)
// Get VBox version
version, err := driver.Version()
if err != nil {
state["error"] = fmt.Errorf("Error reading version for guest additions download: %s", err)
state.Put("error", fmt.Errorf("Error reading version for guest additions download: %s", err))
return multistep.ActionHalt
}
@@ -71,7 +71,7 @@ func (s *stepDownloadGuestAdditions) Run(state map[string]interface{}) multistep
url, err = config.tpl.Process(url, tplData)
if err != nil {
err := fmt.Errorf("Error preparing guest additions url: %s", err)
state["error"] = err
state.Put("error", err)
ui.Error(err.Error())
return multistep.ActionHalt
}
@@ -85,7 +85,7 @@ func (s *stepDownloadGuestAdditions) Run(state map[string]interface{}) multistep
url, err = common.DownloadableURL(url)
if err != nil {
err := fmt.Errorf("Error preparing guest additions url: %s", err)
state["error"] = err
state.Put("error", err)
ui.Error(err.Error())
return multistep.ActionHalt
}
@@ -103,9 +103,9 @@ func (s *stepDownloadGuestAdditions) Run(state map[string]interface{}) multistep
return downStep.Run(state)
}
func (s *stepDownloadGuestAdditions) Cleanup(state map[string]interface{}) {}
func (s *stepDownloadGuestAdditions) Cleanup(state multistep.StateBag) {}
func (s *stepDownloadGuestAdditions) downloadAdditionsSHA256(state map[string]interface{}, additionsVersion string, additionsName string) (string, multistep.StepAction) {
func (s *stepDownloadGuestAdditions) downloadAdditionsSHA256(state multistep.StateBag, additionsVersion string, additionsName string) (string, multistep.StepAction) {
// First things first, we get the list of checksums for the files available
// for this version.
checksumsUrl := fmt.Sprintf(
@@ -114,9 +114,9 @@ func (s *stepDownloadGuestAdditions) downloadAdditionsSHA256(state map[string]in
checksumsFile, err := ioutil.TempFile("", "packer")
if err != nil {
state["error"] = fmt.Errorf(
state.Put("error", fmt.Errorf(
"Failed creating temporary file to store guest addition checksums: %s",
err)
err))
return "", multistep.ActionHalt
}
defer os.Remove(checksumsFile.Name())
@@ -136,9 +136,9 @@ func (s *stepDownloadGuestAdditions) downloadAdditionsSHA256(state map[string]in
// Next, we find the checksum for the file we're looking to download.
// It is an error if the checksum cannot be found.
checksumsF, err := os.Open(state["guest_additions_checksums_path"].(string))
checksumsF, err := os.Open(state.Get("guest_additions_checksums_path").(string))
if err != nil {
state["error"] = fmt.Errorf("Error opening guest addition checksums: %s", err)
state.Put("error", fmt.Errorf("Error opening guest addition checksums: %s", err))
return "", multistep.ActionHalt
}
defer checksumsF.Close()
@@ -166,8 +166,8 @@ func (s *stepDownloadGuestAdditions) downloadAdditionsSHA256(state map[string]in
}
if checksum == "" {
state["error"] = fmt.Errorf(
"The checksum for the file '%s' could not be found.", additionsName)
state.Put("error", fmt.Errorf(
"The checksum for the file '%s' could not be found.", additionsName))
return "", multistep.ActionHalt
}
+12 -12
View File
@@ -15,25 +15,25 @@ import (
// exportPath string - The path to the resulting export.
type stepExport struct{}
func (s *stepExport) Run(state map[string]interface{}) multistep.StepAction {
config := state["config"].(*config)
driver := state["driver"].(Driver)
ui := state["ui"].(packer.Ui)
vmName := state["vmName"].(string)
func (s *stepExport) Run(state multistep.StateBag) multistep.StepAction {
config := state.Get("config").(*config)
driver := state.Get("driver").(Driver)
ui := state.Get("ui").(packer.Ui)
vmName := state.Get("vmName").(string)
// Clear out the Packer-created forwarding rule
ui.Say("Preparing to export machine...")
ui.Message(fmt.Sprintf("Deleting forwarded port mapping for SSH (host port %d)", state["sshHostPort"]))
ui.Message(fmt.Sprintf("Deleting forwarded port mapping for SSH (host port %d)", state.Get("sshHostPort")))
command := []string{"modifyvm", vmName, "--natpf1", "delete", "packerssh"}
if err := driver.VBoxManage(command...); err != nil {
err := fmt.Errorf("Error deleting port forwarding rule: %s", err)
state["error"] = err
state.Put("error", err)
ui.Error(err.Error())
return multistep.ActionHalt
}
// Remove the attached floppy disk, if it exists
if _, ok := state["floppy_path"]; ok {
if _, ok := state.GetOk("floppy_path"); ok {
ui.Message("Removing floppy drive...")
command := []string{
"storageattach", vmName,
@@ -43,7 +43,7 @@ func (s *stepExport) Run(state map[string]interface{}) multistep.StepAction {
"--medium", "none",
}
if err := driver.VBoxManage(command...); err != nil {
state["error"] = fmt.Errorf("Error removing floppy: %s", err)
state.Put("error", fmt.Errorf("Error removing floppy: %s", err))
return multistep.ActionHalt
}
@@ -63,14 +63,14 @@ func (s *stepExport) Run(state map[string]interface{}) multistep.StepAction {
err := driver.VBoxManage(command...)
if err != nil {
err := fmt.Errorf("Error exporting virtual machine: %s", err)
state["error"] = err
state.Put("error", err)
ui.Error(err.Error())
return multistep.ActionHalt
}
state["exportPath"] = outputPath
state.Put("exportPath", outputPath)
return multistep.ActionContinue
}
func (s *stepExport) Cleanup(state map[string]interface{}) {}
func (s *stepExport) Cleanup(state multistep.StateBag) {}
+8 -8
View File
@@ -17,11 +17,11 @@ import (
// Produces:
type stepForwardSSH struct{}
func (s *stepForwardSSH) Run(state map[string]interface{}) multistep.StepAction {
config := state["config"].(*config)
driver := state["driver"].(Driver)
ui := state["ui"].(packer.Ui)
vmName := state["vmName"].(string)
func (s *stepForwardSSH) Run(state multistep.StateBag) multistep.StepAction {
config := state.Get("config").(*config)
driver := state.Get("driver").(Driver)
ui := state.Get("ui").(packer.Ui)
vmName := state.Get("vmName").(string)
log.Printf("Looking for available SSH port between %d and %d", config.SSHHostPortMin, config.SSHHostPortMax)
var sshHostPort uint
@@ -45,15 +45,15 @@ func (s *stepForwardSSH) Run(state map[string]interface{}) multistep.StepAction
}
if err := driver.VBoxManage(command...); err != nil {
err := fmt.Errorf("Error creating port forwarding rule: %s", err)
state["error"] = err
state.Put("error", err)
ui.Error(err.Error())
return multistep.ActionHalt
}
// Save the port we're using so that future steps can use it
state["sshHostPort"] = sshHostPort
state.Put("sshHostPort", sshHostPort)
return multistep.ActionContinue
}
func (s *stepForwardSSH) Cleanup(state map[string]interface{}) {}
func (s *stepForwardSSH) Cleanup(state multistep.StateBag) {}
+6 -6
View File
@@ -23,13 +23,13 @@ type stepHTTPServer struct {
l net.Listener
}
func (s *stepHTTPServer) Run(state map[string]interface{}) multistep.StepAction {
config := state["config"].(*config)
ui := state["ui"].(packer.Ui)
func (s *stepHTTPServer) Run(state multistep.StateBag) multistep.StepAction {
config := state.Get("config").(*config)
ui := state.Get("ui").(packer.Ui)
var httpPort uint = 0
if config.HTTPDir == "" {
state["http_port"] = httpPort
state.Put("http_port", httpPort)
return multistep.ActionContinue
}
@@ -62,12 +62,12 @@ func (s *stepHTTPServer) Run(state map[string]interface{}) multistep.StepAction
go server.Serve(s.l)
// Save the address into the state so it can be accessed in the future
state["http_port"] = httpPort
state.Put("http_port", httpPort)
return multistep.ActionContinue
}
func (s *stepHTTPServer) Cleanup(map[string]interface{}) {
func (s *stepHTTPServer) Cleanup(multistep.StateBag) {
if s.l != nil {
// Close the listener so that the HTTP server stops
s.l.Close()
@@ -10,9 +10,9 @@ import (
type stepPrepareOutputDir struct{}
func (stepPrepareOutputDir) Run(state map[string]interface{}) multistep.StepAction {
config := state["config"].(*config)
ui := state["ui"].(packer.Ui)
func (stepPrepareOutputDir) Run(state multistep.StateBag) multistep.StepAction {
config := state.Get("config").(*config)
ui := state.Get("ui").(packer.Ui)
if _, err := os.Stat(config.OutputDir); err == nil && config.PackerForce {
ui.Say("Deleting previous output directory...")
@@ -20,20 +20,20 @@ func (stepPrepareOutputDir) Run(state map[string]interface{}) multistep.StepActi
}
if err := os.MkdirAll(config.OutputDir, 0755); err != nil {
state["error"] = err
state.Put("error", err)
return multistep.ActionHalt
}
return multistep.ActionContinue
}
func (stepPrepareOutputDir) Cleanup(state map[string]interface{}) {
_, cancelled := state[multistep.StateCancelled]
_, halted := state[multistep.StateHalted]
func (stepPrepareOutputDir) Cleanup(state multistep.StateBag) {
_, cancelled := state.GetOk(multistep.StateCancelled)
_, halted := state.GetOk(multistep.StateHalted)
if cancelled || halted {
config := state["config"].(*config)
ui := state["ui"].(packer.Ui)
config := state.Get("config").(*config)
ui := state.Get("ui").(packer.Ui)
ui.Say("Deleting output directory...")
for i := 0; i < 5; i++ {
+9 -9
View File
@@ -16,11 +16,11 @@ type stepRun struct {
vmName string
}
func (s *stepRun) Run(state map[string]interface{}) multistep.StepAction {
config := state["config"].(*config)
driver := state["driver"].(Driver)
ui := state["ui"].(packer.Ui)
vmName := state["vmName"].(string)
func (s *stepRun) Run(state multistep.StateBag) multistep.StepAction {
config := state.Get("config").(*config)
driver := state.Get("driver").(Driver)
ui := state.Get("ui").(packer.Ui)
vmName := state.Get("vmName").(string)
ui.Say("Starting the virtual machine...")
guiArgument := "gui"
@@ -33,7 +33,7 @@ func (s *stepRun) Run(state map[string]interface{}) multistep.StepAction {
command := []string{"startvm", vmName, "--type", guiArgument}
if err := driver.VBoxManage(command...); err != nil {
err := fmt.Errorf("Error starting VM: %s", err)
state["error"] = err
state.Put("error", err)
ui.Error(err.Error())
return multistep.ActionHalt
}
@@ -48,13 +48,13 @@ func (s *stepRun) Run(state map[string]interface{}) multistep.StepAction {
return multistep.ActionContinue
}
func (s *stepRun) Cleanup(state map[string]interface{}) {
func (s *stepRun) Cleanup(state multistep.StateBag) {
if s.vmName == "" {
return
}
driver := state["driver"].(Driver)
ui := state["ui"].(packer.Ui)
driver := state.Get("driver").(Driver)
ui := state.Get("ui").(packer.Ui)
if running, _ := driver.IsRunning(s.vmName); running {
if err := driver.VBoxManage("controlvm", s.vmName, "poweroff"); err != nil {
+10 -10
View File
@@ -23,12 +23,12 @@ import (
// <nothing>
type stepShutdown struct{}
func (s *stepShutdown) Run(state map[string]interface{}) multistep.StepAction {
comm := state["communicator"].(packer.Communicator)
config := state["config"].(*config)
driver := state["driver"].(Driver)
ui := state["ui"].(packer.Ui)
vmName := state["vmName"].(string)
func (s *stepShutdown) Run(state multistep.StateBag) multistep.StepAction {
comm := state.Get("communicator").(packer.Communicator)
config := state.Get("config").(*config)
driver := state.Get("driver").(Driver)
ui := state.Get("ui").(packer.Ui)
vmName := state.Get("vmName").(string)
if config.ShutdownCommand != "" {
ui.Say("Gracefully halting virtual machine...")
@@ -36,7 +36,7 @@ func (s *stepShutdown) Run(state map[string]interface{}) multistep.StepAction {
cmd := &packer.RemoteCmd{Command: config.ShutdownCommand}
if err := cmd.StartWithUi(comm, ui); err != nil {
err := fmt.Errorf("Failed to send shutdown command: %s", err)
state["error"] = err
state.Put("error", err)
ui.Error(err.Error())
return multistep.ActionHalt
}
@@ -53,7 +53,7 @@ func (s *stepShutdown) Run(state map[string]interface{}) multistep.StepAction {
select {
case <-shutdownTimer:
err := errors.New("Timeout while waiting for machine to shut down.")
state["error"] = err
state.Put("error", err)
ui.Error(err.Error())
return multistep.ActionHalt
default:
@@ -64,7 +64,7 @@ func (s *stepShutdown) Run(state map[string]interface{}) multistep.StepAction {
ui.Say("Halting the virtual machine...")
if err := driver.Stop(vmName); err != nil {
err := fmt.Errorf("Error stopping VM: %s", err)
state["error"] = err
state.Put("error", err)
ui.Error(err.Error())
return multistep.ActionHalt
}
@@ -74,4 +74,4 @@ func (s *stepShutdown) Run(state map[string]interface{}) multistep.StepAction {
return multistep.ActionContinue
}
func (s *stepShutdown) Cleanup(state map[string]interface{}) {}
func (s *stepShutdown) Cleanup(state multistep.StateBag) {}
+5 -5
View File
@@ -11,14 +11,14 @@ import (
// pop-up messages don't exist.
type stepSuppressMessages struct{}
func (stepSuppressMessages) Run(state map[string]interface{}) multistep.StepAction {
driver := state["driver"].(Driver)
ui := state["ui"].(packer.Ui)
func (stepSuppressMessages) Run(state multistep.StateBag) multistep.StepAction {
driver := state.Get("driver").(Driver)
ui := state.Get("ui").(packer.Ui)
log.Println("Suppressing annoying messages in VirtualBox")
if err := driver.SuppressMessages(); err != nil {
err := fmt.Errorf("Error configuring VirtualBox to suppress messages: %s", err)
state["error"] = err
state.Put("error", err)
ui.Error(err.Error())
return multistep.ActionHalt
}
@@ -26,4 +26,4 @@ func (stepSuppressMessages) Run(state map[string]interface{}) multistep.StepActi
return multistep.ActionContinue
}
func (stepSuppressMessages) Cleanup(map[string]interface{}) {}
func (stepSuppressMessages) Cleanup(multistep.StateBag) {}
+10 -10
View File
@@ -32,12 +32,12 @@ type bootCommandTemplateData struct {
// <nothing>
type stepTypeBootCommand struct{}
func (s *stepTypeBootCommand) Run(state map[string]interface{}) multistep.StepAction {
config := state["config"].(*config)
driver := state["driver"].(Driver)
httpPort := state["http_port"].(uint)
ui := state["ui"].(packer.Ui)
vmName := state["vmName"].(string)
func (s *stepTypeBootCommand) Run(state multistep.StateBag) multistep.StepAction {
config := state.Get("config").(*config)
driver := state.Get("driver").(Driver)
httpPort := state.Get("http_port").(uint)
ui := state.Get("ui").(packer.Ui)
vmName := state.Get("vmName").(string)
tplData := &bootCommandTemplateData{
"10.0.2.2",
@@ -50,7 +50,7 @@ func (s *stepTypeBootCommand) Run(state map[string]interface{}) multistep.StepAc
command, err := config.tpl.Process(command, tplData)
if err != nil {
err := fmt.Errorf("Error preparing boot command: %s", err)
state["error"] = err
state.Put("error", err)
ui.Error(err.Error())
return multistep.ActionHalt
}
@@ -73,13 +73,13 @@ func (s *stepTypeBootCommand) Run(state map[string]interface{}) multistep.StepAc
// Since typing is sometimes so slow, we check for an interrupt
// in between each character.
if _, ok := state[multistep.StateCancelled]; ok {
if _, ok := state.GetOk(multistep.StateCancelled); ok {
return multistep.ActionHalt
}
if err := driver.VBoxManage("controlvm", vmName, "keyboardputscancode", code); err != nil {
err := fmt.Errorf("Error sending boot command: %s", err)
state["error"] = err
state.Put("error", err)
ui.Error(err.Error())
return multistep.ActionHalt
}
@@ -89,7 +89,7 @@ func (s *stepTypeBootCommand) Run(state map[string]interface{}) multistep.StepAc
return multistep.ActionContinue
}
func (*stepTypeBootCommand) Cleanup(map[string]interface{}) {}
func (*stepTypeBootCommand) Cleanup(multistep.StateBag) {}
func scancodes(message string) []string {
special := make(map[string][]string)
@@ -14,22 +14,22 @@ type guestAdditionsPathTemplate struct {
// This step uploads the guest additions ISO to the VM.
type stepUploadGuestAdditions struct{}
func (s *stepUploadGuestAdditions) Run(state map[string]interface{}) multistep.StepAction {
comm := state["communicator"].(packer.Communicator)
config := state["config"].(*config)
driver := state["driver"].(Driver)
guestAdditionsPath := state["guest_additions_path"].(string)
ui := state["ui"].(packer.Ui)
func (s *stepUploadGuestAdditions) Run(state multistep.StateBag) multistep.StepAction {
comm := state.Get("communicator").(packer.Communicator)
config := state.Get("config").(*config)
driver := state.Get("driver").(Driver)
guestAdditionsPath := state.Get("guest_additions_path").(string)
ui := state.Get("ui").(packer.Ui)
version, err := driver.Version()
if err != nil {
state["error"] = fmt.Errorf("Error reading version for guest additions upload: %s", err)
state.Put("error", fmt.Errorf("Error reading version for guest additions upload: %s", err))
return multistep.ActionHalt
}
f, err := os.Open(guestAdditionsPath)
if err != nil {
state["error"] = fmt.Errorf("Error opening guest additions ISO: %s", err)
state.Put("error", fmt.Errorf("Error opening guest additions ISO: %s", err))
return multistep.ActionHalt
}
@@ -40,18 +40,18 @@ func (s *stepUploadGuestAdditions) Run(state map[string]interface{}) multistep.S
config.GuestAdditionsPath, err = config.tpl.Process(config.GuestAdditionsPath, tplData)
if err != nil {
err := fmt.Errorf("Error preparing guest additions path: %s", err)
state["error"] = err
state.Put("error", err)
ui.Error(err.Error())
return multistep.ActionHalt
}
ui.Say("Uploading VirtualBox guest additions ISO...")
if err := comm.Upload(config.GuestAdditionsPath, f); err != nil {
state["error"] = fmt.Errorf("Error uploading guest additions: %s", err)
state.Put("error", fmt.Errorf("Error uploading guest additions: %s", err))
return multistep.ActionHalt
}
return multistep.ActionContinue
}
func (s *stepUploadGuestAdditions) Cleanup(state map[string]interface{}) {}
func (s *stepUploadGuestAdditions) Cleanup(state multistep.StateBag) {}
+8 -8
View File
@@ -12,11 +12,11 @@ import (
// can be useful for various provisioning reasons.
type stepUploadVersion struct{}
func (s *stepUploadVersion) Run(state map[string]interface{}) multistep.StepAction {
comm := state["communicator"].(packer.Communicator)
config := state["config"].(*config)
driver := state["driver"].(Driver)
ui := state["ui"].(packer.Ui)
func (s *stepUploadVersion) Run(state multistep.StateBag) multistep.StepAction {
comm := state.Get("communicator").(packer.Communicator)
config := state.Get("config").(*config)
driver := state.Get("driver").(Driver)
ui := state.Get("ui").(packer.Ui)
if config.VBoxVersionFile == "" {
log.Println("VBoxVersionFile is empty. Not uploading.")
@@ -25,7 +25,7 @@ func (s *stepUploadVersion) Run(state map[string]interface{}) multistep.StepActi
version, err := driver.Version()
if err != nil {
state["error"] = fmt.Errorf("Error reading version for metadata upload: %s", err)
state.Put("error", fmt.Errorf("Error reading version for metadata upload: %s", err))
return multistep.ActionHalt
}
@@ -33,11 +33,11 @@ func (s *stepUploadVersion) Run(state map[string]interface{}) multistep.StepActi
var data bytes.Buffer
data.WriteString(version)
if err := comm.Upload(config.VBoxVersionFile, &data); err != nil {
state["error"] = fmt.Errorf("Error uploading VirtualBox version: %s", err)
state.Put("error", fmt.Errorf("Error uploading VirtualBox version: %s", err))
return multistep.ActionHalt
}
return multistep.ActionContinue
}
func (s *stepUploadVersion) Cleanup(state map[string]interface{}) {}
func (s *stepUploadVersion) Cleanup(state multistep.StateBag) {}
+8 -8
View File
@@ -19,11 +19,11 @@ type commandTemplate struct {
// Produces:
type stepVBoxManage struct{}
func (s *stepVBoxManage) Run(state map[string]interface{}) multistep.StepAction {
config := state["config"].(*config)
driver := state["driver"].(Driver)
ui := state["ui"].(packer.Ui)
vmName := state["vmName"].(string)
func (s *stepVBoxManage) Run(state multistep.StateBag) multistep.StepAction {
config := state.Get("config").(*config)
driver := state.Get("driver").(Driver)
ui := state.Get("ui").(packer.Ui)
vmName := state.Get("vmName").(string)
if len(config.VBoxManage) > 0 {
ui.Say("Executing custom VBoxManage commands...")
@@ -42,7 +42,7 @@ func (s *stepVBoxManage) Run(state map[string]interface{}) multistep.StepAction
command[i], err = config.tpl.Process(arg, tplData)
if err != nil {
err := fmt.Errorf("Error preparing vboxmanage command: %s", err)
state["error"] = err
state.Put("error", err)
ui.Error(err.Error())
return multistep.ActionHalt
}
@@ -51,7 +51,7 @@ func (s *stepVBoxManage) Run(state map[string]interface{}) multistep.StepAction
ui.Message(fmt.Sprintf("Executing: %s", strings.Join(command, " ")))
if err := driver.VBoxManage(command...); err != nil {
err := fmt.Errorf("Error executing command: %s", err)
state["error"] = err
state.Put("error", err)
ui.Error(err.Error())
return multistep.ActionHalt
}
@@ -60,4 +60,4 @@ func (s *stepVBoxManage) Run(state map[string]interface{}) multistep.StepAction
return multistep.ActionContinue
}
func (s *stepVBoxManage) Cleanup(state map[string]interface{}) {}
func (s *stepVBoxManage) Cleanup(state multistep.StateBag) {}
+9 -9
View File
@@ -376,12 +376,12 @@ func (b *Builder) Run(ui packer.Ui, hook packer.Hook, cache packer.Cache) (packe
}
// Setup the state bag
state := make(map[string]interface{})
state["cache"] = cache
state["config"] = &b.config
state["driver"] = driver
state["hook"] = hook
state["ui"] = ui
state := new(multistep.BasicStateBag)
state.Put("cache", cache)
state.Put("config", &b.config)
state.Put("driver", driver)
state.Put("hook", hook)
state.Put("ui", ui)
// Run!
if b.config.PackerDebug {
@@ -396,16 +396,16 @@ func (b *Builder) Run(ui packer.Ui, hook packer.Hook, cache packer.Cache) (packe
b.runner.Run(state)
// If there was an error, return that
if rawErr, ok := state["error"]; ok {
if rawErr, ok := state.GetOk("error"); ok {
return nil, rawErr.(error)
}
// If we were interrupted or cancelled, then just exit.
if _, ok := state[multistep.StateCancelled]; ok {
if _, ok := state.GetOk(multistep.StateCancelled); ok {
return nil, errors.New("Build was cancelled.")
}
if _, ok := state[multistep.StateHalted]; ok {
if _, ok := state.GetOk(multistep.StateHalted); ok {
return nil, errors.New("Build was halted.")
}
+7 -6
View File
@@ -4,16 +4,17 @@ import (
gossh "code.google.com/p/go.crypto/ssh"
"errors"
"fmt"
"github.com/mitchellh/multistep"
"github.com/mitchellh/packer/communicator/ssh"
"io/ioutil"
"log"
"os"
)
func sshAddress(state map[string]interface{}) (string, error) {
config := state["config"].(*config)
driver := state["driver"].(Driver)
vmxPath := state["vmx_path"].(string)
func sshAddress(state multistep.StateBag) (string, error) {
config := state.Get("config").(*config)
driver := state.Get("driver").(Driver)
vmxPath := state.Get("vmx_path").(string)
log.Println("Lookup up IP information...")
f, err := os.Open(vmxPath)
@@ -58,8 +59,8 @@ func sshAddress(state map[string]interface{}) (string, error) {
return fmt.Sprintf("%s:%d", ipAddress, config.SSHPort), nil
}
func sshConfig(state map[string]interface{}) (*gossh.ClientConfig, error) {
config := state["config"].(*config)
func sshConfig(state multistep.StateBag) (*gossh.ClientConfig, error) {
config := state.Get("config").(*config)
auth := []gossh.ClientAuth{
gossh.ClientAuthPassword(ssh.Password(config.SSHPassword)),
+5 -5
View File
@@ -23,9 +23,9 @@ var KeepFileExtensions = []string{".nvram", ".vmdk", ".vmsd", ".vmx", ".vmxf"}
// <nothing>
type stepCleanFiles struct{}
func (stepCleanFiles) Run(state map[string]interface{}) multistep.StepAction {
config := state["config"].(*config)
ui := state["ui"].(packer.Ui)
func (stepCleanFiles) Run(state multistep.StateBag) multistep.StepAction {
config := state.Get("config").(*config)
ui := state.Get("ui").(packer.Ui)
ui.Say("Deleting unnecessary VMware files...")
visit := func(path string, info os.FileInfo, err error) error {
@@ -55,11 +55,11 @@ func (stepCleanFiles) Run(state map[string]interface{}) multistep.StepAction {
}
if err := filepath.Walk(config.OutputDir, visit); err != nil {
state["error"] = err
state.Put("error", err)
return multistep.ActionHalt
}
return multistep.ActionContinue
}
func (stepCleanFiles) Cleanup(map[string]interface{}) {}
func (stepCleanFiles) Cleanup(multistep.StateBag) {}
+9 -9
View File
@@ -22,20 +22,20 @@ import (
// <nothing>
type stepCleanVMX struct{}
func (s stepCleanVMX) Run(state map[string]interface{}) multistep.StepAction {
isoPath := state["iso_path"].(string)
ui := state["ui"].(packer.Ui)
vmxPath := state["vmx_path"].(string)
func (s stepCleanVMX) Run(state multistep.StateBag) multistep.StepAction {
isoPath := state.Get("iso_path").(string)
ui := state.Get("ui").(packer.Ui)
vmxPath := state.Get("vmx_path").(string)
ui.Say("Cleaning VMX prior to finishing up...")
vmxData, err := s.readVMX(vmxPath)
if err != nil {
state["error"] = fmt.Errorf("Error reading VMX: %s", err)
state.Put("error", fmt.Errorf("Error reading VMX: %s", err))
return multistep.ActionHalt
}
if _, ok := state["floppy_path"]; ok {
if _, ok := state.GetOk("floppy_path"); ok {
// Delete the floppy0 entries so the floppy is no longer mounted
ui.Message("Unmounting floppy from VMX...")
for k, _ := range vmxData {
@@ -47,7 +47,7 @@ func (s stepCleanVMX) Run(state map[string]interface{}) multistep.StepAction {
vmxData["floppy0.present"] = "FALSE"
}
ui.Message("Detatching ISO from CD-ROM device...")
ui.Message("Detaching ISO from CD-ROM device...")
devRe := regexp.MustCompile(`^ide\d:\d\.`)
for k, _ := range vmxData {
match := devRe.FindString(k)
@@ -67,14 +67,14 @@ func (s stepCleanVMX) Run(state map[string]interface{}) multistep.StepAction {
// Rewrite the VMX
if err := WriteVMX(vmxPath, vmxData); err != nil {
state["error"] = fmt.Errorf("Error writing VMX: %s", err)
state.Put("error", fmt.Errorf("Error writing VMX: %s", err))
return multistep.ActionHalt
}
return multistep.ActionContinue
}
func (stepCleanVMX) Cleanup(map[string]interface{}) {}
func (stepCleanVMX) Cleanup(multistep.StateBag) {}
func (stepCleanVMX) readVMX(vmxPath string) (map[string]string, error) {
vmxF, err := os.Open(vmxPath)
+7 -7
View File
@@ -20,11 +20,11 @@ import (
// <nothing>
type stepCompactDisk struct{}
func (stepCompactDisk) Run(state map[string]interface{}) multistep.StepAction {
config := state["config"].(*config)
driver := state["driver"].(Driver)
ui := state["ui"].(packer.Ui)
full_disk_path := state["full_disk_path"].(string)
func (stepCompactDisk) Run(state multistep.StateBag) multistep.StepAction {
config := state.Get("config").(*config)
driver := state.Get("driver").(Driver)
ui := state.Get("ui").(packer.Ui)
full_disk_path := state.Get("full_disk_path").(string)
if config.SkipCompaction == true {
log.Println("Skipping disk compaction step...")
@@ -33,11 +33,11 @@ func (stepCompactDisk) Run(state map[string]interface{}) multistep.StepAction {
ui.Say("Compacting the disk image")
if err := driver.CompactDisk(full_disk_path); err != nil {
state["error"] = fmt.Errorf("Error compacting disk: %s", err)
state.Put("error", fmt.Errorf("Error compacting disk: %s", err))
return multistep.ActionHalt
}
return multistep.ActionContinue
}
func (stepCompactDisk) Cleanup(map[string]interface{}) {}
func (stepCompactDisk) Cleanup(multistep.StateBag) {}
+9 -9
View File
@@ -22,15 +22,15 @@ import (
// vnc_port uint - The port that VNC is configured to listen on.
type stepConfigureVNC struct{}
func (stepConfigureVNC) Run(state map[string]interface{}) multistep.StepAction {
config := state["config"].(*config)
ui := state["ui"].(packer.Ui)
vmxPath := state["vmx_path"].(string)
func (stepConfigureVNC) Run(state multistep.StateBag) multistep.StepAction {
config := state.Get("config").(*config)
ui := state.Get("ui").(packer.Ui)
vmxPath := state.Get("vmx_path").(string)
f, err := os.Open(vmxPath)
if err != nil {
err := fmt.Errorf("Error reading VMX data: %s", err)
state["error"] = err
state.Put("error", err)
ui.Error(err.Error())
return multistep.ActionHalt
}
@@ -38,7 +38,7 @@ func (stepConfigureVNC) Run(state map[string]interface{}) multistep.StepAction {
vmxBytes, err := ioutil.ReadAll(f)
if err != nil {
err := fmt.Errorf("Error reading VMX data: %s", err)
state["error"] = err
state.Put("error", err)
ui.Error(err.Error())
return multistep.ActionHalt
}
@@ -67,15 +67,15 @@ func (stepConfigureVNC) Run(state map[string]interface{}) multistep.StepAction {
if err := WriteVMX(vmxPath, vmxData); err != nil {
err := fmt.Errorf("Error writing VMX data: %s", err)
state["error"] = err
state.Put("error", err)
ui.Error(err.Error())
return multistep.ActionHalt
}
state["vnc_port"] = vncPort
state.Put("vnc_port", vncPort)
return multistep.ActionContinue
}
func (stepConfigureVNC) Cleanup(map[string]interface{}) {
func (stepConfigureVNC) Cleanup(multistep.StateBag) {
}
+7 -7
View File
@@ -18,23 +18,23 @@ import (
// full_disk_path (string) - The full path to the created disk.
type stepCreateDisk struct{}
func (stepCreateDisk) Run(state map[string]interface{}) multistep.StepAction {
config := state["config"].(*config)
driver := state["driver"].(Driver)
ui := state["ui"].(packer.Ui)
func (stepCreateDisk) Run(state multistep.StateBag) multistep.StepAction {
config := state.Get("config").(*config)
driver := state.Get("driver").(Driver)
ui := state.Get("ui").(packer.Ui)
ui.Say("Creating virtual machine disk")
full_disk_path := filepath.Join(config.OutputDir, config.DiskName+".vmdk")
if err := driver.CreateDisk(full_disk_path, fmt.Sprintf("%dM", config.DiskSize), config.DiskTypeId); err != nil {
err := fmt.Errorf("Error creating disk: %s", err)
state["error"] = err
state.Put("error", err)
ui.Error(err.Error())
return multistep.ActionHalt
}
state["full_disk_path"] = full_disk_path
state.Put("full_disk_path", full_disk_path)
return multistep.ActionContinue
}
func (stepCreateDisk) Cleanup(map[string]interface{}) {}
func (stepCreateDisk) Cleanup(multistep.StateBag) {}
+11 -11
View File
@@ -28,10 +28,10 @@ type vmxTemplateData struct {
// vmx_path string - The path to the VMX file.
type stepCreateVMX struct{}
func (stepCreateVMX) Run(state map[string]interface{}) multistep.StepAction {
config := state["config"].(*config)
isoPath := state["iso_path"].(string)
ui := state["ui"].(packer.Ui)
func (stepCreateVMX) Run(state multistep.StateBag) multistep.StepAction {
config := state.Get("config").(*config)
isoPath := state.Get("iso_path").(string)
ui := state.Get("ui").(packer.Ui)
ui.Say("Building and writing VMX file")
@@ -47,7 +47,7 @@ func (stepCreateVMX) Run(state map[string]interface{}) multistep.StepAction {
f, err := os.Open(config.VMXTemplatePath)
if err != nil {
err := fmt.Errorf("Error reading VMX template: %s", err)
state["error"] = err
state.Put("error", err)
ui.Error(err.Error())
return multistep.ActionHalt
}
@@ -56,7 +56,7 @@ func (stepCreateVMX) Run(state map[string]interface{}) multistep.StepAction {
rawBytes, err := ioutil.ReadAll(f)
if err != nil {
err := fmt.Errorf("Error reading VMX template: %s", err)
state["error"] = err
state.Put("error", err)
ui.Error(err.Error())
return multistep.ActionHalt
}
@@ -67,7 +67,7 @@ func (stepCreateVMX) Run(state map[string]interface{}) multistep.StepAction {
vmxContents, err := config.tpl.Process(vmxTemplate, tplData)
if err != nil {
err := fmt.Errorf("Error procesing VMX template: %s", err)
state["error"] = err
state.Put("error", err)
ui.Error(err.Error())
return multistep.ActionHalt
}
@@ -81,7 +81,7 @@ func (stepCreateVMX) Run(state map[string]interface{}) multistep.StepAction {
}
}
if floppyPathRaw, ok := state["floppy_path"]; ok {
if floppyPathRaw, ok := state.GetOk("floppy_path"); ok {
log.Println("Floppy path present, setting in VMX")
vmxData["floppy0.present"] = "TRUE"
vmxData["floppy0.fileType"] = "file"
@@ -91,17 +91,17 @@ func (stepCreateVMX) Run(state map[string]interface{}) multistep.StepAction {
vmxPath := filepath.Join(config.OutputDir, config.VMName+".vmx")
if err := WriteVMX(vmxPath, vmxData); err != nil {
err := fmt.Errorf("Error creating VMX file: %s", err)
state["error"] = err
state.Put("error", err)
ui.Error(err.Error())
return multistep.ActionHalt
}
state["vmx_path"] = vmxPath
state.Put("vmx_path", vmxPath)
return multistep.ActionContinue
}
func (stepCreateVMX) Cleanup(map[string]interface{}) {
func (stepCreateVMX) Cleanup(multistep.StateBag) {
}
// This is the default VMX template used if no other template is given.
+6 -6
View File
@@ -23,13 +23,13 @@ type stepHTTPServer struct {
l net.Listener
}
func (s *stepHTTPServer) Run(state map[string]interface{}) multistep.StepAction {
config := state["config"].(*config)
ui := state["ui"].(packer.Ui)
func (s *stepHTTPServer) Run(state multistep.StateBag) multistep.StepAction {
config := state.Get("config").(*config)
ui := state.Get("ui").(packer.Ui)
var httpPort uint = 0
if config.HTTPDir == "" {
state["http_port"] = httpPort
state.Put("http_port", httpPort)
return multistep.ActionContinue
}
@@ -62,12 +62,12 @@ func (s *stepHTTPServer) Run(state map[string]interface{}) multistep.StepAction
go server.Serve(s.l)
// Save the address into the state so it can be accessed in the future
state["http_port"] = httpPort
state.Put("http_port", httpPort)
return multistep.ActionContinue
}
func (s *stepHTTPServer) Cleanup(map[string]interface{}) {
func (s *stepHTTPServer) Cleanup(multistep.StateBag) {
if s.l != nil {
// Close the listener so that the HTTP server stops
s.l.Close()
+9 -9
View File
@@ -10,9 +10,9 @@ import (
type stepPrepareOutputDir struct{}
func (stepPrepareOutputDir) Run(state map[string]interface{}) multistep.StepAction {
config := state["config"].(*config)
ui := state["ui"].(packer.Ui)
func (stepPrepareOutputDir) Run(state multistep.StateBag) multistep.StepAction {
config := state.Get("config").(*config)
ui := state.Get("ui").(packer.Ui)
if _, err := os.Stat(config.OutputDir); err == nil && config.PackerForce {
ui.Say("Deleting previous output directory...")
@@ -20,20 +20,20 @@ func (stepPrepareOutputDir) Run(state map[string]interface{}) multistep.StepActi
}
if err := os.MkdirAll(config.OutputDir, 0755); err != nil {
state["error"] = err
state.Put("error", err)
return multistep.ActionHalt
}
return multistep.ActionContinue
}
func (stepPrepareOutputDir) Cleanup(state map[string]interface{}) {
_, cancelled := state[multistep.StateCancelled]
_, halted := state[multistep.StateHalted]
func (stepPrepareOutputDir) Cleanup(state multistep.StateBag) {
_, cancelled := state.GetOk(multistep.StateCancelled)
_, halted := state.GetOk(multistep.StateHalted)
if cancelled || halted {
config := state["config"].(*config)
ui := state["ui"].(packer.Ui)
config := state.Get("config").(*config)
ui := state.Get("ui").(packer.Ui)
ui.Say("Deleting output directory...")
for i := 0; i < 5; i++ {
+7 -7
View File
@@ -8,9 +8,9 @@ import (
type stepPrepareTools struct{}
func (*stepPrepareTools) Run(state map[string]interface{}) multistep.StepAction {
config := state["config"].(*config)
driver := state["driver"].(Driver)
func (*stepPrepareTools) Run(state multistep.StateBag) multistep.StepAction {
config := state.Get("config").(*config)
driver := state.Get("driver").(Driver)
if config.ToolsUploadFlavor == "" {
return multistep.ActionContinue
@@ -18,16 +18,16 @@ func (*stepPrepareTools) Run(state map[string]interface{}) multistep.StepAction
path := driver.ToolsIsoPath(config.ToolsUploadFlavor)
if _, err := os.Stat(path); err != nil {
state["error"] = fmt.Errorf(
state.Put("error", fmt.Errorf(
"Couldn't find VMware tools for '%s'! VMware often downloads these\n"+
"tools on-demand. However, to do this, you need to create a fake VM\n"+
"of the proper type then click the 'install tools' option in the\n"+
"VMware GUI.", config.ToolsUploadFlavor)
"VMware GUI.", config.ToolsUploadFlavor))
return multistep.ActionHalt
}
state["tools_upload_source"] = path
state.Put("tools_upload_source", path)
return multistep.ActionContinue
}
func (*stepPrepareTools) Cleanup(map[string]interface{}) {}
func (*stepPrepareTools) Cleanup(multistep.StateBag) {}
+10 -10
View File
@@ -22,12 +22,12 @@ type stepRun struct {
vmxPath string
}
func (s *stepRun) Run(state map[string]interface{}) multistep.StepAction {
config := state["config"].(*config)
driver := state["driver"].(Driver)
ui := state["ui"].(packer.Ui)
vmxPath := state["vmx_path"].(string)
vncPort := state["vnc_port"].(uint)
func (s *stepRun) Run(state multistep.StateBag) multistep.StepAction {
config := state.Get("config").(*config)
driver := state.Get("driver").(Driver)
ui := state.Get("ui").(packer.Ui)
vmxPath := state.Get("vmx_path").(string)
vncPort := state.Get("vnc_port").(uint)
// Set the VMX path so that we know we started the machine
s.bootTime = time.Now()
@@ -43,7 +43,7 @@ func (s *stepRun) Run(state map[string]interface{}) multistep.StepAction {
if err := driver.Start(vmxPath, config.Headless); err != nil {
err := fmt.Errorf("Error starting VM: %s", err)
state["error"] = err
state.Put("error", err)
ui.Error(err.Error())
return multistep.ActionHalt
}
@@ -57,9 +57,9 @@ func (s *stepRun) Run(state map[string]interface{}) multistep.StepAction {
return multistep.ActionContinue
}
func (s *stepRun) Cleanup(state map[string]interface{}) {
driver := state["driver"].(Driver)
ui := state["ui"].(packer.Ui)
func (s *stepRun) Cleanup(state multistep.StateBag) {
driver := state.Get("driver").(Driver)
ui := state.Get("ui").(packer.Ui)
// If we started the machine... stop it.
if s.vmxPath != "" {
+12 -12
View File
@@ -27,12 +27,12 @@ import (
// <nothing>
type stepShutdown struct{}
func (s *stepShutdown) Run(state map[string]interface{}) multistep.StepAction {
comm := state["communicator"].(packer.Communicator)
config := state["config"].(*config)
driver := state["driver"].(Driver)
ui := state["ui"].(packer.Ui)
vmxPath := state["vmx_path"].(string)
func (s *stepShutdown) Run(state multistep.StateBag) multistep.StepAction {
comm := state.Get("communicator").(packer.Communicator)
config := state.Get("config").(*config)
driver := state.Get("driver").(Driver)
ui := state.Get("ui").(packer.Ui)
vmxPath := state.Get("vmx_path").(string)
if config.ShutdownCommand != "" {
ui.Say("Gracefully halting virtual machine...")
@@ -46,7 +46,7 @@ func (s *stepShutdown) Run(state map[string]interface{}) multistep.StepAction {
}
if err := comm.Start(cmd); err != nil {
err := fmt.Errorf("Failed to send shutdown command: %s", err)
state["error"] = err
state.Put("error", err)
ui.Error(err.Error())
return multistep.ActionHalt
}
@@ -56,9 +56,9 @@ func (s *stepShutdown) Run(state map[string]interface{}) multistep.StepAction {
// If the command failed to run, notify the user in some way.
if cmd.ExitStatus != 0 {
state["error"] = fmt.Errorf(
state.Put("error", fmt.Errorf(
"Shutdown command has non-zero exit status.\n\nStdout: %s\n\nStderr: %s",
stdout.String(), stderr.String())
stdout.String(), stderr.String()))
return multistep.ActionHalt
}
@@ -77,7 +77,7 @@ func (s *stepShutdown) Run(state map[string]interface{}) multistep.StepAction {
select {
case <-shutdownTimer:
err := errors.New("Timeout while waiting for machine to shut down.")
state["error"] = err
state.Put("error", err)
ui.Error(err.Error())
return multistep.ActionHalt
default:
@@ -87,7 +87,7 @@ func (s *stepShutdown) Run(state map[string]interface{}) multistep.StepAction {
} else {
if err := driver.Stop(vmxPath); err != nil {
err := fmt.Errorf("Error stopping VM: %s", err)
state["error"] = err
state.Put("error", err)
ui.Error(err.Error())
return multistep.ActionHalt
}
@@ -132,4 +132,4 @@ LockWaitLoop:
return multistep.ActionContinue
}
func (s *stepShutdown) Cleanup(state map[string]interface{}) {}
func (s *stepShutdown) Cleanup(state multistep.StateBag) {}
+11 -11
View File
@@ -34,18 +34,18 @@ type bootCommandTemplateData struct {
// <nothing>
type stepTypeBootCommand struct{}
func (s *stepTypeBootCommand) Run(state map[string]interface{}) multistep.StepAction {
config := state["config"].(*config)
httpPort := state["http_port"].(uint)
ui := state["ui"].(packer.Ui)
vncPort := state["vnc_port"].(uint)
func (s *stepTypeBootCommand) Run(state multistep.StateBag) multistep.StepAction {
config := state.Get("config").(*config)
httpPort := state.Get("http_port").(uint)
ui := state.Get("ui").(packer.Ui)
vncPort := state.Get("vnc_port").(uint)
// Connect to VNC
ui.Say("Connecting to VM via VNC")
nc, err := net.Dial("tcp", fmt.Sprintf("127.0.0.1:%d", vncPort))
if err != nil {
err := fmt.Errorf("Error connecting to VNC: %s", err)
state["error"] = err
state.Put("error", err)
ui.Error(err.Error())
return multistep.ActionHalt
}
@@ -54,7 +54,7 @@ func (s *stepTypeBootCommand) Run(state map[string]interface{}) multistep.StepAc
c, err := vnc.Client(nc, &vnc.ClientConfig{Exclusive: true})
if err != nil {
err := fmt.Errorf("Error handshaking with VNC: %s", err)
state["error"] = err
state.Put("error", err)
ui.Error(err.Error())
return multistep.ActionHalt
}
@@ -73,7 +73,7 @@ func (s *stepTypeBootCommand) Run(state map[string]interface{}) multistep.StepAc
hostIp, err := ipFinder.HostIP()
if err != nil {
err := fmt.Errorf("Error detecting host IP: %s", err)
state["error"] = err
state.Put("error", err)
ui.Error(err.Error())
return multistep.ActionHalt
}
@@ -91,14 +91,14 @@ func (s *stepTypeBootCommand) Run(state map[string]interface{}) multistep.StepAc
command, err := config.tpl.Process(command, tplData)
if err != nil {
err := fmt.Errorf("Error preparing boot command: %s", err)
state["error"] = err
state.Put("error", err)
ui.Error(err.Error())
return multistep.ActionHalt
}
// Check for interrupts between typing things so we can cancel
// since this isn't the fastest thing.
if _, ok := state[multistep.StateCancelled]; ok {
if _, ok := state.GetOk(multistep.StateCancelled); ok {
return multistep.ActionHalt
}
@@ -108,7 +108,7 @@ func (s *stepTypeBootCommand) Run(state map[string]interface{}) multistep.StepAc
return multistep.ActionContinue
}
func (*stepTypeBootCommand) Cleanup(map[string]interface{}) {}
func (*stepTypeBootCommand) Cleanup(multistep.StateBag) {}
func vncSendString(c *vnc.ClientConn, original string) {
special := make(map[string]uint32)
+9 -9
View File
@@ -13,20 +13,20 @@ type toolsUploadPathTemplate struct {
type stepUploadTools struct{}
func (*stepUploadTools) Run(state map[string]interface{}) multistep.StepAction {
config := state["config"].(*config)
func (*stepUploadTools) Run(state multistep.StateBag) multistep.StepAction {
config := state.Get("config").(*config)
if config.ToolsUploadFlavor == "" {
return multistep.ActionContinue
}
comm := state["communicator"].(packer.Communicator)
tools_source := state["tools_upload_source"].(string)
ui := state["ui"].(packer.Ui)
comm := state.Get("communicator").(packer.Communicator)
tools_source := state.Get("tools_upload_source").(string)
ui := state.Get("ui").(packer.Ui)
ui.Say(fmt.Sprintf("Uploading the '%s' VMware Tools", config.ToolsUploadFlavor))
f, err := os.Open(tools_source)
if err != nil {
state["error"] = fmt.Errorf("Error opening VMware Tools ISO: %s", err)
state.Put("error", fmt.Errorf("Error opening VMware Tools ISO: %s", err))
return multistep.ActionHalt
}
defer f.Close()
@@ -35,17 +35,17 @@ func (*stepUploadTools) Run(state map[string]interface{}) multistep.StepAction {
config.ToolsUploadPath, err = config.tpl.Process(config.ToolsUploadPath, tplData)
if err != nil {
err := fmt.Errorf("Error preparing upload path: %s", err)
state["error"] = err
state.Put("error", err)
ui.Error(err.Error())
return multistep.ActionHalt
}
if err := comm.Upload(config.ToolsUploadPath, f); err != nil {
state["error"] = fmt.Errorf("Error uploading VMware Tools: %s", err)
state.Put("error", fmt.Errorf("Error uploading VMware Tools: %s", err))
return multistep.ActionHalt
}
return multistep.ActionContinue
}
func (*stepUploadTools) Cleanup(map[string]interface{}) {}
func (*stepUploadTools) Cleanup(multistep.StateBag) {}
+24 -2
View File
@@ -44,10 +44,28 @@ func (c Command) Run(env packer.Environment, args []string) int {
ui := env.Ui()
// Variables
ui.Say("Variables and their defaults:\n")
if len(tpl.Variables) == 0 {
ui.Say("Variables:\n")
ui.Say(" <No variables>")
} else {
requiredHeader := false
for k, v := range tpl.Variables {
if v.Required {
if !requiredHeader {
requiredHeader = true
ui.Say("Required variables:\n")
}
ui.Machine("template-variable", k, v.Default, "1")
ui.Say(" " + k)
}
}
if requiredHeader {
ui.Say("")
}
ui.Say("Optional variables and their defaults:\n")
keys := make([]string, 0, len(tpl.Variables))
max := 0
for k, _ := range tpl.Variables {
@@ -61,10 +79,14 @@ func (c Command) Run(env packer.Environment, args []string) int {
for _, k := range keys {
v := tpl.Variables[k]
if v.Required {
continue
}
padding := strings.Repeat(" ", max-len(k))
output := fmt.Sprintf(" %s%s = %s", k, padding, v)
ui.Machine("template-variable", k, v)
ui.Machine("template-variable", k, v.Default, "0")
ui.Say(output)
}
}
+2 -2
View File
@@ -11,7 +11,7 @@ import (
// MultistepDebugFn will return a proper multistep.DebugPauseFn to
// use for debugging if you're using multistep in your builder.
func MultistepDebugFn(ui packer.Ui) multistep.DebugPauseFn {
return func(loc multistep.DebugLocation, name string, state map[string]interface{}) {
return func(loc multistep.DebugLocation, name string, state multistep.StateBag) {
var locationString string
switch loc {
case multistep.DebugLocationAfterRun:
@@ -41,7 +41,7 @@ func MultistepDebugFn(ui packer.Ui) multistep.DebugPauseFn {
case <-result:
return
case <-time.After(100 * time.Millisecond):
if _, ok := state[multistep.StateCancelled]; ok {
if _, ok := state.GetOk(multistep.StateCancelled); ok {
return
}
}
+8 -8
View File
@@ -25,11 +25,11 @@ type StepConnectSSH struct {
// SSHAddress is a function that returns the TCP address to connect to
// for SSH. This is a function so that you can query information
// if necessary for this address.
SSHAddress func(map[string]interface{}) (string, error)
SSHAddress func(multistep.StateBag) (string, error)
// SSHConfig is a function that returns the proper client configuration
// for SSH access.
SSHConfig func(map[string]interface{}) (*gossh.ClientConfig, error)
SSHConfig func(multistep.StateBag) (*gossh.ClientConfig, error)
// SSHWaitTimeout is the total timeout to wait for SSH to become available.
SSHWaitTimeout time.Duration
@@ -40,8 +40,8 @@ type StepConnectSSH struct {
comm packer.Communicator
}
func (s *StepConnectSSH) Run(state map[string]interface{}) multistep.StepAction {
ui := state["ui"].(packer.Ui)
func (s *StepConnectSSH) Run(state multistep.StateBag) multistep.StepAction {
ui := state.Get("ui").(packer.Ui)
var comm packer.Communicator
var err error
@@ -69,14 +69,14 @@ WaitLoop:
ui.Say("Connected to SSH!")
s.comm = comm
state["communicator"] = comm
state.Put("communicator", comm)
break WaitLoop
case <-timeout:
ui.Error("Timeout waiting for SSH.")
close(cancel)
return multistep.ActionHalt
case <-time.After(1 * time.Second):
if _, ok := state[multistep.StateCancelled]; ok {
if _, ok := state.GetOk(multistep.StateCancelled); ok {
// The step sequence was cancelled, so cancel waiting for SSH
// and just start the halting process.
close(cancel)
@@ -89,10 +89,10 @@ WaitLoop:
return multistep.ActionContinue
}
func (s *StepConnectSSH) Cleanup(map[string]interface{}) {
func (s *StepConnectSSH) Cleanup(multistep.StateBag) {
}
func (s *StepConnectSSH) waitForSSH(state map[string]interface{}, cancel <-chan struct{}) (packer.Communicator, error) {
func (s *StepConnectSSH) waitForSSH(state multistep.StateBag, cancel <-chan struct{}) (packer.Communicator, error) {
handshakeAttempts := 0
var comm packer.Communicator
+12 -11
View File
@@ -22,19 +22,20 @@ type StepCreateFloppy struct {
floppyPath string
}
func (s *StepCreateFloppy) Run(state map[string]interface{}) multistep.StepAction {
func (s *StepCreateFloppy) Run(state multistep.StateBag) multistep.StepAction {
if len(s.Files) == 0 {
log.Println("No floppy files specified. Floppy disk will not be made.")
return multistep.ActionContinue
}
ui := state["ui"].(packer.Ui)
ui := state.Get("ui").(packer.Ui)
ui.Say("Creating floppy disk...")
// Create a temporary file to be our floppy drive
floppyF, err := ioutil.TempFile("", "packer")
if err != nil {
state["error"] = fmt.Errorf("Error creating temporary file for floppy: %s", err)
state.Put("error",
fmt.Errorf("Error creating temporary file for floppy: %s", err))
return multistep.ActionHalt
}
defer floppyF.Close()
@@ -46,7 +47,7 @@ func (s *StepCreateFloppy) Run(state map[string]interface{}) multistep.StepActio
// Set the size of the file to be a floppy sized
if err := floppyF.Truncate(1440 * 1024); err != nil {
state["error"] = fmt.Errorf("Error creating floppy: %s", err)
state.Put("error", fmt.Errorf("Error creating floppy: %s", err))
return multistep.ActionHalt
}
@@ -54,7 +55,7 @@ func (s *StepCreateFloppy) Run(state map[string]interface{}) multistep.StepActio
log.Println("Initializing block device backed by temporary file")
device, err := fs.NewFileDisk(floppyF)
if err != nil {
state["error"] = fmt.Errorf("Error creating floppy: %s", err)
state.Put("error", fmt.Errorf("Error creating floppy: %s", err))
return multistep.ActionHalt
}
@@ -66,7 +67,7 @@ func (s *StepCreateFloppy) Run(state map[string]interface{}) multistep.StepActio
OEMName: "packer",
}
if fat.FormatSuperFloppy(device, formatConfig); err != nil {
state["error"] = fmt.Errorf("Error creating floppy: %s", err)
state.Put("error", fmt.Errorf("Error creating floppy: %s", err))
return multistep.ActionHalt
}
@@ -74,7 +75,7 @@ func (s *StepCreateFloppy) Run(state map[string]interface{}) multistep.StepActio
log.Println("Initializing FAT filesystem on block device")
fatFs, err := fat.New(device)
if err != nil {
state["error"] = fmt.Errorf("Error creating floppy: %s", err)
state.Put("error", fmt.Errorf("Error creating floppy: %s", err))
return multistep.ActionHalt
}
@@ -82,7 +83,7 @@ func (s *StepCreateFloppy) Run(state map[string]interface{}) multistep.StepActio
log.Println("Reading the root directory from the filesystem")
rootDir, err := fatFs.RootDir()
if err != nil {
state["error"] = fmt.Errorf("Error creating floppy: %s", err)
state.Put("error", fmt.Errorf("Error creating floppy: %s", err))
return multistep.ActionHalt
}
@@ -90,18 +91,18 @@ func (s *StepCreateFloppy) Run(state map[string]interface{}) multistep.StepActio
for _, filename := range s.Files {
ui.Message(fmt.Sprintf("Copying: %s", filepath.Base(filename)))
if s.addSingleFile(rootDir, filename); err != nil {
state["error"] = fmt.Errorf("Error adding file to floppy: %s", err)
state.Put("error", fmt.Errorf("Error adding file to floppy: %s", err))
return multistep.ActionHalt
}
}
// Set the path to the floppy so it can be used later
state["floppy_path"] = s.floppyPath
state.Put("floppy_path", s.floppyPath)
return multistep.ActionContinue
}
func (s *StepCreateFloppy) Cleanup(map[string]interface{}) {
func (s *StepCreateFloppy) Cleanup(multistep.StateBag) {
if s.floppyPath != "" {
log.Printf("Deleting floppy disk: %s", s.floppyPath)
os.Remove(s.floppyPath)
+10 -10
View File
@@ -37,16 +37,16 @@ type StepDownload struct {
Url []string
}
func (s *StepDownload) Run(state map[string]interface{}) multistep.StepAction {
cache := state["cache"].(packer.Cache)
ui := state["ui"].(packer.Ui)
func (s *StepDownload) Run(state multistep.StateBag) multistep.StepAction {
cache := state.Get("cache").(packer.Cache)
ui := state.Get("ui").(packer.Ui)
var checksum []byte
if s.Checksum != "" {
var err error
checksum, err = hex.DecodeString(s.Checksum)
if err != nil {
state["error"] = fmt.Errorf("Error parsing checksum: %s", err)
state.Put("error", fmt.Errorf("Error parsing checksum: %s", err))
return multistep.ActionHalt
}
}
@@ -89,20 +89,20 @@ func (s *StepDownload) Run(state map[string]interface{}) multistep.StepAction {
if finalPath == "" {
err := fmt.Errorf("%s download failed.", s.Description)
state["error"] = err
state.Put("error", err)
ui.Error(err.Error())
return multistep.ActionHalt
}
state[s.ResultKey] = finalPath
state.Put(s.ResultKey, finalPath)
return multistep.ActionContinue
}
func (s *StepDownload) Cleanup(map[string]interface{}) {}
func (s *StepDownload) Cleanup(multistep.StateBag) {}
func (s *StepDownload) download(config *DownloadConfig, state map[string]interface{}) (string, error, bool) {
func (s *StepDownload) download(config *DownloadConfig, state multistep.StateBag) (string, error, bool) {
var path string
ui := state["ui"].(packer.Ui)
ui := state.Get("ui").(packer.Ui)
download := NewDownloadClient(config)
downloadCompleteCh := make(chan error, 1)
@@ -129,7 +129,7 @@ func (s *StepDownload) download(config *DownloadConfig, state map[string]interfa
ui.Message(fmt.Sprintf("Download progress: %d%%", progress))
}
case <-time.After(1 * time.Second):
if _, ok := state[multistep.StateCancelled]; ok {
if _, ok := state.GetOk(multistep.StateCancelled); ok {
ui.Say("Interrupt received. Cancelling download...")
return "", nil, false
}
+29 -10
View File
@@ -4,6 +4,7 @@ import (
"github.com/mitchellh/multistep"
"github.com/mitchellh/packer/packer"
"log"
"time"
)
// StepProvision runs the provisioners.
@@ -17,18 +18,36 @@ import (
// <nothing>
type StepProvision struct{}
func (*StepProvision) Run(state map[string]interface{}) multistep.StepAction {
comm := state["communicator"].(packer.Communicator)
hook := state["hook"].(packer.Hook)
ui := state["ui"].(packer.Ui)
func (*StepProvision) Run(state multistep.StateBag) multistep.StepAction {
comm := state.Get("communicator").(packer.Communicator)
hook := state.Get("hook").(packer.Hook)
ui := state.Get("ui").(packer.Ui)
// Run the provisioner in a goroutine so we can continually check
// for cancellations...
log.Println("Running the provision hook")
if err := hook.Run(packer.HookProvision, ui, comm, nil); err != nil {
state["error"] = err
return multistep.ActionHalt
}
errCh := make(chan error, 1)
go func() {
errCh <- hook.Run(packer.HookProvision, ui, comm, nil)
}()
return multistep.ActionContinue
for {
select {
case err := <-errCh:
if err != nil {
state.Put("error", err)
return multistep.ActionHalt
}
return multistep.ActionContinue
case <-time.After(1 * time.Second):
if _, ok := state.GetOk(multistep.StateCancelled); ok {
log.Println("Cancelling provisioning due to interrupt...")
hook.Cancel()
return multistep.ActionHalt
}
}
}
}
func (*StepProvision) Cleanup(map[string]interface{}) {}
func (*StepProvision) Cleanup(multistep.StateBag) {}
+6 -1
View File
@@ -24,7 +24,12 @@ func (k *SimpleKeychain) AddPEMKey(key string) (err error) {
return errors.New("no block in key")
}
rsakey, err := x509.ParsePKCS1PrivateKey(block.Bytes)
var rsakey interface{}
rsakey, err = x509.ParsePKCS1PrivateKey(block.Bytes)
if err != nil {
rsakey, err = x509.ParsePKCS8PrivateKey(block.Bytes)
}
if err != nil {
return
}
+28 -11
View File
@@ -77,7 +77,7 @@ type coreBuild struct {
hooks map[string][]Hook
postProcessors [][]coreBuildPostProcessor
provisioners []coreBuildProvisioner
variables map[string]string
variables map[string]coreBuildVariable
debug bool
force bool
@@ -101,6 +101,12 @@ type coreBuildProvisioner struct {
config []interface{}
}
// A user-variable that is part of a single build.
type coreBuildVariable struct {
Default string
Required bool
}
// Returns the name of the build.
func (b *coreBuild) Name() string {
return b.name
@@ -120,27 +126,36 @@ func (b *coreBuild) Prepare(userVars map[string]string) (err error) {
b.prepareCalled = true
// Compile the variables
varErrs := make([]error, 0)
variables := make(map[string]string)
for k, v := range b.variables {
variables[k] = v
variables[k] = v.Default
if v.Required {
if _, ok := userVars[k]; !ok {
varErrs = append(varErrs,
fmt.Errorf("Required user variable '%s' not set", k))
}
}
}
if userVars != nil {
errs := make([]error, 0)
for k, v := range userVars {
if _, ok := variables[k]; !ok {
errs = append(
errs, fmt.Errorf("Unknown user variable: %s", k))
varErrs = append(
varErrs, fmt.Errorf("Unknown user variable: %s", k))
continue
}
variables[k] = v
}
}
if len(errs) > 0 {
return &MultiError{
Errors: errs,
}
// If there were any problem with variables, return an error right
// away because we can't be certain anything else will actually work.
if len(varErrs) > 0 {
return &MultiError{
Errors: varErrs,
}
}
@@ -207,10 +222,12 @@ func (b *coreBuild) Run(originalUi Ui, cache Cache) ([]Artifact, error) {
hooks[HookProvision] = make([]Hook, 0, 1)
}
hooks[HookProvision] = append(hooks[HookProvision], &ProvisionHook{provisioners})
hooks[HookProvision] = append(hooks[HookProvision], &ProvisionHook{
Provisioners: provisioners,
})
}
hook := &DispatchHook{hooks}
hook := &DispatchHook{Mapping: hooks}
artifacts := make([]Artifact, 0, 1)
// The builder just has a normal Ui, but targetted
+35 -17
View File
@@ -13,17 +13,17 @@ func testBuild() *coreBuild {
builderConfig: 42,
builderType: "foo",
hooks: map[string][]Hook{
"foo": []Hook{&TestHook{}},
"foo": []Hook{&MockHook{}},
},
provisioners: []coreBuildProvisioner{
coreBuildProvisioner{&TestProvisioner{}, []interface{}{42}},
coreBuildProvisioner{&MockProvisioner{}, []interface{}{42}},
},
postProcessors: [][]coreBuildPostProcessor{
[]coreBuildPostProcessor{
coreBuildPostProcessor{&TestPostProcessor{artifactId: "pp"}, "testPP", make(map[string]interface{}), true},
},
},
variables: make(map[string]string),
variables: make(map[string]coreBuildVariable),
}
}
@@ -59,9 +59,9 @@ func TestBuild_Prepare(t *testing.T) {
assert.Equal(builder.prepareConfig, []interface{}{42, packerConfig}, "prepare config should be 42")
coreProv := build.provisioners[0]
prov := coreProv.provisioner.(*TestProvisioner)
assert.True(prov.prepCalled, "prepare should be called")
assert.Equal(prov.prepConfigs, []interface{}{42, packerConfig}, "prepare should be called with proper config")
prov := coreProv.provisioner.(*MockProvisioner)
assert.True(prov.PrepCalled, "prepare should be called")
assert.Equal(prov.PrepConfigs, []interface{}{42, packerConfig}, "prepare should be called with proper config")
corePP := build.postProcessors[0][0]
pp := corePP.processor.(*TestPostProcessor)
@@ -104,9 +104,9 @@ func TestBuild_Prepare_Debug(t *testing.T) {
assert.Equal(builder.prepareConfig, []interface{}{42, packerConfig}, "prepare config should be 42")
coreProv := build.provisioners[0]
prov := coreProv.provisioner.(*TestProvisioner)
assert.True(prov.prepCalled, "prepare should be called")
assert.Equal(prov.prepConfigs, []interface{}{42, packerConfig}, "prepare should be called with proper config")
prov := coreProv.provisioner.(*MockProvisioner)
assert.True(prov.PrepCalled, "prepare should be called")
assert.Equal(prov.PrepConfigs, []interface{}{42, packerConfig}, "prepare should be called with proper config")
}
func TestBuildPrepare_variables_default(t *testing.T) {
@@ -116,7 +116,7 @@ func TestBuildPrepare_variables_default(t *testing.T) {
}
build := testBuild()
build.variables["foo"] = "bar"
build.variables["foo"] = coreBuildVariable{Default: "bar"}
builder := build.builder.(*TestBuilder)
err := build.Prepare(nil)
@@ -135,7 +135,7 @@ func TestBuildPrepare_variables_default(t *testing.T) {
func TestBuildPrepare_variables_nonexist(t *testing.T) {
build := testBuild()
build.variables["foo"] = "bar"
build.variables["foo"] = coreBuildVariable{Default: "bar"}
err := build.Prepare(map[string]string{"bar": "baz"})
if err == nil {
@@ -150,7 +150,7 @@ func TestBuildPrepare_variables_override(t *testing.T) {
}
build := testBuild()
build.variables["foo"] = "bar"
build.variables["foo"] = coreBuildVariable{Default: "bar"}
builder := build.builder.(*TestBuilder)
err := build.Prepare(map[string]string{"foo": "baz"})
@@ -167,6 +167,24 @@ func TestBuildPrepare_variables_override(t *testing.T) {
}
}
func TestBuildPrepare_variablesRequired(t *testing.T) {
build := testBuild()
build.variables["foo"] = coreBuildVariable{Required: true}
err := build.Prepare(map[string]string{})
if err == nil {
t.Fatal("should have had error")
}
// Test with setting the value
build = testBuild()
build.variables["foo"] = coreBuildVariable{Required: true}
err = build.Prepare(map[string]string{"foo": ""})
if err != nil {
t.Fatalf("should not have error: %s", err)
}
}
func TestBuild_Run(t *testing.T) {
assert := asserts.NewTestingAsserts(t, true)
@@ -187,14 +205,14 @@ func TestBuild_Run(t *testing.T) {
dispatchHook := builder.runHook
dispatchHook.Run("foo", nil, nil, 42)
hook := build.hooks["foo"][0].(*TestHook)
assert.True(hook.runCalled, "run should be called")
assert.Equal(hook.runData, 42, "should have correct data")
hook := build.hooks["foo"][0].(*MockHook)
assert.True(hook.RunCalled, "run should be called")
assert.Equal(hook.RunData, 42, "should have correct data")
// Verify provisioners run
dispatchHook.Run(HookProvision, nil, nil, 42)
prov := build.provisioners[0].provisioner.(*TestProvisioner)
assert.True(prov.provCalled, "provision should be called")
prov := build.provisioners[0].provisioner.(*MockProvisioner)
assert.True(prov.ProvCalled, "provision should be called")
// Verify post-processor was run
pp := build.postProcessors[0][0].processor.(*TestPostProcessor)
+3 -3
View File
@@ -20,7 +20,7 @@ func init() {
func testComponentFinder() *ComponentFinder {
builderFactory := func(n string) (Builder, error) { return testBuilder(), nil }
ppFactory := func(n string) (PostProcessor, error) { return new(TestPostProcessor), nil }
provFactory := func(n string) (Provisioner, error) { return new(TestProvisioner), nil }
provFactory := func(n string) (Provisioner, error) { return new(MockProvisioner), nil }
return &ComponentFinder{
Builder: builderFactory,
PostProcessor: ppFactory,
@@ -227,7 +227,7 @@ func TestEnvironment_DefaultCli_Version(t *testing.T) {
func TestEnvironment_Hook(t *testing.T) {
assert := asserts.NewTestingAsserts(t, true)
hook := &TestHook{}
hook := &MockHook{}
hooks := make(map[string]Hook)
hooks["foo"] = hook
@@ -309,7 +309,7 @@ func TestEnvironment_PostProcessor_Error(t *testing.T) {
func TestEnvironmentProvisioner(t *testing.T) {
assert := asserts.NewTestingAsserts(t, true)
p := &TestProvisioner{}
p := &MockProvisioner{}
ps := make(map[string]Provisioner)
ps["foo"] = p
+47
View File
@@ -1,5 +1,9 @@
package packer
import (
"sync"
)
// This is the hook that should be fired for provisioners to run.
const HookProvision = "packer_provision"
@@ -11,19 +15,40 @@ const HookProvision = "packer_provision"
// you must reference the documentation for the specific hook you're interested
// in. In addition to that, the Hook is given access to a UI so that it can
// output things to the user.
//
// Cancel is called when the hook needs to be cancelled. This will usually
// be called when Run is still in progress so the mechanism that handles this
// must be race-free. Cancel should attempt to cancel the hook in the
// quickest, safest way possible.
type Hook interface {
Run(string, Ui, Communicator, interface{}) error
Cancel()
}
// A Hook implementation that dispatches based on an internal mapping.
type DispatchHook struct {
Mapping map[string][]Hook
l sync.Mutex
cancelled bool
runningHook Hook
}
// Runs the hook with the given name by dispatching it to the proper
// hooks if a mapping exists. If a mapping doesn't exist, then nothing
// happens.
func (h *DispatchHook) Run(name string, ui Ui, comm Communicator, data interface{}) error {
h.l.Lock()
h.cancelled = false
h.l.Unlock()
// Make sure when we exit that we reset the running hook.
defer func() {
h.l.Lock()
defer h.l.Unlock()
h.runningHook = nil
}()
hooks, ok := h.Mapping[name]
if !ok {
// No hooks for that name. No problem.
@@ -31,6 +56,15 @@ func (h *DispatchHook) Run(name string, ui Ui, comm Communicator, data interface
}
for _, hook := range hooks {
h.l.Lock()
if h.cancelled {
h.l.Unlock()
return nil
}
h.runningHook = hook
h.l.Unlock()
if err := hook.Run(name, ui, comm, data); err != nil {
return err
}
@@ -38,3 +72,16 @@ func (h *DispatchHook) Run(name string, ui Ui, comm Communicator, data interface
return nil
}
// Cancels all the hooks that are currently in-flight, if any. This will
// block until the hooks are all cancelled.
func (h *DispatchHook) Cancel() {
h.l.Lock()
defer h.l.Unlock()
if h.runningHook != nil {
h.runningHook.Cancel()
}
h.cancelled = true
}
+31
View File
@@ -0,0 +1,31 @@
package packer
// MockHook is an implementation of Hook that can be used for tests.
type MockHook struct {
RunFunc func() error
RunCalled bool
RunComm Communicator
RunData interface{}
RunName string
RunUi Ui
CancelCalled bool
}
func (t *MockHook) Run(name string, ui Ui, comm Communicator, data interface{}) error {
t.RunCalled = true
t.RunComm = comm
t.RunData = data
t.RunName = name
t.RunUi = ui
if t.RunFunc == nil {
return nil
}
return t.RunFunc()
}
func (t *MockHook) Cancel() {
t.CancelCalled = true
}
+56 -19
View File
@@ -2,52 +2,89 @@ package packer
import (
"cgl.tideland.biz/asserts"
"sync"
"testing"
"time"
)
type TestHook struct {
runCalled bool
runComm Communicator
runData interface{}
runName string
runUi Ui
// A helper Hook implementation for testing cancels.
type CancelHook struct {
sync.Mutex
cancelCh chan struct{}
doneCh chan struct{}
Cancelled bool
}
func (t *TestHook) Run(name string, ui Ui, comm Communicator, data interface{}) error {
t.runCalled = true
t.runComm = comm
t.runData = data
t.runName = name
t.runUi = ui
func (h *CancelHook) Run(string, Ui, Communicator, interface{}) error {
h.Lock()
h.cancelCh = make(chan struct{})
h.doneCh = make(chan struct{})
h.Unlock()
defer close(h.doneCh)
select {
case <-h.cancelCh:
h.Cancelled = true
case <-time.After(1 * time.Second):
}
return nil
}
func (h *CancelHook) Cancel() {
h.Lock()
close(h.cancelCh)
h.Unlock()
<-h.doneCh
}
func TestDispatchHook_Implements(t *testing.T) {
assert := asserts.NewTestingAsserts(t, true)
var r Hook
c := &DispatchHook{nil}
c := &DispatchHook{}
assert.Implementor(c, &r, "should be a Hook")
}
func TestDispatchHook_Run_NoHooks(t *testing.T) {
// Just make sure nothing blows up
dh := &DispatchHook{make(map[string][]Hook)}
dh := &DispatchHook{}
dh.Run("foo", nil, nil, nil)
}
func TestDispatchHook_Run(t *testing.T) {
assert := asserts.NewTestingAsserts(t, true)
hook := &TestHook{}
hook := &MockHook{}
mapping := make(map[string][]Hook)
mapping["foo"] = []Hook{hook}
dh := &DispatchHook{mapping}
dh := &DispatchHook{Mapping: mapping}
dh.Run("foo", nil, nil, 42)
assert.True(hook.runCalled, "run should be called")
assert.Equal(hook.runName, "foo", "should be proper event")
assert.Equal(hook.runData, 42, "should be correct data")
assert.True(hook.RunCalled, "run should be called")
assert.Equal(hook.RunName, "foo", "should be proper event")
assert.Equal(hook.RunData, 42, "should be correct data")
}
func TestDispatchHook_cancel(t *testing.T) {
hook := new(CancelHook)
dh := &DispatchHook{
Mapping: map[string][]Hook{
"foo": []Hook{hook},
},
}
go dh.Run("foo", nil, nil, 42)
time.Sleep(100 * time.Millisecond)
dh.Cancel()
if !hook.Cancelled {
t.Fatal("hook should've cancelled")
}
}
+10 -1
View File
@@ -19,8 +19,17 @@ func (c *cmdHook) Run(name string, ui packer.Ui, comm packer.Communicator, data
return c.hook.Run(name, ui, comm, data)
}
func (c *cmdHook) Cancel() {
defer func() {
r := recover()
c.checkExit(r, nil)
}()
c.hook.Cancel()
}
func (c *cmdHook) checkExit(p interface{}, cb func()) {
if c.client.Exited() {
if c.client.Exited() && cb != nil {
cb()
} else if p != nil && !Killed {
log.Panic(p)
-7
View File
@@ -1,17 +1,10 @@
package plugin
import (
"github.com/mitchellh/packer/packer"
"os/exec"
"testing"
)
type helperHook byte
func (helperHook) Run(string, packer.Ui, packer.Communicator, interface{}) error {
return nil
}
func TestHook_NoExist(t *testing.T) {
c := NewClient(&ClientConfig{Cmd: exec.Command("i-should-not-exist")})
defer c.Kill()
+20 -8
View File
@@ -19,8 +19,14 @@ import (
"os/signal"
"runtime"
"strconv"
"sync/atomic"
)
// This is a count of the number of interrupts the process has received.
// This is updated with sync/atomic whenever a SIGINT is received and can
// be checked by the plugin safely to take action.
var Interrupts int32 = 0
const MagicCookieKey = "PACKER_PLUGIN_MAGIC_COOKIE"
const MagicCookieValue = "d602bf8f470bc67ca7faa0386276bbdd4330efaf76d1a219cb4d6991ca9872b2"
@@ -86,17 +92,18 @@ func serve(server *rpc.Server) (err error) {
return
}
// Registers a signal handler to "swallow" interrupts so that the
// Registers a signal handler to swallow and count interrupts so that the
// plugin isn't killed. The main host Packer process is responsible
// for killing the plugins when interrupted.
func swallowInterrupts() {
func countInterrupts() {
ch := make(chan os.Signal, 1)
signal.Notify(ch, os.Interrupt)
go func() {
for {
<-ch
log.Println("Received interrupt signal. Ignoring.")
newCount := atomic.AddInt32(&Interrupts, 1)
log.Printf("Received interrupt signal (count: %d). Ignoring.", newCount)
}
}()
}
@@ -108,7 +115,7 @@ func ServeBuilder(builder packer.Builder) {
server := rpc.NewServer()
packrpc.RegisterBuilder(server, builder)
swallowInterrupts()
countInterrupts()
if err := serve(server); err != nil {
log.Printf("ERROR: %s", err)
os.Exit(1)
@@ -122,7 +129,7 @@ func ServeCommand(command packer.Command) {
server := rpc.NewServer()
packrpc.RegisterCommand(server, command)
swallowInterrupts()
countInterrupts()
if err := serve(server); err != nil {
log.Printf("ERROR: %s", err)
os.Exit(1)
@@ -136,7 +143,7 @@ func ServeHook(hook packer.Hook) {
server := rpc.NewServer()
packrpc.RegisterHook(server, hook)
swallowInterrupts()
countInterrupts()
if err := serve(server); err != nil {
log.Printf("ERROR: %s", err)
os.Exit(1)
@@ -150,7 +157,7 @@ func ServePostProcessor(p packer.PostProcessor) {
server := rpc.NewServer()
packrpc.RegisterPostProcessor(server, p)
swallowInterrupts()
countInterrupts()
if err := serve(server); err != nil {
log.Printf("ERROR: %s", err)
os.Exit(1)
@@ -164,9 +171,14 @@ func ServeProvisioner(p packer.Provisioner) {
server := rpc.NewServer()
packrpc.RegisterProvisioner(server, p)
swallowInterrupts()
countInterrupts()
if err := serve(server); err != nil {
log.Printf("ERROR: %s", err)
os.Exit(1)
}
}
// Tests whether or not the plugin was interrupted or not.
func Interrupted() bool {
return atomic.LoadInt32(&Interrupts) > 0
}

Some files were not shown because too many files have changed in this diff Show More