func TestGetImageManifest(t *testing.T) { dir, err := ioutil.TempDir("", tstprefix) if err != nil { t.Fatalf("error creating tempdir: %v", err) } defer os.RemoveAll(dir) s, err := NewStore(dir) if err != nil { t.Fatalf("Unexpected error: %v", err) } defer s.Close() imj := `{ "acKind": "ImageManifest", "acVersion": "0.8.8", "name": "example.com/test01" }` aci, err := aci.NewACI(dir, imj, nil) if err != nil { t.Fatalf("error creating test tar: %v", err) } // Rewind the ACI if _, err := aci.Seek(0, 0); err != nil { t.Fatalf("unexpected error %v", err) } key, err := s.WriteACI(aci, ACIFetchInfo{Latest: false}) if err != nil { t.Fatalf("unexpected error: %v", err) } wanted := "example.com/test01" im, err := s.GetImageManifest(key) if err != nil { t.Fatalf("unexpected error: %v", err) } if im.Name.String() != wanted { t.Errorf("expected im with name: %s, got: %s", wanted, im.Name.String()) } // test nonexistent key im, err = s.GetImageManifest("sha512-aaaaaaaaaaaaaaaaa") if err == nil { t.Fatalf("expected non-nil error!") } }
func TestTreeStore(t *testing.T) { if !sys.HasChrootCapability() { t.Skipf("chroot capability not available. Disabling test.") } dir, err := ioutil.TempDir("", tstprefix) if err != nil { t.Fatalf("error creating tempdir: %v", err) } defer os.RemoveAll(dir) s, err := NewStore(dir) if err != nil { t.Fatalf("unexpected error: %v", err) } imj := ` { "acKind": "ImageManifest", "acVersion": "0.7.1", "name": "example.com/test01" } ` entries := []*aci.ACIEntry{ // An empty dir { Header: &tar.Header{ Name: "rootfs/a", Typeflag: tar.TypeDir, }, }, { Contents: "hello", Header: &tar.Header{ Name: "hello.txt", Size: 5, }, }, { Header: &tar.Header{ Name: "rootfs/link.txt", Linkname: "rootfs/hello.txt", Typeflag: tar.TypeSymlink, }, }, // dangling symlink { Header: &tar.Header{ Name: "rootfs/link2.txt", Linkname: "rootfs/missingfile.txt", Typeflag: tar.TypeSymlink, }, }, { Header: &tar.Header{ Name: "rootfs/fifo", Typeflag: tar.TypeFifo, }, }, } aci, err := aci.NewACI(dir, imj, entries) if err != nil { t.Fatalf("error creating test tar: %v", err) } defer aci.Close() // Rewind the ACI if _, err := aci.Seek(0, 0); err != nil { t.Fatalf("unexpected error %v", err) } // Import the new ACI key, err := s.WriteACI(aci, false) if err != nil { t.Fatalf("unexpected error: %v", err) } // Ask the store to render the treestore id, err := s.RenderTreeStore(key, false) if err != nil { t.Fatalf("unexpected error: %v", err) } // Verify image Hash. Should be the same. err = s.CheckTreeStore(id) if err != nil { t.Fatalf("unexpected error: %v", err) } // Change a file permission rootfs := s.GetTreeStoreRootFS(id) err = os.Chmod(filepath.Join(rootfs, "a"), 0600) if err != nil { t.Fatalf("unexpected error: %v", err) } // Verify image Hash. Should be different err = s.CheckTreeStore(id) if err == nil { t.Errorf("expected non-nil error!") } // rebuild the tree prevID := id id, err = s.RenderTreeStore(key, true) if err != nil { t.Fatalf("unexpected error: %v", err) } if id != prevID { t.Fatalf("unexpected different IDs. prevID: %s, id: %s", prevID, id) } // Add a file rootfs = s.GetTreeStoreRootFS(id) err = ioutil.WriteFile(filepath.Join(rootfs, "newfile"), []byte("newfile"), 0644) if err != nil { t.Fatalf("unexpected error: %v", err) } // Verify image Hash. Should be different err = s.CheckTreeStore(id) if err == nil { t.Errorf("expected non-nil error!") } }
func TestGetAci(t *testing.T) { type test struct { name types.ACIdentifier labels types.Labels expected int // the aci index to expect or -1 if not result expected, } type acidef struct { imj string latest bool } dir, err := ioutil.TempDir("", tstprefix) if err != nil { t.Fatalf("error creating tempdir: %v", err) } defer os.RemoveAll(dir) s, err := NewStore(dir) if err != nil { t.Fatalf("unexpected error %v", err) } tests := []struct { acidefs []acidef tests []test }{ { []acidef{ { `{ "acKind": "ImageManifest", "acVersion": "0.7.1", "name": "example.com/test01" }`, false, }, { `{ "acKind": "ImageManifest", "acVersion": "0.7.1", "name": "example.com/test02", "labels": [ { "name": "version", "value": "1.0.0" } ] }`, true, }, { `{ "acKind": "ImageManifest", "acVersion": "0.7.1", "name": "example.com/test02", "labels": [ { "name": "version", "value": "2.0.0" } ] }`, false, }, }, []test{ { "example.com/unexistentaci", types.Labels{}, -1, }, { "example.com/test01", types.Labels{}, 0, }, { "example.com/test02", // Workaround for https://github.com/golang/go/issues/6820 : // `go vet` does not correctly detect types.Labels as a container []types.Label{ { Name: "version", Value: "1.0.0", }, }, 1, }, { "example.com/test02", []types.Label{ { Name: "version", Value: "2.0.0", }, }, 2, }, { "example.com/test02", types.Labels{}, 1, }, }, }, } for _, tt := range tests { var keys []string // Create ACIs for _, ad := range tt.acidefs { aci, err := aci.NewACI(dir, ad.imj, nil) if err != nil { t.Fatalf("error creating test tar: %v", err) } // Rewind the ACI if _, err := aci.Seek(0, 0); err != nil { t.Fatalf("unexpected error %v", err) } key, err := s.WriteACI(aci, ad.latest) if err != nil { t.Fatalf("unexpected error: %v", err) } keys = append(keys, key) } for _, test := range tt.tests { key, err := s.GetACI(test.name, test.labels) if test.expected == -1 { if err == nil { t.Fatalf("Expected no key for name %s, got %s", test.name, key) } } else { if err != nil { t.Fatalf("unexpected error on GetACI for name %s, labels: %v: %v", test.name, test.labels, err) } if keys[test.expected] != key { t.Errorf("expected key: %s, got %s. GetACI with name: %s, labels: %v", key, keys[test.expected], test.name, test.labels) } } } } }
func TestDownloading(t *testing.T) { dir, err := ioutil.TempDir("", "download-image") if err != nil { t.Fatalf("error creating tempdir: %v", err) } defer os.RemoveAll(dir) imj := `{ "acKind": "ImageManifest", "acVersion": "0.5.5", "name": "example.com/test01" }` entries := []*aci.ACIEntry{ // An empty file { Contents: "hello", Header: &tar.Header{ Name: "rootfs/file01.txt", Size: 5, }, }, } aci, err := aci.NewACI(dir, imj, entries) if err != nil { t.Fatalf("error creating test tar: %v", err) } // Rewind the ACI if _, err := aci.Seek(0, 0); err != nil { t.Fatalf("unexpected error: %v", err) } body, err := ioutil.ReadAll(aci) if err != nil { t.Fatalf("unexpected error: %v", err) } noauthServer := &serverHandler{ body: body, t: t, auth: "none", } basicServer := &serverHandler{ body: body, t: t, auth: "basic", } oauthServer := &serverHandler{ body: body, t: t, auth: "bearer", } denyServer := &serverHandler{ body: body, t: t, auth: "deny", } noAuthTS := httptest.NewTLSServer(noauthServer) defer noAuthTS.Close() basicTS := httptest.NewTLSServer(basicServer) defer basicTS.Close() oauthTS := httptest.NewTLSServer(oauthServer) defer oauthTS.Close() denyAuthTS := httptest.NewServer(denyServer) noAuth := http.Header{} // YmFyOmJheg== is base64(bar:baz) basicAuth := http.Header{"Authorization": {"Basic YmFyOmJheg=="}} bearerAuth := http.Header{"Authorization": {"Bearer sometoken"}} urlToName := map[string]string{ noAuthTS.URL: "no auth", basicTS.URL: "basic", oauthTS.URL: "oauth", denyAuthTS.URL: "deny auth", } tests := []struct { ACIURL string hit bool options http.Header authFail bool }{ {noAuthTS.URL, false, noAuth, false}, {noAuthTS.URL, true, noAuth, false}, {noAuthTS.URL, true, bearerAuth, false}, {noAuthTS.URL, true, basicAuth, false}, {basicTS.URL, false, noAuth, true}, {basicTS.URL, false, bearerAuth, true}, {basicTS.URL, false, basicAuth, false}, {oauthTS.URL, false, noAuth, true}, {oauthTS.URL, false, basicAuth, true}, {oauthTS.URL, false, bearerAuth, false}, {denyAuthTS.URL, false, basicAuth, false}, {denyAuthTS.URL, true, bearerAuth, false}, {denyAuthTS.URL, true, noAuth, false}, } s, err := store.NewStore(dir) if err != nil { t.Fatalf("unexpected error %v", err) } for _, tt := range tests { _, ok, err := s.GetRemote(tt.ACIURL) if err != nil { t.Fatalf("unexpected err: %v", err) } if tt.hit == false && ok { t.Fatalf("expected miss got a hit") } if tt.hit == true && !ok { t.Fatalf("expected a hit got a miss") } parsed, err := url.Parse(tt.ACIURL) if err != nil { panic(fmt.Sprintf("Invalid url from test server: %s", tt.ACIURL)) } headers := map[string]config.Headerer{ parsed.Host: &testHeaderer{tt.options}, } ft := &fetcher{ imageActionData: imageActionData{ s: s, headers: headers, insecureSkipVerify: true, }, } _, aciFile, err := ft.fetch(tt.ACIURL, "", nil) if err == nil { defer os.Remove(aciFile.Name()) } if err != nil && !tt.authFail { t.Fatalf("expected download to succeed, it failed: %v (server: %q, headers: `%v`)", err, urlToName[tt.ACIURL], tt.options) } if err == nil && tt.authFail { t.Fatalf("expected download to fail, it succeeded (server: %q, headers: `%v`)", urlToName[tt.ACIURL], tt.options) } if err != nil { continue } key, err := s.WriteACI(aciFile, false) if err != nil { t.Fatalf("unexpected err: %v", err) } rem := store.NewRemote(tt.ACIURL, "") rem.BlobKey = key err = s.WriteRemote(rem) if err != nil { t.Fatalf("unexpected err: %v", err) } } s.Dump(false) }
func TestDownloading(t *testing.T) { dir, err := ioutil.TempDir("", "download-image") if err != nil { t.Fatalf("error creating tempdir: %v", err) } defer os.RemoveAll(dir) imj := `{ "acKind": "ImageManifest", "acVersion": "0.7.4", "name": "example.com/test01" }` entries := []*aci.ACIEntry{ // An empty file { Contents: "hello", Header: &tar.Header{ Name: "rootfs/file01.txt", Size: 5, }, }, } aci, err := aci.NewACI(dir, imj, entries) if err != nil { t.Fatalf("error creating test tar: %v", err) } // Rewind the ACI if _, err := aci.Seek(0, 0); err != nil { t.Fatalf("unexpected error: %v", err) } body, err := ioutil.ReadAll(aci) if err != nil { t.Fatalf("unexpected error: %v", err) } noauthServer := &serverHandler{ body: body, t: t, auth: "none", } basicServer := &serverHandler{ body: body, t: t, auth: "basic", } oauthServer := &serverHandler{ body: body, t: t, auth: "bearer", } denyServer := &serverHandler{ body: body, t: t, auth: "deny", } noAuthTS := httptest.NewTLSServer(noauthServer) defer noAuthTS.Close() basicTS := httptest.NewTLSServer(basicServer) defer basicTS.Close() oauthTS := httptest.NewTLSServer(oauthServer) defer oauthTS.Close() denyAuthTS := httptest.NewServer(denyServer) noAuth := http.Header{} // YmFyOmJheg== is base64(bar:baz) basicAuth := http.Header{"Authorization": {"Basic YmFyOmJheg=="}} bearerAuth := http.Header{"Authorization": {"Bearer sometoken"}} urlToName := map[string]string{ noAuthTS.URL: "no auth", basicTS.URL: "basic", oauthTS.URL: "oauth", denyAuthTS.URL: "deny auth", } tests := []struct { aciURL string remoteExists bool options http.Header authFail bool }{ {noAuthTS.URL, false, noAuth, false}, {noAuthTS.URL, true, noAuth, false}, {noAuthTS.URL, true, bearerAuth, false}, {noAuthTS.URL, true, basicAuth, false}, {basicTS.URL, false, noAuth, true}, {basicTS.URL, false, bearerAuth, true}, {basicTS.URL, false, basicAuth, false}, {oauthTS.URL, false, noAuth, true}, {oauthTS.URL, false, basicAuth, true}, {oauthTS.URL, false, bearerAuth, false}, {denyAuthTS.URL, false, basicAuth, false}, {denyAuthTS.URL, true, bearerAuth, false}, {denyAuthTS.URL, true, noAuth, false}, } s, err := imagestore.NewStore(dir) if err != nil { t.Fatalf("unexpected error %v", err) } for _, tt := range tests { _, err := s.GetRemote(tt.aciURL) if err != nil { if err != imagestore.ErrRemoteNotFound { t.Fatalf("unexpected err: %v", err) } if tt.remoteExists { t.Fatalf("should've found the remote, got %v", err) } } else if !tt.remoteExists { t.Fatalf("should've gotten a remote not found error") } parsed, err := url.Parse(tt.aciURL) if err != nil { panic(fmt.Sprintf("Invalid url from test server: %s", tt.aciURL)) } headers := map[string]config.Headerer{ parsed.Host: &testHeaderer{tt.options}, } ft := &image.Fetcher{ S: s, Headers: headers, InsecureFlags: insecureFlags, } u, err := url.Parse(tt.aciURL) if err != nil { t.Fatalf("unexpected error %v", err) } d, err := dist.NewACIArchiveFromTransportURL(u) if err != nil { t.Fatalf("unexpected error %v", err) } _, err = ft.FetchImage(d, tt.aciURL, "") if err != nil && !tt.authFail { t.Fatalf("expected download to succeed, it failed: %v (server: %q, headers: `%v`)", err, urlToName[tt.aciURL], tt.options) } if err == nil && tt.authFail { t.Fatalf("expected download to fail, it succeeded (server: %q, headers: `%v`)", urlToName[tt.aciURL], tt.options) } } s.Dump(false) }
// TODO(sgotti) when the TreeStore will use an interface, change it to a // test implementation without relying on store/imagestore func testStoreWriteACI(dir string, s *imagestore.Store) (string, error) { imj := ` { "acKind": "ImageManifest", "acVersion": "0.8.8", "name": "example.com/test01" } ` entries := []*aci.ACIEntry{ // An empty dir { Header: &tar.Header{ Name: "rootfs/a", Typeflag: tar.TypeDir, }, }, { Contents: "hello", Header: &tar.Header{ Name: "hello.txt", Size: 5, }, }, { Header: &tar.Header{ Name: "rootfs/link.txt", Linkname: "rootfs/hello.txt", Typeflag: tar.TypeSymlink, }, }, // dangling symlink { Header: &tar.Header{ Name: "rootfs/link2.txt", Linkname: "rootfs/missingfile.txt", Typeflag: tar.TypeSymlink, }, }, { Header: &tar.Header{ Name: "rootfs/fifo", Typeflag: tar.TypeFifo, }, }, } aci, err := aci.NewACI(dir, imj, entries) if err != nil { return "", err } defer aci.Close() // Rewind the ACI if _, err := aci.Seek(0, 0); err != nil { return "", err } // Import the new ACI key, err := s.WriteACI(aci, imagestore.ACIFetchInfo{Latest: false}) if err != nil { return "", err } return key, nil }