Skip to content
Merged
Show file tree
Hide file tree
Changes from 11 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
2 changes: 1 addition & 1 deletion .vscode/.testcontainers-go.code-workspace
Original file line number Diff line number Diff line change
Expand Up @@ -262,4 +262,4 @@
"path": "../modulegen"
}
]
}
}
49 changes: 47 additions & 2 deletions docs/modules/azure.md
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,7 @@ The Azure module exposes the following Go packages:
- [ServiceBus](#servicebus): `github.com/testcontainers/testcontainers-go/modules/azure/servicebus`.
!!! warning "EULA Acceptance"
Due to licensing restrictions you are required to explicitly accept an End User License Agreement (EULA) for the EventHubs container image. This is facilitated through the `WithAcceptEULA` function.

- [CosmosDB](#cosmosdb): `github.com/testcontainers/testcontainers-go/modules/azure/cosmosdb`.
<!--codeinclude-->
[Creating a Azurite container](../../modules/azure/azurite/examples_test.go) inside_block:runAzuriteContainer
<!--/codeinclude-->
Expand Down Expand Up @@ -307,4 +307,49 @@ In the following example, inspired by the [Azure Event Hubs Go SDK](https://lear
[Create Client](../../modules/azure/servicebus/examples_test.go) inside_block:createClient
[Send messages to a Queue](../../modules/azure/servicebus/examples_test.go) inside_block:sendMessages
[Receive messages from a Queue](../../modules/azure/servicebus/examples_test.go) inside_block:receiveMessages
<!--/codeinclude-->
<!--/codeinclude-->

## CosmosDB

### Run function

- Not available until the next release <a href="https://github.com/testcontainers/testcontainers-go"><span class="tc-version">:material-tag: main</span></a>

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

```golang
func Run(ctx context.Context, img string, opts ...testcontainers.ContainerCustomizer) (*Container, error)
```

- `context.Context`, the Go context.
- `string`, the Docker image to use.
- `testcontainers.ContainerCustomizer`, a variadic argument for passing options.

#### Image

Use the second argument in the `Run` function to set a valid Docker image.
In example: `Run(context.Background(), "mcr.microsoft.com/cosmosdb/linux/azure-cosmos-emulator:vnext-preview")`.

### Container Options

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

{% include "../features/common_functional_options_list.md" %}

### Container Methods

The CosmosDB container exposes the following methods:

#### ConnectionString

- Not available until the next release <a href="https://github.com/testcontainers/testcontainers-go"><span class="tc-version">:material-tag: main</span></a>

Returns the connection string to connect to the CosmosDB container and an error, passing the Go context as parameter.

### Examples

#### Connect and Create database

<!--codeinclude-->
[Connect_CreateDatabase](../../modules/azure/cosmosdb/examples_test.go) inside_block:ExampleRun_connect
<!--/codeinclude-->
63 changes: 63 additions & 0 deletions modules/azure/cosmosdb/cosmosdb.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,63 @@
package cosmosdb

import (
"context"
"fmt"

"github.com/docker/go-connections/nat"

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

const (
defaultPort = "8081/tcp"
defaultProtocol = "http"
)

// Container represents the CosmosDB container type used in the module
type Container struct {
testcontainers.Container
}

// Run creates an instance of the CosmosDB container type
func Run(ctx context.Context, img string, opts ...testcontainers.ContainerCustomizer) (*Container, error) {
// Initialize with module defaults
moduleOpts := []testcontainers.ContainerCustomizer{
testcontainers.WithExposedPorts(defaultPort),
testcontainers.WithCmdArgs("--enable-explorer", "false"),
testcontainers.WithWaitStrategy(
wait.ForAll(wait.ForAll(
wait.ForLog("Started"),
wait.ForListeningPort(nat.Port(defaultPort))),
),
),
}

// Add user-provided options
moduleOpts = append(moduleOpts, opts...)

ctr, err := testcontainers.Run(ctx, img, moduleOpts...)
var c *Container
if ctr != nil {
c = &Container{Container: ctr}
}

if err != nil {
return c, fmt.Errorf("run cosmosdb: %w", err)
}

return c, nil
}

func (c *Container) ConnectionString(ctx context.Context) (string, error) {
endpoint, err := c.PortEndpoint(ctx, defaultPort, defaultProtocol)
if err != nil {
return "", fmt.Errorf("port endpoint: %w", err)
}

// Well-known key for Azure Cosmos DB Emulator
// See: https://learn.microsoft.com/azure/cosmos-db/emulator
const testAccKey = "C2y6yDjf5/R+ob0N8A7Cgv30VRDJIWEHLM+4QDU5DE2nQ9nDuVTqobD4b8mGGyPMbIZnqyMsEcaGQy67XIw/Jw=="
return fmt.Sprintf("AccountEndpoint=%s;AccountKey=%s;", endpoint, testAccKey), nil
}
77 changes: 77 additions & 0 deletions modules/azure/cosmosdb/cosmosdb_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,77 @@
package cosmosdb_test

import (
"context"
"encoding/json"
"testing"

"github.com/Azure/azure-sdk-for-go/sdk/data/azcosmos"
"github.com/stretchr/testify/require"

"github.com/testcontainers/testcontainers-go"
"github.com/testcontainers/testcontainers-go/modules/azure/cosmosdb"
)

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

ctr, err := cosmosdb.Run(ctx, "mcr.microsoft.com/cosmosdb/linux/azure-cosmos-emulator:vnext-preview")
testcontainers.CleanupContainer(t, ctr)
require.NoError(t, err)

// Create Azure Cosmos client
connStr, err := ctr.ConnectionString(ctx)
require.NoError(t, err)
require.NotNil(t, connStr)

p, err := cosmosdb.NewContainerPolicy(ctx, ctr)
require.NoError(t, err)

client, err := azcosmos.NewClientFromConnectionString(connStr, p.ClientOptions())
require.NoError(t, err)
require.NotNil(t, client)

// Create database
createDatabaseResp, err := client.CreateDatabase(ctx, azcosmos.DatabaseProperties{ID: "myDatabase"}, nil)
require.NoError(t, err)
require.NotNil(t, createDatabaseResp)

dbClient, err := client.NewDatabase("myDatabase")
require.NoError(t, err)
require.NotNil(t, dbClient)

// Create container
containerProps := azcosmos.ContainerProperties{
ID: "myContainer",
PartitionKeyDefinition: azcosmos.PartitionKeyDefinition{Paths: []string{"/category"}},
}
createContainerResp, err := dbClient.CreateContainer(ctx, containerProps, nil)
require.NoError(t, err)
require.NotNil(t, createContainerResp)
containerClient, err := dbClient.NewContainer("myContainer")
require.NoError(t, err)
require.NotNil(t, containerClient)

// Create item
type Product struct {
ID string `json:"id"`
Category string `json:"category"`
Name string `json:"name"`
}

testItem := Product{ID: "item123", Category: "gear-surf-surfboards", Name: "Yamba Surfboard"}

pk := azcosmos.NewPartitionKeyString(testItem.Category)

jsonItem, err := json.Marshal(testItem)
require.NoError(t, err)

createItemResp, err := containerClient.CreateItem(ctx, pk, jsonItem, nil)
require.NoError(t, err)
require.NotNil(t, createItemResp)

// Read item
readItemResp, err := containerClient.ReadItem(ctx, pk, testItem.ID, nil)
require.NoError(t, err)
require.NotNil(t, readItemResp)
}
85 changes: 85 additions & 0 deletions modules/azure/cosmosdb/examples_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,85 @@
package cosmosdb_test

import (
"context"
"fmt"
"log"
"net/http"

"github.com/Azure/azure-sdk-for-go/sdk/data/azcosmos"

"github.com/testcontainers/testcontainers-go"
"github.com/testcontainers/testcontainers-go/modules/azure/cosmosdb"
)

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

cosmosdbContainer, err := cosmosdb.Run(ctx, "mcr.microsoft.com/cosmosdb/linux/azure-cosmos-emulator:vnext-preview")
defer func() {
if err := testcontainers.TerminateContainer(cosmosdbContainer); err != nil {
log.Printf("failed to terminate container: %s", err)
}
}()
if err != nil {
log.Printf("failed to start container: %s", err)
return
}
// }

state, err := cosmosdbContainer.State(ctx)
if err != nil {
log.Printf("failed to get container state: %s", err)
return
}

fmt.Println(state.Running)

// Output:
// true
}

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

cosmosdbContainer, err := cosmosdb.Run(ctx, "mcr.microsoft.com/cosmosdb/linux/azure-cosmos-emulator:vnext-preview")
defer func() {
if err := testcontainers.TerminateContainer(cosmosdbContainer); err != nil {
log.Printf("failed to terminate container: %s", err)
}
}()
if err != nil {
log.Printf("failed to start container: %s", err)
return
}

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

p, err := cosmosdb.NewContainerPolicy(ctx, cosmosdbContainer)
if err != nil {
log.Printf("failed to create policy: %s", err)
return
}

client, err := azcosmos.NewClientFromConnectionString(connString, p.ClientOptions())
if err != nil {
log.Printf("failed to create cosmosdb client: %s", err)
return
}

createDatabaseResp, err := client.CreateDatabase(ctx, azcosmos.DatabaseProperties{ID: "myDatabase"}, nil)
if err != nil {
log.Printf("failed to create database: %s", err)
return
}
// }

fmt.Println(createDatabaseResp.RawResponse.StatusCode == http.StatusCreated)

// Output:
// true
}
45 changes: 45 additions & 0 deletions modules/azure/cosmosdb/policy.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,45 @@
package cosmosdb

import (
"context"
"fmt"
"net/http"

"github.com/Azure/azure-sdk-for-go/sdk/azcore"
"github.com/Azure/azure-sdk-for-go/sdk/azcore/policy"
"github.com/Azure/azure-sdk-for-go/sdk/data/azcosmos"
)

// ContainerPolicy ensures that requests always target the CosmosDB emulator container endpoint.
// It overrides the CosmosDB client's globalEndpointManager, which would otherwise dynamically
// update [http.Request.Host] based on global endpoint discovery, pinning all requests to the container.
type ContainerPolicy struct {
endpoint string
}

func NewContainerPolicy(ctx context.Context, c *Container) (*ContainerPolicy, error) {
endpoint, err := c.PortEndpoint(ctx, defaultPort, "")
if err != nil {
return nil, fmt.Errorf("port endpoint: %w", err)
}

return &ContainerPolicy{
endpoint: endpoint,
}, nil
}

func (p *ContainerPolicy) Do(req *policy.Request) (*http.Response, error) {
req.Raw().Host = p.endpoint
req.Raw().URL.Host = p.endpoint

return req.Next()
}

// ClientOptions returns Azure CosmosDB client options that contain ContainerPolicy.
func (p *ContainerPolicy) ClientOptions() *azcosmos.ClientOptions {
return &azcosmos.ClientOptions{
ClientOptions: azcore.ClientOptions{
PerRetryPolicies: []policy.Policy{p},
},
}
}
7 changes: 4 additions & 3 deletions modules/azure/go.mod
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,8 @@ go 1.24.0
toolchain go1.24.7

require (
github.com/Azure/azure-sdk-for-go/sdk/azcore v1.17.0
github.com/Azure/azure-sdk-for-go/sdk/azcore v1.19.1
github.com/Azure/azure-sdk-for-go/sdk/data/azcosmos v1.4.1
github.com/Azure/azure-sdk-for-go/sdk/data/aztables v1.3.0
github.com/Azure/azure-sdk-for-go/sdk/messaging/azeventhubs v1.3.0
github.com/Azure/azure-sdk-for-go/sdk/messaging/azservicebus v1.8.0
Expand All @@ -19,8 +20,8 @@ require (

require (
dario.cat/mergo v1.0.2 // indirect
github.com/Azure/azure-sdk-for-go/sdk/azidentity v1.8.2 // indirect
github.com/Azure/azure-sdk-for-go/sdk/internal v1.10.0 // indirect
github.com/Azure/azure-sdk-for-go v68.0.0+incompatible // indirect
github.com/Azure/azure-sdk-for-go/sdk/internal v1.11.2 // indirect
github.com/Azure/go-amqp v1.3.0 // indirect
github.com/Azure/go-ansiterm v0.0.0-20210617225240-d185dfc1b5a1 // indirect
github.com/Microsoft/go-winio v0.6.2 // indirect
Expand Down
Loading