mirror of
https://github.com/imjasonh/kontain.me
synced 2026-07-08 09:04:54 +00:00
Support pull by digest, for images that have previously been built
This commit is contained in:
parent
e9b607a2f7
commit
7f6572d0a2
4 changed files with 53 additions and 33 deletions
|
|
@ -71,10 +71,15 @@ func (s *server) ServeHTTP(w http.ResponseWriter, r *http.Request) {
|
||||||
switch {
|
switch {
|
||||||
case path == "": // API Version check.
|
case path == "": // API Version check.
|
||||||
w.Header().Set("Docker-Distribution-API-Version", "registry/2.0")
|
w.Header().Set("Docker-Distribution-API-Version", "registry/2.0")
|
||||||
|
case strings.Contains(path, "/blobs/"),
|
||||||
|
strings.Contains(path, "/manifests/sha256:"):
|
||||||
|
// Extract requested blob digest and redirect to serve it from GCS.
|
||||||
|
// If it doesn't exist, this will return 404.
|
||||||
|
parts := strings.Split(r.URL.Path, "/")
|
||||||
|
digest := parts[len(parts)-1]
|
||||||
|
serve.Blob(w, r, digest)
|
||||||
case strings.Contains(path, "/manifests/"):
|
case strings.Contains(path, "/manifests/"):
|
||||||
s.serveBuildpackManifest(w, r)
|
s.serveBuildpackManifest(w, r)
|
||||||
case strings.Contains(path, "/blobs/"):
|
|
||||||
serve.Blob(w, r)
|
|
||||||
default:
|
default:
|
||||||
serve.Error(w, serve.ErrNotFound)
|
serve.Error(w, serve.ErrNotFound)
|
||||||
}
|
}
|
||||||
|
|
@ -149,7 +154,7 @@ func (s *server) serveBuildpackManifest(w http.ResponseWriter, r *http.Request)
|
||||||
}
|
}
|
||||||
|
|
||||||
// Serve the manifest.
|
// Serve the manifest.
|
||||||
serve.Manifest(w, img)
|
serve.Manifest(w, r, img)
|
||||||
}
|
}
|
||||||
|
|
||||||
type cachedManifest struct {
|
type cachedManifest struct {
|
||||||
|
|
|
||||||
|
|
@ -59,10 +59,15 @@ func (s *server) ServeHTTP(w http.ResponseWriter, r *http.Request) {
|
||||||
// API Version check.
|
// API Version check.
|
||||||
w.Header().Set("Docker-Distribution-API-Version", "registry/2.0")
|
w.Header().Set("Docker-Distribution-API-Version", "registry/2.0")
|
||||||
return
|
return
|
||||||
case strings.HasPrefix(path, "ko/") && strings.Contains(path, "/manifests/"):
|
case strings.Contains(path, "/blobs/"),
|
||||||
|
strings.Contains(path, "/manifests/sha256:"):
|
||||||
|
// Extract requested blob digest and redirect to serve it from GCS.
|
||||||
|
// If it doesn't exist, this will return 404.
|
||||||
|
parts := strings.Split(r.URL.Path, "/")
|
||||||
|
digest := parts[len(parts)-1]
|
||||||
|
serve.Blob(w, r, digest)
|
||||||
|
case strings.Contains(path, "/manifests/"):
|
||||||
s.serveKoManifest(w, r)
|
s.serveKoManifest(w, r)
|
||||||
case strings.HasPrefix(path, "ko/") && strings.Contains(path, "/blobs/"):
|
|
||||||
serve.Blob(w, r)
|
|
||||||
default:
|
default:
|
||||||
serve.Error(w, serve.ErrNotFound)
|
serve.Error(w, serve.ErrNotFound)
|
||||||
}
|
}
|
||||||
|
|
@ -119,5 +124,5 @@ func (s *server) serveKoManifest(w http.ResponseWriter, r *http.Request) {
|
||||||
serve.Error(w, serve.ErrInvalid)
|
serve.Error(w, serve.ErrInvalid)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
serve.Manifest(w, img)
|
serve.Manifest(w, r, img)
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -42,10 +42,15 @@ func (s *server) ServeHTTP(w http.ResponseWriter, r *http.Request) {
|
||||||
// API Version check.
|
// API Version check.
|
||||||
w.Header().Set("Docker-Distribution-API-Version", "registry/2.0")
|
w.Header().Set("Docker-Distribution-API-Version", "registry/2.0")
|
||||||
return
|
return
|
||||||
|
case strings.Contains(path, "/blobs/"),
|
||||||
|
strings.Contains(path, "/manifests/sha256:"):
|
||||||
|
// Extract requested blob digest and redirect to serve it from GCS.
|
||||||
|
// If it doesn't exist, this will return 404.
|
||||||
|
parts := strings.Split(r.URL.Path, "/")
|
||||||
|
digest := parts[len(parts)-1]
|
||||||
|
serve.Blob(w, r, digest)
|
||||||
case strings.Contains(path, "/manifests/"):
|
case strings.Contains(path, "/manifests/"):
|
||||||
s.serveRandomManifest(w, r)
|
s.serveRandomManifest(w, r)
|
||||||
case strings.Contains(path, "/blobs/"):
|
|
||||||
serve.Blob(w, r)
|
|
||||||
default:
|
default:
|
||||||
serve.Error(w, serve.ErrNotFound)
|
serve.Error(w, serve.ErrNotFound)
|
||||||
}
|
}
|
||||||
|
|
@ -77,5 +82,5 @@ func (s *server) serveRandomManifest(w http.ResponseWriter, r *http.Request) {
|
||||||
serve.Error(w, err)
|
serve.Error(w, err)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
serve.Manifest(w, img)
|
serve.Manifest(w, r, img)
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -8,7 +8,6 @@ import (
|
||||||
"io/ioutil"
|
"io/ioutil"
|
||||||
"log"
|
"log"
|
||||||
"net/http"
|
"net/http"
|
||||||
"strings"
|
|
||||||
|
|
||||||
"cloud.google.com/go/storage"
|
"cloud.google.com/go/storage"
|
||||||
"github.com/google/go-containerregistry/pkg/v1"
|
"github.com/google/go-containerregistry/pkg/v1"
|
||||||
|
|
@ -17,16 +16,12 @@ import (
|
||||||
|
|
||||||
const bucket = "kontainme"
|
const bucket = "kontainme"
|
||||||
|
|
||||||
func Blob(w http.ResponseWriter, r *http.Request) {
|
func Blob(w http.ResponseWriter, r *http.Request, digest string) {
|
||||||
// Extract requested blob digest and redirect to serve it from GCS.
|
|
||||||
// If it doesn't exist, this will return 404.
|
|
||||||
parts := strings.Split(r.URL.Path, "/")
|
|
||||||
digest := parts[len(parts)-1]
|
|
||||||
url := fmt.Sprintf("https://storage.googleapis.com/%s/blobs/%s", bucket, digest)
|
url := fmt.Sprintf("https://storage.googleapis.com/%s/blobs/%s", bucket, digest)
|
||||||
http.Redirect(w, r, url, http.StatusSeeOther)
|
http.Redirect(w, r, url, http.StatusSeeOther)
|
||||||
}
|
}
|
||||||
|
|
||||||
func writeBlob(h v1.Hash, rc io.ReadCloser) error {
|
func writeBlob(h v1.Hash, rc io.ReadCloser, contentType string) error {
|
||||||
ctx := context.Background() // TODO
|
ctx := context.Background() // TODO
|
||||||
client, err := storage.NewClient(ctx)
|
client, err := storage.NewClient(ctx)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
|
@ -38,6 +33,7 @@ func writeBlob(h v1.Hash, rc io.ReadCloser) error {
|
||||||
w := client.Bucket(bucket).Object(fmt.Sprintf("blobs/%s", h)).
|
w := client.Bucket(bucket).Object(fmt.Sprintf("blobs/%s", h)).
|
||||||
If(storage.Conditions{DoesNotExist: true}).
|
If(storage.Conditions{DoesNotExist: true}).
|
||||||
NewWriter(ctx)
|
NewWriter(ctx)
|
||||||
|
w.ObjectAttrs.ContentType = contentType
|
||||||
if _, err := io.Copy(w, rc); err != nil {
|
if _, err := io.Copy(w, rc); err != nil {
|
||||||
if herr, ok := err.(*googleapi.Error); ok && herr.Code == http.StatusPreconditionFailed {
|
if herr, ok := err.(*googleapi.Error); ok && herr.Code == http.StatusPreconditionFailed {
|
||||||
return nil
|
return nil
|
||||||
|
|
@ -56,28 +52,29 @@ func writeBlob(h v1.Hash, rc io.ReadCloser) error {
|
||||||
}
|
}
|
||||||
return fmt.Errorf("w.Close: %v", err)
|
return fmt.Errorf("w.Close: %v", err)
|
||||||
}
|
}
|
||||||
|
log.Printf("Wrote blob %s", h.String())
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// ServeManifest writes config and layer blobs for the image, then serves the
|
// ServeManifest writes config and layer blobs for the image, then serves the
|
||||||
// manifest contents pointing to those blobs.
|
// manifest contents pointing to those blobs.
|
||||||
func Manifest(w http.ResponseWriter, img v1.Image) {
|
func Manifest(w http.ResponseWriter, r *http.Request, img v1.Image) {
|
||||||
// Write config blob for later serving.
|
// Write config blob for later serving.
|
||||||
ch, err := img.ConfigName()
|
ch, err := img.ConfigName()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Printf("ERROR (serveManifest ConfigName): %s", err)
|
log.Printf("ERROR (serveManifest ConfigName): %v", err)
|
||||||
http.Error(w, err.Error(), http.StatusInternalServerError)
|
http.Error(w, err.Error(), http.StatusInternalServerError)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
cb, err := img.RawConfigFile()
|
cb, err := img.RawConfigFile()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Printf("ERROR (serveManifest RawConfigFile): %s", err)
|
log.Printf("ERROR (serveManifest RawConfigFile): %v", err)
|
||||||
http.Error(w, err.Error(), http.StatusInternalServerError)
|
http.Error(w, err.Error(), http.StatusInternalServerError)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
log.Printf("writing config blob %q", ch)
|
log.Printf("writing config blob %q", ch)
|
||||||
if err := writeBlob(ch, ioutil.NopCloser(bytes.NewReader(cb))); err != nil {
|
if err := writeBlob(ch, ioutil.NopCloser(bytes.NewReader(cb)), ""); err != nil {
|
||||||
log.Printf("ERROR (serveManifest writeBlob): %s", err)
|
log.Printf("ERROR (serveManifest writeBlob): %v", err)
|
||||||
http.Error(w, err.Error(), http.StatusInternalServerError)
|
http.Error(w, err.Error(), http.StatusInternalServerError)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
@ -85,48 +82,56 @@ func Manifest(w http.ResponseWriter, img v1.Image) {
|
||||||
// Write layer blobs for later serving.
|
// Write layer blobs for later serving.
|
||||||
layers, err := img.Layers()
|
layers, err := img.Layers()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Printf("ERROR (serveManifest Layers): %s", err)
|
log.Printf("ERROR (serveManifest Layers): %v", err)
|
||||||
http.Error(w, err.Error(), http.StatusInternalServerError)
|
http.Error(w, err.Error(), http.StatusInternalServerError)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
for _, l := range layers {
|
for _, l := range layers {
|
||||||
rc, err := l.Compressed()
|
rc, err := l.Compressed()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Printf("ERROR (serveManifest l.Compressed): %s", err)
|
log.Printf("ERROR (serveManifest l.Compressed): %v", err)
|
||||||
http.Error(w, err.Error(), http.StatusInternalServerError)
|
http.Error(w, err.Error(), http.StatusInternalServerError)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
lh, err := l.Digest()
|
lh, err := l.Digest()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Printf("ERROR (serveManifest l.Digest): %s", err)
|
log.Printf("ERROR (serveManifest l.Digest): %v", err)
|
||||||
http.Error(w, err.Error(), http.StatusInternalServerError)
|
http.Error(w, err.Error(), http.StatusInternalServerError)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
log.Printf("writing layer blob %q", lh)
|
log.Printf("writing layer blob %q", lh)
|
||||||
if err := writeBlob(lh, rc); err != nil {
|
if err := writeBlob(lh, rc, ""); err != nil {
|
||||||
log.Printf("ERROR (serveManifest writeBlob): %s", err)
|
log.Printf("ERROR (serveManifest writeBlob): %v", err)
|
||||||
http.Error(w, err.Error(), http.StatusInternalServerError)
|
http.Error(w, err.Error(), http.StatusInternalServerError)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Serve the manifest.
|
// Write the manifest as a blob.
|
||||||
b, err := img.RawManifest()
|
b, err := img.RawManifest()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Printf("ERROR (serveManifest RawManifest): %s", err)
|
log.Printf("ERROR (serveManifest RawManifest): %v", err)
|
||||||
http.Error(w, err.Error(), http.StatusInternalServerError)
|
http.Error(w, err.Error(), http.StatusInternalServerError)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
mt, err := img.MediaType()
|
mt, err := img.MediaType()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Printf("ERROR (serveManifest MediaType): %s", err)
|
log.Printf("ERROR (serveManifest MediaType): %v", err)
|
||||||
http.Error(w, err.Error(), http.StatusInternalServerError)
|
http.Error(w, err.Error(), http.StatusInternalServerError)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
w.Header().Set("Content-Type", string(mt))
|
digest, err := img.Digest()
|
||||||
if _, err := io.Copy(w, bytes.NewReader(b)); err != nil {
|
if err != nil {
|
||||||
log.Printf("ERROR (serveManifest Copy): %s", err)
|
log.Printf("ERROR (serveManifest Digest): %v", err)
|
||||||
|
http.Error(w, err.Error(), http.StatusInternalServerError)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
if err := writeBlob(digest, ioutil.NopCloser(bytes.NewReader(b)), string(mt)); err != nil {
|
||||||
|
log.Printf("ERROR (serveManifest writeBlob): %v", err)
|
||||||
|
http.Error(w, err.Error(), http.StatusInternalServerError)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
// Redirect to manifest blob.
|
||||||
|
Blob(w, r, digest.String())
|
||||||
fmt.Printf("Served manifest: %s", string(b))
|
fmt.Printf("Served manifest: %s", string(b))
|
||||||
}
|
}
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue