@@ -3,6 +3,7 @@ package etcd
33import (
44 "context"
55
6+ "k8s.io/apimachinery/pkg/api/errors"
67 metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
78 "k8s.io/apimachinery/pkg/runtime"
89 "k8s.io/apiserver/pkg/registry/generic"
@@ -47,7 +48,8 @@ func NewREST(
4748 subjectAccessReviewRegistry authorizationclient.SubjectAccessReviewInterface ,
4849 limitVerifier imageadmission.LimitVerifier ,
4950 registryWhitelister whitelist.RegistryWhitelister ,
50- ) (* REST , * StatusREST , * InternalREST , error ) {
51+ imageLayerIndex ImageLayerIndex ,
52+ ) (* REST , * LayersREST , * StatusREST , * InternalREST , error ) {
5153 store := registry.Store {
5254 NewFunc : func () runtime.Object { return & imageapi.ImageStream {} },
5355 NewListFunc : func () runtime.Object { return & imageapi.ImageStreamList {} },
@@ -72,9 +74,11 @@ func NewREST(
7274 AttrFunc : storage .AttrFunc (storage .DefaultNamespaceScopedAttr ).WithFieldMutation (imageapi .ImageStreamSelector ),
7375 }
7476 if err := store .CompleteWithOptions (options ); err != nil {
75- return nil , nil , nil , err
77+ return nil , nil , nil , nil , err
7678 }
7779
80+ layersREST := & LayersREST {index : imageLayerIndex , store : & store }
81+
7882 statusStrategy := imagestream .NewStatusStrategy (strategy )
7983 statusStore := store
8084 statusStore .Decorator = nil
@@ -89,7 +93,7 @@ func NewREST(
8993 internalStore .UpdateStrategy = internalStrategy
9094
9195 internalREST := & InternalREST {store : & internalStore }
92- return rest , statusREST , internalREST , nil
96+ return rest , layersREST , statusREST , internalREST , nil
9397}
9498
9599// 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
139143 return r .store .Update (ctx , name , objInfo , createValidation , updateValidation )
140144}
141145
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+
142212// LegacyREST allows us to wrap and alter some behavior
143213type LegacyREST struct {
144214 * REST
0 commit comments