Skip to content

Commit 0839962

Browse files
authored
Merge branch 'master' into hyperdisk_slo_sli
2 parents b075d44 + ec41c54 commit 0839962

File tree

5 files changed

+118
-3
lines changed

5 files changed

+118
-3
lines changed

Makefile

+1-1
Original file line numberDiff line numberDiff line change
@@ -126,7 +126,7 @@ init-buildx:
126126
$(DOCKER) run --rm --privileged multiarch/qemu-user-static --reset --credential yes --persistent yes
127127
# Ensure we use a builder that can leverage it (the default on linux will not)
128128
-$(DOCKER) buildx rm multiarch-multiplatform-builder
129-
$(DOCKER) buildx create --use --name=multiarch-multiplatform-builder --driver-opt network=host --driver-opt image=moby/buildkit:v0.10.6
129+
$(DOCKER) buildx create --use --name=multiarch-multiplatform-builder --driver-opt network=host --driver-opt image=moby/buildkit:v0.11.3
130130
# Register gcloud as a Docker credential helper.
131131
# Required for "docker buildx build --push".
132132
gcloud auth configure-docker --quiet

pkg/common/utils.go

+20
Original file line numberDiff line numberDiff line change
@@ -59,11 +59,19 @@ const (
5959
multiRegionalLocationFmt = "^[a-z]+$"
6060
// Example: us-east1
6161
regionalLocationFmt = "^[a-z]+-[a-z]+[0-9]$"
62+
63+
// Full or partial URL of the machine type resource, in the format:
64+
// zones/zone/machineTypes/machine-type
65+
machineTypePattern = "zones/[^/]+/machineTypes/([^/]+)$"
6266
)
6367

6468
var (
6569
multiRegionalPattern = regexp.MustCompile(multiRegionalLocationFmt)
6670
regionalPattern = regexp.MustCompile(regionalLocationFmt)
71+
72+
// Full or partial URL of the machine type resource, in the format:
73+
// zones/zone/machineTypes/machine-type
74+
machineTypeRegex = regexp.MustCompile(machineTypePattern)
6775
)
6876

6977
func BytesToGbRoundDown(bytes int64) int64 {
@@ -268,3 +276,15 @@ func ConvertMiStringToInt64(str string) (int64, error) {
268276
}
269277
return volumehelpers.RoundUpToMiB(quantity)
270278
}
279+
280+
// ParseMachineType returns an extracted machineType from a URL, or empty if not found.
281+
// machineTypeUrl: Full or partial URL of the machine type resource, in the format:
282+
//
283+
// zones/zone/machineTypes/machine-type
284+
func ParseMachineType(machineTypeUrl string) (string, error) {
285+
machineType := machineTypeRegex.FindStringSubmatch(machineTypeUrl)
286+
if machineType == nil {
287+
return "", fmt.Errorf("failed to parse machineTypeUrl. Expected suffix: zones/{zone}/machineTypes/{machine-type}. Got: %s", machineTypeUrl)
288+
}
289+
return machineType[1], nil
290+
}

pkg/common/utils_test.go

+54
Original file line numberDiff line numberDiff line change
@@ -799,3 +799,57 @@ func TestConvertMiStringToInt64(t *testing.T) {
799799
})
800800
}
801801
}
802+
803+
func TestParseMachineType(t *testing.T) {
804+
tests := []struct {
805+
desc string
806+
inputMachineTypeUrl string
807+
expectedMachineType string
808+
expectError bool
809+
}{
810+
{
811+
desc: "full URL machine type",
812+
inputMachineTypeUrl: "https://www.googleapis.com/compute/v1/projects/my-project/zones/us-central1-c/machineTypes/c3-highcpu-4",
813+
expectedMachineType: "c3-highcpu-4",
814+
},
815+
{
816+
desc: "partial URL machine type",
817+
inputMachineTypeUrl: "zones/us-central1-c/machineTypes/n2-standard-4",
818+
expectedMachineType: "n2-standard-4",
819+
},
820+
{
821+
desc: "custom partial URL machine type",
822+
inputMachineTypeUrl: "zones/us-central1-c/machineTypes/e2-custom-2-4096",
823+
expectedMachineType: "e2-custom-2-4096",
824+
},
825+
{
826+
desc: "incorrect URL",
827+
inputMachineTypeUrl: "https://www.googleapis.com/compute/v1/projects/psch-gke-dev/zones/us-central1-c",
828+
expectError: true,
829+
},
830+
{
831+
desc: "incorrect partial URL",
832+
inputMachineTypeUrl: "zones/us-central1-c/machineTypes/",
833+
expectError: true,
834+
},
835+
{
836+
desc: "missing zone",
837+
inputMachineTypeUrl: "zones//machineTypes/n2-standard-4",
838+
expectError: true,
839+
},
840+
}
841+
for _, tc := range tests {
842+
t.Run(tc.desc, func(t *testing.T) {
843+
actualMachineFamily, err := ParseMachineType(tc.inputMachineTypeUrl)
844+
if err != nil && !tc.expectError {
845+
t.Errorf("Got error %v parsing machine type %s; expect no error", err, tc.inputMachineTypeUrl)
846+
}
847+
if err == nil && tc.expectError {
848+
t.Errorf("Got no error parsing machine type %s; expect an error", tc.inputMachineTypeUrl)
849+
}
850+
if err == nil && actualMachineFamily != tc.expectedMachineType {
851+
t.Errorf("Got %s parsing machine type; expect %s", actualMachineFamily, tc.expectedMachineType)
852+
}
853+
})
854+
}
855+
}

pkg/gce-cloud-provider/compute/gce-compute.go

+25-1
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,7 @@ import (
1818
"context"
1919
"encoding/json"
2020
"fmt"
21+
"regexp"
2122
"strings"
2223
"time"
2324

@@ -38,9 +39,12 @@ const (
3839
waitForImageCreationTimeOut = 5 * time.Minute
3940
diskKind = "compute#disk"
4041
cryptoKeyVerDelimiter = "/cryptoKeyVersions"
42+
// Example message: "[pd-standard] features are not compatible for creating instance"
43+
pdDiskTypeUnsupportedPattern = `\[([a-z-]+)\] features are not compatible for creating instance`
4144
)
4245

4346
var hyperdiskTypes = []string{"hyperdisk-extreme", "hyperdisk-throughput"}
47+
var pdDiskTypeUnsupportedRegex = regexp.MustCompile(pdDiskTypeUnsupportedPattern)
4448

4549
type GCEAPIVersion string
4650

@@ -69,6 +73,15 @@ var WaitForOpBackoff = wait.Backoff{
6973
Steps: 100,
7074
Cap: 0}
7175

76+
// Custom error type to propagate error messages up to clients.
77+
type UnsupportedDiskError struct {
78+
DiskType string
79+
}
80+
81+
func (udErr *UnsupportedDiskError) Error() string {
82+
return ""
83+
}
84+
7285
type GCECompute interface {
7386
// Metadata information
7487
GetDefaultProject() string
@@ -838,12 +851,23 @@ func (cloud *CloudProvider) WaitForAttach(ctx context.Context, project string, v
838851
})
839852
}
840853

854+
func wrapOpErr(name string, opErr *computev1.OperationErrorErrors) error {
855+
if opErr.Code == "UNSUPPORTED_OPERATION" {
856+
if diskType := pdDiskTypeUnsupportedRegex.FindStringSubmatch(opErr.Message); diskType != nil {
857+
return &UnsupportedDiskError{
858+
DiskType: diskType[1],
859+
}
860+
}
861+
}
862+
return fmt.Errorf("operation %v failed (%v): %v", name, opErr.Code, opErr.Message)
863+
}
864+
841865
func opIsDone(op *computev1.Operation) (bool, error) {
842866
if op == nil || op.Status != operationStatusDone {
843867
return false, nil
844868
}
845869
if op.Error != nil && len(op.Error.Errors) > 0 && op.Error.Errors[0] != nil {
846-
return true, fmt.Errorf("operation %v failed (%v): %v", op.Name, op.Error.Errors[0].Code, op.Error.Errors[0].Message)
870+
return true, wrapOpErr(op.Name, op.Error.Errors[0])
847871
}
848872
return true, nil
849873
}

pkg/gce-pd-csi-driver/controller.go

+18-1
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,7 @@ package gceGCEDriver
1616

1717
import (
1818
"context"
19+
"errors"
1920
"fmt"
2021
"math/rand"
2122
"regexp"
@@ -515,6 +516,15 @@ func (gceCS *GCEControllerServer) validateControllerPublishVolumeRequest(ctx con
515516
return project, volKey, nil
516517
}
517518

519+
func parseMachineType(machineTypeUrl string) string {
520+
machineType, parseErr := common.ParseMachineType(machineTypeUrl)
521+
if parseErr != nil {
522+
// Parse errors represent an unexpected API change with instance.MachineType; log a warning.
523+
klog.Warningf("ParseMachineType(%v): %v", machineTypeUrl, parseErr)
524+
}
525+
return machineType
526+
}
527+
518528
func (gceCS *GCEControllerServer) executeControllerPublishVolume(ctx context.Context, req *csi.ControllerPublishVolumeRequest) (*csi.ControllerPublishVolumeResponse, error) {
519529
project, volKey, err := gceCS.validateControllerPublishVolumeRequest(ctx, req)
520530

@@ -590,7 +600,14 @@ func (gceCS *GCEControllerServer) executeControllerPublishVolume(ctx context.Con
590600
}
591601
err = gceCS.CloudProvider.AttachDisk(ctx, project, volKey, readWrite, attachableDiskTypePersistent, instanceZone, instanceName)
592602
if err != nil {
593-
// Emit metric for error
603+
var udErr *gce.UnsupportedDiskError
604+
if errors.As(err, &udErr) {
605+
// If we encountered an UnsupportedDiskError, rewrite the error message to be more user friendly.
606+
// The error message from GCE is phrased around disk create on VM creation, not runtime attach.
607+
machineType := parseMachineType(instance.MachineType)
608+
return nil, status.Errorf(codes.InvalidArgument, "'%s' is not a compatible disk type with the machine type %s, please review the GCP online documentation for available persistent disk options", udErr.DiskType, machineType)
609+
}
610+
// Emit metric for error
594611
gceCS.Metrics.RecordOperationErrorMetrics("ControllerPublishVolume", err, diskToPublish.GetPDType())
595612
return nil, status.Errorf(codes.Internal, "unknown Attach error: %v", err.Error())
596613
}

0 commit comments

Comments
 (0)