@@ -11,23 +11,20 @@ import (
1111 "github.com/golang/glog"
1212 godigest "github.com/opencontainers/go-digest"
1313
14+ imagev1 "github.com/openshift/api/image/v1"
1415 imageapi "github.com/openshift/origin/pkg/image/apis/image"
1516 "github.com/openshift/origin/pkg/image/apis/image/docker10"
1617)
1718
18- func fillImageLayers (image * imageapi.Image , manifest docker10.DockerImageManifest ) error {
19- if len (image .DockerImageLayers ) != 0 {
20- // DockerImageLayers is already filled by the registry.
21- return nil
22- }
23-
19+ func getImageLayers (manifest docker10.DockerImageManifest ) ([]imageapi.ImageLayer , error ) {
20+ var imageLayers []imageapi.ImageLayer
2421 switch manifest .SchemaVersion {
2522 case 1 :
2623 if len (manifest .History ) != len (manifest .FSLayers ) {
27- return fmt .Errorf ("the image %s (%s) has mismatched history and fslayer cardinality (%d != %d)" , image . Name , image . DockerImageReference , len (manifest .History ), len (manifest .FSLayers ))
24+ return nil , fmt .Errorf ("mismatched history and fslayer cardinality (%d != %d)" , len (manifest .History ), len (manifest .FSLayers ))
2825 }
2926
30- image . DockerImageLayers = make ([]imageapi.ImageLayer , len (manifest .FSLayers ))
27+ imageLayers = make ([]imageapi.ImageLayer , len (manifest .FSLayers ))
3128 for i , obj := range manifest .History {
3229 layer := manifest .FSLayers [i ]
3330
@@ -42,29 +39,80 @@ func fillImageLayers(image *imageapi.Image, manifest docker10.DockerImageManifes
4239 // in order from the oldest to the youngest.
4340 revidx := (len (manifest .History ) - 1 ) - i // n-1, n-2, ..., 1, 0
4441
45- image . DockerImageLayers [revidx ].Name = layer .DockerBlobSum
46- image . DockerImageLayers [revidx ].LayerSize = size .Size
47- image . DockerImageLayers [revidx ].MediaType = schema1 .MediaTypeManifestLayer
42+ imageLayers [revidx ].Name = layer .DockerBlobSum
43+ imageLayers [revidx ].LayerSize = size .Size
44+ imageLayers [revidx ].MediaType = schema1 .MediaTypeManifestLayer
4845 }
4946 case 2 :
5047 // The layer list is ordered starting from the base image (opposite order of schema1).
5148 // So, we do not need to change the order of layers.
52- image . DockerImageLayers = make ([]imageapi.ImageLayer , len (manifest .Layers ))
49+ imageLayers = make ([]imageapi.ImageLayer , len (manifest .Layers ))
5350 for i , layer := range manifest .Layers {
54- image . DockerImageLayers [i ].Name = layer .Digest
55- image . DockerImageLayers [i ].LayerSize = layer .Size
56- image . DockerImageLayers [i ].MediaType = layer .MediaType
51+ imageLayers [i ].Name = layer .Digest
52+ imageLayers [i ].LayerSize = layer .Size
53+ imageLayers [i ].MediaType = layer .MediaType
5754 }
5855 default :
59- return fmt .Errorf ("unrecognized Docker image manifest schema %d for %q (%s) " , manifest .SchemaVersion , image . Name , image . DockerImageReference )
56+ return nil , fmt .Errorf ("unrecognized Docker image manifest schema %d" , manifest .SchemaVersion )
6057 }
6158
62- if image .Annotations == nil {
63- image .Annotations = map [string ]string {}
59+ return imageLayers , nil
60+ }
61+
62+ // reorderImageLayers mutates the given image. It reorders the layers in ascending order.
63+ // Ascending order matches the order of layers in schema 2. Schema 1 has reversed (descending) order of layers.
64+ func reorderImageLayers (imageLayers []imageapi.ImageLayer , layersOrder , imageManifestMediaType string ) bool {
65+ if imageLayers == nil || len (imageLayers ) == 0 {
66+ return false
6467 }
65- image .Annotations [imageapi .DockerImageLayersOrderAnnotation ] = imageapi .DockerImageLayersOrderAscending
6668
67- return nil
69+ if layersOrder == "" {
70+ switch imageManifestMediaType {
71+ case schema1 .MediaTypeManifest , schema1 .MediaTypeSignedManifest :
72+ layersOrder = imageapi .DockerImageLayersOrderAscending
73+ case schema2 .MediaTypeManifest :
74+ layersOrder = imageapi .DockerImageLayersOrderDescending
75+ default :
76+ return false
77+ }
78+ }
79+
80+ if layersOrder == imageapi .DockerImageLayersOrderDescending {
81+ // reverse order of the layers (lowest = 0, highest = i)
82+ for i , j := 0 , len (imageLayers )- 1 ; i < j ; i , j = i + 1 , j - 1 {
83+ imageLayers [i ], imageLayers [j ] = imageLayers [j ], imageLayers [i ]
84+ }
85+ }
86+
87+ return true
88+ }
89+
90+ func convertImageLayers (imageLayers []imagev1.ImageLayer ) []imageapi.ImageLayer {
91+ if imageLayers == nil {
92+ return nil
93+ }
94+
95+ result := make ([]imageapi.ImageLayer , len (imageLayers ))
96+ for i := range imageLayers {
97+ result [i ].MediaType = imageLayers [i ].MediaType
98+ result [i ].Name = imageLayers [i ].Name
99+ result [i ].LayerSize = imageLayers [i ].LayerSize
100+ }
101+ return result
102+ }
103+
104+ func GetImageMetadata (image * imagev1.Image ) (imageapi.DockerImage , error ) {
105+ if len (image .DockerImageManifest ) == 0 {
106+ return imageapi.DockerImage {}, nil
107+ }
108+
109+ imageLayers := convertImageLayers (image .DockerImageLayers )
110+ reorderImageLayers (imageLayers , image .Annotations [imageapi .DockerImageLayersOrderAnnotation ], image .DockerImageManifestMediaType )
111+
112+ _ , imageMetadata , _ , _ , err := getImageMetadata (image .Name , image .DockerImageReference ,
113+ image .DockerImageManifest , image .DockerImageConfig , imageLayers )
114+ return imageMetadata , err
115+
68116}
69117
70118// ImageWithMetadata mutates the given image. It parses raw DockerImageManifest data stored in the image and
@@ -74,110 +122,109 @@ func ImageWithMetadata(image *imageapi.Image) error {
74122 return nil
75123 }
76124
77- ReorderImageLayers (image )
125+ if ok := reorderImageLayers (image .DockerImageLayers ,
126+ image .Annotations [imageapi .DockerImageLayersOrderAnnotation ], image .DockerImageManifestMediaType ); ok {
127+ if image .Annotations == nil {
128+ image .Annotations = map [string ]string {}
129+ }
130+ image .Annotations [imageapi .DockerImageLayersOrderAnnotation ] = imageapi .DockerImageLayersOrderAscending
131+ }
78132
79133 if len (image .DockerImageLayers ) > 0 && image .DockerImageMetadata .Size > 0 && len (image .DockerImageManifestMediaType ) > 0 {
80134 glog .V (5 ).Infof ("Image metadata already filled for %s" , image .Name )
81135 return nil
82136 }
137+ imageManifestMediaType , imageMetadata , imageLayers , orderAscending , err := getImageMetadata (image .Name , image .DockerImageReference ,
138+ image .DockerImageManifest , image .DockerImageConfig , image .DockerImageLayers )
139+ if err != nil {
140+ return err
141+ }
142+ image .DockerImageManifestMediaType = imageManifestMediaType
143+ image .DockerImageMetadata = imageMetadata
144+ image .DockerImageLayers = imageLayers
145+ if orderAscending {
146+ if image .Annotations == nil {
147+ image .Annotations = map [string ]string {}
148+ }
149+ image .Annotations [imageapi .DockerImageLayersOrderAnnotation ] = imageapi .DockerImageLayersOrderAscending
150+ }
83151
152+ return nil
153+ }
154+
155+ func getImageMetadata (imageName , imageReference , imageManifest , imageConfig string ,
156+ imageLayers []imageapi.ImageLayer ) (string , imageapi.DockerImage , []imageapi.ImageLayer , bool , error ) {
84157 manifest := docker10.DockerImageManifest {}
85- if err := json .Unmarshal ([]byte (image . DockerImageManifest ), & manifest ); err != nil {
86- return err
158+ if err := json .Unmarshal ([]byte (imageManifest ), & manifest ); err != nil {
159+ return "" , imageapi. DockerImage {}, []imageapi. ImageLayer {}, false , err
87160 }
88161
89- err := fillImageLayers (image , manifest )
90- if err != nil {
91- return err
162+ var err error
163+ var orderAscending bool
164+ if len (imageLayers ) == 0 {
165+ imageLayers , err = getImageLayers (manifest )
166+ if err != nil {
167+ return "" , imageapi.DockerImage {}, []imageapi.ImageLayer {}, false , fmt .Errorf ("the image %s (%s) failed reading layers: %v" , imageName , imageReference , err )
168+ }
169+ orderAscending = true
92170 }
93171
172+ var imageManifestMediaType string
173+ var imageMetadata imageapi.DockerImage
94174 switch manifest .SchemaVersion {
95175 case 1 :
96- image . DockerImageManifestMediaType = schema1 .MediaTypeManifest
176+ imageManifestMediaType = schema1 .MediaTypeManifest
97177
98178 if len (manifest .History ) == 0 {
99179 // It should never have an empty history, but just in case.
100- return fmt .Errorf ("the image %s (%s) has a schema 1 manifest, but it doesn't have history" , image . Name , image . DockerImageReference )
180+ return "" , imageapi. DockerImage {}, []imageapi. ImageLayer {}, false , fmt .Errorf ("the image %s (%s) has a schema 1 manifest, but it doesn't have history" , imageName , imageReference )
101181 }
102182
103183 v1Metadata := docker10.DockerV1CompatibilityImage {}
104184 if err := json .Unmarshal ([]byte (manifest .History [0 ].DockerV1Compatibility ), & v1Metadata ); err != nil {
105- return err
185+ return "" , imageapi. DockerImage {}, []imageapi. ImageLayer {}, false , err
106186 }
107187
108- if err := imageapi .Convert_compatibility_to_api_DockerImage (& v1Metadata , & image . DockerImageMetadata ); err != nil {
109- return err
188+ if err := imageapi .Convert_compatibility_to_api_DockerImage (& v1Metadata , & imageMetadata ); err != nil {
189+ return "" , imageapi. DockerImage {}, []imageapi. ImageLayer {}, false , err
110190 }
111191 case 2 :
112- image . DockerImageManifestMediaType = schema2 .MediaTypeManifest
192+ imageManifestMediaType = schema2 .MediaTypeManifest
113193
114- if len (image . DockerImageConfig ) == 0 {
115- return fmt .Errorf ("dockerImageConfig must not be empty for manifest schema 2" )
194+ if len (imageConfig ) == 0 {
195+ return "" , imageapi. DockerImage {}, []imageapi. ImageLayer {}, false , fmt .Errorf ("dockerImageConfig must not be empty for manifest schema 2" )
116196 }
117197
118198 config := docker10.DockerImageConfig {}
119- if err := json .Unmarshal ([]byte (image . DockerImageConfig ), & config ); err != nil {
120- return fmt .Errorf ("failed to parse dockerImageConfig: %v" , err )
199+ if err := json .Unmarshal ([]byte (imageConfig ), & config ); err != nil {
200+ return "" , imageapi. DockerImage {}, []imageapi. ImageLayer {}, false , fmt .Errorf ("failed to parse dockerImageConfig: %v" , err )
121201 }
122202
123- if err := imageapi .Convert_imageconfig_to_api_DockerImage (& config , & image . DockerImageMetadata ); err != nil {
124- return err
203+ if err := imageapi .Convert_imageconfig_to_api_DockerImage (& config , & imageMetadata ); err != nil {
204+ return "" , imageapi. DockerImage {}, []imageapi. ImageLayer {}, false , err
125205 }
126- image . DockerImageMetadata .ID = manifest .Config .Digest
206+ imageMetadata .ID = manifest .Config .Digest
127207
128208 default :
129- return fmt .Errorf ("unrecognized Docker image manifest schema %d for %q (%s)" , manifest .SchemaVersion , image . Name , image . DockerImageReference )
209+ return "" , imageapi. DockerImage {}, []imageapi. ImageLayer {}, false , fmt .Errorf ("unrecognized Docker image manifest schema %d for %q (%s)" , manifest .SchemaVersion , imageName , imageReference )
130210 }
131211
132212 layerSet := sets .NewString ()
133213 if manifest .SchemaVersion == 2 {
134214 layerSet .Insert (manifest .Config .Digest )
135- image . DockerImageMetadata . Size = int64 (len (image . DockerImageConfig ))
215+ imageMetadata . Size = int64 (len (imageConfig ))
136216 } else {
137- image . DockerImageMetadata .Size = 0
217+ imageMetadata .Size = 0
138218 }
139- for _ , layer := range image . DockerImageLayers {
219+ for _ , layer := range imageLayers {
140220 if layerSet .Has (layer .Name ) {
141221 continue
142222 }
143223 layerSet .Insert (layer .Name )
144- image .DockerImageMetadata .Size += layer .LayerSize
145- }
146-
147- return nil
148- }
149-
150- // ReorderImageLayers mutates the given image. It reorders the layers in ascending order.
151- // Ascending order matches the order of layers in schema 2. Schema 1 has reversed (descending) order of layers.
152- func ReorderImageLayers (image * imageapi.Image ) {
153- if len (image .DockerImageLayers ) == 0 {
154- return
155- }
156-
157- layersOrder , ok := image .Annotations [imageapi .DockerImageLayersOrderAnnotation ]
158- if ! ok {
159- switch image .DockerImageManifestMediaType {
160- case schema1 .MediaTypeManifest , schema1 .MediaTypeSignedManifest :
161- layersOrder = imageapi .DockerImageLayersOrderAscending
162- case schema2 .MediaTypeManifest :
163- layersOrder = imageapi .DockerImageLayersOrderDescending
164- default :
165- return
166- }
167- }
168-
169- if layersOrder == imageapi .DockerImageLayersOrderDescending {
170- // reverse order of the layers (lowest = 0, highest = i)
171- for i , j := 0 , len (image .DockerImageLayers )- 1 ; i < j ; i , j = i + 1 , j - 1 {
172- image .DockerImageLayers [i ], image .DockerImageLayers [j ] = image .DockerImageLayers [j ], image .DockerImageLayers [i ]
173- }
174- }
175-
176- if image .Annotations == nil {
177- image .Annotations = map [string ]string {}
224+ imageMetadata .Size += layer .LayerSize
178225 }
179226
180- image . Annotations [ imageapi . DockerImageLayersOrderAnnotation ] = imageapi . DockerImageLayersOrderAscending
227+ return imageManifestMediaType , imageMetadata , imageLayers , orderAscending , nil
181228}
182229
183230// ManifestMatchesImage returns true if the provided manifest matches the name of the image.
0 commit comments