Browse Source

Add containerFields query parameter to ListImages

Aaron Kirkbride 1 year ago
parent
commit
6f921b6e23

+ 14
- 10
api/v6/api.go View File

@@ -42,19 +42,19 @@ type ControllerStatus struct {
42 42
 }
43 43
 
44 44
 type Container struct {
45
-	Name           string
46
-	Current        image.Info
47
-	LatestFiltered image.Info
45
+	Name           string     `json:",omitempty"`
46
+	Current        image.Info `json:",omitempty"`
47
+	LatestFiltered image.Info `json:",omitempty"`
48 48
 
49 49
 	// All available images (ignoring tag filters)
50
-	Available               []image.Info
51
-	AvailableError          string `json:",omitempty"`
52
-	AvailableImagesCount    int
53
-	NewAvailableImagesCount int
50
+	Available               []image.Info `json:",omitempty"`
51
+	AvailableError          string       `json:",omitempty"`
52
+	AvailableImagesCount    int          `json:",omitempty"`
53
+	NewAvailableImagesCount int          `json:",omitempty"`
54 54
 
55 55
 	// Filtered available images (matching tag filters)
56
-	FilteredImagesCount    int
57
-	NewFilteredImagesCount int
56
+	FilteredImagesCount    int `json:",omitempty"`
57
+	NewFilteredImagesCount int `json:",omitempty"`
58 58
 }
59 59
 
60 60
 // --- config types
@@ -75,13 +75,17 @@ type Deprecated interface {
75 75
 	SyncNotify(context.Context) error
76 76
 }
77 77
 
78
+type ListImagesOptions struct {
79
+	OverrideContainerFields []string
80
+}
81
+
78 82
 type NotDeprecated interface {
79 83
 	// from v5
80 84
 	Export(context.Context) ([]byte, error)
81 85
 
82 86
 	// v6
83 87
 	ListServices(ctx context.Context, namespace string) ([]ControllerStatus, error)
84
-	ListImages(context.Context, update.ResourceSpec) ([]ImageStatus, error)
88
+	ListImages(ctx context.Context, spec update.ResourceSpec, opts ListImagesOptions) ([]ImageStatus, error)
85 89
 	UpdateManifests(context.Context, update.Spec) (job.ID, error)
86 90
 	SyncStatus(ctx context.Context, ref string) ([]string, error)
87 91
 	JobStatus(context.Context, job.ID) (job.Status, error)

+ 1
- 1
cmd/fluxctl/list_images_cmd.go View File

@@ -67,7 +67,7 @@ func (opts *controllerShowOpts) RunE(cmd *cobra.Command, args []string) error {
67 67
 
68 68
 	ctx := context.Background()
69 69
 
70
-	controllers, err := opts.API.ListImages(ctx, resourceSpec)
70
+	controllers, err := opts.API.ListImages(ctx, resourceSpec, v6.ListImagesOptions{})
71 71
 	if err != nil {
72 72
 		return err
73 73
 	}

+ 62
- 19
daemon/daemon.go View File

@@ -138,7 +138,7 @@ func (cs clusterContainers) Containers(i int) []resource.Container {
138 138
 }
139 139
 
140 140
 // List the images available for set of services
141
-func (d *Daemon) ListImages(ctx context.Context, spec update.ResourceSpec) ([]v6.ImageStatus, error) {
141
+func (d *Daemon) ListImages(ctx context.Context, spec update.ResourceSpec, opts v6.ListImagesOptions) ([]v6.ImageStatus, error) {
142 142
 	var services []cluster.Controller
143 143
 	var err error
144 144
 	if spec == update.ResourceSpecAll {
@@ -163,7 +163,10 @@ func (d *Daemon) ListImages(ctx context.Context, spec update.ResourceSpec) ([]v6
163 163
 
164 164
 	var res []v6.ImageStatus
165 165
 	for _, service := range services {
166
-		serviceContainers := getServiceContainers(service, imageRepos, policyResourceMap)
166
+		serviceContainers, err := getServiceContainers(service, imageRepos, policyResourceMap, opts.OverrideContainerFields)
167
+		if err != nil {
168
+			return nil, err
169
+		}
167 170
 		res = append(res, v6.ImageStatus{
168 171
 			ID:         service.ID,
169 172
 			Containers: serviceContainers,
@@ -557,13 +560,37 @@ func containers2containers(cs []resource.Container) []v6.Container {
557 560
 	return res
558 561
 }
559 562
 
560
-func getServiceContainers(service cluster.Controller, imageRepos update.ImageRepos, policyResourceMap policy.ResourceMap) (res []v6.Container) {
563
+func getServiceContainers(service cluster.Controller, imageRepos update.ImageRepos, policyResourceMap policy.ResourceMap, overrideFields []string) (res []v6.Container, err error) {
564
+	fields := map[string]struct{}{
565
+		"Name":                    struct{}{},
566
+		"Current":                 struct{}{},
567
+		"LatestFiltered":          struct{}{},
568
+		"Available":               struct{}{},
569
+		"AvailableError":          struct{}{},
570
+		"AvailableImagesCount":    struct{}{},
571
+		"NewAvailableImagesCount": struct{}{},
572
+		"FilteredImagesCount":     struct{}{},
573
+		"NewFilteredImagesCount":  struct{}{},
574
+	}
575
+
576
+	// If overrideFields is provided, override the default fields to return
577
+	if len(overrideFields) > 0 {
578
+		newFieldsMap := make(map[string]struct{})
579
+		for _, f := range overrideFields {
580
+			if _, ok := fields[f]; !ok {
581
+				return nil, errors.Errorf("%s is an invalid field", f)
582
+			}
583
+			newFieldsMap[f] = struct{}{}
584
+		}
585
+		fields = newFieldsMap
586
+	}
587
+
561 588
 	for _, c := range service.ContainersOrNil() {
589
+		var container v6.Container
590
+
562 591
 		imageRepo := c.Image.Name
563 592
 		tagPattern := getTagPattern(policyResourceMap, service.ID, c.Name)
564
-
565 593
 		currentImage := imageRepos.FindImageInfo(imageRepo, c.Image)
566
-		latestFilteredImage, _ := imageRepos.LatestFilteredImage(imageRepo, tagPattern)
567 594
 
568 595
 		// All available images
569 596
 		availableImages := imageRepos.Available(imageRepo)
@@ -591,21 +618,37 @@ func getServiceContainers(service cluster.Controller, imageRepos update.ImageRep
591 618
 		}
592 619
 		newFilteredImagesCount := len(newFilteredImages)
593 620
 
594
-		res = append(res, v6.Container{
595
-			Name:           c.Name,
596
-			Current:        currentImage,
597
-			LatestFiltered: latestFilteredImage,
598
-
599
-			Available:               availableImages,
600
-			AvailableError:          availableImagesErr,
601
-			AvailableImagesCount:    availableImagesCount,
602
-			NewAvailableImagesCount: newAvailableImagesCount,
603
-
604
-			FilteredImagesCount:    filteredImagesCount,
605
-			NewFilteredImagesCount: newFilteredImagesCount,
606
-		})
621
+		if _, ok := fields["Name"]; ok {
622
+			container.Name = c.Name
623
+		}
624
+		if _, ok := fields["Current"]; ok {
625
+			container.Current = currentImage
626
+		}
627
+		if _, ok := fields["LatestFiltered"]; ok {
628
+			container.LatestFiltered, _ = imageRepos.LatestFilteredImage(imageRepo, tagPattern)
629
+		}
630
+		if _, ok := fields["Available"]; ok {
631
+			container.Available = availableImages
632
+		}
633
+		if _, ok := fields["AvailableError"]; ok {
634
+			container.AvailableError = availableImagesErr
635
+		}
636
+		if _, ok := fields["AvailableImagesCount"]; ok {
637
+			container.AvailableImagesCount = availableImagesCount
638
+		}
639
+		if _, ok := fields["NewAvailableImagesCount"]; ok {
640
+			container.NewAvailableImagesCount = newAvailableImagesCount
641
+		}
642
+		if _, ok := fields["FilteredImagesCount"]; ok {
643
+			container.FilteredImagesCount = filteredImagesCount
644
+		}
645
+		if _, ok := fields["NewFilteredImagesCount"]; ok {
646
+			container.NewFilteredImagesCount = newFilteredImagesCount
647
+		}
648
+		res = append(res, container)
607 649
 	}
608
-	return res
650
+
651
+	return res, nil
609 652
 }
610 653
 
611 654
 func policyCommitMessage(us policy.Updates, cause update.Cause) string {

+ 2
- 2
daemon/daemon_test.go View File

@@ -139,7 +139,7 @@ func TestDaemon_ListImages(t *testing.T) {
139 139
 
140 140
 	// List all images for services
141 141
 	ss := update.ResourceSpec(update.ResourceSpecAll)
142
-	is, err := d.ListImages(ctx, ss)
142
+	is, err := d.ListImages(ctx, ss, v6.ListImagesOptions{})
143 143
 	if err != nil {
144 144
 		t.Fatalf("Error: %s", err.Error())
145 145
 	}
@@ -150,7 +150,7 @@ func TestDaemon_ListImages(t *testing.T) {
150 150
 
151 151
 	// List images for specific service
152 152
 	ss = update.ResourceSpec(svc)
153
-	is, err = d.ListImages(ctx, ss)
153
+	is, err = d.ListImages(ctx, ss, v6.ListImagesOptions{})
154 154
 	if err != nil {
155 155
 		t.Fatalf("Error: %s", err.Error())
156 156
 	}

+ 3
- 3
http/client/client.go View File

@@ -57,9 +57,9 @@ func (c *Client) ListServices(ctx context.Context, namespace string) ([]v6.Contr
57 57
 	return res, err
58 58
 }
59 59
 
60
-func (c *Client) ListImages(ctx context.Context, s update.ResourceSpec) ([]v6.ImageStatus, error) {
60
+func (c *Client) ListImages(ctx context.Context, s update.ResourceSpec, opts v6.ListImagesOptions) ([]v6.ImageStatus, error) {
61 61
 	var res []v6.ImageStatus
62
-	err := c.Get(ctx, &res, transport.ListImages, "service", string(s))
62
+	err := c.Get(ctx, &res, transport.ListImages, "service", string(s), "containerFields", strings.Join(opts.OverrideContainerFields, ","))
63 63
 	return res, err
64 64
 }
65 65
 
@@ -210,7 +210,7 @@ func (c *Client) executeRequest(req *http.Request) (*http.Response, error) {
210 210
 			if err := json.Unmarshal(body, &niceError); err != nil {
211 211
 				return resp, errors.Wrap(err, "decoding response body of error")
212 212
 			}
213
-			 // just in case it's JSON but not one of our own errors
213
+			// just in case it's JSON but not one of our own errors
214 214
 			if niceError.Err != nil {
215 215
 				return resp, &niceError
216 216
 			}

+ 18
- 3
http/daemon/server.go View File

@@ -3,6 +3,7 @@ package daemon
3 3
 import (
4 4
 	"encoding/json"
5 5
 	"net/http"
6
+	"strings"
6 7
 
7 8
 	"github.com/gorilla/mux"
8 9
 	"github.com/pkg/errors"
@@ -11,6 +12,7 @@ import (
11 12
 	"github.com/weaveworks/flux"
12 13
 
13 14
 	"github.com/weaveworks/flux/api"
15
+	"github.com/weaveworks/flux/api/v6"
14 16
 	transport "github.com/weaveworks/flux/http"
15 17
 	"github.com/weaveworks/flux/job"
16 18
 	fluxmetrics "github.com/weaveworks/flux/metrics"
@@ -91,14 +93,27 @@ func (s HTTPServer) SyncStatus(w http.ResponseWriter, r *http.Request) {
91 93
 }
92 94
 
93 95
 func (s HTTPServer) ListImages(w http.ResponseWriter, r *http.Request) {
94
-	service := mux.Vars(r)["service"]
96
+	queryValues := r.URL.Query()
97
+
98
+	// service - Select services to update.
99
+	service := queryValues.Get("service")
100
+	if service == "" {
101
+		service = string(update.ResourceSpecAll)
102
+	}
95 103
 	spec, err := update.ParseResourceSpec(service)
96 104
 	if err != nil {
97 105
 		transport.WriteError(w, r, http.StatusBadRequest, errors.Wrapf(err, "parsing service spec %q", service))
98 106
 		return
99 107
 	}
100 108
 
101
-	d, err := s.server.ListImages(r.Context(), spec)
109
+	// containerFields - Override which fields to return in the container struct.
110
+	var opts v6.ListImagesOptions
111
+	containerFields := queryValues.Get("containerFields")
112
+	if containerFields != "" {
113
+		opts.OverrideContainerFields = strings.Split(containerFields, ",")
114
+	}
115
+
116
+	d, err := s.server.ListImages(r.Context(), spec, opts)
102 117
 	if err != nil {
103 118
 		transport.ErrorResponse(w, r, err)
104 119
 		return
@@ -122,7 +137,7 @@ func (s HTTPServer) UpdateManifests(w http.ResponseWriter, r *http.Request) {
122 137
 }
123 138
 
124 139
 func (s HTTPServer) ListServices(w http.ResponseWriter, r *http.Request) {
125
-	namespace := mux.Vars(r)["namespace"]
140
+	namespace := r.URL.Query().Get("namespace")
126 141
 	res, err := s.server.ListServices(r.Context(), namespace)
127 142
 	if err != nil {
128 143
 		transport.ErrorResponse(w, r, err)

+ 2
- 2
http/transport.go View File

@@ -29,8 +29,8 @@ func DeprecateVersions(r *mux.Router, versions ...string) {
29 29
 func NewAPIRouter() *mux.Router {
30 30
 	r := mux.NewRouter()
31 31
 
32
-	r.NewRoute().Name(ListServices).Methods("GET").Path("/v6/services").Queries("namespace", "{namespace}") // optional namespace!
33
-	r.NewRoute().Name(ListImages).Methods("GET").Path("/v6/images").Queries("service", "{service}")
32
+	r.NewRoute().Name(ListServices).Methods("GET").Path("/v6/services")
33
+	r.NewRoute().Name(ListImages).Methods("GET").Path("/v6/images")
34 34
 
35 35
 	r.NewRoute().Name(UpdateManifests).Methods("POST").Path("/v9/update-manifests")
36 36
 	r.NewRoute().Name(JobStatus).Methods("GET").Path("/v6/jobs").Queries("id", "{id}")

+ 4
- 4
image/image.go View File

@@ -225,16 +225,16 @@ func (i Ref) WithNewTag(t string) Ref {
225 225
 // from its registry.
226 226
 type Info struct {
227 227
 	// the reference to this image; probably a tagged image name
228
-	ID Ref
228
+	ID Ref `json:",omitempty"`
229 229
 	// the digest we got when fetching the metadata, which will be
230 230
 	// different each time a manifest is uploaded for the reference
231
-	Digest string
231
+	Digest string `json:",omitempty"`
232 232
 	// an identifier for the *image* this reference points to; this
233 233
 	// will be the same for references that point at the same image
234 234
 	// (but does not necessarily equal Docker's image ID)
235
-	ImageID string
235
+	ImageID string `json:",omitempty"`
236 236
 	// the time at which the image pointed at was created
237
-	CreatedAt time.Time
237
+	CreatedAt time.Time `json:",omitempty"`
238 238
 }
239 239
 
240 240
 // MarshalJSON returns the Info value in JSON (as bytes). It is

+ 2
- 2
remote/logging.go View File

@@ -43,13 +43,13 @@ func (p *ErrorLoggingServer) ListServices(ctx context.Context, maybeNamespace st
43 43
 	return p.server.ListServices(ctx, maybeNamespace)
44 44
 }
45 45
 
46
-func (p *ErrorLoggingServer) ListImages(ctx context.Context, spec update.ResourceSpec) (_ []v6.ImageStatus, err error) {
46
+func (p *ErrorLoggingServer) ListImages(ctx context.Context, spec update.ResourceSpec, opts v6.ListImagesOptions) (_ []v6.ImageStatus, err error) {
47 47
 	defer func() {
48 48
 		if err != nil {
49 49
 			p.logger.Log("method", "ListImages", "error", err)
50 50
 		}
51 51
 	}()
52
-	return p.server.ListImages(ctx, spec)
52
+	return p.server.ListImages(ctx, spec, opts)
53 53
 }
54 54
 
55 55
 func (p *ErrorLoggingServer) JobStatus(ctx context.Context, jobID job.ID) (_ job.Status, err error) {

+ 2
- 2
remote/metrics.go View File

@@ -56,14 +56,14 @@ func (i *instrumentedServer) ListServices(ctx context.Context, namespace string)
56 56
 	return i.s.ListServices(ctx, namespace)
57 57
 }
58 58
 
59
-func (i *instrumentedServer) ListImages(ctx context.Context, spec update.ResourceSpec) (_ []v6.ImageStatus, err error) {
59
+func (i *instrumentedServer) ListImages(ctx context.Context, spec update.ResourceSpec, opts v6.ListImagesOptions) (_ []v6.ImageStatus, err error) {
60 60
 	defer func(begin time.Time) {
61 61
 		requestDuration.With(
62 62
 			fluxmetrics.LabelMethod, "ListImages",
63 63
 			fluxmetrics.LabelSuccess, fmt.Sprint(err == nil),
64 64
 		).Observe(time.Since(begin).Seconds())
65 65
 	}(time.Now())
66
-	return i.s.ListImages(ctx, spec)
66
+	return i.s.ListImages(ctx, spec, opts)
67 67
 }
68 68
 
69 69
 func (i *instrumentedServer) UpdateManifests(ctx context.Context, spec update.Spec) (_ job.ID, err error) {

+ 3
- 3
remote/mock.go View File

@@ -65,7 +65,7 @@ func (p *MockServer) ListServices(ctx context.Context, ns string) ([]v6.Controll
65 65
 	return p.ListServicesAnswer, p.ListServicesError
66 66
 }
67 67
 
68
-func (p *MockServer) ListImages(context.Context, update.ResourceSpec) ([]v6.ImageStatus, error) {
68
+func (p *MockServer) ListImages(context.Context, update.ResourceSpec, v6.ListImagesOptions) ([]v6.ImageStatus, error) {
69 69
 	return p.ListImagesAnswer, p.ListImagesError
70 70
 }
71 71
 
@@ -194,7 +194,7 @@ func ServerTestBattery(t *testing.T, wrap func(mock api.UpstreamServer) api.Upst
194 194
 		t.Error("expected error from ListServices, got nil")
195 195
 	}
196 196
 
197
-	ims, err := client.ListImages(ctx, update.ResourceSpecAll)
197
+	ims, err := client.ListImages(ctx, update.ResourceSpecAll, v6.ListImagesOptions{})
198 198
 	if err != nil {
199 199
 		t.Error(err)
200 200
 	}
@@ -202,7 +202,7 @@ func ServerTestBattery(t *testing.T, wrap func(mock api.UpstreamServer) api.Upst
202 202
 		t.Error(fmt.Errorf("expected:\n%#v\ngot:\n%#v", mock.ListImagesAnswer, ims))
203 203
 	}
204 204
 	mock.ListImagesError = fmt.Errorf("list images error")
205
-	if _, err = client.ListImages(ctx, update.ResourceSpecAll); err == nil {
205
+	if _, err = client.ListImages(ctx, update.ResourceSpecAll, v6.ListImagesOptions{}); err == nil {
206 206
 		t.Error("expected error from ListImages, got nil")
207 207
 	}
208 208
 

+ 1
- 1
remote/rpc/baseclient.go View File

@@ -33,7 +33,7 @@ func (bc baseClient) ListServices(context.Context, string) ([]v6.ControllerStatu
33 33
 	return nil, remote.UpgradeNeededError(errors.New("ListServices method not implemented"))
34 34
 }
35 35
 
36
-func (bc baseClient) ListImages(context.Context, update.ResourceSpec) ([]v6.ImageStatus, error) {
36
+func (bc baseClient) ListImages(context.Context, update.ResourceSpec, v6.ListImagesOptions) ([]v6.ImageStatus, error) {
37 37
 	return nil, remote.UpgradeNeededError(errors.New("ListImages method not implemented"))
38 38
 }
39 39
 

+ 1
- 1
remote/rpc/clientV6.go View File

@@ -99,7 +99,7 @@ func (p *RPCClientV6) ListServices(ctx context.Context, namespace string) ([]v6.
99 99
 	return services, err
100 100
 }
101 101
 
102
-func (p *RPCClientV6) ListImages(ctx context.Context, spec update.ResourceSpec) ([]v6.ImageStatus, error) {
102
+func (p *RPCClientV6) ListImages(ctx context.Context, spec update.ResourceSpec, opts v6.ListImagesOptions) ([]v6.ImageStatus, error) {
103 103
 	var images []v6.ImageStatus
104 104
 	if err := requireServiceSpecKinds(spec, supportedKindsV6); err != nil {
105 105
 		return images, remote.UpgradeNeededError(err)

+ 1
- 1
remote/rpc/clientV7.go View File

@@ -62,7 +62,7 @@ func (p *RPCClientV7) ListServices(ctx context.Context, namespace string) ([]v6.
62 62
 	return resp.Result, err
63 63
 }
64 64
 
65
-func (p *RPCClientV7) ListImages(ctx context.Context, spec update.ResourceSpec) ([]v6.ImageStatus, error) {
65
+func (p *RPCClientV7) ListImages(ctx context.Context, spec update.ResourceSpec, opts v6.ListImagesOptions) ([]v6.ImageStatus, error) {
66 66
 	var resp ListImagesResponse
67 67
 	if err := requireServiceSpecKinds(spec, supportedKindsV7); err != nil {
68 68
 		return resp.Result, remote.UpgradeNeededError(err)

+ 1
- 1
remote/rpc/clientV8.go View File

@@ -32,7 +32,7 @@ func NewClientV8(conn io.ReadWriteCloser) *RPCClientV8 {
32 32
 	return &RPCClientV8{NewClientV7(conn)}
33 33
 }
34 34
 
35
-func (p *RPCClientV8) ListImages(ctx context.Context, spec update.ResourceSpec) ([]v6.ImageStatus, error) {
35
+func (p *RPCClientV8) ListImages(ctx context.Context, spec update.ResourceSpec, opts v6.ListImagesOptions) ([]v6.ImageStatus, error) {
36 36
 	var resp ListImagesResponse
37 37
 	if err := requireServiceSpecKinds(spec, supportedKindsV8); err != nil {
38 38
 		return resp.Result, remote.UnsupportedResourceKind(err)

+ 1
- 1
remote/rpc/server.go View File

@@ -89,7 +89,7 @@ type ListImagesResponse struct {
89 89
 }
90 90
 
91 91
 func (p *RPCServer) ListImages(spec update.ResourceSpec, resp *ListImagesResponse) error {
92
-	v, err := p.s.ListImages(context.Background(), spec)
92
+	v, err := p.s.ListImages(context.Background(), spec, v6.ListImagesOptions{})
93 93
 	resp.Result = v
94 94
 	if err != nil {
95 95
 		if err, ok := errors.Cause(err).(*fluxerr.Error); ok {

Loading…
Cancel
Save