github.com/scaleway/scaleway-cli@v1.11.1/pkg/commands/images.go (about) 1 // Copyright (C) 2015 Scaleway. All rights reserved. 2 // Use of this source code is governed by a MIT-style 3 // license that can be found in the LICENSE.md file. 4 5 package commands 6 7 import ( 8 "fmt" 9 "sort" 10 "strings" 11 "sync" 12 "text/tabwriter" 13 "time" 14 15 "github.com/Sirupsen/logrus" 16 "github.com/docker/go-units" 17 "github.com/renstrom/fuzzysearch/fuzzy" 18 "github.com/scaleway/scaleway-cli/pkg/api" 19 "github.com/scaleway/scaleway-cli/pkg/utils" 20 ) 21 22 // ImagesArgs are flags for the `RunImages` function 23 type ImagesArgs struct { 24 All bool 25 NoTrunc bool 26 Quiet bool 27 Filters map[string]string 28 } 29 30 // RunImages is the handler for 'scw images' 31 func RunImages(ctx CommandContext, args ImagesArgs) error { 32 wg := sync.WaitGroup{} 33 chEntries := make(chan api.ScalewayImageInterface) 34 errChan := make(chan error, 10) 35 var entries = []api.ScalewayImageInterface{} 36 37 filterType := args.Filters["type"] 38 39 if filterType == "" || filterType == "image" { 40 wg.Add(1) 41 go func() { 42 defer wg.Done() 43 images, err := ctx.API.GetImages() 44 if err != nil { 45 errChan <- fmt.Errorf("unable to fetch images from the Scaleway API: %v", err) 46 return 47 } 48 for _, val := range *images { 49 creationDate, err := time.Parse("2006-01-02T15:04:05.000000+00:00", val.CreationDate) 50 if err != nil { 51 errChan <- fmt.Errorf("unable to parse creation date from the Scaleway API: %v", err) 52 return 53 } 54 archAvailable := make(map[string]struct{}) 55 zoneAvailable := make(map[string]struct{}) 56 57 for _, version := range val.Versions { 58 if val.CurrentPublicVersion == version.ID { 59 for _, local := range version.LocalImages { 60 archAvailable[local.Arch] = struct{}{} 61 zoneAvailable[local.Zone] = struct{}{} 62 } 63 break 64 } 65 } 66 regions := []string{} 67 for k := range zoneAvailable { 68 regions = append(regions, k) 69 } 70 archs := []string{} 71 for k := range archAvailable { 72 archs = append(archs, k) 73 } 74 chEntries <- api.ScalewayImageInterface{ 75 Type: "image", 76 CreationDate: creationDate, 77 Identifier: val.CurrentPublicVersion, 78 Name: val.Name, 79 Tag: "latest", 80 Organization: val.Organization.ID, 81 Public: val.Public, 82 Region: regions, 83 Archs: archs, 84 } 85 } 86 }() 87 } 88 89 if args.All || filterType != "" { 90 if filterType == "" || filterType == "snapshot" { 91 wg.Add(1) 92 go func() { 93 defer wg.Done() 94 snapshots, err := ctx.API.GetSnapshots() 95 if err != nil { 96 errChan <- fmt.Errorf("unable to fetch snapshots from the Scaleway API: %v", err) 97 return 98 } 99 for _, val := range *snapshots { 100 creationDate, err := time.Parse("2006-01-02T15:04:05.000000+00:00", val.CreationDate) 101 if err != nil { 102 errChan <- fmt.Errorf("unable to parse creation date from the Scaleway API: %v", err) 103 return 104 } 105 chEntries <- api.ScalewayImageInterface{ 106 Type: "snapshot", 107 CreationDate: creationDate, 108 Identifier: val.Identifier, 109 Name: val.Name, 110 Tag: "<snapshot>", 111 VirtualSize: val.Size, 112 Public: false, 113 Organization: val.Organization, 114 // FIXME the region should not be hardcoded 115 Region: []string{"par1"}, 116 } 117 } 118 }() 119 } 120 121 if filterType == "" || filterType == "bootscript" { 122 wg.Add(1) 123 go func() { 124 defer wg.Done() 125 bootscripts, err := ctx.API.GetBootscripts() 126 if err != nil { 127 errChan <- fmt.Errorf("unable to fetch bootscripts from the Scaleway API: %v", err) 128 return 129 } 130 for _, val := range *bootscripts { 131 chEntries <- api.ScalewayImageInterface{ 132 Type: "bootscript", 133 Identifier: val.Identifier, 134 Name: val.Title, 135 Tag: "<bootscript>", 136 Public: false, 137 Region: []string{""}, 138 Archs: []string{val.Arch}, 139 } 140 } 141 }() 142 } 143 144 if filterType == "" || filterType == "volume" { 145 wg.Add(1) 146 go func() { 147 defer wg.Done() 148 volumes, err := ctx.API.GetVolumes() 149 if err != nil { 150 errChan <- fmt.Errorf("unable to fetch volumes from the Scaleway API: %v", err) 151 return 152 } 153 for _, val := range *volumes { 154 creationDate, err := time.Parse("2006-01-02T15:04:05.000000+00:00", val.CreationDate) 155 if err != nil { 156 errChan <- fmt.Errorf("unable to parse creation date from the Scaleway API: %v", err) 157 return 158 } 159 chEntries <- api.ScalewayImageInterface{ 160 Type: "volume", 161 CreationDate: creationDate, 162 Identifier: val.Identifier, 163 Name: val.Name, 164 Tag: "<volume>", 165 VirtualSize: val.Size, 166 Public: false, 167 Organization: val.Organization, 168 // FIXME the region should not be hardcoded 169 Region: []string{"par1"}, 170 } 171 } 172 }() 173 } 174 } 175 176 go func() { 177 wg.Wait() 178 close(chEntries) 179 }() 180 181 for { 182 entry, ok := <-chEntries 183 if !ok { 184 break 185 } 186 entries = append(entries, entry) 187 } 188 select { 189 case err := <-errChan: 190 return err 191 default: 192 break 193 } 194 for key, value := range args.Filters { 195 switch key { 196 case "organization", "type", "name", "public": 197 continue 198 default: 199 logrus.Warnf("Unknown filter: '%s=%s'", key, value) 200 } 201 } 202 203 w := tabwriter.NewWriter(ctx.Stdout, 20, 1, 3, ' ', 0) 204 defer w.Flush() 205 if !args.Quiet { 206 fmt.Fprintf(w, "REPOSITORY\tTAG\tIMAGE ID\tCREATED\tREGION\tARCH\n") 207 } 208 sort.Sort(api.ByCreationDate(entries)) 209 for _, image := range entries { 210 if image.Identifier == "" { 211 continue 212 } 213 for key, value := range args.Filters { 214 switch key { 215 case "type": 216 if value != image.Type { 217 goto skipimage 218 } 219 case "organization": 220 switch value { 221 case "me": 222 value = ctx.API.Organization 223 case "official-distribs": 224 value = "a283af0b-d13e-42e1-a43f-855ffbf281ab" 225 case "official-apps": 226 value = "c3884e19-7a3e-4b69-9db8-50e7f902aafc" 227 } 228 if image.Organization != value { 229 goto skipimage 230 } 231 case "name": 232 if fuzzy.RankMatch(strings.ToLower(value), strings.ToLower(image.Name)) == -1 { 233 goto skipimage 234 } 235 case "public": 236 if (value == "true" && !image.Public) || (value == "false" && image.Public) { 237 goto skipimage 238 } 239 } 240 } 241 242 if args.Quiet { 243 fmt.Fprintf(ctx.Stdout, "%s\n", image.Identifier) 244 } else { 245 tag := image.Tag 246 shortID := utils.TruncIf(image.Identifier, 8, !args.NoTrunc) 247 name := utils.Wordify(image.Name) 248 if !image.Public && image.Type == "image" { 249 name = "user/" + name 250 } 251 shortName := utils.TruncIf(name, 25, !args.NoTrunc) 252 var creationDate string 253 if image.CreationDate.IsZero() { 254 creationDate = "n/a" 255 } else { 256 creationDate = units.HumanDuration(time.Now().UTC().Sub(image.CreationDate)) 257 } 258 if len(image.Archs) == 0 { 259 image.Archs = []string{"n/a"} 260 } 261 sort.Strings(image.Archs) 262 fmt.Fprintf(w, "%s\t%s\t%s\t%s\t%s\t%v\n", shortName, tag, shortID, creationDate, image.Region, image.Archs) 263 } 264 265 skipimage: 266 continue 267 } 268 return nil 269 }