diff --git a/.github/ISSUE_TEMPLATE/bug_report.yml b/.github/ISSUE_TEMPLATE/bug_report.yml index dd43c52e5b..70383e57cb 100644 --- a/.github/ISSUE_TEMPLATE/bug_report.yml +++ b/.github/ISSUE_TEMPLATE/bug_report.yml @@ -1,7 +1,7 @@ -name: Bug Report +name: Bug report description: File a bug report title: "[Bug]: " -labels: ["type/bug"] +labels: ["bug"] body: - type: markdown attributes: @@ -36,7 +36,7 @@ body: - type: input id: host-arch attributes: - label: Host Arch + label: Host arch description: Which architecture are you using? placeholder: e.g. x86, ARM validations: @@ -44,7 +44,7 @@ body: - type: input id: go-version attributes: - label: Go Version + label: Go version description: Which Go version are you using? placeholder: e.g. 1.18 validations: @@ -81,7 +81,7 @@ body: - type: textarea id: additional-information attributes: - label: Additional Information + label: Additional information description: | Any links or references to have more context about the issue. diff --git a/.github/ISSUE_TEMPLATE/enhancement.yml b/.github/ISSUE_TEMPLATE/enhancement.yml index 5b21685c48..9b292862e9 100644 --- a/.github/ISSUE_TEMPLATE/enhancement.yml +++ b/.github/ISSUE_TEMPLATE/enhancement.yml @@ -1,7 +1,7 @@ name: Enhancement description: Suggest an enhancement title: "[Enhancement]: " -labels: ["type/enhancement"] +labels: ["enhancement"] body: - type: markdown attributes: diff --git a/.github/ISSUE_TEMPLATE/feature.yml b/.github/ISSUE_TEMPLATE/feature.yml index ae86287742..2a23213975 100644 --- a/.github/ISSUE_TEMPLATE/feature.yml +++ b/.github/ISSUE_TEMPLATE/feature.yml @@ -1,7 +1,7 @@ name: Feature description: Suggest a new feature title: "[Feature]: " -labels: ["type/feature"] +labels: ["feature"] body: - type: markdown attributes: diff --git a/.github/PULL_REQUEST_TEMPLATE.md b/.github/PULL_REQUEST_TEMPLATE.md new file mode 100644 index 0000000000..5866b696c4 --- /dev/null +++ b/.github/PULL_REQUEST_TEMPLATE.md @@ -0,0 +1,40 @@ + + +## What does this PR do? + + + +## Why is it important? + + + +## Related issues + + +- + + + + + diff --git a/.github/release-drafter.yml b/.github/release-drafter.yml index c950617c5f..1800f3c76b 100644 --- a/.github/release-drafter.yml +++ b/.github/release-drafter.yml @@ -6,6 +6,7 @@ template: | categories: - title: 🚀 Features labels: + - 'enhancement' - 'feature' - title: ⚠️ Breaking Changes labels: diff --git a/.github/settings.yml b/.github/settings.yml index 362fb9eb56..e9b570d9e8 100644 --- a/.github/settings.yml +++ b/.github/settings.yml @@ -181,7 +181,7 @@ branches: # Required. Require branches to be up to date before merging. strict: true # Required. The list of status checks to require in order to merge into this branch - contexts: ["static-analysis", "tests (1.x, ubuntu-latest)"] + contexts: ["static-analysis", "test (1.x, ubuntu-latest)"] # Required. Enforce all configured restrictions for administrators. Set to true to enforce required status checks for repository administrators. Set to null to disable. enforce_admins: false # Prevent merge commits from being pushed to matching branches diff --git a/container.go b/container.go index 5f0684640a..ecf0186305 100644 --- a/container.go +++ b/container.go @@ -65,21 +65,23 @@ type Container interface { // ImageBuildInfo defines what is needed to build an image type ImageBuildInfo interface { - GetContext() (io.Reader, error) // the path to the build context - GetDockerfile() string // the relative path to the Dockerfile, including the fileitself - ShouldPrintBuildLog() bool // allow build log to be printed to stdout - ShouldBuildImage() bool // return true if the image needs to be built - GetBuildArgs() map[string]*string // return the environment args used to build the from Dockerfile + GetContext() (io.Reader, error) // the path to the build context + GetDockerfile() string // the relative path to the Dockerfile, including the fileitself + ShouldPrintBuildLog() bool // allow build log to be printed to stdout + ShouldBuildImage() bool // return true if the image needs to be built + GetBuildArgs() map[string]*string // return the environment args used to build the from Dockerfile + GetAuthConfigs() map[string]types.AuthConfig // return the auth configs to be able to pull from an authenticated docker registry } // FromDockerfile represents the parameters needed to build an image from a Dockerfile // rather than using a pre-built one type FromDockerfile struct { - Context string // the path to the context of of the docker build - ContextArchive io.Reader // the tar archive file to send to docker that contains the build context - Dockerfile string // the path from the context to the Dockerfile for the image, defaults to "Dockerfile" - BuildArgs map[string]*string // enable user to pass build args to docker daemon - PrintBuildLog bool // enable user to print build log + Context string // the path to the context of of the docker build + ContextArchive io.Reader // the tar archive file to send to docker that contains the build context + Dockerfile string // the path from the context to the Dockerfile for the image, defaults to "Dockerfile" + BuildArgs map[string]*string // enable user to pass build args to docker daemon + PrintBuildLog bool // enable user to print build log + AuthConfigs map[string]types.AuthConfig // enable auth configs to be able to pull from an authenticated docker registry } type ContainerFile struct { @@ -230,6 +232,11 @@ func (c *ContainerRequest) GetDockerfile() string { return f } +// GetAuthConfigs returns the auth configs to be able to pull from an authenticated docker registry +func (c *ContainerRequest) GetAuthConfigs() map[string]types.AuthConfig { + return c.FromDockerfile.AuthConfigs +} + func (c *ContainerRequest) ShouldBuildImage() bool { return c.FromDockerfile.Context != "" || c.FromDockerfile.ContextArchive != nil } diff --git a/container_test.go b/container_test.go index 88953d21bc..b8cc1329e8 100644 --- a/container_test.go +++ b/container_test.go @@ -12,6 +12,7 @@ import ( "testing" "time" + "github.com/docker/docker/api/types" "github.com/stretchr/testify/assert" "github.com/testcontainers/testcontainers-go/wait" @@ -127,6 +128,50 @@ func Test_GetDockerfile(t *testing.T) { } } +func Test_GetAuthConfigs(t *testing.T) { + type TestCase struct { + name string + ExpectedAuthConfigs map[string]types.AuthConfig + ContainerRequest ContainerRequest + } + + testTable := []TestCase{ + { + name: "defaults to no auth", + ExpectedAuthConfigs: nil, + ContainerRequest: ContainerRequest{ + FromDockerfile: FromDockerfile{}, + }, + }, + { + name: "will specify credentials", + ExpectedAuthConfigs: map[string]types.AuthConfig{ + "https://myregistry.com/": { + Username: "username", + Password: "password", + }, + }, + ContainerRequest: ContainerRequest{ + FromDockerfile: FromDockerfile{ + AuthConfigs: map[string]types.AuthConfig{ + "https://myregistry.com/": { + Username: "username", + Password: "password", + }, + }, + }, + }, + }, + } + + for _, testCase := range testTable { + t.Run(testCase.name, func(t *testing.T) { + cfgs := testCase.ContainerRequest.GetAuthConfigs() + assert.Equal(t, testCase.ExpectedAuthConfigs, cfgs) + }) + } +} + func Test_BuildImageWithContexts(t *testing.T) { type TestCase struct { Name string diff --git a/docker.go b/docker.go index 80c4e63c63..1e1e5ba697 100644 --- a/docker.go +++ b/docker.go @@ -879,6 +879,7 @@ func (p *DockerProvider) BuildImage(ctx context.Context, img ImageBuildInfo) (st buildOptions := types.ImageBuildOptions{ BuildArgs: img.GetBuildArgs(), Dockerfile: img.GetDockerfile(), + AuthConfigs: img.GetAuthConfigs(), Context: buildContext, Tags: []string{repoTag}, Remove: true, diff --git a/docker_test.go b/docker_test.go index 9714cf4f49..f051c36eb5 100644 --- a/docker_test.go +++ b/docker_test.go @@ -5,8 +5,8 @@ import ( "database/sql" "errors" "fmt" + // Import mysql into the scope of this package (required) - _ "github.com/go-sql-driver/mysql" "io" "io/ioutil" "math/rand" @@ -18,6 +18,8 @@ import ( "testing" "time" + _ "github.com/go-sql-driver/mysql" + "github.com/docker/docker/api/types/container" "github.com/docker/docker/api/types/strslice" "github.com/docker/go-units" @@ -43,6 +45,7 @@ const ( nginxAlpineImage = "docker.io/nginx:alpine" nginxDefaultPort = "80/tcp" nginxHighPort = "8080/tcp" + daemonMaxVersion = "1.41" ) var providerType = ProviderDocker @@ -53,6 +56,7 @@ func init() { } } +// testNetworkAliases { func TestContainerAttachedToNewNetwork(t *testing.T) { aliases := []string{"alias1", "alias2", "alias3"} networkName := "new-network" @@ -130,6 +134,8 @@ func TestContainerAttachedToNewNetwork(t *testing.T) { } } +// } + func TestContainerWithHostNetworkOptions(t *testing.T) { absPath, err := filepath.Abs("./testresources/nginx-highport.conf") if err != nil { @@ -980,6 +986,7 @@ func TestContainerCreationWaitsForLogContextTimeout(t *testing.T) { } func TestContainerCreationWaitsForLog(t *testing.T) { + // exposePorts { ctx := context.Background() req := ContainerRequest{ Image: "docker.io/mysql:latest", @@ -995,13 +1002,18 @@ func TestContainerCreationWaitsForLog(t *testing.T) { ContainerRequest: req, Started: true, }) + // } require.NoError(t, err) terminateContainerOnEnd(t, ctx, mysqlC) + // containerHost { host, _ := mysqlC.Host(ctx) + // } + // mappedPort { p, _ := mysqlC.MappedPort(ctx, "3306/tcp") port := p.Int() + // } connectionString := fmt.Sprintf("%s:%s@tcp(%s:%d)/%s?tls=skip-verify", "root", "password", host, port, "database") @@ -1033,7 +1045,108 @@ func Test_BuildContainerFromDockerfile(t *testing.T) { WaitingFor: wait.ForLog("Ready to accept connections"), } - t.Log("creating generic container request from container request") + redisC, err := prepareRedisImage(ctx, req, t) + require.NoError(t, err) + terminateContainerOnEnd(t, ctx, redisC) + + checkSuccessfulRedisImage(ctx, redisC, t) + +} + +func Test_BuildContainerFromDockerfileWithAuthConfig_ShouldSucceedWithAuthConfigs(t *testing.T) { + prepareLocalRegistryWithAuth(t) + defer func() { + ctx := context.Background() + testcontainersClient, err := client.NewClientWithOpts(client.WithVersion(daemonMaxVersion)) + if err != nil { + t.Log("could not create client to cleanup registry: ", err) + } + + _, err = testcontainersClient.ImageRemove(ctx, "localhost:5000/redis:5.0-alpine", types.ImageRemoveOptions{ + Force: true, + PruneChildren: true, + }) + if err != nil { + t.Log("could not remove image: ", err) + } + + }() + + t.Log("getting context") + ctx := context.Background() + t.Log("got context, creating container request") + req := ContainerRequest{ + FromDockerfile: FromDockerfile{ + Context: "./testresources", + Dockerfile: "auth.Dockerfile", + AuthConfigs: map[string]types.AuthConfig{ + "localhost:5000": { + Username: "testuser", + Password: "testpassword", + }, + }, + }, + + ExposedPorts: []string{"6379/tcp"}, + WaitingFor: wait.ForLog("Ready to accept connections"), + } + + redisC, err := prepareRedisImage(ctx, req, t) + require.NoError(t, err) + terminateContainerOnEnd(t, ctx, redisC) + + checkSuccessfulRedisImage(ctx, redisC, t) +} + +func Test_BuildContainerFromDockerfileWithAuthConfig_ShouldFailWithoutAuthConfigs(t *testing.T) { + prepareLocalRegistryWithAuth(t) + + t.Log("getting context") + ctx := context.Background() + t.Log("got context, creating container request") + req := ContainerRequest{ + FromDockerfile: FromDockerfile{ + Context: "./testresources", + Dockerfile: "auth.Dockerfile", + }, + ExposedPorts: []string{"6379/tcp"}, + WaitingFor: wait.ForLog("Ready to accept connections"), + } + + redisC, err := prepareRedisImage(ctx, req, t) + require.Error(t, err) + terminateContainerOnEnd(t, ctx, redisC) +} + +func prepareLocalRegistryWithAuth(t *testing.T) { + ctx := context.Background() + wd, err := os.Getwd() + assert.NoError(t, err) + req := ContainerRequest{ + Image: "registry:2", + ExposedPorts: []string{"5000:5000/tcp"}, + Env: map[string]string{ + "REGISTRY_AUTH": "htpasswd", + "REGISTRY_AUTH_HTPASSWD_REALM": "Registry", + "REGISTRY_AUTH_HTPASSWD_PATH": "/auth/htpasswd", + "REGISTRY_STORAGE_FILESYSTEM_ROOTDIRECTORY": "/data", + }, + Mounts: ContainerMounts{ + ContainerMount{ + Source: GenericBindMountSource{ + HostPath: fmt.Sprintf("%s/testresources/auth", wd), + }, + Target: "/auth", + }, + ContainerMount{ + Source: GenericBindMountSource{ + HostPath: fmt.Sprintf("%s/testresources/data", wd), + }, + Target: "/data", + }, + }, + WaitingFor: wait.ForExposedPort(), + } genContainerReq := GenericContainerRequest{ ProviderType: providerType, @@ -1041,14 +1154,38 @@ func Test_BuildContainerFromDockerfile(t *testing.T) { Started: true, } + t.Log("creating registry container") + + registryC, err := GenericContainer(ctx, genContainerReq) + assert.NoError(t, err) + + t.Cleanup(func() { + assert.NoError(t, registryC.Terminate(context.Background())) + }) + + ctx, cancel := context.WithCancel(context.Background()) + t.Cleanup(cancel) +} + +func prepareRedisImage(ctx context.Context, req ContainerRequest, t *testing.T) (Container, error) { + genContainerReq := GenericContainerRequest{ + ProviderType: providerType, + ContainerRequest: req, + Started: true, + } + t.Log("creating redis container") redisC, err := GenericContainer(ctx, genContainerReq) - require.NoError(t, err) - terminateContainerOnEnd(t, ctx, redisC) t.Log("created redis container") + return redisC, err +} + +func checkSuccessfulRedisImage(ctx context.Context, redisC Container, t *testing.T) { + t.Log("created redis container") + t.Log("getting redis container endpoint") endpoint, err := redisC.Endpoint(ctx, "") if err != nil { @@ -1057,12 +1194,12 @@ func Test_BuildContainerFromDockerfile(t *testing.T) { t.Log("retrieved redis container endpoint") - client := redis.NewClient(&redis.Options{ + redisClient := redis.NewClient(&redis.Options{ Addr: endpoint, }) t.Log("pinging redis") - pong, err := client.Ping(ctx).Result() + pong, err := redisClient.Ping(ctx).Result() require.NoError(t, err) t.Log("received response from redis") @@ -1242,6 +1379,7 @@ func TestContainerCreationWaitsForLogAndPort(t *testing.T) { require.NoError(t, err) terminateContainerOnEnd(t, ctx, mysqlC) + // buildingAddresses { host, _ := mysqlC.Host(ctx) p, _ := mysqlC.MappedPort(ctx, "3306/tcp") port := p.Int() @@ -1252,6 +1390,7 @@ func TestContainerCreationWaitsForLogAndPort(t *testing.T) { if err != nil { t.Fatal(err) } + // } defer db.Close() @@ -2437,6 +2576,9 @@ func assertExtractedFiles(t *testing.T, ctx context.Context, container Container require.NoError(t, err) for _, srcFile := range srcFiles { + if srcFile.IsDir() { + continue + } srcBytes, err := ioutil.ReadFile(filepath.Join(hostFilePath, srcFile.Name())) if err != nil { require.NoError(t, err) diff --git a/docs/features/build_from_dockerfile.md b/docs/features/build_from_dockerfile.md index 85068f62c1..bd146c0a30 100644 --- a/docs/features/build_from_dockerfile.md +++ b/docs/features/build_from_dockerfile.md @@ -58,5 +58,26 @@ fromDockerfile := testcontainers.FromDockerfile{ } ``` -**Please Note** if you specify a `ContextArchive` this will cause Testcontainers-go to ignore the path passed +**Please Note** if you specify a `ContextArchive` this will cause _Testcontainers for Go_ to ignore the path passed in to `Context`. + +## Images requiring auth + +If you are building a local Docker image that is fetched from a Docker image in a registry requiring authentication +(e.g., assuming you are fetching from a custom registry such as `myregistry.com`), you will need to specify the +credentials to succeed, as follows: + +```go +req := ContainerRequest{ + FromDockerfile: testcontainers.FromDockerfile{ + Context: "/path/to/build/context", + Dockerfile: "CustomDockerfile", + AuthConfigs: map[string]types.AuthConfig{ + "https://myregistry.com": { + Username: "myusername", + Password: "mypassword", + }, + }, + }, +} +``` \ No newline at end of file diff --git a/docs/features/networking.md b/docs/features/networking.md new file mode 100644 index 0000000000..bee2f0f0ad --- /dev/null +++ b/docs/features/networking.md @@ -0,0 +1,56 @@ +# Networking and communicating with containers + +## Exposing container ports to the host + +It is common to want to connect to a container from your test process, running on the test 'host' machine. +For example, you may be testing some code that needs to connect to a backend or data store container. + +Generally, each required port needs to be explicitly exposed. For example, we can specify one or more ports as follows: + + +[Exposing ports](../../docker_test.go) inside_block:exposePorts + + +Note that this exposed port number is from the *perspective of the container*. + +*From the host's perspective* Testcontainers actually exposes this on a random free port. +This is by design, to avoid port collisions that may arise with locally running software or in between parallel test runs. + +Because there is this layer of indirection, it is necessary to ask Testcontainers for the actual mapped port at runtime. +This can be done using the `MappedPort` function, which takes the original (container) port as an argument: + + +[Retrieving actual ports at runtime](../../docker_test.go) inside_block:mappedPort + + +!!! warning + Because the randomised port mapping happens during container startup, the container must be running at the time `MappedPort` is called. + You may need to ensure that the startup order of components in your tests caters for this. + +## Getting the container host + +When running with a local Docker daemon, exposed ports will usually be reachable on `localhost`. +However, in some CI environments they may instead be reachable on a different host. + +As such, Testcontainers provides a convenience function to obtain an address on which the container should be reachable from the host machine. + + +[Getting the container host](../../docker_test.go) inside_block:containerHost + + +It is normally advisable to use `Host` and `MappedPort` together when constructing addresses - for example: + + +[Getting the container host and mapped port](../../docker_test.go) inside_block:buildingAddresses + + +## Advanced networking + +Docker provides the ability for you to create custom networks and place containers on one or more networks. Then, communication can occur between networked containers without the need of exposing ports through the host. With Testcontainers, you can do this as well. + +!!! tip + Note that _Testcontainers for Go_ allows a container to be on multiple networks including network aliases. + + +[Creating custom networks](../../docker_test.go) inside_block:testNetworkAliases + diff --git a/docs/quickstart/gotest.md b/docs/quickstart/gotest.md index efbda64ccd..675fa03255 100644 --- a/docs/quickstart/gotest.md +++ b/docs/quickstart/gotest.md @@ -1,4 +1,4 @@ -Testcontainers-go plays well with the native `go test` framework. +_Testcontainers for Go_ plays well with the native `go test` framework. The ideal use case is for integration or end to end tests. It helps you to spin up and manage the dependencies life cycle via Docker. diff --git a/file_test.go b/file_test.go index 5b1c130d45..6c176c110a 100644 --- a/file_test.go +++ b/file_test.go @@ -73,6 +73,9 @@ func Test_TarDir(t *testing.T) { } for _, srcFile := range srcFiles { + if srcFile.IsDir() { + continue + } srcBytes, err := ioutil.ReadFile(filepath.Join(src, srcFile.Name())) if err != nil { t.Fatal(err) diff --git a/mkdocs.yml b/mkdocs.yml index 0c7fa8ef56..4283d8d990 100644 --- a/mkdocs.yml +++ b/mkdocs.yml @@ -30,6 +30,7 @@ nav: - quickstart/gotest.md - Features: - features/creating_container.md + - features/networking.md - features/garbage_collector.md - features/build_from_dockerfile.md - features/docker_compose.md diff --git a/testresources/auth.Dockerfile b/testresources/auth.Dockerfile new file mode 100644 index 0000000000..270ae9d9e9 --- /dev/null +++ b/testresources/auth.Dockerfile @@ -0,0 +1 @@ +FROM localhost:5000/redis:5.0-alpine \ No newline at end of file diff --git a/testresources/auth/htpasswd b/testresources/auth/htpasswd new file mode 100644 index 0000000000..9a5d4aaff6 --- /dev/null +++ b/testresources/auth/htpasswd @@ -0,0 +1,2 @@ +testuser:$2y$05$tTymaYlWwJOqie.bcSUUN.I.kxmo1m5TLzYQ4/ejJ46UMXGtq78EO + diff --git a/testresources/data/docker/registry/v2/blobs/sha256/21/213ec9aee27d8be045c6a92b7eac22c9a64b44558193775a1a7f626352392b49/data b/testresources/data/docker/registry/v2/blobs/sha256/21/213ec9aee27d8be045c6a92b7eac22c9a64b44558193775a1a7f626352392b49/data new file mode 100644 index 0000000000..caac84b41c Binary files /dev/null and b/testresources/data/docker/registry/v2/blobs/sha256/21/213ec9aee27d8be045c6a92b7eac22c9a64b44558193775a1a7f626352392b49/data differ diff --git a/testresources/data/docker/registry/v2/blobs/sha256/25/2595acf2c0bea71c4135062d027e4cf088b1674b3bb008b2571796491aa662b1/data b/testresources/data/docker/registry/v2/blobs/sha256/25/2595acf2c0bea71c4135062d027e4cf088b1674b3bb008b2571796491aa662b1/data new file mode 100644 index 0000000000..81c85d096f Binary files /dev/null and b/testresources/data/docker/registry/v2/blobs/sha256/25/2595acf2c0bea71c4135062d027e4cf088b1674b3bb008b2571796491aa662b1/data differ diff --git a/testresources/data/docker/registry/v2/blobs/sha256/64/64acd9e2f7c70ed5b56219f5b8949a8cbc67c03f8c51fdb6786bec66eec4476b/data b/testresources/data/docker/registry/v2/blobs/sha256/64/64acd9e2f7c70ed5b56219f5b8949a8cbc67c03f8c51fdb6786bec66eec4476b/data new file mode 100644 index 0000000000..7f2bf8fe86 Binary files /dev/null and b/testresources/data/docker/registry/v2/blobs/sha256/64/64acd9e2f7c70ed5b56219f5b8949a8cbc67c03f8c51fdb6786bec66eec4476b/data differ diff --git a/testresources/data/docker/registry/v2/blobs/sha256/66/662f040a4fc0e397e379a82a7d79663bb26698ae009d674e70b0224c4b155edb/data b/testresources/data/docker/registry/v2/blobs/sha256/66/662f040a4fc0e397e379a82a7d79663bb26698ae009d674e70b0224c4b155edb/data new file mode 100644 index 0000000000..8d34bd5df0 --- /dev/null +++ b/testresources/data/docker/registry/v2/blobs/sha256/66/662f040a4fc0e397e379a82a7d79663bb26698ae009d674e70b0224c4b155edb/data @@ -0,0 +1,41 @@ +{ + "schemaVersion": 2, + "mediaType": "application/vnd.docker.distribution.manifest.v2+json", + "config": { + "mediaType": "application/vnd.docker.container.image.v1+json", + "size": 6320, + "digest": "sha256:960343481690ac146b55e1b704b9685104f1710bd557c7a409c48fdc721929fa" + }, + "layers": [ + { + "mediaType": "application/vnd.docker.image.rootfs.diff.tar.gzip", + "size": 2806054, + "digest": "sha256:213ec9aee27d8be045c6a92b7eac22c9a64b44558193775a1a7f626352392b49" + }, + { + "mediaType": "application/vnd.docker.image.rootfs.diff.tar.gzip", + "size": 1271, + "digest": "sha256:fb541f77610a7550755893b11853752742e9b173e4e9967f4db6b02c2e51ce4a" + }, + { + "mediaType": "application/vnd.docker.image.rootfs.diff.tar.gzip", + "size": 398361, + "digest": "sha256:dc2e3041aaa57579fe87bf26fbb56fcf7aef49b3f5a0e0ee37eab519855dd37e" + }, + { + "mediaType": "application/vnd.docker.image.rootfs.diff.tar.gzip", + "size": 6444723, + "digest": "sha256:a59c682d8704ef96c5dec2f59481ac24993fb3c25a4a139e22e2db664a34b06a" + }, + { + "mediaType": "application/vnd.docker.image.rootfs.diff.tar.gzip", + "size": 135, + "digest": "sha256:64acd9e2f7c70ed5b56219f5b8949a8cbc67c03f8c51fdb6786bec66eec4476b" + }, + { + "mediaType": "application/vnd.docker.image.rootfs.diff.tar.gzip", + "size": 580, + "digest": "sha256:2595acf2c0bea71c4135062d027e4cf088b1674b3bb008b2571796491aa662b1" + } + ] +} \ No newline at end of file diff --git a/testresources/data/docker/registry/v2/blobs/sha256/96/960343481690ac146b55e1b704b9685104f1710bd557c7a409c48fdc721929fa/data b/testresources/data/docker/registry/v2/blobs/sha256/96/960343481690ac146b55e1b704b9685104f1710bd557c7a409c48fdc721929fa/data new file mode 100644 index 0000000000..4233a50f51 --- /dev/null +++ b/testresources/data/docker/registry/v2/blobs/sha256/96/960343481690ac146b55e1b704b9685104f1710bd557c7a409c48fdc721929fa/data @@ -0,0 +1 @@ +{"architecture":"amd64","config":{"Hostname":"","Domainname":"","User":"","AttachStdin":false,"AttachStdout":false,"AttachStderr":false,"ExposedPorts":{"6379/tcp":{}},"Tty":false,"OpenStdin":false,"StdinOnce":false,"Env":["PATH=/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin","REDIS_VERSION=5.0.14","REDIS_DOWNLOAD_URL=http://download.redis.io/releases/redis-5.0.14.tar.gz","REDIS_DOWNLOAD_SHA=3ea5024766d983249e80d4aa9457c897a9f079957d0fb1f35682df233f997f32"],"Cmd":["redis-server"],"Image":"sha256:c6b61b4eb28dfbb356b1faf35269891803301551508b1604cf67492e23f58496","Volumes":{"/data":{}},"WorkingDir":"/data","Entrypoint":["docker-entrypoint.sh"],"OnBuild":null,"Labels":null},"container":"3f5209e45fd8d25352646faf4c9ed85bd0f55581859dac5c0b6f71a0f354d59f","container_config":{"Hostname":"3f5209e45fd8","Domainname":"","User":"","AttachStdin":false,"AttachStdout":false,"AttachStderr":false,"ExposedPorts":{"6379/tcp":{}},"Tty":false,"OpenStdin":false,"StdinOnce":false,"Env":["PATH=/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin","REDIS_VERSION=5.0.14","REDIS_DOWNLOAD_URL=http://download.redis.io/releases/redis-5.0.14.tar.gz","REDIS_DOWNLOAD_SHA=3ea5024766d983249e80d4aa9457c897a9f079957d0fb1f35682df233f997f32"],"Cmd":["/bin/sh","-c","#(nop) ","CMD [\"redis-server\"]"],"Image":"sha256:c6b61b4eb28dfbb356b1faf35269891803301551508b1604cf67492e23f58496","Volumes":{"/data":{}},"WorkingDir":"/data","Entrypoint":["docker-entrypoint.sh"],"OnBuild":null,"Labels":{}},"created":"2022-10-07T03:33:38.951799853Z","docker_version":"20.10.12","history":[{"created":"2022-08-09T17:19:53.274069586Z","created_by":"/bin/sh -c #(nop) ADD file:2a949686d9886ac7c10582a6c29116fd29d3077d02755e87e111870d63607725 in / "},{"created":"2022-08-09T17:19:53.47374331Z","created_by":"/bin/sh -c #(nop) CMD [\"/bin/sh\"]","empty_layer":true},{"created":"2022-10-07T03:30:17.540132626Z","created_by":"/bin/sh -c addgroup -S -g 1000 redis \u0026\u0026 adduser -S -G redis -u 999 redis"},{"created":"2022-10-07T03:30:18.701947002Z","created_by":"/bin/sh -c apk add --no-cache \t\t'su-exec\u003e=0.2' \t\ttzdata"},{"created":"2022-10-07T03:33:00.969689587Z","created_by":"/bin/sh -c #(nop) ENV REDIS_VERSION=5.0.14","empty_layer":true},{"created":"2022-10-07T03:33:01.061281294Z","created_by":"/bin/sh -c #(nop) ENV REDIS_DOWNLOAD_URL=http://download.redis.io/releases/redis-5.0.14.tar.gz","empty_layer":true},{"created":"2022-10-07T03:33:01.154686334Z","created_by":"/bin/sh -c #(nop) ENV REDIS_DOWNLOAD_SHA=3ea5024766d983249e80d4aa9457c897a9f079957d0fb1f35682df233f997f32","empty_layer":true},{"created":"2022-10-07T03:33:37.807285887Z","created_by":"/bin/sh -c set -eux; \t\tapk add --no-cache --virtual .build-deps \t\tcoreutils \t\tdpkg-dev dpkg \t\tgcc \t\tlinux-headers \t\tmake \t\tmusl-dev \t\topenssl-dev \t\twget \t; \t\twget -O redis.tar.gz \"$REDIS_DOWNLOAD_URL\"; \techo \"$REDIS_DOWNLOAD_SHA *redis.tar.gz\" | sha256sum -c -; \tmkdir -p /usr/src/redis; \ttar -xzf redis.tar.gz -C /usr/src/redis --strip-components=1; \trm redis.tar.gz; \t\tgrep -q '^#define CONFIG_DEFAULT_PROTECTED_MODE 1$' /usr/src/redis/src/server.h; \tsed -ri 's!^(#define CONFIG_DEFAULT_PROTECTED_MODE) 1$!\\1 0!' /usr/src/redis/src/server.h; \tgrep -q '^#define CONFIG_DEFAULT_PROTECTED_MODE 0$' /usr/src/redis/src/server.h; \t\tgnuArch=\"$(dpkg-architecture --query DEB_BUILD_GNU_TYPE)\"; \textraJemallocConfigureFlags=\"--build=$gnuArch\"; \tdpkgArch=\"$(dpkg --print-architecture)\"; \tcase \"${dpkgArch##*-}\" in \t\tamd64 | i386 | x32) extraJemallocConfigureFlags=\"$extraJemallocConfigureFlags --with-lg-page=12\" ;; \t\t*) extraJemallocConfigureFlags=\"$extraJemallocConfigureFlags --with-lg-page=16\" ;; \tesac; \textraJemallocConfigureFlags=\"$extraJemallocConfigureFlags --with-lg-hugepage=21\"; \tgrep -F 'cd jemalloc \u0026\u0026 ./configure ' /usr/src/redis/deps/Makefile; \tsed -ri 's!cd jemalloc \u0026\u0026 ./configure !\u0026'\"$extraJemallocConfigureFlags\"' !' /usr/src/redis/deps/Makefile; \tgrep -F \"cd jemalloc \u0026\u0026 ./configure $extraJemallocConfigureFlags \" /usr/src/redis/deps/Makefile; \t\tmake -C /usr/src/redis -j \"$(nproc)\" all; \tmake -C /usr/src/redis install; \t\tserverMd5=\"$(md5sum /usr/local/bin/redis-server | cut -d' ' -f1)\"; export serverMd5; \tfind /usr/local/bin/redis* -maxdepth 0 \t\t-type f -not -name redis-server \t\t-exec sh -eux -c ' \t\t\tmd5=\"$(md5sum \"$1\" | cut -d\" \" -f1)\"; \t\t\ttest \"$md5\" = \"$serverMd5\"; \t\t' -- '{}' ';' \t\t-exec ln -svfT 'redis-server' '{}' ';' \t; \t\trm -r /usr/src/redis; \t\trunDeps=\"$( \t\tscanelf --needed --nobanner --format '%n#p' --recursive /usr/local \t\t\t| tr ',' '\\n' \t\t\t| sort -u \t\t\t| awk 'system(\"[ -e /usr/local/lib/\" $1 \" ]\") == 0 { next } { print \"so:\" $1 }' \t)\"; \tapk add --no-network --virtual .redis-rundeps $runDeps; \tapk del --no-network .build-deps; \t\tredis-cli --version; \tredis-server --version"},{"created":"2022-10-07T03:33:38.347225861Z","created_by":"/bin/sh -c mkdir /data \u0026\u0026 chown redis:redis /data"},{"created":"2022-10-07T03:33:38.444090076Z","created_by":"/bin/sh -c #(nop) VOLUME [/data]","empty_layer":true},{"created":"2022-10-07T03:33:38.545951015Z","created_by":"/bin/sh -c #(nop) WORKDIR /data","empty_layer":true},{"created":"2022-10-07T03:33:38.658945137Z","created_by":"/bin/sh -c #(nop) COPY file:a9e7249f657e2eec627bb4be492ad18aae3e5e1f0e47d22644eaf1ef2138c0ce in /usr/local/bin/ "},{"created":"2022-10-07T03:33:38.754618391Z","created_by":"/bin/sh -c #(nop) ENTRYPOINT [\"docker-entrypoint.sh\"]","empty_layer":true},{"created":"2022-10-07T03:33:38.850313099Z","created_by":"/bin/sh -c #(nop) EXPOSE 6379","empty_layer":true},{"created":"2022-10-07T03:33:38.951799853Z","created_by":"/bin/sh -c #(nop) CMD [\"redis-server\"]","empty_layer":true}],"os":"linux","rootfs":{"type":"layers","diff_ids":["sha256:994393dc58e7931862558d06e46aa2bb17487044f670f310dffe1d24e4d1eec7","sha256:6dbd9594c43d4115a12f2e203dfd586ba420dbd75a00d2d6c3feecdeb0048371","sha256:5669106330164180bda406cb49aa2126735bc29065b55354736d2656dffdbb96","sha256:ae23d15ebd31905b77598e96646e2cf46463bf8bd50e3b65c32ded7502402a9e","sha256:82566308f0b016b3848e916f16363ef5329f1fac362347fcb8cb99b1ba9461d7","sha256:beeee888b45e7cbe9e51c618ffe059806875e5570cb5607da75bd9e0b2649a43"]}} \ No newline at end of file diff --git a/testresources/data/docker/registry/v2/blobs/sha256/a5/a59c682d8704ef96c5dec2f59481ac24993fb3c25a4a139e22e2db664a34b06a/data b/testresources/data/docker/registry/v2/blobs/sha256/a5/a59c682d8704ef96c5dec2f59481ac24993fb3c25a4a139e22e2db664a34b06a/data new file mode 100644 index 0000000000..1ac50ad3c7 Binary files /dev/null and b/testresources/data/docker/registry/v2/blobs/sha256/a5/a59c682d8704ef96c5dec2f59481ac24993fb3c25a4a139e22e2db664a34b06a/data differ diff --git a/testresources/data/docker/registry/v2/blobs/sha256/dc/dc2e3041aaa57579fe87bf26fbb56fcf7aef49b3f5a0e0ee37eab519855dd37e/data b/testresources/data/docker/registry/v2/blobs/sha256/dc/dc2e3041aaa57579fe87bf26fbb56fcf7aef49b3f5a0e0ee37eab519855dd37e/data new file mode 100644 index 0000000000..f6708738a6 Binary files /dev/null and b/testresources/data/docker/registry/v2/blobs/sha256/dc/dc2e3041aaa57579fe87bf26fbb56fcf7aef49b3f5a0e0ee37eab519855dd37e/data differ diff --git a/testresources/data/docker/registry/v2/blobs/sha256/fb/fb541f77610a7550755893b11853752742e9b173e4e9967f4db6b02c2e51ce4a/data b/testresources/data/docker/registry/v2/blobs/sha256/fb/fb541f77610a7550755893b11853752742e9b173e4e9967f4db6b02c2e51ce4a/data new file mode 100644 index 0000000000..eca594a3ac Binary files /dev/null and b/testresources/data/docker/registry/v2/blobs/sha256/fb/fb541f77610a7550755893b11853752742e9b173e4e9967f4db6b02c2e51ce4a/data differ diff --git a/testresources/data/docker/registry/v2/repositories/redis/_layers/sha256/213ec9aee27d8be045c6a92b7eac22c9a64b44558193775a1a7f626352392b49/link b/testresources/data/docker/registry/v2/repositories/redis/_layers/sha256/213ec9aee27d8be045c6a92b7eac22c9a64b44558193775a1a7f626352392b49/link new file mode 100644 index 0000000000..a36da7dcd8 --- /dev/null +++ b/testresources/data/docker/registry/v2/repositories/redis/_layers/sha256/213ec9aee27d8be045c6a92b7eac22c9a64b44558193775a1a7f626352392b49/link @@ -0,0 +1 @@ +sha256:213ec9aee27d8be045c6a92b7eac22c9a64b44558193775a1a7f626352392b49 \ No newline at end of file diff --git a/testresources/data/docker/registry/v2/repositories/redis/_layers/sha256/2595acf2c0bea71c4135062d027e4cf088b1674b3bb008b2571796491aa662b1/link b/testresources/data/docker/registry/v2/repositories/redis/_layers/sha256/2595acf2c0bea71c4135062d027e4cf088b1674b3bb008b2571796491aa662b1/link new file mode 100644 index 0000000000..0e19e655c1 --- /dev/null +++ b/testresources/data/docker/registry/v2/repositories/redis/_layers/sha256/2595acf2c0bea71c4135062d027e4cf088b1674b3bb008b2571796491aa662b1/link @@ -0,0 +1 @@ +sha256:2595acf2c0bea71c4135062d027e4cf088b1674b3bb008b2571796491aa662b1 \ No newline at end of file diff --git a/testresources/data/docker/registry/v2/repositories/redis/_layers/sha256/64acd9e2f7c70ed5b56219f5b8949a8cbc67c03f8c51fdb6786bec66eec4476b/link b/testresources/data/docker/registry/v2/repositories/redis/_layers/sha256/64acd9e2f7c70ed5b56219f5b8949a8cbc67c03f8c51fdb6786bec66eec4476b/link new file mode 100644 index 0000000000..8fb2ca14b5 --- /dev/null +++ b/testresources/data/docker/registry/v2/repositories/redis/_layers/sha256/64acd9e2f7c70ed5b56219f5b8949a8cbc67c03f8c51fdb6786bec66eec4476b/link @@ -0,0 +1 @@ +sha256:64acd9e2f7c70ed5b56219f5b8949a8cbc67c03f8c51fdb6786bec66eec4476b \ No newline at end of file diff --git a/testresources/data/docker/registry/v2/repositories/redis/_layers/sha256/960343481690ac146b55e1b704b9685104f1710bd557c7a409c48fdc721929fa/link b/testresources/data/docker/registry/v2/repositories/redis/_layers/sha256/960343481690ac146b55e1b704b9685104f1710bd557c7a409c48fdc721929fa/link new file mode 100644 index 0000000000..ff0826dc7f --- /dev/null +++ b/testresources/data/docker/registry/v2/repositories/redis/_layers/sha256/960343481690ac146b55e1b704b9685104f1710bd557c7a409c48fdc721929fa/link @@ -0,0 +1 @@ +sha256:960343481690ac146b55e1b704b9685104f1710bd557c7a409c48fdc721929fa \ No newline at end of file diff --git a/testresources/data/docker/registry/v2/repositories/redis/_layers/sha256/a59c682d8704ef96c5dec2f59481ac24993fb3c25a4a139e22e2db664a34b06a/link b/testresources/data/docker/registry/v2/repositories/redis/_layers/sha256/a59c682d8704ef96c5dec2f59481ac24993fb3c25a4a139e22e2db664a34b06a/link new file mode 100644 index 0000000000..719dc0acdb --- /dev/null +++ b/testresources/data/docker/registry/v2/repositories/redis/_layers/sha256/a59c682d8704ef96c5dec2f59481ac24993fb3c25a4a139e22e2db664a34b06a/link @@ -0,0 +1 @@ +sha256:a59c682d8704ef96c5dec2f59481ac24993fb3c25a4a139e22e2db664a34b06a \ No newline at end of file diff --git a/testresources/data/docker/registry/v2/repositories/redis/_layers/sha256/dc2e3041aaa57579fe87bf26fbb56fcf7aef49b3f5a0e0ee37eab519855dd37e/link b/testresources/data/docker/registry/v2/repositories/redis/_layers/sha256/dc2e3041aaa57579fe87bf26fbb56fcf7aef49b3f5a0e0ee37eab519855dd37e/link new file mode 100644 index 0000000000..34fb3fb32a --- /dev/null +++ b/testresources/data/docker/registry/v2/repositories/redis/_layers/sha256/dc2e3041aaa57579fe87bf26fbb56fcf7aef49b3f5a0e0ee37eab519855dd37e/link @@ -0,0 +1 @@ +sha256:dc2e3041aaa57579fe87bf26fbb56fcf7aef49b3f5a0e0ee37eab519855dd37e \ No newline at end of file diff --git a/testresources/data/docker/registry/v2/repositories/redis/_layers/sha256/fb541f77610a7550755893b11853752742e9b173e4e9967f4db6b02c2e51ce4a/link b/testresources/data/docker/registry/v2/repositories/redis/_layers/sha256/fb541f77610a7550755893b11853752742e9b173e4e9967f4db6b02c2e51ce4a/link new file mode 100644 index 0000000000..b2cd2f62e0 --- /dev/null +++ b/testresources/data/docker/registry/v2/repositories/redis/_layers/sha256/fb541f77610a7550755893b11853752742e9b173e4e9967f4db6b02c2e51ce4a/link @@ -0,0 +1 @@ +sha256:fb541f77610a7550755893b11853752742e9b173e4e9967f4db6b02c2e51ce4a \ No newline at end of file diff --git a/testresources/data/docker/registry/v2/repositories/redis/_manifests/revisions/sha256/662f040a4fc0e397e379a82a7d79663bb26698ae009d674e70b0224c4b155edb/link b/testresources/data/docker/registry/v2/repositories/redis/_manifests/revisions/sha256/662f040a4fc0e397e379a82a7d79663bb26698ae009d674e70b0224c4b155edb/link new file mode 100644 index 0000000000..58e62086c4 --- /dev/null +++ b/testresources/data/docker/registry/v2/repositories/redis/_manifests/revisions/sha256/662f040a4fc0e397e379a82a7d79663bb26698ae009d674e70b0224c4b155edb/link @@ -0,0 +1 @@ +sha256:662f040a4fc0e397e379a82a7d79663bb26698ae009d674e70b0224c4b155edb \ No newline at end of file diff --git a/testresources/data/docker/registry/v2/repositories/redis/_manifests/tags/5.0-alpine/current/link b/testresources/data/docker/registry/v2/repositories/redis/_manifests/tags/5.0-alpine/current/link new file mode 100644 index 0000000000..58e62086c4 --- /dev/null +++ b/testresources/data/docker/registry/v2/repositories/redis/_manifests/tags/5.0-alpine/current/link @@ -0,0 +1 @@ +sha256:662f040a4fc0e397e379a82a7d79663bb26698ae009d674e70b0224c4b155edb \ No newline at end of file diff --git a/testresources/data/docker/registry/v2/repositories/redis/_manifests/tags/5.0-alpine/index/sha256/662f040a4fc0e397e379a82a7d79663bb26698ae009d674e70b0224c4b155edb/link b/testresources/data/docker/registry/v2/repositories/redis/_manifests/tags/5.0-alpine/index/sha256/662f040a4fc0e397e379a82a7d79663bb26698ae009d674e70b0224c4b155edb/link new file mode 100644 index 0000000000..58e62086c4 --- /dev/null +++ b/testresources/data/docker/registry/v2/repositories/redis/_manifests/tags/5.0-alpine/index/sha256/662f040a4fc0e397e379a82a7d79663bb26698ae009d674e70b0224c4b155edb/link @@ -0,0 +1 @@ +sha256:662f040a4fc0e397e379a82a7d79663bb26698ae009d674e70b0224c4b155edb \ No newline at end of file