mirror of
https://github.com/k3s-io/kubernetes.git
synced 2025-07-29 14:37:00 +00:00
kubelet: set both deprecated Beta and GA labels for zone/region topology from the cloud provider
Signed-off-by: Andrew Sy Kim <kiman@vmware.com>
This commit is contained in:
parent
3032c81187
commit
4c194d52da
@ -35,15 +35,14 @@ const (
|
|||||||
// TODO: stop applying the beta Arch labels in Kubernetes 1.18.
|
// TODO: stop applying the beta Arch labels in Kubernetes 1.18.
|
||||||
LabelArch = "beta.kubernetes.io/arch"
|
LabelArch = "beta.kubernetes.io/arch"
|
||||||
|
|
||||||
// GA versions of the legacy beta labels.
|
|
||||||
// TODO: update kubelet and controllers to set both beta and GA labels, then export these constants
|
// 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"
|
labelInstanceTypeGA = "kubernetes.io/instance-type"
|
||||||
)
|
)
|
||||||
|
|
||||||
var kubeletLabels = sets.NewString(
|
var kubeletLabels = sets.NewString(
|
||||||
v1.LabelHostname,
|
v1.LabelHostname,
|
||||||
|
v1.LabelZoneFailureDomainStable,
|
||||||
|
v1.LabelZoneRegionStable,
|
||||||
v1.LabelZoneFailureDomain,
|
v1.LabelZoneFailureDomain,
|
||||||
v1.LabelZoneRegion,
|
v1.LabelZoneRegion,
|
||||||
v1.LabelInstanceType,
|
v1.LabelInstanceType,
|
||||||
@ -53,8 +52,6 @@ var kubeletLabels = sets.NewString(
|
|||||||
LabelOS,
|
LabelOS,
|
||||||
LabelArch,
|
LabelArch,
|
||||||
|
|
||||||
labelZoneFailureDomainGA,
|
|
||||||
labelZoneRegionGA,
|
|
||||||
labelInstanceTypeGA,
|
labelInstanceTypeGA,
|
||||||
)
|
)
|
||||||
|
|
||||||
|
@ -150,6 +150,8 @@ func (kl *Kubelet) reconcileExtendedResource(initialNode, node *v1.Node) bool {
|
|||||||
func (kl *Kubelet) updateDefaultLabels(initialNode, existingNode *v1.Node) bool {
|
func (kl *Kubelet) updateDefaultLabels(initialNode, existingNode *v1.Node) bool {
|
||||||
defaultLabels := []string{
|
defaultLabels := []string{
|
||||||
v1.LabelHostname,
|
v1.LabelHostname,
|
||||||
|
v1.LabelZoneFailureDomainStable,
|
||||||
|
v1.LabelZoneRegionStable,
|
||||||
v1.LabelZoneFailureDomain,
|
v1.LabelZoneFailureDomain,
|
||||||
v1.LabelZoneRegion,
|
v1.LabelZoneRegion,
|
||||||
v1.LabelInstanceType,
|
v1.LabelInstanceType,
|
||||||
@ -342,10 +344,14 @@ func (kl *Kubelet) initialNode(ctx context.Context) (*v1.Node, error) {
|
|||||||
if zone.FailureDomain != "" {
|
if zone.FailureDomain != "" {
|
||||||
klog.Infof("Adding node label from cloud provider: %s=%s", v1.LabelZoneFailureDomain, zone.FailureDomain)
|
klog.Infof("Adding node label from cloud provider: %s=%s", v1.LabelZoneFailureDomain, zone.FailureDomain)
|
||||||
node.ObjectMeta.Labels[v1.LabelZoneFailureDomain] = zone.FailureDomain
|
node.ObjectMeta.Labels[v1.LabelZoneFailureDomain] = zone.FailureDomain
|
||||||
|
klog.Infof("Adding node label from cloud provider: %s=%s", v1.LabelZoneFailureDomainStable, zone.FailureDomain)
|
||||||
|
node.ObjectMeta.Labels[v1.LabelZoneFailureDomainStable] = zone.FailureDomain
|
||||||
}
|
}
|
||||||
if zone.Region != "" {
|
if zone.Region != "" {
|
||||||
klog.Infof("Adding node label from cloud provider: %s=%s", v1.LabelZoneRegion, zone.Region)
|
klog.Infof("Adding node label from cloud provider: %s=%s", v1.LabelZoneRegion, zone.Region)
|
||||||
node.ObjectMeta.Labels[v1.LabelZoneRegion] = zone.Region
|
node.ObjectMeta.Labels[v1.LabelZoneRegion] = zone.Region
|
||||||
|
klog.Infof("Adding node label from cloud provider: %s=%s", v1.LabelZoneRegionStable, zone.Region)
|
||||||
|
node.ObjectMeta.Labels[v1.LabelZoneRegionStable] = zone.Region
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1524,6 +1524,8 @@ func TestUpdateDefaultLabels(t *testing.T) {
|
|||||||
ObjectMeta: metav1.ObjectMeta{
|
ObjectMeta: metav1.ObjectMeta{
|
||||||
Labels: map[string]string{
|
Labels: map[string]string{
|
||||||
v1.LabelHostname: "new-hostname",
|
v1.LabelHostname: "new-hostname",
|
||||||
|
v1.LabelZoneFailureDomainStable: "new-zone-failure-domain",
|
||||||
|
v1.LabelZoneRegionStable: "new-zone-region",
|
||||||
v1.LabelZoneFailureDomain: "new-zone-failure-domain",
|
v1.LabelZoneFailureDomain: "new-zone-failure-domain",
|
||||||
v1.LabelZoneRegion: "new-zone-region",
|
v1.LabelZoneRegion: "new-zone-region",
|
||||||
v1.LabelInstanceType: "new-instance-type",
|
v1.LabelInstanceType: "new-instance-type",
|
||||||
@ -1540,6 +1542,8 @@ func TestUpdateDefaultLabels(t *testing.T) {
|
|||||||
needsUpdate: true,
|
needsUpdate: true,
|
||||||
finalLabels: map[string]string{
|
finalLabels: map[string]string{
|
||||||
v1.LabelHostname: "new-hostname",
|
v1.LabelHostname: "new-hostname",
|
||||||
|
v1.LabelZoneFailureDomainStable: "new-zone-failure-domain",
|
||||||
|
v1.LabelZoneRegionStable: "new-zone-region",
|
||||||
v1.LabelZoneFailureDomain: "new-zone-failure-domain",
|
v1.LabelZoneFailureDomain: "new-zone-failure-domain",
|
||||||
v1.LabelZoneRegion: "new-zone-region",
|
v1.LabelZoneRegion: "new-zone-region",
|
||||||
v1.LabelInstanceType: "new-instance-type",
|
v1.LabelInstanceType: "new-instance-type",
|
||||||
@ -1553,6 +1557,8 @@ func TestUpdateDefaultLabels(t *testing.T) {
|
|||||||
ObjectMeta: metav1.ObjectMeta{
|
ObjectMeta: metav1.ObjectMeta{
|
||||||
Labels: map[string]string{
|
Labels: map[string]string{
|
||||||
v1.LabelHostname: "new-hostname",
|
v1.LabelHostname: "new-hostname",
|
||||||
|
v1.LabelZoneFailureDomainStable: "new-zone-failure-domain",
|
||||||
|
v1.LabelZoneRegionStable: "new-zone-region",
|
||||||
v1.LabelZoneFailureDomain: "new-zone-failure-domain",
|
v1.LabelZoneFailureDomain: "new-zone-failure-domain",
|
||||||
v1.LabelZoneRegion: "new-zone-region",
|
v1.LabelZoneRegion: "new-zone-region",
|
||||||
v1.LabelInstanceType: "new-instance-type",
|
v1.LabelInstanceType: "new-instance-type",
|
||||||
@ -1565,6 +1571,8 @@ func TestUpdateDefaultLabels(t *testing.T) {
|
|||||||
ObjectMeta: metav1.ObjectMeta{
|
ObjectMeta: metav1.ObjectMeta{
|
||||||
Labels: map[string]string{
|
Labels: map[string]string{
|
||||||
v1.LabelHostname: "old-hostname",
|
v1.LabelHostname: "old-hostname",
|
||||||
|
v1.LabelZoneFailureDomainStable: "old-zone-failure-domain",
|
||||||
|
v1.LabelZoneRegionStable: "old-zone-region",
|
||||||
v1.LabelZoneFailureDomain: "old-zone-failure-domain",
|
v1.LabelZoneFailureDomain: "old-zone-failure-domain",
|
||||||
v1.LabelZoneRegion: "old-zone-region",
|
v1.LabelZoneRegion: "old-zone-region",
|
||||||
v1.LabelInstanceType: "old-instance-type",
|
v1.LabelInstanceType: "old-instance-type",
|
||||||
@ -1576,6 +1584,8 @@ func TestUpdateDefaultLabels(t *testing.T) {
|
|||||||
needsUpdate: true,
|
needsUpdate: true,
|
||||||
finalLabels: map[string]string{
|
finalLabels: map[string]string{
|
||||||
v1.LabelHostname: "new-hostname",
|
v1.LabelHostname: "new-hostname",
|
||||||
|
v1.LabelZoneFailureDomainStable: "new-zone-failure-domain",
|
||||||
|
v1.LabelZoneRegionStable: "new-zone-region",
|
||||||
v1.LabelZoneFailureDomain: "new-zone-failure-domain",
|
v1.LabelZoneFailureDomain: "new-zone-failure-domain",
|
||||||
v1.LabelZoneRegion: "new-zone-region",
|
v1.LabelZoneRegion: "new-zone-region",
|
||||||
v1.LabelInstanceType: "new-instance-type",
|
v1.LabelInstanceType: "new-instance-type",
|
||||||
@ -1589,6 +1599,8 @@ func TestUpdateDefaultLabels(t *testing.T) {
|
|||||||
ObjectMeta: metav1.ObjectMeta{
|
ObjectMeta: metav1.ObjectMeta{
|
||||||
Labels: map[string]string{
|
Labels: map[string]string{
|
||||||
v1.LabelHostname: "new-hostname",
|
v1.LabelHostname: "new-hostname",
|
||||||
|
v1.LabelZoneFailureDomainStable: "new-zone-failure-domain",
|
||||||
|
v1.LabelZoneRegionStable: "new-zone-region",
|
||||||
v1.LabelZoneFailureDomain: "new-zone-failure-domain",
|
v1.LabelZoneFailureDomain: "new-zone-failure-domain",
|
||||||
v1.LabelZoneRegion: "new-zone-region",
|
v1.LabelZoneRegion: "new-zone-region",
|
||||||
v1.LabelInstanceType: "new-instance-type",
|
v1.LabelInstanceType: "new-instance-type",
|
||||||
@ -1601,6 +1613,8 @@ func TestUpdateDefaultLabels(t *testing.T) {
|
|||||||
ObjectMeta: metav1.ObjectMeta{
|
ObjectMeta: metav1.ObjectMeta{
|
||||||
Labels: map[string]string{
|
Labels: map[string]string{
|
||||||
v1.LabelHostname: "new-hostname",
|
v1.LabelHostname: "new-hostname",
|
||||||
|
v1.LabelZoneFailureDomainStable: "new-zone-failure-domain",
|
||||||
|
v1.LabelZoneRegionStable: "new-zone-region",
|
||||||
v1.LabelZoneFailureDomain: "new-zone-failure-domain",
|
v1.LabelZoneFailureDomain: "new-zone-failure-domain",
|
||||||
v1.LabelZoneRegion: "new-zone-region",
|
v1.LabelZoneRegion: "new-zone-region",
|
||||||
v1.LabelInstanceType: "new-instance-type",
|
v1.LabelInstanceType: "new-instance-type",
|
||||||
@ -1613,6 +1627,8 @@ func TestUpdateDefaultLabels(t *testing.T) {
|
|||||||
needsUpdate: false,
|
needsUpdate: false,
|
||||||
finalLabels: map[string]string{
|
finalLabels: map[string]string{
|
||||||
v1.LabelHostname: "new-hostname",
|
v1.LabelHostname: "new-hostname",
|
||||||
|
v1.LabelZoneFailureDomainStable: "new-zone-failure-domain",
|
||||||
|
v1.LabelZoneRegionStable: "new-zone-region",
|
||||||
v1.LabelZoneFailureDomain: "new-zone-failure-domain",
|
v1.LabelZoneFailureDomain: "new-zone-failure-domain",
|
||||||
v1.LabelZoneRegion: "new-zone-region",
|
v1.LabelZoneRegion: "new-zone-region",
|
||||||
v1.LabelInstanceType: "new-instance-type",
|
v1.LabelInstanceType: "new-instance-type",
|
||||||
@ -1632,6 +1648,8 @@ func TestUpdateDefaultLabels(t *testing.T) {
|
|||||||
ObjectMeta: metav1.ObjectMeta{
|
ObjectMeta: metav1.ObjectMeta{
|
||||||
Labels: map[string]string{
|
Labels: map[string]string{
|
||||||
v1.LabelHostname: "new-hostname",
|
v1.LabelHostname: "new-hostname",
|
||||||
|
v1.LabelZoneFailureDomainStable: "new-zone-failure-domain",
|
||||||
|
v1.LabelZoneRegionStable: "new-zone-region",
|
||||||
v1.LabelZoneFailureDomain: "new-zone-failure-domain",
|
v1.LabelZoneFailureDomain: "new-zone-failure-domain",
|
||||||
v1.LabelZoneRegion: "new-zone-region",
|
v1.LabelZoneRegion: "new-zone-region",
|
||||||
v1.LabelInstanceType: "new-instance-type",
|
v1.LabelInstanceType: "new-instance-type",
|
||||||
@ -1644,6 +1662,8 @@ func TestUpdateDefaultLabels(t *testing.T) {
|
|||||||
needsUpdate: false,
|
needsUpdate: false,
|
||||||
finalLabels: map[string]string{
|
finalLabels: map[string]string{
|
||||||
v1.LabelHostname: "new-hostname",
|
v1.LabelHostname: "new-hostname",
|
||||||
|
v1.LabelZoneFailureDomainStable: "new-zone-failure-domain",
|
||||||
|
v1.LabelZoneRegionStable: "new-zone-region",
|
||||||
v1.LabelZoneFailureDomain: "new-zone-failure-domain",
|
v1.LabelZoneFailureDomain: "new-zone-failure-domain",
|
||||||
v1.LabelZoneRegion: "new-zone-region",
|
v1.LabelZoneRegion: "new-zone-region",
|
||||||
v1.LabelInstanceType: "new-instance-type",
|
v1.LabelInstanceType: "new-instance-type",
|
||||||
@ -1658,6 +1678,8 @@ func TestUpdateDefaultLabels(t *testing.T) {
|
|||||||
ObjectMeta: metav1.ObjectMeta{
|
ObjectMeta: metav1.ObjectMeta{
|
||||||
Labels: map[string]string{
|
Labels: map[string]string{
|
||||||
v1.LabelHostname: "new-hostname",
|
v1.LabelHostname: "new-hostname",
|
||||||
|
v1.LabelZoneFailureDomainStable: "new-zone-failure-domain",
|
||||||
|
v1.LabelZoneRegionStable: "new-zone-region",
|
||||||
v1.LabelZoneFailureDomain: "new-zone-failure-domain",
|
v1.LabelZoneFailureDomain: "new-zone-failure-domain",
|
||||||
v1.LabelZoneRegion: "new-zone-region",
|
v1.LabelZoneRegion: "new-zone-region",
|
||||||
v1.LabelInstanceType: "new-instance-type",
|
v1.LabelInstanceType: "new-instance-type",
|
||||||
@ -1670,6 +1692,8 @@ func TestUpdateDefaultLabels(t *testing.T) {
|
|||||||
ObjectMeta: metav1.ObjectMeta{
|
ObjectMeta: metav1.ObjectMeta{
|
||||||
Labels: map[string]string{
|
Labels: map[string]string{
|
||||||
v1.LabelHostname: "new-hostname",
|
v1.LabelHostname: "new-hostname",
|
||||||
|
v1.LabelZoneFailureDomainStable: "new-zone-failure-domain",
|
||||||
|
v1.LabelZoneRegionStable: "new-zone-region",
|
||||||
v1.LabelZoneFailureDomain: "new-zone-failure-domain",
|
v1.LabelZoneFailureDomain: "new-zone-failure-domain",
|
||||||
v1.LabelZoneRegion: "new-zone-region",
|
v1.LabelZoneRegion: "new-zone-region",
|
||||||
v1.LabelInstanceType: "new-instance-type",
|
v1.LabelInstanceType: "new-instance-type",
|
||||||
@ -1681,6 +1705,8 @@ func TestUpdateDefaultLabels(t *testing.T) {
|
|||||||
needsUpdate: false,
|
needsUpdate: false,
|
||||||
finalLabels: map[string]string{
|
finalLabels: map[string]string{
|
||||||
v1.LabelHostname: "new-hostname",
|
v1.LabelHostname: "new-hostname",
|
||||||
|
v1.LabelZoneFailureDomainStable: "new-zone-failure-domain",
|
||||||
|
v1.LabelZoneRegionStable: "new-zone-region",
|
||||||
v1.LabelZoneFailureDomain: "new-zone-failure-domain",
|
v1.LabelZoneFailureDomain: "new-zone-failure-domain",
|
||||||
v1.LabelZoneRegion: "new-zone-region",
|
v1.LabelZoneRegion: "new-zone-region",
|
||||||
v1.LabelInstanceType: "new-instance-type",
|
v1.LabelInstanceType: "new-instance-type",
|
||||||
@ -1694,6 +1720,8 @@ func TestUpdateDefaultLabels(t *testing.T) {
|
|||||||
ObjectMeta: metav1.ObjectMeta{
|
ObjectMeta: metav1.ObjectMeta{
|
||||||
Labels: map[string]string{
|
Labels: map[string]string{
|
||||||
v1.LabelHostname: "new-hostname",
|
v1.LabelHostname: "new-hostname",
|
||||||
|
v1.LabelZoneFailureDomainStable: "new-zone-failure-domain",
|
||||||
|
v1.LabelZoneRegionStable: "new-zone-region",
|
||||||
v1.LabelZoneFailureDomain: "new-zone-failure-domain",
|
v1.LabelZoneFailureDomain: "new-zone-failure-domain",
|
||||||
v1.LabelZoneRegion: "new-zone-region",
|
v1.LabelZoneRegion: "new-zone-region",
|
||||||
v1.LabelInstanceType: "new-instance-type",
|
v1.LabelInstanceType: "new-instance-type",
|
||||||
@ -1708,6 +1736,8 @@ func TestUpdateDefaultLabels(t *testing.T) {
|
|||||||
needsUpdate: true,
|
needsUpdate: true,
|
||||||
finalLabels: map[string]string{
|
finalLabels: map[string]string{
|
||||||
v1.LabelHostname: "new-hostname",
|
v1.LabelHostname: "new-hostname",
|
||||||
|
v1.LabelZoneFailureDomainStable: "new-zone-failure-domain",
|
||||||
|
v1.LabelZoneRegionStable: "new-zone-region",
|
||||||
v1.LabelZoneFailureDomain: "new-zone-failure-domain",
|
v1.LabelZoneFailureDomain: "new-zone-failure-domain",
|
||||||
v1.LabelZoneRegion: "new-zone-region",
|
v1.LabelZoneRegion: "new-zone-region",
|
||||||
v1.LabelInstanceType: "new-instance-type",
|
v1.LabelInstanceType: "new-instance-type",
|
||||||
|
@ -35,6 +35,7 @@ import (
|
|||||||
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
||||||
"k8s.io/apimachinery/pkg/types"
|
"k8s.io/apimachinery/pkg/types"
|
||||||
utilfeature "k8s.io/apiserver/pkg/util/feature"
|
utilfeature "k8s.io/apiserver/pkg/util/feature"
|
||||||
|
volumehelpers "k8s.io/cloud-provider/volume/helpers"
|
||||||
"k8s.io/kubernetes/pkg/features"
|
"k8s.io/kubernetes/pkg/features"
|
||||||
"k8s.io/kubernetes/pkg/util/mount"
|
"k8s.io/kubernetes/pkg/util/mount"
|
||||||
"k8s.io/kubernetes/pkg/volume"
|
"k8s.io/kubernetes/pkg/volume"
|
||||||
@ -519,7 +520,7 @@ func (c *awsElasticBlockStoreProvisioner) Provision(selectedNode *v1.Node, allow
|
|||||||
pv := &v1.PersistentVolume{
|
pv := &v1.PersistentVolume{
|
||||||
ObjectMeta: metav1.ObjectMeta{
|
ObjectMeta: metav1.ObjectMeta{
|
||||||
Name: c.options.PVName,
|
Name: c.options.PVName,
|
||||||
Labels: map[string]string{},
|
Labels: labels,
|
||||||
Annotations: map[string]string{
|
Annotations: map[string]string{
|
||||||
util.VolumeDynamicallyCreatedByKey: "aws-ebs-dynamic-provisioner",
|
util.VolumeDynamicallyCreatedByKey: "aws-ebs-dynamic-provisioner",
|
||||||
},
|
},
|
||||||
@ -547,21 +548,9 @@ func (c *awsElasticBlockStoreProvisioner) Provision(selectedNode *v1.Node, allow
|
|||||||
pv.Spec.AccessModes = c.plugin.GetAccessModes()
|
pv.Spec.AccessModes = c.plugin.GetAccessModes()
|
||||||
}
|
}
|
||||||
|
|
||||||
requirements := make([]v1.NodeSelectorRequirement, 0)
|
|
||||||
if len(labels) != 0 {
|
|
||||||
if pv.Labels == nil {
|
|
||||||
pv.Labels = make(map[string]string)
|
|
||||||
}
|
|
||||||
for k, v := range labels {
|
|
||||||
pv.Labels[k] = v
|
|
||||||
requirements = append(requirements, v1.NodeSelectorRequirement{Key: k, Operator: v1.NodeSelectorOpIn, Values: []string{v}})
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
pv.Spec.NodeAffinity = new(v1.VolumeNodeAffinity)
|
pv.Spec.NodeAffinity = new(v1.VolumeNodeAffinity)
|
||||||
pv.Spec.NodeAffinity.Required = new(v1.NodeSelector)
|
pv.Spec.NodeAffinity.Required = new(v1.NodeSelector)
|
||||||
pv.Spec.NodeAffinity.Required.NodeSelectorTerms = make([]v1.NodeSelectorTerm, 1)
|
pv.Spec.NodeAffinity.Required.NodeSelectorTerms = volumehelpers.TranslateZoneRegionLabelsToNodeSelectorTerms(labels)
|
||||||
pv.Spec.NodeAffinity.Required.NodeSelectorTerms[0].MatchExpressions = requirements
|
|
||||||
|
|
||||||
return pv, nil
|
return pv, nil
|
||||||
}
|
}
|
||||||
|
@ -207,7 +207,7 @@ func TestPlugin(t *testing.T) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
n := len(persistentSpec.Spec.NodeAffinity.Required.NodeSelectorTerms)
|
n := len(persistentSpec.Spec.NodeAffinity.Required.NodeSelectorTerms)
|
||||||
if n != 1 {
|
if n != 2 {
|
||||||
t.Errorf("Provision() returned unexpected number of NodeSelectorTerms %d. Expected %d", n, 1)
|
t.Errorf("Provision() returned unexpected number of NodeSelectorTerms %d. Expected %d", n, 1)
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -228,6 +228,25 @@ func TestPlugin(t *testing.T) {
|
|||||||
if len(req.Values) != 1 || req.Values[0] != "yes" {
|
if len(req.Values) != 1 || req.Values[0] != "yes" {
|
||||||
t.Errorf("Provision() returned unexpected requirement value in NodeAffinity %v", req.Values)
|
t.Errorf("Provision() returned unexpected requirement value in NodeAffinity %v", req.Values)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
n = len(persistentSpec.Spec.NodeAffinity.Required.NodeSelectorTerms[1].MatchExpressions)
|
||||||
|
if n != 1 {
|
||||||
|
t.Errorf("Provision() returned unexpected number of MatchExpressions %d. Expected %d", n, 1)
|
||||||
|
}
|
||||||
|
|
||||||
|
req = persistentSpec.Spec.NodeAffinity.Required.NodeSelectorTerms[1].MatchExpressions[0]
|
||||||
|
if req.Key != "fakepdmanager" {
|
||||||
|
t.Errorf("Provision() returned unexpected requirement key in NodeAffinity %v", req.Key)
|
||||||
|
}
|
||||||
|
|
||||||
|
if req.Operator != v1.NodeSelectorOpIn {
|
||||||
|
t.Errorf("Provision() returned unexpected requirement operator in NodeAffinity %v", req.Operator)
|
||||||
|
}
|
||||||
|
|
||||||
|
if len(req.Values) != 1 || req.Values[0] != "yes" {
|
||||||
|
t.Errorf("Provision() returned unexpected requirement value in NodeAffinity %v", req.Values)
|
||||||
|
}
|
||||||
|
|
||||||
// Test Deleter
|
// Test Deleter
|
||||||
volSpec := &volume.Spec{
|
volSpec := &volume.Spec{
|
||||||
PersistentVolume: persistentSpec,
|
PersistentVolume: persistentSpec,
|
||||||
|
@ -322,22 +322,16 @@ func (p *azureDiskProvisioner) Provision(selectedNode *v1.Node, allowedTopologie
|
|||||||
if zoned {
|
if zoned {
|
||||||
// Set node affinity labels based on availability zone labels.
|
// Set node affinity labels based on availability zone labels.
|
||||||
if len(labels) > 0 {
|
if len(labels) > 0 {
|
||||||
requirements := make([]v1.NodeSelectorRequirement, 0)
|
nodeSelectorTerms = volumehelpers.TranslateZoneRegionLabelsToNodeSelectorTerms(labels)
|
||||||
for k, v := range labels {
|
|
||||||
requirements = append(requirements, v1.NodeSelectorRequirement{Key: k, Operator: v1.NodeSelectorOpIn, Values: []string{v}})
|
|
||||||
}
|
}
|
||||||
|
|
||||||
nodeSelectorTerms = append(nodeSelectorTerms, v1.NodeSelectorTerm{
|
|
||||||
MatchExpressions: requirements,
|
|
||||||
})
|
|
||||||
}
|
|
||||||
} else {
|
} else {
|
||||||
// Set node affinity labels based on fault domains.
|
// Set node affinity labels based on fault domains.
|
||||||
// This is required because unzoned AzureDisk can't be attached to zoned nodes.
|
// This is required because unzoned AzureDisk can't be attached to zoned nodes.
|
||||||
// There are at most 3 fault domains available in each region.
|
// There are at most 3 fault domains available in each region.
|
||||||
// Refer https://docs.microsoft.com/en-us/azure/virtual-machines/windows/manage-availability.
|
// Refer https://docs.microsoft.com/en-us/azure/virtual-machines/windows/manage-availability.
|
||||||
for i := 0; i < 3; i++ {
|
for i := 0; i < 3; i++ {
|
||||||
requirements := []v1.NodeSelectorRequirement{
|
deprecatedTopologyReqs := []v1.NodeSelectorRequirement{
|
||||||
{
|
{
|
||||||
Key: v1.LabelZoneRegion,
|
Key: v1.LabelZoneRegion,
|
||||||
Operator: v1.NodeSelectorOpIn,
|
Operator: v1.NodeSelectorOpIn,
|
||||||
@ -349,8 +343,25 @@ func (p *azureDiskProvisioner) Provision(selectedNode *v1.Node, allowedTopologie
|
|||||||
Values: []string{strconv.Itoa(i)},
|
Values: []string{strconv.Itoa(i)},
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
|
topologyReqs := []v1.NodeSelectorRequirement{
|
||||||
|
{
|
||||||
|
Key: v1.LabelZoneRegionStable,
|
||||||
|
Operator: v1.NodeSelectorOpIn,
|
||||||
|
Values: []string{diskController.GetLocation()},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
Key: v1.LabelZoneFailureDomainStable,
|
||||||
|
Operator: v1.NodeSelectorOpIn,
|
||||||
|
Values: []string{strconv.Itoa(i)},
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
nodeSelectorTerms = append(nodeSelectorTerms, v1.NodeSelectorTerm{
|
nodeSelectorTerms = append(nodeSelectorTerms, v1.NodeSelectorTerm{
|
||||||
MatchExpressions: requirements,
|
MatchExpressions: deprecatedTopologyReqs,
|
||||||
|
})
|
||||||
|
nodeSelectorTerms = append(nodeSelectorTerms, v1.NodeSelectorTerm{
|
||||||
|
MatchExpressions: topologyReqs,
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -31,6 +31,7 @@ import (
|
|||||||
"k8s.io/apimachinery/pkg/types"
|
"k8s.io/apimachinery/pkg/types"
|
||||||
utilfeature "k8s.io/apiserver/pkg/util/feature"
|
utilfeature "k8s.io/apiserver/pkg/util/feature"
|
||||||
cloudprovider "k8s.io/cloud-provider"
|
cloudprovider "k8s.io/cloud-provider"
|
||||||
|
volumehelpers "k8s.io/cloud-provider/volume/helpers"
|
||||||
"k8s.io/klog"
|
"k8s.io/klog"
|
||||||
"k8s.io/kubernetes/pkg/features"
|
"k8s.io/kubernetes/pkg/features"
|
||||||
"k8s.io/kubernetes/pkg/util/mount"
|
"k8s.io/kubernetes/pkg/util/mount"
|
||||||
@ -618,18 +619,9 @@ func (c *cinderVolumeProvisioner) Provision(selectedNode *v1.Node, allowedTopolo
|
|||||||
pv.Spec.AccessModes = c.plugin.GetAccessModes()
|
pv.Spec.AccessModes = c.plugin.GetAccessModes()
|
||||||
}
|
}
|
||||||
|
|
||||||
requirements := make([]v1.NodeSelectorRequirement, 0)
|
|
||||||
for k, v := range labels {
|
|
||||||
if v != "" {
|
|
||||||
requirements = append(requirements, v1.NodeSelectorRequirement{Key: k, Operator: v1.NodeSelectorOpIn, Values: []string{v}})
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if len(requirements) > 0 {
|
|
||||||
pv.Spec.NodeAffinity = new(v1.VolumeNodeAffinity)
|
pv.Spec.NodeAffinity = new(v1.VolumeNodeAffinity)
|
||||||
pv.Spec.NodeAffinity.Required = new(v1.NodeSelector)
|
pv.Spec.NodeAffinity.Required = new(v1.NodeSelector)
|
||||||
pv.Spec.NodeAffinity.Required.NodeSelectorTerms = make([]v1.NodeSelectorTerm, 1)
|
pv.Spec.NodeAffinity.Required.NodeSelectorTerms = volumehelpers.TranslateZoneRegionLabelsToNodeSelectorTerms(labels)
|
||||||
pv.Spec.NodeAffinity.Required.NodeSelectorTerms[0].MatchExpressions = requirements
|
|
||||||
}
|
|
||||||
|
|
||||||
return pv, nil
|
return pv, nil
|
||||||
}
|
}
|
||||||
|
@ -123,6 +123,7 @@ func (fake *fakePDManager) DetachDisk(c *cinderVolumeUnmounter) error {
|
|||||||
func (fake *fakePDManager) CreateVolume(c *cinderVolumeProvisioner, node *v1.Node, allowedTopologies []v1.TopologySelectorTerm) (volumeID string, volumeSizeGB int, labels map[string]string, fstype string, err error) {
|
func (fake *fakePDManager) CreateVolume(c *cinderVolumeProvisioner, node *v1.Node, allowedTopologies []v1.TopologySelectorTerm) (volumeID string, volumeSizeGB int, labels map[string]string, fstype string, err error) {
|
||||||
labels = make(map[string]string)
|
labels = make(map[string]string)
|
||||||
labels[v1.LabelZoneFailureDomain] = "nova"
|
labels[v1.LabelZoneFailureDomain] = "nova"
|
||||||
|
labels[v1.LabelZoneFailureDomainStable] = "nova"
|
||||||
return "test-volume-name", 1, labels, "", nil
|
return "test-volume-name", 1, labels, "", nil
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -229,7 +230,7 @@ func TestPlugin(t *testing.T) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
n := len(persistentSpec.Spec.NodeAffinity.Required.NodeSelectorTerms)
|
n := len(persistentSpec.Spec.NodeAffinity.Required.NodeSelectorTerms)
|
||||||
if n != 1 {
|
if n != 2 {
|
||||||
t.Errorf("Provision() returned unexpected number of NodeSelectorTerms %d. Expected %d", n, 1)
|
t.Errorf("Provision() returned unexpected number of NodeSelectorTerms %d. Expected %d", n, 1)
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -252,6 +253,25 @@ func TestPlugin(t *testing.T) {
|
|||||||
t.Errorf("Provision() returned unexpected requirement value in NodeAffinity %v", req.Values)
|
t.Errorf("Provision() returned unexpected requirement value in NodeAffinity %v", req.Values)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
n = len(persistentSpec.Spec.NodeAffinity.Required.NodeSelectorTerms[1].MatchExpressions)
|
||||||
|
if n != 1 {
|
||||||
|
t.Errorf("Provision() returned unexpected number of MatchExpressions %d. Expected %d", n, 1)
|
||||||
|
}
|
||||||
|
|
||||||
|
req = persistentSpec.Spec.NodeAffinity.Required.NodeSelectorTerms[1].MatchExpressions[0]
|
||||||
|
|
||||||
|
if req.Key != v1.LabelZoneFailureDomainStable {
|
||||||
|
t.Errorf("Provision() returned unexpected requirement key in NodeAffinity %v", req.Key)
|
||||||
|
}
|
||||||
|
|
||||||
|
if req.Operator != v1.NodeSelectorOpIn {
|
||||||
|
t.Errorf("Provision() returned unexpected requirement operator in NodeAffinity %v", req.Operator)
|
||||||
|
}
|
||||||
|
|
||||||
|
if len(req.Values) != 1 || req.Values[0] != "nova" {
|
||||||
|
t.Errorf("Provision() returned unexpected requirement value in NodeAffinity %v", req.Values)
|
||||||
|
}
|
||||||
|
|
||||||
// Test Deleter
|
// Test Deleter
|
||||||
volSpec := &volume.Spec{
|
volSpec := &volume.Spec{
|
||||||
PersistentVolume: persistentSpec,
|
PersistentVolume: persistentSpec,
|
||||||
|
@ -157,6 +157,12 @@ func getZonesFromNodes(kubeClient clientset.Interface) (sets.String, error) {
|
|||||||
for _, node := range nodes.Items {
|
for _, node := range nodes.Items {
|
||||||
if zone, ok := node.Labels[v1.LabelZoneFailureDomain]; ok {
|
if zone, ok := node.Labels[v1.LabelZoneFailureDomain]; ok {
|
||||||
zones.Insert(zone)
|
zones.Insert(zone)
|
||||||
|
// don't need to check GA label if beta label already exists
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
if zone, ok := node.Labels[v1.LabelZoneFailureDomainStable]; ok {
|
||||||
|
zones.Insert(zone)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
klog.V(4).Infof("zones found: %v", zones)
|
klog.V(4).Infof("zones found: %v", zones)
|
||||||
@ -229,9 +235,12 @@ func (util *DiskUtil) CreateVolume(c *cinderVolumeProvisioner, node *v1.Node, al
|
|||||||
if IgnoreVolumeAZ == false {
|
if IgnoreVolumeAZ == false {
|
||||||
if volumeAZ != "" {
|
if volumeAZ != "" {
|
||||||
volumeLabels[v1.LabelZoneFailureDomain] = volumeAZ
|
volumeLabels[v1.LabelZoneFailureDomain] = volumeAZ
|
||||||
|
volumeLabels[v1.LabelZoneFailureDomainStable] = volumeAZ
|
||||||
}
|
}
|
||||||
|
|
||||||
if volumeRegion != "" {
|
if volumeRegion != "" {
|
||||||
volumeLabels[v1.LabelZoneRegion] = volumeRegion
|
volumeLabels[v1.LabelZoneRegion] = volumeRegion
|
||||||
|
volumeLabels[v1.LabelZoneRegionStable] = volumeRegion
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return volumeID, volSizeGiB, volumeLabels, fstype, nil
|
return volumeID, volSizeGiB, volumeLabels, fstype, nil
|
||||||
|
@ -458,6 +458,7 @@ func createPVSpec(name string, readOnly bool, zones []string) *volume.Spec {
|
|||||||
zonesLabel := strings.Join(zones, cloudvolume.LabelMultiZoneDelimiter)
|
zonesLabel := strings.Join(zones, cloudvolume.LabelMultiZoneDelimiter)
|
||||||
spec.PersistentVolume.ObjectMeta.Labels = map[string]string{
|
spec.PersistentVolume.ObjectMeta.Labels = map[string]string{
|
||||||
v1.LabelZoneFailureDomain: zonesLabel,
|
v1.LabelZoneFailureDomain: zonesLabel,
|
||||||
|
v1.LabelZoneFailureDomainStable: zonesLabel,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -515,7 +515,7 @@ func (c *gcePersistentDiskProvisioner) Provision(selectedNode *v1.Node, allowedT
|
|||||||
pv := &v1.PersistentVolume{
|
pv := &v1.PersistentVolume{
|
||||||
ObjectMeta: metav1.ObjectMeta{
|
ObjectMeta: metav1.ObjectMeta{
|
||||||
Name: c.options.PVName,
|
Name: c.options.PVName,
|
||||||
Labels: map[string]string{},
|
Labels: labels,
|
||||||
Annotations: map[string]string{
|
Annotations: map[string]string{
|
||||||
util.VolumeDynamicallyCreatedByKey: "gce-pd-dynamic-provisioner",
|
util.VolumeDynamicallyCreatedByKey: "gce-pd-dynamic-provisioner",
|
||||||
},
|
},
|
||||||
@ -542,32 +542,15 @@ func (c *gcePersistentDiskProvisioner) Provision(selectedNode *v1.Node, allowedT
|
|||||||
pv.Spec.AccessModes = c.plugin.GetAccessModes()
|
pv.Spec.AccessModes = c.plugin.GetAccessModes()
|
||||||
}
|
}
|
||||||
|
|
||||||
requirements := make([]v1.NodeSelectorRequirement, 0)
|
|
||||||
if len(labels) != 0 {
|
|
||||||
if pv.Labels == nil {
|
|
||||||
pv.Labels = make(map[string]string)
|
|
||||||
}
|
|
||||||
for k, v := range labels {
|
|
||||||
pv.Labels[k] = v
|
|
||||||
var values []string
|
|
||||||
if k == v1.LabelZoneFailureDomain {
|
|
||||||
values, err = volumehelpers.LabelZonesToList(v)
|
|
||||||
if err != nil {
|
|
||||||
return nil, fmt.Errorf("failed to convert label string for Zone: %s to a List: %v", v, err)
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
values = []string{v}
|
|
||||||
}
|
|
||||||
requirements = append(requirements, v1.NodeSelectorRequirement{Key: k, Operator: v1.NodeSelectorOpIn, Values: values})
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if len(requirements) > 0 {
|
|
||||||
pv.Spec.NodeAffinity = new(v1.VolumeNodeAffinity)
|
pv.Spec.NodeAffinity = new(v1.VolumeNodeAffinity)
|
||||||
pv.Spec.NodeAffinity.Required = new(v1.NodeSelector)
|
pv.Spec.NodeAffinity.Required = new(v1.NodeSelector)
|
||||||
pv.Spec.NodeAffinity.Required.NodeSelectorTerms = make([]v1.NodeSelectorTerm, 1)
|
|
||||||
pv.Spec.NodeAffinity.Required.NodeSelectorTerms[0].MatchExpressions = requirements
|
nodeSelectorTerms, err := volumehelpers.TranslateZoneLabelsToNodeSelectorTerms(labels)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pv.Spec.NodeAffinity.Required.NodeSelectorTerms = nodeSelectorTerms
|
||||||
|
|
||||||
return pv, nil
|
return pv, nil
|
||||||
}
|
}
|
||||||
|
@ -86,6 +86,7 @@ func (fake *fakePDManager) CreateVolume(c *gcePersistentDiskProvisioner, node *v
|
|||||||
labels = make(map[string]string)
|
labels = make(map[string]string)
|
||||||
labels["fakepdmanager"] = "yes"
|
labels["fakepdmanager"] = "yes"
|
||||||
labels[v1.LabelZoneFailureDomain] = "zone1__zone2"
|
labels[v1.LabelZoneFailureDomain] = "zone1__zone2"
|
||||||
|
labels[v1.LabelZoneFailureDomainStable] = "zone1__zone2"
|
||||||
return "test-gce-volume-name", 100, labels, "", nil
|
return "test-gce-volume-name", 100, labels, "", nil
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -203,10 +204,14 @@ func TestPlugin(t *testing.T) {
|
|||||||
t.Errorf("Provision() returned unexpected value for %s: %v", v1.LabelZoneFailureDomain, persistentSpec.Labels[v1.LabelZoneFailureDomain])
|
t.Errorf("Provision() returned unexpected value for %s: %v", v1.LabelZoneFailureDomain, persistentSpec.Labels[v1.LabelZoneFailureDomain])
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if persistentSpec.Labels[v1.LabelZoneFailureDomainStable] != "zone1__zone2" {
|
||||||
|
t.Errorf("Provision() returned unexpected value for %s: %v", v1.LabelZoneFailureDomainStable, persistentSpec.Labels[v1.LabelZoneFailureDomainStable])
|
||||||
|
}
|
||||||
|
|
||||||
if persistentSpec.Spec.NodeAffinity == nil {
|
if persistentSpec.Spec.NodeAffinity == nil {
|
||||||
t.Errorf("Unexpected nil NodeAffinity found")
|
t.Errorf("Unexpected nil NodeAffinity found")
|
||||||
}
|
}
|
||||||
if len(persistentSpec.Spec.NodeAffinity.Required.NodeSelectorTerms) != 1 {
|
if len(persistentSpec.Spec.NodeAffinity.Required.NodeSelectorTerms) != 2 {
|
||||||
t.Errorf("Unexpected number of NodeSelectorTerms")
|
t.Errorf("Unexpected number of NodeSelectorTerms")
|
||||||
}
|
}
|
||||||
term := persistentSpec.Spec.NodeAffinity.Required.NodeSelectorTerms[0]
|
term := persistentSpec.Spec.NodeAffinity.Required.NodeSelectorTerms[0]
|
||||||
@ -227,6 +232,24 @@ func TestPlugin(t *testing.T) {
|
|||||||
t.Errorf("ZoneFailureDomain elements %v does not match zone labels %v", r.Values, zones)
|
t.Errorf("ZoneFailureDomain elements %v does not match zone labels %v", r.Values, zones)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
term = persistentSpec.Spec.NodeAffinity.Required.NodeSelectorTerms[1]
|
||||||
|
if len(term.MatchExpressions) != 2 {
|
||||||
|
t.Errorf("Unexpected number of NodeSelectorRequirements in volume NodeAffinity: %d", len(term.MatchExpressions))
|
||||||
|
}
|
||||||
|
r, _ = getNodeSelectorRequirementWithKey("fakepdmanager", term)
|
||||||
|
if r == nil || r.Values[0] != "yes" || r.Operator != v1.NodeSelectorOpIn {
|
||||||
|
t.Errorf("NodeSelectorRequirement fakepdmanager-in-yes not found in volume NodeAffinity")
|
||||||
|
}
|
||||||
|
zones, _ = volumehelpers.ZonesToSet("zone1,zone2")
|
||||||
|
r, _ = getNodeSelectorRequirementWithKey(v1.LabelZoneFailureDomainStable, term)
|
||||||
|
if r == nil {
|
||||||
|
t.Errorf("NodeSelectorRequirement %s-in-%v not found in volume NodeAffinity", v1.LabelZoneFailureDomainStable, zones)
|
||||||
|
}
|
||||||
|
sort.Strings(r.Values)
|
||||||
|
if !reflect.DeepEqual(r.Values, zones.List()) {
|
||||||
|
t.Errorf("ZoneFailureDomain elements %v does not match zone labels %v", r.Values, zones)
|
||||||
|
}
|
||||||
|
|
||||||
// Test Deleter
|
// Test Deleter
|
||||||
volSpec := &volume.Spec{
|
volSpec := &volume.Spec{
|
||||||
PersistentVolume: persistentSpec,
|
PersistentVolume: persistentSpec,
|
||||||
|
@ -349,7 +349,11 @@ func udevadmChangeToDrive(drivePath string) error {
|
|||||||
// Checks whether the given GCE PD volume spec is associated with a regional PD.
|
// Checks whether the given GCE PD volume spec is associated with a regional PD.
|
||||||
func isRegionalPD(spec *volume.Spec) bool {
|
func isRegionalPD(spec *volume.Spec) bool {
|
||||||
if spec.PersistentVolume != nil {
|
if spec.PersistentVolume != nil {
|
||||||
zonesLabel := spec.PersistentVolume.Labels[v1.LabelZoneFailureDomain]
|
zonesLabel, ok := spec.PersistentVolume.Labels[v1.LabelZoneFailureDomain]
|
||||||
|
if !ok {
|
||||||
|
zonesLabel = spec.PersistentVolume.Labels[v1.LabelZoneFailureDomainStable]
|
||||||
|
}
|
||||||
|
|
||||||
zones := strings.Split(zonesLabel, cloudvolume.LabelMultiZoneDelimiter)
|
zones := strings.Split(zonesLabel, cloudvolume.LabelMultiZoneDelimiter)
|
||||||
return len(zones) > 1
|
return len(zones) > 1
|
||||||
}
|
}
|
||||||
|
@ -223,6 +223,231 @@ func TestCheckVolumeNodeAffinity(t *testing.T) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func Test_CheckNodeAffinityWithTopology(t *testing.T) {
|
||||||
|
tests := []struct {
|
||||||
|
name string
|
||||||
|
pv *v1.PersistentVolume
|
||||||
|
nodeLabels map[string]string
|
||||||
|
expectSuccess bool
|
||||||
|
}{
|
||||||
|
{
|
||||||
|
name: "PV has only beta label, node has beta/GA label -- zone only",
|
||||||
|
pv: testVolumeWithNodeAffinity(t, &v1.VolumeNodeAffinity{
|
||||||
|
Required: &v1.NodeSelector{
|
||||||
|
NodeSelectorTerms: []v1.NodeSelectorTerm{
|
||||||
|
{
|
||||||
|
MatchExpressions: []v1.NodeSelectorRequirement{
|
||||||
|
{
|
||||||
|
Key: v1.LabelZoneFailureDomain,
|
||||||
|
Operator: v1.NodeSelectorOpIn,
|
||||||
|
Values: []string{"zone"},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
}),
|
||||||
|
nodeLabels: map[string]string{
|
||||||
|
v1.LabelZoneFailureDomain: "zone",
|
||||||
|
v1.LabelZoneFailureDomainStable: "zone",
|
||||||
|
},
|
||||||
|
expectSuccess: true,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "PV has only beta label, node has beta/GA label -- zone and region",
|
||||||
|
pv: testVolumeWithNodeAffinity(t, &v1.VolumeNodeAffinity{
|
||||||
|
Required: &v1.NodeSelector{
|
||||||
|
NodeSelectorTerms: []v1.NodeSelectorTerm{
|
||||||
|
{
|
||||||
|
MatchExpressions: []v1.NodeSelectorRequirement{
|
||||||
|
{
|
||||||
|
Key: v1.LabelZoneFailureDomain,
|
||||||
|
Operator: v1.NodeSelectorOpIn,
|
||||||
|
Values: []string{"zone"},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
Key: v1.LabelZoneRegion,
|
||||||
|
Operator: v1.NodeSelectorOpIn,
|
||||||
|
Values: []string{"region"},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
}),
|
||||||
|
nodeLabels: map[string]string{
|
||||||
|
v1.LabelZoneFailureDomain: "zone",
|
||||||
|
v1.LabelZoneFailureDomainStable: "zone",
|
||||||
|
v1.LabelZoneRegion: "region",
|
||||||
|
v1.LabelZoneRegionStable: "region",
|
||||||
|
},
|
||||||
|
expectSuccess: true,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "PV has both beta/GA label, node has only beta -- zone only",
|
||||||
|
pv: testVolumeWithNodeAffinity(t, &v1.VolumeNodeAffinity{
|
||||||
|
Required: &v1.NodeSelector{
|
||||||
|
NodeSelectorTerms: []v1.NodeSelectorTerm{
|
||||||
|
{
|
||||||
|
MatchExpressions: []v1.NodeSelectorRequirement{
|
||||||
|
{
|
||||||
|
Key: v1.LabelZoneFailureDomain,
|
||||||
|
Operator: v1.NodeSelectorOpIn,
|
||||||
|
Values: []string{"zone"},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
MatchExpressions: []v1.NodeSelectorRequirement{
|
||||||
|
{
|
||||||
|
Key: v1.LabelZoneFailureDomainStable,
|
||||||
|
Operator: v1.NodeSelectorOpIn,
|
||||||
|
Values: []string{"zone"},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
}),
|
||||||
|
nodeLabels: map[string]string{
|
||||||
|
v1.LabelZoneFailureDomain: "zone",
|
||||||
|
},
|
||||||
|
expectSuccess: true,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "PV has both beta/GA label, node has only beta -- zone and region",
|
||||||
|
pv: testVolumeWithNodeAffinity(t, &v1.VolumeNodeAffinity{
|
||||||
|
Required: &v1.NodeSelector{
|
||||||
|
NodeSelectorTerms: []v1.NodeSelectorTerm{
|
||||||
|
{
|
||||||
|
MatchExpressions: []v1.NodeSelectorRequirement{
|
||||||
|
{
|
||||||
|
Key: v1.LabelZoneFailureDomain,
|
||||||
|
Operator: v1.NodeSelectorOpIn,
|
||||||
|
Values: []string{"zone"},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
Key: v1.LabelZoneRegion,
|
||||||
|
Operator: v1.NodeSelectorOpIn,
|
||||||
|
Values: []string{"region"},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
MatchExpressions: []v1.NodeSelectorRequirement{
|
||||||
|
{
|
||||||
|
Key: v1.LabelZoneFailureDomainStable,
|
||||||
|
Operator: v1.NodeSelectorOpIn,
|
||||||
|
Values: []string{"zone"},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
Key: v1.LabelZoneRegionStable,
|
||||||
|
Operator: v1.NodeSelectorOpIn,
|
||||||
|
Values: []string{"region"},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
}),
|
||||||
|
nodeLabels: map[string]string{
|
||||||
|
v1.LabelZoneFailureDomain: "zone",
|
||||||
|
v1.LabelZoneRegion: "region",
|
||||||
|
},
|
||||||
|
expectSuccess: true,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "PV has both beta/GA label, node has both beta/GA label -- zone only",
|
||||||
|
pv: testVolumeWithNodeAffinity(t, &v1.VolumeNodeAffinity{
|
||||||
|
Required: &v1.NodeSelector{
|
||||||
|
NodeSelectorTerms: []v1.NodeSelectorTerm{
|
||||||
|
{
|
||||||
|
MatchExpressions: []v1.NodeSelectorRequirement{
|
||||||
|
{
|
||||||
|
Key: v1.LabelZoneFailureDomain,
|
||||||
|
Operator: v1.NodeSelectorOpIn,
|
||||||
|
Values: []string{"zone"},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
MatchExpressions: []v1.NodeSelectorRequirement{
|
||||||
|
{
|
||||||
|
Key: v1.LabelZoneFailureDomainStable,
|
||||||
|
Operator: v1.NodeSelectorOpIn,
|
||||||
|
Values: []string{"zone"},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
}),
|
||||||
|
nodeLabels: map[string]string{
|
||||||
|
v1.LabelZoneFailureDomain: "zone",
|
||||||
|
v1.LabelZoneFailureDomainStable: "zone",
|
||||||
|
},
|
||||||
|
expectSuccess: true,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "PV has both beta/GA label, node has both beta/GA label -- zone and region",
|
||||||
|
pv: testVolumeWithNodeAffinity(t, &v1.VolumeNodeAffinity{
|
||||||
|
Required: &v1.NodeSelector{
|
||||||
|
NodeSelectorTerms: []v1.NodeSelectorTerm{
|
||||||
|
{
|
||||||
|
MatchExpressions: []v1.NodeSelectorRequirement{
|
||||||
|
{
|
||||||
|
Key: v1.LabelZoneFailureDomain,
|
||||||
|
Operator: v1.NodeSelectorOpIn,
|
||||||
|
Values: []string{"zone"},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
Key: v1.LabelZoneRegion,
|
||||||
|
Operator: v1.NodeSelectorOpIn,
|
||||||
|
Values: []string{"region"},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
MatchExpressions: []v1.NodeSelectorRequirement{
|
||||||
|
{
|
||||||
|
Key: v1.LabelZoneFailureDomainStable,
|
||||||
|
Operator: v1.NodeSelectorOpIn,
|
||||||
|
Values: []string{"zone"},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
Key: v1.LabelZoneRegionStable,
|
||||||
|
Operator: v1.NodeSelectorOpIn,
|
||||||
|
Values: []string{"region"},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
}),
|
||||||
|
nodeLabels: map[string]string{
|
||||||
|
v1.LabelZoneFailureDomain: "zone",
|
||||||
|
v1.LabelZoneRegion: "region",
|
||||||
|
v1.LabelZoneFailureDomainStable: "zone",
|
||||||
|
v1.LabelZoneRegionStable: "region",
|
||||||
|
},
|
||||||
|
expectSuccess: true,
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, test := range tests {
|
||||||
|
t.Run(test.name, func(t *testing.T) {
|
||||||
|
err := CheckNodeAffinity(test.pv, test.nodeLabels)
|
||||||
|
|
||||||
|
if err != nil && test.expectSuccess {
|
||||||
|
t.Errorf("CheckTopology returned error: %v", err)
|
||||||
|
}
|
||||||
|
if err == nil && !test.expectSuccess {
|
||||||
|
t.Error("CheckTopology returned success, expected error")
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
func testVolumeWithNodeAffinity(t *testing.T, affinity *v1.VolumeNodeAffinity) *v1.PersistentVolume {
|
func testVolumeWithNodeAffinity(t *testing.T, affinity *v1.VolumeNodeAffinity) *v1.PersistentVolume {
|
||||||
objMeta := metav1.ObjectMeta{Name: "test-constraints"}
|
objMeta := metav1.ObjectMeta{Name: "test-constraints"}
|
||||||
return &v1.PersistentVolume{
|
return &v1.PersistentVolume{
|
||||||
|
@ -403,7 +403,7 @@ func (v *vsphereVolumeProvisioner) Provision(selectedNode *v1.Node, allowedTopol
|
|||||||
pv := &v1.PersistentVolume{
|
pv := &v1.PersistentVolume{
|
||||||
ObjectMeta: metav1.ObjectMeta{
|
ObjectMeta: metav1.ObjectMeta{
|
||||||
Name: v.options.PVName,
|
Name: v.options.PVName,
|
||||||
Labels: map[string]string{},
|
Labels: volSpec.Labels,
|
||||||
Annotations: map[string]string{
|
Annotations: map[string]string{
|
||||||
util.VolumeDynamicallyCreatedByKey: "vsphere-volume-dynamic-provisioner",
|
util.VolumeDynamicallyCreatedByKey: "vsphere-volume-dynamic-provisioner",
|
||||||
},
|
},
|
||||||
@ -430,34 +430,16 @@ func (v *vsphereVolumeProvisioner) Provision(selectedNode *v1.Node, allowedTopol
|
|||||||
pv.Spec.AccessModes = v.plugin.GetAccessModes()
|
pv.Spec.AccessModes = v.plugin.GetAccessModes()
|
||||||
}
|
}
|
||||||
|
|
||||||
labels := volSpec.Labels
|
|
||||||
requirements := make([]v1.NodeSelectorRequirement, 0)
|
|
||||||
if len(labels) != 0 {
|
|
||||||
if pv.Labels == nil {
|
|
||||||
pv.Labels = make(map[string]string)
|
|
||||||
}
|
|
||||||
for k, v := range labels {
|
|
||||||
pv.Labels[k] = v
|
|
||||||
var values []string
|
|
||||||
if k == v1.LabelZoneFailureDomain {
|
|
||||||
values, err = volumehelpers.LabelZonesToList(v)
|
|
||||||
if err != nil {
|
|
||||||
return nil, fmt.Errorf("failed to convert label string for Zone: %s to a List: %v", v, err)
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
values = []string{v}
|
|
||||||
}
|
|
||||||
requirements = append(requirements, v1.NodeSelectorRequirement{Key: k, Operator: v1.NodeSelectorOpIn, Values: values})
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if len(requirements) > 0 {
|
|
||||||
pv.Spec.NodeAffinity = new(v1.VolumeNodeAffinity)
|
pv.Spec.NodeAffinity = new(v1.VolumeNodeAffinity)
|
||||||
pv.Spec.NodeAffinity.Required = new(v1.NodeSelector)
|
pv.Spec.NodeAffinity.Required = new(v1.NodeSelector)
|
||||||
pv.Spec.NodeAffinity.Required.NodeSelectorTerms = make([]v1.NodeSelectorTerm, 1)
|
|
||||||
pv.Spec.NodeAffinity.Required.NodeSelectorTerms[0].MatchExpressions = requirements
|
nodeSelectorTerms, err := volumehelpers.TranslateZoneLabelsToNodeSelectorTerms(volSpec.Labels)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pv.Spec.NodeAffinity.Required.NodeSelectorTerms = nodeSelectorTerms
|
||||||
|
|
||||||
return pv, nil
|
return pv, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -163,11 +163,11 @@ func setAllowedUpdateLabels(node *api.Node, value string) *api.Node {
|
|||||||
node.Labels["kubernetes.io/hostname"] = value
|
node.Labels["kubernetes.io/hostname"] = value
|
||||||
node.Labels["failure-domain.beta.kubernetes.io/zone"] = value
|
node.Labels["failure-domain.beta.kubernetes.io/zone"] = value
|
||||||
node.Labels["failure-domain.beta.kubernetes.io/region"] = value
|
node.Labels["failure-domain.beta.kubernetes.io/region"] = value
|
||||||
|
node.Labels["topology.kubernetes.io/zone"] = value
|
||||||
|
node.Labels["topology.kubernetes.io/region"] = value
|
||||||
node.Labels["beta.kubernetes.io/instance-type"] = value
|
node.Labels["beta.kubernetes.io/instance-type"] = value
|
||||||
node.Labels["beta.kubernetes.io/os"] = value
|
node.Labels["beta.kubernetes.io/os"] = value
|
||||||
node.Labels["beta.kubernetes.io/arch"] = value
|
node.Labels["beta.kubernetes.io/arch"] = value
|
||||||
node.Labels["failure-domain.kubernetes.io/zone"] = value
|
|
||||||
node.Labels["failure-domain.kubernetes.io/region"] = value
|
|
||||||
node.Labels["kubernetes.io/instance-type"] = value
|
node.Labels["kubernetes.io/instance-type"] = value
|
||||||
node.Labels["kubernetes.io/os"] = value
|
node.Labels["kubernetes.io/os"] = value
|
||||||
node.Labels["kubernetes.io/arch"] = value
|
node.Labels["kubernetes.io/arch"] = value
|
||||||
|
@ -22,6 +22,7 @@ import (
|
|||||||
"errors"
|
"errors"
|
||||||
"fmt"
|
"fmt"
|
||||||
"io"
|
"io"
|
||||||
|
"reflect"
|
||||||
"sync"
|
"sync"
|
||||||
|
|
||||||
v1 "k8s.io/api/core/v1"
|
v1 "k8s.io/api/core/v1"
|
||||||
@ -117,11 +118,13 @@ func (l *persistentVolumeLabel) Admit(ctx context.Context, a admission.Attribute
|
|||||||
return admission.NewForbidden(a, err)
|
return admission.NewForbidden(a, err)
|
||||||
}
|
}
|
||||||
|
|
||||||
requirements := make([]api.NodeSelectorRequirement, 0)
|
|
||||||
if len(volumeLabels) != 0 {
|
if len(volumeLabels) != 0 {
|
||||||
if volume.Labels == nil {
|
if volume.Labels == nil {
|
||||||
volume.Labels = make(map[string]string)
|
volume.Labels = make(map[string]string)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
deprecatedTopologyReqs := make([]api.NodeSelectorRequirement, 0)
|
||||||
|
topologyReqs := make([]api.NodeSelectorRequirement, 0)
|
||||||
for k, v := range volumeLabels {
|
for k, v := range volumeLabels {
|
||||||
// We (silently) replace labels if they are provided.
|
// We (silently) replace labels if they are provided.
|
||||||
// This should be OK because they are in the kubernetes.io namespace
|
// This should be OK because they are in the kubernetes.io namespace
|
||||||
@ -129,8 +132,11 @@ func (l *persistentVolumeLabel) Admit(ctx context.Context, a admission.Attribute
|
|||||||
volume.Labels[k] = v
|
volume.Labels[k] = v
|
||||||
|
|
||||||
// Set NodeSelectorRequirements based on the labels
|
// Set NodeSelectorRequirements based on the labels
|
||||||
|
//
|
||||||
|
// we currently set both beta (failure-domain.beta.kubernetes.io/zone) and
|
||||||
|
// GA (topology.kubernetes.io/zone) topology labels for volumes
|
||||||
var values []string
|
var values []string
|
||||||
if k == v1.LabelZoneFailureDomain {
|
if k == v1.LabelZoneFailureDomain || k == v1.LabelZoneFailureDomainStable {
|
||||||
zones, err := volumehelpers.LabelZonesToSet(v)
|
zones, err := volumehelpers.LabelZonesToSet(v)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return admission.NewForbidden(a, fmt.Errorf("failed to convert label string for Zone: %s to a Set", v))
|
return admission.NewForbidden(a, fmt.Errorf("failed to convert label string for Zone: %s to a Set", v))
|
||||||
@ -140,7 +146,17 @@ func (l *persistentVolumeLabel) Admit(ctx context.Context, a admission.Attribute
|
|||||||
} else {
|
} else {
|
||||||
values = []string{v}
|
values = []string{v}
|
||||||
}
|
}
|
||||||
requirements = append(requirements, api.NodeSelectorRequirement{Key: k, Operator: api.NodeSelectorOpIn, Values: values})
|
|
||||||
|
// separate topology requirements based on deprecated vs stable zone/region labels
|
||||||
|
// all other labels apply to both requirements
|
||||||
|
if k == v1.LabelZoneFailureDomain || k == v1.LabelZoneRegion {
|
||||||
|
deprecatedTopologyReqs = append(deprecatedTopologyReqs, api.NodeSelectorRequirement{Key: k, Operator: api.NodeSelectorOpIn, Values: values})
|
||||||
|
} else if k == v1.LabelZoneFailureDomainStable || k == v1.LabelZoneRegionStable {
|
||||||
|
topologyReqs = append(topologyReqs, api.NodeSelectorRequirement{Key: k, Operator: api.NodeSelectorOpIn, Values: values})
|
||||||
|
} else {
|
||||||
|
deprecatedTopologyReqs = append(deprecatedTopologyReqs, api.NodeSelectorRequirement{Key: k, Operator: api.NodeSelectorOpIn, Values: values})
|
||||||
|
topologyReqs = append(topologyReqs, api.NodeSelectorRequirement{Key: k, Operator: api.NodeSelectorOpIn, Values: values})
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if volume.Spec.NodeAffinity == nil {
|
if volume.Spec.NodeAffinity == nil {
|
||||||
@ -153,25 +169,53 @@ func (l *persistentVolumeLabel) Admit(ctx context.Context, a admission.Attribute
|
|||||||
// Need at least one term pre-allocated whose MatchExpressions can be appended to
|
// Need at least one term pre-allocated whose MatchExpressions can be appended to
|
||||||
volume.Spec.NodeAffinity.Required.NodeSelectorTerms = make([]api.NodeSelectorTerm, 1)
|
volume.Spec.NodeAffinity.Required.NodeSelectorTerms = make([]api.NodeSelectorTerm, 1)
|
||||||
}
|
}
|
||||||
if nodeSelectorRequirementKeysExistInNodeSelectorTerms(requirements, volume.Spec.NodeAffinity.Required.NodeSelectorTerms) {
|
|
||||||
klog.V(4).Infof("NodeSelectorRequirements for cloud labels %v conflict with existing NodeAffinity %v. Skipping addition of NodeSelectorRequirements for cloud labels.",
|
deprecatedNodeTerms := volume.DeepCopy().Spec.NodeAffinity.Required.NodeSelectorTerms
|
||||||
requirements, volume.Spec.NodeAffinity)
|
stableNodeTerms := volume.DeepCopy().Spec.NodeAffinity.Required.NodeSelectorTerms
|
||||||
} else {
|
|
||||||
for _, req := range requirements {
|
// only attempt to rewrite beta/stable topology labels if there are no conflicting labels on the PV at all
|
||||||
for i := range volume.Spec.NodeAffinity.Required.NodeSelectorTerms {
|
if nodeSelectorRequirementKeysExistInNodeSelectorTerms(deprecatedTopologyReqs, volume.Spec.NodeAffinity.Required.NodeSelectorTerms) ||
|
||||||
volume.Spec.NodeAffinity.Required.NodeSelectorTerms[i].MatchExpressions = append(volume.Spec.NodeAffinity.Required.NodeSelectorTerms[i].MatchExpressions, req)
|
nodeSelectorRequirementKeysExistInNodeSelectorTerms(topologyReqs, volume.Spec.NodeAffinity.Required.NodeSelectorTerms) {
|
||||||
|
klog.V(4).Infof("NodeSelectorRequirements for cloud labels %v conflict with existing NodeAffinity %v or %v. Skipping addition of NodeSelectorRequirements for cloud labels.",
|
||||||
|
deprecatedTopologyReqs, topologyReqs, volume.Spec.NodeAffinity)
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, req := range deprecatedTopologyReqs {
|
||||||
|
for i := range deprecatedNodeTerms {
|
||||||
|
deprecatedNodeTerms[i].MatchExpressions = append(deprecatedNodeTerms[i].MatchExpressions, req)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
for _, req := range topologyReqs {
|
||||||
|
for i := range stableNodeTerms {
|
||||||
|
stableNodeTerms[i].MatchExpressions = append(stableNodeTerms[i].MatchExpressions, req)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Deprecated and stable node selector terms are the same, i.e. the cloud provider
|
||||||
|
// didn't specify eithr zone/region labels. In the case set either one without
|
||||||
|
// expanding the set of selector terms
|
||||||
|
if reflect.DeepEqual(deprecatedTopologyReqs, topologyReqs) {
|
||||||
|
volume.Spec.NodeAffinity.Required.NodeSelectorTerms = stableNodeTerms
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// for deprecated topology labels, we overwrite existing terms directly with the deprecated topology reqs appended
|
||||||
|
volume.Spec.NodeAffinity.Required.NodeSelectorTerms = deprecatedNodeTerms
|
||||||
|
|
||||||
|
// for new stable topology labels, we expand node selector requirements by copying existing selector terms but using stable topology labels
|
||||||
|
volume.Spec.NodeAffinity.Required.NodeSelectorTerms = append(volume.Spec.NodeAffinity.Required.NodeSelectorTerms, stableNodeTerms...)
|
||||||
|
}
|
||||||
|
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (l *persistentVolumeLabel) findVolumeLabels(volume *api.PersistentVolume) (map[string]string, error) {
|
func (l *persistentVolumeLabel) findVolumeLabels(volume *api.PersistentVolume) (map[string]string, error) {
|
||||||
existingLabels := volume.Labels
|
existingLabels := volume.Labels
|
||||||
|
|
||||||
// All cloud providers set only these two labels.
|
// deprecated zone/region labels are still the source of truth
|
||||||
domain, domainOK := existingLabels[v1.LabelZoneFailureDomain]
|
domain, domainOK := existingLabels[v1.LabelZoneFailureDomain]
|
||||||
region, regionOK := existingLabels[v1.LabelZoneRegion]
|
region, regionOK := existingLabels[v1.LabelZoneRegion]
|
||||||
isDynamicallyProvisioned := metav1.HasAnnotation(volume.ObjectMeta, persistentvolume.AnnDynamicallyProvisioned)
|
isDynamicallyProvisioned := metav1.HasAnnotation(volume.ObjectMeta, persistentvolume.AnnDynamicallyProvisioned)
|
||||||
@ -180,6 +224,8 @@ func (l *persistentVolumeLabel) findVolumeLabels(volume *api.PersistentVolume) (
|
|||||||
return map[string]string{
|
return map[string]string{
|
||||||
v1.LabelZoneFailureDomain: domain,
|
v1.LabelZoneFailureDomain: domain,
|
||||||
v1.LabelZoneRegion: region,
|
v1.LabelZoneRegion: region,
|
||||||
|
v1.LabelZoneFailureDomainStable: domain,
|
||||||
|
v1.LabelZoneRegionStable: region,
|
||||||
}, nil
|
}, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -69,6 +69,7 @@ func Test_PVLAdmission(t *testing.T) {
|
|||||||
"a": "1",
|
"a": "1",
|
||||||
"b": "2",
|
"b": "2",
|
||||||
v1.LabelZoneFailureDomain: "1__2__3",
|
v1.LabelZoneFailureDomain: "1__2__3",
|
||||||
|
v1.LabelZoneFailureDomainStable: "1__2__3",
|
||||||
}),
|
}),
|
||||||
preAdmissionPV: &api.PersistentVolume{
|
preAdmissionPV: &api.PersistentVolume{
|
||||||
ObjectMeta: metav1.ObjectMeta{Name: "noncloud", Namespace: "myns"},
|
ObjectMeta: metav1.ObjectMeta{Name: "noncloud", Namespace: "myns"},
|
||||||
@ -177,6 +178,7 @@ func Test_PVLAdmission(t *testing.T) {
|
|||||||
"a": "1",
|
"a": "1",
|
||||||
"b": "2",
|
"b": "2",
|
||||||
v1.LabelZoneFailureDomain: "1__2__3",
|
v1.LabelZoneFailureDomain: "1__2__3",
|
||||||
|
v1.LabelZoneFailureDomainStable: "1__2__3",
|
||||||
}),
|
}),
|
||||||
preAdmissionPV: &api.PersistentVolume{
|
preAdmissionPV: &api.PersistentVolume{
|
||||||
ObjectMeta: metav1.ObjectMeta{Name: "awsebs", Namespace: "myns"},
|
ObjectMeta: metav1.ObjectMeta{Name: "awsebs", Namespace: "myns"},
|
||||||
@ -196,6 +198,7 @@ func Test_PVLAdmission(t *testing.T) {
|
|||||||
"a": "1",
|
"a": "1",
|
||||||
"b": "2",
|
"b": "2",
|
||||||
v1.LabelZoneFailureDomain: "1__2__3",
|
v1.LabelZoneFailureDomain: "1__2__3",
|
||||||
|
v1.LabelZoneFailureDomainStable: "1__2__3",
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
Spec: api.PersistentVolumeSpec{
|
Spec: api.PersistentVolumeSpec{
|
||||||
@ -226,6 +229,25 @@ func Test_PVLAdmission(t *testing.T) {
|
|||||||
},
|
},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
MatchExpressions: []api.NodeSelectorRequirement{
|
||||||
|
{
|
||||||
|
Key: "a",
|
||||||
|
Operator: api.NodeSelectorOpIn,
|
||||||
|
Values: []string{"1"},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
Key: "b",
|
||||||
|
Operator: api.NodeSelectorOpIn,
|
||||||
|
Values: []string{"2"},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
Key: v1.LabelZoneFailureDomainStable,
|
||||||
|
Operator: api.NodeSelectorOpIn,
|
||||||
|
Values: []string{"1", "2", "3"},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
@ -239,6 +261,8 @@ func Test_PVLAdmission(t *testing.T) {
|
|||||||
pvlabeler: mockVolumeLabels(map[string]string{
|
pvlabeler: mockVolumeLabels(map[string]string{
|
||||||
v1.LabelZoneFailureDomain: "domain1",
|
v1.LabelZoneFailureDomain: "domain1",
|
||||||
v1.LabelZoneRegion: "region1",
|
v1.LabelZoneRegion: "region1",
|
||||||
|
v1.LabelZoneFailureDomainStable: "domain1",
|
||||||
|
v1.LabelZoneRegionStable: "region1",
|
||||||
}),
|
}),
|
||||||
preAdmissionPV: &api.PersistentVolume{
|
preAdmissionPV: &api.PersistentVolume{
|
||||||
ObjectMeta: metav1.ObjectMeta{
|
ObjectMeta: metav1.ObjectMeta{
|
||||||
@ -246,6 +270,8 @@ func Test_PVLAdmission(t *testing.T) {
|
|||||||
Labels: map[string]string{
|
Labels: map[string]string{
|
||||||
v1.LabelZoneFailureDomain: "existingDomain",
|
v1.LabelZoneFailureDomain: "existingDomain",
|
||||||
v1.LabelZoneRegion: "existingRegion",
|
v1.LabelZoneRegion: "existingRegion",
|
||||||
|
v1.LabelZoneFailureDomainStable: "existingDomain",
|
||||||
|
v1.LabelZoneRegionStable: "existingRegion",
|
||||||
},
|
},
|
||||||
Annotations: map[string]string{
|
Annotations: map[string]string{
|
||||||
persistentvolume.AnnDynamicallyProvisioned: "kubernetes.io/aws-ebs",
|
persistentvolume.AnnDynamicallyProvisioned: "kubernetes.io/aws-ebs",
|
||||||
@ -266,6 +292,8 @@ func Test_PVLAdmission(t *testing.T) {
|
|||||||
Labels: map[string]string{
|
Labels: map[string]string{
|
||||||
v1.LabelZoneFailureDomain: "existingDomain",
|
v1.LabelZoneFailureDomain: "existingDomain",
|
||||||
v1.LabelZoneRegion: "existingRegion",
|
v1.LabelZoneRegion: "existingRegion",
|
||||||
|
v1.LabelZoneFailureDomainStable: "existingDomain",
|
||||||
|
v1.LabelZoneRegionStable: "existingRegion",
|
||||||
},
|
},
|
||||||
Annotations: map[string]string{
|
Annotations: map[string]string{
|
||||||
persistentvolume.AnnDynamicallyProvisioned: "kubernetes.io/aws-ebs",
|
persistentvolume.AnnDynamicallyProvisioned: "kubernetes.io/aws-ebs",
|
||||||
@ -294,6 +322,20 @@ func Test_PVLAdmission(t *testing.T) {
|
|||||||
},
|
},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
MatchExpressions: []api.NodeSelectorRequirement{
|
||||||
|
{
|
||||||
|
Key: v1.LabelZoneRegionStable,
|
||||||
|
Operator: api.NodeSelectorOpIn,
|
||||||
|
Values: []string{"existingRegion"},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
Key: v1.LabelZoneFailureDomainStable,
|
||||||
|
Operator: api.NodeSelectorOpIn,
|
||||||
|
Values: []string{"existingDomain"},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
@ -307,6 +349,8 @@ func Test_PVLAdmission(t *testing.T) {
|
|||||||
pvlabeler: mockVolumeLabels(map[string]string{
|
pvlabeler: mockVolumeLabels(map[string]string{
|
||||||
v1.LabelZoneFailureDomain: "domain1",
|
v1.LabelZoneFailureDomain: "domain1",
|
||||||
v1.LabelZoneRegion: "region1",
|
v1.LabelZoneRegion: "region1",
|
||||||
|
v1.LabelZoneFailureDomainStable: "domain1",
|
||||||
|
v1.LabelZoneRegionStable: "region1",
|
||||||
}),
|
}),
|
||||||
preAdmissionPV: &api.PersistentVolume{
|
preAdmissionPV: &api.PersistentVolume{
|
||||||
ObjectMeta: metav1.ObjectMeta{
|
ObjectMeta: metav1.ObjectMeta{
|
||||||
@ -331,6 +375,8 @@ func Test_PVLAdmission(t *testing.T) {
|
|||||||
Labels: map[string]string{
|
Labels: map[string]string{
|
||||||
v1.LabelZoneFailureDomain: "domain1",
|
v1.LabelZoneFailureDomain: "domain1",
|
||||||
v1.LabelZoneRegion: "region1",
|
v1.LabelZoneRegion: "region1",
|
||||||
|
v1.LabelZoneFailureDomainStable: "domain1",
|
||||||
|
v1.LabelZoneRegionStable: "region1",
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
Spec: api.PersistentVolumeSpec{
|
Spec: api.PersistentVolumeSpec{
|
||||||
@ -356,6 +402,20 @@ func Test_PVLAdmission(t *testing.T) {
|
|||||||
},
|
},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
MatchExpressions: []api.NodeSelectorRequirement{
|
||||||
|
{
|
||||||
|
Key: v1.LabelZoneRegionStable,
|
||||||
|
Operator: api.NodeSelectorOpIn,
|
||||||
|
Values: []string{"region1"},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
Key: v1.LabelZoneFailureDomainStable,
|
||||||
|
Operator: api.NodeSelectorOpIn,
|
||||||
|
Values: []string{"domain1"},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
@ -370,6 +430,7 @@ func Test_PVLAdmission(t *testing.T) {
|
|||||||
"a": "1",
|
"a": "1",
|
||||||
"b": "2",
|
"b": "2",
|
||||||
v1.LabelZoneFailureDomain: "1__2__3",
|
v1.LabelZoneFailureDomain: "1__2__3",
|
||||||
|
v1.LabelZoneFailureDomainStable: "1__2__3",
|
||||||
}),
|
}),
|
||||||
preAdmissionPV: &api.PersistentVolume{
|
preAdmissionPV: &api.PersistentVolume{
|
||||||
ObjectMeta: metav1.ObjectMeta{Name: "gcepd", Namespace: "myns"},
|
ObjectMeta: metav1.ObjectMeta{Name: "gcepd", Namespace: "myns"},
|
||||||
@ -389,6 +450,7 @@ func Test_PVLAdmission(t *testing.T) {
|
|||||||
"a": "1",
|
"a": "1",
|
||||||
"b": "2",
|
"b": "2",
|
||||||
v1.LabelZoneFailureDomain: "1__2__3",
|
v1.LabelZoneFailureDomain: "1__2__3",
|
||||||
|
v1.LabelZoneFailureDomainStable: "1__2__3",
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
Spec: api.PersistentVolumeSpec{
|
Spec: api.PersistentVolumeSpec{
|
||||||
@ -419,6 +481,25 @@ func Test_PVLAdmission(t *testing.T) {
|
|||||||
},
|
},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
MatchExpressions: []api.NodeSelectorRequirement{
|
||||||
|
{
|
||||||
|
Key: "a",
|
||||||
|
Operator: api.NodeSelectorOpIn,
|
||||||
|
Values: []string{"1"},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
Key: "b",
|
||||||
|
Operator: api.NodeSelectorOpIn,
|
||||||
|
Values: []string{"2"},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
Key: v1.LabelZoneFailureDomainStable,
|
||||||
|
Operator: api.NodeSelectorOpIn,
|
||||||
|
Values: []string{"1", "2", "3"},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
@ -433,6 +514,7 @@ func Test_PVLAdmission(t *testing.T) {
|
|||||||
"a": "1",
|
"a": "1",
|
||||||
"b": "2",
|
"b": "2",
|
||||||
v1.LabelZoneFailureDomain: "1__2__3",
|
v1.LabelZoneFailureDomain: "1__2__3",
|
||||||
|
v1.LabelZoneFailureDomainStable: "1__2__3",
|
||||||
}),
|
}),
|
||||||
preAdmissionPV: &api.PersistentVolume{
|
preAdmissionPV: &api.PersistentVolume{
|
||||||
ObjectMeta: metav1.ObjectMeta{
|
ObjectMeta: metav1.ObjectMeta{
|
||||||
@ -455,6 +537,7 @@ func Test_PVLAdmission(t *testing.T) {
|
|||||||
"a": "1",
|
"a": "1",
|
||||||
"b": "2",
|
"b": "2",
|
||||||
v1.LabelZoneFailureDomain: "1__2__3",
|
v1.LabelZoneFailureDomain: "1__2__3",
|
||||||
|
v1.LabelZoneFailureDomainStable: "1__2__3",
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
Spec: api.PersistentVolumeSpec{
|
Spec: api.PersistentVolumeSpec{
|
||||||
@ -485,6 +568,25 @@ func Test_PVLAdmission(t *testing.T) {
|
|||||||
},
|
},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
MatchExpressions: []api.NodeSelectorRequirement{
|
||||||
|
{
|
||||||
|
Key: "a",
|
||||||
|
Operator: api.NodeSelectorOpIn,
|
||||||
|
Values: []string{"1"},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
Key: "b",
|
||||||
|
Operator: api.NodeSelectorOpIn,
|
||||||
|
Values: []string{"2"},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
Key: v1.LabelZoneFailureDomainStable,
|
||||||
|
Operator: api.NodeSelectorOpIn,
|
||||||
|
Values: []string{"1", "2", "3"},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
@ -499,6 +601,7 @@ func Test_PVLAdmission(t *testing.T) {
|
|||||||
"a": "1",
|
"a": "1",
|
||||||
"b": "2",
|
"b": "2",
|
||||||
v1.LabelZoneFailureDomain: "1__2__3",
|
v1.LabelZoneFailureDomain: "1__2__3",
|
||||||
|
v1.LabelZoneFailureDomainStable: "1__2__3",
|
||||||
}),
|
}),
|
||||||
preAdmissionPV: &api.PersistentVolume{
|
preAdmissionPV: &api.PersistentVolume{
|
||||||
ObjectMeta: metav1.ObjectMeta{
|
ObjectMeta: metav1.ObjectMeta{
|
||||||
@ -521,6 +624,7 @@ func Test_PVLAdmission(t *testing.T) {
|
|||||||
"a": "1",
|
"a": "1",
|
||||||
"b": "2",
|
"b": "2",
|
||||||
v1.LabelZoneFailureDomain: "1__2__3",
|
v1.LabelZoneFailureDomain: "1__2__3",
|
||||||
|
v1.LabelZoneFailureDomainStable: "1__2__3",
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
Spec: api.PersistentVolumeSpec{
|
Spec: api.PersistentVolumeSpec{
|
||||||
@ -551,6 +655,25 @@ func Test_PVLAdmission(t *testing.T) {
|
|||||||
},
|
},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
MatchExpressions: []api.NodeSelectorRequirement{
|
||||||
|
{
|
||||||
|
Key: "a",
|
||||||
|
Operator: api.NodeSelectorOpIn,
|
||||||
|
Values: []string{"1"},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
Key: "b",
|
||||||
|
Operator: api.NodeSelectorOpIn,
|
||||||
|
Values: []string{"2"},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
Key: v1.LabelZoneFailureDomainStable,
|
||||||
|
Operator: api.NodeSelectorOpIn,
|
||||||
|
Values: []string{"1", "2", "3"},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
@ -565,6 +688,7 @@ func Test_PVLAdmission(t *testing.T) {
|
|||||||
"a": "1",
|
"a": "1",
|
||||||
"b": "2",
|
"b": "2",
|
||||||
v1.LabelZoneFailureDomain: "1__2__3",
|
v1.LabelZoneFailureDomain: "1__2__3",
|
||||||
|
v1.LabelZoneFailureDomainStable: "1__2__3",
|
||||||
}),
|
}),
|
||||||
preAdmissionPV: &api.PersistentVolume{
|
preAdmissionPV: &api.PersistentVolume{
|
||||||
ObjectMeta: metav1.ObjectMeta{
|
ObjectMeta: metav1.ObjectMeta{
|
||||||
@ -590,6 +714,7 @@ func Test_PVLAdmission(t *testing.T) {
|
|||||||
"a": "1",
|
"a": "1",
|
||||||
"b": "2",
|
"b": "2",
|
||||||
v1.LabelZoneFailureDomain: "1__2__3",
|
v1.LabelZoneFailureDomain: "1__2__3",
|
||||||
|
v1.LabelZoneFailureDomainStable: "1__2__3",
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
Spec: api.PersistentVolumeSpec{
|
Spec: api.PersistentVolumeSpec{
|
||||||
@ -620,6 +745,25 @@ func Test_PVLAdmission(t *testing.T) {
|
|||||||
},
|
},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
MatchExpressions: []api.NodeSelectorRequirement{
|
||||||
|
{
|
||||||
|
Key: "a",
|
||||||
|
Operator: api.NodeSelectorOpIn,
|
||||||
|
Values: []string{"1"},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
Key: "b",
|
||||||
|
Operator: api.NodeSelectorOpIn,
|
||||||
|
Values: []string{"2"},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
Key: v1.LabelZoneFailureDomainStable,
|
||||||
|
Operator: api.NodeSelectorOpIn,
|
||||||
|
Values: []string{"1", "2", "3"},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
@ -822,6 +966,7 @@ func Test_PVLAdmission(t *testing.T) {
|
|||||||
"a": "1",
|
"a": "1",
|
||||||
"b": "2",
|
"b": "2",
|
||||||
v1.LabelZoneFailureDomain: "1__2__3",
|
v1.LabelZoneFailureDomain: "1__2__3",
|
||||||
|
v1.LabelZoneFailureDomainStable: "1__2__3",
|
||||||
}),
|
}),
|
||||||
preAdmissionPV: &api.PersistentVolume{
|
preAdmissionPV: &api.PersistentVolume{
|
||||||
ObjectMeta: metav1.ObjectMeta{
|
ObjectMeta: metav1.ObjectMeta{
|
||||||
@ -844,6 +989,7 @@ func Test_PVLAdmission(t *testing.T) {
|
|||||||
"a": "1",
|
"a": "1",
|
||||||
"b": "2",
|
"b": "2",
|
||||||
v1.LabelZoneFailureDomain: "1__2__3",
|
v1.LabelZoneFailureDomain: "1__2__3",
|
||||||
|
v1.LabelZoneFailureDomainStable: "1__2__3",
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
Spec: api.PersistentVolumeSpec{
|
Spec: api.PersistentVolumeSpec{
|
||||||
@ -874,6 +1020,25 @@ func Test_PVLAdmission(t *testing.T) {
|
|||||||
},
|
},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
MatchExpressions: []api.NodeSelectorRequirement{
|
||||||
|
{
|
||||||
|
Key: "a",
|
||||||
|
Operator: api.NodeSelectorOpIn,
|
||||||
|
Values: []string{"1"},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
Key: "b",
|
||||||
|
Operator: api.NodeSelectorOpIn,
|
||||||
|
Values: []string{"2"},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
Key: v1.LabelZoneFailureDomainStable,
|
||||||
|
Operator: api.NodeSelectorOpIn,
|
||||||
|
Values: []string{"1", "2", "3"},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
@ -900,6 +1065,8 @@ func Test_PVLAdmission(t *testing.T) {
|
|||||||
if !reflect.DeepEqual(testcase.preAdmissionPV, testcase.postAdmissionPV) {
|
if !reflect.DeepEqual(testcase.preAdmissionPV, testcase.postAdmissionPV) {
|
||||||
t.Logf("expected PV: %+v", testcase.postAdmissionPV)
|
t.Logf("expected PV: %+v", testcase.postAdmissionPV)
|
||||||
t.Logf("actual PV: %+v", testcase.preAdmissionPV)
|
t.Logf("actual PV: %+v", testcase.preAdmissionPV)
|
||||||
|
t.Logf("expected PV node affinity: %+v", testcase.postAdmissionPV.Spec.NodeAffinity.Required)
|
||||||
|
t.Logf("actual PV node affinity: %+v", testcase.preAdmissionPV.Spec.NodeAffinity.Required)
|
||||||
t.Error("unexpected PV")
|
t.Error("unexpected PV")
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -927,10 +1094,12 @@ func sortMatchExpressions(pv *api.PersistentVolume) {
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
match := pv.Spec.NodeAffinity.Required.NodeSelectorTerms[0].MatchExpressions
|
for t := range pv.Spec.NodeAffinity.Required.NodeSelectorTerms {
|
||||||
|
match := pv.Spec.NodeAffinity.Required.NodeSelectorTerms[t].MatchExpressions
|
||||||
sort.Slice(match, func(i, j int) bool {
|
sort.Slice(match, func(i, j int) bool {
|
||||||
return match[i].Key < match[j].Key
|
return match[i].Key < match[j].Key
|
||||||
})
|
})
|
||||||
|
|
||||||
pv.Spec.NodeAffinity.Required.NodeSelectorTerms[0].MatchExpressions = match
|
pv.Spec.NodeAffinity.Required.NodeSelectorTerms[t].MatchExpressions = match
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -120,7 +120,11 @@ func SelectZonesForVolume(zoneParameterPresent, zonesParameterPresent bool, zone
|
|||||||
var ok bool
|
var ok bool
|
||||||
zoneFromNode, ok = node.ObjectMeta.Labels[v1.LabelZoneFailureDomain]
|
zoneFromNode, ok = node.ObjectMeta.Labels[v1.LabelZoneFailureDomain]
|
||||||
if !ok {
|
if !ok {
|
||||||
return nil, fmt.Errorf("%s Label for node missing", v1.LabelZoneFailureDomain)
|
zoneFromNode, ok = node.ObjectMeta.Labels[v1.LabelZoneFailureDomainStable]
|
||||||
|
}
|
||||||
|
|
||||||
|
if !ok {
|
||||||
|
return nil, fmt.Errorf("Label %q or %q for node is missing", v1.LabelZoneFailureDomain, v1.LabelZoneFailureDomainStable)
|
||||||
}
|
}
|
||||||
// if single replica volume and node with zone found, return immediately
|
// if single replica volume and node with zone found, return immediately
|
||||||
if numReplicas == 1 {
|
if numReplicas == 1 {
|
||||||
@ -135,7 +139,8 @@ func SelectZonesForVolume(zoneParameterPresent, zonesParameterPresent bool, zone
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (len(allowedTopologies) > 0) && (allowedZones.Len() == 0) {
|
if (len(allowedTopologies) > 0) && (allowedZones.Len() == 0) {
|
||||||
return nil, fmt.Errorf("no matchLabelExpressions with %s key found in allowedTopologies. Please specify matchLabelExpressions with %s key", v1.LabelZoneFailureDomain, v1.LabelZoneFailureDomain)
|
return nil, fmt.Errorf("no matchLabelExpressions with keys %q,%q found in allowedTopologies. Please specify matchLabelExpressions with keys: %q,%q",
|
||||||
|
v1.LabelZoneFailureDomain, v1.LabelZoneFailureDomainStable, v1.LabelZoneFailureDomain, v1.LabelZoneFailureDomainStable)
|
||||||
}
|
}
|
||||||
|
|
||||||
if allowedZones.Len() > 0 {
|
if allowedZones.Len() > 0 {
|
||||||
@ -185,7 +190,7 @@ func ZonesFromAllowedTopologies(allowedTopologies []v1.TopologySelectorTerm) (se
|
|||||||
zones := make(sets.String)
|
zones := make(sets.String)
|
||||||
for _, term := range allowedTopologies {
|
for _, term := range allowedTopologies {
|
||||||
for _, exp := range term.MatchLabelExpressions {
|
for _, exp := range term.MatchLabelExpressions {
|
||||||
if exp.Key == v1.LabelZoneFailureDomain {
|
if exp.Key == v1.LabelZoneFailureDomain || exp.Key == v1.LabelZoneFailureDomainStable {
|
||||||
for _, value := range exp.Values {
|
for _, value := range exp.Values {
|
||||||
zones.Insert(value)
|
zones.Insert(value)
|
||||||
}
|
}
|
||||||
@ -308,3 +313,103 @@ func getPVCNameHashAndIndexOffset(pvcName string) (hash uint32, index uint32) {
|
|||||||
|
|
||||||
return hash, index
|
return hash, index
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// TranslateZoneRegionLabelsToNodeSelectorTerms translates the set of provided labels as []v1.NodeSelectorTerms.
|
||||||
|
// For zone/region topologies, a node can have either only the beta label (failure-domain.beta.kubernetes.io/{zone,region})
|
||||||
|
// or both the beta and GA label (topology.kubernetes.io/{zone,region}). We have to put each topology label into
|
||||||
|
// separate node selector terms so PVs can be scheduled on nodes with either one or both labels
|
||||||
|
func TranslateZoneRegionLabelsToNodeSelectorTerms(labels map[string]string) []v1.NodeSelectorTerm {
|
||||||
|
nodeSelectorTerms := make([]v1.NodeSelectorTerm, 0)
|
||||||
|
deprecatedTopologyReqs := make([]v1.NodeSelectorRequirement, 0)
|
||||||
|
topologyReqs := make([]v1.NodeSelectorRequirement, 0)
|
||||||
|
|
||||||
|
if len(labels) == 0 {
|
||||||
|
return nodeSelectorTerms
|
||||||
|
}
|
||||||
|
|
||||||
|
for k, v := range labels {
|
||||||
|
if k == v1.LabelZoneFailureDomain || k == v1.LabelZoneRegion {
|
||||||
|
deprecatedTopologyReqs = append(deprecatedTopologyReqs, v1.NodeSelectorRequirement{Key: k, Operator: v1.NodeSelectorOpIn, Values: []string{v}})
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
if k == v1.LabelZoneFailureDomainStable || k == v1.LabelZoneRegionStable {
|
||||||
|
topologyReqs = append(topologyReqs, v1.NodeSelectorRequirement{Key: k, Operator: v1.NodeSelectorOpIn, Values: []string{v}})
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
// remaining labels outside known topology labels get added to both beta and GA node selectors
|
||||||
|
deprecatedTopologyReqs = append(deprecatedTopologyReqs, v1.NodeSelectorRequirement{Key: k, Operator: v1.NodeSelectorOpIn, Values: []string{v}})
|
||||||
|
topologyReqs = append(topologyReqs, v1.NodeSelectorRequirement{Key: k, Operator: v1.NodeSelectorOpIn, Values: []string{v}})
|
||||||
|
}
|
||||||
|
|
||||||
|
if len(deprecatedTopologyReqs) > 0 {
|
||||||
|
nodeSelectorTerms = append(nodeSelectorTerms, v1.NodeSelectorTerm{
|
||||||
|
MatchExpressions: deprecatedTopologyReqs,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
if len(topologyReqs) > 0 {
|
||||||
|
nodeSelectorTerms = append(nodeSelectorTerms, v1.NodeSelectorTerm{
|
||||||
|
MatchExpressions: topologyReqs,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
return nodeSelectorTerms
|
||||||
|
}
|
||||||
|
|
||||||
|
// TranslateZoneLabelsToNodeSelectorTerms translates the set of provided labels as []v1.NodeSelectorTerms.
|
||||||
|
// For zone topologies, a node can have either only the beta label (failure-domain.beta.kubernetes.io/zone)
|
||||||
|
// or both the beta and GA label (topology.kubernetes.io/zone). We have to put each topology label into
|
||||||
|
// separate node selector terms so PVs can be scheduled on nodes with either one or both labels
|
||||||
|
func TranslateZoneLabelsToNodeSelectorTerms(labels map[string]string) ([]v1.NodeSelectorTerm, error) {
|
||||||
|
nodeSelectorTerms := make([]v1.NodeSelectorTerm, 0)
|
||||||
|
deprecatedTopologyReqs := make([]v1.NodeSelectorRequirement, 0)
|
||||||
|
topologyReqs := make([]v1.NodeSelectorRequirement, 0)
|
||||||
|
|
||||||
|
if len(labels) == 0 {
|
||||||
|
return nodeSelectorTerms, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
for k, v := range labels {
|
||||||
|
var values []string
|
||||||
|
if k == v1.LabelZoneFailureDomain {
|
||||||
|
values, err := LabelZonesToList(v)
|
||||||
|
if err != nil {
|
||||||
|
return nil, fmt.Errorf("failed to convert label string for Zone: %s to a List: %v", v, err)
|
||||||
|
}
|
||||||
|
|
||||||
|
deprecatedTopologyReqs = append(deprecatedTopologyReqs, v1.NodeSelectorRequirement{Key: k, Operator: v1.NodeSelectorOpIn, Values: values})
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
if k == v1.LabelZoneFailureDomainStable {
|
||||||
|
values, err := LabelZonesToList(v)
|
||||||
|
if err != nil {
|
||||||
|
return nil, fmt.Errorf("failed to convert label string for Zone: %s to a List: %v", v, err)
|
||||||
|
}
|
||||||
|
|
||||||
|
topologyReqs = append(topologyReqs, v1.NodeSelectorRequirement{Key: k, Operator: v1.NodeSelectorOpIn, Values: values})
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
// remaining labels outside known topology labels get added to both beta and GA node selectors
|
||||||
|
values = []string{v}
|
||||||
|
deprecatedTopologyReqs = append(deprecatedTopologyReqs, v1.NodeSelectorRequirement{Key: k, Operator: v1.NodeSelectorOpIn, Values: values})
|
||||||
|
topologyReqs = append(topologyReqs, v1.NodeSelectorRequirement{Key: k, Operator: v1.NodeSelectorOpIn, Values: values})
|
||||||
|
}
|
||||||
|
|
||||||
|
if len(deprecatedTopologyReqs) > 0 {
|
||||||
|
nodeSelectorTerms = append(nodeSelectorTerms, v1.NodeSelectorTerm{
|
||||||
|
MatchExpressions: deprecatedTopologyReqs,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
if len(topologyReqs) > 0 {
|
||||||
|
nodeSelectorTerms = append(nodeSelectorTerms, v1.NodeSelectorTerm{
|
||||||
|
MatchExpressions: topologyReqs,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
return nodeSelectorTerms, nil
|
||||||
|
}
|
||||||
|
@ -17,7 +17,10 @@ limitations under the License.
|
|||||||
package helpers
|
package helpers
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"fmt"
|
||||||
"hash/fnv"
|
"hash/fnv"
|
||||||
|
"reflect"
|
||||||
|
"sort"
|
||||||
"testing"
|
"testing"
|
||||||
|
|
||||||
"k8s.io/api/core/v1"
|
"k8s.io/api/core/v1"
|
||||||
@ -417,7 +420,7 @@ func TestChooseZonesForVolume(t *testing.T) {
|
|||||||
func TestSelectZoneForVolume(t *testing.T) {
|
func TestSelectZoneForVolume(t *testing.T) {
|
||||||
|
|
||||||
nodeWithZoneLabels := &v1.Node{}
|
nodeWithZoneLabels := &v1.Node{}
|
||||||
nodeWithZoneLabels.Labels = map[string]string{v1.LabelZoneFailureDomain: "zoneX"}
|
nodeWithZoneLabels.Labels = map[string]string{v1.LabelZoneFailureDomain: "zoneX", v1.LabelZoneFailureDomainStable: "zoneX"}
|
||||||
|
|
||||||
nodeWithNoLabels := &v1.Node{}
|
nodeWithNoLabels := &v1.Node{}
|
||||||
|
|
||||||
@ -658,6 +661,10 @@ func TestSelectZoneForVolume(t *testing.T) {
|
|||||||
Key: v1.LabelZoneFailureDomain,
|
Key: v1.LabelZoneFailureDomain,
|
||||||
Values: []string{"zoneX", "zoneY"},
|
Values: []string{"zoneX", "zoneY"},
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
Key: v1.LabelZoneFailureDomainStable,
|
||||||
|
Values: []string{"zoneX", "zoneY"},
|
||||||
|
},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
@ -689,6 +696,22 @@ func TestSelectZoneForVolume(t *testing.T) {
|
|||||||
},
|
},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
MatchLabelExpressions: []v1.TopologySelectorLabelRequirement{
|
||||||
|
{
|
||||||
|
Key: v1.LabelZoneFailureDomainStable,
|
||||||
|
Values: []string{"zoneX"},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
MatchLabelExpressions: []v1.TopologySelectorLabelRequirement{
|
||||||
|
{
|
||||||
|
Key: v1.LabelZoneFailureDomainStable,
|
||||||
|
Values: []string{"zoneY"},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
},
|
},
|
||||||
Reject: false,
|
Reject: false,
|
||||||
ExpectedZones: "zoneX,zoneY",
|
ExpectedZones: "zoneX,zoneY",
|
||||||
@ -1110,7 +1133,7 @@ func TestSelectZonesForVolume(t *testing.T) {
|
|||||||
// Select zones from node label and AllowedTopologies [Pass]
|
// Select zones from node label and AllowedTopologies [Pass]
|
||||||
// [1] Node with zone labels
|
// [1] Node with zone labels
|
||||||
// [2] no Zone/Zones parameters
|
// [2] no Zone/Zones parameters
|
||||||
// [3] AllowedTopologies with single term with multiple values specified
|
// [3] AllowedTopologies with single term with multiple values specified, both beta and GA zone labels
|
||||||
// [4] ReplicaCount specified
|
// [4] ReplicaCount specified
|
||||||
// [5] ZonesWithNodes irrelevant
|
// [5] ZonesWithNodes irrelevant
|
||||||
// Note: the test Name suffix is used as the pvcname and it's suffix is important to influence ChooseZonesForVolume
|
// Note: the test Name suffix is used as the pvcname and it's suffix is important to influence ChooseZonesForVolume
|
||||||
@ -1126,6 +1149,10 @@ func TestSelectZonesForVolume(t *testing.T) {
|
|||||||
Key: v1.LabelZoneFailureDomain,
|
Key: v1.LabelZoneFailureDomain,
|
||||||
Values: []string{"zoneV", "zoneW", "zoneX", "zoneY", "zoneZ"},
|
Values: []string{"zoneV", "zoneW", "zoneX", "zoneY", "zoneZ"},
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
Key: v1.LabelZoneFailureDomainStable,
|
||||||
|
Values: []string{"zoneV", "zoneW", "zoneX", "zoneY", "zoneZ"},
|
||||||
|
},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
@ -1138,7 +1165,35 @@ func TestSelectZonesForVolume(t *testing.T) {
|
|||||||
// Select zones from node label and AllowedTopologies [Pass]
|
// Select zones from node label and AllowedTopologies [Pass]
|
||||||
// [1] Node with zone labels
|
// [1] Node with zone labels
|
||||||
// [2] no Zone/Zones parameters
|
// [2] no Zone/Zones parameters
|
||||||
// [3] AllowedTopologies with single term with multiple values specified
|
// [3] AllowedTopologies with single term with multiple values specified, only GA zone labels
|
||||||
|
// [4] ReplicaCount specified
|
||||||
|
// [5] ZonesWithNodes irrelevant
|
||||||
|
// Note: the test Name suffix is used as the pvcname and it's suffix is important to influence ChooseZonesForVolume
|
||||||
|
// to NOT pick zoneX from AllowedTopologies if zoneFromNode is incorrectly set or not set at all.
|
||||||
|
{
|
||||||
|
Name: "Node_with_Zone_labels_and_Multiple_Allowed_Topology_values_Superset-1",
|
||||||
|
Node: nodeWithZoneLabels,
|
||||||
|
ReplicaCount: 2,
|
||||||
|
AllowedTopologies: []v1.TopologySelectorTerm{
|
||||||
|
{
|
||||||
|
MatchLabelExpressions: []v1.TopologySelectorLabelRequirement{
|
||||||
|
{
|
||||||
|
Key: v1.LabelZoneFailureDomainStable,
|
||||||
|
Values: []string{"zoneV", "zoneW", "zoneX", "zoneY", "zoneZ"},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
Reject: false,
|
||||||
|
ExpectSpecificZone: true,
|
||||||
|
ExpectedZone: "zoneX",
|
||||||
|
ExpectedZones: "zoneV,zoneW,zoneX,zoneY,zoneZ",
|
||||||
|
},
|
||||||
|
|
||||||
|
// Select zones from node label and AllowedTopologies [Pass]
|
||||||
|
// [1] Node with zone labels
|
||||||
|
// [2] no Zone/Zones parameters
|
||||||
|
// [3] AllowedTopologies with single term with multiple values specified, has both beta/GA zone labels
|
||||||
// [4] ReplicaCount specified
|
// [4] ReplicaCount specified
|
||||||
// [5] ZonesWithNodes irrelevant
|
// [5] ZonesWithNodes irrelevant
|
||||||
{
|
{
|
||||||
@ -1152,6 +1207,10 @@ func TestSelectZonesForVolume(t *testing.T) {
|
|||||||
Key: v1.LabelZoneFailureDomain,
|
Key: v1.LabelZoneFailureDomain,
|
||||||
Values: []string{"zoneX", "zoneY"},
|
Values: []string{"zoneX", "zoneY"},
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
Key: v1.LabelZoneFailureDomainStable,
|
||||||
|
Values: []string{"zoneX", "zoneY"},
|
||||||
|
},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
@ -1275,7 +1334,7 @@ func TestSelectZonesForVolume(t *testing.T) {
|
|||||||
// Select zones from node label and AllowedTopologies [Pass]
|
// Select zones from node label and AllowedTopologies [Pass]
|
||||||
// [1] Node with zone labels
|
// [1] Node with zone labels
|
||||||
// [2] no Zone/Zones parametes specified
|
// [2] no Zone/Zones parametes specified
|
||||||
// [3] AllowedTopologies with multiple terms specified
|
// [3] AllowedTopologies with multiple terms specified, has both beta/GA zone labels
|
||||||
// [4] ReplicaCount specified
|
// [4] ReplicaCount specified
|
||||||
// [5] ZonesWithNodes irrelevant
|
// [5] ZonesWithNodes irrelevant
|
||||||
{
|
{
|
||||||
@ -1299,6 +1358,22 @@ func TestSelectZonesForVolume(t *testing.T) {
|
|||||||
},
|
},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
MatchLabelExpressions: []v1.TopologySelectorLabelRequirement{
|
||||||
|
{
|
||||||
|
Key: v1.LabelZoneFailureDomainStable,
|
||||||
|
Values: []string{"zoneX"},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
MatchLabelExpressions: []v1.TopologySelectorLabelRequirement{
|
||||||
|
{
|
||||||
|
Key: v1.LabelZoneFailureDomainStable,
|
||||||
|
Values: []string{"zoneY"},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
},
|
},
|
||||||
Reject: false,
|
Reject: false,
|
||||||
ExpectSpecificZone: true,
|
ExpectSpecificZone: true,
|
||||||
@ -1637,3 +1712,396 @@ func checkFnv32(t *testing.T, s string, expected uint32) {
|
|||||||
t.Fatalf("hash of %q was %v, expected %v", s, h.Sum32(), expected)
|
t.Fatalf("hash of %q was %v, expected %v", s, h.Sum32(), expected)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func Test_TranslateZoneRegionLabelsToNodeSelectorTerms(t *testing.T) {
|
||||||
|
testcases := []struct {
|
||||||
|
name string
|
||||||
|
labels map[string]string
|
||||||
|
nodeSelectorTerm []v1.NodeSelectorTerm
|
||||||
|
}{
|
||||||
|
{
|
||||||
|
name: "empty label set",
|
||||||
|
labels: map[string]string{},
|
||||||
|
nodeSelectorTerm: []v1.NodeSelectorTerm{},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "nil label set",
|
||||||
|
labels: nil,
|
||||||
|
nodeSelectorTerm: []v1.NodeSelectorTerm{},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "with beta zone labels",
|
||||||
|
labels: map[string]string{
|
||||||
|
v1.LabelZoneFailureDomain: "zone1",
|
||||||
|
},
|
||||||
|
nodeSelectorTerm: []v1.NodeSelectorTerm{
|
||||||
|
{
|
||||||
|
MatchExpressions: []v1.NodeSelectorRequirement{
|
||||||
|
{
|
||||||
|
Key: v1.LabelZoneFailureDomain,
|
||||||
|
Operator: v1.NodeSelectorOpIn,
|
||||||
|
Values: []string{"zone1"},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "with beta and stable zone labels",
|
||||||
|
labels: map[string]string{
|
||||||
|
v1.LabelZoneFailureDomain: "zone1",
|
||||||
|
v1.LabelZoneFailureDomainStable: "zone1",
|
||||||
|
},
|
||||||
|
nodeSelectorTerm: []v1.NodeSelectorTerm{
|
||||||
|
{
|
||||||
|
MatchExpressions: []v1.NodeSelectorRequirement{
|
||||||
|
{
|
||||||
|
Key: v1.LabelZoneFailureDomain,
|
||||||
|
Operator: v1.NodeSelectorOpIn,
|
||||||
|
Values: []string{"zone1"},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
MatchExpressions: []v1.NodeSelectorRequirement{
|
||||||
|
{
|
||||||
|
Key: v1.LabelZoneFailureDomainStable,
|
||||||
|
Operator: v1.NodeSelectorOpIn,
|
||||||
|
Values: []string{"zone1"},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "with beta zone and region labels",
|
||||||
|
labels: map[string]string{
|
||||||
|
v1.LabelZoneFailureDomain: "zone1",
|
||||||
|
v1.LabelZoneRegion: "region1",
|
||||||
|
},
|
||||||
|
nodeSelectorTerm: []v1.NodeSelectorTerm{
|
||||||
|
{
|
||||||
|
MatchExpressions: []v1.NodeSelectorRequirement{
|
||||||
|
{
|
||||||
|
Key: v1.LabelZoneFailureDomain,
|
||||||
|
Operator: v1.NodeSelectorOpIn,
|
||||||
|
Values: []string{"zone1"},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
Key: v1.LabelZoneRegion,
|
||||||
|
Operator: v1.NodeSelectorOpIn,
|
||||||
|
Values: []string{"region1"},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "with stable zone and region labels",
|
||||||
|
labels: map[string]string{
|
||||||
|
v1.LabelZoneFailureDomain: "zone1",
|
||||||
|
v1.LabelZoneRegion: "region1",
|
||||||
|
},
|
||||||
|
nodeSelectorTerm: []v1.NodeSelectorTerm{
|
||||||
|
{
|
||||||
|
MatchExpressions: []v1.NodeSelectorRequirement{
|
||||||
|
{
|
||||||
|
Key: v1.LabelZoneFailureDomain,
|
||||||
|
Operator: v1.NodeSelectorOpIn,
|
||||||
|
Values: []string{"zone1"},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
Key: v1.LabelZoneRegion,
|
||||||
|
Operator: v1.NodeSelectorOpIn,
|
||||||
|
Values: []string{"region1"},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "with beta/stable and zone/region labels",
|
||||||
|
labels: map[string]string{
|
||||||
|
v1.LabelZoneFailureDomain: "zone1",
|
||||||
|
v1.LabelZoneFailureDomainStable: "zone1",
|
||||||
|
v1.LabelZoneRegion: "region1",
|
||||||
|
v1.LabelZoneRegionStable: "region1",
|
||||||
|
},
|
||||||
|
nodeSelectorTerm: []v1.NodeSelectorTerm{
|
||||||
|
{
|
||||||
|
MatchExpressions: []v1.NodeSelectorRequirement{
|
||||||
|
{
|
||||||
|
Key: v1.LabelZoneFailureDomain,
|
||||||
|
Operator: v1.NodeSelectorOpIn,
|
||||||
|
Values: []string{"zone1"},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
Key: v1.LabelZoneRegion,
|
||||||
|
Operator: v1.NodeSelectorOpIn,
|
||||||
|
Values: []string{"region1"},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
MatchExpressions: []v1.NodeSelectorRequirement{
|
||||||
|
{
|
||||||
|
Key: v1.LabelZoneFailureDomainStable,
|
||||||
|
Operator: v1.NodeSelectorOpIn,
|
||||||
|
Values: []string{"zone1"},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
Key: v1.LabelZoneRegionStable,
|
||||||
|
Operator: v1.NodeSelectorOpIn,
|
||||||
|
Values: []string{"region1"},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "with beta/stable and zone/region and other labels",
|
||||||
|
labels: map[string]string{
|
||||||
|
v1.LabelZoneFailureDomain: "zone1",
|
||||||
|
v1.LabelZoneFailureDomainStable: "zone1",
|
||||||
|
v1.LabelZoneRegion: "region1",
|
||||||
|
v1.LabelZoneRegionStable: "region1",
|
||||||
|
"hello": "world",
|
||||||
|
},
|
||||||
|
nodeSelectorTerm: []v1.NodeSelectorTerm{
|
||||||
|
{
|
||||||
|
MatchExpressions: []v1.NodeSelectorRequirement{
|
||||||
|
{
|
||||||
|
Key: v1.LabelZoneFailureDomain,
|
||||||
|
Operator: v1.NodeSelectorOpIn,
|
||||||
|
Values: []string{"zone1"},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
Key: v1.LabelZoneRegion,
|
||||||
|
Operator: v1.NodeSelectorOpIn,
|
||||||
|
Values: []string{"region1"},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
Key: "hello",
|
||||||
|
Operator: v1.NodeSelectorOpIn,
|
||||||
|
Values: []string{"world"},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
MatchExpressions: []v1.NodeSelectorRequirement{
|
||||||
|
{
|
||||||
|
Key: v1.LabelZoneFailureDomainStable,
|
||||||
|
Operator: v1.NodeSelectorOpIn,
|
||||||
|
Values: []string{"zone1"},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
Key: v1.LabelZoneRegionStable,
|
||||||
|
Operator: v1.NodeSelectorOpIn,
|
||||||
|
Values: []string{"region1"},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
Key: "hello",
|
||||||
|
Operator: v1.NodeSelectorOpIn,
|
||||||
|
Values: []string{"world"},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, testcase := range testcases {
|
||||||
|
t.Run(testcase.name, func(t *testing.T) {
|
||||||
|
nodeSelectorTerm := TranslateZoneRegionLabelsToNodeSelectorTerms(testcase.labels)
|
||||||
|
sortNodeSelectorTerm(nodeSelectorTerm)
|
||||||
|
sortNodeSelectorTerm(testcase.nodeSelectorTerm)
|
||||||
|
|
||||||
|
if !reflect.DeepEqual(nodeSelectorTerm, testcase.nodeSelectorTerm) {
|
||||||
|
t.Logf("actual node selector term: %v", nodeSelectorTerm)
|
||||||
|
t.Logf("expected node selector term: %v", testcase.nodeSelectorTerm)
|
||||||
|
t.Error("unexpected node selector term")
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func Test_TranslateZoneLabelsToNodeSelectorTerms(t *testing.T) {
|
||||||
|
testcases := []struct {
|
||||||
|
name string
|
||||||
|
labels map[string]string
|
||||||
|
nodeSelectorTerm []v1.NodeSelectorTerm
|
||||||
|
expectedErr error
|
||||||
|
}{
|
||||||
|
{
|
||||||
|
name: "empty label set",
|
||||||
|
labels: map[string]string{},
|
||||||
|
nodeSelectorTerm: []v1.NodeSelectorTerm{},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "nil label set",
|
||||||
|
labels: nil,
|
||||||
|
nodeSelectorTerm: []v1.NodeSelectorTerm{},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "with beta zone labels",
|
||||||
|
labels: map[string]string{
|
||||||
|
v1.LabelZoneFailureDomain: "zone1",
|
||||||
|
},
|
||||||
|
nodeSelectorTerm: []v1.NodeSelectorTerm{
|
||||||
|
{
|
||||||
|
MatchExpressions: []v1.NodeSelectorRequirement{
|
||||||
|
{
|
||||||
|
Key: v1.LabelZoneFailureDomain,
|
||||||
|
Operator: v1.NodeSelectorOpIn,
|
||||||
|
Values: []string{"zone1"},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "with beta multi-zone labels",
|
||||||
|
labels: map[string]string{
|
||||||
|
v1.LabelZoneFailureDomain: "zone1__zone2",
|
||||||
|
},
|
||||||
|
nodeSelectorTerm: []v1.NodeSelectorTerm{
|
||||||
|
{
|
||||||
|
MatchExpressions: []v1.NodeSelectorRequirement{
|
||||||
|
{
|
||||||
|
Key: v1.LabelZoneFailureDomain,
|
||||||
|
Operator: v1.NodeSelectorOpIn,
|
||||||
|
Values: []string{"zone1", "zone2"},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "with beta and stable zone labels",
|
||||||
|
labels: map[string]string{
|
||||||
|
v1.LabelZoneFailureDomain: "zone1",
|
||||||
|
v1.LabelZoneFailureDomainStable: "zone1",
|
||||||
|
},
|
||||||
|
nodeSelectorTerm: []v1.NodeSelectorTerm{
|
||||||
|
{
|
||||||
|
MatchExpressions: []v1.NodeSelectorRequirement{
|
||||||
|
{
|
||||||
|
Key: v1.LabelZoneFailureDomain,
|
||||||
|
Operator: v1.NodeSelectorOpIn,
|
||||||
|
Values: []string{"zone1"},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
MatchExpressions: []v1.NodeSelectorRequirement{
|
||||||
|
{
|
||||||
|
Key: v1.LabelZoneFailureDomainStable,
|
||||||
|
Operator: v1.NodeSelectorOpIn,
|
||||||
|
Values: []string{"zone1"},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "with beta and stable multi-zone labels",
|
||||||
|
labels: map[string]string{
|
||||||
|
v1.LabelZoneFailureDomain: "zone1__zone2",
|
||||||
|
v1.LabelZoneFailureDomainStable: "zone1__zone2",
|
||||||
|
},
|
||||||
|
nodeSelectorTerm: []v1.NodeSelectorTerm{
|
||||||
|
{
|
||||||
|
MatchExpressions: []v1.NodeSelectorRequirement{
|
||||||
|
{
|
||||||
|
Key: v1.LabelZoneFailureDomain,
|
||||||
|
Operator: v1.NodeSelectorOpIn,
|
||||||
|
Values: []string{"zone1", "zone2"},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
MatchExpressions: []v1.NodeSelectorRequirement{
|
||||||
|
{
|
||||||
|
Key: v1.LabelZoneFailureDomainStable,
|
||||||
|
Operator: v1.NodeSelectorOpIn,
|
||||||
|
Values: []string{"zone1", "zone2"},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "with beta/stable zones and others",
|
||||||
|
labels: map[string]string{
|
||||||
|
v1.LabelZoneFailureDomain: "zone1__zone2",
|
||||||
|
v1.LabelZoneFailureDomainStable: "zone1__zone2",
|
||||||
|
"hello": "world",
|
||||||
|
},
|
||||||
|
nodeSelectorTerm: []v1.NodeSelectorTerm{
|
||||||
|
{
|
||||||
|
MatchExpressions: []v1.NodeSelectorRequirement{
|
||||||
|
{
|
||||||
|
Key: v1.LabelZoneFailureDomain,
|
||||||
|
Operator: v1.NodeSelectorOpIn,
|
||||||
|
Values: []string{"zone1", "zone2"},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
Key: "hello",
|
||||||
|
Operator: v1.NodeSelectorOpIn,
|
||||||
|
Values: []string{"world"},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
MatchExpressions: []v1.NodeSelectorRequirement{
|
||||||
|
{
|
||||||
|
Key: v1.LabelZoneFailureDomainStable,
|
||||||
|
Operator: v1.NodeSelectorOpIn,
|
||||||
|
Values: []string{"zone1", "zone2"},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
Key: "hello",
|
||||||
|
Operator: v1.NodeSelectorOpIn,
|
||||||
|
Values: []string{"world"},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "with invalid zone value",
|
||||||
|
labels: map[string]string{
|
||||||
|
v1.LabelZoneFailureDomain: "zone1__",
|
||||||
|
},
|
||||||
|
expectedErr: fmt.Errorf("failed to convert label string for Zone: zone1__ to a List: %q separated list (%q) must not contain an empty string", "__", "zone1__"),
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, testcase := range testcases {
|
||||||
|
t.Run(testcase.name, func(t *testing.T) {
|
||||||
|
nodeSelectorTerm, err := TranslateZoneLabelsToNodeSelectorTerms(testcase.labels)
|
||||||
|
if !reflect.DeepEqual(err, testcase.expectedErr) {
|
||||||
|
t.Logf("actual err: %v", err)
|
||||||
|
t.Logf("expected err: %v", testcase.expectedErr)
|
||||||
|
t.Fatal("unexpected error")
|
||||||
|
}
|
||||||
|
|
||||||
|
sortNodeSelectorTerm(nodeSelectorTerm)
|
||||||
|
sortNodeSelectorTerm(testcase.nodeSelectorTerm)
|
||||||
|
|
||||||
|
if !reflect.DeepEqual(nodeSelectorTerm, testcase.nodeSelectorTerm) {
|
||||||
|
t.Logf("actual node selector term: %v", nodeSelectorTerm)
|
||||||
|
t.Logf("expected node selector term: %v", testcase.nodeSelectorTerm)
|
||||||
|
t.Error("unexpected node selector term")
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func sortNodeSelectorTerm(nodeSelectorTerms []v1.NodeSelectorTerm) {
|
||||||
|
for _, nodeSelectorTerm := range nodeSelectorTerms {
|
||||||
|
sort.Slice(nodeSelectorTerm.MatchExpressions, func(i, j int) bool {
|
||||||
|
return nodeSelectorTerm.MatchExpressions[i].Key < nodeSelectorTerm.MatchExpressions[j].Key
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
@ -73,13 +73,16 @@ func translateAllowedTopologies(terms []v1.TopologySelectorTerm) ([]v1.TopologyS
|
|||||||
newTopologies := []v1.TopologySelectorTerm{}
|
newTopologies := []v1.TopologySelectorTerm{}
|
||||||
for _, term := range terms {
|
for _, term := range terms {
|
||||||
newTerm := v1.TopologySelectorTerm{}
|
newTerm := v1.TopologySelectorTerm{}
|
||||||
|
zoneFound := false
|
||||||
for _, exp := range term.MatchLabelExpressions {
|
for _, exp := range term.MatchLabelExpressions {
|
||||||
var newExp v1.TopologySelectorLabelRequirement
|
var newExp v1.TopologySelectorLabelRequirement
|
||||||
if exp.Key == v1.LabelZoneFailureDomain {
|
if !zoneFound && (exp.Key == v1.LabelZoneFailureDomain || exp.Key == v1.LabelZoneFailureDomainStable) {
|
||||||
newExp = v1.TopologySelectorLabelRequirement{
|
newExp = v1.TopologySelectorLabelRequirement{
|
||||||
Key: GCEPDTopologyKey,
|
Key: GCEPDTopologyKey,
|
||||||
Values: exp.Values,
|
Values: exp.Values,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
zoneFound = true
|
||||||
} else if exp.Key == GCEPDTopologyKey {
|
} else if exp.Key == GCEPDTopologyKey {
|
||||||
newExp = exp
|
newExp = exp
|
||||||
} else {
|
} else {
|
||||||
@ -240,7 +243,11 @@ func (g *gcePersistentDiskCSITranslator) TranslateInTreePVToCSI(pv *v1.Persisten
|
|||||||
return nil, fmt.Errorf("pv is nil or GCE Persistent Disk source not defined on pv")
|
return nil, fmt.Errorf("pv is nil or GCE Persistent Disk source not defined on pv")
|
||||||
}
|
}
|
||||||
|
|
||||||
zonesLabel := pv.Labels[v1.LabelZoneFailureDomain]
|
zonesLabel, ok := pv.Labels[v1.LabelZoneFailureDomain]
|
||||||
|
if !ok {
|
||||||
|
zonesLabel = pv.Labels[v1.LabelZoneFailureDomainStable]
|
||||||
|
}
|
||||||
|
|
||||||
zones := strings.Split(zonesLabel, cloudvolume.LabelMultiZoneDelimiter)
|
zones := strings.Split(zonesLabel, cloudvolume.LabelMultiZoneDelimiter)
|
||||||
if len(zones) == 1 && len(zones[0]) != 0 {
|
if len(zones) == 1 && len(zones[0]) != 0 {
|
||||||
// Zonal
|
// Zonal
|
||||||
|
@ -72,10 +72,15 @@ func TestTranslatePDInTreeStorageClassToCSI(t *testing.T) {
|
|||||||
expOptions: NewStorageClass(map[string]string{}, generateToplogySelectors(GCEPDTopologyKey, []string{"foo"})),
|
expOptions: NewStorageClass(map[string]string{}, generateToplogySelectors(GCEPDTopologyKey, []string{"foo"})),
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
name: "some translated topology",
|
name: "some translated topology using deprecated zone label",
|
||||||
options: NewStorageClass(map[string]string{}, generateToplogySelectors(v1.LabelZoneFailureDomain, []string{"foo"})),
|
options: NewStorageClass(map[string]string{}, generateToplogySelectors(v1.LabelZoneFailureDomain, []string{"foo"})),
|
||||||
expOptions: NewStorageClass(map[string]string{}, generateToplogySelectors(GCEPDTopologyKey, []string{"foo"})),
|
expOptions: NewStorageClass(map[string]string{}, generateToplogySelectors(GCEPDTopologyKey, []string{"foo"})),
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
name: "some translated topology",
|
||||||
|
options: NewStorageClass(map[string]string{}, generateToplogySelectors(v1.LabelZoneFailureDomainStable, []string{"foo"})),
|
||||||
|
expOptions: NewStorageClass(map[string]string{}, generateToplogySelectors(GCEPDTopologyKey, []string{"foo"})),
|
||||||
|
},
|
||||||
{
|
{
|
||||||
name: "zone and topology",
|
name: "zone and topology",
|
||||||
options: NewStorageClass(map[string]string{"zone": "foo"}, generateToplogySelectors(GCEPDTopologyKey, []string{"foo"})),
|
options: NewStorageClass(map[string]string{"zone": "foo"}, generateToplogySelectors(GCEPDTopologyKey, []string{"foo"})),
|
||||||
|
@ -2639,11 +2639,15 @@ func (c *Cloud) GetVolumeLabels(volumeName KubernetesVolumeID) (map[string]strin
|
|||||||
}
|
}
|
||||||
|
|
||||||
labels[v1.LabelZoneFailureDomain] = az
|
labels[v1.LabelZoneFailureDomain] = az
|
||||||
|
labels[v1.LabelZoneFailureDomainStable] = az
|
||||||
|
|
||||||
region, err := azToRegion(az)
|
region, err := azToRegion(az)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
labels[v1.LabelZoneRegion] = region
|
labels[v1.LabelZoneRegion] = region
|
||||||
|
labels[v1.LabelZoneRegionStable] = region
|
||||||
|
|
||||||
return labels, nil
|
return labels, nil
|
||||||
}
|
}
|
||||||
|
@ -1263,7 +1263,10 @@ func TestGetVolumeLabels(t *testing.T) {
|
|||||||
assert.Nil(t, err, "Error creating Volume %v", err)
|
assert.Nil(t, err, "Error creating Volume %v", err)
|
||||||
assert.Equal(t, map[string]string{
|
assert.Equal(t, map[string]string{
|
||||||
v1.LabelZoneFailureDomain: "us-east-1a",
|
v1.LabelZoneFailureDomain: "us-east-1a",
|
||||||
v1.LabelZoneRegion: "us-east-1"}, labels)
|
v1.LabelZoneRegion: "us-east-1",
|
||||||
|
v1.LabelZoneFailureDomainStable: "us-east-1a",
|
||||||
|
v1.LabelZoneRegionStable: "us-east-1",
|
||||||
|
}, labels)
|
||||||
awsServices.ec2.(*MockedFakeEC2).AssertExpectations(t)
|
awsServices.ec2.(*MockedFakeEC2).AssertExpectations(t)
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -1338,6 +1341,8 @@ func TestGetLabelsForVolume(t *testing.T) {
|
|||||||
map[string]string{
|
map[string]string{
|
||||||
v1.LabelZoneFailureDomain: "us-east-1a",
|
v1.LabelZoneFailureDomain: "us-east-1a",
|
||||||
v1.LabelZoneRegion: "us-east-1",
|
v1.LabelZoneRegion: "us-east-1",
|
||||||
|
v1.LabelZoneFailureDomainStable: "us-east-1a",
|
||||||
|
v1.LabelZoneRegionStable: "us-east-1",
|
||||||
},
|
},
|
||||||
nil,
|
nil,
|
||||||
},
|
},
|
||||||
|
@ -636,7 +636,9 @@ func (az *Cloud) SetInformers(informerFactory informers.SharedInformerFactory) {
|
|||||||
prevNode := prev.(*v1.Node)
|
prevNode := prev.(*v1.Node)
|
||||||
newNode := obj.(*v1.Node)
|
newNode := obj.(*v1.Node)
|
||||||
if newNode.Labels[v1.LabelZoneFailureDomain] ==
|
if newNode.Labels[v1.LabelZoneFailureDomain] ==
|
||||||
prevNode.Labels[v1.LabelZoneFailureDomain] {
|
prevNode.Labels[v1.LabelZoneFailureDomain] &&
|
||||||
|
newNode.Labels[v1.LabelZoneFailureDomainStable] ==
|
||||||
|
prevNode.Labels[v1.LabelZoneFailureDomainStable] {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
az.updateNodeCaches(prevNode, newNode)
|
az.updateNodeCaches(prevNode, newNode)
|
||||||
@ -671,6 +673,10 @@ func (az *Cloud) updateNodeCaches(prevNode, newNode *v1.Node) {
|
|||||||
if prevNode != nil {
|
if prevNode != nil {
|
||||||
// Remove from nodeZones cache.
|
// Remove from nodeZones cache.
|
||||||
prevZone, ok := prevNode.ObjectMeta.Labels[v1.LabelZoneFailureDomain]
|
prevZone, ok := prevNode.ObjectMeta.Labels[v1.LabelZoneFailureDomain]
|
||||||
|
if !ok {
|
||||||
|
prevZone, ok = prevNode.ObjectMeta.Labels[v1.LabelZoneFailureDomainStable]
|
||||||
|
}
|
||||||
|
|
||||||
if ok && az.isAvailabilityZone(prevZone) {
|
if ok && az.isAvailabilityZone(prevZone) {
|
||||||
az.nodeZones[prevZone].Delete(prevNode.ObjectMeta.Name)
|
az.nodeZones[prevZone].Delete(prevNode.ObjectMeta.Name)
|
||||||
if az.nodeZones[prevZone].Len() == 0 {
|
if az.nodeZones[prevZone].Len() == 0 {
|
||||||
@ -694,6 +700,10 @@ func (az *Cloud) updateNodeCaches(prevNode, newNode *v1.Node) {
|
|||||||
if newNode != nil {
|
if newNode != nil {
|
||||||
// Add to nodeZones cache.
|
// Add to nodeZones cache.
|
||||||
newZone, ok := newNode.ObjectMeta.Labels[v1.LabelZoneFailureDomain]
|
newZone, ok := newNode.ObjectMeta.Labels[v1.LabelZoneFailureDomain]
|
||||||
|
if !ok {
|
||||||
|
newZone, ok = newNode.ObjectMeta.Labels[v1.LabelZoneFailureDomainStable]
|
||||||
|
}
|
||||||
|
|
||||||
if ok && az.isAvailabilityZone(newZone) {
|
if ok && az.isAvailabilityZone(newZone) {
|
||||||
if az.nodeZones[newZone] == nil {
|
if az.nodeZones[newZone] == nil {
|
||||||
az.nodeZones[newZone] = sets.NewString()
|
az.nodeZones[newZone] = sets.NewString()
|
||||||
|
@ -335,6 +335,8 @@ func (c *Cloud) GetAzureDiskLabels(diskURI string) (map[string]string, error) {
|
|||||||
labels := map[string]string{
|
labels := map[string]string{
|
||||||
v1.LabelZoneRegion: c.Location,
|
v1.LabelZoneRegion: c.Location,
|
||||||
v1.LabelZoneFailureDomain: zone,
|
v1.LabelZoneFailureDomain: zone,
|
||||||
|
v1.LabelZoneRegionStable: c.Location,
|
||||||
|
v1.LabelZoneFailureDomainStable: zone,
|
||||||
}
|
}
|
||||||
return labels, nil
|
return labels, nil
|
||||||
}
|
}
|
||||||
|
@ -694,7 +694,9 @@ func (g *Cloud) SetInformers(informerFactory informers.SharedInformerFactory) {
|
|||||||
prevNode := prev.(*v1.Node)
|
prevNode := prev.(*v1.Node)
|
||||||
newNode := obj.(*v1.Node)
|
newNode := obj.(*v1.Node)
|
||||||
if newNode.Labels[v1.LabelZoneFailureDomain] ==
|
if newNode.Labels[v1.LabelZoneFailureDomain] ==
|
||||||
prevNode.Labels[v1.LabelZoneFailureDomain] {
|
prevNode.Labels[v1.LabelZoneFailureDomain] &&
|
||||||
|
newNode.Labels[v1.LabelZoneFailureDomainStable] ==
|
||||||
|
prevNode.Labels[v1.LabelZoneFailureDomainStable] {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
g.updateNodeZones(prevNode, newNode)
|
g.updateNodeZones(prevNode, newNode)
|
||||||
@ -726,6 +728,10 @@ func (g *Cloud) updateNodeZones(prevNode, newNode *v1.Node) {
|
|||||||
defer g.nodeZonesLock.Unlock()
|
defer g.nodeZonesLock.Unlock()
|
||||||
if prevNode != nil {
|
if prevNode != nil {
|
||||||
prevZone, ok := prevNode.ObjectMeta.Labels[v1.LabelZoneFailureDomain]
|
prevZone, ok := prevNode.ObjectMeta.Labels[v1.LabelZoneFailureDomain]
|
||||||
|
if !ok {
|
||||||
|
prevZone, ok = prevNode.ObjectMeta.Labels[v1.LabelZoneFailureDomainStable]
|
||||||
|
}
|
||||||
|
|
||||||
if ok {
|
if ok {
|
||||||
g.nodeZones[prevZone].Delete(prevNode.ObjectMeta.Name)
|
g.nodeZones[prevZone].Delete(prevNode.ObjectMeta.Name)
|
||||||
if g.nodeZones[prevZone].Len() == 0 {
|
if g.nodeZones[prevZone].Len() == 0 {
|
||||||
@ -735,6 +741,10 @@ func (g *Cloud) updateNodeZones(prevNode, newNode *v1.Node) {
|
|||||||
}
|
}
|
||||||
if newNode != nil {
|
if newNode != nil {
|
||||||
newZone, ok := newNode.ObjectMeta.Labels[v1.LabelZoneFailureDomain]
|
newZone, ok := newNode.ObjectMeta.Labels[v1.LabelZoneFailureDomain]
|
||||||
|
if !ok {
|
||||||
|
newZone, ok = newNode.ObjectMeta.Labels[v1.LabelZoneFailureDomainStable]
|
||||||
|
}
|
||||||
|
|
||||||
if ok {
|
if ok {
|
||||||
if g.nodeZones[newZone] == nil {
|
if g.nodeZones[newZone] == nil {
|
||||||
g.nodeZones[newZone] = sets.NewString()
|
g.nodeZones[newZone] = sets.NewString()
|
||||||
|
@ -499,7 +499,10 @@ func (g *Cloud) GetLabelsForVolume(ctx context.Context, pv *v1.PersistentVolume)
|
|||||||
}
|
}
|
||||||
|
|
||||||
// If the zone is already labeled, honor the hint
|
// If the zone is already labeled, honor the hint
|
||||||
zone := pv.Labels[v1.LabelZoneFailureDomain]
|
zone, ok := pv.Labels[v1.LabelZoneFailureDomain]
|
||||||
|
if !ok {
|
||||||
|
zone = pv.Labels[v1.LabelZoneFailureDomainStable]
|
||||||
|
}
|
||||||
|
|
||||||
labels, err := g.GetAutoLabelsForPD(pv.Spec.GCEPersistentDisk.PDName, zone)
|
labels, err := g.GetAutoLabelsForPD(pv.Spec.GCEPersistentDisk.PDName, zone)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
@ -856,6 +859,8 @@ func (g *Cloud) GetAutoLabelsForPD(name string, zone string) (map[string]string,
|
|||||||
}
|
}
|
||||||
labels[v1.LabelZoneFailureDomain] = zoneInfo.zone
|
labels[v1.LabelZoneFailureDomain] = zoneInfo.zone
|
||||||
labels[v1.LabelZoneRegion] = disk.Region
|
labels[v1.LabelZoneRegion] = disk.Region
|
||||||
|
labels[v1.LabelZoneFailureDomainStable] = zoneInfo.zone
|
||||||
|
labels[v1.LabelZoneRegionStable] = disk.Region
|
||||||
case multiZone:
|
case multiZone:
|
||||||
if zoneInfo.replicaZones == nil || zoneInfo.replicaZones.Len() <= 0 {
|
if zoneInfo.replicaZones == nil || zoneInfo.replicaZones.Len() <= 0 {
|
||||||
// Unexpected, but sanity-check
|
// Unexpected, but sanity-check
|
||||||
@ -864,6 +869,9 @@ func (g *Cloud) GetAutoLabelsForPD(name string, zone string) (map[string]string,
|
|||||||
labels[v1.LabelZoneFailureDomain] =
|
labels[v1.LabelZoneFailureDomain] =
|
||||||
volumehelpers.ZonesSetToLabelValue(zoneInfo.replicaZones)
|
volumehelpers.ZonesSetToLabelValue(zoneInfo.replicaZones)
|
||||||
labels[v1.LabelZoneRegion] = disk.Region
|
labels[v1.LabelZoneRegion] = disk.Region
|
||||||
|
labels[v1.LabelZoneFailureDomainStable] =
|
||||||
|
volumehelpers.ZonesSetToLabelValue(zoneInfo.replicaZones)
|
||||||
|
labels[v1.LabelZoneRegionStable] = disk.Region
|
||||||
case nil:
|
case nil:
|
||||||
// Unexpected, but sanity-check
|
// Unexpected, but sanity-check
|
||||||
return nil, fmt.Errorf("PD did not have ZoneInfo: %v", disk)
|
return nil, fmt.Errorf("PD did not have ZoneInfo: %v", disk)
|
||||||
|
@ -465,13 +465,23 @@ func TestGetAutoLabelsForPD_Basic(t *testing.T) {
|
|||||||
if err != nil {
|
if err != nil {
|
||||||
t.Error(err)
|
t.Error(err)
|
||||||
}
|
}
|
||||||
|
|
||||||
if labels[v1.LabelZoneFailureDomain] != zone {
|
if labels[v1.LabelZoneFailureDomain] != zone {
|
||||||
t.Errorf("Failure domain is '%v', but zone is '%v'",
|
t.Errorf("Failure domain is '%v', but zone is '%v'",
|
||||||
labels[v1.LabelZoneFailureDomain], zone)
|
labels[v1.LabelZoneFailureDomain], zone)
|
||||||
}
|
}
|
||||||
|
|
||||||
if labels[v1.LabelZoneRegion] != gceRegion {
|
if labels[v1.LabelZoneRegion] != gceRegion {
|
||||||
t.Errorf("Region is '%v', but region is 'us-central1'", labels[v1.LabelZoneRegion])
|
t.Errorf("Region is '%v', but region is 'us-central1'", labels[v1.LabelZoneRegion])
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if labels[v1.LabelZoneFailureDomainStable] != zone {
|
||||||
|
t.Errorf("Failure domain is '%v', but zone is '%v'",
|
||||||
|
labels[v1.LabelZoneFailureDomainStable], zone)
|
||||||
|
}
|
||||||
|
if labels[v1.LabelZoneRegionStable] != gceRegion {
|
||||||
|
t.Errorf("Region is '%v', but region is 'us-central1'", labels[v1.LabelZoneRegionStable])
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestGetAutoLabelsForPD_NoZone(t *testing.T) {
|
func TestGetAutoLabelsForPD_NoZone(t *testing.T) {
|
||||||
@ -508,6 +518,14 @@ func TestGetAutoLabelsForPD_NoZone(t *testing.T) {
|
|||||||
if labels[v1.LabelZoneRegion] != gceRegion {
|
if labels[v1.LabelZoneRegion] != gceRegion {
|
||||||
t.Errorf("Region is '%v', but region is 'europe-west1'", labels[v1.LabelZoneRegion])
|
t.Errorf("Region is '%v', but region is 'europe-west1'", labels[v1.LabelZoneRegion])
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if labels[v1.LabelZoneFailureDomainStable] != zone {
|
||||||
|
t.Errorf("Failure domain is '%v', but zone is '%v'",
|
||||||
|
labels[v1.LabelZoneFailureDomainStable], zone)
|
||||||
|
}
|
||||||
|
if labels[v1.LabelZoneRegionStable] != gceRegion {
|
||||||
|
t.Errorf("Region is '%v', but region is 'europe-west1'", labels[v1.LabelZoneRegionStable])
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestGetAutoLabelsForPD_DiskNotFound(t *testing.T) {
|
func TestGetAutoLabelsForPD_DiskNotFound(t *testing.T) {
|
||||||
@ -594,6 +612,14 @@ func TestGetAutoLabelsForPD_DupDisk(t *testing.T) {
|
|||||||
if labels[v1.LabelZoneRegion] != gceRegion {
|
if labels[v1.LabelZoneRegion] != gceRegion {
|
||||||
t.Errorf("Region is '%v', but region is 'us-west1'", labels[v1.LabelZoneRegion])
|
t.Errorf("Region is '%v', but region is 'us-west1'", labels[v1.LabelZoneRegion])
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if labels[v1.LabelZoneFailureDomainStable] != zone {
|
||||||
|
t.Errorf("Failure domain is '%v', but zone is '%v'",
|
||||||
|
labels[v1.LabelZoneFailureDomainStable], zone)
|
||||||
|
}
|
||||||
|
if labels[v1.LabelZoneRegionStable] != gceRegion {
|
||||||
|
t.Errorf("Region is '%v', but region is 'us-west1'", labels[v1.LabelZoneRegionStable])
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestGetAutoLabelsForPD_DupDiskNoZone(t *testing.T) {
|
func TestGetAutoLabelsForPD_DupDiskNoZone(t *testing.T) {
|
||||||
|
@ -60,12 +60,20 @@ func splitNodesByZone(nodes []*v1.Node) map[string][]*v1.Node {
|
|||||||
return zones
|
return zones
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// getZone checks the deprecated beta zone label first and falls back to GA label if the beta label does not exist
|
||||||
|
// if both don't exist, returns the default zone which is ""
|
||||||
func getZone(n *v1.Node) string {
|
func getZone(n *v1.Node) string {
|
||||||
zone, ok := n.Labels[v1.LabelZoneFailureDomain]
|
zone, ok := n.Labels[v1.LabelZoneFailureDomain]
|
||||||
if !ok {
|
if ok {
|
||||||
return defaultZone
|
|
||||||
}
|
|
||||||
return zone
|
return zone
|
||||||
|
}
|
||||||
|
|
||||||
|
zone, ok = n.Labels[v1.LabelZoneFailureDomainStable]
|
||||||
|
if ok {
|
||||||
|
return zone
|
||||||
|
}
|
||||||
|
|
||||||
|
return defaultZone
|
||||||
}
|
}
|
||||||
|
|
||||||
func makeHostURL(projectsAPIEndpoint, projectID, zone, host string) string {
|
func makeHostURL(projectsAPIEndpoint, projectID, zone, host string) string {
|
||||||
|
@ -736,6 +736,8 @@ func (os *OpenStack) GetLabelsForVolume(ctx context.Context, pv *v1.PersistentVo
|
|||||||
labels := make(map[string]string)
|
labels := make(map[string]string)
|
||||||
labels[v1.LabelZoneFailureDomain] = volume.AvailabilityZone
|
labels[v1.LabelZoneFailureDomain] = volume.AvailabilityZone
|
||||||
labels[v1.LabelZoneRegion] = os.region
|
labels[v1.LabelZoneRegion] = os.region
|
||||||
|
labels[v1.LabelZoneFailureDomainStable] = volume.AvailabilityZone
|
||||||
|
labels[v1.LabelZoneRegionStable] = os.region
|
||||||
klog.V(4).Infof("The Volume %s has labels %v", pv.Spec.Cinder.VolumeID, labels)
|
klog.V(4).Infof("The Volume %s has labels %v", pv.Spec.Cinder.VolumeID, labels)
|
||||||
|
|
||||||
return labels, nil
|
return labels, nil
|
||||||
|
@ -203,8 +203,8 @@ func (nm *NodeManager) DiscoverNode(node *v1.Node) error {
|
|||||||
node.Name, vm, res.vc, res.datacenter.Name())
|
node.Name, vm, res.vc, res.datacenter.Name())
|
||||||
|
|
||||||
// Get the node zone information
|
// Get the node zone information
|
||||||
nodeFd := node.ObjectMeta.Labels[v1.LabelZoneFailureDomain]
|
nodeFd := getNodeZoneFailureDomain(node)
|
||||||
nodeRegion := node.ObjectMeta.Labels[v1.LabelZoneRegion]
|
nodeRegion := getNodeRegion(node)
|
||||||
nodeZone := &cloudprovider.Zone{FailureDomain: nodeFd, Region: nodeRegion}
|
nodeZone := &cloudprovider.Zone{FailureDomain: nodeFd, Region: nodeRegion}
|
||||||
nodeInfo := &NodeInfo{dataCenter: res.datacenter, vm: vm, vcServer: res.vc, vmUUID: nodeUUID, zone: nodeZone}
|
nodeInfo := &NodeInfo{dataCenter: res.datacenter, vm: vm, vcServer: res.vc, vmUUID: nodeUUID, zone: nodeZone}
|
||||||
nm.addNodeInfo(node.ObjectMeta.Name, nodeInfo)
|
nm.addNodeInfo(node.ObjectMeta.Name, nodeInfo)
|
||||||
@ -230,6 +230,34 @@ func (nm *NodeManager) DiscoverNode(node *v1.Node) error {
|
|||||||
return vclib.ErrNoVMFound
|
return vclib.ErrNoVMFound
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func getNodeZoneFailureDomain(node *v1.Node) string {
|
||||||
|
zone, ok := node.Labels[v1.LabelZoneFailureDomain]
|
||||||
|
if ok {
|
||||||
|
return zone
|
||||||
|
}
|
||||||
|
|
||||||
|
zone, ok = node.Labels[v1.LabelZoneFailureDomainStable]
|
||||||
|
if ok {
|
||||||
|
return zone
|
||||||
|
}
|
||||||
|
|
||||||
|
return ""
|
||||||
|
}
|
||||||
|
|
||||||
|
func getNodeRegion(node *v1.Node) string {
|
||||||
|
region, ok := node.Labels[v1.LabelZoneRegion]
|
||||||
|
if ok {
|
||||||
|
return region
|
||||||
|
}
|
||||||
|
|
||||||
|
region, ok = node.Labels[v1.LabelZoneRegionStable]
|
||||||
|
if ok {
|
||||||
|
return region
|
||||||
|
}
|
||||||
|
|
||||||
|
return ""
|
||||||
|
}
|
||||||
|
|
||||||
func (nm *NodeManager) RegisterNode(node *v1.Node) error {
|
func (nm *NodeManager) RegisterNode(node *v1.Node) error {
|
||||||
nm.addNode(node)
|
nm.addNode(node)
|
||||||
return nm.DiscoverNode(node)
|
return nm.DiscoverNode(node)
|
||||||
|
@ -1649,6 +1649,8 @@ func (vs *VSphere) GetVolumeLabels(volumePath string) (map[string]string, error)
|
|||||||
if len(dsZones) > 0 {
|
if len(dsZones) > 0 {
|
||||||
labels[v1.LabelZoneRegion] = dsZones[0].Region
|
labels[v1.LabelZoneRegion] = dsZones[0].Region
|
||||||
labels[v1.LabelZoneFailureDomain] = dsZones[0].FailureDomain
|
labels[v1.LabelZoneFailureDomain] = dsZones[0].FailureDomain
|
||||||
|
labels[v1.LabelZoneRegionStable] = dsZones[0].Region
|
||||||
|
labels[v1.LabelZoneFailureDomainStable] = dsZones[0].FailureDomain
|
||||||
}
|
}
|
||||||
return labels, nil
|
return labels, nil
|
||||||
}
|
}
|
||||||
|
@ -497,7 +497,7 @@ func verifyPVZoneLabels(client clientset.Interface, namespace string, scParamete
|
|||||||
ginkgo.By("Verify zone information is present in the volume labels")
|
ginkgo.By("Verify zone information is present in the volume labels")
|
||||||
for _, pv := range persistentvolumes {
|
for _, pv := range persistentvolumes {
|
||||||
// Multiple zones are separated with "__"
|
// Multiple zones are separated with "__"
|
||||||
pvZoneLabels := strings.Split(pv.ObjectMeta.Labels["failure-domain.beta.kubernetes.io/zone"], "__")
|
pvZoneLabels := strings.Split(pv.ObjectMeta.Labels[v1.LabelZoneFailureDomain], "__")
|
||||||
for _, zone := range zones {
|
for _, zone := range zones {
|
||||||
gomega.Expect(pvZoneLabels).Should(gomega.ContainElement(zone), "Incorrect or missing zone labels in pv.")
|
gomega.Expect(pvZoneLabels).Should(gomega.ContainElement(zone), "Incorrect or missing zone labels in pv.")
|
||||||
}
|
}
|
||||||
|
Loading…
Reference in New Issue
Block a user