Compare commits

..

40 Commits

Author SHA1 Message Date
Matthew Hooker dae12393c2 Cut version 1.0.0-rc1 2017-03-16 13:53:41 -07:00
Matthew Hooker 99aec637af fix version 2017-03-16 12:02:01 -07:00
Matthew Hooker 10b4521265 display version correctly 2017-03-16 11:45:21 -07:00
Matthew Hooker eda1a0393f update changelog for 1.0rc1 2017-03-16 11:19:31 -07:00
Matthew Hooker c958a3b827 fix indentation 2017-03-15 18:11:06 -07:00
Rickard von Essen 6d9b40043d amazon-chroot: Fixed indentation 2017-03-15 18:11:05 -07:00
Matthew Hooker ce3936df5b builder/amazon: validate ssh key name/file
When using ssh_key_name, ssh_private_key file must be given,
or ssh_agent_auth enabled.

When automatically retrieving the winrm password, if ssh_key_name is
given, ssh_private_key_file must also be given.
2017-03-15 18:11:05 -07:00
Anton Kvashenkin aaff53c9d2 README: update packer website url with HTTPS 2017-03-15 18:11:04 -07:00
Matthew Hooker edaefd12d2 communicator/winrm: make directory upload behave more like scp
changes behavior to be in-line with the docs.
Resolves #3562
2017-03-15 18:11:04 -07:00
Matthew Hooker fef47cc1c9 website/docs: fix file upload tar script 2017-03-15 18:11:04 -07:00
Matthew Hooker a86c299bcc website/docs: make file provisioner reqs clearer
Make extra clear the file provisioner needs destination directory to exist.
2017-03-15 18:11:03 -07:00
Matthew Hooker 009f8a6b8b website/docs: note docker behavior in file provisioner 2017-03-15 18:11:03 -07:00
Matthew Hooker cc6a1e8303 builder/amazon: set force_deregister to true on -force 2017-03-15 18:11:02 -07:00
Matthew Hooker c6e2f3cf2f website/docs: atlas token not required 2017-03-15 18:11:02 -07:00
Matthew Hooker 647b03f228 builder/hyper-v: validate output dir in step, not in config
essentially same work as #2233
2017-03-15 18:11:02 -07:00
Tadas Medisauskas c258bb2b5a Add support for Hyper-V admins security group 2017-03-15 18:11:01 -07:00
Jack Pearkes 6f1c0516d3 website: update middleman-hashicorp to 0.3.13 2017-03-15 18:11:01 -07:00
Seth Vargo 0bb9a1d08e Add bootstrap 2017-03-15 18:11:00 -07:00
Jason Costello 030f0a54ba Make containers non-fluid for header and footer 2017-03-15 18:11:00 -07:00
Jason Costello 024418a10c Remove "by hashicorp" 2017-03-15 18:11:00 -07:00
Jason Costello 68e0944be9 For real 2017-03-15 18:10:59 -07:00
Jason Costello f3b02a7913 Add meganav tag 2017-03-15 18:10:59 -07:00
Jason Costello 755c0e634b Remove p img width: 100% 2017-03-15 18:10:58 -07:00
Jason Costello 158504e180 Add mega nav 2017-03-15 18:10:58 -07:00
Rickard von Essen f660b6d6cd googlecompute: Added missing disk_name option to docs. 2017-03-15 18:10:57 -07:00
Rickard von Essen 2d266d2de2 googlecompute: Correct values for on_host_maintenance
If preemptible is true then on_host_maintenance must be TERMINATE.

Also corrected order in docs.

Closes #4620
2017-03-15 18:10:57 -07:00
Seth Vargo 62929162a9 Update license 2017-03-15 18:10:57 -07:00
Seth Vargo cbd80abfbf Update license 2017-03-15 18:10:56 -07:00
Seth Vargo 1cafc506d9 Add website license 2017-03-15 18:10:56 -07:00
Seth Vargo 1653e5f226 Update website 2017-03-15 18:10:55 -07:00
Seth Vargo 1dcb45851a Delete config.ru 2017-03-15 18:10:55 -07:00
Seth Vargo 84d303f54e Update middleman-hashicorp 2017-03-15 18:10:55 -07:00
Rickard von Essen a97df2b328 amazon: Step Region Copy crashing on device mapping
Closes #4635
2017-03-15 18:10:54 -07:00
Matthew Hooker 567ce48c3c the digitalocean ubuntu user is root 2017-03-15 18:10:54 -07:00
Matthew Hooker 376d13b4fb docs/qemu: reformat 2017-03-15 18:10:53 -07:00
Michael Ledin e731b01d8a Add floppy size limitation notice.
Should help people dealing with #4570 and #3328 because packer copies
`floppy_files` to floppy without any error even if they exceed 1.44 MB
limit. `floppy_dirs`throws error about FAT size limit exceeded but
without mentioning that real issue is the floppy size limit:
```
Build 'qemu' errored: Error adding path virtio_iso to floppy: FAT FULL
```
2017-03-15 18:10:53 -07:00
Kerim Satirli 6a1c4e8280 fixes broken link to EBS surrogate page 2017-03-15 18:10:53 -07:00
Matthew Hooker a6a5aaaeec fix display on ios device 2017-03-15 18:10:52 -07:00
Matthew Hooker 1ce723de6a fix comment 2017-03-15 18:10:52 -07:00
Matthew Hooker 87b55db6bf fix triton layout 2017-03-15 18:10:51 -07:00
1318 changed files with 24483 additions and 99221 deletions
+1 -7
View File
@@ -1,11 +1,6 @@
_Please read these instructions before submitting_
**DELETE THIS TEMPLATE BEFORE SUBMITTING**
_Only use Github issues to report bugs or feature requests, see
https://www.packer.io/community.html_
For example, _Timeouts waiting for SSH/WinRM_ are generally not bugs within packer and are better addressed by the mailing list. Ask on the mailing list if you are unsure.
_Only use Github issues to report bugs or feature requests, see https://www.packer.io/community/_
If you are planning to open a pull-request just open the pull-request instead of making an issue first.
@@ -23,4 +18,3 @@ Describe the problem and include the following information:
Please paste this in a gist https://gist.github.com
- The _simplest example template and scripts_ needed to reproduce the bug.
Include these in your gist https://gist.github.com
+2 -2
View File
@@ -4,8 +4,8 @@ Describe the change you are making here!
Please include tests. Check out these examples:
- https://github.com/hashicorp/packer/blob/master/builder/virtualbox/common/ssh_config_test.go#L19-L37
- https://github.com/hashicorp/packer/blob/master/post-processor/compress/post-processor_test.go#L153-L182
- https://github.com/mitchellh/packer/blob/master/builder/virtualbox/common/ssh_config_test.go#L19-L37
- https://github.com/mitchellh/packer/blob/master/post-processor/compress/post-processor_test.go#L153-L182
If your PR resolves any open issue(s), please indicate them like this so they will be closed when your PR is merged:
+1 -1
View File
@@ -7,7 +7,7 @@ language: go
go:
- 1.7.4
- 1.8.3
- 1.8
install:
- make deps
+35 -211
View File
@@ -1,239 +1,62 @@
## 1.0.3 (July 17, 2017)
### IMRPOVEMENTS:
* builder/Azure: Update to latest Azure SDK, enabling support for managed
disks. [GH-4511]
* builder/cloudstack: Add default cidr_list [ 0.0.0.0/0 ]. [GH-5125]
* builder/cloudstack: Add support for ssh_agent_auth. [GH-5130]
* builder/cloudstack: Add support for using a HTTP server. [GH-5017]
* builder/cloudstack: Allow reading api_url, api_key, and secret_key from env
vars. [GH-5124]
* builder/cloudstack: Make expunge optional and improve logging output.
[GH-5099]
* builder/googlecompute: Allow using URL's for network and subnetwork.
[GH-5035]
* builder/hyperv: Add support for floppy_dirs with hyperv-iso builder.
* builder/hyperv: Add support for override of system %temp% path.
* core: Experimental Android ARM support. [GH-5111]
* post-processor/atlas: Disallow packer push of vagrant.box artifacts to atlas.
[GH-4780]
* postprocessor/atlas: Disallow pushing vagrant.box artifacts now that Vagrant
cloud is live. [GH-4780]
### BUG FIXES:
* builder/amazon: Fix panic that happens if ami_block_device_mappings is empty.
[GH-5059]
* builder/azure: Write private SSH to file in debug mode. [GH-5070] [GH-5074]
* builder/cloudstack: Properly report back errors. [GH-5103] [GH-5123]
* builder/docker: Fix windows filepath in docker-toolbox call [GH-4887]
* builder/docker: Fix windows filepath in docker-toolbox call. [GH-4887]
* builder/hyperv: Use SID to verify membersip in Admin group, fixing for non-
english users. [GH-5022]
* builder/hyperv: Verify membership in the group Hyper-V Administrators by SID
not name. [GH-5022]
* builder/openstack: Update gophercloud version, fixing builds > 1 hr long.
[GH-5046]
* builder/parallels: Skip missing paths when looking for unnecessary files.
[GH-5058]
* builder/vmware-esxi: Fix VNC port discovery default timeout. [GH-5051]
* communicator/ssh: Add ProvisionerTypes to communicator tests, resolving panic
[GH-5116]
* communicator/ssh: Resolve race condition that sometimes truncates ssh
provisioner stdout [GH-4719]
* post-processor/checksum: Fix interpolation of "output". [GH-5112]
* push: Push vars in packer config, not just those set from command line and in
var-file. [GH-4992]
## 1.0.2 (June 21, 2017)
### BUG FIXES:
* communicator/ssh: Fix truncated stdout from remote ssh provisioner. [GH-5050]
* builder/amazon: Fix bugs related to stop instance command. [GH-4719]
* communicator/ssh: Fix ssh connection errors. [GH-5038]
* core: Remove logging that shouldn't be there when running commands. [GH-5042]
* provisioner/shell: Fix bug where scripts were being run under `sh`. [GH-5043]
### IMRPOVEMENTS:
* provisioner/windows-restart: make it clear that timeouts come from the
provisioner, not winrm. [GH-5040]
## 1.0.1 (June 19, 2017)
### IMPROVEMENTS:
* builder/amazon: Allow amis to be copied to other regions, encrypted with
custom KMS keys. [GH-4948]
* builder/amazon: Allow configuration of api endpoint to support api-compatible
cloud providers. [GH-4896]
* builder/amazon: Fix regex used for ami name validation [GH-4902]
* builder/amazon: Look up vpc from subnet id if no vpc was specified. [GH-4879]
* builder/amazon: Print temporary security group name to the UI. [GH-4997]
* builder/amazon: Support Assume Role with MFA and ECS Task Roles. Also updates
to a newer version of aws-sdk-go. [GH-4996]
* builder/amazon: Use retry logic when creating instance tags. [GH-4876]
* builder/amazon: Validate ami name. [GH-4762]
* builder/azure: Add build output to artifact. [GH-4953]
* builder/azure: Use disk URI as artifact ID. [GH-4981]
* builder/digitalocean: Added support for monitoring. [GH-4782]
* builder/digitalocean: Support for copying snapshot to other regions.
[GH-4893]
* builder/hyper-v: Remove the check for administrator rights when sending key
strokes to Hyper-V. [GH-4687] # builder/openstack: Fix private key error
message to match documentation [GH-4898]
* builder/null: Support SSH agent auth [GH-4956]
* builder/openstack: Add ssh agent support. [GH-4655]
* builder/openstack: Support client x509 certificates. [GH-4921]
* builder/parallels-iso: Configuration of disk type, plain or expanding.
[GH-4621]
* builder/triton: An SSH agent can be used to authenticate requests, making
`triton_key_material` optional. [GH-4838]
* builder/triton: If no source machine networks are specified, instances are
started on the default public and internal networks. [GH-4838]
* builder/virtualbox: Add sata port count configuration option. [GH-4699]
* builder/virtualbox: Don't add port forwarding when using "none" communicator.
[GH-4960]
* builder/vmware: Add option to remove interfaces from the vmx. [GH-4927]
* builder/vmware: Properly remove mounted CDs on OS X. [GH-4810]
* builder/vmware: VNC probe timeout is configurable. [GH-4919]
* command/push: add `-sensitive` flag to mark pushed vars are sensitive.
[GH-4970]
* command/push: Vagrant support in Terraform Enterprise is deprecated.
[GH-4950]
* communicator/ssh: Add ssh agent support for bastion connections. [GH-4940]
* communicator/winrm: Add NTLM authentication support. [GH-4979]
* communicator/winrm: Add support for file downloads. [GH-4748]
* core: add telemetry for better product support. [GH-5015]
* core: Build binaries for arm64 [GH-4892]
* post-processor/amazon-import: Add support for `license_type`. [GH-4634]
* post-processor/vagrant-cloud: Get vagrant cloud token from environment.
[GH-4982]
* provisioner/ansible-local: Add extra-vars `packer_build_name`,
`packer_builder_type`, and `packer_http_addr`. [GH-4821]
* provisioner/ansible: Add `inventory_directory` option to control where to
place the generated inventory file. [GH-4760]
* provisioner/ansible: Add `skip_version_check` flag for when ansible will be
installed from a prior provisioner. [GH-4983]
* provisioner/ansible: Add extra-vars `packer_build_name` and
`packer_builder_type`. [GH-4821]
* provisioner/chef-solo: Add option to select Chef version. [GH-4791]
* provisioner/salt: Add salt bin directory configuration. [GH-5009]
* provisioner/salt: Add support for grains. [GH-4961]
* provisioner/shell: Use `env` to set environment variables to support freebsd
out of the box. [GH-4909]
* website/docs: Clarify language, improve formatting. [GH-4866]
* website/docs: Update docker metadata fields that can be changed. [GH-4867]
## 1.0rc1 (March 16, 2017)
### BUG FIXES:
* builder/amazon-ebssurrogate: Use ami device settings when creating the AMI.
[GH-4972]
* builder/amazon: don't try to delete extra volumes during clean up. [GH-4930]
* builder/amazon: fix `force_delete_snapshot` when the launch instance has
extra volumes. [GH-4931]
* builder/amazon: Only delete temporary key if we created one. [GH-4850]
* builder/azure: Replace calls to panic with error returns. [GH-4846]
* communicator/winrm: Use KeepAlive to keep long-running connections open.
[GH-4952]
* core: Correctly reject config files which have junk after valid json.
[GH-4906]
* post-processor/checksum: fix crash when invalid checksum is used. [GH-4812]
* post-processor/vagrant-cloud: don't read files to upload in to memory first.
[GH-5005]
* post-processor/vagrant-cloud: only upload once under normal conditions.
[GH-5008]
* provisioner/ansible-local: Correctly set the default staging directory under
Windows. [GH-4792]
### FEATURES:
* **New builder:** `alicloud-ecs` for building Alicloud ECS images. [GH-4619]
## 1.0.0 (April 4, 2017)
### BUG FIXES:
* builder/amazon: Fix b/c issue by reporting again the tags we create.
[GH-4704]
* builder/amazon: Fix crash in `step_region_copy`. [GH-4642]
* builder/googlecompute: Correct values for `on_host_maintenance`. [GH-4643]
* builder/googlecompute: Use "default" service account. [GH-4749]
* builder/hyper-v: Don't wait for shutdown_command to return. [GH-4691]
* builder/virtualbox: fix `none` communicator by allowing skipping upload of
version file. [GH-4678]
* builder/virtualbox: retry removing floppy controller. [GH-4705]
* communicator/ssh: don't return error if we can't close connection. [GH-4741]
* communicator/ssh: fix nil pointer error. [GH-4690]
* core: fix version number
* core: Invoking packer `--help` or `--version` now exits with status 0.
[GH-4723]
* core: show correct step name when debugging. [GH-4672]
* communicator/winrm: Directory uploads behave more like scp. [GH-4438]
* builder/amazon: Fix crash in `step_region_copy`. [GH-4642]
### IMPROVEMENTS:
### IMRPOVEMENTS:
* builder/amazon-chroot: Ability to give an empty list in `copy_files` to
prevent the default `/etc/resolv.conf` file from being copied. If
`copy_files` isn't given at all, the default behavior remains. [GH-4708]
* builder/amazon: set force_deregister to true on -force. [GH-4649]
* builder/amazon: validate ssh key name/file. [GH-4665]
* builder/ansible: Clearer error message when we have problems getting the
ansible version. [GH-4694]
* builder/amazon: set force_deregister to true on -force. [GH-4649]
* builder/hyper-v: validate output dir in step, not in config. [GH-4645]
* More diligently try to complete azure-setup.sh. [GH-4752]
* website: fix display on ios devices. [GH-4618]
## 0.12.3 (March 1, 2017)
### BACKWARDS INCOMPATIBILITIES:
* provisioner/ansible: by default, the staging dir will be randomized. [GH-4472]
### FEATURES:
* **New builder:** `ebs-surrogate` for building AMIs from EBS volumes. [GH-4351]
### IMPROVEMENTS:
* builder/amazon-chroot: support encrypted boot volume. [GH-4584]
* builder/amazon: Add BuildRegion and SourceAMI template variables. [GH-4399]
* builder/amazon: Change EC2 Windows password timeout to 20 minutes. [GH-4590]
* builder/amazon: enable ena when `enhanced_networking` is set. [GH-4578]
* builder/azure:: add two new config variables for temp_compute_name and
temp_resource_group_name. [GH-4468]
* builder/amazon-chroot: support encrypted boot volume. [GH-4584]
* builder/docker: create export dir if needed. [GH-4439]
* builder/googlecompute: Add `on_host_maintenance` option. [GH-4544]
* builder/openstack: add reuse_ips option to try to re-use existing IPs.
[GH-4564]
* builder/openstack: add reuse_ips option to try to re-use existing IPs. [GH-4564]
* communicator/docker: preserve file mode. [GH-4443]
* communicator/winrm: support ProxyFromEnvironment. [GH-4463]
* core: make VNC links clickable in terminal. [GH-4497] [GH-4498]
* post-processor/amazon-import: support AMI attributes on import [GH-4216]
* provisioner/ansible: use randomized staging dir [GH-4472]
* communicator/ssh: Use SSH agent when enabled for bastion step. [GH-4598]
* builder/amazon: enable ena when `enhanced_networking` is set. [GH-4578]
* builder/vmware-esxi: try for longer to connect to vnc port. [GH-4480]
[GH-4610]
* builder/vmware: allow extra options for ovftool. [GH-4536]
* builder/vmware: don't cache ip address so we know if it changes. [GH-4532]
* communicator/docker: preserve file mode. [GH-4443]
* communicator/ssh: Use SSH agent when enabled for bastion step. [GH-4598]
* communicator/winrm: support ProxyFromEnvironment. [GH-4463]
* core: don't show ui color if we're not colorized. [GH-4525]
* core: make VNC links clickable in terminal. [GH-4497] [GH-4498]
* builder/vmware: don't cache ip address so we know if it changes. [GH-4532]
* builder/vmware: allow extra options for ovftool. [GH-4536]
* docs: add community page. [GH-4550]
* post-processor/amazon-import: support AMI attributes on import [GH-4216]
* post-processor/docker-import: print stderr on docker import failure.
[GH-4529]
* builder/azure:: add two new config variables for temp_compute_name and
temp_resource_group_name. [GH-4468]
### BUG FIXES:
* builder/amazon-ebsvolume: Fix interpolation of block_device. [GH-4464]
* builder/amazon: Fix ssh agent authentication. [GH-4597]
* builder/docker: Don't force tag if using a docker version that doesn't
support it. [GH-4560]
* builder/amazon-ebsvolume: Fix interpolation of block_device. [GH-4464]
* builder/googlecompute: fix bug when creating image from custom image_family.
[GH-4518]
* builder/virtualbox: remove guest additions before saving image. [GH-4496]
* core: always check for an error first when walking a path. [GH-4467]
* core: update crypto/ssh lib to fix large file uploads. [GH-4546]
* builder/docker: Don't force tag if using a docker version that doesn't
support it. [GH-4560]
* provisioner/chef-client: only upload knife config if we're cleaning.
[GH-4534]
* core: update crypto/ssh lib to fix large file uploads. [GH-4546]
## 0.12.2 (January 20, 2017)
@@ -246,13 +69,12 @@
* builder/hyperv-iso: add `iso_target_extension` option. [GH-4294]
* builder/openstack: Add support for instance metadata. [GH-4361]
* builder/openstack: Attempt to use existing floating IPs before allocating a
new one. [GH-4357]
* builder/openstack: Attempt to use existing floating IPs before allocating
a new one. [GH-4357]
* builder/parallels-iso: add `iso_target_extension` option. [GH-4294]
* builder/qemu: add `iso_target_extension` option. [GH-4294]
* builder/qemu: add `use_default_display` option for osx compatibility.
[GH-4293]
* builder/qemu: Detect input disk image format during copy/convert. [GH-4343]
* builder/virtualbox-iso: add `iso_target_extension` option. [GH-4294]
* builder/virtualbox: add `skip_export` option to skip exporting the VM after
build completes. [GH-4339]
@@ -264,22 +86,23 @@
* builder/vmware: Try to use `ip address` to find host IP. [GH-4411]
* common/step_http\_server: set `PACKER_HTTP_ADDR` env var for accessing http
server from inside builder. [GH-4409]
* provisioner/powershell: Allow equals sign in value of environment variables.
[GH-4328]
* provisioner/powershell: Allow equals sign in value of environment
variables. [GH-4328]
* provisioner/puppet-server: Add default facts. [GH-4286]
* builder/qemu: Detect input disk image format during copy/convert. [GH-4343]
### BUG FIXES:
* builder/amazon-chroot: Panic in AMI region copy step. [GH-4341]
* builder/amazon: Crashes when new EBS vols are used. [GH-4308]
* builder/amazon: Fix crash in amazon-instance. [GH-4372]
* builder/amazon: fix run volume tagging [GH-4420]
* builder/amazon: fix when using non-existant security\_group\_id. [GH-4425]
* builder/amazon: Properly error if we don't have the
ec2:DescribeSecurityGroups permission. [GH-4304]
* builder/amazon: Properly wait for security group to exist. [GH-4369]
* builder/docker: Fix crash when performing log in to ECR with an invalid URL.
[GH-4385]
* builder/amazon: fix run volume tagging [GH-4420]
* builder/amazon: fix when using non-existant security\_group\_id. [GH-4425]
* builder/docker: Fix crash when performing log in to ECR with an invalid
URL. [GH-4385]
* builder/openstack: fix for finding resource by ID. [GH-4301]
* builder/qemu: Explicitly set WinRMPort for StepConnect. [GH-4321]
* builder/virtualbox: Explicitly set WinRMPort for StepConnect. [GH-4321]
@@ -290,7 +113,8 @@
* command/push: Don't interpolate variables when pushing. [GH-4389]
* common/step_http_server: make port range inclusive. [GH-4398]
* communicator/winrm: update winrm client, resolving `MaxMemoryPerShellMB`
errors and properly error logging instead of panicking. [GH-4412] [GH-4424]
errors and properly error logging instead of panicking. [GH-4412]
[GH-4424]
* provider/windows-shell: Allows equals sign in env var value. [GH-4423]
## 0.12.1 (December 15, 2016)
@@ -302,9 +126,9 @@
"packer fix template.json" to migrate a template. [GH-4285]
* builder/openstack: No long supports the `api_key` option for rackspace.
[GH-4283]
* post-processor/manifest: Changed `filename` field to be `output`, to be more
consistent with other post-processors. `packer fix` will fix this for you.
[GH-4192]
* post-processor/manifest: Changed `filename` field to be `output`, to be
more consistent with other post-processors. `packer fix` will fix this
for you. [GH-4192]
* post-processor/shell-local: Now runs per-builder instead of per-file. The
filename is no longer passed in as an argument to the script, but instead
needs to be gleaned from the manifest post-processor. [GH-4189]
+8 -8
View File
@@ -65,28 +65,28 @@ following steps in order to be able to compile and test Packer. These instructio
```
3. Download the Packer source (and its dependencies) by running `go get
github.com/hashicorp/packer`. This will download the Packer source to
`$GOPATH/src/github.com/hashicorp/packer`.
github.com/mitchellh/packer`. This will download the Packer source to
`$GOPATH/src/github.com/mitchellh/packer`.
4. When working on packer `cd $GOPATH/src/github.com/hashicorp/packer` so you
4. When working on packer `cd $GOPATH/src/github.com/mitchellh/packer` so you
can run `make` and easily access other files. Run `make help` to get
information about make targets.
5. Make your changes to the Packer source. You can run `make` in
`$GOPATH/src/github.com/hashicorp/packer` to run tests and build the packer
`$GOPATH/src/github.com/mitchellh/packer` to run tests and build the packer
binary. Any compilation errors will be shown when the binaries are
rebuilding. If you don't have `make` you can simply run `go build -o bin/packer .` from the project root.
6. After running building packer successfully, use
`$GOPATH/src/github.com/hashicorp/packer/bin/packer` to build a machine and
verify your changes work. For instance: `$GOPATH/src/github.com/hashicorp/packer/bin/packer build template.json`.
`$GOPATH/src/github.com/mitchellh/packer/bin/packer` to build a machine and
verify your changes work. For instance: `$GOPATH/src/github.com/mitchellh/packer/bin/packer build template.json`.
7. If everything works well and the tests pass, run `go fmt` on your code
before submitting a pull-request.
### Opening an Pull Request
When you are ready to open a pull-request, you will need to [fork packer](https://github.com/hashicorp/packer#fork-destination-box), push your changes to your fork, and then open a pull-request.
When you are ready to open a pull-request, you will need to [fork packer](https://github.com/mitchellh/packer#fork-destination-box), push your changes to your fork, and then open a pull-request.
For example, my github username is `cbednarski` so I would do the following:
@@ -104,7 +104,7 @@ From there, open your fork in your browser to open a new pull-request.
The easiest way to work on a fork is to set it as a remote of the packer project. After following the steps in "Setting up Go to work on Packer":
1. Navigate to $GOPATH/src/github.com/hashicorp/packer
1. Navigate to $GOPATH/src/github.com/mitchellh/packer
2. Add the remote `git remote add <name of remote> <github url of fork>`. For example `git remote add mwhooker https://github.com/mwhooker/packer.git`.
3. Checkout a feature branch: `git checkout -b new-feature`
4. Make changes
+10 -25
View File
@@ -5,16 +5,6 @@ GITSHA:=$(shell git rev-parse HEAD)
# Get the current local branch name from git (if we can, this may be blank)
GITBRANCH:=$(shell git symbolic-ref --short HEAD 2>/dev/null)
GOFMT_FILES?=$$(find . -not -path "./vendor/*" -name "*.go")
GOOS=$(shell go env GOOS)
GOARCH=$(shell go env GOARCH)
# Get the git commit
GIT_DIRTY=$(shell test -n "`git status --porcelain`" && echo "+CHANGES" || true)
GIT_COMMIT=$(shell git rev-parse --short HEAD)
GIT_IMPORT=github.com/hashicorp/packer/version
GOLDFLAGS=-X $(GIT_IMPORT).GitCommit=$(GIT_COMMIT)$(GIT_DIRTY)
export GOLDFLAGS
default: deps generate test dev
@@ -23,36 +13,32 @@ ci: deps test
release: deps test releasebin package ## Build a release build
bin: deps ## Build debug/test build
@go get github.com/mitchellh/gox
@echo "WARN: 'make bin' is for debug / test builds only. Use 'make release' for release builds."
@sh -c "$(CURDIR)/scripts/build.sh"
@GO15VENDOREXPERIMENT=1 sh -c "$(CURDIR)/scripts/build.sh"
releasebin: deps
@go get github.com/mitchellh/gox
@grep 'const VersionPrerelease = "dev"' version/version.go > /dev/null ; if [ $$? -eq 0 ]; then \
echo "ERROR: You must remove prerelease tags from version/version.go prior to release."; \
exit 1; \
fi
@sh -c "$(CURDIR)/scripts/build.sh"
@GO15VENDOREXPERIMENT=1 sh -c "$(CURDIR)/scripts/build.sh"
package:
$(if $(VERSION),,@echo 'VERSION= needed to release; Use make package skip compilation'; exit 1)
@sh -c "$(CURDIR)/scripts/dist.sh $(VERSION)"
deps:
@go get golang.org/x/tools/cmd/stringer
@go get github.com/kardianos/govendor
@govendor sync
go get github.com/mitchellh/gox
go get golang.org/x/tools/cmd/stringer
go get github.com/kardianos/govendor
govendor sync
dev: deps ## Build and install a development build
@grep 'const VersionPrerelease = ""' version/version.go > /dev/null ; if [ $$? -eq 0 ]; then \
echo "ERROR: You must add prerelease tags to version/version.go prior to making a dev build."; \
exit 1; \
fi
@mkdir -p pkg/$(GOOS)_$(GOARCH)
@go install -ldflags '$(GOLDFLAGS)'
@cp $(GOPATH)/bin/packer bin
@cp $(GOPATH)/bin/packer pkg/$(GOOS)_$(GOARCH)
@PACKER_DEV=1 GO15VENDOREXPERIMENT=1 sh -c "$(CURDIR)/scripts/build.sh"
fmt: ## Format Go code
@gofmt -w -s $(GOFMT_FILES)
@@ -60,9 +46,6 @@ fmt: ## Format Go code
fmt-check: ## Check go code formatting
$(CURDIR)/scripts/gofmtcheck.sh $(GOFMT_FILES)
fmt-docs:
@find ./website/source/docs -name "*.md" -exec pandoc --wrap auto --columns 79 --atx-headers -s -f "markdown_github+yaml_metadata_block" -t "markdown_github+yaml_metadata_block" {} -o {} \;
# Install js-beautify with npm install -g js-beautify
fmt-examples:
find examples -name *.json | xargs js-beautify -r -s 2 -n -eol "\n"
@@ -89,9 +72,11 @@ testrace: deps ## Test for race conditions
@go test -race $(TEST) $(TESTARGS) -timeout=2m
updatedeps:
go get -u github.com/mitchellh/gox
go get -u golang.org/x/tools/cmd/stringer
@echo "INFO: Packer deps are managed by govendor. See CONTRIBUTING.md"
help:
@grep -E '^[a-zA-Z_-]+:.*?## .*$$' $(MAKEFILE_LIST) | sort | awk 'BEGIN {FS = ":.*?## "}; {printf "\033[36m%-30s\033[0m %s\n", $$1, $$2}'
.PHONY: bin checkversion ci default deps fmt fmt-docs fmt-examples generate releasebin test testacc testrace updatedeps
.PHONY: bin checkversion ci default deps fmt fmt-examples generate releasebin test testacc testrace updatedeps
+10 -15
View File
@@ -5,9 +5,9 @@
[![GoDoc][godoc-badge]][godoc]
[![GoReportCard][report-badge]][report]
[travis-badge]: https://travis-ci.org/hashicorp/packer.svg?branch=master
[travis]: https://travis-ci.org/hashicorp/packer
[appveyor-badge]: https://ci.appveyor.com/api/projects/status/miavlgnp989e5obc/branch/master?svg=true
[travis-badge]: https://travis-ci.org/mitchellh/packer.svg?branch=master
[travis]: https://travis-ci.org/mitchellh/packer
[appveyor-badge]: https://ci.appveyor.com/api/projects/status/github/mitchellh/packer?branch=master&svg=true
[appveyor]: https://ci.appveyor.com/project/hashicorp/packer
[godoc-badge]: https://godoc.org/github.com/mitchellh/packer?status.svg
[godoc]: https://godoc.org/github.com/mitchellh/packer
@@ -16,7 +16,7 @@
* Website: https://www.packer.io
* IRC: `#packer-tool` on Freenode
* Mailing list: [Google Groups](https://groups.google.com/forum/#!forum/packer-tool)
* Mailing list: [Google Groups](http://groups.google.com/group/packer-tool)
Packer is a tool for building identical machine images for multiple platforms
from a single source configuration.
@@ -27,19 +27,14 @@ comes out of the box with support for the following platforms:
* Amazon EC2 (AMI). Both EBS-backed and instance-store AMIs
* Azure
* CloudStack
* DigitalOcean
* Docker
* Google Compute Engine
* Hyper-V
* 1&1
* OpenStack
* Parallels
* ProfitBricks
* QEMU. Both KVM and Xen images.
* Triton (Joyent Public Cloud)
* VMware
* VirtualBox
* VMware
Support for other platforms can be added via plugins.
@@ -49,16 +44,16 @@ The images that Packer creates can easily be turned into
## Quick Start
Download and install packages and dependencies
```
go get github.com/hashicorp/packer
go get github.com/mitchellh/packer
```
**Note:** There is a great
[introduction and getting started guide](https://www.packer.io/intro)
[introduction and getting started guide](http://www.packer.io/intro)
for those with a bit more patience. Otherwise, the quick start below
will get you up and running quickly, at the sacrifice of not explaining some
key points.
First, [download a pre-built Packer binary](https://www.packer.io/downloads.html)
First, [download a pre-built Packer binary](http://www.packer.io/downloads.html)
for your operating system or [compile Packer yourself](CONTRIBUTING.md#setting-up-go-to-work-on-packer).
After Packer is installed, create your first template, which tells Packer
@@ -103,8 +98,8 @@ they're run, etc. is up to you.
Comprehensive documentation is viewable on the Packer website:
https://www.packer.io/docs
http://www.packer.io/docs
## Developing Packer
See [CONTRIBUTING.md](https://github.com/hashicorp/packer/blob/master/CONTRIBUTING.md) for best practices and instructions on setting up your development environment to work on Packer.
See [CONTRIBUTING.md](https://github.com/mitchellh/packer/blob/master/CONTRIBUTING.md) for best practices and instructions on setting up your development environment to work on Packer.
+4 -4
View File
@@ -13,7 +13,7 @@ os: Windows Server 2012 R2
environment:
GOPATH: c:\gopath
clone_folder: c:\gopath\src\github.com\hashicorp\packer
clone_folder: c:\gopath\src\github.com\mitchellh\packer
install:
- set GO15VENDOREXPERIMENT=1
@@ -29,9 +29,9 @@ build_script:
- ps: |
go.exe test (go.exe list ./... `
|? { -not $_.Contains('/vendor/') } `
|? { $_ -ne 'github.com/hashicorp/packer/builder/parallels/common' } `
|? { $_ -ne 'github.com/hashicorp/packer/common' }`
|? { $_ -ne 'github.com/hashicorp/packer/provisioner/ansible' })
|? { $_ -ne 'github.com/mitchellh/packer/builder/parallels/common' } `
|? { $_ -ne 'github.com/mitchellh/packer/common' }`
|? { $_ -ne 'github.com/mitchellh/packer/provisioner/ansible' })
test: off
-83
View File
@@ -1,83 +0,0 @@
package ecs
import (
"fmt"
"os"
"github.com/denverdino/aliyungo/common"
"github.com/denverdino/aliyungo/ecs"
"github.com/hashicorp/packer/template/interpolate"
)
// Config of alicloud
type AlicloudAccessConfig struct {
AlicloudAccessKey string `mapstructure:"access_key"`
AlicloudSecretKey string `mapstructure:"secret_key"`
AlicloudRegion string `mapstructure:"region"`
AlicloudSkipValidation bool `mapstructure:"skip_region_validation"`
}
// Client for AlicloudClient
func (c *AlicloudAccessConfig) Client() (*ecs.Client, error) {
if err := c.loadAndValidate(); err != nil {
return nil, err
}
client := ecs.NewClient(c.AlicloudAccessKey, c.AlicloudSecretKey)
client.SetBusinessInfo("Packer")
if _, err := client.DescribeRegions(); err != nil {
return nil, err
}
return client, nil
}
func (c *AlicloudAccessConfig) Prepare(ctx *interpolate.Context) []error {
var errs []error
if err := c.Config(); err != nil {
errs = append(errs, err)
}
if c.AlicloudRegion != "" && !c.AlicloudSkipValidation {
if c.validateRegion() != nil {
errs = append(errs, fmt.Errorf("Unknown alicloud region: %s", c.AlicloudRegion))
}
}
if len(errs) > 0 {
return errs
}
return nil
}
func (c *AlicloudAccessConfig) Config() error {
if c.AlicloudAccessKey == "" {
c.AlicloudAccessKey = os.Getenv("ALICLOUD_ACCESS_KEY")
}
if c.AlicloudSecretKey == "" {
c.AlicloudSecretKey = os.Getenv("ALICLOUD_SECRET_KEY")
}
if c.AlicloudAccessKey == "" || c.AlicloudSecretKey == "" {
return fmt.Errorf("ALICLOUD_ACCESS_KEY and ALICLOUD_SECRET_KEY must be set in template file or environment variables.")
}
return nil
}
func (c *AlicloudAccessConfig) loadAndValidate() error {
if err := c.validateRegion(); err != nil {
return err
}
return nil
}
func (c *AlicloudAccessConfig) validateRegion() error {
for _, valid := range common.ValidRegions {
if c.AlicloudRegion == string(valid) {
return nil
}
}
return fmt.Errorf("Not a valid alicloud region: %s", c.AlicloudRegion)
}
@@ -1,44 +0,0 @@
package ecs
import (
"testing"
)
func testAlicloudAccessConfig() *AlicloudAccessConfig {
return &AlicloudAccessConfig{
AlicloudAccessKey: "ak",
AlicloudSecretKey: "acs",
}
}
func TestAlicloudAccessConfigPrepareRegion(t *testing.T) {
c := testAlicloudAccessConfig()
c.AlicloudRegion = ""
if err := c.Prepare(nil); err != nil {
t.Fatalf("shouldn't have err: %s", err)
}
c.AlicloudRegion = "cn-beijing-3"
if err := c.Prepare(nil); err == nil {
t.Fatal("should have error")
}
c.AlicloudRegion = "cn-beijing"
if err := c.Prepare(nil); err != nil {
t.Fatalf("shouldn't have err: %s", err)
}
c.AlicloudRegion = "unknown"
if err := c.Prepare(nil); err == nil {
t.Fatalf("should have err")
}
c.AlicloudRegion = "unknown"
c.AlicloudSkipValidation = true
if err := c.Prepare(nil); err != nil {
t.Fatalf("shouldn't have err: %s", err)
}
c.AlicloudSkipValidation = false
}
-135
View File
@@ -1,135 +0,0 @@
package ecs
import (
"fmt"
"log"
"sort"
"strings"
"github.com/denverdino/aliyungo/common"
"github.com/denverdino/aliyungo/ecs"
"github.com/hashicorp/packer/packer"
)
type Artifact struct {
// A map of regions to alicloud image IDs.
AlicloudImages map[string]string
// BuilderId is the unique ID for the builder that created this alicloud image
BuilderIdValue string
// Alcloud connection for performing API stuff.
Client *ecs.Client
}
func (a *Artifact) BuilderId() string {
return a.BuilderIdValue
}
func (*Artifact) Files() []string {
// We have no files
return nil
}
func (a *Artifact) Id() string {
parts := make([]string, 0, len(a.AlicloudImages))
for region, ecsImageId := range a.AlicloudImages {
parts = append(parts, fmt.Sprintf("%s:%s", region, ecsImageId))
}
sort.Strings(parts)
return strings.Join(parts, ",")
}
func (a *Artifact) String() string {
alicloudImageStrings := make([]string, 0, len(a.AlicloudImages))
for region, id := range a.AlicloudImages {
single := fmt.Sprintf("%s: %s", region, id)
alicloudImageStrings = append(alicloudImageStrings, single)
}
sort.Strings(alicloudImageStrings)
return fmt.Sprintf("Alicloud images were created:\n\n%s", strings.Join(alicloudImageStrings, "\n"))
}
func (a *Artifact) State(name string) interface{} {
switch name {
case "atlas.artifact.metadata":
return a.stateAtlasMetadata()
default:
return nil
}
}
func (a *Artifact) Destroy() error {
errors := make([]error, 0)
for region, imageId := range a.AlicloudImages {
log.Printf("Delete alicloud image ID (%s) from region (%s)", imageId, region)
// Get alicloud image metadata
images, _, err := a.Client.DescribeImages(&ecs.DescribeImagesArgs{
RegionId: common.Region(region),
ImageId: imageId})
if err != nil {
errors = append(errors, err)
}
if len(images) == 0 {
err := fmt.Errorf("Error retrieving details for alicloud image(%s), no alicloud images found", imageId)
errors = append(errors, err)
continue
}
//Unshared the shared account before destroy
sharePermissions, err := a.Client.DescribeImageSharePermission(&ecs.ModifyImageSharePermissionArgs{RegionId: common.Region(region), ImageId: imageId})
if err != nil {
errors = append(errors, err)
}
accountsNumber := len(sharePermissions.Accounts.Account)
if accountsNumber > 0 {
accounts := make([]string, accountsNumber)
for index, account := range sharePermissions.Accounts.Account {
accounts[index] = account.AliyunId
}
err := a.Client.ModifyImageSharePermission(&ecs.ModifyImageSharePermissionArgs{
RegionId: common.Region(region),
ImageId: imageId,
RemoveAccount: accounts,
})
if err != nil {
errors = append(errors, err)
}
}
// Delete alicloud images
if err := a.Client.DeleteImage(common.Region(region), imageId); err != nil {
errors = append(errors, err)
}
//Delete the snapshot of this images
for _, diskDevices := range images[0].DiskDeviceMappings.DiskDeviceMapping {
if err := a.Client.DeleteSnapshot(diskDevices.SnapshotId); err != nil {
errors = append(errors, err)
}
}
}
if len(errors) > 0 {
if len(errors) == 1 {
return errors[0]
} else {
return &packer.MultiError{Errors: errors}
}
}
return nil
}
func (a *Artifact) stateAtlasMetadata() interface{} {
metadata := make(map[string]string)
for region, imageId := range a.AlicloudImages {
k := fmt.Sprintf("region.%s", region)
metadata[k] = imageId
}
return metadata
}
-47
View File
@@ -1,47 +0,0 @@
package ecs
import (
"reflect"
"testing"
"github.com/hashicorp/packer/packer"
)
func TestArtifact_Impl(t *testing.T) {
var _ packer.Artifact = new(Artifact)
}
func TestArtifactId(t *testing.T) {
expected := `east:foo,west:bar`
ecsImages := make(map[string]string)
ecsImages["east"] = "foo"
ecsImages["west"] = "bar"
a := &Artifact{
AlicloudImages: ecsImages,
}
result := a.Id()
if result != expected {
t.Fatalf("bad: %s", result)
}
}
func TestArtifactState_atlasMetadata(t *testing.T) {
a := &Artifact{
AlicloudImages: map[string]string{
"east": "foo",
"west": "bar",
},
}
actual := a.State("atlas.artifact.metadata")
expected := map[string]string{
"region.east": "foo",
"region.west": "bar",
}
if !reflect.DeepEqual(actual, expected) {
t.Fatalf("bad: %#v", actual)
}
}
-241
View File
@@ -1,241 +0,0 @@
// The alicloud contains a packer.Builder implementation that
// builds ecs images for alicloud.
package ecs
import (
"log"
"github.com/hashicorp/packer/common"
"github.com/hashicorp/packer/helper/communicator"
"github.com/hashicorp/packer/helper/config"
"github.com/hashicorp/packer/packer"
"github.com/hashicorp/packer/template/interpolate"
"github.com/mitchellh/multistep"
)
// The unique ID for this builder
const BuilderId = "alibaba.alicloud"
type Config struct {
common.PackerConfig `mapstructure:",squash"`
AlicloudAccessConfig `mapstructure:",squash"`
AlicloudImageConfig `mapstructure:",squash"`
RunConfig `mapstructure:",squash"`
ctx interpolate.Context
}
type Builder struct {
config Config
runner multistep.Runner
}
type InstanceNetWork string
const (
ClassicNet = InstanceNetWork("classic")
VpcNet = InstanceNetWork("vpc")
ALICLOUD_DEFAULT_SHORT_TIMEOUT = 180
ALICLOUD_DEFAULT_TIMEOUT = 1800
ALICLOUD_DEFAULT_LONG_TIMEOUT = 3600
)
func (b *Builder) Prepare(raws ...interface{}) ([]string, error) {
err := config.Decode(&b.config, &config.DecodeOpts{
Interpolate: true,
InterpolateContext: &b.config.ctx,
InterpolateFilter: &interpolate.RenderFilter{
Exclude: []string{
"run_command",
},
},
}, raws...)
b.config.ctx.EnableEnv = true
if err != nil {
return nil, err
}
// Accumulate any errors
var errs *packer.MultiError
errs = packer.MultiErrorAppend(errs, b.config.AlicloudAccessConfig.Prepare(&b.config.ctx)...)
errs = packer.MultiErrorAppend(errs, b.config.AlicloudImageConfig.Prepare(&b.config.ctx)...)
errs = packer.MultiErrorAppend(errs, b.config.RunConfig.Prepare(&b.config.ctx)...)
if errs != nil && len(errs.Errors) > 0 {
return nil, errs
}
log.Println(common.ScrubConfig(b.config, b.config.AlicloudAccessKey, b.config.AlicloudSecretKey))
return nil, nil
}
func (b *Builder) Run(ui packer.Ui, hook packer.Hook, cache packer.Cache) (packer.Artifact, error) {
client, err := b.config.Client()
if err != nil {
return nil, err
}
state := new(multistep.BasicStateBag)
state.Put("config", b.config)
state.Put("client", client)
state.Put("hook", hook)
state.Put("ui", ui)
state.Put("networktype", b.chooseNetworkType())
var steps []multistep.Step
// Build the steps
steps = []multistep.Step{
&stepPreValidate{
AlicloudDestImageName: b.config.AlicloudImageName,
ForceDelete: b.config.AlicloudImageForceDetele,
},
&stepCheckAlicloudSourceImage{
SourceECSImageId: b.config.AlicloudSourceImage,
},
&StepConfigAlicloudKeyPair{
Debug: b.config.PackerDebug,
KeyPairName: b.config.SSHKeyPairName,
PrivateKeyFile: b.config.Comm.SSHPrivateKey,
TemporaryKeyPairName: b.config.TemporaryKeyPairName,
SSHAgentAuth: b.config.Comm.SSHAgentAuth,
//DebugKeyPath: b.config.Com
RegionId: b.config.AlicloudRegion,
},
}
if b.chooseNetworkType() == VpcNet {
steps = append(steps,
&stepConfigAlicloudVPC{
VpcId: b.config.VpcId,
CidrBlock: b.config.CidrBlock,
VpcName: b.config.VpcName,
},
&stepConfigAlicloudVSwitch{
VSwitchId: b.config.VSwitchId,
ZoneId: b.config.ZoneId,
CidrBlock: b.config.CidrBlock,
VSwitchName: b.config.VSwitchName,
})
}
steps = append(steps,
&stepConfigAlicloudSecurityGroup{
SecurityGroupId: b.config.SecurityGroupId,
SecurityGroupName: b.config.SecurityGroupId,
RegionId: b.config.AlicloudRegion,
},
&stepCreateAlicloudInstance{
IOOptimized: b.config.IOOptimized,
InstanceType: b.config.InstanceType,
UserData: b.config.UserData,
UserDataFile: b.config.UserDataFile,
RegionId: b.config.AlicloudRegion,
InternetChargeType: b.config.InternetChargeType,
InternetMaxBandwidthOut: b.config.InternetMaxBandwidthOut,
InstnaceName: b.config.InstanceName,
ZoneId: b.config.ZoneId,
})
if b.chooseNetworkType() == VpcNet {
steps = append(steps, &setpConfigAlicloudEIP{
AssociatePublicIpAddress: b.config.AssociatePublicIpAddress,
RegionId: b.config.AlicloudRegion,
InternetChargeType: b.config.InternetChargeType,
})
} else {
steps = append(steps, &stepConfigAlicloudPublicIP{
RegionId: b.config.AlicloudRegion,
})
}
steps = append(steps,
&stepRunAlicloudInstance{},
&stepMountAlicloudDisk{},
&stepAttachKeyPar{},
&communicator.StepConnect{
Config: &b.config.RunConfig.Comm,
Host: SSHHost(
client,
b.config.SSHPrivateIp),
SSHConfig: SSHConfig(
b.config.RunConfig.Comm.SSHAgentAuth,
b.config.RunConfig.Comm.SSHUsername,
b.config.RunConfig.Comm.SSHPassword),
},
&common.StepProvision{},
&stepStopAlicloudInstance{
ForceStop: b.config.ForceStopInstance,
},
&stepDeleteAlicloudImageSnapshots{
AlicloudImageForceDeteleSnapshots: b.config.AlicloudImageForceDeteleSnapshots,
AlicloudImageForceDetele: b.config.AlicloudImageForceDetele,
AlicloudImageName: b.config.AlicloudImageName,
},
&stepCreateAlicloudImage{},
&setpRegionCopyAlicloudImage{
AlicloudImageDestinationRegions: b.config.AlicloudImageDestinationRegions,
AlicloudImageDestinationNames: b.config.AlicloudImageDestinationNames,
RegionId: b.config.AlicloudRegion,
},
&setpShareAlicloudImage{
AlicloudImageShareAccounts: b.config.AlicloudImageShareAccounts,
AlicloudImageUNShareAccounts: b.config.AlicloudImageUNShareAccounts,
RegionId: b.config.AlicloudRegion,
})
// Run!
b.runner = common.NewRunner(steps, b.config.PackerConfig, ui)
b.runner.Run(state)
// If there was an error, return that
if rawErr, ok := state.GetOk("error"); ok {
return nil, rawErr.(error)
}
// If there are no ECS images, then just return
if _, ok := state.GetOk("alicloudimages"); !ok {
return nil, nil
}
// Build the artifact and return it
artifact := &Artifact{
AlicloudImages: state.Get("alicloudimages").(map[string]string),
BuilderIdValue: BuilderId,
Client: client,
}
return artifact, nil
}
func (b *Builder) Cancel() {
if b.runner != nil {
log.Println("Cancelling the step runner...")
b.runner.Cancel()
}
}
func (b *Builder) chooseNetworkType() InstanceNetWork {
if b.isVpcNetRequired() {
return VpcNet
} else {
return ClassicNet
}
}
func (b *Builder) isVpcNetRequired() bool {
// UserData and KeyPair only works in VPC
return b.isVpcSpecified() || b.isUserDataNeeded() || b.isKeyPairNeeded()
}
func (b *Builder) isVpcSpecified() bool {
return b.config.VpcId != "" || b.config.VSwitchId != ""
}
func (b *Builder) isUserDataNeeded() bool {
// Public key setup requires userdata
if b.config.RunConfig.Comm.SSHPrivateKey != "" {
return true
}
return b.config.UserData != "" || b.config.UserDataFile != ""
}
func (b *Builder) isKeyPairNeeded() bool {
return b.config.SSHKeyPairName != "" || b.config.TemporaryKeyPairName != ""
}
-331
View File
@@ -1,331 +0,0 @@
package ecs
import (
"os"
"testing"
"fmt"
"github.com/denverdino/aliyungo/common"
"github.com/denverdino/aliyungo/ecs"
builderT "github.com/hashicorp/packer/helper/builder/testing"
"github.com/hashicorp/packer/packer"
)
func TestBuilderAcc_basic(t *testing.T) {
builderT.Test(t, builderT.TestCase{
PreCheck: func() {
testAccPreCheck(t)
},
Builder: &Builder{},
Template: testBuilderAccBasic,
})
}
//func TestBuilderAcc_windows(t *testing.T) {
// builderT.Test(t, builderT.TestCase{
// PreCheck: func() {
// testAccPreCheck(t)
// },
// Builder: &Builder{},
// Template: testBuilderAccWindows,
// })
//}
//func TestBuilderAcc_regionCopy(t *testing.T) {
// builderT.Test(t, builderT.TestCase{
// PreCheck: func() {
// testAccPreCheck(t)
// },
// Builder: &Builder{},
// Template: testBuilderAccRegionCopy,
// Check: checkRegionCopy([]string{"cn-hangzhou", "cn-shenzhen"}),
// })
//}
func TestBuilderAcc_forceDelete(t *testing.T) {
// Build the same alicloud image twice, with ecs_image_force_delete on the second run
builderT.Test(t, builderT.TestCase{
PreCheck: func() {
testAccPreCheck(t)
},
Builder: &Builder{},
Template: buildForceDeregisterConfig("false", "delete"),
SkipArtifactTeardown: true,
})
builderT.Test(t, builderT.TestCase{
PreCheck: func() {
testAccPreCheck(t)
},
Builder: &Builder{},
Template: buildForceDeregisterConfig("true", "delete"),
})
}
func TestBuilderAcc_ECSImageSharing(t *testing.T) {
builderT.Test(t, builderT.TestCase{
PreCheck: func() {
testAccPreCheck(t)
},
Builder: &Builder{},
Template: testBuilderAccSharing,
Check: checkECSImageSharing("1309208528360047"),
})
}
func TestBuilderAcc_forceDeleteSnapshot(t *testing.T) {
destImageName := "delete"
// Build the same alicloud image name twice, with force_delete_snapshot on the second run
builderT.Test(t, builderT.TestCase{
PreCheck: func() {
testAccPreCheck(t)
},
Builder: &Builder{},
Template: buildForceDeleteSnapshotConfig("false", destImageName),
SkipArtifactTeardown: true,
})
// Get image data by image image name
client, _ := testAliyunClient()
images, _, _ := client.DescribeImages(&ecs.DescribeImagesArgs{
ImageName: "packer-test-" + destImageName,
RegionId: common.Region("cn-beijing")})
image := images[0]
// Get snapshot ids for image
snapshotIds := []string{}
for _, device := range image.DiskDeviceMappings.DiskDeviceMapping {
if device.Device != "" && device.SnapshotId != "" {
snapshotIds = append(snapshotIds, device.SnapshotId)
}
}
builderT.Test(t, builderT.TestCase{
PreCheck: func() {
testAccPreCheck(t)
},
Builder: &Builder{},
Template: buildForceDeleteSnapshotConfig("true", destImageName),
Check: checkSnapshotsDeleted(snapshotIds),
})
}
func checkSnapshotsDeleted(snapshotIds []string) builderT.TestCheckFunc {
return func(artifacts []packer.Artifact) error {
// Verify the snapshots are gone
client, _ := testAliyunClient()
snapshotResp, _, err := client.DescribeSnapshots(
&ecs.DescribeSnapshotsArgs{RegionId: common.Region("cn-beijing"), SnapshotIds: snapshotIds},
)
if err != nil {
return fmt.Errorf("Query snapshot failed %v", err)
}
if len(snapshotResp) > 0 {
return fmt.Errorf("Snapshots weren't successfully deleted by " +
"`ecs_image_force_delete_snapshots`")
}
return nil
}
}
func checkECSImageSharing(uid string) builderT.TestCheckFunc {
return func(artifacts []packer.Artifact) error {
if len(artifacts) > 1 {
return fmt.Errorf("more than 1 artifact")
}
// Get the actual *Artifact pointer so we can access the AMIs directly
artifactRaw := artifacts[0]
artifact, ok := artifactRaw.(*Artifact)
if !ok {
return fmt.Errorf("unknown artifact: %#v", artifactRaw)
}
// describe the image, get block devices with a snapshot
client, _ := testAliyunClient()
imageSharePermissionResponse, err := client.DescribeImageSharePermission(
&ecs.ModifyImageSharePermissionArgs{
RegionId: "cn-beijing",
ImageId: artifact.AlicloudImages["cn-beijing"],
})
if err != nil {
return fmt.Errorf("Error retrieving Image Attributes for ECS Image Artifact (%#v) "+
"in ECS Image Sharing Test: %s", artifact, err)
}
if len(imageSharePermissionResponse.Accounts.Account) != 1 &&
imageSharePermissionResponse.Accounts.Account[0].AliyunId != uid {
return fmt.Errorf("share account is incorrect %d",
len(imageSharePermissionResponse.Accounts.Account))
}
return nil
}
}
func checkRegionCopy(regions []string) builderT.TestCheckFunc {
return func(artifacts []packer.Artifact) error {
if len(artifacts) > 1 {
return fmt.Errorf("more than 1 artifact")
}
// Get the actual *Artifact pointer so we can access the AMIs directly
artifactRaw := artifacts[0]
artifact, ok := artifactRaw.(*Artifact)
if !ok {
return fmt.Errorf("unknown artifact: %#v", artifactRaw)
}
// Verify that we copied to only the regions given
regionSet := make(map[string]struct{})
for _, r := range regions {
regionSet[r] = struct{}{}
}
for r := range artifact.AlicloudImages {
if r == "cn-beijing" {
delete(regionSet, r)
continue
}
if _, ok := regionSet[r]; !ok {
return fmt.Errorf("unknown region: %s", r)
}
delete(regionSet, r)
}
if len(regionSet) > 0 {
return fmt.Errorf("didn't copy to: %#v", regionSet)
}
client, _ := testAliyunClient()
for key, value := range artifact.AlicloudImages {
client.WaitForImageReady(common.Region(key), value, 1800)
}
return nil
}
}
func testAccPreCheck(t *testing.T) {
if v := os.Getenv("ALICLOUD_ACCESS_KEY"); v == "" {
t.Fatal("ALICLOUD_ACCESS_KEY must be set for acceptance tests")
}
if v := os.Getenv("ALICLOUD_SECRET_KEY"); v == "" {
t.Fatal("ALICLOUD_SECRET_KEY must be set for acceptance tests")
}
}
func testAliyunClient() (*ecs.Client, error) {
access := &AlicloudAccessConfig{AlicloudRegion: "cn-beijing"}
err := access.Config()
if err != nil {
return nil, err
}
client, err := access.Client()
if err != nil {
return nil, err
}
return client, nil
}
const testBuilderAccBasic = `
{ "builders": [{
"type": "test",
"region": "cn-beijing",
"instance_type": "ecs.n1.tiny",
"source_image":"ubuntu_16_0402_64_40G_base_20170222.vhd",
"ssh_username": "ubuntu",
"io_optimized":"true",
"ssh_username":"root",
"image_name": "packer-test_{{timestamp}}"
}]
}`
const testBuilderAccRegionCopy = `
{
"builders": [{
"type": "test",
"region": "cn-beijing",
"instance_type": "ecs.n1.tiny",
"source_image":"ubuntu_16_0402_64_40G_base_20170222.vhd",
"io_optimized":"true",
"ssh_username":"root",
"image_name": "packer-test_{{timestamp}}",
"image_copy_regions": ["cn-hangzhou", "cn-shenzhen"]
}]
}
`
const testBuilderAccForceDelete = `
{
"builders": [{
"type": "test",
"region": "cn-beijing",
"instance_type": "ecs.n1.tiny",
"source_image":"ubuntu_16_0402_64_40G_base_20170222.vhd",
"io_optimized":"true",
"ssh_username":"root",
"image_force_delete": "%s",
"image_name": "packer-test_%s"
}]
}
`
const testBuilderAccForceDeleteSnapshot = `
{
"builders": [{
"type": "test",
"region": "cn-beijing",
"instance_type": "ecs.n1.tiny",
"source_image":"ubuntu_16_0402_64_40G_base_20170222.vhd",
"io_optimized":"true",
"ssh_username":"root",
"image_force_delete_snapshots": "%s",
"image_force_delete": "%s",
"image_name": "packer-test-%s"
}]
}
`
// share with catsby
const testBuilderAccSharing = `
{
"builders": [{
"type": "test",
"region": "cn-beijing",
"instance_type": "ecs.n1.tiny",
"source_image":"ubuntu_16_0402_64_40G_base_20170222.vhd",
"io_optimized":"true",
"ssh_username":"root",
"image_name": "packer-test_{{timestamp}}",
"image_share_account":["1309208528360047"]
}]
}
`
func buildForceDeregisterConfig(val, name string) string {
return fmt.Sprintf(testBuilderAccForceDelete, val, name)
}
func buildForceDeleteSnapshotConfig(val, name string) string {
return fmt.Sprintf(testBuilderAccForceDeleteSnapshot, val, val, name)
}
const testBuilderAccWindows = `
{ "builders": [{
"type": "test",
"region": "cn-beijing",
"instance_type": "ecs.n1.tiny",
"source_image":"win2008_64_ent_r2_zh-cn_40G_alibase_20170301.vhd",
"io_optimized":"true",
"image_force_delete":"true",
"communicator": "winrm",
"winrm_port": 5985,
"winrm_username": "Administrator",
"winrm_password": "Test1234",
"image_name": "packer-test_{{timestamp}}"
}]
}`
-95
View File
@@ -1,95 +0,0 @@
package ecs
import (
"testing"
"github.com/hashicorp/packer/packer"
)
func testBuilderConfig() map[string]interface{} {
return map[string]interface{}{
"access_key": "foo",
"secret_key": "bar",
"source_image": "foo",
"instance_type": "ecs.n1.tiny",
"region": "cn-beijing",
"ssh_username": "root",
"image_name": "foo",
"io_optimized": true,
}
}
func TestBuilder_ImplementsBuilder(t *testing.T) {
var raw interface{}
raw = &Builder{}
if _, ok := raw.(packer.Builder); !ok {
t.Fatalf("Builder should be a builder")
}
}
func TestBuilder_Prepare_BadType(t *testing.T) {
b := &Builder{}
c := map[string]interface{}{
"access_key": []string{},
}
warnings, err := b.Prepare(c)
if len(warnings) > 0 {
t.Fatalf("bad: %#v", warnings)
}
if err == nil {
t.Fatalf("prepare should fail")
}
}
func TestBuilderPrepare_ECSImageName(t *testing.T) {
var b Builder
config := testBuilderConfig()
// Test good
config["image_name"] = "ecs.n1.tiny"
warnings, err := b.Prepare(config)
if len(warnings) > 0 {
t.Fatalf("bad: %#v", warnings)
}
if err != nil {
t.Fatalf("should not have error: %s", err)
}
// Test bad
config["ecs_image_name"] = "foo {{"
b = Builder{}
warnings, err = b.Prepare(config)
if len(warnings) > 0 {
t.Fatalf("bad: %#v", warnings)
}
if err == nil {
t.Fatal("should have error")
}
// Test bad
delete(config, "image_name")
b = Builder{}
warnings, err = b.Prepare(config)
if len(warnings) > 0 {
t.Fatalf("bad: %#v", warnings)
}
if err == nil {
t.Fatal("should have error")
}
}
func TestBuilderPrepare_InvalidKey(t *testing.T) {
var b Builder
config := testBuilderConfig()
// Add a random key
config["i_should_not_be_valid"] = true
warnings, err := b.Prepare(config)
if len(warnings) > 0 {
t.Fatalf("bad: %#v", warnings)
}
if err == nil {
t.Fatal("should have error")
}
}
-99
View File
@@ -1,99 +0,0 @@
package ecs
import (
"fmt"
"github.com/denverdino/aliyungo/common"
"github.com/hashicorp/packer/template/interpolate"
"regexp"
"strings"
)
type AlicloudDiskDevice struct {
DiskName string `mapstructure:"disk_name"`
DiskCategory string `mapstructure:"disk_category"`
DiskSize int `mapstructure:"disk_size"`
SnapshotId string `mapstructure:"disk_snapshot_id"`
Description string `mapstructure:"disk_description"`
DeleteWithInstance bool `mapstructure:"disk_delete_with_instance"`
Device string `mapstructure:"disk_device"`
}
type AlicloudDiskDevices struct {
ECSImagesDiskMappings []AlicloudDiskDevice `mapstructure:"image_disk_mappings"`
}
type AlicloudImageConfig struct {
AlicloudImageName string `mapstructure:"image_name"`
AlicloudImageVersion string `mapstructure:"image_version"`
AlicloudImageDescription string `mapstructure:"image_description"`
AlicloudImageShareAccounts []string `mapstructure:"image_share_account"`
AlicloudImageUNShareAccounts []string `mapstructure:"image_unshare_account"`
AlicloudImageDestinationRegions []string `mapstructure:"image_copy_regions"`
AlicloudImageDestinationNames []string `mapstructure:"image_copy_names"`
AlicloudImageForceDetele bool `mapstructure:"image_force_delete"`
AlicloudImageForceDeteleSnapshots bool `mapstructure:"image_force_delete_snapshots"`
AlicloudImageForceDeleteInstances bool `mapstructure:"image_force_delete_instances"`
AlicloudImageSkipRegionValidation bool `mapstructure:"skip_region_validation"`
AlicloudDiskDevices `mapstructure:",squash"`
}
func (c *AlicloudImageConfig) Prepare(ctx *interpolate.Context) []error {
var errs []error
if c.AlicloudImageName == "" {
errs = append(errs, fmt.Errorf("image_name must be specified"))
} else if len(c.AlicloudImageName) < 2 || len(c.AlicloudImageName) > 128 {
errs = append(errs, fmt.Errorf("image_name must less than 128 letters and more than 1 letters"))
} else if strings.HasPrefix(c.AlicloudImageName, "http://") ||
strings.HasPrefix(c.AlicloudImageName, "https://") {
errs = append(errs, fmt.Errorf("image_name can't start with 'http://' or 'https://'"))
}
reg := regexp.MustCompile("\\s+")
if reg.FindString(c.AlicloudImageName) != "" {
errs = append(errs, fmt.Errorf("image_name can't include spaces"))
}
if len(c.AlicloudImageDestinationRegions) > 0 {
regionSet := make(map[string]struct{})
regions := make([]string, 0, len(c.AlicloudImageDestinationRegions))
for _, region := range c.AlicloudImageDestinationRegions {
// If we already saw the region, then don't look again
if _, ok := regionSet[region]; ok {
continue
}
// Mark that we saw the region
regionSet[region] = struct{}{}
if !c.AlicloudImageSkipRegionValidation {
// Verify the region is real
if valid := validateRegion(region); valid != nil {
errs = append(errs, fmt.Errorf("Unknown region: %s", region))
continue
}
}
regions = append(regions, region)
}
c.AlicloudImageDestinationRegions = regions
}
if len(errs) > 0 {
return errs
}
return nil
}
func validateRegion(region string) error {
for _, valid := range common.ValidRegions {
if region == string(valid) {
return nil
}
}
return fmt.Errorf("Not a valid alicloud region: %s", region)
}
-64
View File
@@ -1,64 +0,0 @@
package ecs
import (
"testing"
"github.com/denverdino/aliyungo/common"
)
func testAlicloudImageConfig() *AlicloudImageConfig {
return &AlicloudImageConfig{
AlicloudImageName: "foo",
}
}
func TestECSImageConfigPrepare_name(t *testing.T) {
c := testAlicloudImageConfig()
if err := c.Prepare(nil); err != nil {
t.Fatalf("shouldn't have err: %s", err)
}
c.AlicloudImageName = ""
if err := c.Prepare(nil); err == nil {
t.Fatal("should have error")
}
}
func TestAMIConfigPrepare_regions(t *testing.T) {
c := testAlicloudImageConfig()
c.AlicloudImageDestinationRegions = nil
if err := c.Prepare(nil); err != nil {
t.Fatalf("shouldn't have err: %s", err)
}
c.AlicloudImageDestinationRegions = regionsToString()
if err := c.Prepare(nil); err != nil {
t.Fatalf("shouldn't have err: %s", err)
}
c.AlicloudImageDestinationRegions = []string{"foo"}
if err := c.Prepare(nil); err == nil {
t.Fatal("should have error")
}
c.AlicloudImageDestinationRegions = []string{"cn-beijing", "cn-hangzhou", "eu-central-1"}
if err := c.Prepare(nil); err != nil {
t.Fatalf("bad: %s", err)
}
c.AlicloudImageDestinationRegions = []string{"unknow"}
c.AlicloudImageSkipRegionValidation = true
if err := c.Prepare(nil); err != nil {
t.Fatal("shouldn't have error")
}
c.AlicloudImageSkipRegionValidation = false
}
func regionsToString() []string {
var regions []string
for _, region := range common.ValidRegions {
regions = append(regions, string(region))
}
return regions
}
-22
View File
@@ -1,22 +0,0 @@
package ecs
import (
"fmt"
"github.com/hashicorp/packer/packer"
"github.com/mitchellh/multistep"
)
func message(state multistep.StateBag, module string) {
_, cancelled := state.GetOk(multistep.StateCancelled)
_, halted := state.GetOk(multistep.StateHalted)
ui := state.Get("ui").(packer.Ui)
if cancelled || halted {
ui.Say(fmt.Sprintf("Deleting %s because of cancellation or error...", module))
} else {
ui.Say(fmt.Sprintf("Cleaning up '%s'", module))
}
}
-72
View File
@@ -1,72 +0,0 @@
package ecs
import (
"errors"
"fmt"
"os"
"strings"
"github.com/hashicorp/packer/common/uuid"
"github.com/hashicorp/packer/helper/communicator"
"github.com/hashicorp/packer/template/interpolate"
)
type RunConfig struct {
AssociatePublicIpAddress bool `mapstructure:"associate_public_ip_address"`
ZoneId string `mapstructure:"zone_id"`
IOOptimized bool `mapstructure:"io_optimized"`
InstanceType string `mapstructure:"instance_type"`
Description string `mapstructure:"description"`
AlicloudSourceImage string `mapstructure:"source_image"`
ForceStopInstance bool `mapstructure:"force_stop_instance"`
SecurityGroupId string `mapstructure:"security_group_id"`
SecurityGroupName string `mapstructure:"security_group_name"`
UserData string `mapstructure:"user_data"`
UserDataFile string `mapstructure:"user_data_file"`
VpcId string `mapstructure:"vpc_id"`
VpcName string `mapstructure:"vpc_name"`
CidrBlock string `mapstructure:"vpc_cidr_block"`
VSwitchId string `mapstructure:"vswitch_id"`
VSwitchName string `mapstructure:"vswitch_id"`
InstanceName string `mapstructure:"instance_name"`
InternetChargeType string `mapstructure:"internet_charge_type"`
InternetMaxBandwidthOut int `mapstructure:"internet_max_bandwidth_out"`
TemporaryKeyPairName string `mapstructure:"temporary_key_pair_name"`
// Communicator settings
Comm communicator.Config `mapstructure:",squash"`
SSHKeyPairName string `mapstructure:"ssh_keypair_name"`
SSHPrivateIp bool `mapstructure:"ssh_private_ip"`
}
func (c *RunConfig) Prepare(ctx *interpolate.Context) []error {
if c.SSHKeyPairName == "" && c.TemporaryKeyPairName == "" &&
c.Comm.SSHPrivateKey == "" && c.Comm.SSHPassword == "" && c.Comm.WinRMPassword == "" {
c.TemporaryKeyPairName = fmt.Sprintf("packer_%s", uuid.TimeOrderedUUID())
}
// Validation
errs := c.Comm.Prepare(ctx)
if c.AlicloudSourceImage == "" {
errs = append(errs, errors.New("A source_image must be specified"))
}
if strings.TrimSpace(c.AlicloudSourceImage) != c.AlicloudSourceImage {
errs = append(errs, errors.New("The source_image can't include spaces"))
}
if c.InstanceType == "" {
errs = append(errs, errors.New("An aliclod_instance_type must be specified"))
}
if c.UserData != "" && c.UserDataFile != "" {
errs = append(errs, fmt.Errorf("Only one of user_data or user_data_file can be specified."))
} else if c.UserDataFile != "" {
if _, err := os.Stat(c.UserDataFile); err != nil {
errs = append(errs, fmt.Errorf("user_data_file not found: %s", c.UserDataFile))
}
}
return errs
}
-122
View File
@@ -1,122 +0,0 @@
package ecs
import (
"io/ioutil"
"testing"
"github.com/hashicorp/packer/helper/communicator"
)
func testConfig() *RunConfig {
return &RunConfig{
AlicloudSourceImage: "alicloud_images",
InstanceType: "ecs.n1.tiny",
Comm: communicator.Config{
SSHUsername: "alicloud",
},
}
}
func TestRunConfigPrepare(t *testing.T) {
c := testConfig()
err := c.Prepare(nil)
if len(err) > 0 {
t.Fatalf("err: %s", err)
}
}
func TestRunConfigPrepare_InstanceType(t *testing.T) {
c := testConfig()
c.InstanceType = ""
if err := c.Prepare(nil); len(err) != 1 {
t.Fatalf("err: %s", err)
}
}
func TestRunConfigPrepare_SourceECSImage(t *testing.T) {
c := testConfig()
c.AlicloudSourceImage = ""
if err := c.Prepare(nil); len(err) != 1 {
t.Fatalf("err: %s", err)
}
}
func TestRunConfigPrepare_SSHPort(t *testing.T) {
c := testConfig()
c.Comm.SSHPort = 0
if err := c.Prepare(nil); len(err) != 0 {
t.Fatalf("err: %s", err)
}
if c.Comm.SSHPort != 22 {
t.Fatalf("invalid value: %d", c.Comm.SSHPort)
}
c.Comm.SSHPort = 44
if err := c.Prepare(nil); len(err) != 0 {
t.Fatalf("err: %s", err)
}
if c.Comm.SSHPort != 44 {
t.Fatalf("invalid value: %d", c.Comm.SSHPort)
}
}
func TestRunConfigPrepare_UserData(t *testing.T) {
c := testConfig()
tf, err := ioutil.TempFile("", "packer")
if err != nil {
t.Fatalf("err: %s", err)
}
defer tf.Close()
c.UserData = "foo"
c.UserDataFile = tf.Name()
if err := c.Prepare(nil); len(err) != 1 {
t.Fatalf("err: %s", err)
}
}
func TestRunConfigPrepare_UserDataFile(t *testing.T) {
c := testConfig()
if err := c.Prepare(nil); len(err) != 0 {
t.Fatalf("err: %s", err)
}
c.UserDataFile = "idontexistidontthink"
if err := c.Prepare(nil); len(err) != 1 {
t.Fatalf("err: %s", err)
}
tf, err := ioutil.TempFile("", "packer")
if err != nil {
t.Fatalf("err: %s", err)
}
defer tf.Close()
c.UserDataFile = tf.Name()
if err := c.Prepare(nil); len(err) != 0 {
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 name is empty")
}
c.TemporaryKeyPairName = "ssh-key-123"
if err := c.Prepare(nil); len(err) != 0 {
t.Fatalf("err: %s", err)
}
if c.TemporaryKeyPairName != "ssh-key-123" {
t.Fatal("keypair name does not match")
}
}
-83
View File
@@ -1,83 +0,0 @@
package ecs
import (
"fmt"
"net"
"os"
"time"
packerssh "github.com/hashicorp/packer/communicator/ssh"
"github.com/mitchellh/multistep"
"golang.org/x/crypto/ssh"
"golang.org/x/crypto/ssh/agent"
)
var (
// modified in tests
sshHostSleepDuration = time.Second
)
type alicloudSSHHelper interface {
}
// SSHHost returns a function that can be given to the SSH communicator
func SSHHost(e alicloudSSHHelper, private bool) func(multistep.StateBag) (string, error) {
return func(state multistep.StateBag) (string, error) {
ipAddress := state.Get("ipaddress").(string)
return ipAddress, nil
}
}
// SSHConfig returns a function that can be used for the SSH communicator
// config for connecting to the instance created over SSH using the private key
// or password.
func SSHConfig(useAgent bool, username, password string) func(multistep.StateBag) (*ssh.ClientConfig, error) {
return func(state multistep.StateBag) (*ssh.ClientConfig, error) {
if useAgent {
authSock := os.Getenv("SSH_AUTH_SOCK")
if authSock == "" {
return nil, fmt.Errorf("SSH_AUTH_SOCK is not set")
}
sshAgent, err := net.Dial("unix", authSock)
if err != nil {
return nil, fmt.Errorf("Cannot connect to SSH Agent socket %q: %s", authSock, err)
}
return &ssh.ClientConfig{
User: username,
Auth: []ssh.AuthMethod{
ssh.PublicKeysCallback(agent.NewClient(sshAgent).Signers),
},
HostKeyCallback: ssh.InsecureIgnoreHostKey(),
}, nil
}
privateKey, hasKey := state.GetOk("privateKey")
if hasKey {
signer, err := ssh.ParsePrivateKey([]byte(privateKey.(string)))
if err != nil {
return nil, fmt.Errorf("Error setting up SSH config: %s", err)
}
return &ssh.ClientConfig{
User: username,
Auth: []ssh.AuthMethod{
ssh.PublicKeys(signer),
},
HostKeyCallback: ssh.InsecureIgnoreHostKey(),
}, nil
} else {
return &ssh.ClientConfig{
User: username,
Auth: []ssh.AuthMethod{
ssh.Password(password),
ssh.KeyboardInteractive(
packerssh.PasswordKeyboardInteractive(password)),
},
HostKeyCallback: ssh.InsecureIgnoreHostKey(),
}, nil
}
}
}
@@ -1,72 +0,0 @@
package ecs
import (
"fmt"
"github.com/denverdino/aliyungo/common"
"github.com/denverdino/aliyungo/ecs"
"github.com/hashicorp/packer/packer"
"github.com/mitchellh/multistep"
)
type stepAttachKeyPar struct {
}
func (s *stepAttachKeyPar) Run(state multistep.StateBag) multistep.StepAction {
keyPairName := state.Get("keyPair").(string)
if keyPairName == "" {
return multistep.ActionContinue
}
ui := state.Get("ui").(packer.Ui)
client := state.Get("client").(*ecs.Client)
config := state.Get("config").(Config)
instance := state.Get("instance").(*ecs.InstanceAttributesType)
retry_times := 3
for {
err := client.AttachKeyPair(&ecs.AttachKeyPairArgs{RegionId: common.Region(config.AlicloudRegion),
KeyPairName: keyPairName, InstanceIds: "[\"" + instance.InstanceId + "\"]"})
if err != nil {
e, _ := err.(*common.Error)
if (!(e.Code == "MissingParameter" || e.Code == "DependencyViolation.WindowsInstance" ||
e.Code == "InvalidKeyPairName.NotFound" || e.Code == "InvalidRegionId.NotFound")) &&
retry_times > 0 {
retry_times = retry_times - 1
continue
}
err := fmt.Errorf("Error attaching keypair %s to instance %s : %s",
keyPairName, instance.InstanceId, err)
state.Put("error", err)
ui.Error(err.Error())
return multistep.ActionHalt
}
break
}
ui.Message(fmt.Sprintf("Attach keypair %s to instance: %s", keyPairName, instance.InstanceId))
return multistep.ActionContinue
}
func (s *stepAttachKeyPar) Cleanup(state multistep.StateBag) {
keyPairName := state.Get("keyPair").(string)
if keyPairName == "" {
return
}
client := state.Get("client").(*ecs.Client)
config := state.Get("config").(Config)
ui := state.Get("ui").(packer.Ui)
instance := state.Get("instance").(*ecs.InstanceAttributesType)
err := client.DetachKeyPair(&ecs.DetachKeyPairArgs{RegionId: common.Region(config.AlicloudRegion),
KeyPairName: keyPairName, InstanceIds: "[\"" + instance.InstanceId + "\"]"})
if err != nil {
err := fmt.Errorf("Error Detaching keypair %s to instance %s : %s", keyPairName,
instance.InstanceId, err)
state.Put("error", err)
ui.Error(err.Error())
return
}
ui.Message(fmt.Sprintf("Detach keypair %s from instance: %s", keyPairName, instance.InstanceId))
}
@@ -1,42 +0,0 @@
package ecs
import (
"fmt"
"github.com/denverdino/aliyungo/common"
"github.com/denverdino/aliyungo/ecs"
"github.com/hashicorp/packer/packer"
"github.com/mitchellh/multistep"
)
type stepCheckAlicloudSourceImage struct {
SourceECSImageId string
}
func (s *stepCheckAlicloudSourceImage) Run(state multistep.StateBag) multistep.StepAction {
client := state.Get("client").(*ecs.Client)
config := state.Get("config").(Config)
ui := state.Get("ui").(packer.Ui)
images, _, err := client.DescribeImages(&ecs.DescribeImagesArgs{RegionId: common.Region(config.AlicloudRegion),
ImageId: config.AlicloudSourceImage})
if err != nil {
err := fmt.Errorf("Error querying alicloud image: %s", err)
state.Put("error", err)
ui.Error(err.Error())
return multistep.ActionHalt
}
if len(images) == 0 {
err := fmt.Errorf("No alicloud image was found matching filters: %v", config.AlicloudSourceImage)
state.Put("error", err)
ui.Error(err.Error())
return multistep.ActionHalt
}
ui.Message(fmt.Sprintf("Found image ID: %s", images[0].ImageId))
state.Put("source_image", &images[0])
return multistep.ActionContinue
}
func (s *stepCheckAlicloudSourceImage) Cleanup(multistep.StateBag) {}
-79
View File
@@ -1,79 +0,0 @@
package ecs
import (
"fmt"
"github.com/denverdino/aliyungo/common"
"github.com/denverdino/aliyungo/ecs"
"github.com/hashicorp/packer/packer"
"github.com/mitchellh/multistep"
)
type setpConfigAlicloudEIP struct {
AssociatePublicIpAddress bool
RegionId string
InternetChargeType string
allocatedId string
}
func (s *setpConfigAlicloudEIP) Run(state multistep.StateBag) multistep.StepAction {
client := state.Get("client").(*ecs.Client)
ui := state.Get("ui").(packer.Ui)
instance := state.Get("instance").(*ecs.InstanceAttributesType)
ui.Say("Allocating eip")
ipaddress, allocateId, err := client.AllocateEipAddress(&ecs.AllocateEipAddressArgs{
RegionId: common.Region(s.RegionId), InternetChargeType: common.InternetChargeType(s.InternetChargeType),
})
if err != nil {
state.Put("error", err)
ui.Say(fmt.Sprintf("Error allocating eip: %s", err))
return multistep.ActionHalt
}
s.allocatedId = allocateId
if err = client.WaitForEip(common.Region(s.RegionId), allocateId,
ecs.EipStatusAvailable, ALICLOUD_DEFAULT_SHORT_TIMEOUT); err != nil {
state.Put("error", err)
ui.Say(fmt.Sprintf("Error allocating eip: %s", err))
return multistep.ActionHalt
}
if err = client.AssociateEipAddress(allocateId, instance.InstanceId); err != nil {
state.Put("error", err)
ui.Say(fmt.Sprintf("Error binding eip: %s", err))
return multistep.ActionHalt
}
if err = client.WaitForEip(common.Region(s.RegionId), allocateId,
ecs.EipStatusInUse, ALICLOUD_DEFAULT_SHORT_TIMEOUT); err != nil {
state.Put("error", err)
ui.Say(fmt.Sprintf("Error associating eip: %s", err))
return multistep.ActionHalt
}
ui.Say(fmt.Sprintf("Allocated eip %s", ipaddress))
state.Put("ipaddress", ipaddress)
return multistep.ActionContinue
}
func (s *setpConfigAlicloudEIP) Cleanup(state multistep.StateBag) {
if len(s.allocatedId) == 0 {
return
}
client := state.Get("client").(*ecs.Client)
instance := state.Get("instance").(*ecs.InstanceAttributesType)
ui := state.Get("ui").(packer.Ui)
message(state, "EIP")
if err := client.UnassociateEipAddress(s.allocatedId, instance.InstanceId); err != nil {
ui.Say(fmt.Sprintf("Failed to unassociate eip."))
}
if err := client.WaitForEip(common.Region(s.RegionId), s.allocatedId, ecs.EipStatusAvailable, ALICLOUD_DEFAULT_SHORT_TIMEOUT); err != nil {
ui.Say(fmt.Sprintf("Timeout while unassociating eip."))
}
if err := client.ReleaseEipAddress(s.allocatedId); err != nil {
ui.Say(fmt.Sprintf("Failed to release eip."))
}
}
@@ -1,139 +0,0 @@
package ecs
import (
"fmt"
"io/ioutil"
"os"
"runtime"
"github.com/denverdino/aliyungo/common"
"github.com/denverdino/aliyungo/ecs"
"github.com/hashicorp/packer/packer"
"github.com/mitchellh/multistep"
)
type StepConfigAlicloudKeyPair struct {
Debug bool
SSHAgentAuth bool
DebugKeyPath string
TemporaryKeyPairName string
KeyPairName string
PrivateKeyFile string
RegionId string
keyName string
}
func (s *StepConfigAlicloudKeyPair) Run(state multistep.StateBag) multistep.StepAction {
ui := state.Get("ui").(packer.Ui)
if s.PrivateKeyFile != "" {
ui.Say("Using existing SSH private key")
privateKeyBytes, err := ioutil.ReadFile(s.PrivateKeyFile)
if err != nil {
state.Put("error", fmt.Errorf(
"Error loading configured private key file: %s", err))
return multistep.ActionHalt
}
state.Put("keyPair", s.KeyPairName)
state.Put("privateKey", string(privateKeyBytes))
return multistep.ActionContinue
}
if s.SSHAgentAuth && s.KeyPairName == "" {
ui.Say("Using SSH Agent with key pair in source image")
return multistep.ActionContinue
}
if s.SSHAgentAuth && s.KeyPairName != "" {
ui.Say(fmt.Sprintf("Using SSH Agent for existing key pair %s", s.KeyPairName))
state.Put("keyPair", s.KeyPairName)
return multistep.ActionContinue
}
if s.TemporaryKeyPairName == "" {
ui.Say("Not using temporary keypair")
state.Put("keyPair", "")
return multistep.ActionContinue
}
client := state.Get("client").(*ecs.Client)
ui.Say(fmt.Sprintf("Creating temporary keypair: %s", s.TemporaryKeyPairName))
keyResp, err := client.CreateKeyPair(&ecs.CreateKeyPairArgs{
KeyPairName: s.TemporaryKeyPairName,
RegionId: common.Region(s.RegionId),
})
if err != nil {
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 = s.TemporaryKeyPairName
// Set some state data for use in future steps
state.Put("keyPair", s.keyName)
state.Put("privateKey", keyResp.PrivateKeyBody)
// 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.PrivateKeyBody)); 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 *StepConfigAlicloudKeyPair) Cleanup(state multistep.StateBag) {
// If no key name is set, then we never created it, so just return
// If we used an SSH private key file, do not go about deleting
// keypairs
if s.PrivateKeyFile != "" || (s.KeyPairName == "" && s.keyName == "") {
return
}
client := state.Get("client").(*ecs.Client)
ui := state.Get("ui").(packer.Ui)
// Remove the keypair
ui.Say("Deleting temporary keypair...")
err := client.DeleteKeyPairs(&ecs.DeleteKeyPairsArgs{
RegionId: common.Region(s.RegionId),
KeyPairNames: "[\"" + s.keyName + "\"]",
})
if err != nil {
ui.Error(fmt.Sprintf(
"Error cleaning up keypair. Please delete the key manually: %s", s.keyName))
}
// Also remove the physical key if we're debugging.
if s.Debug {
if err := os.Remove(s.DebugKeyPath); err != nil {
ui.Error(fmt.Sprintf(
"Error removing debug key '%s': %s", s.DebugKeyPath, err))
}
}
}
@@ -1,35 +0,0 @@
package ecs
import (
"fmt"
"github.com/denverdino/aliyungo/ecs"
"github.com/hashicorp/packer/packer"
"github.com/mitchellh/multistep"
)
type stepConfigAlicloudPublicIP struct {
publicIPAdress string
RegionId string
}
func (s *stepConfigAlicloudPublicIP) Run(state multistep.StateBag) multistep.StepAction {
client := state.Get("client").(*ecs.Client)
ui := state.Get("ui").(packer.Ui)
instance := state.Get("instance").(*ecs.InstanceAttributesType)
ipaddress, err := client.AllocatePublicIpAddress(instance.InstanceId)
if err != nil {
state.Put("error", err)
ui.Say(fmt.Sprintf("Error allocating public ip: %s", err))
return multistep.ActionHalt
}
s.publicIPAdress = ipaddress
ui.Say(fmt.Sprintf("Allocated public ip address %s.", ipaddress))
state.Put("ipaddress", ipaddress)
return multistep.ActionContinue
}
func (s *stepConfigAlicloudPublicIP) Cleanup(state multistep.StateBag) {
}
@@ -1,137 +0,0 @@
package ecs
import (
"errors"
"fmt"
"time"
"github.com/denverdino/aliyungo/common"
"github.com/denverdino/aliyungo/ecs"
"github.com/hashicorp/packer/packer"
"github.com/mitchellh/multistep"
)
type stepConfigAlicloudSecurityGroup struct {
SecurityGroupId string
SecurityGroupName string
Description string
VpcId string
RegionId string
isCreate bool
}
func (s *stepConfigAlicloudSecurityGroup) Run(state multistep.StateBag) multistep.StepAction {
client := state.Get("client").(*ecs.Client)
ui := state.Get("ui").(packer.Ui)
networkType := state.Get("networktype").(InstanceNetWork)
var securityGroupItems []ecs.SecurityGroupItemType
var err error
if len(s.SecurityGroupId) != 0 {
if networkType == VpcNet {
vpcId := state.Get("vpcid").(string)
securityGroupItems, _, err = client.DescribeSecurityGroups(&ecs.DescribeSecurityGroupsArgs{
VpcId: vpcId,
RegionId: common.Region(s.RegionId),
})
} else {
securityGroupItems, _, err = client.DescribeSecurityGroups(&ecs.DescribeSecurityGroupsArgs{
RegionId: common.Region(s.RegionId),
})
}
if err != nil {
ui.Say(fmt.Sprintf("Failed querying security group: %s", err))
state.Put("error", err)
return multistep.ActionHalt
}
for _, securityGroupItem := range securityGroupItems {
if securityGroupItem.SecurityGroupId == s.SecurityGroupId {
state.Put("securitygroupid", s.SecurityGroupId)
s.isCreate = false
return multistep.ActionContinue
}
}
s.isCreate = false
message := fmt.Sprintf("The specified security group {%s} doesn't exist.", s.SecurityGroupId)
state.Put("error", errors.New(message))
ui.Say(message)
return multistep.ActionHalt
}
var securityGroupId string
ui.Say("Creating security groups...")
if networkType == VpcNet {
vpcId := state.Get("vpcid").(string)
securityGroupId, err = client.CreateSecurityGroup(&ecs.CreateSecurityGroupArgs{
RegionId: common.Region(s.RegionId),
SecurityGroupName: s.SecurityGroupName,
VpcId: vpcId,
})
} else {
securityGroupId, err = client.CreateSecurityGroup(&ecs.CreateSecurityGroupArgs{
RegionId: common.Region(s.RegionId),
SecurityGroupName: s.SecurityGroupName,
})
}
if err != nil {
state.Put("error", err)
ui.Say(fmt.Sprintf("Failed creating security group %s.", err))
return multistep.ActionHalt
}
state.Put("securitygroupid", securityGroupId)
s.isCreate = true
s.SecurityGroupId = securityGroupId
err = client.AuthorizeSecurityGroupEgress(&ecs.AuthorizeSecurityGroupEgressArgs{
SecurityGroupId: securityGroupId,
RegionId: common.Region(s.RegionId),
IpProtocol: ecs.IpProtocolAll,
PortRange: "-1/-1",
NicType: ecs.NicTypeInternet,
DestCidrIp: "0.0.0.0/0", //The input parameter "DestGroupId" or "DestCidrIp" cannot be both blank.
})
if err != nil {
state.Put("error", err)
ui.Say(fmt.Sprintf("Failed authorizing security group: %s", err))
return multistep.ActionHalt
}
err = client.AuthorizeSecurityGroup(&ecs.AuthorizeSecurityGroupArgs{
SecurityGroupId: securityGroupId,
RegionId: common.Region(s.RegionId),
IpProtocol: ecs.IpProtocolAll,
PortRange: "-1/-1",
NicType: ecs.NicTypeInternet,
SourceCidrIp: "0.0.0.0/0", //The input parameter "SourceGroupId" or "SourceCidrIp" cannot be both blank.
})
if err != nil {
state.Put("error", err)
ui.Say(fmt.Sprintf("Failed authorizing security group: %s", err))
return multistep.ActionHalt
}
return multistep.ActionContinue
}
func (s *stepConfigAlicloudSecurityGroup) Cleanup(state multistep.StateBag) {
if !s.isCreate {
return
}
client := state.Get("client").(*ecs.Client)
ui := state.Get("ui").(packer.Ui)
message(state, "security group")
start := time.Now().Add(10 * time.Second)
for {
if err := client.DeleteSecurityGroup(common.Region(s.RegionId), s.SecurityGroupId); err != nil {
e, _ := err.(*common.Error)
if e.Code == "DependencyViolation" && time.Now().Before(start) {
time.Sleep(1 * time.Second)
continue
}
ui.Error(fmt.Sprintf("Failed to delete security group, it may still be around: %s", err))
return
}
break
}
}
-96
View File
@@ -1,96 +0,0 @@
package ecs
import (
"errors"
"fmt"
"time"
"github.com/denverdino/aliyungo/common"
"github.com/denverdino/aliyungo/ecs"
"github.com/hashicorp/packer/packer"
"github.com/mitchellh/multistep"
)
type stepConfigAlicloudVPC struct {
VpcId string
CidrBlock string //192.168.0.0/16 or 172.16.0.0/16 (default)
VpcName string
isCreate bool
}
func (s *stepConfigAlicloudVPC) Run(state multistep.StateBag) multistep.StepAction {
config := state.Get("config").(Config)
client := state.Get("client").(*ecs.Client)
ui := state.Get("ui").(packer.Ui)
if len(s.VpcId) != 0 {
vpcs, _, err := client.DescribeVpcs(&ecs.DescribeVpcsArgs{
VpcId: s.VpcId,
RegionId: common.Region(config.AlicloudRegion),
})
if err != nil {
ui.Say(fmt.Sprintf("Failed querying vpcs: %s", err))
state.Put("error", err)
return multistep.ActionHalt
}
if len(vpcs) > 0 {
vpc := vpcs[0]
state.Put("vpcid", vpc.VpcId)
s.isCreate = false
return multistep.ActionContinue
}
message := fmt.Sprintf("The specified vpc {%s} doesn't exist.", s.VpcId)
state.Put("error", errors.New(message))
ui.Say(message)
return multistep.ActionHalt
}
ui.Say("Creating vpc")
vpc, err := client.CreateVpc(&ecs.CreateVpcArgs{
RegionId: common.Region(config.AlicloudRegion),
CidrBlock: s.CidrBlock,
VpcName: s.VpcName,
})
if err != nil {
state.Put("error", err)
ui.Say(fmt.Sprintf("Failed creating vpc: %s", err))
return multistep.ActionHalt
}
err = client.WaitForVpcAvailable(common.Region(config.AlicloudRegion), vpc.VpcId, ALICLOUD_DEFAULT_SHORT_TIMEOUT)
if err != nil {
state.Put("error", err)
ui.Say(fmt.Sprintf("Failed waiting for vpc to become available: %s", err))
return multistep.ActionHalt
}
state.Put("vpcid", vpc.VpcId)
s.isCreate = true
s.VpcId = vpc.VpcId
return multistep.ActionContinue
}
func (s *stepConfigAlicloudVPC) Cleanup(state multistep.StateBag) {
if !s.isCreate {
return
}
client := state.Get("client").(*ecs.Client)
ui := state.Get("ui").(packer.Ui)
message(state, "VPC")
start := time.Now().Add(10 * time.Second)
for {
if err := client.DeleteVpc(s.VpcId); err != nil {
e, _ := err.(*common.Error)
if (e.Code == "DependencyViolation.Instance" || e.Code == "DependencyViolation.RouteEntry" ||
e.Code == "DependencyViolation.VSwitch" ||
e.Code == "DependencyViolation.SecurityGroup") && time.Now().Before(start) {
time.Sleep(1 * time.Second)
continue
}
ui.Error(fmt.Sprintf("Error deleting vpc, it may still be around: %s", err))
return
}
break
}
}
-148
View File
@@ -1,148 +0,0 @@
package ecs
import (
"errors"
"fmt"
"time"
"github.com/denverdino/aliyungo/common"
"github.com/denverdino/aliyungo/ecs"
"github.com/hashicorp/packer/packer"
"github.com/mitchellh/multistep"
)
type stepConfigAlicloudVSwitch struct {
VSwitchId string
ZoneId string
isCreate bool
CidrBlock string
VSwitchName string
}
func (s *stepConfigAlicloudVSwitch) Run(state multistep.StateBag) multistep.StepAction {
client := state.Get("client").(*ecs.Client)
ui := state.Get("ui").(packer.Ui)
vpcId := state.Get("vpcid").(string)
config := state.Get("config").(Config)
if len(s.VSwitchId) != 0 {
vswitchs, _, err := client.DescribeVSwitches(&ecs.DescribeVSwitchesArgs{
VpcId: vpcId,
VSwitchId: s.VSwitchId,
ZoneId: s.ZoneId,
})
if err != nil {
ui.Say(fmt.Sprintf("Failed querying vswitch: %s", err))
state.Put("error", err)
return multistep.ActionHalt
}
if len(vswitchs) > 0 {
vswitch := vswitchs[0]
state.Put("vswitchid", vswitch.VSwitchId)
s.isCreate = false
return multistep.ActionContinue
}
s.isCreate = false
message := fmt.Sprintf("The specified vswitch {%s} doesn't exist.", s.VSwitchId)
state.Put("error", errors.New(message))
ui.Say(message)
return multistep.ActionHalt
}
if s.ZoneId == "" {
zones, err := client.DescribeZones(common.Region(config.AlicloudRegion))
if err != nil {
ui.Say(fmt.Sprintf("Query for available zones failed: %s", err))
state.Put("error", err)
return multistep.ActionHalt
}
var instanceTypes []string
for _, zone := range zones {
isVSwitchSupported := false
for _, resourceType := range zone.AvailableResourceCreation.ResourceTypes {
if resourceType == ecs.ResourceTypeVSwitch {
isVSwitchSupported = true
}
}
if isVSwitchSupported {
for _, instanceType := range zone.AvailableInstanceTypes.InstanceTypes {
if instanceType == config.InstanceType {
s.ZoneId = zone.ZoneId
break
}
instanceTypes = append(instanceTypes, instanceType)
}
}
}
if s.ZoneId == "" {
if len(instanceTypes) > 0 {
ui.Say(fmt.Sprintf("The instance type %s isn't available in this region."+
"\n You can either change the instance to one of following: %v \n"+
"or choose another region.", config.InstanceType, instanceTypes))
state.Put("error", fmt.Errorf("The instance type %s isn't available in this region."+
"\n You can either change the instance to one of following: %v \n"+
"or choose another region.", config.InstanceType, instanceTypes))
return multistep.ActionHalt
} else {
ui.Say(fmt.Sprintf("The instance type %s isn't available in this region."+
"\n You can change to other regions.", config.InstanceType))
state.Put("error", fmt.Errorf("The instance type %s isn't available in this region."+
"\n You can change to other regions.", config.InstanceType))
return multistep.ActionHalt
}
}
}
if config.CidrBlock == "" {
s.CidrBlock = "172.16.0.0/24" //use the default CirdBlock
}
ui.Say("Creating vswitch...")
vswitchId, err := client.CreateVSwitch(&ecs.CreateVSwitchArgs{
CidrBlock: s.CidrBlock,
ZoneId: s.ZoneId,
VpcId: vpcId,
VSwitchName: s.VSwitchName,
})
if err != nil {
state.Put("error", err)
ui.Say(fmt.Sprintf("Create vswitch failed %v", err))
return multistep.ActionHalt
}
if err := client.WaitForVSwitchAvailable(vpcId, s.VSwitchId, ALICLOUD_DEFAULT_TIMEOUT); err != nil {
state.Put("error", err)
ui.Error(fmt.Sprintf("Timeout waiting for vswitch to become avaiable: %v", err))
return multistep.ActionHalt
}
state.Put("vswitchid", vswitchId)
s.isCreate = true
s.VSwitchId = vswitchId
return multistep.ActionContinue
}
func (s *stepConfigAlicloudVSwitch) Cleanup(state multistep.StateBag) {
if !s.isCreate {
return
}
client := state.Get("client").(*ecs.Client)
ui := state.Get("ui").(packer.Ui)
message(state, "vSwitch")
start := time.Now().Add(10 * time.Second)
for {
if err := client.DeleteVSwitch(s.VSwitchId); err != nil {
e, _ := err.(*common.Error)
if (e.Code == "IncorrectVSwitchStatus" || e.Code == "DependencyViolation" ||
e.Code == "DependencyViolation.HaVip" ||
e.Code == "IncorretRouteEntryStatus") && time.Now().Before(start) {
time.Sleep(1 * time.Second)
continue
}
ui.Error(fmt.Sprintf("Error deleting vswitch, it may still be around: %s", err))
return
}
break
}
}
-94
View File
@@ -1,94 +0,0 @@
package ecs
import (
"fmt"
"github.com/denverdino/aliyungo/common"
"github.com/denverdino/aliyungo/ecs"
"github.com/hashicorp/packer/packer"
"github.com/mitchellh/multistep"
)
type stepCreateAlicloudImage struct {
image *ecs.ImageType
}
func (s *stepCreateAlicloudImage) Run(state multistep.StateBag) multistep.StepAction {
config := state.Get("config").(Config)
client := state.Get("client").(*ecs.Client)
ui := state.Get("ui").(packer.Ui)
// Create the alicloud image
ui.Say(fmt.Sprintf("Creating image: %s", config.AlicloudImageName))
var imageId string
var err error
instance := state.Get("instance").(*ecs.InstanceAttributesType)
imageId, err = client.CreateImage(&ecs.CreateImageArgs{
RegionId: common.Region(config.AlicloudRegion),
InstanceId: instance.InstanceId,
ImageName: config.AlicloudImageName,
ImageVersion: config.AlicloudImageVersion,
Description: config.AlicloudImageDescription})
if err != nil {
err := fmt.Errorf("Error creating image: %s", err)
state.Put("error", err)
ui.Error(err.Error())
return multistep.ActionHalt
}
err = client.WaitForImageReady(common.Region(config.AlicloudRegion),
imageId, ALICLOUD_DEFAULT_LONG_TIMEOUT)
if err != nil {
err := fmt.Errorf("Timeout waiting for image to be created: %s", err)
state.Put("error", err)
ui.Error(err.Error())
return multistep.ActionHalt
}
images, _, err := client.DescribeImages(&ecs.DescribeImagesArgs{
RegionId: common.Region(config.AlicloudRegion),
ImageId: imageId})
if err != nil {
err := fmt.Errorf("Error querying created image: %s", err)
state.Put("error", err)
ui.Error(err.Error())
return multistep.ActionHalt
}
if len(images) == 0 {
err := fmt.Errorf("Unable to find created image: %s", err)
state.Put("error", err)
ui.Error(err.Error())
return multistep.ActionHalt
}
s.image = &images[0]
state.Put("alicloudimage", imageId)
alicloudImages := make(map[string]string)
alicloudImages[config.AlicloudRegion] = images[0].ImageId
state.Put("alicloudimages", alicloudImages)
return multistep.ActionContinue
}
func (s *stepCreateAlicloudImage) Cleanup(state multistep.StateBag) {
if s.image == nil {
return
}
_, cancelled := state.GetOk(multistep.StateCancelled)
_, halted := state.GetOk(multistep.StateHalted)
if !cancelled && !halted {
return
}
client := state.Get("client").(*ecs.Client)
ui := state.Get("ui").(packer.Ui)
config := state.Get("config").(Config)
ui.Say("Deleting the image because of cancellation or error...")
if err := client.DeleteImage(common.Region(config.AlicloudRegion), s.image.ImageId); err != nil {
ui.Error(fmt.Sprintf("Error deleting image, it may still be around: %s", err))
return
}
}
@@ -1,163 +0,0 @@
package ecs
import (
"fmt"
"io/ioutil"
"log"
"github.com/denverdino/aliyungo/common"
"github.com/denverdino/aliyungo/ecs"
"github.com/hashicorp/packer/packer"
"github.com/mitchellh/multistep"
)
type stepCreateAlicloudInstance struct {
IOOptimized bool
InstanceType string
UserData string
UserDataFile string
instanceId string
RegionId string
InternetChargeType string
InternetMaxBandwidthOut int
InstnaceName string
ZoneId string
instance *ecs.InstanceAttributesType
}
func (s *stepCreateAlicloudInstance) Run(state multistep.StateBag) multistep.StepAction {
client := state.Get("client").(*ecs.Client)
config := state.Get("config").(Config)
ui := state.Get("ui").(packer.Ui)
source_image := state.Get("source_image").(*ecs.ImageType)
network_type := state.Get("networktype").(InstanceNetWork)
securityGroupId := state.Get("securitygroupid").(string)
var instanceId string
var err error
ioOptimized := ecs.IoOptimizedNone
if s.IOOptimized {
ioOptimized = ecs.IoOptimizedOptimized
}
password := config.Comm.SSHPassword
if password == "" && config.Comm.WinRMPassword != "" {
password = config.Comm.WinRMPassword
}
ui.Say("Creating instance.")
if network_type == VpcNet {
userData, err := s.getUserData(state)
if err != nil {
state.Put("error", err)
ui.Error(err.Error())
return multistep.ActionHalt
}
vswitchId := state.Get("vswitchid").(string)
instanceId, err = client.CreateInstance(&ecs.CreateInstanceArgs{
RegionId: common.Region(s.RegionId),
ImageId: source_image.ImageId,
InstanceType: s.InstanceType,
InternetChargeType: common.InternetChargeType(s.InternetChargeType), //"PayByTraffic",
InternetMaxBandwidthOut: s.InternetMaxBandwidthOut,
UserData: userData,
IoOptimized: ioOptimized,
VSwitchId: vswitchId,
SecurityGroupId: securityGroupId,
InstanceName: s.InstnaceName,
Password: password,
ZoneId: s.ZoneId,
DataDisk: diskDeviceToDiskType(config.AlicloudImageConfig.ECSImagesDiskMappings),
})
if err != nil {
err := fmt.Errorf("Error creating instance: %s", err)
state.Put("error", err)
ui.Error(err.Error())
return multistep.ActionHalt
}
} else {
if s.InstanceType == "" {
s.InstanceType = "PayByTraffic"
}
if s.InternetMaxBandwidthOut == 0 {
s.InternetMaxBandwidthOut = 5
}
instanceId, err = client.CreateInstance(&ecs.CreateInstanceArgs{
RegionId: common.Region(s.RegionId),
ImageId: source_image.ImageId,
InstanceType: s.InstanceType,
InternetChargeType: common.InternetChargeType(s.InternetChargeType), //"PayByTraffic",
InternetMaxBandwidthOut: s.InternetMaxBandwidthOut,
IoOptimized: ioOptimized,
SecurityGroupId: securityGroupId,
InstanceName: s.InstnaceName,
Password: password,
ZoneId: s.ZoneId,
DataDisk: diskDeviceToDiskType(config.AlicloudImageConfig.ECSImagesDiskMappings),
})
if err != nil {
err := fmt.Errorf("Error creating instance: %s", err)
state.Put("error", err)
ui.Error(err.Error())
return multistep.ActionHalt
}
}
err = client.WaitForInstance(instanceId, ecs.Stopped, ALICLOUD_DEFAULT_TIMEOUT)
if err != nil {
err := fmt.Errorf("Error creating instance: %s", err)
state.Put("error", err)
ui.Error(err.Error())
return multistep.ActionHalt
}
instance, err := client.DescribeInstanceAttribute(instanceId)
if err != nil {
ui.Say(err.Error())
return multistep.ActionHalt
}
s.instance = instance
state.Put("instance", instance)
return multistep.ActionContinue
}
func (s *stepCreateAlicloudInstance) Cleanup(state multistep.StateBag) {
if s.instance == nil {
return
}
message(state, "instance")
client := state.Get("client").(*ecs.Client)
ui := state.Get("ui").(packer.Ui)
err := client.DeleteInstance(s.instance.InstanceId)
if err != nil {
ui.Say(fmt.Sprintf("Failed to clean up instance %s: %v", s.instance.InstanceId, err.Error()))
}
}
func (s *stepCreateAlicloudInstance) getUserData(state multistep.StateBag) (string, error) {
userData := s.UserData
if s.UserDataFile != "" {
data, err := ioutil.ReadFile(s.UserDataFile)
if err != nil {
return "", err
}
userData = string(data)
}
log.Printf(userData)
return userData, nil
}
func diskDeviceToDiskType(diskDevices []AlicloudDiskDevice) []ecs.DataDiskType {
result := make([]ecs.DataDiskType, len(diskDevices))
for _, diskDevice := range diskDevices {
result = append(result, ecs.DataDiskType{
DiskName: diskDevice.DiskName,
Category: ecs.DiskCategory(diskDevice.DiskCategory),
Size: diskDevice.DiskSize,
SnapshotId: diskDevice.SnapshotId,
Description: diskDevice.Description,
DeleteWithInstance: diskDevice.DeleteWithInstance,
Device: diskDevice.Device,
})
}
return result
}
@@ -1,63 +0,0 @@
package ecs
import (
"fmt"
"log"
"github.com/denverdino/aliyungo/common"
"github.com/denverdino/aliyungo/ecs"
"github.com/hashicorp/packer/packer"
"github.com/mitchellh/multistep"
)
type stepDeleteAlicloudImageSnapshots struct {
AlicloudImageForceDetele bool
AlicloudImageForceDeteleSnapshots bool
AlicloudImageName string
}
func (s *stepDeleteAlicloudImageSnapshots) Run(state multistep.StateBag) multistep.StepAction {
client := state.Get("client").(*ecs.Client)
ui := state.Get("ui").(packer.Ui)
config := state.Get("config").(Config)
ui.Say("Deleting image snapshots.")
// Check for force delete
if s.AlicloudImageForceDetele {
images, _, err := client.DescribeImages(&ecs.DescribeImagesArgs{
RegionId: common.Region(config.AlicloudRegion),
ImageName: s.AlicloudImageName,
})
if len(images) < 1 {
return multistep.ActionContinue
}
for _, image := range images {
if image.ImageOwnerAlias != string(ecs.ImageOwnerSelf) {
log.Printf("You can only delete instances based on customized images %s ", image.ImageId)
continue
}
err = client.DeleteImage(common.Region(config.AlicloudRegion), image.ImageId)
if err != nil {
err := fmt.Errorf("Failed to delete image: %s", err)
state.Put("error", err)
ui.Error(err.Error())
return multistep.ActionHalt
}
if s.AlicloudImageForceDeteleSnapshots {
for _, diskDevice := range image.DiskDeviceMappings.DiskDeviceMapping {
if err := client.DeleteSnapshot(diskDevice.SnapshotId); err != nil {
err := fmt.Errorf("Deleting ECS snapshot failed: %s", err)
state.Put("error", err)
ui.Error(err.Error())
return multistep.ActionHalt
}
}
}
}
}
return multistep.ActionContinue
}
func (s *stepDeleteAlicloudImageSnapshots) Cleanup(state multistep.StateBag) {
}
-71
View File
@@ -1,71 +0,0 @@
package ecs
import (
"fmt"
"github.com/denverdino/aliyungo/ecs"
"github.com/hashicorp/packer/packer"
"github.com/mitchellh/multistep"
)
type stepMountAlicloudDisk struct {
}
func (s *stepMountAlicloudDisk) Run(state multistep.StateBag) multistep.StepAction {
client := state.Get("client").(*ecs.Client)
config := state.Get("config").(Config)
ui := state.Get("ui").(packer.Ui)
instance := state.Get("instance").(*ecs.InstanceAttributesType)
alicloudDiskDevices := config.ECSImagesDiskMappings
if len(config.ECSImagesDiskMappings) == 0 {
return multistep.ActionContinue
}
ui.Say("Mounting disks.")
disks, _, err := client.DescribeDisks(&ecs.DescribeDisksArgs{InstanceId: instance.InstanceId,
RegionId: instance.RegionId})
if err != nil {
err := fmt.Errorf("Error querying disks: %s", err)
state.Put("error", err)
ui.Error(err.Error())
return multistep.ActionHalt
}
for _, disk := range disks {
if disk.Status == ecs.DiskStatusAvailable {
if err := client.AttachDisk(&ecs.AttachDiskArgs{DiskId: disk.DiskId,
InstanceId: instance.InstanceId,
Device: getDevice(&disk, alicloudDiskDevices),
}); err != nil {
err := fmt.Errorf("Error mounting disks: %s", err)
state.Put("error", err)
ui.Error(err.Error())
return multistep.ActionHalt
}
}
}
for _, disk := range disks {
if err := client.WaitForDisk(instance.RegionId, disk.DiskId, ecs.DiskStatusInUse, ALICLOUD_DEFAULT_SHORT_TIMEOUT); err != nil {
err := fmt.Errorf("Timeout waiting for mount: %s", err)
state.Put("error", err)
ui.Error(err.Error())
return multistep.ActionHalt
}
}
ui.Say("Finished mounting disks.")
return multistep.ActionContinue
}
func (s *stepMountAlicloudDisk) Cleanup(state multistep.StateBag) {
}
func getDevice(disk *ecs.DiskItemType, diskDevices []AlicloudDiskDevice) string {
if disk.Device != "" {
return disk.Device
}
for _, alicloudDiskDevice := range diskDevices {
if alicloudDiskDevice.DiskName == disk.DiskName || alicloudDiskDevice.SnapshotId == disk.SourceSnapshotId {
return alicloudDiskDevice.Device
}
}
return ""
}
-48
View File
@@ -1,48 +0,0 @@
package ecs
import (
"fmt"
"github.com/denverdino/aliyungo/common"
"github.com/denverdino/aliyungo/ecs"
"github.com/hashicorp/packer/packer"
"github.com/mitchellh/multistep"
)
type stepPreValidate struct {
AlicloudDestImageName string
ForceDelete bool
}
func (s *stepPreValidate) Run(state multistep.StateBag) multistep.StepAction {
ui := state.Get("ui").(packer.Ui)
if s.ForceDelete {
ui.Say("Force delete flag found, skipping prevalidating image name.")
return multistep.ActionContinue
}
client := state.Get("client").(*ecs.Client)
config := state.Get("config").(Config)
ui.Say("Prevalidating image name...")
images, _, err := client.DescribeImages(&ecs.DescribeImagesArgs{
ImageName: s.AlicloudDestImageName,
RegionId: common.Region(config.AlicloudRegion)})
if err != nil {
err := fmt.Errorf("Error querying alicloud image: %s", err)
state.Put("error", err)
ui.Error(err.Error())
return multistep.ActionHalt
}
if len(images) > 0 {
err := fmt.Errorf("Error: name conflicts with an existing alicloud image: %s", images[0].ImageId)
state.Put("error", err)
ui.Error(err.Error())
return multistep.ActionHalt
}
return multistep.ActionContinue
}
func (s *stepPreValidate) Cleanup(multistep.StateBag) {}
@@ -1,71 +0,0 @@
package ecs
import (
"fmt"
"github.com/denverdino/aliyungo/common"
"github.com/denverdino/aliyungo/ecs"
"github.com/hashicorp/packer/packer"
"github.com/mitchellh/multistep"
)
type setpRegionCopyAlicloudImage struct {
AlicloudImageDestinationRegions []string
AlicloudImageDestinationNames []string
RegionId string
}
func (s *setpRegionCopyAlicloudImage) Run(state multistep.StateBag) multistep.StepAction {
if len(s.AlicloudImageDestinationRegions) == 0 {
return multistep.ActionContinue
}
client := state.Get("client").(*ecs.Client)
ui := state.Get("ui").(packer.Ui)
imageId := state.Get("alicloudimage").(string)
alicloudImages := state.Get("alicloudimages").(map[string]string)
region := common.Region(s.RegionId)
numberOfName := len(s.AlicloudImageDestinationNames)
for index, destinationRegion := range s.AlicloudImageDestinationRegions {
if destinationRegion == s.RegionId {
continue
}
ecsImageName := ""
if numberOfName > 0 && index < numberOfName {
ecsImageName = s.AlicloudImageDestinationNames[index]
}
imageId, err := client.CopyImage(
&ecs.CopyImageArgs{
RegionId: region,
ImageId: imageId,
DestinationRegionId: common.Region(destinationRegion),
DestinationImageName: ecsImageName,
})
if err != nil {
state.Put("error", err)
ui.Say(fmt.Sprintf("Error copying images: %s", err))
return multistep.ActionHalt
}
alicloudImages[destinationRegion] = imageId
}
return multistep.ActionContinue
}
func (s *setpRegionCopyAlicloudImage) Cleanup(state multistep.StateBag) {
_, cancelled := state.GetOk(multistep.StateCancelled)
_, halted := state.GetOk(multistep.StateHalted)
if cancelled || halted {
ui := state.Get("ui").(packer.Ui)
client := state.Get("client").(*ecs.Client)
alicloudImages := state.Get("alicloudimages").(map[string]string)
ui.Say(fmt.Sprintf("Stopping copy image because cancellation or error..."))
for copyedRegionId, copyedImageId := range alicloudImages {
if copyedRegionId == s.RegionId {
continue
}
if err := client.CancelCopyImage(common.Region(copyedRegionId), copyedImageId); err != nil {
ui.Say(fmt.Sprintf("Error cancelling copy image: %v", err))
}
}
}
}
-56
View File
@@ -1,56 +0,0 @@
package ecs
import (
"fmt"
"github.com/denverdino/aliyungo/ecs"
"github.com/hashicorp/packer/packer"
"github.com/mitchellh/multistep"
)
type stepRunAlicloudInstance struct {
}
func (s *stepRunAlicloudInstance) Run(state multistep.StateBag) multistep.StepAction {
client := state.Get("client").(*ecs.Client)
ui := state.Get("ui").(packer.Ui)
instance := state.Get("instance").(*ecs.InstanceAttributesType)
err := client.StartInstance(instance.InstanceId)
if err != nil {
err := fmt.Errorf("Error starting instance: %s", err)
state.Put("error", err)
ui.Error(err.Error())
return multistep.ActionHalt
}
ui.Say("Starting instance.")
err = client.WaitForInstance(instance.InstanceId, ecs.Running, ALICLOUD_DEFAULT_TIMEOUT)
if err != nil {
err := fmt.Errorf("Timeout waiting for instance to start: %s", err)
state.Put("error", err)
ui.Error(err.Error())
return multistep.ActionHalt
}
return multistep.ActionContinue
}
func (s *stepRunAlicloudInstance) Cleanup(state multistep.StateBag) {
_, cancelled := state.GetOk(multistep.StateCancelled)
_, halted := state.GetOk(multistep.StateHalted)
if cancelled || halted {
ui := state.Get("ui").(packer.Ui)
client := state.Get("client").(*ecs.Client)
instance := state.Get("instance").(*ecs.InstanceAttributesType)
instanceAttrubite, _ := client.DescribeInstanceAttribute(instance.InstanceId)
if instanceAttrubite.Status == ecs.Starting || instanceAttrubite.Status == ecs.Running {
if err := client.StopInstance(instance.InstanceId, true); err != nil {
ui.Say(fmt.Sprintf("Error stopping instance %s, it may still be around %s", instance.InstanceId, err))
return
}
if err := client.WaitForInstance(instance.InstanceId, ecs.Stopped, ALICLOUD_DEFAULT_TIMEOUT); err != nil {
ui.Say(fmt.Sprintf("Error stopping instance %s, it may still be around %s", instance.InstanceId, err))
}
}
}
}
-60
View File
@@ -1,60 +0,0 @@
package ecs
import (
"fmt"
"github.com/denverdino/aliyungo/common"
"github.com/denverdino/aliyungo/ecs"
"github.com/hashicorp/packer/packer"
"github.com/mitchellh/multistep"
)
type setpShareAlicloudImage struct {
AlicloudImageShareAccounts []string
AlicloudImageUNShareAccounts []string
RegionId string
}
func (s *setpShareAlicloudImage) Run(state multistep.StateBag) multistep.StepAction {
client := state.Get("client").(*ecs.Client)
ui := state.Get("ui").(packer.Ui)
alicloudImages := state.Get("alicloudimages").(map[string]string)
for copyedRegion, copyedImageId := range alicloudImages {
err := client.ModifyImageSharePermission(
&ecs.ModifyImageSharePermissionArgs{
RegionId: common.Region(copyedRegion),
ImageId: copyedImageId,
AddAccount: s.AlicloudImageShareAccounts,
RemoveAccount: s.AlicloudImageUNShareAccounts,
})
if err != nil {
state.Put("error", err)
ui.Say(fmt.Sprintf("Failed modifying image share permissions: %s", err))
return multistep.ActionHalt
}
}
return multistep.ActionContinue
}
func (s *setpShareAlicloudImage) Cleanup(state multistep.StateBag) {
_, cancelled := state.GetOk(multistep.StateCancelled)
_, halted := state.GetOk(multistep.StateHalted)
if cancelled || halted {
ui := state.Get("ui").(packer.Ui)
client := state.Get("client").(*ecs.Client)
alicloudImages := state.Get("alicloudimages").(map[string]string)
ui.Say("Restoring image share permission because cancellations or error...")
for copyedRegion, copyedImageId := range alicloudImages {
err := client.ModifyImageSharePermission(
&ecs.ModifyImageSharePermissionArgs{
RegionId: common.Region(copyedRegion),
ImageId: copyedImageId,
AddAccount: s.AlicloudImageUNShareAccounts,
RemoveAccount: s.AlicloudImageShareAccounts,
})
if err != nil {
ui.Say(fmt.Sprintf("Restoring image share permission failed: %s", err))
}
}
}
}
@@ -1,43 +0,0 @@
package ecs
import (
"fmt"
"github.com/denverdino/aliyungo/ecs"
"github.com/hashicorp/packer/packer"
"github.com/mitchellh/multistep"
)
type stepStopAlicloudInstance struct {
ForceStop bool
}
func (s *stepStopAlicloudInstance) Run(state multistep.StateBag) multistep.StepAction {
client := state.Get("client").(*ecs.Client)
instance := state.Get("instance").(*ecs.InstanceAttributesType)
ui := state.Get("ui").(packer.Ui)
err := client.StopInstance(instance.InstanceId, s.ForceStop)
if err != nil {
if err != nil {
err := fmt.Errorf("Error stopping alicloud instance: %s", err)
state.Put("error", err)
ui.Error(err.Error())
return multistep.ActionHalt
}
}
err = client.WaitForInstance(instance.InstanceId, ecs.Stopped, ALICLOUD_DEFAULT_TIMEOUT)
if err != nil {
err := fmt.Errorf("Error waiting for alicloud instance to stop: %s", err)
state.Put("error", err)
ui.Error(err.Error())
return multistep.ActionHalt
}
return multistep.ActionContinue
}
func (s *stepStopAlicloudInstance) Cleanup(multistep.StateBag) {
// No cleanup...
}
+21 -17
View File
@@ -9,13 +9,14 @@ import (
"log"
"runtime"
"github.com/aws/aws-sdk-go/aws/session"
"github.com/aws/aws-sdk-go/service/ec2"
awscommon "github.com/hashicorp/packer/builder/amazon/common"
"github.com/hashicorp/packer/common"
"github.com/hashicorp/packer/helper/config"
"github.com/hashicorp/packer/packer"
"github.com/hashicorp/packer/template/interpolate"
"github.com/mitchellh/multistep"
awscommon "github.com/mitchellh/packer/builder/amazon/common"
"github.com/mitchellh/packer/common"
"github.com/mitchellh/packer/helper/config"
"github.com/mitchellh/packer/packer"
"github.com/mitchellh/packer/template/interpolate"
)
// The unique ID for this builder
@@ -86,6 +87,10 @@ func (b *Builder) Prepare(raws ...interface{}) ([]string, error) {
b.config.ChrootMounts = make([][]string, 0)
}
if b.config.CopyFiles == nil {
b.config.CopyFiles = make([]string, 0)
}
if len(b.config.ChrootMounts) == 0 {
b.config.ChrootMounts = [][]string{
{"proc", "proc", "/proc"},
@@ -96,12 +101,8 @@ func (b *Builder) Prepare(raws ...interface{}) ([]string, error) {
}
}
// set default copy file if we're not giving our own
if b.config.CopyFiles == nil {
b.config.CopyFiles = make([]string, 0)
if !b.config.FromScratch {
b.config.CopyFiles = []string{"/etc/resolv.conf"}
}
if len(b.config.CopyFiles) == 0 && !b.config.FromScratch {
b.config.CopyFiles = []string{"/etc/resolv.conf"}
}
if b.config.CommandWrapper == "" {
@@ -181,7 +182,12 @@ func (b *Builder) Run(ui packer.Ui, hook packer.Hook, cache packer.Cache) (packe
return nil, errors.New("The amazon-chroot builder only works on Linux environments.")
}
session, err := b.config.Session()
config, err := b.config.Config()
if err != nil {
return nil, err
}
session, err := session.NewSession(config)
if err != nil {
return nil, err
}
@@ -258,11 +264,9 @@ func (b *Builder) Run(ui packer.Ui, hook packer.Hook, cache packer.Cache) (packe
Name: b.config.AMIName,
},
&awscommon.StepAMIRegionCopy{
AccessConfig: &b.config.AccessConfig,
Regions: b.config.AMIRegions,
RegionKeyIds: b.config.AMIRegionKMSKeyIDs,
EncryptBootVolume: b.config.AMIEncryptBootVolume,
Name: b.config.AMIName,
AccessConfig: &b.config.AccessConfig,
Regions: b.config.AMIRegions,
Name: b.config.AMIName,
},
&awscommon.StepModifyAMIAttributes{
Description: b.config.AMIDescription,
+1 -32
View File
@@ -1,9 +1,8 @@
package chroot
import (
"github.com/mitchellh/packer/packer"
"testing"
"github.com/hashicorp/packer/packer"
)
func testConfig() map[string]interface{} {
@@ -118,33 +117,3 @@ func TestBuilderPrepare_CommandWrapper(t *testing.T) {
t.Errorf("err: %s", err)
}
}
func TestBuilderPrepare_CopyFiles(t *testing.T) {
b := &Builder{}
config := testConfig()
warnings, err := b.Prepare(config)
if len(warnings) > 0 {
t.Fatalf("bad: %#v", warnings)
}
if err != nil {
t.Errorf("err: %s", err)
}
if len(b.config.CopyFiles) != 1 && b.config.CopyFiles[0] != "/etc/resolv.conf" {
t.Errorf("Was expecting default value for copy_files.")
}
config["copy_files"] = []string{}
warnings, err = b.Prepare(config)
if len(warnings) > 0 {
t.Fatalf("bad: %#v", warnings)
}
if err != nil {
t.Errorf("err: %s", err)
}
if len(b.config.CopyFiles) > 0 {
t.Errorf("Was expecting no default value for copy_files.")
}
}
+2 -5
View File
@@ -12,7 +12,7 @@ import (
"strings"
"syscall"
"github.com/hashicorp/packer/packer"
"github.com/mitchellh/packer/packer"
)
// Communicator is a special communicator that works by executing
@@ -69,10 +69,7 @@ func (c *Communicator) Upload(dst string, r io.Reader, fi *os.FileInfo) error {
return fmt.Errorf("Error preparing shell script: %s", err)
}
defer os.Remove(tf.Name())
if _, err := io.Copy(tf, r); err != nil {
return err
}
io.Copy(tf, r)
cpCmd, err := c.CmdWrapper(fmt.Sprintf("cp %s %s", tf.Name(), dst))
if err != nil {
+1 -1
View File
@@ -1,7 +1,7 @@
package chroot
import (
"github.com/hashicorp/packer/packer"
"github.com/mitchellh/packer/packer"
"testing"
)
+3 -3
View File
@@ -3,9 +3,9 @@ package chroot
import (
"fmt"
"github.com/hashicorp/packer/packer"
"github.com/hashicorp/packer/post-processor/shell-local"
"github.com/hashicorp/packer/template/interpolate"
"github.com/mitchellh/packer/packer"
"github.com/mitchellh/packer/post-processor/shell-local"
"github.com/mitchellh/packer/template/interpolate"
)
func RunLocalCommands(commands []string, wrappedCommand CommandWrapper, ctx interpolate.Context, ui packer.Ui) error {
+2 -2
View File
@@ -7,9 +7,9 @@ import (
"time"
"github.com/aws/aws-sdk-go/service/ec2"
awscommon "github.com/hashicorp/packer/builder/amazon/common"
"github.com/hashicorp/packer/packer"
"github.com/mitchellh/multistep"
awscommon "github.com/mitchellh/packer/builder/amazon/common"
"github.com/mitchellh/packer/packer"
)
// StepAttachVolume attaches the previously created volume to an
@@ -4,8 +4,8 @@ import (
"fmt"
"github.com/aws/aws-sdk-go/service/ec2"
"github.com/hashicorp/packer/packer"
"github.com/mitchellh/multistep"
"github.com/mitchellh/packer/packer"
)
// StepCheckRootDevice makes sure the root device on the AMI is EBS-backed.
@@ -1,14 +1,14 @@
package chroot
import (
"log"
"github.com/hashicorp/packer/packer"
"github.com/mitchellh/multistep"
"github.com/mitchellh/packer/packer"
"log"
)
// StepChrootProvision provisions the instance within a chroot.
type StepChrootProvision struct {
mounts []string
}
func (s *StepChrootProvision) Run(state multistep.StateBag) multistep.StepAction {
+1 -1
View File
@@ -3,8 +3,8 @@ package chroot
import (
"bytes"
"fmt"
"github.com/hashicorp/packer/packer"
"github.com/mitchellh/multistep"
"github.com/mitchellh/packer/packer"
"log"
"path/filepath"
)
+2 -2
View File
@@ -6,9 +6,9 @@ import (
"github.com/aws/aws-sdk-go/aws"
"github.com/aws/aws-sdk-go/service/ec2"
awscommon "github.com/hashicorp/packer/builder/amazon/common"
"github.com/hashicorp/packer/packer"
"github.com/mitchellh/multistep"
awscommon "github.com/mitchellh/packer/builder/amazon/common"
"github.com/mitchellh/packer/packer"
)
// StepCreateVolume creates a new volume from the snapshot of the root
+1 -1
View File
@@ -2,8 +2,8 @@ package chroot
import (
"fmt"
"github.com/hashicorp/packer/packer"
"github.com/mitchellh/multistep"
"github.com/mitchellh/packer/packer"
"log"
)
+1 -1
View File
@@ -2,8 +2,8 @@ package chroot
import (
"fmt"
"github.com/hashicorp/packer/packer"
"github.com/mitchellh/multistep"
"github.com/mitchellh/packer/packer"
"log"
)
+1 -1
View File
@@ -2,8 +2,8 @@ package chroot
import (
"fmt"
"github.com/hashicorp/packer/packer"
"github.com/mitchellh/multistep"
"github.com/mitchellh/packer/packer"
"log"
"os"
"path/filepath"
+8 -9
View File
@@ -4,11 +4,10 @@ import (
"fmt"
"log"
"github.com/aws/aws-sdk-go/aws/ec2metadata"
"github.com/aws/aws-sdk-go/aws/session"
"github.com/aws/aws-sdk-go/service/ec2"
"github.com/hashicorp/packer/packer"
"github.com/mitchellh/multistep"
"github.com/mitchellh/packer/builder/amazon/common"
"github.com/mitchellh/packer/packer"
)
// StepInstanceInfo verifies that this builder is running on an EC2 instance.
@@ -20,11 +19,9 @@ func (s *StepInstanceInfo) Run(state multistep.StateBag) multistep.StepAction {
// Get our own instance ID
ui.Say("Gathering information about this EC2 instance...")
sess := session.New()
ec2meta := ec2metadata.New(sess)
identity, err := ec2meta.GetInstanceIdentityDocument()
instanceIdBytes, err := common.GetInstanceMetaData("instance-id")
if err != nil {
log.Printf("Error: %s", err)
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.")
@@ -32,10 +29,12 @@ func (s *StepInstanceInfo) Run(state multistep.StateBag) multistep.StepAction {
ui.Error(err.Error())
return multistep.ActionHalt
}
log.Printf("Instance ID: %s", identity.InstanceID)
instanceId := string(instanceIdBytes)
log.Printf("Instance ID: %s", instanceId)
// Query the entire instance metadata
instancesResp, err := ec2conn.DescribeInstances(&ec2.DescribeInstancesInput{InstanceIds: []*string{&identity.InstanceID}})
instancesResp, err := ec2conn.DescribeInstances(&ec2.DescribeInstancesInput{InstanceIds: []*string{&instanceId}})
if err != nil {
err := fmt.Errorf("Error getting instance data: %s", err)
state.Put("error", err)
+3 -3
View File
@@ -9,9 +9,9 @@ import (
"strings"
"github.com/aws/aws-sdk-go/service/ec2"
"github.com/hashicorp/packer/packer"
"github.com/hashicorp/packer/template/interpolate"
"github.com/mitchellh/multistep"
"github.com/mitchellh/packer/packer"
"github.com/mitchellh/packer/template/interpolate"
)
type mountPathData struct {
@@ -82,7 +82,7 @@ func (s *StepMountDevice) Run(state multistep.StateBag) multistep.StepAction {
ui.Say("Mounting the root device...")
stderr := new(bytes.Buffer)
// build mount options from mount_options config, useful for nouuid options
// build mount options from mount_options config, usefull for nouuid options
// or other specific device type settings for mount
opts := ""
if len(s.MountOptions) > 0 {
+1 -1
View File
@@ -3,8 +3,8 @@ package chroot
import (
"bytes"
"fmt"
"github.com/hashicorp/packer/packer"
"github.com/mitchellh/multistep"
"github.com/mitchellh/packer/packer"
"os"
"os/exec"
"syscall"
@@ -1,8 +1,8 @@
package chroot
import (
"github.com/hashicorp/packer/packer"
"github.com/mitchellh/multistep"
"github.com/mitchellh/packer/packer"
)
type postMountCommandsData struct {
@@ -1,8 +1,8 @@
package chroot
import (
"github.com/hashicorp/packer/packer"
"github.com/mitchellh/multistep"
"github.com/mitchellh/packer/packer"
)
type preMountCommandsData struct {
+3 -3
View File
@@ -2,15 +2,15 @@ package chroot
import (
"fmt"
"github.com/mitchellh/multistep"
"github.com/mitchellh/packer/packer"
"log"
"os"
"github.com/hashicorp/packer/packer"
"github.com/mitchellh/multistep"
)
// StepPrepareDevice finds an available device and sets it.
type StepPrepareDevice struct {
mounts []string
}
func (s *StepPrepareDevice) Run(state multistep.StateBag) multistep.StepAction {
+2 -2
View File
@@ -5,9 +5,9 @@ import (
"github.com/aws/aws-sdk-go/aws"
"github.com/aws/aws-sdk-go/service/ec2"
awscommon "github.com/hashicorp/packer/builder/amazon/common"
"github.com/hashicorp/packer/packer"
"github.com/mitchellh/multistep"
awscommon "github.com/mitchellh/packer/builder/amazon/common"
"github.com/mitchellh/packer/packer"
)
// StepRegisterAMI creates the AMI.
+2 -2
View File
@@ -6,9 +6,9 @@ import (
"time"
"github.com/aws/aws-sdk-go/service/ec2"
awscommon "github.com/hashicorp/packer/builder/amazon/common"
"github.com/hashicorp/packer/packer"
"github.com/mitchellh/multistep"
awscommon "github.com/mitchellh/packer/builder/amazon/common"
"github.com/mitchellh/packer/packer"
)
// StepSnapshot creates a snapshot of the created volume.
+63 -63
View File
@@ -2,82 +2,63 @@ package common
import (
"fmt"
"log"
"os"
"io/ioutil"
"net/http"
"strings"
"unicode"
"github.com/aws/aws-sdk-go/aws"
"github.com/aws/aws-sdk-go/aws/credentials"
"github.com/aws/aws-sdk-go/aws/credentials/ec2rolecreds"
"github.com/aws/aws-sdk-go/aws/ec2metadata"
"github.com/aws/aws-sdk-go/aws/session"
"github.com/hashicorp/packer/template/interpolate"
"github.com/mitchellh/packer/template/interpolate"
)
// AccessConfig is for common configuration related to AWS access
type AccessConfig struct {
AccessKey string `mapstructure:"access_key"`
CustomEndpointEc2 string `mapstructure:"custom_endpoint_ec2"`
MFACode string `mapstructure:"mfa_code"`
ProfileName string `mapstructure:"profile"`
RawRegion string `mapstructure:"region"`
SecretKey string `mapstructure:"secret_key"`
SkipValidation bool `mapstructure:"skip_region_validation"`
Token string `mapstructure:"token"`
session *session.Session
AccessKey string `mapstructure:"access_key"`
SecretKey string `mapstructure:"secret_key"`
RawRegion string `mapstructure:"region"`
SkipValidation bool `mapstructure:"skip_region_validation"`
Token string `mapstructure:"token"`
ProfileName string `mapstructure:"profile"`
}
// Config returns a valid aws.Config object for access to AWS services, or
// an error if the authentication and region couldn't be resolved
func (c *AccessConfig) Session() (*session.Session, error) {
if c.session != nil {
return c.session, nil
}
func (c *AccessConfig) Config() (*aws.Config, error) {
var creds *credentials.Credentials
region, err := c.Region()
if err != nil {
return nil, err
}
config := aws.NewConfig().WithRegion(region).WithMaxRetries(11)
if c.ProfileName != "" {
if err := os.Setenv("AWS_PROFILE", c.ProfileName); err != nil {
log.Printf("Set env error: %s", err)
profile, err := NewFromProfile(c.ProfileName)
if err != nil {
return nil, err
}
}
config := aws.NewConfig().WithRegion(region).WithMaxRetries(11).WithCredentialsChainVerboseErrors(true)
if c.CustomEndpointEc2 != "" {
config = config.WithEndpoint(c.CustomEndpointEc2)
}
if c.AccessKey != "" {
creds := credentials.NewChainCredentials(
[]credentials.Provider{
&credentials.StaticProvider{
Value: credentials.Value{
AccessKeyID: c.AccessKey,
SecretAccessKey: c.SecretKey,
SessionToken: c.Token,
},
},
})
config = config.WithCredentials(creds)
}
opts := session.Options{
SharedConfigState: session.SharedConfigEnable,
Config: *config,
}
if c.MFACode != "" {
opts.AssumeRoleTokenProvider = func() (string, error) {
return c.MFACode, nil
creds, err = profile.CredentialsFromProfile(config)
if err != nil {
return nil, err
}
} else {
creds = credentials.NewChainCredentials([]credentials.Provider{
&credentials.StaticProvider{Value: credentials.Value{
AccessKeyID: c.AccessKey,
SecretAccessKey: c.SecretKey,
SessionToken: c.Token,
}},
&credentials.EnvProvider{},
&credentials.SharedCredentialsProvider{Filename: "", Profile: ""},
&ec2rolecreds.EC2RoleProvider{
Client: ec2metadata.New(session.New(config)),
},
})
}
c.session, err = session.NewSessionWithOptions(opts)
if err != nil {
return nil, err
}
return c.session, nil
return config.WithCredentials(creds), nil
}
// Region returns the aws.Region object for access to AWS services, requesting
@@ -85,28 +66,26 @@ func (c *AccessConfig) Session() (*session.Session, error) {
func (c *AccessConfig) Region() (string, error) {
if c.RawRegion != "" {
if !c.SkipValidation {
if valid := ValidateRegion(c.RawRegion); !valid {
if valid := ValidateRegion(c.RawRegion); valid == false {
return "", fmt.Errorf("Not a valid region: %s", c.RawRegion)
}
}
return c.RawRegion, nil
}
sess := session.New()
ec2meta := ec2metadata.New(sess)
identity, err := ec2meta.GetInstanceIdentityDocument()
md, err := GetInstanceMetaData("placement/availability-zone")
if err != nil {
log.Println("Error getting region from metadata service, "+
"probably because we're not running on AWS.", err)
return "", nil
return "", err
}
return identity.Region, nil
region := strings.TrimRightFunc(string(md), unicode.IsLetter)
return region, nil
}
func (c *AccessConfig) Prepare(ctx *interpolate.Context) []error {
var errs []error
if c.RawRegion != "" && !c.SkipValidation {
if valid := ValidateRegion(c.RawRegion); !valid {
if valid := ValidateRegion(c.RawRegion); valid == false {
errs = append(errs, fmt.Errorf("Unknown region: %s", c.RawRegion))
}
}
@@ -117,3 +96,24 @@ func (c *AccessConfig) Prepare(ctx *interpolate.Context) []error {
return nil
}
func GetInstanceMetaData(path string) (contents []byte, err error) {
url := "http://169.254.169.254/latest/meta-data/" + path
resp, err := http.Get(url)
if err != nil {
return
}
defer resp.Body.Close()
if resp.StatusCode != 200 {
err = fmt.Errorf("Code %d returned for url %s", resp.StatusCode, url)
return
}
body, err := ioutil.ReadAll(resp.Body)
if err != nil {
return
}
return []byte(body), err
}
+4 -52
View File
@@ -3,7 +3,7 @@ package common
import (
"fmt"
"github.com/hashicorp/packer/template/interpolate"
"github.com/mitchellh/packer/template/interpolate"
)
// AMIConfig is for common configuration related to creating AMIs.
@@ -22,21 +22,11 @@ type AMIConfig struct {
AMIForceDeleteSnapshot bool `mapstructure:"force_delete_snapshot"`
AMIEncryptBootVolume bool `mapstructure:"encrypt_boot"`
AMIKmsKeyId string `mapstructure:"kms_key_id"`
AMIRegionKMSKeyIDs map[string]string `mapstructure:"region_kms_key_ids"`
SnapshotTags map[string]string `mapstructure:"snapshot_tags"`
SnapshotUsers []string `mapstructure:"snapshot_users"`
SnapshotGroups []string `mapstructure:"snapshot_groups"`
}
func stringInSlice(s []string, searchstr string) bool {
for _, item := range s {
if item == searchstr {
return true
}
}
return false
}
func (c *AMIConfig) Prepare(ctx *interpolate.Context) []error {
var errs []error
if c.AMIName == "" {
@@ -58,62 +48,24 @@ func (c *AMIConfig) Prepare(ctx *interpolate.Context) []error {
if !c.AMISkipRegionValidation {
// Verify the region is real
if valid := ValidateRegion(region); !valid {
if valid := ValidateRegion(region); valid == false {
errs = append(errs, fmt.Errorf("Unknown region: %s", region))
continue
}
}
// Make sure that if we have region_kms_key_ids defined,
// the regions in ami_regions are also in region_kms_key_ids
if len(c.AMIRegionKMSKeyIDs) > 0 {
if _, ok := c.AMIRegionKMSKeyIDs[region]; !ok {
errs = append(errs, fmt.Errorf("Region %s is in ami_regions but not in region_kms_key_ids", region))
}
}
regions = append(regions, region)
}
c.AMIRegions = regions
}
// Make sure that if we have region_kms_key_ids defined,
// the regions in region_kms_key_ids are also in ami_regions
if len(c.AMIRegionKMSKeyIDs) > 0 {
for kmsKeyRegion := range c.AMIRegionKMSKeyIDs {
if !stringInSlice(c.AMIRegions, kmsKeyRegion) {
errs = append(errs, fmt.Errorf("Region %s is in region_kms_key_ids but not in ami_regions", kmsKeyRegion))
}
}
}
if len(c.AMIUsers) > 0 && c.AMIEncryptBootVolume {
errs = append(errs, fmt.Errorf("Cannot share AMI with encrypted boot volume"))
}
if len(c.SnapshotUsers) > 0 {
if len(c.AMIKmsKeyId) == 0 && c.AMIEncryptBootVolume {
errs = append(errs, fmt.Errorf("Cannot share snapshot encrypted with default KMS key"))
}
if len(c.AMIRegionKMSKeyIDs) > 0 {
for _, kmsKey := range c.AMIRegionKMSKeyIDs {
if len(kmsKey) == 0 {
errs = append(errs, fmt.Errorf("Cannot share snapshot encrypted with default KMS key"))
}
}
}
}
if len(c.AMIName) < 3 || len(c.AMIName) > 128 {
errs = append(errs, fmt.Errorf("ami_name must be between 3 and 128 characters long"))
}
if c.AMIName != templateCleanAMIName(c.AMIName) {
errs = append(errs, fmt.Errorf("AMIName should only contain "+
"alphanumeric characters, parentheses (()), square brackets ([]), spaces "+
"( ), periods (.), slashes (/), dashes (-), single quotes ('), at-signs "+
"(@), or underscores(_). You can use the `clean_ami_name` template "+
"filter to automatically clean your ami name."))
if len(c.SnapshotUsers) > 0 && len(c.AMIKmsKeyId) == 0 && c.AMIEncryptBootVolume {
errs = append(errs, fmt.Errorf("Cannot share snapshot encrypted with default KMS key"))
}
if len(errs) > 0 {
-95
View File
@@ -57,67 +57,6 @@ func TestAMIConfigPrepare_regions(t *testing.T) {
}
c.AMISkipRegionValidation = false
c.AMIRegions = []string{"us-east-1", "us-east-2", "us-west-1"}
c.AMIRegionKMSKeyIDs = map[string]string{
"us-east-1": "123-456-7890",
"us-west-1": "789-012-3456",
"us-east-2": "456-789-0123",
}
if err := c.Prepare(nil); err != nil {
t.Fatal("shouldn't have error")
}
c.AMIRegions = []string{"us-east-1", "us-east-2", "us-west-1"}
c.AMIRegionKMSKeyIDs = map[string]string{
"us-east-1": "123-456-7890",
"us-west-1": "789-012-3456",
"us-east-2": "",
}
if err := c.Prepare(nil); err != nil {
t.Fatal("should have passed; we are able to use default KMS key if not sharing")
}
c.SnapshotUsers = []string{"user-foo", "user-bar"}
c.AMIRegions = []string{"us-east-1", "us-east-2", "us-west-1"}
c.AMIRegionKMSKeyIDs = map[string]string{
"us-east-1": "123-456-7890",
"us-west-1": "789-012-3456",
"us-east-2": "",
}
if err := c.Prepare(nil); err == nil {
t.Fatal("should have an error b/c can't use default KMS key if sharing")
}
c.AMIRegions = []string{"us-east-1", "us-west-1"}
c.AMIRegionKMSKeyIDs = map[string]string{
"us-east-1": "123-456-7890",
"us-west-1": "789-012-3456",
"us-east-2": "456-789-0123",
}
if err := c.Prepare(nil); err == nil {
t.Fatal("should have error b/c theres a region in the key map that isn't in ami_regions")
}
c.AMIRegions = []string{"us-east-1", "us-west-1", "us-east-2"}
c.AMIRegionKMSKeyIDs = map[string]string{
"us-east-1": "123-456-7890",
"us-west-1": "789-012-3456",
}
if err := c.Prepare(nil); err == nil {
t.Fatal("should have error b/c theres a region in in ami_regions that isn't in the key map")
}
c.SnapshotUsers = []string{"foo", "bar"}
c.AMIKmsKeyId = "123-abc-456"
c.AMIEncryptBootVolume = true
c.AMIRegions = []string{"us-east-1", "us-west-1"}
c.AMIRegionKMSKeyIDs = map[string]string{
"us-east-1": "123-456-7890",
"us-west-1": "",
}
if err := c.Prepare(nil); err == nil {
t.Fatal("should have error b/c theres a region in in ami_regions that isn't in the key map")
}
}
func TestAMIConfigPrepare_Share_EncryptedBoot(t *testing.T) {
@@ -135,37 +74,3 @@ func TestAMIConfigPrepare_Share_EncryptedBoot(t *testing.T) {
t.Fatal("shouldn't be able to share ami with encrypted boot volume")
}
}
func TestAMINameValidation(t *testing.T) {
c := testAMIConfig()
c.AMIName = "aa"
if err := c.Prepare(nil); err == nil {
t.Fatal("shouldn't be able to have an ami name with less than 3 characters")
}
var longAmiName string
for i := 0; i < 129; i++ {
longAmiName += "a"
}
c.AMIName = longAmiName
if err := c.Prepare(nil); err == nil {
t.Fatal("shouldn't be able to have an ami name with great than 128 characters")
}
c.AMIName = "+aaa"
if err := c.Prepare(nil); err == nil {
t.Fatal("shouldn't be able to have an ami name with invalid characters")
}
c.AMIName = "fooBAR1()[] ./-'@_"
if err := c.Prepare(nil); err != nil {
t.Fatal("should be able to use all of the allowed AMI characters")
}
c.AMIName = `xyz-base-2017-04-05-1934`
if err := c.Prepare(nil); err != nil {
t.Fatalf("expected `xyz-base-2017-04-05-1934` to pass validation.")
}
}
+1 -1
View File
@@ -9,7 +9,7 @@ import (
"github.com/aws/aws-sdk-go/aws"
"github.com/aws/aws-sdk-go/aws/session"
"github.com/aws/aws-sdk-go/service/ec2"
"github.com/hashicorp/packer/packer"
"github.com/mitchellh/packer/packer"
)
// Artifact is an artifact implementation that contains built AMIs.
+1 -1
View File
@@ -4,7 +4,7 @@ import (
"reflect"
"testing"
"github.com/hashicorp/packer/packer"
"github.com/mitchellh/packer/packer"
)
func TestArtifact_Impl(t *testing.T) {
+1 -1
View File
@@ -5,7 +5,7 @@ import (
"github.com/aws/aws-sdk-go/aws"
"github.com/aws/aws-sdk-go/service/ec2"
"github.com/hashicorp/packer/template/interpolate"
"github.com/mitchellh/packer/template/interpolate"
)
// BlockDevice
+159
View File
@@ -0,0 +1,159 @@
package common
import (
"fmt"
"os"
"path"
"github.com/aws/aws-sdk-go/aws"
"github.com/aws/aws-sdk-go/aws/credentials"
"github.com/aws/aws-sdk-go/aws/session"
"github.com/aws/aws-sdk-go/service/sts"
"github.com/go-ini/ini"
"github.com/mitchellh/go-homedir"
)
type CLIConfig struct {
ProfileName string
SourceProfile string
AssumeRoleInput *sts.AssumeRoleInput
SourceCredentials *credentials.Credentials
profileCfg *ini.Section
profileCred *ini.Section
}
// Return a new CLIConfig with stored profile settings
func NewFromProfile(name string) (*CLIConfig, error) {
c := &CLIConfig{}
c.AssumeRoleInput = new(sts.AssumeRoleInput)
err := c.Prepare(name)
if err != nil {
return nil, err
}
sessName, err := c.getSessionName(c.profileCfg.Key("role_session_name").Value())
if err != nil {
return nil, err
}
c.AssumeRoleInput.RoleSessionName = aws.String(sessName)
arn := c.profileCfg.Key("role_arn").Value()
if arn != "" {
c.AssumeRoleInput.RoleArn = aws.String(arn)
}
id := c.profileCfg.Key("external_id").Value()
if id != "" {
c.AssumeRoleInput.ExternalId = aws.String(id)
}
c.SourceCredentials = credentials.NewStaticCredentials(
c.profileCred.Key("aws_access_key_id").Value(),
c.profileCred.Key("aws_secret_access_key").Value(),
c.profileCred.Key("aws_session_token").Value(),
)
return c, nil
}
// Return AWS Credentials using current profile. Must supply source config.
func (c *CLIConfig) CredentialsFromProfile(conf *aws.Config) (*credentials.Credentials, error) {
// If the profile name is equal to the source profile, there is no role to assume so return
// the source credentials as they were captured.
if c.ProfileName == c.SourceProfile {
return c.SourceCredentials, nil
}
srcCfg := aws.NewConfig().Copy(conf).WithCredentials(c.SourceCredentials)
session, err := session.NewSession(srcCfg)
if err != nil {
return nil, err
}
svc := sts.New(session)
res, err := svc.AssumeRole(c.AssumeRoleInput)
if err != nil {
return nil, err
}
return credentials.NewStaticCredentials(
*res.Credentials.AccessKeyId,
*res.Credentials.SecretAccessKey,
*res.Credentials.SessionToken,
), nil
}
// Sets params in the struct based on the file section
func (c *CLIConfig) Prepare(name string) error {
var err error
c.ProfileName = name
c.profileCfg, err = configFromName(c.ProfileName)
if err != nil {
return err
}
c.SourceProfile = c.profileCfg.Key("source_profile").Value()
if c.SourceProfile == "" {
c.SourceProfile = c.ProfileName
}
c.profileCred, err = credsFromName(c.SourceProfile)
if err != nil {
return err
}
return nil
}
func (c *CLIConfig) getSessionName(rawName string) (string, error) {
if rawName == "" {
name := "packer-"
host, err := os.Hostname()
if err != nil {
return name, err
}
return fmt.Sprintf("%s%s", name, host), nil
} else {
return rawName, nil
}
}
func configFromName(name string) (*ini.Section, error) {
filePath := os.Getenv("AWS_CONFIG_FILE")
if filePath == "" {
home, err := homedir.Dir()
if err != nil {
return nil, err
}
filePath = path.Join(home, ".aws", "config")
}
file, err := readFile(filePath)
if err != nil {
return nil, err
}
profileName := fmt.Sprintf("profile %s", name)
cfg, err := file.GetSection(profileName)
if err != nil {
return nil, err
}
return cfg, nil
}
func credsFromName(name string) (*ini.Section, error) {
filePath := os.Getenv("AWS_SHARED_CREDENTIALS_FILE")
if filePath == "" {
home, err := homedir.Dir()
if err != nil {
return nil, err
}
filePath = path.Join(home, ".aws", "credentials")
}
file, err := readFile(filePath)
if err != nil {
return nil, err
}
cfg, err := file.GetSection(name)
if err != nil {
return nil, err
}
return cfg, nil
}
func readFile(path string) (*ini.File, error) {
cfg, err := ini.Load(path)
if err != nil {
return nil, err
}
return cfg, nil
}
+118
View File
@@ -0,0 +1,118 @@
package common
import (
"io/ioutil"
"os"
"path"
"strconv"
"testing"
"time"
"github.com/aws/aws-sdk-go/aws"
)
func init() {
os.Setenv("AWS_ACCESS_KEY_ID", "")
os.Setenv("AWS_ACCESS_KEY", "")
os.Setenv("AWS_SECRET_ACCESS_KEY", "")
os.Setenv("AWS_SECRET_KEY", "")
os.Setenv("AWS_CONFIG_FILE", "")
os.Setenv("AWS_SHARED_CREDENTIALS_FILE", "")
}
func testCLIConfig() *CLIConfig {
return &CLIConfig{}
}
func TestCLIConfigNewFromProfile(t *testing.T) {
tmpDir := mockConfig(t)
c, err := NewFromProfile("testing2")
if err != nil {
t.Error(err)
}
if c.AssumeRoleInput.RoleArn != nil {
t.Errorf("RoleArn should be nil. Instead %p", c.AssumeRoleInput.RoleArn)
}
if c.AssumeRoleInput.ExternalId != nil {
t.Errorf("ExternalId should be nil. Instead %p", c.AssumeRoleInput.ExternalId)
}
mockConfigClose(t, tmpDir)
}
func TestAssumeRole(t *testing.T) {
tmpDir := mockConfig(t)
c, err := NewFromProfile("testing1")
if err != nil {
t.Error(err)
}
// Role
e := "arn:aws:iam::123456789011:role/rolename"
a := *c.AssumeRoleInput.RoleArn
if e != a {
t.Errorf("RoleArn value should be %s. Instead %s", e, a)
}
// Session
a = *c.AssumeRoleInput.RoleSessionName
e = "testsession"
if e != a {
t.Errorf("RoleSessionName value should be %s. Instead %s", e, a)
}
config := aws.NewConfig()
_, err = c.CredentialsFromProfile(config)
if err == nil {
t.Error("Should have errored")
}
mockConfigClose(t, tmpDir)
}
func mockConfig(t *testing.T) string {
time := time.Now().UnixNano()
dir, err := ioutil.TempDir("", strconv.FormatInt(time, 10))
if err != nil {
t.Error(err)
}
cfg := []byte(`[profile testing1]
region=us-west-2
source_profile=testingcredentials
role_arn = arn:aws:iam::123456789011:role/rolename
role_session_name = testsession
[profile testing2]
region=us-west-2
`)
cfgFile := path.Join(dir, "config")
err = ioutil.WriteFile(cfgFile, cfg, 0644)
if err != nil {
t.Error(err)
}
os.Setenv("AWS_CONFIG_FILE", cfgFile)
crd := []byte(`[testingcredentials]
aws_access_key_id = foo
aws_secret_access_key = bar
[testing2]
aws_access_key_id = baz
aws_secret_access_key = qux
`)
crdFile := path.Join(dir, "credentials")
err = ioutil.WriteFile(crdFile, crd, 0644)
if err != nil {
t.Error(err)
}
os.Setenv("AWS_SHARED_CREDENTIALS_FILE", crdFile)
return dir
}
func mockConfigClose(t *testing.T, dir string) {
err := os.RemoveAll(dir)
if err != nil {
t.Error(err)
}
}
+3 -3
View File
@@ -7,9 +7,9 @@ import (
"regexp"
"time"
"github.com/hashicorp/packer/common/uuid"
"github.com/hashicorp/packer/helper/communicator"
"github.com/hashicorp/packer/template/interpolate"
"github.com/mitchellh/packer/common/uuid"
"github.com/mitchellh/packer/helper/communicator"
"github.com/mitchellh/packer/template/interpolate"
)
var reShutdownBehavior = regexp.MustCompile("^(stop|terminate)$")
+1 -1
View File
@@ -6,7 +6,7 @@ import (
"regexp"
"testing"
"github.com/hashicorp/packer/helper/communicator"
"github.com/mitchellh/packer/helper/communicator"
)
func init() {
+2 -5
View File
@@ -8,8 +8,8 @@ import (
"time"
"github.com/aws/aws-sdk-go/service/ec2"
packerssh "github.com/hashicorp/packer/communicator/ssh"
"github.com/mitchellh/multistep"
packerssh "github.com/mitchellh/packer/communicator/ssh"
"golang.org/x/crypto/ssh"
"golang.org/x/crypto/ssh/agent"
)
@@ -88,7 +88,6 @@ func SSHConfig(useAgent bool, username, password string) func(multistep.StateBag
Auth: []ssh.AuthMethod{
ssh.PublicKeysCallback(agent.NewClient(sshAgent).Signers),
},
HostKeyCallback: ssh.InsecureIgnoreHostKey(),
}, nil
}
@@ -104,13 +103,11 @@ func SSHConfig(useAgent bool, username, password string) func(multistep.StateBag
Auth: []ssh.AuthMethod{
ssh.PublicKeys(signer),
},
HostKeyCallback: ssh.InsecureIgnoreHostKey(),
}, nil
} else {
return &ssh.ClientConfig{
User: username,
HostKeyCallback: ssh.InsecureIgnoreHostKey(),
User: username,
Auth: []ssh.AuthMethod{
ssh.Password(password),
ssh.KeyboardInteractive(
+1 -1
View File
@@ -163,7 +163,7 @@ func WaitForState(conf *StateChangeConf) (i interface{}, err error) {
log.Printf("Waiting for state to become: %s", conf.Target)
sleepSeconds := SleepSeconds()
maxTicks := TimeoutSeconds()/sleepSeconds + 1
maxTicks := int(TimeoutSeconds()/sleepSeconds) + 1
notfoundTick := 0
for {
+15 -23
View File
@@ -5,18 +5,17 @@ import (
"sync"
"github.com/aws/aws-sdk-go/aws"
"github.com/aws/aws-sdk-go/aws/session"
"github.com/aws/aws-sdk-go/service/ec2"
"github.com/hashicorp/packer/packer"
"github.com/mitchellh/multistep"
"github.com/mitchellh/packer/packer"
)
type StepAMIRegionCopy struct {
AccessConfig *AccessConfig
Regions []string
RegionKeyIds map[string]string
EncryptBootVolume bool
Name string
AccessConfig *AccessConfig
Regions []string
Name string
}
func (s *StepAMIRegionCopy) Run(state multistep.StateBag) multistep.StepAction {
@@ -34,7 +33,6 @@ func (s *StepAMIRegionCopy) Run(state multistep.StateBag) multistep.StepAction {
var lock sync.Mutex
var wg sync.WaitGroup
var regKeyID string
errs := new(packer.MultiError)
for _, region := range s.Regions {
if region == *ec2conn.Config.Region {
@@ -46,13 +44,10 @@ func (s *StepAMIRegionCopy) Run(state multistep.StateBag) multistep.StepAction {
wg.Add(1)
ui.Message(fmt.Sprintf("Copying to: %s", region))
if s.EncryptBootVolume {
regKeyID = s.RegionKeyIds[region]
}
go func(region string) {
defer wg.Done()
id, snapshotIds, err := amiRegionCopy(state, s.AccessConfig, s.Name, ami, region, *ec2conn.Config.Region, regKeyID)
id, snapshotIds, err := amiRegionCopy(state, s.AccessConfig, s.Name, ami, region, *ec2conn.Config.Region)
lock.Lock()
defer lock.Unlock()
amis[region] = id
@@ -85,29 +80,26 @@ func (s *StepAMIRegionCopy) Cleanup(state multistep.StateBag) {
// amiRegionCopy does a copy for the given AMI to the target region and
// returns the resulting ID and snapshot IDs, or error.
func amiRegionCopy(state multistep.StateBag, config *AccessConfig, name string, imageId string,
target string, source string, keyID string) (string, []string, error) {
target string, source string) (string, []string, error) {
snapshotIds := []string{}
isEncrypted := false
// Connect to the region where the AMI will be copied to
session, err := config.Session()
awsConfig, err := config.Config()
if err != nil {
return "", snapshotIds, err
}
// if we've provided a map of key ids to regions, use those keys.
if len(keyID) > 0 {
isEncrypted = true
awsConfig.Region = aws.String(target)
session, err := session.NewSession(awsConfig)
if err != nil {
return "", snapshotIds, err
}
regionconn := ec2.New(session.Copy(&aws.Config{
Region: aws.String(target)},
))
regionconn := ec2.New(session)
resp, err := regionconn.CopyImage(&ec2.CopyImageInput{
SourceRegion: &source,
SourceImageId: &imageId,
Name: &name,
Encrypted: aws.Bool(isEncrypted),
KmsKeyId: aws.String(keyID),
})
if err != nil {
+6 -13
View File
@@ -2,15 +2,16 @@ package common
import (
"fmt"
"log"
"github.com/aws/aws-sdk-go/aws"
"github.com/aws/aws-sdk-go/aws/awserr"
"github.com/aws/aws-sdk-go/aws/session"
"github.com/aws/aws-sdk-go/service/ec2"
retry "github.com/hashicorp/packer/common"
"github.com/hashicorp/packer/packer"
"github.com/hashicorp/packer/template/interpolate"
"github.com/mitchellh/multistep"
retry "github.com/mitchellh/packer/common"
"github.com/mitchellh/packer/packer"
"github.com/mitchellh/packer/template/interpolate"
)
type StepCreateTags struct {
@@ -93,7 +94,6 @@ func (s *StepCreateTags) Run(state multistep.StateBag) multistep.StepAction {
ui.Error(err.Error())
return multistep.ActionHalt
}
ReportTags(ui, amiTags)
ui.Say("Creating snapshot tags")
snapshotTags, err := ConvertToEC2Tags(s.SnapshotTags, *ec2conn.Config.Region, sourceAMI, s.Ctx)
@@ -102,10 +102,9 @@ func (s *StepCreateTags) Run(state multistep.StateBag) multistep.StepAction {
ui.Error(err.Error())
return multistep.ActionHalt
}
ReportTags(ui, snapshotTags)
// Retry creating tags for about 2.5 minutes
err = retry.Retry(0.2, 30, 11, func(_ uint) (bool, error) {
err = retry.Retry(0.2, 30, 11, func() (bool, error) {
// Tag images and snapshots
_, err := regionconn.CreateTags(&ec2.CreateTagsInput{
Resources: resourceIds,
@@ -151,13 +150,6 @@ func (s *StepCreateTags) Cleanup(state multistep.StateBag) {
// No cleanup...
}
func ReportTags(ui packer.Ui, tags []*ec2.Tag) {
for _, tag := range tags {
ui.Message(fmt.Sprintf("Adding tag: \"%s\": \"%s\"",
aws.StringValue(tag.Key), aws.StringValue(tag.Value)))
}
}
func ConvertToEC2Tags(tags map[string]string, region, sourceAmiId string, ctx interpolate.Context) ([]*ec2.Tag, error) {
var ec2Tags []*ec2.Tag
for key, value := range tags {
@@ -171,6 +163,7 @@ func ConvertToEC2Tags(tags map[string]string, region, sourceAmiId string, ctx in
return ec2Tags, fmt.Errorf("Error processing tag: %s:%s - %s", key, value, err)
}
log.Printf("Adding tag: \"%s\": \"%s\"", key, interpolatedValue)
ec2Tags = append(ec2Tags, &ec2.Tag{
Key: aws.String(key),
Value: aws.String(interpolatedValue),
+2 -2
View File
@@ -5,8 +5,8 @@ import (
"github.com/aws/aws-sdk-go/aws"
"github.com/aws/aws-sdk-go/service/ec2"
"github.com/hashicorp/packer/packer"
"github.com/mitchellh/multistep"
"github.com/mitchellh/packer/packer"
)
type StepDeregisterAMI struct {
@@ -51,7 +51,7 @@ func (s *StepDeregisterAMI) Run(state multistep.StateBag) multistep.StepAction {
// Delete snapshot(s) by image
if s.ForceDeleteSnapshot {
for _, b := range i.BlockDeviceMappings {
if b.Ebs != nil && aws.StringValue(b.Ebs.SnapshotId) != "" {
if b.Ebs != nil {
_, err := ec2conn.DeleteSnapshot(&ec2.DeleteSnapshotInput{
SnapshotId: b.Ebs.SnapshotId,
})
+5 -5
View File
@@ -6,8 +6,8 @@ import (
"github.com/aws/aws-sdk-go/aws"
"github.com/aws/aws-sdk-go/service/ec2"
"github.com/hashicorp/packer/packer"
"github.com/mitchellh/multistep"
"github.com/mitchellh/packer/packer"
)
type StepCreateEncryptedAMICopy struct {
@@ -25,7 +25,7 @@ func (s *StepCreateEncryptedAMICopy) Run(state multistep.StateBag) multistep.Ste
// Encrypt boot not set, so skip step
if !s.EncryptBootVolume {
if kmsKeyId != "" {
log.Printf("Ignoring KMS Key ID: %s, encrypted=false", kmsKeyId)
log.Printf(fmt.Sprintf("Ignoring KMS Key ID: %s, encrypted=false", kmsKeyId))
}
return multistep.ActionContinue
}
@@ -36,14 +36,14 @@ func (s *StepCreateEncryptedAMICopy) Run(state multistep.StateBag) multistep.Ste
var region, id string
if amis != nil {
for region, id = range amis {
break // There is only ever one region:ami pair in this map
break // Only get the first
}
}
ui.Say(fmt.Sprintf("Copying AMI: %s(%s)", region, id))
if kmsKeyId != "" {
ui.Say(fmt.Sprintf("Encrypting with KMS Key ID: %s", kmsKeyId))
ui.Say(fmt.Sprintf("Encypting with KMS Key ID: %s", kmsKeyId))
}
copyOpts := &ec2.CopyImageInput{
@@ -161,7 +161,7 @@ func (s *StepCreateEncryptedAMICopy) Cleanup(state multistep.StateBag) {
ec2conn := state.Get("ec2").(*ec2.EC2)
ui := state.Get("ui").(packer.Ui)
ui.Say("Deregistering the AMI because cancellation or error...")
ui.Say("Deregistering the AMI because cancelation or error...")
deregisterOpts := &ec2.DeregisterImageInput{ImageId: s.image.ImageId}
if _, err := ec2conn.DeregisterImage(deregisterOpts); err != nil {
ui.Error(fmt.Sprintf("Error deregistering AMI, may still be around: %s", err))
+2 -2
View File
@@ -11,9 +11,9 @@ import (
"time"
"github.com/aws/aws-sdk-go/service/ec2"
"github.com/hashicorp/packer/helper/communicator"
"github.com/hashicorp/packer/packer"
"github.com/mitchellh/multistep"
"github.com/mitchellh/packer/helper/communicator"
"github.com/mitchellh/packer/packer"
)
// StepGetPassword reads the password from a Windows server and sets it
+11 -7
View File
@@ -7,8 +7,8 @@ import (
"runtime"
"github.com/aws/aws-sdk-go/service/ec2"
"github.com/hashicorp/packer/packer"
"github.com/mitchellh/multistep"
"github.com/mitchellh/packer/packer"
)
type StepKeyPair struct {
@@ -19,7 +19,7 @@ type StepKeyPair struct {
KeyPairName string
PrivateKeyFile string
doCleanup bool
keyName string
}
func (s *StepKeyPair) Run(state multistep.StateBag) multistep.StepAction {
@@ -67,10 +67,11 @@ func (s *StepKeyPair) Run(state multistep.StateBag) multistep.StepAction {
return multistep.ActionHalt
}
s.doCleanup = true
// Set the keyname so we know to delete it later
s.keyName = s.TemporaryKeyPairName
// Set some state data for use in future steps
state.Put("keyPair", s.TemporaryKeyPairName)
state.Put("keyPair", s.keyName)
state.Put("privateKey", *keyResp.KeyMaterial)
// If we're in debug mode, output the private key to the working
@@ -103,7 +104,10 @@ func (s *StepKeyPair) Run(state multistep.StateBag) multistep.StepAction {
}
func (s *StepKeyPair) Cleanup(state multistep.StateBag) {
if !s.doCleanup {
// If no key name is set, then we never created it, so just return
// If we used an SSH private key file, do not go about deleting
// keypairs
if s.PrivateKeyFile != "" || (s.KeyPairName == "" && s.keyName == "") {
return
}
@@ -112,10 +116,10 @@ func (s *StepKeyPair) Cleanup(state multistep.StateBag) {
// Remove the keypair
ui.Say("Deleting temporary keypair...")
_, err := ec2conn.DeleteKeyPair(&ec2.DeleteKeyPairInput{KeyName: &s.TemporaryKeyPairName})
_, err := ec2conn.DeleteKeyPair(&ec2.DeleteKeyPairInput{KeyName: &s.keyName})
if err != nil {
ui.Error(fmt.Sprintf(
"Error cleaning up keypair. Please delete the key manually: %s", s.TemporaryKeyPairName))
"Error cleaning up keypair. Please delete the key manually: %s", s.keyName))
}
// Also remove the physical key if we're debugging.
@@ -6,9 +6,9 @@ import (
"github.com/aws/aws-sdk-go/aws"
"github.com/aws/aws-sdk-go/aws/session"
"github.com/aws/aws-sdk-go/service/ec2"
"github.com/hashicorp/packer/packer"
"github.com/hashicorp/packer/template/interpolate"
"github.com/mitchellh/multistep"
"github.com/mitchellh/packer/packer"
"github.com/mitchellh/packer/template/interpolate"
)
type StepModifyAMIAttributes struct {
@@ -5,8 +5,8 @@ import (
"github.com/aws/aws-sdk-go/aws"
"github.com/aws/aws-sdk-go/service/ec2"
"github.com/hashicorp/packer/packer"
"github.com/mitchellh/multistep"
"github.com/mitchellh/packer/packer"
)
type StepModifyEBSBackedInstance struct {
+1 -1
View File
@@ -5,8 +5,8 @@ import (
"github.com/aws/aws-sdk-go/aws"
"github.com/aws/aws-sdk-go/service/ec2"
"github.com/hashicorp/packer/packer"
"github.com/mitchellh/multistep"
"github.com/mitchellh/packer/packer"
)
// StepPreValidate provides an opportunity to pre-validate any configuration for
@@ -9,13 +9,11 @@ import (
"time"
"github.com/aws/aws-sdk-go/aws"
"github.com/aws/aws-sdk-go/aws/awserr"
"github.com/aws/aws-sdk-go/service/ec2"
retry "github.com/hashicorp/packer/common"
"github.com/hashicorp/packer/packer"
"github.com/hashicorp/packer/template/interpolate"
"github.com/mitchellh/multistep"
"github.com/mitchellh/packer/packer"
"github.com/mitchellh/packer/template/interpolate"
)
type StepRunSourceInstance struct {
@@ -291,25 +289,10 @@ func (s *StepRunSourceInstance) Run(state multistep.StateBag) multistep.StepActi
return multistep.ActionHalt
}
ReportTags(ui, ec2Tags)
// Retry creating tags for about 2.5 minutes
err = retry.Retry(0.2, 30, 11, func(_ uint) (bool, error) {
_, err := ec2conn.CreateTags(&ec2.CreateTagsInput{
Tags: ec2Tags,
Resources: []*string{instance.InstanceId},
})
if err == nil {
return true, nil
}
if awsErr, ok := err.(awserr.Error); ok {
if awsErr.Code() == "InvalidInstanceID.NotFound" {
return false, nil
}
}
return true, err
_, err = ec2conn.CreateTags(&ec2.CreateTagsInput{
Tags: ec2Tags,
Resources: []*string{instance.InstanceId},
})
if err != nil {
err := fmt.Errorf("Error tagging source instance: %s", err)
state.Put("error", err)
@@ -357,10 +340,7 @@ func (s *StepRunSourceInstance) Cleanup(state multistep.StateBag) {
Target: "cancelled",
}
_, err := WaitForState(&stateChange)
if err != nil {
ui.Error(err.Error())
}
WaitForState(&stateChange)
}
@@ -377,9 +357,6 @@ func (s *StepRunSourceInstance) Cleanup(state multistep.StateBag) {
Target: "terminated",
}
_, err := WaitForState(&stateChange)
if err != nil {
ui.Error(err.Error())
}
WaitForState(&stateChange)
}
}
+9 -12
View File
@@ -8,10 +8,10 @@ import (
"github.com/aws/aws-sdk-go/aws"
"github.com/aws/aws-sdk-go/private/waiter"
"github.com/aws/aws-sdk-go/service/ec2"
"github.com/hashicorp/packer/common/uuid"
"github.com/hashicorp/packer/helper/communicator"
"github.com/hashicorp/packer/packer"
"github.com/mitchellh/multistep"
"github.com/mitchellh/packer/common/uuid"
"github.com/mitchellh/packer/helper/communicator"
"github.com/mitchellh/packer/packer"
)
type StepSecurityGroup struct {
@@ -49,17 +49,14 @@ func (s *StepSecurityGroup) Run(state multistep.StateBag) multistep.StepAction {
}
// Create the group
groupName := fmt.Sprintf("packer_%s", uuid.TimeOrderedUUID())
ui.Say(fmt.Sprintf("Creating temporary security group for this instance: %s", groupName))
ui.Say("Creating temporary security group for this instance...")
groupName := fmt.Sprintf("packer %s", uuid.TimeOrderedUUID())
log.Printf("Temporary group name: %s", groupName)
group := &ec2.CreateSecurityGroupInput{
GroupName: &groupName,
Description: aws.String("Temporary group for Packer"),
VpcId: &s.VpcId,
}
if s.VpcId != "" {
group.VpcId = &s.VpcId
}
groupResp, err := ec2conn.CreateSecurityGroup(group)
if err != nil {
ui.Error(err.Error())
@@ -80,10 +77,10 @@ func (s *StepSecurityGroup) Run(state multistep.StateBag) multistep.StepAction {
}
// We loop and retry this a few times because sometimes the security
// group isn't available immediately because AWS resources are eventually
// group isn't available immediately because AWS resources are eventaully
// consistent.
ui.Say(fmt.Sprintf(
"Authorizing access to port %d on the temporary security group...",
"Authorizing access to port %d the temporary security group...",
port))
for i := 0; i < 5; i++ {
_, err = ec2conn.AuthorizeSecurityGroupIngress(req)
@@ -7,8 +7,8 @@ import (
"time"
"github.com/aws/aws-sdk-go/service/ec2"
"github.com/hashicorp/packer/packer"
"github.com/mitchellh/multistep"
"github.com/mitchellh/packer/packer"
)
// StepSourceAMIInfo extracts critical information from the source AMI
@@ -85,7 +85,7 @@ func (s *StepSourceAMIInfo) Run(state multistep.StateBag) multistep.StepAction {
return multistep.ActionHalt
}
if len(imageResp.Images) > 1 && !s.AmiFilters.MostRecent {
if len(imageResp.Images) > 1 && s.AmiFilters.MostRecent == false {
err := fmt.Errorf("Your query returned more than one result. Please try a more specific search, or set most_recent to true.")
state.Put("error", err)
ui.Error(err.Error())
@@ -3,11 +3,9 @@ package common
import (
"fmt"
"github.com/aws/aws-sdk-go/aws/awserr"
"github.com/aws/aws-sdk-go/service/ec2"
"github.com/hashicorp/packer/common"
"github.com/hashicorp/packer/packer"
"github.com/mitchellh/multistep"
"github.com/mitchellh/packer/packer"
)
type StepStopEBSBackedInstance struct {
@@ -30,47 +28,15 @@ func (s *StepStopEBSBackedInstance) Run(state multistep.StateBag) multistep.Step
if !s.DisableStopInstance {
// Stop the instance so we can create an AMI from it
ui.Say("Stopping the source instance...")
// Amazon EC2 API follows an eventual consistency model.
// This means that if you run a command to modify or describe a resource
// that you just created, its ID might not have propagated throughout
// the system, and you will get an error responding that the resource
// does not exist.
// Work around this by retrying a few times, up to about 5 minutes.
err := common.Retry(10, 60, 6, func(i uint) (bool, error) {
ui.Message(fmt.Sprintf("Stopping instance, attempt %d", i+1))
_, err = ec2conn.StopInstances(&ec2.StopInstancesInput{
InstanceIds: []*string{instance.InstanceId},
})
if err == nil {
// success
return true, nil
}
if awsErr, ok := err.(awserr.Error); ok {
if awsErr.Code() == "InvalidInstanceID.NotFound" {
ui.Message(fmt.Sprintf(
"Error stopping instance; will retry ..."+
"Error: %s", err))
// retry
return false, nil
}
}
// errored, but not in expected way. Don't want to retry
return true, err
_, err = ec2conn.StopInstances(&ec2.StopInstancesInput{
InstanceIds: []*string{instance.InstanceId},
})
if err != nil {
err := fmt.Errorf("Error stopping instance: %s", err)
state.Put("error", err)
ui.Error(err.Error())
return multistep.ActionHalt
}
} else {
ui.Say("Automatic instance stop disabled. Please stop instance manually.")
}
@@ -78,7 +44,7 @@ func (s *StepStopEBSBackedInstance) Run(state multistep.StateBag) multistep.Step
// Wait for the instance to actual stop
ui.Say("Waiting for the instance to stop...")
stateChange := StateChangeConf{
Pending: []string{"running", "pending", "stopping"},
Pending: []string{"running", "stopping"},
Target: "stopped",
Refresh: InstanceStateRefreshFunc(ec2conn, *instance.InstanceId),
StepState: state,
@@ -4,9 +4,9 @@ import (
"fmt"
"github.com/aws/aws-sdk-go/service/ec2"
"github.com/hashicorp/packer/packer"
"github.com/hashicorp/packer/template/interpolate"
"github.com/mitchellh/multistep"
"github.com/mitchellh/packer/packer"
"github.com/mitchellh/packer/template/interpolate"
)
type StepTagEBSVolumes struct {
@@ -44,8 +44,6 @@ func (s *StepTagEBSVolumes) Run(state multistep.StateBag) multistep.StepAction {
return multistep.ActionHalt
}
ReportTags(ui, tags)
_, err = ec2conn.CreateTags(&ec2.CreateTagsInput{
Resources: volumeIds,
Tags: tags,
+20 -23
View File
@@ -9,14 +9,15 @@ import (
"fmt"
"log"
"github.com/aws/aws-sdk-go/aws/session"
"github.com/aws/aws-sdk-go/service/ec2"
awscommon "github.com/hashicorp/packer/builder/amazon/common"
"github.com/hashicorp/packer/common"
"github.com/hashicorp/packer/helper/communicator"
"github.com/hashicorp/packer/helper/config"
"github.com/hashicorp/packer/packer"
"github.com/hashicorp/packer/template/interpolate"
"github.com/mitchellh/multistep"
awscommon "github.com/mitchellh/packer/builder/amazon/common"
"github.com/mitchellh/packer/common"
"github.com/mitchellh/packer/helper/communicator"
"github.com/mitchellh/packer/helper/config"
"github.com/mitchellh/packer/packer"
"github.com/mitchellh/packer/template/interpolate"
)
// The unique ID for this builder
@@ -77,28 +78,26 @@ func (b *Builder) Prepare(raws ...interface{}) ([]string, error) {
}
func (b *Builder) Run(ui packer.Ui, hook packer.Hook, cache packer.Cache) (packer.Artifact, error) {
config, err := b.config.Config()
if err != nil {
return nil, err
}
session, err := b.config.Session()
session, err := session.NewSession(config)
if err != nil {
return nil, err
}
ec2conn := ec2.New(session)
// If the subnet is specified but not the VpcId or AZ, try to determine them automatically
if b.config.SubnetId != "" && (b.config.AvailabilityZone == "" || b.config.VpcId == "") {
log.Printf("[INFO] Finding AZ and VpcId for the given subnet '%s'", b.config.SubnetId)
// If the subnet is specified but not the AZ, try to determine the AZ automatically
if b.config.SubnetId != "" && b.config.AvailabilityZone == "" {
log.Printf("[INFO] Finding AZ for the given subnet '%s'", b.config.SubnetId)
resp, err := ec2conn.DescribeSubnets(&ec2.DescribeSubnetsInput{SubnetIds: []*string{&b.config.SubnetId}})
if err != nil {
return nil, err
}
if b.config.AvailabilityZone == "" {
b.config.AvailabilityZone = *resp.Subnets[0].AvailabilityZone
log.Printf("[INFO] AvailabilityZone found: '%s'", b.config.AvailabilityZone)
}
if b.config.VpcId == "" {
b.config.VpcId = *resp.Subnets[0].VpcId
log.Printf("[INFO] VpcId found: '%s'", b.config.VpcId)
}
b.config.AvailabilityZone = *resp.Subnets[0].AvailabilityZone
log.Printf("[INFO] AZ found: '%s'", b.config.AvailabilityZone)
}
// Setup the state bag and initial state for the steps
@@ -193,11 +192,9 @@ func (b *Builder) Run(ui packer.Ui, hook packer.Hook, cache packer.Cache) (packe
Name: b.config.AMIName,
},
&awscommon.StepAMIRegionCopy{
AccessConfig: &b.config.AccessConfig,
Regions: b.config.AMIRegions,
RegionKeyIds: b.config.AMIRegionKMSKeyIDs,
EncryptBootVolume: b.config.AMIEncryptBootVolume,
Name: b.config.AMIName,
AccessConfig: &b.config.AccessConfig,
Regions: b.config.AMIRegions,
Name: b.config.AMIName,
},
&awscommon.StepModifyAMIAttributes{
Description: b.config.AMIDescription,
+9 -4
View File
@@ -6,10 +6,11 @@ import (
"testing"
"github.com/aws/aws-sdk-go/aws"
"github.com/aws/aws-sdk-go/aws/session"
"github.com/aws/aws-sdk-go/service/ec2"
"github.com/hashicorp/packer/builder/amazon/common"
builderT "github.com/hashicorp/packer/helper/builder/testing"
"github.com/hashicorp/packer/packer"
"github.com/mitchellh/packer/builder/amazon/common"
builderT "github.com/mitchellh/packer/helper/builder/testing"
"github.com/mitchellh/packer/packer"
)
func TestBuilderAcc_basic(t *testing.T) {
@@ -254,11 +255,15 @@ func testAccPreCheck(t *testing.T) {
func testEC2Conn() (*ec2.EC2, error) {
access := &common.AccessConfig{RawRegion: "us-east-1"}
session, err := access.Session()
config, err := access.Config()
if err != nil {
return nil, err
}
session, err := session.NewSession(config)
if err != nil {
return nil, err
}
return ec2.New(session), nil
}
+1 -1
View File
@@ -3,7 +3,7 @@ package ebs
import (
"testing"
"github.com/hashicorp/packer/packer"
"github.com/mitchellh/packer/packer"
)
func testConfig() map[string]interface{} {
+20 -7
View File
@@ -5,9 +5,9 @@ import (
"github.com/aws/aws-sdk-go/aws"
"github.com/aws/aws-sdk-go/service/ec2"
"github.com/hashicorp/packer/builder/amazon/common"
"github.com/hashicorp/packer/packer"
"github.com/mitchellh/multistep"
"github.com/mitchellh/packer/builder/amazon/common"
"github.com/mitchellh/packer/packer"
)
// stepCleanupVolumes cleans up any orphaned volumes that were not designated to
@@ -37,8 +37,22 @@ func (s *stepCleanupVolumes) Cleanup(state multistep.StateBag) {
ui.Say("Cleaning up any extra volumes...")
// We don't actually care about the value here, but we need Set behavior
save := make(map[string]struct{})
for _, b := range s.BlockDevices.AMIMappings {
if !b.DeleteOnTermination {
save[b.DeviceName] = struct{}{}
}
}
for _, b := range s.BlockDevices.LaunchMappings {
if !b.DeleteOnTermination {
save[b.DeviceName] = struct{}{}
}
}
// Collect Volume information from the cached Instance as a map of volume-id
// to device name, to compare with save list below
// to device name, to compare with save list above
var vl []*string
volList := make(map[string]string)
for _, bdm := range instance.BlockDeviceMappings {
@@ -77,11 +91,10 @@ func (s *stepCleanupVolumes) Cleanup(state multistep.StateBag) {
return
}
// Filter out any devices created as part of the launch mappings, since
// we'll let amazon follow the `delete_on_termination` setting.
for _, b := range s.BlockDevices.LaunchMappings {
// Filter out any devices marked for saving
for saveName := range save {
for volKey, volName := range volList {
if volName == b.DeviceName {
if volName == saveName {
delete(volList, volKey)
}
}
+3 -3
View File
@@ -4,9 +4,9 @@ import (
"fmt"
"github.com/aws/aws-sdk-go/service/ec2"
awscommon "github.com/hashicorp/packer/builder/amazon/common"
"github.com/hashicorp/packer/packer"
"github.com/mitchellh/multistep"
awscommon "github.com/mitchellh/packer/builder/amazon/common"
"github.com/mitchellh/packer/packer"
)
type stepCreateAMI struct {
@@ -92,7 +92,7 @@ func (s *stepCreateAMI) Cleanup(state multistep.StateBag) {
ec2conn := state.Get("ec2").(*ec2.EC2)
ui := state.Get("ui").(packer.Ui)
ui.Say("Deregistering the AMI because cancellation or error...")
ui.Say("Deregistering the AMI because cancelation or error...")
deregisterOpts := &ec2.DeregisterImageInput{ImageId: s.image.ImageId}
if _, err := ec2conn.DeregisterImage(deregisterOpts); err != nil {
ui.Error(fmt.Sprintf("Error deregistering AMI, may still be around: %s", err))
+5 -5
View File
@@ -7,9 +7,9 @@ import (
"github.com/aws/aws-sdk-go/aws"
"github.com/aws/aws-sdk-go/service/ec2"
"github.com/hashicorp/packer/builder/amazon/common"
builderT "github.com/hashicorp/packer/helper/builder/testing"
"github.com/hashicorp/packer/packer"
"github.com/mitchellh/packer/builder/amazon/common"
builderT "github.com/mitchellh/packer/helper/builder/testing"
"github.com/mitchellh/packer/packer"
)
type TFBuilder struct {
@@ -84,11 +84,11 @@ func checkTags() builderT.TestCheckFunc {
})
if err != nil {
return fmt.Errorf("Error retrieving Snapshots for AMI Artifact (%#v) in Tags Test: %s", artifact, err)
return fmt.Errorf("Error retreiving Snapshots for AMI Artifcat (%#v) in Tags Test: %s", artifact, err)
}
if len(resp.Snapshots) == 0 {
return fmt.Errorf("No Snapshots found for AMI Artifact (%#v) in Tags Test", artifact)
return fmt.Errorf("No Snapshots found for AMI Artifcat (%#v) in Tags Test", artifact)
}
// Grab the snapshots, check the tags
+25 -25
View File
@@ -7,14 +7,16 @@ import (
"fmt"
"log"
"github.com/aws/aws-sdk-go/aws/session"
"github.com/aws/aws-sdk-go/service/ec2"
awscommon "github.com/hashicorp/packer/builder/amazon/common"
"github.com/hashicorp/packer/common"
"github.com/hashicorp/packer/helper/communicator"
"github.com/hashicorp/packer/helper/config"
"github.com/hashicorp/packer/packer"
"github.com/hashicorp/packer/template/interpolate"
"github.com/hashicorp/errwrap"
"github.com/mitchellh/multistep"
awscommon "github.com/mitchellh/packer/builder/amazon/common"
"github.com/mitchellh/packer/common"
"github.com/mitchellh/packer/helper/communicator"
"github.com/mitchellh/packer/helper/config"
"github.com/mitchellh/packer/packer"
"github.com/mitchellh/packer/template/interpolate"
)
const BuilderId = "mitchellh.amazon.ebssurrogate"
@@ -92,27 +94,27 @@ func (b *Builder) Prepare(raws ...interface{}) ([]string, error) {
}
func (b *Builder) Run(ui packer.Ui, hook packer.Hook, cache packer.Cache) (packer.Artifact, error) {
session, err := b.config.Session()
awsConfig, err := b.config.Config()
if err != nil {
return nil, err
}
ec2conn := ec2.New(session)
// If the subnet is specified but not the VpcId or AZ, try to determine them automatically
if b.config.SubnetId != "" && (b.config.AvailabilityZone == "" || b.config.VpcId == "") {
log.Printf("[INFO] Finding AZ and VpcId for the given subnet '%s'", b.config.SubnetId)
awsSession, err := session.NewSession(awsConfig)
if err != nil {
return nil, errwrap.Wrapf("Error creating AWS Session: {{err}}", err)
}
ec2conn := ec2.New(awsSession)
// If the subnet is specified but not the AZ, try to determine the AZ automatically
if b.config.SubnetId != "" && b.config.AvailabilityZone == "" {
log.Printf("[INFO] Finding AZ for the given subnet '%s'", b.config.SubnetId)
resp, err := ec2conn.DescribeSubnets(&ec2.DescribeSubnetsInput{SubnetIds: []*string{&b.config.SubnetId}})
if err != nil {
return nil, err
}
if b.config.AvailabilityZone == "" {
b.config.AvailabilityZone = *resp.Subnets[0].AvailabilityZone
log.Printf("[INFO] AvailabilityZone found: '%s'", b.config.AvailabilityZone)
}
if b.config.VpcId == "" {
b.config.VpcId = *resp.Subnets[0].VpcId
log.Printf("[INFO] VpcId found: '%s'", b.config.VpcId)
}
b.config.AvailabilityZone = *resp.Subnets[0].AvailabilityZone
log.Printf("[INFO] AZ found: '%s'", b.config.AvailabilityZone)
}
// Setup the state bag and initial state for the steps
@@ -201,7 +203,7 @@ func (b *Builder) Run(ui packer.Ui, hook packer.Hook, cache packer.Cache) (packe
},
&StepRegisterAMI{
RootDevice: b.config.RootDevice,
BlockDevices: b.config.BlockDevices.BuildAMIDevices(),
BlockDevices: b.config.BlockDevices.BuildLaunchDevices(),
},
&awscommon.StepCreateEncryptedAMICopy{
KeyID: b.config.AMIKmsKeyId,
@@ -209,11 +211,9 @@ func (b *Builder) Run(ui packer.Ui, hook packer.Hook, cache packer.Cache) (packe
Name: b.config.AMIName,
},
&awscommon.StepAMIRegionCopy{
AccessConfig: &b.config.AccessConfig,
Regions: b.config.AMIRegions,
RegionKeyIds: b.config.AMIRegionKMSKeyIDs,
EncryptBootVolume: b.config.AMIEncryptBootVolume,
Name: b.config.AMIName,
AccessConfig: &b.config.AccessConfig,
Regions: b.config.AMIRegions,
Name: b.config.AMIName,
},
&awscommon.StepModifyAMIAttributes{
Description: b.config.AMIDescription,
+1 -1
View File
@@ -3,7 +3,7 @@ package ebssurrogate
import (
"testing"
"github.com/hashicorp/packer/packer"
"github.com/mitchellh/packer/packer"
)
func testConfig() map[string]interface{} {
@@ -4,7 +4,7 @@ import (
"errors"
"github.com/aws/aws-sdk-go/aws"
"github.com/aws/aws-sdk-go/service/ec2"
"github.com/hashicorp/packer/template/interpolate"
"github.com/mitchellh/packer/template/interpolate"
)
type RootBlockDevice struct {
@@ -5,9 +5,9 @@ import (
"github.com/aws/aws-sdk-go/aws"
"github.com/aws/aws-sdk-go/service/ec2"
awscommon "github.com/hashicorp/packer/builder/amazon/common"
"github.com/hashicorp/packer/packer"
"github.com/mitchellh/multistep"
awscommon "github.com/mitchellh/packer/builder/amazon/common"
"github.com/mitchellh/packer/packer"
)
// StepRegisterAMI creates the AMI.
@@ -25,7 +25,16 @@ func (s *StepRegisterAMI) Run(state multistep.StateBag) multistep.StepAction {
ui.Say("Registering the AMI...")
blockDevicesExcludingRoot := DeduplicateRootVolume(s.BlockDevices, s.RootDevice, snapshotId)
blockDevicesExcludingRoot := make([]*ec2.BlockDeviceMapping, 0, len(s.BlockDevices)-1)
for _, blockDevice := range s.BlockDevices {
if *blockDevice.DeviceName == s.RootDevice.SourceDeviceName {
continue
}
blockDevicesExcludingRoot = append(blockDevicesExcludingRoot, blockDevice)
}
blockDevicesExcludingRoot = append(blockDevicesExcludingRoot, s.RootDevice.createBlockDeviceMapping(snapshotId))
registerOpts := &ec2.RegisterImageInput{
Name: &config.AMIName,
@@ -109,25 +118,10 @@ func (s *StepRegisterAMI) Cleanup(state multistep.StateBag) {
ec2conn := state.Get("ec2").(*ec2.EC2)
ui := state.Get("ui").(packer.Ui)
ui.Say("Deregistering the AMI because cancellation or error...")
ui.Say("Deregistering the AMI because cancelation or error...")
deregisterOpts := &ec2.DeregisterImageInput{ImageId: s.image.ImageId}
if _, err := ec2conn.DeregisterImage(deregisterOpts); err != nil {
ui.Error(fmt.Sprintf("Error deregistering AMI, may still be around: %s", err))
return
}
}
func DeduplicateRootVolume(BlockDevices []*ec2.BlockDeviceMapping, RootDevice RootBlockDevice, snapshotId string) []*ec2.BlockDeviceMapping {
// Defensive coding to make sure we only add the root volume once
blockDevicesExcludingRoot := make([]*ec2.BlockDeviceMapping, 0, len(BlockDevices))
for _, blockDevice := range BlockDevices {
if *blockDevice.DeviceName == RootDevice.SourceDeviceName {
continue
}
blockDevicesExcludingRoot = append(blockDevicesExcludingRoot, blockDevice)
}
blockDevicesExcludingRoot = append(blockDevicesExcludingRoot, RootDevice.createBlockDeviceMapping(snapshotId))
return blockDevicesExcludingRoot
}
@@ -1,37 +0,0 @@
package ebssurrogate
import (
"testing"
"github.com/aws/aws-sdk-go/service/ec2"
)
func GetStringPointer() *string {
tmp := "/dev/name"
return &tmp
}
func GetTestDevice() *ec2.BlockDeviceMapping {
TestDev := ec2.BlockDeviceMapping{
DeviceName: GetStringPointer(),
}
return &TestDev
}
func TestStepRegisterAmi_DeduplicateRootVolume(t *testing.T) {
TestRootDevice := RootBlockDevice{}
TestRootDevice.SourceDeviceName = "/dev/name"
blockDevices := []*ec2.BlockDeviceMapping{}
blockDevicesExcludingRoot := DeduplicateRootVolume(blockDevices, TestRootDevice, "12342351")
if len(blockDevicesExcludingRoot) != 1 {
t.Fatalf("Unexpected length of block devices list")
}
TestBlockDevice := GetTestDevice()
blockDevices = append(blockDevices, TestBlockDevice)
blockDevicesExcludingRoot = DeduplicateRootVolume(blockDevices, TestRootDevice, "12342351")
if len(blockDevicesExcludingRoot) != 1 {
t.Fatalf("Unexpected length of block devices list")
}
}
@@ -6,9 +6,9 @@ import (
"time"
"github.com/aws/aws-sdk-go/service/ec2"
awscommon "github.com/hashicorp/packer/builder/amazon/common"
"github.com/hashicorp/packer/packer"
"github.com/mitchellh/multistep"
awscommon "github.com/mitchellh/packer/builder/amazon/common"
"github.com/mitchellh/packer/packer"
)
// StepSnapshotNewRootVolume creates a snapshot of the created volume.
+1 -1
View File
@@ -7,7 +7,7 @@ import (
"strings"
"github.com/aws/aws-sdk-go/service/ec2"
"github.com/hashicorp/packer/packer"
"github.com/mitchellh/packer/packer"
)
// map of region to list of volume IDs
+2 -2
View File
@@ -1,8 +1,8 @@
package ebsvolume
import (
awscommon "github.com/hashicorp/packer/builder/amazon/common"
"github.com/hashicorp/packer/template/interpolate"
awscommon "github.com/mitchellh/packer/builder/amazon/common"
"github.com/mitchellh/packer/template/interpolate"
)
type BlockDevice struct {
+20 -18
View File
@@ -6,14 +6,16 @@ import (
"fmt"
"log"
"github.com/aws/aws-sdk-go/aws/session"
"github.com/aws/aws-sdk-go/service/ec2"
awscommon "github.com/hashicorp/packer/builder/amazon/common"
"github.com/hashicorp/packer/common"
"github.com/hashicorp/packer/helper/communicator"
"github.com/hashicorp/packer/helper/config"
"github.com/hashicorp/packer/packer"
"github.com/hashicorp/packer/template/interpolate"
"github.com/hashicorp/errwrap"
"github.com/mitchellh/multistep"
awscommon "github.com/mitchellh/packer/builder/amazon/common"
"github.com/mitchellh/packer/common"
"github.com/mitchellh/packer/helper/communicator"
"github.com/mitchellh/packer/helper/config"
"github.com/mitchellh/packer/packer"
"github.com/mitchellh/packer/template/interpolate"
)
const BuilderId = "mitchellh.amazon.ebsvolume"
@@ -70,27 +72,27 @@ func (b *Builder) Prepare(raws ...interface{}) ([]string, error) {
}
func (b *Builder) Run(ui packer.Ui, hook packer.Hook, cache packer.Cache) (packer.Artifact, error) {
session, err := b.config.Session()
config, err := b.config.Config()
if err != nil {
return nil, err
}
session, err := session.NewSession(config)
if err != nil {
return nil, errwrap.Wrapf("Error creating AWS Session: {{err}}", err)
}
ec2conn := ec2.New(session)
// If the subnet is specified but not the VpcId or AZ, try to determine them automatically
if b.config.SubnetId != "" && (b.config.AvailabilityZone == "" || b.config.VpcId == "") {
log.Printf("[INFO] Finding AZ and VpcId for the given subnet '%s'", b.config.SubnetId)
// If the subnet is specified but not the AZ, try to determine the AZ automatically
if b.config.SubnetId != "" && b.config.AvailabilityZone == "" {
log.Printf("[INFO] Finding AZ for the given subnet '%s'", b.config.SubnetId)
resp, err := ec2conn.DescribeSubnets(&ec2.DescribeSubnetsInput{SubnetIds: []*string{&b.config.SubnetId}})
if err != nil {
return nil, err
}
if b.config.AvailabilityZone == "" {
b.config.AvailabilityZone = *resp.Subnets[0].AvailabilityZone
log.Printf("[INFO] AvailabilityZone found: '%s'", b.config.AvailabilityZone)
}
if b.config.VpcId == "" {
b.config.VpcId = *resp.Subnets[0].VpcId
log.Printf("[INFO] VpcId found: '%s'", b.config.VpcId)
}
b.config.AvailabilityZone = *resp.Subnets[0].AvailabilityZone
log.Printf("[INFO] AZ found: '%s'", b.config.AvailabilityZone)
}
// Setup the state bag and initial state for the steps

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