github.com/rothwerx/packer@v0.9.0/builder/amazon/ebs/step_cleanup_volumes.go (about) 1 package ebs 2 3 import ( 4 "fmt" 5 6 "github.com/aws/aws-sdk-go/aws" 7 "github.com/aws/aws-sdk-go/service/ec2" 8 "github.com/mitchellh/multistep" 9 "github.com/mitchellh/packer/builder/amazon/common" 10 "github.com/mitchellh/packer/packer" 11 ) 12 13 // stepCleanupVolumes cleans up any orphaned volumes that were not designated to 14 // remain after termination of the instance. These volumes are typically ones 15 // that are marked as "delete on terminate:false" in the source_ami of a build. 16 type stepCleanupVolumes struct { 17 BlockDevices common.BlockDevices 18 } 19 20 func (s *stepCleanupVolumes) Run(state multistep.StateBag) multistep.StepAction { 21 // stepCleanupVolumes is for Cleanup only 22 return multistep.ActionContinue 23 } 24 25 func (s *stepCleanupVolumes) Cleanup(state multistep.StateBag) { 26 ec2conn := state.Get("ec2").(*ec2.EC2) 27 instanceRaw := state.Get("instance") 28 var instance *ec2.Instance 29 if instanceRaw != nil { 30 instance = instanceRaw.(*ec2.Instance) 31 } 32 ui := state.Get("ui").(packer.Ui) 33 amisRaw := state.Get("amis") 34 if amisRaw == nil { 35 ui.Say("No AMIs to cleanup") 36 return 37 } 38 39 if instance == nil { 40 ui.Say("No volumes to clean up, skipping") 41 return 42 } 43 44 ui.Say("Cleaning up any extra volumes...") 45 46 // We don't actually care about the value here, but we need Set behavior 47 save := make(map[string]struct{}) 48 for _, b := range s.BlockDevices.AMIMappings { 49 if !b.DeleteOnTermination { 50 save[b.DeviceName] = struct{}{} 51 } 52 } 53 54 for _, b := range s.BlockDevices.LaunchMappings { 55 if !b.DeleteOnTermination { 56 save[b.DeviceName] = struct{}{} 57 } 58 } 59 60 // Collect Volume information from the cached Instance as a map of volume-id 61 // to device name, to compare with save list above 62 var vl []*string 63 volList := make(map[string]string) 64 for _, bdm := range instance.BlockDeviceMappings { 65 if bdm.Ebs != nil { 66 vl = append(vl, bdm.Ebs.VolumeId) 67 volList[*bdm.Ebs.VolumeId] = *bdm.DeviceName 68 } 69 } 70 71 // Using the volume list from the cached Instance, check with AWS for up to 72 // date information on them 73 resp, err := ec2conn.DescribeVolumes(&ec2.DescribeVolumesInput{ 74 Filters: []*ec2.Filter{ 75 &ec2.Filter{ 76 Name: aws.String("volume-id"), 77 Values: vl, 78 }, 79 }, 80 }) 81 82 if err != nil { 83 ui.Say(fmt.Sprintf("Error describing volumes: %s", err)) 84 return 85 } 86 87 // If any of the returned volumes are in a "deleting" stage or otherwise not 88 // available, remove them from the list of volumes 89 for _, v := range resp.Volumes { 90 if v.State != nil && *v.State != "available" { 91 delete(volList, *v.VolumeId) 92 } 93 } 94 95 if len(resp.Volumes) == 0 { 96 ui.Say("No volumes to clean up, skipping") 97 return 98 } 99 100 // Filter out any devices marked for saving 101 for saveName, _ := range save { 102 for volKey, volName := range volList { 103 if volName == saveName { 104 delete(volList, volKey) 105 } 106 } 107 } 108 109 // Destroy remaining volumes 110 for k, _ := range volList { 111 ui.Say(fmt.Sprintf("Destroying volume (%s)...", k)) 112 _, err := ec2conn.DeleteVolume(&ec2.DeleteVolumeInput{VolumeId: aws.String(k)}) 113 if err != nil { 114 ui.Say(fmt.Sprintf("Error deleting volume: %s", k)) 115 } 116 117 } 118 }