diff --git a/pkg/api/deep_copy_generated.go b/pkg/api/deep_copy_generated.go index 6bbc7638c8c..b282343f1ed 100644 --- a/pkg/api/deep_copy_generated.go +++ b/pkg/api/deep_copy_generated.go @@ -921,6 +921,7 @@ func deepCopy_api_NodeList(in NodeList, out *NodeList, c *conversion.Cloner) err func deepCopy_api_NodeSpec(in NodeSpec, out *NodeSpec, c *conversion.Cloner) error { out.PodCIDR = in.PodCIDR out.ExternalID = in.ExternalID + out.ProviderID = in.ProviderID out.Unschedulable = in.Unschedulable return nil } diff --git a/pkg/api/types.go b/pkg/api/types.go index e2f09829f99..70b48abd769 100644 --- a/pkg/api/types.go +++ b/pkg/api/types.go @@ -1241,6 +1241,10 @@ type NodeSpec struct { // External ID of the node assigned by some machine database (e.g. a cloud provider) ExternalID string `json:"externalID,omitempty"` + // ID of the node assigned by the cloud provider + // Note: format is "://" + ProviderID string `json:"providerID,omitempty"` + // Unschedulable controls node schedulability of new pods. By default node is schedulable. Unschedulable bool `json:"unschedulable,omitempty"` } diff --git a/pkg/api/v1/conversion_generated.go b/pkg/api/v1/conversion_generated.go index a01943f5fac..02cb44d4250 100644 --- a/pkg/api/v1/conversion_generated.go +++ b/pkg/api/v1/conversion_generated.go @@ -995,6 +995,7 @@ func convert_api_NodeSpec_To_v1_NodeSpec(in *api.NodeSpec, out *NodeSpec, s conv } out.PodCIDR = in.PodCIDR out.ExternalID = in.ExternalID + out.ProviderID = in.ProviderID out.Unschedulable = in.Unschedulable return nil } @@ -3269,6 +3270,7 @@ func convert_v1_NodeSpec_To_api_NodeSpec(in *NodeSpec, out *api.NodeSpec, s conv } out.PodCIDR = in.PodCIDR out.ExternalID = in.ExternalID + out.ProviderID = in.ProviderID out.Unschedulable = in.Unschedulable return nil } diff --git a/pkg/api/v1/deep_copy_generated.go b/pkg/api/v1/deep_copy_generated.go index 2b408eff4b0..e99397bc3bf 100644 --- a/pkg/api/v1/deep_copy_generated.go +++ b/pkg/api/v1/deep_copy_generated.go @@ -852,6 +852,7 @@ func deepCopy_v1_NodeList(in NodeList, out *NodeList, c *conversion.Cloner) erro func deepCopy_v1_NodeSpec(in NodeSpec, out *NodeSpec, c *conversion.Cloner) error { out.PodCIDR = in.PodCIDR out.ExternalID = in.ExternalID + out.ProviderID = in.ProviderID out.Unschedulable = in.Unschedulable return nil } diff --git a/pkg/api/v1/defaults_test.go b/pkg/api/v1/defaults_test.go index c13c2f7fc05..a359ee0de10 100644 --- a/pkg/api/v1/defaults_test.go +++ b/pkg/api/v1/defaults_test.go @@ -397,6 +397,9 @@ func TestSetDefaultNodeExternalID(t *testing.T) { if n2.Spec.ExternalID != name { t.Errorf("Expected default External ID: %s, got: %s", name, n2.Spec.ExternalID) } + if n2.Spec.ProviderID != "" { + t.Errorf("Expected empty default Cloud Provider ID, got: %s", n2.Spec.ProviderID) + } } func TestSetDefaultObjectFieldSelectorAPIVersion(t *testing.T) { diff --git a/pkg/api/v1/types.go b/pkg/api/v1/types.go index 3d1f25f769b..5d9faf38e46 100644 --- a/pkg/api/v1/types.go +++ b/pkg/api/v1/types.go @@ -1229,7 +1229,9 @@ type NodeSpec struct { // PodCIDR represents the pod IP range assigned to the node PodCIDR string `json:"podCIDR,omitempty" description:"pod IP range assigned to the node"` // External ID of the node assigned by some machine database (e.g. a cloud provider) - ExternalID string `json:"externalID,omitempty" description:"external ID assigned to the node by some machine database (e.g. a cloud provider). Defaults to node name when empty."` + ExternalID string `json:"externalID,omitempty" description:"deprecated. External ID assigned to the node by some machine database (e.g. a cloud provider). Defaults to node name when empty."` + // ID of the node assigned by the cloud provider + ProviderID string `json:"providerID,omitempty" description:"ID of the node assigned by the cloud provider in the format: ://"` // Unschedulable controls node schedulability of new pods. By default node is schedulable. Unschedulable bool `json:"unschedulable,omitempty" description:"disable pod scheduling on the node"` } diff --git a/pkg/api/v1beta1/conversion.go b/pkg/api/v1beta1/conversion.go index ac1b6f9d000..6b4654bb779 100644 --- a/pkg/api/v1beta1/conversion.go +++ b/pkg/api/v1beta1/conversion.go @@ -888,6 +888,7 @@ func addConversionFuncs() { } out.PodCIDR = in.Spec.PodCIDR out.ExternalID = in.Spec.ExternalID + out.ProviderID = in.Spec.ProviderID out.Unschedulable = in.Spec.Unschedulable return s.Convert(&in.Status.Capacity, &out.NodeResources.Capacity, 0) }, @@ -920,6 +921,7 @@ func addConversionFuncs() { } out.Spec.PodCIDR = in.PodCIDR out.Spec.ExternalID = in.ExternalID + out.Spec.ProviderID = in.ProviderID out.Spec.Unschedulable = in.Unschedulable return s.Convert(&in.NodeResources.Capacity, &out.Status.Capacity, 0) }, diff --git a/pkg/api/v1beta1/defaults_test.go b/pkg/api/v1beta1/defaults_test.go index d254396b634..e1e62d5dc1f 100644 --- a/pkg/api/v1beta1/defaults_test.go +++ b/pkg/api/v1beta1/defaults_test.go @@ -326,6 +326,9 @@ func TestSetDefaultMinionExternalID(t *testing.T) { if m2.ExternalID != name { t.Errorf("Expected default External ID: %s, got: %s", name, m2.ExternalID) } + if m2.ProviderID != "" { + t.Errorf("Expected empty default Cloud Provider ID, got: %s", m2.ProviderID) + } } func TestSetDefaultObjectFieldSelectorAPIVersion(t *testing.T) { diff --git a/pkg/api/v1beta1/types.go b/pkg/api/v1beta1/types.go index eb456722443..a4b5bd6ba18 100644 --- a/pkg/api/v1beta1/types.go +++ b/pkg/api/v1beta1/types.go @@ -1210,7 +1210,9 @@ type Minion struct { // Labels for the node Labels map[string]string `json:"labels,omitempty" description:"map of string keys and values that can be used to organize and categorize minions; labels of a minion assigned by the scheduler must match the scheduled pod's nodeSelector"` // External ID of the node - ExternalID string `json:"externalID,omitempty" description:"external id of the node assigned by some machine database (e.g. a cloud provider). Defaults to node name when empty."` + ExternalID string `json:"externalID,omitempty" description:"deprecated. External id of the node assigned by some machine database (e.g. a cloud provider). Defaults to node name when empty."` + // ID of the node assigned by the cloud provider + ProviderID string `json:"providerID,omitempty" description:"ID of the node assigned by the cloud provider in the format: ://"` } // MinionList is a list of minions. diff --git a/pkg/api/v1beta2/conversion.go b/pkg/api/v1beta2/conversion.go index cdef72d683e..0418056cabb 100644 --- a/pkg/api/v1beta2/conversion.go +++ b/pkg/api/v1beta2/conversion.go @@ -810,6 +810,7 @@ func addConversionFuncs() { } out.PodCIDR = in.Spec.PodCIDR out.ExternalID = in.Spec.ExternalID + out.ProviderID = in.Spec.ProviderID out.Unschedulable = in.Spec.Unschedulable return s.Convert(&in.Status.Capacity, &out.NodeResources.Capacity, 0) }, @@ -842,6 +843,7 @@ func addConversionFuncs() { } out.Spec.PodCIDR = in.PodCIDR out.Spec.ExternalID = in.ExternalID + out.Spec.ProviderID = in.ProviderID out.Spec.Unschedulable = in.Unschedulable return s.Convert(&in.NodeResources.Capacity, &out.Status.Capacity, 0) }, diff --git a/pkg/api/v1beta2/defaults_test.go b/pkg/api/v1beta2/defaults_test.go index 0a9224b5f8f..aaf70963e54 100644 --- a/pkg/api/v1beta2/defaults_test.go +++ b/pkg/api/v1beta2/defaults_test.go @@ -325,6 +325,9 @@ func TestSetDefaultMinionExternalID(t *testing.T) { if m2.ExternalID != name { t.Errorf("Expected default External ID: %s, got: %s", name, m2.ExternalID) } + if m2.ProviderID != "" { + t.Errorf("Expected empty default Cloud Provider ID, got: %s", m2.ProviderID) + } } func TestSetDefaultObjectFieldSelectorAPIVersion(t *testing.T) { diff --git a/pkg/api/v1beta2/types.go b/pkg/api/v1beta2/types.go index ae654016fdc..1bef33e39ab 100644 --- a/pkg/api/v1beta2/types.go +++ b/pkg/api/v1beta2/types.go @@ -1226,7 +1226,9 @@ type Minion struct { // Labels for the node Labels map[string]string `json:"labels,omitempty" description:"map of string keys and values that can be used to organize and categorize minions; labels of a minion assigned by the scheduler must match the scheduled pod's nodeSelector"` // External ID of the node - ExternalID string `json:"externalID,omitempty" description:"external id of the node assigned by some machine database (e.g. a cloud provider). Defaults to node name when empty."` + ExternalID string `json:"externalID,omitempty" description:"deprecated. External id of the node assigned by some machine database (e.g. a cloud provider). Defaults to node name when empty."` + // ID of the node assigned by the cloud provider + ProviderID string `json:"providerID,omitempty" description:"ID of the node assigned by the cloud provider in the format: ://"` } // MinionList is a list of minions. diff --git a/pkg/api/v1beta3/conversion_generated.go b/pkg/api/v1beta3/conversion_generated.go index da2f98b40c2..c1af048ff6f 100644 --- a/pkg/api/v1beta3/conversion_generated.go +++ b/pkg/api/v1beta3/conversion_generated.go @@ -902,6 +902,7 @@ func convert_api_NodeSpec_To_v1beta3_NodeSpec(in *api.NodeSpec, out *NodeSpec, s } out.PodCIDR = in.PodCIDR out.ExternalID = in.ExternalID + out.ProviderID = in.ProviderID out.Unschedulable = in.Unschedulable return nil } @@ -3047,6 +3048,7 @@ func convert_v1beta3_NodeSpec_To_api_NodeSpec(in *NodeSpec, out *api.NodeSpec, s } out.PodCIDR = in.PodCIDR out.ExternalID = in.ExternalID + out.ProviderID = in.ProviderID out.Unschedulable = in.Unschedulable return nil } diff --git a/pkg/api/v1beta3/deep_copy_generated.go b/pkg/api/v1beta3/deep_copy_generated.go index 15ed66e26b0..5f8db18e954 100644 --- a/pkg/api/v1beta3/deep_copy_generated.go +++ b/pkg/api/v1beta3/deep_copy_generated.go @@ -856,6 +856,7 @@ func deepCopy_v1beta3_NodeList(in NodeList, out *NodeList, c *conversion.Cloner) func deepCopy_v1beta3_NodeSpec(in NodeSpec, out *NodeSpec, c *conversion.Cloner) error { out.PodCIDR = in.PodCIDR out.ExternalID = in.ExternalID + out.ProviderID = in.ProviderID out.Unschedulable = in.Unschedulable return nil } diff --git a/pkg/api/v1beta3/defaults_test.go b/pkg/api/v1beta3/defaults_test.go index 6d5a411979b..745be4c4f9f 100644 --- a/pkg/api/v1beta3/defaults_test.go +++ b/pkg/api/v1beta3/defaults_test.go @@ -334,6 +334,9 @@ func TestSetDefaultNodeExternalID(t *testing.T) { if n2.Spec.ExternalID != name { t.Errorf("Expected default External ID: %s, got: %s", name, n2.Spec.ExternalID) } + if n2.Spec.ProviderID != "" { + t.Errorf("Expected empty default Cloud Provider ID, got: %s", n2.Spec.ProviderID) + } } func TestSetDefaultObjectFieldSelectorAPIVersion(t *testing.T) { diff --git a/pkg/api/v1beta3/types.go b/pkg/api/v1beta3/types.go index c5fdb8180bd..cc4977b94d2 100644 --- a/pkg/api/v1beta3/types.go +++ b/pkg/api/v1beta3/types.go @@ -1236,7 +1236,9 @@ type NodeSpec struct { // PodCIDR represents the pod IP range assigned to the node PodCIDR string `json:"podCIDR,omitempty" description:"pod IP range assigned to the node"` // External ID of the node assigned by some machine database (e.g. a cloud provider) - ExternalID string `json:"externalID,omitempty" description:"external ID assigned to the node by some machine database (e.g. a cloud provider). Defaults to node name when empty."` + ExternalID string `json:"externalID,omitempty" description:"deprecated. External ID assigned to the node by some machine database (e.g. a cloud provider). Defaults to node name when empty."` + // ID of the node assigned by the cloud provider + ProviderID string `json:"providerID,omitempty" description:"ID of the node assigned by the cloud provider in the format: ://"` // Unschedulable controls node schedulability of new pods. By default node is schedulable. Unschedulable bool `json:"unschedulable,omitempty" description:"disable pod scheduling on the node"` } diff --git a/pkg/cloudprovider/aws/aws.go b/pkg/cloudprovider/aws/aws.go index 343ce8feadf..3e905e81608 100644 --- a/pkg/cloudprovider/aws/aws.go +++ b/pkg/cloudprovider/aws/aws.go @@ -41,6 +41,8 @@ import ( "github.com/golang/glog" ) +const ProviderName = "aws" + // Abstraction over EC2, to allow mocking/other implementations type EC2 interface { // Query EC2 for instances matching the filter @@ -244,7 +246,7 @@ func (s *awsSdkEC2) DeleteVolume(volumeID string) (resp *ec2.DeleteVolumeOutput, } func init() { - cloudprovider.RegisterCloudProvider("aws", func(config io.Reader) (cloudprovider.Interface, error) { + cloudprovider.RegisterCloudProvider(ProviderName, func(config io.Reader) (cloudprovider.Interface, error) { metadata := &awsSdkMetadata{} return newAWSCloud(config, getAuth, metadata) }) @@ -360,6 +362,11 @@ func (aws *AWSCloud) Clusters() (cloudprovider.Clusters, bool) { return nil, false } +// ProviderName returns the cloud provider ID. +func (aws *AWSCloud) ProviderName() string { + return ProviderName +} + // TCPLoadBalancer returns an implementation of TCPLoadBalancer for Amazon Web Services. func (aws *AWSCloud) TCPLoadBalancer() (cloudprovider.TCPLoadBalancer, bool) { return nil, false @@ -414,7 +421,7 @@ func (aws *AWSCloud) NodeAddresses(name string) ([]api.NodeAddress, error) { return addresses, nil } -// ExternalID returns the cloud provider ID of the specified instance. +// ExternalID returns the cloud provider ID of the specified instance (deprecated). func (aws *AWSCloud) ExternalID(name string) (string, error) { inst, err := aws.getInstancesByDnsName(name) if err != nil { @@ -423,6 +430,17 @@ func (aws *AWSCloud) ExternalID(name string) (string, error) { return *inst.InstanceID, nil } +// InstanceID returns the cloud provider ID of the specified instance. +func (aws *AWSCloud) InstanceID(name string) (string, error) { + inst, err := aws.getInstancesByDnsName(name) + if err != nil { + return "", err + } + // In the future it is possible to also return an endpoint as: + // // + return "/" + *inst.Placement.AvailabilityZone + "/" + *inst.InstanceID, nil +} + // Return the instances matching the relevant private dns name. func (aws *AWSCloud) getInstancesByDnsName(name string) (*ec2.Instance, error) { f := &ec2InstanceFilter{} diff --git a/pkg/cloudprovider/cloud.go b/pkg/cloudprovider/cloud.go index ab5feea5b7a..f0835821703 100644 --- a/pkg/cloudprovider/cloud.go +++ b/pkg/cloudprovider/cloud.go @@ -18,6 +18,7 @@ package cloudprovider import ( "errors" + "fmt" "net" "strings" @@ -36,6 +37,8 @@ type Interface interface { Clusters() (Clusters, bool) // Routes returns a routes interface along with whether the interface is supported. Routes() (Routes, bool) + // ProviderName returns the cloud provider ID. + ProviderName() string } // Clusters is an abstract, pluggable interface for clusters of containers. @@ -59,6 +62,18 @@ func GetLoadBalancerName(service *api.Service) string { return ret } +func GetInstanceProviderID(cloud Interface, nodeName string) (string, error) { + instances, ok := cloud.Instances() + if !ok { + return "", fmt.Errorf("failed to get instances from cloud provider") + } + instanceID, err := instances.InstanceID(nodeName) + if err != nil { + return "", fmt.Errorf("failed to get instance ID from cloud provider: %v", err) + } + return cloud.ProviderName() + "://" + instanceID, nil +} + // TCPLoadBalancer is an abstract, pluggable interface for TCP load balancers. type TCPLoadBalancer interface { // TODO: Break this up into different interfaces (LB, etc) when we have more than one type of service @@ -85,8 +100,10 @@ type Instances interface { // returns the address of the calling instance. We should do a rename to // make this clearer. NodeAddresses(name string) ([]api.NodeAddress, error) - // ExternalID returns the cloud provider ID of the specified instance. + // ExternalID returns the cloud provider ID of the specified instance (deprecated). ExternalID(name string) (string, error) + // InstanceID returns the cloud provider ID of the specified instance. + InstanceID(name string) (string, error) // List lists instances that match 'filter' which is a regular expression which must match the entire instance name (fqdn) List(filter string) ([]string, error) // GetNodeResources gets the resources for a particular node diff --git a/pkg/cloudprovider/fake/fake.go b/pkg/cloudprovider/fake/fake.go index d9db3b6c187..15ef13c1522 100644 --- a/pkg/cloudprovider/fake/fake.go +++ b/pkg/cloudprovider/fake/fake.go @@ -26,6 +26,8 @@ import ( "github.com/GoogleCloudPlatform/kubernetes/pkg/cloudprovider" ) +const ProviderName = "fake" + // FakeBalancer is a fake storage of balancer information type FakeBalancer struct { Name string @@ -81,6 +83,11 @@ func (f *FakeCloud) Clusters() (cloudprovider.Clusters, bool) { return f, true } +// ProviderName returns the cloud provider ID. +func (f *FakeCloud) ProviderName() string { + return ProviderName +} + // TCPLoadBalancer returns a fake implementation of TCPLoadBalancer. // Actually it just returns f itself. func (f *FakeCloud) TCPLoadBalancer() (cloudprovider.TCPLoadBalancer, bool) { @@ -152,6 +159,12 @@ func (f *FakeCloud) ExternalID(instance string) (string, error) { return f.ExtID[instance], f.Err } +// InstanceID returns the cloud provider ID of the specified instance. +func (f *FakeCloud) InstanceID(instance string) (string, error) { + f.addCall("instance-id") + return f.ExtID[instance], nil +} + // List is a test-spy implementation of Instances.List. // It adds an entry "list" into the internal method call record. func (f *FakeCloud) List(filter string) ([]string, error) { diff --git a/pkg/cloudprovider/gce/gce.go b/pkg/cloudprovider/gce/gce.go index 863bc5ddae5..38544416060 100644 --- a/pkg/cloudprovider/gce/gce.go +++ b/pkg/cloudprovider/gce/gce.go @@ -42,6 +42,8 @@ import ( "google.golang.org/cloud/compute/metadata" ) +const ProviderName = "gce" + const EXTERNAL_IP_METADATA_URL = "http://169.254.169.254/computeMetadata/v1/instance/network-interfaces/0/access-configs/0/external-ip" // GCECloud is an implementation of Interface, TCPLoadBalancer and Instances for Google Compute Engine. @@ -67,7 +69,7 @@ type Config struct { } func init() { - cloudprovider.RegisterCloudProvider("gce", func(config io.Reader) (cloudprovider.Interface, error) { return newGCECloud(config) }) + cloudprovider.RegisterCloudProvider(ProviderName, func(config io.Reader) (cloudprovider.Interface, error) { return newGCECloud(config) }) } func getMetadata(url string) (string, error) { @@ -182,6 +184,11 @@ func (gce *GCECloud) Clusters() (cloudprovider.Clusters, bool) { return gce, true } +// ProviderName returns the cloud provider ID. +func (gce *GCECloud) ProviderName() string { + return ProviderName +} + // TCPLoadBalancer returns an implementation of TCPLoadBalancer for Google Compute Engine. func (gce *GCECloud) TCPLoadBalancer() (cloudprovider.TCPLoadBalancer, bool) { return gce, true @@ -481,7 +488,7 @@ func (gce *GCECloud) NodeAddresses(_ string) ([]api.NodeAddress, error) { }, nil } -// ExternalID returns the cloud provider ID of the specified instance. +// ExternalID returns the cloud provider ID of the specified instance (deprecated). func (gce *GCECloud) ExternalID(instance string) (string, error) { inst, err := gce.getInstanceByName(instance) if err != nil { @@ -490,6 +497,11 @@ func (gce *GCECloud) ExternalID(instance string) (string, error) { return strconv.FormatUint(inst.Id, 10), nil } +// InstanceID returns the cloud provider ID of the specified instance. +func (gce *GCECloud) InstanceID(instance string) (string, error) { + return gce.projectID + "/" + gce.zone + "/" + canonicalizeInstanceName(instance), nil +} + // List is an implementation of Instances.List. func (gce *GCECloud) List(filter string) ([]string, error) { listCall := gce.service.Instances.List(gce.projectID, gce.zone) diff --git a/pkg/cloudprovider/mesos/mesos.go b/pkg/cloudprovider/mesos/mesos.go index 97cd5407d1c..cacc3b40b17 100644 --- a/pkg/cloudprovider/mesos/mesos.go +++ b/pkg/cloudprovider/mesos/mesos.go @@ -31,7 +31,7 @@ import ( ) var ( - PluginName = "mesos" + ProviderName = "mesos" CloudProvider *MesosCloud noHostNameSpecified = errors.New("No hostname specified") @@ -39,7 +39,7 @@ var ( func init() { cloudprovider.RegisterCloudProvider( - PluginName, + ProviderName, func(configReader io.Reader) (cloudprovider.Interface, error) { provider, err := newMesosCloud(configReader) if err == nil { @@ -110,6 +110,11 @@ func (c *MesosCloud) Routes() (cloudprovider.Routes, bool) { return nil, false } +// ProviderName returns the cloud provider ID. +func (c *MesosCloud) ProviderName() string { + return ProviderName +} + // ListClusters lists the names of the available Mesos clusters. func (c *MesosCloud) ListClusters() ([]string, error) { // Always returns a single cluster (this one!) @@ -161,7 +166,7 @@ func ipAddress(name string) (net.IP, error) { return ipaddr, nil } -// ExternalID returns the cloud provider ID of the specified instance. +// ExternalID returns the cloud provider ID of the specified instance (deprecated). func (c *MesosCloud) ExternalID(instance string) (string, error) { ip, err := ipAddress(instance) if err != nil { @@ -170,6 +175,11 @@ func (c *MesosCloud) ExternalID(instance string) (string, error) { return ip.String(), nil } +// InstanceID returns the cloud provider ID of the specified instance. +func (c *MesosCloud) InstanceID(name string) (string, error) { + return "", nil +} + // List lists instances that match 'filter' which is a regular expression // which must match the entire instance name (fqdn). func (c *MesosCloud) List(filter string) ([]string, error) { diff --git a/pkg/cloudprovider/openstack/openstack.go b/pkg/cloudprovider/openstack/openstack.go index fb855bec152..53db708f1a1 100644 --- a/pkg/cloudprovider/openstack/openstack.go +++ b/pkg/cloudprovider/openstack/openstack.go @@ -42,6 +42,8 @@ import ( "github.com/golang/glog" ) +const ProviderName = "openstack" + var ErrNotFound = errors.New("Failed to find object") var ErrMultipleResults = errors.New("Multiple results where only one expected") var ErrNoAddressFound = errors.New("No address found for host") @@ -99,7 +101,7 @@ type Config struct { } func init() { - cloudprovider.RegisterCloudProvider("openstack", func(config io.Reader) (cloudprovider.Interface, error) { + cloudprovider.RegisterCloudProvider(ProviderName, func(config io.Reader) (cloudprovider.Interface, error) { cfg, err := readConfig(config) if err != nil { return nil, err @@ -355,7 +357,7 @@ func (i *Instances) NodeAddresses(name string) ([]api.NodeAddress, error) { return addrs, nil } -// ExternalID returns the cloud provider ID of the specified instance. +// ExternalID returns the cloud provider ID of the specified instance (deprecated). func (i *Instances) ExternalID(name string) (string, error) { srv, err := getServerByName(i.compute, name) if err != nil { @@ -364,6 +366,17 @@ func (i *Instances) ExternalID(name string) (string, error) { return srv.ID, nil } +// InstanceID returns the cloud provider ID of the specified instance. +func (i *Instances) InstanceID(name string) (string, error) { + srv, err := getServerByName(i.compute, name) + if err != nil { + return "", err + } + // In the future it is possible to also return an endpoint as: + // / + return "/" + srv.ID, nil +} + func (i *Instances) GetNodeResources(name string) (*api.NodeResources, error) { glog.V(4).Infof("GetNodeResources(%v) called", name) @@ -394,6 +407,11 @@ func (os *OpenStack) Clusters() (cloudprovider.Clusters, bool) { return nil, false } +// ProviderName returns the cloud provider ID. +func (os *OpenStack) ProviderName() string { + return ProviderName +} + type LoadBalancer struct { network *gophercloud.ServiceClient compute *gophercloud.ServiceClient diff --git a/pkg/cloudprovider/ovirt/ovirt.go b/pkg/cloudprovider/ovirt/ovirt.go index f4325a4f7d8..6c78202df1d 100644 --- a/pkg/cloudprovider/ovirt/ovirt.go +++ b/pkg/cloudprovider/ovirt/ovirt.go @@ -33,6 +33,8 @@ import ( "github.com/GoogleCloudPlatform/kubernetes/pkg/cloudprovider" ) +const ProviderName = "ovirt" + type OVirtInstance struct { UUID string Name string @@ -75,7 +77,7 @@ type XmlVmsList struct { } func init() { - cloudprovider.RegisterCloudProvider("ovirt", + cloudprovider.RegisterCloudProvider(ProviderName, func(config io.Reader) (cloudprovider.Interface, error) { return newOVirtCloud(config) }) @@ -115,6 +117,11 @@ func (aws *OVirtCloud) Clusters() (cloudprovider.Clusters, bool) { return nil, false } +// ProviderName returns the cloud provider ID. +func (v *OVirtCloud) ProviderName() string { + return ProviderName +} + // TCPLoadBalancer returns an implementation of TCPLoadBalancer for oVirt cloud func (v *OVirtCloud) TCPLoadBalancer() (cloudprovider.TCPLoadBalancer, bool) { return nil, false @@ -160,7 +167,7 @@ func (v *OVirtCloud) NodeAddresses(name string) ([]api.NodeAddress, error) { return []api.NodeAddress{{Type: api.NodeLegacyHostIP, Address: address.String()}}, nil } -// ExternalID returns the cloud provider ID of the specified instance. +// ExternalID returns the cloud provider ID of the specified instance (deprecated). func (v *OVirtCloud) ExternalID(name string) (string, error) { instance, err := v.fetchInstance(name) if err != nil { @@ -169,6 +176,17 @@ func (v *OVirtCloud) ExternalID(name string) (string, error) { return instance.UUID, nil } +// InstanceID returns the cloud provider ID of the specified instance. +func (v *OVirtCloud) InstanceID(name string) (string, error) { + instance, err := v.fetchInstance(name) + if err != nil { + return "", err + } + // TODO: define a way to identify the provider instance to complete + // the format /. + return "/" + instance.UUID, err +} + func getInstancesFromXml(body io.Reader) (OVirtInstanceMap, error) { if body == nil { return nil, fmt.Errorf("ovirt rest-api response body is missing") diff --git a/pkg/cloudprovider/rackspace/rackspace.go b/pkg/cloudprovider/rackspace/rackspace.go index 72fce7bcc00..8df8230da65 100644 --- a/pkg/cloudprovider/rackspace/rackspace.go +++ b/pkg/cloudprovider/rackspace/rackspace.go @@ -38,6 +38,8 @@ import ( "github.com/golang/glog" ) +const ProviderName = "rackspace" + var ErrNotFound = errors.New("Failed to find object") var ErrMultipleResults = errors.New("Multiple results where only one expected") var ErrNoAddressFound = errors.New("No address found for host") @@ -89,7 +91,7 @@ type Config struct { } func init() { - cloudprovider.RegisterCloudProvider("rackspace", func(config io.Reader) (cloudprovider.Interface, error) { + cloudprovider.RegisterCloudProvider(ProviderName, func(config io.Reader) (cloudprovider.Interface, error) { cfg, err := readConfig(config) if err != nil { return nil, err @@ -364,11 +366,16 @@ func (i *Instances) NodeAddresses(name string) ([]api.NodeAddress, error) { return []api.NodeAddress{{Type: api.NodeLegacyHostIP, Address: net.ParseIP(ip).String()}}, nil } -// ExternalID returns the cloud provider ID of the specified instance. +// ExternalID returns the cloud provider ID of the specified instance (deprecated). func (i *Instances) ExternalID(name string) (string, error) { return "", fmt.Errorf("unimplemented") } +// InstanceID returns the cloud provider ID of the specified instance. +func (i *Instances) InstanceID(name string) (string, error) { + return "", nil +} + func (i *Instances) GetNodeResources(name string) (*api.NodeResources, error) { glog.V(2).Infof("GetNodeResources(%v) called", name) @@ -399,6 +406,11 @@ func (os *Rackspace) Clusters() (cloudprovider.Clusters, bool) { return nil, false } +// ProviderName returns the cloud provider ID. +func (os *Rackspace) ProviderName() string { + return ProviderName +} + func (os *Rackspace) TCPLoadBalancer() (cloudprovider.TCPLoadBalancer, bool) { return nil, false } diff --git a/pkg/cloudprovider/vagrant/vagrant.go b/pkg/cloudprovider/vagrant/vagrant.go index fcbb1f04ca2..2bd18150d17 100644 --- a/pkg/cloudprovider/vagrant/vagrant.go +++ b/pkg/cloudprovider/vagrant/vagrant.go @@ -31,6 +31,8 @@ import ( "github.com/GoogleCloudPlatform/kubernetes/pkg/cloudprovider" ) +const ProviderName = "vagrant" + // VagrantCloud is an implementation of Interface, TCPLoadBalancer and Instances for developer managed Vagrant cluster. type VagrantCloud struct { saltURL string @@ -40,7 +42,7 @@ type VagrantCloud struct { } func init() { - cloudprovider.RegisterCloudProvider("vagrant", func(config io.Reader) (cloudprovider.Interface, error) { return newVagrantCloud() }) + cloudprovider.RegisterCloudProvider(ProviderName, func(config io.Reader) (cloudprovider.Interface, error) { return newVagrantCloud() }) } // SaltToken is an authorization token required by Salt REST API. @@ -84,6 +86,11 @@ func (v *VagrantCloud) Clusters() (cloudprovider.Clusters, bool) { return nil, false } +// ProviderName returns the cloud provider ID. +func (v *VagrantCloud) ProviderName() string { + return ProviderName +} + // TCPLoadBalancer returns an implementation of TCPLoadBalancer for Vagrant cloud. func (v *VagrantCloud) TCPLoadBalancer() (cloudprovider.TCPLoadBalancer, bool) { return nil, false @@ -135,7 +142,7 @@ func (v *VagrantCloud) NodeAddresses(instance string) ([]api.NodeAddress, error) return []api.NodeAddress{{Type: api.NodeLegacyHostIP, Address: ip.String()}}, nil } -// ExternalID returns the cloud provider ID of the specified instance. +// ExternalID returns the cloud provider ID of the specified instance (deprecated). func (v *VagrantCloud) ExternalID(instance string) (string, error) { // Due to vagrant not running with a dedicated DNS setup, we return the IP address of a minion as its hostname at this time minion, err := v.getInstanceByAddress(instance) @@ -145,6 +152,15 @@ func (v *VagrantCloud) ExternalID(instance string) (string, error) { return minion.IP, nil } +// InstanceID returns the cloud provider ID of the specified instance. +func (v *VagrantCloud) InstanceID(instance string) (string, error) { + minion, err := v.getInstanceByAddress(instance) + if err != nil { + return "", err + } + return minion.IP, nil +} + // saltMinionsByRole filters a list of minions that have a matching role. func (v *VagrantCloud) saltMinionsByRole(minions []SaltMinion, role string) []SaltMinion { var filteredMinions []SaltMinion diff --git a/pkg/kubelet/kubelet.go b/pkg/kubelet/kubelet.go index db752463a5e..b67eb48df46 100644 --- a/pkg/kubelet/kubelet.go +++ b/pkg/kubelet/kubelet.go @@ -700,11 +700,19 @@ func (kl *Kubelet) initialNodeStatus() (*api.Node, error) { } // TODO(roberthbailey): Can we do this without having credentials to talk // to the cloud provider? - instanceID, err := instances.ExternalID(kl.hostname) + // TODO: ExternalID is deprecated, we'll have to drop this code + externalID, err := instances.ExternalID(kl.hostname) if err != nil { - return nil, fmt.Errorf("failed to get instance ID from cloud provider: %v", err) + return nil, fmt.Errorf("failed to get external ID from cloud provider: %v", err) + } + node.Spec.ExternalID = externalID + // TODO: We can't assume that the node has credentials to talk to the + // cloudprovider from arbitrary nodes. At most, we should talk to a + // local metadata server here. + node.Spec.ProviderID, err = cloudprovider.GetInstanceProviderID(kl.cloud, kl.hostname) + if err != nil { + return nil, err } - node.Spec.ExternalID = instanceID } else { node.Spec.ExternalID = kl.hostname }