Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: add distribution-registry module #2341

Merged
merged 13 commits into from
Mar 22, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
2 changes: 1 addition & 1 deletion .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -94,7 +94,7 @@ jobs:
matrix:
go-version: [1.21.x, 1.x]
platform: [ubuntu-latest]
module: [artemis, cassandra, chroma, clickhouse, cockroachdb, compose, consul, couchbase, elasticsearch, gcloud, inbucket, k3s, k6, kafka, localstack, mariadb, milvus, minio, mockserver, mongodb, mssql, mysql, nats, neo4j, ollama, openfga, openldap, opensearch, postgres, pulsar, qdrant, rabbitmq, redis, redpanda, surrealdb, vault, weaviate]
module: [artemis, cassandra, chroma, clickhouse, cockroachdb, compose, consul, couchbase, elasticsearch, gcloud, inbucket, k3s, k6, kafka, localstack, mariadb, milvus, minio, mockserver, mongodb, mssql, mysql, nats, neo4j, ollama, openfga, openldap, opensearch, postgres, pulsar, qdrant, rabbitmq, redis, redpanda, registry, surrealdb, vault, weaviate]
uses: ./.github/workflows/ci-test-go.yml
with:
go-version: ${{ matrix.go-version }}
Expand Down
4 changes: 4 additions & 0 deletions .vscode/.testcontainers-go.code-workspace
Original file line number Diff line number Diff line change
Expand Up @@ -149,6 +149,10 @@
"name": "module / redpanda",
"path": "../modules/redpanda"
},
{
"name": "module / registry",
"path": "../modules/registry"
},
{
"name": "module / surrealdb",
"path": "../modules/surrealdb"
Expand Down
16 changes: 15 additions & 1 deletion container.go
Original file line number Diff line number Diff line change
Expand Up @@ -99,11 +99,25 @@ type FromDockerfile struct {
}

type ContainerFile struct {
HostFilePath string
HostFilePath string // If Reader is present, HostFilePath is ignored
Reader io.Reader // If Reader is present, HostFilePath is ignored
ContainerFilePath string
FileMode int64
}

// validate validates the ContainerFile
func (c *ContainerFile) validate() error {
if c.HostFilePath == "" && c.Reader == nil {
return errors.New("either HostFilePath or Reader must be specified")
}

if c.ContainerFilePath == "" {
return errors.New("ContainerFilePath must be specified")
}

return nil
}

// ContainerRequest represents the parameters used to get a running container
type ContainerRequest struct {
FromDockerfile
Expand Down
73 changes: 73 additions & 0 deletions container_file_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,73 @@
// This test is testing very internal logic that should not be exported away from this package. We'll
// leave it in the main testcontainers package. Do not use for user facing examples.
package testcontainers

import (
"errors"
"os"
"path/filepath"
"testing"
)

func TestContainerFileValidation(t *testing.T) {
type ContainerFileValidationTestCase struct {
Name string
ExpectedError error
File ContainerFile
}

f, err := os.Open(filepath.Join(".", "testdata", "hello.sh"))
if err != nil {
t.Fatal(err)
}

testTable := []ContainerFileValidationTestCase{
{
Name: "valid container file: has hostfilepath",
File: ContainerFile{
HostFilePath: "/path/to/host",
ContainerFilePath: "/path/to/container",
},
},
{
Name: "valid container file: has reader",
File: ContainerFile{
Reader: f,
ContainerFilePath: "/path/to/container",
},
},
{
Name: "invalid container file",
ExpectedError: errors.New("either HostFilePath or Reader must be specified"),
File: ContainerFile{
HostFilePath: "",
Reader: nil,
ContainerFilePath: "/path/to/container",
},
},
{
Name: "invalid container file",
ExpectedError: errors.New("ContainerFilePath must be specified"),
File: ContainerFile{
HostFilePath: "/path/to/host",
ContainerFilePath: "",
},
},
}

for _, testCase := range testTable {
t.Run(testCase.Name, func(t *testing.T) {
err := testCase.File.validate()
switch {
case err == nil && testCase.ExpectedError == nil:
return
case err == nil && testCase.ExpectedError != nil:
t.Errorf("did not receive expected error: %s", testCase.ExpectedError.Error())
case err != nil && testCase.ExpectedError == nil:
t.Errorf("received unexpected error: %s", err.Error())
case err.Error() != testCase.ExpectedError.Error():
t.Errorf("errors mismatch: %s != %s", err.Error(), testCase.ExpectedError.Error())
}
})
}
}
9 changes: 8 additions & 1 deletion docker_files_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ package testcontainers_test

import (
"context"
"os"
"path/filepath"
"testing"
"time"
Expand All @@ -22,12 +23,18 @@ func TestCopyFileToContainer(t *testing.T) {
t.Fatal(err)
}

r, err := os.Open(absPath)
if err != nil {
t.Fatal(err)
}

container, err := testcontainers.GenericContainer(ctx, testcontainers.GenericContainerRequest{
ContainerRequest: testcontainers.ContainerRequest{
Image: "docker.io/bash",
Files: []testcontainers.ContainerFile{
{
HostFilePath: absPath,
Reader: r,
HostFilePath: absPath, // will be discarded internally
ContainerFilePath: "/hello.sh",
FileMode: 0o700,
},
Expand Down
10 changes: 10 additions & 0 deletions docs/features/files_and_mounts.md
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,16 @@ If you would like to copy a file to a container, you can do it in two different
[Copying a list of files](../../docker_files_test.go) inside_block:copyFileOnCreate
<!--/codeinclude-->

The `ContainerFile` struct will accept the following fields:

- `HostFilePath`: the path to the file in the host machine. Optional (see below).
- `Reader`: a `io.Reader` that will be used to copy the file to the container. Optional.
- `ContainerFilePath`: the path to the file in the container. Mandatory.
- `Mode`: the file mode, which is optional.

!!!info
If the `Reader` field is set, the `HostFilePath` field will be ignored.

2. Using the `CopyFileToContainer` method on a `running` container:

<!--codeinclude-->
Expand Down
13 changes: 10 additions & 3 deletions docs/modules/chroma.md
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,11 @@ Since testcontainers-go <a href="https://github.com/testcontainers/testcontainer

The Testcontainers module for Chroma.

## Resources

- [Chroma Docs](https://docs.trychroma.com/getting-started) - Chroma official documentation.
- [Chroma Cookbook](http://cookbook.chromadb.dev) - Community-driven Chroma cookbook.

## Adding this module to your project dependencies

Please run the following command to add the Chroma module to your Go dependencies:
Expand Down Expand Up @@ -38,7 +43,7 @@ When starting the Chroma container, you can pass options in a variadic way to co
#### Image

If you need to set a different Chroma Docker image, you can use `testcontainers.WithImage` with a valid Docker image
for Chroma. E.g. `testcontainers.WithImage("chromadb/chroma:0.4.22.dev44")`.
for Chroma. E.g. `testcontainers.WithImage("chromadb/chroma:0.4.24")`.

{% include "../features/common_functional_options.md" %}

Expand All @@ -65,20 +70,22 @@ First of all, you need to import the Chroma module and the Swagger client:
```golang
import (
chromago "github.com/amikos-tech/chroma-go"
chromaopenapi "github.com/amikos-tech/chroma-go/swagger"
"github.com/amikos-tech/chroma-go/types"
)
```

Then, you can create a Chroma client using the Chroma module:

<!--codeinclude-->
[Get the client](../../modules/chroma/examples_test.go) inside_block:createClient
[Get the client](../../modules/chroma/examples_test.go) inside_block:getClient
<!--/codeinclude-->

### Working with Collections

<!--codeinclude-->
[Create Collection](../../modules/chroma/examples_test.go) inside_block:createCollection
[List Collections](../../modules/chroma/examples_test.go) inside_block:listCollections
[Add Data to Collection](../../modules/chroma/examples_test.go) inside_block:addData
[Query Collection](../../modules/chroma/examples_test.go) inside_block:queryCollection
[Delete Collection](../../modules/chroma/examples_test.go) inside_block:deleteCollection
<!--/codeinclude-->
113 changes: 113 additions & 0 deletions docs/modules/registry.md
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-->
2 changes: 1 addition & 1 deletion examples/nginx/go.mod
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@ require (
github.com/containerd/log v0.1.0 // indirect
github.com/cpuguy83/dockercfg v0.3.1 // indirect
github.com/distribution/reference v0.5.0 // indirect
github.com/docker/docker v25.0.3+incompatible // indirect
github.com/docker/docker v25.0.5+incompatible // indirect
github.com/docker/go-connections v0.5.0 // indirect
github.com/docker/go-units v0.5.0 // indirect
github.com/felixge/httpsnoop v1.0.3 // indirect
Expand Down
4 changes: 2 additions & 2 deletions examples/nginx/go.sum
Original file line number Diff line number Diff line change
Expand Up @@ -23,8 +23,8 @@ github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c
github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
github.com/distribution/reference v0.5.0 h1:/FUIFXtfc/x2gpa5/VGfiGLuOIdYa1t65IKK2OFGvA0=
github.com/distribution/reference v0.5.0/go.mod h1:BbU0aIcezP1/5jX/8MP0YiH4SdvB5Y4f/wlDRiLyi3E=
github.com/docker/docker v25.0.3+incompatible h1:D5fy/lYmY7bvZa0XTZ5/UJPljor41F+vdyJG5luQLfQ=
github.com/docker/docker v25.0.3+incompatible/go.mod h1:eEKB0N0r5NX/I1kEveEz05bcu8tLC/8azJZsviup8Sk=
github.com/docker/docker v25.0.5+incompatible h1:UmQydMduGkrD5nQde1mecF/YnSbTOaPeFIeP5C4W+DE=
github.com/docker/docker v25.0.5+incompatible/go.mod h1:eEKB0N0r5NX/I1kEveEz05bcu8tLC/8azJZsviup8Sk=
github.com/docker/go-connections v0.5.0 h1:USnMq7hx7gwdVZq1L49hLXaFtUdTADjXGp+uj1Br63c=
github.com/docker/go-connections v0.5.0/go.mod h1:ov60Kzw0kKElRwhNs9UlUHAE/F9Fe6GLaXnqyDdmEXc=
github.com/docker/go-units v0.5.0 h1:69rxXcBk27SvSaaxTtLh/8llcHD8vYHT7WSdRZ/jvr4=
Expand Down
2 changes: 1 addition & 1 deletion examples/toxiproxy/go.mod
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,7 @@ require (
github.com/cpuguy83/dockercfg v0.3.1 // indirect
github.com/dgryski/go-rendezvous v0.0.0-20200823014737-9f7001d12a5f // indirect
github.com/distribution/reference v0.5.0 // indirect
github.com/docker/docker v25.0.3+incompatible // indirect
github.com/docker/docker v25.0.5+incompatible // indirect
github.com/docker/go-connections v0.5.0 // indirect
github.com/docker/go-units v0.5.0 // indirect
github.com/felixge/httpsnoop v1.0.3 // indirect
Expand Down
4 changes: 2 additions & 2 deletions examples/toxiproxy/go.sum
Original file line number Diff line number Diff line change
Expand Up @@ -29,8 +29,8 @@ github.com/dgryski/go-rendezvous v0.0.0-20200823014737-9f7001d12a5f h1:lO4WD4F/r
github.com/dgryski/go-rendezvous v0.0.0-20200823014737-9f7001d12a5f/go.mod h1:cuUVRXasLTGF7a8hSLbxyZXjz+1KgoB3wDUb6vlszIc=
github.com/distribution/reference v0.5.0 h1:/FUIFXtfc/x2gpa5/VGfiGLuOIdYa1t65IKK2OFGvA0=
github.com/distribution/reference v0.5.0/go.mod h1:BbU0aIcezP1/5jX/8MP0YiH4SdvB5Y4f/wlDRiLyi3E=
github.com/docker/docker v25.0.3+incompatible h1:D5fy/lYmY7bvZa0XTZ5/UJPljor41F+vdyJG5luQLfQ=
github.com/docker/docker v25.0.3+incompatible/go.mod h1:eEKB0N0r5NX/I1kEveEz05bcu8tLC/8azJZsviup8Sk=
github.com/docker/docker v25.0.5+incompatible h1:UmQydMduGkrD5nQde1mecF/YnSbTOaPeFIeP5C4W+DE=
github.com/docker/docker v25.0.5+incompatible/go.mod h1:eEKB0N0r5NX/I1kEveEz05bcu8tLC/8azJZsviup8Sk=
github.com/docker/go-connections v0.5.0 h1:USnMq7hx7gwdVZq1L49hLXaFtUdTADjXGp+uj1Br63c=
github.com/docker/go-connections v0.5.0/go.mod h1:ov60Kzw0kKElRwhNs9UlUHAE/F9Fe6GLaXnqyDdmEXc=
github.com/docker/go-units v0.5.0 h1:69rxXcBk27SvSaaxTtLh/8llcHD8vYHT7WSdRZ/jvr4=
Expand Down