solace

package module
v0.43.0 Latest Latest
Warning

This package is not in the latest version of its module.

Go to latest
Published: Jun 19, 2026 License: MIT Imports: 12 Imported by: 0

Documentation

Index

Examples

Constants

This section is empty.

Variables

View Source
var (
	ServiceAMQP = Service{
		Name:       "amqp",
		Port:       5672,
		Protocol:   "amqp",
		SupportSSL: false,
	}
	ServiceMQTT = Service{
		Name:       "mqtt",
		Port:       1883,
		Protocol:   "tcp",
		SupportSSL: false,
	}
	ServiceREST = Service{
		Name:       "rest",
		Port:       9000,
		Protocol:   "http",
		SupportSSL: false,
	}
	ServiceManagement = Service{
		Name:       "management",
		Port:       8080,
		Protocol:   "http",
		SupportSSL: false,
	}
	ServiceSMF = Service{
		Name:       "smf",
		Port:       55555,
		Protocol:   "tcp",
		SupportSSL: true,
	}
	ServiceSMFSSL = Service{
		Name:       "smf",
		Port:       55443,
		Protocol:   "tcps",
		SupportSSL: true,
	}
)

Functions

This section is empty.

Types

type Container

type Container struct {
	testcontainers.Container
	// contains filtered or unexported fields
}

Container represents a Solace container with additional settings

func Run

Run starts a Solace container with the provided image and options

The container requires specific ulimits to start successfully:

  • nofile: 1048576 (number of open files)
  • core: -1 (for core dumps)
  • memlock: -1 (for memory locking)

See https://docs.solace.com for more information. - https://docs.solace.com/Software-Broker/Managing-Core-Files.htm - https://docs.solace.com/Software-Broker/Container-Tasks/Config-Container-Storage.htm?Highlight=ulimit

Example
package main

import (
	"context"
	"fmt"
	"log"

	"github.com/testcontainers/testcontainers-go"
	sc "github.com/testcontainers/testcontainers-go/modules/solace"
)

func main() {
	// runSolaceContainer {
	ctx := context.Background()
	ctr, err := sc.Run(ctx, "solace/solace-pubsub-standard:latest",
		sc.WithCredentials("admin", "admin"),
		sc.WithServices(sc.ServiceAMQP, sc.ServiceManagement),
		testcontainers.WithEnv(map[string]string{
			"username_admin_globalaccesslevel": "admin",
			"username_admin_password":          "admin",
		}),
		sc.WithShmSize(1<<30),
	)
	defer func() {
		if err := testcontainers.TerminateContainer(ctr); err != nil {
			log.Printf("failed to terminate container: %s", err)
		}
	}()
	fmt.Println(err)
	// }

}
Output:
<nil>
Example (WithTopicAndQueue)
package main

import (
	"context"
	"fmt"
	"log"
	"time"

	"solace.dev/go/messaging"
	"solace.dev/go/messaging/pkg/solace"
	"solace.dev/go/messaging/pkg/solace/config"
	"solace.dev/go/messaging/pkg/solace/message"
	"solace.dev/go/messaging/pkg/solace/resource"

	"github.com/testcontainers/testcontainers-go"
	sc "github.com/testcontainers/testcontainers-go/modules/solace"
)

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

	ctr, err := sc.Run(ctx, "solace/solace-pubsub-standard:latest",
		sc.WithCredentials("admin", "admin"),
		sc.WithVPN("test-vpn"),
		sc.WithServices(sc.ServiceAMQP, sc.ServiceManagement, sc.ServiceSMF),
		testcontainers.WithEnv(map[string]string{
			"username_admin_globalaccesslevel": "admin",
			"username_admin_password":          "admin",
		}),
		sc.WithShmSize(1<<30),
		sc.WithQueue("TestQueue", "Topic/MyTopic"),
	)
	defer func() {
		if err := testcontainers.TerminateContainer(ctr); err != nil {
			log.Printf("failed to terminate container: %s", err)
		}
	}()
	fmt.Println(err)
	// the [testMessagePublishAndConsume] function is responsible for printing the output
	// to the console, so be aware of that when adding it to other examples.
	err = testMessagePublishAndConsume(ctr, "TestQueue", "Topic/MyTopic")
	fmt.Println(err)

}

func testMessagePublishAndConsume(ctr *sc.Container, queueName, topicName string) error {

	smfURL, err := ctr.BrokerURLFor(context.Background(), sc.ServiceSMF)
	if err != nil {
		return fmt.Errorf("failed to get SMF URL: %w", err)
	}

	brokerConfig := config.ServicePropertyMap{
		config.TransportLayerPropertyHost:                 smfURL,
		config.ServicePropertyVPNName:                     ctr.VPN(),
		config.AuthenticationPropertyScheme:               config.AuthenticationSchemeBasic,
		config.AuthenticationPropertySchemeBasicUserName:  ctr.Username(),
		config.AuthenticationPropertySchemeBasicPassword:  ctr.Password(),
		config.TransportLayerPropertyReconnectionAttempts: 0,
	}

	messagingService, err := messaging.NewMessagingServiceBuilder().
		FromConfigurationProvider(brokerConfig).
		Build()
	if err != nil {
		return fmt.Errorf("failed to build messaging service: %w", err)
	}

	if err := messagingService.Connect(); err != nil {
		return fmt.Errorf("failed to connect to messaging service: %w", err)
	}
	defer func() {
		if err := messagingService.Disconnect(); err != nil {
			log.Printf("Error disconnecting from messaging service: %v", err)
		}
	}()

	err = publishTestMessage(messagingService, topicName)
	if err != nil {
		return fmt.Errorf("failed to publish message: %w", err)
	}

	err = consumeTestMessage(messagingService, queueName)
	if err != nil {
		return fmt.Errorf("failed to consume message: %w", err)
	}

	return nil
}

func publishTestMessage(messagingService solace.MessagingService, topicName string) error {

	directPublisher, err := messagingService.CreateDirectMessagePublisherBuilder().Build()
	if err != nil {
		return fmt.Errorf("failed to build publisher: %w", err)
	}

	if err := directPublisher.Start(); err != nil {
		return fmt.Errorf("failed to start publisher: %w", err)
	}
	defer func() {
		if err := directPublisher.Terminate(1 * time.Second); err != nil {
			log.Printf("Error terminating direct publisher: %v", err)
		}
	}()

	messageBuilder := messagingService.MessageBuilder()
	message, err := messageBuilder.
		WithProperty("custom-property", "test-value").
		BuildWithStringPayload("Hello from Solace testcontainers!")
	if err != nil {
		return fmt.Errorf("failed to build message: %w", err)
	}

	topic := resource.TopicOf(topicName)

	if err := directPublisher.Publish(message, topic); err != nil {
		return fmt.Errorf("failed to publish message: %w", err)
	}

	fmt.Printf("Published message to topic: %s\n", topicName)
	return nil
}

func consumeTestMessage(messagingService solace.MessagingService, queueName string) error {

	persistentReceiver, err := messagingService.CreatePersistentMessageReceiverBuilder().
		Build(resource.QueueDurableExclusive(queueName))
	if err != nil {
		return fmt.Errorf("failed to build receiver: %w", err)
	}

	messageReceived := make(chan message.InboundMessage, 1)
	errorChan := make(chan error, 1)

	messageHandler := func(msg message.InboundMessage) {
		payload, ok := msg.GetPayloadAsString()
		if ok {
			fmt.Printf("Received message: %s\n", payload)
		}
		messageReceived <- msg
	}

	if err := persistentReceiver.Start(); err != nil {
		return fmt.Errorf("failed to start receiver: %w", err)
	}
	defer func() {
		if err := persistentReceiver.Terminate(1 * time.Second); err != nil {
			log.Printf("Error terminating persistent receiver: %v", err)
		}
	}()

	if err := persistentReceiver.ReceiveAsync(messageHandler); err != nil {
		return fmt.Errorf("failed to start async receive: %w", err)
	}

	select {
	case <-messageReceived:
		fmt.Printf("Successfully received message from queue: %s\n", queueName)

		return nil
	case err := <-errorChan:
		return fmt.Errorf("error receiving message: %w", err)
	case <-time.After(15 * time.Second):
		return fmt.Errorf("timeout waiting for message from queue: %s", queueName)
	}
}
Output:
<nil>
Published message to topic: Topic/MyTopic
Received message: Hello from Solace testcontainers!
Successfully received message from queue: TestQueue
<nil>

func (*Container) BrokerURLFor

func (c *Container) BrokerURLFor(ctx context.Context, service Service) (string, error)

BrokerURLFor returns the origin URL for a given service

func (*Container) Password

func (c *Container) Password() string

Password returns the password configured for the Solace container

func (*Container) Username

func (c *Container) Username() string

Username returns the username configured for the Solace container

func (*Container) VPN

func (c *Container) VPN() string

VPN returns the VPN name configured for the Solace container

type Option

type Option func(*options) error

Option is an option for the Solace container.

func WithCredentials

func WithCredentials(username, password string) Option

WithCredentials sets the client credentials (username, password)

func WithQueue

func WithQueue(queueName, topic string) Option

WithQueue subscribes a given topic to a queue (for SMF/AMQP testing)

func WithServices

func WithServices(srv ...Service) Option

WithServices configures the services to be exposed with their wait strategies

func WithShmSize

func WithShmSize(size int64) Option

WithShmSize sets the size of the /dev/shm volume

func WithVPN

func WithVPN(vpn string) Option

WithVPN sets the VPN name

func (Option) Customize

Customize is a NOOP. It's defined to satisfy the testcontainers.ContainerCustomizer interface.

type Service

type Service struct {
	Name       string
	Port       int
	Protocol   string
	SupportSSL bool
}

Service represents a Solace service with its name, port, protocol, and SSL support.

Jump to

Keyboard shortcuts

? : This menu
/ : Search site
f or F : Jump to
y or Y : Canonical URL