Merge pull request #4434 from ddysher/node-hostip

Node addresses
This commit is contained in:
Brian Grant 2015-03-05 10:33:15 -08:00
commit 9fcb48cab6
14 changed files with 189 additions and 51 deletions

View File

@ -789,12 +789,12 @@ type NodeSpec struct {
// NodeStatus is information about the current status of a node.
type NodeStatus struct {
// Queried from cloud provider, if available.
HostIP string `json:"hostIP,omitempty"`
// NodePhase is the current lifecycle phase of the node.
Phase NodePhase `json:"phase,omitempty"`
// Conditions is an array of current node conditions.
Conditions []NodeCondition `json:"conditions,omitempty"`
// Queried from cloud provider, if available.
Addresses []NodeAddress `json:"addresses,omitempty"`
}
type NodePhase string
@ -830,6 +830,22 @@ type NodeCondition struct {
Message string `json:"message,omitempty"`
}
type NodeAddressType string
// These are valid address types of node. NodeLegacyHostIP is used to transit
// from out-dated HostIP field to NodeAddress.
const (
NodeLegacyHostIP NodeAddressType = "LegacyHostIP"
NodeHostName NodeAddressType = "Hostname"
NodeExternalIP NodeAddressType = "ExternalIP"
NodeInternalIP NodeAddressType = "InternalIP"
)
type NodeAddress struct {
Type NodeAddressType `json:"type"`
Address string `json:"address"`
}
// NodeResources is an object for conveying resource information about a node.
// see https://github.com/GoogleCloudPlatform/kubernetes/blob/master/docs/resources.md for more details.
// TODO: Use ResourceList instead?

View File

@ -696,8 +696,15 @@ func init() {
if err := s.Convert(&in.Status.Conditions, &out.Status.Conditions, 0); err != nil {
return err
}
if err := s.Convert(&in.Status.Addresses, &out.Status.Addresses, 0); err != nil {
return err
}
out.HostIP = in.Status.HostIP
for _, address := range in.Status.Addresses {
if address.Type == newer.NodeLegacyHostIP {
out.HostIP = address.Address
}
}
out.PodCIDR = in.Spec.PodCIDR
out.ExternalID = in.Spec.ExternalID
return s.Convert(&in.Spec.Capacity, &out.NodeResources.Capacity, 0)
@ -718,12 +725,19 @@ func init() {
if err := s.Convert(&in.Status.Conditions, &out.Status.Conditions, 0); err != nil {
return err
}
if err := s.Convert(&in.Status.Addresses, &out.Status.Addresses, 0); err != nil {
return err
}
out.Status.HostIP = in.HostIP
if in.HostIP != "" {
out.Status.Addresses = append(out.Status.Addresses,
newer.NodeAddress{Type: newer.NodeLegacyHostIP, Address: 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 {
if err := s.Convert(&in.TypeMeta, &out.TypeMeta, 0); err != nil {
return err
@ -748,6 +762,7 @@ func init() {
}
return nil
},
func(in *Namespace, out *newer.Namespace, s conversion.Scope) error {
if err := s.Convert(&in.TypeMeta, &out.TypeMeta, 0); err != nil {
return err
@ -763,6 +778,7 @@ func init() {
}
return nil
},
func(in *newer.LimitRangeSpec, out *LimitRangeSpec, s conversion.Scope) error {
*out = LimitRangeSpec{}
out.Limits = make([]LimitRangeItem, len(in.Limits), len(in.Limits))
@ -783,6 +799,7 @@ func init() {
}
return nil
},
func(in *newer.LimitRangeItem, out *LimitRangeItem, s conversion.Scope) error {
*out = LimitRangeItem{}
out.Type = LimitType(in.Type)
@ -805,6 +822,7 @@ func init() {
}
return nil
},
func(in *newer.ResourceQuota, out *ResourceQuota, s conversion.Scope) error {
if err := s.Convert(&in.TypeMeta, &out.TypeMeta, 0); err != nil {
return err
@ -835,6 +853,7 @@ func init() {
}
return nil
},
func(in *newer.ResourceQuotaUsage, out *ResourceQuotaUsage, s conversion.Scope) error {
if err := s.Convert(&in.TypeMeta, &out.TypeMeta, 0); err != nil {
return err
@ -859,6 +878,7 @@ func init() {
}
return nil
},
func(in *newer.ResourceQuotaSpec, out *ResourceQuotaSpec, s conversion.Scope) error {
*out = ResourceQuotaSpec{}
if err := s.Convert(&in.Hard, &out.Hard, 0); err != nil {
@ -873,6 +893,7 @@ func init() {
}
return nil
},
func(in *newer.ResourceQuotaStatus, out *ResourceQuotaStatus, s conversion.Scope) error {
*out = ResourceQuotaStatus{}
if err := s.Convert(&in.Hard, &out.Hard, 0); err != nil {
@ -893,6 +914,7 @@ func init() {
}
return nil
},
// Object ID <-> Name
// TODO: amend the conversion package to allow overriding specific fields.
func(in *ObjectReference, out *newer.ObjectReference, s conversion.Scope) error {
@ -1133,6 +1155,7 @@ func init() {
out.TimeoutSeconds = in.TimeoutSeconds
return nil
},
func(in *newer.Endpoints, out *Endpoints, s conversion.Scope) error {
if err := s.Convert(&in.TypeMeta, &out.TypeMeta, 0); err != nil {
return err

View File

@ -623,6 +623,8 @@ type NodeStatus struct {
Phase NodePhase `json:"phase,omitempty" description:"node phase is the current lifecycle phase of the node"`
// Conditions is an array of current node conditions.
Conditions []NodeCondition `json:"conditions,omitempty" description:"conditions is an array of current node conditions"`
// Queried from cloud provider, if available.
Addresses []NodeAddress `json:"addresses,omitempty" description:"list of addresses reachable to the node"`
}
type NodePhase string
@ -658,6 +660,20 @@ type NodeCondition struct {
Message string `json:"message,omitempty" description:"human readable message indicating details about last transition"`
}
type NodeAddressType string
// These are valid address types of node.
const (
NodeHostName NodeAddressType = "Hostname"
NodeExternalIP NodeAddressType = "ExternalIP"
NodeInternalIP NodeAddressType = "InternalIP"
)
type NodeAddress struct {
Type NodeAddressType `json:"type" description:"type of the node address, e.g. external ip, internal ip, hostname, etc"`
Address string `json:"address" description:"string representation of the address"`
}
// NodeResources represents resources on a Kubernetes system node
// see https://github.com/GoogleCloudPlatform/kubernetes/blob/master/docs/resources.md for more details.
type NodeResources struct {
@ -680,6 +696,7 @@ type ResourceList map[ResourceName]util.IntOrString
// The name of the minion according to etcd is in ID.
type Minion struct {
TypeMeta `json:",inline"`
// DEPRECATED: Use Status.Addresses instead.
// Queried from cloud provider, if available.
HostIP string `json:"hostIP,omitempty" description:"IP address of the node"`
// Resources available on the node

View File

@ -616,8 +616,15 @@ func init() {
if err := s.Convert(&in.Status.Conditions, &out.Status.Conditions, 0); err != nil {
return err
}
if err := s.Convert(&in.Status.Addresses, &out.Status.Addresses, 0); err != nil {
return err
}
out.HostIP = in.Status.HostIP
for _, address := range in.Status.Addresses {
if address.Type == newer.NodeLegacyHostIP {
out.HostIP = address.Address
}
}
out.PodCIDR = in.Spec.PodCIDR
out.ExternalID = in.Spec.ExternalID
return s.Convert(&in.Spec.Capacity, &out.NodeResources.Capacity, 0)
@ -638,12 +645,19 @@ func init() {
if err := s.Convert(&in.Status.Conditions, &out.Status.Conditions, 0); err != nil {
return err
}
if err := s.Convert(&in.Status.Addresses, &out.Status.Addresses, 0); err != nil {
return err
}
out.Status.HostIP = in.HostIP
if in.HostIP != "" {
out.Status.Addresses = append(out.Status.Addresses,
newer.NodeAddress{Type: newer.NodeLegacyHostIP, Address: 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 {
if err := s.Convert(&in.TypeMeta, &out.TypeMeta, 0); err != nil {
return err
@ -668,6 +682,7 @@ func init() {
}
return nil
},
func(in *Namespace, out *newer.Namespace, s conversion.Scope) error {
if err := s.Convert(&in.TypeMeta, &out.TypeMeta, 0); err != nil {
return err
@ -683,6 +698,7 @@ func init() {
}
return nil
},
func(in *newer.LimitRangeSpec, out *LimitRangeSpec, s conversion.Scope) error {
*out = LimitRangeSpec{}
out.Limits = make([]LimitRangeItem, len(in.Limits), len(in.Limits))
@ -703,6 +719,7 @@ func init() {
}
return nil
},
func(in *newer.LimitRangeItem, out *LimitRangeItem, s conversion.Scope) error {
*out = LimitRangeItem{}
out.Type = LimitType(in.Type)
@ -725,6 +742,7 @@ func init() {
}
return nil
},
func(in *newer.ResourceQuota, out *ResourceQuota, s conversion.Scope) error {
if err := s.Convert(&in.TypeMeta, &out.TypeMeta, 0); err != nil {
return err
@ -755,6 +773,7 @@ func init() {
}
return nil
},
func(in *newer.ResourceQuotaUsage, out *ResourceQuotaUsage, s conversion.Scope) error {
if err := s.Convert(&in.TypeMeta, &out.TypeMeta, 0); err != nil {
return err
@ -779,6 +798,7 @@ func init() {
}
return nil
},
func(in *newer.ResourceQuotaSpec, out *ResourceQuotaSpec, s conversion.Scope) error {
*out = ResourceQuotaSpec{}
if err := s.Convert(&in.Hard, &out.Hard, 0); err != nil {
@ -793,6 +813,7 @@ func init() {
}
return nil
},
func(in *newer.ResourceQuotaStatus, out *ResourceQuotaStatus, s conversion.Scope) error {
*out = ResourceQuotaStatus{}
if err := s.Convert(&in.Hard, &out.Hard, 0); err != nil {
@ -813,6 +834,7 @@ func init() {
}
return nil
},
// Object ID <-> Name
// TODO: amend the conversion package to allow overriding specific fields.
func(in *ObjectReference, out *newer.ObjectReference, s conversion.Scope) error {
@ -1049,6 +1071,7 @@ func init() {
out.TimeoutSeconds = in.TimeoutSeconds
return nil
},
func(in *newer.Endpoints, out *Endpoints, s conversion.Scope) error {
if err := s.Convert(&in.TypeMeta, &out.TypeMeta, 0); err != nil {
return err

View File

@ -587,6 +587,8 @@ type NodeStatus struct {
Phase NodePhase `json:"phase,omitempty" description:"node phase is the current lifecycle phase of the node"`
// Conditions is an array of current node conditions.
Conditions []NodeCondition `json:"conditions,omitempty" description:"conditions is an array of current node conditions"`
// Queried from cloud provider, if available.
Addresses []NodeAddress `json:"addresses,omitempty" description:"list of addresses reachable to the node"`
}
type NodePhase string
@ -622,6 +624,20 @@ type NodeCondition struct {
Message string `json:"message,omitempty" description:"human readable message indicating details about last transition"`
}
type NodeAddressType string
// These are valid address types of node.
const (
NodeHostName NodeAddressType = "Hostname"
NodeExternalIP NodeAddressType = "ExternalIP"
NodeInternalIP NodeAddressType = "InternalIP"
)
type NodeAddress struct {
Type NodeAddressType `json:"type" description:"type of the node address, e.g. external ip, internal ip, hostname, etc"`
Address string `json:"address" description:"string representation of the address"`
}
// NodeResources represents resources on a Kubernetes system node
// see https://github.com/GoogleCloudPlatform/kubernetes/blob/master/docs/resources.md for more details.
type NodeResources struct {
@ -644,12 +660,13 @@ type ResourceList map[ResourceName]util.IntOrString
// The name of the minion according to etcd is in ID.
type Minion struct {
TypeMeta `json:",inline"`
// DEPRECATED: Use Status.Addresses instead.
// Queried from cloud provider, if available.
HostIP string `json:"hostIP,omitempty" description:"IP address of the node"`
// Pod IP range assigned to the node
PodCIDR string `json:"cidr,omitempty" description:"IP range assigned to the node"`
// Resources available on the node
NodeResources NodeResources `json:"resources,omitempty" description:"characterization of node resources"`
// Pod IP range assigned to the node
PodCIDR string `json:"cidr,omitempty" description:"IP range assigned to the node"`
// Status describes the current status of a node
Status NodeStatus `json:"status,omitempty" description:"current status of node"`
// Labels for the node

View File

@ -820,12 +820,12 @@ type NodeSpec struct {
// NodeStatus is information about the current status of a node.
type NodeStatus struct {
// Queried from cloud provider, if available.
HostIP string `json:"hostIP,omitempty"`
// NodePhase is the current lifecycle phase of the node.
Phase NodePhase `json:"phase,omitempty"`
// Conditions is an array of current node conditions.
Conditions []NodeCondition `json:"conditions,omitempty"`
// Queried from cloud provider, if available.
Addresses []NodeAddress `json:"addresses,omitempty"`
}
type NodePhase string
@ -861,6 +861,20 @@ type NodeCondition struct {
Message string `json:"message,omitempty"`
}
type NodeAddressType string
// These are valid address type of node.
const (
NodeHostName NodeAddressType = "Hostname"
NodeExternalIP NodeAddressType = "ExternalIP"
NodeInternalIP NodeAddressType = "InternalIP"
)
type NodeAddress struct {
Type NodeAddressType `json:"type"`
Address string `json:"address"`
}
// ResourceName is the name identifying various resources in a ResourceList.
type ResourceName string

View File

@ -1919,7 +1919,9 @@ func TestValidateMinion(t *testing.T) {
Labels: validSelector,
},
Status: api.NodeStatus{
HostIP: "something",
Addresses: []api.NodeAddress{
{Type: api.NodeLegacyHostIP, Address: "something"},
},
},
},
{
@ -1927,7 +1929,9 @@ func TestValidateMinion(t *testing.T) {
Name: "abc",
},
Status: api.NodeStatus{
HostIP: "something",
Addresses: []api.NodeAddress{
{Type: api.NodeLegacyHostIP, Address: "something"},
},
},
},
}
@ -1944,7 +1948,7 @@ func TestValidateMinion(t *testing.T) {
Labels: validSelector,
},
Status: api.NodeStatus{
HostIP: "something",
Addresses: []api.NodeAddress{},
},
},
"invalid-labels": {
@ -2073,7 +2077,9 @@ func TestValidateMinionUpdate(t *testing.T) {
Labels: map[string]string{"bar": "foo"},
},
Status: api.NodeStatus{
HostIP: "1.2.3.4",
Addresses: []api.NodeAddress{
{Type: api.NodeLegacyHostIP, Address: "1.2.3.4"},
},
},
}, api.Node{
ObjectMeta: api.ObjectMeta{

View File

@ -730,9 +730,6 @@ func TestCreateMinion(t *testing.T) {
ObjectMeta: api.ObjectMeta{
Name: "minion-1",
},
Status: api.NodeStatus{
HostIP: "123.321.456.654",
},
Spec: api.NodeSpec{
Capacity: api.ResourceList{
api.ResourceCPU: resource.MustParse("1000m"),

View File

@ -108,7 +108,7 @@ func (s *NodeController) Run(period time.Duration, syncNodeList, syncNodeStatus
glog.Errorf("Error loading initial static nodes: %v", err)
}
}
nodes, err = s.PopulateIPs(nodes)
nodes, err = s.PopulateAddresses(nodes)
if err != nil {
glog.Errorf("Error getting nodes ips: %v", err)
}
@ -220,7 +220,7 @@ func (s *NodeController) SyncNodeStatus() error {
return err
}
nodes = s.DoChecks(nodes)
nodes, err = s.PopulateIPs(nodes)
nodes, err = s.PopulateAddresses(nodes)
if err != nil {
return err
}
@ -264,8 +264,8 @@ func latestReadyTime(node *api.Node) util.Time {
return readyTime
}
// PopulateIPs queries IPs for given list of nodes.
func (s *NodeController) PopulateIPs(nodes *api.NodeList) (*api.NodeList, error) {
// PopulateAddresses queries Address for given list of nodes.
func (s *NodeController) PopulateAddresses(nodes *api.NodeList) (*api.NodeList, error) {
if s.isRunningCloudProvider() {
instances, ok := s.cloud.Instances()
if !ok {
@ -277,7 +277,8 @@ func (s *NodeController) PopulateIPs(nodes *api.NodeList) (*api.NodeList, error)
if err != nil {
glog.Errorf("error getting instance ip address for %s: %v", node.Name, err)
} else {
node.Status.HostIP = hostIP.String()
address := api.NodeAddress{Type: api.NodeLegacyHostIP, Address: hostIP.String()}
node.Status.Addresses = append(node.Status.Addresses, address)
}
}
} else {
@ -285,7 +286,8 @@ func (s *NodeController) PopulateIPs(nodes *api.NodeList) (*api.NodeList, error)
node := &nodes.Items[i]
addr := net.ParseIP(node.Name)
if addr != nil {
node.Status.HostIP = node.Name
address := api.NodeAddress{Type: api.NodeLegacyHostIP, Address: addr.String()}
node.Status.Addresses = append(node.Status.Addresses, address)
} else {
addrs, err := lookupIP(node.Name)
if err != nil {
@ -293,7 +295,8 @@ func (s *NodeController) PopulateIPs(nodes *api.NodeList) (*api.NodeList, error)
} else if len(addrs) == 0 {
glog.Errorf("No ip address for node %v", node.Name)
} else {
node.Status.HostIP = addrs[0].String()
address := api.NodeAddress{Type: api.NodeLegacyHostIP, Address: addrs[0].String()}
node.Status.Addresses = append(node.Status.Addresses, address)
}
}
}

View File

@ -621,35 +621,37 @@ func TestHealthCheckNode(t *testing.T) {
}
}
func TestPopulateNodeIPs(t *testing.T) {
func TestPopulateNodeAddresses(t *testing.T) {
table := []struct {
nodes *api.NodeList
fakeCloud *fake_cloud.FakeCloud
expectedFail bool
expectedIP string
nodes *api.NodeList
fakeCloud *fake_cloud.FakeCloud
expectedFail bool
expectedAddresses []api.NodeAddress
}{
{
nodes: &api.NodeList{Items: []api.Node{*newNode("node0"), *newNode("node1")}},
fakeCloud: &fake_cloud.FakeCloud{IP: net.ParseIP("1.2.3.4")},
expectedIP: "1.2.3.4",
nodes: &api.NodeList{Items: []api.Node{*newNode("node0"), *newNode("node1")}},
fakeCloud: &fake_cloud.FakeCloud{IP: net.ParseIP("1.2.3.4")},
expectedAddresses: []api.NodeAddress{
{Type: api.NodeLegacyHostIP, Address: "1.2.3.4"},
},
},
{
nodes: &api.NodeList{Items: []api.Node{*newNode("node0"), *newNode("node1")}},
fakeCloud: &fake_cloud.FakeCloud{Err: ErrQueryIPAddress},
expectedIP: "",
nodes: &api.NodeList{Items: []api.Node{*newNode("node0"), *newNode("node1")}},
fakeCloud: &fake_cloud.FakeCloud{Err: ErrQueryIPAddress},
expectedAddresses: nil,
},
}
for _, item := range table {
nodeController := NewNodeController(item.fakeCloud, ".*", nil, nil, nil, nil, 10, time.Minute)
result, err := nodeController.PopulateIPs(item.nodes)
result, err := nodeController.PopulateAddresses(item.nodes)
// In case of IP querying error, we should continue.
if err != nil {
t.Errorf("unexpected error: %v", err)
}
for _, node := range result.Items {
if node.Status.HostIP != item.expectedIP {
t.Errorf("expect HostIP %s, got %s", item.expectedIP, node.Status.HostIP)
if !reflect.DeepEqual(item.expectedAddresses, node.Status.Addresses) {
t.Errorf("expect HostIP %s, got %s", item.expectedAddresses, node.Status.Addresses)
}
}
}
@ -1031,7 +1033,9 @@ func TestSyncNodeStatus(t *testing.T) {
Reason: "Node health check succeeded: kubelet /healthz endpoint returns ok",
},
},
HostIP: "1.2.3.4",
Addresses: []api.NodeAddress{
{Type: api.NodeLegacyHostIP, Address: "1.2.3.4"},
},
},
},
{
@ -1044,7 +1048,9 @@ func TestSyncNodeStatus(t *testing.T) {
Reason: "Node health check succeeded: kubelet /healthz endpoint returns ok",
},
},
HostIP: "1.2.3.4",
Addresses: []api.NodeAddress{
{Type: api.NodeLegacyHostIP, Address: "1.2.3.4"},
},
},
},
},

View File

@ -140,6 +140,23 @@ func (p *PodCache) clearNodeStatus() {
p.currentNodes = map[objKey]api.NodeStatus{}
}
func (p *PodCache) getHostAddress(addresses []api.NodeAddress) string {
addressMap := make(map[api.NodeAddressType][]api.NodeAddress)
for i := range addresses {
addressMap[addresses[i].Type] = append(addressMap[addresses[i].Type], addresses[i])
}
if addresses, ok := addressMap[api.NodeLegacyHostIP]; ok {
return addresses[0].Address
}
if addresses, ok := addressMap[api.NodeInternalIP]; ok {
return addresses[0].Address
}
if addresses, ok := addressMap[api.NodeExternalIP]; ok {
return addresses[0].Address
}
return ""
}
// TODO: once Host gets moved to spec, this can take a podSpec + metadata instead of an
// entire pod?
func (p *PodCache) updatePodStatus(pod *api.Pod) error {
@ -193,7 +210,7 @@ func (p *PodCache) computePodStatus(pod *api.Pod) (api.PodStatus, error) {
if err != nil {
glog.V(5).Infof("error getting pod %s status: %v, retry later", pod.Name, err)
} else {
newStatus.HostIP = nodeStatus.HostIP
newStatus.HostIP = p.getHostAddress(nodeStatus.Addresses)
newStatus.Info = result.Status.Info
newStatus.PodIP = result.Status.PodIP
if newStatus.Info == nil {

View File

@ -224,7 +224,9 @@ func makeHealthyNode(name string, ip string) *api.Node {
return &api.Node{
ObjectMeta: api.ObjectMeta{Name: name},
Status: api.NodeStatus{
HostIP: ip,
Addresses: []api.NodeAddress{
{Type: api.NodeLegacyHostIP, Address: ip},
},
Conditions: []api.NodeCondition{
{Type: api.NodeReady, Status: api.ConditionFull},
},

View File

@ -121,13 +121,6 @@ func (rs *REST) Update(ctx api.Context, obj runtime.Object) (runtime.Object, boo
return nil, false, err
}
// This is hacky, but minion HostIP has been moved from spec to status since v1beta2. When updating
// minion from older client, HostIP will be lost. Fix it here temporarily until we strip out status
// info from user input.
if minion.Status.HostIP == "" {
minion.Status.HostIP = oldMinion.Status.HostIP
}
if errs := validation.ValidateMinionUpdate(oldMinion, minion); len(errs) > 0 {
return nil, false, kerrors.NewInvalid("minion", minion.Name, errs)
}

View File

@ -121,7 +121,9 @@ func TestMinionRegistryValidatesCreate(t *testing.T) {
Labels: validSelector,
},
Status: api.NodeStatus{
HostIP: "something",
Addresses: []api.NodeAddress{
{Type: api.NodeLegacyHostIP, Address: "something"},
},
},
},
"invalid-labels": {
@ -158,7 +160,9 @@ func TestCreate(t *testing.T) {
// valid
&api.Node{
Status: api.NodeStatus{
HostIP: "something",
Addresses: []api.NodeAddress{
{Type: api.NodeLegacyHostIP, Address: "something"},
},
},
},
// invalid