Merge pull request #67739 from liggitt/hostname-override

Automatic merge from submit-queue (batch tested with PRs 67739, 65222). If you want to cherry-pick this change to another branch, please follow the instructions <a href="https://github.com/kubernetes/community/blob/master/contributors/devel/cherry-picks.md">here</a>.

Honor --hostname-override, report compatible hostname addresses with cloud provider

xref #67714

7828e5d made cloud providers authoritative for the addresses reported on Node objects, so that the addresses used by the node (and requested as SANs in serving certs) could be verified via cloud provider metadata.

This had the effect of no longer reporting addresses of type Hostname for Node objects for some cloud providers. Cloud providers that have the instance hostname available in metadata should add a `type: Hostname` address to node status. This is being tracked in #67714

This PR does a couple other things to ease the transition to authoritative cloud providers:
* if `--hostname-override` is set on the kubelet, make the kubelet report that `Hostname` address. if it can't be verified via cloud-provider metadata (for cert approval, etc), the kubelet deployer is responsible for fixing the situation by adjusting the kubelet configuration (as they were in 1.11 and previously)
* if `--hostname-override` is not set, *and* the cloud provider didn't report a Hostname address, *and* the auto-detected hostname matches one of the addresses the cloud provider *did* report, make the kubelet report that as a Hostname address. That lets the addresses remain verifiable via cloud provider metadata, while still including a `Hostname` address whenever possible.

/sig node
/sig cloud-provider

/cc @mikedanese

fyi @hh

```release-note
NONE
```
pull/8/head
Kubernetes Submit Queue 2018-08-28 12:31:00 -07:00 committed by GitHub
commit a26e1ddacc
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
4 changed files with 138 additions and 6 deletions

View File

@ -481,6 +481,7 @@ func NewMainKubelet(kubeCfg *kubeletconfiginternal.KubeletConfiguration,
klet := &Kubelet{
hostname: hostname,
hostnameOverridden: len(hostnameOverride) > 0,
nodeName: nodeName,
kubeClient: kubeDeps.KubeClient,
heartbeatClient: kubeDeps.HeartbeatClient,
@ -867,7 +868,11 @@ type serviceLister interface {
type Kubelet struct {
kubeletConfiguration kubeletconfiginternal.KubeletConfiguration
hostname string
// hostname is the hostname the kubelet detected or was given via flag/config
hostname string
// hostnameOverridden indicates the hostname was overridden via flag/config
hostnameOverridden bool
nodeName types.NodeName
runtimeCache kubecontainer.RuntimeCache
kubeClient clientset.Interface

View File

@ -490,7 +490,7 @@ func (kl *Kubelet) defaultNodeStatusFuncs() []func(*v1.Node) error {
}
var setters []func(n *v1.Node) error
setters = append(setters,
nodestatus.NodeAddress(kl.nodeIP, kl.nodeIPValidator, kl.hostname, kl.externalCloudProvider, kl.cloud, nodeAddressesFunc),
nodestatus.NodeAddress(kl.nodeIP, kl.nodeIPValidator, kl.hostname, kl.hostnameOverridden, kl.externalCloudProvider, kl.cloud, nodeAddressesFunc),
nodestatus.MachineInfo(string(kl.nodeName), kl.maxPods, kl.podsPerCore, kl.GetCachedMachineInfo, kl.containerManager.GetCapacity,
kl.containerManager.GetDevicePluginResourceCapacity, kl.containerManager.GetNodeAllocatableReservation, kl.recordEvent),
nodestatus.VersionInfo(kl.cadvisor.VersionInfo, kl.containerRuntime.Type, kl.containerRuntime.Version),

View File

@ -59,6 +59,7 @@ type Setter func(node *v1.Node) error
func NodeAddress(nodeIP net.IP, // typically Kubelet.nodeIP
validateNodeIPFunc func(net.IP) error, // typically Kubelet.nodeIPValidator
hostname string, // typically Kubelet.hostname
hostnameOverridden bool, // was the hostname force set?
externalCloudProvider bool, // typically Kubelet.externalCloudProvider
cloud cloudprovider.Interface, // typically Kubelet.cloud
nodeAddressesFunc func() ([]v1.NodeAddress, error), // typically Kubelet.cloudResourceSyncManager.NodeAddresses
@ -111,10 +112,38 @@ func NodeAddress(nodeIP net.IP, // typically Kubelet.nodeIP
return fmt.Errorf("failed to get node address from cloud provider that matches ip: %v", nodeIP)
}
// Only add a NodeHostName address if the cloudprovider did not specify any addresses.
// (we assume the cloudprovider is authoritative if it specifies any addresses)
if len(nodeAddresses) == 0 {
nodeAddresses = []v1.NodeAddress{{Type: v1.NodeHostName, Address: hostname}}
switch {
case len(nodeAddresses) == 0:
// the cloud provider didn't specify any addresses
nodeAddresses = append(nodeAddresses, v1.NodeAddress{Type: v1.NodeHostName, Address: hostname})
case !hasAddressType(nodeAddresses, v1.NodeHostName) && hasAddressValue(nodeAddresses, hostname):
// the cloud provider didn't specify an address of type Hostname,
// but the auto-detected hostname matched an address reported by the cloud provider,
// so we can add it and count on the value being verifiable via cloud provider metadata
nodeAddresses = append(nodeAddresses, v1.NodeAddress{Type: v1.NodeHostName, Address: hostname})
case hostnameOverridden:
// the hostname was force-set via flag/config.
// this means the hostname might not be able to be validated via cloud provider metadata,
// but was a choice by the kubelet deployer we should honor
var existingHostnameAddress *v1.NodeAddress
for i := range nodeAddresses {
if nodeAddresses[i].Type == v1.NodeHostName {
existingHostnameAddress = &nodeAddresses[i]
break
}
}
if existingHostnameAddress == nil {
// no existing Hostname address found, add it
glog.Warningf("adding overridden hostname of %v to cloudprovider-reported addresses", hostname)
nodeAddresses = append(nodeAddresses, v1.NodeAddress{Type: v1.NodeHostName, Address: hostname})
} else {
// override the Hostname address reported by the cloud provider
glog.Warningf("replacing cloudprovider-reported hostname of %v with overridden hostname of %v", existingHostnameAddress.Address, hostname)
existingHostnameAddress.Address = hostname
}
}
node.Status.Addresses = nodeAddresses
} else {
@ -163,6 +192,23 @@ func NodeAddress(nodeIP net.IP, // typically Kubelet.nodeIP
}
}
func hasAddressType(addresses []v1.NodeAddress, addressType v1.NodeAddressType) bool {
for _, address := range addresses {
if address.Type == addressType {
return true
}
}
return false
}
func hasAddressValue(addresses []v1.NodeAddress, addressValue string) bool {
for _, address := range addresses {
if address.Address == addressValue {
return true
}
}
return false
}
// MachineInfo returns a Setter that updates machine-related information on the node.
func MachineInfo(nodeName string,
maxPods int,

View File

@ -55,6 +55,7 @@ const (
func TestNodeAddress(t *testing.T) {
cases := []struct {
name string
hostnameOverride bool
nodeIP net.IP
nodeAddresses []v1.NodeAddress
expectedAddresses []v1.NodeAddress
@ -151,6 +152,85 @@ func TestNodeAddress(t *testing.T) {
expectedAddresses: nil,
shouldError: true,
},
{
name: "no cloud reported hostnames",
nodeAddresses: []v1.NodeAddress{},
expectedAddresses: []v1.NodeAddress{
{Type: v1.NodeHostName, Address: testKubeletHostname}, // detected hostname is auto-added in the absence of cloud-reported hostnames
},
shouldError: false,
},
{
name: "cloud reports hostname, no override",
nodeAddresses: []v1.NodeAddress{
{Type: v1.NodeInternalIP, Address: "10.1.1.1"},
{Type: v1.NodeExternalIP, Address: "55.55.55.55"},
{Type: v1.NodeHostName, Address: "cloud-host"},
},
expectedAddresses: []v1.NodeAddress{
{Type: v1.NodeInternalIP, Address: "10.1.1.1"},
{Type: v1.NodeExternalIP, Address: "55.55.55.55"},
{Type: v1.NodeHostName, Address: "cloud-host"}, // cloud-reported hostname wins over detected hostname
},
shouldError: false,
},
{
name: "cloud reports hostname, overridden",
nodeAddresses: []v1.NodeAddress{
{Type: v1.NodeInternalIP, Address: "10.1.1.1"},
{Type: v1.NodeHostName, Address: "cloud-host"},
{Type: v1.NodeExternalIP, Address: "55.55.55.55"},
},
expectedAddresses: []v1.NodeAddress{
{Type: v1.NodeInternalIP, Address: "10.1.1.1"},
{Type: v1.NodeHostName, Address: testKubeletHostname}, // hostname-override wins over cloud-reported hostname
{Type: v1.NodeExternalIP, Address: "55.55.55.55"},
},
hostnameOverride: true,
shouldError: false,
},
{
name: "cloud doesn't report hostname, no override, detected hostname mismatch",
nodeAddresses: []v1.NodeAddress{
{Type: v1.NodeInternalIP, Address: "10.1.1.1"},
{Type: v1.NodeExternalIP, Address: "55.55.55.55"},
},
expectedAddresses: []v1.NodeAddress{
{Type: v1.NodeInternalIP, Address: "10.1.1.1"},
{Type: v1.NodeExternalIP, Address: "55.55.55.55"},
// detected hostname is not auto-added if it doesn't match any cloud-reported addresses
},
shouldError: false,
},
{
name: "cloud doesn't report hostname, no override, detected hostname match",
nodeAddresses: []v1.NodeAddress{
{Type: v1.NodeInternalIP, Address: "10.1.1.1"},
{Type: v1.NodeExternalIP, Address: "55.55.55.55"},
{Type: v1.NodeExternalDNS, Address: testKubeletHostname}, // cloud-reported address value matches detected hostname
},
expectedAddresses: []v1.NodeAddress{
{Type: v1.NodeInternalIP, Address: "10.1.1.1"},
{Type: v1.NodeExternalIP, Address: "55.55.55.55"},
{Type: v1.NodeExternalDNS, Address: testKubeletHostname},
{Type: v1.NodeHostName, Address: testKubeletHostname}, // detected hostname gets auto-added
},
shouldError: false,
},
{
name: "cloud doesn't report hostname, hostname override, hostname mismatch",
nodeAddresses: []v1.NodeAddress{
{Type: v1.NodeInternalIP, Address: "10.1.1.1"},
{Type: v1.NodeExternalIP, Address: "55.55.55.55"},
},
expectedAddresses: []v1.NodeAddress{
{Type: v1.NodeInternalIP, Address: "10.1.1.1"},
{Type: v1.NodeExternalIP, Address: "55.55.55.55"},
{Type: v1.NodeHostName, Address: testKubeletHostname}, // overridden hostname gets auto-added
},
hostnameOverride: true,
shouldError: false,
},
}
for _, testCase := range cases {
t.Run(testCase.name, func(t *testing.T) {
@ -178,6 +258,7 @@ func TestNodeAddress(t *testing.T) {
setter := NodeAddress(nodeIP,
nodeIPValidator,
hostname,
testCase.hostnameOverride,
externalCloudProvider,
cloud,
nodeAddressesFunc)