From 3d0596596ac03957db6646836e6b4ec0d222e23a Mon Sep 17 00:00:00 2001 From: Vincent Ambo Date: Fri, 2 Aug 2019 00:45:22 +0100 Subject: feat(go): Return error responses in registry format The registry specifies a format for how errors should be returned and this commit implements it: https://docs.docker.com/registry/spec/api/#errors --- tools/nixery/main.go | 44 +++++++++++++++++++++++++++++++++++--------- 1 file changed, 35 insertions(+), 9 deletions(-) (limited to 'tools/nixery/main.go') diff --git a/tools/nixery/main.go b/tools/nixery/main.go index 91a9e6d4943f..9574a3a68c65 100644 --- a/tools/nixery/main.go +++ b/tools/nixery/main.go @@ -138,7 +138,7 @@ type image struct { // // The later field is simply treated as opaque JSON and passed through. type BuildResult struct { - Error string `json:"error` + Error string `json:"error"` Pkgs []string `json:"pkgs"` Manifest json.RawMessage `json:"manifest"` @@ -338,13 +338,29 @@ var ( layerRegex = regexp.MustCompile(`^/v2/([\w|\-|\.|\_|\/]+)/blobs/sha256:(\w+)$`) ) -func getConfig(key, desc string) string { - value := os.Getenv(key) - if value == "" { - log.Fatalln(desc + " must be specified") +// Error format corresponding to the registry protocol V2 specification. This +// allows feeding back errors to clients in a way that can be presented to +// users. +type registryError struct { + Code string `json:"code"` + Message string `json:"message"` +} + +type registryErrors struct { + Errors []registryError `json:"errors"` +} + +func writeError(w http.ResponseWriter, status int, code, message string) { + err := registryErrors{ + Errors: []registryError{ + {code, message}, + }, } + json, _ := json.Marshal(err) - return value + w.WriteHeader(status) + w.Header().Add("Content-Type", "application/json") + w.Write(json) } type registryHandler struct { @@ -364,16 +380,17 @@ func (h *registryHandler) ServeHTTP(w http.ResponseWriter, r *http.Request) { buildResult, err := buildImage(h.ctx, h.cfg, &image, h.bucket) if err != nil { + writeError(w, 500, "UNKNOWN", "image build failure") log.Println("Failed to build image manifest", err) - w.WriteHeader(500) return } // Some error types have special handling, which is applied // here. if buildResult.Error == "not_found" { - log.Printf("Could not find packages: %v\n", buildResult.Pkgs) - w.WriteHeader(404) + s := fmt.Sprintf("Could not find Nix packages: %v", buildResult.Pkgs) + writeError(w, 404, "MANIFEST_UNKNOWN", s) + log.Println(s) return } @@ -399,6 +416,15 @@ func (h *registryHandler) ServeHTTP(w http.ResponseWriter, r *http.Request) { w.WriteHeader(404) } +func getConfig(key, desc string) string { + value := os.Getenv(key) + if value == "" { + log.Fatalln(desc + " must be specified") + } + + return value +} + func main() { cfg := &config{ bucket: getConfig("BUCKET", "GCS bucket for layer storage"), -- cgit 1.4.1