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  }