Compare commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
7833b5df47 | ||
|
|
6bda6813aa | ||
|
|
bdd27e2562 | ||
|
|
f17f05ec82 | ||
|
|
899c62ce06 | ||
|
|
ce22c1c46a | ||
|
|
2f9163ad0e | ||
|
|
7caff84c06 |
No files matched your search
@@ -4,3 +4,9 @@ plan.tfplan
|
|||||||
*.log
|
*.log
|
||||||
*.out
|
*.out
|
||||||
*.html
|
*.html
|
||||||
|
|
||||||
|
# IntelliJ IDEA project files
|
||||||
|
.idea
|
||||||
|
*.iprg
|
||||||
|
|
||||||
|
.DS_Store
|
||||||
+1
-2
@@ -1,5 +1,4 @@
|
|||||||
# These owners will be the default owners for everything in
|
# These owners will be the default owners for everything in
|
||||||
# the repo, unless a later match takes precedence.
|
# the repo, unless a later match takes precedence.
|
||||||
|
|
||||||
@hashicorp/boundary
|
* @hashicorp/boundary
|
||||||
|
|
||||||
@@ -1,6 +1,6 @@
|
|||||||
module github.com/hashicorp/boundary-plugin-aws
|
module github.com/hashicorp/boundary-plugin-aws
|
||||||
|
|
||||||
go 1.21
|
go 1.23.3
|
||||||
|
|
||||||
require (
|
require (
|
||||||
github.com/aws/aws-sdk-go-v2 v1.20.1
|
github.com/aws/aws-sdk-go-v2 v1.20.1
|
||||||
@@ -11,7 +11,7 @@ require (
|
|||||||
github.com/aws/smithy-go v1.14.1
|
github.com/aws/smithy-go v1.14.1
|
||||||
github.com/google/go-cmp v0.6.0
|
github.com/google/go-cmp v0.6.0
|
||||||
github.com/google/uuid v1.4.0
|
github.com/google/uuid v1.4.0
|
||||||
github.com/hashicorp/boundary/sdk v0.0.43-0.20240717182311-a20aae98794a
|
github.com/hashicorp/boundary/sdk v0.0.49
|
||||||
github.com/hashicorp/go-multierror v1.1.1
|
github.com/hashicorp/go-multierror v1.1.1
|
||||||
github.com/hashicorp/go-secure-stdlib/awsutil/v2 v2.0.0
|
github.com/hashicorp/go-secure-stdlib/awsutil/v2 v2.0.0
|
||||||
github.com/hashicorp/terraform-json v0.14.0
|
github.com/hashicorp/terraform-json v0.14.0
|
||||||
|
|||||||
@@ -78,8 +78,8 @@ github.com/google/go-cmp v0.6.0 h1:ofyhxvXcZhMsU5ulbFiLKl/XBFqE1GSq7atu8tAmTRI=
|
|||||||
github.com/google/go-cmp v0.6.0/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY=
|
github.com/google/go-cmp v0.6.0/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY=
|
||||||
github.com/google/uuid v1.4.0 h1:MtMxsa51/r9yyhkyLsVeVt0B+BGQZzpQiTQ4eHZ8bc4=
|
github.com/google/uuid v1.4.0 h1:MtMxsa51/r9yyhkyLsVeVt0B+BGQZzpQiTQ4eHZ8bc4=
|
||||||
github.com/google/uuid v1.4.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
|
github.com/google/uuid v1.4.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
|
||||||
github.com/hashicorp/boundary/sdk v0.0.43-0.20240717182311-a20aae98794a h1:SwVze6sYE5o+J9qdcgj29auY1k6O8oSo9DC2+2Gb9rw=
|
github.com/hashicorp/boundary/sdk v0.0.49 h1:XOb6mSKyrU/wI20+5xTYBHQUP7eIeKcLxKSCpCs4yzM=
|
||||||
github.com/hashicorp/boundary/sdk v0.0.43-0.20240717182311-a20aae98794a/go.mod h1:9iOT7kDM6mYcSkKxNuZlv8rP7U5BG1kXoevjLLL8lNQ=
|
github.com/hashicorp/boundary/sdk v0.0.49/go.mod h1:IHP79to8aIi22FiY58pgBqJL96/U9D8ZAUhS2DdC+Us=
|
||||||
github.com/hashicorp/errwrap v1.0.0/go.mod h1:YH+1FKiLXxHSkmPseP+kNlulaMuP3n2brvKWEqk/Jc4=
|
github.com/hashicorp/errwrap v1.0.0/go.mod h1:YH+1FKiLXxHSkmPseP+kNlulaMuP3n2brvKWEqk/Jc4=
|
||||||
github.com/hashicorp/errwrap v1.1.0 h1:OxrOeh75EUXMY8TBjag2fzXGZ40LB6IKw45YeGUDY2I=
|
github.com/hashicorp/errwrap v1.1.0 h1:OxrOeh75EUXMY8TBjag2fzXGZ40LB6IKw45YeGUDY2I=
|
||||||
github.com/hashicorp/errwrap v1.1.0/go.mod h1:YH+1FKiLXxHSkmPseP+kNlulaMuP3n2brvKWEqk/Jc4=
|
github.com/hashicorp/errwrap v1.1.0/go.mod h1:YH+1FKiLXxHSkmPseP+kNlulaMuP3n2brvKWEqk/Jc4=
|
||||||
|
|||||||
@@ -114,7 +114,7 @@ func (s *AwsCredentialPersistedState) ValidateCreds(ctx context.Context) error {
|
|||||||
return errors.BadRequestStatus("missing credentials config")
|
return errors.BadRequestStatus("missing credentials config")
|
||||||
}
|
}
|
||||||
if _, err := s.CredentialsConfig.GetCallerIdentity(ctx, s.testOpts...); err != nil {
|
if _, err := s.CredentialsConfig.GetCallerIdentity(ctx, s.testOpts...); err != nil {
|
||||||
st, _ := errors.ParseAWSError(err, "validating credentials")
|
st, _ := errors.ParseAWSError("validating credentials", err)
|
||||||
return st.Err()
|
return st.Err()
|
||||||
}
|
}
|
||||||
return nil
|
return nil
|
||||||
@@ -135,7 +135,7 @@ func (s *AwsCredentialPersistedState) RotateCreds(ctx context.Context) error {
|
|||||||
if err := s.CredentialsConfig.RotateKeys(ctx, append([]awsutil.Option{
|
if err := s.CredentialsConfig.RotateKeys(ctx, append([]awsutil.Option{
|
||||||
awsutil.WithValidityCheckTimeout(rotationWaitTimeout),
|
awsutil.WithValidityCheckTimeout(rotationWaitTimeout),
|
||||||
}, s.testOpts...)...); err != nil {
|
}, s.testOpts...)...); err != nil {
|
||||||
st, _ := errors.ParseAWSError(err, "error rotating credentials")
|
st, _ := errors.ParseAWSError("rotating credentials", err)
|
||||||
return st.Err()
|
return st.Err()
|
||||||
}
|
}
|
||||||
s.CredsLastRotatedTime = time.Now()
|
s.CredsLastRotatedTime = time.Now()
|
||||||
@@ -192,7 +192,7 @@ func (s *AwsCredentialPersistedState) DeleteCreds(ctx context.Context) error {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Otherwise treat it as an actual error.
|
// Otherwise treat it as an actual error.
|
||||||
st, _ := errors.ParseAWSError(err, "failed to delete access key")
|
st, _ := errors.ParseAWSError("deleting credentials", err)
|
||||||
return st.Err()
|
return st.Err()
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -252,17 +252,17 @@ func AwsCredentialPersistedStateFromProto(secrets *structpb.Struct, attrs *Crede
|
|||||||
|
|
||||||
accessKeyId, err := values.GetStringValue(secrets, ConstAccessKeyId, false)
|
accessKeyId, err := values.GetStringValue(secrets, ConstAccessKeyId, false)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, fmt.Errorf("persisted state integrity error: %w", err)
|
return nil, fmt.Errorf("persisted state integrity: %w", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
secretAccessKey, err := values.GetStringValue(secrets, ConstSecretAccessKey, false)
|
secretAccessKey, err := values.GetStringValue(secrets, ConstSecretAccessKey, false)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, fmt.Errorf("persisted state integrity error: %w", err)
|
return nil, fmt.Errorf("persisted state integrity: %w", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
credsLastRotatedTime, err := values.GetTimeValue(secrets, ConstCredsLastRotatedTime)
|
credsLastRotatedTime, err := values.GetTimeValue(secrets, ConstCredsLastRotatedTime)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, fmt.Errorf("persisted state integrity error: %w", err)
|
return nil, fmt.Errorf("persisted state integrity: %w", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
s, err := NewAwsCredentialPersistedState(opts...)
|
s, err := NewAwsCredentialPersistedState(opts...)
|
||||||
|
|||||||
@@ -186,7 +186,7 @@ func TestAwsCredentialPersistedStateFromProto(t *testing.T) {
|
|||||||
attrs: &CredentialAttributes{
|
attrs: &CredentialAttributes{
|
||||||
Region: "us-west-2",
|
Region: "us-west-2",
|
||||||
},
|
},
|
||||||
expectedErr: "persisted state integrity error: could not parse time in value \"creds_last_rotated_time\": parsing time \"notatime\" as \"2006-01-02T15:04:05.999999999Z07:00\": cannot parse \"notatime\" as \"2006\"",
|
expectedErr: "persisted state integrity: could not parse time in value \"creds_last_rotated_time\": parsing time \"notatime\" as \"2006-01-02T15:04:05.999999999Z07:00\": cannot parse \"notatime\" as \"2006\"",
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
name: "option error",
|
name: "option error",
|
||||||
@@ -458,7 +458,7 @@ func TestAwsCatalogPersistedState_RotateCreds(t *testing.T) {
|
|||||||
),
|
),
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
expectedErr: "aws service unknown: unknown error: error rotating credentials",
|
expectedErr: "aws service unknown: unknown error: rotating credentials",
|
||||||
expectedStatusCode: codes.Unknown,
|
expectedStatusCode: codes.Unknown,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
@@ -568,7 +568,7 @@ func TestAwsCatalogPersistedState_ReplaceCreds(t *testing.T) {
|
|||||||
},
|
},
|
||||||
},
|
},
|
||||||
credentialConfig: &awsutil.CredentialsConfig{},
|
credentialConfig: &awsutil.CredentialsConfig{},
|
||||||
expectedErr: "aws service unknown: unknown error: failed to delete access key",
|
expectedErr: "aws service unknown: unknown error: deleting credentials",
|
||||||
expectedStatusCode: codes.Unknown,
|
expectedStatusCode: codes.Unknown,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
@@ -693,7 +693,7 @@ func TestAwsCatalogPersistedState_DeleteCreds(t *testing.T) {
|
|||||||
),
|
),
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
expectedErr: "aws service unknown: unknown error: failed to delete access key",
|
expectedErr: "aws service unknown: unknown error: deleting credentials",
|
||||||
expectedStatusCode: codes.Unknown,
|
expectedStatusCode: codes.Unknown,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
|
|||||||
+15
-14
@@ -81,11 +81,12 @@ func InvalidArgumentError(msg string, f map[string]string) error {
|
|||||||
// and credentials. This method will fallback to parsing the http status code
|
// and credentials. This method will fallback to parsing the http status code
|
||||||
// when it cannot match an aws error code. This method does not handle specific
|
// when it cannot match an aws error code. This method does not handle specific
|
||||||
// service type errors such as S3 or EC2.
|
// service type errors such as S3 or EC2.
|
||||||
func ParseAWSError(err error, msg string) (st *status.Status, permission *pb.Permission) {
|
func ParseAWSError(op string, err error) (st *status.Status, permission *pb.Permission) {
|
||||||
if err == nil {
|
if err == nil {
|
||||||
return nil, nil
|
return nil, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
msg := fmt.Sprintf("%s: %v", op, err)
|
||||||
// find the service name of the aws api
|
// find the service name of the aws api
|
||||||
serviceName := "unknown"
|
serviceName := "unknown"
|
||||||
var oe *smithy.OperationError
|
var oe *smithy.OperationError
|
||||||
@@ -113,7 +114,7 @@ func ParseAWSError(err error, msg string) (st *status.Status, permission *pb.Per
|
|||||||
Codes: retry.DefaultThrottleErrorCodes,
|
Codes: retry.DefaultThrottleErrorCodes,
|
||||||
}
|
}
|
||||||
if throttleErr.IsErrorThrottle(err).Bool() {
|
if throttleErr.IsErrorThrottle(err).Bool() {
|
||||||
statusMsg := fmt.Sprintf("aws service %s: throttling error: %s", serviceName, msg)
|
statusMsg := fmt.Sprintf("aws service %s: throttling: %s", serviceName, msg)
|
||||||
return status.New(codes.Unavailable, statusMsg), nil
|
return status.New(codes.Unavailable, statusMsg), nil
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -126,31 +127,31 @@ func ParseAWSError(err error, msg string) (st *status.Status, permission *pb.Per
|
|||||||
case awsErrorInvalidAccessKeyId:
|
case awsErrorInvalidAccessKeyId:
|
||||||
fallthrough
|
fallthrough
|
||||||
case awsErrorExpiredToken:
|
case awsErrorExpiredToken:
|
||||||
statusMsg := fmt.Sprintf("aws service %s: invalid credentials error: %s", serviceName, msg)
|
statusMsg := fmt.Sprintf("aws service %s: invalid credentials: %s", serviceName, msg)
|
||||||
return status.New(codes.PermissionDenied, statusMsg), &pb.Permission{
|
return status.New(codes.PermissionDenied, statusMsg), &pb.Permission{
|
||||||
State: pb.StateType_STATE_TYPE_ERROR,
|
State: pb.StateType_STATE_TYPE_ERROR,
|
||||||
ErrorDetails: apiErr.ErrorMessage(),
|
ErrorDetails: apiErr.ErrorMessage(),
|
||||||
CheckedAt: timestamppb.Now(),
|
CheckedAt: timestamppb.Now(),
|
||||||
}
|
}
|
||||||
case awsErrorNoSuchBucket:
|
case awsErrorNoSuchBucket:
|
||||||
statusMsg := fmt.Sprintf("aws %s error: %s", serviceName, msg)
|
statusMsg := fmt.Sprintf("aws service %s: %s", serviceName, msg)
|
||||||
return status.New(codes.NotFound, statusMsg), &pb.Permission{
|
return status.New(codes.NotFound, statusMsg), &pb.Permission{
|
||||||
State: pb.StateType_STATE_TYPE_ERROR,
|
State: pb.StateType_STATE_TYPE_ERROR,
|
||||||
ErrorDetails: apiErr.ErrorMessage(),
|
ErrorDetails: apiErr.ErrorMessage(),
|
||||||
CheckedAt: timestamppb.Now(),
|
CheckedAt: timestamppb.Now(),
|
||||||
}
|
}
|
||||||
case awsErrorBadDigest:
|
case awsErrorBadDigest:
|
||||||
statusMsg := fmt.Sprintf("aws %s error: %s", serviceName, msg)
|
statusMsg := fmt.Sprintf("aws service %s: %s", serviceName, msg)
|
||||||
return status.New(codes.Aborted, statusMsg), nil
|
return status.New(codes.Aborted, statusMsg), nil
|
||||||
case awsErrorNoSuchKey:
|
case awsErrorNoSuchKey:
|
||||||
fallthrough
|
fallthrough
|
||||||
case awsErrorInvalidObjectState:
|
case awsErrorInvalidObjectState:
|
||||||
statusMsg := fmt.Sprintf("aws %s error: %s", serviceName, msg)
|
statusMsg := fmt.Sprintf("aws service %s: %s", serviceName, msg)
|
||||||
return status.New(codes.NotFound, statusMsg), nil
|
return status.New(codes.NotFound, statusMsg), nil
|
||||||
case awsErrorRequestTimeout:
|
case awsErrorRequestTimeout:
|
||||||
fallthrough
|
fallthrough
|
||||||
case awsErrorRequestTimeoutException:
|
case awsErrorRequestTimeoutException:
|
||||||
statusMsg := fmt.Sprintf("aws %s error: %s", serviceName, msg)
|
statusMsg := fmt.Sprintf("aws service %s: %s", serviceName, msg)
|
||||||
return status.New(codes.DeadlineExceeded, statusMsg), nil
|
return status.New(codes.DeadlineExceeded, statusMsg), nil
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -165,39 +166,39 @@ func ParseAWSError(err error, msg string) (st *status.Status, permission *pb.Per
|
|||||||
defer httpErr.Response.Body.Close()
|
defer httpErr.Response.Body.Close()
|
||||||
switch httpErr.HTTPStatusCode() {
|
switch httpErr.HTTPStatusCode() {
|
||||||
case http.StatusBadRequest:
|
case http.StatusBadRequest:
|
||||||
statusMsg := fmt.Sprintf("aws service %s: bad request error: %s", serviceName, msg)
|
statusMsg := fmt.Sprintf("aws service %s: bad request: %s", serviceName, msg)
|
||||||
return status.New(codes.InvalidArgument, statusMsg), nil
|
return status.New(codes.InvalidArgument, statusMsg), nil
|
||||||
case http.StatusUnauthorized:
|
case http.StatusUnauthorized:
|
||||||
statusMsg := fmt.Sprintf("aws service %s: invalid credentials error: %s", serviceName, msg)
|
statusMsg := fmt.Sprintf("aws service %s: invalid credentials: %s", serviceName, msg)
|
||||||
return status.New(codes.PermissionDenied, statusMsg), &pb.Permission{
|
return status.New(codes.PermissionDenied, statusMsg), &pb.Permission{
|
||||||
State: pb.StateType_STATE_TYPE_ERROR,
|
State: pb.StateType_STATE_TYPE_ERROR,
|
||||||
ErrorDetails: buf.String(),
|
ErrorDetails: buf.String(),
|
||||||
CheckedAt: timestamppb.Now(),
|
CheckedAt: timestamppb.Now(),
|
||||||
}
|
}
|
||||||
case http.StatusForbidden:
|
case http.StatusForbidden:
|
||||||
statusMsg := fmt.Sprintf("aws service %s: invalid credentials error: %s", serviceName, msg)
|
statusMsg := fmt.Sprintf("aws service %s: invalid credentials: %s", serviceName, msg)
|
||||||
return status.New(codes.PermissionDenied, statusMsg), &pb.Permission{
|
return status.New(codes.PermissionDenied, statusMsg), &pb.Permission{
|
||||||
State: pb.StateType_STATE_TYPE_ERROR,
|
State: pb.StateType_STATE_TYPE_ERROR,
|
||||||
ErrorDetails: buf.String(),
|
ErrorDetails: buf.String(),
|
||||||
CheckedAt: timestamppb.Now(),
|
CheckedAt: timestamppb.Now(),
|
||||||
}
|
}
|
||||||
case http.StatusNotFound:
|
case http.StatusNotFound:
|
||||||
statusMsg := fmt.Sprintf("aws service %s: resource not found error: %s", serviceName, msg)
|
statusMsg := fmt.Sprintf("aws service %s: resource not found: %s", serviceName, msg)
|
||||||
return status.New(codes.NotFound, statusMsg), nil
|
return status.New(codes.NotFound, statusMsg), nil
|
||||||
case http.StatusTooManyRequests:
|
case http.StatusTooManyRequests:
|
||||||
statusMsg := fmt.Sprintf("aws service %s: throttling error: %s", serviceName, msg)
|
statusMsg := fmt.Sprintf("aws service %s: throttling: %s", serviceName, msg)
|
||||||
return status.New(codes.Unavailable, statusMsg), nil
|
return status.New(codes.Unavailable, statusMsg), nil
|
||||||
case http.StatusInternalServerError:
|
case http.StatusInternalServerError:
|
||||||
fallthrough
|
fallthrough
|
||||||
case http.StatusBadGateway:
|
case http.StatusBadGateway:
|
||||||
fallthrough
|
fallthrough
|
||||||
case http.StatusServiceUnavailable:
|
case http.StatusServiceUnavailable:
|
||||||
statusMsg := fmt.Sprintf("aws service %s: connectivity error: %s", serviceName, msg)
|
statusMsg := fmt.Sprintf("aws service %s: connectivity: %s", serviceName, msg)
|
||||||
return status.New(codes.Unavailable, statusMsg), nil
|
return status.New(codes.Unavailable, statusMsg), nil
|
||||||
case http.StatusRequestTimeout:
|
case http.StatusRequestTimeout:
|
||||||
fallthrough
|
fallthrough
|
||||||
case http.StatusGatewayTimeout:
|
case http.StatusGatewayTimeout:
|
||||||
statusMsg := fmt.Sprintf("aws service %s: timeout error: %s", serviceName, msg)
|
statusMsg := fmt.Sprintf("aws service %s: timeout: %s", serviceName, msg)
|
||||||
return status.New(codes.DeadlineExceeded, statusMsg), nil
|
return status.New(codes.DeadlineExceeded, statusMsg), nil
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -44,7 +44,7 @@ func Test_ParseAWSError(t *testing.T) {
|
|||||||
name: "http-status-code-bad-request",
|
name: "http-status-code-bad-request",
|
||||||
err: TestAwsHttpResponseError(http.StatusBadRequest, "bad request"),
|
err: TestAwsHttpResponseError(http.StatusBadRequest, "bad request"),
|
||||||
expectedStatusCode: codes.InvalidArgument,
|
expectedStatusCode: codes.InvalidArgument,
|
||||||
expectedStatusMsg: "aws service unknown: bad request error: test",
|
expectedStatusMsg: "aws service unknown: bad request: test",
|
||||||
expectedPermission: &pb.Permission{
|
expectedPermission: &pb.Permission{
|
||||||
State: pb.StateType_STATE_TYPE_UNKNOWN,
|
State: pb.StateType_STATE_TYPE_UNKNOWN,
|
||||||
},
|
},
|
||||||
@@ -53,7 +53,7 @@ func Test_ParseAWSError(t *testing.T) {
|
|||||||
name: "http-status-code-unauthorized",
|
name: "http-status-code-unauthorized",
|
||||||
err: TestAwsHttpResponseError(http.StatusUnauthorized, "unauthorized request"),
|
err: TestAwsHttpResponseError(http.StatusUnauthorized, "unauthorized request"),
|
||||||
expectedStatusCode: codes.PermissionDenied,
|
expectedStatusCode: codes.PermissionDenied,
|
||||||
expectedStatusMsg: "aws service unknown: invalid credentials error: test",
|
expectedStatusMsg: "aws service unknown: invalid credentials: test",
|
||||||
expectedPermission: &pb.Permission{
|
expectedPermission: &pb.Permission{
|
||||||
State: pb.StateType_STATE_TYPE_ERROR,
|
State: pb.StateType_STATE_TYPE_ERROR,
|
||||||
ErrorDetails: "unauthorized request",
|
ErrorDetails: "unauthorized request",
|
||||||
@@ -64,7 +64,7 @@ func Test_ParseAWSError(t *testing.T) {
|
|||||||
name: "http-status-code-forbidden",
|
name: "http-status-code-forbidden",
|
||||||
err: TestAwsHttpResponseError(http.StatusForbidden, "forbidden request"),
|
err: TestAwsHttpResponseError(http.StatusForbidden, "forbidden request"),
|
||||||
expectedStatusCode: codes.PermissionDenied,
|
expectedStatusCode: codes.PermissionDenied,
|
||||||
expectedStatusMsg: "aws service unknown: invalid credentials error: test",
|
expectedStatusMsg: "aws service unknown: invalid credentials: test",
|
||||||
expectedPermission: &pb.Permission{
|
expectedPermission: &pb.Permission{
|
||||||
State: pb.StateType_STATE_TYPE_ERROR,
|
State: pb.StateType_STATE_TYPE_ERROR,
|
||||||
ErrorDetails: "forbidden request",
|
ErrorDetails: "forbidden request",
|
||||||
@@ -75,7 +75,7 @@ func Test_ParseAWSError(t *testing.T) {
|
|||||||
name: "http-status-code-not-found",
|
name: "http-status-code-not-found",
|
||||||
err: TestAwsHttpResponseError(http.StatusNotFound, "not found request"),
|
err: TestAwsHttpResponseError(http.StatusNotFound, "not found request"),
|
||||||
expectedStatusCode: codes.NotFound,
|
expectedStatusCode: codes.NotFound,
|
||||||
expectedStatusMsg: "aws service unknown: resource not found error: test",
|
expectedStatusMsg: "aws service unknown: resource not found: test",
|
||||||
expectedPermission: &pb.Permission{
|
expectedPermission: &pb.Permission{
|
||||||
State: pb.StateType_STATE_TYPE_UNKNOWN,
|
State: pb.StateType_STATE_TYPE_UNKNOWN,
|
||||||
},
|
},
|
||||||
@@ -84,7 +84,7 @@ func Test_ParseAWSError(t *testing.T) {
|
|||||||
name: "http-status-code-too-many-requests",
|
name: "http-status-code-too-many-requests",
|
||||||
err: TestAwsHttpResponseError(http.StatusTooManyRequests, "too many requests"),
|
err: TestAwsHttpResponseError(http.StatusTooManyRequests, "too many requests"),
|
||||||
expectedStatusCode: codes.Unavailable,
|
expectedStatusCode: codes.Unavailable,
|
||||||
expectedStatusMsg: "aws service unknown: throttling error: test",
|
expectedStatusMsg: "aws service unknown: throttling: test",
|
||||||
expectedPermission: &pb.Permission{
|
expectedPermission: &pb.Permission{
|
||||||
State: pb.StateType_STATE_TYPE_UNKNOWN,
|
State: pb.StateType_STATE_TYPE_UNKNOWN,
|
||||||
},
|
},
|
||||||
@@ -93,7 +93,7 @@ func Test_ParseAWSError(t *testing.T) {
|
|||||||
name: "http-status-code-request-timeout",
|
name: "http-status-code-request-timeout",
|
||||||
err: TestAwsHttpResponseError(http.StatusRequestTimeout, "request timeout"),
|
err: TestAwsHttpResponseError(http.StatusRequestTimeout, "request timeout"),
|
||||||
expectedStatusCode: codes.DeadlineExceeded,
|
expectedStatusCode: codes.DeadlineExceeded,
|
||||||
expectedStatusMsg: "aws service unknown: timeout error: test",
|
expectedStatusMsg: "aws service unknown: timeout: test",
|
||||||
expectedPermission: &pb.Permission{
|
expectedPermission: &pb.Permission{
|
||||||
State: pb.StateType_STATE_TYPE_UNKNOWN,
|
State: pb.StateType_STATE_TYPE_UNKNOWN,
|
||||||
},
|
},
|
||||||
@@ -102,7 +102,7 @@ func Test_ParseAWSError(t *testing.T) {
|
|||||||
name: "http-status-code-internal-server-error",
|
name: "http-status-code-internal-server-error",
|
||||||
err: TestAwsHttpResponseError(http.StatusInternalServerError, "internal server error"),
|
err: TestAwsHttpResponseError(http.StatusInternalServerError, "internal server error"),
|
||||||
expectedStatusCode: codes.Unavailable,
|
expectedStatusCode: codes.Unavailable,
|
||||||
expectedStatusMsg: "aws service unknown: connectivity error: test",
|
expectedStatusMsg: "aws service unknown: connectivity: test",
|
||||||
expectedPermission: &pb.Permission{
|
expectedPermission: &pb.Permission{
|
||||||
State: pb.StateType_STATE_TYPE_UNKNOWN,
|
State: pb.StateType_STATE_TYPE_UNKNOWN,
|
||||||
},
|
},
|
||||||
@@ -111,7 +111,7 @@ func Test_ParseAWSError(t *testing.T) {
|
|||||||
name: "http-status-code-bad-gateway",
|
name: "http-status-code-bad-gateway",
|
||||||
err: TestAwsHttpResponseError(http.StatusBadGateway, "bad gateway"),
|
err: TestAwsHttpResponseError(http.StatusBadGateway, "bad gateway"),
|
||||||
expectedStatusCode: codes.Unavailable,
|
expectedStatusCode: codes.Unavailable,
|
||||||
expectedStatusMsg: "aws service unknown: connectivity error: test",
|
expectedStatusMsg: "aws service unknown: connectivity: test",
|
||||||
expectedPermission: &pb.Permission{
|
expectedPermission: &pb.Permission{
|
||||||
State: pb.StateType_STATE_TYPE_UNKNOWN,
|
State: pb.StateType_STATE_TYPE_UNKNOWN,
|
||||||
},
|
},
|
||||||
@@ -120,7 +120,7 @@ func Test_ParseAWSError(t *testing.T) {
|
|||||||
name: "http-status-code-service-unavailable",
|
name: "http-status-code-service-unavailable",
|
||||||
err: TestAwsHttpResponseError(http.StatusServiceUnavailable, "service unavailable"),
|
err: TestAwsHttpResponseError(http.StatusServiceUnavailable, "service unavailable"),
|
||||||
expectedStatusCode: codes.Unavailable,
|
expectedStatusCode: codes.Unavailable,
|
||||||
expectedStatusMsg: "aws service unknown: connectivity error: test",
|
expectedStatusMsg: "aws service unknown: connectivity: test",
|
||||||
expectedPermission: &pb.Permission{
|
expectedPermission: &pb.Permission{
|
||||||
State: pb.StateType_STATE_TYPE_UNKNOWN,
|
State: pb.StateType_STATE_TYPE_UNKNOWN,
|
||||||
},
|
},
|
||||||
@@ -129,7 +129,7 @@ func Test_ParseAWSError(t *testing.T) {
|
|||||||
name: "http-status-code-gateway-timeout",
|
name: "http-status-code-gateway-timeout",
|
||||||
err: TestAwsHttpResponseError(http.StatusGatewayTimeout, "gateway timeout"),
|
err: TestAwsHttpResponseError(http.StatusGatewayTimeout, "gateway timeout"),
|
||||||
expectedStatusCode: codes.DeadlineExceeded,
|
expectedStatusCode: codes.DeadlineExceeded,
|
||||||
expectedStatusMsg: "aws service unknown: timeout error: test",
|
expectedStatusMsg: "aws service unknown: timeout: test",
|
||||||
expectedPermission: &pb.Permission{
|
expectedPermission: &pb.Permission{
|
||||||
State: pb.StateType_STATE_TYPE_UNKNOWN,
|
State: pb.StateType_STATE_TYPE_UNKNOWN,
|
||||||
},
|
},
|
||||||
@@ -138,7 +138,7 @@ func Test_ParseAWSError(t *testing.T) {
|
|||||||
name: "aws-throttling",
|
name: "aws-throttling",
|
||||||
err: TestAwsError("Throttling", "throttling error"),
|
err: TestAwsError("Throttling", "throttling error"),
|
||||||
expectedStatusCode: codes.Unavailable,
|
expectedStatusCode: codes.Unavailable,
|
||||||
expectedStatusMsg: "aws service unknown: throttling error: test",
|
expectedStatusMsg: "aws service unknown: throttling: test",
|
||||||
expectedPermission: &pb.Permission{
|
expectedPermission: &pb.Permission{
|
||||||
State: pb.StateType_STATE_TYPE_UNKNOWN,
|
State: pb.StateType_STATE_TYPE_UNKNOWN,
|
||||||
},
|
},
|
||||||
@@ -147,7 +147,7 @@ func Test_ParseAWSError(t *testing.T) {
|
|||||||
name: "aws-throttling-exception",
|
name: "aws-throttling-exception",
|
||||||
err: TestAwsError("ThrottlingException", "throttling exception"),
|
err: TestAwsError("ThrottlingException", "throttling exception"),
|
||||||
expectedStatusCode: codes.Unavailable,
|
expectedStatusCode: codes.Unavailable,
|
||||||
expectedStatusMsg: "aws service unknown: throttling error: test",
|
expectedStatusMsg: "aws service unknown: throttling: test",
|
||||||
expectedPermission: &pb.Permission{
|
expectedPermission: &pb.Permission{
|
||||||
State: pb.StateType_STATE_TYPE_UNKNOWN,
|
State: pb.StateType_STATE_TYPE_UNKNOWN,
|
||||||
},
|
},
|
||||||
@@ -156,7 +156,7 @@ func Test_ParseAWSError(t *testing.T) {
|
|||||||
name: "aws-throttled-exception",
|
name: "aws-throttled-exception",
|
||||||
err: TestAwsError("ThrottledException", "throttled exception"),
|
err: TestAwsError("ThrottledException", "throttled exception"),
|
||||||
expectedStatusCode: codes.Unavailable,
|
expectedStatusCode: codes.Unavailable,
|
||||||
expectedStatusMsg: "aws service unknown: throttling error: test",
|
expectedStatusMsg: "aws service unknown: throttling: test",
|
||||||
expectedPermission: &pb.Permission{
|
expectedPermission: &pb.Permission{
|
||||||
State: pb.StateType_STATE_TYPE_UNKNOWN,
|
State: pb.StateType_STATE_TYPE_UNKNOWN,
|
||||||
},
|
},
|
||||||
@@ -165,7 +165,7 @@ func Test_ParseAWSError(t *testing.T) {
|
|||||||
name: "aws-request-throttled-exception",
|
name: "aws-request-throttled-exception",
|
||||||
err: TestAwsError("RequestThrottledException", "request throttled exception"),
|
err: TestAwsError("RequestThrottledException", "request throttled exception"),
|
||||||
expectedStatusCode: codes.Unavailable,
|
expectedStatusCode: codes.Unavailable,
|
||||||
expectedStatusMsg: "aws service unknown: throttling error: test",
|
expectedStatusMsg: "aws service unknown: throttling: test",
|
||||||
expectedPermission: &pb.Permission{
|
expectedPermission: &pb.Permission{
|
||||||
State: pb.StateType_STATE_TYPE_UNKNOWN,
|
State: pb.StateType_STATE_TYPE_UNKNOWN,
|
||||||
},
|
},
|
||||||
@@ -174,7 +174,7 @@ func Test_ParseAWSError(t *testing.T) {
|
|||||||
name: "aws-request-too-many-requests-exception",
|
name: "aws-request-too-many-requests-exception",
|
||||||
err: TestAwsError("TooManyRequestsException", "too many requests exception"),
|
err: TestAwsError("TooManyRequestsException", "too many requests exception"),
|
||||||
expectedStatusCode: codes.Unavailable,
|
expectedStatusCode: codes.Unavailable,
|
||||||
expectedStatusMsg: "aws service unknown: throttling error: test",
|
expectedStatusMsg: "aws service unknown: throttling: test",
|
||||||
expectedPermission: &pb.Permission{
|
expectedPermission: &pb.Permission{
|
||||||
State: pb.StateType_STATE_TYPE_UNKNOWN,
|
State: pb.StateType_STATE_TYPE_UNKNOWN,
|
||||||
},
|
},
|
||||||
@@ -183,7 +183,7 @@ func Test_ParseAWSError(t *testing.T) {
|
|||||||
name: "aws-request-provisioned-throughput-exceeded-exception",
|
name: "aws-request-provisioned-throughput-exceeded-exception",
|
||||||
err: TestAwsError("ProvisionedThroughputExceededException", "provisioned throughput exceeded exception"),
|
err: TestAwsError("ProvisionedThroughputExceededException", "provisioned throughput exceeded exception"),
|
||||||
expectedStatusCode: codes.Unavailable,
|
expectedStatusCode: codes.Unavailable,
|
||||||
expectedStatusMsg: "aws service unknown: throttling error: test",
|
expectedStatusMsg: "aws service unknown: throttling: test",
|
||||||
expectedPermission: &pb.Permission{
|
expectedPermission: &pb.Permission{
|
||||||
State: pb.StateType_STATE_TYPE_UNKNOWN,
|
State: pb.StateType_STATE_TYPE_UNKNOWN,
|
||||||
},
|
},
|
||||||
@@ -192,7 +192,7 @@ func Test_ParseAWSError(t *testing.T) {
|
|||||||
name: "aws-request-transaction-in-progress-exception",
|
name: "aws-request-transaction-in-progress-exception",
|
||||||
err: TestAwsError("TransactionInProgressException", "transaction in progress exception"),
|
err: TestAwsError("TransactionInProgressException", "transaction in progress exception"),
|
||||||
expectedStatusCode: codes.Unavailable,
|
expectedStatusCode: codes.Unavailable,
|
||||||
expectedStatusMsg: "aws service unknown: throttling error: test",
|
expectedStatusMsg: "aws service unknown: throttling: test",
|
||||||
expectedPermission: &pb.Permission{
|
expectedPermission: &pb.Permission{
|
||||||
State: pb.StateType_STATE_TYPE_UNKNOWN,
|
State: pb.StateType_STATE_TYPE_UNKNOWN,
|
||||||
},
|
},
|
||||||
@@ -201,7 +201,7 @@ func Test_ParseAWSError(t *testing.T) {
|
|||||||
name: "aws-request-limit-exceeded",
|
name: "aws-request-limit-exceeded",
|
||||||
err: TestAwsError("RequestLimitExceeded", "request limit exceeded"),
|
err: TestAwsError("RequestLimitExceeded", "request limit exceeded"),
|
||||||
expectedStatusCode: codes.Unavailable,
|
expectedStatusCode: codes.Unavailable,
|
||||||
expectedStatusMsg: "aws service unknown: throttling error: test",
|
expectedStatusMsg: "aws service unknown: throttling: test",
|
||||||
expectedPermission: &pb.Permission{
|
expectedPermission: &pb.Permission{
|
||||||
State: pb.StateType_STATE_TYPE_UNKNOWN,
|
State: pb.StateType_STATE_TYPE_UNKNOWN,
|
||||||
},
|
},
|
||||||
@@ -210,7 +210,7 @@ func Test_ParseAWSError(t *testing.T) {
|
|||||||
name: "aws-request-bandwidth-limit-exceeded",
|
name: "aws-request-bandwidth-limit-exceeded",
|
||||||
err: TestAwsError("BandwidthLimitExceeded", "bandwidth limit exceeded"),
|
err: TestAwsError("BandwidthLimitExceeded", "bandwidth limit exceeded"),
|
||||||
expectedStatusCode: codes.Unavailable,
|
expectedStatusCode: codes.Unavailable,
|
||||||
expectedStatusMsg: "aws service unknown: throttling error: test",
|
expectedStatusMsg: "aws service unknown: throttling: test",
|
||||||
expectedPermission: &pb.Permission{
|
expectedPermission: &pb.Permission{
|
||||||
State: pb.StateType_STATE_TYPE_UNKNOWN,
|
State: pb.StateType_STATE_TYPE_UNKNOWN,
|
||||||
},
|
},
|
||||||
@@ -219,7 +219,7 @@ func Test_ParseAWSError(t *testing.T) {
|
|||||||
name: "aws-request-limit-exceeded-exception",
|
name: "aws-request-limit-exceeded-exception",
|
||||||
err: TestAwsError("LimitExceededException", "limit exceeded exception"),
|
err: TestAwsError("LimitExceededException", "limit exceeded exception"),
|
||||||
expectedStatusCode: codes.Unavailable,
|
expectedStatusCode: codes.Unavailable,
|
||||||
expectedStatusMsg: "aws service unknown: throttling error: test",
|
expectedStatusMsg: "aws service unknown: throttling: test",
|
||||||
expectedPermission: &pb.Permission{
|
expectedPermission: &pb.Permission{
|
||||||
State: pb.StateType_STATE_TYPE_UNKNOWN,
|
State: pb.StateType_STATE_TYPE_UNKNOWN,
|
||||||
},
|
},
|
||||||
@@ -228,7 +228,7 @@ func Test_ParseAWSError(t *testing.T) {
|
|||||||
name: "aws-request-throttled",
|
name: "aws-request-throttled",
|
||||||
err: TestAwsError("RequestThrottled", "request throttled"),
|
err: TestAwsError("RequestThrottled", "request throttled"),
|
||||||
expectedStatusCode: codes.Unavailable,
|
expectedStatusCode: codes.Unavailable,
|
||||||
expectedStatusMsg: "aws service unknown: throttling error: test",
|
expectedStatusMsg: "aws service unknown: throttling: test",
|
||||||
expectedPermission: &pb.Permission{
|
expectedPermission: &pb.Permission{
|
||||||
State: pb.StateType_STATE_TYPE_UNKNOWN,
|
State: pb.StateType_STATE_TYPE_UNKNOWN,
|
||||||
},
|
},
|
||||||
@@ -237,7 +237,7 @@ func Test_ParseAWSError(t *testing.T) {
|
|||||||
name: "aws-request-slow-down",
|
name: "aws-request-slow-down",
|
||||||
err: TestAwsError("SlowDown", "slow down"),
|
err: TestAwsError("SlowDown", "slow down"),
|
||||||
expectedStatusCode: codes.Unavailable,
|
expectedStatusCode: codes.Unavailable,
|
||||||
expectedStatusMsg: "aws service unknown: throttling error: test",
|
expectedStatusMsg: "aws service unknown: throttling: test",
|
||||||
expectedPermission: &pb.Permission{
|
expectedPermission: &pb.Permission{
|
||||||
State: pb.StateType_STATE_TYPE_UNKNOWN,
|
State: pb.StateType_STATE_TYPE_UNKNOWN,
|
||||||
},
|
},
|
||||||
@@ -246,7 +246,7 @@ func Test_ParseAWSError(t *testing.T) {
|
|||||||
name: "aws-request-prior-request-not-complete",
|
name: "aws-request-prior-request-not-complete",
|
||||||
err: TestAwsError("PriorRequestNotComplete", "prior request not complete"),
|
err: TestAwsError("PriorRequestNotComplete", "prior request not complete"),
|
||||||
expectedStatusCode: codes.Unavailable,
|
expectedStatusCode: codes.Unavailable,
|
||||||
expectedStatusMsg: "aws service unknown: throttling error: test",
|
expectedStatusMsg: "aws service unknown: throttling: test",
|
||||||
expectedPermission: &pb.Permission{
|
expectedPermission: &pb.Permission{
|
||||||
State: pb.StateType_STATE_TYPE_UNKNOWN,
|
State: pb.StateType_STATE_TYPE_UNKNOWN,
|
||||||
},
|
},
|
||||||
@@ -255,7 +255,7 @@ func Test_ParseAWSError(t *testing.T) {
|
|||||||
name: "aws-request-ec2-throttled-exception",
|
name: "aws-request-ec2-throttled-exception",
|
||||||
err: TestAwsError("EC2ThrottledException", "ec2 throttled exception"),
|
err: TestAwsError("EC2ThrottledException", "ec2 throttled exception"),
|
||||||
expectedStatusCode: codes.Unavailable,
|
expectedStatusCode: codes.Unavailable,
|
||||||
expectedStatusMsg: "aws service unknown: throttling error: test",
|
expectedStatusMsg: "aws service unknown: throttling: test",
|
||||||
expectedPermission: &pb.Permission{
|
expectedPermission: &pb.Permission{
|
||||||
State: pb.StateType_STATE_TYPE_UNKNOWN,
|
State: pb.StateType_STATE_TYPE_UNKNOWN,
|
||||||
},
|
},
|
||||||
@@ -264,7 +264,7 @@ func Test_ParseAWSError(t *testing.T) {
|
|||||||
name: "aws-request-timeout",
|
name: "aws-request-timeout",
|
||||||
err: TestAwsError("RequestTimeout", "request timeout"),
|
err: TestAwsError("RequestTimeout", "request timeout"),
|
||||||
expectedStatusCode: codes.DeadlineExceeded,
|
expectedStatusCode: codes.DeadlineExceeded,
|
||||||
expectedStatusMsg: "aws unknown error: test",
|
expectedStatusMsg: "aws service unknown: test",
|
||||||
expectedPermission: &pb.Permission{
|
expectedPermission: &pb.Permission{
|
||||||
State: pb.StateType_STATE_TYPE_UNKNOWN,
|
State: pb.StateType_STATE_TYPE_UNKNOWN,
|
||||||
},
|
},
|
||||||
@@ -273,7 +273,7 @@ func Test_ParseAWSError(t *testing.T) {
|
|||||||
name: "aws-request-timeout-exception",
|
name: "aws-request-timeout-exception",
|
||||||
err: TestAwsError("RequestTimeoutException", "request timeout exception"),
|
err: TestAwsError("RequestTimeoutException", "request timeout exception"),
|
||||||
expectedStatusCode: codes.DeadlineExceeded,
|
expectedStatusCode: codes.DeadlineExceeded,
|
||||||
expectedStatusMsg: "aws unknown error: test",
|
expectedStatusMsg: "aws service unknown: test",
|
||||||
expectedPermission: &pb.Permission{
|
expectedPermission: &pb.Permission{
|
||||||
State: pb.StateType_STATE_TYPE_UNKNOWN,
|
State: pb.StateType_STATE_TYPE_UNKNOWN,
|
||||||
},
|
},
|
||||||
@@ -282,7 +282,7 @@ func Test_ParseAWSError(t *testing.T) {
|
|||||||
name: "aws-request-access-denied",
|
name: "aws-request-access-denied",
|
||||||
err: TestAwsError(awsErrorAccessDenied, "not authorized to perform action"),
|
err: TestAwsError(awsErrorAccessDenied, "not authorized to perform action"),
|
||||||
expectedStatusCode: codes.PermissionDenied,
|
expectedStatusCode: codes.PermissionDenied,
|
||||||
expectedStatusMsg: "aws service unknown: invalid credentials error: test",
|
expectedStatusMsg: "aws service unknown: invalid credentials: test",
|
||||||
expectedPermission: &pb.Permission{
|
expectedPermission: &pb.Permission{
|
||||||
State: pb.StateType_STATE_TYPE_ERROR,
|
State: pb.StateType_STATE_TYPE_ERROR,
|
||||||
ErrorDetails: "not authorized to perform action",
|
ErrorDetails: "not authorized to perform action",
|
||||||
@@ -293,7 +293,7 @@ func Test_ParseAWSError(t *testing.T) {
|
|||||||
name: "aws-request-invalid-access-key-id",
|
name: "aws-request-invalid-access-key-id",
|
||||||
err: TestAwsError(awsErrorInvalidAccessKeyId, "The AWS Access Key Id you provided does not exist in our records."),
|
err: TestAwsError(awsErrorInvalidAccessKeyId, "The AWS Access Key Id you provided does not exist in our records."),
|
||||||
expectedStatusCode: codes.PermissionDenied,
|
expectedStatusCode: codes.PermissionDenied,
|
||||||
expectedStatusMsg: "aws service unknown: invalid credentials error: test",
|
expectedStatusMsg: "aws service unknown: invalid credentials: test",
|
||||||
expectedPermission: &pb.Permission{
|
expectedPermission: &pb.Permission{
|
||||||
State: pb.StateType_STATE_TYPE_ERROR,
|
State: pb.StateType_STATE_TYPE_ERROR,
|
||||||
ErrorDetails: "The AWS Access Key Id you provided does not exist in our records.",
|
ErrorDetails: "The AWS Access Key Id you provided does not exist in our records.",
|
||||||
@@ -304,7 +304,7 @@ func Test_ParseAWSError(t *testing.T) {
|
|||||||
name: "aws-request-expired-token",
|
name: "aws-request-expired-token",
|
||||||
err: TestAwsError(awsErrorExpiredToken, "The security token included in the request is expired."),
|
err: TestAwsError(awsErrorExpiredToken, "The security token included in the request is expired."),
|
||||||
expectedStatusCode: codes.PermissionDenied,
|
expectedStatusCode: codes.PermissionDenied,
|
||||||
expectedStatusMsg: "aws service unknown: invalid credentials error: test",
|
expectedStatusMsg: "aws service unknown: invalid credentials: test",
|
||||||
expectedPermission: &pb.Permission{
|
expectedPermission: &pb.Permission{
|
||||||
State: pb.StateType_STATE_TYPE_ERROR,
|
State: pb.StateType_STATE_TYPE_ERROR,
|
||||||
ErrorDetails: "The security token included in the request is expired.",
|
ErrorDetails: "The security token included in the request is expired.",
|
||||||
@@ -316,14 +316,14 @@ func Test_ParseAWSError(t *testing.T) {
|
|||||||
t.Run(tt.name, func(t *testing.T) {
|
t.Run(tt.name, func(t *testing.T) {
|
||||||
require, assert := require.New(t), assert.New(t)
|
require, assert := require.New(t), assert.New(t)
|
||||||
|
|
||||||
actualStatus, permission := ParseAWSError(tt.err, "test")
|
actualStatus, permission := ParseAWSError("test", tt.err)
|
||||||
if tt.expectedStatusCode == codes.OK {
|
if tt.expectedStatusCode == codes.OK {
|
||||||
require.Nil(actualStatus)
|
require.Nil(actualStatus)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
require.Equal(tt.expectedStatusCode, actualStatus.Code())
|
require.Equal(tt.expectedStatusCode, actualStatus.Code())
|
||||||
require.Equal(tt.expectedStatusMsg, actualStatus.Message())
|
require.Contains(actualStatus.Message(), tt.expectedStatusMsg)
|
||||||
require.Len(actualStatus.Details(), 0)
|
require.Len(actualStatus.Details(), 0)
|
||||||
CheckSimilarPermission(assert, tt.expectedPermission, permission, true)
|
CheckSimilarPermission(assert, tt.expectedPermission, permission, true)
|
||||||
})
|
})
|
||||||
|
|||||||
+1
-1
@@ -32,6 +32,6 @@ type AwsPlugin struct {
|
|||||||
func NewAwsPlugin() *AwsPlugin {
|
func NewAwsPlugin() *AwsPlugin {
|
||||||
return &AwsPlugin{
|
return &AwsPlugin{
|
||||||
HostPlugin: &host.HostPlugin{},
|
HostPlugin: &host.HostPlugin{},
|
||||||
StoragePlugin: &storage.StoragePlugin{},
|
StoragePlugin: storage.New(),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -108,6 +108,14 @@ The following `attributes` are valid on an AWS host catalog resource:
|
|||||||
be performed. See the [Credential Rotation](../../../README.md#credential-rotation) readme for more information.
|
be performed. See the [Credential Rotation](../../../README.md#credential-rotation) readme for more information.
|
||||||
- `region` (string): The region to configure the host catalog for. All host sets
|
- `region` (string): The region to configure the host catalog for. All host sets
|
||||||
in this catalog will be configured for this region.
|
in this catalog will be configured for this region.
|
||||||
|
- `role_arn` (string): The AWS Role ARN used for `AssumeRole` authentication. If
|
||||||
|
provided, `disable_credential_rotation` must be also set to `true.
|
||||||
|
- `role_external_id` (string): The external id configured for the `AssumeRole`
|
||||||
|
provider.
|
||||||
|
- `role_session_name` (string): The session name configured for the `AssumeRole`
|
||||||
|
provider.
|
||||||
|
- `role_tags` (object): The key-value pair tags configured for the `AssumeRole`
|
||||||
|
provider.
|
||||||
|
|
||||||
The following `secrets` are required on an AWS host catalog resource:
|
The following `secrets` are required on an AWS host catalog resource:
|
||||||
|
|
||||||
|
|||||||
@@ -683,7 +683,7 @@ func TestPluginOnCreateCatalogErr(t *testing.T) {
|
|||||||
),
|
),
|
||||||
}),
|
}),
|
||||||
},
|
},
|
||||||
expectedErrContains: "error rotating credentials",
|
expectedErrContains: "rotating credentials",
|
||||||
expectedErrCode: codes.Unknown,
|
expectedErrCode: codes.Unknown,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
@@ -1089,7 +1089,7 @@ func TestPluginOnUpdateCatalogErr(t *testing.T) {
|
|||||||
),
|
),
|
||||||
}),
|
}),
|
||||||
},
|
},
|
||||||
expectedErrContains: "failed to delete access key",
|
expectedErrContains: "deleting credentials",
|
||||||
expectedErrCode: codes.Unknown,
|
expectedErrCode: codes.Unknown,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
@@ -1167,7 +1167,7 @@ func TestPluginOnUpdateCatalogErr(t *testing.T) {
|
|||||||
),
|
),
|
||||||
}),
|
}),
|
||||||
},
|
},
|
||||||
expectedErrContains: "error rotating credentials",
|
expectedErrContains: "rotating credentials",
|
||||||
expectedErrCode: codes.Unknown,
|
expectedErrCode: codes.Unknown,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
@@ -1298,7 +1298,7 @@ func TestPluginOnDeleteCatalogErr(t *testing.T) {
|
|||||||
),
|
),
|
||||||
}),
|
}),
|
||||||
},
|
},
|
||||||
expectedErrContains: "aws service unknown: unknown error: failed to delete access key",
|
expectedErrContains: "aws service unknown: unknown error: deleting credentials:",
|
||||||
expectedErrCode: codes.Unknown,
|
expectedErrCode: codes.Unknown,
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -50,12 +50,12 @@ func setStatePermission(req any, permission *pb.Permission) *pb.StorageBucketCre
|
|||||||
// credentials, and connectivity error types.
|
// credentials, and connectivity error types.
|
||||||
//
|
//
|
||||||
// https://docs.aws.amazon.com/AmazonS3/latest/API/ErrorResponses.html
|
// https://docs.aws.amazon.com/AmazonS3/latest/API/ErrorResponses.html
|
||||||
func parseS3Error(err error, msg string, req any) *status.Status {
|
func parseS3Error(op string, err error, req any) *status.Status {
|
||||||
if err == nil {
|
if err == nil {
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
st, p := errors.ParseAWSError(err, msg)
|
st, p := errors.ParseAWSError(op, err)
|
||||||
if p != nil {
|
if p != nil {
|
||||||
state := setStatePermission(req, p)
|
state := setStatePermission(req, p)
|
||||||
if st, err = st.WithDetails(state); err != nil {
|
if st, err = st.WithDetails(state); err != nil {
|
||||||
|
|||||||
@@ -34,7 +34,7 @@ func Test_ParseS3Error(t *testing.T) {
|
|||||||
req: &pb.GetObjectRequest{},
|
req: &pb.GetObjectRequest{},
|
||||||
err: TestAwsS3Error("NoSuchBucket", "GetObject", "no such bucket"),
|
err: TestAwsS3Error("NoSuchBucket", "GetObject", "no such bucket"),
|
||||||
expectedStatusCode: codes.NotFound,
|
expectedStatusCode: codes.NotFound,
|
||||||
expectedStatusMsg: "aws s3 error: test",
|
expectedStatusMsg: "aws service s3: test:",
|
||||||
expectedDetails: &pb.StorageBucketCredentialState{
|
expectedDetails: &pb.StorageBucketCredentialState{
|
||||||
State: &pb.Permissions{
|
State: &pb.Permissions{
|
||||||
Read: &pb.Permission{
|
Read: &pb.Permission{
|
||||||
@@ -50,7 +50,7 @@ func Test_ParseS3Error(t *testing.T) {
|
|||||||
req: &pb.GetObjectRequest{},
|
req: &pb.GetObjectRequest{},
|
||||||
err: TestAwsS3Error("NoSuchKey", "GetObject", "no such key"),
|
err: TestAwsS3Error("NoSuchKey", "GetObject", "no such key"),
|
||||||
expectedStatusCode: codes.NotFound,
|
expectedStatusCode: codes.NotFound,
|
||||||
expectedStatusMsg: "aws s3 error: test",
|
expectedStatusMsg: "aws service s3: test:",
|
||||||
expectedDetails: &pb.StorageBucketCredentialState{
|
expectedDetails: &pb.StorageBucketCredentialState{
|
||||||
State: &pb.Permissions{
|
State: &pb.Permissions{
|
||||||
Read: &pb.Permission{
|
Read: &pb.Permission{
|
||||||
@@ -66,7 +66,7 @@ func Test_ParseS3Error(t *testing.T) {
|
|||||||
err: errors.TestAwsHttpResponseError(404, "not found"),
|
err: errors.TestAwsHttpResponseError(404, "not found"),
|
||||||
expectedStatusCode: codes.NotFound,
|
expectedStatusCode: codes.NotFound,
|
||||||
// cannot embed services within mock http errors, therefore unknown service
|
// cannot embed services within mock http errors, therefore unknown service
|
||||||
expectedStatusMsg: "aws service unknown: resource not found error: test",
|
expectedStatusMsg: "aws service unknown: resource not found: test:",
|
||||||
expectedDetails: &pb.StorageBucketCredentialState{
|
expectedDetails: &pb.StorageBucketCredentialState{
|
||||||
State: &pb.Permissions{
|
State: &pb.Permissions{
|
||||||
Read: &pb.Permission{
|
Read: &pb.Permission{
|
||||||
@@ -81,7 +81,7 @@ func Test_ParseS3Error(t *testing.T) {
|
|||||||
req: &pb.GetObjectRequest{},
|
req: &pb.GetObjectRequest{},
|
||||||
err: TestAwsS3Error("InvalidObjectState", "GetObject", "invalid object state"),
|
err: TestAwsS3Error("InvalidObjectState", "GetObject", "invalid object state"),
|
||||||
expectedStatusCode: codes.NotFound,
|
expectedStatusCode: codes.NotFound,
|
||||||
expectedStatusMsg: "aws s3 error: test",
|
expectedStatusMsg: "aws service s3: test:",
|
||||||
expectedDetails: &pb.StorageBucketCredentialState{
|
expectedDetails: &pb.StorageBucketCredentialState{
|
||||||
State: &pb.Permissions{
|
State: &pb.Permissions{
|
||||||
Read: &pb.Permission{
|
Read: &pb.Permission{
|
||||||
@@ -96,7 +96,7 @@ func Test_ParseS3Error(t *testing.T) {
|
|||||||
req: &pb.PutObjectRequest{},
|
req: &pb.PutObjectRequest{},
|
||||||
err: TestAwsS3Error(s3ErrorBadDigest, "PutObject", "checksum mismatch"),
|
err: TestAwsS3Error(s3ErrorBadDigest, "PutObject", "checksum mismatch"),
|
||||||
expectedStatusCode: codes.Aborted,
|
expectedStatusCode: codes.Aborted,
|
||||||
expectedStatusMsg: "aws s3 error: test",
|
expectedStatusMsg: "aws service s3: test:",
|
||||||
expectedDetails: &pb.StorageBucketCredentialState{
|
expectedDetails: &pb.StorageBucketCredentialState{
|
||||||
State: &pb.Permissions{
|
State: &pb.Permissions{
|
||||||
Write: &pb.Permission{
|
Write: &pb.Permission{
|
||||||
@@ -111,7 +111,7 @@ func Test_ParseS3Error(t *testing.T) {
|
|||||||
req: &pb.GetObjectRequest{},
|
req: &pb.GetObjectRequest{},
|
||||||
err: TestAwsS3Error("Throttling", "action", "throttling exception"),
|
err: TestAwsS3Error("Throttling", "action", "throttling exception"),
|
||||||
expectedStatusCode: codes.Unavailable,
|
expectedStatusCode: codes.Unavailable,
|
||||||
expectedStatusMsg: "aws service s3: throttling error: test",
|
expectedStatusMsg: "aws service s3: throttling: test:",
|
||||||
expectedDetails: &pb.StorageBucketCredentialState{
|
expectedDetails: &pb.StorageBucketCredentialState{
|
||||||
State: &pb.Permissions{
|
State: &pb.Permissions{
|
||||||
Read: &pb.Permission{
|
Read: &pb.Permission{
|
||||||
@@ -126,7 +126,7 @@ func Test_ParseS3Error(t *testing.T) {
|
|||||||
req: &pb.GetObjectRequest{},
|
req: &pb.GetObjectRequest{},
|
||||||
err: TestAwsS3Error("RequestTimeout", "action", "request timeout"),
|
err: TestAwsS3Error("RequestTimeout", "action", "request timeout"),
|
||||||
expectedStatusCode: codes.DeadlineExceeded,
|
expectedStatusCode: codes.DeadlineExceeded,
|
||||||
expectedStatusMsg: "aws s3 error: test",
|
expectedStatusMsg: "aws service s3: test:",
|
||||||
expectedDetails: &pb.StorageBucketCredentialState{
|
expectedDetails: &pb.StorageBucketCredentialState{
|
||||||
State: &pb.Permissions{
|
State: &pb.Permissions{
|
||||||
Read: &pb.Permission{
|
Read: &pb.Permission{
|
||||||
@@ -141,7 +141,7 @@ func Test_ParseS3Error(t *testing.T) {
|
|||||||
req: &pb.GetObjectRequest{},
|
req: &pb.GetObjectRequest{},
|
||||||
err: TestAwsS3Error("AccessDenied", "action", "access denied"),
|
err: TestAwsS3Error("AccessDenied", "action", "access denied"),
|
||||||
expectedStatusCode: codes.PermissionDenied,
|
expectedStatusCode: codes.PermissionDenied,
|
||||||
expectedStatusMsg: "aws service s3: invalid credentials error: test",
|
expectedStatusMsg: "aws service s3: invalid credentials: test:",
|
||||||
expectedDetails: &pb.StorageBucketCredentialState{
|
expectedDetails: &pb.StorageBucketCredentialState{
|
||||||
State: &pb.Permissions{
|
State: &pb.Permissions{
|
||||||
Read: &pb.Permission{
|
Read: &pb.Permission{
|
||||||
@@ -157,14 +157,14 @@ func Test_ParseS3Error(t *testing.T) {
|
|||||||
t.Run(tt.name, func(t *testing.T) {
|
t.Run(tt.name, func(t *testing.T) {
|
||||||
require, assert := require.New(t), assert.New(t)
|
require, assert := require.New(t), assert.New(t)
|
||||||
|
|
||||||
actualStatus := parseS3Error(tt.err, "test", tt.req)
|
actualStatus := parseS3Error("test", tt.err, tt.req)
|
||||||
if tt.expectedStatusCode == codes.OK {
|
if tt.expectedStatusCode == codes.OK {
|
||||||
require.Nil(actualStatus)
|
require.Nil(actualStatus)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
require.Equal(tt.expectedStatusCode, actualStatus.Code())
|
require.Equal(tt.expectedStatusCode, actualStatus.Code())
|
||||||
require.Equal(tt.expectedStatusMsg, actualStatus.Message())
|
require.Contains(actualStatus.Message(), tt.expectedStatusMsg)
|
||||||
require.Len(actualStatus.Details(), 1)
|
require.Len(actualStatus.Details(), 1)
|
||||||
|
|
||||||
foundDetail := false
|
foundDetail := false
|
||||||
|
|||||||
@@ -14,6 +14,7 @@ import (
|
|||||||
"os"
|
"os"
|
||||||
"path"
|
"path"
|
||||||
"strings"
|
"strings"
|
||||||
|
"sync"
|
||||||
|
|
||||||
"github.com/aws/aws-sdk-go-v2/aws"
|
"github.com/aws/aws-sdk-go-v2/aws"
|
||||||
"github.com/aws/aws-sdk-go-v2/service/s3"
|
"github.com/aws/aws-sdk-go-v2/service/s3"
|
||||||
@@ -32,6 +33,14 @@ import (
|
|||||||
// Ensure that we are implementing StoragePluginServiceServer
|
// Ensure that we are implementing StoragePluginServiceServer
|
||||||
var _ pb.StoragePluginServiceServer = (*StoragePlugin)(nil)
|
var _ pb.StoragePluginServiceServer = (*StoragePlugin)(nil)
|
||||||
|
|
||||||
|
// clientCache caches previously initialized S3API clients created with a RoleARN attribute.
|
||||||
|
type clientCache struct {
|
||||||
|
// cache is a map of cached s3 clients. The key of the map
|
||||||
|
// is the public id of the storage bucket
|
||||||
|
cache map[string]S3API
|
||||||
|
sync.RWMutex
|
||||||
|
}
|
||||||
|
|
||||||
// StoragePlugin implements the StoragePluginServiceServer interface for the
|
// StoragePlugin implements the StoragePluginServiceServer interface for the
|
||||||
// AWS storage service plugin.
|
// AWS storage service plugin.
|
||||||
type StoragePlugin struct {
|
type StoragePlugin struct {
|
||||||
@@ -42,6 +51,112 @@ type StoragePlugin struct {
|
|||||||
|
|
||||||
// testStorageStateOpts are passed in to the stored state to control test behavior
|
// testStorageStateOpts are passed in to the stored state to control test behavior
|
||||||
testStorageStateOpts []awsStoragePersistedStateOption
|
testStorageStateOpts []awsStoragePersistedStateOption
|
||||||
|
|
||||||
|
clients *clientCache
|
||||||
|
}
|
||||||
|
|
||||||
|
// New creates a new StoragePlugin
|
||||||
|
func New() *StoragePlugin {
|
||||||
|
c := &clientCache{
|
||||||
|
cache: make(map[string]S3API),
|
||||||
|
}
|
||||||
|
return &StoragePlugin{
|
||||||
|
clients: c,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// getClient returns an S3API client for the given storage bucket id.
|
||||||
|
func (p *StoragePlugin) getClient(ctx context.Context,
|
||||||
|
storageBucketId string,
|
||||||
|
storageState *awsStoragePersistedState,
|
||||||
|
opt ...s3Option) (S3API, error) {
|
||||||
|
if storageBucketId == "" {
|
||||||
|
// No storage bucket ID to key cache on, create new client and return
|
||||||
|
client, err := storageState.s3Client(ctx, opt...)
|
||||||
|
if err != nil {
|
||||||
|
return nil, errors.BadRequestStatus("error creating S3 client: %s", err)
|
||||||
|
}
|
||||||
|
return client, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
opts, err := getOpts(opt...)
|
||||||
|
if err != nil {
|
||||||
|
return nil, fmt.Errorf("error parsing options in cache: %w", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
if opts.withCacheRefresh {
|
||||||
|
// Forced cache refresh
|
||||||
|
p.clients.Lock()
|
||||||
|
defer p.clients.Unlock()
|
||||||
|
client, err := storageState.s3Client(ctx, opt...)
|
||||||
|
if err != nil {
|
||||||
|
return nil, errors.BadRequestStatus("error creating S3 client: %s", err)
|
||||||
|
}
|
||||||
|
p.clients.cache[storageBucketId] = client
|
||||||
|
return client, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
p.clients.RLock()
|
||||||
|
client, ok := p.clients.cache[storageBucketId]
|
||||||
|
p.clients.RUnlock()
|
||||||
|
|
||||||
|
if !ok || client.Credentials().Expired() {
|
||||||
|
// We got a cache miss or the credentials has expired, time to refresh
|
||||||
|
p.clients.Lock()
|
||||||
|
defer p.clients.Unlock()
|
||||||
|
|
||||||
|
// Check cache again in case another caller updated it since we got the lock
|
||||||
|
client, ok = p.clients.cache[storageBucketId]
|
||||||
|
if ok && !client.Credentials().Expired() {
|
||||||
|
// Got a cache hit with valid credentials, return cached client
|
||||||
|
return client, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// Create new client and cache it
|
||||||
|
var err error
|
||||||
|
client, err = storageState.s3Client(ctx, opt...)
|
||||||
|
if err != nil {
|
||||||
|
return nil, errors.BadRequestStatus("error creating S3 client: %s", err)
|
||||||
|
}
|
||||||
|
p.clients.cache[storageBucketId] = client
|
||||||
|
}
|
||||||
|
return client, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
type s3Caller func(client S3API) (any, error)
|
||||||
|
|
||||||
|
// call gets the s3client and passes it to the s3caller function, if a permission error is returned
|
||||||
|
// it forces a cache refresh and tries again.
|
||||||
|
func (p *StoragePlugin) call(
|
||||||
|
ctx context.Context,
|
||||||
|
fn s3Caller,
|
||||||
|
storageBucketId string,
|
||||||
|
storageState *awsStoragePersistedState,
|
||||||
|
opts ...s3Option) (any, error) {
|
||||||
|
|
||||||
|
s3Client, err := p.getClient(ctx, storageBucketId, storageState, opts...)
|
||||||
|
if err != nil {
|
||||||
|
return nil, errors.BadRequestStatus("error getting S3 client: %s", err)
|
||||||
|
}
|
||||||
|
firstAttempt := true
|
||||||
|
for {
|
||||||
|
resp, err := fn(s3Client)
|
||||||
|
if err != nil {
|
||||||
|
st, _ := errors.ParseAWSError("", err)
|
||||||
|
if st.Code() == codes.PermissionDenied && firstAttempt {
|
||||||
|
// We got a permission error, this is the first time so refresh cache and try again
|
||||||
|
firstAttempt = false
|
||||||
|
opts = append(opts, WithCacheRefresh(true))
|
||||||
|
s3Client, err = p.getClient(ctx, storageBucketId, storageState, opts...)
|
||||||
|
if err != nil {
|
||||||
|
return nil, errors.BadRequestStatus("error refreshing S3 client: %s", err)
|
||||||
|
}
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
return resp, nil
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// OnCreateStorageBucket is called when a storage bucket is created.
|
// OnCreateStorageBucket is called when a storage bucket is created.
|
||||||
@@ -346,19 +461,20 @@ func (p *StoragePlugin) HeadObject(ctx context.Context, req *pb.HeadObjectReques
|
|||||||
if storageAttributes.EndpointUrl != "" {
|
if storageAttributes.EndpointUrl != "" {
|
||||||
opts = append(opts, WithEndpoint(storageAttributes.EndpointUrl))
|
opts = append(opts, WithEndpoint(storageAttributes.EndpointUrl))
|
||||||
}
|
}
|
||||||
s3Client, err := storageState.S3Client(ctx, opts...)
|
|
||||||
if err != nil {
|
|
||||||
return nil, errors.BadRequestStatus("error getting S3 client: %s", err)
|
|
||||||
}
|
|
||||||
|
|
||||||
objectKey := path.Join(bucket.GetBucketPrefix(), req.GetKey())
|
objectKey := path.Join(bucket.GetBucketPrefix(), req.GetKey())
|
||||||
resp, err := s3Client.HeadObject(ctx, &s3.HeadObjectInput{
|
headCall := func(s3Client S3API) (any, error) {
|
||||||
Bucket: aws.String(bucket.GetBucketName()),
|
return s3Client.HeadObject(ctx, &s3.HeadObjectInput{
|
||||||
Key: aws.String(objectKey),
|
Bucket: aws.String(bucket.GetBucketName()),
|
||||||
})
|
Key: aws.String(objectKey),
|
||||||
if err != nil {
|
})
|
||||||
return nil, parseS3Error(err, "failed to head object", req).Err()
|
|
||||||
}
|
}
|
||||||
|
headResp, err := p.call(ctx, headCall, bucket.GetId(), storageState, opts...)
|
||||||
|
if err != nil {
|
||||||
|
return nil, parseS3Error("head object", err, req).Err()
|
||||||
|
}
|
||||||
|
resp := headResp.(*s3.HeadObjectOutput)
|
||||||
|
|
||||||
return &pb.HeadObjectResponse{
|
return &pb.HeadObjectResponse{
|
||||||
ContentLength: resp.ContentLength,
|
ContentLength: resp.ContentLength,
|
||||||
LastModified: timestamppb.New(*resp.LastModified),
|
LastModified: timestamppb.New(*resp.LastModified),
|
||||||
@@ -472,19 +588,19 @@ func (p *StoragePlugin) GetObject(req *pb.GetObjectRequest, stream pb.StoragePlu
|
|||||||
if storageAttributes.EndpointUrl != "" {
|
if storageAttributes.EndpointUrl != "" {
|
||||||
opts = append(opts, WithEndpoint(storageAttributes.EndpointUrl))
|
opts = append(opts, WithEndpoint(storageAttributes.EndpointUrl))
|
||||||
}
|
}
|
||||||
s3Client, err := storageState.S3Client(stream.Context(), opts...)
|
|
||||||
if err != nil {
|
|
||||||
return errors.BadRequestStatus("error getting S3 client: %s", err)
|
|
||||||
}
|
|
||||||
|
|
||||||
objectKey := path.Join(bucket.GetBucketPrefix(), req.GetKey())
|
objectKey := path.Join(bucket.GetBucketPrefix(), req.GetKey())
|
||||||
resp, err := s3Client.GetObject(stream.Context(), &s3.GetObjectInput{
|
getCall := func(s3Client S3API) (any, error) {
|
||||||
Bucket: aws.String(bucket.GetBucketName()),
|
return s3Client.GetObject(stream.Context(), &s3.GetObjectInput{
|
||||||
Key: aws.String(objectKey),
|
Bucket: aws.String(bucket.GetBucketName()),
|
||||||
})
|
Key: aws.String(objectKey),
|
||||||
if err != nil {
|
})
|
||||||
return parseS3Error(err, "failed to get object", req).Err()
|
|
||||||
}
|
}
|
||||||
|
getResp, err := p.call(stream.Context(), getCall, bucket.GetId(), storageState, opts...)
|
||||||
|
if err != nil {
|
||||||
|
return parseS3Error("get object", err, req).Err()
|
||||||
|
}
|
||||||
|
resp := getResp.(*s3.GetObjectOutput)
|
||||||
|
|
||||||
defer resp.Body.Close()
|
defer resp.Body.Close()
|
||||||
reader := bufio.NewReader(resp.Body)
|
reader := bufio.NewReader(resp.Body)
|
||||||
@@ -583,10 +699,6 @@ func (p *StoragePlugin) PutObject(ctx context.Context, req *pb.PutObjectRequest)
|
|||||||
if storageAttributes.EndpointUrl != "" {
|
if storageAttributes.EndpointUrl != "" {
|
||||||
opts = append(opts, WithEndpoint(storageAttributes.EndpointUrl))
|
opts = append(opts, WithEndpoint(storageAttributes.EndpointUrl))
|
||||||
}
|
}
|
||||||
s3Client, err := storageState.S3Client(ctx, opts...)
|
|
||||||
if err != nil {
|
|
||||||
return nil, errors.BadRequestStatus("error getting S3 client: %s", err)
|
|
||||||
}
|
|
||||||
|
|
||||||
hash := sha256.New()
|
hash := sha256.New()
|
||||||
if _, err := io.Copy(hash, file); err != nil {
|
if _, err := io.Copy(hash, file); err != nil {
|
||||||
@@ -598,18 +710,23 @@ func (p *StoragePlugin) PutObject(ctx context.Context, req *pb.PutObjectRequest)
|
|||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, errors.UnknownStatus("failed to rewind file pointer")
|
return nil, errors.UnknownStatus("failed to rewind file pointer")
|
||||||
}
|
}
|
||||||
|
|
||||||
objectKey := path.Join(bucket.GetBucketPrefix(), req.GetKey())
|
objectKey := path.Join(bucket.GetBucketPrefix(), req.GetKey())
|
||||||
resp, err := s3Client.PutObject(ctx, &s3.PutObjectInput{
|
|
||||||
Bucket: aws.String(bucket.GetBucketName()),
|
putCall := func(s3Client S3API) (any, error) {
|
||||||
Key: aws.String(objectKey),
|
return s3Client.PutObject(ctx, &s3.PutObjectInput{
|
||||||
Body: file,
|
Bucket: aws.String(bucket.GetBucketName()),
|
||||||
ChecksumAlgorithm: types.ChecksumAlgorithmSha256,
|
Key: aws.String(objectKey),
|
||||||
ChecksumSHA256: aws.String(checksum),
|
Body: file,
|
||||||
})
|
ChecksumAlgorithm: types.ChecksumAlgorithmSha256,
|
||||||
if err != nil {
|
ChecksumSHA256: aws.String(checksum),
|
||||||
return nil, parseS3Error(err, "failed to put object", req).Err()
|
})
|
||||||
}
|
}
|
||||||
|
putResp, err := p.call(ctx, putCall, bucket.GetId(), storageState, opts...)
|
||||||
|
if err != nil {
|
||||||
|
return nil, parseS3Error("put object", err, req).Err()
|
||||||
|
}
|
||||||
|
resp := putResp.(*s3.PutObjectOutput)
|
||||||
|
|
||||||
if resp.ChecksumSHA256 == nil {
|
if resp.ChecksumSHA256 == nil {
|
||||||
return nil, errors.UnknownStatus("missing checksum response from aws")
|
return nil, errors.UnknownStatus("missing checksum response from aws")
|
||||||
}
|
}
|
||||||
@@ -678,10 +795,6 @@ func (p *StoragePlugin) DeleteObjects(ctx context.Context, req *pb.DeleteObjects
|
|||||||
if storageAttributes.EndpointUrl != "" {
|
if storageAttributes.EndpointUrl != "" {
|
||||||
opts = append(opts, WithEndpoint(storageAttributes.EndpointUrl))
|
opts = append(opts, WithEndpoint(storageAttributes.EndpointUrl))
|
||||||
}
|
}
|
||||||
client, err := storageState.S3Client(ctx, opts...)
|
|
||||||
if err != nil {
|
|
||||||
return nil, errors.BadRequestStatus("error getting S3 client: %s", err)
|
|
||||||
}
|
|
||||||
|
|
||||||
prefix := path.Join(bucket.GetBucketPrefix(), req.GetKeyPrefix())
|
prefix := path.Join(bucket.GetBucketPrefix(), req.GetKeyPrefix())
|
||||||
if strings.HasSuffix(req.GetKeyPrefix(), "/") {
|
if strings.HasSuffix(req.GetKeyPrefix(), "/") {
|
||||||
@@ -691,12 +804,19 @@ func (p *StoragePlugin) DeleteObjects(ctx context.Context, req *pb.DeleteObjects
|
|||||||
}
|
}
|
||||||
|
|
||||||
if !req.Recursive {
|
if !req.Recursive {
|
||||||
_, err := client.DeleteObject(ctx, &s3.DeleteObjectInput{
|
deleteCall := func(s3Client S3API) (any, error) {
|
||||||
Bucket: aws.String(bucket.GetBucketName()),
|
return s3Client.DeleteObject(ctx, &s3.DeleteObjectInput{
|
||||||
Key: aws.String(prefix),
|
Bucket: aws.String(bucket.GetBucketName()),
|
||||||
})
|
Key: aws.String(prefix),
|
||||||
|
})
|
||||||
|
}
|
||||||
|
_, err := p.call(ctx, deleteCall, bucket.GetId(), storageState, opts...)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, parseS3Error(err, "failed to delete object", req).Err()
|
return nil, parseS3Error("delete object", err, req).Err()
|
||||||
|
}
|
||||||
|
|
||||||
|
if err != nil {
|
||||||
|
return nil, parseS3Error("delete object", err, req).Err()
|
||||||
}
|
}
|
||||||
return &pb.DeleteObjectsResponse{
|
return &pb.DeleteObjectsResponse{
|
||||||
ObjectsDeleted: uint32(1),
|
ObjectsDeleted: uint32(1),
|
||||||
@@ -704,10 +824,13 @@ func (p *StoragePlugin) DeleteObjects(ctx context.Context, req *pb.DeleteObjects
|
|||||||
}
|
}
|
||||||
|
|
||||||
const maxkeys = 1000
|
const maxkeys = 1000
|
||||||
|
|
||||||
objects := []types.ObjectIdentifier{}
|
objects := []types.ObjectIdentifier{}
|
||||||
|
client, err := p.getClient(ctx, bucket.GetId(), storageState, opts...)
|
||||||
|
if err != nil {
|
||||||
|
return nil, errors.BadRequestStatus("error getting S3 client: %s", err)
|
||||||
|
}
|
||||||
var conToken *string
|
var conToken *string
|
||||||
truncated := true
|
truncated, firstAttempt := true, true
|
||||||
for truncated {
|
for truncated {
|
||||||
res, err := client.ListObjectsV2(ctx, &s3.ListObjectsV2Input{
|
res, err := client.ListObjectsV2(ctx, &s3.ListObjectsV2Input{
|
||||||
Bucket: aws.String(bucket.GetBucketName()),
|
Bucket: aws.String(bucket.GetBucketName()),
|
||||||
@@ -716,7 +839,19 @@ func (p *StoragePlugin) DeleteObjects(ctx context.Context, req *pb.DeleteObjects
|
|||||||
ContinuationToken: conToken,
|
ContinuationToken: conToken,
|
||||||
})
|
})
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, parseS3Error(err, "failed to list objects", req).Err()
|
st, _ := errors.ParseAWSError("", err)
|
||||||
|
if st.Code() == codes.PermissionDenied && firstAttempt {
|
||||||
|
// This is first attempt and we get a permission error, refresh cache and try again
|
||||||
|
firstAttempt = false
|
||||||
|
opts = append(opts, WithCacheRefresh(true))
|
||||||
|
client, err = p.getClient(ctx, bucket.GetId(), storageState, opts...)
|
||||||
|
if err != nil {
|
||||||
|
return nil, errors.BadRequestStatus("error refreshing S3 client: %s", err)
|
||||||
|
}
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil, parseS3Error("list objects", err, req).Err()
|
||||||
}
|
}
|
||||||
truncated = res.IsTruncated
|
truncated = res.IsTruncated
|
||||||
conToken = res.NextContinuationToken
|
conToken = res.NextContinuationToken
|
||||||
@@ -738,7 +873,7 @@ func (p *StoragePlugin) DeleteObjects(ctx context.Context, req *pb.DeleteObjects
|
|||||||
},
|
},
|
||||||
})
|
})
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, parseS3Error(err, "failed to delete objects", req).Err()
|
return nil, parseS3Error("delete objects", err, req).Err()
|
||||||
}
|
}
|
||||||
deleted += len(res.Deleted)
|
deleted += len(res.Deleted)
|
||||||
}
|
}
|
||||||
@@ -770,7 +905,7 @@ func dryRunValidation(ctx context.Context, state *awsStoragePersistedState, attr
|
|||||||
opts = append(opts, WithEndpoint(attrs.EndpointUrl))
|
opts = append(opts, WithEndpoint(attrs.EndpointUrl))
|
||||||
}
|
}
|
||||||
|
|
||||||
client, err := state.S3Client(ctx, opts...)
|
client, err := state.s3Client(ctx, opts...)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return status.New(codes.InvalidArgument, fmt.Sprintf("error getting S3 client: %s", err))
|
return status.New(codes.InvalidArgument, fmt.Sprintf("error getting S3 client: %s", err))
|
||||||
}
|
}
|
||||||
@@ -786,7 +921,7 @@ func dryRunValidation(ctx context.Context, state *awsStoragePersistedState, attr
|
|||||||
Body: bytes.NewReader([]byte("hashicorp boundary aws plugin access test")),
|
Body: bytes.NewReader([]byte("hashicorp boundary aws plugin access test")),
|
||||||
}); err != nil {
|
}); err != nil {
|
||||||
var st *status.Status
|
var st *status.Status
|
||||||
st, permissions.Write = errors.ParseAWSError(err, "failed to put object")
|
st, permissions.Write = errors.ParseAWSError("put object", err)
|
||||||
errs = multierror.Append(errs, st.Err())
|
errs = multierror.Append(errs, st.Err())
|
||||||
} else {
|
} else {
|
||||||
permissions.Write = &pb.Permission{State: pb.StateType_STATE_TYPE_OK, CheckedAt: timestamppb.Now()}
|
permissions.Write = &pb.Permission{State: pb.StateType_STATE_TYPE_OK, CheckedAt: timestamppb.Now()}
|
||||||
@@ -797,7 +932,7 @@ func dryRunValidation(ctx context.Context, state *awsStoragePersistedState, attr
|
|||||||
Key: aws.String(objectKey),
|
Key: aws.String(objectKey),
|
||||||
}); err != nil {
|
}); err != nil {
|
||||||
var st *status.Status
|
var st *status.Status
|
||||||
st, permissions.Read = errors.ParseAWSError(err, "failed to get object")
|
st, permissions.Read = errors.ParseAWSError("get object", err)
|
||||||
errs = multierror.Append(errs, st.Err())
|
errs = multierror.Append(errs, st.Err())
|
||||||
} else {
|
} else {
|
||||||
permissions.Read = &pb.Permission{State: pb.StateType_STATE_TYPE_OK, CheckedAt: timestamppb.Now()}
|
permissions.Read = &pb.Permission{State: pb.StateType_STATE_TYPE_OK, CheckedAt: timestamppb.Now()}
|
||||||
@@ -808,7 +943,7 @@ func dryRunValidation(ctx context.Context, state *awsStoragePersistedState, attr
|
|||||||
Key: aws.String(objectKey),
|
Key: aws.String(objectKey),
|
||||||
}); err != nil {
|
}); err != nil {
|
||||||
var st *status.Status
|
var st *status.Status
|
||||||
st, permissions.Read = errors.ParseAWSError(err, "failed to head object")
|
st, permissions.Read = errors.ParseAWSError("head object", err)
|
||||||
errs = multierror.Append(errs, st.Err())
|
errs = multierror.Append(errs, st.Err())
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -817,7 +952,7 @@ func dryRunValidation(ctx context.Context, state *awsStoragePersistedState, attr
|
|||||||
Prefix: aws.String(objectKey),
|
Prefix: aws.String(objectKey),
|
||||||
}); err != nil {
|
}); err != nil {
|
||||||
var st *status.Status
|
var st *status.Status
|
||||||
st, permissions.Read = errors.ParseAWSError(err, "failed to list object")
|
st, permissions.Read = errors.ParseAWSError("list object", err)
|
||||||
errs = multierror.Append(errs, st.Err())
|
errs = multierror.Append(errs, st.Err())
|
||||||
} else if res == nil || len(res.Contents) != 1 || *res.Contents[0].Key != objectKey {
|
} else if res == nil || len(res.Contents) != 1 || *res.Contents[0].Key != objectKey {
|
||||||
permissions.Read = &pb.Permission{State: pb.StateType_STATE_TYPE_UNKNOWN, CheckedAt: timestamppb.Now()}
|
permissions.Read = &pb.Permission{State: pb.StateType_STATE_TYPE_UNKNOWN, CheckedAt: timestamppb.Now()}
|
||||||
@@ -829,7 +964,7 @@ func dryRunValidation(ctx context.Context, state *awsStoragePersistedState, attr
|
|||||||
Key: aws.String(objectKey),
|
Key: aws.String(objectKey),
|
||||||
}); err != nil {
|
}); err != nil {
|
||||||
var st *status.Status
|
var st *status.Status
|
||||||
st, permissions.Delete = errors.ParseAWSError(err, "failed to delete object")
|
st, permissions.Delete = errors.ParseAWSError("delete object", err)
|
||||||
errs = multierror.Append(errs, st.Err())
|
errs = multierror.Append(errs, st.Err())
|
||||||
} else {
|
} else {
|
||||||
permissions.Delete = &pb.Permission{State: pb.StateType_STATE_TYPE_OK, CheckedAt: timestamppb.Now()}
|
permissions.Delete = &pb.Permission{State: pb.StateType_STATE_TYPE_OK, CheckedAt: timestamppb.Now()}
|
||||||
|
|||||||
@@ -153,7 +153,7 @@ func TestStoragePlugin_OnCreateStorageBucket(t *testing.T) {
|
|||||||
),
|
),
|
||||||
}),
|
}),
|
||||||
},
|
},
|
||||||
expectedErrContains: "aws service unknown: unknown error: error rotating credentials",
|
expectedErrContains: "aws service unknown: unknown error: rotating credentials:",
|
||||||
expectedErrCode: codes.Unknown,
|
expectedErrCode: codes.Unknown,
|
||||||
expectedDetails: nil,
|
expectedDetails: nil,
|
||||||
},
|
},
|
||||||
@@ -178,7 +178,7 @@ func TestStoragePlugin_OnCreateStorageBucket(t *testing.T) {
|
|||||||
}),
|
}),
|
||||||
)),
|
)),
|
||||||
},
|
},
|
||||||
expectedErrContains: "aws service s3: invalid credentials error: failed to put object",
|
expectedErrContains: "aws service s3: invalid credentials: put object:",
|
||||||
expectedErrCode: codes.FailedPrecondition,
|
expectedErrCode: codes.FailedPrecondition,
|
||||||
expectedDetails: &pb.StorageBucketCredentialState{
|
expectedDetails: &pb.StorageBucketCredentialState{
|
||||||
State: &pb.Permissions{
|
State: &pb.Permissions{
|
||||||
@@ -211,7 +211,7 @@ func TestStoragePlugin_OnCreateStorageBucket(t *testing.T) {
|
|||||||
}),
|
}),
|
||||||
)),
|
)),
|
||||||
},
|
},
|
||||||
expectedErrContains: "aws service s3: throttling error: failed to put object",
|
expectedErrContains: "aws service s3: throttling: put object:",
|
||||||
expectedErrCode: codes.FailedPrecondition,
|
expectedErrCode: codes.FailedPrecondition,
|
||||||
expectedDetails: &pb.StorageBucketCredentialState{
|
expectedDetails: &pb.StorageBucketCredentialState{
|
||||||
State: &pb.Permissions{
|
State: &pb.Permissions{
|
||||||
@@ -245,7 +245,7 @@ func TestStoragePlugin_OnCreateStorageBucket(t *testing.T) {
|
|||||||
),
|
),
|
||||||
),
|
),
|
||||||
},
|
},
|
||||||
expectedErrContains: "aws service s3: invalid credentials error: failed to get object",
|
expectedErrContains: "aws service s3: invalid credentials: get object:",
|
||||||
expectedErrCode: codes.FailedPrecondition,
|
expectedErrCode: codes.FailedPrecondition,
|
||||||
expectedDetails: &pb.StorageBucketCredentialState{
|
expectedDetails: &pb.StorageBucketCredentialState{
|
||||||
State: &pb.Permissions{
|
State: &pb.Permissions{
|
||||||
@@ -280,7 +280,7 @@ func TestStoragePlugin_OnCreateStorageBucket(t *testing.T) {
|
|||||||
),
|
),
|
||||||
),
|
),
|
||||||
},
|
},
|
||||||
expectedErrContains: "aws service s3: throttling error: failed to get object",
|
expectedErrContains: "aws service s3: throttling: get object:",
|
||||||
expectedErrCode: codes.FailedPrecondition,
|
expectedErrCode: codes.FailedPrecondition,
|
||||||
expectedDetails: &pb.StorageBucketCredentialState{
|
expectedDetails: &pb.StorageBucketCredentialState{
|
||||||
State: &pb.Permissions{
|
State: &pb.Permissions{
|
||||||
@@ -314,7 +314,7 @@ func TestStoragePlugin_OnCreateStorageBucket(t *testing.T) {
|
|||||||
),
|
),
|
||||||
),
|
),
|
||||||
},
|
},
|
||||||
expectedErrContains: "aws service s3: invalid credentials error: failed to head object",
|
expectedErrContains: "aws service s3: invalid credentials: head object:",
|
||||||
expectedErrCode: codes.FailedPrecondition,
|
expectedErrCode: codes.FailedPrecondition,
|
||||||
expectedDetails: &pb.StorageBucketCredentialState{
|
expectedDetails: &pb.StorageBucketCredentialState{
|
||||||
State: &pb.Permissions{
|
State: &pb.Permissions{
|
||||||
@@ -349,7 +349,7 @@ func TestStoragePlugin_OnCreateStorageBucket(t *testing.T) {
|
|||||||
),
|
),
|
||||||
),
|
),
|
||||||
},
|
},
|
||||||
expectedErrContains: "aws service s3: throttling error: failed to head object",
|
expectedErrContains: "aws service s3: throttling: head object:",
|
||||||
expectedErrCode: codes.FailedPrecondition,
|
expectedErrCode: codes.FailedPrecondition,
|
||||||
expectedDetails: &pb.StorageBucketCredentialState{
|
expectedDetails: &pb.StorageBucketCredentialState{
|
||||||
State: &pb.Permissions{
|
State: &pb.Permissions{
|
||||||
@@ -384,7 +384,7 @@ func TestStoragePlugin_OnCreateStorageBucket(t *testing.T) {
|
|||||||
),
|
),
|
||||||
),
|
),
|
||||||
},
|
},
|
||||||
expectedErrContains: "aws service s3: invalid credentials error: failed to delete object",
|
expectedErrContains: "aws service s3: invalid credentials: delete object:",
|
||||||
expectedErrCode: codes.FailedPrecondition,
|
expectedErrCode: codes.FailedPrecondition,
|
||||||
expectedDetails: &pb.StorageBucketCredentialState{
|
expectedDetails: &pb.StorageBucketCredentialState{
|
||||||
State: &pb.Permissions{
|
State: &pb.Permissions{
|
||||||
@@ -857,7 +857,7 @@ func TestStoragePlugin_OnUpdateStorageBucket(t *testing.T) {
|
|||||||
),
|
),
|
||||||
}),
|
}),
|
||||||
},
|
},
|
||||||
expectedErrContains: "aws service unknown: unknown error: failed to delete access key",
|
expectedErrContains: "aws service unknown: unknown error: deleting credentials:",
|
||||||
expectedErrCode: codes.Unknown,
|
expectedErrCode: codes.Unknown,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
@@ -896,7 +896,7 @@ func TestStoragePlugin_OnUpdateStorageBucket(t *testing.T) {
|
|||||||
),
|
),
|
||||||
}),
|
}),
|
||||||
},
|
},
|
||||||
expectedErrContains: "aws service unknown: unknown error: error rotating credentials",
|
expectedErrContains: "aws service unknown: unknown error: rotating credentials",
|
||||||
expectedErrCode: codes.Unknown,
|
expectedErrCode: codes.Unknown,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
@@ -920,7 +920,7 @@ func TestStoragePlugin_OnUpdateStorageBucket(t *testing.T) {
|
|||||||
testMockS3WithPutObjectError(TestAwsS3Error("AccessDenied", "PutObject", "not authorized")),
|
testMockS3WithPutObjectError(TestAwsS3Error("AccessDenied", "PutObject", "not authorized")),
|
||||||
)),
|
)),
|
||||||
},
|
},
|
||||||
expectedErrContains: "aws service s3: invalid credentials error: failed to put object",
|
expectedErrContains: "aws service s3: invalid credentials: put object:",
|
||||||
expectedErrCode: codes.FailedPrecondition,
|
expectedErrCode: codes.FailedPrecondition,
|
||||||
expectedDetails: &pb.StorageBucketCredentialState{
|
expectedDetails: &pb.StorageBucketCredentialState{
|
||||||
State: &pb.Permissions{
|
State: &pb.Permissions{
|
||||||
@@ -953,7 +953,7 @@ func TestStoragePlugin_OnUpdateStorageBucket(t *testing.T) {
|
|||||||
testMockS3WithPutObjectError(TestAwsS3Error("ThrottlingException", "PutObject", "throttling exception")),
|
testMockS3WithPutObjectError(TestAwsS3Error("ThrottlingException", "PutObject", "throttling exception")),
|
||||||
)),
|
)),
|
||||||
},
|
},
|
||||||
expectedErrContains: "aws service s3: throttling error: failed to put object",
|
expectedErrContains: "aws service s3: throttling: put object",
|
||||||
expectedErrCode: codes.FailedPrecondition,
|
expectedErrCode: codes.FailedPrecondition,
|
||||||
expectedDetails: &pb.StorageBucketCredentialState{
|
expectedDetails: &pb.StorageBucketCredentialState{
|
||||||
State: &pb.Permissions{
|
State: &pb.Permissions{
|
||||||
@@ -987,7 +987,7 @@ func TestStoragePlugin_OnUpdateStorageBucket(t *testing.T) {
|
|||||||
),
|
),
|
||||||
),
|
),
|
||||||
},
|
},
|
||||||
expectedErrContains: "aws service s3: invalid credentials error: failed to get object",
|
expectedErrContains: "aws service s3: invalid credentials: get object",
|
||||||
expectedErrCode: codes.FailedPrecondition,
|
expectedErrCode: codes.FailedPrecondition,
|
||||||
expectedDetails: &pb.StorageBucketCredentialState{
|
expectedDetails: &pb.StorageBucketCredentialState{
|
||||||
State: &pb.Permissions{
|
State: &pb.Permissions{
|
||||||
@@ -1022,7 +1022,7 @@ func TestStoragePlugin_OnUpdateStorageBucket(t *testing.T) {
|
|||||||
),
|
),
|
||||||
),
|
),
|
||||||
},
|
},
|
||||||
expectedErrContains: "aws service s3: throttling error: failed to get object",
|
expectedErrContains: "aws service s3: throttling: get object",
|
||||||
expectedErrCode: codes.FailedPrecondition,
|
expectedErrCode: codes.FailedPrecondition,
|
||||||
expectedDetails: &pb.StorageBucketCredentialState{
|
expectedDetails: &pb.StorageBucketCredentialState{
|
||||||
State: &pb.Permissions{
|
State: &pb.Permissions{
|
||||||
@@ -1056,7 +1056,7 @@ func TestStoragePlugin_OnUpdateStorageBucket(t *testing.T) {
|
|||||||
),
|
),
|
||||||
),
|
),
|
||||||
},
|
},
|
||||||
expectedErrContains: "aws service s3: invalid credentials error: failed to head object",
|
expectedErrContains: "aws service s3: invalid credentials: head object",
|
||||||
expectedErrCode: codes.FailedPrecondition,
|
expectedErrCode: codes.FailedPrecondition,
|
||||||
expectedDetails: &pb.StorageBucketCredentialState{
|
expectedDetails: &pb.StorageBucketCredentialState{
|
||||||
State: &pb.Permissions{
|
State: &pb.Permissions{
|
||||||
@@ -1091,7 +1091,7 @@ func TestStoragePlugin_OnUpdateStorageBucket(t *testing.T) {
|
|||||||
),
|
),
|
||||||
),
|
),
|
||||||
},
|
},
|
||||||
expectedErrContains: "aws service s3: throttling error: failed to head object",
|
expectedErrContains: "aws service s3: throttling: head object",
|
||||||
expectedErrCode: codes.FailedPrecondition,
|
expectedErrCode: codes.FailedPrecondition,
|
||||||
expectedDetails: &pb.StorageBucketCredentialState{
|
expectedDetails: &pb.StorageBucketCredentialState{
|
||||||
State: &pb.Permissions{
|
State: &pb.Permissions{
|
||||||
@@ -1255,7 +1255,7 @@ func TestStoragePlugin_OnUpdateStorageBucket(t *testing.T) {
|
|||||||
),
|
),
|
||||||
),
|
),
|
||||||
},
|
},
|
||||||
expectedErrContains: "aws service s3: invalid credentials error: failed to head object",
|
expectedErrContains: "aws service s3: invalid credentials: head object",
|
||||||
expectedErrCode: codes.FailedPrecondition,
|
expectedErrCode: codes.FailedPrecondition,
|
||||||
expectedDetails: &pb.StorageBucketCredentialState{
|
expectedDetails: &pb.StorageBucketCredentialState{
|
||||||
State: &pb.Permissions{
|
State: &pb.Permissions{
|
||||||
@@ -1363,7 +1363,7 @@ func TestStoragePlugin_OnUpdateStorageBucket(t *testing.T) {
|
|||||||
),
|
),
|
||||||
),
|
),
|
||||||
},
|
},
|
||||||
expectedErrContains: "aws service s3: invalid credentials error: failed to put object",
|
expectedErrContains: "aws service s3: invalid credentials: put object",
|
||||||
expectedErrCode: codes.FailedPrecondition,
|
expectedErrCode: codes.FailedPrecondition,
|
||||||
expectedDetails: &pb.StorageBucketCredentialState{
|
expectedDetails: &pb.StorageBucketCredentialState{
|
||||||
State: &pb.Permissions{
|
State: &pb.Permissions{
|
||||||
@@ -1486,7 +1486,7 @@ func TestStoragePlugin_OnUpdateStorageBucket(t *testing.T) {
|
|||||||
),
|
),
|
||||||
}),
|
}),
|
||||||
},
|
},
|
||||||
expectedErrContains: "aws service unknown: unknown error: failed to delete access key",
|
expectedErrContains: "aws service unknown: unknown error: deleting credentials",
|
||||||
expectedErrCode: codes.Unknown,
|
expectedErrCode: codes.Unknown,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
@@ -1599,7 +1599,7 @@ func TestStoragePlugin_OnUpdateStorageBucket(t *testing.T) {
|
|||||||
),
|
),
|
||||||
),
|
),
|
||||||
},
|
},
|
||||||
expectedErrContains: "aws service s3: invalid credentials error: failed to put object",
|
expectedErrContains: "aws service s3: invalid credentials: put object",
|
||||||
expectedErrCode: codes.FailedPrecondition,
|
expectedErrCode: codes.FailedPrecondition,
|
||||||
expectedDetails: &pb.StorageBucketCredentialState{
|
expectedDetails: &pb.StorageBucketCredentialState{
|
||||||
State: &pb.Permissions{
|
State: &pb.Permissions{
|
||||||
@@ -1738,7 +1738,7 @@ func TestStoragePlugin_OnUpdateStorageBucket(t *testing.T) {
|
|||||||
),
|
),
|
||||||
}),
|
}),
|
||||||
},
|
},
|
||||||
expectedErrContains: "aws service unknown: unknown error: error rotating credentials",
|
expectedErrContains: "aws service unknown: unknown error: rotating credentials",
|
||||||
expectedErrCode: codes.Unknown,
|
expectedErrCode: codes.Unknown,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
@@ -1973,7 +1973,7 @@ func TestStoragePlugin_OnDeleteStorageBucket(t *testing.T) {
|
|||||||
),
|
),
|
||||||
}),
|
}),
|
||||||
},
|
},
|
||||||
expectedErrContains: "aws service unknown: unknown error: failed to delete access key",
|
expectedErrContains: "aws service unknown: unknown error: deleting credentials",
|
||||||
expectedErrCode: codes.Unknown,
|
expectedErrCode: codes.Unknown,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
@@ -2172,7 +2172,7 @@ func TestStoragePlugin_HeadObject(t *testing.T) {
|
|||||||
),
|
),
|
||||||
),
|
),
|
||||||
},
|
},
|
||||||
expectedErrContains: "aws service s3: invalid credentials error: failed to head object",
|
expectedErrContains: "aws service s3: invalid credentials: head object",
|
||||||
expectedErrCode: codes.PermissionDenied,
|
expectedErrCode: codes.PermissionDenied,
|
||||||
expectedDetails: &pb.StorageBucketCredentialState{
|
expectedDetails: &pb.StorageBucketCredentialState{
|
||||||
State: &pb.Permissions{
|
State: &pb.Permissions{
|
||||||
@@ -2196,7 +2196,7 @@ func TestStoragePlugin_HeadObject(t *testing.T) {
|
|||||||
),
|
),
|
||||||
),
|
),
|
||||||
},
|
},
|
||||||
expectedErrContains: "aws service s3: throttling error: failed to head object",
|
expectedErrContains: "aws service s3: throttling: head object",
|
||||||
expectedErrCode: codes.Unavailable,
|
expectedErrCode: codes.Unavailable,
|
||||||
expectedDetails: &pb.StorageBucketCredentialState{
|
expectedDetails: &pb.StorageBucketCredentialState{
|
||||||
State: &pb.Permissions{
|
State: &pb.Permissions{
|
||||||
@@ -2219,7 +2219,7 @@ func TestStoragePlugin_HeadObject(t *testing.T) {
|
|||||||
),
|
),
|
||||||
),
|
),
|
||||||
},
|
},
|
||||||
expectedErrContains: "aws s3 error: failed to head object",
|
expectedErrContains: "aws service s3: head object",
|
||||||
expectedErrCode: codes.NotFound,
|
expectedErrCode: codes.NotFound,
|
||||||
expectedDetails: &pb.StorageBucketCredentialState{
|
expectedDetails: &pb.StorageBucketCredentialState{
|
||||||
State: &pb.Permissions{
|
State: &pb.Permissions{
|
||||||
@@ -2425,7 +2425,7 @@ func TestStoragePlugin_ValidatePermissions(t *testing.T) {
|
|||||||
}),
|
}),
|
||||||
)),
|
)),
|
||||||
},
|
},
|
||||||
expectedErrContains: "aws service s3: invalid credentials error: failed to put object",
|
expectedErrContains: "aws service s3: invalid credentials: put object",
|
||||||
expectedErrCode: codes.FailedPrecondition,
|
expectedErrCode: codes.FailedPrecondition,
|
||||||
expectedDetails: &pb.StorageBucketCredentialState{
|
expectedDetails: &pb.StorageBucketCredentialState{
|
||||||
State: &pb.Permissions{
|
State: &pb.Permissions{
|
||||||
@@ -2458,7 +2458,7 @@ func TestStoragePlugin_ValidatePermissions(t *testing.T) {
|
|||||||
}),
|
}),
|
||||||
)),
|
)),
|
||||||
},
|
},
|
||||||
expectedErrContains: "aws service s3: throttling error: failed to put object",
|
expectedErrContains: "aws service s3: throttling: put object",
|
||||||
expectedErrCode: codes.FailedPrecondition,
|
expectedErrCode: codes.FailedPrecondition,
|
||||||
expectedDetails: &pb.StorageBucketCredentialState{
|
expectedDetails: &pb.StorageBucketCredentialState{
|
||||||
State: &pb.Permissions{
|
State: &pb.Permissions{
|
||||||
@@ -2492,7 +2492,7 @@ func TestStoragePlugin_ValidatePermissions(t *testing.T) {
|
|||||||
),
|
),
|
||||||
),
|
),
|
||||||
},
|
},
|
||||||
expectedErrContains: "aws service s3: invalid credentials error: failed to get object",
|
expectedErrContains: "aws service s3: invalid credentials: get object",
|
||||||
expectedErrCode: codes.FailedPrecondition,
|
expectedErrCode: codes.FailedPrecondition,
|
||||||
expectedDetails: &pb.StorageBucketCredentialState{
|
expectedDetails: &pb.StorageBucketCredentialState{
|
||||||
State: &pb.Permissions{
|
State: &pb.Permissions{
|
||||||
@@ -2527,7 +2527,7 @@ func TestStoragePlugin_ValidatePermissions(t *testing.T) {
|
|||||||
),
|
),
|
||||||
),
|
),
|
||||||
},
|
},
|
||||||
expectedErrContains: "aws service s3: throttling error: failed to get object",
|
expectedErrContains: "aws service s3: throttling: get object",
|
||||||
expectedErrCode: codes.FailedPrecondition,
|
expectedErrCode: codes.FailedPrecondition,
|
||||||
expectedDetails: &pb.StorageBucketCredentialState{
|
expectedDetails: &pb.StorageBucketCredentialState{
|
||||||
State: &pb.Permissions{
|
State: &pb.Permissions{
|
||||||
@@ -2561,7 +2561,7 @@ func TestStoragePlugin_ValidatePermissions(t *testing.T) {
|
|||||||
),
|
),
|
||||||
),
|
),
|
||||||
},
|
},
|
||||||
expectedErrContains: "aws service s3: invalid credentials error: failed to head object",
|
expectedErrContains: "aws service s3: invalid credentials: head object",
|
||||||
expectedErrCode: codes.FailedPrecondition,
|
expectedErrCode: codes.FailedPrecondition,
|
||||||
expectedDetails: &pb.StorageBucketCredentialState{
|
expectedDetails: &pb.StorageBucketCredentialState{
|
||||||
State: &pb.Permissions{
|
State: &pb.Permissions{
|
||||||
@@ -2596,7 +2596,7 @@ func TestStoragePlugin_ValidatePermissions(t *testing.T) {
|
|||||||
),
|
),
|
||||||
),
|
),
|
||||||
},
|
},
|
||||||
expectedErrContains: "aws service s3: throttling error: failed to head object",
|
expectedErrContains: "aws service s3: throttling: head object",
|
||||||
expectedErrCode: codes.FailedPrecondition,
|
expectedErrCode: codes.FailedPrecondition,
|
||||||
expectedDetails: &pb.StorageBucketCredentialState{
|
expectedDetails: &pb.StorageBucketCredentialState{
|
||||||
State: &pb.Permissions{
|
State: &pb.Permissions{
|
||||||
@@ -2683,7 +2683,7 @@ func TestStoragePlugin_ValidatePermissions(t *testing.T) {
|
|||||||
}),
|
}),
|
||||||
)),
|
)),
|
||||||
},
|
},
|
||||||
expectedErrContains: "aws service s3: invalid credentials error: failed to put object",
|
expectedErrContains: "aws service s3: invalid credentials: put object",
|
||||||
expectedErrCode: codes.FailedPrecondition,
|
expectedErrCode: codes.FailedPrecondition,
|
||||||
expectedDetails: &pb.StorageBucketCredentialState{
|
expectedDetails: &pb.StorageBucketCredentialState{
|
||||||
State: &pb.Permissions{
|
State: &pb.Permissions{
|
||||||
@@ -2851,7 +2851,7 @@ func TestStoragePlugin_GetObject(t *testing.T) {
|
|||||||
),
|
),
|
||||||
),
|
),
|
||||||
},
|
},
|
||||||
expectedErrContains: "aws service s3: invalid credentials error: failed to get object",
|
expectedErrContains: "aws service s3: invalid credentials: get object",
|
||||||
expectedErrCode: codes.PermissionDenied,
|
expectedErrCode: codes.PermissionDenied,
|
||||||
expectedDetails: &pb.StorageBucketCredentialState{
|
expectedDetails: &pb.StorageBucketCredentialState{
|
||||||
State: &pb.Permissions{
|
State: &pb.Permissions{
|
||||||
@@ -2875,7 +2875,7 @@ func TestStoragePlugin_GetObject(t *testing.T) {
|
|||||||
),
|
),
|
||||||
),
|
),
|
||||||
},
|
},
|
||||||
expectedErrContains: "aws s3 error: failed to get object",
|
expectedErrContains: "aws service s3: get object",
|
||||||
expectedErrCode: codes.NotFound,
|
expectedErrCode: codes.NotFound,
|
||||||
expectedDetails: &pb.StorageBucketCredentialState{
|
expectedDetails: &pb.StorageBucketCredentialState{
|
||||||
State: &pb.Permissions{
|
State: &pb.Permissions{
|
||||||
@@ -2898,7 +2898,7 @@ func TestStoragePlugin_GetObject(t *testing.T) {
|
|||||||
),
|
),
|
||||||
),
|
),
|
||||||
},
|
},
|
||||||
expectedErrContains: "aws s3 error: failed to get object",
|
expectedErrContains: "aws service s3: get object",
|
||||||
expectedErrCode: codes.NotFound,
|
expectedErrCode: codes.NotFound,
|
||||||
expectedDetails: &pb.StorageBucketCredentialState{
|
expectedDetails: &pb.StorageBucketCredentialState{
|
||||||
State: &pb.Permissions{
|
State: &pb.Permissions{
|
||||||
@@ -2921,7 +2921,7 @@ func TestStoragePlugin_GetObject(t *testing.T) {
|
|||||||
),
|
),
|
||||||
),
|
),
|
||||||
},
|
},
|
||||||
expectedErrContains: "aws s3 error: failed to get object",
|
expectedErrContains: "aws service s3: get object",
|
||||||
expectedErrCode: codes.NotFound,
|
expectedErrCode: codes.NotFound,
|
||||||
expectedDetails: &pb.StorageBucketCredentialState{
|
expectedDetails: &pb.StorageBucketCredentialState{
|
||||||
State: &pb.Permissions{
|
State: &pb.Permissions{
|
||||||
@@ -2945,7 +2945,7 @@ func TestStoragePlugin_GetObject(t *testing.T) {
|
|||||||
),
|
),
|
||||||
),
|
),
|
||||||
},
|
},
|
||||||
expectedErrContains: "aws service s3: throttling error: failed to get object",
|
expectedErrContains: "aws service s3: throttling: get object",
|
||||||
expectedErrCode: codes.Unavailable,
|
expectedErrCode: codes.Unavailable,
|
||||||
expectedDetails: &pb.StorageBucketCredentialState{
|
expectedDetails: &pb.StorageBucketCredentialState{
|
||||||
State: &pb.Permissions{
|
State: &pb.Permissions{
|
||||||
@@ -3300,7 +3300,7 @@ func TestStoragePlugin_PutObject(t *testing.T) {
|
|||||||
),
|
),
|
||||||
),
|
),
|
||||||
},
|
},
|
||||||
expectedErrContains: "aws service s3: invalid credentials error: failed to put object",
|
expectedErrContains: "aws service s3: invalid credentials: put object",
|
||||||
expectedErrCode: codes.PermissionDenied,
|
expectedErrCode: codes.PermissionDenied,
|
||||||
expectedDetails: &pb.StorageBucketCredentialState{
|
expectedDetails: &pb.StorageBucketCredentialState{
|
||||||
State: &pb.Permissions{
|
State: &pb.Permissions{
|
||||||
@@ -3324,7 +3324,7 @@ func TestStoragePlugin_PutObject(t *testing.T) {
|
|||||||
),
|
),
|
||||||
),
|
),
|
||||||
},
|
},
|
||||||
expectedErrContains: "aws s3 error: failed to put object",
|
expectedErrContains: "aws service s3: put object",
|
||||||
expectedErrCode: codes.NotFound,
|
expectedErrCode: codes.NotFound,
|
||||||
expectedDetails: &pb.StorageBucketCredentialState{
|
expectedDetails: &pb.StorageBucketCredentialState{
|
||||||
State: &pb.Permissions{
|
State: &pb.Permissions{
|
||||||
@@ -3348,7 +3348,7 @@ func TestStoragePlugin_PutObject(t *testing.T) {
|
|||||||
),
|
),
|
||||||
),
|
),
|
||||||
},
|
},
|
||||||
expectedErrContains: "aws service s3: throttling error: failed to put object",
|
expectedErrContains: "aws service s3: throttling: put object",
|
||||||
expectedErrCode: codes.Unavailable,
|
expectedErrCode: codes.Unavailable,
|
||||||
expectedDetails: &pb.StorageBucketCredentialState{
|
expectedDetails: &pb.StorageBucketCredentialState{
|
||||||
State: &pb.Permissions{
|
State: &pb.Permissions{
|
||||||
@@ -3640,7 +3640,7 @@ func TestStoragePlugin_DeleteObjects(t *testing.T) {
|
|||||||
),
|
),
|
||||||
),
|
),
|
||||||
},
|
},
|
||||||
expectedErrContains: "aws service s3: invalid credentials error: failed to delete object",
|
expectedErrContains: "aws service s3: invalid credentials: delete object",
|
||||||
expectedErrCode: codes.PermissionDenied,
|
expectedErrCode: codes.PermissionDenied,
|
||||||
expectedDetails: &pb.StorageBucketCredentialState{
|
expectedDetails: &pb.StorageBucketCredentialState{
|
||||||
State: &pb.Permissions{
|
State: &pb.Permissions{
|
||||||
@@ -3664,7 +3664,7 @@ func TestStoragePlugin_DeleteObjects(t *testing.T) {
|
|||||||
),
|
),
|
||||||
),
|
),
|
||||||
},
|
},
|
||||||
expectedErrContains: "aws service s3: invalid credentials error: failed to list objects",
|
expectedErrContains: "aws service s3: invalid credentials: list objects",
|
||||||
expectedErrCode: codes.PermissionDenied,
|
expectedErrCode: codes.PermissionDenied,
|
||||||
expectedDetails: &pb.StorageBucketCredentialState{
|
expectedDetails: &pb.StorageBucketCredentialState{
|
||||||
State: &pb.Permissions{
|
State: &pb.Permissions{
|
||||||
@@ -3696,7 +3696,7 @@ func TestStoragePlugin_DeleteObjects(t *testing.T) {
|
|||||||
),
|
),
|
||||||
),
|
),
|
||||||
},
|
},
|
||||||
expectedErrContains: "aws service s3: invalid credentials error: failed to delete objects",
|
expectedErrContains: "aws service s3: invalid credentials: delete objects",
|
||||||
expectedErrCode: codes.PermissionDenied,
|
expectedErrCode: codes.PermissionDenied,
|
||||||
expectedDetails: &pb.StorageBucketCredentialState{
|
expectedDetails: &pb.StorageBucketCredentialState{
|
||||||
State: &pb.Permissions{
|
State: &pb.Permissions{
|
||||||
@@ -3720,7 +3720,7 @@ func TestStoragePlugin_DeleteObjects(t *testing.T) {
|
|||||||
),
|
),
|
||||||
),
|
),
|
||||||
},
|
},
|
||||||
expectedErrContains: "aws service s3: throttling error: failed to delete object",
|
expectedErrContains: "aws service s3: throttling: delete object",
|
||||||
expectedErrCode: codes.Unavailable,
|
expectedErrCode: codes.Unavailable,
|
||||||
expectedDetails: &pb.StorageBucketCredentialState{
|
expectedDetails: &pb.StorageBucketCredentialState{
|
||||||
State: &pb.Permissions{
|
State: &pb.Permissions{
|
||||||
@@ -3743,7 +3743,7 @@ func TestStoragePlugin_DeleteObjects(t *testing.T) {
|
|||||||
),
|
),
|
||||||
),
|
),
|
||||||
},
|
},
|
||||||
expectedErrContains: "aws service s3: throttling error: failed to list objects",
|
expectedErrContains: "aws service s3: throttling: list objects",
|
||||||
expectedErrCode: codes.Unavailable,
|
expectedErrCode: codes.Unavailable,
|
||||||
expectedDetails: &pb.StorageBucketCredentialState{
|
expectedDetails: &pb.StorageBucketCredentialState{
|
||||||
State: &pb.Permissions{
|
State: &pb.Permissions{
|
||||||
@@ -3774,7 +3774,7 @@ func TestStoragePlugin_DeleteObjects(t *testing.T) {
|
|||||||
),
|
),
|
||||||
),
|
),
|
||||||
},
|
},
|
||||||
expectedErrContains: "aws service s3: throttling error: failed to delete objects",
|
expectedErrContains: "aws service s3: throttling: delete objects",
|
||||||
expectedErrCode: codes.Unavailable,
|
expectedErrCode: codes.Unavailable,
|
||||||
expectedDetails: &pb.StorageBucketCredentialState{
|
expectedDetails: &pb.StorageBucketCredentialState{
|
||||||
State: &pb.Permissions{
|
State: &pb.Permissions{
|
||||||
@@ -3917,3 +3917,89 @@ func TestStoragePlugin_DeleteObjects(t *testing.T) {
|
|||||||
})
|
})
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func TestS3ClientCache(t *testing.T) {
|
||||||
|
ctx := context.Background()
|
||||||
|
assert, require := assert.New(t), require.New(t)
|
||||||
|
|
||||||
|
cred1, err := awsutil.NewCredentialsConfig(
|
||||||
|
awsutil.WithRegion("us-east-1"),
|
||||||
|
awsutil.WithAccessKey("cred1"),
|
||||||
|
awsutil.WithSecretKey("secret1"),
|
||||||
|
)
|
||||||
|
require.NoError(err)
|
||||||
|
cred2, err := awsutil.NewCredentialsConfig(
|
||||||
|
awsutil.WithRegion("us-east-1"),
|
||||||
|
awsutil.WithAccessKey("cred2"),
|
||||||
|
awsutil.WithSecretKey("secret2"),
|
||||||
|
)
|
||||||
|
require.NoError(err)
|
||||||
|
cred3, err := awsutil.NewCredentialsConfig(
|
||||||
|
awsutil.WithRegion("us-east-1"),
|
||||||
|
awsutil.WithAccessKey("cred3"),
|
||||||
|
awsutil.WithSecretKey("secret3"),
|
||||||
|
)
|
||||||
|
require.NoError(err)
|
||||||
|
|
||||||
|
p := New()
|
||||||
|
cred, err := credential.NewAwsCredentialPersistedState(
|
||||||
|
credential.WithCredentialsConfig(cred1),
|
||||||
|
)
|
||||||
|
require.NoError(err)
|
||||||
|
|
||||||
|
state, err := newAwsStoragePersistedState(withCredentials(cred))
|
||||||
|
require.NoError(err)
|
||||||
|
|
||||||
|
// Cache should be empty before first call
|
||||||
|
assert.Empty(p.clients.cache)
|
||||||
|
|
||||||
|
client, err := p.getClient(ctx, "id", state)
|
||||||
|
require.NoError(err)
|
||||||
|
require.NotNil(client)
|
||||||
|
assert.Equal(cred1.AccessKey, client.Credentials().AccessKeyID)
|
||||||
|
assert.Equal(cred1.SecretKey, client.Credentials().SecretAccessKey)
|
||||||
|
|
||||||
|
// Update state with new credentials
|
||||||
|
cred, err = credential.NewAwsCredentialPersistedState(
|
||||||
|
credential.WithCredentialsConfig(cred2),
|
||||||
|
)
|
||||||
|
require.NoError(err)
|
||||||
|
state, err = newAwsStoragePersistedState(withCredentials(cred))
|
||||||
|
require.NoError(err)
|
||||||
|
|
||||||
|
// Client is cached and creds valid so it should still grab initial client
|
||||||
|
client, err = p.getClient(ctx, "id", state)
|
||||||
|
require.NoError(err)
|
||||||
|
require.NotNil(client)
|
||||||
|
assert.Equal(cred1.AccessKey, client.Credentials().AccessKeyID)
|
||||||
|
assert.Equal(cred1.SecretKey, client.Credentials().SecretAccessKey)
|
||||||
|
|
||||||
|
// Force credentials to expire
|
||||||
|
c := client.(*s3Client)
|
||||||
|
c.creds.CanExpire = true
|
||||||
|
p.clients.Lock()
|
||||||
|
p.clients.cache["id"] = c
|
||||||
|
p.clients.Unlock()
|
||||||
|
|
||||||
|
// Getting client should now create new client on cred2 since the credentials have expired
|
||||||
|
client, err = p.getClient(ctx, "id", state)
|
||||||
|
require.NoError(err)
|
||||||
|
require.NotNil(client)
|
||||||
|
assert.Equal(cred2.AccessKey, client.Credentials().AccessKeyID)
|
||||||
|
assert.Equal(cred2.SecretKey, client.Credentials().SecretAccessKey)
|
||||||
|
|
||||||
|
// Update state with new credentials again
|
||||||
|
cred, err = credential.NewAwsCredentialPersistedState(
|
||||||
|
credential.WithCredentialsConfig(cred3),
|
||||||
|
)
|
||||||
|
require.NoError(err)
|
||||||
|
state, err = newAwsStoragePersistedState(withCredentials(cred))
|
||||||
|
require.NoError(err)
|
||||||
|
|
||||||
|
// Getting client and force cache refresh should return cred3 based client
|
||||||
|
client, err = p.getClient(ctx, "id", state, WithCacheRefresh(true))
|
||||||
|
require.NoError(err)
|
||||||
|
require.NotNil(client)
|
||||||
|
assert.Equal(cred3.AccessKey, client.Credentials().AccessKeyID)
|
||||||
|
assert.Equal(cred3.SecretKey, client.Credentials().SecretAccessKey)
|
||||||
|
}
|
||||||
@@ -27,6 +27,16 @@ type S3API interface {
|
|||||||
PutObject(ctx context.Context, params *s3.PutObjectInput, optFns ...func(*s3.Options)) (*s3.PutObjectOutput, error)
|
PutObject(ctx context.Context, params *s3.PutObjectInput, optFns ...func(*s3.Options)) (*s3.PutObjectOutput, error)
|
||||||
DeleteObjects(ctx context.Context, params *s3.DeleteObjectsInput, optFns ...func(*s3.Options)) (*s3.DeleteObjectsOutput, error)
|
DeleteObjects(ctx context.Context, params *s3.DeleteObjectsInput, optFns ...func(*s3.Options)) (*s3.DeleteObjectsOutput, error)
|
||||||
ListObjectsV2(ctx context.Context, params *s3.ListObjectsV2Input, optFns ...func(*s3.Options)) (*s3.ListObjectsV2Output, error)
|
ListObjectsV2(ctx context.Context, params *s3.ListObjectsV2Input, optFns ...func(*s3.Options)) (*s3.ListObjectsV2Output, error)
|
||||||
|
Credentials() aws.Credentials
|
||||||
|
}
|
||||||
|
|
||||||
|
type s3Client struct {
|
||||||
|
*s3.Client
|
||||||
|
creds aws.Credentials
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *s3Client) Credentials() aws.Credentials {
|
||||||
|
return c.creds
|
||||||
}
|
}
|
||||||
|
|
||||||
var customClient = &http.Client{
|
var customClient = &http.Client{
|
||||||
@@ -91,9 +101,9 @@ func (s *awsStoragePersistedState) toProto() (*storagebuckets.StorageBucketPersi
|
|||||||
return &storagebuckets.StorageBucketPersisted{Data: data}, nil
|
return &storagebuckets.StorageBucketPersisted{Data: data}, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// S3Client returns a configured S3 client based on the session
|
// s3Client returns a configured S3 client based on the session
|
||||||
// information stored in the state.
|
// information stored in the state.
|
||||||
func (s *awsStoragePersistedState) S3Client(ctx context.Context, opt ...s3Option) (S3API, error) {
|
func (s *awsStoragePersistedState) s3Client(ctx context.Context, opt ...s3Option) (S3API, error) {
|
||||||
opts, err := getOpts(opt...)
|
opts, err := getOpts(opt...)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, fmt.Errorf("error parsing options when fetching S3 client: %w", err)
|
return nil, fmt.Errorf("error parsing options when fetching S3 client: %w", err)
|
||||||
@@ -114,7 +124,16 @@ func (s *awsStoragePersistedState) S3Client(ctx context.Context, opt ...s3Option
|
|||||||
if s.testS3APIFunc != nil {
|
if s.testS3APIFunc != nil {
|
||||||
return s.testS3APIFunc(*awsCfg)
|
return s.testS3APIFunc(*awsCfg)
|
||||||
}
|
}
|
||||||
return s3.NewFromConfig(*awsCfg, s3Opts...), nil
|
|
||||||
|
// Retrieve the credentials provider from the client
|
||||||
|
credsProvider := awsCfg.Credentials
|
||||||
|
creds, err := credsProvider.Retrieve(ctx)
|
||||||
|
if err != nil {
|
||||||
|
return nil, fmt.Errorf("failed to retrieve credentials: %v\n", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
c := s3.NewFromConfig(*awsCfg, s3Opts...)
|
||||||
|
return &s3Client{Client: c, creds: creds}, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// getOpts iterates the inbound s3Options and returns a struct
|
// getOpts iterates the inbound s3Options and returns a struct
|
||||||
@@ -136,13 +155,22 @@ type s3Option func(*s3Options) error
|
|||||||
|
|
||||||
// options = how options are represented
|
// options = how options are represented
|
||||||
type s3Options struct {
|
type s3Options struct {
|
||||||
withEndpoint string
|
withEndpoint string
|
||||||
|
withCacheRefresh bool
|
||||||
}
|
}
|
||||||
|
|
||||||
func getDefaultS3Options() s3Options {
|
func getDefaultS3Options() s3Options {
|
||||||
return s3Options{}
|
return s3Options{}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// WithCacheRefresh controls if the cache should be forced to refresh
|
||||||
|
func WithCacheRefresh(refresh bool) s3Option {
|
||||||
|
return func(o *s3Options) error {
|
||||||
|
o.withCacheRefresh = refresh
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// WithEndpoint contains the endpoint to use
|
// WithEndpoint contains the endpoint to use
|
||||||
func WithEndpoint(with string) s3Option {
|
func WithEndpoint(with string) s3Option {
|
||||||
return func(o *s3Options) error {
|
return func(o *s3Options) error {
|
||||||
|
|||||||
@@ -64,7 +64,7 @@ func TestAwsStoragePersistedStateS3Client(t *testing.T) {
|
|||||||
)
|
)
|
||||||
require.NoError(err)
|
require.NoError(err)
|
||||||
|
|
||||||
clientRaw, err := state.S3Client(context.Background(), tc.s3Opts...)
|
clientRaw, err := state.s3Client(context.Background(), tc.s3Opts...)
|
||||||
require.NoError(err)
|
require.NoError(err)
|
||||||
require.NotNil(clientRaw)
|
require.NotNil(clientRaw)
|
||||||
client, ok := clientRaw.(*testMockS3)
|
client, ok := clientRaw.(*testMockS3)
|
||||||
|
|||||||
+112
-7
@@ -17,12 +17,16 @@ import (
|
|||||||
"github.com/hashicorp/boundary-plugin-aws/plugin/service/storage"
|
"github.com/hashicorp/boundary-plugin-aws/plugin/service/storage"
|
||||||
"github.com/hashicorp/boundary/sdk/pbs/controller/api/resources/storagebuckets"
|
"github.com/hashicorp/boundary/sdk/pbs/controller/api/resources/storagebuckets"
|
||||||
pb "github.com/hashicorp/boundary/sdk/pbs/plugin"
|
pb "github.com/hashicorp/boundary/sdk/pbs/plugin"
|
||||||
|
"github.com/stretchr/testify/assert"
|
||||||
"github.com/stretchr/testify/require"
|
"github.com/stretchr/testify/require"
|
||||||
"google.golang.org/grpc"
|
"google.golang.org/grpc"
|
||||||
"google.golang.org/grpc/credentials/insecure"
|
"google.golang.org/grpc/credentials/insecure"
|
||||||
|
"google.golang.org/grpc/status"
|
||||||
"google.golang.org/protobuf/types/known/structpb"
|
"google.golang.org/protobuf/types/known/structpb"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
// NOTE: this test does not vaildate against dynamic credentials because
|
||||||
|
// this credential type can only be tested on an EC2 instance.
|
||||||
func TestStoragePlugin(t *testing.T) {
|
func TestStoragePlugin(t *testing.T) {
|
||||||
region := os.Getenv("AWS_REGION")
|
region := os.Getenv("AWS_REGION")
|
||||||
if region == "" {
|
if region == "" {
|
||||||
@@ -75,6 +79,12 @@ func TestStoragePlugin(t *testing.T) {
|
|||||||
iamSecretAccessKeyMissingPutObject, err := tf.GetOutputString("iam_secret_access_key_missing_put_obj")
|
iamSecretAccessKeyMissingPutObject, err := tf.GetOutputString("iam_secret_access_key_missing_put_obj")
|
||||||
require.NoError(err)
|
require.NoError(err)
|
||||||
|
|
||||||
|
iamAccessKeyMissingDeleteObject, err := tf.GetOutputString("iam_access_key_missing_delete_obj")
|
||||||
|
require.NoError(err)
|
||||||
|
|
||||||
|
iamSecretAccessKeyMissingDeleteObject, err := tf.GetOutputString("iam_secret_access_key_missing_delete_obj")
|
||||||
|
require.NoError(err)
|
||||||
|
|
||||||
// Start the workflow now. Set up the storage bucket. Note that this
|
// Start the workflow now. Set up the storage bucket. Note that this
|
||||||
// will cause the state to go out of drift above in the sense that
|
// will cause the state to go out of drift above in the sense that
|
||||||
// the access key ID/secret access key will no longer be valid. We
|
// the access key ID/secret access key will no longer be valid. We
|
||||||
@@ -121,7 +131,7 @@ func TestStoragePlugin(t *testing.T) {
|
|||||||
testPluginOnDeleteStorageBucket(ctx, t, p, bucketName, region, keyid, secret, true)
|
testPluginOnDeleteStorageBucket(ctx, t, p, bucketName, region, keyid, secret, true)
|
||||||
|
|
||||||
// ********************
|
// ********************
|
||||||
// * Object Methods: PutObject, GetObject, HeadObject
|
// * Object Methods: PutObject, GetObject, HeadObject, DeleteObjects
|
||||||
// ********************
|
// ********************
|
||||||
//
|
//
|
||||||
// Reassign the keyid and secret first.
|
// Reassign the keyid and secret first.
|
||||||
@@ -132,11 +142,59 @@ func TestStoragePlugin(t *testing.T) {
|
|||||||
// * Validate Permissions
|
// * Validate Permissions
|
||||||
// ********************
|
// ********************
|
||||||
//
|
//
|
||||||
|
var expectedSBCState *pb.StorageBucketCredentialState
|
||||||
|
|
||||||
// Validate error is returned for missing get object permission
|
// Validate error is returned for missing get object permission
|
||||||
testPluginValidatePermissions(ctx, t, p, bucketName, region, iamAccessKeyMissingGetObject, iamSecretAccessKeyMissingGetObject)
|
expectedSBCState = &pb.StorageBucketCredentialState{
|
||||||
|
State: &pb.Permissions{
|
||||||
|
Write: &pb.Permission{
|
||||||
|
State: pb.StateType_STATE_TYPE_OK,
|
||||||
|
},
|
||||||
|
Read: &pb.Permission{
|
||||||
|
State: pb.StateType_STATE_TYPE_ERROR,
|
||||||
|
},
|
||||||
|
Delete: &pb.Permission{
|
||||||
|
State: pb.StateType_STATE_TYPE_OK,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
}
|
||||||
|
testPluginValidatePermissions(ctx, t, p, bucketName, region, iamAccessKeyMissingGetObject, iamSecretAccessKeyMissingGetObject, expectedSBCState)
|
||||||
|
|
||||||
// Validate error is returned for missing put object permission
|
// Validate error is returned for missing put object permission
|
||||||
testPluginValidatePermissions(ctx, t, p, bucketName, region, iamAccessKeyMissingPutObject, iamSecretAccessKeyMissingPutObject)
|
expectedSBCState = &pb.StorageBucketCredentialState{
|
||||||
|
State: &pb.Permissions{
|
||||||
|
Write: &pb.Permission{
|
||||||
|
State: pb.StateType_STATE_TYPE_ERROR,
|
||||||
|
},
|
||||||
|
Read: &pb.Permission{
|
||||||
|
// Since we cannot write an object to S3,
|
||||||
|
// we cannot test against the ability to read from S3.
|
||||||
|
State: pb.StateType_STATE_TYPE_UNKNOWN,
|
||||||
|
},
|
||||||
|
Delete: &pb.Permission{
|
||||||
|
// We can still try deleting an object that does
|
||||||
|
// not exist in the S3 bucket to validate access.
|
||||||
|
State: pb.StateType_STATE_TYPE_OK,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
}
|
||||||
|
testPluginValidatePermissions(ctx, t, p, bucketName, region, iamAccessKeyMissingPutObject, iamSecretAccessKeyMissingPutObject, expectedSBCState)
|
||||||
|
|
||||||
|
// Validate error is returned for missing delete object permission
|
||||||
|
expectedSBCState = &pb.StorageBucketCredentialState{
|
||||||
|
State: &pb.Permissions{
|
||||||
|
Write: &pb.Permission{
|
||||||
|
State: pb.StateType_STATE_TYPE_OK,
|
||||||
|
},
|
||||||
|
Read: &pb.Permission{
|
||||||
|
State: pb.StateType_STATE_TYPE_OK,
|
||||||
|
},
|
||||||
|
Delete: &pb.Permission{
|
||||||
|
State: pb.StateType_STATE_TYPE_ERROR,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
}
|
||||||
|
testPluginValidatePermissions(ctx, t, p, bucketName, region, iamAccessKeyMissingDeleteObject, iamSecretAccessKeyMissingDeleteObject, expectedSBCState)
|
||||||
}
|
}
|
||||||
|
|
||||||
func testPluginOnCreateStorageBucket(ctx context.Context, t *testing.T, p *storage.StoragePlugin, bucketName, region, accessKeyId, secretAccessKey string, rotate bool) (string, string) {
|
func testPluginOnCreateStorageBucket(ctx context.Context, t *testing.T, p *storage.StoragePlugin, bucketName, region, accessKeyId, secretAccessKey string, rotate bool) (string, string) {
|
||||||
@@ -289,10 +347,12 @@ func testPluginOnDeleteStorageBucket(ctx context.Context, t *testing.T, p *stora
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func testPluginValidatePermissions(ctx context.Context, t *testing.T, p *storage.StoragePlugin, bucketName, region, accessKeyId, secretAccessKey string) {
|
func testPluginValidatePermissions(ctx context.Context, t *testing.T, p *storage.StoragePlugin, bucketName, region, accessKeyId, secretAccessKey string, expectedState *pb.StorageBucketCredentialState) {
|
||||||
t.Helper()
|
t.Helper()
|
||||||
t.Logf("testing ValidatePermissions")
|
t.Logf("testing ValidatePermissions")
|
||||||
require := require.New(t)
|
require, assert := require.New(t), assert.New(t)
|
||||||
|
|
||||||
|
require.NotNil(expectedState)
|
||||||
|
|
||||||
reqAttrs, err := structpb.NewStruct(map[string]any{
|
reqAttrs, err := structpb.NewStruct(map[string]any{
|
||||||
credential.ConstRegion: region,
|
credential.ConstRegion: region,
|
||||||
@@ -315,6 +375,51 @@ func testPluginValidatePermissions(ctx context.Context, t *testing.T, p *storage
|
|||||||
|
|
||||||
_, err = p.ValidatePermissions(ctx, req)
|
_, err = p.ValidatePermissions(ctx, req)
|
||||||
require.Error(err)
|
require.Error(err)
|
||||||
|
|
||||||
|
var actualSBCState *pb.StorageBucketCredentialState
|
||||||
|
if st, ok := status.FromError(err); ok {
|
||||||
|
for _, detail := range st.Details() {
|
||||||
|
if statusDetail, ok := detail.(*pb.StorageBucketCredentialState); ok {
|
||||||
|
actualSBCState = statusDetail
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
require.NotNil(actualSBCState)
|
||||||
|
require.NotNil(actualSBCState.GetState())
|
||||||
|
|
||||||
|
expectedReadState := expectedState.GetState().GetRead()
|
||||||
|
if expectedReadState != nil {
|
||||||
|
actualReadState := actualSBCState.GetState().GetRead()
|
||||||
|
require.NotNil(actualReadState)
|
||||||
|
assert.Equal(expectedReadState.GetState(), actualReadState.GetState())
|
||||||
|
assert.NotEmpty(actualReadState.GetCheckedAt())
|
||||||
|
if expectedReadState.GetState() == pb.StateType_STATE_TYPE_ERROR {
|
||||||
|
assert.NotEmpty(actualReadState.GetErrorDetails())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
expectedWriteState := expectedState.GetState().GetWrite()
|
||||||
|
if expectedWriteState != nil {
|
||||||
|
actualWriteState := actualSBCState.GetState().GetWrite()
|
||||||
|
require.NotNil(actualWriteState)
|
||||||
|
assert.Equal(expectedWriteState.GetState(), actualWriteState.GetState())
|
||||||
|
assert.NotEmpty(actualWriteState.GetCheckedAt())
|
||||||
|
if expectedWriteState.GetState() == pb.StateType_STATE_TYPE_ERROR {
|
||||||
|
assert.NotEmpty(actualWriteState.GetErrorDetails())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
expectedDeleteState := expectedState.GetState().GetDelete()
|
||||||
|
if expectedDeleteState != nil {
|
||||||
|
actualDeleteState := actualSBCState.GetState().GetDelete()
|
||||||
|
require.NotNil(actualDeleteState)
|
||||||
|
assert.Equal(expectedDeleteState.GetState(), actualDeleteState.GetState())
|
||||||
|
assert.NotEmpty(actualDeleteState.GetCheckedAt())
|
||||||
|
if expectedDeleteState.GetState() == pb.StateType_STATE_TYPE_ERROR {
|
||||||
|
assert.NotEmpty(actualDeleteState.GetErrorDetails())
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func testPluginObjectMethods(ctx context.Context, t *testing.T, p *storage.StoragePlugin, bucketName, region, accessKeyId, secretAccessKey string) {
|
func testPluginObjectMethods(ctx context.Context, t *testing.T, p *storage.StoragePlugin, bucketName, region, accessKeyId, secretAccessKey string) {
|
||||||
@@ -369,7 +474,7 @@ func testPluginObjectMethods(ctx context.Context, t *testing.T, p *storage.Stora
|
|||||||
|
|
||||||
// Need to create a GRPC server to test GetObject
|
// Need to create a GRPC server to test GetObject
|
||||||
t.Logf("testing GetObject")
|
t.Logf("testing GetObject")
|
||||||
lis, err := net.Listen("tcp", "localhost:2030")
|
lis, err := net.Listen("tcp", "[::1]:2030")
|
||||||
require.NoError(err)
|
require.NoError(err)
|
||||||
grpcServer := grpc.NewServer()
|
grpcServer := grpc.NewServer()
|
||||||
pb.RegisterStoragePluginServiceServer(grpcServer, &storage.StoragePlugin{})
|
pb.RegisterStoragePluginServiceServer(grpcServer, &storage.StoragePlugin{})
|
||||||
@@ -377,7 +482,7 @@ func testPluginObjectMethods(ctx context.Context, t *testing.T, p *storage.Stora
|
|||||||
grpcServer.Serve(lis)
|
grpcServer.Serve(lis)
|
||||||
}()
|
}()
|
||||||
|
|
||||||
conn, err := grpc.Dial("localhost:2030", grpc.WithTransportCredentials(insecure.NewCredentials()))
|
conn, err := grpc.Dial("[::1]:2030", grpc.WithTransportCredentials(insecure.NewCredentials()))
|
||||||
require.NoError(err)
|
require.NoError(err)
|
||||||
defer conn.Close()
|
defer conn.Close()
|
||||||
client := pb.NewStoragePluginServiceClient(conn)
|
client := pb.NewStoragePluginServiceClient(conn)
|
||||||
|
|||||||
Vendored
+97
-12
@@ -32,7 +32,9 @@ resource "aws_iam_policy" "valid" {
|
|||||||
"Action": [
|
"Action": [
|
||||||
"s3:PutObject",
|
"s3:PutObject",
|
||||||
"s3:GetObject",
|
"s3:GetObject",
|
||||||
"s3:GetObjectAttributes"
|
"s3:DeleteObject",
|
||||||
|
"s3:GetObjectAttributes",
|
||||||
|
"s3:ListBucket"
|
||||||
],
|
],
|
||||||
"Effect": "Allow",
|
"Effect": "Allow",
|
||||||
"Resource": "${aws_s3_bucket.test.arn}*"
|
"Resource": "${aws_s3_bucket.test.arn}*"
|
||||||
@@ -79,7 +81,9 @@ resource "aws_iam_policy" "missing_put_obj" {
|
|||||||
{
|
{
|
||||||
"Action": [
|
"Action": [
|
||||||
"s3:GetObject",
|
"s3:GetObject",
|
||||||
"s3:GetObjectAttributes"
|
"s3:GetObjectAttributes",
|
||||||
|
"s3:ListBucket",
|
||||||
|
"s3:DeleteObject"
|
||||||
],
|
],
|
||||||
"Effect": "Allow",
|
"Effect": "Allow",
|
||||||
"Resource": "${aws_s3_bucket.test.arn}*"
|
"Resource": "${aws_s3_bucket.test.arn}*"
|
||||||
@@ -125,7 +129,7 @@ resource "aws_iam_policy" "missing_get_obj" {
|
|||||||
{
|
{
|
||||||
"Action": [
|
"Action": [
|
||||||
"s3:PutObject",
|
"s3:PutObject",
|
||||||
"s3:GetObjectAttributes"
|
"s3:DeleteObject"
|
||||||
],
|
],
|
||||||
"Effect": "Allow",
|
"Effect": "Allow",
|
||||||
"Resource": "${aws_s3_bucket.test.arn}*"
|
"Resource": "${aws_s3_bucket.test.arn}*"
|
||||||
@@ -141,18 +145,69 @@ resource "aws_iam_role_policy_attachment" "missing_get_obj" {
|
|||||||
policy_arn = aws_iam_policy.missing_get_obj.arn
|
policy_arn = aws_iam_policy.missing_get_obj.arn
|
||||||
}
|
}
|
||||||
|
|
||||||
|
resource "aws_iam_role" "missing_delete_obj" {
|
||||||
|
name = "${random_id.prefix.dec}-missing-delete-obj"
|
||||||
|
tags = local.tags
|
||||||
|
|
||||||
|
assume_role_policy = jsonencode({
|
||||||
|
Version = "2012-10-17"
|
||||||
|
Statement = [
|
||||||
|
{
|
||||||
|
Action = "sts:AssumeRole"
|
||||||
|
Effect = "Allow"
|
||||||
|
Sid = ""
|
||||||
|
Principal = {
|
||||||
|
AWS: data.aws_caller_identity.current.user_id
|
||||||
|
}
|
||||||
|
},
|
||||||
|
]
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
resource "aws_iam_policy" "missing_delete_obj" {
|
||||||
|
name = "${random_id.prefix.dec}-missing-delete-obj"
|
||||||
|
tags = local.tags
|
||||||
|
|
||||||
|
policy = <<EOT
|
||||||
|
{
|
||||||
|
"Version": "2012-10-17",
|
||||||
|
"Statement": [
|
||||||
|
{
|
||||||
|
"Action": [
|
||||||
|
"s3:PutObject",
|
||||||
|
"s3:GetObject",
|
||||||
|
"s3:GetObjectAttributes",
|
||||||
|
"s3:ListBucket"
|
||||||
|
],
|
||||||
|
"Effect": "Allow",
|
||||||
|
"Resource": "${aws_s3_bucket.test.arn}*"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
|
||||||
|
}
|
||||||
|
EOT
|
||||||
|
}
|
||||||
|
|
||||||
|
resource "aws_iam_role_policy_attachment" "missing_delete_obj" {
|
||||||
|
role = aws_iam_role.missing_delete_obj.name
|
||||||
|
policy_arn = aws_iam_policy.missing_delete_obj.arn
|
||||||
|
}
|
||||||
|
|
||||||
# Static Credential Testing
|
# Static Credential Testing
|
||||||
resource "random_id" "user_name" {
|
resource "random_id" "user_name" {
|
||||||
count = var.iam_user_count
|
count = var.iam_user_count
|
||||||
prefix = random_id.prefix.dec
|
prefix = "demo-${local.hashicorp_email}-boundary-iam-user"
|
||||||
byte_length = 4
|
byte_length = 4
|
||||||
}
|
}
|
||||||
|
|
||||||
resource "aws_iam_user" "test" {
|
resource "aws_iam_user" "test" {
|
||||||
count = var.iam_user_count
|
count = var.iam_user_count
|
||||||
name = random_id.user_name[count.index].dec
|
name = random_id.user_name[count.index].dec
|
||||||
force_destroy = true
|
force_destroy = true
|
||||||
tags = local.tags
|
permissions_boundary = "arn:aws:iam::${data.aws_caller_identity.current.account_id}:policy/DemoUser"
|
||||||
|
tags = {
|
||||||
|
"boundary-demo" = local.hashicorp_email
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
resource "aws_iam_access_key" "test" {
|
resource "aws_iam_access_key" "test" {
|
||||||
@@ -195,17 +250,44 @@ resource "aws_iam_user_policy_attachment" "test_credentials" {
|
|||||||
policy_arn = aws_iam_policy.credentials[count.index].arn
|
policy_arn = aws_iam_policy.credentials[count.index].arn
|
||||||
}
|
}
|
||||||
|
|
||||||
|
# Edge case for missing DeleteObject permission
|
||||||
|
resource "random_id" "missing_delete_obj" {
|
||||||
|
prefix = "demo-${local.hashicorp_email}-boundary-iam-user"
|
||||||
|
byte_length = 4
|
||||||
|
}
|
||||||
|
|
||||||
|
resource "aws_iam_user" "missing_delete_obj" {
|
||||||
|
name = random_id.missing_delete_obj.dec
|
||||||
|
force_destroy = true
|
||||||
|
permissions_boundary = "arn:aws:iam::${data.aws_caller_identity.current.account_id}:policy/DemoUser"
|
||||||
|
tags = {
|
||||||
|
"boundary-demo" = local.hashicorp_email
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
resource "aws_iam_access_key" "missing_delete_obj" {
|
||||||
|
user = aws_iam_user.missing_delete_obj.name
|
||||||
|
}
|
||||||
|
|
||||||
|
resource "aws_iam_user_policy_attachment" "missing_delete_obj" {
|
||||||
|
user = aws_iam_user.missing_delete_obj.name
|
||||||
|
policy_arn = aws_iam_policy.missing_delete_obj.arn
|
||||||
|
}
|
||||||
|
|
||||||
# Edge case for missing GetObject permission
|
# Edge case for missing GetObject permission
|
||||||
|
|
||||||
resource "random_id" "missing_get_obj" {
|
resource "random_id" "missing_get_obj" {
|
||||||
prefix = random_id.prefix.dec
|
prefix = "demo-${local.hashicorp_email}-boundary-iam-user"
|
||||||
byte_length = 4
|
byte_length = 4
|
||||||
}
|
}
|
||||||
|
|
||||||
resource "aws_iam_user" "missing_get_obj" {
|
resource "aws_iam_user" "missing_get_obj" {
|
||||||
name = random_id.missing_get_obj.dec
|
name = random_id.missing_get_obj.dec
|
||||||
force_destroy = true
|
force_destroy = true
|
||||||
tags = local.tags
|
permissions_boundary = "arn:aws:iam::${data.aws_caller_identity.current.account_id}:policy/DemoUser"
|
||||||
|
tags = {
|
||||||
|
"boundary-demo" = local.hashicorp_email
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
resource "aws_iam_access_key" "missing_get_obj" {
|
resource "aws_iam_access_key" "missing_get_obj" {
|
||||||
@@ -220,14 +302,17 @@ resource "aws_iam_user_policy_attachment" "missing_get_obj" {
|
|||||||
# Edge case for missing PutObject permission
|
# Edge case for missing PutObject permission
|
||||||
|
|
||||||
resource "random_id" "missing_put_obj" {
|
resource "random_id" "missing_put_obj" {
|
||||||
prefix = random_id.prefix.dec
|
prefix = "demo-${local.hashicorp_email}-boundary-iam-user"
|
||||||
byte_length = 4
|
byte_length = 4
|
||||||
}
|
}
|
||||||
|
|
||||||
resource "aws_iam_user" "missing_put_obj" {
|
resource "aws_iam_user" "missing_put_obj" {
|
||||||
name = random_id.missing_put_obj.dec
|
name = random_id.missing_put_obj.dec
|
||||||
force_destroy = true
|
force_destroy = true
|
||||||
tags = local.tags
|
permissions_boundary = "arn:aws:iam::${data.aws_caller_identity.current.account_id}:policy/DemoUser"
|
||||||
|
tags = {
|
||||||
|
"boundary-demo" = local.hashicorp_email
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
resource "aws_iam_access_key" "missing_put_obj" {
|
resource "aws_iam_access_key" "missing_put_obj" {
|
||||||
|
|||||||
Vendored
+15
-1
@@ -13,10 +13,14 @@ output "missing_put_obj_policy_arn" {
|
|||||||
value = aws_iam_role.missing_put_obj.arn
|
value = aws_iam_role.missing_put_obj.arn
|
||||||
}
|
}
|
||||||
|
|
||||||
output "missing_put_get_policy_arn" {
|
output "missing_get_obj_policy_arn" {
|
||||||
value = aws_iam_role.missing_get_obj.arn
|
value = aws_iam_role.missing_get_obj.arn
|
||||||
}
|
}
|
||||||
|
|
||||||
|
output "missing_delete_obj_policy_arn" {
|
||||||
|
value = aws_iam_role.missing_delete_obj.arn
|
||||||
|
}
|
||||||
|
|
||||||
output "iam_user_names" {
|
output "iam_user_names" {
|
||||||
value = aws_iam_user.test.*.name
|
value = aws_iam_user.test.*.name
|
||||||
}
|
}
|
||||||
@@ -54,3 +58,13 @@ output "iam_secret_access_key_missing_get_obj" {
|
|||||||
value = aws_iam_access_key.missing_get_obj.secret
|
value = aws_iam_access_key.missing_get_obj.secret
|
||||||
sensitive = true
|
sensitive = true
|
||||||
}
|
}
|
||||||
|
|
||||||
|
output "iam_access_key_missing_delete_obj" {
|
||||||
|
value = aws_iam_access_key.missing_delete_obj.id
|
||||||
|
sensitive = true
|
||||||
|
}
|
||||||
|
|
||||||
|
output "iam_secret_access_key_missing_delete_obj" {
|
||||||
|
value = aws_iam_access_key.missing_delete_obj.secret
|
||||||
|
sensitive = true
|
||||||
|
}
|
||||||
Vendored
+9
-1
@@ -4,6 +4,14 @@
|
|||||||
resource "aws_s3_bucket" "test" {
|
resource "aws_s3_bucket" "test" {
|
||||||
bucket = random_id.prefix.dec
|
bucket = random_id.prefix.dec
|
||||||
force_destroy = true
|
force_destroy = true
|
||||||
acl = "private"
|
|
||||||
tags = local.tags
|
tags = local.tags
|
||||||
}
|
}
|
||||||
|
|
||||||
|
resource "aws_s3_bucket_public_access_block" "secure_access" {
|
||||||
|
bucket = aws_s3_bucket.test.id
|
||||||
|
|
||||||
|
block_public_acls = true
|
||||||
|
block_public_policy = true
|
||||||
|
ignore_public_acls = true
|
||||||
|
restrict_public_buckets = true
|
||||||
|
}
|
||||||
Vendored
+5
-3
@@ -4,7 +4,7 @@
|
|||||||
data "aws_caller_identity" "current" {}
|
data "aws_caller_identity" "current" {}
|
||||||
|
|
||||||
resource "random_id" "prefix" {
|
resource "random_id" "prefix" {
|
||||||
prefix = "storage-e2e-test"
|
prefix = "aws-plugin-e2e-test"
|
||||||
byte_length = 4
|
byte_length = 4
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -13,8 +13,10 @@ variable "iam_user_count" {
|
|||||||
}
|
}
|
||||||
|
|
||||||
locals {
|
locals {
|
||||||
|
hashicorp_email = split(":", data.aws_caller_identity.current.user_id)[1]
|
||||||
tags = {
|
tags = {
|
||||||
user_id = split(":", data.aws_caller_identity.current.user_id)[1]
|
User = split(":", data.aws_caller_identity.current.user_id)[1]
|
||||||
repo = "boundary-plugin-aws"
|
Repo = "boundary-plugin-aws"
|
||||||
|
Environment = "Demo"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
Reference in new issue
Block a user