Compare commits

...
Author SHA1 Message Date
A-440Hz 95f9e80b81 test: refactor error syntax 2022-03-10 10:03:37 -08:00
A-440Hz 9e1fb04057 test: update tests to validate error codes 2022-03-09 15:23:22 -08:00
A-440Hz 4656760fb8 refactor: update errors to include error codes 2022-03-08 16:12:16 -08:00
Chris Marchesi 6ce3e27677 Merge pull request #8 from hashicorp/update-readme-cli-example
Update README to have full, correct examples
2022-01-04 16:47:53 -08:00
4 changed files with 241 additions and 136 deletions

No files matched your search

+39 -11
View File
@@ -7,9 +7,23 @@ import (
"time" "time"
"github.com/mitchellh/mapstructure" "github.com/mitchellh/mapstructure"
"google.golang.org/grpc/codes"
"google.golang.org/grpc/status"
"google.golang.org/protobuf/types/known/structpb" "google.golang.org/protobuf/types/known/structpb"
) )
func invalidArgumentError(msg string, f map[string]string) error {
var fieldMsgs []string
for field, val := range f {
fieldMsgs = append(fieldMsgs, fmt.Sprintf("%s: %s", field, val))
}
if len(fieldMsgs) > 0 {
sort.Strings(fieldMsgs)
msg = fmt.Sprintf("%s: [%s]", msg, strings.Join(fieldMsgs, ", "))
}
return status.Error(codes.InvalidArgument, msg)
}
// CatalogAttributes is a Go-native representation of the Attributes // CatalogAttributes is a Go-native representation of the Attributes
// map. // map.
type CatalogAttributes struct { type CatalogAttributes struct {
@@ -20,22 +34,27 @@ type CatalogAttributes struct {
func getCatalogAttributes(in *structpb.Struct) (*CatalogAttributes, error) { func getCatalogAttributes(in *structpb.Struct) (*CatalogAttributes, error) {
unknownFields := structFields(in) unknownFields := structFields(in)
result := new(CatalogAttributes) result := new(CatalogAttributes)
badFields := make(map[string]string)
var err error var err error
result.Region, err = getStringValue(in, constRegion, true) result.Region, err = getStringValue(in, constRegion, true)
if err != nil { if err != nil {
return nil, err badFields[fmt.Sprintf("attributes.%s", constRegion)] = err.Error()
} }
delete(unknownFields, constRegion) delete(unknownFields, constRegion)
result.DisableCredentialRotation, err = getBoolValue(in, constDisableCredentialRotation, false) result.DisableCredentialRotation, err = getBoolValue(in, constDisableCredentialRotation, false)
if err != nil { if err != nil {
return nil, err badFields[fmt.Sprintf("attributes.%s", constDisableCredentialRotation)] = err.Error()
} }
delete(unknownFields, constDisableCredentialRotation) delete(unknownFields, constDisableCredentialRotation)
if len(unknownFields) != 0 { for s := range unknownFields {
return nil, fmt.Errorf("unknown catalog attribute fields provided: %s", keysAsString(unknownFields)) badFields[fmt.Sprintf("attributes.%s", s)] = "unrecognized field"
}
if len(badFields) > 0 {
return nil, invalidArgumentError("Invalid arguments in catalog attributes", badFields)
} }
return result, nil return result, nil
@@ -50,22 +69,27 @@ type CatalogSecrets struct {
func getCatalogSecrets(in *structpb.Struct) (*CatalogSecrets, error) { func getCatalogSecrets(in *structpb.Struct) (*CatalogSecrets, error) {
unknownFields := structFields(in) unknownFields := structFields(in)
result := new(CatalogSecrets) result := new(CatalogSecrets)
badFields := make(map[string]string)
var err error var err error
result.AccessKeyId, err = getStringValue(in, constAccessKeyId, true) result.AccessKeyId, err = getStringValue(in, constAccessKeyId, true)
if err != nil { if err != nil {
return nil, err badFields[fmt.Sprintf("secrets.%s", constAccessKeyId)] = err.Error()
} }
delete(unknownFields, constAccessKeyId) delete(unknownFields, constAccessKeyId)
result.SecretAccessKey, err = getStringValue(in, constSecretAccessKey, true) result.SecretAccessKey, err = getStringValue(in, constSecretAccessKey, true)
if err != nil { if err != nil {
return nil, err badFields[fmt.Sprintf("secrets.%s", constSecretAccessKey)] = err.Error()
} }
delete(unknownFields, constSecretAccessKey) delete(unknownFields, constSecretAccessKey)
if len(unknownFields) != 0 { for s := range unknownFields {
return nil, fmt.Errorf("unknown catalog secret fields provided: %s", keysAsString(unknownFields)) badFields[fmt.Sprintf("secrets.%s", s)] = "unrecognized field"
}
if len(badFields) > 0 {
return nil, invalidArgumentError("Error in the secrets provided", badFields)
} }
return result, nil return result, nil
@@ -84,9 +108,13 @@ func getSetAttributes(in *structpb.Struct) (*SetAttributes, error) {
// here for now. Make this more complex if we add more attributes // here for now. Make this more complex if we add more attributes
// to host sets. // to host sets.
unknownFields := structFields(in) unknownFields := structFields(in)
badFields := make(map[string]string)
delete(unknownFields, constDescribeInstancesFilters) delete(unknownFields, constDescribeInstancesFilters)
if len(unknownFields) != 0 { for a := range unknownFields {
return nil, fmt.Errorf("unknown set attribute fields provided: %s", keysAsString(unknownFields)) badFields[fmt.Sprintf("attributes.%s", a)] = "unrecognized field"
}
if len(badFields) > 0 {
return nil, invalidArgumentError("Error in the attributes provided", badFields)
} }
// Mapstructure complains if it expects a slice as output and sees a scalar // Mapstructure complains if it expects a slice as output and sees a scalar
@@ -101,7 +129,7 @@ func getSetAttributes(in *structpb.Struct) (*SetAttributes, error) {
} }
if err := mapstructure.Decode(inMap, &setAttrs); err != nil { if err := mapstructure.Decode(inMap, &setAttrs); err != nil {
return nil, fmt.Errorf("error decoding set attributes: %w", err) return nil, status.Errorf(codes.InvalidArgument, "error decoding set attributes: %s", err)
} }
return &setAttrs, nil return &setAttrs, nil
+10 -4
View File
@@ -5,6 +5,8 @@ import (
"time" "time"
"github.com/stretchr/testify/require" "github.com/stretchr/testify/require"
"google.golang.org/grpc/codes"
"google.golang.org/grpc/status"
"google.golang.org/protobuf/types/known/structpb" "google.golang.org/protobuf/types/known/structpb"
) )
@@ -35,7 +37,7 @@ func TestGetCatalogAttributes(t *testing.T) {
"foo": true, "foo": true,
"bar": true, "bar": true,
}), }),
expectedErrContains: "unknown catalog attribute fields provided: bar, foo", expectedErrContains: "attributes.bar: Unrecognized field, attributes.foo: Unrecognized field",
}, },
{ {
name: "default", name: "default",
@@ -68,6 +70,7 @@ func TestGetCatalogAttributes(t *testing.T) {
if tc.expectedErrContains != "" { if tc.expectedErrContains != "" {
require.Error(err) require.Error(err)
require.Contains(err.Error(), tc.expectedErrContains) require.Contains(err.Error(), tc.expectedErrContains)
require.Equal(status.Code(err), codes.InvalidArgument)
return return
} }
@@ -104,7 +107,7 @@ func TestGetCatalogSecrets(t *testing.T) {
"foo": true, "foo": true,
"bar": true, "bar": true,
}), }),
expectedErrContains: "unknown catalog secret fields provided: bar, foo", expectedErrContains: "secrets.bar: Unrecognized field, secrets.foo: Unrecognized field",
}, },
{ {
name: "good", name: "good",
@@ -127,6 +130,7 @@ func TestGetCatalogSecrets(t *testing.T) {
if tc.expectedErrContains != "" { if tc.expectedErrContains != "" {
require.Error(err) require.Error(err)
require.Contains(err.Error(), tc.expectedErrContains) require.Contains(err.Error(), tc.expectedErrContains)
require.Equal(status.Code(err), codes.InvalidArgument)
return return
} }
@@ -170,7 +174,7 @@ func TestGetSetAttributes(t *testing.T) {
"foo": true, "foo": true,
"bar": true, "bar": true,
}), }),
expectedErrContains: "unknown set attribute fields provided: bar, foo", expectedErrContains: "attributes.bar: Unrecognized field, attributes.foo: Unrecognized field",
}, },
{ {
name: "good", name: "good",
@@ -194,6 +198,7 @@ func TestGetSetAttributes(t *testing.T) {
if tc.expectedErrContains != "" { if tc.expectedErrContains != "" {
require.Error(err) require.Error(err)
require.Contains(err.Error(), tc.expectedErrContains) require.Contains(err.Error(), tc.expectedErrContains)
require.Equal(status.Code(err), codes.InvalidArgument)
return return
} }
@@ -413,7 +418,8 @@ func TestValidateRegion(t *testing.T) {
require := require.New(t) require := require.New(t)
err := validateRegion(tc.in) err := validateRegion(tc.in)
if tc.expectedErr != "" { if tc.expectedErr != "" {
require.EqualError(err, tc.expectedErr) require.Contains(err.Error(), tc.expectedErr)
require.Equal(status.Code(err), codes.InvalidArgument)
return return
} }
+56 -54
View File
@@ -12,6 +12,8 @@ import (
"github.com/aws/aws-sdk-go/aws/endpoints" "github.com/aws/aws-sdk-go/aws/endpoints"
"github.com/aws/aws-sdk-go/service/ec2" "github.com/aws/aws-sdk-go/service/ec2"
pb "github.com/hashicorp/boundary/sdk/pbs/plugin" pb "github.com/hashicorp/boundary/sdk/pbs/plugin"
"google.golang.org/grpc/codes"
"google.golang.org/grpc/status"
) )
// rotationWaitTimeout controls the time we wait for credential rotation to // rotationWaitTimeout controls the time we wait for credential rotation to
@@ -36,17 +38,17 @@ var (
func (p *AwsPlugin) OnCreateCatalog(ctx context.Context, req *pb.OnCreateCatalogRequest) (*pb.OnCreateCatalogResponse, error) { func (p *AwsPlugin) OnCreateCatalog(ctx context.Context, req *pb.OnCreateCatalogRequest) (*pb.OnCreateCatalogResponse, error) {
catalog := req.GetCatalog() catalog := req.GetCatalog()
if catalog == nil { if catalog == nil {
return nil, errors.New("catalog is nil") return nil, status.Error(codes.InvalidArgument, "catalog is nil")
} }
secrets := catalog.GetSecrets() secrets := catalog.GetSecrets()
if secrets == nil { if secrets == nil {
return nil, errors.New("secrets are required") return nil, status.Error(codes.InvalidArgument, "secrets are required")
} }
attrs := catalog.GetAttributes() attrs := catalog.GetAttributes()
if attrs == nil { if attrs == nil {
return nil, errors.New("attributes are required") return nil, status.Error(codes.InvalidArgument, "attributes are required")
} }
catalogAttributes, err := getCatalogAttributes(attrs) catalogAttributes, err := getCatalogAttributes(attrs)
@@ -68,24 +70,24 @@ func (p *AwsPlugin) OnCreateCatalog(ctx context.Context, req *pb.OnCreateCatalog
withSecretAccessKey(catalogSecrets.SecretAccessKey), withSecretAccessKey(catalogSecrets.SecretAccessKey),
}, p.testStateOpts...)...) }, p.testStateOpts...)...)
if err != nil { if err != nil {
return nil, fmt.Errorf("error setting up persisted state: %w", err) return nil, status.Errorf(codes.InvalidArgument, "error setting up persisted state: %s", err)
} }
// Try to rotate the credentials if we're not skipping them. // Try to rotate the credentials if we're not skipping them.
if !catalogAttributes.DisableCredentialRotation { if !catalogAttributes.DisableCredentialRotation {
if err := state.RotateCreds(); err != nil { if err := state.RotateCreds(); err != nil {
return nil, fmt.Errorf("error during credential rotation: %w", err) return nil, status.Errorf(codes.InvalidArgument, "error during credential rotation: %s", err)
} }
} else { } else {
// Simply validate if we aren't rotating. // Simply validate if we aren't rotating.
if err := state.ValidateCreds(); err != nil { if err := state.ValidateCreds(); err != nil {
return nil, fmt.Errorf("error during credential validation: %w", err) return nil, status.Errorf(codes.InvalidArgument, "error during credential validation: %s", err)
} }
} }
persistedProto, err := state.ToProto() persistedProto, err := state.ToProto()
if err != nil { if err != nil {
return nil, err return nil, status.Error(codes.InvalidArgument, err.Error())
} }
return &pb.OnCreateCatalogResponse{ return &pb.OnCreateCatalogResponse{
@@ -99,7 +101,7 @@ func (p *AwsPlugin) OnUpdateCatalog(ctx context.Context, req *pb.OnUpdateCatalog
// fetching the new copy to keep things a little less messy. // fetching the new copy to keep things a little less messy.
catalog := req.GetNewCatalog() catalog := req.GetNewCatalog()
if catalog == nil { if catalog == nil {
return nil, errors.New("new catalog is nil") return nil, status.Error(codes.InvalidArgument, "new catalog is nil")
} }
var updateSecrets bool var updateSecrets bool
@@ -112,7 +114,7 @@ func (p *AwsPlugin) OnUpdateCatalog(ctx context.Context, req *pb.OnUpdateCatalog
attrs := catalog.GetAttributes() attrs := catalog.GetAttributes()
if attrs == nil { if attrs == nil {
return nil, errors.New("new catalog missing attributes") return nil, status.Error(codes.InvalidArgument, "new catalog missing attributes")
} }
catalogAttributes, err := getCatalogAttributes(attrs) catalogAttributes, err := getCatalogAttributes(attrs)
@@ -134,7 +136,7 @@ func (p *AwsPlugin) OnUpdateCatalog(ctx context.Context, req *pb.OnUpdateCatalog
// is fine and important, but this may change in the future. // is fine and important, but this may change in the future.
state, err := awsCatalogPersistedStateFromProto(req.GetPersisted(), p.testStateOpts...) state, err := awsCatalogPersistedStateFromProto(req.GetPersisted(), p.testStateOpts...)
if err != nil { if err != nil {
return nil, fmt.Errorf("error loading persisted state: %w", err) return nil, status.Errorf(codes.InvalidArgument, "error loading persisted state: %s", err)
} }
if catalogAttributes.DisableCredentialRotation && !updateSecrets { if catalogAttributes.DisableCredentialRotation && !updateSecrets {
@@ -142,7 +144,7 @@ func (p *AwsPlugin) OnUpdateCatalog(ctx context.Context, req *pb.OnUpdateCatalog
// rotation for credentials currently being managed by rotation. // rotation for credentials currently being managed by rotation.
// This is not allowed. // This is not allowed.
if !state.CredsLastRotatedTime.IsZero() { if !state.CredsLastRotatedTime.IsZero() {
return nil, errors.New("cannot disable rotation for already-rotated credentials") return nil, status.Error(codes.FailedPrecondition, "cannot disable rotation for already-rotated credentials")
} }
} }
@@ -156,7 +158,7 @@ func (p *AwsPlugin) OnUpdateCatalog(ctx context.Context, req *pb.OnUpdateCatalog
// rotation time as well and deletes the credentials if we are // rotation time as well and deletes the credentials if we are
// managing them (ie: if we've rotated them before). // managing them (ie: if we've rotated them before).
if err := state.ReplaceCreds(catalogSecrets.AccessKeyId, catalogSecrets.SecretAccessKey); err != nil { if err := state.ReplaceCreds(catalogSecrets.AccessKeyId, catalogSecrets.SecretAccessKey); err != nil {
return nil, fmt.Errorf("error attempting to replace credentials: %w", err) return nil, status.Errorf(codes.InvalidArgument, "error attempting to replace credentials: %s", err)
} }
} }
@@ -164,14 +166,14 @@ func (p *AwsPlugin) OnUpdateCatalog(ctx context.Context, req *pb.OnUpdateCatalog
// If we're enabling rotation now but didn't before, or have // If we're enabling rotation now but didn't before, or have
// freshly replaced credentials, we can rotate here. // freshly replaced credentials, we can rotate here.
if err := state.RotateCreds(); err != nil { if err := state.RotateCreds(); err != nil {
return nil, fmt.Errorf("error during credential rotation: %w", err) return nil, status.Errorf(codes.InvalidArgument, "error during credential rotation: %s", err)
} }
} }
// That's it! // That's it!
persistedProto, err := state.ToProto() persistedProto, err := state.ToProto()
if err != nil { if err != nil {
return nil, err return nil, status.Error(codes.InvalidArgument, err.Error())
} }
return &pb.OnUpdateCatalogResponse{ return &pb.OnUpdateCatalogResponse{
@@ -188,7 +190,7 @@ func (p *AwsPlugin) OnDeleteCatalog(ctx context.Context, req *pb.OnDeleteCatalog
// may be corrupt/and or legitimately missing. // may be corrupt/and or legitimately missing.
state, err := awsCatalogPersistedStateFromProto(req.GetPersisted(), p.testStateOpts...) state, err := awsCatalogPersistedStateFromProto(req.GetPersisted(), p.testStateOpts...)
if err != nil { if err != nil {
return nil, fmt.Errorf("error loading persisted state: %w", err) return nil, status.Errorf(codes.InvalidArgument, "error loading persisted state: %s", err)
} }
if !state.CredsLastRotatedTime.IsZero() { if !state.CredsLastRotatedTime.IsZero() {
@@ -196,7 +198,7 @@ func (p *AwsPlugin) OnDeleteCatalog(ctx context.Context, req *pb.OnDeleteCatalog
// credentials to ensure that it has the proper permissions to do // credentials to ensure that it has the proper permissions to do
// it. // it.
if err := state.DeleteCreds(); err != nil { if err := state.DeleteCreds(); err != nil {
return nil, fmt.Errorf("error removing rotated credentials during catalog deletion: %w", err) return nil, status.Errorf(codes.Aborted, "error removing rotated credentials during catalog deletion: %s", err)
} }
} }
@@ -206,54 +208,54 @@ func (p *AwsPlugin) OnDeleteCatalog(ctx context.Context, req *pb.OnDeleteCatalog
func (p *AwsPlugin) OnCreateSet(ctx context.Context, req *pb.OnCreateSetRequest) (*pb.OnCreateSetResponse, error) { func (p *AwsPlugin) OnCreateSet(ctx context.Context, req *pb.OnCreateSetRequest) (*pb.OnCreateSetResponse, error) {
catalog := req.GetCatalog() catalog := req.GetCatalog()
if catalog == nil { if catalog == nil {
return nil, errors.New("catalog is nil") return nil, status.Error(codes.InvalidArgument, "catalog is nil")
} }
catalogAttrsRaw := catalog.GetAttributes() catalogAttrsRaw := catalog.GetAttributes()
if catalogAttrsRaw == nil { if catalogAttrsRaw == nil {
return nil, errors.New("catalog missing attributes") return nil, status.Error(codes.InvalidArgument, "catalog missing attributes")
} }
catalogAttributes, err := getCatalogAttributes(catalogAttrsRaw) catalogAttributes, err := getCatalogAttributes(catalogAttrsRaw)
if err != nil { if err != nil {
return nil, fmt.Errorf("integrity error in host catalog attributes: %w", err) return nil, err
} }
if err := validateRegion(catalogAttributes.Region); err != nil { if err := validateRegion(catalogAttributes.Region); err != nil {
return nil, fmt.Errorf("integrity error in host catalog attributes: %w", err) return nil, err
} }
state, err := awsCatalogPersistedStateFromProto(req.GetPersisted(), p.testStateOpts...) state, err := awsCatalogPersistedStateFromProto(req.GetPersisted(), p.testStateOpts...)
if err != nil { if err != nil {
return nil, fmt.Errorf("error loading persisted state: %w", err) return nil, status.Errorf(codes.InvalidArgument, "error loading persisted state: %s", err)
} }
set := req.GetSet() set := req.GetSet()
if set == nil { if set == nil {
return nil, errors.New("set is nil") return nil, status.Error(codes.InvalidArgument, "set is nil")
} }
if set.GetAttributes() == nil { if set.GetAttributes() == nil {
return nil, errors.New("set missing attributes") return nil, status.Error(codes.InvalidArgument, "set missing attributes")
} }
setAttrs, err := getSetAttributes(set.GetAttributes()) setAttrs, err := getSetAttributes(set.GetAttributes())
if err != nil { if err != nil {
return nil, fmt.Errorf("error parsing set attributes: %w", err) return nil, err
} }
ec2Client, err := state.EC2Client(catalogAttributes.Region) ec2Client, err := state.EC2Client(catalogAttributes.Region)
if err != nil { if err != nil {
return nil, fmt.Errorf("error getting EC2 client: %w", err) return nil, status.Errorf(codes.InvalidArgument, "error getting EC2 client: %s", err)
} }
input, err := buildDescribeInstancesInput(setAttrs, true) input, err := buildDescribeInstancesInput(setAttrs, true)
if err != nil { if err != nil {
return nil, fmt.Errorf("error building DescribeInstances parameters: %w", err) return nil, status.Errorf(codes.InvalidArgument, "error building DescribeInstances parameters: %s", err)
} }
_, err = ec2Client.DescribeInstances(input) _, err = ec2Client.DescribeInstances(input)
if err == nil { if err == nil {
return nil, errors.New("query error: DescribeInstances DryRun should have returned error, but none was found") return nil, status.Error(codes.FailedPrecondition, "query error: DescribeInstances DryRun should have returned error, but none was found")
} }
if awsErr, ok := err.(awserr.Error); ok && awsErr.Code() == "DryRunOperation" { if awsErr, ok := err.(awserr.Error); ok && awsErr.Code() == "DryRunOperation" {
@@ -261,62 +263,62 @@ func (p *AwsPlugin) OnCreateSet(ctx context.Context, req *pb.OnCreateSetRequest)
return &pb.OnCreateSetResponse{}, nil return &pb.OnCreateSetResponse{}, nil
} }
return nil, fmt.Errorf("error performing dry run of DescribeInstances: %w", err) return nil, status.Errorf(codes.InvalidArgument, "error performing dry run of DescribeInstances: %s", err)
} }
func (p *AwsPlugin) OnUpdateSet(ctx context.Context, req *pb.OnUpdateSetRequest) (*pb.OnUpdateSetResponse, error) { func (p *AwsPlugin) OnUpdateSet(ctx context.Context, req *pb.OnUpdateSetRequest) (*pb.OnUpdateSetResponse, error) {
catalog := req.GetCatalog() catalog := req.GetCatalog()
if catalog == nil { if catalog == nil {
return nil, errors.New("catalog is nil") return nil, status.Error(codes.InvalidArgument, "catalog is nil")
} }
catalogAttrsRaw := catalog.GetAttributes() catalogAttrsRaw := catalog.GetAttributes()
if catalogAttrsRaw == nil { if catalogAttrsRaw == nil {
return nil, errors.New("catalog missing attributes") return nil, status.Error(codes.InvalidArgument, "catalog missing attributes")
} }
catalogAttributes, err := getCatalogAttributes(catalogAttrsRaw) catalogAttributes, err := getCatalogAttributes(catalogAttrsRaw)
if err != nil { if err != nil {
return nil, fmt.Errorf("integrity error in host catalog attributes: %w", err) return nil, err
} }
if err := validateRegion(catalogAttributes.Region); err != nil { if err := validateRegion(catalogAttributes.Region); err != nil {
return nil, fmt.Errorf("integrity error in host catalog attributes: %w", err) return nil, err
} }
state, err := awsCatalogPersistedStateFromProto(req.GetPersisted(), p.testStateOpts...) state, err := awsCatalogPersistedStateFromProto(req.GetPersisted(), p.testStateOpts...)
if err != nil { if err != nil {
return nil, fmt.Errorf("error loading persisted state: %w", err) return nil, status.Errorf(codes.InvalidArgument, "error loading persisted state: %s", err)
} }
// As with catalog, we don't need to really look at the old host // As with catalog, we don't need to really look at the old host
// set here, just need to work off of/validate the new set config // set here, just need to work off of/validate the new set config
set := req.GetNewSet() set := req.GetNewSet()
if set == nil { if set == nil {
return nil, errors.New("new set is nil") return nil, status.Error(codes.InvalidArgument, "new set is nil")
} }
if set.GetAttributes() == nil { if set.GetAttributes() == nil {
return nil, errors.New("new set missing attributes") return nil, status.Error(codes.InvalidArgument, "new set missing attributes")
} }
setAttrs, err := getSetAttributes(set.GetAttributes()) setAttrs, err := getSetAttributes(set.GetAttributes())
if err != nil { if err != nil {
return nil, fmt.Errorf("error parsing set attributes: %w", err) return nil, err
} }
ec2Client, err := state.EC2Client(catalogAttributes.Region) ec2Client, err := state.EC2Client(catalogAttributes.Region)
if err != nil { if err != nil {
return nil, fmt.Errorf("error getting EC2 client: %w", err) return nil, status.Errorf(codes.InvalidArgument, "error getting EC2 client: %s", err)
} }
input, err := buildDescribeInstancesInput(setAttrs, true) input, err := buildDescribeInstancesInput(setAttrs, true)
if err != nil { if err != nil {
return nil, fmt.Errorf("error building DescribeInstances parameters: %w", err) return nil, status.Errorf(codes.InvalidArgument, "error building DescribeInstances parameters: %s", err)
} }
_, err = ec2Client.DescribeInstances(input) _, err = ec2Client.DescribeInstances(input)
if err == nil { if err == nil {
return nil, errors.New("query error: DescribeInstances DryRun should have returned error, but none was found") return nil, status.Error(codes.FailedPrecondition, "query error: DescribeInstances DryRun should have returned error, but none was found")
} }
if awsErr, ok := err.(awserr.Error); ok && awsErr.Code() == "DryRunOperation" { if awsErr, ok := err.(awserr.Error); ok && awsErr.Code() == "DryRunOperation" {
@@ -324,7 +326,7 @@ func (p *AwsPlugin) OnUpdateSet(ctx context.Context, req *pb.OnUpdateSetRequest)
return &pb.OnUpdateSetResponse{}, nil return &pb.OnUpdateSetResponse{}, nil
} }
return nil, fmt.Errorf("error performing dry run of DescribeInstances: %w", err) return nil, status.Errorf(codes.InvalidArgument, "error performing dry run of DescribeInstances: %s", err)
} }
func (p *AwsPlugin) OnDeleteSet(ctx context.Context, req *pb.OnDeleteSetRequest) (*pb.OnDeleteSetResponse, error) { func (p *AwsPlugin) OnDeleteSet(ctx context.Context, req *pb.OnDeleteSetRequest) (*pb.OnDeleteSetResponse, error) {
@@ -336,31 +338,31 @@ func (p *AwsPlugin) OnDeleteSet(ctx context.Context, req *pb.OnDeleteSetRequest)
func (p *AwsPlugin) ListHosts(ctx context.Context, req *pb.ListHostsRequest) (*pb.ListHostsResponse, error) { func (p *AwsPlugin) ListHosts(ctx context.Context, req *pb.ListHostsRequest) (*pb.ListHostsResponse, error) {
catalog := req.GetCatalog() catalog := req.GetCatalog()
if catalog == nil { if catalog == nil {
return nil, errors.New("catalog is nil") return nil, status.Error(codes.InvalidArgument, "catalog is nil")
} }
catalogAttrsRaw := catalog.GetAttributes() catalogAttrsRaw := catalog.GetAttributes()
if catalogAttrsRaw == nil { if catalogAttrsRaw == nil {
return nil, errors.New("catalog missing attributes") return nil, status.Error(codes.InvalidArgument, "catalog missing attributes")
} }
catalogAttributes, err := getCatalogAttributes(catalogAttrsRaw) catalogAttributes, err := getCatalogAttributes(catalogAttrsRaw)
if err != nil { if err != nil {
return nil, fmt.Errorf("integrity error in host catalog attributes: %w", err) return nil, err
} }
if err := validateRegion(catalogAttributes.Region); err != nil { if err := validateRegion(catalogAttributes.Region); err != nil {
return nil, fmt.Errorf("integrity error in host catalog attributes: %w", err) return nil, err
} }
state, err := awsCatalogPersistedStateFromProto(req.GetPersisted(), p.testStateOpts...) state, err := awsCatalogPersistedStateFromProto(req.GetPersisted(), p.testStateOpts...)
if err != nil { if err != nil {
return nil, fmt.Errorf("error loading persisted state: %w", err) return nil, status.Errorf(codes.InvalidArgument, "error loading persisted state: %s", err)
} }
sets := req.GetSets() sets := req.GetSets()
if sets == nil { if sets == nil {
return nil, errors.New("sets is nil") return nil, status.Error(codes.InvalidArgument, "sets is nil")
} }
// Build all the queries in advance. // Build all the queries in advance.
@@ -375,20 +377,20 @@ func (p *AwsPlugin) ListHosts(ctx context.Context, req *pb.ListHostsRequest) (*p
for i, set := range sets { for i, set := range sets {
// Validate Id since we use it in output // Validate Id since we use it in output
if set.GetId() == "" { if set.GetId() == "" {
return nil, errors.New("set missing id") return nil, status.Error(codes.InvalidArgument, "set missing id")
} }
if set.GetAttributes() == nil { if set.GetAttributes() == nil {
return nil, errors.New("set missing attributes") return nil, status.Error(codes.InvalidArgument, "set missing attributes")
} }
setAttrs, err := getSetAttributes(set.GetAttributes()) setAttrs, err := getSetAttributes(set.GetAttributes())
if err != nil { if err != nil {
return nil, fmt.Errorf("error parsing set attributes: %w", err) return nil, err
} }
input, err := buildDescribeInstancesInput(setAttrs, false) input, err := buildDescribeInstancesInput(setAttrs, false)
if err != nil { if err != nil {
return nil, fmt.Errorf("error building DescribeInstances parameters for host set id %q: %w", set.GetId(), err) return nil, status.Errorf(codes.InvalidArgument, "error building DescribeInstances parameters for host set id %q: %s", set.GetId(), err)
} }
queries[i] = hostSetQuery{ queries[i] = hostSetQuery{
Id: set.GetId(), Id: set.GetId(),
@@ -398,7 +400,7 @@ func (p *AwsPlugin) ListHosts(ctx context.Context, req *pb.ListHostsRequest) (*p
ec2Client, err := state.EC2Client(catalogAttributes.Region) ec2Client, err := state.EC2Client(catalogAttributes.Region)
if err != nil { if err != nil {
return nil, fmt.Errorf("error getting EC2 client: %w", err) return nil, status.Errorf(codes.InvalidArgument, "error getting EC2 client: %s", err)
} }
// Run all queries now and assemble output. // Run all queries now and assemble output.
@@ -406,7 +408,7 @@ func (p *AwsPlugin) ListHosts(ctx context.Context, req *pb.ListHostsRequest) (*p
for i, query := range queries { for i, query := range queries {
output, err := ec2Client.DescribeInstances(query.Input) output, err := ec2Client.DescribeInstances(query.Input)
if err != nil { if err != nil {
return nil, fmt.Errorf("error running DescribeInstances for host set id %q: %w", query.Id, err) return nil, status.Errorf(codes.InvalidArgument, "error running DescribeInstances for host set id %q: %s", query.Id, err)
} }
queries[i].Output = output queries[i].Output = output
@@ -417,7 +419,7 @@ func (p *AwsPlugin) ListHosts(ctx context.Context, req *pb.ListHostsRequest) (*p
for _, instance := range reservation.Instances { for _, instance := range reservation.Instances {
host, err := awsInstanceToHost(instance) host, err := awsInstanceToHost(instance)
if err != nil { if err != nil {
return nil, fmt.Errorf("error processing host results for host set id %q: %w", query.Id, err) return nil, status.Errorf(codes.InvalidArgument, "error processing host results for host set id %q: %s", query.Id, err)
} }
queries[i].OutputHosts = append(queries[i].OutputHosts, host) queries[i].OutputHosts = append(queries[i].OutputHosts, host)
@@ -461,7 +463,7 @@ func (p *AwsPlugin) ListHosts(ctx context.Context, req *pb.ListHostsRequest) (*p
func validateRegion(region string) error { func validateRegion(region string) error {
_, found := endpoints.PartitionForRegion(endpoints.DefaultPartitions(), region) _, found := endpoints.PartitionForRegion(endpoints.DefaultPartitions(), region)
if !found { if !found {
return fmt.Errorf("not a valid region: %s", region) return status.Errorf(codes.InvalidArgument, "not a valid region: %s", region)
} }
return nil return nil
+136 -67
View File
@@ -14,6 +14,8 @@ import (
pb "github.com/hashicorp/boundary/sdk/pbs/plugin" pb "github.com/hashicorp/boundary/sdk/pbs/plugin"
"github.com/hashicorp/go-secure-stdlib/awsutil" "github.com/hashicorp/go-secure-stdlib/awsutil"
"github.com/stretchr/testify/require" "github.com/stretchr/testify/require"
"google.golang.org/grpc/codes"
"google.golang.org/grpc/status"
"google.golang.org/protobuf/types/known/structpb" "google.golang.org/protobuf/types/known/structpb"
) )
@@ -22,19 +24,22 @@ func TestPluginOnCreateCatalogErr(t *testing.T) {
name string name string
req *pb.OnCreateCatalogRequest req *pb.OnCreateCatalogRequest
opts []awsCatalogPersistedStateOption opts []awsCatalogPersistedStateOption
expectedErr string expectedErrContains string
expectedErrCode codes.Code
}{ }{
{ {
name: "nil catalog", name: "nil catalog",
req: &pb.OnCreateCatalogRequest{}, req: &pb.OnCreateCatalogRequest{},
expectedErr: "catalog is nil", expectedErrContains: "catalog is nil",
expectedErrCode: codes.InvalidArgument,
}, },
{ {
name: "nil secrets", name: "nil secrets",
req: &pb.OnCreateCatalogRequest{ req: &pb.OnCreateCatalogRequest{
Catalog: &hostcatalogs.HostCatalog{}, Catalog: &hostcatalogs.HostCatalog{},
}, },
expectedErr: "secrets are required", expectedErrContains: "secrets are required",
expectedErrCode: codes.InvalidArgument,
}, },
{ {
name: "nil attributes", name: "nil attributes",
@@ -43,7 +48,8 @@ func TestPluginOnCreateCatalogErr(t *testing.T) {
Secrets: new(structpb.Struct), Secrets: new(structpb.Struct),
}, },
}, },
expectedErr: "attributes are required", expectedErrContains: "attributes are required",
expectedErrCode: codes.InvalidArgument,
}, },
{ {
name: "error reading attributes", name: "error reading attributes",
@@ -53,7 +59,8 @@ func TestPluginOnCreateCatalogErr(t *testing.T) {
Attributes: new(structpb.Struct), Attributes: new(structpb.Struct),
}, },
}, },
expectedErr: "missing required value \"region\"", expectedErrContains: "missing required value \"region\"",
expectedErrCode: codes.InvalidArgument,
}, },
{ {
name: "error reading secrets", name: "error reading secrets",
@@ -65,7 +72,8 @@ func TestPluginOnCreateCatalogErr(t *testing.T) {
}), }),
}, },
}, },
expectedErr: "missing required value \"access_key_id\"", expectedErrContains: "missing required value \"access_key_id\"",
expectedErrCode: codes.InvalidArgument,
}, },
{ {
name: "invalid region", name: "invalid region",
@@ -80,7 +88,8 @@ func TestPluginOnCreateCatalogErr(t *testing.T) {
}), }),
}, },
}, },
expectedErr: "not a valid region: foobar", expectedErrContains: "not a valid region: foobar",
expectedErrCode: codes.InvalidArgument,
}, },
{ {
name: "persisted state setup error", name: "persisted state setup error",
@@ -100,7 +109,8 @@ func TestPluginOnCreateCatalogErr(t *testing.T) {
return errors.New(testOptionErr) return errors.New(testOptionErr)
}, },
}, },
expectedErr: fmt.Sprintf("error setting up persisted state: %s", testOptionErr), expectedErrContains: fmt.Sprintf("error setting up persisted state: %s", testOptionErr),
expectedErrCode: codes.InvalidArgument,
}, },
{ {
name: "rotation error", name: "rotation error",
@@ -124,7 +134,8 @@ func TestPluginOnCreateCatalogErr(t *testing.T) {
), ),
}), }),
}, },
expectedErr: fmt.Sprintf("error during credential rotation: error rotating credentials: error calling CreateAccessKey: error calling aws.GetUser: %s", testGetUserErr), expectedErrContains: fmt.Sprintf("error during credential rotation: error rotating credentials: error calling CreateAccessKey: error calling aws.GetUser: %s", testGetUserErr),
expectedErrCode: codes.InvalidArgument,
}, },
{ {
name: "validation error", name: "validation error",
@@ -149,7 +160,8 @@ func TestPluginOnCreateCatalogErr(t *testing.T) {
), ),
}), }),
}, },
expectedErr: fmt.Sprintf("error during credential validation: error validating credentials: %s", testGetCallerIdentityErr), expectedErrContains: fmt.Sprintf("error during credential validation: error validating credentials: %s", testGetCallerIdentityErr),
expectedErrCode: codes.InvalidArgument,
}, },
} }
@@ -161,7 +173,8 @@ func TestPluginOnCreateCatalogErr(t *testing.T) {
testStateOpts: tc.opts, testStateOpts: tc.opts,
} }
_, err := p.OnCreateCatalog(context.Background(), tc.req) _, err := p.OnCreateCatalog(context.Background(), tc.req)
require.EqualError(err, tc.expectedErr) require.Contains(err.Error(), tc.expectedErrContains)
require.Equal(status.Code(err).String(), tc.expectedErrCode.String())
}) })
} }
} }
@@ -171,12 +184,14 @@ func TestPluginOnUpdateCatalogErr(t *testing.T) {
name string name string
req *pb.OnUpdateCatalogRequest req *pb.OnUpdateCatalogRequest
opts []awsCatalogPersistedStateOption opts []awsCatalogPersistedStateOption
expectedErr string expectedErrContains string
expectedErrCode codes.Code
}{ }{
{ {
name: "nil new catalog", name: "nil new catalog",
req: &pb.OnUpdateCatalogRequest{}, req: &pb.OnUpdateCatalogRequest{},
expectedErr: "new catalog is nil", expectedErrContains: "new catalog is nil",
expectedErrCode: codes.InvalidArgument,
}, },
{ {
name: "nil new attributes", name: "nil new attributes",
@@ -185,7 +200,8 @@ func TestPluginOnUpdateCatalogErr(t *testing.T) {
Secrets: new(structpb.Struct), Secrets: new(structpb.Struct),
}, },
}, },
expectedErr: "new catalog missing attributes", expectedErrContains: "new catalog missing attributes",
expectedErrCode: codes.InvalidArgument,
}, },
{ {
name: "error reading attributes", name: "error reading attributes",
@@ -195,7 +211,8 @@ func TestPluginOnUpdateCatalogErr(t *testing.T) {
Attributes: new(structpb.Struct), Attributes: new(structpb.Struct),
}, },
}, },
expectedErr: "missing required value \"region\"", expectedErrContains: "missing required value \"region\"",
expectedErrCode: codes.InvalidArgument,
}, },
{ {
name: "invalid region", name: "invalid region",
@@ -206,7 +223,8 @@ func TestPluginOnUpdateCatalogErr(t *testing.T) {
}), }),
}, },
}, },
expectedErr: "not a valid region: foobar", expectedErrContains: "not a valid region: foobar",
expectedErrCode: codes.InvalidArgument,
}, },
{ {
name: "persisted state setup error", name: "persisted state setup error",
@@ -229,7 +247,8 @@ func TestPluginOnUpdateCatalogErr(t *testing.T) {
return errors.New(testOptionErr) return errors.New(testOptionErr)
}, },
}, },
expectedErr: fmt.Sprintf("error loading persisted state: %s", testOptionErr), expectedErrContains: fmt.Sprintf("error loading persisted state: %s", testOptionErr),
expectedErrCode: codes.InvalidArgument,
}, },
{ {
name: "cannot disable rotation for already rotated credentials", name: "cannot disable rotation for already rotated credentials",
@@ -248,7 +267,8 @@ func TestPluginOnUpdateCatalogErr(t *testing.T) {
}), }),
}, },
}, },
expectedErr: "cannot disable rotation for already-rotated credentials", expectedErrContains: "cannot disable rotation for already-rotated credentials",
expectedErrCode: codes.FailedPrecondition,
}, },
{ {
name: "error reading secrets", name: "error reading secrets",
@@ -267,7 +287,8 @@ func TestPluginOnUpdateCatalogErr(t *testing.T) {
}), }),
}, },
}, },
expectedErr: "missing required value \"access_key_id\"", expectedErrContains: "missing required value \"access_key_id\"",
expectedErrCode: codes.InvalidArgument,
}, },
{ {
name: "updating secrets, replace error", name: "updating secrets, replace error",
@@ -299,7 +320,8 @@ func TestPluginOnUpdateCatalogErr(t *testing.T) {
), ),
}), }),
}, },
expectedErr: fmt.Sprintf("error attempting to replace credentials: error deleting old access key: %s", testDeleteAccessKeyErr), expectedErrContains: fmt.Sprintf("error attempting to replace credentials: error deleting old access key: %s", testDeleteAccessKeyErr),
expectedErrCode: codes.InvalidArgument,
}, },
{ {
name: "rotation error", name: "rotation error",
@@ -325,7 +347,8 @@ func TestPluginOnUpdateCatalogErr(t *testing.T) {
), ),
}), }),
}, },
expectedErr: fmt.Sprintf("error during credential rotation: error rotating credentials: error calling CreateAccessKey: error calling aws.GetUser: %s", testGetUserErr), expectedErrContains: fmt.Sprintf("error during credential rotation: error rotating credentials: error calling CreateAccessKey: error calling aws.GetUser: %s", testGetUserErr),
expectedErrCode: codes.InvalidArgument,
}, },
} }
@@ -337,7 +360,8 @@ func TestPluginOnUpdateCatalogErr(t *testing.T) {
testStateOpts: tc.opts, testStateOpts: tc.opts,
} }
_, err := p.OnUpdateCatalog(context.Background(), tc.req) _, err := p.OnUpdateCatalog(context.Background(), tc.req)
require.EqualError(err, tc.expectedErr) require.Contains(err.Error(), tc.expectedErrContains)
require.Equal(status.Code(err).String(), tc.expectedErrCode.String())
}) })
} }
} }
@@ -347,7 +371,8 @@ func TestPluginOnDeleteCatalogErr(t *testing.T) {
name string name string
req *pb.OnDeleteCatalogRequest req *pb.OnDeleteCatalogRequest
opts []awsCatalogPersistedStateOption opts []awsCatalogPersistedStateOption
expectedErr string expectedErrContains string
expectedErrCode codes.Code
}{ }{
{ {
name: "persisted state setup error", name: "persisted state setup error",
@@ -370,7 +395,8 @@ func TestPluginOnDeleteCatalogErr(t *testing.T) {
return errors.New(testOptionErr) return errors.New(testOptionErr)
}, },
}, },
expectedErr: fmt.Sprintf("error loading persisted state: %s", testOptionErr), expectedErrContains: fmt.Sprintf("error loading persisted state: %s", testOptionErr),
expectedErrCode: codes.InvalidArgument,
}, },
{ {
name: "delete error", name: "delete error",
@@ -397,7 +423,8 @@ func TestPluginOnDeleteCatalogErr(t *testing.T) {
), ),
}), }),
}, },
expectedErr: fmt.Sprintf("error removing rotated credentials during catalog deletion: error deleting old access key: %s", testDeleteAccessKeyErr), expectedErrContains: fmt.Sprintf("error removing rotated credentials during catalog deletion: error deleting old access key: %s", testDeleteAccessKeyErr),
expectedErrCode: codes.Aborted,
}, },
} }
@@ -409,7 +436,8 @@ func TestPluginOnDeleteCatalogErr(t *testing.T) {
testStateOpts: tc.opts, testStateOpts: tc.opts,
} }
_, err := p.OnDeleteCatalog(context.Background(), tc.req) _, err := p.OnDeleteCatalog(context.Background(), tc.req)
require.EqualError(err, tc.expectedErr) require.Contains(err.Error(), tc.expectedErrContains)
require.Equal(status.Code(err).String(), tc.expectedErrCode.String())
}) })
} }
} }
@@ -419,12 +447,14 @@ func TestPluginOnCreateSetErr(t *testing.T) {
name string name string
req *pb.OnCreateSetRequest req *pb.OnCreateSetRequest
opts []awsCatalogPersistedStateOption opts []awsCatalogPersistedStateOption
expectedErr string expectedErrContains string
expectedErrCode codes.Code
}{ }{
{ {
name: "nil catalog", name: "nil catalog",
req: &pb.OnCreateSetRequest{}, req: &pb.OnCreateSetRequest{},
expectedErr: "catalog is nil", expectedErrContains: "catalog is nil",
expectedErrCode: codes.InvalidArgument,
}, },
{ {
name: "nil catalog attributes", name: "nil catalog attributes",
@@ -433,7 +463,8 @@ func TestPluginOnCreateSetErr(t *testing.T) {
Secrets: new(structpb.Struct), Secrets: new(structpb.Struct),
}, },
}, },
expectedErr: "catalog missing attributes", expectedErrContains: "catalog missing attributes",
expectedErrCode: codes.InvalidArgument,
}, },
{ {
name: "error reading catalog attributes", name: "error reading catalog attributes",
@@ -443,7 +474,8 @@ func TestPluginOnCreateSetErr(t *testing.T) {
Attributes: new(structpb.Struct), Attributes: new(structpb.Struct),
}, },
}, },
expectedErr: "integrity error in host catalog attributes: missing required value \"region\"", expectedErrContains: "missing required value \"region\"",
expectedErrCode: codes.InvalidArgument,
}, },
{ {
name: "invalid region", name: "invalid region",
@@ -454,7 +486,8 @@ func TestPluginOnCreateSetErr(t *testing.T) {
}), }),
}, },
}, },
expectedErr: "integrity error in host catalog attributes: not a valid region: foobar", expectedErrContains: "not a valid region: foobar",
expectedErrCode: codes.InvalidArgument,
}, },
{ {
name: "persisted state setup error", name: "persisted state setup error",
@@ -477,7 +510,8 @@ func TestPluginOnCreateSetErr(t *testing.T) {
return errors.New(testOptionErr) return errors.New(testOptionErr)
}, },
}, },
expectedErr: fmt.Sprintf("error loading persisted state: %s", testOptionErr), expectedErrContains: fmt.Sprintf("error loading persisted state: %s", testOptionErr),
expectedErrCode: codes.InvalidArgument,
}, },
{ {
name: "nil set", name: "nil set",
@@ -495,7 +529,8 @@ func TestPluginOnCreateSetErr(t *testing.T) {
}), }),
}, },
}, },
expectedErr: "set is nil", expectedErrContains: "set is nil",
expectedErrCode: codes.InvalidArgument,
}, },
{ {
name: "nil attributes in set", name: "nil attributes in set",
@@ -514,7 +549,8 @@ func TestPluginOnCreateSetErr(t *testing.T) {
}, },
Set: &hostsets.HostSet{}, Set: &hostsets.HostSet{},
}, },
expectedErr: "set missing attributes", expectedErrContains: "set missing attributes",
expectedErrCode: codes.InvalidArgument,
}, },
{ {
name: "set attribute load error", name: "set attribute load error",
@@ -538,7 +574,8 @@ func TestPluginOnCreateSetErr(t *testing.T) {
}), }),
}, },
}, },
expectedErr: "error parsing set attributes: unknown set attribute fields provided: bar, foo", expectedErrContains: "attributes.bar: Unrecognized field, attributes.foo: Unrecognized field",
expectedErrCode: codes.InvalidArgument,
}, },
{ {
name: "client load error", name: "client load error",
@@ -566,7 +603,8 @@ func TestPluginOnCreateSetErr(t *testing.T) {
awsutil.MockOptionErr(errors.New(testOptionErr)), awsutil.MockOptionErr(errors.New(testOptionErr)),
}), }),
}, },
expectedErr: fmt.Sprintf("error getting EC2 client: error getting AWS session: error reading options in NewCredentialsConfig: %s", testOptionErr), expectedErrContains: fmt.Sprintf("error getting EC2 client: error getting AWS session: error reading options in NewCredentialsConfig: %s", testOptionErr),
expectedErrCode: codes.InvalidArgument,
}, },
{ {
name: "DescribeInstances error", name: "DescribeInstances error",
@@ -595,7 +633,8 @@ func TestPluginOnCreateSetErr(t *testing.T) {
testMockEC2WithDescribeInstancesError(errors.New(testDescribeInstancesError)), testMockEC2WithDescribeInstancesError(errors.New(testDescribeInstancesError)),
)), )),
}, },
expectedErr: fmt.Sprintf("error performing dry run of DescribeInstances: %s", testDescribeInstancesError), expectedErrContains: fmt.Sprintf("error performing dry run of DescribeInstances: %s", testDescribeInstancesError),
expectedErrCode: codes.InvalidArgument,
}, },
{ {
name: "DescribeInstances non-error array filter", name: "DescribeInstances non-error array filter",
@@ -623,7 +662,8 @@ func TestPluginOnCreateSetErr(t *testing.T) {
nil, nil,
)), )),
}, },
expectedErr: "query error: DescribeInstances DryRun should have returned error, but none was found", expectedErrContains: "query error: DescribeInstances DryRun should have returned error, but none was found",
expectedErrCode: codes.FailedPrecondition,
}, },
{ {
name: "DescribeInstances non-error string filter", name: "DescribeInstances non-error string filter",
@@ -651,7 +691,8 @@ func TestPluginOnCreateSetErr(t *testing.T) {
nil, nil,
)), )),
}, },
expectedErr: "query error: DescribeInstances DryRun should have returned error, but none was found", expectedErrContains: "query error: DescribeInstances DryRun should have returned error, but none was found",
expectedErrCode: codes.FailedPrecondition,
}, },
} }
@@ -663,7 +704,8 @@ func TestPluginOnCreateSetErr(t *testing.T) {
testStateOpts: tc.opts, testStateOpts: tc.opts,
} }
_, err := p.OnCreateSet(context.Background(), tc.req) _, err := p.OnCreateSet(context.Background(), tc.req)
require.EqualError(err, tc.expectedErr) require.Contains(err.Error(), tc.expectedErrContains)
require.Equal(status.Code(err).String(), tc.expectedErrCode.String())
}) })
} }
} }
@@ -673,12 +715,14 @@ func TestPluginOnUpdateSetErr(t *testing.T) {
name string name string
req *pb.OnUpdateSetRequest req *pb.OnUpdateSetRequest
opts []awsCatalogPersistedStateOption opts []awsCatalogPersistedStateOption
expectedErr string expectedErrContains string
expectedErrCode codes.Code
}{ }{
{ {
name: "nil catalog", name: "nil catalog",
req: &pb.OnUpdateSetRequest{}, req: &pb.OnUpdateSetRequest{},
expectedErr: "catalog is nil", expectedErrContains: "catalog is nil",
expectedErrCode: codes.InvalidArgument,
}, },
{ {
name: "nil catalog attributes", name: "nil catalog attributes",
@@ -687,7 +731,8 @@ func TestPluginOnUpdateSetErr(t *testing.T) {
Secrets: new(structpb.Struct), Secrets: new(structpb.Struct),
}, },
}, },
expectedErr: "catalog missing attributes", expectedErrContains: "catalog missing attributes",
expectedErrCode: codes.InvalidArgument,
}, },
{ {
name: "catalog attribute load error", name: "catalog attribute load error",
@@ -697,7 +742,8 @@ func TestPluginOnUpdateSetErr(t *testing.T) {
Attributes: new(structpb.Struct), Attributes: new(structpb.Struct),
}, },
}, },
expectedErr: "integrity error in host catalog attributes: missing required value \"region\"", expectedErrContains: "missing required value \"region\"",
expectedErrCode: codes.InvalidArgument,
}, },
{ {
name: "invalid region", name: "invalid region",
@@ -709,7 +755,8 @@ func TestPluginOnUpdateSetErr(t *testing.T) {
}), }),
}, },
}, },
expectedErr: "integrity error in host catalog attributes: not a valid region: foobar", expectedErrContains: "not a valid region: foobar",
expectedErrCode: codes.InvalidArgument,
}, },
{ {
name: "persisted state setup error", name: "persisted state setup error",
@@ -732,7 +779,8 @@ func TestPluginOnUpdateSetErr(t *testing.T) {
return errors.New(testOptionErr) return errors.New(testOptionErr)
}, },
}, },
expectedErr: fmt.Sprintf("error loading persisted state: %s", testOptionErr), expectedErrContains: fmt.Sprintf("error loading persisted state: %s", testOptionErr),
expectedErrCode: codes.InvalidArgument,
}, },
{ {
name: "nil set", name: "nil set",
@@ -750,7 +798,8 @@ func TestPluginOnUpdateSetErr(t *testing.T) {
}), }),
}, },
}, },
expectedErr: "new set is nil", expectedErrContains: "new set is nil",
expectedErrCode: codes.InvalidArgument,
}, },
{ {
name: "nil attributes in set", name: "nil attributes in set",
@@ -769,7 +818,8 @@ func TestPluginOnUpdateSetErr(t *testing.T) {
}, },
NewSet: &hostsets.HostSet{}, NewSet: &hostsets.HostSet{},
}, },
expectedErr: "new set missing attributes", expectedErrContains: "new set missing attributes",
expectedErrCode: codes.InvalidArgument,
}, },
{ {
name: "set attribute load error", name: "set attribute load error",
@@ -793,7 +843,8 @@ func TestPluginOnUpdateSetErr(t *testing.T) {
}), }),
}, },
}, },
expectedErr: "error parsing set attributes: unknown set attribute fields provided: bar, foo", expectedErrContains: "attributes.bar: Unrecognized field, attributes.foo: Unrecognized field",
expectedErrCode: codes.InvalidArgument,
}, },
{ {
name: "client load error", name: "client load error",
@@ -821,7 +872,8 @@ func TestPluginOnUpdateSetErr(t *testing.T) {
awsutil.MockOptionErr(errors.New(testOptionErr)), awsutil.MockOptionErr(errors.New(testOptionErr)),
}), }),
}, },
expectedErr: fmt.Sprintf("error getting EC2 client: error getting AWS session: error reading options in NewCredentialsConfig: %s", testOptionErr), expectedErrContains: fmt.Sprintf("error getting EC2 client: error getting AWS session: error reading options in NewCredentialsConfig: %s", testOptionErr),
expectedErrCode: codes.InvalidArgument,
}, },
{ {
name: "DescribeInstances error", name: "DescribeInstances error",
@@ -850,7 +902,8 @@ func TestPluginOnUpdateSetErr(t *testing.T) {
testMockEC2WithDescribeInstancesError(errors.New(testDescribeInstancesError)), testMockEC2WithDescribeInstancesError(errors.New(testDescribeInstancesError)),
)), )),
}, },
expectedErr: fmt.Sprintf("error performing dry run of DescribeInstances: %s", testDescribeInstancesError), expectedErrContains: fmt.Sprintf("error performing dry run of DescribeInstances: %s", testDescribeInstancesError),
expectedErrCode: codes.InvalidArgument,
}, },
{ {
name: "DescribeInstances non-error", name: "DescribeInstances non-error",
@@ -878,7 +931,8 @@ func TestPluginOnUpdateSetErr(t *testing.T) {
nil, nil,
)), )),
}, },
expectedErr: "query error: DescribeInstances DryRun should have returned error, but none was found", expectedErrContains: "query error: DescribeInstances DryRun should have returned error, but none was found",
expectedErrCode: codes.FailedPrecondition,
}, },
} }
@@ -890,7 +944,8 @@ func TestPluginOnUpdateSetErr(t *testing.T) {
testStateOpts: tc.opts, testStateOpts: tc.opts,
} }
_, err := p.OnUpdateSet(context.Background(), tc.req) _, err := p.OnUpdateSet(context.Background(), tc.req)
require.EqualError(err, tc.expectedErr) require.Contains(err.Error(), tc.expectedErrContains)
require.Equal(status.Code(err).String(), tc.expectedErrCode.String())
}) })
} }
} }
@@ -900,12 +955,14 @@ func TestPluginListHostsErr(t *testing.T) {
name string name string
req *pb.ListHostsRequest req *pb.ListHostsRequest
opts []awsCatalogPersistedStateOption opts []awsCatalogPersistedStateOption
expectedErr string expectedErrContains string
expectedErrCode codes.Code
}{ }{
{ {
name: "nil catalog", name: "nil catalog",
req: &pb.ListHostsRequest{}, req: &pb.ListHostsRequest{},
expectedErr: "catalog is nil", expectedErrContains: "catalog is nil",
expectedErrCode: codes.InvalidArgument,
}, },
{ {
name: "nil catalog attributes", name: "nil catalog attributes",
@@ -914,7 +971,8 @@ func TestPluginListHostsErr(t *testing.T) {
Secrets: new(structpb.Struct), Secrets: new(structpb.Struct),
}, },
}, },
expectedErr: "catalog missing attributes", expectedErrContains: "catalog missing attributes",
expectedErrCode: codes.InvalidArgument,
}, },
{ {
name: "error reading catalog attributes", name: "error reading catalog attributes",
@@ -924,7 +982,8 @@ func TestPluginListHostsErr(t *testing.T) {
Attributes: new(structpb.Struct), Attributes: new(structpb.Struct),
}, },
}, },
expectedErr: "integrity error in host catalog attributes: missing required value \"region\"", expectedErrContains: "attributes.region: missing required value \"region\"",
expectedErrCode: codes.InvalidArgument,
}, },
{ {
name: "invalid region", name: "invalid region",
@@ -936,7 +995,8 @@ func TestPluginListHostsErr(t *testing.T) {
}), }),
}, },
}, },
expectedErr: "integrity error in host catalog attributes: not a valid region: foobar", expectedErrContains: "not a valid region: ",
expectedErrCode: codes.InvalidArgument,
}, },
{ {
name: "persisted state setup error", name: "persisted state setup error",
@@ -959,7 +1019,8 @@ func TestPluginListHostsErr(t *testing.T) {
return errors.New(testOptionErr) return errors.New(testOptionErr)
}, },
}, },
expectedErr: fmt.Sprintf("error loading persisted state: %s", testOptionErr), expectedErrContains: fmt.Sprintf("error loading persisted state: %s", testOptionErr),
expectedErrCode: codes.InvalidArgument,
}, },
{ {
name: "nil sets", name: "nil sets",
@@ -977,7 +1038,8 @@ func TestPluginListHostsErr(t *testing.T) {
}), }),
}, },
}, },
expectedErr: "sets is nil", expectedErrContains: "sets is nil",
expectedErrCode: codes.InvalidArgument,
}, },
{ {
name: "set missing id", name: "set missing id",
@@ -996,7 +1058,8 @@ func TestPluginListHostsErr(t *testing.T) {
}, },
Sets: []*hostsets.HostSet{&hostsets.HostSet{}}, Sets: []*hostsets.HostSet{&hostsets.HostSet{}},
}, },
expectedErr: "set missing id", expectedErrContains: "set missing id",
expectedErrCode: codes.InvalidArgument,
}, },
{ {
name: "set missing attributes", name: "set missing attributes",
@@ -1019,7 +1082,8 @@ func TestPluginListHostsErr(t *testing.T) {
}, },
}, },
}, },
expectedErr: "set missing attributes", expectedErrContains: "set missing attributes",
expectedErrCode: codes.InvalidArgument,
}, },
{ {
name: "set attribute load error", name: "set attribute load error",
@@ -1046,7 +1110,8 @@ func TestPluginListHostsErr(t *testing.T) {
}, },
}, },
}, },
expectedErr: "error parsing set attributes: unknown set attribute fields provided: bar, foo", expectedErrContains: "attributes.bar: Unrecognized field, attributes.foo: Unrecognized field",
expectedErrCode: codes.InvalidArgument,
}, },
{ {
name: "client load error", name: "client load error",
@@ -1077,7 +1142,8 @@ func TestPluginListHostsErr(t *testing.T) {
awsutil.MockOptionErr(errors.New(testOptionErr)), awsutil.MockOptionErr(errors.New(testOptionErr)),
}), }),
}, },
expectedErr: fmt.Sprintf("error getting EC2 client: error getting AWS session: error reading options in NewCredentialsConfig: %s", testOptionErr), expectedErrContains: fmt.Sprintf("error getting EC2 client: error getting AWS session: error reading options in NewCredentialsConfig: %s", testOptionErr),
expectedErrCode: codes.InvalidArgument,
}, },
{ {
name: "DescribeInstances error", name: "DescribeInstances error",
@@ -1109,7 +1175,8 @@ func TestPluginListHostsErr(t *testing.T) {
testMockEC2WithDescribeInstancesError(errors.New(testDescribeInstancesError)), testMockEC2WithDescribeInstancesError(errors.New(testDescribeInstancesError)),
)), )),
}, },
expectedErr: fmt.Sprintf("error running DescribeInstances for host set id \"foobar\": %s", testDescribeInstancesError), expectedErrContains: fmt.Sprintf("error running DescribeInstances for host set id \"foobar\": %s", testDescribeInstancesError),
expectedErrCode: codes.InvalidArgument,
}, },
{ {
name: "awsInstanceToHost error", name: "awsInstanceToHost error",
@@ -1153,7 +1220,8 @@ func TestPluginListHostsErr(t *testing.T) {
), ),
)), )),
}, },
expectedErr: "error processing host results for host set id \"foobar\": response integrity error: missing instance id", expectedErrContains: "error processing host results for host set id \"foobar\": response integrity error: missing instance id",
expectedErrCode: codes.InvalidArgument,
}, },
} }
@@ -1165,7 +1233,8 @@ func TestPluginListHostsErr(t *testing.T) {
testStateOpts: tc.opts, testStateOpts: tc.opts,
} }
_, err := p.ListHosts(context.Background(), tc.req) _, err := p.ListHosts(context.Background(), tc.req)
require.EqualError(err, tc.expectedErr) require.Contains(err.Error(), tc.expectedErrContains)
require.Equal(status.Code(err).String(), tc.expectedErrCode.String())
}) })
} }
} }