Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 5 additions & 1 deletion docs/modules/redis.md
Original file line number Diff line number Diff line change
Expand Up @@ -73,10 +73,12 @@ In the case you have a custom config file for Redis, it's possible to copy that

#### WithTLS

- Since testcontainers-go <a href="https://github.com/testcontainers/testcontainers-go/releases/tag/v0.37.0"><span class="tc-version">:material-tag: v0.37.0</span></a>

In the case you want to enable TLS for the Redis container, you can use the `WithTLS()` option. This options enables TLS on the `6379/tcp` port and uses a secure URL (e.g. `rediss://host:port`).

!!!info
In case you want to use Non-mutual TLS (i.e. client authentication is not required), you can customize the CMD arguments by using the `WithCmdArgs` option. E.g. `WithCmdArgs("--tls-auth-clients no")`.
In case you want to use Non-mutual TLS (i.e. client authentication is not required), you can customize the CMD arguments by using the `WithCmdArgs` option. E.g. `WithCmdArgs("--tls-auth-clients", "no")`.

The module automatically generates three certificates, a CA certificate, a client certificate and a Redis certificate. Please use the `TLSConfig()` container method to get the TLS configuration and use it to configure the Redis client. See more details in the [TLSConfig](#tlsconfig) section.

Expand All @@ -94,6 +96,8 @@ If the container is started with TLS enabled, the connection string is `rediss:/

#### TLSConfig

- Since testcontainers-go <a href="https://github.com/testcontainers/testcontainers-go/releases/tag/v0.37.0"><span class="tc-version">:material-tag: v0.37.0</span></a>

This method returns the TLS configuration for the Redis container, nil if TLS is not enabled.

<!--codeinclude-->
Expand Down
25 changes: 24 additions & 1 deletion docs/modules/valkey.md
Original file line number Diff line number Diff line change
Expand Up @@ -66,6 +66,17 @@ You can easily set the valkey logging level. E.g. `WithLogLevel(LogLevelDebug)`.

In the case you have a custom config file for Valkey, it's possible to copy that file into the container before it's started. E.g. `WithConfigFile(filepath.Join("testdata", "valkey.conf"))`.

#### WithTLS

- 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>

In the case you want to enable TLS for the Valkey container, you can use the `WithTLS()` option. This options enables TLS on the `6379/tcp` port and uses a secure URL (e.g. `rediss://host:port`).

!!!info
In case you want to use Non-mutual TLS (i.e. client authentication is not required), you can customize the CMD arguments by using the `WithCmdArgs` option. E.g. `WithCmdArgs("--tls-auth-clients", "no")`.

The module automatically generates three certificates, a CA certificate, a client certificate and a Valkey certificate. Please use the `TLSConfig()` container method to get the TLS configuration and use it to configure the Valkey client. See more details in the [TLSConfig](#tlsconfig) section.

### Container Methods

The Valkey container exposes the following methods:
Expand All @@ -78,4 +89,16 @@ This method returns the connection string to connect to the Valkey container, us

<!--codeinclude-->
[Get connection string](../../modules/valkey/valkey_test.go) inside_block:connectionString
<!--/codeinclude-->
<!--/codeinclude-->

#### TLSConfig

- 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>

This method returns the TLS configuration for the Valkey container, nil if TLS is not enabled.

<!--codeinclude-->
[Get TLS config](../../modules/valkey/valkey_test.go) inside_block:tlsConfig
<!--/codeinclude-->

In the above example, the options are used to configure a Valkey client with TLS enabled.
72 changes: 67 additions & 5 deletions modules/valkey/examples_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -6,19 +6,21 @@ import (
"log"
"path/filepath"

"github.com/valkey-io/valkey-go"

"github.com/testcontainers/testcontainers-go"
"github.com/testcontainers/testcontainers-go/modules/valkey"
tcvalkey "github.com/testcontainers/testcontainers-go/modules/valkey"
)

func ExampleRun() {
// runValkeyContainer {
ctx := context.Background()

valkeyContainer, err := valkey.Run(ctx,
valkeyContainer, err := tcvalkey.Run(ctx,
"valkey/valkey:7.2.5",
valkey.WithSnapshotting(10, 1),
valkey.WithLogLevel(valkey.LogLevelVerbose),
valkey.WithConfigFile(filepath.Join("testdata", "valkey7.conf")),
tcvalkey.WithSnapshotting(10, 1),
tcvalkey.WithLogLevel(tcvalkey.LogLevelVerbose),
tcvalkey.WithConfigFile(filepath.Join("testdata", "valkey7.conf")),
)
defer func() {
if err := testcontainers.TerminateContainer(valkeyContainer); err != nil {
Expand All @@ -42,3 +44,63 @@ func ExampleRun() {
// Output:
// true
}

func ExampleRun_withTLS() {
ctx := context.Background()

valkeyContainer, err := tcvalkey.Run(ctx,
"valkey/valkey:7.2.5",
tcvalkey.WithSnapshotting(10, 1),
tcvalkey.WithLogLevel(tcvalkey.LogLevelVerbose),
tcvalkey.WithTLS(),
)
defer func() {
if err := testcontainers.TerminateContainer(valkeyContainer); err != nil {
log.Printf("failed to terminate container: %s", err)
}
}()
if err != nil {
log.Printf("failed to start container: %s", err)
return
}

if valkeyContainer.TLSConfig() == nil {
log.Println("TLS is not enabled")
return
}

uri, err := valkeyContainer.ConnectionString(ctx)
if err != nil {
log.Printf("failed to get connection string: %s", err)
return
}

// You will likely want to wrap your Valkey package of choice in an
// interface to aid in unit testing and limit lock-in throughout your
// codebase but that's out of scope for this example
options, err := valkey.ParseURL(uri)
if err != nil {
log.Printf("failed to parse connection string: %s", err)
return
}

options.TLSConfig = valkeyContainer.TLSConfig()

client, err := valkey.NewClient(options)
if err != nil {
log.Printf("failed to create valkey client: %s", err)
return
}
defer func() {
err := flushValkey(ctx, client)
if err != nil {
log.Printf("failed to flush valkey: %s", err)
}
}()

resp := client.Do(ctx, client.B().Ping().Build().Pin())
fmt.Println(resp.String())

// Output:
// {"Message":{"Value":"PONG","Type":"simple string"}}
}
4 changes: 2 additions & 2 deletions modules/valkey/go.mod
Original file line number Diff line number Diff line change
Expand Up @@ -6,9 +6,10 @@ toolchain go1.23.6

require (
github.com/google/uuid v1.6.0
github.com/mdelapenya/tlscert v0.2.0
github.com/stretchr/testify v1.10.0
github.com/testcontainers/testcontainers-go v0.37.0
github.com/valkey-io/valkey-go v1.0.41
github.com/valkey-io/valkey-go v1.0.59
)

replace github.com/testcontainers/testcontainers-go => ../..
Expand Down Expand Up @@ -60,7 +61,6 @@ require (
go.opentelemetry.io/otel/trace v1.35.0 // indirect
go.opentelemetry.io/proto/otlp v1.0.0 // indirect
golang.org/x/crypto v0.37.0 // indirect
golang.org/x/net v0.38.0 // indirect
golang.org/x/sys v0.32.0 // indirect
google.golang.org/genproto/googleapis/api v0.0.0-20240318140521-94a12d6c2237 // indirect
google.golang.org/genproto/googleapis/rpc v0.0.0-20240318140521-94a12d6c2237 // indirect
Expand Down
10 changes: 6 additions & 4 deletions modules/valkey/go.sum
Original file line number Diff line number Diff line change
Expand Up @@ -60,6 +60,8 @@ github.com/lufia/plan9stats v0.0.0-20240513124658-fba389f38bae h1:dIZY4ULFcto4tA
github.com/lufia/plan9stats v0.0.0-20240513124658-fba389f38bae/go.mod h1:ilwx/Dta8jXAgpFYFvSWEMwxmbWXyiUHkd5FwyKhb5k=
github.com/magiconair/properties v1.8.10 h1:s31yESBquKXCV9a/ScB3ESkOjUYYv+X0rg8SYxI99mE=
github.com/magiconair/properties v1.8.10/go.mod h1:Dhd985XPs7jluiymwWYZ0G4Z61jb3vdS329zhj2hYo0=
github.com/mdelapenya/tlscert v0.2.0 h1:7H81W6Z/4weDvZBNOfQte5GpIMo0lGYEeWbkGp5LJHI=
github.com/mdelapenya/tlscert v0.2.0/go.mod h1:O4njj3ELLnJjGdkN7M/vIVCpZ+Cf0L6muqOG4tLSl8o=
github.com/moby/docker-image-spec v1.3.1 h1:jMKff3w6PgbfSa69GfNg+zN/XLhfXJGnEx3Nl2EsFP0=
github.com/moby/docker-image-spec v1.3.1/go.mod h1:eKmb5VW8vQEh/BAr2yvVNvuiJuY6UIocYsFu/DxxRpo=
github.com/moby/patternmatcher v0.6.0 h1:GmP9lR19aU5GqSSFko+5pRqHi+Ohk1O69aFiKkVGiPk=
Expand All @@ -74,8 +76,8 @@ github.com/moby/term v0.5.0 h1:xt8Q1nalod/v7BqbG21f8mQPqH+xAaC9C3N3wfWbVP0=
github.com/moby/term v0.5.0/go.mod h1:8FzsFHVUBGZdbDsJw/ot+X+d5HLUbvklYLJ9uGfcI3Y=
github.com/morikuni/aec v1.0.0 h1:nP9CBfwrvYnBRgY6qfDQkygYDmYwOilePFkwzv4dU8A=
github.com/morikuni/aec v1.0.0/go.mod h1:BbKIizmSmc5MMPqRYbxO4ZU0S0+P200+tUnFx7PXmsc=
github.com/onsi/gomega v1.31.1 h1:KYppCUK+bUgAZwHOu7EXVBKyQA6ILvOESHkn/tgoqvo=
github.com/onsi/gomega v1.31.1/go.mod h1:y40C95dwAD1Nz36SsEnxvfFe8FFfNxzI5eJ0EYGyAy0=
github.com/onsi/gomega v1.36.2 h1:koNYke6TVk6ZmnyHrCXba/T/MoLBXFjeC1PtvYgw0A8=
github.com/onsi/gomega v1.36.2/go.mod h1:DdwyADRjrc825LhMEkD76cHR5+pUnjhUN8GlHlRPHzY=
github.com/opencontainers/go-digest v1.0.0 h1:apOUWs51W5PlhuyGyz9FCeeBIOUDA/6nW8Oi/yOhh5U=
github.com/opencontainers/go-digest v1.0.0/go.mod h1:0JzlMkj0TRzQZfJkVvzbP0HBR3IKzErnv2BNG4W4MAM=
github.com/opencontainers/image-spec v1.1.1 h1:y0fUlFfIZhPF1W537XOLg0/fcx6zcHCJwooC2xJA040=
Expand All @@ -102,8 +104,8 @@ github.com/tklauser/go-sysconf v0.3.14 h1:g5vzr9iPFFz24v2KZXs/pvpvh8/V9Fw6vQK5ZZ
github.com/tklauser/go-sysconf v0.3.14/go.mod h1:1ym4lWMLUOhuBOPGtRcJm7tEGX4SCYNEEEtghGG/8uY=
github.com/tklauser/numcpus v0.8.0 h1:Mx4Wwe/FjZLeQsK/6kt2EOepwwSl7SmJrK5bV/dXYgY=
github.com/tklauser/numcpus v0.8.0/go.mod h1:ZJZlAY+dmR4eut8epnzf0u/VwodKmryxR8txiloSqBE=
github.com/valkey-io/valkey-go v1.0.41 h1:pWgh9MP24Vl0ANZ0KxEMwB/LHvTUKwlm2SPuWIrSlFw=
github.com/valkey-io/valkey-go v1.0.41/go.mod h1:LXqAbjygRuA1YRocojTslAGx2dQB4p8feaseGviWka4=
github.com/valkey-io/valkey-go v1.0.59 h1:W67Z0UY+Qqk3k8NKkFCFlM3X4yQUniixl7dSJAch2Qo=
github.com/valkey-io/valkey-go v1.0.59/go.mod h1:bHmwjIEOrGq/ubOJfh5uMRs7Xj6mV3mQ/ZXUbmqpjqY=
github.com/yuin/goldmark v1.1.27/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74=
github.com/yuin/goldmark v1.2.1/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74=
github.com/yusufpapurcu/wmi v1.2.4 h1:zFUKzehAFReQwLys1b/iSMl+JQGSCSjtVqQn9bBrPo0=
Expand Down
82 changes: 82 additions & 0 deletions modules/valkey/options.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,82 @@
package valkey

import (
"crypto/tls"
"fmt"
"net"

"github.com/mdelapenya/tlscert"

"github.com/testcontainers/testcontainers-go"
)

type options struct {
tlsEnabled bool
tlsConfig *tls.Config
}

// Compiler check to ensure that Option implements the testcontainers.ContainerCustomizer interface.
var _ testcontainers.ContainerCustomizer = (Option)(nil)

// Option is an option for the Redpanda container.
type Option func(*options) error

// Customize is a NOOP. It's defined to satisfy the testcontainers.ContainerCustomizer interface.
func (o Option) Customize(*testcontainers.GenericContainerRequest) error {
// NOOP to satisfy interface.
return nil
}
Comment on lines +24 to +28
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

suggestion: not needed for this PR but as this is a very common pattern we should create a noop customiser we can just embed to avoid this repeating boilerplate.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Makes sense! Thanks for the suggestion!


// WithTLS sets the TLS configuration for the redis container, setting
// the 6380/tcp port to listen on for TLS connections and using a secure URL (rediss://).
func WithTLS() Option {
return func(o *options) error {
o.tlsEnabled = true
return nil
}
}

// createTLSCerts creates a CA certificate, a client certificate and a Valkey certificate.
func createTLSCerts() (caCert *tlscert.Certificate, clientCert *tlscert.Certificate, valkeyCert *tlscert.Certificate, err error) {
// ips is the extra list of IPs to include in the certificates.
// It's used to allow the client and Valkey certificates to be used in the same host
// when the tests are run using a remote docker daemon.
ips := []net.IP{net.ParseIP("127.0.0.1")}

// Generate CA certificate
caCert, err = tlscert.SelfSignedFromRequestE(tlscert.Request{
Host: "localhost",
IPAddresses: ips,
Name: "ca",
SubjectCommonName: "ca",
IsCA: true,
})
if err != nil {
return nil, nil, nil, fmt.Errorf("generate CA certificate: %w", err)
}

// Generate client certificate
clientCert, err = tlscert.SelfSignedFromRequestE(tlscert.Request{
Host: "localhost",
Name: "Redis Client",
SubjectCommonName: "localhost",
IPAddresses: ips,
Parent: caCert,
})
if err != nil {
return nil, nil, nil, fmt.Errorf("generate client certificate: %w", err)
}

// Generate Valkey certificate
valkeyCert, err = tlscert.SelfSignedFromRequestE(tlscert.Request{
Host: "localhost",
IPAddresses: ips,
Name: "Valkey Server",
Parent: caCert,
})
if err != nil {
return nil, nil, nil, fmt.Errorf("generate Valkey certificate: %w", err)
}

return caCert, clientCert, valkeyCert, nil
}
Loading
Loading