Skip to content
Merged
Show file tree
Hide file tree
Changes from 13 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
29 changes: 27 additions & 2 deletions .github/workflows/go-cm-integ-tests.yml
Original file line number Diff line number Diff line change
Expand Up @@ -45,8 +45,33 @@ jobs:
role-to-assume: ${{ secrets.GO_IAM_ROLE }}
aws-region: us-east-1

- name: Build & Run
working-directory: ./go/cluster_management
- name: Build & Run Create Multi region
working-directory: ./go/cluster_management/cmd/create_multi_region
run: |
go env -w GOPROXY=direct
go test
- name: Build & Run Create Single region
working-directory: ./go/cluster_management/cmd/create_single_region
run: |
go env -w GOPROXY=direct
go test
- name: Build & Run Get Cluster
working-directory: ./go/cluster_management/cmd/get_cluster
run: |
go env -w GOPROXY=direct
go test
- name: Build & Run Update Cluster
working-directory: ./go/cluster_management/cmd/update_cluster
run: |
go env -w GOPROXY=direct
go test
- name: Build & Run Delete Single region
working-directory: ./go/cluster_management/cmd/delete_single_region
run: |
go env -w GOPROXY=direct
go test
- name: Build & Run Delete Multi region
working-directory: ./go/cluster_management/cmd/delete_multi_region
run: |
go env -w GOPROXY=direct
go test
36 changes: 36 additions & 0 deletions go/cluster_management/Makefile
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
# Build all cluster management examples
# Go parameters
GOCMD=go
GOBUILD=$(GOCMD) build
GOCLEAN=$(GOCMD) clean
BINARY_DIR=bin

# Find all cmd directories
CMD_DIRS := $(wildcard cmd/*)
BINARIES := $(patsubst cmd/%,$(BINARY_DIR)/%,$(CMD_DIRS))

# Default target
.PHONY: all
all: clean build

# Create bin directory
$(BINARY_DIR):
mkdir -p $(BINARY_DIR)

# Build all commands
.PHONY: build
build: $(BINARY_DIR) $(BINARIES)

# Rule to build each binary
$(BINARY_DIR)/%: cmd/%
$(GOBUILD) -o $@ ./cmd/$*

# Clean build artifacts
.PHONY: clean
clean:
$(GOCLEAN)
rm -rf $(BINARY_DIR)

# Build specific commands
.PHONY: $(CMD_DIRS)
$(CMD_DIRS): cmd/%: $(BINARY_DIR)/%
45 changes: 0 additions & 45 deletions go/cluster_management/client_util.go

This file was deleted.

153 changes: 153 additions & 0 deletions go/cluster_management/cmd/create_multi_region/create_multi_region.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,153 @@
package main

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

"github.com/aws/aws-sdk-go-v2/aws"
"github.com/aws/aws-sdk-go-v2/config"
"github.com/aws/aws-sdk-go-v2/service/dsql"
dtypes "github.com/aws/aws-sdk-go-v2/service/dsql/types"
)

func CreateMultiRegionClusters(ctx context.Context, witness, region1, region2 string) error {

cfg, err := config.LoadDefaultConfig(ctx, config.WithRegion(region1))
if err != nil {
log.Fatalf("Failed to load AWS configuration: %v", err)
}

// Create a DSQL region 1 client
client := dsql.NewFromConfig(cfg, func(o *dsql.Options) {
o.Region = region1
})

cfg2, err := config.LoadDefaultConfig(ctx, config.WithRegion(region2))
if err != nil {
log.Fatalf("Failed to load AWS configuration: %v", err)
}

// Create a DSQL region 2 client
client2 := dsql.NewFromConfig(cfg2, func(o *dsql.Options) {
o.Region = region2
})

// Create cluster
deleteProtect := true

// We can only set the witness region for the first cluster
input := &dsql.CreateClusterInput{
DeletionProtectionEnabled: &deleteProtect,
MultiRegionProperties: &dtypes.MultiRegionProperties{
WitnessRegion: aws.String(witness),
},
Tags: map[string]string{
"Name": "go multi-region cluster",
},
}

clusterProperties, err := client.CreateCluster(context.Background(), input)

if err != nil {
return fmt.Errorf("failed to create first cluster: %v", err)
}

// create second cluster
cluster2Arns := []string{*clusterProperties.Arn}

// For the second cluster we can set witness region and designate the first cluster as a peer
input2 := &dsql.CreateClusterInput{
DeletionProtectionEnabled: &deleteProtect,
MultiRegionProperties: &dtypes.MultiRegionProperties{
WitnessRegion: aws.String("us-west-2"),
Clusters: cluster2Arns,
},
Tags: map[string]string{
"Name": "go multi-region cluster",
},
}

clusterProperties2, err := client2.CreateCluster(context.Background(), input2)

if err != nil {
return fmt.Errorf("failed to create second cluster: %v", err)
}

// link initial cluster to second cluster
cluster1Arns := []string{*clusterProperties2.Arn}

// Now that we know the second cluster arn we can set it as a peer of the first cluster
input3 := dsql.UpdateClusterInput{
Identifier: clusterProperties.Identifier,
MultiRegionProperties: &dtypes.MultiRegionProperties{
WitnessRegion: aws.String("us-west-2"),
Clusters: cluster1Arns,
}}

_, err = client.UpdateCluster(context.Background(), &input3)

if err != nil {
return fmt.Errorf("failed to update cluster to associate with first cluster. %v", err)
}

// Create the waiter with our custom options for first cluster
waiter := dsql.NewClusterActiveWaiter(client, func(o *dsql.ClusterActiveWaiterOptions) {
o.MaxDelay = 30 * time.Second // Creating a multi-region cluster can take a few minutes
o.MinDelay = 10 * time.Second
o.LogWaitAttempts = true
})

// Now that multiRegionProperties is fully defined for both clusters
// they'll begin the transition to ACTIVE

// Create the input for the clusterProperties to monitor for first cluster
getInput := &dsql.GetClusterInput{
Identifier: clusterProperties.Identifier,
}

// Wait for the first cluster to become active
fmt.Printf("Waiting for first cluster %s to become active...\n", *clusterProperties.Identifier)
err = waiter.Wait(ctx, getInput, 5*time.Minute)
if err != nil {
return fmt.Errorf("error waiting for first cluster to become active: %w", err)
}

// Create the waiter with our custom options
waiter2 := dsql.NewClusterActiveWaiter(client2, func(o *dsql.ClusterActiveWaiterOptions) {
o.MaxDelay = 30 * time.Second // Creating a multi-region cluster can take a few minutes
o.MinDelay = 10 * time.Second
o.LogWaitAttempts = true
})

// Create the input for the clusterProperties to monitor for second
getInput2 := &dsql.GetClusterInput{
Identifier: clusterProperties2.Identifier,
}

// Wait for the second cluster to become active
fmt.Printf("Waiting for second cluster %s to become active...\n", *clusterProperties2.Identifier)
err = waiter2.Wait(ctx, getInput2, 5*time.Minute)
if err != nil {
return fmt.Errorf("error waiting for second cluster to become active: %w", err)
}

fmt.Printf("Cluster %s is now active\n", *clusterProperties.Identifier)
fmt.Printf("Cluster %s is now active\n", *clusterProperties2.Identifier)
return nil
}

// Example usage in main function
func main() {
// Set up context with timeout
ctx, cancel := context.WithTimeout(context.Background(), 10*time.Minute)
defer cancel()

err := CreateMultiRegionClusters(ctx, "us-west-2", "us-east-1", "us-east-2")
if err != nil {
fmt.Printf("failed to create multi-region clusters: %v", err)
panic(err)
}

}
Original file line number Diff line number Diff line change
@@ -0,0 +1,69 @@
package main

import (
"context"
"fmt"
"os"
"testing"
"time"
)

// Global variables for shared resources
var (
testCtx context.Context
cancel context.CancelFunc
)

func TestMain(m *testing.M) {
// Setup before running tests
setup()

// Run all tests
code := m.Run()

// Cleanup after tests complete
teardown()

// Exit with the test status code
os.Exit(code)
}

func setup() {
// Initialize context with timeout for all tests
Comment thread
imforster marked this conversation as resolved.
Outdated
testCtx, cancel = context.WithTimeout(context.Background(), 10*time.Minute)
}

func teardown() {
// Cancel the context
Comment thread
imforster marked this conversation as resolved.
Outdated
cancel()

// Clean up any resources, close connections, etc.
}

// Test for CreateMultiRegionCluster function
func TestCreateMultiRegionCluster(t *testing.T) {
tests := []struct {
name string
region1 string
region2 string
wantErr bool
}{
{
name: "Create multi-region clusters",
region1: "us-east-1",
region2: "us-east-2",
wantErr: false,
},
// Add more test cases as needed
Comment thread
imforster marked this conversation as resolved.
Outdated
}

for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
err := CreateMultiRegionClusters(testCtx, "us-west-2", tt.region1, tt.region2)
if err != nil {
fmt.Printf("failed to create multi-region clusters: %v", err)
panic(err)
}
})
}
}
Loading
Loading