Skip to content

Commit 058aa58

Browse files
authored
Start building out AWS utility functions for CLIs, with aws-sdk-go-v2 (#43)
* Start building out AWS utility functions for CLIs, with aws-sdk-go-v2 * Rename aws to awscommons * Add awscommons to README
1 parent 6867043 commit 058aa58

File tree

7 files changed

+242
-80
lines changed

7 files changed

+242
-80
lines changed

README.md

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,7 @@ This repo contains the following packages:
1919
* shell
2020
* ssh
2121
* retry
22+
* awscommons
2223

2324
Each of these packages is described below.
2425

@@ -138,6 +139,12 @@ This package contains helper methods for initiating SSH connections and running
138139

139140
This package contains helper methods for retrying an action up to a limit.
140141

142+
### awscommons
143+
144+
This package contains routines for interacting with AWS. Meant to provide high level interfaces used throughout various Gruntwork CLIs.
145+
146+
Note that the routines in this package are adapted for `aws-sdk-go-v2`, not v1 (`aws-sdk-go`).
147+
141148

142149
## Running tests
143150

awscommons/v2/auth.go

Lines changed: 35 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,35 @@
1+
package awscommons
2+
3+
import (
4+
"context"
5+
6+
"github.com/aws/aws-sdk-go-v2/aws"
7+
"github.com/aws/aws-sdk-go-v2/config"
8+
"github.com/gruntwork-io/go-commons/errors"
9+
)
10+
11+
const (
12+
DefaultRegion = "us-east-1"
13+
)
14+
15+
// Options represents all the parameters necessary for setting up authentication credentials with AWS.
16+
type Options struct {
17+
Region string
18+
19+
Context context.Context
20+
}
21+
22+
// NewOptions will create a new aws.Options struct that provides reasonable defaults for unspecified values.
23+
func NewOptions(region string) *Options {
24+
return &Options{
25+
Region: region,
26+
Context: context.TODO(),
27+
}
28+
}
29+
30+
// NewDefaultConfig will retrieve a new authenticated AWS config using SDK default credentials. This config can be used
31+
// to setup new AWS service clients.
32+
func NewDefaultConfig(opts *Options) (aws.Config, error) {
33+
cfg, err := config.LoadDefaultConfig(opts.Context, config.WithRegion(opts.Region))
34+
return cfg, errors.WithStackTrace(err)
35+
}

awscommons/v2/doc.go

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,4 @@
1+
// Package awscommons contains routines for interacting with AWS. Meant to provide high level interfaces used throughout
2+
// various Gruntwork CLIs.
3+
// NOTE: The routines in this package are adapted for aws-sdk-go-v2, not V1.
4+
package awscommons

awscommons/v2/secretsmanager.go

Lines changed: 62 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,62 @@
1+
package awscommons
2+
3+
import (
4+
goerrors "errors"
5+
6+
"github.com/aws/aws-sdk-go-v2/aws"
7+
"github.com/aws/aws-sdk-go-v2/service/secretsmanager"
8+
"github.com/aws/aws-sdk-go-v2/service/secretsmanager/types"
9+
10+
"github.com/gruntwork-io/go-commons/errors"
11+
)
12+
13+
// GetSecretsManagerMetadata returns the metadata of the Secrets Manager entry with the given ID.
14+
func GetSecretsManagerMetadata(opts *Options, secretID string) (*secretsmanager.DescribeSecretOutput, error) {
15+
client, err := NewSecretsManagerClient(opts)
16+
if err != nil {
17+
return nil, err
18+
}
19+
20+
secret, err := client.DescribeSecret(opts.Context, &secretsmanager.DescribeSecretInput{SecretId: aws.String(secretID)})
21+
return secret, errors.WithStackTrace(err)
22+
}
23+
24+
// SecretsManagerEntryExists returns whether or not the SecretsManager Entry with the given ARN exists. This will return
25+
// an error if it exists, but is not accessible due to permission issues, or if there is an authentication issue.
26+
func SecretsManagerEntryExists(opts *Options, arn string) (bool, error) {
27+
_, err := GetSecretsManagerMetadata(opts, arn)
28+
if err != nil {
29+
// If the err is 404, then return that it doesn't exist without error. Otherwise, return the error.
30+
rawErr := errors.Unwrap(err)
31+
var resourceNotFoundErr *types.ResourceNotFoundException
32+
if goerrors.As(rawErr, &resourceNotFoundErr) {
33+
return false, nil
34+
}
35+
return false, errors.WithStackTrace(err)
36+
}
37+
// At this point, we know the secret exists so return that.
38+
return true, nil
39+
}
40+
41+
// GetSecretsManagerSecretString will return the secret value stored at the given Secrets Manager ARN.
42+
func GetSecretsManagerSecretString(opts *Options, arn string) (string, error) {
43+
client, err := NewSecretsManagerClient(opts)
44+
if err != nil {
45+
return "", err
46+
}
47+
48+
secretVal, err := client.GetSecretValue(opts.Context, &secretsmanager.GetSecretValueInput{SecretId: aws.String(arn)})
49+
if err != nil {
50+
return "", errors.WithStackTrace(err)
51+
}
52+
return aws.ToString(secretVal.SecretString), nil
53+
}
54+
55+
// NewSecretsManagerClient will return a new AWS SDK client for interacting with AWS Secrets Manager.
56+
func NewSecretsManagerClient(opts *Options) (*secretsmanager.Client, error) {
57+
cfg, err := NewDefaultConfig(opts)
58+
if err != nil {
59+
return nil, errors.WithStackTrace(err)
60+
}
61+
return secretsmanager.NewFromConfig(cfg), nil
62+
}

awscommons/v2/secretsmanager_test.go

Lines changed: 76 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,76 @@
1+
package awscommons
2+
3+
import (
4+
"testing"
5+
6+
goaws "github.com/aws/aws-sdk-go-v2/aws"
7+
"github.com/gruntwork-io/terratest/modules/aws"
8+
"github.com/gruntwork-io/terratest/modules/random"
9+
"github.com/stretchr/testify/assert"
10+
"github.com/stretchr/testify/require"
11+
)
12+
13+
// Test GetSecretsManagerSecret by creating the secret in terratest and using the function to read the value to make
14+
// sure it reads the correct secret value.
15+
func TestGetSecretString(t *testing.T) {
16+
t.Parallel()
17+
18+
name := "refarch-deployer-test-secret-" + random.UniqueId()
19+
secretVal := random.UniqueId()
20+
region := aws.GetRandomStableRegion(t, nil, nil)
21+
opts := NewOptions(region)
22+
23+
secretARN := aws.CreateSecretStringWithDefaultKey(t, region, "Test Secret for refarch-deployer", name, secretVal)
24+
defer aws.DeleteSecret(t, region, secretARN, true)
25+
26+
actualSecret, err := GetSecretsManagerSecretString(opts, secretARN)
27+
require.NoError(t, err)
28+
assert.Equal(t, secretVal, actualSecret)
29+
}
30+
31+
// Test SecretsManagerEntryExists returns false when calling it on a secrets manager entry that doesn't exist.
32+
func TestSecretsManagerEntryExistsFalse(t *testing.T) {
33+
t.Parallel()
34+
35+
opts := NewOptions("us-east-1")
36+
exists, err := SecretsManagerEntryExists(opts, "secret-that-doesnt-exist")
37+
require.NoError(t, err)
38+
assert.False(t, exists)
39+
}
40+
41+
// Test SecretsManagerEntryExists returns true when calling it on a secrets manager entry that exists.
42+
func TestSecretsManagerEntryExistsTrueWithARN(t *testing.T) {
43+
t.Parallel()
44+
45+
name := "refarch-deployer-test-secret-" + random.UniqueId()
46+
secretVal := random.UniqueId()
47+
region := aws.GetRandomStableRegion(t, nil, nil)
48+
opts := NewOptions(region)
49+
50+
secretARN := aws.CreateSecretStringWithDefaultKey(t, region, "Test Secret for refarch-deployer", name, secretVal)
51+
defer aws.DeleteSecret(t, region, secretARN, true)
52+
53+
exists, err := SecretsManagerEntryExists(opts, secretARN)
54+
require.NoError(t, err)
55+
assert.True(t, exists)
56+
}
57+
58+
// Test SecretsManagerEntryExists returns true when calling it on a secrets manager entry that exists with name.
59+
func TestSecretsManagerEntryExistsTrueWithName(t *testing.T) {
60+
t.Parallel()
61+
62+
name := "refarch-deployer-test-secret-" + random.UniqueId()
63+
secretVal := random.UniqueId()
64+
region := aws.GetRandomStableRegion(t, nil, nil)
65+
opts := NewOptions(region)
66+
67+
secretARN := aws.CreateSecretStringWithDefaultKey(t, region, "Test Secret for refarch-deployer", name, secretVal)
68+
defer aws.DeleteSecret(t, region, secretARN, true)
69+
70+
secret, err := GetSecretsManagerMetadata(opts, secretARN)
71+
require.NoError(t, err)
72+
73+
exists, err := SecretsManagerEntryExists(opts, goaws.ToString(secret.Name))
74+
require.NoError(t, err)
75+
assert.True(t, exists)
76+
}

go.mod

Lines changed: 11 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -3,15 +3,24 @@ module github.com/gruntwork-io/go-commons
33
go 1.13
44

55
require (
6+
github.com/aws/aws-sdk-go v1.38.0 // indirect
7+
github.com/aws/aws-sdk-go-v2 v1.3.3
8+
github.com/aws/aws-sdk-go-v2/config v1.1.6
9+
github.com/aws/aws-sdk-go-v2/service/secretsmanager v1.2.2
610
github.com/bgentry/speakeasy v0.1.0
711
github.com/fatih/color v1.9.0
812
github.com/go-errors/errors v1.0.2-0.20180813162953-d98b870cc4e0
9-
github.com/gruntwork-io/kubergrunt v0.6.10
13+
github.com/go-sql-driver/mysql v1.5.0 // indirect
1014
github.com/gruntwork-io/terratest v0.32.9
1115
github.com/hashicorp/go-multierror v1.1.0
16+
github.com/kr/text v0.2.0 // indirect
1217
github.com/mattn/go-zglob v0.0.2-0.20190814121620-e3c945676326
18+
github.com/niemeyer/pretty v0.0.0-20200227124842-a10e7caefd8e // indirect
1319
github.com/sirupsen/logrus v1.6.0
1420
github.com/stretchr/testify v1.6.1
1521
github.com/urfave/cli v1.22.4
16-
golang.org/x/crypto v0.0.0-20210220033148-5ea612d1eb83
22+
golang.org/x/crypto v0.0.0-20210317152858-513c2a44f670
23+
golang.org/x/sys v0.0.0-20210319071255-635bc2c9138d // indirect
24+
golang.org/x/term v0.0.0-20210317153231-de623e64d2a6 // indirect
25+
gopkg.in/check.v1 v1.0.0-20200902074654-038fdea0a05b // indirect
1726
)

0 commit comments

Comments
 (0)