test/e2e: check both beta and zone label for getting cluster zone

Signed-off-by: Andrew Sy Kim <kiman@vmware.com>
This commit is contained in:
Andrew Sy Kim
2019-08-28 13:14:02 -04:00
parent 07229d6c51
commit 349749644f
32 changed files with 201 additions and 1352 deletions

View File

@@ -22,7 +22,6 @@ import (
"errors"
"fmt"
"io"
"reflect"
"sync"
v1 "k8s.io/api/core/v1"
@@ -118,13 +117,11 @@ func (l *persistentVolumeLabel) Admit(ctx context.Context, a admission.Attribute
return admission.NewForbidden(a, err)
}
requirements := make([]api.NodeSelectorRequirement, 0)
if len(volumeLabels) != 0 {
if volume.Labels == nil {
volume.Labels = make(map[string]string)
}
deprecatedTopologyReqs := make([]api.NodeSelectorRequirement, 0)
topologyReqs := make([]api.NodeSelectorRequirement, 0)
for k, v := range volumeLabels {
// We (silently) replace labels if they are provided.
// This should be OK because they are in the kubernetes.io namespace
@@ -132,11 +129,8 @@ func (l *persistentVolumeLabel) Admit(ctx context.Context, a admission.Attribute
volume.Labels[k] = v
// 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
if k == v1.LabelZoneFailureDomain || k == v1.LabelZoneFailureDomainStable {
if k == v1.LabelZoneFailureDomain {
zones, err := volumehelpers.LabelZonesToSet(v)
if err != nil {
return admission.NewForbidden(a, fmt.Errorf("failed to convert label string for Zone: %s to a Set", v))
@@ -146,17 +140,7 @@ func (l *persistentVolumeLabel) Admit(ctx context.Context, a admission.Attribute
} else {
values = []string{v}
}
// 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})
}
requirements = append(requirements, api.NodeSelectorRequirement{Key: k, Operator: api.NodeSelectorOpIn, Values: values})
}
if volume.Spec.NodeAffinity == nil {
@@ -169,44 +153,16 @@ func (l *persistentVolumeLabel) Admit(ctx context.Context, a admission.Attribute
// Need at least one term pre-allocated whose MatchExpressions can be appended to
volume.Spec.NodeAffinity.Required.NodeSelectorTerms = make([]api.NodeSelectorTerm, 1)
}
deprecatedNodeTerms := volume.DeepCopy().Spec.NodeAffinity.Required.NodeSelectorTerms
stableNodeTerms := volume.DeepCopy().Spec.NodeAffinity.Required.NodeSelectorTerms
// only attempt to rewrite beta/stable topology labels if there are no conflicting labels on the PV at all
if nodeSelectorRequirementKeysExistInNodeSelectorTerms(deprecatedTopologyReqs, volume.Spec.NodeAffinity.Required.NodeSelectorTerms) ||
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)
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.",
requirements, volume.Spec.NodeAffinity)
} else {
for _, req := range requirements {
for i := range volume.Spec.NodeAffinity.Required.NodeSelectorTerms {
volume.Spec.NodeAffinity.Required.NodeSelectorTerms[i].MatchExpressions = append(volume.Spec.NodeAffinity.Required.NodeSelectorTerms[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
@@ -215,17 +171,15 @@ func (l *persistentVolumeLabel) Admit(ctx context.Context, a admission.Attribute
func (l *persistentVolumeLabel) findVolumeLabels(volume *api.PersistentVolume) (map[string]string, error) {
existingLabels := volume.Labels
// deprecated zone/region labels are still the source of truth
// All cloud providers set only these two labels.
domain, domainOK := existingLabels[v1.LabelZoneFailureDomain]
region, regionOK := existingLabels[v1.LabelZoneRegion]
isDynamicallyProvisioned := metav1.HasAnnotation(volume.ObjectMeta, persistentvolume.AnnDynamicallyProvisioned)
if isDynamicallyProvisioned && domainOK && regionOK {
// PV already has all the labels and we can trust the dynamic provisioning that it provided correct values.
return map[string]string{
v1.LabelZoneFailureDomain: domain,
v1.LabelZoneRegion: region,
v1.LabelZoneFailureDomainStable: domain,
v1.LabelZoneRegionStable: region,
v1.LabelZoneFailureDomain: domain,
v1.LabelZoneRegion: region,
}, nil
}

View File

@@ -66,10 +66,9 @@ func Test_PVLAdmission(t *testing.T) {
name: "non-cloud PV ignored",
handler: newPersistentVolumeLabel(),
pvlabeler: mockVolumeLabels(map[string]string{
"a": "1",
"b": "2",
v1.LabelZoneFailureDomain: "1__2__3",
v1.LabelZoneFailureDomainStable: "1__2__3",
"a": "1",
"b": "2",
v1.LabelZoneFailureDomain: "1__2__3",
}),
preAdmissionPV: &api.PersistentVolume{
ObjectMeta: metav1.ObjectMeta{Name: "noncloud", Namespace: "myns"},
@@ -175,10 +174,9 @@ func Test_PVLAdmission(t *testing.T) {
name: "AWS EBS PV labeled correctly",
handler: newPersistentVolumeLabel(),
pvlabeler: mockVolumeLabels(map[string]string{
"a": "1",
"b": "2",
v1.LabelZoneFailureDomain: "1__2__3",
v1.LabelZoneFailureDomainStable: "1__2__3",
"a": "1",
"b": "2",
v1.LabelZoneFailureDomain: "1__2__3",
}),
preAdmissionPV: &api.PersistentVolume{
ObjectMeta: metav1.ObjectMeta{Name: "awsebs", Namespace: "myns"},
@@ -195,10 +193,9 @@ func Test_PVLAdmission(t *testing.T) {
Name: "awsebs",
Namespace: "myns",
Labels: map[string]string{
"a": "1",
"b": "2",
v1.LabelZoneFailureDomain: "1__2__3",
v1.LabelZoneFailureDomainStable: "1__2__3",
"a": "1",
"b": "2",
v1.LabelZoneFailureDomain: "1__2__3",
},
},
Spec: api.PersistentVolumeSpec{
@@ -229,25 +226,6 @@ 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"},
},
},
},
},
},
},
@@ -259,19 +237,15 @@ func Test_PVLAdmission(t *testing.T) {
name: "existing labels from dynamic provisioning are not changed",
handler: newPersistentVolumeLabel(),
pvlabeler: mockVolumeLabels(map[string]string{
v1.LabelZoneFailureDomain: "domain1",
v1.LabelZoneRegion: "region1",
v1.LabelZoneFailureDomainStable: "domain1",
v1.LabelZoneRegionStable: "region1",
v1.LabelZoneFailureDomain: "domain1",
v1.LabelZoneRegion: "region1",
}),
preAdmissionPV: &api.PersistentVolume{
ObjectMeta: metav1.ObjectMeta{
Name: "awsebs", Namespace: "myns",
Labels: map[string]string{
v1.LabelZoneFailureDomain: "existingDomain",
v1.LabelZoneRegion: "existingRegion",
v1.LabelZoneFailureDomainStable: "existingDomain",
v1.LabelZoneRegionStable: "existingRegion",
v1.LabelZoneFailureDomain: "existingDomain",
v1.LabelZoneRegion: "existingRegion",
},
Annotations: map[string]string{
persistentvolume.AnnDynamicallyProvisioned: "kubernetes.io/aws-ebs",
@@ -290,10 +264,8 @@ func Test_PVLAdmission(t *testing.T) {
Name: "awsebs",
Namespace: "myns",
Labels: map[string]string{
v1.LabelZoneFailureDomain: "existingDomain",
v1.LabelZoneRegion: "existingRegion",
v1.LabelZoneFailureDomainStable: "existingDomain",
v1.LabelZoneRegionStable: "existingRegion",
v1.LabelZoneFailureDomain: "existingDomain",
v1.LabelZoneRegion: "existingRegion",
},
Annotations: map[string]string{
persistentvolume.AnnDynamicallyProvisioned: "kubernetes.io/aws-ebs",
@@ -322,20 +294,6 @@ 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"},
},
},
},
},
},
},
@@ -347,10 +305,8 @@ func Test_PVLAdmission(t *testing.T) {
name: "existing labels from user are changed",
handler: newPersistentVolumeLabel(),
pvlabeler: mockVolumeLabels(map[string]string{
v1.LabelZoneFailureDomain: "domain1",
v1.LabelZoneRegion: "region1",
v1.LabelZoneFailureDomainStable: "domain1",
v1.LabelZoneRegionStable: "region1",
v1.LabelZoneFailureDomain: "domain1",
v1.LabelZoneRegion: "region1",
}),
preAdmissionPV: &api.PersistentVolume{
ObjectMeta: metav1.ObjectMeta{
@@ -373,10 +329,8 @@ func Test_PVLAdmission(t *testing.T) {
Name: "awsebs",
Namespace: "myns",
Labels: map[string]string{
v1.LabelZoneFailureDomain: "domain1",
v1.LabelZoneRegion: "region1",
v1.LabelZoneFailureDomainStable: "domain1",
v1.LabelZoneRegionStable: "region1",
v1.LabelZoneFailureDomain: "domain1",
v1.LabelZoneRegion: "region1",
},
},
Spec: api.PersistentVolumeSpec{
@@ -402,20 +356,6 @@ 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"},
},
},
},
},
},
},
@@ -427,10 +367,9 @@ func Test_PVLAdmission(t *testing.T) {
name: "GCE PD PV labeled correctly",
handler: newPersistentVolumeLabel(),
pvlabeler: mockVolumeLabels(map[string]string{
"a": "1",
"b": "2",
v1.LabelZoneFailureDomain: "1__2__3",
v1.LabelZoneFailureDomainStable: "1__2__3",
"a": "1",
"b": "2",
v1.LabelZoneFailureDomain: "1__2__3",
}),
preAdmissionPV: &api.PersistentVolume{
ObjectMeta: metav1.ObjectMeta{Name: "gcepd", Namespace: "myns"},
@@ -447,10 +386,9 @@ func Test_PVLAdmission(t *testing.T) {
Name: "gcepd",
Namespace: "myns",
Labels: map[string]string{
"a": "1",
"b": "2",
v1.LabelZoneFailureDomain: "1__2__3",
v1.LabelZoneFailureDomainStable: "1__2__3",
"a": "1",
"b": "2",
v1.LabelZoneFailureDomain: "1__2__3",
},
},
Spec: api.PersistentVolumeSpec{
@@ -481,25 +419,6 @@ 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"},
},
},
},
},
},
},
@@ -511,10 +430,9 @@ func Test_PVLAdmission(t *testing.T) {
name: "Azure Disk PV labeled correctly",
handler: newPersistentVolumeLabel(),
pvlabeler: mockVolumeLabels(map[string]string{
"a": "1",
"b": "2",
v1.LabelZoneFailureDomain: "1__2__3",
v1.LabelZoneFailureDomainStable: "1__2__3",
"a": "1",
"b": "2",
v1.LabelZoneFailureDomain: "1__2__3",
}),
preAdmissionPV: &api.PersistentVolume{
ObjectMeta: metav1.ObjectMeta{
@@ -534,10 +452,9 @@ func Test_PVLAdmission(t *testing.T) {
Name: "azurepd",
Namespace: "myns",
Labels: map[string]string{
"a": "1",
"b": "2",
v1.LabelZoneFailureDomain: "1__2__3",
v1.LabelZoneFailureDomainStable: "1__2__3",
"a": "1",
"b": "2",
v1.LabelZoneFailureDomain: "1__2__3",
},
},
Spec: api.PersistentVolumeSpec{
@@ -568,25 +485,6 @@ 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"},
},
},
},
},
},
},
@@ -598,10 +496,9 @@ func Test_PVLAdmission(t *testing.T) {
name: "Cinder Disk PV labeled correctly",
handler: newPersistentVolumeLabel(),
pvlabeler: mockVolumeLabels(map[string]string{
"a": "1",
"b": "2",
v1.LabelZoneFailureDomain: "1__2__3",
v1.LabelZoneFailureDomainStable: "1__2__3",
"a": "1",
"b": "2",
v1.LabelZoneFailureDomain: "1__2__3",
}),
preAdmissionPV: &api.PersistentVolume{
ObjectMeta: metav1.ObjectMeta{
@@ -621,10 +518,9 @@ func Test_PVLAdmission(t *testing.T) {
Name: "azurepd",
Namespace: "myns",
Labels: map[string]string{
"a": "1",
"b": "2",
v1.LabelZoneFailureDomain: "1__2__3",
v1.LabelZoneFailureDomainStable: "1__2__3",
"a": "1",
"b": "2",
v1.LabelZoneFailureDomain: "1__2__3",
},
},
Spec: api.PersistentVolumeSpec{
@@ -655,25 +551,6 @@ 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"},
},
},
},
},
},
},
@@ -685,10 +562,9 @@ func Test_PVLAdmission(t *testing.T) {
name: "AWS EBS PV overrides user applied labels",
handler: newPersistentVolumeLabel(),
pvlabeler: mockVolumeLabels(map[string]string{
"a": "1",
"b": "2",
v1.LabelZoneFailureDomain: "1__2__3",
v1.LabelZoneFailureDomainStable: "1__2__3",
"a": "1",
"b": "2",
v1.LabelZoneFailureDomain: "1__2__3",
}),
preAdmissionPV: &api.PersistentVolume{
ObjectMeta: metav1.ObjectMeta{
@@ -711,10 +587,9 @@ func Test_PVLAdmission(t *testing.T) {
Name: "awsebs",
Namespace: "myns",
Labels: map[string]string{
"a": "1",
"b": "2",
v1.LabelZoneFailureDomain: "1__2__3",
v1.LabelZoneFailureDomainStable: "1__2__3",
"a": "1",
"b": "2",
v1.LabelZoneFailureDomain: "1__2__3",
},
},
Spec: api.PersistentVolumeSpec{
@@ -745,25 +620,6 @@ 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"},
},
},
},
},
},
},
@@ -963,10 +819,9 @@ func Test_PVLAdmission(t *testing.T) {
name: "vSphere PV labeled correctly",
handler: newPersistentVolumeLabel(),
pvlabeler: mockVolumeLabels(map[string]string{
"a": "1",
"b": "2",
v1.LabelZoneFailureDomain: "1__2__3",
v1.LabelZoneFailureDomainStable: "1__2__3",
"a": "1",
"b": "2",
v1.LabelZoneFailureDomain: "1__2__3",
}),
preAdmissionPV: &api.PersistentVolume{
ObjectMeta: metav1.ObjectMeta{
@@ -986,10 +841,9 @@ func Test_PVLAdmission(t *testing.T) {
Name: "vSpherePV",
Namespace: "myns",
Labels: map[string]string{
"a": "1",
"b": "2",
v1.LabelZoneFailureDomain: "1__2__3",
v1.LabelZoneFailureDomainStable: "1__2__3",
"a": "1",
"b": "2",
v1.LabelZoneFailureDomain: "1__2__3",
},
},
Spec: api.PersistentVolumeSpec{
@@ -1020,25 +874,6 @@ 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"},
},
},
},
},
},
},
@@ -1065,8 +900,6 @@ func Test_PVLAdmission(t *testing.T) {
if !reflect.DeepEqual(testcase.preAdmissionPV, testcase.postAdmissionPV) {
t.Logf("expected PV: %+v", testcase.postAdmissionPV)
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")
}
@@ -1094,12 +927,10 @@ func sortMatchExpressions(pv *api.PersistentVolume) {
return
}
for t := range pv.Spec.NodeAffinity.Required.NodeSelectorTerms {
match := pv.Spec.NodeAffinity.Required.NodeSelectorTerms[t].MatchExpressions
sort.Slice(match, func(i, j int) bool {
return match[i].Key < match[j].Key
})
match := pv.Spec.NodeAffinity.Required.NodeSelectorTerms[0].MatchExpressions
sort.Slice(match, func(i, j int) bool {
return match[i].Key < match[j].Key
})
pv.Spec.NodeAffinity.Required.NodeSelectorTerms[t].MatchExpressions = match
}
pv.Spec.NodeAffinity.Required.NodeSelectorTerms[0].MatchExpressions = match
}