Skip to content

Commit cde400a

Browse files
committed
add smithy error parser
Signed-off-by: Pankaj Walke <[email protected]>
1 parent ce73264 commit cde400a

File tree

3 files changed

+104
-92
lines changed

3 files changed

+104
-92
lines changed

pkg/cloud/awserrors/errors.go

+53
Original file line numberDiff line numberDiff line change
@@ -18,10 +18,13 @@ limitations under the License.
1818
package awserrors
1919

2020
import (
21+
"errors"
2122
"net/http"
2223

2324
"github.com/aws/aws-sdk-go/aws/awserr"
2425
"github.com/aws/aws-sdk-go/service/ssm"
26+
"github.com/aws/smithy-go"
27+
smithyhttp "github.com/aws/smithy-go/transport/http"
2528
)
2629

2730
// Error singletons for AWS errors.
@@ -84,6 +87,13 @@ type EC2Error struct {
8487
Code int
8588
}
8689

90+
// SmithyError holds parsed smithy errors from aws-sdk-go-v2 API calls
91+
type SmithyError struct {
92+
errCode string
93+
errMessage string
94+
statusCode int
95+
}
96+
8797
// Error implements the Error interface.
8898
func (e *EC2Error) Error() string {
8999
return e.msg
@@ -224,3 +234,46 @@ func IsPermissionNotFoundError(err error) bool {
224234
}
225235
return false
226236
}
237+
238+
// ParseSmithyError returns parsed SmithyError from AWS API calls.
239+
func ParseSmithyError(err error) *SmithyError {
240+
smithyErr := &SmithyError{}
241+
242+
if err == nil {
243+
return nil
244+
}
245+
246+
var ae smithy.APIError
247+
if errors.As(err, &ae) {
248+
smithyErr.errCode = ae.ErrorCode()
249+
smithyErr.errMessage = ae.Error()
250+
}
251+
252+
var re *smithyhttp.ResponseError
253+
if errors.As(err, &re) {
254+
if re.Response != nil {
255+
smithyErr.statusCode = re.Response.StatusCode
256+
}
257+
var innerAE smithy.APIError
258+
if re.Err != nil && errors.As(re.Err, &innerAE) {
259+
smithyErr.errCode = innerAE.ErrorCode()
260+
}
261+
}
262+
263+
return smithyErr
264+
}
265+
266+
// ErrorCode returns the error code from the SmithyError.
267+
func (s *SmithyError) ErrorCode() string {
268+
return s.errCode
269+
}
270+
271+
// ErrorMessage returns the error message from the SmithyError.
272+
func (s *SmithyError) ErrorMessage() string {
273+
return s.errMessage
274+
}
275+
276+
// StatusCode returns the status code from the SmithyError.
277+
func (s *SmithyError) StatusCode() int {
278+
return s.statusCode
279+
}

pkg/cloud/metricsv2/metrics.go

+6-33
Original file line numberDiff line numberDiff line change
@@ -19,19 +19,18 @@ package metricsv2
1919

2020
import (
2121
"context"
22-
"errors"
2322
"fmt"
2423
"strconv"
2524
"time"
2625

2726
awsmiddleware "github.com/aws/aws-sdk-go-v2/aws/middleware"
28-
"github.com/aws/smithy-go"
2927
"github.com/aws/smithy-go/middleware"
3028
smithyhttp "github.com/aws/smithy-go/transport/http"
3129
"github.com/prometheus/client_golang/prometheus"
3230
"k8s.io/apimachinery/pkg/runtime"
3331
"sigs.k8s.io/controller-runtime/pkg/metrics"
3432

33+
"sigs.k8s.io/cluster-api-provider-aws/v2/pkg/cloud/awserrors"
3534
"sigs.k8s.io/cluster-api-provider-aws/v2/pkg/record"
3635
"sigs.k8s.io/cluster-api-provider-aws/v2/version"
3736
)
@@ -165,7 +164,9 @@ func getAttemptContextMiddleware() middleware.FinalizeMiddleware {
165164
}
166165

167166
if err != nil {
168-
request.ErrorCode, _, request.StatusCode = parseSmithyError(err)
167+
smithyErr := awserrors.ParseSmithyError(err)
168+
request.ErrorCode = smithyErr.ErrorCode()
169+
request.StatusCode = smithyErr.StatusCode()
169170
}
170171
}
171172

@@ -180,7 +181,8 @@ func getRecordAWSPermissionsIssueMiddleware(target runtime.Object) middleware.Fi
180181
request := getContext(ctx)
181182
if request != nil {
182183
var errMessage string
183-
request.ErrorCode, errMessage, _ = parseSmithyError(err)
184+
smithyErr := awserrors.ParseSmithyError(err)
185+
request.ErrorCode = smithyErr.ErrorCode()
184186
switch request.ErrorCode {
185187
case "AccessDenied", "AuthFailure", "UnauthorizedOperation", "NoCredentialProviders",
186188
"ExpiredToken", "InvalidClientTokenId", "SignatureDoesNotMatch", "ValidationError":
@@ -215,35 +217,6 @@ func getContext(ctx context.Context) *RequestData {
215217
return rctx.(*RequestData)
216218
}
217219

218-
// parseSmithyError parse Smithy Error and returns errorCode, errorMessage and statusCode.
219-
func parseSmithyError(err error) (string, string, int) {
220-
var errCode, errMessage string
221-
var statusCode int
222-
223-
if err == nil {
224-
return errCode, errMessage, statusCode
225-
}
226-
227-
var ae smithy.APIError
228-
if errors.As(err, &ae) {
229-
errCode = ae.ErrorCode()
230-
errMessage = ae.Error()
231-
}
232-
233-
var re *smithyhttp.ResponseError
234-
if errors.As(err, &re) {
235-
if re.Response != nil {
236-
statusCode = re.Response.StatusCode
237-
}
238-
var innerAE smithy.APIError
239-
if re.Err != nil && errors.As(re.Err, &innerAE) {
240-
errCode = innerAE.ErrorCode()
241-
}
242-
}
243-
244-
return errCode, errMessage, statusCode
245-
}
246-
247220
// CaptureRequestMetrics will monitor and capture request metrics.
248221
func (r *RequestData) CaptureRequestMetrics() {
249222
if r.Service == "" || r.Region == "" || r.OperationName == "" || r.Controller == "" {

pkg/cloud/services/s3/s3.go

+45-59
Original file line numberDiff line numberDiff line change
@@ -31,12 +31,12 @@ import (
3131
"github.com/aws/aws-sdk-go/aws"
3232
"github.com/aws/aws-sdk-go/service/sts"
3333
"github.com/aws/aws-sdk-go/service/sts/stsiface"
34-
"github.com/aws/smithy-go"
3534
"github.com/pkg/errors"
3635
"k8s.io/utils/ptr"
3736

3837
infrav1 "sigs.k8s.io/cluster-api-provider-aws/v2/api/v1beta2"
3938
iam "sigs.k8s.io/cluster-api-provider-aws/v2/iam/api/v1beta1"
39+
"sigs.k8s.io/cluster-api-provider-aws/v2/pkg/cloud/awserrors"
4040
"sigs.k8s.io/cluster-api-provider-aws/v2/pkg/cloud/scope"
4141
"sigs.k8s.io/cluster-api-provider-aws/v2/util/system"
4242
)
@@ -122,21 +122,15 @@ func (s *Service) DeleteBucket(ctx context.Context) error {
122122
Bucket: aws.String(bucketName),
123123
})
124124
if err != nil {
125-
var aerr smithy.APIError
126-
if errors.As(err, &aerr) {
127-
switch aerr.ErrorCode() {
128-
case (&types.NoSuchBucket{}).ErrorCode():
129-
log.Info("Bucket already removed")
130-
case "BucketNotEmpty":
131-
log.Info("Bucket not empty, skipping removal")
132-
default:
133-
return errors.Wrap(aerr, "deleting S3 bucket")
134-
}
135-
136-
return nil
125+
smithyErr := awserrors.ParseSmithyError(err)
126+
switch smithyErr.ErrorCode() {
127+
case (&types.NoSuchBucket{}).ErrorCode():
128+
log.Info("Bucket already removed")
129+
case "BucketNotEmpty":
130+
log.Info("Bucket not empty, skipping removal")
131+
default:
132+
return errors.Wrap(err, "deleting S3 bucket")
137133
}
138-
139-
return errors.Wrap(err, "deleting S3 bucket")
140134
}
141135

142136
return nil
@@ -211,32 +205,30 @@ func (s *Service) Delete(ctx context.Context, m *scope.MachineScope) error {
211205
Key: aws.String(key),
212206
})
213207
if err != nil {
214-
var aerr smithy.APIError
215-
if errors.As(err, &aerr) {
216-
switch aerr.ErrorCode() {
217-
case "Forbidden":
218-
// In the case that the IAM policy does not have sufficient
219-
// permissions to get the object, we will attempt to delete it
220-
// anyway for backwards compatibility reasons.
221-
s.scope.Debug("Received 403 forbidden from S3 HeadObject call. If GetObject permission has been granted to the controller but not ListBucket, object is already deleted. Attempting deletion anyway in case GetObject permission hasn't been granted to the controller but DeleteObject has.", "bucket", bucket, "key", key)
222-
223-
if err := s.deleteObject(ctx, bucket, key); err != nil {
224-
return err
225-
}
208+
smithyErr := awserrors.ParseSmithyError(err)
209+
switch smithyErr.ErrorCode() {
210+
case "Forbidden":
211+
// In the case that the IAM policy does not have sufficient
212+
// permissions to get the object, we will attempt to delete it
213+
// anyway for backwards compatibility reasons.
214+
s.scope.Debug("Received 403 forbidden from S3 HeadObject call. If GetObject permission has been granted to the controller but not ListBucket, object is already deleted. Attempting deletion anyway in case GetObject permission hasn't been granted to the controller but DeleteObject has.", "bucket", bucket, "key", key)
215+
216+
if err := s.deleteObject(ctx, bucket, key); err != nil {
217+
return err
218+
}
226219

227-
s.scope.Debug("Delete object call succeeded despite missing GetObject permission", "bucket", bucket, "key", key)
220+
s.scope.Debug("Delete object call succeeded despite missing GetObject permission", "bucket", bucket, "key", key)
228221

229-
return nil
230-
case "NotFound":
231-
s.scope.Debug("Either bucket or object does not exist", "bucket", bucket, "key", key)
232-
return nil
233-
case (&types.NoSuchKey{}).ErrorCode():
234-
s.scope.Debug("Object already deleted", "bucket", bucket, "key", key)
235-
return nil
236-
case (&types.NoSuchBucket{}).ErrorCode():
237-
s.scope.Debug("Bucket does not exist", "bucket", bucket)
238-
return nil
239-
}
222+
return nil
223+
case "NotFound":
224+
s.scope.Debug("Either bucket or object does not exist", "bucket", bucket, "key", key)
225+
return nil
226+
case (&types.NoSuchKey{}).ErrorCode():
227+
s.scope.Debug("Object already deleted", "bucket", bucket, "key", key)
228+
return nil
229+
case (&types.NoSuchBucket{}).ErrorCode():
230+
s.scope.Debug("Bucket does not exist", "bucket", bucket)
231+
return nil
240232
}
241233
return errors.Wrap(err, "deleting S3 object")
242234
}
@@ -252,13 +244,11 @@ func (s *Service) deleteObject(ctx context.Context, bucket, key string) error {
252244
Key: aws.String(key),
253245
}); err != nil {
254246
if ptr.Deref(s.scope.Bucket().BestEffortDeleteObjects, false) {
255-
var aerr smithy.APIError
256-
if errors.As(err, &aerr) {
257-
switch aerr.ErrorCode() {
258-
case "Forbidden", "AccessDenied":
259-
s.scope.Debug("Ignoring deletion error", "bucket", bucket, "key", key, "error", aerr.ErrorMessage())
260-
return nil
261-
}
247+
smithyErr := awserrors.ParseSmithyError(err)
248+
switch smithyErr.ErrorCode() {
249+
case "Forbidden", "AccessDenied":
250+
s.scope.Debug("Ignoring deletion error", "bucket", bucket, "key", key, "error", smithyErr.ErrorMessage())
251+
return nil
262252
}
263253
}
264254
return errors.Wrap(err, "deleting S3 object")
@@ -284,20 +274,16 @@ func (s *Service) createBucketIfNotExist(ctx context.Context, bucketName string)
284274
return nil
285275
}
286276

287-
var aerr smithy.APIError
288-
if errors.As(err, &aerr) {
289-
switch aerr.ErrorCode() {
290-
// If bucket already exists, all good.
291-
//
292-
// TODO: This will fail if bucket is shared with other cluster.
293-
case (&types.BucketAlreadyOwnedByYou{}).ErrorCode():
294-
return nil
295-
default:
296-
return errors.Wrap(aerr, "creating S3 bucket")
297-
}
277+
smithyErr := awserrors.ParseSmithyError(err)
278+
switch smithyErr.ErrorCode() {
279+
// If bucket already exists, all good.
280+
//
281+
// TODO: This will fail if bucket is shared with other cluster.
282+
case (&types.BucketAlreadyOwnedByYou{}).ErrorCode():
283+
return nil
284+
default:
285+
return errors.Wrap(err, "creating S3 bucket")
298286
}
299-
300-
return errors.Wrap(err, "creating S3 bucket")
301287
}
302288

303289
func (s *Service) ensureBucketPolicy(ctx context.Context, bucketName string) error {

0 commit comments

Comments
 (0)