mirror of https://github.com/k3s-io/k3s
Merge pull request #4383 from simon3z/instances-id
cloudprovider: add instance id to NodeSpecpull/6/head
commit
a862c37b29
|
@ -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.
|
||||
|
|
|
@ -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 {
|
||||
|
|
|
@ -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.
|
||||
|
|
|
@ -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 {
|
||||
|
|
|
@ -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.
|
||||
|
|
|
@ -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.
|
||||
|
|
|
@ -122,6 +122,28 @@ func (aws *AWSCloud) Zones() (cloudprovider.Zones, bool) {
|
|||
|
||||
// IPAddress is an implementation of Instances.IPAddress.
|
||||
func (aws *AWSCloud) IPAddress(name string) (net.IP, error) {
|
||||
inst, err := aws.getInstancesByDnsName(name)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
ip := net.ParseIP(inst.PrivateIpAddress)
|
||||
if ip == nil {
|
||||
return nil, fmt.Errorf("invalid network IP: %s", inst.PrivateIpAddress)
|
||||
}
|
||||
return ip, nil
|
||||
}
|
||||
|
||||
// ExternalID returns the cloud provider ID of the specified instance.
|
||||
func (aws *AWSCloud) ExternalID(name string) (string, error) {
|
||||
inst, err := aws.getInstancesByDnsName(name)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
return inst.InstanceId, nil
|
||||
}
|
||||
|
||||
// Return the instances matching the relevant private dns name.
|
||||
func (aws *AWSCloud) getInstancesByDnsName(name string) (*ec2.Instance, error) {
|
||||
f := ec2.NewFilter()
|
||||
f.Add("private-dns-name", name)
|
||||
|
||||
|
@ -142,12 +164,7 @@ func (aws *AWSCloud) IPAddress(name string) (net.IP, error) {
|
|||
return nil, fmt.Errorf("multiple instances found for host: %s", name)
|
||||
}
|
||||
|
||||
ipAddress := resp.Reservations[0].Instances[0].PrivateIpAddress
|
||||
ip := net.ParseIP(ipAddress)
|
||||
if ip == nil {
|
||||
return nil, fmt.Errorf("invalid network IP: %s", ipAddress)
|
||||
}
|
||||
return ip, nil
|
||||
return &resp.Reservations[0].Instances[0], nil
|
||||
}
|
||||
|
||||
// Return a list of instances matching regex string.
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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 {
|
||||
|
|
|
@ -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) {
|
||||
|
|
|
@ -299,21 +299,39 @@ func canonicalizeInstanceName(name string) string {
|
|||
return name
|
||||
}
|
||||
|
||||
// IPAddress is an implementation of Instances.IPAddress.
|
||||
func (gce *GCECloud) IPAddress(instance string) (net.IP, error) {
|
||||
instance = canonicalizeInstanceName(instance)
|
||||
res, err := gce.service.Instances.Get(gce.projectID, gce.zone, instance).Do()
|
||||
// Return the instances matching the relevant name.
|
||||
func (gce *GCECloud) getInstanceByName(name string) (*compute.Instance, error) {
|
||||
name = canonicalizeInstanceName(name)
|
||||
res, err := gce.service.Instances.Get(gce.projectID, gce.zone, name).Do()
|
||||
if err != nil {
|
||||
glog.Errorf("Failed to retrieve TargetInstance resource for instance:%s", instance)
|
||||
glog.Errorf("Failed to retrieve TargetInstance resource for instance:%s", name)
|
||||
return nil, err
|
||||
}
|
||||
ip := net.ParseIP(res.NetworkInterfaces[0].AccessConfigs[0].NatIP)
|
||||
return res, nil
|
||||
}
|
||||
|
||||
// IPAddress is an implementation of Instances.IPAddress.
|
||||
func (gce *GCECloud) IPAddress(instance string) (net.IP, error) {
|
||||
inst, err := gce.getInstanceByName(instance)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
ip := net.ParseIP(inst.NetworkInterfaces[0].AccessConfigs[0].NatIP)
|
||||
if ip == nil {
|
||||
return nil, fmt.Errorf("invalid network IP: %s", res.NetworkInterfaces[0].AccessConfigs[0].NatIP)
|
||||
return nil, fmt.Errorf("invalid network IP: %s", inst.NetworkInterfaces[0].AccessConfigs[0].NatIP)
|
||||
}
|
||||
return ip, nil
|
||||
}
|
||||
|
||||
// ExternalID returns the cloud provider ID of the specified instance.
|
||||
func (gce *GCECloud) ExternalID(instance string) (string, error) {
|
||||
inst, err := gce.getInstanceByName(instance)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
return string(inst.Id), nil
|
||||
}
|
||||
|
||||
// fqdnSuffix is hacky function to compute the delta between hostame and hostname -f.
|
||||
func fqdnSuffix() (string, error) {
|
||||
fullHostname, err := exec.Command("hostname", "-f").Output()
|
||||
|
|
|
@ -312,6 +312,15 @@ 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) {
|
||||
srv, err := getServerByName(i.compute, name)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
return srv.ID, nil
|
||||
}
|
||||
|
||||
func (i *Instances) GetNodeResources(name string) (*api.NodeResources, error) {
|
||||
glog.V(2).Infof("GetNodeResources(%v) called", name)
|
||||
|
||||
|
|
|
@ -25,6 +25,7 @@ import (
|
|||
"net/http"
|
||||
"net/url"
|
||||
"path"
|
||||
"sort"
|
||||
"strings"
|
||||
|
||||
"code.google.com/p/gcfg"
|
||||
|
@ -32,6 +33,14 @@ import (
|
|||
"github.com/GoogleCloudPlatform/kubernetes/pkg/cloudprovider"
|
||||
)
|
||||
|
||||
type OVirtInstance struct {
|
||||
UUID string
|
||||
Name string
|
||||
IPAddress string
|
||||
}
|
||||
|
||||
type OVirtInstanceMap map[string]OVirtInstance
|
||||
|
||||
type OVirtCloud struct {
|
||||
VmsRequest *url.URL
|
||||
HostsRequest *url.URL
|
||||
|
@ -48,8 +57,15 @@ type OVirtApiConfig struct {
|
|||
}
|
||||
}
|
||||
|
||||
type XmlVmAddress struct {
|
||||
Address string `xml:"address,attr"`
|
||||
}
|
||||
|
||||
type XmlVmInfo struct {
|
||||
UUID string `xml:"id,attr"`
|
||||
Name string `xml:"name"`
|
||||
Hostname string `xml:"guest_info>fqdn"`
|
||||
Addresses []XmlVmAddress `xml:"guest_info>ips>ip"`
|
||||
State string `xml:"status>state"`
|
||||
}
|
||||
|
||||
|
@ -115,16 +131,40 @@ func (v *OVirtCloud) Zones() (cloudprovider.Zones, bool) {
|
|||
}
|
||||
|
||||
// IPAddress returns the address of a particular machine instance
|
||||
func (v *OVirtCloud) IPAddress(instance string) (net.IP, error) {
|
||||
// since the instance now is the IP in the ovirt env, this is trivial no-op
|
||||
ip, err := net.LookupIP(instance)
|
||||
if err != nil || len(ip) < 1 {
|
||||
return nil, fmt.Errorf("cannot find ip address for: %s", instance)
|
||||
}
|
||||
return ip[0], nil
|
||||
func (v *OVirtCloud) IPAddress(name string) (net.IP, error) {
|
||||
instance, err := v.fetchInstance(name)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
func getInstancesFromXml(body io.Reader) ([]string, error) {
|
||||
var address net.IP
|
||||
|
||||
if instance.IPAddress != "" {
|
||||
address = net.ParseIP(instance.IPAddress)
|
||||
if address == nil {
|
||||
return nil, fmt.Errorf("couldn't parse address: %s", instance.IPAddress)
|
||||
}
|
||||
} else {
|
||||
resolved, err := net.LookupIP(name)
|
||||
if err != nil || len(resolved) < 1 {
|
||||
return nil, fmt.Errorf("couldn't lookup address: %s", name)
|
||||
}
|
||||
address = resolved[0]
|
||||
}
|
||||
|
||||
return address, nil
|
||||
}
|
||||
|
||||
// ExternalID returns the cloud provider ID of the specified instance.
|
||||
func (v *OVirtCloud) ExternalID(name string) (string, error) {
|
||||
instance, err := v.fetchInstance(name)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
return instance.UUID, nil
|
||||
}
|
||||
|
||||
func getInstancesFromXml(body io.Reader) (OVirtInstanceMap, error) {
|
||||
if body == nil {
|
||||
return nil, fmt.Errorf("ovirt rest-api response body is missing")
|
||||
}
|
||||
|
@ -140,20 +180,28 @@ func getInstancesFromXml(body io.Reader) ([]string, error) {
|
|||
return nil, err
|
||||
}
|
||||
|
||||
var instances []string
|
||||
instances := make(OVirtInstanceMap)
|
||||
|
||||
for _, vm := range vmlist.Vm {
|
||||
// Always return only vms that are up and running
|
||||
if vm.Hostname != "" && strings.ToLower(vm.State) == "up" {
|
||||
instances = append(instances, vm.Hostname)
|
||||
address := ""
|
||||
if len(vm.Addresses) > 0 {
|
||||
address = vm.Addresses[0].Address
|
||||
}
|
||||
|
||||
instances[vm.Hostname] = OVirtInstance{
|
||||
UUID: vm.UUID,
|
||||
Name: vm.Name,
|
||||
IPAddress: address,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return instances, nil
|
||||
}
|
||||
|
||||
// List enumerates the set of minions instances known by the cloud provider
|
||||
func (v *OVirtCloud) List(filter string) ([]string, error) {
|
||||
func (v *OVirtCloud) fetchAllInstances() (OVirtInstanceMap, error) {
|
||||
response, err := http.Get(v.VmsRequest.String())
|
||||
if err != nil {
|
||||
return nil, err
|
||||
|
@ -164,6 +212,41 @@ func (v *OVirtCloud) List(filter string) ([]string, error) {
|
|||
return getInstancesFromXml(response.Body)
|
||||
}
|
||||
|
||||
func (v *OVirtCloud) fetchInstance(name string) (*OVirtInstance, error) {
|
||||
allInstances, err := v.fetchAllInstances()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
instance, found := allInstances[name]
|
||||
if !found {
|
||||
return nil, fmt.Errorf("cannot find instance: %s", name)
|
||||
}
|
||||
|
||||
return &instance, nil
|
||||
}
|
||||
|
||||
func (m *OVirtInstanceMap) ListSortedNames() []string {
|
||||
var names []string
|
||||
|
||||
for k := range *m {
|
||||
names = append(names, k)
|
||||
}
|
||||
|
||||
sort.Strings(names)
|
||||
|
||||
return names
|
||||
}
|
||||
|
||||
// List enumerates the set of minions instances known by the cloud provider
|
||||
func (v *OVirtCloud) List(filter string) ([]string, error) {
|
||||
instances, err := v.fetchAllInstances()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return instances.ListSortedNames(), nil
|
||||
}
|
||||
|
||||
func (v *OVirtCloud) GetNodeResources(name string) (*api.NodeResources, error) {
|
||||
return nil, nil
|
||||
}
|
||||
|
|
|
@ -118,7 +118,9 @@ func TestOVirtCloudXmlParsing(t *testing.T) {
|
|||
if len(instances4) != 2 {
|
||||
t.Fatalf("Unexpected number of instance(s): %d", len(instances4))
|
||||
}
|
||||
if instances4[0] != "host1" || instances4[1] != "host3" {
|
||||
|
||||
names := instances4.ListSortedNames()
|
||||
if names[0] != "host1" || names[1] != "host3" {
|
||||
t.Fatalf("Unexpected instance(s): %s", instances4)
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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)
|
||||
|
||||
|
|
|
@ -99,8 +99,8 @@ func (v *VagrantCloud) Zones() (cloudprovider.Zones, bool) {
|
|||
return nil, false
|
||||
}
|
||||
|
||||
// IPAddress returns the address of a particular machine instance.
|
||||
func (v *VagrantCloud) IPAddress(instance string) (net.IP, error) {
|
||||
// getInstanceByAddress retuns
|
||||
func (v *VagrantCloud) getInstanceByAddress(address string) (*SaltMinion, error) {
|
||||
token, err := v.saltLogin()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
|
@ -112,11 +112,31 @@ func (v *VagrantCloud) IPAddress(instance string) (net.IP, error) {
|
|||
filteredMinions := v.saltMinionsByRole(minions, "kubernetes-pool")
|
||||
for _, minion := range filteredMinions {
|
||||
// Due to vagrant not running with a dedicated DNS setup, we return the IP address of a minion as its hostname at this time
|
||||
if minion.IP == instance {
|
||||
if minion.IP == address {
|
||||
return &minion, nil
|
||||
}
|
||||
}
|
||||
return nil, fmt.Errorf("unable to find instance for address: %s", address)
|
||||
}
|
||||
|
||||
// IPAddress returns the address of a particular machine instance.
|
||||
func (v *VagrantCloud) IPAddress(instance string) (net.IP, 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)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return net.ParseIP(minion.IP), nil
|
||||
}
|
||||
|
||||
// ExternalID returns the cloud provider ID of the specified instance.
|
||||
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)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
return nil, fmt.Errorf("unable to find IP address for instance: %s", instance)
|
||||
return minion.IP, nil
|
||||
}
|
||||
|
||||
// saltMinionsByRole filters a list of minions that have a matching role.
|
||||
|
|
Loading…
Reference in New Issue