about summary refs log tree commit diff
path: root/tools/nixery
diff options
context:
space:
mode:
authorVincent Ambo <tazjin@google.com>2019-07-31T20·36+0100
committerVincent Ambo <github@tazj.in>2019-08-02T00·08+0100
commit119af77b43775999e39b6e88911d46b5cc60b93d (patch)
treeddfbd43a734ba5d9f3fc931118fbb71accfaceec /tools/nixery
parent2f1bc55597c382496930f4ba8f4ef25914fb9748 (diff)
feat(go): Return errors with correct status codes to clients
Uses the structured errors feature introduced in the Nix code to
return more sensible errors to clients. For now this is quite limited,
but already a lot better than before:

* packages that could not be found result in 404s
* all other errors result in 500s

This way the registry clients will not attempt to interpret the
returned garbage data/empty response as something useful.
Diffstat (limited to 'tools/nixery')
-rw-r--r--tools/nixery/main.go20
1 files changed, 17 insertions, 3 deletions
diff --git a/tools/nixery/main.go b/tools/nixery/main.go
index a4e0b9a80a16..91a9e6d4943f 100644
--- a/tools/nixery/main.go
+++ b/tools/nixery/main.go
@@ -138,6 +138,9 @@ type image struct {
 //
 // The later field is simply treated as opaque JSON and passed through.
 type BuildResult struct {
+	Error string   `json:"error`
+	Pkgs  []string `json:"pkgs"`
+
 	Manifest       json.RawMessage `json:"manifest"`
 	LayerLocations map[string]struct {
 		Path string `json:"path"`
@@ -183,7 +186,7 @@ func convenienceNames(packages []string) []string {
 
 // Call out to Nix and request that an image be built. Nix will, upon success,
 // return a manifest for the container image.
-func buildImage(ctx *context.Context, cfg *config, image *image, bucket *storage.BucketHandle) ([]byte, error) {
+func buildImage(ctx *context.Context, cfg *config, image *image, bucket *storage.BucketHandle) (*BuildResult, error) {
 	packages, err := json.Marshal(image.packages)
 	if err != nil {
 		return nil, err
@@ -249,7 +252,7 @@ func buildImage(ctx *context.Context, cfg *config, image *image, bucket *storage
 		}
 	}
 
-	return json.Marshal(result.Manifest)
+	return &result, nil
 }
 
 // uploadLayer uploads a single layer to Cloud Storage bucket. Before writing
@@ -358,7 +361,7 @@ func (h *registryHandler) ServeHTTP(w http.ResponseWriter, r *http.Request) {
 		imageTag := manifestMatches[2]
 		log.Printf("Requesting manifest for image %q at tag %q", imageName, imageTag)
 		image := imageFromName(imageName, imageTag)
-		manifest, err := buildImage(h.ctx, h.cfg, &image, h.bucket)
+		buildResult, err := buildImage(h.ctx, h.cfg, &image, h.bucket)
 
 		if err != nil {
 			log.Println("Failed to build image manifest", err)
@@ -366,6 +369,17 @@ func (h *registryHandler) ServeHTTP(w http.ResponseWriter, r *http.Request) {
 			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)
+			return
+		}
+
+		// This marshaling error is ignored because we know that this
+		// field represents valid JSON data.
+		manifest, _ := json.Marshal(buildResult.Manifest)
 		w.Header().Add("Content-Type", manifestMediaType)
 		w.Write(manifest)
 		return