Compare commits
213 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| ca9426d6e7 | |||
| 692a45effa | |||
| 9d954538dd | |||
| 4484810c05 | |||
| e62e3c5af8 | |||
| 7e95b4e075 | |||
| a6397c160a | |||
| ef7b6d613e | |||
| 3c301dc73a | |||
| 78133976eb | |||
| 1d8ecd4444 | |||
| e6e4face8a | |||
| 5f929437d4 | |||
| 1525555468 | |||
| 5ff156ef40 | |||
| d5555d45cc | |||
| 28d25c718c | |||
| f79c5543d1 | |||
| 3ad9d27c86 | |||
| 97d042b7a3 | |||
| 6093789ffc | |||
| 9ed461cbd3 | |||
| c1e469a0f7 | |||
| 3732d6863a | |||
| 6d946fe87c | |||
| 30eccf4130 | |||
| 2d5ca19b37 | |||
| cfe3eee547 | |||
| bda4754e68 | |||
| 0f64704725 | |||
| cb8fa1010a | |||
| 981f4fcd80 | |||
| 71b1548e8d | |||
| 74017ec8d8 | |||
| d4ed1c37e8 | |||
| f2687cbdd0 | |||
| 02e23aa10b | |||
| fcb12262f6 | |||
| f8ff737b0a | |||
| 3ddfcf6720 | |||
| dde88c5ce8 | |||
| 0fadb8d02f | |||
| db418f38c4 | |||
| 0a1d696457 | |||
| 67e5ff92bc | |||
| 91dc87f64e | |||
| 11a68992ad | |||
| 0bfbe02424 | |||
| 61d9bb73a7 | |||
| 207c1ebabd | |||
| 2bd13bbfe7 | |||
| 8374b981a4 | |||
| 6b53feb3c6 | |||
| dba8b5209d | |||
| bb8e7f7814 | |||
| d6fca7975a | |||
| b52e5d699d | |||
| 2941836ab0 | |||
| 20737c9fe9 | |||
| 7668bcb41f | |||
| 7c56148f95 | |||
| f9feeac355 | |||
| 6a8e4f1439 | |||
| 3e801a1b2b | |||
| b295c1670e | |||
| 219c2589d8 | |||
| 5fbd374646 | |||
| eea960ba57 | |||
| d258c15b61 | |||
| d6e7f4b27d | |||
| 642c651df4 | |||
| 4e0c25c95f | |||
| d29c5cd231 | |||
| f5850199ba | |||
| dbf978f8d1 | |||
| 4c537105ef | |||
| 1bad19f58b | |||
| 06b5b4304d | |||
| 5315aaba0d | |||
| 3a899892dc | |||
| ef0a077168 | |||
| de0c59cc18 | |||
| 53ab6b9607 | |||
| 6f8afdeaf6 | |||
| 1f63abc622 | |||
| 46a2b81a1d | |||
| 7fe487cd4f | |||
| 06b83d148b | |||
| 9035b51165 | |||
| a775a58129 | |||
| 694275c7cd | |||
| 27a2270593 | |||
| 8c30dc78ac | |||
| c4cf8553c8 | |||
| a511b603e2 | |||
| 81edbb4173 | |||
| 147eea955e | |||
| 67c71aa836 | |||
| 535d2a7cfc | |||
| 5736dbd3aa | |||
| b57fa3cbdb | |||
| a59bbd892d | |||
| fb8b82fca5 | |||
| e26f7e87e0 | |||
| 25e7b90a96 | |||
| c0ef20bb80 | |||
| 6746ffc133 | |||
| 682cd97991 | |||
| 8dd3850482 | |||
| e89f182b33 | |||
| 25a310cf45 | |||
| bf1a2f4899 | |||
| 250077a0ff | |||
| 89a29e7b21 | |||
| 13a4c34bc5 | |||
| 175408dcb2 | |||
| 3043eddfaf | |||
| 0058929186 | |||
| fc62adfe5d | |||
| cc8d0ad2eb | |||
| 45c88e2aea | |||
| 290b9ef836 | |||
| a1c96d3b97 | |||
| 0271d9a15e | |||
| ede100bb7b | |||
| 2da53f54f4 | |||
| 5477d7166b | |||
| ab5b094d73 | |||
| eb8f6c9769 | |||
| f2e909356e | |||
| 952b2c921f | |||
| a613446205 | |||
| 907ca4d951 | |||
| 822c52c46f | |||
| 082dc6c14d | |||
| 797255fca6 | |||
| 05e1654eb8 | |||
| 12547d3203 | |||
| 8024dde44d | |||
| 0b16317401 | |||
| 06515fd195 | |||
| 47ee7ca977 | |||
| b6a6c8571b | |||
| caaf679f27 | |||
| 659383618f | |||
| 84b0bf6dba | |||
| 86c43a1bc9 | |||
| 6cd10286db | |||
| 4e87275f87 | |||
| 3001c16eee | |||
| 5f3ae970c0 | |||
| 5be22de623 | |||
| 15132a9f17 | |||
| ffaca0a226 | |||
| eea558d2a5 | |||
| 4370726ba7 | |||
| 73ac1fc82c | |||
| 375eb57077 | |||
| e6881cee6a | |||
| d4c4581885 | |||
| 0c11cf7c88 | |||
| 67887c11e4 | |||
| f1eae75389 | |||
| 7681d618d6 | |||
| 90b4dc7860 | |||
| b842e53028 | |||
| 3abacee131 | |||
| 4e2936d270 | |||
| 570be4d581 | |||
| ab99d424ef | |||
| a1b4887324 | |||
| 23b21ff4d0 | |||
| 8e7c2796fc | |||
| b5606af9e2 | |||
| 1b8646c963 | |||
| 36698d4b6d | |||
| 8d5f8dc423 | |||
| ea5361a9ac | |||
| a82f1c18ee | |||
| 98f3bc57a0 | |||
| 964546e050 | |||
| 1719cf8006 | |||
| 6e098d1aaf | |||
| 66f7f5aad5 | |||
| 84001c7c76 | |||
| 21171db836 | |||
| 2e90660afc | |||
| cd12c3e030 | |||
| 017d27d7eb | |||
| 27a6dac7fd | |||
| 98ddf043cc | |||
| dfb44a2abe | |||
| dbae49f0c8 | |||
| a03c66272f | |||
| 65eb05384f | |||
| c157da38c9 | |||
| 4c4020f723 | |||
| e4c22472bb | |||
| dc9a803efd | |||
| d1fff21045 | |||
| f97b88654f | |||
| e613b0cdcd | |||
| 7ed2498407 | |||
| 5d8a211774 | |||
| 1b91110a59 | |||
| 729232c7c7 | |||
| 293db31a0f | |||
| 9f55695964 | |||
| a1893b0bce | |||
| fc595de0fd | |||
| e21e980647 | |||
| 332d666180 | |||
| 87f47ba6de |
+112
@@ -1,3 +1,115 @@
|
||||
## 0.3.8 (September 22, 2013)
|
||||
|
||||
FEATURES:
|
||||
|
||||
* core: You can now specify `only` and `except` configurations on any
|
||||
provisioner or post-processor to specify a list of builds that they
|
||||
are valid for. [GH-438]
|
||||
* builders/virtualbox: Guest additions can be attached rather than uploaded,
|
||||
easier to handle for Windows guests. [GH-405]
|
||||
* provisioner/chef-solo: Ability to specify a custom Chef configuration
|
||||
template.
|
||||
* provisioner/chef-solo: Roles and data bags support. [GH-348]
|
||||
|
||||
IMPROVEMENTS:
|
||||
|
||||
* core: User variables can now be used for integer, boolean, etc.
|
||||
values. [GH-418]
|
||||
* core: Plugins made with incompatible versions will no longer load.
|
||||
* builder/amazon/all: Interrupts work while waiting for AMI to be ready.
|
||||
* provisioner/shell: Script line-endings are automatically converted to
|
||||
Unix-style line-endings. Can be disabled by setting "binary" to "true".
|
||||
[GH-277]
|
||||
|
||||
BUG FIXES:
|
||||
|
||||
* core: Set TCP KeepAlives on internally created RPC connections so that
|
||||
they don't die. [GH-416]
|
||||
* builder/amazon/all: While waiting for AMI, will detect "failed" state.
|
||||
* builder/amazon/all: Waiting for state will detect if the resource (AMI,
|
||||
instance, etc.) disappears from under it.
|
||||
* builder/amazon/instance: Exclude only contents of /tmp, not /tmp
|
||||
itself. [GH-437]
|
||||
* builder/amazon/instance: Make AccessKey/SecretKey available to bundle
|
||||
command even when they come from the environment. [GH-434]
|
||||
* builder/virtualbox: F1-F12 and delete scancodes now work. [GH-425]
|
||||
* post-processor/vagrant: Override configurations properly work. [GH-426]
|
||||
* provisioner/puppet-masterless: Fix failure case when both facter vars
|
||||
are used and prevent_sudo. [GH-415]
|
||||
* provisioner/puppet-masterless: User variables now work properly in
|
||||
manifest file and hiera path. [GH-448]
|
||||
|
||||
## 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
-1
@@ -47,7 +47,9 @@ it raises the chances we can quickly merge or address your contributions.
|
||||
If you have never worked with Go before, you will have to complete the
|
||||
following steps in order to be able to compile and test Packer.
|
||||
|
||||
1. Install Go. On a Mac, you can `brew install go`.
|
||||
1. Install Go. On a Mac, you can `brew install go`. Make sure the Go
|
||||
version is at least Go 1.1. Packer will not work with anything less than
|
||||
Go 1.1.
|
||||
|
||||
2. Set and export the `GOPATH` environment variable. For example, you can
|
||||
add `export GOPATH=$HOME/Documents/golang` to your `.bash_profile`.
|
||||
|
||||
@@ -2,6 +2,7 @@ NO_COLOR=\033[0m
|
||||
OK_COLOR=\033[32;01m
|
||||
ERROR_COLOR=\033[31;01m
|
||||
WARN_COLOR=\033[33;01m
|
||||
DEPS = $(go list -f '{{range .TestImports}}{{.}} {{end}}' ./...)
|
||||
|
||||
all: deps
|
||||
@mkdir -p bin/
|
||||
@@ -11,7 +12,12 @@ all: deps
|
||||
deps:
|
||||
@echo "$(OK_COLOR)==> Installing dependencies$(NO_COLOR)"
|
||||
@go get -d -v ./...
|
||||
@go list -f '{{range .TestImports}}{{.}} {{end}}' ./... | xargs -n1 go get -d
|
||||
@echo $(DEPS) | xargs -n1 go get -d
|
||||
|
||||
updatedeps:
|
||||
@echo "$(OK_COLOR)==> Updating all dependencies$(NO_COLOR)"
|
||||
@go get -d -v -u ./...
|
||||
@echo $(DEPS) | xargs -n1 go get -d -u
|
||||
|
||||
clean:
|
||||
@rm -rf bin/ local/ pkg/ src/ website/.sass-cache website/build
|
||||
@@ -23,4 +29,4 @@ test: deps
|
||||
@echo "$(OK_COLOR)==> Testing Packer...$(NO_COLOR)"
|
||||
go test ./...
|
||||
|
||||
.PHONY: all deps format test
|
||||
.PHONY: all clean deps format test updatedeps
|
||||
|
||||
@@ -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.
|
||||
|
||||
@@ -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,
|
||||
}
|
||||
|
||||
@@ -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
|
||||
}
|
||||
|
||||
@@ -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) {}
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -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) {}
|
||||
|
||||
@@ -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) {}
|
||||
|
||||
@@ -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
|
||||
}
|
||||
|
||||
@@ -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) {}
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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,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,21 @@ 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
|
||||
stateChange := awscommon.StateChangeConf{
|
||||
Conn: ec2conn,
|
||||
Pending: []string{"pending"},
|
||||
Target: "available",
|
||||
Refresh: awscommon.AMIStateRefreshFunc(ec2conn, registerResp.ImageId),
|
||||
StepState: state,
|
||||
}
|
||||
|
||||
ui.Say("Waiting for AMI to become ready...")
|
||||
if err := awscommon.WaitForAMI(ec2conn, registerResp.ImageId); err != nil {
|
||||
if _, err := awscommon.WaitForState(&stateChange); 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 +71,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) {}
|
||||
|
||||
@@ -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) {}
|
||||
|
||||
@@ -18,7 +18,14 @@ type AccessConfig struct {
|
||||
// Auth returns a valid aws.Auth object for access to AWS services, or
|
||||
// an error if the authentication couldn't be resolved.
|
||||
func (c *AccessConfig) Auth() (aws.Auth, error) {
|
||||
return aws.GetAuth(c.AccessKey, c.SecretKey)
|
||||
auth, err := aws.GetAuth(c.AccessKey, c.SecretKey)
|
||||
if err == nil {
|
||||
// Store the accesskey and secret that we got...
|
||||
c.AccessKey = auth.AccessKey
|
||||
c.SecretKey = auth.SecretKey
|
||||
}
|
||||
|
||||
return auth, err
|
||||
}
|
||||
|
||||
// Region returns the aws.Region object for access to AWS services, requesting
|
||||
|
||||
@@ -1,30 +0,0 @@
|
||||
package common
|
||||
|
||||
import (
|
||||
"github.com/mitchellh/goamz/ec2"
|
||||
"log"
|
||||
"time"
|
||||
)
|
||||
|
||||
// WaitForAMI waits for the given AMI ID to become ready.
|
||||
func WaitForAMI(c *ec2.EC2, imageId string) error {
|
||||
for {
|
||||
imageResp, err := c.Images([]string{imageId}, ec2.NewFilter())
|
||||
if err != nil {
|
||||
if ec2err, ok := err.(*ec2.Error); ok && ec2err.Code == "InvalidAMIID.NotFound" {
|
||||
log.Println("AMI not found, probably state issues on AWS side. Trying again.")
|
||||
continue
|
||||
}
|
||||
|
||||
return err
|
||||
}
|
||||
|
||||
if imageResp.Images[0].State == "available" {
|
||||
return nil
|
||||
}
|
||||
|
||||
log.Printf("Image in state %s, sleeping 2s before checking again",
|
||||
imageResp.Images[0].State)
|
||||
time.Sleep(2 * time.Second)
|
||||
}
|
||||
}
|
||||
@@ -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)
|
||||
}
|
||||
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -1,87 +0,0 @@
|
||||
package common
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"fmt"
|
||||
"github.com/mitchellh/goamz/ec2"
|
||||
"github.com/mitchellh/multistep"
|
||||
"log"
|
||||
"time"
|
||||
)
|
||||
|
||||
// StateRefreshFunc is a function type used for StateChangeConf that is
|
||||
// responsible for refreshing the item being watched for a state change.
|
||||
//
|
||||
// It returns three results. `result` is any object that will be returned
|
||||
// as the final object after waiting for state change. This allows you to
|
||||
// return the final updated object, for example an EC2 instance after refreshing
|
||||
// it.
|
||||
//
|
||||
// `state` is the latest state of that object. And `err` is any error that
|
||||
// may have happened while refreshing the state.
|
||||
type StateRefreshFunc func() (result interface{}, state string, err error)
|
||||
|
||||
// StateChangeConf is the configuration struct used for `WaitForState`.
|
||||
type StateChangeConf struct {
|
||||
Conn *ec2.EC2
|
||||
Pending []string
|
||||
Refresh StateRefreshFunc
|
||||
StepState map[string]interface{}
|
||||
Target string
|
||||
}
|
||||
|
||||
// InstanceStateRefreshFunc returns a StateRefreshFunc that is used to watch
|
||||
// an EC2 instance.
|
||||
func InstanceStateRefreshFunc(conn *ec2.EC2, i *ec2.Instance) StateRefreshFunc {
|
||||
return func() (interface{}, string, error) {
|
||||
resp, err := conn.Instances([]string{i.InstanceId}, ec2.NewFilter())
|
||||
if err != nil {
|
||||
log.Printf("Error on InstanceStateRefresh: %s", err)
|
||||
return nil, "", err
|
||||
}
|
||||
|
||||
i = &resp.Reservations[0].Instances[0]
|
||||
return i, i.State.Name, nil
|
||||
}
|
||||
}
|
||||
|
||||
// WaitForState watches an object and waits for it to achieve a certain
|
||||
// state.
|
||||
func WaitForState(conf *StateChangeConf) (i interface{}, err error) {
|
||||
log.Printf("Waiting for state to become: %s", conf.Target)
|
||||
|
||||
for {
|
||||
var currentState string
|
||||
i, currentState, err = conf.Refresh()
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
if currentState == conf.Target {
|
||||
return
|
||||
}
|
||||
|
||||
if conf.StepState != nil {
|
||||
if _, ok := conf.StepState[multistep.StateCancelled]; ok {
|
||||
return nil, errors.New("interrupted")
|
||||
}
|
||||
}
|
||||
|
||||
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)
|
||||
}
|
||||
|
||||
return
|
||||
}
|
||||
@@ -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 {
|
||||
|
||||
@@ -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")
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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 {
|
||||
|
||||
@@ -0,0 +1,133 @@
|
||||
package common
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"fmt"
|
||||
"github.com/mitchellh/goamz/ec2"
|
||||
"github.com/mitchellh/multistep"
|
||||
"log"
|
||||
"time"
|
||||
)
|
||||
|
||||
// StateRefreshFunc is a function type used for StateChangeConf that is
|
||||
// responsible for refreshing the item being watched for a state change.
|
||||
//
|
||||
// It returns three results. `result` is any object that will be returned
|
||||
// as the final object after waiting for state change. This allows you to
|
||||
// return the final updated object, for example an EC2 instance after refreshing
|
||||
// it.
|
||||
//
|
||||
// `state` is the latest state of that object. And `err` is any error that
|
||||
// may have happened while refreshing the state.
|
||||
type StateRefreshFunc func() (result interface{}, state string, err error)
|
||||
|
||||
// StateChangeConf is the configuration struct used for `WaitForState`.
|
||||
type StateChangeConf struct {
|
||||
Conn *ec2.EC2
|
||||
Pending []string
|
||||
Refresh StateRefreshFunc
|
||||
StepState multistep.StateBag
|
||||
Target string
|
||||
}
|
||||
|
||||
// AMIStateRefreshFunc returns a StateRefreshFunc that is used to watch
|
||||
// an AMI for state changes.
|
||||
func AMIStateRefreshFunc(conn *ec2.EC2, imageId string) StateRefreshFunc {
|
||||
return func() (interface{}, string, error) {
|
||||
resp, err := conn.Images([]string{imageId}, ec2.NewFilter())
|
||||
if err != nil {
|
||||
if ec2err, ok := err.(*ec2.Error); ok && ec2err.Code == "InvalidAMIID.NotFound" {
|
||||
// Set this to nil as if we didn't find anything.
|
||||
resp = nil
|
||||
} else {
|
||||
log.Printf("Error on AMIStateRefresh: %s", err)
|
||||
return nil, "", err
|
||||
}
|
||||
}
|
||||
|
||||
if resp == nil || len(resp.Images) == 0 {
|
||||
// Sometimes AWS has consistency issues and doesn't see the
|
||||
// AMI. Return an empty state.
|
||||
return nil, "", nil
|
||||
}
|
||||
|
||||
i := resp.Images[0]
|
||||
return i, i.State, nil
|
||||
}
|
||||
}
|
||||
|
||||
// InstanceStateRefreshFunc returns a StateRefreshFunc that is used to watch
|
||||
// an EC2 instance.
|
||||
func InstanceStateRefreshFunc(conn *ec2.EC2, i *ec2.Instance) StateRefreshFunc {
|
||||
return func() (interface{}, string, error) {
|
||||
resp, err := conn.Instances([]string{i.InstanceId}, ec2.NewFilter())
|
||||
if err != nil {
|
||||
log.Printf("Error on InstanceStateRefresh: %s", err)
|
||||
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
|
||||
}
|
||||
}
|
||||
|
||||
// WaitForState watches an object and waits for it to achieve a certain
|
||||
// state.
|
||||
func WaitForState(conf *StateChangeConf) (i interface{}, err error) {
|
||||
log.Printf("Waiting for state to become: %s", conf.Target)
|
||||
|
||||
notfoundTick := 0
|
||||
|
||||
for {
|
||||
var currentState string
|
||||
i, currentState, err = conf.Refresh()
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
if i == nil {
|
||||
// If we didn't find the resource, check if we have been
|
||||
// not finding it for awhile, and if so, report an error.
|
||||
notfoundTick += 1
|
||||
if notfoundTick > 20 {
|
||||
return nil, errors.New("couldn't find resource")
|
||||
}
|
||||
} else {
|
||||
// Reset the counter for when a resource isn't found
|
||||
notfoundTick = 0
|
||||
|
||||
if currentState == conf.Target {
|
||||
return
|
||||
}
|
||||
|
||||
if conf.StepState != nil {
|
||||
if _, ok := conf.StepState.GetOk(multistep.StateCancelled); ok {
|
||||
return nil, errors.New("interrupted")
|
||||
}
|
||||
}
|
||||
|
||||
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)
|
||||
}
|
||||
|
||||
return
|
||||
}
|
||||
@@ -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,45 +35,35 @@ 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
|
||||
}
|
||||
|
||||
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 {
|
||||
stateChange := StateChangeConf{
|
||||
Conn: regionconn,
|
||||
Pending: []string{"pending"},
|
||||
Target: "available",
|
||||
Refresh: AMIStateRefreshFunc(regionconn, resp.ImageId),
|
||||
StepState: state,
|
||||
}
|
||||
|
||||
ui.Say(fmt.Sprintf("Waiting for AMI (%s) in region (%s) to become ready...",
|
||||
resp.ImageId, region))
|
||||
if _, err := WaitForState(&stateChange); 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...
|
||||
}
|
||||
|
||||
@@ -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...
|
||||
}
|
||||
|
||||
@@ -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 {
|
||||
|
||||
@@ -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...")
|
||||
|
||||
|
||||
@@ -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)
|
||||
}
|
||||
}
|
||||
@@ -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,
|
||||
}
|
||||
|
||||
@@ -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,21 @@ 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
|
||||
stateChange := awscommon.StateChangeConf{
|
||||
Conn: ec2conn,
|
||||
Pending: []string{"pending"},
|
||||
Target: "available",
|
||||
Refresh: awscommon.AMIStateRefreshFunc(ec2conn, createResp.ImageId),
|
||||
StepState: state,
|
||||
}
|
||||
|
||||
ui.Say("Waiting for AMI to become ready...")
|
||||
if err := awscommon.WaitForAMI(ec2conn, createResp.ImageId); err != nil {
|
||||
if _, err := awscommon.WaitForState(&stateChange); 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 +58,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...
|
||||
}
|
||||
|
||||
@@ -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...
|
||||
}
|
||||
|
||||
@@ -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"
|
||||
@@ -82,7 +83,7 @@ func (b *Builder) Prepare(raws ...interface{}) error {
|
||||
"-u {{.AccountId}} " +
|
||||
"-c {{.CertPath}} " +
|
||||
"-r {{.Architecture}} " +
|
||||
"-e {{.PrivatePath}} " +
|
||||
"-e {{.PrivatePath}}/* " +
|
||||
"-d {{.Destination}} " +
|
||||
"-p {{.Prefix}} " +
|
||||
"--batch"
|
||||
@@ -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,
|
||||
}
|
||||
|
||||
@@ -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,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,21 @@ 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
|
||||
stateChange := awscommon.StateChangeConf{
|
||||
Conn: ec2conn,
|
||||
Pending: []string{"pending"},
|
||||
Target: "available",
|
||||
Refresh: awscommon.AMIStateRefreshFunc(ec2conn, registerResp.ImageId),
|
||||
StepState: state,
|
||||
}
|
||||
|
||||
ui.Say("Waiting for AMI to become ready...")
|
||||
if err := awscommon.WaitForAMI(ec2conn, registerResp.ImageId); err != nil {
|
||||
if _, err := awscommon.WaitForState(&stateChange); 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 +56,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,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
@@ -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
|
||||
}
|
||||
|
||||
@@ -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,
|
||||
}
|
||||
|
||||
|
||||
@@ -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()
|
||||
|
||||
@@ -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 {
|
||||
|
||||
@@ -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))
|
||||
}
|
||||
|
||||
@@ -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,11 +35,12 @@ 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
|
||||
pub_sshformat := string(ssh.MarshalAuthorizedKey(&pub))
|
||||
// TODO properly handle the public key error
|
||||
pub, _ := ssh.NewPublicKey(&priv.PublicKey)
|
||||
pub_sshformat := string(ssh.MarshalAuthorizedKey(pub))
|
||||
|
||||
// The name of the public key on DO
|
||||
name := fmt.Sprintf("packer-%s", hex.EncodeToString(identifier.NewUUID().Raw()))
|
||||
@@ -48,7 +49,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 +60,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)
|
||||
|
||||
@@ -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
|
||||
}
|
||||
|
||||
@@ -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
|
||||
}
|
||||
|
||||
@@ -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
|
||||
}
|
||||
|
||||
@@ -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
|
||||
}
|
||||
|
||||
@@ -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
|
||||
}
|
||||
|
||||
@@ -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
|
||||
}
|
||||
|
||||
@@ -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())
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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,
|
||||
}
|
||||
|
||||
@@ -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",
|
||||
|
||||
@@ -39,7 +39,7 @@ func (c *RunConfig) Prepare(t *packer.ConfigTemplate) []error {
|
||||
}
|
||||
|
||||
if c.RawSSHTimeout == "" {
|
||||
c.RawSSHTimeout = "1m"
|
||||
c.RawSSHTimeout = "5m"
|
||||
}
|
||||
|
||||
// Validation
|
||||
|
||||
@@ -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")
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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 {
|
||||
|
||||
@@ -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...
|
||||
}
|
||||
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -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 {
|
||||
|
||||
@@ -28,10 +28,12 @@ type config struct {
|
||||
DiskSize uint `mapstructure:"disk_size"`
|
||||
FloppyFiles []string `mapstructure:"floppy_files"`
|
||||
Format string `mapstructure:"format"`
|
||||
GuestAdditionsAttach bool `mapstructure:"guest_additions_attach"`
|
||||
GuestAdditionsPath string `mapstructure:"guest_additions_path"`
|
||||
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 +91,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 +147,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 +216,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"))
|
||||
@@ -350,6 +362,7 @@ func (b *Builder) Run(ui packer.Ui, hook packer.Hook, cache packer.Cache) (packe
|
||||
new(stepCreateVM),
|
||||
new(stepCreateDisk),
|
||||
new(stepAttachISO),
|
||||
new(stepAttachGuestAdditions),
|
||||
new(stepAttachFloppy),
|
||||
new(stepForwardSSH),
|
||||
new(stepVBoxManage),
|
||||
@@ -368,12 +381,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 +401,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.")
|
||||
}
|
||||
|
||||
|
||||
@@ -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()
|
||||
|
||||
@@ -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)),
|
||||
|
||||
@@ -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,
|
||||
|
||||
@@ -0,0 +1,81 @@
|
||||
package virtualbox
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"github.com/mitchellh/multistep"
|
||||
"github.com/mitchellh/packer/packer"
|
||||
"log"
|
||||
)
|
||||
|
||||
// This step attaches the VirtualBox guest additions as a inserted CD onto
|
||||
// the virtual machine.
|
||||
//
|
||||
// Uses:
|
||||
// config *config
|
||||
// driver Driver
|
||||
// guest_additions_path string
|
||||
// ui packer.Ui
|
||||
// vmName string
|
||||
//
|
||||
// Produces:
|
||||
type stepAttachGuestAdditions struct {
|
||||
attachedPath string
|
||||
}
|
||||
|
||||
func (s *stepAttachGuestAdditions) Run(state multistep.StateBag) multistep.StepAction {
|
||||
config := state.Get("config").(*config)
|
||||
driver := state.Get("driver").(Driver)
|
||||
guestAdditionsPath := state.Get("guest_additions_path").(string)
|
||||
ui := state.Get("ui").(packer.Ui)
|
||||
vmName := state.Get("vmName").(string)
|
||||
|
||||
// If we're not attaching the guest additions then just return
|
||||
if !config.GuestAdditionsAttach {
|
||||
log.Println("Not attaching guest additions since we're uploading.")
|
||||
return multistep.ActionContinue
|
||||
}
|
||||
|
||||
// Attach the guest additions to the computer
|
||||
log.Println("Attaching guest additions ISO onto IDE controller...")
|
||||
command := []string{
|
||||
"storageattach", vmName,
|
||||
"--storagectl", "IDE Controller",
|
||||
"--port", "1",
|
||||
"--device", "0",
|
||||
"--type", "dvddrive",
|
||||
"--medium", guestAdditionsPath,
|
||||
}
|
||||
if err := driver.VBoxManage(command...); err != nil {
|
||||
err := fmt.Errorf("Error attaching guest additions: %s", err)
|
||||
state.Put("error", err)
|
||||
ui.Error(err.Error())
|
||||
return multistep.ActionHalt
|
||||
}
|
||||
|
||||
// Track the path so that we can unregister it from VirtualBox later
|
||||
s.attachedPath = guestAdditionsPath
|
||||
|
||||
return multistep.ActionContinue
|
||||
}
|
||||
|
||||
func (s *stepAttachGuestAdditions) Cleanup(state multistep.StateBag) {
|
||||
if s.attachedPath == "" {
|
||||
return
|
||||
}
|
||||
|
||||
driver := state.Get("driver").(Driver)
|
||||
ui := state.Get("ui").(packer.Ui)
|
||||
vmName := state.Get("vmName").(string)
|
||||
|
||||
command := []string{
|
||||
"storageattach", vmName,
|
||||
"--storagectl", "IDE Controller",
|
||||
"--port", "1",
|
||||
"--device", "0",
|
||||
"--medium", "none",
|
||||
}
|
||||
|
||||
if err := driver.VBoxManage(command...); err != nil {
|
||||
ui.Error(fmt.Sprintf("Error unregistering guest additions: %s", err))
|
||||
}
|
||||
}
|
||||
@@ -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,
|
||||
|
||||
@@ -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) {}
|
||||
|
||||
@@ -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
|
||||
}
|
||||
|
||||
|
||||
@@ -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) {}
|
||||
|
||||
@@ -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) {}
|
||||
|
||||
@@ -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++ {
|
||||
|
||||
@@ -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 {
|
||||
|
||||
@@ -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) {}
|
||||
|
||||
@@ -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) {}
|
||||
|
||||
@@ -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,32 +89,37 @@ 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 {
|
||||
// Scancodes reference: http://www.win.tue.nl/~aeb/linux/kbd/scancodes-1.html
|
||||
//
|
||||
// Scancodes represent raw keyboard output and are fed to the VM by the
|
||||
// VBoxManage controlvm keyboardputscancode program.
|
||||
//
|
||||
// Scancodes are recorded here in pairs. The first entry represents
|
||||
// the key press and the second entry represents the key release and is
|
||||
// derived from the first by the addition of 0x80.
|
||||
special := make(map[string][]string)
|
||||
special["<bs>"] = []string{"ff", "08"}
|
||||
special["<del>"] = []string{"ff", "ff"}
|
||||
special["<bs>"] = []string{"0e", "8e"}
|
||||
special["<del>"] = []string{"53", "d3"}
|
||||
special["<enter>"] = []string{"1c", "9c"}
|
||||
special["<esc>"] = []string{"01", "81"}
|
||||
special["<f1>"] = []string{"ff", "be"}
|
||||
special["<f2>"] = []string{"ff", "bf"}
|
||||
special["<f3>"] = []string{"ff", "c0"}
|
||||
special["<f4>"] = []string{"ff", "c1"}
|
||||
special["<f5>"] = []string{"ff", "c2"}
|
||||
special["<f6>"] = []string{"ff", "c3"}
|
||||
special["<f7>"] = []string{"ff", "c4"}
|
||||
special["<f8>"] = []string{"ff", "c5"}
|
||||
special["<f9>"] = []string{"ff", "c6"}
|
||||
special["<f10>"] = []string{"ff", "c7"}
|
||||
special["<f11>"] = []string{"ff", "c8"}
|
||||
special["<f12>"] = []string{"ff", "c9"}
|
||||
special["<f1>"] = []string{"3b", "bb"}
|
||||
special["<f2>"] = []string{"3c", "bc"}
|
||||
special["<f3>"] = []string{"3d", "bd"}
|
||||
special["<f4>"] = []string{"3e", "be"}
|
||||
special["<f5>"] = []string{"3f", "bf"}
|
||||
special["<f6>"] = []string{"40", "c0"}
|
||||
special["<f7>"] = []string{"41", "c1"}
|
||||
special["<f8>"] = []string{"42", "c2"}
|
||||
special["<f9>"] = []string{"43", "c3"}
|
||||
special["<f10>"] = []string{"44", "c4"}
|
||||
special["<return>"] = []string{"1c", "9c"}
|
||||
special["<tab>"] = []string{"0f", "8f"}
|
||||
|
||||
shiftedChars := "~!@#$%^&*()_+{}|:\"<>?"
|
||||
|
||||
// Scancodes reference: http://www.win.tue.nl/~aeb/linux/kbd/scancodes-1.html
|
||||
scancodeIndex := make(map[string]uint)
|
||||
scancodeIndex["1234567890-="] = 0x02
|
||||
scancodeIndex["!@#$%^&*()_+"] = 0x02
|
||||
|
||||
@@ -4,6 +4,7 @@ import (
|
||||
"fmt"
|
||||
"github.com/mitchellh/multistep"
|
||||
"github.com/mitchellh/packer/packer"
|
||||
"log"
|
||||
"os"
|
||||
)
|
||||
|
||||
@@ -14,22 +15,28 @@ 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)
|
||||
|
||||
// If we're attaching then don't do this, since we attached.
|
||||
if config.GuestAdditionsAttach {
|
||||
log.Println("Not uploading guest additions since we're attaching.")
|
||||
return multistep.ActionContinue
|
||||
}
|
||||
|
||||
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 +47,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) {}
|
||||
|
||||
@@ -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) {}
|
||||
|
||||
@@ -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) {}
|
||||
|
||||
@@ -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.")
|
||||
}
|
||||
|
||||
|
||||
@@ -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)),
|
||||
|
||||
@@ -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) {}
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -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) {}
|
||||
|
||||
@@ -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) {
|
||||
}
|
||||
|
||||
@@ -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) {}
|
||||
|
||||
@@ -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.
|
||||
|
||||
@@ -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++ {
|
||||
|
||||
@@ -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
@@ -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 != "" {
|
||||
|
||||
@@ -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) {}
|
||||
|
||||
@@ -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)
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user