Skip to content

Commit

Permalink
refactor: Switch to AWS endpoint resolution v2
Browse files Browse the repository at this point in the history
The v2 AWS SDK introduces a new endpoint resolution mechanism where
custom endpoints are specified on a per-service basis, while the
previous global mechanism is deprecated. This changeset switches to the
newer mechanism.

The default resolver under the new mechanism uses the base endpoint in
a service client's `Options` as the means to customize endpoints, so
it's enough for tests to make sure the base endpoint gets set.

Under the previous mechanism, the CHAMBER_AWS_SSM_ENDPOINT environment
variable was used to customize not just the endpoint for SSM, but also
for Secrets Manager. This changeset deprecates the latter use in favor
of a new CHAMBER_AWS_SECRETS_MANAGER_ENDPOINT environment variable.
There seems to also be SDK-native ways to use the environment for
endpoint customization, so this whole thing might go away at some point.
  • Loading branch information
bhavanki committed Jun 10, 2024
1 parent 37a70e0 commit 5b37a6a
Show file tree
Hide file tree
Showing 6 changed files with 49 additions and 30 deletions.
17 changes: 17 additions & 0 deletions store/secretsmanagerstore.go
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ import (
"encoding/json"
"errors"
"fmt"
"os"
"reflect"
"sort"
"strconv"
Expand All @@ -17,6 +18,12 @@ import (
"github.com/aws/aws-sdk-go-v2/service/sts"
)

const (
// CustomSecretsManagerEndpointEnvVar is the name of the environment variable specifying a custom
// base Secrets Manager endpoint.
CustomSecretsManagerEndpointEnvVar = "CHAMBER_AWS_SECRETS_MANAGER_ENDPOINT"
)

// We store all Chamber metadata in a stringified JSON format,
// in a field named "_chamber_metadata"
const metadataKey = "_chamber_metadata"
Expand Down Expand Up @@ -85,6 +92,16 @@ func NewSecretsManagerStore(ctx context.Context, numRetries int) (*SecretsManage
if err != nil {
return nil, err
}
customSecretsManagerEndpoint, ok := os.LookupEnv(CustomSecretsManagerEndpointEnvVar)
if ok {
cfg.BaseEndpoint = aws.String(customSecretsManagerEndpoint)
} else {
// Preserving incorrect and deprecated use of the SSM environment variable from v2
customSecretsManagerEndpoint, ok = os.LookupEnv(CustomSSMEndpointEnvVar)
if ok {
cfg.BaseEndpoint = aws.String(customSecretsManagerEndpoint)
}
}

svc := secretsmanager.NewFromConfig(cfg)

Expand Down
26 changes: 18 additions & 8 deletions store/secretsmanagerstore_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -194,23 +194,33 @@ func TestNewSecretsManagerStore(t *testing.T) {
assert.Equal(t, "us-west-1", s.config.Region)
})

t.Run("Should use CHAMBER_AWS_SSM_ENDPOINT if set", func(t *testing.T) {
t.Run("Should use CHAMBER_AWS_SECRETS_MANAGER_ENDPOINT if set", func(t *testing.T) {
os.Setenv("CHAMBER_AWS_SECRETS_MANAGER_ENDPOINT", "mycustomendpoint")
defer os.Unsetenv("CHAMBER_AWS_SECRETS_MANAGER_ENDPOINT")

s, err := NewSecretsManagerStore(context.Background(), 1)
assert.Nil(t, err)
secretsmanagerClient := s.svc.(*secretsmanager.Client)
assert.Equal(t, "mycustomendpoint", *secretsmanagerClient.Options().BaseEndpoint)
// default endpoint resolution (v2) uses the client's BaseEndpoint
})

t.Run("Should use CHAMBER_AWS_SSM_ENDPOINT if set (deprecated)", func(t *testing.T) {
os.Setenv("CHAMBER_AWS_SSM_ENDPOINT", "mycustomendpoint")
defer os.Unsetenv("CHAMBER_AWS_SSM_ENDPOINT")

s, err := NewSecretsManagerStore(context.Background(), 1)
assert.Nil(t, err)
endpoint, err := s.config.EndpointResolverWithOptions.ResolveEndpoint(secretsmanager.ServiceID, "us-west-2")
assert.Nil(t, err)
assert.Equal(t, "mycustomendpoint", endpoint.URL)
secretsmanagerClient := s.svc.(*secretsmanager.Client)
assert.Equal(t, "mycustomendpoint", *secretsmanagerClient.Options().BaseEndpoint)
// default endpoint resolution (v2) uses the client's BaseEndpoint
})

t.Run("Should use default AWS SSM endpoint if CHAMBER_AWS_SSM_ENDPOINT not set", func(t *testing.T) {
t.Run("Should use default AWS secrets manager endpoint if CHAMBER_AWS_SECRETS_MANAGER_ENDPOINT not set", func(t *testing.T) {
s, err := NewSecretsManagerStore(context.Background(), 1)
assert.Nil(t, err)
_, err = s.config.EndpointResolverWithOptions.ResolveEndpoint(secretsmanager.ServiceID, "us-west-2")
var notFoundError *aws.EndpointNotFoundError
assert.ErrorAs(t, err, &notFoundError)
secretsmanagerClient := s.svc.(*secretsmanager.Client)
assert.Nil(t, secretsmanagerClient.Options().BaseEndpoint)
})
}

Expand Down
3 changes: 1 addition & 2 deletions store/shared.go
Original file line number Diff line number Diff line change
Expand Up @@ -10,8 +10,7 @@ import (
)

const (
RegionEnvVar = "CHAMBER_AWS_REGION"
CustomSSMEndpointEnvVar = "CHAMBER_AWS_SSM_ENDPOINT"
RegionEnvVar = "CHAMBER_AWS_REGION"
)

func getConfig(ctx context.Context, numRetries int, retryMode aws.RetryMode) (aws.Config, string, error) {
Expand Down
13 changes: 0 additions & 13 deletions store/shared_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -10,14 +10,6 @@ import (
)

func TestGetConfig(t *testing.T) {
originalEndpoint := os.Getenv(CustomSSMEndpointEnvVar)
os.Setenv(CustomSSMEndpointEnvVar, "https://example.com/custom-endpoint")
if originalEndpoint != "" {
defer os.Setenv(CustomSSMEndpointEnvVar, originalEndpoint)
} else {
defer os.Unsetenv(CustomSSMEndpointEnvVar)
}

originalRegion := os.Getenv(RegionEnvVar)
os.Setenv(RegionEnvVar, "us-west-2")
if originalRegion != "" {
Expand All @@ -31,11 +23,6 @@ func TestGetConfig(t *testing.T) {
assert.NoError(t, err)
assert.Equal(t, "us-west-2", region)

endpoint, err := config.EndpointResolverWithOptions.ResolveEndpoint("ssm", "us-west-2")
assert.NoError(t, err)
assert.Equal(t, "https://example.com/custom-endpoint", endpoint.URL)
assert.Equal(t, aws.EndpointSourceCustom, endpoint.Source)

assert.Equal(t, 3, config.RetryMaxAttempts)
assert.Equal(t, aws.RetryModeStandard, config.RetryMode)
}
9 changes: 8 additions & 1 deletion store/ssmstore.go
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,10 @@ import (
)

const (
// CustomSSMEndpointEnvVar is the name of the environment variable specifying a custom base SSM
// endpoint.
CustomSSMEndpointEnvVar = "CHAMBER_AWS_SSM_ENDPOINT"

// DefaultKeyID is the default alias for the KMS key used to encrypt/decrypt secrets
DefaultKeyID = "alias/parameter_store_key"

Expand Down Expand Up @@ -61,10 +65,13 @@ func NewSSMStoreWithRetryMode(ctx context.Context, numRetries int, retryMode aws

func ssmStoreUsingRetryer(ctx context.Context, numRetries int, retryMode aws.RetryMode) (*SSMStore, error) {
cfg, _, err := getConfig(ctx, numRetries, retryMode)

if err != nil {
return nil, err
}
customSsmEndpoint, ok := os.LookupEnv(CustomSSMEndpointEnvVar)
if ok {
cfg.BaseEndpoint = aws.String(customSsmEndpoint)
}

svc := ssm.NewFromConfig(cfg)

Expand Down
11 changes: 5 additions & 6 deletions store/ssmstore_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -377,17 +377,16 @@ func TestNewSSMStore(t *testing.T) {

s, err := NewSSMStore(context.Background(), 1)
assert.Nil(t, err)
endpoint, err := s.config.EndpointResolverWithOptions.ResolveEndpoint(ssm.ServiceID, "us-west-2")
assert.Nil(t, err)
assert.Equal(t, "mycustomendpoint", endpoint.URL)
ssmClient := s.svc.(*ssm.Client)
assert.Equal(t, "mycustomendpoint", *ssmClient.Options().BaseEndpoint)
// default endpoint resolution (v2) uses the client's BaseEndpoint
})

t.Run("Should use default AWS SSM endpoint if CHAMBER_AWS_SSM_ENDPOINT not set", func(t *testing.T) {
s, err := NewSSMStore(context.Background(), 1)
assert.Nil(t, err)
_, err = s.config.EndpointResolverWithOptions.ResolveEndpoint(ssm.ServiceID, "us-west-2")
var notFoundError *aws.EndpointNotFoundError
assert.ErrorAs(t, err, &notFoundError)
ssmClient := s.svc.(*ssm.Client)
assert.Nil(t, ssmClient.Options().BaseEndpoint)
})

t.Run("Should set AWS SDK retry mode to default", func(t *testing.T) {
Expand Down

0 comments on commit 5b37a6a

Please sign in to comment.