diff --git a/pkg/api/deep_copy_generated.go b/pkg/api/deep_copy_generated.go index 97bad4c3165..19ba060c3ce 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 5504ba2f213..927d99e0767 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 da1de85a2c0..40cafd46ad3 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 c365033e4ba..7c9ade8eb15 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 18855dd86e5..4f94b375d7c 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 c1186c42b51..1fbe9173d80 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 be9800dc416..1097ae99cba 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 486744c3e30..dc86ed96410 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 } @@ -3109,6 +3110,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/cloud.go b/pkg/cloudprovider/cloud.go index db387085391..f0835821703 100644 --- a/pkg/cloudprovider/cloud.go +++ b/pkg/cloudprovider/cloud.go @@ -18,6 +18,7 @@ package cloudprovider import ( "errors" + "fmt" "net" "strings" @@ -61,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 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 }