// The code in this file creates a Docker image layer containing the binary of the // application itself. package main 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) 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() } 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() } 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 } 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, } }