Refactored disk cloudprovider methods to use generated client; Refactored gce_disks unit tests; Removed unused gce_op.go and associated unit tests.

This commit is contained in:
Cheng Xing 2018-05-23 15:34:56 -07:00
parent 5da925ad4f
commit d33c1e3ba8
8 changed files with 208 additions and 519 deletions

View File

@ -31,7 +31,6 @@ go_library(
"gce_loadbalancer_internal.go", "gce_loadbalancer_internal.go",
"gce_loadbalancer_naming.go", "gce_loadbalancer_naming.go",
"gce_networkendpointgroup.go", "gce_networkendpointgroup.go",
"gce_op.go",
"gce_routes.go", "gce_routes.go",
"gce_securitypolicy.go", "gce_securitypolicy.go",
"gce_targetpool.go", "gce_targetpool.go",

View File

@ -4,6 +4,7 @@ go_library(
name = "go_default_library", name = "go_default_library",
srcs = [ srcs = [
"constants.go", "constants.go",
"context.go",
"doc.go", "doc.go",
"gce_projects.go", "gce_projects.go",
"gen.go", "gen.go",

View File

@ -0,0 +1,31 @@
/*
Copyright 2018 The Kubernetes Authors.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
package cloud
import (
"context"
"time"
)
const (
defaultCallTimeout = 1 * time.Hour
)
// ContextWithCallTimeout returns a context with a default timeout, used for generated client calls.
func ContextWithCallTimeout() (context.Context, context.CancelFunc) {
return context.WithTimeout(context.Background(), defaultCallTimeout)
}

View File

@ -160,22 +160,20 @@ var AllServices = []*ServiceInfo{
Resource: "disks", Resource: "disks",
keyType: Zonal, keyType: Zonal,
serviceType: reflect.TypeOf(&ga.DisksService{}), serviceType: reflect.TypeOf(&ga.DisksService{}),
}, additionalMethods: []string{
{ "Resize",
Object: "Disk", },
Service: "Disks",
Resource: "disks",
version: VersionAlpha,
keyType: Zonal,
serviceType: reflect.TypeOf(&alpha.DisksService{}),
}, },
{ {
Object: "Disk", Object: "Disk",
Service: "RegionDisks", Service: "RegionDisks",
Resource: "disks", Resource: "disks",
version: VersionAlpha, version: VersionBeta,
keyType: Regional, keyType: Regional,
serviceType: reflect.TypeOf(&alpha.DisksService{}), serviceType: reflect.TypeOf(&beta.RegionDisksService{}),
additionalMethods: []string{
"Resize",
},
}, },
{ {
Object: "Firewall", Object: "Firewall",

View File

@ -38,6 +38,8 @@ import (
compute "google.golang.org/api/compute/v1" compute "google.golang.org/api/compute/v1"
"google.golang.org/api/googleapi" "google.golang.org/api/googleapi"
utilfeature "k8s.io/apiserver/pkg/util/feature" utilfeature "k8s.io/apiserver/pkg/util/feature"
"k8s.io/kubernetes/pkg/cloudprovider/providers/gce/cloud"
"k8s.io/kubernetes/pkg/cloudprovider/providers/gce/cloud/meta"
"k8s.io/kubernetes/pkg/features" "k8s.io/kubernetes/pkg/features"
) )
@ -65,7 +67,7 @@ type diskServiceManager interface {
sizeGb int64, sizeGb int64,
tagsStr string, tagsStr string,
diskType string, diskType string,
zone string) (gceObject, error) zone string) error
// Creates a new regional persistent disk on GCE with the given disk spec. // Creates a new regional persistent disk on GCE with the given disk spec.
CreateRegionalDiskOnCloudProvider( CreateRegionalDiskOnCloudProvider(
@ -73,41 +75,35 @@ type diskServiceManager interface {
sizeGb int64, sizeGb int64,
tagsStr string, tagsStr string,
diskType string, diskType string,
zones sets.String) (gceObject, error) zones sets.String) error
// Deletes the persistent disk from GCE with the given diskName. // Deletes the persistent disk from GCE with the given diskName.
DeleteDiskOnCloudProvider(zone string, disk string) (gceObject, error) DeleteDiskOnCloudProvider(zone string, disk string) error
// Deletes the regional persistent disk from GCE with the given diskName. // Deletes the regional persistent disk from GCE with the given diskName.
DeleteRegionalDiskOnCloudProvider(diskName string) (gceObject, error) DeleteRegionalDiskOnCloudProvider(diskName string) error
// Attach a persistent disk on GCE with the given disk spec to the specified instance. // Attach a persistent disk on GCE with the given disk spec to the specified instance.
AttachDiskOnCloudProvider( AttachDiskOnCloudProvider(
disk *GCEDisk, disk *GCEDisk,
readWrite string, readWrite string,
instanceZone string, instanceZone string,
instanceName string) (gceObject, error) instanceName string) error
// Detach a persistent disk on GCE with the given disk spec from the specified instance. // Detach a persistent disk on GCE with the given disk spec from the specified instance.
DetachDiskOnCloudProvider( DetachDiskOnCloudProvider(
instanceZone string, instanceZone string,
instanceName string, instanceName string,
devicePath string) (gceObject, error) devicePath string) error
ResizeDiskOnCloudProvider(disk *GCEDisk, sizeGb int64, zone string) (gceObject, error) ResizeDiskOnCloudProvider(disk *GCEDisk, sizeGb int64, zone string) error
RegionalResizeDiskOnCloudProvider(disk *GCEDisk, sizeGb int64) (gceObject, error) RegionalResizeDiskOnCloudProvider(disk *GCEDisk, sizeGb int64) error
// Gets the persistent disk from GCE with the given diskName. // Gets the persistent disk from GCE with the given diskName.
GetDiskFromCloudProvider(zone string, diskName string) (*GCEDisk, error) GetDiskFromCloudProvider(zone string, diskName string) (*GCEDisk, error)
// Gets the regional persistent disk from GCE with the given diskName. // Gets the regional persistent disk from GCE with the given diskName.
GetRegionalDiskFromCloudProvider(diskName string) (*GCEDisk, error) 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 { type gceServiceManager struct {
@ -121,11 +117,11 @@ func (manager *gceServiceManager) CreateDiskOnCloudProvider(
sizeGb int64, sizeGb int64,
tagsStr string, tagsStr string,
diskType string, diskType string,
zone string) (gceObject, error) { zone string) error {
diskTypeURI, err := manager.getDiskTypeURI( diskTypeURI, err := manager.getDiskTypeURI(
manager.gce.region /* diskRegion */, singleZone{zone}, diskType, false /* useBetaAPI */) manager.gce.region /* diskRegion */, singleZone{zone}, diskType, false /* useBetaAPI */)
if err != nil { if err != nil {
return nil, err return err
} }
diskToCreateV1 := &compute.Disk{ diskToCreateV1 := &compute.Disk{
@ -134,8 +130,10 @@ func (manager *gceServiceManager) CreateDiskOnCloudProvider(
Description: tagsStr, Description: tagsStr,
Type: diskTypeURI, Type: diskTypeURI,
} }
return manager.gce.service.Disks.Insert(
manager.gce.projectID, zone, diskToCreateV1).Do() ctx, cancel := cloud.ContextWithCallTimeout()
defer cancel()
return manager.gce.c.Disks().Insert(ctx, meta.ZonalKey(name, zone), diskToCreateV1)
} }
func (manager *gceServiceManager) CreateRegionalDiskOnCloudProvider( func (manager *gceServiceManager) CreateRegionalDiskOnCloudProvider(
@ -143,42 +141,44 @@ func (manager *gceServiceManager) CreateRegionalDiskOnCloudProvider(
sizeGb int64, sizeGb int64,
tagsStr string, tagsStr string,
diskType string, diskType string,
replicaZones sets.String) (gceObject, error) { replicaZones sets.String) error {
if utilfeature.DefaultFeatureGate.Enabled(features.GCERegionalPersistentDisk) { if !utilfeature.DefaultFeatureGate.Enabled(features.GCERegionalPersistentDisk) {
diskTypeURI, err := manager.getDiskTypeURI( return fmt.Errorf("the regional PD feature is only available with the %s Kubernetes feature gate enabled", features.GCERegionalPersistentDisk)
manager.gce.region /* diskRegion */, multiZone{replicaZones}, diskType, true /* useBetaAPI */)
if err != nil {
return nil, err
}
fullyQualifiedReplicaZones := []string{}
for _, replicaZone := range replicaZones.UnsortedList() {
fullyQualifiedReplicaZones = append(
fullyQualifiedReplicaZones, manager.getReplicaZoneURI(replicaZone, true))
}
diskToCreateBeta := &computebeta.Disk{
Name: name,
SizeGb: sizeGb,
Description: tagsStr,
Type: diskTypeURI,
ReplicaZones: fullyQualifiedReplicaZones,
}
return manager.gce.serviceBeta.RegionDisks.Insert(
manager.gce.projectID, manager.gce.region, diskToCreateBeta).Do()
} }
return nil, fmt.Errorf("the regional PD feature is only available with the %s Kubernetes feature gate enabled", features.GCERegionalPersistentDisk) diskTypeURI, err := manager.getDiskTypeURI(
manager.gce.region /* diskRegion */, multiZone{replicaZones}, diskType, true /* useBetaAPI */)
if err != nil {
return err
}
fullyQualifiedReplicaZones := []string{}
for _, replicaZone := range replicaZones.UnsortedList() {
fullyQualifiedReplicaZones = append(
fullyQualifiedReplicaZones, manager.getReplicaZoneURI(replicaZone, true))
}
diskToCreateBeta := &computebeta.Disk{
Name: name,
SizeGb: sizeGb,
Description: tagsStr,
Type: diskTypeURI,
ReplicaZones: fullyQualifiedReplicaZones,
}
ctx, cancel := cloud.ContextWithCallTimeout()
defer cancel()
return manager.gce.c.BetaRegionDisks().Insert(ctx, meta.RegionalKey(name, manager.gce.region), diskToCreateBeta)
} }
func (manager *gceServiceManager) AttachDiskOnCloudProvider( func (manager *gceServiceManager) AttachDiskOnCloudProvider(
disk *GCEDisk, disk *GCEDisk,
readWrite string, readWrite string,
instanceZone string, instanceZone string,
instanceName string) (gceObject, error) { instanceName string) error {
source, err := manager.getDiskSourceURI(disk) source, err := manager.getDiskSourceURI(disk)
if err != nil { if err != nil {
return nil, err return err
} }
attachedDiskV1 := &compute.AttachedDisk{ attachedDiskV1 := &compute.AttachedDisk{
@ -188,16 +188,19 @@ func (manager *gceServiceManager) AttachDiskOnCloudProvider(
Source: source, Source: source,
Type: diskTypePersistent, Type: diskTypePersistent,
} }
return manager.gce.service.Instances.AttachDisk(
manager.gce.projectID, instanceZone, instanceName, attachedDiskV1).Do() ctx, cancel := cloud.ContextWithCallTimeout()
defer cancel()
return manager.gce.c.Instances().AttachDisk(ctx, meta.ZonalKey(instanceName, instanceZone), attachedDiskV1)
} }
func (manager *gceServiceManager) DetachDiskOnCloudProvider( func (manager *gceServiceManager) DetachDiskOnCloudProvider(
instanceZone string, instanceZone string,
instanceName string, instanceName string,
devicePath string) (gceObject, error) { devicePath string) error {
return manager.gce.service.Instances.DetachDisk( ctx, cancel := cloud.ContextWithCallTimeout()
manager.gce.projectID, instanceZone, instanceName, devicePath).Do() defer cancel()
return manager.gce.c.Instances().DetachDisk(ctx, meta.ZonalKey(instanceName, instanceZone), devicePath)
} }
func (manager *gceServiceManager) GetDiskFromCloudProvider( func (manager *gceServiceManager) GetDiskFromCloudProvider(
@ -211,8 +214,9 @@ func (manager *gceServiceManager) GetDiskFromCloudProvider(
return nil, fmt.Errorf("Can not fetch disk. Zone is specified (%q). But disk name is empty.", zone) return nil, fmt.Errorf("Can not fetch disk. Zone is specified (%q). But disk name is empty.", zone)
} }
diskStable, err := manager.gce.service.Disks.Get( ctx, cancel := cloud.ContextWithCallTimeout()
manager.gce.projectID, zone, diskName).Do() defer cancel()
diskStable, err := manager.gce.c.Disks().Get(ctx, meta.ZonalKey(diskName, zone))
if err != nil { if err != nil {
return nil, err return nil, err
} }
@ -240,56 +244,50 @@ func (manager *gceServiceManager) GetDiskFromCloudProvider(
func (manager *gceServiceManager) GetRegionalDiskFromCloudProvider( func (manager *gceServiceManager) GetRegionalDiskFromCloudProvider(
diskName string) (*GCEDisk, error) { diskName string) (*GCEDisk, error) {
if utilfeature.DefaultFeatureGate.Enabled(features.GCERegionalPersistentDisk) { if !utilfeature.DefaultFeatureGate.Enabled(features.GCERegionalPersistentDisk) {
diskBeta, err := manager.gce.serviceBeta.RegionDisks.Get( return nil, fmt.Errorf("the regional PD feature is only available with the %s Kubernetes feature gate enabled", features.GCERegionalPersistentDisk)
manager.gce.projectID, manager.gce.region, diskName).Do()
if err != nil {
return nil, err
}
zones := sets.NewString()
for _, zoneURI := range diskBeta.ReplicaZones {
zones.Insert(lastComponent(zoneURI))
}
return &GCEDisk{
ZoneInfo: multiZone{zones},
Region: lastComponent(diskBeta.Region),
Name: diskBeta.Name,
Kind: diskBeta.Kind,
Type: diskBeta.Type,
SizeGb: diskBeta.SizeGb,
}, nil
} }
return nil, fmt.Errorf("the regional PD feature is only available with the %s Kubernetes feature gate enabled", features.GCERegionalPersistentDisk) ctx, cancel := cloud.ContextWithCallTimeout()
defer cancel()
diskBeta, err := manager.gce.c.BetaRegionDisks().Get(ctx, meta.RegionalKey(diskName, manager.gce.region))
if err != nil {
return nil, err
}
zones := sets.NewString()
for _, zoneURI := range diskBeta.ReplicaZones {
zones.Insert(lastComponent(zoneURI))
}
return &GCEDisk{
ZoneInfo: multiZone{zones},
Region: lastComponent(diskBeta.Region),
Name: diskBeta.Name,
Kind: diskBeta.Kind,
Type: diskBeta.Type,
SizeGb: diskBeta.SizeGb,
}, nil
} }
func (manager *gceServiceManager) DeleteDiskOnCloudProvider( func (manager *gceServiceManager) DeleteDiskOnCloudProvider(
zone string, zone string,
diskName string) (gceObject, error) { diskName string) error {
return manager.gce.service.Disks.Delete( ctx, cancel := cloud.ContextWithCallTimeout()
manager.gce.projectID, zone, diskName).Do() defer cancel()
return manager.gce.c.Disks().Delete(ctx, meta.ZonalKey(diskName, zone))
} }
func (manager *gceServiceManager) DeleteRegionalDiskOnCloudProvider( func (manager *gceServiceManager) DeleteRegionalDiskOnCloudProvider(
diskName string) (gceObject, error) { diskName string) error {
if utilfeature.DefaultFeatureGate.Enabled(features.GCERegionalPersistentDisk) {
return manager.gce.serviceBeta.RegionDisks.Delete( if !utilfeature.DefaultFeatureGate.Enabled(features.GCERegionalPersistentDisk) {
manager.gce.projectID, manager.gce.region, diskName).Do() return fmt.Errorf("the regional PD feature is only available with the %s Kubernetes feature gate enabled", features.GCERegionalPersistentDisk)
} }
return nil, fmt.Errorf("the regional PD feature is only available with the %s Kubernetes feature gate enabled", features.GCERegionalPersistentDisk) ctx, cancel := cloud.ContextWithCallTimeout()
} defer cancel()
return manager.gce.c.BetaRegionDisks().Delete(ctx, meta.RegionalKey(diskName, manager.gce.region))
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) { func (manager *gceServiceManager) getDiskSourceURI(disk *GCEDisk) (string, error) {
@ -411,21 +409,28 @@ func (manager *gceServiceManager) getRegionFromZone(zoneInfo zoneType) (string,
return region, nil return region, nil
} }
func (manager *gceServiceManager) ResizeDiskOnCloudProvider(disk *GCEDisk, sizeGb int64, zone string) (gceObject, error) { func (manager *gceServiceManager) ResizeDiskOnCloudProvider(disk *GCEDisk, sizeGb int64, zone string) error {
resizeServiceRequest := &compute.DisksResizeRequest{ resizeServiceRequest := &compute.DisksResizeRequest{
SizeGb: sizeGb, SizeGb: sizeGb,
} }
return manager.gce.service.Disks.Resize(manager.gce.projectID, zone, disk.Name, resizeServiceRequest).Do()
ctx, cancel := cloud.ContextWithCallTimeout()
defer cancel()
return manager.gce.c.Disks().Resize(ctx, meta.ZonalKey(disk.Name, zone), resizeServiceRequest)
} }
func (manager *gceServiceManager) RegionalResizeDiskOnCloudProvider(disk *GCEDisk, sizeGb int64) (gceObject, error) { func (manager *gceServiceManager) RegionalResizeDiskOnCloudProvider(disk *GCEDisk, sizeGb int64) error {
if utilfeature.DefaultFeatureGate.Enabled(features.GCERegionalPersistentDisk) { if !utilfeature.DefaultFeatureGate.Enabled(features.GCERegionalPersistentDisk) {
resizeServiceRequest := &computebeta.RegionDisksResizeRequest{ return fmt.Errorf("the regional PD feature is only available with the %s Kubernetes feature gate enabled", features.GCERegionalPersistentDisk)
SizeGb: sizeGb,
}
return manager.gce.serviceBeta.RegionDisks.Resize(manager.gce.projectID, disk.Region, disk.Name, resizeServiceRequest).Do()
} }
return nil, fmt.Errorf("the regional PD feature is only available with the %s Kubernetes feature gate enabled", features.GCERegionalPersistentDisk)
resizeServiceRequest := &computebeta.RegionDisksResizeRequest{
SizeGb: sizeGb,
}
ctx, cancel := cloud.ContextWithCallTimeout()
defer cancel()
return manager.gce.c.BetaRegionDisks().Resize(ctx, meta.RegionalKey(disk.Name, disk.Region), resizeServiceRequest)
} }
// Disks is interface for manipulation with GCE PDs. // Disks is interface for manipulation with GCE PDs.
@ -555,14 +560,7 @@ func (gce *GCECloud) AttachDisk(diskName string, nodeName types.NodeName, readOn
readWrite = "READ_ONLY" readWrite = "READ_ONLY"
} }
attachOp, err := gce.manager.AttachDiskOnCloudProvider( return mc.Observe(gce.manager.AttachDiskOnCloudProvider(disk, readWrite, instance.Zone, instance.Name))
disk, readWrite, instance.Zone, instance.Name)
if err != nil {
return mc.Observe(err)
}
return gce.manager.WaitForZoneOp(attachOp, instance.Zone, mc)
} }
func (gce *GCECloud) DetachDisk(devicePath string, nodeName types.NodeName) error { func (gce *GCECloud) DetachDisk(devicePath string, nodeName types.NodeName) error {
@ -582,12 +580,7 @@ func (gce *GCECloud) DetachDisk(devicePath string, nodeName types.NodeName) erro
} }
mc := newDiskMetricContextZonal("detach", gce.region, inst.Zone) mc := newDiskMetricContextZonal("detach", gce.region, inst.Zone)
detachOp, err := gce.manager.DetachDiskOnCloudProvider(inst.Zone, inst.Name, devicePath) return mc.Observe(gce.manager.DetachDiskOnCloudProvider(inst.Zone, inst.Name, devicePath))
if err != nil {
return mc.Observe(err)
}
return gce.manager.WaitForZoneOp(detachOp, inst.Zone, mc)
} }
func (gce *GCECloud) DiskIsAttached(diskName string, nodeName types.NodeName) (bool, error) { func (gce *GCECloud) DiskIsAttached(diskName string, nodeName types.NodeName) (bool, error) {
@ -675,17 +668,10 @@ func (gce *GCECloud) CreateDisk(
mc := newDiskMetricContextZonal("create", gce.region, zone) mc := newDiskMetricContextZonal("create", gce.region, zone)
createOp, err := gce.manager.CreateDiskOnCloudProvider( err = gce.manager.CreateDiskOnCloudProvider(
name, sizeGb, tagsStr, diskType, zone) name, sizeGb, tagsStr, diskType, zone)
if isGCEError(err, "alreadyExists") { mc.Observe(err)
glog.Warningf("GCE PD %q already exists, reusing", name)
return nil
} else if err != nil {
return mc.Observe(err)
}
err = gce.manager.WaitForZoneOp(createOp, zone, mc)
if isGCEError(err, "alreadyExists") { if isGCEError(err, "alreadyExists") {
glog.Warningf("GCE PD %q already exists, reusing", name) glog.Warningf("GCE PD %q already exists, reusing", name)
return nil return nil
@ -723,17 +709,10 @@ func (gce *GCECloud) CreateRegionalDisk(
mc := newDiskMetricContextRegional("create", gce.region) mc := newDiskMetricContextRegional("create", gce.region)
createOp, err := gce.manager.CreateRegionalDiskOnCloudProvider( err = gce.manager.CreateRegionalDiskOnCloudProvider(
name, sizeGb, tagsStr, diskType, replicaZones) name, sizeGb, tagsStr, diskType, replicaZones)
if isGCEError(err, "alreadyExists") { mc.Observe(err)
glog.Warningf("GCE PD %q already exists, reusing", name)
return nil
} else if err != nil {
return mc.Observe(err)
}
err = gce.manager.WaitForRegionalOp(createOp, mc)
if isGCEError(err, "alreadyExists") { if isGCEError(err, "alreadyExists") {
glog.Warningf("GCE PD %q already exists, reusing", name) glog.Warningf("GCE PD %q already exists, reusing", name)
return nil return nil
@ -786,31 +765,26 @@ func (gce *GCECloud) ResizeDisk(diskToResize string, oldSize resource.Quantity,
switch zoneInfo := disk.ZoneInfo.(type) { switch zoneInfo := disk.ZoneInfo.(type) {
case singleZone: case singleZone:
mc = newDiskMetricContextZonal("resize", disk.Region, zoneInfo.zone) mc = newDiskMetricContextZonal("resize", disk.Region, zoneInfo.zone)
resizeOp, err := gce.manager.ResizeDiskOnCloudProvider(disk, requestGB, zoneInfo.zone) err := gce.manager.ResizeDiskOnCloudProvider(disk, requestGB, zoneInfo.zone)
if err != nil { if err != nil {
return oldSize, mc.Observe(err) return oldSize, mc.Observe(err)
} else {
return newSizeQuant, mc.Observe(err)
} }
waitErr := gce.manager.WaitForZoneOp(resizeOp, zoneInfo.zone, mc)
if waitErr != nil {
return oldSize, waitErr
}
return newSizeQuant, nil
case multiZone: case multiZone:
if utilfeature.DefaultFeatureGate.Enabled(features.GCERegionalPersistentDisk) { if !utilfeature.DefaultFeatureGate.Enabled(features.GCERegionalPersistentDisk) {
mc = newDiskMetricContextRegional("resize", disk.Region) return oldSize, fmt.Errorf("disk.ZoneInfo has unexpected type %T", zoneInfo)
resizeOp, err := gce.manager.RegionalResizeDiskOnCloudProvider(disk, requestGB) }
if err != nil { mc = newDiskMetricContextRegional("resize", disk.Region)
return oldSize, mc.Observe(err) err := gce.manager.RegionalResizeDiskOnCloudProvider(disk, requestGB)
}
waitErr := gce.manager.WaitForRegionalOp(resizeOp, mc) if err != nil {
if waitErr != nil { return oldSize, mc.Observe(err)
return oldSize, waitErr } else {
} return newSizeQuant, mc.Observe(err)
return newSizeQuant, nil
} }
return oldSize, fmt.Errorf("disk.ZoneInfo has unexpected type %T", zoneInfo)
case nil: case nil:
return oldSize, fmt.Errorf("PD has nil ZoneInfo: %v", disk) return oldSize, fmt.Errorf("PD has nil ZoneInfo: %v", disk)
default: default:
@ -1026,21 +1000,14 @@ func (gce *GCECloud) doDeleteDisk(diskToDelete string) error {
switch zoneInfo := disk.ZoneInfo.(type) { switch zoneInfo := disk.ZoneInfo.(type) {
case singleZone: case singleZone:
mc = newDiskMetricContextZonal("delete", disk.Region, zoneInfo.zone) mc = newDiskMetricContextZonal("delete", disk.Region, zoneInfo.zone)
deleteOp, err := gce.manager.DeleteDiskOnCloudProvider(zoneInfo.zone, disk.Name) return mc.Observe(gce.manager.DeleteDiskOnCloudProvider(zoneInfo.zone, disk.Name))
if err != nil {
return mc.Observe(err)
}
return gce.manager.WaitForZoneOp(deleteOp, zoneInfo.zone, mc)
case multiZone: case multiZone:
if utilfeature.DefaultFeatureGate.Enabled(features.GCERegionalPersistentDisk) { if !utilfeature.DefaultFeatureGate.Enabled(features.GCERegionalPersistentDisk) {
mc = newDiskMetricContextRegional("delete", disk.Region) return fmt.Errorf("disk.ZoneInfo has unexpected type %T", zoneInfo)
deleteOp, err := gce.manager.DeleteRegionalDiskOnCloudProvider(disk.Name)
if err != nil {
return mc.Observe(err)
}
return gce.manager.WaitForRegionalOp(deleteOp, mc)
} }
return fmt.Errorf("disk.ZoneInfo has unexpected type %T", zoneInfo)
mc = newDiskMetricContextRegional("delete", disk.Region)
return mc.Observe(gce.manager.DeleteRegionalDiskOnCloudProvider(disk.Name))
case nil: case nil:
return fmt.Errorf("PD has nil ZoneInfo: %v", disk) return fmt.Errorf("PD has nil ZoneInfo: %v", disk)
default: default:

View File

@ -70,9 +70,6 @@ func TestCreateDisk_Basic(t *testing.T) {
if !fakeManager.createDiskCalled { if !fakeManager.createDiskCalled {
t.Error("Never called GCE disk create.") t.Error("Never called GCE disk create.")
} }
if !fakeManager.doesOpMatch {
t.Error("Ops used in WaitForZoneOp does not match what's returned by CreateDisk.")
}
// Partial check of equality between disk description sent to GCE and parameters of method. // Partial check of equality between disk description sent to GCE and parameters of method.
diskToCreate := fakeManager.diskToCreateStable diskToCreate := fakeManager.diskToCreateStable
@ -127,9 +124,6 @@ func TestCreateRegionalDisk_Basic(t *testing.T) {
if !fakeManager.createDiskCalled { if !fakeManager.createDiskCalled {
t.Error("Never called GCE disk create.") t.Error("Never called GCE disk create.")
} }
if !fakeManager.doesOpMatch {
t.Error("Ops used in WaitForZoneOp does not match what's returned by CreateDisk.")
}
// Partial check of equality between disk description sent to GCE and parameters of method. // Partial check of equality between disk description sent to GCE and parameters of method.
diskToCreate := fakeManager.diskToCreateStable diskToCreate := fakeManager.diskToCreateStable
@ -165,7 +159,7 @@ func TestCreateDisk_DiskAlreadyExists(t *testing.T) {
// Inject disk AlreadyExists error. // Inject disk AlreadyExists error.
alreadyExistsError := googleapi.ErrorItem{Reason: "alreadyExists"} alreadyExistsError := googleapi.ErrorItem{Reason: "alreadyExists"}
fakeManager.waitForOpError = &googleapi.Error{ fakeManager.opError = &googleapi.Error{
Errors: []googleapi.ErrorItem{alreadyExistsError}, Errors: []googleapi.ErrorItem{alreadyExistsError},
} }
@ -314,9 +308,6 @@ func TestDeleteDisk_Basic(t *testing.T) {
if !fakeManager.deleteDiskCalled { if !fakeManager.deleteDiskCalled {
t.Error("Never called GCE disk delete.") t.Error("Never called GCE disk delete.")
} }
if !fakeManager.doesOpMatch {
t.Error("Ops used in WaitForZoneOp does not match what's returned by DeleteDisk.")
}
} }
@ -644,16 +635,12 @@ const (
type FakeServiceManager struct { type FakeServiceManager struct {
// Common fields shared among tests // Common fields shared among tests
targetAPI targetClientAPI targetAPI targetClientAPI
gceProjectID string gceProjectID string
gceRegion string gceRegion string
opAlpha *computealpha.Operation // Mocks an operation returned by GCE API calls zonalDisks map[string]string // zone: diskName
opBeta *computebeta.Operation // Mocks an operation returned by GCE API calls regionalDisks map[string]sets.String // diskName: zones
opStable *compute.Operation // Mocks an operation returned by GCE API calls opError error
doesOpMatch bool
zonalDisks map[string]string // zone: diskName
regionalDisks map[string]sets.String // diskName: zones
waitForOpError error // Error to be returned by WaitForZoneOp or WaitForRegionalOp
// Fields for TestCreateDisk // Fields for TestCreateDisk
createDiskCalled bool createDiskCalled bool
@ -684,12 +671,11 @@ func (manager *FakeServiceManager) CreateDiskOnCloudProvider(
sizeGb int64, sizeGb int64,
tagsStr string, tagsStr string,
diskType string, diskType string,
zone string) (gceObject, error) { zone string) error {
manager.createDiskCalled = true manager.createDiskCalled = true
switch t := manager.targetAPI; t { switch t := manager.targetAPI; t {
case targetStable: case targetStable:
manager.opStable = &compute.Operation{}
diskTypeURI := gceComputeAPIEndpoint + "projects/" + fmt.Sprintf(diskTypeURITemplateSingleZone, manager.gceProjectID, zone, diskType) diskTypeURI := gceComputeAPIEndpoint + "projects/" + fmt.Sprintf(diskTypeURITemplateSingleZone, manager.gceProjectID, zone, diskType)
diskToCreateV1 := &compute.Disk{ diskToCreateV1 := &compute.Disk{
Name: name, Name: name,
@ -699,9 +685,8 @@ func (manager *FakeServiceManager) CreateDiskOnCloudProvider(
} }
manager.diskToCreateStable = diskToCreateV1 manager.diskToCreateStable = diskToCreateV1
manager.zonalDisks[zone] = diskToCreateV1.Name manager.zonalDisks[zone] = diskToCreateV1.Name
return manager.opStable, nil return nil
case targetBeta: case targetBeta:
manager.opBeta = &computebeta.Operation{}
diskTypeURI := gceComputeAPIEndpoint + "projects/" + fmt.Sprintf(diskTypeURITemplateSingleZone, manager.gceProjectID, zone, diskType) diskTypeURI := gceComputeAPIEndpoint + "projects/" + fmt.Sprintf(diskTypeURITemplateSingleZone, manager.gceProjectID, zone, diskType)
diskToCreateBeta := &computebeta.Disk{ diskToCreateBeta := &computebeta.Disk{
Name: name, Name: name,
@ -711,9 +696,8 @@ func (manager *FakeServiceManager) CreateDiskOnCloudProvider(
} }
manager.diskToCreateBeta = diskToCreateBeta manager.diskToCreateBeta = diskToCreateBeta
manager.zonalDisks[zone] = diskToCreateBeta.Name manager.zonalDisks[zone] = diskToCreateBeta.Name
return manager.opBeta, nil return nil
case targetAlpha: case targetAlpha:
manager.opAlpha = &computealpha.Operation{}
diskTypeURI := gceComputeAPIEndpointBeta + "projects/" + fmt.Sprintf(diskTypeURITemplateSingleZone, manager.gceProjectID, zone, diskType) diskTypeURI := gceComputeAPIEndpointBeta + "projects/" + fmt.Sprintf(diskTypeURITemplateSingleZone, manager.gceProjectID, zone, diskType)
diskToCreateAlpha := &computealpha.Disk{ diskToCreateAlpha := &computealpha.Disk{
Name: name, Name: name,
@ -723,9 +707,9 @@ func (manager *FakeServiceManager) CreateDiskOnCloudProvider(
} }
manager.diskToCreateAlpha = diskToCreateAlpha manager.diskToCreateAlpha = diskToCreateAlpha
manager.zonalDisks[zone] = diskToCreateAlpha.Name manager.zonalDisks[zone] = diskToCreateAlpha.Name
return manager.opAlpha, nil return nil
default: default:
return nil, fmt.Errorf("unexpected type: %T", t) return fmt.Errorf("unexpected type: %T", t)
} }
} }
@ -738,13 +722,12 @@ func (manager *FakeServiceManager) CreateRegionalDiskOnCloudProvider(
sizeGb int64, sizeGb int64,
tagsStr string, tagsStr string,
diskType string, diskType string,
zones sets.String) (gceObject, error) { zones sets.String) error {
manager.createDiskCalled = true manager.createDiskCalled = true
diskTypeURI := gceComputeAPIEndpointBeta + "projects/" + fmt.Sprintf(diskTypeURITemplateRegional, manager.gceProjectID, manager.gceRegion, diskType) diskTypeURI := gceComputeAPIEndpointBeta + "projects/" + fmt.Sprintf(diskTypeURITemplateRegional, manager.gceProjectID, manager.gceRegion, diskType)
switch t := manager.targetAPI; t { switch t := manager.targetAPI; t {
case targetStable: case targetStable:
manager.opStable = &compute.Operation{}
diskToCreateV1 := &compute.Disk{ diskToCreateV1 := &compute.Disk{
Name: name, Name: name,
SizeGb: sizeGb, SizeGb: sizeGb,
@ -753,13 +736,13 @@ func (manager *FakeServiceManager) CreateRegionalDiskOnCloudProvider(
} }
manager.diskToCreateStable = diskToCreateV1 manager.diskToCreateStable = diskToCreateV1
manager.regionalDisks[diskToCreateV1.Name] = zones manager.regionalDisks[diskToCreateV1.Name] = zones
return manager.opStable, nil return nil
case targetBeta: case targetBeta:
return nil, fmt.Errorf("RegionalDisk CreateDisk op not supported in beta.") return fmt.Errorf("RegionalDisk CreateDisk op not supported in beta.")
case targetAlpha: case targetAlpha:
return nil, fmt.Errorf("RegionalDisk CreateDisk op not supported in alpha.") return fmt.Errorf("RegionalDisk CreateDisk op not supported in alpha.")
default: default:
return nil, fmt.Errorf("unexpected type: %T", t) return fmt.Errorf("unexpected type: %T", t)
} }
} }
@ -767,39 +750,33 @@ func (manager *FakeServiceManager) AttachDiskOnCloudProvider(
disk *GCEDisk, disk *GCEDisk,
readWrite string, readWrite string,
instanceZone string, instanceZone string,
instanceName string) (gceObject, error) { instanceName string) error {
switch t := manager.targetAPI; t { switch t := manager.targetAPI; t {
case targetStable: case targetStable:
manager.opStable = &compute.Operation{} return nil
return manager.opStable, nil
case targetBeta: case targetBeta:
manager.opBeta = &computebeta.Operation{} return nil
return manager.opBeta, nil
case targetAlpha: case targetAlpha:
manager.opAlpha = &computealpha.Operation{} return nil
return manager.opAlpha, nil
default: default:
return nil, fmt.Errorf("unexpected type: %T", t) return fmt.Errorf("unexpected type: %T", t)
} }
} }
func (manager *FakeServiceManager) DetachDiskOnCloudProvider( func (manager *FakeServiceManager) DetachDiskOnCloudProvider(
instanceZone string, instanceZone string,
instanceName string, instanceName string,
devicePath string) (gceObject, error) { devicePath string) error {
switch t := manager.targetAPI; t { switch t := manager.targetAPI; t {
case targetStable: case targetStable:
manager.opStable = &compute.Operation{} return nil
return manager.opStable, nil
case targetBeta: case targetBeta:
manager.opBeta = &computebeta.Operation{} return nil
return manager.opBeta, nil
case targetAlpha: case targetAlpha:
manager.opAlpha = &computealpha.Operation{} return nil
return manager.opAlpha, nil
default: default:
return nil, fmt.Errorf("unexpected type: %T", t) return fmt.Errorf("unexpected type: %T", t)
} }
} }
@ -856,13 +833,13 @@ func (manager *FakeServiceManager) GetRegionalDiskFromCloudProvider(
func (manager *FakeServiceManager) ResizeDiskOnCloudProvider( func (manager *FakeServiceManager) ResizeDiskOnCloudProvider(
disk *GCEDisk, disk *GCEDisk,
size int64, size int64,
zone string) (gceObject, error) { zone string) error {
panic("Not implmented") panic("Not implmented")
} }
func (manager *FakeServiceManager) RegionalResizeDiskOnCloudProvider( func (manager *FakeServiceManager) RegionalResizeDiskOnCloudProvider(
disk *GCEDisk, disk *GCEDisk,
size int64) (gceObject, error) { size int64) error {
panic("Not implemented") panic("Not implemented")
} }
@ -871,91 +848,41 @@ func (manager *FakeServiceManager) RegionalResizeDiskOnCloudProvider(
*/ */
func (manager *FakeServiceManager) DeleteDiskOnCloudProvider( func (manager *FakeServiceManager) DeleteDiskOnCloudProvider(
zone string, zone string,
disk string) (gceObject, error) { disk string) error {
manager.deleteDiskCalled = true manager.deleteDiskCalled = true
delete(manager.zonalDisks, zone) delete(manager.zonalDisks, zone)
switch t := manager.targetAPI; t { switch t := manager.targetAPI; t {
case targetStable: case targetStable:
manager.opStable = &compute.Operation{} return nil
return manager.opStable, nil
case targetBeta: case targetBeta:
manager.opBeta = &computebeta.Operation{} return nil
return manager.opBeta, nil
case targetAlpha: case targetAlpha:
manager.opAlpha = &computealpha.Operation{} return nil
return manager.opAlpha, nil
default: default:
return nil, fmt.Errorf("unexpected type: %T", t) return fmt.Errorf("unexpected type: %T", t)
} }
} }
func (manager *FakeServiceManager) DeleteRegionalDiskOnCloudProvider( func (manager *FakeServiceManager) DeleteRegionalDiskOnCloudProvider(
disk string) (gceObject, error) { disk string) error {
manager.deleteDiskCalled = true manager.deleteDiskCalled = true
delete(manager.regionalDisks, disk) delete(manager.regionalDisks, disk)
switch t := manager.targetAPI; t { switch t := manager.targetAPI; t {
case targetStable: case targetStable:
manager.opStable = &compute.Operation{} return nil
return manager.opStable, nil
case targetBeta: case targetBeta:
manager.opBeta = &computebeta.Operation{} return nil
return manager.opBeta, nil
case targetAlpha: case targetAlpha:
manager.opAlpha = &computealpha.Operation{} return nil
return manager.opAlpha, nil
default: default:
return nil, fmt.Errorf("unexpected type: %T", t) return fmt.Errorf("unexpected type: %T", t)
} }
} }
func (manager *FakeServiceManager) WaitForZoneOp(
op gceObject,
zone string,
mc *metricContext) error {
switch v := op.(type) {
case *computealpha.Operation:
if op.(*computealpha.Operation) == manager.opAlpha {
manager.doesOpMatch = true
}
case *computebeta.Operation:
if op.(*computebeta.Operation) == manager.opBeta {
manager.doesOpMatch = true
}
case *compute.Operation:
if op.(*compute.Operation) == manager.opStable {
manager.doesOpMatch = true
}
default:
return fmt.Errorf("unexpected type: %T", v)
}
return manager.waitForOpError
}
func (manager *FakeServiceManager) WaitForRegionalOp(
op gceObject, mc *metricContext) error {
switch v := op.(type) {
case *computealpha.Operation:
if op.(*computealpha.Operation) == manager.opAlpha {
manager.doesOpMatch = true
}
case *computebeta.Operation:
if op.(*computebeta.Operation) == manager.opBeta {
manager.doesOpMatch = true
}
case *compute.Operation:
if op.(*compute.Operation) == manager.opStable {
manager.doesOpMatch = true
}
default:
return fmt.Errorf("unexpected type: %T", v)
}
return manager.waitForOpError
}
func createNodeZones(zones []string) map[string]sets.String { func createNodeZones(zones []string) map[string]sets.String {
nodeZones := map[string]sets.String{} nodeZones := map[string]sets.String{}
for _, zone := range zones { for _, zone := range zones {

View File

@ -1,180 +0,0 @@
/*
Copyright 2017 The Kubernetes Authors.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
package gce
import (
"encoding/json"
"fmt"
"time"
"k8s.io/apimachinery/pkg/util/wait"
"github.com/golang/glog"
computealpha "google.golang.org/api/compute/v0.alpha"
computebeta "google.golang.org/api/compute/v0.beta"
computev1 "google.golang.org/api/compute/v1"
"google.golang.org/api/googleapi"
)
func (gce *GCECloud) waitForOp(op *computev1.Operation, getOperation func(operationName string) (*computev1.Operation, error), mc *metricContext) error {
if op == nil {
return mc.Observe(fmt.Errorf("operation must not be nil"))
}
if opIsDone(op) {
return getErrorFromOp(op)
}
opStart := time.Now()
opName := op.Name
return wait.Poll(operationPollInterval, operationPollTimeoutDuration, func() (bool, error) {
start := time.Now()
gce.operationPollRateLimiter.Accept()
duration := time.Since(start)
if duration > 5*time.Second {
glog.V(2).Infof("pollOperation: throttled %v for %v", duration, opName)
}
pollOp, err := getOperation(opName)
if err != nil {
glog.Warningf("GCE poll operation %s failed: pollOp: [%v] err: [%v] getErrorFromOp: [%v]",
opName, pollOp, err, getErrorFromOp(pollOp))
}
done := opIsDone(pollOp)
if done {
duration := time.Since(opStart)
if duration > 1*time.Minute {
// Log the JSON. It's cleaner than the %v structure.
enc, err := pollOp.MarshalJSON()
if err != nil {
glog.Warningf("waitForOperation: long operation (%v): %v (failed to encode to JSON: %v)",
duration, pollOp, err)
} else {
glog.V(2).Infof("waitForOperation: long operation (%v): %v",
duration, string(enc))
}
}
}
return done, mc.Observe(getErrorFromOp(pollOp))
})
}
func opIsDone(op *computev1.Operation) bool {
return op != nil && op.Status == "DONE"
}
func getErrorFromOp(op *computev1.Operation) error {
if op != nil && op.Error != nil && len(op.Error.Errors) > 0 {
err := &googleapi.Error{
Code: int(op.HttpErrorStatusCode),
Message: op.Error.Errors[0].Message,
}
glog.Errorf("GCE operation failed: %v", err)
return err
}
return nil
}
func (gce *GCECloud) waitForGlobalOp(op gceObject, mc *metricContext) error {
return gce.waitForGlobalOpInProject(op, gce.ProjectID(), mc)
}
func (gce *GCECloud) waitForRegionOp(op gceObject, region string, mc *metricContext) error {
return gce.waitForRegionOpInProject(op, gce.ProjectID(), region, mc)
}
func (gce *GCECloud) waitForZoneOp(op gceObject, zone string, mc *metricContext) error {
return gce.waitForZoneOpInProject(op, gce.ProjectID(), zone, mc)
}
func (gce *GCECloud) waitForGlobalOpInProject(op gceObject, projectID string, mc *metricContext) error {
switch v := op.(type) {
case *computealpha.Operation:
return gce.waitForOp(convertToV1Operation(op), func(operationName string) (*computev1.Operation, error) {
op, err := gce.serviceAlpha.GlobalOperations.Get(projectID, operationName).Do()
return convertToV1Operation(op), err
}, mc)
case *computebeta.Operation:
return gce.waitForOp(convertToV1Operation(op), func(operationName string) (*computev1.Operation, error) {
op, err := gce.serviceBeta.GlobalOperations.Get(projectID, operationName).Do()
return convertToV1Operation(op), err
}, mc)
case *computev1.Operation:
return gce.waitForOp(op.(*computev1.Operation), func(operationName string) (*computev1.Operation, error) {
return gce.service.GlobalOperations.Get(projectID, operationName).Do()
}, mc)
default:
return fmt.Errorf("unexpected type: %T", v)
}
}
func (gce *GCECloud) waitForRegionOpInProject(op gceObject, projectID, region string, mc *metricContext) error {
switch v := op.(type) {
case *computealpha.Operation:
return gce.waitForOp(convertToV1Operation(op), func(operationName string) (*computev1.Operation, error) {
op, err := gce.serviceAlpha.RegionOperations.Get(projectID, region, operationName).Do()
return convertToV1Operation(op), err
}, mc)
case *computebeta.Operation:
return gce.waitForOp(convertToV1Operation(op), func(operationName string) (*computev1.Operation, error) {
op, err := gce.serviceBeta.RegionOperations.Get(projectID, region, operationName).Do()
return convertToV1Operation(op), err
}, mc)
case *computev1.Operation:
return gce.waitForOp(op.(*computev1.Operation), func(operationName string) (*computev1.Operation, error) {
return gce.service.RegionOperations.Get(projectID, region, operationName).Do()
}, mc)
default:
return fmt.Errorf("unexpected type: %T", v)
}
}
func (gce *GCECloud) waitForZoneOpInProject(op gceObject, projectID, zone string, mc *metricContext) error {
switch v := op.(type) {
case *computealpha.Operation:
return gce.waitForOp(convertToV1Operation(op), func(operationName string) (*computev1.Operation, error) {
op, err := gce.serviceAlpha.ZoneOperations.Get(projectID, zone, operationName).Do()
return convertToV1Operation(op), err
}, mc)
case *computebeta.Operation:
return gce.waitForOp(convertToV1Operation(op), func(operationName string) (*computev1.Operation, error) {
op, err := gce.serviceBeta.ZoneOperations.Get(projectID, zone, operationName).Do()
return convertToV1Operation(op), err
}, mc)
case *computev1.Operation:
return gce.waitForOp(op.(*computev1.Operation), func(operationName string) (*computev1.Operation, error) {
return gce.service.ZoneOperations.Get(projectID, zone, operationName).Do()
}, mc)
default:
return fmt.Errorf("unexpected type: %T", v)
}
}
func convertToV1Operation(object gceObject) *computev1.Operation {
enc, err := object.MarshalJSON()
if err != nil {
panic(fmt.Sprintf("Failed to encode to json: %v", err))
}
var op computev1.Operation
if err := json.Unmarshal(enc, &op); err != nil {
panic(fmt.Sprintf("Failed to convert GCE apiObject %v to v1 operation: %v", object, err))
}
return &op
}

View File

@ -18,16 +18,12 @@ package gce
import ( import (
"context" "context"
"encoding/json"
"reflect" "reflect"
"strings" "strings"
"testing" "testing"
"golang.org/x/oauth2/google" "golang.org/x/oauth2/google"
computealpha "google.golang.org/api/compute/v0.alpha"
computebeta "google.golang.org/api/compute/v0.beta"
computev1 "google.golang.org/api/compute/v1"
"k8s.io/kubernetes/pkg/cloudprovider" "k8s.io/kubernetes/pkg/cloudprovider"
) )
@ -480,56 +476,6 @@ func TestGenerateCloudConfigs(t *testing.T) {
} }
} }
func TestConvertToV1Operation(t *testing.T) {
v1Op := getTestOperation()
enc, _ := v1Op.MarshalJSON()
var op interface{}
var alphaOp computealpha.Operation
var betaOp computebeta.Operation
if err := json.Unmarshal(enc, &alphaOp); err != nil {
t.Errorf("Failed to unmarshal operation: %v", err)
}
if err := json.Unmarshal(enc, &betaOp); err != nil {
t.Errorf("Failed to unmarshal operation: %v", err)
}
op = convertToV1Operation(&alphaOp)
if _, ok := op.(*computev1.Operation); ok {
if !reflect.DeepEqual(op, v1Op) {
t.Errorf("Failed to maintain consistency across conversion")
}
} else {
t.Errorf("Expect output to be type v1 operation, but got %v", op)
}
op = convertToV1Operation(&betaOp)
if _, ok := op.(*computev1.Operation); ok {
if !reflect.DeepEqual(op, v1Op) {
t.Errorf("Failed to maintain consistency across conversion")
}
} else {
t.Errorf("Expect output to be type v1 operation, but got %v", op)
}
}
func getTestOperation() *computev1.Operation {
return &computev1.Operation{
Name: "test",
Description: "test",
Id: uint64(12345),
Error: &computev1.OperationError{
Errors: []*computev1.OperationErrorErrors{
{
Code: "555",
Message: "error",
},
},
},
}
}
func TestNewAlphaFeatureGate(t *testing.T) { func TestNewAlphaFeatureGate(t *testing.T) {
testCases := []struct { testCases := []struct {
alphaFeatures []string alphaFeatures []string