From 75a2c098e0cbc5f3e7df27621fc25a3022387a62 Mon Sep 17 00:00:00 2001 From: Oleg Bulatov Date: Tue, 5 Dec 2017 18:51:58 +0100 Subject: [PATCH] Add integration test for cross-mounting blobs --- pkg/cmd/dockerregistry/dockerregistry.go | 112 +++++++------- .../server/blobdescriptorservice_test.go | 2 +- .../server/pullthroughblobstore_test.go | 4 +- pkg/dockerregistry/testutil/manifests.go | 2 +- pkg/dockerregistry/testutil/util.go | 61 +++++++- pkg/testframework/master.go | 128 +++++++++++++++- pkg/testframework/net.go | 31 ++++ pkg/testframework/project.go | 8 +- pkg/testframework/registry.go | 114 +++++++++------ .../integration/crossmount/crossmount_test.go | 137 ++++++++++++++++++ .../imagelayers/imagelayers_test.go | 135 +++-------------- .../pullthrough/pullthrough_test.go | 70 ++------- .../integration/v2/v2_docker_registry_test.go | 87 ++++------- 13 files changed, 548 insertions(+), 343 deletions(-) create mode 100644 test/integration/crossmount/crossmount_test.go diff --git a/pkg/cmd/dockerregistry/dockerregistry.go b/pkg/cmd/dockerregistry/dockerregistry.go index 6f0f51746a..f6896329bb 100644 --- a/pkg/cmd/dockerregistry/dockerregistry.go +++ b/pkg/cmd/dockerregistry/dockerregistry.go @@ -157,21 +157,32 @@ func Execute(configFile io.Reader) { log.Fatalf("error configuring logger: %v", err) } - err = Start(ctx, dockerConfig, extraConfig) + // inject a logger into the uuid library. warns us if there is a problem + // with uuid generation under low entropy. + uuid.Loggerf = context.GetLogger(ctx).Warnf + + context.GetLoggerWithFields(ctx, versionFields()).Info("start registry") + + srv, err := NewServer(ctx, dockerConfig, extraConfig) + if err != nil { + log.Fatal(err) + } + + if dockerConfig.HTTP.TLS.Certificate == "" { + context.GetLogger(ctx).Infof("listening on %s", srv.Addr) + err = srv.ListenAndServe() + } else { + context.GetLogger(ctx).Infof("listening on %s, tls", srv.Addr) + err = srv.ListenAndServeTLS(dockerConfig.HTTP.TLS.Certificate, dockerConfig.HTTP.TLS.Key) + } if err != nil { log.Fatal(err) } } -// Start runs the Docker registry. Start always returns a non-nil error. -func Start(ctx context.Context, dockerConfig *configuration.Configuration, extraConfig *registryconfig.Configuration) error { +func NewServer(ctx context.Context, dockerConfig *configuration.Configuration, extraConfig *registryconfig.Configuration) (*http.Server, error) { setDefaultLogParameters(dockerConfig) - context.GetLoggerWithFields(ctx, versionFields()).Info("start registry") - // inject a logger into the uuid library. warns us if there is a problem - // with uuid generation under low entropy. - uuid.Loggerf = context.GetLogger(ctx).Warnf - registryClient := client.NewRegistryClient(clientcmd.NewConfig().BindToFile(extraConfig.KubeConfig)) readLimiter := newLimiter(extraConfig.Requests.Read) @@ -186,67 +197,62 @@ func Start(ctx context.Context, dockerConfig *configuration.Configuration, extra handler = panicHandler(handler) handler = gorillahandlers.CombinedLoggingHandler(os.Stdout, handler) - if dockerConfig.HTTP.TLS.Certificate == "" { - context.GetLogger(ctx).Infof("listening on %v", dockerConfig.HTTP.Addr) - return http.ListenAndServe(dockerConfig.HTTP.Addr, handler) - } - - var ( - minVersion uint16 - cipherSuites []uint16 - err error - ) - if s := os.Getenv("REGISTRY_HTTP_TLS_MINVERSION"); len(s) > 0 { - minVersion, err = crypto.TLSVersion(s) - if err != nil { - return fmt.Errorf("invalid TLS version %q specified in REGISTRY_HTTP_TLS_MINVERSION: %v (valid values are %q)", s, err, crypto.ValidTLSVersions()) - } - } - if s := os.Getenv("REGISTRY_HTTP_TLS_CIPHERSUITES"); len(s) > 0 { - for _, cipher := range strings.Split(s, ",") { - cipherSuite, err := crypto.CipherSuite(cipher) + var tlsConf *tls.Config + if dockerConfig.HTTP.TLS.Certificate != "" { + var ( + minVersion uint16 + cipherSuites []uint16 + err error + ) + if s := os.Getenv("REGISTRY_HTTP_TLS_MINVERSION"); len(s) > 0 { + minVersion, err = crypto.TLSVersion(s) if err != nil { - return fmt.Errorf("invalid cipher suite %q specified in REGISTRY_HTTP_TLS_CIPHERSUITES: %v (valid suites are %q)", s, err, crypto.ValidCipherSuites()) + return nil, fmt.Errorf("invalid TLS version %q specified in REGISTRY_HTTP_TLS_MINVERSION: %v (valid values are %q)", s, err, crypto.ValidTLSVersions()) } - cipherSuites = append(cipherSuites, cipherSuite) } - } + if s := os.Getenv("REGISTRY_HTTP_TLS_CIPHERSUITES"); len(s) > 0 { + for _, cipher := range strings.Split(s, ",") { + cipherSuite, err := crypto.CipherSuite(cipher) + if err != nil { + return nil, fmt.Errorf("invalid cipher suite %q specified in REGISTRY_HTTP_TLS_CIPHERSUITES: %v (valid suites are %q)", s, err, crypto.ValidCipherSuites()) + } + cipherSuites = append(cipherSuites, cipherSuite) + } + } + tlsConf = crypto.SecureTLSConfig(&tls.Config{ + ClientAuth: tls.NoClientCert, + MinVersion: minVersion, + CipherSuites: cipherSuites, + }) - tlsConf := crypto.SecureTLSConfig(&tls.Config{ - ClientAuth: tls.NoClientCert, - MinVersion: minVersion, - CipherSuites: cipherSuites, - }) + if len(dockerConfig.HTTP.TLS.ClientCAs) != 0 { + pool := x509.NewCertPool() - if len(dockerConfig.HTTP.TLS.ClientCAs) != 0 { - pool := x509.NewCertPool() + for _, ca := range dockerConfig.HTTP.TLS.ClientCAs { + caPem, err := ioutil.ReadFile(ca) + if err != nil { + return nil, err + } - for _, ca := range dockerConfig.HTTP.TLS.ClientCAs { - caPem, err := ioutil.ReadFile(ca) - if err != nil { - return err + if ok := pool.AppendCertsFromPEM(caPem); !ok { + return nil, fmt.Errorf("could not add CA to pool") + } } - if ok := pool.AppendCertsFromPEM(caPem); !ok { - return fmt.Errorf("could not add CA to pool") + for _, subj := range pool.Subjects() { + context.GetLogger(ctx).Debugf("CA Subject: %s", string(subj)) } - } - for _, subj := range pool.Subjects() { - context.GetLogger(ctx).Debugf("CA Subject: %s", string(subj)) + tlsConf.ClientAuth = tls.RequireAndVerifyClientCert + tlsConf.ClientCAs = pool } - - tlsConf.ClientAuth = tls.RequireAndVerifyClientCert - tlsConf.ClientCAs = pool } - context.GetLogger(ctx).Infof("listening on %v, tls", dockerConfig.HTTP.Addr) - server := &http.Server{ + return &http.Server{ Addr: dockerConfig.HTTP.Addr, Handler: handler, TLSConfig: tlsConf, - } - return server.ListenAndServeTLS(dockerConfig.HTTP.TLS.Certificate, dockerConfig.HTTP.TLS.Key) + }, nil } // configureLogging prepares the context with a logger using the diff --git a/pkg/dockerregistry/server/blobdescriptorservice_test.go b/pkg/dockerregistry/server/blobdescriptorservice_test.go index aa7850fe4d..e64889d387 100644 --- a/pkg/dockerregistry/server/blobdescriptorservice_test.go +++ b/pkg/dockerregistry/server/blobdescriptorservice_test.go @@ -96,7 +96,7 @@ func TestBlobDescriptorServiceIsApplied(t *testing.T) { t.Fatalf("error parsing server url: %v", err) } - desc, _, err := registrytest.UploadRandomTestBlob(ctx, serverURL, nil, "user/app") + desc, _, err := registrytest.UploadRandomTestBlob(ctx, serverURL.String(), nil, "user/app") if err != nil { t.Fatal(err) } diff --git a/pkg/dockerregistry/server/pullthroughblobstore_test.go b/pkg/dockerregistry/server/pullthroughblobstore_test.go index c9ffdfd7a2..1f584f330b 100644 --- a/pkg/dockerregistry/server/pullthroughblobstore_test.go +++ b/pkg/dockerregistry/server/pullthroughblobstore_test.go @@ -54,11 +54,11 @@ func TestPullthroughServeBlob(t *testing.T) { }) registrytest.AddImage(t, fos, testImage, namespace, name, "latest") - blob1Desc, blob1Content, err := registrytest.UploadRandomTestBlob(backgroundCtx, serverURL, nil, repoName) + blob1Desc, blob1Content, err := registrytest.UploadRandomTestBlob(backgroundCtx, serverURL.String(), nil, repoName) if err != nil { t.Fatal(err) } - blob2Desc, blob2Content, err := registrytest.UploadRandomTestBlob(backgroundCtx, serverURL, nil, repoName) + blob2Desc, blob2Content, err := registrytest.UploadRandomTestBlob(backgroundCtx, serverURL.String(), nil, repoName) if err != nil { t.Fatal(err) } diff --git a/pkg/dockerregistry/testutil/manifests.go b/pkg/dockerregistry/testutil/manifests.go index 9858732add..c8d423acf8 100644 --- a/pkg/dockerregistry/testutil/manifests.go +++ b/pkg/dockerregistry/testutil/manifests.go @@ -149,7 +149,7 @@ func CreateAndUploadTestManifest( ) for i := 0; i < layerCount; i++ { - ds, _, err := UploadRandomTestBlob(ctx, serverURL, creds, repoName) + ds, _, err := UploadRandomTestBlob(ctx, serverURL.String(), creds, repoName) if err != nil { return "", "", "", nil, fmt.Errorf("unexpected error generating test blob layer: %v", err) } diff --git a/pkg/dockerregistry/testutil/util.go b/pkg/dockerregistry/testutil/util.go index 0e59a53168..642b1f6012 100644 --- a/pkg/dockerregistry/testutil/util.go +++ b/pkg/dockerregistry/testutil/util.go @@ -4,6 +4,7 @@ import ( "archive/tar" "bytes" "crypto/rand" + "encoding/json" "fmt" "io" mrand "math/rand" @@ -106,18 +107,18 @@ func UploadManifest(ctx context.Context, repo distribution.Repository, tag strin } // UploadRandomTestBlob generates a random tar file and uploads it to the given repository. -func UploadRandomTestBlob(ctx context.Context, serverURL *url.URL, creds auth.CredentialStore, repoName string) (distribution.Descriptor, []byte, error) { +func UploadRandomTestBlob(ctx context.Context, baseURL string, creds auth.CredentialStore, repoName string) (distribution.Descriptor, []byte, error) { payload, desc, err := MakeRandomLayer() if err != nil { return distribution.Descriptor{}, nil, fmt.Errorf("unexpected error generating test layer file: %v", err) } - rt, err := NewTransport(serverURL.String(), repoName, creds) + rt, err := NewTransport(baseURL, repoName, creds) if err != nil { return distribution.Descriptor{}, nil, err } - repo, err := NewRepository(ctx, repoName, serverURL.String(), rt) + repo, err := NewRepository(ctx, repoName, baseURL, rt) if err != nil { return distribution.Descriptor{}, nil, err } @@ -180,7 +181,7 @@ func CreateRandomTarFile() ([]byte, error) { // CreateRandomImage creates an image with a random content. func CreateRandomImage(namespace, name string) (*imageapiv1.Image, error) { - const layersCount = 3 + const layersCount = 2 layersDescs := make([]distribution.Descriptor, layersCount) for i := range layersDescs { @@ -297,3 +298,55 @@ func ping(manager challenge.Manager, endpoint, versionHeader string) ([]auth.API return auth.APIVersions(resp, versionHeader), nil } + +// UploadSchema2Image creates a random image with a schema 2 manifest and +// uploads it to the repository. +func UploadSchema2Image(ctx context.Context, repo distribution.Repository, tag string) (distribution.Manifest, error) { + const layersCount = 2 + + layers := make([]distribution.Descriptor, layersCount) + for i := range layers { + content, desc, err := MakeRandomLayer() + if err != nil { + return nil, fmt.Errorf("make random layer: %v", err) + } + + if err := UploadBlob(ctx, repo, desc, content); err != nil { + return nil, fmt.Errorf("upload random blob: %v", err) + } + + layers[i] = desc + } + + cfg := map[string]interface{}{ + "rootfs": map[string]interface{}{ + "diff_ids": make([]string, len(layers)), + }, + "history": make([]struct{}, len(layers)), + } + + configContent, err := json.Marshal(&cfg) + if err != nil { + return nil, fmt.Errorf("marshal image config: %v", err) + } + + config := distribution.Descriptor{ + Digest: digest.FromBytes(configContent), + Size: int64(len(configContent)), + } + + if err := UploadBlob(ctx, repo, config, configContent); err != nil { + return nil, fmt.Errorf("upload image config: %v", err) + } + + manifest, err := MakeSchema2Manifest(config, layers) + if err != nil { + return manifest, fmt.Errorf("make schema 2 manifest: %v", err) + } + + if err := UploadManifest(ctx, repo, tag, manifest); err != nil { + return manifest, fmt.Errorf("upload schema 2 manifest: %v", err) + } + + return manifest, nil +} diff --git a/pkg/testframework/master.go b/pkg/testframework/master.go index eaec43cac4..143697f2b9 100644 --- a/pkg/testframework/master.go +++ b/pkg/testframework/master.go @@ -9,14 +9,22 @@ import ( "io/ioutil" "net" "net/http" + "os" "path" "strconv" + "testing" "time" + "github.com/docker/distribution" "github.com/docker/docker/api/types" "github.com/docker/docker/api/types/container" "github.com/docker/docker/client" "github.com/docker/docker/pkg/archive" + + "k8s.io/client-go/rest" + + "github.com/openshift/origin/pkg/cmd/util/tokencmd" + projectapiv1 "github.com/openshift/origin/pkg/project/apis/project/v1" ) type MasterContainer struct { @@ -105,7 +113,9 @@ func (c *MasterContainer) WriteConfigs(configDir string) error { } ctx := context.Background() - srcPath := "/var/lib/origin/openshift.local.config" + // If SRC_PATH does end with `/.`, the content of the source directory is copied. + // https://docs.docker.com/engine/reference/commandline/cp/#extended-description + srcPath := "/var/lib/origin/openshift.local.config/." dstPath := configDir content, stat, err := cli.CopyFromContainer(ctx, c.ID, srcPath) @@ -173,3 +183,119 @@ func (c *MasterContainer) Stop() error { return nil } + +type User struct { + Name string + Token string + kubeConfig *rest.Config +} + +func (u *User) KubeConfig() *rest.Config { + return u.kubeConfig +} + +type Repository struct { + distribution.Repository + baseURL string + repoName string + transport http.RoundTripper +} + +func (r *Repository) BaseURL() string { + return r.baseURL +} + +func (r *Repository) RepoName() string { + return r.repoName +} + +func (r *Repository) Transport() http.RoundTripper { + return r.transport +} + +type Master struct { + t *testing.T + tmpDir string + container *MasterContainer + adminKubeConfig *rest.Config +} + +func NewMaster(t *testing.T) *Master { + tmpDir, err := ioutil.TempDir("", "image-registry-test-") + if err != nil { + t.Fatalf("failed to create a temporary directory for the master container: %v", err) + } + + container, err := StartMasterContainer(tmpDir) + if err != nil { + if err := os.RemoveAll(tmpDir); err != nil { + t.Logf("failed to remove the temporary directory: %v", err) + } + t.Fatal(err) + } + + return &Master{ + t: t, + tmpDir: tmpDir, + container: container, + } +} + +func (m *Master) Close() { + if err := m.container.Stop(); err != nil { + m.t.Logf("failed to stop the master container: %v", err) + } + + if err := os.RemoveAll(m.tmpDir); err != nil { + m.t.Logf("failed to remove the temporary directory: %v", err) + } +} + +func (m *Master) AdminKubeConfigPath() string { + return path.Join(m.tmpDir, "master", "admin.kubeconfig") +} + +func (m *Master) AdminKubeConfig() *rest.Config { + if m.adminKubeConfig != nil { + return m.adminKubeConfig + } + + config, err := ConfigFromFile(m.AdminKubeConfigPath()) + if err != nil { + m.t.Fatalf("failed to read the admin kubeconfig file: %v", err) + } + + m.adminKubeConfig = config + + return config +} + +func (m *Master) StartRegistry(t *testing.T) *Registry { + ln, closeFn := StartTestRegistry(t, m.AdminKubeConfigPath()) + return &Registry{ + t: t, + listener: ln, + closeFn: closeFn, + } +} + +func (m *Master) CreateUser(username string, password string) *User { + token, err := tokencmd.RequestToken(m.AdminKubeConfig(), nil, username, password) + if err != nil { + m.t.Fatalf("failed to get a token for the user %s: %v", username, err) + } + + return &User{ + Name: username, + Token: token, + kubeConfig: UserClientConfig(m.AdminKubeConfig(), token), + } +} + +func (m *Master) CreateProject(namespace, user string) *projectapiv1.Project { + project, err := CreateProject(m.AdminKubeConfig(), namespace, user) + if err != nil { + m.t.Fatal(err) + } + return project +} diff --git a/pkg/testframework/net.go b/pkg/testframework/net.go index c6886f4939..10a8d7fe98 100644 --- a/pkg/testframework/net.go +++ b/pkg/testframework/net.go @@ -1,10 +1,41 @@ package testframework import ( + "errors" "net" "strconv" ) +// ErrNoDefaultIP is returned when no suitable non-loopback address can be found. +var ErrNoDefaultIP = errors.New("no suitable IP address") + +// DefaultLocalIP4 returns an IPv4 address that this host can be reached +// on. Will return ErrNoDefaultIP if no suitable address can be found. +// +// github.com/openshift/origin/pkg/cmd/util.DefaultLocalIP4 +func DefaultLocalIP4() (net.IP, error) { + devices, err := net.Interfaces() + if err != nil { + return nil, err + } + for _, dev := range devices { + if (dev.Flags&net.FlagUp != 0) && (dev.Flags&net.FlagLoopback == 0) { + addrs, err := dev.Addrs() + if err != nil { + continue + } + for i := range addrs { + if ip, ok := addrs[i].(*net.IPNet); ok { + if ip.IP.To4() != nil { + return ip.IP, nil + } + } + } + } + } + return nil, ErrNoDefaultIP +} + // FindFreeLocalPort returns the number of an available port number on // the loopback interface. Useful for determining the port to launch // a server on. Error handling required - there is a non-zero chance diff --git a/pkg/testframework/project.go b/pkg/testframework/project.go index eaf06b19d8..2d9b6873b9 100644 --- a/pkg/testframework/project.go +++ b/pkg/testframework/project.go @@ -11,15 +11,15 @@ import ( projectv1 "github.com/openshift/origin/pkg/project/generated/clientset/typed/project/v1" ) -func CreateProject(clientConfig *rest.Config, namespace string, adminUser string) error { +func CreateProject(clientConfig *rest.Config, namespace string, adminUser string) (*projectapiv1.Project, error) { projectClient := projectv1.NewForConfigOrDie(clientConfig) - _, err := projectClient.ProjectRequests().Create(&projectapiv1.ProjectRequest{ + project, err := projectClient.ProjectRequests().Create(&projectapiv1.ProjectRequest{ ObjectMeta: metav1.ObjectMeta{ Name: namespace, }, }) if err != nil { - return err + return project, err } authorizationClient := authorizationv1.NewForConfigOrDie(clientConfig) @@ -32,5 +32,5 @@ func CreateProject(clientConfig *rest.Config, namespace string, adminUser string Name: "admin", }, }) - return err + return project, err } diff --git a/pkg/testframework/registry.go b/pkg/testframework/registry.go index 546060e963..dc64bf6114 100644 --- a/pkg/testframework/registry.go +++ b/pkg/testframework/registry.go @@ -1,9 +1,9 @@ package testframework import ( - "errors" "fmt" "net" + "sync/atomic" "testing" "github.com/docker/distribution/configuration" @@ -14,48 +14,19 @@ import ( registrytest "github.com/openshift/image-registry/pkg/dockerregistry/testutil" ) -// ErrorNoDefaultIP is returned when no suitable non-loopback address can be found. -var ErrorNoDefaultIP = errors.New("no suitable IP address") +type CloseFunc func() error -// DefaultLocalIP4 returns an IPv4 address that this host can be reached -// on. Will return NoDefaultIP if no suitable address can be found. -func DefaultLocalIP4() (net.IP, error) { - devices, err := net.Interfaces() - if err != nil { - return nil, err - } - for _, dev := range devices { - if (dev.Flags&net.FlagUp != 0) && (dev.Flags&net.FlagLoopback == 0) { - addrs, err := dev.Addrs() - if err != nil { - continue - } - for i := range addrs { - if ip, ok := addrs[i].(*net.IPNet); ok { - if ip.IP.To4() != nil { - return ip.IP, nil - } - } - } - } - } - return nil, ErrorNoDefaultIP -} - -func StartTestRegistry(t *testing.T, kubeConfigPath string) (string, error) { - port, err := FindFreeLocalPort() +func StartTestRegistry(t *testing.T, kubeConfigPath string) (net.Listener, CloseFunc) { + localIPv4, err := DefaultLocalIP4() if err != nil { - return "", fmt.Errorf("unable to find a free local port for the registry: %v", err) + t.Fatalf("failed to detect an IPv4 address which would be reachable from containers: %v", err) } - localIPv4, err := DefaultLocalIP4() + ln, err := net.Listen("tcp", fmt.Sprintf("%s:%d", localIPv4, 0)) if err != nil { - return "", err + t.Fatalf("failed to listen on a port: %v", err) } - // FIXME(dmage): the port can be claimed by someone else before the registry is started. - registryAddr := fmt.Sprintf("%s:%d", localIPv4, port) - dockerConfig := &configuration.Configuration{ Version: "0.1", Storage: configuration.Storage{ @@ -71,7 +42,7 @@ func StartTestRegistry(t *testing.T, kubeConfigPath string) (string, error) { "repository": {{ Name: "openshift", Options: configuration.Parameters{ - "dockerregistryurl": registryAddr, + "dockerregistryurl": ln.Addr().String(), "acceptschema2": true, "pullthrough": true, "enforcequota": false, @@ -85,23 +56,76 @@ func StartTestRegistry(t *testing.T, kubeConfigPath string) (string, error) { }, } dockerConfig.Log.Level = "debug" - dockerConfig.HTTP.Addr = registryAddr extraConfig := ®istryconfig.Configuration{ KubeConfig: kubeConfigPath, } if err := registryconfig.InitExtraConfig(dockerConfig, extraConfig); err != nil { - return "", fmt.Errorf("unable to init registry config: %v", err) + t.Fatalf("unable to init registry config: %v", err) } + ctx := context.Background() + ctx = registrytest.WithTestLogger(ctx, t) + srv, err := dockerregistry.NewServer(ctx, dockerConfig, extraConfig) + if err != nil { + t.Fatalf("failed to create a new server: %v", err) + } + + closed := int32(0) go func() { - ctx := context.Background() - ctx = registrytest.WithTestLogger(ctx, t) - err := dockerregistry.Start(ctx, dockerConfig, extraConfig) - // We cannot call t.Fatal here, because it's a different goroutine. - panic(fmt.Errorf("failed to start the image registry: %v", err)) + err := srv.Serve(ln) + if atomic.LoadInt32(&closed) == 0 { + // We cannot call t.Fatal here, because it's a different goroutine. + panic(fmt.Errorf("failed to serve the image registry: %v", err)) + } }() + close := func() error { + atomic.StoreInt32(&closed, 1) + return ln.Close() + } + + return ln, close +} - return registryAddr, WaitTCP(registryAddr) +type Registry struct { + t *testing.T + listener net.Listener + closeFn CloseFunc +} + +func (r *Registry) Close() { + if err := r.closeFn(); err != nil { + r.t.Fatalf("failed to close the registry's listener: %v", err) + } +} + +func (r *Registry) BaseURL() string { + return "http://" + r.listener.Addr().String() +} + +func (r *Registry) Repository(namespace string, imagestream string, user *User) *Repository { + creds := registrytest.NewBasicCredentialStore(user.Name, user.Token) + + baseURL := r.BaseURL() + repoName := fmt.Sprintf("%s/%s", namespace, imagestream) + + transport, err := registrytest.NewTransport(baseURL, repoName, creds) + if err != nil { + r.t.Fatalf("failed to get transport for %s: %v", repoName, err) + } + + ctx := context.Background() + + repo, err := registrytest.NewRepository(ctx, repoName, baseURL, transport) + if err != nil { + r.t.Fatalf("failed to get repository %s: %v", repoName, err) + } + + return &Repository{ + Repository: repo, + baseURL: baseURL, + repoName: repoName, + transport: transport, + } } diff --git a/test/integration/crossmount/crossmount_test.go b/test/integration/crossmount/crossmount_test.go new file mode 100644 index 0000000000..9b22377ecb --- /dev/null +++ b/test/integration/crossmount/crossmount_test.go @@ -0,0 +1,137 @@ +package integration + +import ( + "fmt" + "testing" + + "github.com/docker/distribution" + "github.com/docker/distribution/context" + "github.com/docker/distribution/reference" + "github.com/docker/distribution/registry/client" + + metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" + + imagev1 "github.com/openshift/origin/pkg/image/generated/clientset/typed/image/v1" + + registrytest "github.com/openshift/image-registry/pkg/dockerregistry/testutil" + "github.com/openshift/image-registry/pkg/testframework" +) + +type errRegistryWantsContent struct { + src reference.Canonical + dst reference.Named +} + +func (e errRegistryWantsContent) Error() string { + return fmt.Sprintf("the registry cannot mount %s to %s and wants the content of the blob", e.src, e.dst) +} + +func crossMountImage(ctx context.Context, destRepo distribution.Repository, tag string, srcRepoNamed reference.Named, manifest distribution.Manifest) error { + destBlobs := destRepo.Blobs(ctx) + for _, desc := range manifest.References() { + canonicalRef, _ := reference.WithDigest(srcRepoNamed, desc.Digest) + bw, err := destBlobs.Create(ctx, client.WithMountFrom(canonicalRef)) + if _, ok := err.(distribution.ErrBlobMounted); ok { + continue + } + if err != nil { + return fmt.Errorf("unable to mount blob %s to %s: %v", canonicalRef, destRepo.Named(), err) + } + bw.Cancel(ctx) + bw.Close() + return errRegistryWantsContent{ + src: canonicalRef, + dst: destRepo.Named(), + } + } + if err := registrytest.UploadManifest(ctx, destRepo, tag, manifest); err != nil { + return fmt.Errorf("failed to upload the manifest after cross-mounting blobs: %v", err) + } + return nil +} + +func copyISTag(imageClient imagev1.ImageV1Interface, destNamespace, destISTag, sourceNamespace, sourceISTag string) error { + istag, err := imageClient.ImageStreamTags(sourceNamespace).Get(sourceISTag, metav1.GetOptions{}) + if err != nil { + return fmt.Errorf("copy istag %s/%s to %s/%s: get source: %v", sourceNamespace, sourceISTag, destNamespace, destISTag, err) + } + istag.Name = destISTag + _, err = imageClient.ImageStreamTags(destNamespace).Create(istag) + if err != nil { + return fmt.Errorf("copy istag %s/%s to %s/%s: create destination: %v", sourceNamespace, sourceISTag, destNamespace, destISTag, err) + } + return nil +} + +func TestCrossMount(t *testing.T) { + master := testframework.NewMaster(t) + defer master.Close() + + alice := master.CreateUser("alice", "qwerty") + bob := master.CreateUser("bob", "123456") + aliceproject := master.CreateProject("aliceproject", alice.Name) + bobproject := master.CreateProject("bobproject", bob.Name) + + wantCrossMountError := func(err error) error { + if _, ok := err.(errRegistryWantsContent); !ok { + return fmt.Errorf("want a cross-mount error, got %v", err) + } + return nil + } + wantSuccess := func(err error) error { + if err != nil { + return fmt.Errorf("failed to cross-mount image: %v", err) + } + return nil + } + + for _, test := range []struct { + name string + actor *testframework.User + destinationProject, destinationImageStream string + sourceProject, sourceImageStream string + check func(error) error + }{ + { + "alice_from_foo", + alice, aliceproject.Name, "mounted-foo", aliceproject.Name, "foo", + wantSuccess, + }, + { + "bob_from_foo", + bob, bobproject.Name, "from-foo", aliceproject.Name, "foo", + wantCrossMountError, + }, + { + "bob_from_foo-copy", + bob, bobproject.Name, "from-foo-copy", aliceproject.Name, "foo-copy", + wantCrossMountError, + }, + } { + t.Run(test.name, func(t *testing.T) { + registry := master.StartRegistry(t) + defer registry.Close() + + // Upload a random image to aliceproject/foo:latest + aliceFooRepo := registry.Repository(aliceproject.Name, "foo", alice) + manifest, err := registrytest.UploadSchema2Image(context.Background(), aliceFooRepo, "latest") + if err != nil { + t.Fatal(err) + } + + // Copy image stream tag aliceproject/foo:latest to aliceproject/foo-copy:latest + aliceImageClient := imagev1.NewForConfigOrDie(alice.KubeConfig()) + if err := copyISTag(aliceImageClient, aliceproject.Name, "foo-copy:latest", aliceproject.Name, "foo:latest"); err != nil { + t.Fatal(err) + } + + // Cross-mount image + repo := registry.Repository(test.destinationProject, test.destinationImageStream, test.actor) + sourceNamed, _ := reference.WithName(fmt.Sprintf("%s/%s", test.sourceProject, test.sourceImageStream)) + err = test.check(crossMountImage(context.Background(), repo, "latest", sourceNamed, manifest)) + if err != nil { + t.Error(err) + } + }) + } +} diff --git a/test/integration/imagelayers/imagelayers_test.go b/test/integration/imagelayers/imagelayers_test.go index 6ad8c12573..a009fe7bcb 100644 --- a/test/integration/imagelayers/imagelayers_test.go +++ b/test/integration/imagelayers/imagelayers_test.go @@ -1,21 +1,16 @@ package integration import ( - "encoding/json" "fmt" "io/ioutil" "net/http" - "os" - "path" "testing" "github.com/docker/distribution" "github.com/docker/distribution/context" - "github.com/docker/distribution/digest" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" - "github.com/openshift/origin/pkg/cmd/util/tokencmd" imageapi "github.com/openshift/origin/pkg/image/apis/image" imagev1 "github.com/openshift/origin/pkg/image/generated/clientset/typed/image/v1" @@ -23,64 +18,14 @@ import ( "github.com/openshift/image-registry/pkg/testframework" ) -// uploadImageWithSchema2Manifest creates a random image with a schema 2 -// manifest and uploads it to the repository. -func uploadImageWithSchema2Manifest(ctx context.Context, repo distribution.Repository, tag string) error { - layers := make([]distribution.Descriptor, 3) - for i := range layers { - content, desc, err := registrytest.MakeRandomLayer() - if err != nil { - return fmt.Errorf("make random layer: %v", err) - } - - if err := registrytest.UploadBlob(ctx, repo, desc, content); err != nil { - return fmt.Errorf("upload random blob: %v", err) - } - - layers[i] = desc - } - - cfg := map[string]interface{}{ - "rootfs": map[string]interface{}{ - "diff_ids": make([]string, len(layers)), - }, - "history": make([]struct{}, len(layers)), - } - - configContent, err := json.Marshal(&cfg) - if err != nil { - return fmt.Errorf("marshal image config: %v", err) - } - - config := distribution.Descriptor{ - Digest: digest.FromBytes(configContent), - Size: int64(len(configContent)), - } - - if err := registrytest.UploadBlob(ctx, repo, config, configContent); err != nil { - return fmt.Errorf("upload image config: %v", err) - } - - manifest, err := registrytest.MakeSchema2Manifest(config, layers) - if err != nil { - return fmt.Errorf("make schema 2 manifest: %v", err) - } - - if err := registrytest.UploadManifest(ctx, repo, tag, manifest); err != nil { - return fmt.Errorf("upload schema 2 manifest: %v", err) - } - - return nil -} - // getSchema1Manifest simulates a client which supports only schema 1 // manifests, fetches a manifest from a registry and returns it. -func getSchema1Manifest(transport http.RoundTripper, baseURL, repoName, tag string) (distribution.Manifest, error) { +func getSchema1Manifest(repo *testframework.Repository, tag string) (distribution.Manifest, error) { c := &http.Client{ - Transport: transport, + Transport: repo.Transport(), } - resp, err := c.Get(baseURL + "/v2/" + repoName + "/manifests/" + tag) + resp, err := c.Get(repo.BaseURL() + "/v2/" + repo.RepoName() + "/manifests/" + tag) if err != nil { return nil, err } @@ -88,7 +33,7 @@ func getSchema1Manifest(transport http.RoundTripper, baseURL, repoName, tag stri body, err := ioutil.ReadAll(resp.Body) if err != nil { - return nil, fmt.Errorf("read manifest %s:%s: %v", repoName, tag, err) + return nil, fmt.Errorf("read manifest %s:%s: %v", repo.RepoName(), tag, err) } m, _, err := distribution.UnmarshalManifest(resp.Header.Get("Content-Type"), body) @@ -99,77 +44,31 @@ func getSchema1Manifest(transport http.RoundTripper, baseURL, repoName, tag stri // manifests and schema 2 manifests consistently and it produces similar Image // resources for them. // -// The test relies on ability of the registry to downconvert manifests. +// The test relies on the ability of the registry to downconvert manifests. func TestImageLayers(t *testing.T) { - tmpDir, err := ioutil.TempDir("", "image-registry-test-integration-") - if err != nil { - t.Fatalf("failed to create temporary directory: %s", err) - } - defer os.RemoveAll(tmpDir) - - configDir := path.Join(tmpDir, "config") - adminKubeConfigPath := path.Join(configDir, "master", "admin.kubeconfig") - - masterContainer, err := testframework.StartMasterContainer(configDir) - if err != nil { - t.Fatal(err) - } - defer func() { - if err := masterContainer.Stop(); err != nil { - t.Log(err) - } - }() + master := testframework.NewMaster(t) + defer master.Close() - clusterAdminClientConfig, err := testframework.ConfigFromFile(adminKubeConfigPath) - if err != nil { - t.Fatal(err) - } - - namespace := "image-registry-test-integration" + testuser := master.CreateUser("testuser", "testp@ssw0rd") + testproject := master.CreateProject("image-registry-test-image-layers", testuser.Name) imageStreamName := "test-imagelayers" - user := "testuser" - password := "testp@ssw0rd" - if err := testframework.CreateProject(clusterAdminClientConfig, namespace, user); err != nil { - t.Fatal(err) - } + registry := master.StartRegistry(t) + defer registry.Close() - token, err := tokencmd.RequestToken(clusterAdminClientConfig, nil, user, password) - if err != nil { - t.Fatalf("error requesting token: %v", err) - } - - registryAddr, err := testframework.StartTestRegistry(t, adminKubeConfigPath) - if err != nil { - t.Fatalf("start registry: %v", err) - } - - creds := registrytest.NewBasicCredentialStore(user, token) - - baseURL := "http://" + registryAddr - repoName := fmt.Sprintf("%s/%s", namespace, imageStreamName) + repo := registry.Repository(testproject.Name, imageStreamName, testuser) schema1Tag := "schema1" schema2Tag := "schema2" - transport, err := registrytest.NewTransport(baseURL, repoName, creds) - if err != nil { - t.Fatalf("get transport: %v", err) - } - ctx := context.Background() - repo, err := registrytest.NewRepository(ctx, repoName, baseURL, transport) - if err != nil { - t.Fatalf("get repository: %v", err) - } - - if err := uploadImageWithSchema2Manifest(ctx, repo, schema2Tag); err != nil { + if _, err := registrytest.UploadSchema2Image(ctx, repo, schema2Tag); err != nil { t.Fatalf("upload image with schema 2 manifest: %v", err) } // get the schema2 image's manifest downconverted to a schema 1 manifest - schema1Manifest, err := getSchema1Manifest(transport, baseURL, repoName, schema2Tag) + schema1Manifest, err := getSchema1Manifest(repo, schema2Tag) if err != nil { t.Fatalf("get schema 1 manifest for image schema2: %v", err) } @@ -178,14 +77,14 @@ func TestImageLayers(t *testing.T) { t.Fatalf("upload schema 1 manifest: %v", err) } - imageClient := imagev1.NewForConfigOrDie(clusterAdminClientConfig) + imageClient := imagev1.NewForConfigOrDie(testuser.KubeConfig()) - schema1ISTag, err := imageClient.ImageStreamTags(namespace).Get(imageStreamName+":"+schema1Tag, metav1.GetOptions{}) + schema1ISTag, err := imageClient.ImageStreamTags(testproject.Name).Get(imageStreamName+":"+schema1Tag, metav1.GetOptions{}) if err != nil { t.Fatalf("get image stream tag %s:%s: %v", imageStreamName, schema1Tag, err) } - schema2ISTag, err := imageClient.ImageStreamTags(namespace).Get(imageStreamName+":"+schema2Tag, metav1.GetOptions{}) + schema2ISTag, err := imageClient.ImageStreamTags(testproject.Name).Get(imageStreamName+":"+schema2Tag, metav1.GetOptions{}) if err != nil { t.Fatalf("get image stream tag %s:%s: %v", imageStreamName, schema1Tag, err) } diff --git a/test/integration/pullthrough/pullthrough_test.go b/test/integration/pullthrough/pullthrough_test.go index 8ed5dde939..7d00a3f3fd 100644 --- a/test/integration/pullthrough/pullthrough_test.go +++ b/test/integration/pullthrough/pullthrough_test.go @@ -7,8 +7,6 @@ import ( "net" "net/http" "net/url" - "os" - "path" "strings" "testing" @@ -17,7 +15,6 @@ import ( metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" kapi "k8s.io/kubernetes/pkg/api/v1" - "github.com/openshift/origin/pkg/cmd/util/tokencmd" imageapi "github.com/openshift/origin/pkg/image/apis/image" imageapiv1 "github.com/openshift/origin/pkg/image/apis/image/v1" imagev1 "github.com/openshift/origin/pkg/image/generated/clientset/typed/image/v1" @@ -32,8 +29,8 @@ var gzippedEmptyTar = []byte{ 0, 8, 0, 0, 255, 255, 46, 175, 181, 239, 0, 4, 0, 0, } -func testPullThroughGetManifest(registryAddr string, stream *imageapiv1.ImageStreamImport, user, token, urlPart string) error { - url := fmt.Sprintf("http://%s/v2/%s/%s/manifests/%s", registryAddr, stream.Namespace, stream.Name, urlPart) +func testPullThroughGetManifest(baseURL string, stream *imageapiv1.ImageStreamImport, user, token, urlPart string) error { + url := fmt.Sprintf("%s/v2/%s/%s/manifests/%s", baseURL, stream.Namespace, stream.Name, urlPart) req, err := http.NewRequest("GET", url, nil) if err != nil { @@ -66,8 +63,8 @@ func testPullThroughGetManifest(registryAddr string, stream *imageapiv1.ImageStr return nil } -func testPullThroughStatBlob(registryAddr string, stream *imageapiv1.ImageStreamImport, user, token, digest string) error { - url := fmt.Sprintf("http://%s/v2/%s/%s/blobs/%s", registryAddr, stream.Namespace, stream.Name, digest) +func testPullThroughStatBlob(baseURL string, stream *imageapiv1.ImageStreamImport, user, token, digest string) error { + url := fmt.Sprintf("%s/v2/%s/%s/blobs/%s", baseURL, stream.Namespace, stream.Name, digest) req, err := http.NewRequest("HEAD", url, nil) if err != nil { @@ -94,43 +91,12 @@ func testPullThroughStatBlob(registryAddr string, stream *imageapiv1.ImageStream } func TestPullThroughInsecure(t *testing.T) { - tmpDir, err := ioutil.TempDir("", "image-registry-test-integration-") - if err != nil { - t.Fatalf("failed to create temporary directory: %s", err) - } - defer os.RemoveAll(tmpDir) - - configDir := path.Join(tmpDir, "config") - adminKubeConfigPath := path.Join(configDir, "master", "admin.kubeconfig") - - masterContainer, err := testframework.StartMasterContainer(configDir) - if err != nil { - t.Fatal(err) - } - defer func() { - if err := masterContainer.Stop(); err != nil { - t.Log(err) - } - }() - - clusterAdminClientConfig, err := testframework.ConfigFromFile(adminKubeConfigPath) - if err != nil { - t.Fatal(err) - } + master := testframework.NewMaster(t) + defer master.Close() namespace := "image-registry-test-integration" - //imageStreamName := "test-imagelayers" - user := "testuser" - password := "testp@ssw0rd" - - if err := testframework.CreateProject(clusterAdminClientConfig, namespace, user); err != nil { - t.Fatal(err) - } - - token, err := tokencmd.RequestToken(clusterAdminClientConfig, nil, user, password) - if err != nil { - t.Fatalf("error requesting token: %v", err) - } + testuser := master.CreateUser("testuser", "testp@ssw0rd") + master.CreateProject(namespace, testuser.Name) // start regular HTTP server reponame := "testrepo" @@ -229,9 +195,7 @@ func TestPullThroughInsecure(t *testing.T) { }, } - userClientConfig := testframework.UserClientConfig(clusterAdminClientConfig, token) - - adminImageClient := imagev1.NewForConfigOrDie(userClientConfig) + adminImageClient := imagev1.NewForConfigOrDie(testuser.KubeConfig()) isi, err := adminImageClient.ImageStreamImports(namespace).Create(&stream) if err != nil { @@ -272,24 +236,22 @@ func TestPullThroughInsecure(t *testing.T) { } t.Logf("Run registry...") - registryAddr, err := testframework.StartTestRegistry(t, adminKubeConfigPath) - if err != nil { - t.Fatal(err) - } + registry := master.StartRegistry(t) + defer registry.Close() t.Logf("Run testPullThroughGetManifest with tag...") - if err := testPullThroughGetManifest(registryAddr, &stream, user, token, repotag); err != nil { + if err := testPullThroughGetManifest(registry.BaseURL(), &stream, testuser.Name, testuser.Token, repotag); err != nil { t.Fatal(err) } t.Logf("Run testPullThroughGetManifest with digest...") - if err := testPullThroughGetManifest(registryAddr, &stream, user, token, etcdDigest); err != nil { + if err := testPullThroughGetManifest(registry.BaseURL(), &stream, testuser.Name, testuser.Token, etcdDigest); err != nil { t.Fatal(err) } t.Logf("Run testPullThroughStatBlob (%s == true, spec.tags[%q].importPolicy.insecure == true)...", imageapi.InsecureRepositoryAnnotation, repotag) for digest := range descriptors { - if err := testPullThroughStatBlob(registryAddr, &stream, user, token, digest); err != nil { + if err := testPullThroughStatBlob(registry.BaseURL(), &stream, testuser.Name, testuser.Token, digest); err != nil { t.Fatal(err) } } @@ -307,7 +269,7 @@ func TestPullThroughInsecure(t *testing.T) { t.Logf("Run testPullThroughStatBlob (%s == false, spec.tags[%q].importPolicy.insecure == true)...", imageapi.InsecureRepositoryAnnotation, repotag) for digest := range descriptors { - if err := testPullThroughStatBlob(registryAddr, &stream, user, token, digest); err != nil { + if err := testPullThroughStatBlob(registry.BaseURL(), &stream, testuser.Name, testuser.Token, digest); err != nil { t.Fatal(err) } } @@ -329,7 +291,7 @@ func TestPullThroughInsecure(t *testing.T) { t.Logf("Run testPullThroughStatBlob (%s == false, spec.tags[%q].importPolicy.insecure == false)...", imageapi.InsecureRepositoryAnnotation, repotag) for digest := range descriptors { - if err := testPullThroughStatBlob(registryAddr, &stream, user, token, digest); err == nil { + if err := testPullThroughStatBlob(registry.BaseURL(), &stream, testuser.Name, testuser.Token, digest); err == nil { t.Fatal("unexpexted access to insecure blobs") } else { t.Logf("%#+v", err) diff --git a/test/integration/v2/v2_docker_registry_test.go b/test/integration/v2/v2_docker_registry_test.go index 43da2c5a22..cbb5e00b13 100644 --- a/test/integration/v2/v2_docker_registry_test.go +++ b/test/integration/v2/v2_docker_registry_test.go @@ -6,9 +6,7 @@ import ( "fmt" "io/ioutil" "net/http" - "net/url" - "os" - "path" + "strings" "testing" "github.com/docker/distribution/context" @@ -20,7 +18,6 @@ import ( metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" - "github.com/openshift/origin/pkg/cmd/util/tokencmd" imageapi "github.com/openshift/origin/pkg/image/apis/image" imageclient "github.com/openshift/origin/pkg/image/generated/internalclientset" @@ -87,49 +84,19 @@ func signedManifest(name string, blobs []digest.Digest) ([]byte, digest.Digest, } func TestV2RegistryGetTags(t *testing.T) { - tmpDir, err := ioutil.TempDir("", "image-registry-test-integration-") - if err != nil { - t.Fatalf("failed to create temporary directory: %s", err) - } - defer os.RemoveAll(tmpDir) - - configDir := path.Join(tmpDir, "config") - adminKubeConfigPath := path.Join(configDir, "master", "admin.kubeconfig") - - masterContainer, err := testframework.StartMasterContainer(configDir) - if err != nil { - t.Fatal(err) - } - defer func() { - if err := masterContainer.Stop(); err != nil { - t.Log(err) - } - }() - - clusterAdminClientConfig, err := testframework.ConfigFromFile(adminKubeConfigPath) - if err != nil { - t.Fatal(err) - } + master := testframework.NewMaster(t) + defer master.Close() namespace := "namespace" - user := "admin" - password := "password" + testuser := master.CreateUser("admin", "password") + master.CreateProject(namespace, testuser.Name) - if err := testframework.CreateProject(clusterAdminClientConfig, namespace, user); err != nil { - t.Fatal(err) - } - - token, err := tokencmd.RequestToken(clusterAdminClientConfig, nil, user, password) - if err != nil { - t.Fatalf("error requesting token: %v", err) - } + registry := master.StartRegistry(t) + defer registry.Close() - registryAddr, err := testframework.StartTestRegistry(t, adminKubeConfigPath) - if err != nil { - t.Fatalf("start registry: %v", err) - } + baseURL := registry.BaseURL() - adminImageClient := imageclient.NewForConfigOrDie(clusterAdminClientConfig) + adminImageClient := imageclient.NewForConfigOrDie(master.AdminKubeConfig()) stream := imageapi.ImageStream{ ObjectMeta: metav1.ObjectMeta{ @@ -141,7 +108,7 @@ func TestV2RegistryGetTags(t *testing.T) { t.Fatalf("error creating image stream: %s", err) } - tags, err := getTags(registryAddr, namespace, stream.Name, user, token) + tags, err := getTags(baseURL, namespace, stream.Name, testuser.Name, testuser.Token) if err != nil { t.Fatal(err) } @@ -149,17 +116,17 @@ func TestV2RegistryGetTags(t *testing.T) { t.Fatalf("expected 0 tags, got: %#v", tags) } - err = putEmptyBlob(registryAddr, namespace, stream.Name, user, token) + err = putEmptyBlob(baseURL, namespace, stream.Name, testuser.Name, testuser.Token) if err != nil { t.Fatal(err) } - dgst, err := putManifest(registryAddr, namespace, stream.Name, user, token) + dgst, err := putManifest(baseURL, namespace, stream.Name, testuser.Name, testuser.Token) if err != nil { t.Fatal(err) } - tags, err = getTags(registryAddr, namespace, stream.Name, user, token) + tags, err = getTags(baseURL, namespace, stream.Name, testuser.Name, testuser.Token) if err != nil { t.Fatal(err) } @@ -171,12 +138,12 @@ func TestV2RegistryGetTags(t *testing.T) { } // test get by tag - url := fmt.Sprintf("http://%s/v2/%s/%s/manifests/%s", registryAddr, namespace, stream.Name, imageapi.DefaultImageTag) + url := fmt.Sprintf("%s/v2/%s/%s/manifests/%s", baseURL, namespace, stream.Name, imageapi.DefaultImageTag) req, err := http.NewRequest("GET", url, nil) if err != nil { t.Fatalf("error creating request: %v", err) } - req.SetBasicAuth(user, token) + req.SetBasicAuth(testuser.Name, testuser.Token) resp, err := http.DefaultClient.Do(req) if err != nil { t.Fatalf("error retrieving manifest from registry: %s", err) @@ -201,12 +168,12 @@ func TestV2RegistryGetTags(t *testing.T) { } // test get by digest - url = fmt.Sprintf("http://%s/v2/%s/%s/manifests/%s", registryAddr, namespace, stream.Name, dgst.String()) + url = fmt.Sprintf("%s/v2/%s/%s/manifests/%s", baseURL, namespace, stream.Name, dgst.String()) req, err = http.NewRequest("GET", url, nil) if err != nil { t.Fatalf("error creating request: %v", err) } - req.SetBasicAuth(user, token) + req.SetBasicAuth(testuser.Name, testuser.Token) resp, err = http.DefaultClient.Do(req) if err != nil { t.Fatalf("error retrieving manifest from registry: %s", err) @@ -239,7 +206,7 @@ func TestV2RegistryGetTags(t *testing.T) { if e, a := dgst.String(), image.Image.Name; e != a { t.Errorf("image name: expected %q, got %q", e, a) } - if e, a := fmt.Sprintf("%s/%s/%s@%s", registryAddr, namespace, stream.Name, dgst.String()), image.Image.DockerImageReference; e != a { + if e, a := fmt.Sprintf("%s/%s/%s@%s", strings.TrimPrefix(baseURL, "http://"), namespace, stream.Name, dgst.String()), image.Image.DockerImageReference; e != a { t.Errorf("image dockerImageReference: expected %q, got %q", e, a) } if e, a := "foo", image.Image.DockerImageMetadata.ID; e != a { @@ -253,12 +220,12 @@ func TestV2RegistryGetTags(t *testing.T) { t.Fatalf("expected error getting otherrepo") } - err = putEmptyBlob(registryAddr, namespace, "otherrepo", user, token) + err = putEmptyBlob(baseURL, namespace, "otherrepo", testuser.Name, testuser.Token) if err != nil { t.Fatal(err) } - otherDigest, err := putManifest(registryAddr, namespace, "otherrepo", user, token) + otherDigest, err := putManifest(baseURL, namespace, "otherrepo", testuser.Name, testuser.Token) if err != nil { t.Fatal(err) } @@ -285,14 +252,14 @@ func TestV2RegistryGetTags(t *testing.T) { } } -func putManifest(registryAddr, namespace, name, user, token string) (digest.Digest, error) { +func putManifest(baseURL, namespace, name, user, token string) (digest.Digest, error) { creds := registryutil.NewBasicCredentialStore(user, token) - desc, _, err := registryutil.UploadRandomTestBlob(context.Background(), &url.URL{Host: registryAddr, Scheme: "http"}, creds, namespace+"/"+name) + desc, _, err := registryutil.UploadRandomTestBlob(context.Background(), baseURL, creds, namespace+"/"+name) if err != nil { return "", err } - putUrl := fmt.Sprintf("http://%s/v2/%s/%s/manifests/%s", registryAddr, namespace, name, imageapi.DefaultImageTag) + putUrl := fmt.Sprintf("%s/v2/%s/%s/manifests/%s", baseURL, namespace, name, imageapi.DefaultImageTag) signedManifest, dgst, err := signedManifest(fmt.Sprintf("%s/%s", namespace, name), []digest.Digest{desc.Digest}) if err != nil { return "", err @@ -314,8 +281,8 @@ func putManifest(registryAddr, namespace, name, user, token string) (digest.Dige return dgst, nil } -func putEmptyBlob(registryAddr, namespace, name, user, token string) error { - putUrl := fmt.Sprintf("http://%s/v2/%s/%s/blobs/uploads/", registryAddr, namespace, name) +func putEmptyBlob(baseURL, namespace, name, user, token string) error { + putUrl := fmt.Sprintf("%s/v2/%s/%s/blobs/uploads/", baseURL, namespace, name) method := "POST" for range []int{1, 2} { @@ -345,8 +312,8 @@ func putEmptyBlob(registryAddr, namespace, name, user, token string) error { return nil } -func getTags(registryAddr, namespace, streamName, user, token string) ([]string, error) { - url := fmt.Sprintf("http://%s/v2/%s/%s/tags/list", registryAddr, namespace, streamName) +func getTags(baseURL, namespace, streamName, user, token string) ([]string, error) { + url := fmt.Sprintf("%s/v2/%s/%s/tags/list", baseURL, namespace, streamName) client := http.DefaultClient req, err := http.NewRequest("GET", url, nil) if err != nil {