1
1
package etcd
2
2
3
3
import (
4
+ "k8s.io/apimachinery/pkg/api/errors"
4
5
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
5
6
"k8s.io/apimachinery/pkg/runtime"
6
7
apirequest "k8s.io/apiserver/pkg/endpoints/request"
@@ -46,7 +47,8 @@ func NewREST(
46
47
subjectAccessReviewRegistry authorizationclient.SubjectAccessReviewInterface ,
47
48
limitVerifier imageadmission.LimitVerifier ,
48
49
registryWhitelister whitelist.RegistryWhitelister ,
49
- ) (* REST , * StatusREST , * InternalREST , error ) {
50
+ imageLayerIndex ImageLayerIndex ,
51
+ ) (* REST , * LayersREST , * StatusREST , * InternalREST , error ) {
50
52
store := registry.Store {
51
53
NewFunc : func () runtime.Object { return & imageapi.ImageStream {} },
52
54
NewListFunc : func () runtime.Object { return & imageapi.ImageStreamList {} },
@@ -71,9 +73,11 @@ func NewREST(
71
73
AttrFunc : storage .AttrFunc (storage .DefaultNamespaceScopedAttr ).WithFieldMutation (imageapi .ImageStreamSelector ),
72
74
}
73
75
if err := store .CompleteWithOptions (options ); err != nil {
74
- return nil , nil , nil , err
76
+ return nil , nil , nil , nil , err
75
77
}
76
78
79
+ layersREST := & LayersREST {index : imageLayerIndex , store : & store }
80
+
77
81
statusStrategy := imagestream .NewStatusStrategy (strategy )
78
82
statusStore := store
79
83
statusStore .Decorator = nil
@@ -88,7 +92,7 @@ func NewREST(
88
92
internalStore .UpdateStrategy = internalStrategy
89
93
90
94
internalREST := & InternalREST {store : & internalStore }
91
- return rest , statusREST , internalREST , nil
95
+ return rest , layersREST , statusREST , internalREST , nil
92
96
}
93
97
94
98
// StatusREST implements the REST endpoint for changing the status of an image stream.
@@ -138,6 +142,116 @@ func (r *InternalREST) Update(ctx apirequest.Context, name string, objInfo rest.
138
142
return r .store .Update (ctx , name , objInfo , createValidation , updateValidation )
139
143
}
140
144
145
+ // LayersREST implements the REST endpoint for changing both the spec and status of an image stream.
146
+ type LayersREST struct {
147
+ store * registry.Store
148
+ index ImageLayerIndex
149
+ }
150
+
151
+ var _ rest.Getter = & LayersREST {}
152
+
153
+ func (r * LayersREST ) New () runtime.Object {
154
+ return & imageapi.ImageStreamLayers {}
155
+ }
156
+
157
+ // Get returns the layers for an image stream.
158
+ func (r * LayersREST ) Get (ctx apirequest.Context , name string , options * metav1.GetOptions ) (runtime.Object , error ) {
159
+ if ! r .index .HasSynced () {
160
+ return nil , errors .NewServerTimeout (r .store .DefaultQualifiedResource , "get" , 2 )
161
+ }
162
+ obj , err := r .store .Get (ctx , name , options )
163
+ if err != nil {
164
+ return nil , err
165
+ }
166
+ is := obj .(* imageapi.ImageStream )
167
+ isl := & imageapi.ImageStreamLayers {
168
+ ObjectMeta : is .ObjectMeta ,
169
+ }
170
+
171
+ existing , existingBlobs := make (map [string ]int ), make (map [string ]int )
172
+ existingImage , existingImageBlobs := make (map [string ][]int ), make (map [string ][]int )
173
+ for tag , status := range is .Status .Tags {
174
+ var seen map [string ]struct {}
175
+ for _ , item := range status .Items {
176
+ if len (item .Image ) == 0 {
177
+ continue
178
+ }
179
+ if _ , ok := seen [item .Image ]; ok {
180
+ continue
181
+ }
182
+ if len (status .Items ) > 1 {
183
+ if seen == nil {
184
+ seen = make (map [string ]struct {})
185
+ }
186
+ seen [item .Image ] = struct {}{}
187
+ }
188
+ if indices , ok := existingImage [item .Image ]; ok {
189
+ for _ , index := range indices {
190
+ ref := & isl .Layers [index ]
191
+ ref .Tags = append (ref .Tags , tag )
192
+ }
193
+ if indices , ok := existingImageBlobs [item .Image ]; ok {
194
+ for _ , index := range indices {
195
+ ref := & isl .Blobs [index ]
196
+ ref .Tags = append (ref .Tags , tag )
197
+ }
198
+ }
199
+ continue
200
+ }
201
+
202
+ obj , _ , _ := r .index .GetIndexer ().GetByKey (item .Image )
203
+ entry , ok := obj .(* ImageLayers )
204
+ if ! ok {
205
+ continue
206
+ }
207
+ indices := make ([]int , 0 , len (entry .Layers ))
208
+ for _ , layer := range entry .Layers {
209
+ if index , ok := existing [layer .Name ]; ok {
210
+ ref := & isl .Layers [index ]
211
+ ref .Tags = append (ref .Tags , tag )
212
+ ref .ImageIDs = append (ref .ImageIDs , item .Image )
213
+ indices = append (indices , index )
214
+ continue
215
+ }
216
+ index := len (isl .Layers )
217
+ existing [layer .Name ] = index
218
+ indices = append (indices , index )
219
+ isl .Layers = append (isl .Layers , imageapi.ImageLayerReference {
220
+ Name : layer .Name ,
221
+ LayerSize : layer .LayerSize ,
222
+ MediaType : layer .MediaType ,
223
+ Tags : []string {tag },
224
+ ImageIDs : []string {item .Image },
225
+ })
226
+ }
227
+ existingImage [item .Image ] = indices
228
+
229
+ blobIndices := make ([]int , 0 , 1 )
230
+ if layer := entry .Manifest ; layer != nil {
231
+ if index , ok := existingBlobs [layer .Name ]; ok {
232
+ ref := & isl .Blobs [index ]
233
+ ref .Tags = append (ref .Tags , tag )
234
+ ref .ImageIDs = append (ref .ImageIDs , item .Image )
235
+ blobIndices = append (blobIndices , index )
236
+ continue
237
+ }
238
+ index := len (isl .Blobs )
239
+ existingBlobs [layer .Name ] = index
240
+ blobIndices = append (blobIndices , index )
241
+ isl .Blobs = append (isl .Blobs , imageapi.ImageLayerReference {
242
+ Name : layer .Name ,
243
+ LayerSize : layer .LayerSize ,
244
+ MediaType : layer .MediaType ,
245
+ Tags : []string {tag },
246
+ ImageIDs : []string {item .Image },
247
+ })
248
+ }
249
+ existingImageBlobs [item .Image ] = blobIndices
250
+ }
251
+ }
252
+ return isl , nil
253
+ }
254
+
141
255
// LegacyREST allows us to wrap and alter some behavior
142
256
type LegacyREST struct {
143
257
* REST
0 commit comments