diff --git a/container_test.go b/container_test.go index 423f6fcbb6..b9089aab97 100644 --- a/container_test.go +++ b/container_test.go @@ -7,6 +7,7 @@ import ( "errors" "fmt" "io" + "log" "strings" "testing" "time" @@ -525,13 +526,13 @@ func ExampleGenericContainer_withSubstitutors() { }) // } if err != nil { - panic(err) + log.Fatalf("could not start container: %v", err) } defer func() { err := container.Terminate(ctx) if err != nil { - panic(err) + log.Fatalf("could not terminate container: %v", err) } }() diff --git a/docker_mounts.go b/docker_mounts.go index 28e3096de1..2efdb0c7e2 100644 --- a/docker_mounts.go +++ b/docker_mounts.go @@ -3,6 +3,7 @@ package testcontainers import "github.com/docker/docker/api/types/mount" var mountTypeMapping = map[MountType]mount.Type{ + MountTypeBind: mount.TypeBind, // Deprecated, it will be removed in a future release MountTypeVolume: mount.TypeVolume, MountTypeTmpfs: mount.TypeTmpfs, MountTypePipe: mount.TypeNamedPipe, @@ -100,6 +101,9 @@ func mapToDockerMounts(containerMounts ContainerMounts) []mount.Mount { Source: m.Source.Source(), ReadOnly: m.ReadOnly, Target: m.Target.Target(), + VolumeOptions: &mount.VolumeOptions{ + Labels: GenericLabels(), + }, } switch typedMounter := m.Source.(type) { diff --git a/docker_test.go b/docker_test.go index 32c363aac1..45e276b1ed 100644 --- a/docker_test.go +++ b/docker_test.go @@ -987,7 +987,7 @@ func ExampleDockerProvider_CreateContainer() { state, err := nginxC.State(ctx) if err != nil { - panic(err) + log.Fatalf("failed to get container state: %s", err) // nolint:gocritic } fmt.Println(state.Running) @@ -1019,7 +1019,7 @@ func ExampleContainer_Host() { state, err := nginxC.State(ctx) if err != nil { - panic(err) + log.Fatalf("failed to get container state: %s", err) // nolint:gocritic } fmt.Println(state.Running) @@ -1047,7 +1047,7 @@ func ExampleContainer_Start() { state, err := nginxC.State(ctx) if err != nil { - panic(err) + log.Fatalf("failed to get container state: %s", err) // nolint:gocritic } fmt.Println(state.Running) @@ -1075,7 +1075,7 @@ func ExampleContainer_Stop() { timeout := 10 * time.Second err := nginxC.Stop(ctx, &timeout) if err != nil { - panic(err) + log.Fatalf("failed to stop container: %s", err) // nolint:gocritic } fmt.Println("Container has been stopped") @@ -1109,7 +1109,7 @@ func ExampleContainer_MappedPort() { state, err := nginxC.State(ctx) if err != nil { - panic(err) + log.Fatalf("failed to get container state: %s", err) // nolint:gocritic } fmt.Println(state.Running) diff --git a/docs/features/docker_compose.md b/docs/features/docker_compose.md index 4014eec9ea..ea2a56bc19 100644 --- a/docs/features/docker_compose.md +++ b/docs/features/docker_compose.md @@ -32,22 +32,22 @@ import ( "context" "testing" - "github.com/stretchr/testify/assert" + "github.com/stretchr/testify/require" tc "github.com/testcontainers/testcontainers-go/modules/compose" ) func TestSomething(t *testing.T) { compose, err := tc.NewDockerCompose("testdata/docker-compose.yml") - assert.NoError(t, err, "NewDockerComposeAPI()") + require.NoError(t, err, "NewDockerComposeAPI()") t.Cleanup(func() { - assert.NoError(t, compose.Down(context.Background(), tc.RemoveOrphans(true), tc.RemoveImagesLocal), "compose.Down()") + require.NoError(t, compose.Down(context.Background(), tc.RemoveOrphans(true), tc.RemoveImagesLocal), "compose.Down()") }) ctx, cancel := context.WithCancel(context.Background()) t.Cleanup(cancel) - assert.NoError(t, compose.Up(ctx, tc.Wait(true)), "compose.Up()") + require.NoError(t, compose.Up(ctx, tc.Wait(true)), "compose.Up()") // do some testing here } @@ -62,23 +62,23 @@ import ( "context" "testing" - "github.com/stretchr/testify/assert" + "github.com/stretchr/testify/require" tc "github.com/testcontainers/testcontainers-go/modules/compose" ) func TestSomethingElse(t *testing.T) { identifier := tc.StackIdentifier("some_ident") compose, err := tc.NewDockerComposeWith(tc.WithStackFiles("./testdata/docker-compose-simple.yml"), identifier) - assert.NoError(t, err, "NewDockerComposeAPIWith()") + require.NoError(t, err, "NewDockerComposeAPIWith()") t.Cleanup(func() { - assert.NoError(t, compose.Down(context.Background(), tc.RemoveOrphans(true), tc.RemoveImagesLocal), "compose.Down()") + require.NoError(t, compose.Down(context.Background(), tc.RemoveOrphans(true), tc.RemoveImagesLocal), "compose.Down()") }) ctx, cancel := context.WithCancel(context.Background()) t.Cleanup(cancel) - assert.NoError(t, compose.Up(ctx, tc.Wait(true)), "compose.Up()") + require.NoError(t, compose.Up(ctx, tc.Wait(true)), "compose.Up()") // do some testing here } @@ -110,7 +110,7 @@ import ( "testing" "time" - "github.com/stretchr/testify/assert" + "github.com/stretchr/testify/require" tc "github.com/testcontainers/testcontainers-go/modules/compose" "github.com/testcontainers/testcontainers-go/wait" ) @@ -118,10 +118,10 @@ import ( func TestSomethingWithWaiting(t *testing.T) { identifier := tc.StackIdentifier("some_ident") compose, err := tc.NewDockerComposeWith(tc.WithStackFiles("./testdata/docker-compose-simple.yml"), identifier) - assert.NoError(t, err, "NewDockerComposeAPIWith()") + require.NoError(t, err, "NewDockerComposeAPIWith()") t.Cleanup(func() { - assert.NoError(t, compose.Down(context.Background(), tc.RemoveOrphans(true), tc.RemoveImagesLocal), "compose.Down()") + require.NoError(t, compose.Down(context.Background(), tc.RemoveOrphans(true), tc.RemoveImagesLocal), "compose.Down()") }) ctx, cancel := context.WithCancel(context.Background()) @@ -131,7 +131,7 @@ func TestSomethingWithWaiting(t *testing.T) { WaitForService("nginx", wait.NewHTTPStrategy("/").WithPort("80/tcp").WithStartupTimeout(10*time.Second)). Up(ctx, tc.Wait(true)) - assert.NoError(t, err, "compose.Up()") + require.NoError(t, err, "compose.Up()") // do some testing here } diff --git a/docs/modules/clickhouse.md b/docs/modules/clickhouse.md index 8bf668e688..749ded3bec 100644 --- a/docs/modules/clickhouse.md +++ b/docs/modules/clickhouse.md @@ -73,6 +73,17 @@ initialization before starting the service. [Init script content](../../modules/clickhouse/testdata/init-db.sh) <!--/codeinclude--> +#### Zookeeper + +Clusterized ClickHouse requires to start Zookeeper and pass link to it via `config.xml`. + +<!--codeinclude--> +[Include zookeeper](../../modules/clickhouse/clickhouse_test.go) inside_block:withZookeeper +<!--/codeinclude--> + +!!!warning + The `WithZookeeper` option will `panic` if it's not possible to create the Zookeeper config file. + #### Custom configuration If you need to set a custom configuration, the module provides the `WithConfigFile` option to pass the path to a custom configuration file in XML format. diff --git a/docs/quickstart.md b/docs/quickstart.md index 1f9b1ac315..822b7edd84 100644 --- a/docs/quickstart.md +++ b/docs/quickstart.md @@ -37,11 +37,11 @@ func TestWithRedis(t *testing.T) { Started: true, }) if err != nil { - panic(err) + log.Fatalf("Could not start redis: %s", err) } defer func() { if err := redisC.Terminate(ctx); err != nil { - panic(err) + log.Fatalf("Could not stop redis: %s", err) } }() } diff --git a/from_dockerfile_test.go b/from_dockerfile_test.go index 0e3a4ff10d..7576499a64 100644 --- a/from_dockerfile_test.go +++ b/from_dockerfile_test.go @@ -4,6 +4,7 @@ import ( "context" "fmt" "io" + "log" "strings" "testing" "time" @@ -175,17 +176,17 @@ func ExampleGenericContainer_buildFromDockerfile() { }) // } if err != nil { - panic(err) + log.Fatalf("failed to start container: %v", err) } r, err := c.Logs(ctx) if err != nil { - panic(err) + log.Fatalf("failed to get logs: %v", err) } logs, err := io.ReadAll(r) if err != nil { - panic(err) + log.Fatalf("failed to read logs: %v", err) } fmt.Println(string(logs)) diff --git a/modulegen/_template/examples_test.go.tmpl b/modulegen/_template/examples_test.go.tmpl index ac426e8144..8e47e5cd0a 100644 --- a/modulegen/_template/examples_test.go.tmpl +++ b/modulegen/_template/examples_test.go.tmpl @@ -14,20 +14,20 @@ func Example{{ $entrypoint }}() { {{ $lower }}Container, err := {{ $lower }}.{{ $entrypoint }}(ctx, testcontainers.WithImage("{{ $image }}")) if err != nil { - panic(err) + log.Fatalf("failed to start container: %s", err) } // Clean up the container defer func() { if err := {{ $lower }}Container.Terminate(ctx); err != nil { - panic(err) + log.Fatalf("failed to terminate container: %s", err) } }() // } state, err := {{ $lower }}Container.State(ctx) if err != nil { - panic(err) + log.Fatalf("failed to get container state: %s", err) } fmt.Println(state.Running) diff --git a/modules/artemis/examples_test.go b/modules/artemis/examples_test.go index fb4727d9b4..2b17776bb1 100644 --- a/modules/artemis/examples_test.go +++ b/modules/artemis/examples_test.go @@ -3,6 +3,7 @@ package artemis_test import ( "context" "fmt" + "log" "github.com/go-stomp/stomp/v3" @@ -19,18 +20,18 @@ func ExampleRunContainer() { artemis.WithCredentials("test", "test"), ) if err != nil { - panic(err) + log.Fatalf("failed to start container: %s", err) } defer func() { if err := artemisContainer.Terminate(ctx); err != nil { - panic(err) + log.Fatalf("failed to terminate container: %s", err) } }() // } state, err := artemisContainer.State(ctx) if err != nil { - panic(err) + log.Fatalf("failed to get container state: %s", err) // nolint:gocritic } fmt.Println(state.Running) @@ -39,7 +40,7 @@ func ExampleRunContainer() { // Get broker endpoint. host, err := artemisContainer.BrokerEndpoint(ctx) if err != nil { - panic(err) + log.Fatalf("failed to get broker endpoint: %s", err) } // containerUser { @@ -52,11 +53,11 @@ func ExampleRunContainer() { // Connect to Artemis via STOMP. conn, err := stomp.Dial("tcp", host, stomp.ConnOpt.Login(user, pass)) if err != nil { - panic(err) + log.Fatalf("failed to connect to Artemis: %s", err) } defer func() { if err := conn.Disconnect(); err != nil { - panic(err) + log.Fatalf("failed to disconnect from Artemis: %s", err) } }() // } diff --git a/modules/cassandra/examples_test.go b/modules/cassandra/examples_test.go index b08658c93a..97d1e6a9ec 100644 --- a/modules/cassandra/examples_test.go +++ b/modules/cassandra/examples_test.go @@ -3,6 +3,7 @@ package cassandra_test import ( "context" "fmt" + "log" "path/filepath" "github.com/gocql/gocql" @@ -21,40 +22,40 @@ func ExampleRunContainer() { cassandra.WithConfigFile(filepath.Join("testdata", "config.yaml")), ) if err != nil { - panic(err) + log.Fatalf("failed to start container: %s", err) } // Clean up the container defer func() { if err := cassandraContainer.Terminate(ctx); err != nil { - panic(err) + log.Fatalf("failed to terminate container: %s", err) } }() // } state, err := cassandraContainer.State(ctx) if err != nil { - panic(err) + log.Fatalf("failed to get container state: %s", err) // nolint:gocritic } fmt.Println(state.Running) connectionHost, err := cassandraContainer.ConnectionHost(ctx) if err != nil { - panic(err) + log.Fatalf("failed to get connection host: %s", err) } cluster := gocql.NewCluster(connectionHost) session, err := cluster.CreateSession() if err != nil { - panic(err) + log.Fatalf("failed to create session: %s", err) } defer session.Close() var version string err = session.Query("SELECT release_version FROM system.local").Scan(&version) if err != nil { - panic(err) + log.Fatalf("failed to query: %s", err) } fmt.Println(version) diff --git a/modules/clickhouse/clickhouse.go b/modules/clickhouse/clickhouse.go index 92c7616235..86ebac1704 100644 --- a/modules/clickhouse/clickhouse.go +++ b/modules/clickhouse/clickhouse.go @@ -1,10 +1,14 @@ package clickhouse import ( + "bytes" "context" + _ "embed" "fmt" + "os" "path/filepath" "strings" + "text/template" "github.com/docker/go-connections/nat" @@ -12,6 +16,9 @@ import ( "github.com/testcontainers/testcontainers-go/wait" ) +//go:embed mounts/zk_config.xml.tpl +var zookeeperConfigTpl string + const ( defaultUser = "default" defaultDatabaseName = "clickhouse" @@ -72,6 +79,53 @@ func (c *ClickHouseContainer) ConnectionString(ctx context.Context, args ...stri return connectionString, nil } +// ZookeeperOptions arguments for zookeeper in clickhouse +type ZookeeperOptions struct { + Host, Port string +} + +// renderZookeeperConfig generate default zookeeper configuration for clickhouse +func renderZookeeperConfig(settings ZookeeperOptions) ([]byte, error) { + tpl, err := template.New("bootstrap.yaml").Parse(zookeeperConfigTpl) + if err != nil { + return nil, fmt.Errorf("failed to parse zookeeper config file template: %w", err) + } + + var bootstrapConfig bytes.Buffer + if err := tpl.Execute(&bootstrapConfig, settings); err != nil { + return nil, fmt.Errorf("failed to render zookeeper bootstrap config template: %w", err) + } + + return bootstrapConfig.Bytes(), nil +} + +// 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) { + f, err := os.CreateTemp("", "clickhouse-tc-config-") + if err != nil { + panic(err) + } + + defer f.Close() + + // write data to the temporary file + data, err := renderZookeeperConfig(ZookeeperOptions{Host: host, Port: port}) + if err != nil { + panic(err) + } + if _, err := f.Write(data); err != nil { + panic(err) + } + cf := testcontainers.ContainerFile{ + HostFilePath: f.Name(), + ContainerFilePath: "/etc/clickhouse-server/config.d/zookeeper_config.xml", + FileMode: 0o755, + } + req.Files = append(req.Files, cf) + } +} + // WithInitScripts sets the init scripts to be run when the container starts func WithInitScripts(scripts ...string) testcontainers.CustomizeRequestOption { return func(req *testcontainers.GenericContainerRequest) { diff --git a/modules/clickhouse/clickhouse_test.go b/modules/clickhouse/clickhouse_test.go index c503218abf..075bbf4460 100644 --- a/modules/clickhouse/clickhouse_test.go +++ b/modules/clickhouse/clickhouse_test.go @@ -8,10 +8,12 @@ import ( ch "github.com/ClickHouse/clickhouse-go/v2" "github.com/ClickHouse/clickhouse-go/v2/lib/driver" "github.com/cenkalti/backoff/v4" + "github.com/docker/go-connections/nat" "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" "github.com/testcontainers/testcontainers-go" + "github.com/testcontainers/testcontainers-go/wait" ) const ( @@ -219,6 +221,105 @@ func TestClickHouseWithConfigFile(t *testing.T) { } } +func TestClickHouseWithZookeeper(t *testing.T) { + ctx := context.Background() + + // withZookeeper { + zkPort := nat.Port("2181/tcp") + + zkcontainer, err := testcontainers.GenericContainer(ctx, testcontainers.GenericContainerRequest{ + ContainerRequest: testcontainers.ContainerRequest{ + ExposedPorts: []string{zkPort.Port()}, + Image: "zookeeper:3.7", + WaitingFor: wait.ForListeningPort(zkPort), + }, + Started: true, + }) + if err != nil { + t.Fatal(err) + } + + ipaddr, err := zkcontainer.ContainerIP(ctx) + if err != nil { + t.Fatal(err) + } + + container, err := RunContainer(ctx, + WithUsername(user), + WithPassword(password), + WithDatabase(dbname), + WithZookeeper(ipaddr, zkPort.Port()), + ) + if err != nil { + t.Fatal(err) + } + // } + + // Clean up the container after the test is complete + t.Cleanup(func() { + require.NoError(t, container.Terminate(ctx)) + require.NoError(t, zkcontainer.Terminate(ctx)) + }) + + connectionHost, err := container.ConnectionHost(ctx) + require.NoError(t, err) + + conn, err := ch.Open(&ch.Options{ + Addr: []string{connectionHost}, + Auth: ch.Auth{ + Database: dbname, + Username: user, + Password: password, // --> password is not required + }, + }) + require.NoError(t, err) + assert.NotNil(t, conn) + defer conn.Close() + + // perform assertions + data, err := performReplicatedCRUD(conn) + require.NoError(t, err) + assert.Len(t, data, 1) +} + +func performReplicatedCRUD(conn driver.Conn) ([]Test, error) { + var ( + err error + res []Test + ) + + err = backoff.Retry(func() error { + err = conn.Exec(context.Background(), "CREATE TABLE replicated_test_table (id UInt64) ENGINE = ReplicatedMergeTree('/clickhouse/tables/{shard}/mdb.data_transfer_cp_cdc', '{replica}') PRIMARY KEY (id) ORDER BY (id) SETTINGS index_granularity = 8192;") + if err != nil { + return err + } + + err = conn.Exec(context.Background(), "INSERT INTO replicated_test_table (id) VALUES (1);") + if err != nil { + return err + } + + rows, err := conn.Query(context.Background(), "SELECT * FROM replicated_test_table;") + if err != nil { + return err + } + + for rows.Next() { + var r Test + + err := rows.Scan(&r.Id) + if err != nil { + return err + } + + res = append(res, r) + } + return nil + }, backoff.NewExponentialBackOff()) + + return res, err +} + func performCRUD(conn driver.Conn) ([]Test, error) { var ( err error diff --git a/modules/clickhouse/examples_test.go b/modules/clickhouse/examples_test.go index c331e9c99a..8eccfc7aa7 100644 --- a/modules/clickhouse/examples_test.go +++ b/modules/clickhouse/examples_test.go @@ -3,6 +3,7 @@ package clickhouse_test import ( "context" "fmt" + "log" "path/filepath" "strings" @@ -29,30 +30,30 @@ func ExampleRunContainer() { clickhouse.WithConfigFile(filepath.Join("testdata", "config.xml")), ) if err != nil { - panic(err) + log.Fatalf("failed to start container: %s", err) } defer func() { if err := clickHouseContainer.Terminate(ctx); err != nil { - panic(err) + log.Fatalf("failed to terminate container: %s", err) } }() // } state, err := clickHouseContainer.State(ctx) if err != nil { - panic(err) + log.Fatalf("failed to get container state: %s", err) // nolint:gocritic } fmt.Println(state.Running) connectionString, err := clickHouseContainer.ConnectionString(ctx) if err != nil { - panic(err) + log.Fatalf("failed to get connection string: %s", err) } opts, err := ch.ParseDSN(connectionString) if err != nil { - panic(err) + log.Fatalf("failed to parse DSN: %s", err) } fmt.Println(strings.HasPrefix(opts.ClientInfo.String(), "clickhouse-go/")) diff --git a/modules/clickhouse/mounts/zk_config.xml.tpl b/modules/clickhouse/mounts/zk_config.xml.tpl new file mode 100644 index 0000000000..7b85493322 --- /dev/null +++ b/modules/clickhouse/mounts/zk_config.xml.tpl @@ -0,0 +1,31 @@ +<?xml version="1.0"?> +<clickhouse> + <zookeeper> + <node index="1"> + <host>{{.Host}}</host> + <port>{{.Port}}</port> + </node> + </zookeeper> + + <remote_servers> + <default> + <shard> + <replica> + <host>localhost</host> + <port>9000</port> + </replica> + </shard> + </default> + </remote_servers> + <macros> + <cluster>default</cluster> + <shard>shard</shard> + <replica>replica</replica> + </macros> + + <distributed_ddl> + <path>/clickhouse/task_queue/ddl</path> + </distributed_ddl> + + <format_schema_path>/var/lib/clickhouse/format_schemas/</format_schema_path> +</clickhouse> diff --git a/modules/cockroachdb/examples_test.go b/modules/cockroachdb/examples_test.go index c39a6afb09..97e1d7b2dd 100644 --- a/modules/cockroachdb/examples_test.go +++ b/modules/cockroachdb/examples_test.go @@ -3,6 +3,7 @@ package cockroachdb_test import ( "context" "fmt" + "log" "net/url" "github.com/testcontainers/testcontainers-go/modules/cockroachdb" @@ -14,30 +15,30 @@ func ExampleRunContainer() { cockroachdbContainer, err := cockroachdb.RunContainer(ctx) if err != nil { - panic(err) + log.Fatalf("failed to start container: %s", err) } // Clean up the container defer func() { if err := cockroachdbContainer.Terminate(ctx); err != nil { - panic(err) + log.Fatalf("failed to terminate container: %s", err) } }() // } state, err := cockroachdbContainer.State(ctx) if err != nil { - panic(err) + log.Fatalf("failed to get container state: %s", err) // nolint:gocritic } fmt.Println(state.Running) addr, err := cockroachdbContainer.ConnectionString(ctx) if err != nil { - panic(err) + log.Fatalf("failed to get connection string: %s", err) } u, err := url.Parse(addr) if err != nil { - panic(err) + log.Fatalf("failed to parse connection string: %s", err) } u.Host = fmt.Sprintf("%s:%s", u.Hostname(), "xxx") fmt.Println(u.String()) diff --git a/modules/couchbase/examples_test.go b/modules/couchbase/examples_test.go index 10b0f84291..e89363d3ac 100644 --- a/modules/couchbase/examples_test.go +++ b/modules/couchbase/examples_test.go @@ -3,6 +3,7 @@ package couchbase_test import ( "context" "fmt" + "log" "github.com/couchbase/gocb/v2" @@ -28,25 +29,25 @@ func ExampleRunContainer() { couchbase.WithBuckets(bucket), ) if err != nil { - panic(err) + log.Fatalf("failed to start container: %s", err) } defer func() { if err := couchbaseContainer.Terminate(ctx); err != nil { - panic(err) + log.Fatalf("failed to terminate container: %s", err) } }() // } state, err := couchbaseContainer.State(ctx) if err != nil { - panic(err) + log.Fatalf("failed to get container state: %s", err) // nolint:gocritic } fmt.Println(state.Running) connectionString, err := couchbaseContainer.ConnectionString(ctx) if err != nil { - panic(err) + log.Fatalf("failed to get connection string: %s", err) } cluster, err := gocb.Connect(connectionString, gocb.ClusterOptions{ @@ -54,12 +55,12 @@ func ExampleRunContainer() { Password: couchbaseContainer.Password(), }) if err != nil { - panic(err) + log.Fatalf("failed to connect to cluster: %s", err) } buckets, err := cluster.Buckets().GetAllBuckets(nil) if err != nil { - panic(err) + log.Fatalf("failed to get buckets: %s", err) } fmt.Println(len(buckets)) diff --git a/modules/elasticsearch/examples_test.go b/modules/elasticsearch/examples_test.go index bb288658b8..09ba893e43 100644 --- a/modules/elasticsearch/examples_test.go +++ b/modules/elasticsearch/examples_test.go @@ -4,6 +4,7 @@ import ( "context" "encoding/json" "fmt" + "log" "strings" es "github.com/elastic/go-elasticsearch/v8" @@ -17,18 +18,18 @@ func ExampleRunContainer() { ctx := context.Background() elasticsearchContainer, err := elasticsearch.RunContainer(ctx, testcontainers.WithImage("docker.elastic.co/elasticsearch/elasticsearch:8.9.0")) if err != nil { - panic(err) + log.Fatalf("failed to start container: %s", err) } defer func() { if err := elasticsearchContainer.Terminate(ctx); err != nil { - panic(err) + log.Fatalf("failed to terminate container: %s", err) } }() // } state, err := elasticsearchContainer.State(ctx) if err != nil { - panic(err) + log.Fatalf("failed to get container state: %s", err) // nolint:gocritic } fmt.Println(state.Running) @@ -46,12 +47,12 @@ func ExampleRunContainer_withUsingPassword() { elasticsearch.WithPassword("foo"), ) if err != nil { - panic(err) + log.Fatalf("failed to start container: %s", err) } defer func() { err := elasticsearchContainer.Terminate(ctx) if err != nil { - panic(err) + log.Fatalf("failed to terminate container: %s", err) } }() // } @@ -73,12 +74,12 @@ func ExampleRunContainer_connectUsingElasticsearchClient() { elasticsearch.WithPassword("foo"), ) if err != nil { - panic(err) + log.Fatalf("failed to start container: %s", err) } defer func() { err := elasticsearchContainer.Terminate(ctx) if err != nil { - panic(err) + log.Fatalf("failed to terminate container: %s", err) } }() @@ -93,19 +94,19 @@ func ExampleRunContainer_connectUsingElasticsearchClient() { esClient, err := es.NewClient(cfg) if err != nil { - panic(err) + log.Fatalf("error creating the client: %s", err) // nolint:gocritic } resp, err := esClient.Info() if err != nil { - panic(err) + log.Fatalf("error getting response: %s", err) } defer resp.Body.Close() // } var esResp ElasticsearchResponse if err := json.NewDecoder(resp.Body).Decode(&esResp); err != nil { - panic(err) + log.Fatalf("error decoding response: %s", err) } fmt.Println(esResp.Tagline) diff --git a/modules/gcloud/bigquery_test.go b/modules/gcloud/bigquery_test.go index 2332e747f8..08bbd46a3a 100644 --- a/modules/gcloud/bigquery_test.go +++ b/modules/gcloud/bigquery_test.go @@ -4,6 +4,7 @@ import ( "context" "errors" "fmt" + "log" "cloud.google.com/go/bigquery" "google.golang.org/api/iterator" @@ -26,13 +27,13 @@ func ExampleRunBigQueryContainer() { gcloud.WithProjectID("bigquery-project"), ) if err != nil { - panic(err) + log.Fatalf("failed to run container: %v", err) } // Clean up the container defer func() { if err := bigQueryContainer.Terminate(ctx); err != nil { - panic(err) + log.Fatalf("failed to terminate container: %v", err) } }() // } @@ -49,7 +50,7 @@ func ExampleRunBigQueryContainer() { client, err := bigquery.NewClient(ctx, projectID, opts...) if err != nil { - panic(err) + log.Fatalf("failed to create bigquery client: %v", err) // nolint:gocritic } defer client.Close() // } @@ -57,13 +58,13 @@ func ExampleRunBigQueryContainer() { createFnQuery := client.Query("CREATE FUNCTION testr(arr ARRAY<STRUCT<name STRING, val INT64>>) AS ((SELECT SUM(IF(elem.name = \"foo\",elem.val,null)) FROM UNNEST(arr) AS elem))") _, err = createFnQuery.Read(ctx) if err != nil { - panic(err) + log.Fatalf("failed to create function: %v", err) } selectQuery := client.Query("SELECT testr([STRUCT<name STRING, val INT64>(\"foo\", 10), STRUCT<name STRING, val INT64>(\"bar\", 40), STRUCT<name STRING, val INT64>(\"foo\", 20)])") it, err := selectQuery.Read(ctx) if err != nil { - panic(err) + log.Fatalf("failed to read query: %v", err) } var val []bigquery.Value @@ -73,7 +74,7 @@ func ExampleRunBigQueryContainer() { break } if err != nil { - panic(err) + log.Fatalf("failed to iterate: %v", err) } } diff --git a/modules/gcloud/bigtable_test.go b/modules/gcloud/bigtable_test.go index 9e2f7516ce..0504d68fc0 100644 --- a/modules/gcloud/bigtable_test.go +++ b/modules/gcloud/bigtable_test.go @@ -3,6 +3,7 @@ package gcloud_test import ( "context" "fmt" + "log" "cloud.google.com/go/bigtable" "google.golang.org/api/option" @@ -23,13 +24,13 @@ func ExampleRunBigTableContainer() { gcloud.WithProjectID("bigtable-project"), ) if err != nil { - panic(err) + log.Fatalf("failed to run container: %v", err) } // Clean up the container defer func() { if err := bigTableContainer.Terminate(ctx); err != nil { - panic(err) + log.Fatalf("failed to terminate container: %v", err) } }() // } @@ -49,24 +50,24 @@ func ExampleRunBigTableContainer() { } adminClient, err := bigtable.NewAdminClient(ctx, projectId, instanceId, options...) if err != nil { - panic(err) + log.Fatalf("failed to create admin client: %v", err) // nolint:gocritic } defer adminClient.Close() // } err = adminClient.CreateTable(ctx, tableName) if err != nil { - panic(err) + log.Fatalf("failed to create table: %v", err) } err = adminClient.CreateColumnFamily(ctx, tableName, "name") if err != nil { - panic(err) + log.Fatalf("failed to create column family: %v", err) } // bigTableClient { client, err := bigtable.NewClient(ctx, projectId, instanceId, options...) if err != nil { - panic(err) + log.Fatalf("failed to create client: %v", err) } defer client.Close() // } @@ -77,12 +78,12 @@ func ExampleRunBigTableContainer() { mut.Set("name", "firstName", bigtable.Now(), []byte("Gopher")) err = tbl.Apply(ctx, "1", mut) if err != nil { - panic(err) + log.Fatalf("failed to apply mutation: %v", err) } row, err := tbl.ReadRow(ctx, "1", bigtable.RowFilter(bigtable.FamilyFilter("name"))) if err != nil { - panic(err) + log.Fatalf("failed to read row: %v", err) } fmt.Println(string(row["name"][0].Value)) diff --git a/modules/gcloud/datastore_test.go b/modules/gcloud/datastore_test.go index df81e9f2c5..e9db5116d3 100644 --- a/modules/gcloud/datastore_test.go +++ b/modules/gcloud/datastore_test.go @@ -3,6 +3,7 @@ package gcloud_test import ( "context" "fmt" + "log" "cloud.google.com/go/datastore" "google.golang.org/api/option" @@ -23,13 +24,13 @@ func ExampleRunDatastoreContainer() { gcloud.WithProjectID("datastore-project"), ) if err != nil { - panic(err) + log.Fatalf("failed to run container: %v", err) } // Clean up the container defer func() { if err := datastoreContainer.Terminate(ctx); err != nil { - panic(err) + log.Fatalf("failed to terminate container: %v", err) } }() // } @@ -45,7 +46,7 @@ func ExampleRunDatastoreContainer() { dsClient, err := datastore.NewClient(ctx, projectID, options...) if err != nil { - panic(err) + log.Fatalf("failed to create client: %v", err) // nolint:gocritic } defer dsClient.Close() // } @@ -60,13 +61,13 @@ func ExampleRunDatastoreContainer() { } _, err = dsClient.Put(ctx, k, &data) if err != nil { - panic(err) + log.Fatalf("failed to put data: %v", err) } saved := Task{} err = dsClient.Get(ctx, k, &saved) if err != nil { - panic(err) + log.Fatalf("failed to get data: %v", err) } fmt.Println(saved.Description) diff --git a/modules/gcloud/firestore_test.go b/modules/gcloud/firestore_test.go index 163c718ae7..3e1c6f7977 100644 --- a/modules/gcloud/firestore_test.go +++ b/modules/gcloud/firestore_test.go @@ -3,6 +3,7 @@ package gcloud_test import ( "context" "fmt" + "log" "cloud.google.com/go/firestore" "google.golang.org/api/option" @@ -33,13 +34,13 @@ func ExampleRunFirestoreContainer() { gcloud.WithProjectID("firestore-project"), ) if err != nil { - panic(err) + log.Fatalf("failed to run container: %v", err) } // Clean up the container defer func() { if err := firestoreContainer.Terminate(ctx); err != nil { - panic(err) + log.Fatalf("failed to terminate container: %v", err) } }() // } @@ -49,13 +50,13 @@ func ExampleRunFirestoreContainer() { conn, err := grpc.Dial(firestoreContainer.URI, grpc.WithTransportCredentials(insecure.NewCredentials()), grpc.WithPerRPCCredentials(emulatorCreds{})) if err != nil { - panic(err) + log.Fatalf("failed to dial: %v", err) // nolint:gocritic } options := []option.ClientOption{option.WithGRPCConn(conn)} client, err := firestore.NewClient(ctx, projectID, options...) if err != nil { - panic(err) + log.Fatalf("failed to create client: %v", err) } defer client.Close() // } @@ -74,17 +75,17 @@ func ExampleRunFirestoreContainer() { } _, err = docRef.Create(ctx, data) if err != nil { - panic(err) + log.Fatalf("failed to create document: %v", err) } docsnap, err := docRef.Get(ctx) if err != nil { - panic(err) + log.Fatalf("failed to get document: %v", err) } var saved Person if err := docsnap.DataTo(&saved); err != nil { - panic(err) + log.Fatalf("failed to convert data: %v", err) } fmt.Println(saved.Firstname, saved.Lastname) diff --git a/modules/gcloud/pubsub_test.go b/modules/gcloud/pubsub_test.go index 132f2f9388..5d46424f42 100644 --- a/modules/gcloud/pubsub_test.go +++ b/modules/gcloud/pubsub_test.go @@ -3,6 +3,7 @@ package gcloud_test import ( "context" "fmt" + "log" "cloud.google.com/go/pubsub" "google.golang.org/api/option" @@ -23,13 +24,13 @@ func ExampleRunPubsubContainer() { gcloud.WithProjectID("pubsub-project"), ) if err != nil { - panic(err) + log.Fatalf("failed to run container: %v", err) } // Clean up the container defer func() { if err := pubsubContainer.Terminate(ctx); err != nil { - panic(err) + log.Fatalf("failed to terminate container: %v", err) } }() // } @@ -39,30 +40,30 @@ func ExampleRunPubsubContainer() { conn, err := grpc.Dial(pubsubContainer.URI, grpc.WithTransportCredentials(insecure.NewCredentials())) if err != nil { - panic(err) + log.Fatalf("failed to dial: %v", err) // nolint:gocritic } options := []option.ClientOption{option.WithGRPCConn(conn)} client, err := pubsub.NewClient(ctx, projectID, options...) if err != nil { - panic(err) + log.Fatalf("failed to create client: %v", err) } defer client.Close() // } topic, err := client.CreateTopic(ctx, "greetings") if err != nil { - panic(err) + log.Fatalf("failed to create topic: %v", err) } subscription, err := client.CreateSubscription(ctx, "subscription", pubsub.SubscriptionConfig{Topic: topic}) if err != nil { - panic(err) + log.Fatalf("failed to create subscription: %v", err) } result := topic.Publish(ctx, &pubsub.Message{Data: []byte("Hello World")}) _, err = result.Get(ctx) if err != nil { - panic(err) + log.Fatalf("failed to publish message: %v", err) } var data []byte @@ -73,7 +74,7 @@ func ExampleRunPubsubContainer() { defer cancel() }) if err != nil { - panic(err) + log.Fatalf("failed to receive message: %v", err) } fmt.Println(string(data)) diff --git a/modules/gcloud/spanner_test.go b/modules/gcloud/spanner_test.go index 01fea85bd7..e50777142a 100644 --- a/modules/gcloud/spanner_test.go +++ b/modules/gcloud/spanner_test.go @@ -3,6 +3,7 @@ package gcloud_test import ( "context" "fmt" + "log" "cloud.google.com/go/spanner" database "cloud.google.com/go/spanner/admin/database/apiv1" @@ -28,13 +29,13 @@ func ExampleRunSpannerContainer() { gcloud.WithProjectID("spanner-project"), ) if err != nil { - panic(err) + log.Fatalf("failed to run container: %v", err) } // Clean up the container defer func() { if err := spannerContainer.Terminate(ctx); err != nil { - panic(err) + log.Fatalf("failed to terminate container: %v", err) } }() // } @@ -56,7 +57,7 @@ func ExampleRunSpannerContainer() { instanceAdmin, err := instance.NewInstanceAdminClient(ctx, options...) if err != nil { - panic(err) + log.Fatalf("failed to create instance admin client: %v", err) // nolint:gocritic } defer instanceAdmin.Close() // } @@ -69,18 +70,18 @@ func ExampleRunSpannerContainer() { }, }) if err != nil { - panic(err) + log.Fatalf("failed to create instance: %v", err) } _, err = instanceOp.Wait(ctx) if err != nil { - panic(err) + log.Fatalf("failed to wait for instance creation: %v", err) } // spannerDBAdminClient { c, err := database.NewDatabaseAdminClient(ctx, options...) if err != nil { - panic(err) + log.Fatalf("failed to create admin client: %v", err) } defer c.Close() // } @@ -93,17 +94,17 @@ func ExampleRunSpannerContainer() { }, }) if err != nil { - panic(err) + log.Fatalf("failed to create database: %v", err) } _, err = databaseOp.Wait(ctx) if err != nil { - panic(err) + log.Fatalf("failed to wait for database creation: %v", err) } db := fmt.Sprintf("projects/%s/instances/%s/databases/%s", projectId, instanceId, databaseName) client, err := spanner.NewClient(ctx, db, options...) if err != nil { - panic(err) + log.Fatalf("failed to create client: %v", err) } defer client.Close() @@ -113,18 +114,18 @@ func ExampleRunSpannerContainer() { []interface{}{"Go", "Gopher"}), }) if err != nil { - panic(err) + log.Fatalf("failed to apply mutation: %v", err) } row, err := client.Single().ReadRow(ctx, "Languages", spanner.Key{"Go"}, []string{"mascot"}) if err != nil { - panic(err) + log.Fatalf("failed to read row: %v", err) } var mascot string err = row.ColumnByName("Mascot", &mascot) if err != nil { - panic(err) + log.Fatalf("failed to read column: %v", err) } fmt.Println(mascot) diff --git a/modules/inbucket/examples_test.go b/modules/inbucket/examples_test.go index d657ee69ec..b5c72d12b4 100644 --- a/modules/inbucket/examples_test.go +++ b/modules/inbucket/examples_test.go @@ -3,6 +3,7 @@ package inbucket_test import ( "context" "fmt" + "log" "github.com/testcontainers/testcontainers-go" "github.com/testcontainers/testcontainers-go/modules/inbucket" @@ -14,20 +15,20 @@ func ExampleRunContainer() { inbucketContainer, err := inbucket.RunContainer(ctx, testcontainers.WithImage("inbucket/inbucket:sha-2d409bb")) if err != nil { - panic(err) + log.Fatalf("failed to start container: %s", err) } // Clean up the container defer func() { if err := inbucketContainer.Terminate(ctx); err != nil { - panic(err) + log.Fatalf("failed to terminate container: %s", err) } }() // } state, err := inbucketContainer.State(ctx) if err != nil { - panic(err) + log.Fatalf("failed to get container state: %s", err) // nolint:gocritic } fmt.Println(state.Running) diff --git a/modules/k3s/k3s_example_test.go b/modules/k3s/k3s_example_test.go index c297b1ca67..2d065bc70a 100644 --- a/modules/k3s/k3s_example_test.go +++ b/modules/k3s/k3s_example_test.go @@ -3,6 +3,7 @@ package k3s_test import ( "context" "fmt" + "log" v1 "k8s.io/apimachinery/pkg/apis/meta/v1" "k8s.io/client-go/kubernetes" @@ -20,42 +21,42 @@ func ExampleRunContainer() { testcontainers.WithImage("docker.io/rancher/k3s:v1.27.1-k3s1"), ) if err != nil { - panic(err) + log.Fatalf("failed to start container: %s", err) } // Clean up the container defer func() { if err := k3sContainer.Terminate(ctx); err != nil { - panic(err) + log.Fatalf("failed to terminate container: %s", err) } }() // } state, err := k3sContainer.State(ctx) if err != nil { - panic(err) + log.Fatalf("failed to get container state: %s", err) // nolint:gocritic } fmt.Println(state.Running) kubeConfigYaml, err := k3sContainer.GetKubeConfig(ctx) if err != nil { - panic(err) + log.Fatalf("failed to get kubeconfig: %s", err) } restcfg, err := clientcmd.RESTConfigFromKubeConfig(kubeConfigYaml) if err != nil { - panic(err) + log.Fatalf("failed to create rest config: %s", err) } k8s, err := kubernetes.NewForConfig(restcfg) if err != nil { - panic(err) + log.Fatalf("failed to create k8s client: %s", err) } nodes, err := k8s.CoreV1().Nodes().List(ctx, v1.ListOptions{}) if err != nil { - panic(err) + log.Fatalf("failed to list nodes: %s", err) } fmt.Println(len(nodes.Items)) diff --git a/modules/k6/examples_test.go b/modules/k6/examples_test.go index 69e79fb6af..99cc77dcde 100644 --- a/modules/k6/examples_test.go +++ b/modules/k6/examples_test.go @@ -3,6 +3,7 @@ package k6_test import ( "context" "fmt" + "log" "path/filepath" "github.com/testcontainers/testcontainers-go" @@ -29,12 +30,12 @@ func ExampleRunContainer() { } httpbin, err := testcontainers.GenericContainer(ctx, gcr) if err != nil { - panic(fmt.Errorf("failed to create httpbin container %w", err)) + log.Fatalf("failed to start container: %s", err) } defer func() { if err := httpbin.Terminate(ctx); err != nil { - panic(fmt.Errorf("failed to terminate container: %w", err)) + log.Fatalf("failed to terminate container: %s", err) } }() // } @@ -42,13 +43,13 @@ func ExampleRunContainer() { // getHTTPBinIP { httpbinIP, err := httpbin.ContainerIP(ctx) if err != nil { - panic(fmt.Errorf("failed to get httpbin IP: %w", err)) + log.Fatalf("failed to get container IP: %s", err) // nolint:gocritic } // } absPath, err := filepath.Abs(filepath.Join("scripts", "httpbin.js")) if err != nil { - panic(fmt.Errorf("failed to get path to test script: %w", err)) + log.Fatalf("failed to get absolute path to test script: %s", err) } // runK6Container { @@ -60,12 +61,12 @@ func ExampleRunContainer() { k6.SetEnvVar("HTTPBIN", httpbinIP), ) if err != nil { - panic(fmt.Errorf("failed to start k6 container: %w", err)) + log.Fatalf("failed to start container: %s", err) } defer func() { if err := k6.Terminate(ctx); err != nil { - panic(fmt.Errorf("failed to terminate container: %w", err)) + log.Fatalf("failed to terminate container: %s", err) } }() //} @@ -73,7 +74,7 @@ func ExampleRunContainer() { // assert the result of the test state, err := k6.State(ctx) if err != nil { - panic(err) + log.Fatalf("failed to get container state: %s", err) } fmt.Println(state.ExitCode) diff --git a/modules/kafka/examples_test.go b/modules/kafka/examples_test.go index 61fe81f504..ee68cca9ff 100644 --- a/modules/kafka/examples_test.go +++ b/modules/kafka/examples_test.go @@ -3,6 +3,7 @@ package kafka_test import ( "context" "fmt" + "log" "github.com/testcontainers/testcontainers-go" "github.com/testcontainers/testcontainers-go/modules/kafka" @@ -17,20 +18,20 @@ func ExampleRunContainer() { testcontainers.WithImage("confluentinc/confluent-local:7.5.0"), ) if err != nil { - panic(err) + log.Fatalf("failed to start container: %s", err) } // Clean up the container after defer func() { if err := kafkaContainer.Terminate(ctx); err != nil { - panic(err) + log.Fatalf("failed to terminate container: %s", err) } }() // } state, err := kafkaContainer.State(ctx) if err != nil { - panic(err) + log.Fatalf("failed to get container state: %s", err) // nolint:gocritic } fmt.Println(kafkaContainer.ClusterID) diff --git a/modules/localstack/examples_test.go b/modules/localstack/examples_test.go index ad0c3b28e8..a408a9d3c6 100644 --- a/modules/localstack/examples_test.go +++ b/modules/localstack/examples_test.go @@ -6,6 +6,7 @@ import ( "encoding/json" "fmt" "io" + "log" "net/http" "path/filepath" "strings" @@ -26,20 +27,20 @@ func ExampleRunContainer() { testcontainers.WithImage("localstack/localstack:1.4.0"), ) if err != nil { - panic(err) + log.Fatalf("failed to start container: %s", err) } // Clean up the container defer func() { if err := localstackContainer.Terminate(ctx); err != nil { - panic(err) + log.Fatalf("failed to terminate container: %s", err) } }() // } state, err := localstackContainer.State(ctx) if err != nil { - panic(err) + log.Fatalf("failed to get container state: %s", err) // nolint:gocritic } fmt.Println(state.Running) @@ -54,7 +55,7 @@ func ExampleRunContainer_withNetwork() { newNetwork, err := network.New(ctx, network.WithCheckDuplicate()) if err != nil { - panic(err) + log.Fatalf("failed to create network: %s", err) } nwName := newNetwork.Name @@ -71,20 +72,20 @@ func ExampleRunContainer_withNetwork() { }), ) if err != nil { - panic(err) + log.Fatalf("failed to start container: %s", err) } // } // Clean up the container defer func() { if err := localstackContainer.Terminate(ctx); err != nil { - panic(err) + log.Fatalf("failed to terminate container: %s", err) } }() networks, err := localstackContainer.Networks(ctx) if err != nil { - panic(err) + log.Fatalf("failed to get container networks: %s", err) // nolint:gocritic } fmt.Println(len(networks)) @@ -107,7 +108,7 @@ func ExampleRunContainer_legacyMode() { }), ) if err == nil { - panic(err) + log.Fatalf("expected an error, got nil") } fmt.Println(err) @@ -150,12 +151,12 @@ func ExampleRunContainer_usingLambdas() { }), ) if err != nil { - panic(err) + log.Fatalf("failed to start container: %s", err) } defer func() { err := container.Terminate(ctx) if err != nil { - panic(err) + log.Fatalf("failed to terminate container: %s", err) } }() @@ -179,7 +180,7 @@ func ExampleRunContainer_usingLambdas() { for _, cmd := range lambdaCommands { _, _, err := container.Exec(ctx, cmd) if err != nil { - panic(err) + log.Fatalf("failed to execute command %v: %s", cmd, err) // nolint:gocritic } } @@ -189,13 +190,13 @@ func ExampleRunContainer_usingLambdas() { } _, reader, err := container.Exec(ctx, cmd, exec.Multiplexed()) if err != nil { - panic(err) + log.Fatalf("failed to execute command %v: %s", cmd, err) } buf := new(bytes.Buffer) _, err = buf.ReadFrom(reader) if err != nil { - panic(err) + log.Fatalf("failed to read from reader: %s", err) } content := buf.Bytes() @@ -213,7 +214,7 @@ func ExampleRunContainer_usingLambdas() { v := &FunctionURLConfig{} err = json.Unmarshal(content, v) if err != nil { - panic(err) + log.Fatalf("failed to unmarshal content: %s", err) } httpClient := http.Client{ @@ -225,19 +226,19 @@ func ExampleRunContainer_usingLambdas() { mappedPort, err := container.MappedPort(ctx, "4566/tcp") if err != nil { - panic(err) + log.Fatalf("failed to get mapped port: %s", err) } functionURL = strings.ReplaceAll(functionURL, "4566", mappedPort.Port()) resp, err := httpClient.Post(functionURL, "application/json", bytes.NewBufferString(`{"num1": "10", "num2": "10"}`)) if err != nil { - panic(err) + log.Fatalf("failed to send request to lambda function: %s", err) } jsonResponse, err := io.ReadAll(resp.Body) if err != nil { - panic(err) + log.Fatalf("failed to read response body: %s", err) } fmt.Println(string(jsonResponse)) diff --git a/modules/mariadb/examples_test.go b/modules/mariadb/examples_test.go index d5c057f436..36209a2deb 100644 --- a/modules/mariadb/examples_test.go +++ b/modules/mariadb/examples_test.go @@ -3,6 +3,7 @@ package mariadb_test import ( "context" "fmt" + "log" "path/filepath" "github.com/testcontainers/testcontainers-go" @@ -22,20 +23,20 @@ func ExampleRunContainer() { mariadb.WithPassword(""), ) if err != nil { - panic(err) + log.Fatalf("failed to start container: %s", err) } // Clean up the container defer func() { if err := mariadbContainer.Terminate(ctx); err != nil { - panic(err) + log.Fatalf("failed to terminate container: %s", err) } }() // } state, err := mariadbContainer.State(ctx) if err != nil { - panic(err) + log.Fatalf("failed to get container state: %s", err) // nolint:gocritic } fmt.Println(state.Running) diff --git a/modules/minio/examples_test.go b/modules/minio/examples_test.go index 38c2b9d589..ce72eb515a 100644 --- a/modules/minio/examples_test.go +++ b/modules/minio/examples_test.go @@ -3,6 +3,7 @@ package minio_test import ( "context" "fmt" + "log" "github.com/testcontainers/testcontainers-go" "github.com/testcontainers/testcontainers-go/modules/minio" @@ -14,20 +15,20 @@ func ExampleRunContainer() { minioContainer, err := minio.RunContainer(ctx, testcontainers.WithImage("minio/minio:RELEASE.2024-01-16T16-07-38Z")) if err != nil { - panic(err) + log.Fatalf("failed to start container: %s", err) } // Clean up the container defer func() { if err := minioContainer.Terminate(ctx); err != nil { - panic(err) + log.Fatalf("failed to terminate container: %s", err) } }() // } state, err := minioContainer.State(ctx) if err != nil { - panic(err) + log.Fatalf("failed to get container state: %s", err) // nolint:gocritic } fmt.Println(state.Running) diff --git a/modules/mockserver/examples_test.go b/modules/mockserver/examples_test.go index e890bf6cdd..9125114375 100644 --- a/modules/mockserver/examples_test.go +++ b/modules/mockserver/examples_test.go @@ -4,6 +4,7 @@ import ( "context" "fmt" "io" + "log" "net/http" "strings" @@ -19,20 +20,20 @@ func ExampleRunContainer() { mockserverContainer, err := mockserver.RunContainer(ctx, testcontainers.WithImage("mockserver/mockserver:5.15.0")) if err != nil { - panic(err) + log.Fatalf("failed to start container: %s", err) } // Clean up the container defer func() { if err := mockserverContainer.Terminate(ctx); err != nil { - panic(err) + log.Fatalf("failed to terminate container: %s", err) } }() // } state, err := mockserverContainer.State(ctx) if err != nil { - panic(err) + log.Fatalf("failed to get container state: %s", err) // nolint:gocritic } fmt.Println(state.Running) @@ -47,19 +48,19 @@ func ExampleRunContainer_connect() { mockserverContainer, err := mockserver.RunContainer(ctx, testcontainers.WithImage("mockserver/mockserver:5.15.0")) if err != nil { - panic(err) + log.Fatalf("failed to start container: %s", err) } // Clean up the container defer func() { if err := mockserverContainer.Terminate(ctx); err != nil { - panic(err) + log.Fatalf("failed to terminate container: %s", err) } }() url, err := mockserverContainer.URL(ctx) if err != nil { - panic(err) + log.Fatalf("failed to get container URL: %s", err) // nolint:gocritic } ms := client.NewClientURL(url) // } @@ -71,18 +72,18 @@ func ExampleRunContainer_connect() { requestMatcher = requestMatcher.WithJSONFields(map[string]interface{}{"name": "Tools"}) err = ms.RegisterExpectation(client.NewExpectation(requestMatcher).WithResponse(client.NewResponseOK().WithJSONBody(map[string]any{"test": "value"}))) if err != nil { - panic(err) + log.Fatalf("failed to register expectation: %s", err) } httpClient := &http.Client{} resp, err := httpClient.Post(url+"/api/categories", "application/json", strings.NewReader(`{"name": "Tools"}`)) if err != nil { - panic(err) + log.Fatalf("failed to send request: %s", err) } buf, err := io.ReadAll(resp.Body) if err != nil { - panic(err) + log.Fatalf("failed to read response: %s", err) } resp.Body.Close() diff --git a/modules/mongodb/mongodb_test.go b/modules/mongodb/mongodb_test.go index 22a908d70b..8e936a7c64 100644 --- a/modules/mongodb/mongodb_test.go +++ b/modules/mongodb/mongodb_test.go @@ -3,6 +3,7 @@ package mongodb_test import ( "context" "fmt" + "log" "strings" "go.mongodb.org/mongo-driver/mongo" @@ -19,20 +20,20 @@ func ExampleRunContainer() { mongodbContainer, err := mongodb.RunContainer(ctx, testcontainers.WithImage("mongo:6")) if err != nil { - panic(err) + log.Fatalf("failed to start container: %s", err) } // Clean up the container defer func() { if err := mongodbContainer.Terminate(ctx); err != nil { - panic(err) + log.Fatalf("failed to terminate container: %s", err) } }() // } state, err := mongodbContainer.State(ctx) if err != nil { - panic(err) + log.Fatalf("failed to get container state: %s", err) // nolint:gocritic } fmt.Println(state.Running) @@ -47,30 +48,30 @@ func ExampleRunContainer_connect() { mongodbContainer, err := mongodb.RunContainer(ctx, testcontainers.WithImage("mongo:6")) if err != nil { - panic(err) + log.Fatalf("failed to start container: %s", err) } // Clean up the container defer func() { if err := mongodbContainer.Terminate(ctx); err != nil { - panic(err) + log.Fatalf("failed to terminate container: %s", err) } }() endpoint, err := mongodbContainer.ConnectionString(ctx) if err != nil { - panic(err) + log.Fatalf("failed to get connection string: %s", err) // nolint:gocritic } mongoClient, err := mongo.Connect(ctx, options.Client().ApplyURI(endpoint)) if err != nil { - panic(err) + log.Fatalf("failed to connect to MongoDB: %s", err) } // } err = mongoClient.Ping(ctx, nil) if err != nil { - panic(err) + log.Fatalf("failed to ping MongoDB: %s", err) } fmt.Println(mongoClient.Database("test").Name()) @@ -89,29 +90,29 @@ func ExampleRunContainer_withCredentials() { testcontainers.WithWaitStrategy(wait.ForLog("Waiting for connections")), ) if err != nil { - panic(err) + log.Fatalf("failed to start container: %s", err) } // Clean up the container defer func() { if err := container.Terminate(ctx); err != nil { - panic(err) + log.Fatalf("failed to terminate container: %s", err) } }() connStr, err := container.ConnectionString(ctx) if err != nil { - panic(err) + log.Fatalf("failed to get connection string: %s", err) // nolint:gocritic } mongoClient, err := mongo.Connect(ctx, options.Client().ApplyURI(connStr)) if err != nil { - panic(err) + log.Fatalf("failed to connect to MongoDB: %s", err) } err = mongoClient.Ping(ctx, nil) if err != nil { - panic(err) + log.Fatalf("failed to ping MongoDB: %s", err) } fmt.Println(strings.Split(connStr, "@")[0]) diff --git a/modules/mssql/examples_test.go b/modules/mssql/examples_test.go index 0c1d555e3d..2d7af8f0c7 100644 --- a/modules/mssql/examples_test.go +++ b/modules/mssql/examples_test.go @@ -3,6 +3,7 @@ package mssql_test import ( "context" "fmt" + "log" "github.com/testcontainers/testcontainers-go" "github.com/testcontainers/testcontainers-go/modules/mssql" @@ -20,20 +21,20 @@ func ExampleRunContainer() { mssql.WithPassword(password), ) if err != nil { - panic(err) + log.Fatalf("failed to start container: %s", err) } // Clean up the container defer func() { if err := mssqlContainer.Terminate(ctx); err != nil { - panic(err) + log.Fatalf("failed to terminate container: %s", err) } }() // } state, err := mssqlContainer.State(ctx) if err != nil { - panic(err) + log.Fatalf("failed to get container state: %s", err) // nolint:gocritic } fmt.Println(state.Running) diff --git a/modules/mysql/examples_test.go b/modules/mysql/examples_test.go index 29668dc3c4..a6be342643 100644 --- a/modules/mysql/examples_test.go +++ b/modules/mysql/examples_test.go @@ -4,6 +4,7 @@ import ( "context" "database/sql" "fmt" + "log" "path/filepath" "github.com/testcontainers/testcontainers-go" @@ -23,20 +24,20 @@ func ExampleRunContainer() { mysql.WithScripts(filepath.Join("testdata", "schema.sql")), ) if err != nil { - panic(err) + log.Fatalf("failed to start container: %s", err) } // Clean up the container defer func() { if err := mysqlContainer.Terminate(ctx); err != nil { - panic(err) + log.Fatalf("failed to terminate container: %s", err) } }() // } state, err := mysqlContainer.State(ctx) if err != nil { - panic(err) + log.Fatalf("failed to get container state: %s", err) // nolint:gocritic } fmt.Println(state.Running) @@ -57,12 +58,12 @@ func ExampleRunContainer_connect() { mysql.WithScripts(filepath.Join("testdata", "schema.sql")), ) if err != nil { - panic(err) + log.Fatalf("failed to start container: %s", err) } defer func() { if err := mysqlContainer.Terminate(ctx); err != nil { - panic(err) + log.Fatalf("failed to terminate container: %s", err) } }() @@ -70,23 +71,23 @@ func ExampleRunContainer_connect() { db, err := sql.Open("mysql", connectionString) if err != nil { - panic(err) + log.Fatalf("failed to connect to MySQL: %s", err) // nolint:gocritic } defer db.Close() if err = db.Ping(); err != nil { - panic(err) + log.Fatalf("failed to ping MySQL: %s", err) } stmt, err := db.Prepare("SELECT @@GLOBAL.tmpdir") if err != nil { - panic(err) + log.Fatalf("failed to prepare statement: %s", err) } defer stmt.Close() row := stmt.QueryRow() tmpDir := "" err = row.Scan(&tmpDir) if err != nil { - panic(err) + log.Fatalf("failed to scan row: %s", err) } fmt.Println(tmpDir) diff --git a/modules/nats/examples_test.go b/modules/nats/examples_test.go index ecb1abc844..fd95691cc4 100644 --- a/modules/nats/examples_test.go +++ b/modules/nats/examples_test.go @@ -3,6 +3,7 @@ package nats_test import ( "context" "fmt" + "log" natsgo "github.com/nats-io/nats.go" @@ -18,20 +19,20 @@ func ExampleRunContainer() { testcontainers.WithImage("nats:2.9"), ) if err != nil { - panic(err) + log.Fatalf("failed to start container: %s", err) } // Clean up the container defer func() { if err := natsContainer.Terminate(ctx); err != nil { - panic(err) + log.Fatalf("failed to terminate container: %s", err) } }() // } state, err := natsContainer.State(ctx) if err != nil { - panic(err) + log.Fatalf("failed to get container state: %s", err) // nolint:gocritic } fmt.Println(state.Running) @@ -46,24 +47,24 @@ func ExampleRunContainer_connectWithCredentials() { container, err := nats.RunContainer(ctx, nats.WithUsername("foo"), nats.WithPassword("bar")) if err != nil { - panic(err) + log.Fatalf("failed to start container: %s", err) } // Clean up the container defer func() { if err := container.Terminate(ctx); err != nil { - panic(err) + log.Fatalf("failed to terminate container: %s", err) } }() uri, err := container.ConnectionString(ctx) if err != nil { - panic(err) + log.Fatalf("failed to get connection string: %s", err) // nolint:gocritic } nc, err := natsgo.Connect(uri, natsgo.UserInfo(container.User, container.Password)) if err != nil { - panic(err) + log.Fatalf("failed to connect to NATS: %s", err) } defer nc.Close() // } diff --git a/modules/neo4j/examples_test.go b/modules/neo4j/examples_test.go index 23987e2e12..fcddfa7c41 100644 --- a/modules/neo4j/examples_test.go +++ b/modules/neo4j/examples_test.go @@ -3,6 +3,7 @@ package neo4j_test import ( "context" "fmt" + "log" "github.com/testcontainers/testcontainers-go" "github.com/testcontainers/testcontainers-go/modules/neo4j" @@ -21,20 +22,20 @@ func ExampleRunContainer() { neo4j.WithNeo4jSetting("dbms.tx_log.rotation.size", "42M"), ) if err != nil { - panic(err) + log.Fatalf("failed to start container: %s", err) } // Clean up the container defer func() { if err := neo4jContainer.Terminate(ctx); err != nil { - panic(err) + log.Fatalf("failed to terminate container: %s", err) } }() // } state, err := neo4jContainer.State(ctx) if err != nil { - panic(err) + log.Fatalf("failed to get container state: %s", err) // nolint:gocritic } fmt.Println(state.Running) diff --git a/modules/openldap/examples_test.go b/modules/openldap/examples_test.go index ad71d54a72..6c6ed6d672 100644 --- a/modules/openldap/examples_test.go +++ b/modules/openldap/examples_test.go @@ -3,6 +3,7 @@ package openldap_test import ( "context" "fmt" + "log" "github.com/go-ldap/ldap/v3" @@ -16,20 +17,20 @@ func ExampleRunContainer() { openldapContainer, err := openldap.RunContainer(ctx, testcontainers.WithImage("bitnami/openldap:2.6.6")) if err != nil { - panic(err) + log.Fatalf("failed to start container: %s", err) } // Clean up the container defer func() { if err := openldapContainer.Terminate(ctx); err != nil { - panic(err) + log.Fatalf("failed to terminate container: %s", err) } }() // } state, err := openldapContainer.State(ctx) if err != nil { - panic(err) + log.Fatalf("failed to get container state: %s", err) // nolint:gocritic } fmt.Println(state.Running) @@ -44,31 +45,31 @@ func ExampleRunContainer_connect() { openldapContainer, err := openldap.RunContainer(ctx, testcontainers.WithImage("bitnami/openldap:2.6.6")) if err != nil { - panic(err) + log.Fatalf("failed to start container: %s", err) } // Clean up the container defer func() { if err := openldapContainer.Terminate(ctx); err != nil { - panic(err) + log.Fatalf("failed to terminate container: %s", err) } }() connectionString, err := openldapContainer.ConnectionString(ctx) if err != nil { - panic(err) + log.Fatalf("failed to get connection string: %s", err) // nolint:gocritic } client, err := ldap.DialURL(connectionString) if err != nil { - panic(err) + log.Fatalf("failed to connect to LDAP server: %s", err) } defer client.Close() // First bind with a read only user err = client.Bind("cn=admin,dc=example,dc=org", "adminpassword") if err != nil { - panic(err) + log.Fatalf("failed to bind to LDAP server: %s", err) } // Search for the given username @@ -82,11 +83,11 @@ func ExampleRunContainer_connect() { sr, err := client.Search(searchRequest) if err != nil { - panic(err) + log.Fatalf("failed to search LDAP server: %s", err) } if len(sr.Entries) != 1 { - panic("User does not exist or too many entries returned") + log.Fatal("User does not exist or too many entries returned") } fmt.Println(sr.Entries[0].DN) diff --git a/modules/postgres/examples_test.go b/modules/postgres/examples_test.go index 9e833ac25c..ee164fdb6a 100644 --- a/modules/postgres/examples_test.go +++ b/modules/postgres/examples_test.go @@ -3,6 +3,7 @@ package postgres_test import ( "context" "fmt" + "log" "path/filepath" "time" @@ -32,20 +33,20 @@ func ExampleRunContainer() { WithStartupTimeout(5*time.Second)), ) if err != nil { - panic(err) + log.Fatalf("failed to start container: %s", err) } // Clean up the container defer func() { if err := postgresContainer.Terminate(ctx); err != nil { - panic(err) + log.Fatalf("failed to terminate container: %s", err) } }() // } state, err := postgresContainer.State(ctx) if err != nil { - panic(err) + log.Fatalf("failed to get container state: %s", err) // nolint:gocritic } fmt.Println(state.Running) diff --git a/modules/pulsar/examples_test.go b/modules/pulsar/examples_test.go index a973caea58..94e76c5f19 100644 --- a/modules/pulsar/examples_test.go +++ b/modules/pulsar/examples_test.go @@ -3,6 +3,7 @@ package pulsar_test import ( "context" "fmt" + "log" "github.com/testcontainers/testcontainers-go" "github.com/testcontainers/testcontainers-go/modules/pulsar" @@ -16,20 +17,20 @@ func ExampleRunContainer() { testcontainers.WithImage("docker.io/apachepulsar/pulsar:2.10.2"), ) if err != nil { - panic(err) + log.Fatalf("failed to start container: %s", err) } // Clean up the container defer func() { if err := pulsarContainer.Terminate(ctx); err != nil { - panic(err) + log.Fatalf("failed to terminate container: %s", err) } }() // } state, err := pulsarContainer.State(ctx) if err != nil { - panic(err) + log.Fatalf("failed to get container state: %s", err) // nolint:gocritic } fmt.Println(state.Running) diff --git a/modules/rabbitmq/examples_test.go b/modules/rabbitmq/examples_test.go index 2633472daa..f24bae0169 100644 --- a/modules/rabbitmq/examples_test.go +++ b/modules/rabbitmq/examples_test.go @@ -4,6 +4,7 @@ import ( "context" "fmt" "io" + "log" "path/filepath" "strings" @@ -23,20 +24,20 @@ func ExampleRunContainer() { rabbitmq.WithAdminPassword("password"), ) if err != nil { - panic(err) + log.Fatalf("failed to start container: %s", err) } // Clean up the container defer func() { if err := rabbitmqContainer.Terminate(ctx); err != nil { - panic(err) + log.Fatalf("failed to terminate container: %s", err) } }() // } state, err := rabbitmqContainer.State(ctx) if err != nil { - panic(err) + log.Fatalf("failed to get container state: %s", err) // nolint:gocritic } fmt.Println(state.Running) @@ -54,27 +55,27 @@ func ExampleRunContainer_connectUsingAmqp() { rabbitmq.WithAdminPassword("password"), ) if err != nil { - panic(err) + log.Fatalf("failed to start container: %s", err) } defer func() { if err := rabbitmqContainer.Terminate(ctx); err != nil { - panic(err) + log.Fatalf("failed to terminate container: %s", err) } }() amqpURL, err := rabbitmqContainer.AmqpURL(ctx) if err != nil { - panic(err) + log.Fatalf("failed to get AMQP URL: %s", err) // nolint:gocritic } amqpConnection, err := amqp.Dial(amqpURL) if err != nil { - panic(err) + log.Fatalf("failed to connect to RabbitMQ: %s", err) } defer func() { err := amqpConnection.Close() if err != nil { - panic(err) + log.Fatalf("failed to close connection: %s", err) } }() @@ -102,19 +103,19 @@ func ExampleRunContainer_withSSL() { rabbitmq.WithSSL(sslSettings), ) if err != nil { - panic(err) + log.Fatalf("failed to start container: %s", err) } // } defer func() { if err := rabbitmqContainer.Terminate(ctx); err != nil { - panic(err) + log.Fatalf("failed to terminate container: %s", err) } }() state, err := rabbitmqContainer.State(ctx) if err != nil { - panic(err) + log.Fatalf("failed to get container state: %s", err) // nolint:gocritic } fmt.Println(state.Running) @@ -136,12 +137,12 @@ func ExampleRunContainer_withPlugins() { ), ) if err != nil { - panic(err) + log.Fatalf("failed to start container: %s", err) } defer func() { if err := rabbitmqContainer.Terminate(ctx); err != nil { - panic(err) + log.Fatalf("failed to terminate container: %s", err) } }() @@ -158,23 +159,23 @@ func ExampleRunContainer_withCustomConfigFile() { testcontainers.WithImage("rabbitmq:3.7.25-management-alpine"), ) if err != nil { - panic(err) + log.Fatalf("failed to start container: %s", err) } defer func() { if err := rabbitmqContainer.Terminate(ctx); err != nil { - panic(err) + log.Fatalf("failed to terminate container: %s", err) } }() logs, err := rabbitmqContainer.Logs(ctx) if err != nil { - panic(err) + log.Fatalf("failed to get logs: %s", err) // nolint:gocritic } bytes, err := io.ReadAll(logs) if err != nil { - panic(err) + log.Fatalf("failed to read logs: %s", err) } fmt.Println(strings.Contains(string(bytes), "config file(s) : /etc/rabbitmq/rabbitmq-testcontainers.conf")) @@ -190,12 +191,12 @@ func assertPlugins(container testcontainers.Container, plugins ...string) bool { _, out, err := container.Exec(ctx, []string{"rabbitmq-plugins", "is_enabled", plugin}) if err != nil { - panic(err) + log.Fatalf("failed to execute command: %s", err) } check, err := io.ReadAll(out) if err != nil { - panic(err) + log.Fatalf("failed to read output: %s", err) } if !strings.Contains(string(check), plugin+" is enabled") { diff --git a/modules/redis/examples_test.go b/modules/redis/examples_test.go index 2812902114..90b6d30bde 100644 --- a/modules/redis/examples_test.go +++ b/modules/redis/examples_test.go @@ -3,6 +3,7 @@ package redis_test import ( "context" "fmt" + "log" "path/filepath" "github.com/testcontainers/testcontainers-go" @@ -20,20 +21,20 @@ func ExampleRunContainer() { redis.WithConfigFile(filepath.Join("testdata", "redis7.conf")), ) if err != nil { - panic(err) + log.Fatalf("failed to start container: %s", err) } // Clean up the container defer func() { if err := redisContainer.Terminate(ctx); err != nil { - panic(err) + log.Fatalf("failed to terminate container: %s", err) } }() // } state, err := redisContainer.State(ctx) if err != nil { - panic(err) + log.Fatalf("failed to get container state: %s", err) // nolint:gocritic } fmt.Println(state.Running) diff --git a/modules/redpanda/admin_api.go b/modules/redpanda/admin_api.go index dbdcf0435c..4f99d5cd08 100644 --- a/modules/redpanda/admin_api.go +++ b/modules/redpanda/admin_api.go @@ -12,10 +12,19 @@ import ( type AdminAPIClient struct { BaseURL string + client *http.Client } func NewAdminAPIClient(baseURL string) *AdminAPIClient { - return &AdminAPIClient{BaseURL: baseURL} + return &AdminAPIClient{ + BaseURL: baseURL, + client: http.DefaultClient, + } +} + +func (cl *AdminAPIClient) WithHTTPClient(c *http.Client) *AdminAPIClient { + cl.client = c + return cl } type createUserRequest struct { @@ -46,7 +55,7 @@ func (cl *AdminAPIClient) CreateUser(ctx context.Context, username, password str } req.Header.Set("Content-Type", "application/json") - resp, err := http.DefaultClient.Do(req) + resp, err := cl.client.Do(req) if err != nil { return fmt.Errorf("request failed: %w", err) } diff --git a/modules/redpanda/examples_test.go b/modules/redpanda/examples_test.go index 5b724a7e00..21913f95ff 100644 --- a/modules/redpanda/examples_test.go +++ b/modules/redpanda/examples_test.go @@ -3,6 +3,7 @@ package redpanda_test import ( "context" "fmt" + "log" "github.com/testcontainers/testcontainers-go/modules/redpanda" ) @@ -22,20 +23,20 @@ func ExampleRunContainer() { redpanda.WithEnableSchemaRegistryHTTPBasicAuth(), ) if err != nil { - panic(err) + log.Fatalf("failed to start container: %s", err) } // Clean up the container defer func() { if err := redpandaContainer.Terminate(ctx); err != nil { - panic(err) + log.Fatalf("failed to terminate container: %s", err) } }() // } state, err := redpandaContainer.State(ctx) if err != nil { - panic(err) + log.Fatalf("failed to get container state: %s", err) // nolint:gocritic } fmt.Println(state.Running) diff --git a/modules/redpanda/redpanda.go b/modules/redpanda/redpanda.go index 8756abeab5..6213809d4b 100644 --- a/modules/redpanda/redpanda.go +++ b/modules/redpanda/redpanda.go @@ -3,9 +3,12 @@ package redpanda import ( "bytes" "context" + "crypto/tls" + "crypto/x509" _ "embed" "fmt" "math" + "net/http" "os" "path/filepath" "strings" @@ -200,6 +203,11 @@ func RunContainer(ctx context.Context, opts ...testcontainers.ContainerCustomize return nil, fmt.Errorf("failed to wait for Redpanda readiness: %w", err) } + scheme := "http" + if settings.EnableTLS { + scheme += "s" + } + // 9. Create Redpanda Service Accounts if configured to do so. if len(settings.ServiceAccounts) > 0 { adminAPIPort, err := container.MappedPort(ctx, nat.Port(defaultAdminAPIPort)) @@ -207,8 +215,27 @@ func RunContainer(ctx context.Context, opts ...testcontainers.ContainerCustomize return nil, fmt.Errorf("failed to get mapped Admin API port: %w", err) } - adminAPIUrl := fmt.Sprintf("http://%v:%d", hostIP, adminAPIPort.Int()) + adminAPIUrl := fmt.Sprintf("%s://%v:%d", scheme, hostIP, adminAPIPort.Int()) adminCl := NewAdminAPIClient(adminAPIUrl) + if settings.EnableTLS { + cert, err := tls.X509KeyPair(settings.cert, settings.key) + if err != nil { + return nil, fmt.Errorf("failed to create admin client with cert: %w", err) + } + caCertPool := x509.NewCertPool() + caCertPool.AppendCertsFromPEM(settings.cert) + adminCl = adminCl.WithHTTPClient(&http.Client{ + Timeout: 5 * time.Second, + Transport: &http.Transport{ + ForceAttemptHTTP2: true, + TLSHandshakeTimeout: 10 * time.Second, + TLSClientConfig: &tls.Config{ + Certificates: []tls.Certificate{cert}, + RootCAs: caCertPool, + }, + }, + }) + } for username, password := range settings.ServiceAccounts { if err := adminCl.CreateUser(ctx, username, password); err != nil { @@ -217,11 +244,6 @@ func RunContainer(ctx context.Context, opts ...testcontainers.ContainerCustomize } } - scheme := "http" - if settings.EnableTLS { - scheme += "s" - } - return &Container{Container: container, urlScheme: scheme}, nil } diff --git a/modules/redpanda/redpanda_test.go b/modules/redpanda/redpanda_test.go index b55294304a..0a28940779 100644 --- a/modules/redpanda/redpanda_test.go +++ b/modules/redpanda/redpanda_test.go @@ -16,6 +16,7 @@ import ( "github.com/twmb/franz-go/pkg/kadm" "github.com/twmb/franz-go/pkg/kerr" "github.com/twmb/franz-go/pkg/kgo" + "github.com/twmb/franz-go/pkg/sasl/plain" "github.com/twmb/franz-go/pkg/sasl/scram" "github.com/testcontainers/testcontainers-go" @@ -275,6 +276,23 @@ func TestRedpandaWithOldVersionAndWasm(t *testing.T) { require.ErrorContains(t, err, "SASL_AUTHENTICATION_FAILED") } + // Test wrong mechanism + { + kafkaCl, err := kgo.NewClient( + kgo.SeedBrokers(seedBroker), + kgo.SASL(plain.Auth{ + User: "no-superuser", + Pass: "test", + }.AsMechanism()), + ) + require.NoError(t, err) + + kafkaAdmCl := kadm.NewClient(kafkaCl) + _, err = kafkaAdmCl.Metadata(ctx) + require.Error(t, err) + require.ErrorContains(t, err, "UNSUPPORTED_SASL_MECHANISM") + } + // Test Schema Registry API httpCl := &http.Client{Timeout: 5 * time.Second} // schemaRegistryAddress { @@ -395,6 +413,50 @@ func TestRedpandaWithTLS(t *testing.T) { require.Error(t, results.FirstErr(), kerr.UnknownTopicOrPartition) } +func TestRedpandaWithTLSAndSASL(t *testing.T) { + cert, err := tls.X509KeyPair(localhostCert, localhostKey) + require.NoError(t, err, "failed to load key pair") + + ctx := context.Background() + + container, err := RunContainer(ctx, + WithTLS(localhostCert, localhostKey), + WithEnableSASL(), + WithEnableKafkaAuthorization(), + WithNewServiceAccount("superuser-1", "test"), + WithSuperusers("superuser-1"), + ) + require.NoError(t, err) + + t.Cleanup(func() { + if err := container.Terminate(ctx); err != nil { + t.Fatalf("failed to terminate container: %s", err) + } + }) + + caCertPool := x509.NewCertPool() + caCertPool.AppendCertsFromPEM(localhostCert) + + tlsConfig := &tls.Config{ + Certificates: []tls.Certificate{cert}, + RootCAs: caCertPool, + } + + broker, err := container.KafkaSeedBroker(ctx) + require.NoError(t, err) + + kafkaCl, err := kgo.NewClient( + kgo.SeedBrokers(broker), + kgo.DialTLSConfig(tlsConfig), + kgo.SASL(scram.Auth{ + User: "superuser-1", + Pass: "test", + }.AsSha256Mechanism()), + ) + require.NoError(t, err) + defer kafkaCl.Close() +} + func TestRedpandaListener_Simple(t *testing.T) { ctx := context.Background() diff --git a/modules/vault/examples_test.go b/modules/vault/examples_test.go index e8638269ba..364b07347b 100644 --- a/modules/vault/examples_test.go +++ b/modules/vault/examples_test.go @@ -3,6 +3,7 @@ package vault_test import ( "context" "fmt" + "log" "github.com/testcontainers/testcontainers-go/exec" "github.com/testcontainers/testcontainers-go/modules/vault" @@ -14,20 +15,20 @@ func ExampleRunContainer() { vaultContainer, err := vault.RunContainer(ctx) if err != nil { - panic(err) + log.Fatalf("failed to start container: %s", err) } // Clean up the container defer func() { if err := vaultContainer.Terminate(ctx); err != nil { - panic(err) + log.Fatalf("failed to terminate container: %s", err) } }() // } state, err := vaultContainer.State(ctx) if err != nil { - panic(err) + log.Fatalf("failed to get container state: %s", err) // nolint:gocritic } fmt.Println(state.Running) @@ -42,20 +43,20 @@ func ExampleRunContainer_withToken() { vaultContainer, err := vault.RunContainer(ctx, vault.WithToken("MyToKeN")) if err != nil { - panic(err) + log.Fatalf("failed to start container: %s", err) } // Clean up the container defer func() { if err := vaultContainer.Terminate(ctx); err != nil { - panic(err) + log.Fatalf("failed to terminate container: %s", err) } }() // } state, err := vaultContainer.State(ctx) if err != nil { - panic(err) + log.Fatalf("failed to get container state: %s", err) // nolint:gocritic } fmt.Println(state.Running) @@ -65,7 +66,7 @@ func ExampleRunContainer_withToken() { } exitCode, _, err := vaultContainer.Exec(ctx, cmds, exec.Multiplexed()) if err != nil { - panic(err) + log.Fatalf("failed to execute command: %s", err) } fmt.Println(exitCode) @@ -87,20 +88,20 @@ func ExampleRunContainer_withInitCommand() { "write secret/testing top_secret=password123", // Create a secret )) if err != nil { - panic(err) + log.Fatalf("failed to start container: %s", err) } // Clean up the container defer func() { if err := vaultContainer.Terminate(ctx); err != nil { - panic(err) + log.Fatalf("failed to terminate container: %s", err) } }() // } state, err := vaultContainer.State(ctx) if err != nil { - panic(err) + log.Fatalf("failed to get container state: %s", err) // nolint:gocritic } fmt.Println(state.Running) diff --git a/mounts_test.go b/mounts_test.go index fe7bd4f02e..a89e6355c0 100644 --- a/mounts_test.go +++ b/mounts_test.go @@ -41,6 +41,10 @@ func TestVolumeMount(t *testing.T) { } func TestContainerMounts_PrepareMounts(t *testing.T) { + volumeOptions := &mount.VolumeOptions{ + Labels: GenericLabels(), + } + t.Parallel() tests := []struct { name string @@ -57,9 +61,10 @@ func TestContainerMounts_PrepareMounts(t *testing.T) { mounts: ContainerMounts{{Source: GenericVolumeMountSource{Name: "app-data"}, Target: "/data"}}, want: []mount.Mount{ { - Type: mount.TypeVolume, - Source: "app-data", - Target: "/data", + Type: mount.TypeVolume, + Source: "app-data", + Target: "/data", + VolumeOptions: volumeOptions, }, }, }, @@ -68,10 +73,11 @@ func TestContainerMounts_PrepareMounts(t *testing.T) { mounts: ContainerMounts{{Source: GenericVolumeMountSource{Name: "app-data"}, Target: "/data", ReadOnly: true}}, want: []mount.Mount{ { - Type: mount.TypeVolume, - Source: "app-data", - Target: "/data", - ReadOnly: true, + Type: mount.TypeVolume, + Source: "app-data", + Target: "/data", + ReadOnly: true, + VolumeOptions: volumeOptions, }, }, }, @@ -111,8 +117,9 @@ func TestContainerMounts_PrepareMounts(t *testing.T) { mounts: ContainerMounts{{Source: GenericTmpfsMountSource{}, Target: "/data"}}, want: []mount.Mount{ { - Type: mount.TypeTmpfs, - Target: "/data", + Type: mount.TypeTmpfs, + Target: "/data", + VolumeOptions: volumeOptions, }, }, }, @@ -121,9 +128,10 @@ func TestContainerMounts_PrepareMounts(t *testing.T) { mounts: ContainerMounts{{Source: GenericTmpfsMountSource{}, Target: "/data", ReadOnly: true}}, want: []mount.Mount{ { - Type: mount.TypeTmpfs, - Target: "/data", - ReadOnly: true, + Type: mount.TypeTmpfs, + Target: "/data", + ReadOnly: true, + VolumeOptions: volumeOptions, }, }, }, @@ -148,6 +156,7 @@ func TestContainerMounts_PrepareMounts(t *testing.T) { SizeBytes: 50 * 1024 * 1024, Mode: 0o644, }, + VolumeOptions: volumeOptions, }, }, }, @@ -193,3 +202,34 @@ func TestCreateContainerWithVolume(t *testing.T) { require.NoError(t, err) assert.Equal(t, "test-volume", volume.Name) } + +func TestMountsReceiveRyukLabels(t *testing.T) { + req := ContainerRequest{ + Image: "alpine", + Mounts: ContainerMounts{ + { + Source: GenericVolumeMountSource{ + Name: "app-data", + }, + Target: "/data", + }, + }, + } + + ctx := context.Background() + c, err := GenericContainer(ctx, GenericContainerRequest{ + ContainerRequest: req, + Started: true, + }) + require.NoError(t, err) + terminateContainerOnEnd(t, ctx, c) + + // Check if volume is created with the expected labels + client, err := NewDockerClientWithOpts(ctx) + require.NoError(t, err) + defer client.Close() + + volume, err := client.VolumeInspect(ctx, "app-data") + require.NoError(t, err) + assert.Equal(t, GenericLabels(), volume.Labels) +} diff --git a/network/network_test.go b/network/network_test.go index 69cc2a7dee..05e22756cf 100644 --- a/network/network_test.go +++ b/network/network_test.go @@ -41,7 +41,7 @@ func ExampleNew() { } defer func() { if err := net.Remove(ctx); err != nil { - panic(err) + log.Fatalf("failed to remove network: %s", err) } }() diff --git a/wait/exec_test.go b/wait/exec_test.go index d8bbf2ea72..b1c31f1b6e 100644 --- a/wait/exec_test.go +++ b/wait/exec_test.go @@ -30,7 +30,7 @@ func ExampleExecStrategy() { Started: true, }) if err != nil { - panic(err) + log.Fatalf("failed to start container: %s", err) } defer func() { @@ -41,7 +41,7 @@ func ExampleExecStrategy() { state, err := localstack.State(ctx) if err != nil { - panic(err) + log.Fatalf("failed to get container state: %s", err) // nolint:gocritic } fmt.Println(state.Running) diff --git a/wait/http_test.go b/wait/http_test.go index 7da075222b..8ed91097ad 100644 --- a/wait/http_test.go +++ b/wait/http_test.go @@ -37,7 +37,7 @@ func ExampleHTTPStrategy() { Started: true, }) if err != nil { - panic(err) + log.Fatalf("failed to start container: %s", err) } // } @@ -49,7 +49,7 @@ func ExampleHTTPStrategy() { state, err := c.State(ctx) if err != nil { - panic(err) + log.Fatalf("failed to get container state: %s", err) // nolint:gocritic } fmt.Println(state.Running) @@ -72,7 +72,7 @@ func ExampleHTTPStrategy_WithPort() { Started: true, }) if err != nil { - panic(err) + log.Fatalf("failed to start container: %s", err) } // } @@ -84,7 +84,7 @@ func ExampleHTTPStrategy_WithPort() { state, err := c.State(ctx) if err != nil { - panic(err) + log.Fatalf("failed to get container state: %s", err) // nolint:gocritic } fmt.Println(state.Running) @@ -106,7 +106,7 @@ func ExampleHTTPStrategy_WithForcedIPv4LocalHost() { Started: true, }) if err != nil { - panic(err) + log.Fatalf("failed to start container: %s", err) } defer func() { @@ -117,7 +117,7 @@ func ExampleHTTPStrategy_WithForcedIPv4LocalHost() { state, err := c.State(ctx) if err != nil { - panic(err) + log.Fatalf("failed to get container state: %s", err) // nolint:gocritic } fmt.Println(state.Running) @@ -140,7 +140,7 @@ func ExampleHTTPStrategy_WithBasicAuth() { Started: true, }) if err != nil { - panic(err) + log.Fatalf("failed to start container: %s", err) } // } @@ -152,7 +152,7 @@ func ExampleHTTPStrategy_WithBasicAuth() { state, err := gogs.State(ctx) if err != nil { - panic(err) + log.Fatalf("failed to get container state: %s", err) // nolint:gocritic } fmt.Println(state.Running)