Skip to content

Commit

Permalink
Merge branch 'main' into fix/lifecycle-datarace
Browse files Browse the repository at this point in the history
  • Loading branch information
mdelapenya authored Mar 25, 2024
2 parents bf930ea + 3541728 commit 462c542
Show file tree
Hide file tree
Showing 139 changed files with 2,008 additions and 382 deletions.
2 changes: 1 addition & 1 deletion .github/workflows/ci-test-go.yml
Original file line number Diff line number Diff line change
Expand Up @@ -55,7 +55,7 @@ jobs:
if: ${{ inputs.rootless-docker }}
uses: ScribeMD/rootless-docker@6bd157a512c2fafa4e0243a8aa87d964eb890886 # v0.2.2

- name: Remove Docket root socket
- name: Remove Docker root socket
if: ${{ inputs.rootless-docker }}
run: sudo rm -rf /var/run/docker.sock

Expand Down
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())
}
})
}
}
32 changes: 14 additions & 18 deletions docker.go
Original file line number Diff line number Diff line change
Expand Up @@ -1027,20 +1027,6 @@ func (p *DockerProvider) CreateContainer(ctx context.Context, req ContainerReque
pullOpt := types.ImagePullOptions{
Platform: req.ImagePlatform, // may be empty
}

registry, imageAuth, err := DockerImageAuth(ctx, imageName)
if err != nil {
p.Logger.Printf("Failed to get image auth for %s. Setting empty credentials for the image: %s. Error is:%s", registry, imageName, err)
} else {
// see https://github.com/docker/docs/blob/e8e1204f914767128814dca0ea008644709c117f/engine/api/sdk/examples.md?plain=1#L649-L657
encodedJSON, err := json.Marshal(imageAuth)
if err != nil {
p.Logger.Printf("Failed to marshal image auth. Setting empty credentials for the image: %s. Error is:%s", imageName, err)
} else {
pullOpt.RegistryAuth = base64.URLEncoding.EncodeToString(encodedJSON)
}
}

if err := p.attemptToPullImage(ctx, imageName, pullOpt); err != nil {
return nil, err
}
Expand Down Expand Up @@ -1245,10 +1231,20 @@ func (p *DockerProvider) ReuseOrCreateContainer(ctx context.Context, req Contain
// attemptToPullImage tries to pull the image while respecting the ctx cancellations.
// Besides, if the image cannot be pulled due to ErrorNotFound then no need to retry but terminate immediately.
func (p *DockerProvider) attemptToPullImage(ctx context.Context, tag string, pullOpt types.ImagePullOptions) error {
var (
err error
pull io.ReadCloser
)
registry, imageAuth, err := DockerImageAuth(ctx, tag)
if err != nil {
p.Logger.Printf("Failed to get image auth for %s. Setting empty credentials for the image: %s. Error is:%s", registry, tag, err)
} else {
// see https://github.com/docker/docs/blob/e8e1204f914767128814dca0ea008644709c117f/engine/api/sdk/examples.md?plain=1#L649-L657
encodedJSON, err := json.Marshal(imageAuth)
if err != nil {
p.Logger.Printf("Failed to marshal image auth. Setting empty credentials for the image: %s. Error is:%s", tag, err)
} else {
pullOpt.RegistryAuth = base64.URLEncoding.EncodeToString(encodedJSON)
}
}

var pull io.ReadCloser
err = backoff.Retry(func() error {
pull, err = p.client.ImagePull(ctx, tag, pullOpt)
if err != nil {
Expand Down
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
8 changes: 8 additions & 0 deletions docs/features/wait/http.md
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,8 @@ The HTTP wait strategy will check the result of an HTTP(S) request against the c
- the HTTP request body to be sent.
- the HTTP status code matcher as a function.
- the HTTP response matcher as a function.
- the HTTP headers to be used.
- the HTTP response headers matcher as a function.
- the TLS config to be used for HTTPS.
- the startup timeout to be used in seconds, default is 60 seconds.
- the poll interval to be used in milliseconds, default is 100 milliseconds.
Expand Down Expand Up @@ -41,3 +43,9 @@ Variations on the HTTP wait strategy are supported, including:
<!--codeinclude-->
[Waiting for an HTTP endpoint matching an HTTP status code](../../../wait/http_test.go) inside_block:waitForHTTPStatusCode
<!--/codeinclude-->

## Match for HTTP response headers

<!--codeinclude-->
[Waiting for an HTTP endpoint matching an HTTP response header](../../../wait/http_test.go) inside_block:waitForHTTPHeaders
<!--/codeinclude-->
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-->
Loading

0 comments on commit 462c542

Please sign in to comment.