func runNew(context *cli.Context) { args := context.Args() if len(args) < 1 { fmt.Printf("args: %v", args) log.Errorf("provide an output file name") return } outImageName := context.String("image-name") if outImageName == "" { log.Errorf("provide an image name") return } fileName := args[0] manifest := &schema.ImageManifest{ ACKind: schema.ImageManifestKind, ACVersion: schema.AppContainerVersion, Name: types.ACIdentifier(outImageName), } aciDir, err := util.PrepareACIDir(manifest, "") if err != nil { log.Fatalf("error prepareing ACI dir %v: %v", aciDir, err) } if err := util.BuildACI(aciDir, fileName, context.Bool("overwrite"), false); err != nil { log.Fatalf("error building the final output ACI: %v", err) } log.Infof("Image %v built successfully", fileName) }
func runRm(context *cli.Context) { s := getStore() args := context.Args() if len(args) < 2 { fmt.Println("There need to be at least two arguments.") fmt.Println(context.Command.Usage) return } // Get the manifest of the base image base := args[0] im, err := util.GetManifestFromImage(base) if err != nil { log.Fatalf("Could not extract manifest from base image: %v", err) } if context.Bool("all-but-last") { im.Dependencies = im.Dependencies[len(im.Dependencies)-1:] } else { for _, arg := range args[1 : len(args)-1] { layer, err := util.ExtractLayerInfo(s, arg) if err != nil { log.Fatalf("error extracting layer info from %s: %v", s, err) } for i, dep := range im.Dependencies { if reflect.DeepEqual(layer.ImageName, dep.ImageName) && reflect.DeepEqual(layer.ImageID, dep.ImageID) { im.Dependencies = append(im.Dependencies[:i], im.Dependencies[i+1:]...) } } } } out := args[len(args)-1] baseFile, err := os.Open(base) if err != nil { log.Fatalf("error opening base ACI: %v", err) } defer baseFile.Close() outFile, err := os.Create(out) if err != nil { log.Fatalf("error creating output ACI: %v", err) } defer outFile.Close() flagImageName := context.String("output-image-name") if flagImageName != "" { im.Name = types.ACIdentifier(flagImageName) } if err := overwriteManifest(baseFile, outFile, im); err != nil { log.Fatalf("error writing to output ACI: %v", err) } }
func TestGetAllKeyrings(t *testing.T) { // Redirect stdout (how to DRY?) origStdout := os.Stdout defer func() { os.Stdout = origStdout }() if devnull, err := os.Create("/dev/null"); err != nil { panic(err) } else { os.Stdout = devnull defer devnull.Close() } ks := newStore() defer os.RemoveAll(ks.Path) defer closeSampleKeys() prefix := types.ACIdentifier("example.com/foo") if _, err := ks.StoreTrustedKey(prefix, openSampleKey(0), sampleKeyFingerprints[0]); err != nil { t.Errorf("Error storing key: %v\n", err) } if _, err := ks.StoreTrustedKey(prefix, openSampleKey(1), sampleKeyFingerprints[1]); err != nil { t.Errorf("Error storing key: %v\n", err) } if _, err := ks.StoreTrustedKey(Root, openSampleKey(2), sampleKeyFingerprints[2]); err != nil { t.Errorf("Error storing key: %v\n", err) } kr, err := ks.GetAllKeys() if err != nil { t.Errorf("Error getting all keyrings: %v\n", err) t.FailNow() } kc := countKeys(kr) if len(kc) != 2 { t.Errorf("Got %d keyrings, expected 2: %v\n", len(kc), kc) } if rkc, ok := kc[Root]; !ok { t.Error("No root keyring") } else if rkc != 1 { t.Error("Root keyring %d long, expected 1\n", rkc) } if pkc, ok := kc[prefix]; !ok { t.Error("No prefix keyring") } else if pkc != 2 { t.Error("Prefix keyring %d long, expected 2\n", kc) } }
func (this *oci2rkt) genImageManifest() { // 1. Assemble "acKind" field this.imageManifest.ACKind = "ImageManifest" // 2. Assemble "acVersion" field this.imageManifest.ACVersion = schema.AppContainerVersion // 3. Assemble "name" field this.imageManifest.Name = "oci" // 4. Assemble "labels" field // 4.1 "version" label := new(types.Label) label.Name = types.ACIdentifier("version") label.Value = this.linuxSpec.Version this.imageManifest.Labels = append(this.imageManifest.Labels, *label) // 4.2 "os" label = new(types.Label) label.Name = types.ACIdentifier("os") label.Value = this.linuxSpec.Platform.OS this.imageManifest.Labels = append(this.imageManifest.Labels, *label) // 4.3 "arch" label = new(types.Label) label.Name = types.ACIdentifier("arch") label.Value = this.linuxSpec.Platform.Arch this.imageManifest.Labels = append(this.imageManifest.Labels, *label) // 5. Assemble "app" field app := new(types.App) // 5.1 "exec" //fmt.Printf("this.linuxSpec.Process.Args=%v\n", this.linuxSpec.Process.Args) app.Exec = this.linuxSpec.Process.Args // 5.2 "user" app.User = fmt.Sprintf("%d", this.linuxSpec.Process.User.UID) // 5.3 "group" app.Group = fmt.Sprintf("%d", this.linuxSpec.Process.User.GID) this.imageManifest.App = app }
func runAdd(context *cli.Context) { s := getStore() args := context.Args() if len(args) < 2 { fmt.Println("There need to be at least two arguments.") fmt.Println(context.Command.Usage) } var dependencies types.Dependencies for _, arg := range args[:len(args)-1] { layer, err := util.ExtractLayerInfo(s, arg) if err != nil { log.Fatalf("error extracting layer info from %s: %v", s, err) } dependencies = append(dependencies, layer) } out := args[len(args)-1] outImageName := context.String("output-image-name") manifest := &schema.ImageManifest{ ACKind: schema.ImageManifestKind, ACVersion: schema.AppContainerVersion, Name: types.ACIdentifier(outImageName), Dependencies: dependencies, } aciDir, err := util.PrepareACIDir(manifest, "") if err != nil { log.Fatalf("error prepareing ACI dir %v: %v", aciDir, err) } if err := util.BuildACI(aciDir, out, true, false); err != nil { log.Fatalf("error building the final output ACI: %v", err) } }
func genManifest(path string) *schema.ImageManifest { // Get runtime.json and config.json runtimePath := path + "/runtime.json" configPath := path + "/config.json" runtime, err := ioutil.ReadFile(runtimePath) if err != nil { if debugEnabled { log.Printf("Open file runtime.json failed: %v", err) } return nil } config, err := ioutil.ReadFile(configPath) if err != nil { if debugEnabled { log.Printf("Open file config.json failed: %v", err) } return nil } var spec specs.LinuxSpec err = json.Unmarshal(config, &spec) if err != nil { if debugEnabled { log.Printf("Unmarshal config.json failed: %v", err) } return nil } var runSpec specs.LinuxRuntimeSpec err = json.Unmarshal(runtime, &runSpec) if err != nil { if debugEnabled { log.Printf("Unmarshal runtime.json failed: %v", err) } return nil } // Begin to convert runtime.json/config.json to manifest m := new(schema.ImageManifest) // 1. Assemble "acKind" field m.ACKind = schema.ImageManifestKind // 2. Assemble "acVersion" field m.ACVersion = schema.AppContainerVersion // 3. Assemble "name" field m.Name = types.ACIdentifier(manifestName) // 4. Assemble "labels" field // 4.1 "version" label := new(types.Label) label.Name = types.ACIdentifier("version") label.Value = spec.Version m.Labels = append(m.Labels, *label) // 4.2 "os" label = new(types.Label) label.Name = types.ACIdentifier("os") label.Value = spec.Platform.OS m.Labels = append(m.Labels, *label) // 4.3 "arch" label = new(types.Label) label.Name = types.ACIdentifier("arch") label.Value = spec.Platform.Arch m.Labels = append(m.Labels, *label) // 5. Assemble "app" field app := new(types.App) // 5.1 "exec" app.Exec = spec.Process.Args prefixDir := "" //var exeStr string if app.Exec == nil { app.Exec = append(app.Exec, "/bin/sh") } else { if !filepath.IsAbs(app.Exec[0]) { if spec.Process.Cwd == "" { prefixDir = "/" } else { prefixDir = spec.Process.Cwd } } app.Exec[0] = prefixDir + app.Exec[0] } // 5.2 "user" app.User = fmt.Sprintf("%d", spec.Process.User.UID) // 5.3 "group" app.Group = fmt.Sprintf("%d", spec.Process.User.GID) // 5.4 "eventHandlers" event := new(types.EventHandler) event.Name = "pre-start" for index := range runSpec.Hooks.Prestart { event.Exec = append(event.Exec, runSpec.Hooks.Prestart[index].Path) event.Exec = append(event.Exec, runSpec.Hooks.Prestart[index].Args...) event.Exec = append(event.Exec, runSpec.Hooks.Prestart[index].Env...) } if len(event.Exec) == 0 { event.Exec = append(event.Exec, "/bin/echo") event.Exec = append(event.Exec, "-n") } app.EventHandlers = append(app.EventHandlers, *event) event = new(types.EventHandler) event.Name = "post-stop" for index := range runSpec.Hooks.Poststop { event.Exec = append(event.Exec, runSpec.Hooks.Poststop[index].Path) event.Exec = append(event.Exec, runSpec.Hooks.Poststop[index].Args...) event.Exec = append(event.Exec, runSpec.Hooks.Poststop[index].Env...) } if len(event.Exec) == 0 { event.Exec = append(event.Exec, "/bin/echo") event.Exec = append(event.Exec, "-n") } app.EventHandlers = append(app.EventHandlers, *event) // 5.5 "workingDirectory" app.WorkingDirectory = spec.Process.Cwd // 5.6 "environment" env := new(types.EnvironmentVariable) for index := range spec.Process.Env { s := strings.Split(spec.Process.Env[index], "=") env.Name = s[0] env.Value = s[1] app.Environment = append(app.Environment, *env) } // 5.7 "mountPoints" for index := range spec.Mounts { mount := new(types.MountPoint) mount.Name = types.ACName(spec.Mounts[index].Name) mount.Path = spec.Mounts[index].Path mount.ReadOnly = false app.MountPoints = append(app.MountPoints, *mount) } // 5.8 "ports" // 5.9 "isolators" if runSpec.Linux.Resources != nil { if runSpec.Linux.Resources.CPU.Quota != 0 { cpuLimt := new(ResourceCPU) cpuLimt.Limit = fmt.Sprintf("%dm", runSpec.Linux.Resources.CPU.Quota) isolator := new(types.Isolator) isolator.Name = types.ACIdentifier("resource/cpu") bytes, _ := json.Marshal(cpuLimt) valueRaw := json.RawMessage(bytes) isolator.ValueRaw = &valueRaw app.Isolators = append(app.Isolators, *isolator) } if runSpec.Linux.Resources.Memory.Limit != 0 { memLimt := new(ResourceMem) memLimt.Limit = fmt.Sprintf("%dG", runSpec.Linux.Resources.Memory.Limit/(1024*1024*1024)) isolator := new(types.Isolator) isolator.Name = types.ACIdentifier("resource/memory") bytes, _ := json.Marshal(memLimt) valueRaw := json.RawMessage(bytes) isolator.ValueRaw = &valueRaw app.Isolators = append(app.Isolators, *isolator) } } if len(spec.Linux.Capabilities) != 0 { isolatorCapSet := new(IsolatorCapSet) isolatorCapSet.Sets = append(isolatorCapSet.Sets, spec.Linux.Capabilities...) isolator := new(types.Isolator) isolator.Name = types.ACIdentifier(types.LinuxCapabilitiesRetainSetName) bytes, _ := json.Marshal(isolatorCapSet) valueRaw := json.RawMessage(bytes) isolator.ValueRaw = &valueRaw app.Isolators = append(app.Isolators, *isolator) } // 6. "annotations" // 7. "dependencies" // 8. "pathWhitelist" m.App = app return m }
func NewAPIServiceListInspectPodsTest() testutils.Test { return testutils.TestFunc(func(t *testing.T) { ctx := testutils.NewRktRunCtx() defer ctx.Cleanup() svc := startAPIService(t, ctx) defer stopAPIService(t, svc) c, conn := newAPIClientOrFail(t, "localhost:15441") defer conn.Close() resp, err := c.ListPods(context.Background(), &v1alpha.ListPodsRequest{}) if err != nil { t.Fatalf("Unexpected error: %v", err) } if len(resp.Pods) != 0 { t.Errorf("Unexpected result: %v, should see zero pods", resp.Pods) } patches := []string{"--exec=/inspect --print-msg=HELLO_API --exit-code=0"} imageHash, err := patchImportAndFetchHash("rkt-inspect-print.aci", patches, t, ctx) if err != nil { t.Fatalf("%v", err) } imgID, err := types.NewHash(imageHash) if err != nil { t.Fatalf("Cannot generate types.Hash from %v: %v", imageHash, err) } podManifests := []struct { mfst schema.PodManifest net string expectedExitCode int }{ { // 1, Good pod. schema.PodManifest{ ACKind: schema.PodManifestKind, ACVersion: schema.AppContainerVersion, Apps: []schema.RuntimeApp{ { Name: types.ACName("rkt-inspect"), Image: schema.RuntimeImage{ Name: types.MustACIdentifier("coreos.com/rkt-inspect"), ID: *imgID, }, Annotations: []types.Annotation{{Name: types.ACIdentifier("app-test"), Value: "app-test"}}, }, }, Annotations: []types.Annotation{ {Name: types.ACIdentifier("test"), Value: "test"}, }, }, "default", 0, }, { // 2, Bad pod, won't be launched correctly. schema.PodManifest{ ACKind: schema.PodManifestKind, ACVersion: schema.AppContainerVersion, Apps: []schema.RuntimeApp{ { Name: types.ACName("rkt-inspect"), Image: schema.RuntimeImage{ Name: types.MustACIdentifier("coreos.com/rkt-inspect"), ID: *imgID, }, }, }, }, "non-existent-network", 254, }, } // Launch the pods. for _, entry := range podManifests { manifestFile := generatePodManifestFile(t, &entry.mfst) defer os.Remove(manifestFile) runCmd := fmt.Sprintf("%s run --net=%s --pod-manifest=%s", ctx.Cmd(), entry.net, manifestFile) waitOrFail(t, spawnOrFail(t, runCmd), entry.expectedExitCode) } time.Sleep(delta) gcCmd := fmt.Sprintf("%s gc --mark-only=true", ctx.Cmd()) waitOrFail(t, spawnOrFail(t, gcCmd), 0) gcTime := time.Now() // ListPods(detail=false). resp, err = c.ListPods(context.Background(), &v1alpha.ListPodsRequest{}) if err != nil { t.Fatalf("Unexpected error: %v", err) } if len(resp.Pods) != len(podManifests) { t.Errorf("Unexpected result: %v, should see %v pods", len(resp.Pods), len(podManifests)) } for _, p := range resp.Pods { checkPodBasicsWithGCTime(t, ctx, p, gcTime) // Test InspectPod(). inspectResp, err := c.InspectPod(context.Background(), &v1alpha.InspectPodRequest{Id: p.Id}) if err != nil { t.Fatalf("Unexpected error: %v", err) } checkPodDetails(t, ctx, inspectResp.Pod) } // ListPods(detail=true). resp, err = c.ListPods(context.Background(), &v1alpha.ListPodsRequest{Detail: true}) if err != nil { t.Fatalf("Unexpected error: %v", err) } if len(resp.Pods) != len(podManifests) { t.Errorf("Unexpected result: %v, should see %v pods", len(resp.Pods), len(podManifests)) } for _, p := range resp.Pods { checkPodDetails(t, ctx, p) } // ListPods with corrupt pod directory // Note that we don't checkPodDetails here, the failure this is testing is // the api server panicing, which results in a list call hanging for ages // and then failing. // TODO: do further validation on the partial pods returned for _, p := range resp.Pods { numRemoved := 0 podDir := getPodDir(t, ctx, p.Id) filepath.Walk(filepath.Join(podDir, "appsinfo"), filepath.WalkFunc(func(path string, info os.FileInfo, err error) error { if err != nil { return err } if info.Name() == "manifest" { os.Remove(path) numRemoved++ } return nil })) if numRemoved == 0 { t.Fatalf("Expected to remove at least one app manifest for pod %v", p) } } // ListPods(detail=true). resp, err = c.ListPods(context.Background(), &v1alpha.ListPodsRequest{Detail: true}) if err != nil { t.Fatalf("Unexpected error: %v", err) } if len(resp.Pods) != len(podManifests) { t.Fatalf("Expected %v pods, got %v pods", len(podManifests), len(resp.Pods)) } }) }
import ( "bytes" "io" "io/ioutil" "os" "path/filepath" "strings" "github.com/juju/errors" "golang.org/x/crypto/openpgp" "github.com/appc/spec/schema/types" ) // Intentionally invalid ACIdentifier to mark root key const Root = types.ACIdentifier("@") type Keystore struct { Path string } func New(path string) *Keystore { return &Keystore{path} } func (ks *Keystore) prefixPath(prefix types.ACIdentifier) string { if prefix.Empty() { panic("Empty prefix!") } return filepath.Join(ks.Path, strings.Replace(string(prefix), "/", ",", -1)) }
func TestGetKeyring(t *testing.T) { // Redirect stdout (how to DRY?) origStdout := os.Stdout defer func() { os.Stdout = origStdout }() if devnull, err := os.Create("/dev/null"); err != nil { panic(err) } else { os.Stdout = devnull defer devnull.Close() } ks := newStore() defer os.RemoveAll(ks.Path) defer closeSampleKeys() if _, err := ks.StoreTrustedKey(types.ACIdentifier("example.com/foo"), openSampleKey(0), sampleKeyFingerprints[0]); err != nil { t.Errorf("Error storing key: %v\n", err) } if _, err := ks.StoreTrustedKey(types.ACIdentifier("example.com/foo/bar"), openSampleKey(1), sampleKeyFingerprints[1]); err != nil { t.Errorf("Error storing key: %v\n", err) } checkKeyCount(t, ks, map[types.ACIdentifier]int{ types.ACIdentifier("eggsample.com"): 0, types.ACIdentifier("eggsample.com/foo"): 0, types.ACIdentifier("eggsample.com/foo/bar"): 0, types.ACIdentifier("example.com"): 0, types.ACIdentifier("example.com/foo"): 1, types.ACIdentifier("example.com/foo/baz"): 1, types.ACIdentifier("example.com/foo/bar"): 2, types.ACIdentifier("example.com/foo/bar/baz"): 2, types.ACIdentifier("example.com/foobar"): 1, types.ACIdentifier("example.com/baz"): 0, }) if _, err := ks.StoreTrustedKey(Root, openSampleKey(2), sampleKeyFingerprints[2]); err != nil { t.Errorf("Error storing key: %v\n", err) } checkKeyCount(t, ks, map[types.ACIdentifier]int{ types.ACIdentifier("eggsample.com"): 1, types.ACIdentifier("eggsample.com/foo"): 1, types.ACIdentifier("eggsample.com/foo/bar"): 1, types.ACIdentifier("example.com"): 1, types.ACIdentifier("example.com/foo"): 2, types.ACIdentifier("example.com/foo/baz"): 2, types.ACIdentifier("example.com/foo/bar"): 3, types.ACIdentifier("example.com/foo/bar/baz"): 3, types.ACIdentifier("example.com/foobar"): 2, types.ACIdentifier("example.com/baz"): 1, }) }
func TestImportPrefixEscaped(t *testing.T) { testImport(t, types.ACIdentifier("example.com/foo"), "example.com,foo") }
func TestImportPrefix(t *testing.T) { testImport(t, types.ACIdentifier("example.com"), "example.com") }
func TestMakePodManifestAnnotations(t *testing.T) { ctrl := gomock.NewController(t) defer ctrl.Finish() fr := newFakeRktInterface() fs := newFakeSystemd() r := &Runtime{apisvc: fr, systemd: fs} testCases := []struct { in *api.Pod out *appcschema.PodManifest outerr error }{ { in: &api.Pod{ ObjectMeta: api.ObjectMeta{ UID: "uid-1", Name: "name-1", Namespace: "namespace-1", Annotations: map[string]string{ k8sRktStage1NameAnno: "stage1-override-img", }, }, }, out: &appcschema.PodManifest{ Annotations: []appctypes.Annotation{ { Name: appctypes.ACIdentifier(k8sRktStage1NameAnno), Value: "stage1-override-img", }, { Name: appctypes.ACIdentifier(types.KubernetesPodUIDLabel), Value: "uid-1", }, { Name: appctypes.ACIdentifier(types.KubernetesPodNameLabel), Value: "name-1", }, { Name: appctypes.ACIdentifier(k8sRktKubeletAnno), Value: "true", }, { Name: appctypes.ACIdentifier(types.KubernetesPodNamespaceLabel), Value: "namespace-1", }, { Name: appctypes.ACIdentifier(k8sRktRestartCountAnno), Value: "0", }, }, }, }, } for i, testCase := range testCases { hint := fmt.Sprintf("case #%d", i) result, err := r.makePodManifest(testCase.in, "", []api.Secret{}) assert.Equal(t, err, testCase.outerr, hint) if err == nil { sort.Sort(annotationsByName(result.Annotations)) sort.Sort(annotationsByName(testCase.out.Annotations)) assert.Equal(t, result.Annotations, testCase.out.Annotations, hint) } } }
func TestAPIServiceListInspectPods(t *testing.T) { ctx := testutils.NewRktRunCtx() defer ctx.Cleanup() svc := startAPIService(t, ctx) defer stopAPIService(t, svc) c, conn := newAPIClientOrFail(t, "localhost:15441") defer conn.Close() resp, err := c.ListPods(context.Background(), &v1alpha.ListPodsRequest{}) if err != nil { t.Fatalf("Unexpected error: %v", err) } if len(resp.Pods) != 0 { t.Errorf("Unexpected result: %v, should see zero pods", resp.Pods) } patches := []string{"--exec=/inspect --print-msg=HELLO_API --exit-code=0"} imageHash := patchImportAndFetchHash("rkt-inspect-print.aci", patches, t, ctx) imgID, err := types.NewHash(imageHash) if err != nil { t.Fatalf("Cannot generate types.Hash from %v: %v", imageHash, err) } pm := schema.BlankPodManifest() pm.Apps = []schema.RuntimeApp{ { Name: types.ACName("rkt-inspect"), Image: schema.RuntimeImage{ Name: types.MustACIdentifier("coreos.com/rkt-inspect"), ID: *imgID, }, Annotations: []types.Annotation{{Name: types.ACIdentifier("app-test"), Value: "app-test"}}, }, } pm.Annotations = []types.Annotation{{Name: types.ACIdentifier("test"), Value: "test"}} manifestFile := generatePodManifestFile(t, pm) defer os.Remove(manifestFile) runCmd := fmt.Sprintf("%s run --pod-manifest=%s", ctx.Cmd(), manifestFile) waitOrFail(t, spawnOrFail(t, runCmd), 0) gcCmd := fmt.Sprintf("%s gc --mark-only=true", ctx.Cmd()) waitOrFail(t, spawnOrFail(t, gcCmd), 0) gcTime := time.Now() // ListPods(detail=false). resp, err = c.ListPods(context.Background(), &v1alpha.ListPodsRequest{}) if err != nil { t.Fatalf("Unexpected error: %v", err) } if len(resp.Pods) == 0 { t.Errorf("Unexpected result: %v, should see non-zero pods", resp.Pods) } for _, p := range resp.Pods { checkPodBasicsWithGCTime(t, ctx, p, gcTime) // Test InspectPod(). inspectResp, err := c.InspectPod(context.Background(), &v1alpha.InspectPodRequest{Id: p.Id}) if err != nil { t.Fatalf("Unexpected error: %v", err) } checkPodDetails(t, ctx, inspectResp.Pod) // Test Apps. for i, app := range p.Apps { checkAnnotations(t, pm.Apps[i].Annotations, app.Annotations) } } // ListPods(detail=true). resp, err = c.ListPods(context.Background(), &v1alpha.ListPodsRequest{Detail: true}) if err != nil { t.Fatalf("Unexpected error: %v", err) } if len(resp.Pods) == 0 { t.Errorf("Unexpected result: %v, should see non-zero pods", resp.Pods) } for _, p := range resp.Pods { checkPodDetails(t, ctx, p) } }
func TestRankedName(t *testing.T) { tests := []struct { li string lj string expected bool }{ { "version", "os", true, }, { "os", "version", false, }, { "version", "a", true, }, { "a", "os", false, }, { "os", "a", true, }, { "", "os", false, }, { "os", "", true, }, { "a", "version", false, }, { "a", "b", true, }, { "b", "a", false, }, { "a", "a", false, }, { "version", "version", false, }, } for i, tt := range tests { li := types.Label{ Name: types.ACIdentifier(tt.li), } lj := types.Label{ Name: types.ACIdentifier(tt.lj), } if result := RankedName(li, lj); result != tt.expected { t.Errorf("test %d expected %q < %q = %t but got %t", i, tt.li, tt.lj, tt.expected, result) } } }
// Write will produce the resulting ACI from the current build context, saving // it to the given path, optionally signing it. func (a *ACBuild) Write(output string, overwrite, sign bool, gpgflags []string) (err error) { if err = a.lock(); err != nil { return err } defer func() { if err1 := a.unlock(); err == nil { err = err1 } }() man, err := util.GetManifest(a.CurrentACIPath) if err != nil { return err } if man.App != nil && len(man.App.Exec) == 0 { fmt.Fprintf(os.Stderr, "warning: exec command was never set.\n") } if man.Name == types.ACIdentifier(placeholdername) { return fmt.Errorf("can't write ACI, name was never set") } fileFlags := os.O_CREATE | os.O_WRONLY _, err = os.Stat(output) switch { case os.IsNotExist(err): break case err != nil: return err default: if !overwrite { return fmt.Errorf("ACI already exists: %s", output) } fileFlags |= os.O_TRUNC } // open/create the aci file ofile, err := os.OpenFile(output, fileFlags, 0644) if err != nil { return err } defer ofile.Close() defer func() { // When write is done, if an error is encountered remove the partial // ACI that had been written. if err != nil { os.Remove(output) os.Remove(output + ".asc") } }() // setup compression gzwriter := gzip.NewWriter(ofile) defer gzwriter.Close() // create the aci writer aw := aci.NewImageWriter(*man, tar.NewWriter(gzwriter)) err = filepath.Walk(a.CurrentACIPath, aci.BuildWalker(a.CurrentACIPath, aw, nil)) defer aw.Close() if err != nil { pathErr, ok := err.(*os.PathError) if !ok { fmt.Printf("not a path error!\n") return err } syscallErrno, ok := pathErr.Err.(syscall.Errno) if !ok { fmt.Printf("not a syscall errno!\n") return err } if pathErr.Op == "open" && syscallErrno != syscall.EACCES { return err } problemPath := pathErr.Path[len(path.Join(a.CurrentACIPath, aci.RootfsDir)):] return fmt.Errorf("%q: permission denied - call write as root", problemPath) } if sign { err = signACI(output, output+".asc", gpgflags) if err != nil { return err } } return nil }
package acutil import "github.com/appc/spec/schema/types" // Empty ACName const ACNoName = types.ACName("") // Empty ACIdentifier const ACNoIdentifier = types.ACIdentifier("")
func TestUntrustKey(t *testing.T) { // Redirect stdout (how to DRY?) origStdout := os.Stdout defer func() { os.Stdout = origStdout }() if devnull, err := os.Create("/dev/null"); err != nil { panic(err) } else { os.Stdout = devnull defer devnull.Close() } ks := newStore() defer os.RemoveAll(ks.Path) defer closeSampleKeys() prefix := types.ACIdentifier("example.com/foo") prefix2 := types.ACIdentifier("example.org/bar") if _, err := ks.StoreTrustedKey(prefix, openSampleKey(0), sampleKeyFingerprints[0]); err != nil { t.Errorf("Error storing key: %v\n", err) } if _, err := ks.StoreTrustedKey(prefix, openSampleKey(1), sampleKeyFingerprints[1]); err != nil { t.Errorf("Error storing key: %v\n", err) } if _, err := ks.StoreTrustedKey(prefix2, openSampleKey(1), sampleKeyFingerprints[1]); err != nil { t.Errorf("Error storing key: %v\n", err) } if _, err := ks.StoreTrustedKey(prefix2, openSampleKey(2), sampleKeyFingerprints[2]); err != nil { t.Errorf("Error storing key: %v\n", err) } if _, err := ks.StoreTrustedKey(Root, openSampleKey(2), sampleKeyFingerprints[2]); err != nil { t.Errorf("Error storing key: %v\n", err) } kr, err := ks.GetAllKeys() if err != nil { panic(err) } kc := countKeys(kr) if kc[Root] != 1 || kc[prefix] != 2 || kc[prefix2] != 2 { t.Errorf("Wrong keyrings even before remove: %v\n", kc) } if prefixes, err := ks.UntrustKey(sampleKeyFingerprints[2]); err != nil { t.Errorf("Error untrusting key: %v %v\n", err, prefixes) } else { sort.Sort(acNames(prefixes)) expectedPrefixes := []types.ACIdentifier{Root, prefix2} if !reflect.DeepEqual(prefixes, expectedPrefixes) { t.Errorf("Expected removed prefixes to be %v, got %v instead.\n", expectedPrefixes, prefixes) } } kr, err = ks.GetAllKeys() if err != nil { panic(err) } kc = countKeys(kr) if kc[Root] != 0 || kc[prefix] != 2 || kc[prefix2] != 1 { t.Errorf("Wrong counts after remove: %v\n", kc) } }
func runExec(context *cli.Context) { flagIn := context.String("in") flagCmd := context.String("cmd") flagOut := context.String("out") if flagIn == "" || flagCmd == "" || flagOut == "" { log.Fatalf("--in, --cmd, and --out need to be set") } flagNoOverlay := context.Bool("no-overlay") useOverlay := util.SupportsOverlay() && !flagNoOverlay s := getStore() mounts, err := getMounts(context) if err != nil { log.Fatalf("error parsing mounts: %v", err) } // Render the given image in tree store imageHash, err := renderInStore(s, flagIn) if err != nil { log.Fatalf("error rendering image in store: %s", err) } imagePath := s.GetTreeStorePath(imageHash) // Create a tmp directory tmpDir, err := ioutil.TempDir("", "acbuild-") if err != nil { log.Fatalf("error creating temporary directory: %s", err) } // Copy the manifest into the tmp directory if err := shutil.CopyFile(filepath.Join(imagePath, aci.ManifestFile), filepath.Join(tmpDir, aci.ManifestFile), true); err != nil { log.Fatalf("error copying manifest to a temporary directory: %s", err) } // Extract a ImageManifest from the manifest file manifestFile, err := os.Open(filepath.Join(tmpDir, aci.ManifestFile)) if err != nil { log.Fatalf("error opening the copied manifest file: %v", err) } manifestContent, err := ioutil.ReadAll(manifestFile) if err != nil { log.Fatalf("error reading the copied manifest file: %v", err) } im := &schema.ImageManifest{} if err := im.UnmarshalJSON(manifestContent); err != nil { log.Fatalf("error unmarshalling JSON to manifest: %v", err) } // If an output image name is not given, we grab it from the input ACI flagImageName := context.String("output-image-name") if flagImageName == "" { flagImageName = string(im.Name) } flagJail := context.Bool("jail") // If the system supports overlayfs, use it. // Otherwise, copy the entire rendered image to a working directory. storeRootfsDir := filepath.Join(imagePath, aci.RootfsDir) tmpRootfsDir := filepath.Join(tmpDir, aci.RootfsDir) if useOverlay { upperDir, err := mountOverlayfs(tmpRootfsDir, storeRootfsDir) if err != nil { log.Fatalf("error mounting overlayfs: %v", err) } // Note that defer functions are not run if the program // exits via os.Exit() and by extension log.Fatal(), which // is the behaviour that we want. defer unmountOverlayfs(tmpRootfsDir) if err := runCmdInDir(im, flagCmd, tmpRootfsDir, flagJail, mounts); err != nil { log.Fatalf("error executing command: %v", err) } // We store the delta (i.e. side effects of the executed command) into its own ACI // The name of the ACI is a hash of (command, hash of input image). This will make // implementing caching easier in the future. deltaACIName, err := util.Hash(flagCmd, imageHash) if err != nil { log.Fatalf("error hashing (%s %s): %s", flagCmd, imageHash, err) } deltaManifest := &schema.ImageManifest{ ACKind: schema.ImageManifestKind, ACVersion: schema.AppContainerVersion, Name: types.ACIdentifier(deltaACIName), } deltaACIDir, err := util.PrepareACIDir(deltaManifest, upperDir) if err != nil { log.Fatalf("error preparing delta ACI dir: %v", err) } // Create a temp directory for placing delta ACI deltaACITempDir, err := ioutil.TempDir("", "") if err != nil { log.Fatalf("error creating temp dir to put delta ACI: %v", err) } deltaACIPath := filepath.Join(deltaACITempDir, "delta.aci") // Build the delta ACI if err := util.BuildACI(deltaACIDir, deltaACIPath, true, false); err != nil { log.Fatalf("error building delta ACI: %v", err) } // Put the delta ACI into tree store deltaACIFile, err := os.Open(deltaACIPath) if err != nil { log.Fatalf("error opening the delta ACI file: %v", err) } deltaKey, err := s.WriteACI(deltaACIFile, false) if err != nil { log.Fatalf("error writing the delta ACI into the tree store: %v", err) } deltaKeyHash, err := types.NewHash(deltaKey) if err != nil { log.Fatalf("error creating hash from an image ID (%s): %v", deltaKeyHash, err) } // The manifest for the output ACI manifest := &schema.ImageManifest{ ACKind: schema.ImageManifestKind, ACVersion: schema.AppContainerVersion, Name: types.ACIdentifier(flagImageName), } if context.Bool("split") { layers, err := util.ExtractLayers(s, flagIn) if err != nil { log.Fatalf("error extracting layers from %s: %v", flagIn, err) } manifest.Dependencies = append(manifest.Dependencies, layers...) manifest.Dependencies = append(manifest.Dependencies, types.Dependency{ ImageName: types.ACIdentifier(deltaACIName), ImageID: deltaKeyHash, }) } else { layer, err := util.ExtractLayerInfo(s, flagIn) if err != nil { log.Fatalf("error extracting layer info from input ACI: %v", err) } // two layers: // 1. The original ACI // 2. The delta ACI manifest.Dependencies = types.Dependencies{ layer, types.Dependency{ ImageName: types.ACIdentifier(deltaACIName), ImageID: deltaKeyHash, }, } } // The rootfs is empty aciDir, err := util.PrepareACIDir(manifest, "") if err != nil { log.Fatalf("error prepareing ACI dir %v: %v", aciDir, err) } // Build the output ACI if err := util.BuildACI(aciDir, flagOut, true, false); err != nil { log.Fatalf("error building the final output ACI: %v", err) } } else { if err := shutil.CopyTree(storeRootfsDir, tmpRootfsDir, &shutil.CopyTreeOptions{ Symlinks: true, IgnoreDanglingSymlinks: true, CopyFunction: shutil.Copy, }); err != nil { log.Fatalf("error copying rootfs to a temporary directory: %v", err) } if err := runCmdInDir(im, flagCmd, tmpRootfsDir, flagJail, mounts); err != nil { log.Fatalf("error executing command: %v", err) } err = util.BuildACI(tmpDir, flagOut, true, false) if err != nil { log.Fatalf("error building output ACI image: %v", err) } } }