github.com/cloud-foundations/dominator@v0.0.0-20221004181915-6e4fee580046/cmd/ami-publisher/unusedImages.go (about)

     1  package main
     2  
     3  import (
     4  	"encoding/csv"
     5  	"fmt"
     6  	"os"
     7  	"sort"
     8  	"strconv"
     9  	"syscall"
    10  
    11  	"github.com/Cloud-Foundations/Dominator/imagepublishers/amipublisher"
    12  	"github.com/Cloud-Foundations/Dominator/lib/fsutil"
    13  	libjson "github.com/Cloud-Foundations/Dominator/lib/json"
    14  	"github.com/Cloud-Foundations/Dominator/lib/log"
    15  	libtags "github.com/Cloud-Foundations/Dominator/lib/tags"
    16  )
    17  
    18  const (
    19  	filePerms = syscall.S_IRUSR | syscall.S_IWUSR | syscall.S_IRGRP |
    20  		syscall.S_IROTH
    21  )
    22  
    23  func listUnusedImagesSubcommand(args []string, logger log.DebugLogger) error {
    24  	if err := listUnusedImages(logger); err != nil {
    25  		return fmt.Errorf("Error listing unused images: %s", err)
    26  	}
    27  	logMemoryUsage(logger)
    28  	return nil
    29  }
    30  
    31  func listUnusedImages(logger log.DebugLogger) error {
    32  	results, err := amipublisher.ListUnusedImages(targets, skipTargets,
    33  		searchTags, excludeSearchTags, *minImageAge, logger)
    34  	if err != nil {
    35  		return err
    36  	}
    37  	if err := libjson.WriteWithIndent(os.Stdout, "    ", results); err != nil {
    38  		return err
    39  	}
    40  	if *oldImageInstancesCsvFile != "" {
    41  		err := writeInstancesCsv(*oldImageInstancesCsvFile,
    42  			results.OldInstances)
    43  		if err != nil {
    44  			return err
    45  		}
    46  	}
    47  	if *unusedImagesCsvFile != "" {
    48  		err := writeUnusedImagesCsv(*unusedImagesCsvFile,
    49  			results.UnusedImages)
    50  		if err != nil {
    51  			return err
    52  		}
    53  	}
    54  	return nil
    55  }
    56  
    57  func writeInstancesCsv(filename string,
    58  	instances []amipublisher.Instance) error {
    59  	file, err := fsutil.CreateRenamingWriter(filename, filePerms)
    60  	if err != nil {
    61  		return err
    62  	}
    63  	defer file.Close()
    64  	writer := csv.NewWriter(file)
    65  	defer writer.Flush()
    66  	// First find all the tag keys.
    67  	tagKeysSet := make(map[string]struct{})
    68  	for _, instance := range instances {
    69  		for key := range instance.Tags {
    70  			tagKeysSet[key] = struct{}{}
    71  		}
    72  	}
    73  	tagKeysList := makeTagKeysList(tagKeysSet)
    74  	header := []string{"Account", "Region", "AmiId", "InstanceId", "LaunchTime"}
    75  	header = append(header, tagKeysList...)
    76  	if err := writer.Write(header); err != nil {
    77  		return err
    78  	}
    79  	for _, instance := range instances {
    80  		record := []string{
    81  			instance.AccountName,
    82  			instance.Region,
    83  			instance.AmiId,
    84  			instance.InstanceId,
    85  			instance.LaunchTime,
    86  		}
    87  		err := appendRecordAndWrite(writer, record, tagKeysList, instance.Tags)
    88  		if err != nil {
    89  			return err
    90  		}
    91  	}
    92  	return nil
    93  }
    94  
    95  func writeUnusedImagesCsv(filename string,
    96  	images []amipublisher.Image) error {
    97  	file, err := os.OpenFile(filename, os.O_CREATE|os.O_TRUNC|os.O_WRONLY, filePerms)
    98  	if err != nil {
    99  		return err
   100  	}
   101  	defer file.Close()
   102  	writer := csv.NewWriter(file)
   103  	defer writer.Flush()
   104  	// First find all the tag keys.
   105  	tagKeysSet := make(map[string]struct{})
   106  	for _, image := range images {
   107  		for key := range image.Tags {
   108  			tagKeysSet[key] = struct{}{}
   109  		}
   110  	}
   111  	tagKeysList := makeTagKeysList(tagKeysSet)
   112  	header := []string{
   113  		"Account",
   114  		"Region",
   115  		"AmiId",
   116  		"AmiName",
   117  		"CreationDate",
   118  		"Description",
   119  		"Size",
   120  	}
   121  	header = append(header, tagKeysList...)
   122  	if err := writer.Write(header); err != nil {
   123  		return err
   124  	}
   125  	for _, image := range images {
   126  		record := []string{
   127  			image.AccountName,
   128  			image.Region,
   129  			image.AmiId,
   130  			image.AmiName,
   131  			image.CreationDate,
   132  			image.Description,
   133  			strconv.Itoa(int(image.Size)),
   134  		}
   135  		err := appendRecordAndWrite(writer, record, tagKeysList, image.Tags)
   136  		if err != nil {
   137  			return err
   138  		}
   139  	}
   140  	return nil
   141  }
   142  
   143  func makeTagKeysList(tagKeysSet map[string]struct{}) []string {
   144  	tagKeysList := make([]string, 0, len(tagKeysSet))
   145  	for key := range tagKeysSet {
   146  		tagKeysList = append(tagKeysList, key)
   147  	}
   148  	sort.Strings(tagKeysList)
   149  	return tagKeysList
   150  }
   151  
   152  func appendRecordAndWrite(writer *csv.Writer, record []string,
   153  	tagKeysList []string, tags libtags.Tags) error {
   154  	for _, key := range tagKeysList {
   155  		value := tags[key]
   156  		record = append(record, value)
   157  	}
   158  	return writer.Write(record)
   159  }
   160  
   161  func deleteUnusedImagesSubcommand(args []string, logger log.DebugLogger) error {
   162  	if err := deleteUnusedImages(logger); err != nil {
   163  		return fmt.Errorf("Error deleting unused images: %s", err)
   164  	}
   165  	logMemoryUsage(logger)
   166  	return nil
   167  }
   168  
   169  func deleteUnusedImages(logger log.DebugLogger) error {
   170  	results, err := amipublisher.DeleteUnusedImages(targets, skipTargets,
   171  		searchTags, excludeSearchTags, *minImageAge, logger)
   172  	if err != nil {
   173  		return err
   174  	}
   175  	return libjson.WriteWithIndent(os.Stdout, "    ", results)
   176  }