about summary refs log tree commit diff
path: root/tools/nixery/main.go
diff options
context:
space:
mode:
authorVincent Ambo <tazjin@google.com>2020-10-27T13·50+0100
committerVincent Ambo <mail@tazj.in>2020-10-27T14·29+0100
commit9e5ebb2f4f57c310977e979166e9ab287ef65865 (patch)
tree3d60b762f0e28fd566854da57ccb84383e718533 /tools/nixery/main.go
parent94570aa83f0ebb9a192ff9d62e14b811457a6284 (diff)
feat(main): Implement caching of manifests in CAS
To ensure that registry clients which attempt to pull manifests by
their content hash can interact with Nixery, this change implements
persisting image manifests in the CAS in the same way as image layers.

In combination with the previous refactorings this means that Nixery's
serving flow is now compatible with containerd.

I have verified this locally, but CI currently only runs against
Docker and not containerd, which is something I plan to address in a
subsequent PR.

This fixes #102
Diffstat (limited to 'tools/nixery/main.go')
-rw-r--r--tools/nixery/main.go35
1 files changed, 35 insertions, 0 deletions
diff --git a/tools/nixery/main.go b/tools/nixery/main.go
index 8a6d3c3aedaa..39cfbc525528 100644
--- a/tools/nixery/main.go
+++ b/tools/nixery/main.go
@@ -26,8 +26,11 @@
 package main
 
 import (
+	"context"
+	"crypto/sha256"
 	"encoding/json"
 	"fmt"
+	"io"
 	"io/ioutil"
 	"net/http"
 	"regexp"
@@ -153,6 +156,38 @@ func (h *registryHandler) serveManifestTag(w http.ResponseWriter, r *http.Reques
 	// field represents valid JSON data.
 	manifest, _ := json.Marshal(buildResult.Manifest)
 	w.Header().Add("Content-Type", manifestMediaType)
+
+	// The manifest needs to be persisted to the blob storage (to become
+	// available for clients that fetch manifests by their hash, e.g.
+	// containerd) and served to the client.
+	//
+	// Since we have no stable key to address this manifest (it may be
+	// uncacheable, yet still addressable by blob) we need to separate
+	// out the hashing, uploading and serving phases. The latter is
+	// especially important as clients may start to fetch it by digest
+	// as soon as they see a response.
+	sha256sum := fmt.Sprintf("%x", sha256.Sum256(manifest))
+	path := "layers/" + sha256sum
+	ctx := context.TODO()
+
+	_, _, err = h.state.Storage.Persist(ctx, path, func(sw io.Writer) (string, int64, error) {
+		// We already know the hash, so no additional hash needs to be
+		// constructed here.
+		written, err := sw.Write(manifest)
+		return sha256sum, int64(written), err
+	})
+
+	if err != nil {
+		writeError(w, 500, "MANIFEST_UPLOAD", "could not upload manifest to blob store")
+
+		log.WithError(err).WithFields(log.Fields{
+			"image": name,
+			"tag":   tag,
+		}).Error("could not upload manifest")
+
+		return
+	}
+
 	w.Write(manifest)
 }