Compare commits

..

143 Commits

Author SHA1 Message Date
Mitchell Hashimoto 0b23c8addf v0.3.7 2013-09-09 14:48:42 -07:00
Mitchell Hashimoto aa658e982b provisioner/puppet-masterless: detailed exit codes 2013-09-09 14:46:25 -07:00
Mitchell Hashimoto d29c5cd231 provisioner/puppet-masterless: better messaging 2013-09-09 14:03:40 -07:00
Mitchell Hashimoto f5850199ba provisioner/file: support uploading directories [GH-251] 2013-09-09 14:00:06 -07:00
Mitchell Hashimoto dbf978f8d1 provisioners/puppet-masterless: support hiera configs 2013-09-09 13:24:17 -07:00
Mitchell Hashimoto 4c537105ef provisioner/puppet-masterless: support custom facts 2013-09-08 23:00:11 -07:00
Mitchell Hashimoto 1bad19f58b Merge pull request #406 from andrewschleifer/edits
website: accidently a word
2013-09-08 22:13:22 -07:00
Andrew Schleifer 06b5b4304d accidently a word
specifically, "builders"
2013-09-08 22:27:14 -05:00
Mitchell Hashimoto 5315aaba0d website: document the Puppet provisioner 2013-09-07 23:56:00 -07:00
Mitchell Hashimoto 3a899892dc provisioner/puppet-masterless: more sane messaging 2013-09-07 23:43:06 -07:00
Mitchell Hashimoto ef0a077168 provisioner/puppet-masterless: more validation and tests 2013-09-07 23:31:28 -07:00
Mitchell Hashimoto de0c59cc18 provisioner/puppet-masterless: rework internals, use SCP 2013-09-07 23:08:56 -07:00
Mitchell Hashimoto 53ab6b9607 Update CHANGELOG 2013-09-07 22:28:06 -07:00
Mitchell Hashimoto 6f8afdeaf6 Rename puppet provisioner to puppet-masterless 2013-09-07 22:27:25 -07:00
Mitchell Hashimoto 1f63abc622 Merge pull request #234 from jamtur01/master
Added basic Puppet provisioner
2013-09-07 22:24:16 -07:00
Mark Peek 46a2b81a1d Update CHANGELOG to include clean_ami_name 2013-09-07 18:57:00 -07:00
Mark Peek 7fe487cd4f website: add no_device to amazon block device doc 2013-09-07 18:53:34 -07:00
Mark Peek 06b83d148b website: document isotime and clean_ami_name 2013-09-07 18:50:01 -07:00
Mark Peek 9035b51165 builder/amazon: add clean_ami_name template function
Add a clean_ami_name template function which will translate illegal
characters in an AMI name to '-'. Example usage would be:
    "ami_name": "Ubuntu 12.04 {{isotime | clean_ami_name}}"
2013-09-07 18:42:50 -07:00
Mitchell Hashimoto a775a58129 Update CHANGELOG 2013-09-07 18:14:17 -07:00
Mitchell Hashimoto 694275c7cd Merge pull request #401 from mackrorysd/master
builder/virtualbox: Using the vm_name parameter to name OVA/OVF files and their attachments
2013-09-07 18:12:57 -07:00
Sean Mackrory 27a2270593 Using the vm_name parameter to name OVA/OVF files and their attachments 2013-09-06 23:38:31 -07:00
Mitchell Hashimoto 8c30dc78ac builder/amazon/common: don't need squash here 2013-09-06 11:48:38 -07:00
Mitchell Hashimoto c4cf8553c8 builder/amazon/common: support NoDevice [GH-396] 2013-09-06 11:44:57 -07:00
Mitchell Hashimoto a511b603e2 scripts: build.sh appends .exe on Windows [GH-397] 2013-09-06 11:34:01 -07:00
Mitchell Hashimoto 81edbb4173 communicator/ssh: detect if SCP exited with failure [GH-386] 2013-09-05 19:55:09 -07:00
Mitchell Hashimoto 147eea955e communicator/ssh: prettify log output 2013-09-05 17:22:37 -07:00
Mitchell Hashimoto 67c71aa836 builder/amazon/common: if instance query returns none, ignore 2013-09-05 17:19:23 -07:00
Mitchell Hashimoto 535d2a7cfc Update CHANGELOG 2013-09-05 17:14:50 -07:00
Mitchell Hashimoto 5736dbd3aa Merge pull request #388 from jmassara/ami_region_copy
builder/amazon/all: AMI region copy fixes
2013-09-05 17:14:16 -07:00
Mitchell Hashimoto b57fa3cbdb Update CHANGELOG 2013-09-05 17:12:44 -07:00
Mitchell Hashimoto a59bbd892d Merge branch 'master' of https://github.com/armon/packer into armon-master
Conflicts:
	packer/config_template.go
2013-09-05 17:12:15 -07:00
Mitchell Hashimoto fb8b82fca5 builder/vmware: always set msg.AutoAnswer 2013-09-05 13:44:57 -07:00
Mitchell Hashimoto e26f7e87e0 builder/vmware: auto-answer dialogs [GH-393] 2013-09-05 13:43:01 -07:00
Mitchell Hashimoto 25e7b90a96 Merge pull request #392 from ttres/patch-1
website: minor URL typo
2013-09-05 13:39:10 -07:00
ttres c0ef20bb80 Minor URL typo
URLs with special characters should escape them.
2013-09-05 16:27:38 -03:00
Mitchell Hashimoto 6746ffc133 builder/amazon/all: TemporaryKeyPairName
/cc @whostolebenfrog - I actually simplified things quite a bit. I added
a "uuid" global template function so it just uses that now. I renamed it
so that it is clear it is a temporary keypair.
2013-09-05 12:23:08 -07:00
Mitchell Hashimoto 682cd97991 Update CHANGELOG 2013-09-05 12:20:57 -07:00
Mitchell Hashimoto 8dd3850482 packer: uuid function 2013-09-05 12:19:56 -07:00
Mitchell Hashimoto e89f182b33 Update CHANGELOG 2013-09-05 12:10:40 -07:00
Mitchell Hashimoto 25a310cf45 Merge pull request #389 from whostolebenfrog/master
builder/amazon/ebs: Allow customization of temporary ssh key name
2013-09-05 12:10:03 -07:00
Mitchell Hashimoto bf1a2f4899 builder/virtualbox: process hdd interface as template, validate 2013-09-05 12:07:58 -07:00
Mitchell Hashimoto 250077a0ff Update CHANGELOG 2013-09-05 12:02:18 -07:00
Mitchell Hashimoto 89a29e7b21 Merge pull request #391 from jbronn/hard_drive_interface
builder/virtualbox: Add `hard_drive_interface` option
2013-09-05 12:01:39 -07:00
Armon Dadgar 13a4c34bc5 Adding support for isotime template variable 2013-09-05 11:09:37 -07:00
Justin Bronn 175408dcb2 Document the hard_drive_interface option. 2013-09-05 11:08:55 -07:00
Justin Bronn 3043eddfaf Enable customization of VirtualBox disk controller with hard_drive_interface option (SATA only). 2013-09-05 11:00:08 -07:00
Ben Griffiths 0058929186 Allow customization of SSH Key pair pattern
Defined in a template using ssh_keypair_pattern. Defaults to "packer %s"
2013-09-05 13:28:31 +01:00
Mitchell Hashimoto fc62adfe5d builder/digitalocean: retry longer and don't retry shutdown 2013-09-04 23:01:37 -07:00
Mitchell Hashimoto cc8d0ad2eb builder/digitalocean: retry pending events a lot more 2013-09-04 23:00:54 -07:00
Mitchell Hashimoto 45c88e2aea builder/digitalocean: get compilation working again 2013-09-04 22:53:37 -07:00
Mitchell Hashimoto 290b9ef836 builder/digitalocean: give up on graceful shutdown more quickly 2013-09-04 22:51:28 -07:00
Mitchell Hashimoto a1c96d3b97 builder/digitalocean: proper timeouts so poweroff retries 2013-09-04 22:26:05 -07:00
Mitchell Hashimoto 0271d9a15e builder/digitalocean: properly cleanup goroutines for status checking 2013-09-04 22:21:59 -07:00
Mitchell Hashimoto ede100bb7b builder/digitalocean: actually, we want to try hard on shutdown 2013-09-04 22:14:30 -07:00
Mitchell Hashimoto 2da53f54f4 builder/digitalocean: break loop if we reach off state
Otherwise we get stuck in a long loop. Whoops.
2013-09-04 22:07:19 -07:00
Mitchell Hashimoto 5477d7166b builder/digitalocean: retry power off a number of times
See comment.
2013-09-04 21:59:58 -07:00
Mitchell Hashimoto ab5b094d73 builder/digitalocean: looser pending event string matching.
Most APIs return "has a pending event" but power off seems to return
"already a pending event". WTF. Also, just wtf at the DigitalOcean API.
I can't wait until they change their english and this breaks. Because
it is definitely going to happen, and it's going to be such a fun time.
2013-09-04 21:29:07 -07:00
Mitchell Hashimoto eb8f6c9769 Update CHANGELOG 2013-09-04 21:25:47 -07:00
Mitchell Hashimoto f2e909356e builder/digitalocean: retry on any pending event errors
/cc @pearkes - I hate this thing.
2013-09-04 21:20:41 -07:00
James Massara 952b2c921f Also deregister AMI if it was copied to other regions 2013-09-04 17:48:00 -07:00
James Massara a613446205 builder/amazon: Re-ordered steps for AMI region copying so tags and
launch permissions are also applied to copied AMIs
2013-09-04 16:06:06 -07:00
Mitchell Hashimoto 907ca4d951 Update README 2013-09-03 08:53:29 -07:00
Mitchell Hashimoto 822c52c46f Merge pull request #383 from marcw/readme-format
Add make format information in the README.
2013-09-03 08:52:30 -07:00
Marc Weistroff 082dc6c14d Add make format information in the contributing guidelines. 2013-09-03 14:58:32 +02:00
James Turnbull 797255fca6 Added basic Puppet provisioner 2013-09-03 08:39:49 -04:00
Mitchell Hashimoto 05e1654eb8 Update CHANGELOG 2013-09-02 17:35:00 -07:00
Mitchell Hashimoto 12547d3203 website: update website 2013-09-02 17:34:37 -07:00
Mitchell Hashimoto 8024dde44d Merge pull request #382 from justinsb/specify_openstack_project
builder/openstack: allow project to be specified
2013-09-02 17:33:57 -07:00
Mitchell Hashimoto 0b16317401 command/inspect: fix weird output 2013-09-02 15:38:57 -07:00
Mitchell Hashimoto 06515fd195 up version for dev 2013-09-02 10:42:05 -07:00
Justin Santa Barbara 47ee7ca977 Only pass the project if it is specified 2013-09-02 10:34:44 -07:00
Justin Santa Barbara b6a6c8571b Allow the Openstack project to be specified
It isn't actually optional, according to the spec
2013-09-02 10:34:44 -07:00
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
156 changed files with 2736 additions and 1182 deletions
+71
View File
@@ -1,3 +1,74 @@
## 0.3.7 (September 9, 2013)
BACKWARDS INCOMPATIBILITIES:
* The "event_delay" option for the DigitalOcean builder is now gone.
The builder automatically waits for events to go away. Run your templates
through `packer fix` to get rid of these.
FEATURES:
* **NEW PROVISIONER:** `puppet-masterless`. You can now provision with
a masterless Puppet setup. [GH-234]
* New globally available template function: `uuid`. Generates a new random
UUID.
* New globally available template function: `isotime`. Generates the
current time in ISO standard format.
* New Amazon template function: `clean_ami_name`. Substitutes '-' for
characters that are illegal to use in an AMI name.
IMPROVEMENTS:
* builder/amazon/all: Ability to specify the format of the temporary
keypair created. [GH-389]
* builder/amazon/all: Support the NoDevice flag for block mappings. [GH-396]
* builder/digitalocean: Retry on any pending event errors.
* builder/openstack: Can now specify a project. [GH-382]
* builder/virtualbox: Can now attach hard drive over SATA. [GH-391]
* provisioner/file: Can now upload directories. [GH-251]
BUG FIXES:
* core: Detect if SCP is not enabled on the other side. [GH-386]
* builder/amazon/all: When copying AMI to multiple regions, copy
the metadata (tags and attributes) as well. [GH-388]
* builder/amazon/all: Fix panic case where eventually consistent
instance state caused an index out of bounds.
* builder/virtualbox: The `vm_name` setting now properly sets the OVF
name of the output. [GH-401]
* builder/vmware: Autoanswer VMware dialogs. [GH-393]
* command/inspect: Fix weird output for default values for optional vars.
## 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:
+3
View File
@@ -90,3 +90,6 @@ $ bin/packer
You can run tests by typing `make test`.
This will run tests for Packer core along with all the core builders and commands and such that come with Packer.
If you make any changes to the code, run `make format` in order to automatically
format the code according to Go standards.
+12 -12
View File
@@ -53,6 +53,7 @@ func (b *Builder) Prepare(raws ...interface{}) error {
return err
}
b.config.tpl.UserVars = b.config.PackerUserVars
b.config.tpl.Funcs(awscommon.TemplateFuncs)
// Defaults
if b.config.ChrootMounts == nil {
@@ -167,11 +168,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{
@@ -189,15 +190,14 @@ func (b *Builder) Run(ui packer.Ui, hook packer.Hook, cache packer.Cache) (packe
&StepEarlyCleanup{},
&StepSnapshot{},
&StepRegisterAMI{},
&awscommon.StepAMIRegionCopy{
Regions: b.config.AMIRegions,
},
&awscommon.StepModifyAMIAttributes{
Description: b.config.AMIDescription,
Users: b.config.AMIUsers,
Groups: b.config.AMIGroups,
},
&awscommon.StepAMIRegionCopy{
Regions: b.config.AMIRegions,
Tags: b.config.AMITags,
},
&awscommon.StepCreateTags{
Tags: b.config.AMITags,
},
@@ -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) {}
+5 -3
View File
@@ -2,6 +2,7 @@ package common
import (
"fmt"
"github.com/mitchellh/goamz/aws"
"github.com/mitchellh/goamz/ec2"
"github.com/mitchellh/packer/packer"
"log"
@@ -51,9 +52,10 @@ func (a *Artifact) String() string {
func (a *Artifact) Destroy() error {
errors := make([]error, 0)
for _, imageId := range a.Amis {
log.Printf("Deregistering image ID: %s", imageId)
if _, err := a.Conn.DeregisterImage(imageId); err != nil {
for region, imageId := range a.Amis {
log.Printf("Deregistering image ID (%s) from region (%s)", imageId, region)
regionconn := ec2.New(a.Conn.Auth, aws.Regions[region])
if _, err := regionconn.DeregisterImage(imageId); err != nil {
errors = append(errors, err)
}
+4 -2
View File
@@ -13,11 +13,12 @@ type BlockDevice struct {
VolumeSize int64 `mapstructure:"volume_size"`
DeleteOnTermination bool `mapstructure:"delete_on_termination"`
IOPS int64 `mapstructure:"iops"`
NoDevice bool `mapstructure:"no_device"`
}
type BlockDevices struct {
AMIMappings []BlockDevice `mapstructure:"ami_block_device_mappings,squash"`
LaunchMappings []BlockDevice `mapstructure:"launch_block_device_mappings,squash"`
AMIMappings []BlockDevice `mapstructure:"ami_block_device_mappings"`
LaunchMappings []BlockDevice `mapstructure:"launch_block_device_mappings"`
}
func buildBlockDevices(b []BlockDevice) []ec2.BlockDeviceMapping {
@@ -32,6 +33,7 @@ func buildBlockDevices(b []BlockDevice) []ec2.BlockDeviceMapping {
VolumeSize: blockDevice.VolumeSize,
DeleteOnTermination: blockDevice.DeleteOnTermination,
IOPS: blockDevice.IOPS,
NoDevice: blockDevice.NoDevice,
})
}
return blockDevices
+28 -18
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
}
@@ -40,6 +40,12 @@ func InstanceStateRefreshFunc(conn *ec2.EC2, i *ec2.Instance) StateRefreshFunc {
return nil, "", err
}
if len(resp.Reservations) == 0 || len(resp.Reservations[0].Instances) == 0 {
// Sometimes AWS just has consistency issues and doesn't see
// our instance yet. Return an empty state.
return nil, "", nil
}
i = &resp.Reservations[0].Instances[0]
return i, i.State.Name, nil
}
@@ -57,27 +63,31 @@ func WaitForState(conf *StateChangeConf) (i interface{}, err error) {
return
}
if currentState == conf.Target {
return
}
if conf.StepState != nil {
if _, ok := conf.StepState[multistep.StateCancelled]; ok {
return nil, errors.New("interrupted")
// Check states only if we were able to refresh to an instance
// that exists.
if i != nil {
if currentState == conf.Target {
return
}
}
found := false
for _, allowed := range conf.Pending {
if currentState == allowed {
found = true
break
if conf.StepState != nil {
if _, ok := conf.StepState.GetOk(multistep.StateCancelled); ok {
return nil, errors.New("interrupted")
}
}
}
if !found {
fmt.Errorf("unexpected state '%s', wanted target '%s'", currentState, conf.Target)
return
found := false
for _, allowed := range conf.Pending {
if currentState == allowed {
found = true
break
}
}
if !found {
fmt.Errorf("unexpected state '%s', wanted target '%s'", currentState, conf.Target)
return
}
}
time.Sleep(2 * time.Second)
+25 -19
View File
@@ -11,17 +11,18 @@ import (
// RunConfig contains configuration for running an instance from a source
// AMI and details on how to access that launched image.
type RunConfig struct {
SourceAmi string `mapstructure:"source_ami"`
IamInstanceProfile string `mapstructure:"iam_instance_profile"`
InstanceType string `mapstructure:"instance_type"`
UserData string `mapstructure:"user_data"`
UserDataFile string `mapstructure:"user_data_file"`
RawSSHTimeout string `mapstructure:"ssh_timeout"`
SSHUsername string `mapstructure:"ssh_username"`
SSHPort int `mapstructure:"ssh_port"`
SecurityGroupId string `mapstructure:"security_group_id"`
SubnetId string `mapstructure:"subnet_id"`
VpcId string `mapstructure:"vpc_id"`
SourceAmi string `mapstructure:"source_ami"`
IamInstanceProfile string `mapstructure:"iam_instance_profile"`
InstanceType string `mapstructure:"instance_type"`
UserData string `mapstructure:"user_data"`
UserDataFile string `mapstructure:"user_data_file"`
RawSSHTimeout string `mapstructure:"ssh_timeout"`
SSHUsername string `mapstructure:"ssh_username"`
SSHPort int `mapstructure:"ssh_port"`
SecurityGroupId string `mapstructure:"security_group_id"`
SubnetId string `mapstructure:"subnet_id"`
TemporaryKeyPairName string `mapstructure:"temporary_key_pair_name"`
VpcId string `mapstructure:"vpc_id"`
// Unexported fields that are calculated from others
sshTimeout time.Duration
@@ -45,6 +46,10 @@ func (c *RunConfig) Prepare(t *packer.ConfigTemplate) []error {
c.RawSSHTimeout = "1m"
}
if c.TemporaryKeyPairName == "" {
c.TemporaryKeyPairName = "packer {{uuid}}"
}
// Validation
var err error
errs := make([]error, 0)
@@ -69,14 +74,15 @@ func (c *RunConfig) Prepare(t *packer.ConfigTemplate) []error {
}
templates := map[string]*string{
"iam_instance_profile": &c.IamInstanceProfile,
"instance_type": &c.InstanceType,
"ssh_timeout": &c.RawSSHTimeout,
"security_group_id": &c.SecurityGroupId,
"ssh_username": &c.SSHUsername,
"source_ami": &c.SourceAmi,
"subnet_id": &c.SubnetId,
"vpc_id": &c.VpcId,
"iam_instance_profile": &c.IamInstanceProfile,
"instance_type": &c.InstanceType,
"ssh_timeout": &c.RawSSHTimeout,
"security_group_id": &c.SecurityGroupId,
"ssh_username": &c.SSHUsername,
"source_ami": &c.SourceAmi,
"subnet_id": &c.SubnetId,
"temporary_key_pair_name": &c.TemporaryKeyPairName,
"vpc_id": &c.VpcId,
}
for n, ptr := range templates {
+12
View File
@@ -126,3 +126,15 @@ func TestRunConfigPrepare_UserDataFile(t *testing.T) {
t.Fatalf("err: %s", err)
}
}
func TestRunConfigPrepare_TemporaryKeyPairName(t *testing.T) {
c := testConfig()
c.TemporaryKeyPairName = ""
if err := c.Prepare(nil); len(err) != 0 {
t.Fatalf("err: %s", err)
}
if c.TemporaryKeyPairName == "" {
t.Fatal("keypair empty")
}
}
+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 {
+8 -28
View File
@@ -10,13 +10,12 @@ import (
type StepAMIRegionCopy struct {
Regions []string
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 +35,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,37 +43,18 @@ 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
}
// Need to re-apply Tags since they are not copied with the AMI
if len(s.Tags) > 0 {
ui.Say(fmt.Sprintf("Adding tags to AMI (%s)...", resp.ImageId))
var ec2Tags []ec2.Tag
for key, value := range s.Tags {
ui.Message(fmt.Sprintf("Adding tag: \"%s\": \"%s\"", key, value))
ec2Tags = append(ec2Tags, ec2.Tag{key, value})
}
_, 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
ui.Error(err.Error())
return multistep.ActionHalt
}
}
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...
}
+21 -18
View File
@@ -2,6 +2,7 @@ package common
import (
"fmt"
"github.com/mitchellh/goamz/aws"
"github.com/mitchellh/goamz/ec2"
"github.com/mitchellh/multistep"
"github.com/mitchellh/packer/packer"
@@ -11,33 +12,35 @@ 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)
ami := amis[ec2conn.Region.Name]
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)
if len(s.Tags) > 0 {
ui.Say(fmt.Sprintf("Adding tags to AMI (%s)...", ami))
for region, ami := range amis {
ui.Say(fmt.Sprintf("Adding tags to AMI (%s)...", ami))
var ec2Tags []ec2.Tag
for key, value := range s.Tags {
ui.Message(fmt.Sprintf("Adding tag: \"%s\": \"%s\"", key, value))
ec2Tags = append(ec2Tags, ec2.Tag{key, value})
}
var ec2Tags []ec2.Tag
for key, value := range s.Tags {
ui.Message(fmt.Sprintf("Adding tag: \"%s\": \"%s\"", key, value))
ec2Tags = append(ec2Tags, ec2.Tag{key, value})
}
_, err := ec2conn.CreateTags([]string{ami}, ec2Tags)
if err != nil {
err := fmt.Errorf("Error adding tags to AMI (%s): %s", ami, err)
state["error"] = err
ui.Error(err.Error())
return multistep.ActionHalt
regionconn := ec2.New(ec2conn.Auth, aws.Regions[region])
_, err := regionconn.CreateTags([]string{ami}, ec2Tags)
if err != nil {
err := fmt.Errorf("Error adding tags to AMI (%s): %s", ami, err)
state.Put("error", err)
ui.Error(err.Error())
return multistep.ActionHalt
}
}
}
return multistep.ActionContinue
}
func (s *StepCreateTags) Cleanup(state map[string]interface{}) {
func (s *StepCreateTags) Cleanup(state multistep.StateBag) {
// No cleanup...
}
+44 -17
View File
@@ -1,50 +1,77 @@
package common
import (
"cgl.tideland.biz/identifier"
"encoding/hex"
"fmt"
"github.com/mitchellh/goamz/ec2"
"github.com/mitchellh/multistep"
"github.com/mitchellh/packer/packer"
"log"
"os"
"runtime"
)
type StepKeyPair struct {
Debug bool
DebugKeyPath string
KeyPairName 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)
ui.Say(fmt.Sprintf("Creating temporary keypair: %s", s.KeyPairName))
keyResp, err := ec2conn.CreateKeyPair(s.KeyPairName)
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
}
// Set the keyname so we know to delete it later
s.keyName = keyName
s.keyName = s.KeyPairName
// Set some state data for use in future steps
state["keyPair"] = keyName
state["privateKey"] = keyResp.KeyMaterial
state.Put("keyPair", s.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)
@@ -2,6 +2,7 @@ package common
import (
"fmt"
"github.com/mitchellh/goamz/aws"
"github.com/mitchellh/goamz/ec2"
"github.com/mitchellh/multistep"
"github.com/mitchellh/packer/packer"
@@ -14,11 +15,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)
ami := amis[ec2conn.Region.Name]
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)
// Determine if there is any work to do.
valid := false
@@ -59,21 +59,24 @@ func (s *StepModifyAMIAttributes) Run(state map[string]interface{}) multistep.St
}
}
ui.Say("Modifying AMI attributes...")
for name, opts := range options {
ui.Message(fmt.Sprintf("Modifying: %s", name))
_, err := ec2conn.ModifyImageAttribute(ami, opts)
if err != nil {
err := fmt.Errorf("Error modify AMI attributes: %s", err)
state["error"] = err
ui.Error(err.Error())
return multistep.ActionHalt
for region, ami := range amis {
ui.Say(fmt.Sprintf("Modifying attributes on AMI (%s)...", ami))
regionconn := ec2.New(ec2conn.Auth, aws.Regions[region])
for name, opts := range options {
ui.Message(fmt.Sprintf("Modifying: %s", name))
_, err := regionconn.ModifyImageAttribute(ami, opts)
if err != nil {
err := fmt.Errorf("Error modify AMI attributes: %s", err)
state.Put("error", err)
ui.Error(err.Error())
return multistep.ActionHalt
}
}
}
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...")
+38
View File
@@ -0,0 +1,38 @@
package common
import (
"bytes"
"text/template"
)
func isalphanumeric(b byte) bool {
if '0' <= b && b <= '9' {
return true
}
if 'a' <= b && b <= 'z' {
return true
}
if 'A' <= b && b <= 'Z' {
return true
}
return false
}
// Clean up AMI name by replacing invalid characters with "-"
func templateCleanAMIName(s string) string {
allowed := []byte{'(', ')', ',', '/', '-', '_'}
b := []byte(s)
newb := make([]byte, len(b))
for i, c := range b {
if isalphanumeric(c) || bytes.IndexByte(allowed, c) != -1 {
newb[i] = c
} else {
newb[i] = '-'
}
}
return string(newb[:])
}
var TemplateFuncs = template.FuncMap{
"clean_ami_name": templateCleanAMIName,
}
@@ -0,0 +1,16 @@
package common
import (
"testing"
)
func TestAMITemplatePrepare_clean(t *testing.T) {
origName := "AMZamz09(),/-_:&^$%"
expected := "AMZamz09(),/-_-----"
name := templateCleanAMIName(origName)
if name != expected {
t.Fatalf("template names do not match: expected %s got %s\n", expected, name)
}
}
+19 -13
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"
@@ -43,6 +44,7 @@ func (b *Builder) Prepare(raws ...interface{}) error {
return err
}
b.config.tpl.UserVars = b.config.PackerUserVars
b.config.tpl.Funcs(awscommon.TemplateFuncs)
// Accumulate any errors
errs := common.CheckUnusedConfig(md)
@@ -72,21 +74,26 @@ 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),
KeyPairName: b.config.TemporaryKeyPairName,
},
&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,
@@ -104,15 +111,14 @@ func (b *Builder) Run(ui packer.Ui, hook packer.Hook, cache packer.Cache) (packe
&common.StepProvision{},
&stepStopInstance{},
&stepCreateAMI{},
&awscommon.StepAMIRegionCopy{
Regions: b.config.AMIRegions,
},
&awscommon.StepModifyAMIAttributes{
Description: b.config.AMIDescription,
Users: b.config.AMIUsers,
Groups: b.config.AMIGroups,
},
&awscommon.StepAMIRegionCopy{
Regions: b.config.AMIRegions,
Tags: b.config.AMITags,
},
&awscommon.StepCreateTags{
Tags: b.config.AMITags,
},
@@ -131,18 +137,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...
}
+18 -13
View File
@@ -56,6 +56,7 @@ func (b *Builder) Prepare(raws ...interface{}) error {
return err
}
b.config.tpl.UserVars = b.config.PackerUserVars
b.config.tpl.Funcs(awscommon.TemplateFuncs)
if b.config.BundleDestination == "" {
b.config.BundleDestination = "/tmp"
@@ -176,21 +177,26 @@ 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),
KeyPairName: b.config.TemporaryKeyPairName,
},
&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,
@@ -210,16 +216,15 @@ func (b *Builder) Run(ui packer.Ui, hook packer.Hook, cache packer.Cache) (packe
&StepBundleVolume{},
&StepUploadBundle{},
&StepRegisterAMI{},
&awscommon.StepAMIRegionCopy{
Regions: b.config.AMIRegions,
},
&awscommon.StepModifyAMIAttributes{
Description: b.config.AMIDescription,
Users: b.config.AMIUsers,
Groups: b.config.AMIGroups,
ProductCodes: b.config.AMIProductCodes,
},
&awscommon.StepAMIRegionCopy{
Regions: b.config.AMIRegions,
Tags: b.config.AMITags,
},
&awscommon.StepCreateTags{
Tags: b.config.AMITags,
},
@@ -238,18 +243,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)
+46 -34
View File
@@ -14,6 +14,7 @@ import (
"net/http"
"net/url"
"strings"
"time"
)
const DIGITALOCEAN_API_URL = "https://api.digitalocean.com"
@@ -191,46 +192,57 @@ func NewRequest(d DigitalOceanClient, path string, params url.Values) (map[strin
url := fmt.Sprintf("%s/%s?%s", DIGITALOCEAN_API_URL, path, params.Encode())
var decodedResponse map[string]interface{}
// Do some basic scrubbing so sensitive information doesn't appear in logs
scrubbedUrl := strings.Replace(url, d.ClientID, "CLIENT_ID", -1)
scrubbedUrl = strings.Replace(scrubbedUrl, d.APIKey, "API_KEY", -1)
log.Printf("sending new request to digitalocean: %s", scrubbedUrl)
resp, err := client.Get(url)
if err != nil {
return decodedResponse, err
}
body, err := ioutil.ReadAll(resp.Body)
resp.Body.Close()
if err != nil {
return decodedResponse, err
}
log.Printf("response from digitalocean: %s", body)
err = json.Unmarshal(body, &decodedResponse)
// Check for bad JSON
if err != nil {
err = errors.New(fmt.Sprintf("Failed to decode JSON response (HTTP %v) from DigitalOcean: %s",
resp.StatusCode, body))
return decodedResponse, err
}
// Check for errors sent by digitalocean
status := decodedResponse["status"]
if status != "OK" {
// Get the actual error message if there is one
if status == "ERROR" {
status = decodedResponse["error_message"]
var lastErr error
for attempts := 1; attempts < 10; attempts++ {
resp, err := client.Get(url)
if err != nil {
return nil, err
}
err = errors.New(fmt.Sprintf("Received bad response (HTTP %v) from DigitalOcean: %s", resp.StatusCode, status))
return decodedResponse, err
body, err := ioutil.ReadAll(resp.Body)
resp.Body.Close()
if err != nil {
return nil, err
}
log.Printf("response from digitalocean: %s", body)
var decodedResponse map[string]interface{}
err = json.Unmarshal(body, &decodedResponse)
if err != nil {
err = errors.New(fmt.Sprintf("Failed to decode JSON response (HTTP %v) from DigitalOcean: %s",
resp.StatusCode, body))
return decodedResponse, err
}
// Check for errors sent by digitalocean
status := decodedResponse["status"].(string)
if status == "OK" {
return decodedResponse, nil
}
if status == "ERROR" {
status = decodedResponse["error_message"].(string)
}
lastErr = errors.New(fmt.Sprintf("Received error from DigitalOcean (%d): %s",
resp.StatusCode, status))
log.Println(lastErr)
if strings.Contains(status, "a pending event") {
// Retry, DigitalOcean sends these dumb "pending event"
// errors all the time.
time.Sleep(5 * time.Second)
continue
}
// Some other kind of error. Just return.
return decodedResponse, lastErr
}
return decodedResponse, nil
return nil, lastErr
}
+9 -25
View File
@@ -34,13 +34,11 @@ type config struct {
SSHPort uint `mapstructure:"ssh_port"`
RawSSHTimeout string `mapstructure:"ssh_timeout"`
RawEventDelay string `mapstructure:"event_delay"`
RawStateTimeout string `mapstructure:"state_timeout"`
// These are unexported since they're set by other fields
// being set.
sshTimeout time.Duration
eventDelay time.Duration
stateTimeout time.Duration
tpl *packer.ConfigTemplate
@@ -113,12 +111,6 @@ func (b *Builder) Prepare(raws ...interface{}) error {
b.config.RawSSHTimeout = "1m"
}
if b.config.RawEventDelay == "" {
// Default to 5 second delays after creating events
// to allow DO to process
b.config.RawEventDelay = "5s"
}
if b.config.RawStateTimeout == "" {
// Default to 6 minute timeouts waiting for
// desired state. i.e waiting for droplet to become active
@@ -131,7 +123,6 @@ func (b *Builder) Prepare(raws ...interface{}) error {
"snapshot_name": &b.config.SnapshotName,
"ssh_username": &b.config.SSHUsername,
"ssh_timeout": &b.config.RawSSHTimeout,
"event_delay": &b.config.RawEventDelay,
"state_timeout": &b.config.RawStateTimeout,
}
@@ -162,13 +153,6 @@ func (b *Builder) Prepare(raws ...interface{}) error {
}
b.config.sshTimeout = sshTimeout
eventDelay, err := time.ParseDuration(b.config.RawEventDelay)
if err != nil {
errs = packer.MultiErrorAppend(
errs, fmt.Errorf("Failed parsing event_delay: %s", err))
}
b.config.eventDelay = eventDelay
stateTimeout, err := time.ParseDuration(b.config.RawStateTimeout)
if err != nil {
errs = packer.MultiErrorAppend(
@@ -189,11 +173,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 +208,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,
}
-32
View File
@@ -258,38 +258,6 @@ func TestBuilderPrepare_SSHTimeout(t *testing.T) {
}
func TestBuilderPrepare_EventDelay(t *testing.T) {
var b Builder
config := testConfig()
// Test default
err := b.Prepare(config)
if err != nil {
t.Fatalf("should not have error: %s", err)
}
if b.config.RawEventDelay != "5s" {
t.Errorf("invalid: %d", b.config.RawEventDelay)
}
// Test set
config["event_delay"] = "10s"
b = Builder{}
err = b.Prepare(config)
if err != nil {
t.Fatalf("should not have error: %s", err)
}
// Test bad
config["event_delay"] = "tubes"
b = Builder{}
err = b.Prepare(config)
if err == nil {
t.Fatal("should have error")
}
}
func TestBuilderPrepare_StateTimeout(t *testing.T) {
var b Builder
config := testConfig()
+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 {
+14 -23
View File
@@ -6,19 +6,17 @@ import (
"fmt"
"github.com/mitchellh/multistep"
"github.com/mitchellh/packer/packer"
"log"
"time"
)
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 +27,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,36 +36,29 @@ 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...")
// Sleep arbitrarily before sending destroy request
// Otherwise we get "pending event" errors, even though there isn't
// one.
log.Printf("Sleeping for %v, event_delay", c.RawEventDelay)
time.Sleep(c.eventDelay)
err := client.DestroyDroplet(s.dropletId)
curlstr := fmt.Sprintf("curl '%v/droplets/%v/destroy?client_id=%v&api_key=%v'",
DIGITALOCEAN_API_URL, s.dropletId, c.ClientID, c.APIKey)
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)
+10 -10
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)
err := waitForDropletState("active", dropletId, client, c.stateTimeout)
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
}
+27 -22
View File
@@ -5,45 +5,50 @@ import (
"github.com/mitchellh/multistep"
"github.com/mitchellh/packer/packer"
"log"
"time"
)
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
// one.
log.Printf("Sleeping for %v, event_delay", c.RawEventDelay)
time.Sleep(c.eventDelay)
_, status, err := client.DropletStatus(dropletId)
if err != nil {
err := fmt.Errorf("Error checking droplet state: %s", err)
state.Put("error", err)
ui.Error(err.Error())
return multistep.ActionHalt
}
// Poweroff the droplet so it can be snapshot
err := client.PowerOffDroplet(dropletId)
if status == "off" {
// Droplet is already off, don't do anything
return multistep.ActionContinue
}
// Pull the plug on the Droplet
ui.Say("Forcefully shutting down Droplet...")
err = client.PowerOffDroplet(dropletId)
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
}
log.Println("Waiting for poweroff event to complete...")
// This arbitrary sleep is because we can't wait for the state
// of the droplet to be 'off', as stepShutdown should already
// have accomplished that, and the state indicator is the same.
// We just have to assume that this event will process quickly.
log.Printf("Sleeping for %v, event_delay", c.RawEventDelay)
time.Sleep(c.eventDelay)
err = waitForDropletState("off", dropletId, client, c.stateTimeout)
if err != nil {
state.Put("error", err)
ui.Error(err.Error())
return multistep.ActionHalt
}
return multistep.ActionContinue
}
func (s *stepPowerOff) Cleanup(state map[string]interface{}) {
func (s *stepPowerOff) Cleanup(state multistep.StateBag) {
// no cleanup
}
+45 -20
View File
@@ -10,40 +10,65 @@ 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)
// Sleep arbitrarily before sending the request
// Otherwise we get "pending event" errors, even though there isn't
// one.
log.Printf("Sleeping for %v, event_delay", c.RawEventDelay)
time.Sleep(c.eventDelay)
func (s *stepShutdown) Run(state multistep.StateBag) multistep.StepAction {
client := state.Get("client").(*DigitalOceanClient)
ui := state.Get("ui").(packer.Ui)
dropletId := state.Get("droplet_id").(uint)
// Gracefully power off the droplet. We have to retry this a number
// of times because sometimes it says it completed when it actually
// did absolutely nothing (*ALAKAZAM!* magic!). We give up after
// a pretty arbitrary amount of time.
ui.Say("Gracefully shutting down droplet...")
err := client.ShutdownDroplet(dropletId)
if err != nil {
// If we get an error the first time, actually report it
err := fmt.Errorf("Error shutting down droplet: %s", err)
state["error"] = err
state.Put("error", err)
ui.Error(err.Error())
return multistep.ActionHalt
}
ui.Say("Waiting for droplet to shutdown...")
// A channel we use as a flag to end our goroutines
done := make(chan struct{})
shutdownRetryDone := make(chan struct{})
err = waitForDropletState("off", dropletId, client, c)
// Make sure we wait for the shutdown retry goroutine to end
// before moving on.
defer func() {
close(done)
<-shutdownRetryDone
}()
// Start a goroutine that just keeps trying to shut down the
// droplet.
go func() {
defer close(shutdownRetryDone)
for attempts := 2; attempts > 0; attempts++ {
log.Printf("ShutdownDroplet attempt #%d...", attempts)
err := client.ShutdownDroplet(dropletId)
if err != nil {
log.Printf("Shutdown retry error: %s", err)
}
select {
case <-done:
return
case <-time.After(20 * time.Second):
// Retry!
}
}
}()
err = waitForDropletState("off", dropletId, client, 2*time.Minute)
if err != nil {
err := fmt.Errorf("Error waiting for droplet to become 'off': %s", err)
state["error"] = err
ui.Error(err.Error())
return multistep.ActionHalt
log.Printf("Error waiting for graceful off: %s", err)
}
return multistep.ActionContinue
}
func (s *stepShutdown) Cleanup(state map[string]interface{}) {
func (s *stepShutdown) Cleanup(state multistep.StateBag) {
// no cleanup
}
+13 -13
View File
@@ -10,26 +10,26 @@ 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
}
ui.Say("Waiting for snapshot to complete...")
err = waitForDropletState("active", dropletId, client, c)
err = waitForDropletState("active", dropletId, client, c.stateTimeout)
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
}
+25 -24
View File
@@ -1,16 +1,18 @@
package digitalocean
import (
"errors"
"fmt"
"log"
"time"
)
// waitForState simply blocks until the droplet is in
// a state we expect, while eventually timing out.
func waitForDropletState(desiredState string, dropletId uint, client *DigitalOceanClient, c config) error {
active := make(chan bool, 1)
func waitForDropletState(desiredState string, dropletId uint, client *DigitalOceanClient, timeout time.Duration) error {
done := make(chan struct{})
defer close(done)
result := make(chan error, 1)
go func() {
attempts := 0
for {
@@ -19,36 +21,35 @@ func waitForDropletState(desiredState string, dropletId uint, client *DigitalOce
log.Printf("Checking droplet status... (attempt: %d)", attempts)
_, status, err := client.DropletStatus(dropletId)
if err != nil {
log.Println(err)
break
result <- err
return
}
if status == desiredState {
break
result <- nil
return
}
// Wait 3 seconds in between
time.Sleep(3 * time.Second)
}
active <- true
// Verify we shouldn't exit
select {
case <-done:
// We finished, so just exit the goroutine
return
default:
// Keep going
}
}
}()
log.Printf("Waiting for up to %s for droplet to become %s", c.RawStateTimeout, desiredState)
timeout := time.After(c.stateTimeout)
ActiveWaitLoop:
for {
select {
case <-active:
// We connected. Just break the loop.
break ActiveWaitLoop
case <-timeout:
err := errors.New("Timeout while waiting to for droplet to become active")
return err
}
log.Printf("Waiting for up to %d seconds for droplet to become %s", timeout, desiredState)
select {
case err := <-result:
return err
case <-time.After(timeout):
err := fmt.Errorf("Timeout while waiting to for droplet to become '%s'", desiredState)
return err
}
// If we got this far, there were no errors
return nil
}
+21 -3
View File
@@ -9,9 +9,11 @@ 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"`
Project string `mapstructure:"project"`
Provider string `mapstructure:"provider"`
RawRegion string `mapstructure:"region"`
}
// Auth returns a valid Auth object for access to openstack services, or
@@ -19,6 +21,7 @@ type AccessConfig struct {
func (c *AccessConfig) Auth() (gophercloud.AccessProvider, error) {
username := c.Username
password := c.Password
project := c.Project
provider := c.Provider
if username == "" {
@@ -27,6 +30,9 @@ func (c *AccessConfig) Auth() (gophercloud.AccessProvider, error) {
if password == "" {
password = os.Getenv("SDK_PASSWORD")
}
if project == "" {
project = os.Getenv("SDK_PROJECT")
}
if provider == "" {
provider = os.Getenv("SDK_PROVIDER")
}
@@ -37,9 +43,17 @@ func (c *AccessConfig) Auth() (gophercloud.AccessProvider, error) {
AllowReauth: true,
}
if project != "" {
authoptions.TenantName = project
}
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 +79,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 {
+20 -9
View File
@@ -32,6 +32,7 @@ type config struct {
GuestAdditionsURL string `mapstructure:"guest_additions_url"`
GuestAdditionsSHA256 string `mapstructure:"guest_additions_sha256"`
GuestOSType string `mapstructure:"guest_os_type"`
HardDriveInterface string `mapstructure:"hard_drive_interface"`
Headless bool `mapstructure:"headless"`
HTTPDir string `mapstructure:"http_directory"`
HTTPPortMin uint `mapstructure:"http_port_min"`
@@ -89,6 +90,10 @@ func (b *Builder) Prepare(raws ...interface{}) error {
b.config.GuestAdditionsPath = "VBoxGuestAdditions.iso"
}
if b.config.HardDriveInterface == "" {
b.config.HardDriveInterface = "ide"
}
if b.config.GuestOSType == "" {
b.config.GuestOSType = "Other"
}
@@ -141,6 +146,7 @@ func (b *Builder) Prepare(raws ...interface{}) error {
templates := map[string]*string{
"guest_additions_sha256": &b.config.GuestAdditionsSHA256,
"guest_os_type": &b.config.GuestOSType,
"hard_drive_interface": &b.config.HardDriveInterface,
"http_directory": &b.config.HTTPDir,
"iso_checksum": &b.config.ISOChecksum,
"iso_checksum_type": &b.config.ISOChecksumType,
@@ -209,6 +215,11 @@ func (b *Builder) Prepare(raws ...interface{}) error {
errs, errors.New("invalid format, only 'ovf' or 'ova' are allowed"))
}
if b.config.HardDriveInterface != "ide" && b.config.HardDriveInterface != "sata" {
errs = packer.MultiErrorAppend(
errs, errors.New("hard_drive_interface can only be ide or sata"))
}
if b.config.HTTPPortMin > b.config.HTTPPortMax {
errs = packer.MultiErrorAppend(
errs, errors.New("http_port_min must be less than http_port_max"))
@@ -368,12 +379,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 +399,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.")
}
+32
View File
@@ -252,6 +252,38 @@ func TestBuilderPrepare_GuestAdditionsURL(t *testing.T) {
}
}
func TestBuilderPrepare_HardDriveInterface(t *testing.T) {
var b Builder
config := testConfig()
// Test a default boot_wait
delete(config, "hard_drive_interface")
err := b.Prepare(config)
if err != nil {
t.Fatalf("err: %s", err)
}
if b.config.HardDriveInterface != "ide" {
t.Fatalf("bad: %s", b.config.HardDriveInterface)
}
// Test with a bad
config["hard_drive_interface"] = "fake"
b = Builder{}
err = b.Prepare(config)
if err == nil {
t.Fatal("should have error")
}
// Test with a good
config["hard_drive_interface"] = "sata"
b = Builder{}
err = b.Prepare(config)
if err != nil {
t.Fatalf("should not have error: %s", err)
}
}
func TestBuilderPrepare_HTTPPort(t *testing.T) {
var b Builder
config := testConfig()
+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,
+31 -10
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,21 +34,42 @@ 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
}
// Add the IDE controller so we can later attach the disk
// Add the IDE controller so we can later attach the disk.
// When the hard disk controller is not IDE, this device is still used
// by VirtualBox to deliver the guest extensions.
controllerName := "IDE Controller"
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
}
// Add a SATA controller if we were asked to use SATA. We still attach
// the IDE controller above because some other things (disks) require
// that.
if config.HardDriveInterface == "sata" {
controllerName = "SATA Controller"
command = []string{
"storagectl", vmName,
"--name", controllerName,
"--add", "sata",
"--sataportcount", "1",
}
if err := driver.VBoxManage(command...); err != nil {
err := fmt.Errorf("Error creating disk controller: %s", err)
state.Put("error", err)
ui.Error(err.Error())
return multistep.ActionHalt
}
}
// Attach the disk to the controller
command = []string{
"storageattach", vmName,
@@ -60,7 +81,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 +89,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
}
+13 -13
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,14 +43,14 @@ 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
}
}
// Export the VM to an OVF
outputPath := filepath.Join(config.OutputDir, "packer."+config.Format)
outputPath := filepath.Join(config.OutputDir, vmName+"."+config.Format)
command = []string{
"export",
@@ -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) {}
+14 -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,27 +81,30 @@ 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"
vmxData["floppy0.fileName"] = floppyPathRaw.(string)
}
// Set this so that no dialogs ever appear from Packer.
vmxData["msg.autoAnswer"] = "true"
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) {}
+26 -4
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]
padding := strings.Repeat(" ", max-len(k))
output := fmt.Sprintf(" %s%s = %s", k, padding, v)
if v.Required {
continue
}
ui.Machine("template-variable", k, v)
padding := strings.Repeat(" ", max-len(k))
output := fmt.Sprintf(" %s%s = %s", k, padding, v.Default)
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
}

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