// AddDependency will add a dependency with the given name, id, labels, and size // to the untarred ACI stored at acipath. If the dependency already exists its // fields will be updated to the new values. func AddDependency(acipath, imageName, imageId string, labels types.Labels, size uint) error { acid, err := types.NewACIdentifier(imageName) if err != nil { return err } var hash *types.Hash if imageId != "" { var err error hash, err = types.NewHash(imageId) if err != nil { return err } } fn := func(s *schema.ImageManifest) { removeDep(*acid)(s) s.Dependencies = append(s.Dependencies, types.Dependency{ ImageName: *acid, ImageID: hash, Labels: labels, Size: size, }) } return util.ModifyManifest(fn, acipath) }
func runAddDep(cmd *cobra.Command, args []string) (exit int) { if len(args) == 0 { cmd.Usage() return 1 } if len(args) != 1 { stderr("dependency add: incorrect number of arguments") return 1 } if debug { stderr("Adding dependency %q", args[0]) } app, err := discovery.NewAppFromString(args[0]) if err != nil { stderr("dependency add: couldn't parse dependency name: %v", err) return 1 } appcLabels := types.Labels(labels) for name, value := range app.Labels { if _, ok := appcLabels.Get(string(name)); ok { stderr("multiple %s labels specified", name) return 1 } appcLabels = append(appcLabels, types.Label{ Name: name, Value: value, }) } var hash *types.Hash if imageId != "" { var err error hash, err = types.NewHash(imageId) if err != nil { stderr("dependency add: couldn't parse image ID: %v", err) return 1 } } err = newACBuild().AddDependency(app.Name, hash, appcLabels, size) if err != nil { stderr("dependency add: %v", err) return getErrorCode(err) } return 0 }
func TestAdd2Dependencies(t *testing.T) { workingDir := setUpTest(t) defer cleanUpTest(workingDir) _, _, _, err := runACBuild(workingDir, "dependency", "add", depName, "--image-id", depImageID, "--label", newLabel(depLabel1Key, depLabel1Val), "--label", newLabel(depLabel2Key, depLabel2Val), "--size", strconv.Itoa(int(depSize))) if err != nil { t.Fatalf("%v\n", err) } _, _, _, err = runACBuild(workingDir, "dependency", "add", depName2) if err != nil { t.Fatalf("%v\n", err) } hash, err1 := types.NewHash(depImageID) if err1 != nil { panic(err1) } deps := types.Dependencies{ types.Dependency{ ImageName: *types.MustACIdentifier(depName), ImageID: hash, Labels: types.Labels{ types.Label{ Name: *types.MustACIdentifier(depLabel1Key), Value: depLabel1Val, }, types.Label{ Name: *types.MustACIdentifier(depLabel2Key), Value: depLabel2Val, }, }, Size: depSize, }, types.Dependency{ ImageName: *types.MustACIdentifier(depName2), }, } checkManifest(t, workingDir, manWithDeps(deps)) checkEmptyRootfs(t, workingDir) }
// AddDependency will add a dependency with the given name, id, labels, and size // to the untarred ACI stored at a.CurrentACIPath. If the dependency already // exists its fields will be updated to the new values. func (a *ACBuild) AddDependency(imageName, imageId string, labels types.Labels, size uint) (err error) { if err = a.lock(); err != nil { return err } defer func() { if err1 := a.unlock(); err == nil { err = err1 } }() acid, err := types.NewACIdentifier(imageName) if err != nil { return err } var hash *types.Hash if imageId != "" { var err error hash, err = types.NewHash(imageId) if err != nil { return err } } fn := func(s *schema.ImageManifest) error { removeDep(*acid)(s) s.Dependencies = append(s.Dependencies, types.Dependency{ ImageName: *acid, ImageID: hash, Labels: labels, Size: size, }) return nil } return util.ModifyManifest(fn, a.CurrentACIPath) }
func TestAddDependencyWithImageID(t *testing.T) { workingDir := setUpTest(t) defer cleanUpTest(workingDir) _, _, _, err := runACBuild(workingDir, "dependency", "add", depName, "--image-id", depImageID) if err != nil { t.Fatalf("%v\n", err) } hash, err1 := types.NewHash(depImageID) if err1 != nil { panic(err1) } deps := types.Dependencies{ types.Dependency{ ImageName: *types.MustACIdentifier(depName), ImageID: hash, }, } checkManifest(t, workingDir, manWithDeps(deps)) checkEmptyRootfs(t, workingDir) }
// Test an image with 1 dep. The parent image has a pathWhiteList. func TestPWLOnlyParent(t *testing.T) { dir, err := ioutil.TempDir("", tstprefix) if err != nil { t.Fatalf("error creating tempdir: %v", err) } defer os.RemoveAll(dir) ds := NewTestStore() imj := ` { "acKind": "ImageManifest", "acVersion": "0.1.1", "name": "example.com/test01", "pathWhitelist" : [ "/a/file01.txt", "/a/file02.txt", "/b/link01.txt", "/c/", "/d/" ] } ` entries := []*testTarEntry{ { contents: imj, header: &tar.Header{ Name: "manifest", Size: int64(len(imj)), }, }, { contents: "hello", header: &tar.Header{ Name: "rootfs/a/file01.txt", Size: 5, }, }, { contents: "hello", header: &tar.Header{ Name: "rootfs/a/file02.txt", Size: 5, }, }, // This should not appear in rendered aci { contents: "hello", header: &tar.Header{ Name: "rootfs/a/file03.txt", Size: 5, }, }, { header: &tar.Header{ Name: "rootfs/b/link01.txt", Linkname: "file01.txt", Typeflag: tar.TypeSymlink, }, }, // The file "rootfs/c/file01.txt" should not appear but a new file "rootfs/c/file02.txt" provided by the upper image should appear. // The directory should be left with its permissions { header: &tar.Header{ Name: "rootfs/c", Typeflag: tar.TypeDir, Mode: 0700, }, }, { contents: "hello", header: &tar.Header{ Name: "rootfs/c/file01.txt", Size: 5, Mode: 0700, }, }, // The file "rootfs/d/file01.txt" should not appear but the directory should be left and also its permissions { header: &tar.Header{ Name: "rootfs/d", Typeflag: tar.TypeDir, Mode: 0700, }, }, { contents: "hello", header: &tar.Header{ Name: "rootfs/d/file01.txt", Size: 5, Mode: 0700, }, }, // The file and the directory should not appear { contents: "hello", header: &tar.Header{ Name: "rootfs/e/file01.txt", Size: 5, Mode: 0700, }, }, } key1, err := newTestACI(entries, dir, ds) if err != nil { t.Fatalf("unexpected error: %v", err) } im, err := createImageManifest(imj) if err != nil { t.Fatalf("unexpected error: %v", err) } image1 := Image{Im: im, Key: key1, Level: 1} imj = ` { "acKind": "ImageManifest", "acVersion": "0.1.1", "name": "example.com/test02" } ` k1, _ := types.NewHash(key1) imj, err = addDependencies(imj, types.Dependency{ ImageName: "example.com/test01", ImageID: k1}, ) entries = []*testTarEntry{ { contents: imj, header: &tar.Header{ Name: "manifest", Size: int64(len(imj)), }, }, { contents: "hellohello", header: &tar.Header{ Name: "rootfs/b/file01.txt", Size: 10, }, }, // New file { contents: "hello", header: &tar.Header{ Name: "rootfs/c/file02.txt", Size: 5, }, }, } expectedFiles := []*fileInfo{ &fileInfo{path: "manifest", typeflag: tar.TypeReg}, &fileInfo{path: "rootfs/a/file01.txt", typeflag: tar.TypeReg, size: 5}, &fileInfo{path: "rootfs/a/file02.txt", typeflag: tar.TypeReg, size: 5}, &fileInfo{path: "rootfs/b/link01.txt", typeflag: tar.TypeSymlink}, &fileInfo{path: "rootfs/b/file01.txt", typeflag: tar.TypeReg, size: 10}, &fileInfo{path: "rootfs/c", typeflag: tar.TypeDir, mode: 0700}, &fileInfo{path: "rootfs/c/file02.txt", typeflag: tar.TypeReg, size: 5}, &fileInfo{path: "rootfs/d", typeflag: tar.TypeDir, mode: 0700}, } key2, err := newTestACI(entries, dir, ds) if err != nil { t.Fatalf("unexpected error: %v", err) } im, err = createImageManifest(imj) if err != nil { t.Fatalf("unexpected error: %v", err) } image2 := Image{Im: im, Key: key2, Level: 0} images := Images{image2, image1} err = checkRenderACIFromList(images, expectedFiles, ds) if err != nil { t.Fatalf("unexpected error: %v", err) } err = checkRenderACI("example.com/test02", expectedFiles, ds) if err != nil { t.Fatalf("unexpected error: %v", err) } }
// Test an image with 1 dep. The upper image overrides a dir provided by a // parent with a non-dir file. func TestFileOvverideDir(t *testing.T) { dir, err := ioutil.TempDir("", tstprefix) if err != nil { t.Fatalf("error creating tempdir: %v", err) } defer os.RemoveAll(dir) ds := NewTestStore() imj := ` { "acKind": "ImageManifest", "acVersion": "0.1.1", "name": "example.com/test01" } ` entries := []*testTarEntry{ { contents: imj, header: &tar.Header{ Name: "manifest", Size: int64(len(imj)), }, }, { header: &tar.Header{ Name: "rootfs/a/b", Typeflag: tar.TypeDir, Mode: 0700, }, }, { header: &tar.Header{ Name: "rootfs/a/b/c", Typeflag: tar.TypeDir, Mode: 0700, }, }, { contents: "hello", header: &tar.Header{ Name: "rootfs/a/b/c/file01", Size: 5, }, }, } key1, err := newTestACI(entries, dir, ds) if err != nil { t.Fatalf("unexpected error: %v", err) } im, err := createImageManifest(imj) if err != nil { t.Fatalf("unexpected error: %v", err) } image1 := Image{Im: im, Key: key1, Level: 1} imj = ` { "acKind": "ImageManifest", "acVersion": "0.1.1", "name": "example.com/test02" } ` k1, _ := types.NewHash(key1) imj, err = addDependencies(imj, types.Dependency{ ImageName: "example.com/test01", ImageID: k1}, ) entries = []*testTarEntry{ { contents: imj, header: &tar.Header{ Name: "manifest", Size: int64(len(imj)), }, }, { contents: "hellohello", header: &tar.Header{ Name: "rootfs/a/b", Size: 10, }, }, } expectedFiles := []*fileInfo{ &fileInfo{path: "manifest", typeflag: tar.TypeReg}, &fileInfo{path: "rootfs/a/b", typeflag: tar.TypeReg, size: 10}, } key2, err := newTestACI(entries, dir, ds) if err != nil { t.Fatalf("unexpected error: %v", err) } im, err = createImageManifest(imj) if err != nil { t.Fatalf("unexpected error: %v", err) } image2 := Image{Im: im, Key: key2, Level: 0} images := Images{image2, image1} err = checkRenderACIFromList(images, expectedFiles, ds) if err != nil { t.Fatalf("unexpected error: %v", err) } err = checkRenderACI("example.com/test02", expectedFiles, ds) if err != nil { t.Fatalf("unexpected error: %v", err) } }
// Test A (pwl) ---- B // \-- C -- D func Test3Deps(t *testing.T) { dir, err := ioutil.TempDir("", tstprefix) if err != nil { t.Fatalf("error creating tempdir: %v", err) } defer os.RemoveAll(dir) ds := NewTestStore() // B imj := ` { "acKind": "ImageManifest", "acVersion": "0.1.1", "name": "example.com/test01" } ` entries := []*testTarEntry{ { contents: imj, header: &tar.Header{ Name: "manifest", Size: int64(len(imj)), }, }, // It should be overridden by the one provided by the upper image { contents: "hello", header: &tar.Header{ Name: "rootfs/a/file01.txt", Size: 5, }, }, // It should be overridden by the one provided by the next dep { contents: "hello", header: &tar.Header{ Name: "rootfs/a/file02.txt", Size: 5, }, }, // It should remain like this { contents: "hello", header: &tar.Header{ Name: "rootfs/a/file03.txt", Size: 5, }, }, // It should not appear in rendered aci { contents: "hello", header: &tar.Header{ Name: "rootfs/a/file04.txt", Size: 5, }, }, { header: &tar.Header{ Name: "rootfs/b/link01.txt", Linkname: "file01.txt", Typeflag: tar.TypeSymlink, }, }, } key1, err := newTestACI(entries, dir, ds) if err != nil { t.Fatalf("unexpected error: %v", err) } im, err := createImageManifest(imj) if err != nil { t.Fatalf("unexpected error: %v", err) } image1 := Image{Im: im, Key: key1, Level: 1} // D imj = ` { "acKind": "ImageManifest", "acVersion": "0.1.1", "name": "example.com/test03" } ` entries = []*testTarEntry{ { contents: imj, header: &tar.Header{ Name: "manifest", Size: int64(len(imj)), }, }, // It should be overridden by the one provided by the upper image { contents: "hello", header: &tar.Header{ Name: "rootfs/a/file02.txt", Size: 5, }, }, { contents: "hello", header: &tar.Header{ Name: "rootfs/b/file01.txt", Size: 5, }, }, // It should not appear in rendered aci { header: &tar.Header{ Name: "rootfs/d", Typeflag: tar.TypeDir, Mode: 0700, }, }, { contents: "hello", header: &tar.Header{ Name: "rootfs/d/file01.txt", Size: 5, Mode: 0700, }, }, } key2, err := newTestACI(entries, dir, ds) if err != nil { t.Fatalf("unexpected error: %v", err) } im, err = createImageManifest(imj) if err != nil { t.Fatalf("unexpected error: %v", err) } image2 := Image{Im: im, Key: key2, Level: 2} // C imj = ` { "acKind": "ImageManifest", "acVersion": "0.1.1", "name": "example.com/test02" } ` k2, _ := types.NewHash(key2) imj, err = addDependencies(imj, types.Dependency{ ImageName: "example.com/test03", ImageID: k2}, ) entries = []*testTarEntry{ { contents: imj, header: &tar.Header{ Name: "manifest", Size: int64(len(imj)), }, }, // It should be overridden by the one provided by the upper image { contents: "hellohello", header: &tar.Header{ Name: "rootfs/a/file01.txt", Size: 10, }, }, { contents: "hellohello", header: &tar.Header{ Name: "rootfs/a/file02.txt", Size: 10, }, }, { contents: "hello", header: &tar.Header{ Name: "rootfs/b/file01.txt", Size: 5, }, }, } key3, err := newTestACI(entries, dir, ds) if err != nil { t.Fatalf("unexpected error: %v", err) } im, err = createImageManifest(imj) if err != nil { t.Fatalf("unexpected error: %v", err) } image3 := Image{Im: im, Key: key3, Level: 1} // A imj = ` { "acKind": "ImageManifest", "acVersion": "0.1.1", "name": "example.com/test04", "pathWhitelist" : [ "/a/file01.txt", "/a/file02.txt", "/a/file03.txt", "/b/link01.txt", "/b/file01.txt", "/b/file02.txt", "/c/file01.txt" ] } ` k1, _ := types.NewHash(key1) k3, _ := types.NewHash(key3) imj, err = addDependencies(imj, types.Dependency{ ImageName: "example.com/test01", ImageID: k1}, types.Dependency{ ImageName: "example.com/test02", ImageID: k3}, ) entries = []*testTarEntry{ { contents: imj, header: &tar.Header{ Name: "manifest", Size: int64(len(imj)), }, }, // Overridden { contents: "hellohellohello", header: &tar.Header{ Name: "rootfs/a/file01.txt", Size: 15, }, }, { contents: "hello", header: &tar.Header{ Name: "rootfs/b/file02.txt", Size: 5, }, }, { contents: "hello", header: &tar.Header{ Name: "rootfs/c/file01.txt", Size: 5, }, }, } key4, err := newTestACI(entries, dir, ds) if err != nil { t.Fatalf("unexpected error: %v", err) } im, err = createImageManifest(imj) if err != nil { t.Fatalf("unexpected error: %v", err) } image4 := Image{Im: im, Key: key4, Level: 0} expectedFiles := []*fileInfo{ &fileInfo{path: "manifest", typeflag: tar.TypeReg}, &fileInfo{path: "rootfs/a/file01.txt", typeflag: tar.TypeReg, size: 15}, &fileInfo{path: "rootfs/a/file02.txt", typeflag: tar.TypeReg, size: 10}, &fileInfo{path: "rootfs/a/file03.txt", typeflag: tar.TypeReg, size: 5}, &fileInfo{path: "rootfs/b/link01.txt", typeflag: tar.TypeSymlink}, &fileInfo{path: "rootfs/b/file01.txt", typeflag: tar.TypeReg, size: 5}, &fileInfo{path: "rootfs/b/file02.txt", typeflag: tar.TypeReg, size: 5}, &fileInfo{path: "rootfs/c/file01.txt", typeflag: tar.TypeReg, size: 5}, } images := Images{image4, image3, image2, image1} err = checkRenderACIFromList(images, expectedFiles, ds) if err != nil { t.Fatalf("unexpected error: %v", err) } err = checkRenderACI("example.com/test04", expectedFiles, ds) if err != nil { t.Fatalf("unexpected error: %v", err) } }