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 }