Change:
<!-- Please only use this template for submitting enhancement requests --What would you like to be : kubelet self labels the node with OS/arch, etc. The labeling was backed in 1.3. It's time to promote the labels. Why is this needed: This feature has already been used in multiple places to help schedule pods onto the right nodes. For the transition, kubelet will need to label itself with both the beta and the GA label to handle the master-node version skew for a few releases. /cc /cc
I support this.
+1
Has documentation been updated? Are the possible/expected label values documented? I don't even see a golang documentation page containing the values. Are there upgrade and version-skew tests? Annotation changes in the past have caused compatibility breakage. I wouldn't expect label key changes to be any less error prone. Speaking of breakage, do we need a test to verify that golang changes don't break us by changing the values?
cc
Just saw this, thanks. The values are reported by kubelet as part of the kubelet's reconcile loop, so we have a single writer for the existing labels, so reporting both the old and new label keys doesn't seem concerning in a normal case. The only case I can think of that would cause trouble is if a kubelet was upgraded, reported the new label, then a node with the same name with a different os/arch and back level kubelet was run, and would not maintain the new label. That seems far fetched to me. I agree documentation needs to be updated, and note the release at which the new keys started getting reported. Changing the go constant values is the most concerning part of this and should probably be reverted. I'd recommend choosing new constant names (LabelOSStable or something) to avoid silently changing the behavior of every component that references the existing constants in ways that aren't safe for clusters with 1.13 or older kubelets. For extra credit, the node controller could add/update the stable labels on nodes to match the beta labels reported by legacy nodes until 1.16 when the oldest kubelets in the cluster would be known to report the new labels as well.
Not yet. I will send a PR to update documentation in , in particular, FWIW, and are not used anywhere in the kubernetes repository. But fair enough, there might be outside components relying on those. I'll send a PR to revert & fix this behavior. I'm not sure how likely this would happen (and likely may cause other issues), but I can do that if this is required.
If we don't do that, no one without intimate knowledge of the target cluster of their manifest can start using the new labels until ~1.16.
To expand on comment, since supported node/control-plane version skew is 2 releases, in general the new node labels couldn't be depended upon until 2 releases after introduction.
That's why kubelet reports both at the same time. This is a general problem for any new node label. I will send a PR to add the labels in the node controller, so that folks can start relying the newer labels if they wish.

Discussion:
LabelInstanceType = "beta.kubernetes.io/instance-type" <del> LabelOS   = "beta.kubernetes.io/os" LabelArch = "beta.kubernetes.io/arch" </del> <ins> LabelOS   = "kubernetes.io/os" LabelArch = "kubernetes.io/arch" // The OS/Arch labels are promoted to GA in 1.14. kubelet applies both beta // and GA labels to ensure backward compatibility. // TODO: stop applying the beta OS/Arch labels in Kubernetes 1.17. LegacyLabelOS   = "beta.kubernetes.io/os" LegacyLabelArch = "beta.kubernetes.io/arch" </ins> // GA versions of the legacy beta labels. // TODO: update kubelet and controllers to set both beta and GA labels, then export these constants labelZoneFailureDomainGA = "failure-domain.kubernetes.io/zone" labelZoneRegionGA        = "failure-domain.kubernetes.io/region" labelInstanceTypeGA      = "kubernetes.io/instance-type" <del> labelOSGA                = "kubernetes.io/os" labelArchGA              = "kubernetes.io/arch" </del> // LabelNamespaceSuffixKubelet is an allowed label namespace suffix kubelets can self-set ([*.]kubelet.kubernetes.io/*) LabelNamespaceSuffixKubelet = "kubelet.kubernetes.io"

LabelOS, LabelArch, <ins> LegacyLabelOS, LegacyLabelArch, </ins> labelZoneFailureDomainGA, labelZoneRegionGA, labelInstanceTypeGA, <del> labelOSGA, labelArchGA, </del> ) var kubeletLabelNamespaces = sets.NewString(

kubeletapis.LabelInstanceType, kubeletapis.LabelOS, kubeletapis.LabelArch, <ins> kubeletapis.LegacyLabelOS, kubeletapis.LegacyLabelArch, </ins> } needsUpdate := false

ObjectMeta: metav1.ObjectMeta{ Name: string(kl.nodeName), Labels: map[string]string{ <del> kubeletapis.LabelHostname: kl.hostname, kubeletapis.LabelOS:       goruntime.GOOS, kubeletapis.LabelArch:     goruntime.GOARCH, </del> <ins> kubeletapis.LabelHostname:   kl.hostname, kubeletapis.LabelOS:         goruntime.GOOS, kubeletapis.LabelArch:       goruntime.GOARCH, kubeletapis.LegacyLabelOS:   goruntime.GOOS, kubeletapis.LegacyLabelArch: goruntime.GOARCH, </ins> }, }, Spec: v1.NodeSpec{

ObjectMeta: metav1.ObjectMeta{ Name: testKubeletHostname, Labels: map[string]string{ <del> kubeletapis.LabelHostname: testKubeletHostname, kubeletapis.LabelOS:       goruntime.GOOS, kubeletapis.LabelArch:     goruntime.GOARCH, </del> <ins> kubeletapis.LabelHostname:   testKubeletHostname, kubeletapis.LabelOS:         goruntime.GOOS, kubeletapis.LabelArch:       goruntime.GOARCH, kubeletapis.LegacyLabelOS:   goruntime.GOOS, kubeletapis.LegacyLabelArch: goruntime.GOARCH, </ins> }, }, }, nil

node := &v1.Node{ ObjectMeta: metav1.ObjectMeta{ Labels: map[string]string{ <del> kubeletapis.LabelHostname: testKubeletHostname, kubeletapis.LabelOS:       goruntime.GOOS, kubeletapis.LabelArch:     goruntime.GOARCH, </del> <ins> kubeletapis.LabelHostname:   testKubeletHostname, kubeletapis.LabelOS:         goruntime.GOOS, kubeletapis.LabelArch:       goruntime.GOARCH, kubeletapis.LegacyLabelOS:   goruntime.GOOS, kubeletapis.LegacyLabelArch: goruntime.GOARCH, </ins> }, }, }