about summary refs log tree commit diff
path: root/tools/nixery/server/manifest/manifest.go
diff options
context:
space:
mode:
authorVincent Ambo <tazjin@google.com>2019-10-01T22·24+0100
committerVincent Ambo <github@tazj.in>2019-10-03T12·21+0100
commit3f40c0a2d2e9ca03253da13ec53bc57e9c524f00 (patch)
tree950258c14409b479f0b2441f5e644d9b17fbe71b /tools/nixery/server/manifest/manifest.go
parent87e196757b4e0cb22ec9c9f5ee8475573597382a (diff)
feat(server): Implement package for creating image manifests
The new manifest package creates image manifests and their
configuration. This previously happened in Nix, but is now part of the
server's workload.

This relates to #50.
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
+}