@@ -3,6 +3,7 @@ package etcd
3
3
import (
4
4
"context"
5
5
6
+ "k8s.io/apimachinery/pkg/api/errors"
6
7
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
7
8
"k8s.io/apimachinery/pkg/runtime"
8
9
"k8s.io/apiserver/pkg/registry/generic"
@@ -47,7 +48,8 @@ func NewREST(
47
48
subjectAccessReviewRegistry authorizationclient.SubjectAccessReviewInterface ,
48
49
limitVerifier imageadmission.LimitVerifier ,
49
50
registryWhitelister whitelist.RegistryWhitelister ,
50
- ) (* REST , * StatusREST , * InternalREST , error ) {
51
+ imageLayerIndex ImageLayerIndex ,
52
+ ) (* REST , * LayersREST , * StatusREST , * InternalREST , error ) {
51
53
store := registry.Store {
52
54
NewFunc : func () runtime.Object { return & imageapi.ImageStream {} },
53
55
NewListFunc : func () runtime.Object { return & imageapi.ImageStreamList {} },
@@ -72,9 +74,11 @@ func NewREST(
72
74
AttrFunc : storage .AttrFunc (storage .DefaultNamespaceScopedAttr ).WithFieldMutation (imageapi .ImageStreamSelector ),
73
75
}
74
76
if err := store .CompleteWithOptions (options ); err != nil {
75
- return nil , nil , nil , err
77
+ return nil , nil , nil , nil , err
76
78
}
77
79
80
+ layersREST := & LayersREST {index : imageLayerIndex , store : & store }
81
+
78
82
statusStrategy := imagestream .NewStatusStrategy (strategy )
79
83
statusStore := store
80
84
statusStore .Decorator = nil
@@ -89,7 +93,7 @@ func NewREST(
89
93
internalStore .UpdateStrategy = internalStrategy
90
94
91
95
internalREST := & InternalREST {store : & internalStore }
92
- return rest , statusREST , internalREST , nil
96
+ return rest , layersREST , statusREST , internalREST , nil
93
97
}
94
98
95
99
// StatusREST implements the REST endpoint for changing the status of an image stream.
@@ -139,6 +143,72 @@ func (r *InternalREST) Update(ctx context.Context, name string, objInfo rest.Upd
139
143
return r .store .Update (ctx , name , objInfo , createValidation , updateValidation )
140
144
}
141
145
146
+ // LayersREST implements the REST endpoint for changing both the spec and status of an image stream.
147
+ type LayersREST struct {
148
+ store * registry.Store
149
+ index ImageLayerIndex
150
+ }
151
+
152
+ var _ rest.Getter = & LayersREST {}
153
+
154
+ func (r * LayersREST ) New () runtime.Object {
155
+ return & imageapi.ImageStreamLayers {}
156
+ }
157
+
158
+ // Get returns the layers for an image stream.
159
+ func (r * LayersREST ) Get (ctx context.Context , name string , options * metav1.GetOptions ) (runtime.Object , error ) {
160
+ if ! r .index .HasSynced () {
161
+ return nil , errors .NewServerTimeout (r .store .DefaultQualifiedResource , "get" , 2 )
162
+ }
163
+ obj , err := r .store .Get (ctx , name , options )
164
+ if err != nil {
165
+ return nil , err
166
+ }
167
+ is := obj .(* imageapi.ImageStream )
168
+ isl := & imageapi.ImageStreamLayers {
169
+ ObjectMeta : is .ObjectMeta ,
170
+ Blobs : make (map [string ]imageapi.ImageLayerData ),
171
+ Images : make (map [string ]imageapi.ImageBlobReferences ),
172
+ }
173
+
174
+ for _ , status := range is .Status .Tags {
175
+ for _ , item := range status .Items {
176
+ if len (item .Image ) == 0 {
177
+ continue
178
+ }
179
+
180
+ obj , _ , _ := r .index .GetByKey (item .Image )
181
+ entry , ok := obj .(* ImageLayers )
182
+ if ! ok {
183
+ continue
184
+ }
185
+
186
+ if _ , ok := isl .Images [item .Image ]; ! ok {
187
+ var reference imageapi.ImageBlobReferences
188
+ for _ , layer := range entry .Layers {
189
+ reference .Layers = append (reference .Layers , layer .Name )
190
+ if _ , ok := isl .Blobs [layer .Name ]; ! ok {
191
+ isl .Blobs [layer .Name ] = imageapi.ImageLayerData {LayerSize : & layer .LayerSize , MediaType : layer .MediaType }
192
+ }
193
+ }
194
+ if blob := entry .Manifest ; blob != nil {
195
+ reference .Manifest = & blob .Name
196
+ if _ , ok := isl .Blobs [blob .Name ]; ! ok {
197
+ if blob .LayerSize == 0 {
198
+ // only send media type since we don't the size of the manifest
199
+ isl .Blobs [blob .Name ] = imageapi.ImageLayerData {MediaType : blob .MediaType }
200
+ } else {
201
+ isl .Blobs [blob .Name ] = imageapi.ImageLayerData {LayerSize : & blob .LayerSize , MediaType : blob .MediaType }
202
+ }
203
+ }
204
+ }
205
+ isl .Images [item .Image ] = reference
206
+ }
207
+ }
208
+ }
209
+ return isl , nil
210
+ }
211
+
142
212
// LegacyREST allows us to wrap and alter some behavior
143
213
type LegacyREST struct {
144
214
* REST
0 commit comments