Skip to content

Commit d67ece5

Browse files
committed
Merge branch 'main' into support-buildkit
* main: support Dolt (testcontainers#2177) chore: create TLS certs in a consistent manner (testcontainers#2478) chore(deps): bump idna from 3.6 to 3.7 (testcontainers#2480)
2 parents cd77ebb + b5f1e13 commit d67ece5

35 files changed

+1143
-299
lines changed

.github/workflows/ci.yml

+1-1
Original file line numberDiff line numberDiff line change
@@ -94,7 +94,7 @@ jobs:
9494
matrix:
9595
go-version: [1.21.x, 1.x]
9696
platform: [ubuntu-latest]
97-
module: [artemis, cassandra, chroma, clickhouse, cockroachdb, compose, consul, couchbase, elasticsearch, gcloud, 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, vault, weaviate]
97+
module: [artemis, cassandra, chroma, clickhouse, cockroachdb, compose, consul, couchbase, dolt, elasticsearch, gcloud, 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, vault, weaviate]
9898
uses: ./.github/workflows/ci-test-go.yml
9999
with:
100100
go-version: ${{ matrix.go-version }}

.vscode/.testcontainers-go.code-workspace

+4
Original file line numberDiff line numberDiff line change
@@ -45,6 +45,10 @@
4545
"name": "module / couchbase",
4646
"path": "../modules/couchbase"
4747
},
48+
{
49+
"name": "module / dolt",
50+
"path": "../modules/dolt"
51+
},
4852
{
4953
"name": "module / elasticsearch",
5054
"path": "../modules/elasticsearch"

Pipfile.lock

+4-3
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

container.go

+1-1
Original file line numberDiff line numberDiff line change
@@ -228,7 +228,7 @@ func (c *ContainerRequest) GetContext() (io.Reader, error) {
228228

229229
if dockerIgnoreExists {
230230
// only add .dockerignore if it exists
231-
includes = append(includes, filepath.Join(".dockerignore"))
231+
includes = append(includes, ".dockerignore")
232232
}
233233

234234
includes = append(includes, c.GetDockerfile())

docker.go

+3-1
Original file line numberDiff line numberDiff line change
@@ -640,6 +640,9 @@ func (c *DockerContainer) CopyToContainer(ctx context.Context, fileContent []byt
640640

641641
func (c *DockerContainer) copyToContainer(ctx context.Context, fileContent func(tw io.Writer) error, fileContentSize int64, containerFilePath string, fileMode int64) error {
642642
buffer, err := tarFile(containerFilePath, fileContent, fileContentSize, fileMode)
643+
if err != nil {
644+
return err
645+
}
643646

644647
err = c.provider.client.CopyToContainer(ctx, c.ID, "/", buffer, types.CopyToContainerOptions{})
645648
if err != nil {
@@ -1531,7 +1534,6 @@ func (p *DockerProvider) getDefaultNetwork(ctx context.Context, cli client.APICl
15311534
Attachable: true,
15321535
Labels: core.DefaultLabels(core.SessionID()),
15331536
})
1534-
15351537
if err != nil {
15361538
return "", err
15371539
}

docker_exec_test.go

+3-2
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@ import (
88

99
"github.com/docker/docker/pkg/stdcopy"
1010
"github.com/stretchr/testify/require"
11+
1112
tcexec "github.com/testcontainers/testcontainers-go/exec"
1213
)
1314

@@ -133,6 +134,6 @@ func TestExecWithNonMultiplexedResponse(t *testing.T) {
133134
require.NotNil(t, stdout)
134135
require.NotNil(t, stderr)
135136

136-
require.Equal(t, stdout.String(), "stdout\n")
137-
require.Equal(t, stderr.String(), "stderr\n")
137+
require.Equal(t, "stdout\n", stdout.String())
138+
require.Equal(t, "stderr\n", stderr.String())
138139
}

docs/features/tls.md

+17
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,17 @@
1+
# TLS certificates
2+
3+
Interacting with services that require TLS certificates is a common issue when working with containers. You can create one or more on-the-fly certificates in order to communicate with your services.
4+
5+
_Testcontainers for Go_ uses a library to generate certificates on-the-fly. This library is called [tlscert](https://github.com/mdelapenya/tlscert).
6+
7+
### Examples
8+
9+
In the following example we are going to start an HTTP server with a self-signed certificate.
10+
It exposes one single handler that will return a simple message when accessed.
11+
The example will also create a client that will connect to the server using the generated certificate,
12+
demonstrating how to use the generated certificate to communicate with a service.
13+
14+
<!--codeinclude-->
15+
[Create a self-signed certificate](../../modules/cockroachdb/certs.go) inside_block:exampleSelfSignedCert
16+
[Sign a self-signed certificate](../../modules/cockroachdb/certs.go) inside_block:exampleSignSelfSignedCert
17+
<!--/codeinclude-->

docs/modules/dolt.md

+81
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,81 @@
1+
# Dolt
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 Dolt.
8+
9+
## Adding this module to your project dependencies
10+
11+
Please run the following command to add the Dolt module to your Go dependencies:
12+
13+
```
14+
go get github.com/testcontainers/testcontainers-go/modules/dolt
15+
```
16+
17+
## Usage example
18+
19+
<!--codeinclude-->
20+
[Creating a Dolt container](../../modules/dolt/examples_test.go) inside_block:runDoltContainer
21+
<!--/codeinclude-->
22+
23+
## Module reference
24+
25+
The Dolt module exposes one entrypoint function to create the Dolt container, and this function receives two parameters:
26+
27+
```golang
28+
func RunContainer(ctx context.Context, opts ...testcontainers.ContainerCustomizer) (*DoltContainer, error)
29+
```
30+
31+
- `context.Context`, the Go context.
32+
- `testcontainers.ContainerCustomizer`, a variadic argument for passing options.
33+
34+
### Container Options
35+
36+
When starting the Dolt container, you can pass options in a variadic way to configure it.
37+
38+
#### Image
39+
40+
If you need to set a different Dolt Docker image, you can use `testcontainers.WithImage` with a valid Docker image
41+
for Dolt. E.g. `testcontainers.WithImage("dolthub/dolt-sql-server:1.32.4")`.
42+
43+
{% include "../features/common_functional_options.md" %}
44+
45+
#### Set username, password and database name
46+
47+
If you need to set a different database, and its credentials, you can use `WithUsername`, `WithPassword`, `WithDatabase`
48+
options.
49+
50+
!!!info
51+
The default values for the username is `root`, for password is `test` and for the default database name is `test`.
52+
53+
#### Init Scripts
54+
55+
If you would like to perform DDL or DML operations in the Dolt container, add one or more `*.sql`, `*.sql.gz`, or `*.sh`
56+
scripts to the container request, using the `WithScripts(scriptPaths ...string)`. Those files will be copied under `/docker-entrypoint-initdb.d`.
57+
58+
#### Clone from remotes
59+
60+
If you would like to clone data from a remote into the Dolt container, add an `*.sh`
61+
scripts to the container request, using the `WithScripts(scriptPaths ...string)`. Additionally, use `WithDoltCloneRemoteUrl(url string)` to specify
62+
the remote to clone, and use `WithDoltCredsPublicKey(key string)` along with `WithCredsFile(credsFile string)` to authorize the Dolt container to clone from the remote.
63+
64+
<!--codeinclude-->
65+
[Example of Clone script](../../modules/dolt/testdata/clone-db.sh)
66+
<!--/codeinclude-->
67+
68+
#### Custom configuration
69+
70+
If you need to set a custom configuration, you can use `WithConfigFile` option to pass the path to a custom configuration file.
71+
72+
### Container Methods
73+
74+
#### ConnectionString
75+
76+
This method returns the connection string to connect to the Dolt container, using the default `3306` port.
77+
It's possible to pass extra parameters to the connection string, e.g. `tls=skip-verify` or `application_name=myapp`, in a variadic way.
78+
79+
<!--codeinclude-->
80+
[Get connection string](../../modules/dolt/dolt_test.go) inside_block:connectionString
81+
<!--/codeinclude-->

mkdocs.yml

+2
Original file line numberDiff line numberDiff line change
@@ -45,6 +45,7 @@ nav:
4545
- features/files_and_mounts.md
4646
- features/creating_networks.md
4747
- features/networking.md
48+
- features/tls.md
4849
- features/garbage_collector.md
4950
- features/build_from_dockerfile.md
5051
- features/docker_auth.md
@@ -70,6 +71,7 @@ nav:
7071
- modules/cockroachdb.md
7172
- modules/consul.md
7273
- modules/couchbase.md
74+
- modules/dolt.md
7375
- modules/elasticsearch.md
7476
- modules/gcloud.md
7577
- modules/inbucket.md

modules/cockroachdb/certs.go

+42-133
Original file line numberDiff line numberDiff line change
@@ -1,14 +1,12 @@
11
package cockroachdb
22

33
import (
4-
"crypto/rand"
5-
"crypto/rsa"
64
"crypto/x509"
7-
"crypto/x509/pkix"
8-
"encoding/pem"
9-
"math/big"
5+
"fmt"
106
"net"
117
"time"
8+
9+
"github.com/mdelapenya/tlscert"
1210
)
1311

1412
type TLSConfig struct {
@@ -21,138 +19,49 @@ type TLSConfig struct {
2119

2220
// NewTLSConfig creates a new TLSConfig capable of running CockroachDB & connecting over TLS.
2321
func NewTLSConfig() (*TLSConfig, error) {
24-
caCert, caKey, err := generateCA()
25-
if err != nil {
26-
return nil, err
22+
// exampleSelfSignedCert {
23+
caCert := tlscert.SelfSignedFromRequest(tlscert.Request{
24+
Name: "ca",
25+
SubjectCommonName: "Cockroach Test CA",
26+
Host: "localhost,127.0.0.1",
27+
IsCA: true,
28+
ValidFor: time.Hour,
29+
})
30+
if caCert == nil {
31+
return nil, fmt.Errorf("failed to generate CA certificate")
2732
}
28-
29-
nodeCert, nodeKey, err := generateNode(caCert, caKey)
30-
if err != nil {
31-
return nil, err
33+
// }
34+
35+
// exampleSignSelfSignedCert {
36+
nodeCert := tlscert.SelfSignedFromRequest(tlscert.Request{
37+
Name: "node",
38+
SubjectCommonName: "node",
39+
Host: "localhost,127.0.0.1",
40+
IPAddresses: []net.IP{net.IPv4(127, 0, 0, 1), net.IPv6loopback},
41+
ValidFor: time.Hour,
42+
Parent: caCert, // using the CA certificate as parent
43+
})
44+
if nodeCert == nil {
45+
return nil, fmt.Errorf("failed to generate node certificate")
3246
}
33-
34-
clientCert, clientKey, err := generateClient(caCert, caKey)
35-
if err != nil {
36-
return nil, err
47+
// }
48+
49+
clientCert := tlscert.SelfSignedFromRequest(tlscert.Request{
50+
Name: "client",
51+
SubjectCommonName: defaultUser,
52+
Host: "localhost,127.0.0.1",
53+
ValidFor: time.Hour,
54+
Parent: caCert, // using the CA certificate as parent
55+
})
56+
if clientCert == nil {
57+
return nil, fmt.Errorf("failed to generate client certificate")
3758
}
3859

3960
return &TLSConfig{
40-
CACert: caCert,
41-
NodeCert: nodeCert,
42-
NodeKey: nodeKey,
43-
ClientCert: clientCert,
44-
ClientKey: clientKey,
61+
CACert: caCert.Cert,
62+
NodeCert: nodeCert.Bytes,
63+
NodeKey: nodeCert.KeyBytes,
64+
ClientCert: clientCert.Bytes,
65+
ClientKey: clientCert.KeyBytes,
4566
}, nil
4667
}
47-
48-
func generateCA() (*x509.Certificate, *rsa.PrivateKey, error) {
49-
template := x509.Certificate{
50-
SerialNumber: big.NewInt(2019),
51-
Subject: pkix.Name{
52-
CommonName: "Cockroach Test CA",
53-
},
54-
NotBefore: time.Now().Add(-time.Hour),
55-
NotAfter: time.Now().Add(time.Hour),
56-
KeyUsage: x509.KeyUsageKeyEncipherment | x509.KeyUsageDigitalSignature | x509.KeyUsageCertSign,
57-
BasicConstraintsValid: true,
58-
IsCA: true,
59-
}
60-
61-
caPrivKey, err := rsa.GenerateKey(rand.Reader, 2048)
62-
if err != nil {
63-
return nil, nil, err
64-
}
65-
66-
caBytes, err := x509.CreateCertificate(rand.Reader, &template, &template, caPrivKey.Public(), caPrivKey)
67-
if err != nil {
68-
return nil, nil, err
69-
}
70-
71-
caCert, err := x509.ParseCertificate(caBytes)
72-
if err != nil {
73-
return nil, nil, err
74-
}
75-
76-
return caCert, caPrivKey, nil
77-
}
78-
79-
func generateNode(caCert *x509.Certificate, caKey *rsa.PrivateKey) ([]byte, []byte, error) {
80-
template := x509.Certificate{
81-
SerialNumber: big.NewInt(2019),
82-
Subject: pkix.Name{
83-
CommonName: "node",
84-
},
85-
DNSNames: []string{"localhost"},
86-
IPAddresses: []net.IP{
87-
net.IPv4(127, 0, 0, 1),
88-
net.IPv6loopback,
89-
},
90-
NotBefore: time.Now().Add(-time.Hour),
91-
NotAfter: time.Now().Add(time.Hour),
92-
KeyUsage: x509.KeyUsageKeyEncipherment | x509.KeyUsageDigitalSignature,
93-
ExtKeyUsage: []x509.ExtKeyUsage{
94-
x509.ExtKeyUsageServerAuth,
95-
x509.ExtKeyUsageClientAuth,
96-
},
97-
BasicConstraintsValid: true,
98-
}
99-
100-
certPrivKey, err := rsa.GenerateKey(rand.Reader, 2048)
101-
if err != nil {
102-
return nil, nil, err
103-
}
104-
105-
certBytes, err := x509.CreateCertificate(rand.Reader, &template, caCert, certPrivKey.Public(), caKey)
106-
if err != nil {
107-
return nil, nil, err
108-
}
109-
110-
cert := pem.EncodeToMemory(&pem.Block{
111-
Type: "CERTIFICATE",
112-
Bytes: certBytes,
113-
})
114-
certKey := pem.EncodeToMemory(&pem.Block{
115-
Type: "RSA PRIVATE KEY",
116-
Bytes: x509.MarshalPKCS1PrivateKey(certPrivKey),
117-
})
118-
119-
return cert, certKey, nil
120-
}
121-
122-
func generateClient(caCert *x509.Certificate, caKey *rsa.PrivateKey) ([]byte, []byte, error) {
123-
template := x509.Certificate{
124-
SerialNumber: big.NewInt(2019),
125-
Subject: pkix.Name{
126-
CommonName: defaultUser,
127-
},
128-
NotBefore: time.Now().Add(-time.Hour),
129-
NotAfter: time.Now().Add(time.Hour),
130-
KeyUsage: x509.KeyUsageKeyEncipherment | x509.KeyUsageDigitalSignature,
131-
ExtKeyUsage: []x509.ExtKeyUsage{
132-
x509.ExtKeyUsageServerAuth,
133-
x509.ExtKeyUsageClientAuth,
134-
},
135-
BasicConstraintsValid: true,
136-
}
137-
138-
certPrivKey, err := rsa.GenerateKey(rand.Reader, 2048)
139-
if err != nil {
140-
return nil, nil, err
141-
}
142-
143-
certBytes, err := x509.CreateCertificate(rand.Reader, &template, caCert, certPrivKey.Public(), caKey)
144-
if err != nil {
145-
return nil, nil, err
146-
}
147-
148-
cert := pem.EncodeToMemory(&pem.Block{
149-
Type: "CERTIFICATE",
150-
Bytes: certBytes,
151-
})
152-
certKey := pem.EncodeToMemory(&pem.Block{
153-
Type: "RSA PRIVATE KEY",
154-
Bytes: x509.MarshalPKCS1PrivateKey(certPrivKey),
155-
})
156-
157-
return cert, certKey, nil
158-
}

modules/cockroachdb/go.mod

+1
Original file line numberDiff line numberDiff line change
@@ -36,6 +36,7 @@ require (
3636
github.com/kr/text v0.2.0 // indirect
3737
github.com/lufia/plan9stats v0.0.0-20211012122336-39d0f177ccd0 // indirect
3838
github.com/magiconair/properties v1.8.7 // indirect
39+
github.com/mdelapenya/tlscert v0.1.0
3940
github.com/moby/patternmatcher v0.6.0 // indirect
4041
github.com/moby/sys/sequential v0.5.0 // indirect
4142
github.com/moby/sys/user v0.1.0 // indirect

0 commit comments

Comments
 (0)