Skip to content

Commit da9df18

Browse files
mdelapenyaclaude
andauthored
chore(rabbitmq)!: use Run function (#3428)
* chore(rabbitmq): use Run function 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude <[email protected]> * fix(rabbitmq): use custom credentials and return errors from options - Use settings.AdminUsername and settings.AdminPassword instead of hardcoded defaults - Change Option type to return error for proper error handling - Update for-loop to check and return errors when applying options - Fixes credential handling bug identified in code review 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude <[email protected]> --------- Co-authored-by: Claude <[email protected]>
1 parent 1d901d9 commit da9df18

File tree

3 files changed

+88
-68
lines changed

3 files changed

+88
-68
lines changed

modules/rabbitmq/options.go

Lines changed: 7 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -41,7 +41,7 @@ type SSLSettings struct {
4141
var _ testcontainers.ContainerCustomizer = (*Option)(nil)
4242

4343
// Option is an option for the RabbitMQ container.
44-
type Option func(*options)
44+
type Option func(*options) error
4545

4646
// Customize is a NOOP. It's defined to satisfy the testcontainers.ContainerCustomizer interface.
4747
func (o Option) Customize(*testcontainers.GenericContainerRequest) error {
@@ -51,21 +51,24 @@ func (o Option) Customize(*testcontainers.GenericContainerRequest) error {
5151

5252
// WithAdminPassword sets the password for the default admin user
5353
func WithAdminPassword(password string) Option {
54-
return func(o *options) {
54+
return func(o *options) error {
5555
o.AdminPassword = password
56+
return nil
5657
}
5758
}
5859

5960
// WithAdminUsername sets the default admin username
6061
func WithAdminUsername(username string) Option {
61-
return func(o *options) {
62+
return func(o *options) error {
6263
o.AdminUsername = username
64+
return nil
6365
}
6466
}
6567

6668
// WithSSL enables SSL on the RabbitMQ container, configuring the Erlang config file with the provided settings.
6769
func WithSSL(settings SSLSettings) Option {
68-
return func(o *options) {
70+
return func(o *options) error {
6971
o.SSLSettings = &settings
72+
return nil
7073
}
7174
}

modules/rabbitmq/rabbitmq.go

Lines changed: 51 additions & 64 deletions
Original file line numberDiff line numberDiff line change
@@ -80,45 +80,13 @@ func RunContainer(ctx context.Context, opts ...testcontainers.ContainerCustomize
8080

8181
// Run creates an instance of the RabbitMQ container type
8282
func Run(ctx context.Context, img string, opts ...testcontainers.ContainerCustomizer) (*RabbitMQContainer, error) {
83-
req := testcontainers.ContainerRequest{
84-
Image: img,
85-
Env: map[string]string{
86-
"RABBITMQ_DEFAULT_USER": defaultUser,
87-
"RABBITMQ_DEFAULT_PASS": defaultPassword,
88-
},
89-
ExposedPorts: []string{
90-
DefaultAMQPPort,
91-
DefaultAMQPSPort,
92-
DefaultHTTPSPort,
93-
DefaultHTTPPort,
94-
},
95-
WaitingFor: wait.ForLog(".*Server startup complete.*").AsRegexp().WithStartupTimeout(60 * time.Second),
96-
LifecycleHooks: []testcontainers.ContainerLifecycleHooks{
97-
{
98-
PostStarts: []testcontainers.ContainerHook{},
99-
},
100-
},
101-
}
102-
103-
genericContainerReq := testcontainers.GenericContainerRequest{
104-
ContainerRequest: req,
105-
Started: true,
106-
}
107-
10883
// Gather all config options (defaults and then apply provided options)
10984
settings := defaultOptions()
11085
for _, opt := range opts {
11186
if apply, ok := opt.(Option); ok {
112-
apply(&settings)
113-
}
114-
if err := opt.Customize(&genericContainerReq); err != nil {
115-
return nil, err
116-
}
117-
}
118-
119-
if settings.SSLSettings != nil {
120-
if err := applySSLSettings(settings.SSLSettings)(&genericContainerReq); err != nil {
121-
return nil, err
87+
if err := apply(&settings); err != nil {
88+
return nil, fmt.Errorf("apply option: %w", err)
89+
}
12290
}
12391
}
12492

@@ -133,38 +101,55 @@ func Run(ctx context.Context, img string, opts ...testcontainers.ContainerCustom
133101
return nil, err
134102
}
135103

136-
if err := withConfig(tmpConfigFile)(&genericContainerReq); err != nil {
137-
return nil, err
104+
moduleOpts := []testcontainers.ContainerCustomizer{
105+
testcontainers.WithEnv(map[string]string{
106+
"RABBITMQ_DEFAULT_USER": settings.AdminUsername,
107+
"RABBITMQ_DEFAULT_PASS": settings.AdminPassword,
108+
}),
109+
testcontainers.WithExposedPorts(
110+
DefaultAMQPPort,
111+
DefaultAMQPSPort,
112+
DefaultHTTPSPort,
113+
DefaultHTTPPort,
114+
),
115+
testcontainers.WithWaitStrategy(wait.ForLog(".*Server startup complete.*").AsRegexp().WithStartupTimeout(60 * time.Second)),
116+
withConfig(tmpConfigFile),
138117
}
139118

140-
container, err := testcontainers.GenericContainer(ctx, genericContainerReq)
119+
if settings.SSLSettings != nil {
120+
moduleOpts = append(moduleOpts, applySSLSettings(settings.SSLSettings))
121+
}
122+
123+
moduleOpts = append(moduleOpts, opts...)
124+
125+
ctr, err := testcontainers.Run(ctx, img, moduleOpts...)
141126
var c *RabbitMQContainer
142-
if container != nil {
127+
if ctr != nil {
143128
c = &RabbitMQContainer{
144-
Container: container,
129+
Container: ctr,
145130
AdminUsername: settings.AdminUsername,
146131
AdminPassword: settings.AdminPassword,
147132
}
148133
}
149134

150135
if err != nil {
151-
return c, fmt.Errorf("generic container: %w", err)
136+
return c, fmt.Errorf("run rabbitmq: %w", err)
152137
}
153138

154139
return c, nil
155140
}
156141

157142
func withConfig(hostPath string) testcontainers.CustomizeRequestOption {
158143
return func(req *testcontainers.GenericContainerRequest) error {
159-
req.Env["RABBITMQ_CONFIG_FILE"] = defaultCustomConfPath
144+
if err := testcontainers.WithEnv(map[string]string{"RABBITMQ_CONFIG_FILE": defaultCustomConfPath})(req); err != nil {
145+
return err
146+
}
160147

161-
req.Files = append(req.Files, testcontainers.ContainerFile{
148+
return testcontainers.WithFiles(testcontainers.ContainerFile{
162149
HostFilePath: hostPath,
163150
ContainerFilePath: defaultCustomConfPath,
164151
FileMode: 0o644,
165-
})
166-
167-
return nil
152+
})(req)
168153
}
169154
}
170155

@@ -177,27 +162,29 @@ func applySSLSettings(sslSettings *SSLSettings) testcontainers.CustomizeRequestO
177162
const defaultPermission = 0o644
178163

179164
return func(req *testcontainers.GenericContainerRequest) error {
180-
req.Files = append(req.Files, testcontainers.ContainerFile{
181-
HostFilePath: sslSettings.CACertFile,
182-
ContainerFilePath: rabbitCaCertPath,
183-
FileMode: defaultPermission,
184-
})
185-
req.Files = append(req.Files, testcontainers.ContainerFile{
186-
HostFilePath: sslSettings.CertFile,
187-
ContainerFilePath: rabbitCertPath,
188-
FileMode: defaultPermission,
189-
})
190-
req.Files = append(req.Files, testcontainers.ContainerFile{
191-
HostFilePath: sslSettings.KeyFile,
192-
ContainerFilePath: rabbitKeyPath,
193-
FileMode: defaultPermission,
194-
})
165+
if err := testcontainers.WithFiles(
166+
testcontainers.ContainerFile{
167+
HostFilePath: sslSettings.CACertFile,
168+
ContainerFilePath: rabbitCaCertPath,
169+
FileMode: defaultPermission,
170+
},
171+
testcontainers.ContainerFile{
172+
HostFilePath: sslSettings.CertFile,
173+
ContainerFilePath: rabbitCertPath,
174+
FileMode: defaultPermission,
175+
},
176+
testcontainers.ContainerFile{
177+
HostFilePath: sslSettings.KeyFile,
178+
ContainerFilePath: rabbitKeyPath,
179+
FileMode: defaultPermission,
180+
},
181+
)(req); err != nil {
182+
return err
183+
}
195184

196185
// To verify that TLS has been enabled on the node, container logs should contain an entry about a TLS listener being enabled
197186
// See https://www.rabbitmq.com/ssl.html#enabling-tls-verify-configuration
198-
req.WaitingFor = wait.ForAll(req.WaitingFor, wait.ForLog("started TLS (SSL) listener on [::]:5671"))
199-
200-
return nil
187+
return testcontainers.WithAdditionalWaitStrategy(wait.ForLog("started TLS (SSL) listener on [::]:5671"))(req)
201188
}
202189
}
203190

modules/rabbitmq/rabbitmq_test.go

Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -258,3 +258,33 @@ func requirePluginIsEnabled(t *testing.T, container testcontainers.Container, pl
258258
require.Contains(t, check, plugin+" is enabled")
259259
}
260260
}
261+
262+
func TestRunContainer_withCustomCredentials(t *testing.T) {
263+
ctx := context.Background()
264+
265+
customUsername := "admin"
266+
customPassword := "s3cr3t"
267+
268+
rabbitmqContainer, err := rabbitmq.Run(ctx,
269+
"rabbitmq:3.12.11-management-alpine",
270+
rabbitmq.WithAdminUsername(customUsername),
271+
rabbitmq.WithAdminPassword(customPassword),
272+
)
273+
testcontainers.CleanupContainer(t, rabbitmqContainer)
274+
require.NoError(t, err)
275+
276+
// Verify the container reports the custom credentials
277+
require.Equal(t, customUsername, rabbitmqContainer.AdminUsername)
278+
require.Equal(t, customPassword, rabbitmqContainer.AdminPassword)
279+
280+
// Get the AMQP URL - this will include the custom credentials
281+
amqpURL, err := rabbitmqContainer.AmqpURL(ctx)
282+
require.NoError(t, err)
283+
require.Contains(t, amqpURL, customUsername)
284+
require.Contains(t, amqpURL, customPassword)
285+
286+
// Try to connect using the URL with custom credentials
287+
amqpConnection, err := amqp.Dial(amqpURL)
288+
require.NoError(t, err)
289+
require.NoError(t, amqpConnection.Close())
290+
}

0 commit comments

Comments
 (0)