about summary refs log tree commit diff
path: root/tools/nixery/main.go
diff options
context:
space:
mode:
authorVincent Ambo <tazjin@google.com>2019-08-01T23·45+0100
committerVincent Ambo <github@tazj.in>2019-08-02T00·08+0100
commit3d0596596ac03957db6646836e6b4ec0d222e23a (patch)
treeccfd49d2f4d1da77a86fe71808bbc6bd0263aee2 /tools/nixery/main.go
parent119af77b43775999e39b6e88911d46b5cc60b93d (diff)
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
Diffstat (limited to 'tools/nixery/main.go')
-rw-r--r--tools/nixery/main.go44
1 files changed, 35 insertions, 9 deletions
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"),