about summary refs log tree commit diff
path: root/tools/nixery/server/manifest/manifest.go
diff options
context:
space:
mode:
Diffstat (limited to 'tools/nixery/server/manifest/manifest.go')
-rw-r--r--tools/nixery/server/manifest/manifest.go114
1 files changed, 114 insertions, 0 deletions
diff --git a/tools/nixery/server/manifest/manifest.go b/tools/nixery/server/manifest/manifest.go
new file mode 100644
index 000000000000..dd447796cc78
--- /dev/null
+++ b/tools/nixery/server/manifest/manifest.go
@@ -0,0 +1,114 @@
+// Package image implements logic for creating the image metadata
+// (such as the image manifest and configuration).
+package manifest
+
+import (
+	"crypto/md5"
+	"crypto/sha256"
+	"encoding/json"
+	"fmt"
+)
+
+const (
+	// manifest constants
+	schemaVersion = 2
+
+	// media types
+	manifestType = "application/vnd.docker.distribution.manifest.v2+json"
+	layerType    = "application/vnd.docker.image.rootfs.diff.tar"
+	configType   = "application/vnd.docker.container.image.v1+json"
+
+	// image config constants
+	arch   = "amd64"
+	os     = "linux"
+	fsType = "layers"
+)
+
+type Entry struct {
+	MediaType string `json:"mediaType"`
+	Size      int64  `json:"size"`
+	Digest    string `json:"digest"`
+}
+
+type manifest struct {
+	SchemaVersion int     `json:"schemaVersion"`
+	MediaType     string  `json:"mediaType"`
+	Config        Entry   `json:"config"`
+	Layers        []Entry `json:"layers"`
+}
+
+type imageConfig struct {
+	Architecture string `json:"architecture"`
+	OS           string `json:"os"`
+
+	RootFS struct {
+		FSType  string   `json:"type"`
+		DiffIDs []string `json:"diff_ids"`
+	} `json:"rootfs"`
+
+	// sic! empty struct (rather than `null`) is required by the
+	// image metadata deserialiser in Kubernetes
+	Config struct{} `json:"config"`
+}
+
+// ConfigLayer represents the configuration layer to be included in
+// the manifest, containing its JSON-serialised content and the SHA256
+// & MD5 hashes of its input.
+type ConfigLayer struct {
+	Config []byte
+	SHA256 string
+	MD5    string
+}
+
+// imageConfig creates an image configuration with the values set to
+// the constant defaults.
+//
+// Outside of this module the image configuration is treated as an
+// opaque blob and it is thus returned as an already serialised byte
+// array and its SHA256-hash.
+func configLayer(hashes []string) ConfigLayer {
+	c := imageConfig{}
+	c.Architecture = arch
+	c.OS = os
+	c.RootFS.FSType = fsType
+	c.RootFS.DiffIDs = hashes
+
+	j, _ := json.Marshal(c)
+
+	return ConfigLayer{
+		Config: j,
+		SHA256: fmt.Sprintf("%x", sha256.Sum256(j)),
+		MD5:    fmt.Sprintf("%x", md5.Sum(j)),
+	}
+}
+
+// Manifest creates an image manifest from the specified layer entries
+// and returns its JSON-serialised form as well as the configuration
+// layer.
+//
+// Callers do not need to set the media type for the layer entries.
+func Manifest(layers []Entry) (json.RawMessage, ConfigLayer) {
+	hashes := make([]string, len(layers))
+	for i, l := range layers {
+		l.MediaType = "application/vnd.docker.image.rootfs.diff.tar"
+		layers[i] = l
+		hashes[i] = l.Digest
+	}
+
+	c := configLayer(hashes)
+
+	m := manifest{
+		SchemaVersion: schemaVersion,
+		MediaType:     manifestType,
+		Config: Entry{
+			MediaType: configType,
+			Size:      int64(len(c.Config)),
+			Digest:    "sha256:" + c.SHA256,
+		},
+		Layers: layers,
+	}
+
+	j, _ := json.Marshal(m)
+
+	return json.RawMessage(j), c
+}