github.com/Cloud-Foundations/Dominator@v0.3.4/imagepublishers/amipublisher/usedImages.go (about) 1 package amipublisher 2 3 import ( 4 "github.com/Cloud-Foundations/Dominator/lib/awsutil" 5 "github.com/Cloud-Foundations/Dominator/lib/format" 6 "github.com/Cloud-Foundations/Dominator/lib/log" 7 libtags "github.com/Cloud-Foundations/Dominator/lib/tags" 8 "github.com/aws/aws-sdk-go/aws" 9 "github.com/aws/aws-sdk-go/service/ec2" 10 ) 11 12 func generateUsedResults(rawResults []targetImageUsage, 13 logger log.DebugLogger) UsedImagesResult { 14 logger.Debugln(0, "generating results") 15 results := UsedImagesResult{} 16 for _, result := range rawResults { 17 for amiId, image := range result.images { 18 results.UsedImages = append(results.UsedImages, Image{ 19 Target: awsutil.Target{ 20 AccountName: result.accountName, 21 Region: result.region, 22 }, 23 AmiId: amiId, 24 AmiName: aws.StringValue(image.Name), 25 CreationDate: aws.StringValue(image.CreationDate), 26 Description: aws.StringValue(image.Description), 27 Size: uint(computeImageConsumption(image)), 28 Tags: awsutil.CreateTagsFromList(image.Tags), 29 }) 30 } 31 for _, instance := range result.allUsingInstances { 32 results.UsingInstances = append(results.UsingInstances, Instance{ 33 Target: awsutil.Target{ 34 AccountName: result.accountName, 35 Region: result.region, 36 }, 37 AmiId: aws.StringValue(instance.ImageId), 38 InstanceId: aws.StringValue(instance.InstanceId), 39 LaunchTime: instance.LaunchTime.Format( 40 format.TimeFormatSeconds), 41 Tags: awsutil.CreateTagsFromList(instance.Tags), 42 }) 43 } 44 } 45 return results 46 } 47 48 func listUsedImages(targets awsutil.TargetList, skipList awsutil.TargetList, 49 searchTags, excludeSearchTags libtags.Tags, 50 logger log.DebugLogger) (UsedImagesResult, error) { 51 logger.Debugln(0, "loading credentials") 52 cs, err := awsutil.LoadCredentials() 53 if err != nil { 54 return UsedImagesResult{}, err 55 } 56 rawResults, err := listUsedImagesCS(targets, skipList, searchTags, 57 excludeSearchTags, cs, logger) 58 if err != nil { 59 return UsedImagesResult{}, err 60 } 61 return generateUsedResults(rawResults, logger), nil 62 } 63 64 func listUsedImagesCS(targets awsutil.TargetList, skipList awsutil.TargetList, 65 searchTags, excludeSearchTags libtags.Tags, cs *awsutil.CredentialsStore, 66 logger log.DebugLogger) ([]targetImageUsage, error) { 67 resultsChannel := make(chan targetImageUsage, 1) 68 logger.Debugln(0, "collecting raw data") 69 numTargets, err := cs.ForEachEC2Target(targets, skipList, 70 func(awsService *ec2.EC2, account, region string, logger log.Logger) { 71 usage, err := listTargetUsedImages(awsService, searchTags, 72 excludeSearchTags, cs.AccountNameToId(account), logger) 73 if err != nil { 74 logger.Println(err) 75 } 76 resultsChannel <- targetImageUsage{ 77 accountName: account, 78 region: region, 79 err: err, 80 imageUsage: usage, 81 } 82 }, 83 false, logger) 84 if err != nil { 85 return nil, err 86 } 87 // Collect results. 88 logger.Debugln(0, "waiting for raw data") 89 var firstError error 90 rawResults := make([]targetImageUsage, 0, numTargets) 91 for i := 0; i < numTargets; i++ { 92 result := <-resultsChannel 93 if result.err != nil { 94 if firstError == nil { 95 firstError = result.err 96 } 97 } else { 98 rawResults = append(rawResults, result) 99 } 100 } 101 if firstError != nil { 102 return nil, firstError 103 } 104 // Aggregate used map across accounts. 105 logger.Debugln(0, "aggregating usage across accounts") 106 imagesUsedPerRegion := make(map[string]usedImages) // Key: region. 107 totalImages := 0 108 var totalGiBytes int64 109 totalInstances := 0 110 totalUsingInstances := 0 111 for _, result := range rawResults { 112 usedMap := imagesUsedPerRegion[result.region] 113 if usedMap == nil { 114 usedMap = make(usedImages) 115 imagesUsedPerRegion[result.region] = usedMap 116 } 117 for amiId := range result.used { 118 usedMap[amiId] = struct{}{} 119 } 120 for _, image := range result.images { 121 totalGiBytes += computeImageConsumption(image) 122 } 123 totalImages += len(result.images) 124 totalInstances += len(result.allInstances) 125 totalUsingInstances += len(result.allUsingInstances) 126 } 127 logger.Printf("total images found: %d consuming %s\n", 128 totalImages, format.FormatBytes(uint64(totalGiBytes)<<30)) 129 logger.Printf("instances using images: %d/%d\n", 130 totalUsingInstances, totalInstances) 131 return rawResults, nil 132 } 133 134 func listTargetUsedImages(awsService *ec2.EC2, searchTags libtags.Tags, 135 excludeSearchTags libtags.Tags, accountId string, 136 logger log.Logger) (imageUsage, error) { 137 results := imageUsage{ 138 images: make(map[string]*ec2.Image), 139 used: make(usedImages), 140 } 141 visibleImages := make(map[string]struct{}) 142 if images, err := getImages(awsService, "", searchTags); err != nil { 143 return imageUsage{}, err 144 } else { 145 for _, image := range images { 146 amiId := aws.StringValue(image.ImageId) 147 visibleImages[amiId] = struct{}{} 148 if aws.StringValue(image.OwnerId) == accountId { 149 results.images[amiId] = image 150 } 151 } 152 } 153 if len(excludeSearchTags) > 0 { 154 images, err := getImages(awsService, accountId, excludeSearchTags) 155 if err != nil { 156 return imageUsage{}, err 157 } else { 158 for _, image := range images { 159 amiId := aws.StringValue(image.ImageId) 160 delete(visibleImages, amiId) 161 delete(results.images, amiId) 162 } 163 } 164 } 165 instances, err := describeInstances(awsService, nil) 166 if err != nil { 167 return imageUsage{}, err 168 } 169 results.allInstances = instances 170 for _, instance := range instances { 171 amiId := aws.StringValue(instance.ImageId) 172 results.used[amiId] = struct{}{} 173 if _, ok := visibleImages[amiId]; ok { 174 results.allUsingInstances = append(results.allUsingInstances, 175 instance) 176 } 177 } 178 return results, nil 179 }