diff --git a/pkg/api/unversioned/well_known_labels.go b/pkg/api/unversioned/well_known_labels.go index 379d1065d5b..0815e80832e 100644 --- a/pkg/api/unversioned/well_known_labels.go +++ b/pkg/api/unversioned/well_known_labels.go @@ -18,3 +18,4 @@ package unversioned const LabelZoneFailureDomain = "failure-domain.alpha.kubernetes.io/zone" const LabelZoneRegion = "failure-domain.alpha.kubernetes.io/region" +const LabelInstanceType = "beta.kubernetes.io/instance-type" diff --git a/pkg/cloudprovider/cloud.go b/pkg/cloudprovider/cloud.go index d5d99b17aa7..e145b26dea4 100644 --- a/pkg/cloudprovider/cloud.go +++ b/pkg/cloudprovider/cloud.go @@ -108,6 +108,9 @@ type Instances interface { // InstanceID returns the cloud provider ID of the specified instance. // Note that if the instance does not exist or is no longer running, we must return ("", cloudprovider.InstanceNotFound) InstanceID(name string) (string, error) + // InstanceType returns the type of the specified instance. + // Note that if the instance does not exist or is no longer running, we must return ("", cloudprovider.InstanceNotFound) + InstanceType(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) // AddSSHKeyToAllInstances adds an SSH public key as a legal identity for all instances diff --git a/pkg/cloudprovider/providers/aws/aws.go b/pkg/cloudprovider/providers/aws/aws.go index c74cc707722..25640c47b38 100644 --- a/pkg/cloudprovider/providers/aws/aws.go +++ b/pkg/cloudprovider/providers/aws/aws.go @@ -491,6 +491,10 @@ func readAWSCloudConfig(config io.Reader, metadata EC2Metadata) (*AWSCloudConfig return &cfg, nil } +func getInstanceType(metadata EC2Metadata) (string, error) { + return metadata.GetMetadata("instance-type") +} + func getAvailabilityZone(metadata EC2Metadata) (string, error) { return metadata.GetMetadata("placement/availability-zone") } @@ -751,6 +755,24 @@ func (aws *AWSCloud) InstanceID(name string) (string, error) { } } +// InstanceType returns the type of the specified instance. +func (aws *AWSCloud) InstanceType(name string) (string, error) { + awsInstance, err := aws.getSelfAWSInstance() + if err != nil { + return "", err + } + + if awsInstance.nodeName == name { + return awsInstance.instanceType, nil + } else { + inst, err := aws.getInstanceByNodeName(name) + if err != nil { + return "", err + } + return orEmpty(inst.InstanceType), nil + } +} + // Check if the instance is alive (running or pending) // We typically ignore instances that are not alive func isAlive(instance *ec2.Instance) bool { @@ -873,6 +895,9 @@ type awsInstance struct { // availability zone the instance resides in availabilityZone string + // instance type + instanceType string + mutex sync.Mutex // We must cache because otherwise there is a race condition, @@ -880,8 +905,8 @@ type awsInstance struct { deviceMappings map[mountDevice]string } -func newAWSInstance(ec2 EC2, awsID, nodeName string, availabilityZone string) *awsInstance { - self := &awsInstance{ec2: ec2, awsID: awsID, nodeName: nodeName, availabilityZone: availabilityZone} +func newAWSInstance(ec2 EC2, awsID, nodeName, availabilityZone, instanceType string) *awsInstance { + self := &awsInstance{ec2: ec2, awsID: awsID, nodeName: nodeName, availabilityZone: availabilityZone, instanceType: instanceType} // We lazy-init deviceMappings self.deviceMappings = nil @@ -1157,8 +1182,12 @@ func (s *AWSCloud) getSelfAWSInstance() (*awsInstance, error) { if err != nil { return nil, fmt.Errorf("error fetching availability zone from ec2 metadata service: %v", err) } + instanceType, err := getInstanceType(s.metadata) + if err != nil { + return nil, fmt.Errorf("error fetching instance type from ec2 metadata service: %v", err) + } - i = newAWSInstance(s.ec2, instanceId, privateDnsName, availabilityZone) + i = newAWSInstance(s.ec2, instanceId, privateDnsName, availabilityZone, instanceType) s.selfAWSInstance = i } @@ -1180,7 +1209,7 @@ func (aws *AWSCloud) getAwsInstance(nodeName string) (*awsInstance, error) { return nil, fmt.Errorf("error finding instance %s: %v", nodeName, err) } - awsInstance = newAWSInstance(aws.ec2, orEmpty(instance.InstanceId), orEmpty(instance.PrivateDnsName), orEmpty(instance.Placement.AvailabilityZone)) + awsInstance = newAWSInstance(aws.ec2, orEmpty(instance.InstanceId), orEmpty(instance.PrivateDnsName), orEmpty(instance.Placement.AvailabilityZone), orEmpty(instance.InstanceType)) } return awsInstance, nil diff --git a/pkg/cloudprovider/providers/fake/fake.go b/pkg/cloudprovider/providers/fake/fake.go index fdcc9bcb175..498dce53c32 100644 --- a/pkg/cloudprovider/providers/fake/fake.go +++ b/pkg/cloudprovider/providers/fake/fake.go @@ -52,6 +52,7 @@ type FakeCloud struct { Calls []string Addresses []api.NodeAddress ExtID map[string]string + InstanceTypes map[string]string Machines []string NodeResources *api.NodeResources ClusterList []string @@ -189,6 +190,12 @@ func (f *FakeCloud) InstanceID(instance string) (string, error) { return f.ExtID[instance], nil } +// InstanceType returns the type of the specified instance. +func (f *FakeCloud) InstanceType(instance string) (string, error) { + f.addCall("instance-type") + return f.InstanceTypes[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/providers/gce/gce.go b/pkg/cloudprovider/providers/gce/gce.go index 3f9f542afe6..f7bd672fb14 100644 --- a/pkg/cloudprovider/providers/gce/gce.go +++ b/pkg/cloudprovider/providers/gce/gce.go @@ -1797,6 +1797,15 @@ func (gce *GCECloud) InstanceID(instanceName string) (string, error) { return gce.projectID + "/" + instance.Zone + "/" + instance.Name, nil } +// InstanceType returns the type of the specified instance. +func (gce *GCECloud) InstanceType(instanceName string) (string, error) { + instance, err := gce.getInstanceByName(instanceName) + if err != nil { + return "", err + } + return instance.Type, nil +} + // List is an implementation of Instances.List. func (gce *GCECloud) List(filter string) ([]string, error) { var instances []string @@ -2151,6 +2160,7 @@ type gceInstance struct { Name string ID uint64 Disks []*compute.AttachedDisk + Type string } type gceDisk struct { @@ -2185,7 +2195,7 @@ func (gce *GCECloud) getInstancesByNames(names []string) ([]*gceInstance, error) // Add the filter for hosts listCall = listCall.Filter("name eq (" + strings.Join(remaining, "|") + ")") - listCall = listCall.Fields("items(name,id,disks)") + listCall = listCall.Fields("items(name,id,disks,machineType)") res, err := listCall.Do() if err != nil { @@ -2199,6 +2209,7 @@ func (gce *GCECloud) getInstancesByNames(names []string) ([]*gceInstance, error) Name: name, ID: i.Id, Disks: i.Disks, + Type: lastComponent(i.MachineType), } instances[name] = instance } @@ -2236,6 +2247,7 @@ func (gce *GCECloud) getInstanceByName(name string) (*gceInstance, error) { Name: res.Name, ID: res.Id, Disks: res.Disks, + Type: lastComponent(res.MachineType), }, nil } diff --git a/pkg/cloudprovider/providers/mesos/mesos.go b/pkg/cloudprovider/providers/mesos/mesos.go index 606ffad0e7f..20285843b65 100644 --- a/pkg/cloudprovider/providers/mesos/mesos.go +++ b/pkg/cloudprovider/providers/mesos/mesos.go @@ -216,6 +216,11 @@ func (c *MesosCloud) InstanceID(name string) (string, error) { return "", nil } +// InstanceType returns the type of the specified instance. +func (c *MesosCloud) InstanceType(name string) (string, error) { + return "", nil +} + func (c *MesosCloud) listNodes() (map[string]*slaveNode, error) { //TODO(jdef) use a timeout here? 15s? ctx, cancel := context.WithCancel(context.TODO()) diff --git a/pkg/cloudprovider/providers/openstack/openstack.go b/pkg/cloudprovider/providers/openstack/openstack.go index 86ff39addab..2a9376c2b78 100644 --- a/pkg/cloudprovider/providers/openstack/openstack.go +++ b/pkg/cloudprovider/providers/openstack/openstack.go @@ -480,6 +480,11 @@ func (i *Instances) InstanceID(name string) (string, error) { return "/" + srv.ID, nil } +// InstanceType returns the type of the specified instance. +func (i *Instances) InstanceType(name string) (string, error) { + return "", nil +} + func (os *OpenStack) Clusters() (cloudprovider.Clusters, bool) { return nil, false } diff --git a/pkg/cloudprovider/providers/ovirt/ovirt.go b/pkg/cloudprovider/providers/ovirt/ovirt.go index ba30633d767..24b47c85284 100644 --- a/pkg/cloudprovider/providers/ovirt/ovirt.go +++ b/pkg/cloudprovider/providers/ovirt/ovirt.go @@ -193,6 +193,11 @@ func (v *OVirtCloud) InstanceID(name string) (string, error) { return "/" + instance.UUID, err } +// InstanceType returns the type of the specified instance. +func (v *OVirtCloud) InstanceType(name string) (string, error) { + return "", nil +} + 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/providers/rackspace/rackspace.go b/pkg/cloudprovider/providers/rackspace/rackspace.go index 6cb95dbdb14..fbc0d7bff04 100644 --- a/pkg/cloudprovider/providers/rackspace/rackspace.go +++ b/pkg/cloudprovider/providers/rackspace/rackspace.go @@ -344,6 +344,11 @@ func (i *Instances) InstanceID(name string) (string, error) { return "", nil } +// InstanceType returns the type of the specified instance. +func (i *Instances) InstanceType(name string) (string, error) { + return "", nil +} + func (i *Instances) AddSSHKeyToAllInstances(user string, keyData []byte) error { return errors.New("unimplemented") } diff --git a/pkg/kubelet/kubelet.go b/pkg/kubelet/kubelet.go index f66fad114ef..be75dd49ba4 100644 --- a/pkg/kubelet/kubelet.go +++ b/pkg/kubelet/kubelet.go @@ -1010,6 +1010,14 @@ func (kl *Kubelet) initialNodeStatus() (*api.Node, error) { return nil, err } + instanceType, err := instances.InstanceType(kl.nodeName) + if err != nil { + return nil, err + } + if instanceType != "" { + glog.Infof("Adding node label from cloud provider: %s=%s", unversioned.LabelInstanceType, instanceType) + node.ObjectMeta.Labels[unversioned.LabelInstanceType] = instanceType + } // If the cloud has zone information, label the node with the zone information zones, ok := kl.cloud.Zones() if ok {