Files
threshold-plugin-aws/plugin/plugin_test.go
T

1198 lines
34 KiB
Go
Raw Normal View History

2021-09-22 17:00:37 -07:00
package plugin
import (
"context"
2021-10-06 12:47:05 -07:00
"errors"
2021-09-24 12:47:57 -07:00
"fmt"
2021-09-22 17:00:37 -07:00
"testing"
2021-10-06 16:53:11 -07:00
"github.com/aws/aws-sdk-go/service/ec2"
2021-09-22 17:00:37 -07:00
"github.com/hashicorp/boundary/sdk/pbs/controller/api/resources/hostcatalogs"
2021-10-06 16:53:11 -07:00
"github.com/hashicorp/boundary/sdk/pbs/controller/api/resources/hostsets"
2021-09-22 17:00:37 -07:00
pb "github.com/hashicorp/boundary/sdk/pbs/plugin"
2021-09-23 17:24:54 -07:00
"github.com/hashicorp/go-secure-stdlib/awsutil"
2021-09-22 17:00:37 -07:00
"github.com/stretchr/testify/require"
"google.golang.org/protobuf/types/known/structpb"
)
2021-09-27 16:47:51 -07:00
func TestOnCreateCatalogErr(t *testing.T) {
cases := []struct {
2021-10-06 12:47:05 -07:00
name string
req *pb.OnCreateCatalogRequest
opts []awsCatalogPersistedStateOption
expectedErr string
2021-09-27 16:47:51 -07:00
}{
{
2021-10-06 12:47:05 -07:00
name: "nil catalog",
req: &pb.OnCreateCatalogRequest{},
expectedErr: "catalog is nil",
2021-09-27 16:47:51 -07:00
},
{
name: "nil secrets",
req: &pb.OnCreateCatalogRequest{
Catalog: &hostcatalogs.HostCatalog{},
},
2021-10-06 12:47:05 -07:00
expectedErr: "secrets are required",
2021-09-27 16:47:51 -07:00
},
{
name: "nil attributes",
req: &pb.OnCreateCatalogRequest{
Catalog: &hostcatalogs.HostCatalog{
Secrets: new(structpb.Struct),
},
},
2021-10-06 12:47:05 -07:00
expectedErr: "attributes are required",
2021-09-27 16:47:51 -07:00
},
{
name: "invalid region",
req: &pb.OnCreateCatalogRequest{
Catalog: &hostcatalogs.HostCatalog{
Secrets: new(structpb.Struct),
Attributes: mustStruct(map[string]interface{}{
2021-10-06 12:47:05 -07:00
constRegion: "foobar",
2021-09-27 16:47:51 -07:00
}),
},
},
2021-10-06 12:47:05 -07:00
expectedErr: "not a valid region: foobar",
2021-09-27 16:47:51 -07:00
},
{
name: "missing access key ID",
req: &pb.OnCreateCatalogRequest{
Catalog: &hostcatalogs.HostCatalog{
Secrets: new(structpb.Struct),
Attributes: mustStruct(map[string]interface{}{
2021-10-06 12:47:05 -07:00
constRegion: "us-west-2",
2021-09-27 16:47:51 -07:00
}),
},
},
2021-10-06 12:47:05 -07:00
expectedErr: "missing required value \"access_key_id\"",
2021-09-27 16:47:51 -07:00
},
{
name: "missing secret access key",
req: &pb.OnCreateCatalogRequest{
Catalog: &hostcatalogs.HostCatalog{
Secrets: mustStruct(map[string]interface{}{
2021-10-06 12:47:05 -07:00
constAccessKeyId: "foobar",
2021-09-27 16:47:51 -07:00
}),
Attributes: mustStruct(map[string]interface{}{
2021-10-06 12:47:05 -07:00
constRegion: "us-west-2",
2021-09-27 16:47:51 -07:00
}),
},
},
2021-10-06 12:47:05 -07:00
expectedErr: "missing required value \"secret_access_key\"",
2021-09-27 16:47:51 -07:00
},
{
2021-10-06 12:47:05 -07:00
name: "invalid value for skipping rotation",
2021-09-27 16:47:51 -07:00
req: &pb.OnCreateCatalogRequest{
Catalog: &hostcatalogs.HostCatalog{
Secrets: mustStruct(map[string]interface{}{
2021-10-06 12:47:05 -07:00
constAccessKeyId: "foobar",
constSecretAccessKey: "bazqux",
2021-09-27 16:47:51 -07:00
}),
Attributes: mustStruct(map[string]interface{}{
2021-10-06 12:47:05 -07:00
constRegion: "us-west-2",
constDisableCredentialRotation: "sure",
2021-09-27 16:47:51 -07:00
}),
},
},
2021-10-06 12:47:05 -07:00
expectedErr: "unexpected type for value \"disable_credential_rotation\": want bool, got string",
2021-09-27 16:47:51 -07:00
},
{
2021-10-06 12:47:05 -07:00
name: "persisted state setup error",
2021-09-27 16:47:51 -07:00
req: &pb.OnCreateCatalogRequest{
Catalog: &hostcatalogs.HostCatalog{
Secrets: mustStruct(map[string]interface{}{
2021-10-06 12:47:05 -07:00
constAccessKeyId: "foobar",
constSecretAccessKey: "bazqux",
2021-09-27 16:47:51 -07:00
}),
Attributes: mustStruct(map[string]interface{}{
2021-10-06 12:47:05 -07:00
constRegion: "us-west-2",
2021-09-27 16:47:51 -07:00
}),
},
},
2021-10-06 12:47:05 -07:00
opts: []awsCatalogPersistedStateOption{
func(s *awsCatalogPersistedState) error {
return errors.New(testOptionErr)
},
},
expectedErr: fmt.Sprintf("error setting up persisted state: %s", testOptionErr),
},
{
name: "rotation error",
req: &pb.OnCreateCatalogRequest{
Catalog: &hostcatalogs.HostCatalog{
Secrets: mustStruct(map[string]interface{}{
constAccessKeyId: "foobar",
constSecretAccessKey: "bazqux",
}),
Attributes: mustStruct(map[string]interface{}{
constRegion: "us-west-2",
}),
},
},
opts: []awsCatalogPersistedStateOption{
withStateTestOpts([]awsutil.Option{
awsutil.WithIAMAPIFunc(
awsutil.NewMockIAM(
awsutil.WithGetUserError(errors.New(testGetUserErr)),
),
),
}),
},
expectedErr: fmt.Sprintf("error during credential rotation: error rotating credentials: error calling CreateAccessKey: error calling aws.GetUser: %s", testGetUserErr),
},
{
name: "validation error",
req: &pb.OnCreateCatalogRequest{
Catalog: &hostcatalogs.HostCatalog{
Secrets: mustStruct(map[string]interface{}{
constAccessKeyId: "foobar",
constSecretAccessKey: "bazqux",
}),
Attributes: mustStruct(map[string]interface{}{
constRegion: "us-west-2",
constDisableCredentialRotation: true,
}),
},
},
opts: []awsCatalogPersistedStateOption{
withStateTestOpts([]awsutil.Option{
awsutil.WithSTSAPIFunc(
awsutil.NewMockSTS(
awsutil.WithGetCallerIdentityError(errors.New(testGetCallerIdentityErr)),
),
),
}),
},
expectedErr: fmt.Sprintf("error during credential validation: error validating credentials: %s", testGetCallerIdentityErr),
2021-09-27 16:47:51 -07:00
},
}
for _, tc := range cases {
tc := tc
t.Run(tc.name, func(t *testing.T) {
require := require.New(t)
2021-10-06 12:47:05 -07:00
p := &AwsPlugin{
testStateOpts: tc.opts,
}
2021-09-27 16:47:51 -07:00
_, err := p.OnCreateCatalog(context.Background(), tc.req)
2021-10-06 12:47:05 -07:00
require.EqualError(err, tc.expectedErr)
2021-09-27 16:47:51 -07:00
})
}
}
2021-10-06 12:47:05 -07:00
func TestOnUpdateCatalogErr(t *testing.T) {
cases := []struct {
name string
req *pb.OnUpdateCatalogRequest
opts []awsCatalogPersistedStateOption
expectedErr string
}{
{
name: "nil new catalog",
req: &pb.OnUpdateCatalogRequest{},
expectedErr: "new catalog is nil",
},
{
name: "nil new attributes",
req: &pb.OnUpdateCatalogRequest{
NewCatalog: &hostcatalogs.HostCatalog{
Secrets: new(structpb.Struct),
},
},
expectedErr: "new catalog missing attributes",
},
{
name: "invalid region",
req: &pb.OnUpdateCatalogRequest{
NewCatalog: &hostcatalogs.HostCatalog{
Attributes: mustStruct(map[string]interface{}{
constRegion: "foobar",
}),
},
},
expectedErr: "not a valid region: foobar",
},
{
name: "persisted state setup error",
req: &pb.OnUpdateCatalogRequest{
NewCatalog: &hostcatalogs.HostCatalog{
Attributes: mustStruct(map[string]interface{}{
constRegion: "us-west-2",
}),
},
Persisted: &pb.HostCatalogPersisted{
Secrets: mustStruct(map[string]interface{}{
constAccessKeyId: "foobar",
constSecretAccessKey: "bazqux",
constCredsLastRotatedTime: "2006-01-02T15:04:05+07:00",
}),
},
},
opts: []awsCatalogPersistedStateOption{
func(s *awsCatalogPersistedState) error {
return errors.New(testOptionErr)
},
},
expectedErr: fmt.Sprintf("error loading persisted state: %s", testOptionErr),
},
{
name: "invalid value for credential rotation",
req: &pb.OnUpdateCatalogRequest{
NewCatalog: &hostcatalogs.HostCatalog{
Attributes: mustStruct(map[string]interface{}{
constRegion: "us-west-2",
constDisableCredentialRotation: "sure",
}),
},
Persisted: &pb.HostCatalogPersisted{
Secrets: mustStruct(map[string]interface{}{
constAccessKeyId: "foobar",
constSecretAccessKey: "bazqux",
constCredsLastRotatedTime: "2006-01-02T15:04:05+07:00",
}),
},
},
expectedErr: "unexpected type for value \"disable_credential_rotation\": want bool, got string",
},
{
name: "cannot disable rotation for already rotated credentials",
req: &pb.OnUpdateCatalogRequest{
NewCatalog: &hostcatalogs.HostCatalog{
Attributes: mustStruct(map[string]interface{}{
constRegion: "us-west-2",
constDisableCredentialRotation: true,
}),
},
Persisted: &pb.HostCatalogPersisted{
Secrets: mustStruct(map[string]interface{}{
constAccessKeyId: "foobar",
constSecretAccessKey: "bazqux",
constCredsLastRotatedTime: "2006-01-02T15:04:05+07:00",
}),
},
},
expectedErr: "cannot disable rotation for already-rotated credentials",
},
{
name: "updating secrets, missing access key id",
req: &pb.OnUpdateCatalogRequest{
NewCatalog: &hostcatalogs.HostCatalog{
Secrets: new(structpb.Struct),
Attributes: mustStruct(map[string]interface{}{
constRegion: "us-west-2",
}),
},
Persisted: &pb.HostCatalogPersisted{
Secrets: mustStruct(map[string]interface{}{
constAccessKeyId: "foobar",
constSecretAccessKey: "bazqux",
constCredsLastRotatedTime: "2006-01-02T15:04:05+07:00",
}),
},
},
expectedErr: "missing required value \"access_key_id\"",
},
{
name: "updating secrets, missing secret access key",
req: &pb.OnUpdateCatalogRequest{
NewCatalog: &hostcatalogs.HostCatalog{
Secrets: mustStruct(map[string]interface{}{
constAccessKeyId: "foobar",
}),
Attributes: mustStruct(map[string]interface{}{
constRegion: "us-west-2",
}),
},
Persisted: &pb.HostCatalogPersisted{
Secrets: mustStruct(map[string]interface{}{
constAccessKeyId: "foobar",
constSecretAccessKey: "bazqux",
constCredsLastRotatedTime: "2006-01-02T15:04:05+07:00",
}),
},
},
expectedErr: "missing required value \"secret_access_key\"",
},
{
name: "updating secrets, replace error",
req: &pb.OnUpdateCatalogRequest{
NewCatalog: &hostcatalogs.HostCatalog{
Secrets: mustStruct(map[string]interface{}{
constAccessKeyId: "onetwo",
constSecretAccessKey: "threefour",
}),
Attributes: mustStruct(map[string]interface{}{
constRegion: "us-west-2",
constDisableCredentialRotation: true,
}),
},
Persisted: &pb.HostCatalogPersisted{
Secrets: mustStruct(map[string]interface{}{
constAccessKeyId: "foobar",
constSecretAccessKey: "bazqux",
constCredsLastRotatedTime: "2006-01-02T15:04:05+07:00",
}),
},
},
opts: []awsCatalogPersistedStateOption{
withStateTestOpts([]awsutil.Option{
awsutil.WithIAMAPIFunc(
awsutil.NewMockIAM(
awsutil.WithDeleteAccessKeyError(errors.New(testDeleteAccessKeyErr)),
),
),
}),
},
expectedErr: fmt.Sprintf("error attempting to replace credentials: error deleting old access key: %s", testDeleteAccessKeyErr),
},
{
name: "rotation error",
req: &pb.OnUpdateCatalogRequest{
NewCatalog: &hostcatalogs.HostCatalog{
Attributes: mustStruct(map[string]interface{}{
constRegion: "us-west-2",
}),
},
Persisted: &pb.HostCatalogPersisted{
Secrets: mustStruct(map[string]interface{}{
constAccessKeyId: "foobar",
constSecretAccessKey: "bazqux",
}),
},
},
opts: []awsCatalogPersistedStateOption{
withStateTestOpts([]awsutil.Option{
awsutil.WithIAMAPIFunc(
awsutil.NewMockIAM(
awsutil.WithGetUserError(errors.New(testGetUserErr)),
),
),
}),
},
expectedErr: fmt.Sprintf("error during credential rotation: error rotating credentials: error calling CreateAccessKey: error calling aws.GetUser: %s", testGetUserErr),
},
2021-09-27 16:47:51 -07:00
}
2021-10-06 12:47:05 -07:00
for _, tc := range cases {
tc := tc
t.Run(tc.name, func(t *testing.T) {
require := require.New(t)
p := &AwsPlugin{
testStateOpts: tc.opts,
}
_, err := p.OnUpdateCatalog(context.Background(), tc.req)
require.EqualError(err, tc.expectedErr)
})
}
2021-09-27 16:47:51 -07:00
}
2021-10-06 16:53:11 -07:00
func TestOnDeleteCatalogErr(t *testing.T) {
cases := []struct {
name string
req *pb.OnDeleteCatalogRequest
opts []awsCatalogPersistedStateOption
expectedErr string
}{
{
name: "persisted state setup error",
req: &pb.OnDeleteCatalogRequest{
Catalog: &hostcatalogs.HostCatalog{
Attributes: mustStruct(map[string]interface{}{
constRegion: "us-west-2",
}),
},
Persisted: &pb.HostCatalogPersisted{
Secrets: mustStruct(map[string]interface{}{
constAccessKeyId: "foobar",
constSecretAccessKey: "bazqux",
constCredsLastRotatedTime: "2006-01-02T15:04:05+07:00",
}),
},
},
opts: []awsCatalogPersistedStateOption{
func(s *awsCatalogPersistedState) error {
return errors.New(testOptionErr)
},
},
expectedErr: fmt.Sprintf("error loading persisted state: %s", testOptionErr),
},
{
name: "delete error",
req: &pb.OnDeleteCatalogRequest{
Catalog: &hostcatalogs.HostCatalog{
Attributes: mustStruct(map[string]interface{}{
constRegion: "us-west-2",
}),
},
Persisted: &pb.HostCatalogPersisted{
Secrets: mustStruct(map[string]interface{}{
constAccessKeyId: "foobar",
constSecretAccessKey: "bazqux",
constCredsLastRotatedTime: "2006-01-02T15:04:05+07:00",
}),
},
},
opts: []awsCatalogPersistedStateOption{
withStateTestOpts([]awsutil.Option{
awsutil.WithIAMAPIFunc(
awsutil.NewMockIAM(
awsutil.WithDeleteAccessKeyError(errors.New(testDeleteAccessKeyErr)),
),
),
}),
},
expectedErr: fmt.Sprintf("error removing rotated credentials during catalog deletion: error deleting old access key: %s", testDeleteAccessKeyErr),
},
}
for _, tc := range cases {
tc := tc
t.Run(tc.name, func(t *testing.T) {
require := require.New(t)
p := &AwsPlugin{
testStateOpts: tc.opts,
}
_, err := p.OnDeleteCatalog(context.Background(), tc.req)
require.EqualError(err, tc.expectedErr)
})
}
}
func TestOnCreateSetErr(t *testing.T) {
cases := []struct {
name string
req *pb.OnCreateSetRequest
opts []awsCatalogPersistedStateOption
expectedErr string
}{
{
name: "nil catalog",
req: &pb.OnCreateSetRequest{},
expectedErr: "catalog is nil",
},
{
name: "nil catalog attributes",
req: &pb.OnCreateSetRequest{
Catalog: &hostcatalogs.HostCatalog{
Secrets: new(structpb.Struct),
},
},
expectedErr: "catalog missing attributes",
},
{
name: "persisted state setup error",
req: &pb.OnCreateSetRequest{
Catalog: &hostcatalogs.HostCatalog{
Attributes: mustStruct(map[string]interface{}{
constRegion: "us-west-2",
}),
},
Persisted: &pb.HostCatalogPersisted{
Secrets: mustStruct(map[string]interface{}{
constAccessKeyId: "foobar",
constSecretAccessKey: "bazqux",
constCredsLastRotatedTime: "2006-01-02T15:04:05+07:00",
}),
},
},
opts: []awsCatalogPersistedStateOption{
func(s *awsCatalogPersistedState) error {
return errors.New(testOptionErr)
},
},
expectedErr: fmt.Sprintf("error loading persisted state: %s", testOptionErr),
},
{
name: "invalid region",
req: &pb.OnCreateSetRequest{
Catalog: &hostcatalogs.HostCatalog{
Attributes: mustStruct(map[string]interface{}{
constRegion: "foobar",
}),
},
Persisted: &pb.HostCatalogPersisted{
Secrets: mustStruct(map[string]interface{}{
constAccessKeyId: "foobar",
constSecretAccessKey: "bazqux",
constCredsLastRotatedTime: "2006-01-02T15:04:05+07:00",
}),
},
},
expectedErr: "catalog validation error: not a valid region: foobar",
},
{
name: "nil set",
req: &pb.OnCreateSetRequest{
Catalog: &hostcatalogs.HostCatalog{
Attributes: mustStruct(map[string]interface{}{
constRegion: "us-west-2",
}),
},
Persisted: &pb.HostCatalogPersisted{
Secrets: mustStruct(map[string]interface{}{
constAccessKeyId: "foobar",
constSecretAccessKey: "bazqux",
constCredsLastRotatedTime: "2006-01-02T15:04:05+07:00",
}),
},
},
expectedErr: "set is nil",
},
{
name: "nil attributes in set",
req: &pb.OnCreateSetRequest{
Catalog: &hostcatalogs.HostCatalog{
Attributes: mustStruct(map[string]interface{}{
constRegion: "us-west-2",
}),
},
Persisted: &pb.HostCatalogPersisted{
Secrets: mustStruct(map[string]interface{}{
constAccessKeyId: "foobar",
constSecretAccessKey: "bazqux",
constCredsLastRotatedTime: "2006-01-02T15:04:05+07:00",
}),
},
Set: &hostsets.HostSet{},
},
expectedErr: "set missing attributes",
},
{
name: "client load error",
req: &pb.OnCreateSetRequest{
Catalog: &hostcatalogs.HostCatalog{
Attributes: mustStruct(map[string]interface{}{
constRegion: "us-west-2",
}),
},
Persisted: &pb.HostCatalogPersisted{
Secrets: mustStruct(map[string]interface{}{
constAccessKeyId: "foobar",
constSecretAccessKey: "bazqux",
constCredsLastRotatedTime: "2006-01-02T15:04:05+07:00",
}),
},
Set: &hostsets.HostSet{
Attributes: mustStruct(map[string]interface{}{
constDescribeInstancesFilters: map[string]interface{}{
"tag-key": []interface{}{"foo"},
},
}),
},
},
opts: []awsCatalogPersistedStateOption{
withStateTestOpts([]awsutil.Option{
awsutil.MockOptionErr(errors.New(testOptionErr)),
}),
},
expectedErr: fmt.Sprintf("error getting EC2 client: error getting AWS session: error reading options in NewCredentialsConfig: %s", testOptionErr),
},
{
name: "buildDescribeInstancesInput error",
req: &pb.OnCreateSetRequest{
Catalog: &hostcatalogs.HostCatalog{
Attributes: mustStruct(map[string]interface{}{
constRegion: "us-west-2",
}),
},
Persisted: &pb.HostCatalogPersisted{
Secrets: mustStruct(map[string]interface{}{
constAccessKeyId: "foobar",
constSecretAccessKey: "bazqux",
constCredsLastRotatedTime: "2006-01-02T15:04:05+07:00",
}),
},
Set: &hostsets.HostSet{
Attributes: mustStruct(map[string]interface{}{
constDescribeInstancesFilters: map[string]interface{}{
"tag-key": "foo",
},
}),
},
},
expectedErr: "error building DescribeInstances parameters: error building filters: unexpected type for filter values in \"tag-key\": want array, got string",
},
{
name: "DescribeInstances error",
req: &pb.OnCreateSetRequest{
Catalog: &hostcatalogs.HostCatalog{
Attributes: mustStruct(map[string]interface{}{
constRegion: "us-west-2",
}),
},
Persisted: &pb.HostCatalogPersisted{
Secrets: mustStruct(map[string]interface{}{
constAccessKeyId: "foobar",
constSecretAccessKey: "bazqux",
constCredsLastRotatedTime: "2006-01-02T15:04:05+07:00",
}),
},
Set: &hostsets.HostSet{
Attributes: mustStruct(map[string]interface{}{
constDescribeInstancesFilters: map[string]interface{}{
"tag-key": []interface{}{"foo"},
},
}),
},
},
opts: []awsCatalogPersistedStateOption{
withTestEC2APIFunc(newTestMockEC2(
nil,
testMockEC2WithDescribeInstancesError(errors.New(testDescribeInstancesError)),
)),
},
expectedErr: fmt.Sprintf("error performing dry run of DescribeInstances: %s", testDescribeInstancesError),
},
{
name: "DescribeInstances non-error",
req: &pb.OnCreateSetRequest{
Catalog: &hostcatalogs.HostCatalog{
Attributes: mustStruct(map[string]interface{}{
constRegion: "us-west-2",
}),
},
Persisted: &pb.HostCatalogPersisted{
Secrets: mustStruct(map[string]interface{}{
constAccessKeyId: "foobar",
constSecretAccessKey: "bazqux",
constCredsLastRotatedTime: "2006-01-02T15:04:05+07:00",
}),
},
Set: &hostsets.HostSet{
Attributes: mustStruct(map[string]interface{}{
constDescribeInstancesFilters: map[string]interface{}{
"tag-key": []interface{}{"foo"},
},
}),
},
},
opts: []awsCatalogPersistedStateOption{
withTestEC2APIFunc(newTestMockEC2(
nil,
)),
},
expectedErr: "query error: DescribeInstances DryRun should have returned error, but none was found",
},
}
for _, tc := range cases {
tc := tc
t.Run(tc.name, func(t *testing.T) {
require := require.New(t)
p := &AwsPlugin{
testStateOpts: tc.opts,
}
_, err := p.OnCreateSet(context.Background(), tc.req)
require.EqualError(err, tc.expectedErr)
})
}
}
func TestOnUpdateSetErr(t *testing.T) {
cases := []struct {
name string
req *pb.OnUpdateSetRequest
opts []awsCatalogPersistedStateOption
expectedErr string
}{
{
name: "nil catalog",
req: &pb.OnUpdateSetRequest{},
expectedErr: "catalog is nil",
},
{
name: "nil catalog attributes",
req: &pb.OnUpdateSetRequest{
Catalog: &hostcatalogs.HostCatalog{
Secrets: new(structpb.Struct),
},
},
expectedErr: "catalog missing attributes",
},
{
name: "persisted state setup error",
req: &pb.OnUpdateSetRequest{
Catalog: &hostcatalogs.HostCatalog{
Attributes: mustStruct(map[string]interface{}{
constRegion: "us-west-2",
}),
},
Persisted: &pb.HostCatalogPersisted{
Secrets: mustStruct(map[string]interface{}{
constAccessKeyId: "foobar",
constSecretAccessKey: "bazqux",
constCredsLastRotatedTime: "2006-01-02T15:04:05+07:00",
}),
},
},
opts: []awsCatalogPersistedStateOption{
func(s *awsCatalogPersistedState) error {
return errors.New(testOptionErr)
},
},
expectedErr: fmt.Sprintf("error loading persisted state: %s", testOptionErr),
},
{
name: "invalid region",
req: &pb.OnUpdateSetRequest{
Catalog: &hostcatalogs.HostCatalog{
Attributes: mustStruct(map[string]interface{}{
constRegion: "foobar",
}),
},
Persisted: &pb.HostCatalogPersisted{
Secrets: mustStruct(map[string]interface{}{
constAccessKeyId: "foobar",
constSecretAccessKey: "bazqux",
constCredsLastRotatedTime: "2006-01-02T15:04:05+07:00",
}),
},
},
expectedErr: "catalog validation error: not a valid region: foobar",
},
{
name: "nil set",
req: &pb.OnUpdateSetRequest{
Catalog: &hostcatalogs.HostCatalog{
Attributes: mustStruct(map[string]interface{}{
constRegion: "us-west-2",
}),
},
Persisted: &pb.HostCatalogPersisted{
Secrets: mustStruct(map[string]interface{}{
constAccessKeyId: "foobar",
constSecretAccessKey: "bazqux",
constCredsLastRotatedTime: "2006-01-02T15:04:05+07:00",
}),
},
},
expectedErr: "new set is nil",
},
{
name: "nil attributes in set",
req: &pb.OnUpdateSetRequest{
Catalog: &hostcatalogs.HostCatalog{
Attributes: mustStruct(map[string]interface{}{
constRegion: "us-west-2",
}),
},
Persisted: &pb.HostCatalogPersisted{
Secrets: mustStruct(map[string]interface{}{
constAccessKeyId: "foobar",
constSecretAccessKey: "bazqux",
constCredsLastRotatedTime: "2006-01-02T15:04:05+07:00",
}),
},
NewSet: &hostsets.HostSet{},
},
expectedErr: "new set missing attributes",
},
{
name: "client load error",
req: &pb.OnUpdateSetRequest{
Catalog: &hostcatalogs.HostCatalog{
Attributes: mustStruct(map[string]interface{}{
constRegion: "us-west-2",
}),
},
Persisted: &pb.HostCatalogPersisted{
Secrets: mustStruct(map[string]interface{}{
constAccessKeyId: "foobar",
constSecretAccessKey: "bazqux",
constCredsLastRotatedTime: "2006-01-02T15:04:05+07:00",
}),
},
NewSet: &hostsets.HostSet{
Attributes: mustStruct(map[string]interface{}{
constDescribeInstancesFilters: map[string]interface{}{
"tag-key": []interface{}{"foo"},
},
}),
},
},
opts: []awsCatalogPersistedStateOption{
withStateTestOpts([]awsutil.Option{
awsutil.MockOptionErr(errors.New(testOptionErr)),
}),
},
expectedErr: fmt.Sprintf("error getting EC2 client: error getting AWS session: error reading options in NewCredentialsConfig: %s", testOptionErr),
},
{
name: "buildDescribeInstancesInput error",
req: &pb.OnUpdateSetRequest{
Catalog: &hostcatalogs.HostCatalog{
Attributes: mustStruct(map[string]interface{}{
constRegion: "us-west-2",
}),
},
Persisted: &pb.HostCatalogPersisted{
Secrets: mustStruct(map[string]interface{}{
constAccessKeyId: "foobar",
constSecretAccessKey: "bazqux",
constCredsLastRotatedTime: "2006-01-02T15:04:05+07:00",
}),
},
NewSet: &hostsets.HostSet{
Attributes: mustStruct(map[string]interface{}{
constDescribeInstancesFilters: map[string]interface{}{
"tag-key": "foo",
},
}),
},
},
expectedErr: "error building DescribeInstances parameters: error building filters: unexpected type for filter values in \"tag-key\": want array, got string",
},
{
name: "DescribeInstances error",
req: &pb.OnUpdateSetRequest{
Catalog: &hostcatalogs.HostCatalog{
Attributes: mustStruct(map[string]interface{}{
constRegion: "us-west-2",
}),
},
Persisted: &pb.HostCatalogPersisted{
Secrets: mustStruct(map[string]interface{}{
constAccessKeyId: "foobar",
constSecretAccessKey: "bazqux",
constCredsLastRotatedTime: "2006-01-02T15:04:05+07:00",
}),
},
NewSet: &hostsets.HostSet{
Attributes: mustStruct(map[string]interface{}{
constDescribeInstancesFilters: map[string]interface{}{
"tag-key": []interface{}{"foo"},
},
}),
},
},
opts: []awsCatalogPersistedStateOption{
withTestEC2APIFunc(newTestMockEC2(
nil,
testMockEC2WithDescribeInstancesError(errors.New(testDescribeInstancesError)),
)),
},
expectedErr: fmt.Sprintf("error performing dry run of DescribeInstances: %s", testDescribeInstancesError),
},
{
name: "DescribeInstances non-error",
req: &pb.OnUpdateSetRequest{
Catalog: &hostcatalogs.HostCatalog{
Attributes: mustStruct(map[string]interface{}{
constRegion: "us-west-2",
}),
},
Persisted: &pb.HostCatalogPersisted{
Secrets: mustStruct(map[string]interface{}{
constAccessKeyId: "foobar",
constSecretAccessKey: "bazqux",
constCredsLastRotatedTime: "2006-01-02T15:04:05+07:00",
}),
},
NewSet: &hostsets.HostSet{
Attributes: mustStruct(map[string]interface{}{
constDescribeInstancesFilters: map[string]interface{}{
"tag-key": []interface{}{"foo"},
},
}),
},
},
opts: []awsCatalogPersistedStateOption{
withTestEC2APIFunc(newTestMockEC2(
nil,
)),
},
expectedErr: "query error: DescribeInstances DryRun should have returned error, but none was found",
},
}
for _, tc := range cases {
tc := tc
t.Run(tc.name, func(t *testing.T) {
require := require.New(t)
p := &AwsPlugin{
testStateOpts: tc.opts,
}
_, err := p.OnUpdateSet(context.Background(), tc.req)
require.EqualError(err, tc.expectedErr)
})
}
}
func TestListHostsErr(t *testing.T) {
cases := []struct {
name string
req *pb.ListHostsRequest
opts []awsCatalogPersistedStateOption
expectedErr string
}{
{
name: "nil catalog",
req: &pb.ListHostsRequest{},
expectedErr: "catalog is nil",
},
{
name: "nil catalog attributes",
req: &pb.ListHostsRequest{
Catalog: &hostcatalogs.HostCatalog{
Secrets: new(structpb.Struct),
},
},
expectedErr: "catalog missing attributes",
},
{
name: "persisted state setup error",
req: &pb.ListHostsRequest{
Catalog: &hostcatalogs.HostCatalog{
Attributes: mustStruct(map[string]interface{}{
constRegion: "us-west-2",
}),
},
Persisted: &pb.HostCatalogPersisted{
Secrets: mustStruct(map[string]interface{}{
constAccessKeyId: "foobar",
constSecretAccessKey: "bazqux",
constCredsLastRotatedTime: "2006-01-02T15:04:05+07:00",
}),
},
},
opts: []awsCatalogPersistedStateOption{
func(s *awsCatalogPersistedState) error {
return errors.New(testOptionErr)
},
},
expectedErr: fmt.Sprintf("error loading persisted state: %s", testOptionErr),
},
{
name: "invalid region",
req: &pb.ListHostsRequest{
Catalog: &hostcatalogs.HostCatalog{
Attributes: mustStruct(map[string]interface{}{
constRegion: "foobar",
}),
},
Persisted: &pb.HostCatalogPersisted{
Secrets: mustStruct(map[string]interface{}{
constAccessKeyId: "foobar",
constSecretAccessKey: "bazqux",
constCredsLastRotatedTime: "2006-01-02T15:04:05+07:00",
}),
},
},
expectedErr: "catalog validation error: not a valid region: foobar",
},
{
name: "nil sets",
req: &pb.ListHostsRequest{
Catalog: &hostcatalogs.HostCatalog{
Attributes: mustStruct(map[string]interface{}{
constRegion: "us-west-2",
}),
},
Persisted: &pb.HostCatalogPersisted{
Secrets: mustStruct(map[string]interface{}{
constAccessKeyId: "foobar",
constSecretAccessKey: "bazqux",
constCredsLastRotatedTime: "2006-01-02T15:04:05+07:00",
}),
},
},
expectedErr: "sets is nil",
},
{
name: "set missing id",
req: &pb.ListHostsRequest{
Catalog: &hostcatalogs.HostCatalog{
Attributes: mustStruct(map[string]interface{}{
constRegion: "us-west-2",
}),
},
Persisted: &pb.HostCatalogPersisted{
Secrets: mustStruct(map[string]interface{}{
constAccessKeyId: "foobar",
constSecretAccessKey: "bazqux",
constCredsLastRotatedTime: "2006-01-02T15:04:05+07:00",
}),
},
Sets: []*hostsets.HostSet{&hostsets.HostSet{}},
},
expectedErr: "set missing id",
},
{
name: "set missing attributes",
req: &pb.ListHostsRequest{
Catalog: &hostcatalogs.HostCatalog{
Attributes: mustStruct(map[string]interface{}{
constRegion: "us-west-2",
}),
},
Persisted: &pb.HostCatalogPersisted{
Secrets: mustStruct(map[string]interface{}{
constAccessKeyId: "foobar",
constSecretAccessKey: "bazqux",
constCredsLastRotatedTime: "2006-01-02T15:04:05+07:00",
}),
},
Sets: []*hostsets.HostSet{
&hostsets.HostSet{
Id: "foobar",
},
},
},
expectedErr: "set missing attributes",
},
{
name: "buildDescribeInstancesInput error",
req: &pb.ListHostsRequest{
Catalog: &hostcatalogs.HostCatalog{
Attributes: mustStruct(map[string]interface{}{
constRegion: "us-west-2",
}),
},
Persisted: &pb.HostCatalogPersisted{
Secrets: mustStruct(map[string]interface{}{
constAccessKeyId: "foobar",
constSecretAccessKey: "bazqux",
constCredsLastRotatedTime: "2006-01-02T15:04:05+07:00",
}),
},
Sets: []*hostsets.HostSet{
&hostsets.HostSet{
Id: "foobar",
Attributes: mustStruct(map[string]interface{}{
constDescribeInstancesFilters: map[string]interface{}{
"tag-key": "foo",
},
}),
},
},
},
expectedErr: "error building DescribeInstances parameters for host set id \"foobar\": error building filters: unexpected type for filter values in \"tag-key\": want array, got string",
},
{
name: "client load error",
req: &pb.ListHostsRequest{
Catalog: &hostcatalogs.HostCatalog{
Attributes: mustStruct(map[string]interface{}{
constRegion: "us-west-2",
}),
},
Persisted: &pb.HostCatalogPersisted{
Secrets: mustStruct(map[string]interface{}{
constAccessKeyId: "foobar",
constSecretAccessKey: "bazqux",
constCredsLastRotatedTime: "2006-01-02T15:04:05+07:00",
}),
},
Sets: []*hostsets.HostSet{
&hostsets.HostSet{
Id: "foobar",
Attributes: mustStruct(map[string]interface{}{
constDescribeInstancesFilters: map[string]interface{}{
"tag-key": []interface{}{"foo"},
},
}),
},
},
},
opts: []awsCatalogPersistedStateOption{
withStateTestOpts([]awsutil.Option{
awsutil.MockOptionErr(errors.New(testOptionErr)),
}),
},
expectedErr: fmt.Sprintf("error getting EC2 client: error getting AWS session: error reading options in NewCredentialsConfig: %s", testOptionErr),
},
{
name: "DescribeInstances error",
req: &pb.ListHostsRequest{
Catalog: &hostcatalogs.HostCatalog{
Attributes: mustStruct(map[string]interface{}{
constRegion: "us-west-2",
}),
},
Persisted: &pb.HostCatalogPersisted{
Secrets: mustStruct(map[string]interface{}{
constAccessKeyId: "foobar",
constSecretAccessKey: "bazqux",
constCredsLastRotatedTime: "2006-01-02T15:04:05+07:00",
}),
},
Sets: []*hostsets.HostSet{
&hostsets.HostSet{
Id: "foobar",
Attributes: mustStruct(map[string]interface{}{
constDescribeInstancesFilters: map[string]interface{}{
"tag-key": []interface{}{"foo"},
},
}),
},
},
},
opts: []awsCatalogPersistedStateOption{
withTestEC2APIFunc(newTestMockEC2(
nil,
testMockEC2WithDescribeInstancesError(errors.New(testDescribeInstancesError)),
)),
},
expectedErr: fmt.Sprintf("error running DescribeInstances for host set id \"foobar\": %s", testDescribeInstancesError),
},
{
name: "awsInstanceToHost error",
req: &pb.ListHostsRequest{
Catalog: &hostcatalogs.HostCatalog{
Attributes: mustStruct(map[string]interface{}{
constRegion: "us-west-2",
}),
},
Persisted: &pb.HostCatalogPersisted{
Secrets: mustStruct(map[string]interface{}{
constAccessKeyId: "foobar",
constSecretAccessKey: "bazqux",
constCredsLastRotatedTime: "2006-01-02T15:04:05+07:00",
}),
},
Sets: []*hostsets.HostSet{
&hostsets.HostSet{
Id: "foobar",
Attributes: mustStruct(map[string]interface{}{
constDescribeInstancesFilters: map[string]interface{}{
"tag-key": []interface{}{"foo"},
},
}),
},
},
},
opts: []awsCatalogPersistedStateOption{
withTestEC2APIFunc(newTestMockEC2(
nil,
testMockEC2WithDescribeInstancesOutput(
&ec2.DescribeInstancesOutput{
Reservations: []*ec2.Reservation{
&ec2.Reservation{
Instances: []*ec2.Instance{
&ec2.Instance{
// Blank so we error out
},
},
},
},
},
),
)),
},
expectedErr: "error processing host results for host set id \"foobar\": response integrity error: missing instance state",
},
}
for _, tc := range cases {
tc := tc
t.Run(tc.name, func(t *testing.T) {
require := require.New(t)
p := &AwsPlugin{
testStateOpts: tc.opts,
}
_, err := p.ListHosts(context.Background(), tc.req)
require.EqualError(err, tc.expectedErr)
})
}
}