@@ -5,29 +5,47 @@ import (
55 "io"
66 "net"
77 "net/http"
8+ "os"
89 "strings"
910
1011 v1 "github.com/google/go-containerregistry/pkg/v1"
12+ "github.com/google/go-containerregistry/pkg/v1/layout"
1113 "github.com/google/go-containerregistry/pkg/v1/tarball"
14+ "github.com/google/go-containerregistry/pkg/v1/types"
1215 "github.com/julienschmidt/httprouter"
1316 "github.com/sirupsen/logrus"
1417)
1518
1619type ImageArg struct {
17- Image v1.Image
18- ArgName string
20+ Index v1.ImageIndex
21+ Image v1.Image
22+ BuildArgName string
1923}
2024type LocalRegistry map [string ]ImageArg
2125
2226func LoadRegistry (imagePaths map [string ]string ) (LocalRegistry , error ) {
2327 images := LocalRegistry {}
2428 for name , path := range imagePaths {
25- image , err := tarball . ImageFromPath (path , nil )
29+ stat , err := os . Stat (path )
2630 if err != nil {
27- return nil , fmt .Errorf ("image from path: %w" , err )
31+ return nil , fmt .Errorf ("error inspecting path: %w" , err )
2832 }
2933
30- images [strings .ToLower (name )] = ImageArg {Image : image , ArgName : name }
34+ var index v1.ImageIndex
35+ var image v1.Image
36+ if stat .IsDir () {
37+ index , err = layout .ImageIndexFromPath (path )
38+ if err != nil {
39+ return nil , fmt .Errorf ("image from path: %w" , err )
40+ }
41+ } else {
42+ image , err = tarball .ImageFromPath (path , nil )
43+ if err != nil {
44+ return nil , fmt .Errorf ("image from tarball: %w" , err )
45+ }
46+ }
47+
48+ images [strings .ToLower (name )] = ImageArg {Index : index , Image : image , BuildArgName : name }
3149 }
3250
3351 return images , nil
@@ -63,7 +81,7 @@ func ServeRegistry(reg LocalRegistry) (string, error) {
6381func (registry LocalRegistry ) BuildArgs (port string ) []string {
6482 var buildArgs []string
6583 for name , image := range registry {
66- buildArgs = append (buildArgs , fmt .Sprintf ("%s=localhost:%s/%s" , image .ArgName , port , name ))
84+ buildArgs = append (buildArgs , fmt .Sprintf ("%s=localhost:%s/%s" , image .BuildArgName , port , name ))
6785 }
6886
6987 return buildArgs
@@ -82,30 +100,94 @@ func (registry LocalRegistry) GetManifest(w http.ResponseWriter, r *http.Request
82100 w .WriteHeader (http .StatusNotFound )
83101 return
84102 }
85- image := img .Image
86103
87- mt , err := image .MediaType ()
88- if err != nil {
89- logrus .Errorf ("failed to get media type: %s" , err )
90- w .WriteHeader (http .StatusInternalServerError )
91- return
92- }
104+ var mediaType types.MediaType
105+ var blob []byte
106+ var digest v1.Hash
107+ var err error
108+
109+ if img .Image != nil {
110+ mediaType , err = img .Image .MediaType ()
111+ if err != nil {
112+ logrus .Errorf ("failed to get media type: %s" , err )
113+ w .WriteHeader (http .StatusInternalServerError )
114+ return
115+ }
116+
117+ blob , err = img .Image .RawManifest ()
118+ if err != nil {
119+ logrus .Errorf ("failed to get manifest: %s" , err )
120+ w .WriteHeader (http .StatusInternalServerError )
121+ return
122+ }
123+
124+ digest , err = img .Image .Digest ()
125+ if err != nil {
126+ logrus .Errorf ("failed to get digest: %s" , err )
127+ w .WriteHeader (http .StatusInternalServerError )
128+ return
129+ }
93130
94- blob , err := image .RawManifest ()
95- if err != nil {
96- logrus .Errorf ("failed to get manifest: %s" , err )
97- w .WriteHeader (http .StatusInternalServerError )
98- return
99131 }
100132
101- digest , err := image .Digest ()
102- if err != nil {
103- logrus .Errorf ("failed to get digest: %s" , err )
104- w .WriteHeader (http .StatusInternalServerError )
105- return
133+ if img .Index != nil {
134+ digest , err = img .Index .Digest ()
135+ if err != nil {
136+ logrus .Errorf ("error getting ImageIndex's digest: %s" , err )
137+ w .WriteHeader (http .StatusInternalServerError )
138+ return
139+ }
140+
141+ // Check if we were given a Hash. An err means we were NOT given a Hash
142+ // and got a string like "latest" or a semver. In that case we return
143+ // the ImageIndex itself
144+ refHash , err := v1 .NewHash (ref )
145+ if digest .String () == ref || err != nil {
146+ mediaType , err = img .Index .MediaType ()
147+ if err != nil {
148+ logrus .Errorf ("error getting MediaType: %s" , err )
149+ w .WriteHeader (http .StatusInternalServerError )
150+ return
151+ }
152+
153+ blob , err = img .Index .RawManifest ()
154+ if err != nil {
155+ logrus .Errorf ("error getting RawManifest: %s" , err )
156+ w .WriteHeader (http .StatusInternalServerError )
157+ return
158+ }
159+ } else {
160+ // TODO: technically there could be nested ImageIndex's, but they're
161+ // not common so not bothering to handle those right now
162+
163+ //try and find ref inside ImageIndex
164+ digest = refHash
165+
166+ image , err := img .Index .Image (digest )
167+ if err != nil {
168+ logrus .Errorf ("error getting Image from ImageIndex: %s" , err )
169+ w .WriteHeader (http .StatusInternalServerError )
170+ return
171+ }
172+
173+ mediaType , err = image .MediaType ()
174+ if err != nil {
175+ logrus .Errorf ("error getting MediaType from Image: %s" , err )
176+ w .WriteHeader (http .StatusInternalServerError )
177+ return
178+ }
179+
180+ blob , err = image .RawManifest ()
181+ if err != nil {
182+ logrus .Errorf ("error getting RawManifest from Image: %s" , err )
183+ w .WriteHeader (http .StatusInternalServerError )
184+ return
185+ }
186+ }
187+
106188 }
107189
108- w .Header ().Set ("Content-Type" , string (mt ))
190+ w .Header ().Set ("Content-Type" , string (mediaType ))
109191 w .Header ().Set ("Content-Length" , fmt .Sprintf ("%d" , len (blob )))
110192 w .Header ().Set ("Docker-Content-Digest" , digest .String ())
111193
@@ -133,7 +215,6 @@ func (registry LocalRegistry) GetBlob(w http.ResponseWriter, r *http.Request, p
133215 w .WriteHeader (http .StatusNotFound )
134216 return
135217 }
136- image := img .Image
137218
138219 hash , err := v1 .NewHash (dig )
139220 if err != nil {
@@ -142,48 +223,89 @@ func (registry LocalRegistry) GetBlob(w http.ResponseWriter, r *http.Request, p
142223 return
143224 }
144225
145- cfgHash , err := image .ConfigName ()
146- if err != nil {
147- logrus .Errorf ("failed to get config hash: %s" , err )
148- w .WriteHeader (http .StatusInternalServerError )
149- return
150- }
226+ var layer v1.Layer
151227
152- if hash == cfgHash {
153- manifest , err := image .Manifest ()
154- if err != nil {
155- logrus .Errorf ("get image manifest: %s" , err )
156- return
157- }
228+ if img .Image != nil {
229+ image := img .Image
158230
159- cfgBlob , err := image .RawConfigFile ()
231+ cfgHash , err := image .ConfigName ()
160232 if err != nil {
161- logrus .Errorf ("failed to get config file : %s" , err )
233+ logrus .Errorf ("failed to get config hash : %s" , err )
162234 w .WriteHeader (http .StatusInternalServerError )
163235 return
164236 }
165237
166- w .Header ().Set ("Content-Type" , string (manifest .Config .MediaType ))
167- w .Header ().Set ("Content-Length" , fmt .Sprintf ("%d" , len (cfgBlob )))
238+ if hash == cfgHash {
239+ manifest , err := image .Manifest ()
240+ if err != nil {
241+ logrus .Errorf ("get image manifest: %s" , err )
242+ w .WriteHeader (http .StatusInternalServerError )
243+ return
244+ }
245+
246+ cfgBlob , err := image .RawConfigFile ()
247+ if err != nil {
248+ logrus .Errorf ("failed to get config file: %s" , err )
249+ w .WriteHeader (http .StatusInternalServerError )
250+ return
251+ }
252+
253+ w .Header ().Set ("Content-Type" , string (manifest .Config .MediaType ))
254+ w .Header ().Set ("Content-Length" , fmt .Sprintf ("%d" , len (cfgBlob )))
255+
256+ if r .Method == "HEAD" {
257+ return
258+ }
259+
260+ _ , err = w .Write (cfgBlob )
261+ if err != nil {
262+ logrus .Errorf ("write config blob: %s" , err )
263+ return
264+ }
265+
266+ return
267+ }
168268
169- if r .Method == "HEAD" {
269+ layer , err = image .LayerByDigest (hash )
270+ if err != nil {
271+ logrus .Errorf ("failed to get layer: %s" , err )
272+ w .WriteHeader (http .StatusInternalServerError )
170273 return
171274 }
275+ }
172276
173- _ , err = w .Write (cfgBlob )
277+ if img .Index != nil {
278+ index , err := img .Index .IndexManifest ()
174279 if err != nil {
175- logrus .Errorf ("write config blob: %s" , err )
280+ logrus .Errorf ("error getting Manifest from ImageIndex: %s" , err )
281+ w .WriteHeader (http .StatusInternalServerError )
176282 return
177283 }
178284
179- return
180- }
285+ // Search all images in the ImageIndex for the requested layer
286+ for _ , desc := range index .Manifests {
287+ if desc .MediaType .IsImage () {
288+ img , err := img .Index .Image (desc .Digest )
289+ if err != nil {
290+ logrus .Errorf ("error getting image from ImageIndex: %s" , err )
291+ w .WriteHeader (http .StatusInternalServerError )
292+ return
293+ }
294+
295+ // ignore errors related to not finding the layer and just keep searching
296+ l , err := img .LayerByDigest (hash )
297+ if err == nil {
298+ layer = l
299+ break
300+ }
301+ }
302+ }
181303
182- layer , err := image . LayerByDigest ( hash )
183- if err != nil {
184- logrus . Errorf ( "failed to get layer: %s" , err )
185- w . WriteHeader ( http . StatusInternalServerError )
186- return
304+ if layer == nil {
305+ logrus . Errorf ( "layer not found in ImageIndex: %s" , err )
306+ w . WriteHeader ( http . StatusNotFound )
307+ return
308+ }
187309 }
188310
189311 size , err := layer .Size ()
0 commit comments