-
-
Notifications
You must be signed in to change notification settings - Fork 515
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Merge branch 'main' into Add-InfluxDB-Module
- Loading branch information
Showing
33 changed files
with
1,276 additions
and
2 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,113 @@ | ||
# Registry | ||
|
||
Not available until the next release of testcontainers-go <a href="https://github.com/testcontainers/testcontainers-go"><span class="tc-version">:material-tag: main</span></a> | ||
|
||
## Introduction | ||
|
||
The Testcontainers module for Registry. | ||
|
||
## Adding this module to your project dependencies | ||
|
||
Please run the following command to add the Registry module to your Go dependencies: | ||
|
||
``` | ||
go get github.com/testcontainers/testcontainers-go/modules/registry | ||
``` | ||
|
||
## Usage example | ||
|
||
<!--codeinclude--> | ||
[Creating a Registry container](../../modules/registry/examples_test.go) inside_block:runRegistryContainer | ||
<!--/codeinclude--> | ||
|
||
## Module reference | ||
|
||
The Registry module exposes one entrypoint function to create the Registry container, and this function receives two parameters: | ||
|
||
```golang | ||
func RunContainer(ctx context.Context, opts ...testcontainers.ContainerCustomizer) (*RegistryContainer, error) | ||
``` | ||
|
||
- `context.Context`, the Go context. | ||
- `testcontainers.ContainerCustomizer`, a variadic argument for passing options. | ||
|
||
### Container Options | ||
|
||
When starting the Registry container, you can pass options in a variadic way to configure it. | ||
|
||
#### Image | ||
|
||
If you need to set a different Registry Docker image, you can use `testcontainers.WithImage` with a valid Docker image | ||
for Registry. E.g. `testcontainers.WithImage("registry:2.8.3")`. | ||
|
||
{% include "../features/common_functional_options.md" %} | ||
|
||
#### With Authentication | ||
|
||
It's possible to enable authentication for the Registry container. By default, it is disabled, but you can enable it in two ways: | ||
- You can use `WithHtpasswd` to enable authentication with a string representing the contents of a `htpasswd` file. | ||
A temporary file will be created with the contents of the string and copied to the container. | ||
- You can use `WithHtpasswdFile` to copy a `htpasswd` file from your local filesystem to the container. | ||
In both cases, the `htpasswd` file will be copied into the `/auth` directory inside the container. | ||
<!--codeinclude--> | ||
[Htpasswd string](../../modules/registry/registry_test.go) inside_block:htpasswdString | ||
[Htpasswd file](../../modules/registry/examples_test.go) inside_block:htpasswdFile | ||
<!--/codeinclude--> | ||
#### WithData | ||
In the case you want to initialise the Registry with your own images, you can use `WithData` to copy a directory from your local filesystem to the container. | ||
The directory will be copied into the `/data` directory inside the container. | ||
The format of the directory should be the same as the one used by the Registry to store images. | ||
Otherwise, the Registry will start but you won't be able to read any images from it. | ||
|
||
<!--codeinclude--> | ||
[Including data](../../modules/registry/examples_test.go) inside_block:htpasswdFile | ||
<!--/codeinclude--> | ||
|
||
### Container Methods | ||
|
||
The Registry container exposes the following methods: | ||
|
||
#### Address | ||
|
||
This method returns the HTTP address string to connect to the Distribution Registry, so that you can use to connect to the Registry. | ||
E.g. `http://localhost:32878/v2/_catalog`. | ||
|
||
<!--codeinclude--> | ||
[HTTP Address](../../modules/registry/registry_test.go) inside_block:httpAddress | ||
<!--/codeinclude--> | ||
|
||
#### ImageExists | ||
|
||
The `ImageExists` method allows to check if an image exists in the Registry. It receives the Go context and the image reference as parameters. | ||
|
||
!!! info | ||
The image reference should be in the format `my-registry:port/image:tag` in order to be pushed to the Registry. | ||
|
||
#### PushImage | ||
|
||
The `PushImage` method allows to push an image to the Registry. It receives the Go context and the image reference as parameters. | ||
|
||
!!! info | ||
The image reference should be in the format `my-registry:port/image:tag` in order to be pushed to the Registry. | ||
|
||
<!--codeinclude--> | ||
[Pushing images to the registry](../../modules/registry/examples_test.go) inside_block:pushingImage | ||
<!--/codeinclude--> | ||
|
||
If the push operation is successful, the method will internally wait for the image to be available in the Registry, querying the Registry API, returning an error in case of any failure (e.g. pushing or waiting for the image). | ||
|
||
#### DeleteImage | ||
|
||
The `DeleteImage` method allows to delete an image from the Registry. It receives the Go context and the image reference as parameters. | ||
|
||
!!! info | ||
The image reference should be in the format `image:tag` in order to be deleted from the Registry. | ||
|
||
<!--codeinclude--> | ||
[Deleting images from the registry](../../modules/registry/examples_test.go) inside_block:deletingImage | ||
<!--/codeinclude--> |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,5 @@ | ||
include ../../commons-test.mk | ||
|
||
.PHONY: test | ||
test: | ||
$(MAKE) test-registry |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,246 @@ | ||
package registry_test | ||
|
||
import ( | ||
"context" | ||
"fmt" | ||
"log" | ||
"os" | ||
"path/filepath" | ||
|
||
"github.com/testcontainers/testcontainers-go" | ||
"github.com/testcontainers/testcontainers-go/modules/registry" | ||
"github.com/testcontainers/testcontainers-go/wait" | ||
) | ||
|
||
func ExampleRunContainer() { | ||
// runRegistryContainer { | ||
registryContainer, err := registry.RunContainer(context.Background(), testcontainers.WithImage("registry:2.8.3")) | ||
if err != nil { | ||
log.Fatalf("failed to start container: %s", err) | ||
} | ||
|
||
// Clean up the container | ||
defer func() { | ||
if err := registryContainer.Terminate(context.Background()); err != nil { | ||
log.Fatalf("failed to terminate container: %s", err) // nolint:gocritic | ||
} | ||
}() | ||
// } | ||
|
||
state, err := registryContainer.State(context.Background()) | ||
if err != nil { | ||
log.Fatalf("failed to get container state: %s", err) // nolint:gocritic | ||
} | ||
|
||
fmt.Println(state.Running) | ||
|
||
// Output: | ||
// true | ||
} | ||
|
||
func ExampleRunContainer_withAuthentication() { | ||
// htpasswdFile { | ||
registryContainer, err := registry.RunContainer( | ||
context.Background(), | ||
testcontainers.WithImage("registry:2.8.3"), | ||
registry.WithHtpasswdFile(filepath.Join("testdata", "auth", "htpasswd")), | ||
registry.WithData(filepath.Join("testdata", "data")), | ||
) | ||
// } | ||
if err != nil { | ||
log.Fatalf("failed to start container: %s", err) | ||
} | ||
defer func() { | ||
if err := registryContainer.Terminate(context.Background()); err != nil { | ||
log.Fatalf("failed to terminate container: %s", err) // nolint:gocritic | ||
} | ||
}() | ||
|
||
registryPort, err := registryContainer.MappedPort(context.Background(), "5000/tcp") | ||
if err != nil { | ||
log.Fatalf("failed to get mapped port: %s", err) // nolint:gocritic | ||
} | ||
strPort := registryPort.Port() | ||
|
||
previousAuthConfig := os.Getenv("DOCKER_AUTH_CONFIG") | ||
|
||
// make sure the Docker Auth credentials are set | ||
// using the same as in the Docker Registry | ||
// testuser:testpassword | ||
os.Setenv("DOCKER_AUTH_CONFIG", `{ | ||
"auths": { | ||
"localhost:`+strPort+`": { "username": "testuser", "password": "testpassword", "auth": "dGVzdHVzZXI6dGVzdHBhc3N3b3Jk" } | ||
}, | ||
"credsStore": "desktop" | ||
}`) | ||
defer func() { | ||
// reset the original state after the example. | ||
os.Unsetenv("DOCKER_AUTH_CONFIG") | ||
os.Setenv("DOCKER_AUTH_CONFIG", previousAuthConfig) | ||
}() | ||
|
||
// build a custom redis image from the private registry, | ||
// using RegistryName of the container as the registry. | ||
|
||
redisC, err := testcontainers.GenericContainer(context.Background(), testcontainers.GenericContainerRequest{ | ||
ContainerRequest: testcontainers.ContainerRequest{ | ||
FromDockerfile: testcontainers.FromDockerfile{ | ||
Context: filepath.Join("testdata", "redis"), | ||
BuildArgs: map[string]*string{ | ||
"REGISTRY_PORT": &strPort, | ||
}, | ||
PrintBuildLog: true, | ||
}, | ||
AlwaysPullImage: true, // make sure the authentication takes place | ||
ExposedPorts: []string{"6379/tcp"}, | ||
WaitingFor: wait.ForLog("Ready to accept connections"), | ||
}, | ||
Started: true, | ||
}) | ||
if err != nil { | ||
log.Fatalf("failed to start container: %s", err) // nolint:gocritic | ||
} | ||
defer func() { | ||
if err := redisC.Terminate(context.Background()); err != nil { | ||
log.Fatalf("failed to terminate container: %s", err) // nolint:gocritic | ||
} | ||
}() | ||
|
||
state, err := redisC.State(context.Background()) | ||
if err != nil { | ||
log.Fatalf("failed to get redis container state: %s", err) // nolint:gocritic | ||
} | ||
|
||
fmt.Println(state.Running) | ||
|
||
// Output: | ||
// true | ||
} | ||
|
||
func ExampleRunContainer_pushImage() { | ||
registryContainer, err := registry.RunContainer( | ||
context.Background(), | ||
testcontainers.WithImage("registry:2.8.3"), | ||
registry.WithHtpasswdFile(filepath.Join("testdata", "auth", "htpasswd")), | ||
registry.WithData(filepath.Join("testdata", "data")), | ||
) | ||
if err != nil { | ||
log.Fatalf("failed to start container: %s", err) | ||
} | ||
defer func() { | ||
if err := registryContainer.Terminate(context.Background()); err != nil { | ||
log.Fatalf("failed to terminate container: %s", err) // nolint:gocritic | ||
} | ||
}() | ||
|
||
registryPort, err := registryContainer.MappedPort(context.Background(), "5000/tcp") | ||
if err != nil { | ||
log.Fatalf("failed to get mapped port: %s", err) // nolint:gocritic | ||
} | ||
strPort := registryPort.Port() | ||
|
||
previousAuthConfig := os.Getenv("DOCKER_AUTH_CONFIG") | ||
|
||
// make sure the Docker Auth credentials are set | ||
// using the same as in the Docker Registry | ||
// testuser:testpassword | ||
// Besides, we are also setting the authentication | ||
// for both the registry and localhost to make sure | ||
// the image is pushed to the private registry. | ||
os.Setenv("DOCKER_AUTH_CONFIG", `{ | ||
"auths": { | ||
"localhost:`+strPort+`": { "username": "testuser", "password": "testpassword", "auth": "dGVzdHVzZXI6dGVzdHBhc3N3b3Jk" }, | ||
"`+registryContainer.RegistryName+`": { "username": "testuser", "password": "testpassword", "auth": "dGVzdHVzZXI6dGVzdHBhc3N3b3Jk" } | ||
}, | ||
"credsStore": "desktop" | ||
}`) | ||
defer func() { | ||
// reset the original state after the example. | ||
os.Unsetenv("DOCKER_AUTH_CONFIG") | ||
os.Setenv("DOCKER_AUTH_CONFIG", previousAuthConfig) | ||
}() | ||
|
||
// build a custom redis image from the private registry, | ||
// using RegistryName of the container as the registry. | ||
// We are agoing to build the image with a fixed tag | ||
// that matches the private registry, and we are going to | ||
// push it again to the registry after the build. | ||
|
||
repo := registryContainer.RegistryName + "/customredis" | ||
tag := "v1.2.3" | ||
|
||
redisC, err := testcontainers.GenericContainer(context.Background(), testcontainers.GenericContainerRequest{ | ||
ContainerRequest: testcontainers.ContainerRequest{ | ||
FromDockerfile: testcontainers.FromDockerfile{ | ||
Context: filepath.Join("testdata", "redis"), | ||
BuildArgs: map[string]*string{ | ||
"REGISTRY_PORT": &strPort, | ||
}, | ||
Repo: repo, | ||
Tag: tag, | ||
PrintBuildLog: true, | ||
}, | ||
AlwaysPullImage: true, // make sure the authentication takes place | ||
ExposedPorts: []string{"6379/tcp"}, | ||
WaitingFor: wait.ForLog("Ready to accept connections"), | ||
}, | ||
Started: true, | ||
}) | ||
if err != nil { | ||
log.Fatalf("failed to start container: %s", err) // nolint:gocritic | ||
} | ||
defer func() { | ||
if err := redisC.Terminate(context.Background()); err != nil { | ||
log.Fatalf("failed to terminate container: %s", err) // nolint:gocritic | ||
} | ||
}() | ||
|
||
// pushingImage { | ||
// repo is localhost:32878/customredis | ||
// tag is v1.2.3 | ||
err = registryContainer.PushImage(context.Background(), fmt.Sprintf("%s:%s", repo, tag)) | ||
if err != nil { | ||
log.Fatalf("failed to push image: %s", err) // nolint:gocritic | ||
} | ||
// } | ||
|
||
newImage := fmt.Sprintf("%s:%s", repo, tag) | ||
|
||
// now run a container from the new image | ||
// But first remove the local image to avoid using the local one. | ||
|
||
// deletingImage { | ||
// newImage is customredis:v1.2.3 | ||
err = registryContainer.DeleteImage(context.Background(), newImage) | ||
if err != nil { | ||
log.Fatalf("failed to delete image: %s", err) // nolint:gocritic | ||
} | ||
// } | ||
|
||
newRedisC, err := testcontainers.GenericContainer(context.Background(), testcontainers.GenericContainerRequest{ | ||
ContainerRequest: testcontainers.ContainerRequest{ | ||
Image: newImage, | ||
ExposedPorts: []string{"6379/tcp"}, | ||
WaitingFor: wait.ForLog("Ready to accept connections"), | ||
}, | ||
Started: true, | ||
}) | ||
if err != nil { | ||
log.Fatalf("failed to start container from %s: %s", newImage, err) // nolint:gocritic | ||
} | ||
defer func() { | ||
if err := newRedisC.Terminate(context.Background()); err != nil { | ||
log.Fatalf("failed to terminate container: %s", err) // nolint:gocritic | ||
} | ||
}() | ||
|
||
state, err := newRedisC.State(context.Background()) | ||
if err != nil { | ||
log.Fatalf("failed to get redis container state from %s: %s", newImage, err) // nolint:gocritic | ||
} | ||
|
||
fmt.Println(state.Running) | ||
|
||
// Output: | ||
// true | ||
} |
Oops, something went wrong.