about summary refs log tree commit diff
path: root/image.go
diff options
context:
space:
mode:
authorVincent Ambo <tazjin@gmail.com>2017-03-16T13·06+0100
committerVincent Ambo <tazjin@gmail.com>2017-03-16T13·06+0100
commitfa43472a5d1cd3ffc83582ab34e61ec4598adb4b (patch)
treecb92ea92425eb9f882096654b18886daf1de5984 /image.go
parent1c2d087ec42cd6acc2967140e0f48b2119a5e51a (diff)
refactor: Split up code for readability and add docs
Diffstat (limited to 'image.go')
-rw-r--r--image.go158
1 files changed, 140 insertions, 18 deletions
diff --git a/image.go b/image.go
index e627544462e5..3daeac34d6bb 100644
--- a/image.go
+++ b/image.go
@@ -1,28 +1,150 @@
+// The code in this file creates a Docker image layer containing the binary of the
+// application itself.
+
 package main
 
-import "time"
+import (
+	"archive/tar"
+	"bytes"
+	"compress/gzip"
+	"crypto/sha256"
+	"encoding/json"
+	"fmt"
+	"io/ioutil"
+	"log"
+	"os"
+	"time"
+)
+
+// This function creates a Docker-image digest (i.e. SHA256 hash with
+// algorithm-specification prefix)
+func Digest(b []byte) string {
+	hash := sha256.New()
+	hash.Write(b)
+
+	return fmt.Sprintf("sha256:%x", hash.Sum(nil))
+}
+
+func GetImageOfCurrentExecutable() Image {
+	binary := getCurrentBinary()
+	tarArchive := createTarArchive(&map[string][]byte{
+		"/main": binary,
+	})
+
+	configJson, configElem := createConfig([]string{Digest(tarArchive)})
+	compressed := gzipArchive("Quinistry image", tarArchive)
+	manifest := createManifest(&configElem, &compressed)
+	manifestJson, _ := json.Marshal(manifest)
+
+	return Image{
+		Layer:          compressed,
+		LayerDigest:    Digest(compressed),
+		Manifest:       manifestJson,
+		ManifestDigest: Digest(manifestJson),
+		Config:         configJson,
+		ConfigDigest:   Digest(configJson),
+	}
+
+}
+
+func getCurrentBinary() []byte {
+	path, _ := os.Executable()
+	file, _ := ioutil.ReadFile(path)
+	return file
+}
+
+func createTarArchive(files *map[string][]byte) []byte {
+	buf := new(bytes.Buffer)
+	w := tar.NewWriter(buf)
 
-type RootFs struct {
-	DiffIds []string `json:"diff_ids"`
-	Type    string   `json:"type"`
+	for name, file := range *files {
+		hdr := &tar.Header{
+			Name: name,
+			// Everything is executable \o/
+			Mode: 0755,
+			Size: int64(len(file)),
+		}
+		w.WriteHeader(hdr)
+		w.Write(file)
+	}
+
+	if err := w.Close(); err != nil {
+		log.Fatalln(err)
+		os.Exit(1)
+	}
+
+	return buf.Bytes()
 }
 
-type History struct {
-	Created   time.Time `json:"created"`
-	CreatedBy string    `json:"created_by"`
+func gzipArchive(name string, archive []byte) []byte {
+	buf := new(bytes.Buffer)
+	w := gzip.NewWriter(buf)
+	w.Name = name
+	w.Write(archive)
+
+	if err := w.Close(); err != nil {
+		log.Fatalln(err)
+		os.Exit(1)
+	}
+
+	return buf.Bytes()
 }
 
-type ImageConfig struct {
-	Cmd []string
-	Env []string
+func createConfig(layerDigests []string) (configJson []byte, elem Element) {
+	now := time.Now()
+
+	imageConfig := &ImageConfig{
+		Cmd: []string{"/main"},
+		Env: []string{"PATH=/"},
+	}
+
+	rootFs := RootFs{
+		DiffIds: layerDigests,
+		Type:    "layers",
+	}
+
+	history := []History{
+		{
+			Created:   now,
+			CreatedBy: "Quinistry magic",
+		},
+	}
+
+	config := Config{
+		Created:      now,
+		Author:       "tazjin",
+		Architecture: "amd64",
+		Os:           "linux",
+		Config:       imageConfig,
+		RootFs:       rootFs,
+		History:      history,
+	}
+
+	configJson, _ = json.Marshal(config)
+
+	elem = Element{
+		MediaType: ImageConfigMediaType,
+		Size:      len(configJson),
+		Digest:    Digest(configJson),
+	}
+
+	return
 }
 
-type Config struct {
-	Created      time.Time    `json:"created"`
-	Author       string       `json:"author"`
-	Architecture string       `json:"architecture"`
-	Os           string       `json:"os"`
-	Config       *ImageConfig `json:"config"`
-	RootFs       RootFs       `json:"rootfs"`
-	History      []History    `json:"history"`
+func createManifest(config *Element, layer *[]byte) Manifest {
+	layers := []Element{
+		{
+			MediaType: LayerMediaType,
+			Size:      len(*layer),
+			// Layers must contain the digest of the *gzipped* layer.
+			Digest: Digest(*layer),
+		},
+	}
+
+	return Manifest{
+		SchemaVersion: 2,
+		MediaType:     ManifestMediaType,
+		Config:        *config,
+		Layers:        layers,
+	}
 }