From 1f4dc5559d71ffca74bcfdf10b0b593005f61dd4 Mon Sep 17 00:00:00 2001 From: Bowei Du Date: Wed, 23 Aug 2017 16:27:59 -0700 Subject: [PATCH] Add AddAliasToInstance() to gce cloud provider - Adds AddAliasToInstance() to the GCE cloud provider. - Adds field "secondary-range-name" to the gce.conf configuration file. ```release-note NONE ``` --- pkg/cloudprovider/providers/gce/gce.go | 35 ++++++++++++----- .../providers/gce/gce_instances.go | 39 +++++++++++++++++++ pkg/cloudprovider/providers/gce/gce_test.go | 9 ++++- 3 files changed, 72 insertions(+), 11 deletions(-) diff --git a/pkg/cloudprovider/providers/gce/gce.go b/pkg/cloudprovider/providers/gce/gce.go index aa03e5afb5e..c11c961d2d0 100644 --- a/pkg/cloudprovider/providers/gce/gce.go +++ b/pkg/cloudprovider/providers/gce/gce.go @@ -103,6 +103,7 @@ type GCECloud struct { managedZones []string // List of zones we are spanning (for multi-AZ clusters, primarily when running on master) networkURL string subnetworkURL string + secondaryRangeName string networkProjectID string onXPN bool nodeTags []string // List of tags to use on firewall rules for load balancers @@ -143,21 +144,30 @@ type GCEServiceManager struct { gce *GCECloud } +// ConfigFile is the struct used to parse the /etc/gce.conf configuration file. type ConfigFile struct { Global struct { - TokenURL string `gcfg:"token-url"` - TokenBody string `gcfg:"token-body"` - ProjectID string `gcfg:"project-id"` - NetworkName string `gcfg:"network-name"` - SubnetworkName string `gcfg:"subnetwork-name"` + TokenURL string `gcfg:"token-url"` + TokenBody string `gcfg:"token-body"` + ProjectID string `gcfg:"project-id"` + NetworkName string `gcfg:"network-name"` + SubnetworkName string `gcfg:"subnetwork-name"` + // SecondaryRangeName is the name of the secondary range to allocate IP + // aliases. The secondary range must be present on the subnetwork the + // cluster is attached to. + SecondaryRangeName string `gcfg:"secondary-range-name"` NodeTags []string `gcfg:"node-tags"` NodeInstancePrefix string `gcfg:"node-instance-prefix"` Multizone bool `gcfg:"multizone"` - // Specifying ApiEndpoint will override the default GCE compute API endpoint. + // ApiEndpoint is the GCE compute API endpoint to use. If this is blank, + // then the default endpoint is used. ApiEndpoint string `gcfg:"api-endpoint"` - LocalZone string `gcfg:"local-zone"` - // Possible values: List of api names separated by comma. Default to none. - // For example: MyFeatureFlag + // LocalZone specifies the GCE zone that gce cloud client instance is + // located in (i.e. where the controller will be running). If this is + // blank, then the local zone will be discovered via the metadata server. + LocalZone string `gcfg:"local-zone"` + // AlphaFeatures is a list of API flags to be enabled. Defaults to none. + // Example API name format: "MyFeatureFlag" AlphaFeatures []string `gcfg:"alpha-features"` } } @@ -171,6 +181,7 @@ type CloudConfig struct { ManagedZones []string NetworkURL string SubnetworkURL string + SecondaryRangeName string NodeTags []string NodeInstancePrefix string TokenSource oauth2.TokenSource @@ -313,6 +324,11 @@ func generateCloudConfig(configFile *ConfigFile) (cloudConfig *CloudConfig, err cloudConfig.SubnetworkURL = gceSubnetworkURL(cloudConfig.ApiEndpoint, cloudConfig.ProjectID, cloudConfig.Region, configFile.Global.SubnetworkName) } } + + if configFile != nil { + cloudConfig.SecondaryRangeName = configFile.Global.SecondaryRangeName + } + return cloudConfig, err } @@ -409,6 +425,7 @@ func CreateGCECloud(config *CloudConfig) (*GCECloud, error) { managedZones: config.ManagedZones, networkURL: config.NetworkURL, subnetworkURL: config.SubnetworkURL, + secondaryRangeName: config.SecondaryRangeName, nodeTags: config.NodeTags, nodeInstancePrefix: config.NodeInstancePrefix, useMetadataServer: config.UseMetadataServer, diff --git a/pkg/cloudprovider/providers/gce/gce_instances.go b/pkg/cloudprovider/providers/gce/gce_instances.go index 8d997ae9ed4..3597dab9575 100644 --- a/pkg/cloudprovider/providers/gce/gce_instances.go +++ b/pkg/cloudprovider/providers/gce/gce_instances.go @@ -18,6 +18,7 @@ package gce import ( "fmt" + "net" "net/http" "strconv" "strings" @@ -25,6 +26,7 @@ import ( "cloud.google.com/go/compute/metadata" "github.com/golang/glog" + computealpha "google.golang.org/api/compute/v0.alpha" computebeta "google.golang.org/api/compute/v0.beta" compute "google.golang.org/api/compute/v1" @@ -318,6 +320,43 @@ func (gce *GCECloud) AliasRanges(nodeName types.NodeName) (cidrs []string, err e return } +// AddAliasToInstance adds an alias to the given instance from the named +// secondary range. +func (gce *GCECloud) AddAliasToInstance(nodeName types.NodeName, alias *net.IPNet) error { + + v1instance, err := gce.getInstanceByName(mapNodeNameToInstanceName(nodeName)) + if err != nil { + return err + } + instance, err := gce.serviceAlpha.Instances.Get(gce.projectID, v1instance.Zone, v1instance.Name).Do() + if err != nil { + return err + } + + switch len(instance.NetworkInterfaces) { + case 0: + return fmt.Errorf("Instance %q has no network interfaces", nodeName) + case 1: + default: + glog.Warningf("Instance %q has more than one network interface, using only the first (%v)", + nodeName, instance.NetworkInterfaces) + } + + iface := instance.NetworkInterfaces[0] + iface.AliasIpRanges = append(iface.AliasIpRanges, &computealpha.AliasIpRange{ + IpCidrRange: alias.String(), + SubnetworkRangeName: gce.secondaryRangeName, + }) + + mc := newInstancesMetricContext("addalias", v1instance.Zone) + op, err := gce.serviceAlpha.Instances.UpdateNetworkInterface( + gce.projectID, instance.Zone, instance.Name, iface.Name, iface).Do() + if err != nil { + return mc.Observe(err) + } + return gce.waitForZoneOp(op, v1instance.Zone, mc) +} + // Gets the named instances, returning cloudprovider.InstanceNotFound if any instance is not found func (gce *GCECloud) getInstancesByNames(names []string) ([]*gceInstance, error) { instances := make(map[string]*gceInstance) diff --git a/pkg/cloudprovider/providers/gce/gce_test.go b/pkg/cloudprovider/providers/gce/gce_test.go index f3add41c49b..d05296fec01 100644 --- a/pkg/cloudprovider/providers/gce/gce_test.go +++ b/pkg/cloudprovider/providers/gce/gce_test.go @@ -18,11 +18,12 @@ package gce import ( "encoding/json" - "golang.org/x/oauth2/google" "reflect" "strings" "testing" + "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" @@ -268,6 +269,7 @@ type generateConfigParams struct { ProjectID string NetworkName string SubnetworkName string + SecondaryRangeName string NodeTags []string NodeInstancePrefix string Multizone bool @@ -283,6 +285,7 @@ func newGenerateConfigDefaults() *generateConfigParams { ProjectID: "project-id", NetworkName: "network-name", SubnetworkName: "", + SecondaryRangeName: "", NodeTags: []string{"node-tag"}, NodeInstancePrefix: "node-prefix", Multizone: false, @@ -452,6 +455,7 @@ func TestGenerateCloudConfigs(t *testing.T) { ProjectID string `gcfg:"project-id"` NetworkName string `gcfg:"network-name"` SubnetworkName string `gcfg:"subnetwork-name"` + SecondaryRangeName string `gcfg:"secondary-range-name"` NodeTags []string `gcfg:"node-tags"` NodeInstancePrefix string `gcfg:"node-instance-prefix"` Multizone bool `gcfg:"multizone"` @@ -464,6 +468,7 @@ func TestGenerateCloudConfigs(t *testing.T) { ProjectID: config.ProjectID, NetworkName: config.NetworkName, SubnetworkName: config.SubnetworkName, + SecondaryRangeName: config.SecondaryRangeName, NodeTags: config.NodeTags, NodeInstancePrefix: config.NodeInstancePrefix, Multizone: config.Multizone, @@ -477,7 +482,7 @@ func TestGenerateCloudConfigs(t *testing.T) { } if !reflect.DeepEqual(cloudConfig, tc.cloudConfig) { - t.Errorf("Expecting cloud config: %v, but got %v", tc.cloudConfig, cloudConfig) + t.Errorf("Got %v, want %v", cloudConfig, tc.cloudConfig) } } }