From 0bbab5edc0f6a5b2b2adba16085e92ee6f6ab172 Mon Sep 17 00:00:00 2001 From: Michal Vyskocil Date: Thu, 5 Dec 2024 13:43:59 +0100 Subject: [PATCH] feat(nats): WithConfigFile - pass a configuration file to nats server (#2905) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * feat: WithConfigFile - pass a configuration file to nats server * add a testcase with a incorrect token * use testcontainers.ContainerCustomizer as suggested during CR Add ConfigGile struct, which implements the interface. * drop a custom struct, return and implement an interface directly * docs: document nats.WithConfigFile * Update modules/nats/options.go Co-authored-by: Manuel de la Peña * Update docs/modules/nats.md Co-authored-by: Manuel de la Peña * Update docs/modules/nats.md Co-authored-by: Manuel de la Peña * remove document markers from a unit test * address CR comments * fix a comment --------- Co-authored-by: Manuel de la Peña --- docs/modules/nats.md | 11 ++++++++- modules/nats/nats_test.go | 47 +++++++++++++++++++++++++++++++++++++++ modules/nats/options.go | 22 +++++++++++++++++- 3 files changed, 78 insertions(+), 2 deletions(-) diff --git a/docs/modules/nats.md b/docs/modules/nats.md index 21419bdc76..8a47c962fd 100644 --- a/docs/modules/nats.md +++ b/docs/modules/nats.md @@ -75,6 +75,15 @@ These arguments are passed to the NATS server when it starts, as part of the com [Passing arguments](../../modules/nats/examples_test.go) inside_block:withArguments +#### Custom configuration file + +- Not available until the next release of testcontainers-go :material-tag: main + +It's possible to pass a custom config file to NATS container using `nats.WithConfigFile(strings.NewReader(config))`. The content of `io.Reader` is passed as a `-config /etc/nats.conf` arguments to an entrypoint. + +!!! note + Changing the connectivity (listen address or ports) can break the container setup. So configuration must be done with care. + ### Container Methods The NATS container exposes the following methods: @@ -102,4 +111,4 @@ Exactly like `ConnectionString`, but it panics if an error occurs, returning jus [NATS Cluster](../../modules/nats/examples_test.go) inside_block:cluster - \ No newline at end of file + diff --git a/modules/nats/nats_test.go b/modules/nats/nats_test.go index 660473c2e5..ad777c9556 100644 --- a/modules/nats/nats_test.go +++ b/modules/nats/nats_test.go @@ -1,8 +1,11 @@ package nats_test import ( + "bufio" "context" + "strings" "testing" + "time" "github.com/nats-io/nats.go" "github.com/stretchr/testify/require" @@ -57,3 +60,47 @@ func TestNATS(t *testing.T) { require.Equal(t, "hello", string(msg.Data)) } + +func TestNATSWithConfigFile(t *testing.T) { + const natsConf = ` +listen: 0.0.0.0:4222 +authorization { + token: "s3cr3t" +} +` + ctx := context.Background() + + ctr, err := tcnats.Run(ctx, "nats:2.9", tcnats.WithConfigFile(strings.NewReader(natsConf))) + testcontainers.CleanupContainer(t, ctr) + require.NoError(t, err) + + uri, err := ctr.ConnectionString(ctx) + require.NoError(t, err) + + // connect without a correct token must fail + mallory, err := nats.Connect(uri, nats.Name("Mallory"), nats.Token("secret")) + t.Cleanup(mallory.Close) + require.EqualError(t, err, "nats: Authorization Violation") + + // connect with a correct token must succeed + nc, err := nats.Connect(uri, nats.Name("API Token Test"), nats.Token("s3cr3t")) + t.Cleanup(nc.Close) + require.NoError(t, err) + + // validate /etc/nats.conf mentioned in logs + const expected = "Using configuration file: /etc/nats.conf" + logs, err := ctr.Logs(ctx) + require.NoError(t, err) + sc := bufio.NewScanner(logs) + found := false + time.AfterFunc(5*time.Second, func() { + require.Truef(t, found, "expected log line not found after 5 seconds: %s", expected) + }) + for sc.Scan() { + if strings.Contains(sc.Text(), expected) { + found = true + break + } + } + require.Truef(t, found, "expected log line not found: %s", expected) +} diff --git a/modules/nats/options.go b/modules/nats/options.go index 38856d68a9..60abd10056 100644 --- a/modules/nats/options.go +++ b/modules/nats/options.go @@ -1,6 +1,7 @@ package nats import ( + "io" "strings" "github.com/testcontainers/testcontainers-go" @@ -16,7 +17,7 @@ func defaultOptions() options { } } -// Compiler check to ensure that Option implements the testcontainers.ContainerCustomizer interface. +// Compiler check to ensure that CmdOption implements the testcontainers.ContainerCustomizer interface. var _ testcontainers.ContainerCustomizer = (*CmdOption)(nil) // CmdOption is an option for the NATS container. @@ -49,3 +50,22 @@ func WithArgument(flag string, value string) CmdOption { o.CmdArgs[flag] = value } } + +// WithConfigFile pass a content of io.Reader to the NATS container as /etc/nats.conf +// Changing the connectivity (listen address or ports) can break the container setup. +func WithConfigFile(config io.Reader) testcontainers.CustomizeRequestOption { + return func(req *testcontainers.GenericContainerRequest) error { + if config != nil { + req.Cmd = append(req.Cmd, "-config", "/etc/nats.conf") + req.Files = append( + req.Files, + testcontainers.ContainerFile{ + Reader: config, + ContainerFilePath: "/etc/nats.conf", + FileMode: 0o644, + }, + ) + } + return nil + } +}