diff --git a/builder/amazon/ebsvolume/artifact.go b/builder/amazon/ebsvolume/artifact.go new file mode 100644 index 000000000..e8787d993 --- /dev/null +++ b/builder/amazon/ebsvolume/artifact.go @@ -0,0 +1,87 @@ +package ebsvolume + +import ( + "fmt" + "log" + "sort" + "strings" + + "github.com/aws/aws-sdk-go/service/ec2" + "github.com/mitchellh/packer/packer" +) + +// map of region to list of volume IDs +type EbsVolumes map[string][]string + +// Artifact is an artifact implementation that contains built AMIs. +type Artifact struct { + // A map of regions to EBS Volume IDs. + Volumes EbsVolumes + + // BuilderId is the unique ID for the builder that created this AMI + BuilderIdValue string + + // EC2 connection for performing API stuff. + Conn *ec2.EC2 +} + +func (a *Artifact) BuilderId() string { + return a.BuilderIdValue +} + +func (*Artifact) Files() []string { + // We have no files + return nil +} + +// returns a sorted list of region:ID pairs +func (a *Artifact) idList() []string { + parts := make([]string, 0, len(a.Volumes)) + for region, volumeIDs := range a.Volumes { + for _, volumeID := range volumeIDs { + parts = append(parts, fmt.Sprintf("%s:%s", region, volumeID)) + } + } + + sort.Strings(parts) + return parts +} + +func (a *Artifact) Id() string { + return strings.Join(a.idList(), ",") +} + +func (a *Artifact) String() string { + return fmt.Sprintf("EBS Volumes were created:\n\n%s", strings.Join(a.idList(), "\n")) +} + +func (a *Artifact) State(name string) interface{} { + return nil +} + +func (a *Artifact) Destroy() error { + errors := make([]error, 0) + + for region, volumeIDs := range a.Volumes { + for _, volumeID := range volumeIDs { + log.Printf("Deregistering Volume ID (%s) from region (%s)", volumeID, region) + + input := &ec2.DeleteVolumeInput{ + VolumeId: &volumeID, + } + if _, err := a.Conn.DeleteVolume(input); 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 +} diff --git a/builder/amazon/ebsvolume/builder.go b/builder/amazon/ebsvolume/builder.go index 22724f10f..818dd92d7 100644 --- a/builder/amazon/ebsvolume/builder.go +++ b/builder/amazon/ebsvolume/builder.go @@ -5,7 +5,6 @@ package ebsvolume import ( "fmt" "log" - "strings" "github.com/aws/aws-sdk-go/aws/session" "github.com/aws/aws-sdk-go/service/ec2" @@ -19,6 +18,8 @@ import ( "github.com/mitchellh/packer/template/interpolate" ) +const BuilderId = "mitchellh.amazon.ebsvolume" + type Config struct { common.PackerConfig `mapstructure:",squash"` awscommon.AccessConfig `mapstructure:",squash"` @@ -91,8 +92,6 @@ func (b *Builder) Run(ui packer.Ui, hook packer.Hook, cache packer.Cache) (packe launchBlockDevices := commonBlockDevices(b.config.VolumeMappings) - var volumeIds *[]string - // Build the steps steps := []multistep.Step{ &awscommon.StepSourceAMIInfo{ @@ -132,7 +131,6 @@ func (b *Builder) Run(ui packer.Ui, hook packer.Hook, cache packer.Cache) (packe }, &stepTagEBSVolumes{ VolumeMapping: b.config.VolumeMappings, - VolumeIDs: &volumeIds, }, &awscommon.StepGetPassword{ Debug: b.config.PackerDebug, @@ -168,8 +166,14 @@ func (b *Builder) Run(ui packer.Ui, hook packer.Hook, cache packer.Cache) (packe return nil, rawErr.(error) } - ui.Say(fmt.Sprintf("Created Volumes: %s", strings.Join(*volumeIds, ", "))) - return nil, nil + // Build the artifact and return it + artifact := &Artifact{ + Volumes: state.Get("ebsvolumes").(EbsVolumes), + BuilderIdValue: BuilderId, + Conn: ec2conn, + } + ui.Say(fmt.Sprintf("Created Volumes: %s", artifact)) + return artifact, nil } func (b *Builder) Cancel() { diff --git a/builder/amazon/ebsvolume/step_tag_ebs_volumes.go b/builder/amazon/ebsvolume/step_tag_ebs_volumes.go index 92fb56900..297f8683f 100644 --- a/builder/amazon/ebsvolume/step_tag_ebs_volumes.go +++ b/builder/amazon/ebsvolume/step_tag_ebs_volumes.go @@ -11,7 +11,6 @@ import ( type stepTagEBSVolumes struct { VolumeMapping []BlockDevice - VolumeIDs **[]string } func (s *stepTagEBSVolumes) Run(state multistep.StateBag) multistep.StepAction { @@ -19,15 +18,17 @@ func (s *stepTagEBSVolumes) Run(state multistep.StateBag) multistep.StepAction { instance := state.Get("instance").(*ec2.Instance) ui := state.Get("ui").(packer.Ui) - volumeIds := make([]string, 0, len(s.VolumeMapping)) + volumes := make(EbsVolumes) for _, instanceBlockDevices := range instance.BlockDeviceMappings { for _, configVolumeMapping := range s.VolumeMapping { if configVolumeMapping.DeviceName == *instanceBlockDevices.DeviceName { - volumeIds = append(volumeIds, *instanceBlockDevices.Ebs.VolumeId) + volumes[*ec2conn.Config.Region] = append( + volumes[*ec2conn.Config.Region], + *instanceBlockDevices.Ebs.VolumeId) } } } - *s.VolumeIDs = &volumeIds + state.Put("ebsvolumes", volumes) if len(s.VolumeMapping) > 0 { ui.Say("Tagging EBS volumes...")