diff --git a/pkg/gce-cloud-provider/compute/gce-compute.go b/pkg/gce-cloud-provider/compute/gce-compute.go index a9adc551c..5c20e6864 100644 --- a/pkg/gce-cloud-provider/compute/gce-compute.go +++ b/pkg/gce-cloud-provider/compute/gce-compute.go @@ -859,6 +859,10 @@ func (cloud *CloudProvider) WaitForAttach(ctx context.Context, project string, v } func wrapOpErr(name string, opErr *computev1.OperationErrorErrors) error { + if opErr == nil { + return nil + } + if opErr.Code == "UNSUPPORTED_OPERATION" { if diskType := pdDiskTypeUnsupportedRegex.FindStringSubmatch(opErr.Message); diskType != nil { return &UnsupportedDiskError{ @@ -866,7 +870,29 @@ func wrapOpErr(name string, opErr *computev1.OperationErrorErrors) error { } } } - return fmt.Errorf("operation %v failed (%v): %v", name, opErr.Code, opErr.Message) + grpcErrCode := codeForGCEOpError(*opErr) + return status.Errorf(grpcErrCode, "operation %v failed (%v): %v", name, opErr.Code, opErr.Message) +} + +// codeForGCEOpError return the grpc error code for the passed in +// gce operation error. +func codeForGCEOpError(err computev1.OperationErrorErrors) codes.Code { + userErrors := map[string]codes.Code{ + "RESOURCE_NOT_FOUND": codes.NotFound, + "RESOURCE_ALREADY_EXISTS": codes.AlreadyExists, + "RESOURCE_IN_USE_BY_ANOTHER_RESOURCE": codes.InvalidArgument, + "OPERATION_CANCELED_BY_USER": codes.Aborted, + "QUOTA_EXCEEDED": codes.ResourceExhausted, + "ZONE_RESOURCE_POOL_EXHAUSTED": codes.Unavailable, + "ZONE_RESOURCE_POOL_EXHAUSTED_WITH_DETAILS": codes.Unavailable, + "REGION_QUOTA_EXCEEDED": codes.ResourceExhausted, + "RATE_LIMIT_EXCEEDED": codes.ResourceExhausted, + "INVALID_USAGE": codes.InvalidArgument, + } + if code, ok := userErrors[err.Code]; ok { + return code + } + return codes.Internal } func opIsDone(op *computev1.Operation) (bool, error) { diff --git a/pkg/gce-cloud-provider/compute/gce-compute_test.go b/pkg/gce-cloud-provider/compute/gce-compute_test.go index 1a9c9f6ec..652b111a9 100644 --- a/pkg/gce-cloud-provider/compute/gce-compute_test.go +++ b/pkg/gce-cloud-provider/compute/gce-compute_test.go @@ -18,6 +18,7 @@ import ( "testing" computev1 "google.golang.org/api/compute/v1" + "google.golang.org/grpc/codes" "sigs.k8s.io/gcp-compute-persistent-disk-csi-driver/pkg/common" ) @@ -101,3 +102,75 @@ func TestValidateDiskParameters(t *testing.T) { } } } + +func TestCodeForGCEOpError(t *testing.T) { + testCases := []struct { + name string + inputErr computev1.OperationErrorErrors + expCode codes.Code + }{ + { + name: "RESOURCE_NOT_FOUND error", + inputErr: computev1.OperationErrorErrors{Code: "RESOURCE_NOT_FOUND"}, + expCode: codes.NotFound, + }, + { + name: "RESOURCE_ALREADY_EXISTS error", + inputErr: computev1.OperationErrorErrors{Code: "RESOURCE_ALREADY_EXISTS"}, + expCode: codes.AlreadyExists, + }, + { + name: "OPERATION_CANCELED_BY_USER error", + inputErr: computev1.OperationErrorErrors{Code: "OPERATION_CANCELED_BY_USER"}, + expCode: codes.Aborted, + }, + { + name: "QUOTA_EXCEEDED error", + inputErr: computev1.OperationErrorErrors{Code: "QUOTA_EXCEEDED"}, + expCode: codes.ResourceExhausted, + }, + { + name: "ZONE_RESOURCE_POOL_EXHAUSTED error", + inputErr: computev1.OperationErrorErrors{Code: "ZONE_RESOURCE_POOL_EXHAUSTED"}, + expCode: codes.Unavailable, + }, + { + name: "ZONE_RESOURCE_POOL_EXHAUSTED_WITH_DETAILS error", + inputErr: computev1.OperationErrorErrors{Code: "ZONE_RESOURCE_POOL_EXHAUSTED_WITH_DETAILS"}, + expCode: codes.Unavailable, + }, + { + name: "REGION_QUOTA_EXCEEDED error", + inputErr: computev1.OperationErrorErrors{Code: "REGION_QUOTA_EXCEEDED"}, + expCode: codes.ResourceExhausted, + }, + { + name: "RATE_LIMIT_EXCEEDED error", + inputErr: computev1.OperationErrorErrors{Code: "RATE_LIMIT_EXCEEDED"}, + expCode: codes.ResourceExhausted, + }, + { + name: "INVALID_USAGE error", + inputErr: computev1.OperationErrorErrors{Code: "INVALID_USAGE"}, + expCode: codes.InvalidArgument, + }, + { + name: "RESOURCE_IN_USE_BY_ANOTHER_RESOURCE error", + inputErr: computev1.OperationErrorErrors{Code: "RESOURCE_IN_USE_BY_ANOTHER_RESOURCE"}, + expCode: codes.InvalidArgument, + }, + { + name: "UNSUPPORTED_OPERATION error", + inputErr: computev1.OperationErrorErrors{Code: "UNSUPPORTED_OPERATION"}, + expCode: codes.Internal, + }, + } + + for _, tc := range testCases { + t.Logf("Running test: %v", tc.name) + errCode := codeForGCEOpError(tc.inputErr) + if errCode != tc.expCode { + t.Errorf("test %v failed: got %v, expected %v", tc.name, errCode, tc.expCode) + } + } +}