From bd3cc8311056be47156b029e7ee44ad558589b64 Mon Sep 17 00:00:00 2001 From: andrewsykim Date: Thu, 17 Aug 2017 14:46:25 -0400 Subject: [PATCH] cloudprovider.Zones should support external cloud providers --- pkg/cloudprovider/cloud.go | 13 +++++++++++++ pkg/cloudprovider/providers/aws/aws.go | 14 ++++++++++++++ .../providers/azure/azure_zones.go | 16 ++++++++++++++++ pkg/cloudprovider/providers/cloudstack/BUILD | 1 + .../providers/cloudstack/cloudstack.go | 17 +++++++++++++++++ pkg/cloudprovider/providers/fake/fake.go | 16 ++++++++++++++++ pkg/cloudprovider/providers/gce/gce_zones.go | 18 +++++++++++++++++- .../providers/openstack/openstack.go | 14 ++++++++++++++ pkg/cloudprovider/providers/photon/photon.go | 14 ++++++++++++++ .../providers/rackspace/rackspace.go | 14 ++++++++++++++ pkg/controller/cloud/node_controller.go | 17 ++++++++++++++++- 11 files changed, 152 insertions(+), 2 deletions(-) diff --git a/pkg/cloudprovider/cloud.go b/pkg/cloudprovider/cloud.go index 5c4651f7d3a..685adf1c988 100644 --- a/pkg/cloudprovider/cloud.go +++ b/pkg/cloudprovider/cloud.go @@ -184,5 +184,18 @@ type Zone struct { // Zones is an abstract, pluggable interface for zone enumeration. type Zones interface { // GetZone returns the Zone containing the current failure zone and locality region that the program is running in + // In most cases, this method is called from the kubelet querying a local metadata service to aquire its zone. + // For the case of external cloud providers, use GetZoneByProviderID or GetZoneByNodeName since GetZone + // can no longer be called from the kubelets. GetZone() (Zone, error) + + // GetZoneByProviderID returns the Zone containing the current zone and locality region of the node specified by providerId + // This method is particularly used in the context of external cloud providers where node initialization must be down + // outside the kubelets. + GetZoneByProviderID(providerID string) (Zone, error) + + // GetZoneByNodeName returns the Zone containing the current zone and locality region of the node specified by node name + // This method is particularly used in the context of external cloud providers where node initialization must be down + // outside the kubelets. + GetZoneByNodeName(nodeName types.NodeName) (Zone, error) } diff --git a/pkg/cloudprovider/providers/aws/aws.go b/pkg/cloudprovider/providers/aws/aws.go index 217fbcad45f..da24324ab45 100644 --- a/pkg/cloudprovider/providers/aws/aws.go +++ b/pkg/cloudprovider/providers/aws/aws.go @@ -1201,6 +1201,20 @@ func (c *Cloud) GetZone() (cloudprovider.Zone, error) { }, nil } +// GetZoneByProviderID implements Zones.GetZoneByProviderID +// This is particularly useful in external cloud providers where the kubelet +// does not initialize node data. +func (c *Cloud) GetZoneByProviderID(providerID string) (cloudprovider.Zone, error) { + return cloudprovider.Zone{}, errors.New("GetZoneByProviderID not implemented") +} + +// GetZoneByNodeName implements Zones.GetZoneByNodeName +// This is particularly useful in external cloud providers where the kubelet +// does not initialize node data. +func (c *Cloud) GetZoneByNodeName(nodeName types.NodeName) (cloudprovider.Zone, error) { + return cloudprovider.Zone{}, errors.New("GetZoneByNodeName not imeplemented") +} + // Abstraction around AWS Instance Types // There isn't an API to get information for a particular instance type (that I know of) type awsInstanceType struct { diff --git a/pkg/cloudprovider/providers/azure/azure_zones.go b/pkg/cloudprovider/providers/azure/azure_zones.go index ab2dd9e3b27..3d993d414c6 100644 --- a/pkg/cloudprovider/providers/azure/azure_zones.go +++ b/pkg/cloudprovider/providers/azure/azure_zones.go @@ -18,11 +18,13 @@ package azure import ( "encoding/json" + "errors" "io" "io/ioutil" "net/http" "sync" + "k8s.io/apimachinery/pkg/types" "k8s.io/kubernetes/pkg/cloudprovider" ) @@ -55,6 +57,20 @@ func (az *Cloud) GetZone() (cloudprovider.Zone, error) { return zone, nil } +// GetZoneByProviderID implements Zones.GetZoneByProviderID +// This is particularly useful in external cloud providers where the kubelet +// does not initialize node data. +func (az *Cloud) GetZoneByProviderID(providerID string) (cloudprovider.Zone, error) { + return cloudprovider.Zone{}, errors.New("GetZoneByProviderID not implemented") +} + +// GetZoneByNodeName implements Zones.GetZoneByNodeName +// This is particularly useful in external cloud providers where the kubelet +// does not initialize node data. +func (az *Cloud) GetZoneByNodeName(nodeName types.NodeName) (cloudprovider.Zone, error) { + return cloudprovider.Zone{}, errors.New("GetZoneByNodeName not imeplemented") +} + func fetchFaultDomain() (*string, error) { resp, err := http.Get(instanceInfoURL) if err != nil { diff --git a/pkg/cloudprovider/providers/cloudstack/BUILD b/pkg/cloudprovider/providers/cloudstack/BUILD index ecfe91d5417..0302fae33e4 100644 --- a/pkg/cloudprovider/providers/cloudstack/BUILD +++ b/pkg/cloudprovider/providers/cloudstack/BUILD @@ -19,6 +19,7 @@ go_library( "//vendor/github.com/xanzy/go-cloudstack/cloudstack:go_default_library", "//vendor/gopkg.in/gcfg.v1:go_default_library", "//vendor/k8s.io/api/core/v1:go_default_library", + "//vendor/k8s.io/apimachinery/pkg/types:go_default_library", ], ) diff --git a/pkg/cloudprovider/providers/cloudstack/cloudstack.go b/pkg/cloudprovider/providers/cloudstack/cloudstack.go index 1b86d8243ab..4e0bea291e2 100644 --- a/pkg/cloudprovider/providers/cloudstack/cloudstack.go +++ b/pkg/cloudprovider/providers/cloudstack/cloudstack.go @@ -17,12 +17,15 @@ limitations under the License. package cloudstack import ( + "errors" "fmt" "io" "github.com/golang/glog" "github.com/xanzy/go-cloudstack/cloudstack" "gopkg.in/gcfg.v1" + + "k8s.io/apimachinery/pkg/types" "k8s.io/kubernetes/pkg/cloudprovider" "k8s.io/kubernetes/pkg/controller" ) @@ -130,3 +133,17 @@ func (cs *CSCloud) GetZone() (cloudprovider.Zone, error) { glog.V(2).Infof("Current zone is %v", cs.zone) return cloudprovider.Zone{Region: cs.zone}, nil } + +// GetZoneByProviderID implements Zones.GetZoneByProviderID +// This is particularly useful in external cloud providers where the kubelet +// does not initialize node data. +func (cs *CSCloud) GetZoneByProviderID(providerID string) (cloudprovider.Zone, error) { + return cloudprovider.Zone{}, errors.New("GetZoneByProviderID not implemented") +} + +// GetZoneByNodeName implements Zones.GetZoneByNodeName +// This is particularly useful in external cloud providers where the kubelet +// does not initialize node data. +func (cs *CSCloud) GetZoneByNodeName(nodeName types.NodeName) (cloudprovider.Zone, error) { + return cloudprovider.Zone{}, errors.New("GetZoneByNodeName not imeplemented") +} diff --git a/pkg/cloudprovider/providers/fake/fake.go b/pkg/cloudprovider/providers/fake/fake.go index 05c10c146cc..77604cbcd71 100644 --- a/pkg/cloudprovider/providers/fake/fake.go +++ b/pkg/cloudprovider/providers/fake/fake.go @@ -252,6 +252,22 @@ func (f *FakeCloud) GetZone() (cloudprovider.Zone, error) { return f.Zone, f.Err } +// GetZoneByProviderID implements Zones.GetZoneByProviderID +// This is particularly useful in external cloud providers where the kubelet +// does not initialize node data. +func (f *FakeCloud) GetZoneByProviderID(providerID string) (cloudprovider.Zone, error) { + f.addCall("get-zone-by-provider-id") + return f.Zone, f.Err +} + +// GetZoneByNodeName implements Zones.GetZoneByNodeName +// This is particularly useful in external cloud providers where the kubelet +// does not initialize node data. +func (f *FakeCloud) GetZoneByNodeName(nodeName types.NodeName) (cloudprovider.Zone, error) { + f.addCall("get-zone-by-node-name") + return f.Zone, f.Err +} + func (f *FakeCloud) ListRoutes(clusterName string) ([]*cloudprovider.Route, error) { f.Lock.Lock() defer f.Lock.Unlock() diff --git a/pkg/cloudprovider/providers/gce/gce_zones.go b/pkg/cloudprovider/providers/gce/gce_zones.go index 212e740893c..107bb878f54 100644 --- a/pkg/cloudprovider/providers/gce/gce_zones.go +++ b/pkg/cloudprovider/providers/gce/gce_zones.go @@ -17,12 +17,14 @@ limitations under the License. package gce import ( + "errors" "fmt" + "strings" compute "google.golang.org/api/compute/v1" + "k8s.io/apimachinery/pkg/types" "k8s.io/kubernetes/pkg/cloudprovider" - "strings" ) func newZonesMetricContext(request, region string) *metricContext { @@ -37,6 +39,20 @@ func (gce *GCECloud) GetZone() (cloudprovider.Zone, error) { }, nil } +// GetZoneByProviderID implements Zones.GetZoneByProviderID +// This is particularly useful in external cloud providers where the kubelet +// does not initialize node data. +func (gce *GCECloud) GetZoneByProviderID(providerID string) (cloudprovider.Zone, error) { + return cloudprovider.Zone{}, errors.New("GetZoneByProviderID not implemented") +} + +// GetZoneByNodeName implements Zones.GetZoneByNodeName +// This is particularly useful in external cloud providers where the kubelet +// does not initialize node data. +func (gce *GCECloud) GetZoneByNodeName(nodeName types.NodeName) (cloudprovider.Zone, error) { + return cloudprovider.Zone{}, errors.New("GetZoneByNodeName not imeplemented") +} + // ListZonesInRegion returns all zones in a GCP region func (gce *GCECloud) ListZonesInRegion(region string) ([]*compute.Zone, error) { mc := newZonesMetricContext("list", region) diff --git a/pkg/cloudprovider/providers/openstack/openstack.go b/pkg/cloudprovider/providers/openstack/openstack.go index 14f70c297b8..e794afebbb1 100644 --- a/pkg/cloudprovider/providers/openstack/openstack.go +++ b/pkg/cloudprovider/providers/openstack/openstack.go @@ -549,6 +549,20 @@ func (os *OpenStack) GetZone() (cloudprovider.Zone, error) { return zone, nil } +// GetZoneByProviderID implements Zones.GetZoneByProviderID +// This is particularly useful in external cloud providers where the kubelet +// does not initialize node data. +func (os *OpenStack) GetZoneByProviderID(providerID string) (cloudprovider.Zone, error) { + return cloudprovider.Zone{}, errors.New("GetZoneByProviderID not implemented") +} + +// GetZoneByNodeName implements Zones.GetZoneByNodeName +// This is particularly useful in external cloud providers where the kubelet +// does not initialize node data. +func (os *OpenStack) GetZoneByNodeName(nodeName types.NodeName) (cloudprovider.Zone, error) { + return cloudprovider.Zone{}, errors.New("GetZoneByNodeName not imeplemented") +} + func (os *OpenStack) Routes() (cloudprovider.Routes, bool) { glog.V(4).Info("openstack.Routes() called") diff --git a/pkg/cloudprovider/providers/photon/photon.go b/pkg/cloudprovider/providers/photon/photon.go index e9c9fbc87a7..4d18b7a7030 100644 --- a/pkg/cloudprovider/providers/photon/photon.go +++ b/pkg/cloudprovider/providers/photon/photon.go @@ -521,6 +521,20 @@ func (pc *PCCloud) GetZone() (cloudprovider.Zone, error) { return pc.Zone, nil } +// GetZoneByProviderID implements Zones.GetZoneByProviderID +// This is particularly useful in external cloud providers where the kubelet +// does not initialize node data. +func (pc *PCCloud) GetZoneByProviderID(providerID string) (cloudprovider.Zone, error) { + return cloudprovider.Zone{}, errors.New("GetZoneByProviderID not implemented") +} + +// GetZoneByNodeName implements Zones.GetZoneByNodeName +// This is particularly useful in external cloud providers where the kubelet +// does not initialize node data. +func (pc *PCCloud) GetZoneByNodeName(nodeName k8stypes.NodeName) (cloudprovider.Zone, error) { + return cloudprovider.Zone{}, errors.New("GetZoneByNodeName not imeplemented") +} + // Routes returns a false since the interface is not supported for photon controller. func (pc *PCCloud) Routes() (cloudprovider.Routes, bool) { return nil, false diff --git a/pkg/cloudprovider/providers/rackspace/rackspace.go b/pkg/cloudprovider/providers/rackspace/rackspace.go index b087bfa4cea..6b94209d5d7 100644 --- a/pkg/cloudprovider/providers/rackspace/rackspace.go +++ b/pkg/cloudprovider/providers/rackspace/rackspace.go @@ -554,6 +554,20 @@ func (os *Rackspace) GetZone() (cloudprovider.Zone, error) { return cloudprovider.Zone{Region: os.region}, nil } +// GetZoneByProviderID implements Zones.GetZoneByProviderID +// This is particularly useful in external cloud providers where the kubelet +// does not initialize node data. +func (os *Rackspace) GetZoneByProviderID(providerID string) (cloudprovider.Zone, error) { + return cloudprovider.Zone{}, errors.New("GetZoneByProviderID not implemented") +} + +// GetZoneByNodeName implements Zones.GetZoneByNodeName +// This is particularly useful in external cloud providers where the kubelet +// does not initialize node data. +func (os *Rackspace) GetZoneByNodeName(nodeName types.NodeName) (cloudprovider.Zone, error) { + return cloudprovider.Zone{}, errors.New("GetZoneByNodeName not imeplemented") +} + // Create a volume of given size (in GiB) func (rs *Rackspace) CreateVolume(name string, size int, vtype, availability string, tags *map[string]string) (string, string, error) { return "", "", errors.New("unimplemented") diff --git a/pkg/controller/cloud/node_controller.go b/pkg/controller/cloud/node_controller.go index bb470d41bc4..c4ef9eee827 100644 --- a/pkg/controller/cloud/node_controller.go +++ b/pkg/controller/cloud/node_controller.go @@ -321,7 +321,7 @@ func (cnc *CloudNodeController) AddCloudNode(obj interface{}) { } if zones, ok := cnc.cloud.Zones(); ok { - zone, err := zones.GetZone() + zone, err := getZoneByProviderIDOrName(zones, curNode) if err != nil { return fmt.Errorf("failed to get zone from cloud provider: %v", err) } @@ -430,3 +430,18 @@ func getInstanceTypeByProviderIDOrName(instances cloudprovider.Instances, node * } return instanceType, err } + +// getZoneByProviderIDorName will attempt to get the zone of node using its providerID +// then it's name. If both attempts fail, an error is returned +func getZoneByProviderIDOrName(zones cloudprovider.Zones, node *v1.Node) (cloudprovider.Zone, error) { + zone, err := zones.GetZoneByProviderID(node.Spec.ProviderID) + if err != nil { + providerIDErr := err + zone, err = zones.GetZoneByNodeName(types.NodeName(node.Name)) + if err != nil { + return cloudprovider.Zone{}, fmt.Errorf("Zone: Error fetching by providerID: %v Error fetching by NodeName: %v", providerIDErr, err) + } + } + + return zone, nil +}