Skip to content

Commit

Permalink
chore!: return error from Customize
Browse files Browse the repository at this point in the history
Change ContainerCustomizer.Customize method to return an error so that
options can handle errors gracefully instead of relying on panic or just
a log entry, neither of which are user friendly.

Enable errcheck linter to ensure that errors that aren't handled are
reported.

Run go mod tidy on k3s and weaviate to allow tests to be run using go
1.22.

Run gofumpt on a few files to satisfy golangci-lint.

Fix direct comparison with http.ErrServerClosed flagged by errcheck.

Fixes #2266

BREAKING CHANGE: `ContainerCustomizer.Customize` now returns an error.
  • Loading branch information
stevenh committed Feb 25, 2024
1 parent 8085e2f commit 0a87c6f
Show file tree
Hide file tree
Showing 63 changed files with 523 additions and 191 deletions.
1 change: 1 addition & 0 deletions .golangci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ linters:
- misspell
- nonamedreturns
- testifylint
- errcheck

linters-settings:
errorlint:
Expand Down
1 change: 0 additions & 1 deletion docker.go
Original file line number Diff line number Diff line change
Expand Up @@ -1495,7 +1495,6 @@ func (p *DockerProvider) getDefaultNetwork(ctx context.Context, cli client.APICl
Attachable: true,
Labels: core.DefaultLabels(core.SessionID()),
})

if err != nil {
return "", err
}
Expand Down
17 changes: 17 additions & 0 deletions docs/features/common_functional_options.md
Original file line number Diff line number Diff line change
Expand Up @@ -129,3 +129,20 @@ The above example is updating the predefined command of the image, **appending**

!!!info
This can't be used to replace the command, only to append options.

!!!info
The interface definition for `ContainerCustomizer` was changed to allow
errors the be correctly processed, specifically `Customize` method was
changed from:

```go
Customize(req *GenericContainerRequest)
```

To:

```go
Customize(req *GenericContainerRequest) error
```

- Not available until the next release of testcontainers-go <a href="https:/testcontainers/testcontainers-go"><span class="tc-version">:material-tag: main</span></a>
16 changes: 12 additions & 4 deletions modules/artemis/artemis.go
Original file line number Diff line number Diff line change
Expand Up @@ -49,16 +49,20 @@ func (c *Container) ConsoleURL(ctx context.Context) (string, error) {

// WithCredentials sets the administrator credentials. The default is artemis:artemis.
func WithCredentials(user, password string) testcontainers.CustomizeRequestOption {
return func(req *testcontainers.GenericContainerRequest) {
return func(req *testcontainers.GenericContainerRequest) error {
req.Env["ARTEMIS_USER"] = user
req.Env["ARTEMIS_PASSWORD"] = password

return nil
}
}

// WithAnonymousLogin enables anonymous logins.
func WithAnonymousLogin() testcontainers.CustomizeRequestOption {
return func(req *testcontainers.GenericContainerRequest) {
return func(req *testcontainers.GenericContainerRequest) error {
req.Env["ANONYMOUS_LOGIN"] = "true"

return nil
}
}

Expand All @@ -67,8 +71,10 @@ func WithAnonymousLogin() testcontainers.CustomizeRequestOption {
// Setting this value will override the default.
// See the documentation on `artemis create` for available options.
func WithExtraArgs(args string) testcontainers.CustomizeRequestOption {
return func(req *testcontainers.GenericContainerRequest) {
return func(req *testcontainers.GenericContainerRequest) error {
req.Env["EXTRA_ARGS"] = args

return nil
}
}

Expand All @@ -91,7 +97,9 @@ func RunContainer(ctx context.Context, opts ...testcontainers.ContainerCustomize
}

for _, opt := range opts {
opt.Customize(&req)
if err := opt.Customize(&req); err != nil {
return nil, err
}
}

container, err := testcontainers.GenericContainer(ctx, req)
Expand Down
12 changes: 8 additions & 4 deletions modules/cassandra/cassandra.go
Original file line number Diff line number Diff line change
Expand Up @@ -41,19 +41,21 @@ func (c *CassandraContainer) ConnectionHost(ctx context.Context) (string, error)
// It will also set the "configFile" parameter to the path of the config file
// as a command line argument to the container.
func WithConfigFile(configFile string) testcontainers.CustomizeRequestOption {
return func(req *testcontainers.GenericContainerRequest) {
return func(req *testcontainers.GenericContainerRequest) error {
cf := testcontainers.ContainerFile{
HostFilePath: configFile,
ContainerFilePath: "/etc/cassandra/cassandra.yaml",
FileMode: 0o755,
}
req.Files = append(req.Files, cf)

return nil
}
}

// WithInitScripts sets the init cassandra queries to be run when the container starts
func WithInitScripts(scripts ...string) testcontainers.CustomizeRequestOption {
return func(req *testcontainers.GenericContainerRequest) {
return func(req *testcontainers.GenericContainerRequest) error {
var initScripts []testcontainers.ContainerFile
var execs []testcontainers.Executable
for _, script := range scripts {
Expand All @@ -68,7 +70,7 @@ func WithInitScripts(scripts ...string) testcontainers.CustomizeRequestOption {
}

req.Files = append(req.Files, initScripts...)
testcontainers.WithAfterReadyCommand(execs...)(req)
return testcontainers.WithAfterReadyCommand(execs...)(req)
}
}

Expand Down Expand Up @@ -100,7 +102,9 @@ func RunContainer(ctx context.Context, opts ...testcontainers.ContainerCustomize
}

for _, opt := range opts {
opt.Customize(&genericContainerReq)
if err := opt.Customize(&genericContainerReq); err != nil {
return nil, err
}
}

container, err := testcontainers.GenericContainer(ctx, genericContainerReq)
Expand Down
4 changes: 3 additions & 1 deletion modules/chroma/chroma.go
Original file line number Diff line number Diff line change
Expand Up @@ -33,7 +33,9 @@ func RunContainer(ctx context.Context, opts ...testcontainers.ContainerCustomize
}

for _, opt := range opts {
opt.Customize(&genericContainerReq)
if err := opt.Customize(&genericContainerReq); err != nil {
return nil, err
}
}

container, err := testcontainers.GenericContainer(ctx, genericContainerReq)
Expand Down
38 changes: 27 additions & 11 deletions modules/clickhouse/clickhouse.go
Original file line number Diff line number Diff line change
Expand Up @@ -101,34 +101,36 @@ func renderZookeeperConfig(settings ZookeeperOptions) ([]byte, error) {

// WithZookeeper pass a config to connect clickhouse with zookeeper and make clickhouse as cluster
func WithZookeeper(host, port string) testcontainers.CustomizeRequestOption {
return func(req *testcontainers.GenericContainerRequest) {
return func(req *testcontainers.GenericContainerRequest) error {
f, err := os.CreateTemp("", "clickhouse-tc-config-")
if err != nil {
panic(err)
return fmt.Errorf("temporary file: %w", err)
}

defer f.Close()

// write data to the temporary file
data, err := renderZookeeperConfig(ZookeeperOptions{Host: host, Port: port})
if err != nil {
panic(err)
return fmt.Errorf("zookeeper config: %w", err)
}
if _, err := f.Write(data); err != nil {
panic(err)
return fmt.Errorf("write zookeeper config: %w", err)
}
cf := testcontainers.ContainerFile{
HostFilePath: f.Name(),
ContainerFilePath: "/etc/clickhouse-server/config.d/zookeeper_config.xml",
FileMode: 0o755,
}
req.Files = append(req.Files, cf)

return nil
}
}

// WithInitScripts sets the init scripts to be run when the container starts
func WithInitScripts(scripts ...string) testcontainers.CustomizeRequestOption {
return func(req *testcontainers.GenericContainerRequest) {
return func(req *testcontainers.GenericContainerRequest) error {
initScripts := []testcontainers.ContainerFile{}
for _, script := range scripts {
cf := testcontainers.ContainerFile{
Expand All @@ -139,52 +141,62 @@ func WithInitScripts(scripts ...string) testcontainers.CustomizeRequestOption {
initScripts = append(initScripts, cf)
}
req.Files = append(req.Files, initScripts...)

return nil
}
}

// WithConfigFile sets the XML config file to be used for the clickhouse container
// It will also set the "configFile" parameter to the path of the config file
// as a command line argument to the container.
func WithConfigFile(configFile string) testcontainers.CustomizeRequestOption {
return func(req *testcontainers.GenericContainerRequest) {
return func(req *testcontainers.GenericContainerRequest) error {
cf := testcontainers.ContainerFile{
HostFilePath: configFile,
ContainerFilePath: "/etc/clickhouse-server/config.d/config.xml",
FileMode: 0o755,
}
req.Files = append(req.Files, cf)

return nil
}
}

// WithConfigFile sets the YAML config file to be used for the clickhouse container
// It will also set the "configFile" parameter to the path of the config file
// as a command line argument to the container.
func WithYamlConfigFile(configFile string) testcontainers.CustomizeRequestOption {
return func(req *testcontainers.GenericContainerRequest) {
return func(req *testcontainers.GenericContainerRequest) error {
cf := testcontainers.ContainerFile{
HostFilePath: configFile,
ContainerFilePath: "/etc/clickhouse-server/config.d/config.yaml",
FileMode: 0o755,
}
req.Files = append(req.Files, cf)

return nil
}
}

// WithDatabase sets the initial database to be created when the container starts
// It can be used to define a different name for the default database that is created when the image is first started.
// If it is not specified, then the default value("clickhouse") will be used.
func WithDatabase(dbName string) testcontainers.CustomizeRequestOption {
return func(req *testcontainers.GenericContainerRequest) {
return func(req *testcontainers.GenericContainerRequest) error {
req.Env["CLICKHOUSE_DB"] = dbName

return nil
}
}

// WithPassword sets the initial password of the user to be created when the container starts
// It is required for you to use the ClickHouse image. It must not be empty or undefined.
// This environment variable sets the password for ClickHouse.
func WithPassword(password string) testcontainers.CustomizeRequestOption {
return func(req *testcontainers.GenericContainerRequest) {
return func(req *testcontainers.GenericContainerRequest) error {
req.Env["CLICKHOUSE_PASSWORD"] = password

return nil
}
}

Expand All @@ -193,12 +205,14 @@ func WithPassword(password string) testcontainers.CustomizeRequestOption {
// It will create the specified user with superuser power.
// If it is not specified, then the default user of clickhouse will be used.
func WithUsername(user string) testcontainers.CustomizeRequestOption {
return func(req *testcontainers.GenericContainerRequest) {
return func(req *testcontainers.GenericContainerRequest) error {
if user == "" {
user = defaultUser
}

req.Env["CLICKHOUSE_USER"] = user

return nil
}
}

Expand All @@ -225,7 +239,9 @@ func RunContainer(ctx context.Context, opts ...testcontainers.ContainerCustomize
}

for _, opt := range opts {
opt.Customize(&genericContainerReq)
if err := opt.Customize(&genericContainerReq); err != nil {
return nil, err
}
}

container, err := testcontainers.GenericContainer(ctx, genericContainerReq)
Expand Down
4 changes: 3 additions & 1 deletion modules/cockroachdb/cockroachdb.go
Original file line number Diff line number Diff line change
Expand Up @@ -96,7 +96,9 @@ func RunContainer(ctx context.Context, opts ...testcontainers.ContainerCustomize
if apply, ok := opt.(Option); ok {
apply(&o)
}
opt.Customize(&req)
if err := opt.Customize(&req); err != nil {
return nil, err
}
}

// modify request
Expand Down
3 changes: 2 additions & 1 deletion modules/cockroachdb/options.go
Original file line number Diff line number Diff line change
Expand Up @@ -26,8 +26,9 @@ var _ testcontainers.ContainerCustomizer = (*Option)(nil)
type Option func(*options)

// Customize is a NOOP. It's defined to satisfy the testcontainers.ContainerCustomizer interface.
func (o Option) Customize(*testcontainers.GenericContainerRequest) {
func (o Option) Customize(*testcontainers.GenericContainerRequest) error {
// NOOP to satisfy interface.
return nil
}

// WithDatabase sets the name of the database to use.
Expand Down
1 change: 0 additions & 1 deletion modules/compose/compose_api.go
Original file line number Diff line number Diff line change
Expand Up @@ -231,7 +231,6 @@ func (d *dockerCompose) Up(ctx context.Context, opts ...StackUpOption) error {
Wait: upOptions.Wait,
},
})

if err != nil {
return err
}
Expand Down
12 changes: 9 additions & 3 deletions modules/consul/consul.go
Original file line number Diff line number Diff line change
Expand Up @@ -40,20 +40,24 @@ func (c *ConsulContainer) ApiEndpoint(ctx context.Context) (string, error) {

// WithConfigString takes in a JSON string of keys and values to define a configuration to be used by the instance.
func WithConfigString(config string) testcontainers.CustomizeRequestOption {
return func(req *testcontainers.GenericContainerRequest) {
return func(req *testcontainers.GenericContainerRequest) error {
req.Env["CONSUL_LOCAL_CONFIG"] = config

return nil
}
}

// WithConfigFile takes in a path to a JSON file to define a configuration to be used by the instance.
func WithConfigFile(configPath string) testcontainers.CustomizeRequestOption {
return func(req *testcontainers.GenericContainerRequest) {
return func(req *testcontainers.GenericContainerRequest) error {
cf := testcontainers.ContainerFile{
HostFilePath: configPath,
ContainerFilePath: "/consul/config/node.json",
FileMode: 0o755,
}
req.Files = append(req.Files, cf)

return nil
}
}

Expand All @@ -76,7 +80,9 @@ func RunContainer(ctx context.Context, opts ...testcontainers.ContainerCustomize
}

for _, opt := range opts {
opt.Customize(&containerReq)
if err := opt.Customize(&containerReq); err != nil {
return nil, err
}
}

container, err := testcontainers.GenericContainer(ctx, containerReq)
Expand Down
8 changes: 6 additions & 2 deletions modules/couchbase/couchbase.go
Original file line number Diff line number Diff line change
Expand Up @@ -85,7 +85,9 @@ func RunContainer(ctx context.Context, opts ...testcontainers.ContainerCustomize
}

for _, opt := range opts {
opt.Customize(&genericContainerReq)
if err := opt.Customize(&genericContainerReq); err != nil {
return nil, err
}

// transfer options to the config

Expand Down Expand Up @@ -663,10 +665,12 @@ type serviceCustomizer struct {
enabledService Service
}

func (c serviceCustomizer) Customize(req *testcontainers.GenericContainerRequest) {
func (c serviceCustomizer) Customize(req *testcontainers.GenericContainerRequest) error {
for _, port := range c.enabledService.ports {
req.ExposedPorts = append(req.ExposedPorts, port+"/tcp")
}

return nil
}

// withService creates a serviceCustomizer for the given service.
Expand Down
9 changes: 6 additions & 3 deletions modules/couchbase/options.go
Original file line number Diff line number Diff line change
Expand Up @@ -40,8 +40,9 @@ type credentialsCustomizer struct {
password string
}

func (c credentialsCustomizer) Customize(req *testcontainers.GenericContainerRequest) {
func (c credentialsCustomizer) Customize(req *testcontainers.GenericContainerRequest) error {
// NOOP, we want to simply transfer the credentials to the container
return nil
}

// WithAdminCredentials sets the username and password for the administrator user.
Expand Down Expand Up @@ -73,8 +74,9 @@ type bucketCustomizer struct {
buckets []bucket
}

func (c bucketCustomizer) Customize(req *testcontainers.GenericContainerRequest) {
func (c bucketCustomizer) Customize(req *testcontainers.GenericContainerRequest) error {
// NOOP, we want to simply transfer the buckets to the container
return nil
}

// WithBucket adds buckets to the couchbase container
Expand All @@ -96,8 +98,9 @@ type indexStorageCustomizer struct {
mode indexStorageMode
}

func (c indexStorageCustomizer) Customize(req *testcontainers.GenericContainerRequest) {
func (c indexStorageCustomizer) Customize(req *testcontainers.GenericContainerRequest) error {
// NOOP, we want to simply transfer the index storage mode to the container
return nil
}

// WithBucket adds buckets to the couchbase container
Expand Down
Loading

0 comments on commit 0a87c6f

Please sign in to comment.