diff --git a/modules/dockerregistry/dockerregistry.go b/modules/dockerregistry/dockerregistry.go index e714000e6b..0ac588e4f2 100644 --- a/modules/dockerregistry/dockerregistry.go +++ b/modules/dockerregistry/dockerregistry.go @@ -4,6 +4,7 @@ import ( "context" "github.com/testcontainers/testcontainers-go" + "github.com/testcontainers/testcontainers-go/wait" ) // DockerRegistryContainer represents the DockerRegistry container type used in the module @@ -14,8 +15,9 @@ type DockerRegistryContainer struct { // RunContainer creates an instance of the DockerRegistry container type func RunContainer(ctx context.Context, opts ...testcontainers.ContainerCustomizer) (*DockerRegistryContainer, error) { req := testcontainers.ContainerRequest{ - Image: "docker.io/registry:latest", - ExposedPorts: []string{"5000/tcp"}, + Image: "registry:2", + ExposedPorts: []string{"5000:5000/tcp"}, + WaitingFor: wait.ForExposedPort(), } genericContainerReq := testcontainers.GenericContainerRequest{ @@ -85,3 +87,10 @@ func WithData(dataDir string) testcontainers.CustomizeRequestOption { } } + +// WithImage customizer that will override the registry image used +func WithImage(image string) testcontainers.CustomizeRequestOption { + return func(req *testcontainers.GenericContainerRequest) { + req.Image = image + } +} diff --git a/modules/dockerregistry/dockerregistry_test.go b/modules/dockerregistry/dockerregistry_test.go index 401e1d5962..c6f0cf291b 100644 --- a/modules/dockerregistry/dockerregistry_test.go +++ b/modules/dockerregistry/dockerregistry_test.go @@ -5,8 +5,8 @@ import ( "net/http" "os" "testing" - "time" + "github.com/docker/go-connections/nat" "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" @@ -14,6 +14,12 @@ import ( "github.com/testcontainers/testcontainers-go/wait" ) +var originalDockerAuthConfig string + +func init() { + originalDockerAuthConfig = os.Getenv("DOCKER_AUTH_CONFIG") +} + func TestDockerRegistry(t *testing.T) { ctx := context.Background() @@ -29,28 +35,95 @@ func TestDockerRegistry(t *testing.T) { } }) - port, err := container.MappedPort(ctx, "5000") + port, ipAddress := getRegistryPortAndAddress(t, err, container, ctx) + + // Let's simply check that the registry is up and running with a GET to http://localhost:5000/v2/_catalog + resp, err := http.Get("http://" + ipAddress + ":" + port.Port() + "/v2/_catalog") if err != nil { + // handle err t.Fatal(err) } + defer resp.Body.Close() + _, cancel := context.WithCancel(context.Background()) + t.Cleanup(cancel) +} - ipAddress, err := container.Host(ctx) +func TestDockerRegistryWithCustomImage(t *testing.T) { + ctx := context.Background() + container, err := RunContainer(ctx, WithImage("docker.io/registry:latest")) if err != nil { t.Fatal(err) } - time.Sleep(1 * time.Second) - // perform assertions + // Clean up the container after the test is complete + t.Cleanup(func() { + if err := container.Terminate(ctx); err != nil { + t.Fatalf("failed to terminate container: %s", err) + } + }) + + port, ipAddress := getRegistryPortAndAddress(t, err, container, ctx) + + // Let's check that the registry is up and running with a GET to http://localhost:5000/v2/_catalog also using a different image resp, err := http.Get("http://" + ipAddress + ":" + port.Port() + "/v2/_catalog") if err != nil { // handle err t.Fatal(err) } defer resp.Body.Close() + _, cancel := context.WithCancel(context.Background()) + t.Cleanup(cancel) +} + +func TestDockerRegistryWithData(t *testing.T) { + ctx := context.Background() + wd, err := os.Getwd() + assert.NoError(t, err) + container, err := RunContainer(ctx, WithData(wd+"/../../testdata/data")) + if err != nil { + t.Fatal(err) + } + + // Clean up the container after the test is complete + t.Cleanup(func() { + if err := container.Terminate(ctx); err != nil { + t.Fatalf("failed to terminate container: %s", err) + } + }) + + // Let's check that we are able to start a container form image localhost:5000/redis:5.0-alpine + req := testcontainers.ContainerRequest{ + Image: "localhost:5000/redis:5.0-alpine", + AlwaysPullImage: true, // make sure the authentication takes place + ExposedPorts: []string{"6379/tcp"}, + WaitingFor: wait.ForLog("Ready to accept connections"), + } + + redisContainer, err := testcontainers.GenericContainer(ctx, testcontainers.GenericContainerRequest{ + ContainerRequest: req, + Started: true, + }) + require.Nil(t, err) + terminateContainerOnEnd(t, ctx, redisContainer) + _, cancel := context.WithCancel(context.Background()) + t.Cleanup(cancel) } func TestDockerRegistryWithAuth(t *testing.T) { + t.Cleanup(func() { + os.Setenv("DOCKER_AUTH_CONFIG", originalDockerAuthConfig) + }) + os.Unsetenv("DOCKER_AUTH_CONFIG") + + // using the same credentials as in the Docker Registry + base64 := "dGVzdHVzZXI6dGVzdHBhc3N3b3Jk" // testuser:testpassword + t.Setenv("DOCKER_AUTH_CONFIG", `{ + "auths": { + "localhost:5000": { "username": "testuser", "password": "testpassword", "auth": "`+base64+`" } + }, + "credsStore": "desktop" + }`) ctx := context.Background() wd, err := os.Getwd() assert.NoError(t, err) @@ -67,9 +140,8 @@ func TestDockerRegistryWithAuth(t *testing.T) { } }) - time.Sleep(1 * time.Second) - - ctx = context.Background() + // Let's check that we are able to start a container form image localhost:5000/redis:5.0-alpine + // using default username and password req := testcontainers.ContainerRequest{ Image: "localhost:5000/redis:5.0-alpine", AlwaysPullImage: true, // make sure the authentication takes place @@ -83,6 +155,22 @@ func TestDockerRegistryWithAuth(t *testing.T) { }) require.Nil(t, err) terminateContainerOnEnd(t, ctx, redisContainer) + _, cancel := context.WithCancel(context.Background()) + t.Cleanup(cancel) +} + +func getRegistryPortAndAddress(t *testing.T, err error, container *DockerRegistryContainer, ctx context.Context) (nat.Port, string) { + port, err := container.MappedPort(ctx, "5000") + if err != nil { + t.Fatal(err) + } + + ipAddress, err := container.Host(ctx) + + if err != nil { + t.Fatal(err) + } + return port, ipAddress } func terminateContainerOnEnd(tb testing.TB, ctx context.Context, ctr testcontainers.Container) {