diff --git a/pkg/cloudprovider/providers/gce/gce.go b/pkg/cloudprovider/providers/gce/gce.go index 7117929152..6e817dae6c 100644 --- a/pkg/cloudprovider/providers/gce/gce.go +++ b/pkg/cloudprovider/providers/gce/gce.go @@ -125,7 +125,7 @@ type GCECloud struct { nodeInstancePrefix string // If non-"", an advisory prefix for all nodes in the cluster useMetadataServer bool operationPollRateLimiter flowcontrol.RateLimiter - manager ServiceManager + manager diskServiceManager // sharedResourceLock is used to serialize GCE operations that may mutate shared state to // prevent inconsistencies. For example, load balancers manipulation methods will take the // lock to prevent shared resources from being prematurely deleted while the operation is @@ -138,59 +138,6 @@ type GCECloud struct { AlphaFeatureGate *AlphaFeatureGate } -type ServiceManager interface { - // Creates a new persistent disk on GCE with the given disk spec. - CreateDisk( - name string, - sizeGb int64, - tagsStr string, - diskType string, - zone string) (gceObject, error) - - // Creates a new regional persistent disk on GCE with the given disk spec. - CreateRegionalDisk( - name string, - sizeGb int64, - tagsStr string, - diskType string, - zones sets.String) (gceObject, error) - - // Deletes the persistent disk from GCE with the given diskName. - DeleteDisk(zone string, disk string) (gceObject, error) - - // Deletes the regional persistent disk from GCE with the given diskName. - DeleteRegionalDisk(diskName string) (gceObject, error) - - // Attach a persistent disk on GCE with the given disk spec to the specified instance. - AttachDisk( - disk *GCEDisk, - readWrite string, - instanceZone string, - instanceName string) (gceObject, error) - - // Detach a persistent disk on GCE with the given disk spec from the specified instance. - DetachDisk( - instanceZone string, - instanceName string, - devicePath string) (gceObject, error) - - // Gets the persistent disk from GCE with the given diskName. - GetDisk(zone string, diskName string) (*GCEDisk, error) - - // Gets the regional persistent disk from GCE with the given diskName. - GetRegionalDisk(diskName string) (*GCEDisk, error) - - // Waits until GCE reports the given operation in the given zone as done. - WaitForZoneOp(op gceObject, zone string, mc *metricContext) error - - // Waits until GCE reports the given operation in the given region is done. - WaitForRegionalOp(op gceObject, mc *metricContext) error -} - -type GCEServiceManager struct { - gce *GCECloud -} - type ConfigGlobal struct { TokenURL string `gcfg:"token-url"` TokenBody string `gcfg:"token-body"` @@ -511,7 +458,7 @@ func CreateGCECloud(config *CloudConfig) (*GCECloud, error) { AlphaFeatureGate: config.AlphaFeatureGate, } - gce.manager = &GCEServiceManager{gce} + gce.manager = &gceServiceManager{gce} // Registering the KMS plugin only the first time. kmsPluginRegisterOnce.Do(func() { @@ -768,335 +715,7 @@ func newOauthClient(tokenSource oauth2.TokenSource) (*http.Client, error) { return oauth2.NewClient(oauth2.NoContext, tokenSource), nil } -func (manager *GCEServiceManager) CreateDisk( - name string, - sizeGb int64, - tagsStr string, - diskType string, - zone string) (gceObject, error) { - diskTypeURI, err := manager.getDiskTypeURI( - manager.gce.region /* diskRegion */, singleZone{zone}, diskType) - if err != nil { - return nil, err - } - - if manager.gce.AlphaFeatureGate.Enabled(GCEDiskAlphaFeatureGate) { - diskToCreateAlpha := &computealpha.Disk{ - Name: name, - SizeGb: sizeGb, - Description: tagsStr, - Type: diskTypeURI, - } - - return manager.gce.serviceAlpha.Disks.Insert( - manager.gce.projectID, zone, diskToCreateAlpha).Do() - } - - diskToCreateV1 := &compute.Disk{ - Name: name, - SizeGb: sizeGb, - Description: tagsStr, - Type: diskTypeURI, - } - return manager.gce.service.Disks.Insert( - manager.gce.projectID, zone, diskToCreateV1).Do() -} - -func (manager *GCEServiceManager) CreateRegionalDisk( - name string, - sizeGb int64, - tagsStr string, - diskType string, - replicaZones sets.String) (gceObject, error) { - diskTypeURI, err := manager.getDiskTypeURI( - manager.gce.region /* diskRegion */, multiZone{replicaZones}, diskType) - if err != nil { - return nil, err - } - fullyQualifiedReplicaZones := []string{} - for _, replicaZone := range replicaZones.UnsortedList() { - fullyQualifiedReplicaZones = append( - fullyQualifiedReplicaZones, manager.getReplicaZoneURI(replicaZone)) - } - if manager.gce.AlphaFeatureGate.Enabled(GCEDiskAlphaFeatureGate) { - diskToCreateAlpha := &computealpha.Disk{ - Name: name, - SizeGb: sizeGb, - Description: tagsStr, - Type: diskTypeURI, - ReplicaZones: fullyQualifiedReplicaZones, - } - return manager.gce.serviceAlpha.RegionDisks.Insert( - manager.gce.projectID, manager.gce.region, diskToCreateAlpha).Do() - } - - return nil, fmt.Errorf("The regional PD feature is only available via the GCE Alpha API. Enable \"GCEDiskAlphaAPI\" in the list of \"alpha-features\" in \"gce.conf\" to use the feature.") -} - -func (manager *GCEServiceManager) AttachDisk( - disk *GCEDisk, - readWrite string, - instanceZone string, - instanceName string) (gceObject, error) { - source, err := manager.getDiskSourceURI(disk) - if err != nil { - return nil, err - } - - if manager.gce.AlphaFeatureGate.Enabled(GCEDiskAlphaFeatureGate) { - attachedDiskAlpha := &computealpha.AttachedDisk{ - DeviceName: disk.Name, - Kind: disk.Kind, - Mode: readWrite, - Source: source, - Type: diskTypePersistent, - } - return manager.gce.serviceAlpha.Instances.AttachDisk( - manager.gce.projectID, instanceZone, instanceName, attachedDiskAlpha).Do() - } - - attachedDiskV1 := &compute.AttachedDisk{ - DeviceName: disk.Name, - Kind: disk.Kind, - Mode: readWrite, - Source: source, - Type: disk.Type, - } - return manager.gce.service.Instances.AttachDisk( - manager.gce.projectID, instanceZone, instanceName, attachedDiskV1).Do() -} - -func (manager *GCEServiceManager) DetachDisk( - instanceZone string, - instanceName string, - devicePath string) (gceObject, error) { - if manager.gce.AlphaFeatureGate.Enabled(GCEDiskAlphaFeatureGate) { - manager.gce.serviceAlpha.Instances.DetachDisk( - manager.gce.projectID, instanceZone, instanceName, devicePath).Do() - } - - return manager.gce.service.Instances.DetachDisk( - manager.gce.projectID, instanceZone, instanceName, devicePath).Do() -} - -func (manager *GCEServiceManager) GetDisk( - zone string, - diskName string) (*GCEDisk, error) { - if zone == "" { - return nil, fmt.Errorf("Can not fetch disk %q. Zone is empty.", diskName) - } - - if diskName == "" { - return nil, fmt.Errorf("Can not fetch disk. Zone is specified (%q). But disk name is empty.", zone) - } - - if manager.gce.AlphaFeatureGate.Enabled(GCEDiskAlphaFeatureGate) { - diskAlpha, err := manager.gce.serviceAlpha.Disks.Get( - manager.gce.projectID, zone, diskName).Do() - if err != nil { - return nil, err - } - - var zoneInfo zoneType - if len(diskAlpha.ReplicaZones) > 1 { - zones := sets.NewString() - for _, zoneURI := range diskAlpha.ReplicaZones { - zones.Insert(lastComponent(zoneURI)) - } - zoneInfo = multiZone{zones} - } else { - zoneInfo = singleZone{lastComponent(diskAlpha.Zone)} - if diskAlpha.Zone == "" { - zoneInfo = singleZone{lastComponent(zone)} - } - } - - region := strings.TrimSpace(lastComponent(diskAlpha.Region)) - if region == "" { - region, err = manager.getRegionFromZone(zoneInfo) - if err != nil { - return nil, fmt.Errorf("failed to extract region from zone for %q/%q err=%v", zone, diskName, err) - } - } - - return &GCEDisk{ - ZoneInfo: zoneInfo, - Region: region, - Name: diskAlpha.Name, - Kind: diskAlpha.Kind, - Type: diskAlpha.Type, - }, nil - } - - diskStable, err := manager.gce.service.Disks.Get( - manager.gce.projectID, zone, diskName).Do() - if err != nil { - return nil, err - } - - zoneInfo := singleZone{strings.TrimSpace(lastComponent(diskStable.Zone))} - if zoneInfo.zone == "" { - zoneInfo = singleZone{zone} - } - - region, err := manager.getRegionFromZone(zoneInfo) - if err != nil { - return nil, fmt.Errorf("failed to extract region from zone for %q/%q err=%v", zone, diskName, err) - } - - return &GCEDisk{ - ZoneInfo: zoneInfo, - Region: region, - Name: diskStable.Name, - Kind: diskStable.Kind, - Type: diskStable.Type, - }, nil -} - -func (manager *GCEServiceManager) GetRegionalDisk( - diskName string) (*GCEDisk, error) { - - if manager.gce.AlphaFeatureGate.Enabled(GCEDiskAlphaFeatureGate) { - diskAlpha, err := manager.gce.serviceAlpha.RegionDisks.Get( - manager.gce.projectID, manager.gce.region, diskName).Do() - if err != nil { - return nil, err - } - - zones := sets.NewString() - for _, zoneURI := range diskAlpha.ReplicaZones { - zones.Insert(lastComponent(zoneURI)) - } - - return &GCEDisk{ - ZoneInfo: multiZone{zones}, - Region: lastComponent(diskAlpha.Region), - Name: diskAlpha.Name, - Kind: diskAlpha.Kind, - Type: diskAlpha.Type, - }, nil - } - - return nil, fmt.Errorf("The regional PD feature is only available via the GCE Alpha API. Enable \"GCEDiskAlphaAPI\" in the list of \"alpha-features\" in \"gce.conf\" to use the feature.") -} - -func (manager *GCEServiceManager) DeleteDisk( - zone string, - diskName string) (gceObject, error) { - - if manager.gce.AlphaFeatureGate.Enabled(GCEDiskAlphaFeatureGate) { - return manager.gce.serviceAlpha.Disks.Delete( - manager.gce.projectID, zone, diskName).Do() - } - - return manager.gce.service.Disks.Delete( - manager.gce.projectID, zone, diskName).Do() -} - -func (manager *GCEServiceManager) DeleteRegionalDisk( - diskName string) (gceObject, error) { - if manager.gce.AlphaFeatureGate.Enabled(GCEDiskAlphaFeatureGate) { - return manager.gce.serviceAlpha.RegionDisks.Delete( - manager.gce.projectID, manager.gce.region, diskName).Do() - } - - return nil, fmt.Errorf("DeleteRegionalDisk is a regional PD feature and is only available via the GCE Alpha API. Enable \"GCEDiskAlphaAPI\" in the list of \"alpha-features\" in \"gce.conf\" to use the feature.") -} - -func (manager *GCEServiceManager) WaitForZoneOp( - op gceObject, zone string, mc *metricContext) error { - return manager.gce.waitForZoneOp(op, zone, mc) -} - -func (manager *GCEServiceManager) WaitForRegionalOp( - op gceObject, mc *metricContext) error { - return manager.gce.waitForRegionOp(op, manager.gce.region, mc) -} - -func (manager *GCEServiceManager) getDiskSourceURI(disk *GCEDisk) (string, error) { - getProjectsAPIEndpoint := manager.getProjectsAPIEndpoint() - if manager.gce.AlphaFeatureGate.Enabled(GCEDiskAlphaFeatureGate) { - getProjectsAPIEndpoint = manager.getProjectsAPIEndpointAlpha() - } - - switch zoneInfo := disk.ZoneInfo.(type) { - case singleZone: - if zoneInfo.zone == "" || disk.Region == "" { - // Unexpected, but sanity-check - return "", fmt.Errorf("PD does not have zone/region information: %#v", disk) - } - - return getProjectsAPIEndpoint + fmt.Sprintf( - diskSourceURITemplateSingleZone, - manager.gce.projectID, - zoneInfo.zone, - disk.Name), nil - case multiZone: - if zoneInfo.replicaZones == nil || zoneInfo.replicaZones.Len() <= 0 { - // Unexpected, but sanity-check - return "", fmt.Errorf("PD is regional but does not have any replicaZones specified: %v", disk) - } - return getProjectsAPIEndpoint + fmt.Sprintf( - diskSourceURITemplateRegional, - manager.gce.projectID, - disk.Region, - disk.Name), nil - case nil: - // Unexpected, but sanity-check - return "", fmt.Errorf("PD did not have ZoneInfo: %v", disk) - default: - // Unexpected, but sanity-check - return "", fmt.Errorf("disk.ZoneInfo has unexpected type %T", zoneInfo) - } -} - -func (manager *GCEServiceManager) getDiskTypeURI( - diskRegion string, diskZoneInfo zoneType, diskType string) (string, error) { - getProjectsAPIEndpoint := manager.getProjectsAPIEndpoint() - if manager.gce.AlphaFeatureGate.Enabled(GCEDiskAlphaFeatureGate) { - getProjectsAPIEndpoint = manager.getProjectsAPIEndpointAlpha() - } - - switch zoneInfo := diskZoneInfo.(type) { - case singleZone: - if zoneInfo.zone == "" { - return "", fmt.Errorf("zone is empty: %v", zoneInfo) - } - - return getProjectsAPIEndpoint + fmt.Sprintf( - diskTypeURITemplateSingleZone, - manager.gce.projectID, - zoneInfo.zone, - diskType), nil - case multiZone: - if zoneInfo.replicaZones == nil || zoneInfo.replicaZones.Len() <= 0 { - return "", fmt.Errorf("zoneInfo is regional but does not have any replicaZones specified: %v", zoneInfo) - } - return getProjectsAPIEndpoint + fmt.Sprintf( - diskTypeURITemplateRegional, - manager.gce.projectID, - diskRegion, - diskType), nil - case nil: - return "", fmt.Errorf("zoneInfo nil") - default: - return "", fmt.Errorf("zoneInfo has unexpected type %T", zoneInfo) - } -} - -func (manager *GCEServiceManager) getReplicaZoneURI(zone string) string { - getProjectsAPIEndpoint := manager.getProjectsAPIEndpoint() - if manager.gce.AlphaFeatureGate.Enabled(GCEDiskAlphaFeatureGate) { - getProjectsAPIEndpoint = manager.getProjectsAPIEndpointAlpha() - } - - return getProjectsAPIEndpoint + fmt.Sprintf( - replicaZoneURITemplateSingleZone, - manager.gce.projectID, - zone) -} - -func (manager *GCEServiceManager) getProjectsAPIEndpoint() string { +func (manager *gceServiceManager) getProjectsAPIEndpoint() string { projectsApiEndpoint := gceComputeAPIEndpoint + "projects/" if manager.gce.service != nil { projectsApiEndpoint = manager.gce.service.BasePath @@ -1105,7 +724,7 @@ func (manager *GCEServiceManager) getProjectsAPIEndpoint() string { return projectsApiEndpoint } -func (manager *GCEServiceManager) getProjectsAPIEndpointAlpha() string { +func (manager *gceServiceManager) getProjectsAPIEndpointAlpha() string { projectsApiEndpoint := gceComputeAPIEndpointAlpha + "projects/" if manager.gce.service != nil { projectsApiEndpoint = manager.gce.serviceAlpha.BasePath @@ -1113,37 +732,3 @@ func (manager *GCEServiceManager) getProjectsAPIEndpointAlpha() string { return projectsApiEndpoint } - -func (manager *GCEServiceManager) getRegionFromZone(zoneInfo zoneType) (string, error) { - var zone string - switch zoneInfo := zoneInfo.(type) { - case singleZone: - if zoneInfo.zone == "" { - // Unexpected, but sanity-check - return "", fmt.Errorf("PD is single zone, but zone is not specified: %#v", zoneInfo) - } - - zone = zoneInfo.zone - case multiZone: - if zoneInfo.replicaZones == nil || zoneInfo.replicaZones.Len() <= 0 { - // Unexpected, but sanity-check - return "", fmt.Errorf("PD is regional but does not have any replicaZones specified: %v", zoneInfo) - } - - zone = zoneInfo.replicaZones.UnsortedList()[0] - case nil: - // Unexpected, but sanity-check - return "", fmt.Errorf("zoneInfo is nil") - default: - // Unexpected, but sanity-check - return "", fmt.Errorf("zoneInfo has unexpected type %T", zoneInfo) - } - - region, err := GetGCERegion(zone) - if err != nil { - glog.Warningf("failed to parse GCE region from zone %q: %v", zone, err) - region = manager.gce.region - } - - return region, nil -} diff --git a/pkg/cloudprovider/providers/gce/gce_disks.go b/pkg/cloudprovider/providers/gce/gce_disks.go index ac7f6ee394..f6513598b9 100644 --- a/pkg/cloudprovider/providers/gce/gce_disks.go +++ b/pkg/cloudprovider/providers/gce/gce_disks.go @@ -32,6 +32,8 @@ import ( volumeutil "k8s.io/kubernetes/pkg/volume/util" "github.com/golang/glog" + computealpha "google.golang.org/api/compute/v0.alpha" + compute "google.golang.org/api/compute/v1" "google.golang.org/api/googleapi" ) @@ -52,6 +54,421 @@ const ( replicaZoneURITemplateSingleZone = "%s/zones/%s" // {gce.projectID}/zones/{disk.Zone} ) +type diskServiceManager interface { + // Creates a new persistent disk on GCE with the given disk spec. + CreateDiskOnCloudProvider( + name string, + sizeGb int64, + tagsStr string, + diskType string, + zone string) (gceObject, error) + + // Creates a new regional persistent disk on GCE with the given disk spec. + CreateRegionalDiskOnCloudProvider( + name string, + sizeGb int64, + tagsStr string, + diskType string, + zones sets.String) (gceObject, error) + + // Deletes the persistent disk from GCE with the given diskName. + DeleteDiskOnCloudProvider(zone string, disk string) (gceObject, error) + + // Deletes the regional persistent disk from GCE with the given diskName. + DeleteRegionalDiskOnCloudProvider(diskName string) (gceObject, error) + + // Attach a persistent disk on GCE with the given disk spec to the specified instance. + AttachDiskOnCloudProvider( + disk *GCEDisk, + readWrite string, + instanceZone string, + instanceName string) (gceObject, error) + + // Detach a persistent disk on GCE with the given disk spec from the specified instance. + DetachDiskOnCloudProvider( + instanceZone string, + instanceName string, + devicePath string) (gceObject, error) + + // Gets the persistent disk from GCE with the given diskName. + GetDiskFromCloudProvider(zone string, diskName string) (*GCEDisk, error) + + // Gets the regional persistent disk from GCE with the given diskName. + GetRegionalDiskFromCloudProvider(diskName string) (*GCEDisk, error) + + // Waits until GCE reports the given operation in the given zone as done. + WaitForZoneOp(op gceObject, zone string, mc *metricContext) error + + // Waits until GCE reports the given operation in the given region is done. + WaitForRegionalOp(op gceObject, mc *metricContext) error +} + +type gceServiceManager struct { + gce *GCECloud +} + +func (manager *gceServiceManager) CreateDiskOnCloudProvider( + name string, + sizeGb int64, + tagsStr string, + diskType string, + zone string) (gceObject, error) { + diskTypeURI, err := manager.getDiskTypeURI( + manager.gce.region /* diskRegion */, singleZone{zone}, diskType) + if err != nil { + return nil, err + } + + if manager.gce.AlphaFeatureGate.Enabled(GCEDiskAlphaFeatureGate) { + diskToCreateAlpha := &computealpha.Disk{ + Name: name, + SizeGb: sizeGb, + Description: tagsStr, + Type: diskTypeURI, + } + + return manager.gce.serviceAlpha.Disks.Insert( + manager.gce.projectID, zone, diskToCreateAlpha).Do() + } + + diskToCreateV1 := &compute.Disk{ + Name: name, + SizeGb: sizeGb, + Description: tagsStr, + Type: diskTypeURI, + } + return manager.gce.service.Disks.Insert( + manager.gce.projectID, zone, diskToCreateV1).Do() +} + +func (manager *gceServiceManager) CreateRegionalDiskOnCloudProvider( + name string, + sizeGb int64, + tagsStr string, + diskType string, + replicaZones sets.String) (gceObject, error) { + diskTypeURI, err := manager.getDiskTypeURI( + manager.gce.region /* diskRegion */, multiZone{replicaZones}, diskType) + if err != nil { + return nil, err + } + fullyQualifiedReplicaZones := []string{} + for _, replicaZone := range replicaZones.UnsortedList() { + fullyQualifiedReplicaZones = append( + fullyQualifiedReplicaZones, manager.getReplicaZoneURI(replicaZone)) + } + if manager.gce.AlphaFeatureGate.Enabled(GCEDiskAlphaFeatureGate) { + diskToCreateAlpha := &computealpha.Disk{ + Name: name, + SizeGb: sizeGb, + Description: tagsStr, + Type: diskTypeURI, + ReplicaZones: fullyQualifiedReplicaZones, + } + return manager.gce.serviceAlpha.RegionDisks.Insert( + manager.gce.projectID, manager.gce.region, diskToCreateAlpha).Do() + } + + return nil, fmt.Errorf("The regional PD feature is only available via the GCE Alpha API. Enable \"GCEDiskAlphaAPI\" in the list of \"alpha-features\" in \"gce.conf\" to use the feature.") +} + +func (manager *gceServiceManager) AttachDiskOnCloudProvider( + disk *GCEDisk, + readWrite string, + instanceZone string, + instanceName string) (gceObject, error) { + source, err := manager.getDiskSourceURI(disk) + if err != nil { + return nil, err + } + + if manager.gce.AlphaFeatureGate.Enabled(GCEDiskAlphaFeatureGate) { + attachedDiskAlpha := &computealpha.AttachedDisk{ + DeviceName: disk.Name, + Kind: disk.Kind, + Mode: readWrite, + Source: source, + Type: diskTypePersistent, + } + return manager.gce.serviceAlpha.Instances.AttachDisk( + manager.gce.projectID, instanceZone, instanceName, attachedDiskAlpha).Do() + } + + attachedDiskV1 := &compute.AttachedDisk{ + DeviceName: disk.Name, + Kind: disk.Kind, + Mode: readWrite, + Source: source, + Type: disk.Type, + } + return manager.gce.service.Instances.AttachDisk( + manager.gce.projectID, instanceZone, instanceName, attachedDiskV1).Do() +} + +func (manager *gceServiceManager) DetachDiskOnCloudProvider( + instanceZone string, + instanceName string, + devicePath string) (gceObject, error) { + if manager.gce.AlphaFeatureGate.Enabled(GCEDiskAlphaFeatureGate) { + manager.gce.serviceAlpha.Instances.DetachDisk( + manager.gce.projectID, instanceZone, instanceName, devicePath).Do() + } + + return manager.gce.service.Instances.DetachDisk( + manager.gce.projectID, instanceZone, instanceName, devicePath).Do() +} + +func (manager *gceServiceManager) GetDiskFromCloudProvider( + zone string, + diskName string) (*GCEDisk, error) { + if zone == "" { + return nil, fmt.Errorf("Can not fetch disk %q. Zone is empty.", diskName) + } + + if diskName == "" { + return nil, fmt.Errorf("Can not fetch disk. Zone is specified (%q). But disk name is empty.", zone) + } + + if manager.gce.AlphaFeatureGate.Enabled(GCEDiskAlphaFeatureGate) { + diskAlpha, err := manager.gce.serviceAlpha.Disks.Get( + manager.gce.projectID, zone, diskName).Do() + if err != nil { + return nil, err + } + + var zoneInfo zoneType + if len(diskAlpha.ReplicaZones) > 1 { + zones := sets.NewString() + for _, zoneURI := range diskAlpha.ReplicaZones { + zones.Insert(lastComponent(zoneURI)) + } + zoneInfo = multiZone{zones} + } else { + zoneInfo = singleZone{lastComponent(diskAlpha.Zone)} + if diskAlpha.Zone == "" { + zoneInfo = singleZone{lastComponent(zone)} + } + } + + region := strings.TrimSpace(lastComponent(diskAlpha.Region)) + if region == "" { + region, err = manager.getRegionFromZone(zoneInfo) + if err != nil { + return nil, fmt.Errorf("failed to extract region from zone for %q/%q err=%v", zone, diskName, err) + } + } + + return &GCEDisk{ + ZoneInfo: zoneInfo, + Region: region, + Name: diskAlpha.Name, + Kind: diskAlpha.Kind, + Type: diskAlpha.Type, + }, nil + } + + diskStable, err := manager.gce.service.Disks.Get( + manager.gce.projectID, zone, diskName).Do() + if err != nil { + return nil, err + } + + zoneInfo := singleZone{strings.TrimSpace(lastComponent(diskStable.Zone))} + if zoneInfo.zone == "" { + zoneInfo = singleZone{zone} + } + + region, err := manager.getRegionFromZone(zoneInfo) + if err != nil { + return nil, fmt.Errorf("failed to extract region from zone for %q/%q err=%v", zone, diskName, err) + } + + return &GCEDisk{ + ZoneInfo: zoneInfo, + Region: region, + Name: diskStable.Name, + Kind: diskStable.Kind, + Type: diskStable.Type, + }, nil +} + +func (manager *gceServiceManager) GetRegionalDiskFromCloudProvider( + diskName string) (*GCEDisk, error) { + + if manager.gce.AlphaFeatureGate.Enabled(GCEDiskAlphaFeatureGate) { + diskAlpha, err := manager.gce.serviceAlpha.RegionDisks.Get( + manager.gce.projectID, manager.gce.region, diskName).Do() + if err != nil { + return nil, err + } + + zones := sets.NewString() + for _, zoneURI := range diskAlpha.ReplicaZones { + zones.Insert(lastComponent(zoneURI)) + } + + return &GCEDisk{ + ZoneInfo: multiZone{zones}, + Region: lastComponent(diskAlpha.Region), + Name: diskAlpha.Name, + Kind: diskAlpha.Kind, + Type: diskAlpha.Type, + }, nil + } + + return nil, fmt.Errorf("The regional PD feature is only available via the GCE Alpha API. Enable \"GCEDiskAlphaAPI\" in the list of \"alpha-features\" in \"gce.conf\" to use the feature.") +} + +func (manager *gceServiceManager) DeleteDiskOnCloudProvider( + zone string, + diskName string) (gceObject, error) { + + if manager.gce.AlphaFeatureGate.Enabled(GCEDiskAlphaFeatureGate) { + return manager.gce.serviceAlpha.Disks.Delete( + manager.gce.projectID, zone, diskName).Do() + } + + return manager.gce.service.Disks.Delete( + manager.gce.projectID, zone, diskName).Do() +} + +func (manager *gceServiceManager) DeleteRegionalDiskOnCloudProvider( + diskName string) (gceObject, error) { + if manager.gce.AlphaFeatureGate.Enabled(GCEDiskAlphaFeatureGate) { + return manager.gce.serviceAlpha.RegionDisks.Delete( + manager.gce.projectID, manager.gce.region, diskName).Do() + } + + return nil, fmt.Errorf("DeleteRegionalDiskOnCloudProvider is a regional PD feature and is only available via the GCE Alpha API. Enable \"GCEDiskAlphaAPI\" in the list of \"alpha-features\" in \"gce.conf\" to use the feature.") +} + +func (manager *gceServiceManager) WaitForZoneOp( + op gceObject, zone string, mc *metricContext) error { + return manager.gce.waitForZoneOp(op, zone, mc) +} + +func (manager *gceServiceManager) WaitForRegionalOp( + op gceObject, mc *metricContext) error { + return manager.gce.waitForRegionOp(op, manager.gce.region, mc) +} + +func (manager *gceServiceManager) getDiskSourceURI(disk *GCEDisk) (string, error) { + getProjectsAPIEndpoint := manager.getProjectsAPIEndpoint() + if manager.gce.AlphaFeatureGate.Enabled(GCEDiskAlphaFeatureGate) { + getProjectsAPIEndpoint = manager.getProjectsAPIEndpointAlpha() + } + + switch zoneInfo := disk.ZoneInfo.(type) { + case singleZone: + if zoneInfo.zone == "" || disk.Region == "" { + // Unexpected, but sanity-check + return "", fmt.Errorf("PD does not have zone/region information: %#v", disk) + } + + return getProjectsAPIEndpoint + fmt.Sprintf( + diskSourceURITemplateSingleZone, + manager.gce.projectID, + zoneInfo.zone, + disk.Name), nil + case multiZone: + if zoneInfo.replicaZones == nil || zoneInfo.replicaZones.Len() <= 0 { + // Unexpected, but sanity-check + return "", fmt.Errorf("PD is regional but does not have any replicaZones specified: %v", disk) + } + return getProjectsAPIEndpoint + fmt.Sprintf( + diskSourceURITemplateRegional, + manager.gce.projectID, + disk.Region, + disk.Name), nil + case nil: + // Unexpected, but sanity-check + return "", fmt.Errorf("PD did not have ZoneInfo: %v", disk) + default: + // Unexpected, but sanity-check + return "", fmt.Errorf("disk.ZoneInfo has unexpected type %T", zoneInfo) + } +} + +func (manager *gceServiceManager) getDiskTypeURI( + diskRegion string, diskZoneInfo zoneType, diskType string) (string, error) { + getProjectsAPIEndpoint := manager.getProjectsAPIEndpoint() + if manager.gce.AlphaFeatureGate.Enabled(GCEDiskAlphaFeatureGate) { + getProjectsAPIEndpoint = manager.getProjectsAPIEndpointAlpha() + } + + switch zoneInfo := diskZoneInfo.(type) { + case singleZone: + if zoneInfo.zone == "" { + return "", fmt.Errorf("zone is empty: %v", zoneInfo) + } + + return getProjectsAPIEndpoint + fmt.Sprintf( + diskTypeURITemplateSingleZone, + manager.gce.projectID, + zoneInfo.zone, + diskType), nil + case multiZone: + if zoneInfo.replicaZones == nil || zoneInfo.replicaZones.Len() <= 0 { + return "", fmt.Errorf("zoneInfo is regional but does not have any replicaZones specified: %v", zoneInfo) + } + return getProjectsAPIEndpoint + fmt.Sprintf( + diskTypeURITemplateRegional, + manager.gce.projectID, + diskRegion, + diskType), nil + case nil: + return "", fmt.Errorf("zoneInfo nil") + default: + return "", fmt.Errorf("zoneInfo has unexpected type %T", zoneInfo) + } +} + +func (manager *gceServiceManager) getReplicaZoneURI(zone string) string { + getProjectsAPIEndpoint := manager.getProjectsAPIEndpoint() + if manager.gce.AlphaFeatureGate.Enabled(GCEDiskAlphaFeatureGate) { + getProjectsAPIEndpoint = manager.getProjectsAPIEndpointAlpha() + } + + return getProjectsAPIEndpoint + fmt.Sprintf( + replicaZoneURITemplateSingleZone, + manager.gce.projectID, + zone) +} + +func (manager *gceServiceManager) getRegionFromZone(zoneInfo zoneType) (string, error) { + var zone string + switch zoneInfo := zoneInfo.(type) { + case singleZone: + if zoneInfo.zone == "" { + // Unexpected, but sanity-check + return "", fmt.Errorf("PD is single zone, but zone is not specified: %#v", zoneInfo) + } + + zone = zoneInfo.zone + case multiZone: + if zoneInfo.replicaZones == nil || zoneInfo.replicaZones.Len() <= 0 { + // Unexpected, but sanity-check + return "", fmt.Errorf("PD is regional but does not have any replicaZones specified: %v", zoneInfo) + } + + zone = zoneInfo.replicaZones.UnsortedList()[0] + case nil: + // Unexpected, but sanity-check + return "", fmt.Errorf("zoneInfo is nil") + default: + // Unexpected, but sanity-check + return "", fmt.Errorf("zoneInfo has unexpected type %T", zoneInfo) + } + + region, err := GetGCERegion(zone) + if err != nil { + glog.Warningf("failed to parse GCE region from zone %q: %v", zone, err) + region = manager.gce.region + } + + return region, nil +} + // Disks is interface for manipulation with GCE PDs. type Disks interface { // AttachDisk attaches given disk to the node with the specified NodeName. @@ -175,7 +592,7 @@ func (gce *GCECloud) AttachDisk(diskName string, nodeName types.NodeName, readOn readWrite = "READ_ONLY" } - attachOp, err := gce.manager.AttachDisk( + attachOp, err := gce.manager.AttachDiskOnCloudProvider( disk, readWrite, instance.Zone, instance.Name) if err != nil { @@ -202,7 +619,7 @@ func (gce *GCECloud) DetachDisk(devicePath string, nodeName types.NodeName) erro } mc := newDiskMetricContextZonal("detach", gce.region, inst.Zone) - detachOp, err := gce.manager.DetachDisk(inst.Zone, inst.Name, devicePath) + detachOp, err := gce.manager.DetachDiskOnCloudProvider(inst.Zone, inst.Name, devicePath) if err != nil { return mc.Observe(err) } @@ -292,7 +709,7 @@ func (gce *GCECloud) CreateDisk( mc := newDiskMetricContextZonal("create", gce.region, zone) - createOp, err := gce.manager.CreateDisk( + createOp, err := gce.manager.CreateDiskOnCloudProvider( name, sizeGb, tagsStr, diskType, zone) if isGCEError(err, "alreadyExists") { @@ -341,7 +758,7 @@ func (gce *GCECloud) CreateRegionalDisk( mc := newDiskMetricContextRegional("create", gce.region) - createOp, err := gce.manager.CreateRegionalDisk( + createOp, err := gce.manager.CreateRegionalDiskOnCloudProvider( name, sizeGb, tagsStr, diskType, replicaZones) if isGCEError(err, "alreadyExists") { @@ -469,7 +886,7 @@ func (gce *GCECloud) GetAutoLabelsForPD(name string, zone string) (map[string]st // If not found, returns (nil, nil) func (gce *GCECloud) findDiskByName(diskName string, zone string) (*GCEDisk, error) { mc := newDiskMetricContextZonal("get", gce.region, zone) - disk, err := gce.manager.GetDisk(zone, diskName) + disk, err := gce.manager.GetDiskFromCloudProvider(zone, diskName) if err == nil { return disk, mc.Observe(nil) } @@ -492,7 +909,7 @@ func (gce *GCECloud) getDiskByName(diskName string, zone string) (*GCEDisk, erro // If not found, returns (nil, nil) func (gce *GCECloud) findRegionalDiskByName(diskName string) (*GCEDisk, error) { mc := newDiskMetricContextRegional("get", gce.region) - disk, err := gce.manager.GetRegionalDisk(diskName) + disk, err := gce.manager.GetRegionalDiskFromCloudProvider(diskName) if err == nil { return disk, mc.Observe(nil) } @@ -593,14 +1010,14 @@ func (gce *GCECloud) doDeleteDisk(diskToDelete string) error { switch zoneInfo := disk.ZoneInfo.(type) { case singleZone: mc = newDiskMetricContextZonal("delete", disk.Region, zoneInfo.zone) - deleteOp, err := gce.manager.DeleteDisk(zoneInfo.zone, disk.Name) + deleteOp, err := gce.manager.DeleteDiskOnCloudProvider(zoneInfo.zone, disk.Name) if err != nil { return mc.Observe(err) } return gce.manager.WaitForZoneOp(deleteOp, zoneInfo.zone, mc) case multiZone: mc = newDiskMetricContextRegional("delete", disk.Region) - deleteOp, err := gce.manager.DeleteRegionalDisk(disk.Name) + deleteOp, err := gce.manager.DeleteRegionalDiskOnCloudProvider(disk.Name) if err != nil { return mc.Observe(err) } diff --git a/pkg/cloudprovider/providers/gce/gce_disks_test.go b/pkg/cloudprovider/providers/gce/gce_disks_test.go index 358df9f389..12b465da5d 100644 --- a/pkg/cloudprovider/providers/gce/gce_disks_test.go +++ b/pkg/cloudprovider/providers/gce/gce_disks_test.go @@ -386,8 +386,8 @@ func TestDeleteDisk_SameDiskMultiZone(t *testing.T) { } /* Act */ - // DeleteDisk will call FakeServiceManager.GetDisk() with all zones, - // and FakeServiceManager.GetDisk() always returns a disk, + // DeleteDisk will call FakeServiceManager.GetDiskFromCloudProvider() with all zones, + // and FakeServiceManager.GetDiskFromCloudProvider() always returns a disk, // so DeleteDisk thinks a disk with diskName exists in all zones. err := gce.DeleteDisk(diskName) @@ -662,7 +662,7 @@ func newFakeManager(gceProjectID string, gceRegion string) *FakeServiceManager { * Upon disk creation, disk info is stored in FakeServiceManager * to be used by other tested methods. */ -func (manager *FakeServiceManager) CreateDisk( +func (manager *FakeServiceManager) CreateDiskOnCloudProvider( name string, sizeGb int64, tagsStr string, @@ -716,7 +716,7 @@ func (manager *FakeServiceManager) CreateDisk( * Upon disk creation, disk info is stored in FakeServiceManager * to be used by other tested methods. */ -func (manager *FakeServiceManager) CreateRegionalDisk( +func (manager *FakeServiceManager) CreateRegionalDiskOnCloudProvider( name string, sizeGb int64, tagsStr string, @@ -746,7 +746,7 @@ func (manager *FakeServiceManager) CreateRegionalDisk( } } -func (manager *FakeServiceManager) AttachDisk( +func (manager *FakeServiceManager) AttachDiskOnCloudProvider( disk *GCEDisk, readWrite string, instanceZone string, @@ -767,7 +767,7 @@ func (manager *FakeServiceManager) AttachDisk( } } -func (manager *FakeServiceManager) DetachDisk( +func (manager *FakeServiceManager) DetachDiskOnCloudProvider( instanceZone string, instanceName string, devicePath string) (gceObject, error) { @@ -789,7 +789,7 @@ func (manager *FakeServiceManager) DetachDisk( /** * Gets disk info stored in the FakeServiceManager. */ -func (manager *FakeServiceManager) GetDisk( +func (manager *FakeServiceManager) GetDiskFromCloudProvider( zone string, diskName string) (*GCEDisk, error) { if manager.zonalDisks[zone] == "" { @@ -814,7 +814,7 @@ func (manager *FakeServiceManager) GetDisk( /** * Gets disk info stored in the FakeServiceManager. */ -func (manager *FakeServiceManager) GetRegionalDisk( +func (manager *FakeServiceManager) GetRegionalDiskFromCloudProvider( diskName string) (*GCEDisk, error) { if _, ok := manager.regionalDisks[diskName]; !ok { @@ -839,7 +839,7 @@ func (manager *FakeServiceManager) GetRegionalDisk( /** * Disk info is removed from the FakeServiceManager. */ -func (manager *FakeServiceManager) DeleteDisk( +func (manager *FakeServiceManager) DeleteDiskOnCloudProvider( zone string, disk string) (gceObject, error) { @@ -861,7 +861,7 @@ func (manager *FakeServiceManager) DeleteDisk( } } -func (manager *FakeServiceManager) DeleteRegionalDisk( +func (manager *FakeServiceManager) DeleteRegionalDiskOnCloudProvider( disk string) (gceObject, error) { manager.deleteDiskCalled = true