diff --git a/pkg/api/types.go b/pkg/api/types.go index 28acced55c2..f898afdbf63 100644 --- a/pkg/api/types.go +++ b/pkg/api/types.go @@ -781,6 +781,8 @@ type NodeSpec struct { // PodCIDR represents the pod IP range assigned to the node // Note: assigning IP ranges to nodes might need to be revisited when we support migratable IPs. PodCIDR string `json:"cidr,omitempty"` + // External ID of the node assigned by some machine database (e.g. a cloud provider) + ExternalID string `json:"externalID,omitempty"` } // NodeStatus is information about the current status of a node. diff --git a/pkg/api/v1beta1/conversion.go b/pkg/api/v1beta1/conversion.go index 7126d61ee9b..b1496605ba4 100644 --- a/pkg/api/v1beta1/conversion.go +++ b/pkg/api/v1beta1/conversion.go @@ -699,6 +699,7 @@ func init() { out.HostIP = in.Status.HostIP out.PodCIDR = in.Spec.PodCIDR + out.ExternalID = in.Spec.ExternalID return s.Convert(&in.Spec.Capacity, &out.NodeResources.Capacity, 0) }, func(in *Minion, out *newer.Node, s conversion.Scope) error { @@ -720,6 +721,7 @@ func init() { out.Status.HostIP = in.HostIP out.Spec.PodCIDR = in.PodCIDR + out.Spec.ExternalID = in.ExternalID return s.Convert(&in.NodeResources.Capacity, &out.Spec.Capacity, 0) }, func(in *newer.LimitRange, out *LimitRange, s conversion.Scope) error { diff --git a/pkg/api/v1beta1/types.go b/pkg/api/v1beta1/types.go index 9e2bd555a68..9354847994a 100644 --- a/pkg/api/v1beta1/types.go +++ b/pkg/api/v1beta1/types.go @@ -690,6 +690,8 @@ type Minion struct { Status NodeStatus `json:"status,omitempty" description:"current status of node"` // 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)"` } // MinionList is a list of minions. diff --git a/pkg/api/v1beta2/conversion.go b/pkg/api/v1beta2/conversion.go index c1c5e7603b8..a0ac51de0d0 100644 --- a/pkg/api/v1beta2/conversion.go +++ b/pkg/api/v1beta2/conversion.go @@ -619,6 +619,7 @@ func init() { out.HostIP = in.Status.HostIP out.PodCIDR = in.Spec.PodCIDR + out.ExternalID = in.Spec.ExternalID return s.Convert(&in.Spec.Capacity, &out.NodeResources.Capacity, 0) }, func(in *Minion, out *newer.Node, s conversion.Scope) error { @@ -640,6 +641,7 @@ func init() { out.Status.HostIP = in.HostIP out.Spec.PodCIDR = in.PodCIDR + out.Spec.ExternalID = in.ExternalID return s.Convert(&in.NodeResources.Capacity, &out.Spec.Capacity, 0) }, func(in *newer.LimitRange, out *LimitRange, s conversion.Scope) error { diff --git a/pkg/api/v1beta2/types.go b/pkg/api/v1beta2/types.go index 6787cc6c32e..8d51040071b 100644 --- a/pkg/api/v1beta2/types.go +++ b/pkg/api/v1beta2/types.go @@ -654,6 +654,8 @@ type Minion struct { Status NodeStatus `json:"status,omitempty" description:"current status of node"` // 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)"` } // MinionList is a list of minions. diff --git a/pkg/api/v1beta3/types.go b/pkg/api/v1beta3/types.go index 2258946e6ba..e10c45e058e 100644 --- a/pkg/api/v1beta3/types.go +++ b/pkg/api/v1beta3/types.go @@ -814,6 +814,8 @@ type NodeSpec struct { Capacity ResourceList `json:"capacity,omitempty"` // PodCIDR represents the pod IP range assigned to the node PodCIDR string `json:"cidr,omitempty"` + // External ID of the node assigned by some machine database (e.g. a cloud provider) + ExternalID string `json:"externalID,omitempty"` } // NodeStatus is information about the current status of a node. diff --git a/pkg/cloudprovider/aws/aws.go b/pkg/cloudprovider/aws/aws.go index fda91ee960e..497d16b8802 100644 --- a/pkg/cloudprovider/aws/aws.go +++ b/pkg/cloudprovider/aws/aws.go @@ -150,6 +150,11 @@ func (aws *AWSCloud) IPAddress(name string) (net.IP, error) { return ip, nil } +// ExternalID returns the cloud provider ID of the specified instance. +func (aws *AWSCloud) ExternalID(name string) (string, error) { + return "", fmt.Errorf("unimplemented") +} + // Return a list of instances matching regex string. func (aws *AWSCloud) getInstancesByRegex(regex string) ([]string, error) { resp, err := aws.ec2.Instances(nil, nil) diff --git a/pkg/cloudprovider/cloud.go b/pkg/cloudprovider/cloud.go index 5ba83617940..7960c00c57e 100644 --- a/pkg/cloudprovider/cloud.go +++ b/pkg/cloudprovider/cloud.go @@ -59,6 +59,8 @@ type TCPLoadBalancer interface { type Instances interface { // IPAddress returns an IP address of the specified instance. IPAddress(name string) (net.IP, error) + // ExternalID returns the cloud provider ID of the specified instance. + ExternalID(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/controller/nodecontroller.go b/pkg/cloudprovider/controller/nodecontroller.go index 180016ee44f..6771a3ee15d 100644 --- a/pkg/cloudprovider/controller/nodecontroller.go +++ b/pkg/cloudprovider/controller/nodecontroller.go @@ -231,6 +231,12 @@ func (s *NodeController) PopulateIPs(nodes *api.NodeList) (*api.NodeList, error) } else { node.Status.HostIP = hostIP.String() } + instanceID, err := instances.ExternalID(node.Name) + if err != nil { + glog.Errorf("error getting instance id for %s: %v", node.Name, err) + } else { + node.Spec.ExternalID = instanceID + } } } else { for i := range nodes.Items { diff --git a/pkg/cloudprovider/fake/fake.go b/pkg/cloudprovider/fake/fake.go index 4efc6d82d78..b4bf3b37c9c 100644 --- a/pkg/cloudprovider/fake/fake.go +++ b/pkg/cloudprovider/fake/fake.go @@ -30,6 +30,7 @@ type FakeCloud struct { Err error Calls []string IP net.IP + ExtID string Machines []string NodeResources *api.NodeResources ClusterList []string @@ -110,6 +111,13 @@ func (f *FakeCloud) IPAddress(instance string) (net.IP, error) { return f.IP, f.Err } +// ExternalID is a test-spy implementation of Instances.ExternalID. +// It adds an entry "external-id" into the internal method call record. +func (f *FakeCloud) ExternalID(instance string) (string, error) { + f.addCall("external-id") + return f.ExtID, f.Err +} + // 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 529c810643e..9c36ed0b129 100644 --- a/pkg/cloudprovider/gce/gce.go +++ b/pkg/cloudprovider/gce/gce.go @@ -294,6 +294,11 @@ func (gce *GCECloud) IPAddress(instance string) (net.IP, error) { return ip, nil } +// ExternalID returns the cloud provider ID of the specified instance. +func (gce *GCECloud) ExternalID(instance string) (string, error) { + return "", fmt.Errorf("unimplemented") +} + // fqdnSuffix is hacky function to compute the delta between hostame and hostname -f. func fqdnSuffix() (string, error) { fullHostname, err := exec.Command("hostname", "-f").Output() diff --git a/pkg/cloudprovider/openstack/openstack.go b/pkg/cloudprovider/openstack/openstack.go index 4e5947d6cca..3d93c7a06da 100644 --- a/pkg/cloudprovider/openstack/openstack.go +++ b/pkg/cloudprovider/openstack/openstack.go @@ -312,6 +312,11 @@ func (i *Instances) IPAddress(name string) (net.IP, error) { return net.ParseIP(ip), err } +// ExternalID returns the cloud provider ID of the specified instance. +func (i *Instances) ExternalID(name string) (string, error) { + return "", fmt.Errorf("unimplemented") +} + func (i *Instances) GetNodeResources(name string) (*api.NodeResources, error) { glog.V(2).Infof("GetNodeResources(%v) called", name) diff --git a/pkg/cloudprovider/ovirt/ovirt.go b/pkg/cloudprovider/ovirt/ovirt.go index 1b3e959f2f7..ddb3292dfdf 100644 --- a/pkg/cloudprovider/ovirt/ovirt.go +++ b/pkg/cloudprovider/ovirt/ovirt.go @@ -153,6 +153,11 @@ func (v *OVirtCloud) IPAddress(name string) (net.IP, error) { return address, nil } +// ExternalID returns the cloud provider ID of the specified instance. +func (v *OVirtCloud) ExternalID(name string) (string, error) { + return "", fmt.Errorf("unimplemented") +} + 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 5ab564c9ee2..95d3ad67e02 100644 --- a/pkg/cloudprovider/rackspace/rackspace.go +++ b/pkg/cloudprovider/rackspace/rackspace.go @@ -363,6 +363,11 @@ func (i *Instances) IPAddress(name string) (net.IP, error) { return net.ParseIP(ip), err } +// ExternalID returns the cloud provider ID of the specified instance. +func (i *Instances) ExternalID(name string) (string, error) { + return "", fmt.Errorf("unimplemented") +} + func (i *Instances) GetNodeResources(name string) (*api.NodeResources, error) { glog.V(2).Infof("GetNodeResources(%v) called", name) diff --git a/pkg/cloudprovider/vagrant/vagrant.go b/pkg/cloudprovider/vagrant/vagrant.go index 3a7c6b4c179..ea0cf81e533 100644 --- a/pkg/cloudprovider/vagrant/vagrant.go +++ b/pkg/cloudprovider/vagrant/vagrant.go @@ -119,6 +119,11 @@ func (v *VagrantCloud) IPAddress(instance string) (net.IP, error) { return nil, fmt.Errorf("unable to find IP address for instance: %s", instance) } +// ExternalID returns the cloud provider ID of the specified instance. +func (v *VagrantCloud) ExternalID(instance string) (string, error) { + return "", fmt.Errorf("unimplemented") +} + // saltMinionsByRole filters a list of minions that have a matching role. func (v *VagrantCloud) saltMinionsByRole(minions []SaltMinion, role string) []SaltMinion { var filteredMinions []SaltMinion