diff --git a/pkg/cloudprovider/providers/gce/BUILD b/pkg/cloudprovider/providers/gce/BUILD index 401f5934da5..8d972ae3609 100644 --- a/pkg/cloudprovider/providers/gce/BUILD +++ b/pkg/cloudprovider/providers/gce/BUILD @@ -32,6 +32,7 @@ go_library( "gce_loadbalancer_external.go", "gce_loadbalancer_internal.go", "gce_loadbalancer_naming.go", + "gce_networkendpointgroup.go", "gce_op.go", "gce_routes.go", "gce_targetpool.go", diff --git a/pkg/cloudprovider/providers/gce/gce_alpha.go b/pkg/cloudprovider/providers/gce/gce_alpha.go index 292504f7d6e..8b9c98f5816 100644 --- a/pkg/cloudprovider/providers/gce/gce_alpha.go +++ b/pkg/cloudprovider/providers/gce/gce_alpha.go @@ -29,13 +29,16 @@ const ( // tier to use. Currently supports "Standard" and "Premium" (default). AlphaFeatureNetworkTiers = "NetworkTiers" - GCEDiskAlphaFeatureGate = "DiskAlphaAPI" + AlphaFeatureGCEDisk = "DiskAlphaAPI" + + AlphaFeatureNetworkEndpointGroup = "NetworkEndpointGroup" ) // All known alpha features var knownAlphaFeatures = map[string]bool{ - AlphaFeatureNetworkTiers: true, - GCEDiskAlphaFeatureGate: true, + AlphaFeatureNetworkTiers: true, + AlphaFeatureGCEDisk: true, + AlphaFeatureNetworkEndpointGroup: true, } type AlphaFeatureGate struct { @@ -58,3 +61,10 @@ func NewAlphaFeatureGate(features []string) (*AlphaFeatureGate, error) { } return &AlphaFeatureGate{featureMap}, utilerrors.NewAggregate(errList) } + +func (gce *GCECloud) alphaFeatureEnabled(feature string) error { + if !gce.AlphaFeatureGate.Enabled(feature) { + return fmt.Errorf("alpha feature %q is not enabled.", feature) + } + return nil +} diff --git a/pkg/cloudprovider/providers/gce/gce_disks.go b/pkg/cloudprovider/providers/gce/gce_disks.go index f6513598b9d..48dfd18badd 100644 --- a/pkg/cloudprovider/providers/gce/gce_disks.go +++ b/pkg/cloudprovider/providers/gce/gce_disks.go @@ -119,7 +119,7 @@ func (manager *gceServiceManager) CreateDiskOnCloudProvider( return nil, err } - if manager.gce.AlphaFeatureGate.Enabled(GCEDiskAlphaFeatureGate) { + if manager.gce.AlphaFeatureGate.Enabled(AlphaFeatureGCEDisk) { diskToCreateAlpha := &computealpha.Disk{ Name: name, SizeGb: sizeGb, @@ -157,7 +157,7 @@ func (manager *gceServiceManager) CreateRegionalDiskOnCloudProvider( fullyQualifiedReplicaZones = append( fullyQualifiedReplicaZones, manager.getReplicaZoneURI(replicaZone)) } - if manager.gce.AlphaFeatureGate.Enabled(GCEDiskAlphaFeatureGate) { + if manager.gce.AlphaFeatureGate.Enabled(AlphaFeatureGCEDisk) { diskToCreateAlpha := &computealpha.Disk{ Name: name, SizeGb: sizeGb, @@ -182,7 +182,7 @@ func (manager *gceServiceManager) AttachDiskOnCloudProvider( return nil, err } - if manager.gce.AlphaFeatureGate.Enabled(GCEDiskAlphaFeatureGate) { + if manager.gce.AlphaFeatureGate.Enabled(AlphaFeatureGCEDisk) { attachedDiskAlpha := &computealpha.AttachedDisk{ DeviceName: disk.Name, Kind: disk.Kind, @@ -209,7 +209,7 @@ func (manager *gceServiceManager) DetachDiskOnCloudProvider( instanceZone string, instanceName string, devicePath string) (gceObject, error) { - if manager.gce.AlphaFeatureGate.Enabled(GCEDiskAlphaFeatureGate) { + if manager.gce.AlphaFeatureGate.Enabled(AlphaFeatureGCEDisk) { manager.gce.serviceAlpha.Instances.DetachDisk( manager.gce.projectID, instanceZone, instanceName, devicePath).Do() } @@ -229,7 +229,7 @@ func (manager *gceServiceManager) GetDiskFromCloudProvider( return nil, fmt.Errorf("Can not fetch disk. Zone is specified (%q). But disk name is empty.", zone) } - if manager.gce.AlphaFeatureGate.Enabled(GCEDiskAlphaFeatureGate) { + if manager.gce.AlphaFeatureGate.Enabled(AlphaFeatureGCEDisk) { diskAlpha, err := manager.gce.serviceAlpha.Disks.Get( manager.gce.projectID, zone, diskName).Do() if err != nil { @@ -295,7 +295,7 @@ func (manager *gceServiceManager) GetDiskFromCloudProvider( func (manager *gceServiceManager) GetRegionalDiskFromCloudProvider( diskName string) (*GCEDisk, error) { - if manager.gce.AlphaFeatureGate.Enabled(GCEDiskAlphaFeatureGate) { + if manager.gce.AlphaFeatureGate.Enabled(AlphaFeatureGCEDisk) { diskAlpha, err := manager.gce.serviceAlpha.RegionDisks.Get( manager.gce.projectID, manager.gce.region, diskName).Do() if err != nil { @@ -323,7 +323,7 @@ func (manager *gceServiceManager) DeleteDiskOnCloudProvider( zone string, diskName string) (gceObject, error) { - if manager.gce.AlphaFeatureGate.Enabled(GCEDiskAlphaFeatureGate) { + if manager.gce.AlphaFeatureGate.Enabled(AlphaFeatureGCEDisk) { return manager.gce.serviceAlpha.Disks.Delete( manager.gce.projectID, zone, diskName).Do() } @@ -334,7 +334,7 @@ func (manager *gceServiceManager) DeleteDiskOnCloudProvider( func (manager *gceServiceManager) DeleteRegionalDiskOnCloudProvider( diskName string) (gceObject, error) { - if manager.gce.AlphaFeatureGate.Enabled(GCEDiskAlphaFeatureGate) { + if manager.gce.AlphaFeatureGate.Enabled(AlphaFeatureGCEDisk) { return manager.gce.serviceAlpha.RegionDisks.Delete( manager.gce.projectID, manager.gce.region, diskName).Do() } @@ -354,7 +354,7 @@ func (manager *gceServiceManager) WaitForRegionalOp( func (manager *gceServiceManager) getDiskSourceURI(disk *GCEDisk) (string, error) { getProjectsAPIEndpoint := manager.getProjectsAPIEndpoint() - if manager.gce.AlphaFeatureGate.Enabled(GCEDiskAlphaFeatureGate) { + if manager.gce.AlphaFeatureGate.Enabled(AlphaFeatureGCEDisk) { getProjectsAPIEndpoint = manager.getProjectsAPIEndpointAlpha() } @@ -392,7 +392,7 @@ func (manager *gceServiceManager) getDiskSourceURI(disk *GCEDisk) (string, error func (manager *gceServiceManager) getDiskTypeURI( diskRegion string, diskZoneInfo zoneType, diskType string) (string, error) { getProjectsAPIEndpoint := manager.getProjectsAPIEndpoint() - if manager.gce.AlphaFeatureGate.Enabled(GCEDiskAlphaFeatureGate) { + if manager.gce.AlphaFeatureGate.Enabled(AlphaFeatureGCEDisk) { getProjectsAPIEndpoint = manager.getProjectsAPIEndpointAlpha() } @@ -425,7 +425,7 @@ func (manager *gceServiceManager) getDiskTypeURI( func (manager *gceServiceManager) getReplicaZoneURI(zone string) string { getProjectsAPIEndpoint := manager.getProjectsAPIEndpoint() - if manager.gce.AlphaFeatureGate.Enabled(GCEDiskAlphaFeatureGate) { + if manager.gce.AlphaFeatureGate.Enabled(AlphaFeatureGCEDisk) { getProjectsAPIEndpoint = manager.getProjectsAPIEndpointAlpha() } @@ -569,7 +569,7 @@ func (gce *GCECloud) AttachDisk(diskName string, nodeName types.NodeName, readOn // Try fetching as regional PD var disk *GCEDisk var mc *metricContext - if gce.AlphaFeatureGate.Enabled(GCEDiskAlphaFeatureGate) { + if gce.AlphaFeatureGate.Enabled(AlphaFeatureGCEDisk) { disk, err = gce.getRegionalDiskByName(diskName) if err != nil { glog.V(5).Infof("Could not find regional PD named %q to Attach. Will look for a zonal PD", diskName) @@ -932,7 +932,7 @@ func (gce *GCECloud) getRegionalDiskByName(diskName string) (*GCEDisk, error) { // Prefer getDiskByName, if the zone can be established // Return cloudprovider.DiskNotFound if the given disk cannot be found in any zone func (gce *GCECloud) GetDiskByNameUnknownZone(diskName string) (*GCEDisk, error) { - if gce.AlphaFeatureGate.Enabled(GCEDiskAlphaFeatureGate) { + if gce.AlphaFeatureGate.Enabled(AlphaFeatureGCEDisk) { regionalDisk, err := gce.getRegionalDiskByName(diskName) if err == nil { return regionalDisk, err diff --git a/pkg/cloudprovider/providers/gce/gce_disks_test.go b/pkg/cloudprovider/providers/gce/gce_disks_test.go index 12b465da5d1..66a13e2ee82 100644 --- a/pkg/cloudprovider/providers/gce/gce_disks_test.go +++ b/pkg/cloudprovider/providers/gce/gce_disks_test.go @@ -96,7 +96,7 @@ func TestCreateRegionalDisk_Basic(t *testing.T) { gceProjectId := "test-project" gceRegion := "fake-region" fakeManager := newFakeManager(gceProjectId, gceRegion) - alphaFeatureGate, featureGateErr := NewAlphaFeatureGate([]string{GCEDiskAlphaFeatureGate}) + alphaFeatureGate, featureGateErr := NewAlphaFeatureGate([]string{AlphaFeatureGCEDisk}) if featureGateErr != nil { t.Error(featureGateErr) } diff --git a/pkg/cloudprovider/providers/gce/gce_networkendpointgroup.go b/pkg/cloudprovider/providers/gce/gce_networkendpointgroup.go new file mode 100644 index 00000000000..f8d3e80f4f3 --- /dev/null +++ b/pkg/cloudprovider/providers/gce/gce_networkendpointgroup.go @@ -0,0 +1,148 @@ +/* +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 ( + "context" + computealpha "google.golang.org/api/compute/v0.alpha" + "strings" +) + +const ( + NEGLoadBalancerType = "LOAD_BALANCING" + NEGIPPortNetworkEndpointType = "GCE_VM_IP_PORT" +) + +func newNetworkEndpointGroupMetricContext(request string, zone string) *metricContext { + return newGenericMetricContext("networkendpointgroup_", request, unusedMetricLabel, zone, computeAlphaVersion) +} + +func (gce *GCECloud) GetNetworkEndpointGroup(name string, zone string) (*computealpha.NetworkEndpointGroup, error) { + if err := gce.alphaFeatureEnabled(AlphaFeatureNetworkEndpointGroup); err != nil { + return nil, err + } + mc := newNetworkEndpointGroupMetricContext("get", zone) + v, err := gce.serviceAlpha.NetworkEndpointGroups.Get(gce.ProjectID(), zone, name).Do() + return v, mc.Observe(err) +} + +func (gce *GCECloud) ListNetworkEndpointGroup(zone string) ([]*computealpha.NetworkEndpointGroup, error) { + if err := gce.alphaFeatureEnabled(AlphaFeatureNetworkEndpointGroup); err != nil { + return nil, err + } + mc := newNetworkEndpointGroupMetricContext("list", zone) + networkEndpointGroups := []*computealpha.NetworkEndpointGroup{} + err := gce.serviceAlpha.NetworkEndpointGroups.List(gce.ProjectID(), zone).Pages(context.Background(), func(res *computealpha.NetworkEndpointGroupList) error { + networkEndpointGroups = append(networkEndpointGroups, res.Items...) + return nil + }) + return networkEndpointGroups, mc.Observe(err) +} + +func (gce *GCECloud) AggregatedListNetworkEndpointGroup() (map[string][]*computealpha.NetworkEndpointGroup, error) { + if err := gce.alphaFeatureEnabled(AlphaFeatureNetworkEndpointGroup); err != nil { + return nil, err + } + mc := newNetworkEndpointGroupMetricContext("aggregated_list", "") + zoneNetworkEndpointGroupMap := map[string][]*computealpha.NetworkEndpointGroup{} + err := gce.serviceAlpha.NetworkEndpointGroups.AggregatedList(gce.ProjectID()).Pages(context.Background(), func(res *computealpha.NetworkEndpointGroupAggregatedList) error { + for key, negs := range res.Items { + if len(negs.NetworkEndpointGroups) == 0 { + continue + } + // key has the format of "zones/${zone_name}" + zone := strings.Split(key, "/")[1] + if _, ok := zoneNetworkEndpointGroupMap[zone]; !ok { + zoneNetworkEndpointGroupMap[zone] = []*computealpha.NetworkEndpointGroup{} + } + zoneNetworkEndpointGroupMap[zone] = append(zoneNetworkEndpointGroupMap[zone], negs.NetworkEndpointGroups...) + } + return nil + }) + return zoneNetworkEndpointGroupMap, mc.Observe(err) +} + +func (gce *GCECloud) CreateNetworkEndpointGroup(neg *computealpha.NetworkEndpointGroup, zone string) error { + if err := gce.alphaFeatureEnabled(AlphaFeatureNetworkEndpointGroup); err != nil { + return err + } + mc := newNetworkEndpointGroupMetricContext("create", zone) + op, err := gce.serviceAlpha.NetworkEndpointGroups.Insert(gce.ProjectID(), zone, neg).Do() + if err != nil { + return mc.Observe(err) + } + return gce.waitForZoneOp(op, zone, mc) +} + +func (gce *GCECloud) DeleteNetworkEndpointGroup(name string, zone string) error { + if err := gce.alphaFeatureEnabled(AlphaFeatureNetworkEndpointGroup); err != nil { + return err + } + mc := newNetworkEndpointGroupMetricContext("delete", zone) + op, err := gce.serviceAlpha.NetworkEndpointGroups.Delete(gce.ProjectID(), zone, name).Do() + if err != nil { + return mc.Observe(err) + } + return gce.waitForZoneOp(op, zone, mc) +} + +func (gce *GCECloud) AttachNetworkEndpoints(name, zone string, endpoints []*computealpha.NetworkEndpoint) error { + if err := gce.alphaFeatureEnabled(AlphaFeatureNetworkEndpointGroup); err != nil { + return err + } + mc := newNetworkEndpointGroupMetricContext("attach", zone) + op, err := gce.serviceAlpha.NetworkEndpointGroups.AttachNetworkEndpoints(gce.ProjectID(), zone, name, &computealpha.NetworkEndpointGroupsAttachEndpointsRequest{ + NetworkEndpoints: endpoints, + }).Do() + if err != nil { + return mc.Observe(err) + } + return gce.waitForZoneOp(op, zone, mc) +} + +func (gce *GCECloud) DetachNetworkEndpoints(name, zone string, endpoints []*computealpha.NetworkEndpoint) error { + if err := gce.alphaFeatureEnabled(AlphaFeatureNetworkEndpointGroup); err != nil { + return err + } + mc := newNetworkEndpointGroupMetricContext("detach", zone) + op, err := gce.serviceAlpha.NetworkEndpointGroups.DetachNetworkEndpoints(gce.ProjectID(), zone, name, &computealpha.NetworkEndpointGroupsDetachEndpointsRequest{ + NetworkEndpoints: endpoints, + }).Do() + if err != nil { + return mc.Observe(err) + } + return gce.waitForZoneOp(op, zone, mc) +} + +func (gce *GCECloud) ListNetworkEndpoints(name, zone string, showHealthStatus bool) ([]*computealpha.NetworkEndpointWithHealthStatus, error) { + if err := gce.alphaFeatureEnabled(AlphaFeatureNetworkEndpointGroup); err != nil { + return nil, err + } + healthStatus := "SKIP" + if showHealthStatus { + healthStatus = "SHOW" + } + mc := newNetworkEndpointGroupMetricContext("list_networkendpoints", zone) + networkEndpoints := []*computealpha.NetworkEndpointWithHealthStatus{} + err := gce.serviceAlpha.NetworkEndpointGroups.ListNetworkEndpoints(gce.ProjectID(), zone, name, &computealpha.NetworkEndpointGroupsListEndpointsRequest{ + HealthStatus: healthStatus, + }).Pages(context.Background(), func(res *computealpha.NetworkEndpointGroupsListNetworkEndpoints) error { + networkEndpoints = append(networkEndpoints, res.Items...) + return nil + }) + return networkEndpoints, mc.Observe(err) +} diff --git a/pkg/cloudprovider/providers/gce/metrics.go b/pkg/cloudprovider/providers/gce/metrics.go index 649bd11b132..22f1c2dde6f 100644 --- a/pkg/cloudprovider/providers/gce/metrics.go +++ b/pkg/cloudprovider/providers/gce/metrics.go @@ -67,6 +67,12 @@ func (mc *metricContext) Observe(err error) error { } func newGenericMetricContext(prefix, request, region, zone, version string) *metricContext { + if len(zone) == 0 { + zone = unusedMetricLabel + } + if len(region) == 0 { + region = unusedMetricLabel + } return &metricContext{ start: time.Now(), attributes: []string{prefix + "_" + request, region, zone, version},