Merge pull request #50858 from andrewsykim/49308

Automatic merge from submit-queue (batch tested with PRs 51148, 50816, 49741, 50858, 51223)

cloudprovider.Zones should support external cloud providers

**What this PR does / why we need it**:
Provides methods in cloudprovider.Zones that allows external cloud providers to set the correct zone labels to nodes. Part of https://github.com/kubernetes/kubernetes/issues/48690

**Which issue this PR fixes** *(optional, in `fixes #<issue number>(, fixes #<issue_number>, ...)` format, will close that issue when PR gets merged)*: 
fixes https://github.com/kubernetes/kubernetes/issues/49308

**Special notes for your reviewer**:
Should help with getting ccm/external cloud providers to beta. 

**Release note**:
```release-note
cloudprovider.Zones should support external cloud providers
```

cc @luxas @wlan0 @thockin
pull/6/head
Kubernetes Submit Queue 2017-08-24 21:51:08 -07:00 committed by GitHub
commit 909a0984f1
11 changed files with 152 additions and 2 deletions

View File

@ -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)
}

View File

@ -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 {

View File

@ -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 {

View File

@ -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",
],
)

View File

@ -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")
}

View File

@ -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()

View File

@ -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)

View File

@ -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")

View File

@ -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

View File

@ -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")

View File

@ -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
}