Skip to content

Commit aa1cea5

Browse files
committed
Merge branch 'main' into etcd-module
* main: feat: support databend module (testcontainers#2779) chore: golangci-lint 1.61.0 (testcontainers#2787)
2 parents 60af87a + c1bb0bb commit aa1cea5

File tree

16 files changed

+650
-11
lines changed

16 files changed

+650
-11
lines changed

.github/workflows/ci-test-go.yml

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -73,10 +73,10 @@ jobs:
7373

7474
- name: golangci-lint
7575
if: ${{ inputs.platform == 'ubuntu-latest' }}
76-
uses: golangci/golangci-lint-action@9d1e0624a798bb64f6c3cea93db47765312263dc # v5
76+
uses: golangci/golangci-lint-action@aaa42aa0628b4ae2578232a66b541047968fac86 # v6.1.0
7777
with:
7878
# Optional: version of golangci-lint to use in form of v1.2 or v1.2.3 or `latest` to use the latest version
79-
version: v1.59.1
79+
version: v1.61.0
8080
# Optional: working directory, useful for monorepos
8181
working-directory: ${{ inputs.project-directory }}
8282
# Optional: golangci-lint command line arguments.

.github/workflows/ci.yml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -94,7 +94,7 @@ jobs:
9494
matrix:
9595
go-version: [1.22.x, 1.x]
9696
platform: [ubuntu-latest]
97-
module: [artemis, azurite, cassandra, chroma, clickhouse, cockroachdb, compose, consul, couchbase, dolt, elasticsearch, etcd, gcloud, grafana-lgtm, inbucket, influxdb, k3s, k6, kafka, localstack, mariadb, milvus, minio, mockserver, mongodb, mssql, mysql, nats, neo4j, ollama, openfga, openldap, opensearch, postgres, pulsar, qdrant, rabbitmq, redis, redpanda, registry, surrealdb, valkey, vault, vearch, weaviate]
97+
module: [artemis, azurite, cassandra, chroma, clickhouse, cockroachdb, compose, consul, couchbase, databend, dolt, elasticsearch, etcd, gcloud, grafana-lgtm, inbucket, influxdb, k3s, k6, kafka, localstack, mariadb, milvus, minio, mockserver, mongodb, mssql, mysql, nats, neo4j, ollama, openfga, openldap, opensearch, postgres, pulsar, qdrant, rabbitmq, redis, redpanda, registry, surrealdb, valkey, vault, vearch, weaviate]
9898
uses: ./.github/workflows/ci-test-go.yml
9999
with:
100100
go-version: ${{ matrix.go-version }}

.vscode/.testcontainers-go.code-workspace

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -49,6 +49,10 @@
4949
"name": "module / couchbase",
5050
"path": "../modules/couchbase"
5151
},
52+
{
53+
"name": "module / databend",
54+
"path": "../modules/databend"
55+
},
5256
{
5357
"name": "module / dolt",
5458
"path": "../modules/dolt"

commons-test.mk

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,7 @@ define go_install
66
endef
77

88
$(GOBIN)/golangci-lint:
9-
$(call go_install,github.com/golangci/golangci-lint/cmd/golangci-lint@v1.59.1)
9+
$(call go_install,github.com/golangci/golangci-lint/cmd/golangci-lint@v1.61.0)
1010

1111
$(GOBIN)/gotestsum:
1212
$(call go_install,gotest.tools/gotestsum@latest)

container.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -520,7 +520,7 @@ func (c *ContainerRequest) validateMounts() error {
520520

521521
c.HostConfigModifier(&hostConfig)
522522

523-
if hostConfig.Binds != nil && len(hostConfig.Binds) > 0 {
523+
if len(hostConfig.Binds) > 0 {
524524
for _, bind := range hostConfig.Binds {
525525
parts := strings.Split(bind, ":")
526526
if len(parts) != 2 {

docs/modules/databend.md

Lines changed: 72 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,72 @@
1+
# Databend
2+
3+
Not available until the next release of testcontainers-go <a href="https://github.com/testcontainers/testcontainers-go"><span class="tc-version">:material-tag: main</span></a>
4+
5+
## Introduction
6+
7+
The Testcontainers module for Databend.
8+
9+
## Adding this module to your project dependencies
10+
11+
Please run the following command to add the Databend module to your Go dependencies:
12+
13+
```
14+
go get github.com/testcontainers/testcontainers-go/modules/databend
15+
```
16+
17+
## Usage example
18+
19+
<!--codeinclude-->
20+
[Creating a Databend container](../../modules/databend/examples_test.go) inside_block:runDatabendContainer
21+
<!--/codeinclude-->
22+
23+
## Module Reference
24+
25+
### Run function
26+
27+
- Not available until the next release of testcontainers-go <a href="https://github.com/testcontainers/testcontainers-go"><span class="tc-version">:material-tag: main</span></a>
28+
29+
The Databend module exposes one entrypoint function to create the Databend container, and this function receives three parameters:
30+
31+
```golang
32+
func Run(ctx context.Context, img string, opts ...testcontainers.ContainerCustomizer) (*DatabendContainer, error)
33+
```
34+
35+
- `context.Context`, the Go context.
36+
- `string`, the Docker image to use.
37+
- `testcontainers.ContainerCustomizer`, a variadic argument for passing options.
38+
39+
### Container Options
40+
41+
When starting the Databend container, you can pass options in a variadic way to configure it.
42+
43+
#### Image
44+
45+
If you need to set a different Databend Docker image, you can set a valid Docker image as the second argument in the `Run` function.
46+
E.g. `Run(context.Background(), "datafuselabs/databend:v1.2.615")`.
47+
48+
{% include "../features/common_functional_options.md" %}
49+
50+
#### Set username, password
51+
52+
If you need to set a different user/password/database, you can use `WithUsername`, `WithPassword` options.
53+
54+
!!!info
55+
The default values for the username is `databend`, for password is `databend` and for the default database name is `default`.
56+
57+
### Container Methods
58+
59+
The Databend container exposes the following methods:
60+
61+
#### ConnectionString
62+
63+
This method returns the connection string to connect to the Databend container, using the default `8000` port.
64+
It's possible to pass extra parameters to the connection string, e.g. `sslmode=disable`.
65+
66+
<!--codeinclude-->
67+
[Get connection string](../../modules/databend/databend_test.go) inside_block:connectionString
68+
<!--/codeinclude-->
69+
70+
#### MustGetConnectionString
71+
72+
`MustConnectionString` panics if the address cannot be determined.

internal/config/config_test.go

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,6 @@
11
package config
22

33
import (
4-
"fmt"
54
"os"
65
"path/filepath"
76
"testing"
@@ -517,7 +516,7 @@ func TestReadTCConfig(t *testing.T) {
517516
},
518517
}
519518
for _, tt := range tests {
520-
t.Run(fmt.Sprintf(tt.name), func(t *testing.T) {
519+
t.Run(tt.name, func(t *testing.T) {
521520
tmpDir := t.TempDir()
522521
t.Setenv("HOME", tmpDir)
523522
t.Setenv("USERPROFILE", tmpDir) // Windows support

mkdocs.yml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -74,6 +74,7 @@ nav:
7474
- modules/cockroachdb.md
7575
- modules/consul.md
7676
- modules/couchbase.md
77+
- modules/databend.md
7778
- modules/dolt.md
7879
- modules/elasticsearch.md
7980
- modules/etcd.md

modules/consul/consul_test.go

Lines changed: 2 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,6 @@ import (
77
"testing"
88

99
capi "github.com/hashicorp/consul/api"
10-
"github.com/stretchr/testify/assert"
1110
"github.com/stretchr/testify/require"
1211

1312
"github.com/testcontainers/testcontainers-go"
@@ -47,11 +46,11 @@ func TestConsul(t *testing.T) {
4746
// Check if API is up
4847
host, err := ctr.ApiEndpoint(ctx)
4948
require.NoError(t, err)
50-
assert.NotEmpty(t, len(host))
49+
require.NotEmpty(t, host)
5150

5251
res, err := http.Get("http://" + host)
5352
require.NoError(t, err)
54-
assert.Equal(t, http.StatusOK, res.StatusCode)
53+
require.Equal(t, http.StatusOK, res.StatusCode)
5554

5655
cfg := capi.DefaultConfig()
5756
cfg.Address = host

modules/databend/Makefile

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
include ../../commons-test.mk
2+
3+
.PHONY: test
4+
test:
5+
$(MAKE) test-databend

modules/databend/databend.go

Lines changed: 135 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,135 @@
1+
package databend
2+
3+
import (
4+
"context"
5+
"errors"
6+
"fmt"
7+
"strings"
8+
9+
"github.com/testcontainers/testcontainers-go"
10+
"github.com/testcontainers/testcontainers-go/wait"
11+
)
12+
13+
const (
14+
databendUser = "databend"
15+
defaultUser = "databend"
16+
defaultPassword = "databend"
17+
defaultDatabaseName = "default"
18+
)
19+
20+
// DatabendContainer represents the Databend container type used in the module
21+
type DatabendContainer struct {
22+
testcontainers.Container
23+
username string
24+
password string
25+
database string
26+
}
27+
28+
var _ testcontainers.ContainerCustomizer = (*DatabendOption)(nil)
29+
30+
// DatabendOption is an option for the Databend container.
31+
type DatabendOption func(*DatabendContainer)
32+
33+
// Customize is a NOOP. It's defined to satisfy the testcontainers.ContainerCustomizer interface.
34+
func (o DatabendOption) Customize(*testcontainers.GenericContainerRequest) error {
35+
// NOOP to satisfy interface.
36+
return nil
37+
}
38+
39+
// Run creates an instance of the Databend container type
40+
func Run(ctx context.Context, img string, opts ...testcontainers.ContainerCustomizer) (*DatabendContainer, error) {
41+
req := testcontainers.ContainerRequest{
42+
Image: img,
43+
ExposedPorts: []string{"8000/tcp"},
44+
Env: map[string]string{
45+
"QUERY_DEFAULT_USER": defaultUser,
46+
"QUERY_DEFAULT_PASSWORD": defaultPassword,
47+
},
48+
WaitingFor: wait.ForListeningPort("8000/tcp"),
49+
}
50+
51+
genericContainerReq := testcontainers.GenericContainerRequest{
52+
ContainerRequest: req,
53+
Started: true,
54+
}
55+
56+
for _, opt := range opts {
57+
if err := opt.Customize(&genericContainerReq); err != nil {
58+
return nil, err
59+
}
60+
}
61+
62+
username := req.Env["QUERY_DEFAULT_USER"]
63+
password := req.Env["QUERY_DEFAULT_PASSWORD"]
64+
if password == "" && username == "" {
65+
return nil, errors.New("empty password and user")
66+
}
67+
68+
container, err := testcontainers.GenericContainer(ctx, genericContainerReq)
69+
var c *DatabendContainer
70+
if container != nil {
71+
c = &DatabendContainer{
72+
Container: container,
73+
password: password,
74+
username: username,
75+
database: defaultDatabaseName,
76+
}
77+
}
78+
79+
if err != nil {
80+
return c, fmt.Errorf("generic container: %w", err)
81+
}
82+
83+
return c, nil
84+
}
85+
86+
// MustConnectionString panics if the address cannot be determined.
87+
func (c *DatabendContainer) MustConnectionString(ctx context.Context, args ...string) string {
88+
addr, err := c.ConnectionString(ctx, args...)
89+
if err != nil {
90+
panic(err)
91+
}
92+
return addr
93+
}
94+
95+
func (c *DatabendContainer) ConnectionString(ctx context.Context, args ...string) (string, error) {
96+
containerPort, err := c.MappedPort(ctx, "8000/tcp")
97+
if err != nil {
98+
return "", fmt.Errorf("mapped port: %w", err)
99+
}
100+
101+
host, err := c.Host(ctx)
102+
if err != nil {
103+
return "", err
104+
}
105+
106+
extraArgs := ""
107+
if len(args) > 0 {
108+
extraArgs = "?" + strings.Join(args, "&")
109+
}
110+
if c.database == "" {
111+
return "", errors.New("database name is empty")
112+
}
113+
114+
// databend://databend:databend@localhost:8000/default?sslmode=disable
115+
connectionString := fmt.Sprintf("databend://%s:%s@%s:%s/%s%s", c.username, c.password, host, containerPort.Port(), c.database, extraArgs)
116+
return connectionString, nil
117+
}
118+
119+
// WithUsername sets the username for the Databend container.
120+
// WithUsername is [Run] option that configures the default query user by setting
121+
// the `QUERY_DEFAULT_USER` container environment variable.
122+
func WithUsername(username string) testcontainers.CustomizeRequestOption {
123+
return func(req *testcontainers.GenericContainerRequest) error {
124+
req.Env["QUERY_DEFAULT_USER"] = username
125+
return nil
126+
}
127+
}
128+
129+
// WithPassword sets the password for the Databend container.
130+
func WithPassword(password string) testcontainers.CustomizeRequestOption {
131+
return func(req *testcontainers.GenericContainerRequest) error {
132+
req.Env["QUERY_DEFAULT_PASSWORD"] = password
133+
return nil
134+
}
135+
}

modules/databend/databend_test.go

Lines changed: 74 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,74 @@
1+
package databend_test
2+
3+
import (
4+
"context"
5+
"database/sql"
6+
"testing"
7+
8+
_ "github.com/datafuselabs/databend-go"
9+
"github.com/stretchr/testify/require"
10+
11+
"github.com/testcontainers/testcontainers-go"
12+
"github.com/testcontainers/testcontainers-go/modules/databend"
13+
)
14+
15+
func TestDatabend(t *testing.T) {
16+
ctx := context.Background()
17+
18+
ctr, err := databend.Run(ctx, "datafuselabs/databend:v1.2.615")
19+
testcontainers.CleanupContainer(t, ctr)
20+
require.NoError(t, err)
21+
22+
// perform assertions
23+
// connectionString {
24+
connectionString, err := ctr.ConnectionString(ctx, "sslmode=disable")
25+
// }
26+
require.NoError(t, err)
27+
28+
mustConnectionString := ctr.MustConnectionString(ctx, "sslmode=disable")
29+
require.Equal(t, connectionString, mustConnectionString)
30+
31+
db, err := sql.Open("databend", connectionString)
32+
require.NoError(t, err)
33+
defer db.Close()
34+
35+
err = db.Ping()
36+
require.NoError(t, err)
37+
38+
_, err = db.Exec("CREATE TABLE IF NOT EXISTS a_table ( \n" +
39+
" `col_1` VARCHAR(128) NOT NULL, \n" +
40+
" `col_2` VARCHAR(128) NOT NULL \n" +
41+
")")
42+
require.NoError(t, err)
43+
}
44+
45+
func TestDatabendWithDefaultUserAndPassword(t *testing.T) {
46+
ctx := context.Background()
47+
48+
ctr, err := databend.Run(ctx,
49+
"datafuselabs/databend:v1.2.615",
50+
databend.WithUsername("databend"))
51+
testcontainers.CleanupContainer(t, ctr)
52+
require.NoError(t, err)
53+
54+
// perform assertions
55+
connectionString, err := ctr.ConnectionString(ctx, "sslmode=disable")
56+
require.NoError(t, err)
57+
58+
db, err := sql.Open("databend", connectionString)
59+
require.NoError(t, err)
60+
defer db.Close()
61+
err = db.Ping()
62+
require.NoError(t, err)
63+
64+
var i int
65+
row := db.QueryRow("select 1")
66+
err = row.Scan(&i)
67+
require.NoError(t, err)
68+
69+
_, err = db.Exec("CREATE TABLE IF NOT EXISTS a_table ( \n" +
70+
" `col_1` VARCHAR(128) NOT NULL, \n" +
71+
" `col_2` VARCHAR(128) NOT NULL \n" +
72+
")")
73+
require.NoError(t, err)
74+
}

0 commit comments

Comments
 (0)