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  }