1
0
Fork 0
mirror of https://github.com/imjasonh/kontain.me synced 2026-07-21 22:48:19 +00:00

better errors, better logging, moar packages

This commit is contained in:
Jason Hall 2019-05-10 10:05:27 -04:00
parent 4ef0aafc7b
commit 719a5559e9
6 changed files with 97 additions and 37 deletions

43
pkg/serve/error.go Normal file
View file

@ -0,0 +1,43 @@
package serve
import (
"encoding/json"
"errors"
"net/http"
)
var (
ErrNotFound = errors.New("repository or commit not found")
ErrInvalid = errors.New("requested manifest is invalid")
)
func Error(w http.ResponseWriter, err error) {
code := "INTERNAL_ERROR"
httpCode := http.StatusInternalServerError
if err == ErrNotFound {
code = "MANIFEST_UNKNOWN"
httpCode = http.StatusNotFound
} else if err == ErrInvalid {
code = "NAME_INVALID"
httpCode = http.StatusBadRequest
}
http.Error(w, "", httpCode)
json.NewEncoder(w).Encode(&resp{
Errors: []e{{
Code: code,
Message: err.Error(),
}},
})
}
type resp struct {
Errors []e `json:"errors"`
}
type e struct {
Code string `json:"code"`
Message string `json:"message"`
Reason string `json:"reason,omitempty"`
}

132
pkg/serve/serve.go Normal file
View file

@ -0,0 +1,132 @@
package serve
import (
"bytes"
"context"
"fmt"
"io"
"io/ioutil"
"log"
"net/http"
"strings"
"cloud.google.com/go/storage"
"github.com/google/go-containerregistry/pkg/v1"
"google.golang.org/api/googleapi"
)
const bucket = "kontainme"
func Blob(w http.ResponseWriter, r *http.Request) {
// 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)
http.Redirect(w, r, url, http.StatusSeeOther)
}
func writeBlob(h v1.Hash, rc io.ReadCloser) error {
ctx := context.Background() // TODO
client, err := storage.NewClient(ctx)
if err != nil {
return fmt.Errorf("NewClient: %v", err)
}
// The DoesNotExist precondition can be hit when writing or flushing
// data, which can happen any of three places. Anywhere it happens,
// just ignore the error since that means the blob already exists.
w := client.Bucket(bucket).Object(fmt.Sprintf("blobs/%s", h)).
If(storage.Conditions{DoesNotExist: true}).
NewWriter(ctx)
if _, err := io.Copy(w, rc); err != nil {
if herr, ok := err.(*googleapi.Error); ok && herr.Code == http.StatusPreconditionFailed {
return nil
}
return fmt.Errorf("Copy: %v", err)
}
if err := rc.Close(); err != nil {
if herr, ok := err.(*googleapi.Error); ok && herr.Code == http.StatusPreconditionFailed {
return nil
}
return fmt.Errorf("rc.Close: %v", err)
}
if err := w.Close(); err != nil {
if herr, ok := err.(*googleapi.Error); ok && herr.Code == http.StatusPreconditionFailed {
return nil
}
return fmt.Errorf("w.Close: %v", err)
}
return nil
}
// ServeManifest writes config and layer blobs for the image, then serves the
// manifest contents pointing to those blobs.
func Manifest(w http.ResponseWriter, img v1.Image) {
// Write config blob for later serving.
ch, err := img.ConfigName()
if err != nil {
log.Printf("ERROR (serveManifest ConfigName): %s", err)
http.Error(w, err.Error(), http.StatusInternalServerError)
return
}
cb, err := img.RawConfigFile()
if err != nil {
log.Printf("ERROR (serveManifest RawConfigFile): %s", err)
http.Error(w, err.Error(), http.StatusInternalServerError)
return
}
log.Printf("writing config blob %q", ch)
if err := writeBlob(ch, ioutil.NopCloser(bytes.NewReader(cb))); err != nil {
log.Printf("ERROR (serveManifest writeBlob): %s", err)
http.Error(w, err.Error(), http.StatusInternalServerError)
return
}
// Write layer blobs for later serving.
layers, err := img.Layers()
if err != nil {
log.Printf("ERROR (serveManifest Layers): %s", err)
http.Error(w, err.Error(), http.StatusInternalServerError)
return
}
for _, l := range layers {
rc, err := l.Compressed()
if err != nil {
log.Printf("ERROR (serveManifest l.Compressed): %s", err)
http.Error(w, err.Error(), http.StatusInternalServerError)
return
}
lh, err := l.Digest()
if err != nil {
log.Printf("ERROR (serveManifest l.Digest): %s", err)
http.Error(w, err.Error(), http.StatusInternalServerError)
return
}
log.Printf("writing layer blob %q", lh)
if err := writeBlob(lh, rc); err != nil {
log.Printf("ERROR (serveManifest writeBlob): %s", err)
http.Error(w, err.Error(), http.StatusInternalServerError)
return
}
}
// Serve the manifest.
b, err := img.RawManifest()
if err != nil {
log.Printf("ERROR (serveManifest RawManifest): %s", err)
http.Error(w, err.Error(), http.StatusInternalServerError)
return
}
mt, err := img.MediaType()
if err != nil {
log.Printf("ERROR (serveManifest MediaType): %s", err)
http.Error(w, err.Error(), http.StatusInternalServerError)
return
}
w.Header().Set("Content-Type", string(mt))
if _, err := io.Copy(w, bytes.NewReader(b)); err != nil {
log.Printf("ERROR (serveManifest Copy): %s", err)
return
}
fmt.Printf("Served manifest: %s", string(b))
}