Skip to content

Commit

Permalink
feat(modules): add artemis container
Browse files Browse the repository at this point in the history
  • Loading branch information
abemedia committed Aug 5, 2023
1 parent 5011016 commit 009473c
Show file tree
Hide file tree
Showing 10 changed files with 602 additions and 1 deletion.
6 changes: 6 additions & 0 deletions .github/dependabot.yml
Original file line number Diff line number Diff line change
Expand Up @@ -84,6 +84,12 @@ updates:
interval: monthly
open-pull-requests-limit: 3
rebase-strategy: disabled
- package-ecosystem: gomod
directory: /modules/artemis
schedule:
interval: monthly
open-pull-requests-limit: 3
rebase-strategy: disabled
- package-ecosystem: gomod
directory: /modules/compose
schedule:
Expand Down
2 changes: 1 addition & 1 deletion .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -82,7 +82,7 @@ jobs:
matrix:
go-version: [1.19.x, 1.x]
platform: [ubuntu-latest, macos-latest]
module: [compose, couchbase, k3s, localstack, mysql, neo4j, postgres, pulsar, redis, redpanda, vault]
module: [artemis, compose, couchbase, k3s, localstack, mysql, neo4j, postgres, pulsar, redis, redpanda, vault]
exclude:
- module: compose
go-version: 1.19.x
Expand Down
68 changes: 68 additions & 0 deletions docs/modules/artemis.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,68 @@
# Apache ActiveMQ Artemis

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>

## Introduction

The Testcontainers module for Artemis.

## Adding this module to your project dependencies

Please run the following command to add the Artemis module to your Go dependencies:

```
go get github.com/testcontainers/testcontainers-go/modules/artemis
```

## Usage example

<!--codeinclude-->
[Creating an Artemis container](../../modules/artemis/example_test.go) inside_block:runContainer
<!--/codeinclude-->

## Module reference

The Artemis module exposes one entrypoint function to create the Artemis container, and this function receives two parameters:

```golang
func RunContainer(ctx context.Context, opts ...testcontainers.ContainerCustomizer) (*ArtemisContainer, error)
```

- `context.Context`, the Go context.
- `testcontainers.ContainerCustomizer`, a variadic argument for passing options.

### Container Options

When starting the Artemis container, you can pass options in a variadic way to configure it.

#### Image

If you need to set a different Artemis Docker image, you can use `testcontainers.WithImage` with a valid Docker image
for Artemis. E.g. `testcontainers.WithImage("docker.io/apache/activemq-artemis:2.30.0")`.

#### Wait Strategies

If you need to set a different wait strategy for Artemis, you can use `testcontainers.WithWaitStrategy` with a valid wait strategy
for Artemis.

!!!info
The default deadline for the wait strategy is 60 seconds.

At the same time, it's possible to set a wait strategy and a custom deadline with `testcontainers.WithWaitStrategyAndDeadline`.

#### Docker type modifiers

If you need an advanced configuration for Artemis, you can leverage the following Docker type modifiers:

- `testcontainers.WithConfigModifier`
- `testcontainers.WithHostConfigModifier`
- `testcontainers.WithEndpointSettingsModifier`

Please read the [Create containers: Advanced Settings](../features/creating_container.md#advanced-settings) documentation for more information.

### Container Methods

The Artemis container exposes the following methods:



1 change: 1 addition & 0 deletions mkdocs.yml
Original file line number Diff line number Diff line change
Expand Up @@ -59,6 +59,7 @@ nav:
- SQL: features/wait/sql.md
- Modules:
- modules/index.md
- modules/artemis.md
- modules/couchbase.md
- modules/k3s.md
- modules/localstack.md
Expand Down
5 changes: 5 additions & 0 deletions modules/artemis/Makefile
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
include ../../commons-test.mk

.PHONY: test
test:
$(MAKE) test-artemis
107 changes: 107 additions & 0 deletions modules/artemis/artemis.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,107 @@
package artemis

import (
"context"
"fmt"

"github.com/docker/go-connections/nat"
"github.com/testcontainers/testcontainers-go"
"github.com/testcontainers/testcontainers-go/wait"
)

const (
defaultBrokerPort = "61616/tcp"
defaultHTTPPort = "8161/tcp"
)

// ArtemisContainer represents the Artemis container type used in the module.
type ArtemisContainer struct {
testcontainers.Container
user string
password string
}

// Username returns the administrator username.
func (c *ArtemisContainer) User() string {
return c.user
}

// Password returns the administrator password.
func (c *ArtemisContainer) Password() string {
return c.password
}

// BrokerEndpoint returns the host:port for the combined protocols endpoint.
// The endpoint accepts CORE, MQTT, AMQP, STOMP, HORNETQ and OPENWIRE protocols.
func (c *ArtemisContainer) BrokerEndpoint(ctx context.Context) (string, error) {
return c.PortEndpoint(ctx, nat.Port(defaultBrokerPort), "")
}

// ConsoleURL returns the URL for the management console.
func (c *ArtemisContainer) ConsoleURL(ctx context.Context) (string, error) {
host, err := c.PortEndpoint(ctx, nat.Port(defaultHTTPPort), "")
if err != nil {
return "", err
}
return fmt.Sprintf("http://%s:%s@%s/console", c.user, c.password, host), nil
}

// WithCredentials sets the administrator credentials. The default is artemis:artemis.
func WithCredentials(user, password string) testcontainers.CustomizeRequestOption {
return func(req *testcontainers.GenericContainerRequest) {
req.Env["ARTEMIS_USER"] = user
req.Env["ARTEMIS_PASSWORD"] = password
}
}

// WithAnonymousLogin enables anonymous logins.
func WithAnonymousLogin() testcontainers.CustomizeRequestOption {
return func(req *testcontainers.GenericContainerRequest) {
req.Env["ANONYMOUS_LOGIN"] = "true"
}
}

// Additional arguments sent to the `artemis create“ command.
// The default is `--http-host 0.0.0.0 --relax-jolokia`.
// Setting this value will override the default.
// See the documentation on `artemis create` for available options.
func WithExtraArgs(args string) testcontainers.CustomizeRequestOption {
return func(req *testcontainers.GenericContainerRequest) {
req.Env["EXTRA_ARGS"] = args
}
}

// RunContainer creates an instance of the Artemis container type.
func RunContainer(ctx context.Context, opts ...testcontainers.ContainerCustomizer) (*ArtemisContainer, error) {
req := testcontainers.ContainerRequest{
Image: "docker.io/apache/activemq-artemis:2.30.0-alpine",
Env: map[string]string{
"ARTEMIS_USER": "artemis",
"ARTEMIS_PASSWORD": "artemis",
},
ExposedPorts: []string{defaultBrokerPort, defaultHTTPPort},
WaitingFor: wait.ForAll(
wait.ForLog("Server is now live"),
wait.ForLog("REST API available"),
),
}

genericContainerReq := testcontainers.GenericContainerRequest{
ContainerRequest: req,
Started: true,
}

for _, opt := range opts {
opt.Customize(&genericContainerReq)
}

container, err := testcontainers.GenericContainer(ctx, genericContainerReq)
if err != nil {
return nil, err
}

user := req.Env["ARTEMIS_USER"]
password := req.Env["ARTEMIS_PASSWORD"]

return &ArtemisContainer{Container: container, user: user, password: password}, nil
}
159 changes: 159 additions & 0 deletions modules/artemis/artemis_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,159 @@
package artemis_test

import (
"context"
"encoding/json"
"net/http"
"testing"
"time"

"github.com/go-stomp/stomp/v3"
"github.com/testcontainers/testcontainers-go"
"github.com/testcontainers/testcontainers-go/modules/artemis"
)

func TestArtemis(t *testing.T) {
ctx := context.Background()

tests := []struct {
name string
opts []testcontainers.ContainerCustomizer
user, pass string
hook func(*testing.T, *artemis.ArtemisContainer)
}{
{
name: "Default",
user: "artemis",
pass: "artemis",
},
{
name: "WithCredentials",
opts: []testcontainers.ContainerCustomizer{
artemis.WithCredentials("test", "test"),
},
user: "test",
pass: "test",
},
{
name: "WithAnonymous",
opts: []testcontainers.ContainerCustomizer{
artemis.WithAnonymousLogin(),
},
},
{
name: "WithExtraArgs",
opts: []testcontainers.ContainerCustomizer{
artemis.WithExtraArgs("--http-host 0.0.0.0 --relax-jolokia --queues ArgsTestQueue"),
},
user: "artemis",
pass: "artemis",
hook: func(t *testing.T, container *artemis.ArtemisContainer) {
expectQueue(t, container, "ArgsTestQueue")
},
},
}

for _, test := range tests {
t.Run(test.name, func(t *testing.T) {
container, err := artemis.RunContainer(ctx, test.opts...)
if err != nil {
t.Fatal(err)
}
t.Cleanup(func() {
if err := container.Terminate(ctx); err != nil {
t.Fatalf("failed to terminate container: %s", err)
}
})

u, err := container.ConsoleURL(ctx)
if err != nil {
t.Fatal(err)
}

res, err := http.Get(u)
if err != nil {
t.Fatal(err)
}
res.Body.Close()

if res.StatusCode != http.StatusOK {
t.Error("failed to access console")
}

if test.user != "" && container.User() != test.user {
t.Fatal("unexpected user")
}

if test.pass != "" && container.Password() != test.pass {
t.Fatal("unexpected password")
}

host, err := container.BrokerEndpoint(ctx)
if err != nil {
t.Fatal(err)
}

var opt []func(*stomp.Conn) error
if test.user != "" || test.pass != "" {
opt = append(opt, stomp.ConnOpt.Login(test.user, test.pass))
}

conn, err := stomp.Dial("tcp", host, opt...)
if err != nil {
t.Fatal(err)
}
t.Cleanup(func() { conn.Disconnect() })

sub, err := conn.Subscribe("test", stomp.AckAuto)
if err != nil {
t.Fatal(err)
}
t.Cleanup(func() { sub.Unsubscribe() })

err = conn.Send("test", "", []byte("test"))
if err != nil {
t.Fatal(err)
}

ticker := time.NewTicker(10 * time.Second)
select {
case <-ticker.C:
t.Fatal("timed out waiting for message")
case msg := <-sub.C:
if string(msg.Body) != "test" {
t.Fatal("received unexpected message bytes")
}
}

if test.hook != nil {
test.hook(t, container)
}
})
}
}

func expectQueue(t *testing.T, container *artemis.ArtemisContainer, queueName string) {
u, err := container.ConsoleURL(context.Background())
if err != nil {
t.Fatal(err)
}

r, err := http.Get(u + `/jolokia/read/org.apache.activemq.artemis:broker="0.0.0.0"/QueueNames`)
if err != nil {
t.Fatal(err)
}
defer r.Body.Close()

var res struct{ Value []string }
if err = json.NewDecoder(r.Body).Decode(&res); err != nil {
t.Fatal(err)
}

for _, v := range res.Value {
if v == queueName {
return
}
}

t.Fatalf("should contain queue %q", queueName)
}

0 comments on commit 009473c

Please sign in to comment.