func (e *generator) createArtifact() { manifest := &ct.ImageManifest{ Type: ct.ImageManifestTypeV1, Meta: map[string]string{"foo": "bar"}, Entrypoints: map[string]*ct.ImageEntrypoint{ "_default": { Env: map[string]string{"key": "default-val"}, WorkingDir: "/", Args: []string{"bash"}, }, "web": { Env: map[string]string{"key": "other-val"}, WorkingDir: "/app", Args: []string{"/bin/web-server"}, Uid: typeconv.Uint32Ptr(1000), Gid: typeconv.Uint32Ptr(1000), }, }, Rootfs: []*ct.ImageRootfs{ { Platform: &ct.ImagePlatform{ Architecture: "amd64", OS: "linux", }, Layers: []*ct.ImageLayer{ { ID: "34510d7fb6c0b108121b5f5f2e86f0c4c27a1e6d1dbbbd131189a65fab641775", Type: ct.ImageLayerTypeSquashfs, Length: 34570240, Hashes: map[string]string{"sha512_256": "34510d7fb6c0b108121b5f5f2e86f0c4c27a1e6d1dbbbd131189a65fab641775"}, }, { ID: "d038205ed955b94a475dfaf81b4e593b77d3b74420d673fb0bf0a7ca8c4cc345", Type: ct.ImageLayerTypeSquashfs, Length: 92368896, Hashes: map[string]string{"sha512_256": "d038205ed955b94a475dfaf81b4e593b77d3b74420d673fb0bf0a7ca8c4cc345"}, }, }, }, }, } artifact := &ct.Artifact{ Type: ct.ArtifactTypeFlynn, URI: e.resourceIds["SLUGRUNNER_IMAGE_URI"], RawManifest: manifest.RawManifest(), Hashes: manifest.Hashes(), Size: int64(len(manifest.RawManifest())), LayerURLTemplate: "https://dl.flynn.io/tuf?target=/layers/{id}.squashfs", } err := e.client.CreateArtifact(artifact) if err != nil { log.Fatal(err) } e.resourceIds["artifact"] = artifact.ID }
func run(dir string, uid, gid int) error { client, err := controller.NewClient("", os.Getenv("CONTROLLER_KEY")) if err != nil { return err } // create a squashfs layer layer, err := ioutil.TempFile("", "squashfs-") if err != nil { return err } defer os.Remove(layer.Name()) defer layer.Close() if out, err := exec.Command("mksquashfs", dir, layer.Name(), "-noappend").CombinedOutput(); err != nil { return fmt.Errorf("mksquashfs error: %s: %s", err, out) } h := sha512.New512_256() length, err := io.Copy(h, layer) if err != nil { return err } layerSHA := hex.EncodeToString(h.Sum(nil)) // upload the layer to the blobstore if _, err := layer.Seek(0, os.SEEK_SET); err != nil { return err } layerURL := fmt.Sprintf("http://blobstore.discoverd/slugs/layers/%s.squashfs", layerSHA) if err := upload(layer, layerURL); err != nil { return err } manifest := &ct.ImageManifest{ Type: ct.ImageManifestTypeV1, // TODO: parse Procfile / .release and add to manifest.Entrypoints Entrypoints: map[string]*ct.ImageEntrypoint{ "_default": { Env: map[string]string{ "PATH": "/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin", "TERM": "xterm", "HOME": "/app", }, WorkingDir: "/app", Args: []string{"/runner/init", "bash"}, Uid: typeconv.Uint32Ptr(uint32(uid)), Gid: typeconv.Uint32Ptr(uint32(gid)), }, }, Rootfs: []*ct.ImageRootfs{{ Platform: ct.DefaultImagePlatform, Layers: []*ct.ImageLayer{{ ID: layerSHA, Type: ct.ImageLayerTypeSquashfs, Length: length, Hashes: map[string]string{"sha512_256": layerSHA}, }}, }}, } rawManifest := manifest.RawManifest() manifestURL := fmt.Sprintf("http://blobstore.discoverd/slugs/images/%s.json", manifest.ID()) if err := upload(bytes.NewReader(rawManifest), manifestURL); err != nil { return err } artifact := &ct.Artifact{ ID: os.Getenv("SLUG_IMAGE_ID"), Type: ct.ArtifactTypeFlynn, URI: manifestURL, Meta: map[string]string{ "blobstore": "true", }, RawManifest: rawManifest, Hashes: manifest.Hashes(), Size: int64(len(rawManifest)), LayerURLTemplate: "http://blobstore.discoverd/slugs/layers/{id}.squashfs", } // create artifact if err := client.CreateArtifact(artifact); err != nil { return err } fmt.Printf("-----> Compiled slug size is %s\n", units.BytesSize(float64(length))) return nil }