nebius/SWE-rebench-V2-PRs huggingface dataset:
Repository: NetApp/harvest
Pull request #373 (2021-08-02 22:34:15)

fix: template version selection should try earliest


Patch:
diff --git a/cmd/poller/collector/helpers.go b/cmd/poller/collector/helpers.go
index 3e80a6bf..90d1d610 100644
--- a/cmd/poller/collector/helpers.go
+++ b/cmd/poller/collector/helpers.go
@@ -115,9 +115,13 @@ func (c *AbstractCollector) ImportSubTemplate(model, filename string, ver [3]int
 	return tree.Import("yaml", subTemplateFp)
 }
 
-//getClosestIndex input versions should be sorted
-// returns -1 if not match else returns equals or closest match to the left
+//getClosestIndex returns the closest left match to the sorted list of input versions
+// returns -1 when the versions list is empty
+// returns equal or closest match to the left
 func getClosestIndex(versions []*version.Version, version *version.Version) int {
+	if len(versions) == 0 {
+		return -1
+	}
 	var l = 0
 	var r = len(versions) - 1
 	for l <= r {
@@ -131,6 +135,9 @@ func getClosestIndex(versions []*version.Version, version *version.Version) int
 			return m
 		}
 	}
+	if l == 0 {
+		return 0
+	}
 	return l - 1
 }
 


Test patch:
diff --git a/cmd/poller/collector/helpers_test.go b/cmd/poller/collector/helpers_test.go
index 9a22b58c..5b8ee593 100644
--- a/cmd/poller/collector/helpers_test.go
+++ b/cmd/poller/collector/helpers_test.go
@@ -33,7 +33,7 @@ func Test_getClosestIndex(t *testing.T) {
 
 	tests := []test{
 		{"MatchCase", args{setupVersions([]string{"9.8.0", "9.8.1", "9.9.0", "10.10.10"}), buildVersion("9.8.1")}, 1},
-		{"NonMatchCase", args{setupVersions([]string{"9.8.0", "9.8.1", "9.9.0", "10.10.10"}), buildVersion("9.7.1")}, -1},
+		{"MatchFirstYolo", args{setupVersions([]string{"9.8.0", "9.8.1", "9.9.0", "10.10.10"}), buildVersion("9.7.1")}, 0},
 		{"ClosestMatchCase", args{setupVersions([]string{"9.8.0", "9.8.1", "9.9.0", "10.10.10"}), buildVersion("9.9.2")}, 2},
 		{"EmptyCase", args{setupVersions([]string{}), buildVersion("9.8.1")}, -1},
 	}
Repository: kubernetes-sigs/gcp-compute-persistent-disk-csi-driver
Pull request #1560 (2024-01-03 00:11:18)

[release-1.12] Filter multiattach errors
This is an automated cherry-pick of #1559

/assign mattcary

```release-note
Filter user misconfigured multiattach errors.
```


Patch:
diff --git a/pkg/common/utils.go b/pkg/common/utils.go
index 368a853a..73eb7e69 100644
--- a/pkg/common/utils.go
+++ b/pkg/common/utils.go
@@ -335,6 +335,9 @@ func CodeForError(sourceError error) codes.Code {
 		return codes.Internal
 	}
 
+	if code, err := isUserMultiAttachError(sourceError); err == nil {
+		return code
+	}
 	if code, err := existingErrorCode(sourceError); err == nil {
 		return code
 	}
@@ -372,6 +375,17 @@ func isContextError(err error) (codes.Code, error) {
 	return codes.Unknown, fmt.Errorf("Not a context error: %w", err)
 }
 
+// isUserMultiAttachError returns an InvalidArgument if the error is
+// multi-attach detected from the API server. If we get this error from the API
+// server, it means that the kubelet doesn't know about the multiattch so it is
+// due to user configuration.
+func isUserMultiAttachError(err error) (codes.Code, error) {
+	if strings.Contains(err.Error(), "The disk resource") && strings.Contains(err.Error(), "is already being used") {
+		return codes.InvalidArgument, nil
+	}
+	return codes.Unknown, fmt.Errorf("Not a user multiattach error: %w", err)
+}
+
 func existingErrorCode(err error) (codes.Code, error) {
 	if err == nil {
 		return codes.Unknown, fmt.Errorf("null error")


Test patch:
diff --git a/pkg/common/utils_test.go b/pkg/common/utils_test.go
index 3f5993a0..f60aace9 100644
--- a/pkg/common/utils_test.go
+++ b/pkg/common/utils_test.go
@@ -1015,6 +1015,11 @@ func TestCodeForError(t *testing.T) {
 			inputErr: nil,
 			expCode:  codes.Internal,
 		},
+		{
+			name:     "user multiattach error",
+			inputErr: fmt.Errorf("The disk resource 'projects/foo/disk/bar' is already being used by 'projects/foo/instances/1'"),
+			expCode:  codes.InvalidArgument,
+		},
 	}
 
 	for _, tc := range testCases {
@@ -1076,6 +1081,34 @@ func TestIsContextError(t *testing.T) {
 	}
 }
 
+func TestIsUserMultiAttachError(t *testing.T) {
+	cases := []struct {
+		errorString  string
+		expectedCode codes.Code
+		expectCode   bool
+	}{
+		{
+			errorString:  "The disk resource 'projects/foo/disk/bar' is already being used by 'projects/foo/instance/biz'",
+			expectedCode: codes.InvalidArgument,
+			expectCode:   true,
+		},
+		{
+			errorString: "The disk resource is ok!",
+			expectCode:  false,
+		},
+	}
+	for _, test := range cases {
+		code, err := isUserMultiAttachError(fmt.Errorf(test.errorString))
+		if test.expectCode {
+			if err != nil || code != test.expectedCode {
+				t.Errorf("Failed with non-nil error %v or bad code %v: %s", err, code, test.errorString)
+			}
+		} else if err == nil {
+			t.Errorf("Expected error for test but got none: %s", test.errorString)
+		}
+	}
+}
+
 func TestIsValidDiskEncryptionKmsKey(t *testing.T) {
 	cases := []struct {
 		diskEncryptionKmsKey string
Repository: kubernetes-sigs/gcp-compute-persistent-disk-csi-driver
Pull request #1610 (2024-02-17 00:48:29)

[release-1.13] Map UNSUPPORTED_OPERATION GCE operation error codes to InvalidArgument
This is an automated cherry-pick of #1606

/assign amacaskill

```release-note
NONE
```


Patch:
diff --git a/pkg/gce-cloud-provider/compute/gce-compute.go b/pkg/gce-cloud-provider/compute/gce-compute.go
index f73c6b6d..d71f4b4b 100644
--- a/pkg/gce-cloud-provider/compute/gce-compute.go
+++ b/pkg/gce-cloud-provider/compute/gce-compute.go
@@ -978,6 +978,7 @@ func codeForGCEOpError(err computev1.OperationErrorErrors) codes.Code {
 		"REGION_QUOTA_EXCEEDED":                     codes.ResourceExhausted,
 		"RATE_LIMIT_EXCEEDED":                       codes.ResourceExhausted,
 		"INVALID_USAGE":                             codes.InvalidArgument,
+		"UNSUPPORTED_OPERATION":                     codes.InvalidArgument,
 	}
 	if code, ok := userErrors[err.Code]; ok {
 		return code


Test patch:
diff --git a/pkg/gce-cloud-provider/compute/gce-compute_test.go b/pkg/gce-cloud-provider/compute/gce-compute_test.go
index 652b111a..563fb602 100644
--- a/pkg/gce-cloud-provider/compute/gce-compute_test.go
+++ b/pkg/gce-cloud-provider/compute/gce-compute_test.go
@@ -162,7 +162,7 @@ func TestCodeForGCEOpError(t *testing.T) {
 		{
 			name:     "UNSUPPORTED_OPERATION error",
 			inputErr: computev1.OperationErrorErrors{Code: "UNSUPPORTED_OPERATION"},
-			expCode:  codes.Internal,
+			expCode:  codes.InvalidArgument,
 		},
 	}
Repository: kubernetes-sigs/gcp-compute-persistent-disk-csi-driver
Pull request #1724 (2024-05-28 18:18:17)

[release-1.13] Return Unavailable for 'connection reset by peer' errors
This is an automated cherry-pick of #1720

/assign amacaskill

```release-note
Return Unavailable for 'connection reset by peer' errors.
```


Patch:
diff --git a/pkg/common/utils.go b/pkg/common/utils.go
index 36e628e5..a005297b 100644
--- a/pkg/common/utils.go
+++ b/pkg/common/utils.go
@@ -346,6 +346,9 @@ func CodeForError(sourceError error) codes.Code {
 	if code, err := isContextError(sourceError); err == nil {
 		return code
 	}
+	if code, err := isConnectionResetError(sourceError); err == nil {
+		return code
+	}
 
 	var apiErr *googleapi.Error
 	if !errors.As(sourceError, &apiErr) {
@@ -377,6 +380,20 @@ func isContextError(err error) (codes.Code, error) {
 	return codes.Unknown, fmt.Errorf("Not a context error: %w", err)
 }
 
+// isConnectionResetError returns the grpc error code Unavailable if the
+// passed in error contains the "connection reset by peer" string.
+func isConnectionResetError(err error) (codes.Code, error) {
+	if err == nil {
+		return codes.Unknown, fmt.Errorf("null error")
+	}
+
+	errStr := err.Error()
+	if strings.Contains(errStr, "connection reset by peer") {
+		return codes.Unavailable, nil
+	}
+	return codes.Unknown, fmt.Errorf("Not a connection reset error: %w", err)
+}
+
 // isUserMultiAttachError returns an InvalidArgument if the error is
 // multi-attach detected from the API server. If we get this error from the API
 // server, it means that the kubelet doesn't know about the multiattch so it is


Test patch:
diff --git a/pkg/common/utils_test.go b/pkg/common/utils_test.go
index 8212f278..644f9475 100644
--- a/pkg/common/utils_test.go
+++ b/pkg/common/utils_test.go
@@ -22,6 +22,7 @@ import (
 	"fmt"
 	"net/http"
 	"reflect"
+	"syscall"
 	"testing"
 
 	"github.com/GoogleCloudPlatform/k8s-cloud-provider/pkg/cloud/meta"
@@ -1006,6 +1007,16 @@ func TestCodeForError(t *testing.T) {
 			inputErr: context.DeadlineExceeded,
 			expCode:  codes.DeadlineExceeded,
 		},
+		{
+			name:     "connection reset error",
+			inputErr: fmt.Errorf("failed to getDisk: connection reset by peer"),
+			expCode:  codes.Unavailable,
+		},
+		{
+			name:     "wrapped connection reset error",
+			inputErr: fmt.Errorf("received error: %v", syscall.ECONNRESET),
+			expCode:  codes.Unavailable,
+		},
 		{
 			name:     "status error with Aborted error code",
 			inputErr: status.Error(codes.Aborted, "aborted error"),
Repository: kubernetes-sigs/gcp-compute-persistent-disk-csi-driver
Pull request #1790 (2024-07-19 21:10:27)

[release-1.14] Change OPERATION_CANCELED_BY_USER to Canceled instead of Aborted
This is an automated cherry-pick of #1789

/assign amacaskill

```release-note
Change OPERATION_CANCELED_BY_USER to map to Canceled error code instead of Aborted.
```


Patch:
diff --git a/pkg/gce-cloud-provider/compute/gce-compute.go b/pkg/gce-cloud-provider/compute/gce-compute.go
index 13f00fcc..b08a28cf 100644
--- a/pkg/gce-cloud-provider/compute/gce-compute.go
+++ b/pkg/gce-cloud-provider/compute/gce-compute.go
@@ -1129,7 +1129,7 @@ func codeForGCEOpError(err computev1.OperationErrorErrors) 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,
+		"OPERATION_CANCELED_BY_USER":                codes.Canceled,
 		"QUOTA_EXCEEDED":                            codes.ResourceExhausted,
 		"ZONE_RESOURCE_POOL_EXHAUSTED":              codes.Unavailable,
 		"ZONE_RESOURCE_POOL_EXHAUSTED_WITH_DETAILS": codes.Unavailable,


Test patch:
diff --git a/pkg/gce-cloud-provider/compute/gce-compute_test.go b/pkg/gce-cloud-provider/compute/gce-compute_test.go
index 563fb602..9fec830c 100644
--- a/pkg/gce-cloud-provider/compute/gce-compute_test.go
+++ b/pkg/gce-cloud-provider/compute/gce-compute_test.go
@@ -122,7 +122,7 @@ func TestCodeForGCEOpError(t *testing.T) {
 		{
 			name:     "OPERATION_CANCELED_BY_USER error",
 			inputErr: computev1.OperationErrorErrors{Code: "OPERATION_CANCELED_BY_USER"},
-			expCode:  codes.Aborted,
+			expCode:  codes.Canceled,
 		},
 		{
 			name:     "QUOTA_EXCEEDED error",
Repository: kubernetes-sigs/gcp-compute-persistent-disk-csi-driver
Pull request #1813 (2024-08-26 18:19:32)

[release-1.12] Return Unavailable for 'connection reset by peer' errors
This is an automated cherry-pick of #1720

/assign hime

```release-note
Return Unavailable for 'connection reset by peer' errors.
```


Patch:
diff --git a/pkg/common/utils.go b/pkg/common/utils.go
index 73eb7e69..ea076d05 100644
--- a/pkg/common/utils.go
+++ b/pkg/common/utils.go
@@ -344,6 +344,9 @@ func CodeForError(sourceError error) codes.Code {
 	if code, err := isContextError(sourceError); err == nil {
 		return code
 	}
+	if code, err := isConnectionResetError(sourceError); err == nil {
+		return code
+	}
 
 	var apiErr *googleapi.Error
 	if !errors.As(sourceError, &apiErr) {
@@ -375,6 +378,20 @@ func isContextError(err error) (codes.Code, error) {
 	return codes.Unknown, fmt.Errorf("Not a context error: %w", err)
 }
 
+// isConnectionResetError returns the grpc error code Unavailable if the
+// passed in error contains the "connection reset by peer" string.
+func isConnectionResetError(err error) (codes.Code, error) {
+	if err == nil {
+		return codes.Unknown, fmt.Errorf("null error")
+	}
+
+	errStr := err.Error()
+	if strings.Contains(errStr, "connection reset by peer") {
+		return codes.Unavailable, nil
+	}
+	return codes.Unknown, fmt.Errorf("Not a connection reset error: %w", err)
+}
+
 // isUserMultiAttachError returns an InvalidArgument if the error is
 // multi-attach detected from the API server. If we get this error from the API
 // server, it means that the kubelet doesn't know about the multiattch so it is


Test patch:
diff --git a/pkg/common/utils_test.go b/pkg/common/utils_test.go
index f60aace9..5d829766 100644
--- a/pkg/common/utils_test.go
+++ b/pkg/common/utils_test.go
@@ -22,6 +22,7 @@ import (
 	"fmt"
 	"net/http"
 	"reflect"
+	"syscall"
 	"testing"
 
 	"github.com/GoogleCloudPlatform/k8s-cloud-provider/pkg/cloud/meta"
@@ -1005,6 +1006,16 @@ func TestCodeForError(t *testing.T) {
 			inputErr: context.DeadlineExceeded,
 			expCode:  codes.DeadlineExceeded,
 		},
+		{
+			name:     "connection reset error",
+			inputErr: fmt.Errorf("failed to getDisk: connection reset by peer"),
+			expCode:  codes.Unavailable,
+		},
+		{
+			name:     "wrapped connection reset error",
+			inputErr: fmt.Errorf("received error: %v", syscall.ECONNRESET),
+			expCode:  codes.Unavailable,
+		},
 		{
 			name:     "status error with Aborted error code",
 			inputErr: status.Error(codes.Aborted, "aborted error"),
Repository: kubernetes-sigs/gcp-compute-persistent-disk-csi-driver
Pull request #1848 (2024-10-22 21:53:53)

[release-1.15] Map RESOURCE_OPERATION_RATE_EXCEEDED to ResourceExhausted
This is an automated cherry-pick of #1847

/assign amacaskill

```release-note
Map RESOURCE_OPERATION_RATE_EXCEEDED to ResourceExhausted.
```


Patch:
diff --git a/pkg/gce-cloud-provider/compute/gce-compute.go b/pkg/gce-cloud-provider/compute/gce-compute.go
index dc45b6be..c2c2b760 100644
--- a/pkg/gce-cloud-provider/compute/gce-compute.go
+++ b/pkg/gce-cloud-provider/compute/gce-compute.go
@@ -1244,6 +1244,7 @@ func codeForGCEOpError(err computev1.OperationErrorErrors) codes.Code {
 		"RATE_LIMIT_EXCEEDED":                       codes.ResourceExhausted,
 		"INVALID_USAGE":                             codes.InvalidArgument,
 		"UNSUPPORTED_OPERATION":                     codes.InvalidArgument,
+		"RESOURCE_OPERATION_RATE_EXCEEDED":          codes.ResourceExhausted,
 	}
 	if code, ok := userErrors[err.Code]; ok {
 		return code


Test patch:
diff --git a/pkg/gce-cloud-provider/compute/gce-compute_test.go b/pkg/gce-cloud-provider/compute/gce-compute_test.go
index 9fec830c..cd5fc9c2 100644
--- a/pkg/gce-cloud-provider/compute/gce-compute_test.go
+++ b/pkg/gce-cloud-provider/compute/gce-compute_test.go
@@ -164,6 +164,11 @@ func TestCodeForGCEOpError(t *testing.T) {
 			inputErr: computev1.OperationErrorErrors{Code: "UNSUPPORTED_OPERATION"},
 			expCode:  codes.InvalidArgument,
 		},
+		{
+			name:     "RESOURCE_OPERATION_RATE_EXCEEDED error",
+			inputErr: computev1.OperationErrorErrors{Code: "RESOURCE_OPERATION_RATE_EXCEEDED"},
+			expCode:  codes.ResourceExhausted,
+		},
 	}
 
 	for _, tc := range testCases {
Repository: kubernetes-sigs/gcp-compute-persistent-disk-csi-driver
Pull request #1850 (2024-10-23 16:37:52)

[release-1.15] Add StatusConflict http kind to userErrorCodeMap.
This is an automated cherry-pick of #1846

/assign amacaskill

```release-note
Map http error code 409 (Status conflict) to grpc FailedPrecondition.
```


Patch:
diff --git a/pkg/common/utils.go b/pkg/common/utils.go
index c3b71c1c..a69d3045 100644
--- a/pkg/common/utils.go
+++ b/pkg/common/utils.go
@@ -98,6 +98,7 @@ var (
 		http.StatusBadRequest:      codes.InvalidArgument,
 		http.StatusTooManyRequests: codes.ResourceExhausted,
 		http.StatusNotFound:        codes.NotFound,
+		http.StatusConflict:        codes.FailedPrecondition,
 	}
 
 	// Regular expressions for validating parent_id, key and value of a resource tag.


Test patch:
diff --git a/pkg/common/utils_test.go b/pkg/common/utils_test.go
index 1b9b44e3..0df0ed1d 100644
--- a/pkg/common/utils_test.go
+++ b/pkg/common/utils_test.go
@@ -1241,6 +1241,14 @@ func TestCodeForError(t *testing.T) {
 			}),
 			expCode: codes.NotFound,
 		},
+		{
+			name: "googleapi.Error that wraps apierror.APIError of http kind status conflict",
+			inputErr: getGoogleAPIWrappedError(&googleapi.Error{
+				Code:    409,
+				Message: "status conflict error",
+			}),
+			expCode: codes.FailedPrecondition,
+		},
 		{
 			name: "googleapi.Error that wraps apierror.APIError of status kind",
 			inputErr: getGoogleAPIWrappedError(status.New(
Repository: kubernetes-sigs/gcp-compute-persistent-disk-csi-driver
Pull request #1910 (2025-01-21 18:00:10)

Add handling for TPC to GetRegionFromZones
Add handling for TPC to GetRegionFromZones

**Does this PR introduce a user-facing change?**
```
Bug fix preventing PDCSI driver from being compatible with TPC
```


Patch:
diff --git a/pkg/common/utils.go b/pkg/common/utils.go
index d1060772..3abe5dde 100644
--- a/pkg/common/utils.go
+++ b/pkg/common/utils.go
@@ -184,17 +184,22 @@ func NodeIDToZoneAndName(id string) (string, string, error) {
 }
 
 func GetRegionFromZones(zones []string) (string, error) {
+	const tpcPrefix = "u"
 	regions := sets.String{}
 	if len(zones) < 1 {
 		return "", fmt.Errorf("no zones specified")
 	}
 	for _, zone := range zones {
 		// Zone expected format {locale}-{region}-{zone}
+		// TPC zone expected format is u-{}-{}-{}, e.g. u-europe-central2-a. See go/tpc-region-naming.
 		splitZone := strings.Split(zone, "-")
-		if len(splitZone) != 3 {
-			return "", fmt.Errorf("zone in unexpected format, expected: {locale}-{region}-{zone}, got: %v", zone)
+		if len(splitZone) == 3 {
+			regions.Insert(strings.Join(splitZone[0:2], "-"))
+		} else if len(splitZone) == 4 && splitZone[0] == tpcPrefix {
+			regions.Insert(strings.Join(splitZone[0:3], "-"))
+		} else {
+			return "", fmt.Errorf("zone in unexpected format, expected: {locale}-{region}-{zone} or u-{locale}-{region}-{zone}, got: %v", zone)
 		}
-		regions.Insert(strings.Join(splitZone[0:2], "-"))
 	}
 	if regions.Len() != 1 {
 		return "", fmt.Errorf("multiple or no regions gotten from zones, got: %v", regions)


Test patch:
diff --git a/pkg/common/utils_test.go b/pkg/common/utils_test.go
index 5917487c..58629dd8 100644
--- a/pkg/common/utils_test.go
+++ b/pkg/common/utils_test.go
@@ -293,6 +293,16 @@ func TestGetRegionFromZones(t *testing.T) {
 			zones:  []string{"blah/blooh"},
 			expErr: true,
 		},
+		{
+			name:      "tpc zone",
+			zones:     []string{"u-europe-central2-a"},
+			expRegion: "u-europe-central2",
+		},
+		{
+			name:   "malformed tpc zone",
+			zones:  []string{"us-europe-central2-a"},
+			expErr: true,
+		},
 	}
 	for _, tc := range testCases {
 		t.Logf("test case: %s", tc.name)
Repository: kubernetes-sigs/aws-ebs-csi-driver
Pull request #767 (2021-02-24 14:34:38)

Fix error message when IOPSPerGB is missing in io1 volumes
Explicitly return specific message in this case, otherwise the cloud returns generic `The parameter iops must be specified for io1 volumes`, which mentions `iops`, but the parameter that the user needs to set is `IOPSPerGB`.

Fixes: #766


Patch:
diff --git a/pkg/driver/controller.go b/pkg/driver/controller.go
index 0b829896..ea923dce 100644
--- a/pkg/driver/controller.go
+++ b/pkg/driver/controller.go
@@ -183,6 +183,12 @@ func (d *controllerService) CreateVolume(ctx context.Context, req *csi.CreateVol
 		}
 	}
 
+	if volumeType == cloud.VolumeTypeIO1 {
+		if iopsPerGB == 0 {
+			return nil, status.Errorf(codes.InvalidArgument, "The parameter IOPSPerGB must be specified for io1 volumes")
+		}
+	}
+
 	snapshotID := ""
 	volumeSource := req.GetVolumeContentSource()
 	if volumeSource != nil {


Test patch:
diff --git a/pkg/driver/controller_test.go b/pkg/driver/controller_test.go
index 55878689..b4b1d7ec 100644
--- a/pkg/driver/controller_test.go
+++ b/pkg/driver/controller_test.go
@@ -1080,6 +1080,7 @@ func TestCreateVolume(t *testing.T) {
 					VolumeCapabilities: stdVolCap,
 					Parameters: map[string]string{
 						VolumeTypeKey: cloud.VolumeTypeIO1,
+						IopsPerGBKey:  "5",
 						"unknownKey":  "unknownValue",
 					},
 				}
@@ -1484,6 +1485,7 @@ func TestCreateVolume(t *testing.T) {
 					VolumeCapabilities: invalidVolCap,
 					Parameters: map[string]string{
 						VolumeTypeKey: cloud.VolumeTypeIO1,
+						IopsPerGBKey:  "5",
 						"unknownKey":  "unknownValue",
 					},
 				}
@@ -1557,6 +1559,46 @@ func TestCreateVolume(t *testing.T) {
 				}
 			},
 		},
+		{
+			name: "fail with missing iopsPerGB parameter",
+			testFunc: func(t *testing.T) {
+				req := &csi.CreateVolumeRequest{
+					Name:               "vol-test",
+					CapacityRange:      stdCapRange,
+					VolumeCapabilities: stdVolCap,
+					Parameters: map[string]string{
+						VolumeTypeKey: cloud.VolumeTypeIO1,
+					},
+				}
+
+				ctx := context.Background()
+
+				mockCtl := gomock.NewController(t)
+				defer mockCtl.Finish()
+
+				mockCloud := mocks.NewMockCloud(mockCtl)
+				mockCloud.EXPECT().GetDiskByName(gomock.Eq(ctx), gomock.Eq(req.Name), gomock.Eq(stdVolSize)).Return(nil, cloud.ErrNotFound)
+
+				awsDriver := controllerService{
+					cloud:         mockCloud,
+					inFlight:      internal.NewInFlight(),
+					driverOptions: &DriverOptions{},
+				}
+
+				_, err := awsDriver.CreateVolume(ctx, req)
+				if err == nil {
+					t.Fatalf("Expected CreateVolume to fail but got no error")
+				}
+
+				srvErr, ok := status.FromError(err)
+				if !ok {
+					t.Fatalf("Could not get error status code from error: %v", srvErr)
+				}
+				if srvErr.Code() != codes.InvalidArgument {
+					t.Fatalf("Expect InvalidArgument but got: %s", srvErr.Code())
+				}
+			},
+		},
 	}
 
 	for _, tc := range testCases {
Repository: kubernetes-sigs/gcp-compute-persistent-disk-csi-driver
Pull request #1998 (2025-03-17 22:02:10)

[release-1.17] Add handling for TPC to GetRegionFromZones
This is an automated cherry-pick of #1910

/assign amacaskill

**Does this PR introduce a user-facing change?**
```release-note
Bug fix preventing PDCSI driver from being compatible with TPC
```


Patch:
diff --git a/pkg/common/utils.go b/pkg/common/utils.go
index d1060772..3abe5dde 100644
--- a/pkg/common/utils.go
+++ b/pkg/common/utils.go
@@ -184,17 +184,22 @@ func NodeIDToZoneAndName(id string) (string, string, error) {
 }
 
 func GetRegionFromZones(zones []string) (string, error) {
+	const tpcPrefix = "u"
 	regions := sets.String{}
 	if len(zones) < 1 {
 		return "", fmt.Errorf("no zones specified")
 	}
 	for _, zone := range zones {
 		// Zone expected format {locale}-{region}-{zone}
+		// TPC zone expected format is u-{}-{}-{}, e.g. u-europe-central2-a. See go/tpc-region-naming.
 		splitZone := strings.Split(zone, "-")
-		if len(splitZone) != 3 {
-			return "", fmt.Errorf("zone in unexpected format, expected: {locale}-{region}-{zone}, got: %v", zone)
+		if len(splitZone) == 3 {
+			regions.Insert(strings.Join(splitZone[0:2], "-"))
+		} else if len(splitZone) == 4 && splitZone[0] == tpcPrefix {
+			regions.Insert(strings.Join(splitZone[0:3], "-"))
+		} else {
+			return "", fmt.Errorf("zone in unexpected format, expected: {locale}-{region}-{zone} or u-{locale}-{region}-{zone}, got: %v", zone)
 		}
-		regions.Insert(strings.Join(splitZone[0:2], "-"))
 	}
 	if regions.Len() != 1 {
 		return "", fmt.Errorf("multiple or no regions gotten from zones, got: %v", regions)


Test patch:
diff --git a/pkg/common/utils_test.go b/pkg/common/utils_test.go
index 5917487c..58629dd8 100644
--- a/pkg/common/utils_test.go
+++ b/pkg/common/utils_test.go
@@ -293,6 +293,16 @@ func TestGetRegionFromZones(t *testing.T) {
 			zones:  []string{"blah/blooh"},
 			expErr: true,
 		},
+		{
+			name:      "tpc zone",
+			zones:     []string{"u-europe-central2-a"},
+			expRegion: "u-europe-central2",
+		},
+		{
+			name:   "malformed tpc zone",
+			zones:  []string{"us-europe-central2-a"},
+			expErr: true,
+		},
 	}
 	for _, tc := range testCases {
 		t.Logf("test case: %s", tc.name)