Skip to content

Commit a266afb

Browse files
committed
Support of 'scw images --filter' (scaleway#134)
1 parent b1145cf commit a266afb

File tree

4 files changed

+190
-74
lines changed

4 files changed

+190
-74
lines changed

README.md

+22-1
Original file line numberDiff line numberDiff line change
@@ -410,9 +410,29 @@ List images.
410410
Options:
411411

412412
-a, --all=false Show all iamges
413+
-f, --filter="" Filter output based on conditions provided
413414
-h, --help=false Print usage
414415
--no-trunc=false Don't truncate output
415416
-q, --quiet=false Only show numeric IDs
417+
418+
Examples:
419+
420+
$ scw images
421+
$ scw images -a
422+
$ scw images -q
423+
$ scw images --no-trunc
424+
$ scw images -f organization=me
425+
$ scw images -f organization=official-distribs
426+
$ scw images -f organization=official-apps
427+
$ scw images -f organization=UUIDOFORGANIZATION
428+
$ scw images -f name=ubuntu
429+
$ scw images -f type=image
430+
$ scw images -f type=bootscript
431+
$ scw images -f type=snapshot
432+
$ scw images -f type=volume
433+
$ scw images -f public=true
434+
$ scw images -f public=false
435+
$ scw images -f "organization=me type=volume" -q
416436
```
417437

418438

@@ -1088,10 +1108,11 @@ $ scw inspect myserver | jq '.[0].public_ip.address'
10881108

10891109
#### Features
10901110

1111+
* Support of `scw images --filter` option *(type, organization, name, public)* ([#134](https://github.com/scaleway/scaleway-cli/issues/134))
10911112
* Syncing cache to disk after server creation when running `scw run` in a non-detached mode
10921113
* Bump to Golang 1.5
10931114
* Support --tmp-ssh-key `scw {run,create}` option ([#99](https://github.com/scaleway/scaleway-cli/issues/99))
1094-
* Support -f `scw run --rm` option ([#117](https://github.com/scaleway/scaleway-cli/issues/117))
1115+
* Support of `scw run --rm` option ([#117](https://github.com/scaleway/scaleway-cli/issues/117))
10951116
* Support of `--gateway=login@host` ([#110](https://github.com/scaleway/scaleway-cli/issues/110))
10961117
* Upload local ssh key to scaleway account on `scw login` ([#100](https://github.com/scaleway/scaleway-cli/issues/100))
10971118
* Add a 'running indicator' for `scw run`, can be disabled with the new flag `--quiet`

pkg/api/helpers.go

+1
Original file line numberDiff line numberDiff line change
@@ -37,6 +37,7 @@ type ScalewayImageInterface struct {
3737
VirtualSize float64
3838
Public bool
3939
Type string
40+
Organization string
4041
}
4142

4243
// ResolveGateway tries to resolve a server public ip address, else returns the input string, i.e. IPv4, hostname

pkg/cli/cmd_images.go

+41-5
Original file line numberDiff line numberDiff line change
@@ -4,27 +4,52 @@
44

55
package cli
66

7-
import "github.com/scaleway/scaleway-cli/pkg/commands"
7+
import (
8+
"strings"
9+
10+
"github.com/Sirupsen/logrus"
11+
"github.com/scaleway/scaleway-cli/pkg/commands"
12+
)
813

914
var cmdImages = &Command{
1015
Exec: runImages,
1116
UsageLine: "images [OPTIONS]",
1217
Description: "List images",
1318
Help: "List images.",
19+
Examples: `
20+
$ scw images
21+
$ scw images -a
22+
$ scw images -q
23+
$ scw images --no-trunc
24+
$ scw images -f organization=me
25+
$ scw images -f organization=official-distribs
26+
$ scw images -f organization=official-apps
27+
$ scw images -f organization=UUIDOFORGANIZATION
28+
$ scw images -f name=ubuntu
29+
$ scw images -f type=image
30+
$ scw images -f type=bootscript
31+
$ scw images -f type=snapshot
32+
$ scw images -f type=volume
33+
$ scw images -f public=true
34+
$ scw images -f public=false
35+
$ scw images -f "organization=me type=volume" -q
36+
`,
1437
}
1538

1639
func init() {
1740
cmdImages.Flag.BoolVar(&imagesA, []string{"a", "-all"}, false, "Show all iamges")
1841
cmdImages.Flag.BoolVar(&imagesNoTrunc, []string{"-no-trunc"}, false, "Don't truncate output")
1942
cmdImages.Flag.BoolVar(&imagesQ, []string{"q", "-quiet"}, false, "Only show numeric IDs")
2043
cmdImages.Flag.BoolVar(&imagesHelp, []string{"h", "-help"}, false, "Print usage")
44+
cmdImages.Flag.StringVar(&imagesFilters, []string{"f", "-filter"}, "", "Filter output based on conditions provided")
2145
}
2246

2347
// Flags
24-
var imagesA bool // -a flag
25-
var imagesQ bool // -q flag
26-
var imagesNoTrunc bool // -no-trunc flag
27-
var imagesHelp bool // -h, --help flag
48+
var imagesA bool // -a flag
49+
var imagesQ bool // -q flag
50+
var imagesNoTrunc bool // -no-trunc flag
51+
var imagesHelp bool // -h, --help flag
52+
var imagesFilters string // -f, --filters
2853

2954
func runImages(cmd *Command, rawArgs []string) error {
3055
if imagesHelp {
@@ -38,6 +63,17 @@ func runImages(cmd *Command, rawArgs []string) error {
3863
All: imagesA,
3964
Quiet: imagesQ,
4065
NoTrunc: imagesNoTrunc,
66+
Filters: make(map[string]string, 0),
67+
}
68+
if imagesFilters != "" {
69+
for _, filter := range strings.Split(imagesFilters, " ") {
70+
parts := strings.SplitN(filter, "=", 2)
71+
if _, ok := args.Filters[parts[0]]; ok {
72+
logrus.Warnf("Duplicated filter: %q", parts[0])
73+
} else {
74+
args.Filters[parts[0]] = parts[1]
75+
}
76+
}
4177
}
4278
ctx := cmd.GetContext(rawArgs)
4379
return commands.RunImages(ctx, args)

pkg/commands/images.go

+126-68
Original file line numberDiff line numberDiff line change
@@ -7,10 +7,12 @@ package commands
77
import (
88
"fmt"
99
"sort"
10+
"strings"
1011
"sync"
1112
"text/tabwriter"
1213
"time"
1314

15+
"github.com/renstrom/fuzzysearch/fuzzy"
1416
"github.com/scaleway/scaleway-cli/pkg/api"
1517
"github.com/scaleway/scaleway-cli/pkg/utils"
1618
"github.com/scaleway/scaleway-cli/vendor/github.com/Sirupsen/logrus"
@@ -22,6 +24,7 @@ type ImagesArgs struct {
2224
All bool
2325
NoTrunc bool
2426
Quiet bool
27+
Filters map[string]string
2528
}
2629

2730
// RunImages is the handler for 'scw images'
@@ -30,98 +33,111 @@ func RunImages(ctx CommandContext, args ImagesArgs) error {
3033
chEntries := make(chan api.ScalewayImageInterface)
3134
var entries = []api.ScalewayImageInterface{}
3235

33-
// FIXME: remove log.Fatalf in routines
36+
filterType := args.Filters["type"]
3437

35-
wg.Add(1)
36-
go func() {
37-
defer wg.Done()
38-
images, err := ctx.API.GetImages()
39-
if err != nil {
40-
logrus.Fatalf("unable to fetch images from the Scaleway API: %v", err)
41-
}
42-
for _, val := range *images {
43-
creationDate, err := time.Parse("2006-01-02T15:04:05.000000+00:00", val.CreationDate)
44-
if err != nil {
45-
logrus.Fatalf("unable to parse creation date from the Scaleway API: %v", err)
46-
}
47-
chEntries <- api.ScalewayImageInterface{
48-
Type: "image",
49-
CreationDate: creationDate,
50-
Identifier: val.Identifier,
51-
Name: val.Name,
52-
Public: val.Public,
53-
Tag: "latest",
54-
VirtualSize: float64(val.RootVolume.Size),
55-
}
56-
}
57-
}()
38+
// FIXME: remove log.Fatalf in routines
5839

59-
if args.All {
40+
if filterType == "" || filterType == "image" {
6041
wg.Add(1)
6142
go func() {
6243
defer wg.Done()
63-
snapshots, err := ctx.API.GetSnapshots()
44+
images, err := ctx.API.GetImages()
6445
if err != nil {
65-
logrus.Fatalf("unable to fetch snapshots from the Scaleway API: %v", err)
46+
logrus.Fatalf("unable to fetch images from the Scaleway API: %v", err)
6647
}
67-
for _, val := range *snapshots {
48+
for _, val := range *images {
6849
creationDate, err := time.Parse("2006-01-02T15:04:05.000000+00:00", val.CreationDate)
6950
if err != nil {
7051
logrus.Fatalf("unable to parse creation date from the Scaleway API: %v", err)
7152
}
7253
chEntries <- api.ScalewayImageInterface{
73-
Type: "snapshot",
54+
Type: "image",
7455
CreationDate: creationDate,
7556
Identifier: val.Identifier,
7657
Name: val.Name,
77-
Tag: "<snapshot>",
78-
VirtualSize: float64(val.Size),
79-
Public: false,
58+
Public: val.Public,
59+
Tag: "latest",
60+
VirtualSize: float64(val.RootVolume.Size),
61+
Organization: val.Organization,
8062
}
8163
}
8264
}()
65+
}
8366

84-
wg.Add(1)
85-
go func() {
86-
defer wg.Done()
87-
bootscripts, err := ctx.API.GetBootscripts()
88-
if err != nil {
89-
logrus.Fatalf("unable to fetch bootscripts from the Scaleway API: %v", err)
90-
}
91-
for _, val := range *bootscripts {
92-
chEntries <- api.ScalewayImageInterface{
93-
Type: "bootscript",
94-
Identifier: val.Identifier,
95-
Name: val.Title,
96-
Tag: "<bootscript>",
97-
Public: false,
67+
if args.All || filterType != "" {
68+
if filterType == "" || filterType == "snapshot" {
69+
wg.Add(1)
70+
go func() {
71+
defer wg.Done()
72+
snapshots, err := ctx.API.GetSnapshots()
73+
if err != nil {
74+
logrus.Fatalf("unable to fetch snapshots from the Scaleway API: %v", err)
9875
}
99-
}
100-
}()
76+
for _, val := range *snapshots {
77+
creationDate, err := time.Parse("2006-01-02T15:04:05.000000+00:00", val.CreationDate)
78+
if err != nil {
79+
logrus.Fatalf("unable to parse creation date from the Scaleway API: %v", err)
80+
}
81+
chEntries <- api.ScalewayImageInterface{
82+
Type: "snapshot",
83+
CreationDate: creationDate,
84+
Identifier: val.Identifier,
85+
Name: val.Name,
86+
Tag: "<snapshot>",
87+
VirtualSize: float64(val.Size),
88+
Public: false,
89+
Organization: val.Organization,
90+
}
91+
}
92+
}()
93+
}
10194

102-
wg.Add(1)
103-
go func() {
104-
defer wg.Done()
105-
volumes, err := ctx.API.GetVolumes()
106-
if err != nil {
107-
logrus.Fatalf("unable to fetch volumes from the Scaleway API: %v", err)
108-
}
109-
for _, val := range *volumes {
110-
creationDate, err := time.Parse("2006-01-02T15:04:05.000000+00:00", val.CreationDate)
95+
if filterType == "" || filterType == "bootscript" {
96+
wg.Add(1)
97+
go func() {
98+
defer wg.Done()
99+
bootscripts, err := ctx.API.GetBootscripts()
111100
if err != nil {
112-
logrus.Fatalf("unable to parse creation date from the Scaleway API: %v", err)
101+
logrus.Fatalf("unable to fetch bootscripts from the Scaleway API: %v", err)
113102
}
114-
chEntries <- api.ScalewayImageInterface{
115-
Type: "volume",
116-
CreationDate: creationDate,
117-
Identifier: val.Identifier,
118-
Name: val.Name,
119-
Tag: "<volume>",
120-
VirtualSize: float64(val.Size),
121-
Public: false,
103+
for _, val := range *bootscripts {
104+
chEntries <- api.ScalewayImageInterface{
105+
Type: "bootscript",
106+
Identifier: val.Identifier,
107+
Name: val.Title,
108+
Tag: "<bootscript>",
109+
Public: false,
110+
}
122111
}
123-
}
124-
}()
112+
}()
113+
}
114+
115+
if filterType == "" || filterType == "volume" {
116+
wg.Add(1)
117+
go func() {
118+
defer wg.Done()
119+
volumes, err := ctx.API.GetVolumes()
120+
if err != nil {
121+
logrus.Fatalf("unable to fetch volumes from the Scaleway API: %v", err)
122+
}
123+
for _, val := range *volumes {
124+
creationDate, err := time.Parse("2006-01-02T15:04:05.000000+00:00", val.CreationDate)
125+
if err != nil {
126+
logrus.Fatalf("unable to parse creation date from the Scaleway API: %v", err)
127+
}
128+
chEntries <- api.ScalewayImageInterface{
129+
Type: "volume",
130+
CreationDate: creationDate,
131+
Identifier: val.Identifier,
132+
Name: val.Name,
133+
Tag: "<volume>",
134+
VirtualSize: float64(val.Size),
135+
Public: false,
136+
Organization: val.Organization,
137+
}
138+
}
139+
}()
140+
}
125141
}
126142

127143
go func() {
@@ -144,13 +160,52 @@ func RunImages(ctx CommandContext, args ImagesArgs) error {
144160
}
145161
}
146162

163+
for key, value := range args.Filters {
164+
switch key {
165+
case "organization", "type", "name", "public":
166+
continue
167+
default:
168+
logrus.Warnf("Unknown filter: '%s=%s'", key, value)
169+
}
170+
}
171+
147172
w := tabwriter.NewWriter(ctx.Stdout, 20, 1, 3, ' ', 0)
148173
defer w.Flush()
149174
if !args.Quiet {
150175
fmt.Fprintf(w, "REPOSITORY\tTAG\tIMAGE ID\tCREATED\tVIRTUAL SIZE\n")
151176
}
152177
sort.Sort(api.ByCreationDate(entries))
153178
for _, image := range entries {
179+
180+
for key, value := range args.Filters {
181+
switch key {
182+
case "type":
183+
if value != image.Type {
184+
goto skipimage
185+
}
186+
case "organization":
187+
switch value {
188+
case "me":
189+
value = ctx.API.Organization
190+
case "official-distribs":
191+
value = "a283af0b-d13e-42e1-a43f-855ffbf281ab"
192+
case "official-apps":
193+
value = "c3884e19-7a3e-4b69-9db8-50e7f902aafc"
194+
}
195+
if image.Organization != value {
196+
goto skipimage
197+
}
198+
case "name":
199+
if fuzzy.RankMatch(strings.ToLower(value), strings.ToLower(image.Name)) == -1 {
200+
goto skipimage
201+
}
202+
case "public":
203+
if (value == "true" && !image.Public) || (value == "false" && image.Public) {
204+
goto skipimage
205+
}
206+
}
207+
}
208+
154209
if args.Quiet {
155210
fmt.Fprintf(ctx.Stdout, "%s\n", image.Identifier)
156211
} else {
@@ -174,6 +229,9 @@ func RunImages(ctx CommandContext, args ImagesArgs) error {
174229
}
175230
fmt.Fprintf(w, "%s\t%s\t%s\t%s\t%s\n", shortName, tag, shortID, creationDate, virtualSize)
176231
}
232+
233+
skipimage:
234+
continue
177235
}
178236
return nil
179237
}

0 commit comments

Comments
 (0)