Add CrossNamespacePodAffinity quota scope and PodAffinityTerm.NamespaceSelector APIs, and CrossNamespacePodAffinity quota scope implementation.

This commit is contained in:
Abdullah Gharaibeh
2021-01-26 14:28:35 -05:00
parent 4f9317596c
commit 3c5f018f8e
85 changed files with 11392 additions and 3610 deletions

View File

@@ -523,6 +523,7 @@ func dropDisabledFields(
podSpec.SetHostnameAsFQDN = nil
}
dropDisabledPodAffinityTermFields(podSpec, oldPodSpec)
}
// dropDisabledProcMountField removes disabled fields from PodSpec related
@@ -572,6 +573,66 @@ func dropDisabledEphemeralVolumeSourceAlphaFields(podSpec, oldPodSpec *api.PodSp
}
}
func dropPodAffinityTermNamespaceSelector(terms []api.PodAffinityTerm) {
for i := range terms {
terms[i].NamespaceSelector = nil
}
}
func dropWeightedPodAffinityTermNamespaceSelector(terms []api.WeightedPodAffinityTerm) {
for i := range terms {
terms[i].PodAffinityTerm.NamespaceSelector = nil
}
}
// dropDisabledPodAffinityTermFields removes disabled fields from PodSpec related
// to PodAffinityTerm only if it is not already used by the old spec
func dropDisabledPodAffinityTermFields(podSpec, oldPodSpec *api.PodSpec) {
if !utilfeature.DefaultFeatureGate.Enabled(features.PodAffinityNamespaceSelector) &&
podSpec != nil && podSpec.Affinity != nil &&
!podAffinityNamespaceSelectorInUse(oldPodSpec) {
if podSpec.Affinity.PodAffinity != nil {
dropPodAffinityTermNamespaceSelector(podSpec.Affinity.PodAffinity.RequiredDuringSchedulingIgnoredDuringExecution)
dropWeightedPodAffinityTermNamespaceSelector(podSpec.Affinity.PodAffinity.PreferredDuringSchedulingIgnoredDuringExecution)
}
if podSpec.Affinity.PodAntiAffinity != nil {
dropPodAffinityTermNamespaceSelector(podSpec.Affinity.PodAntiAffinity.RequiredDuringSchedulingIgnoredDuringExecution)
dropWeightedPodAffinityTermNamespaceSelector(podSpec.Affinity.PodAntiAffinity.PreferredDuringSchedulingIgnoredDuringExecution)
}
}
}
func podAffinityNamespaceSelectorInUse(podSpec *api.PodSpec) bool {
if podSpec == nil || podSpec.Affinity == nil {
return false
}
if podSpec.Affinity.PodAffinity != nil {
for _, t := range podSpec.Affinity.PodAffinity.RequiredDuringSchedulingIgnoredDuringExecution {
if t.NamespaceSelector != nil {
return true
}
}
for _, t := range podSpec.Affinity.PodAffinity.PreferredDuringSchedulingIgnoredDuringExecution {
if t.PodAffinityTerm.NamespaceSelector != nil {
return true
}
}
}
if podSpec.Affinity.PodAntiAffinity != nil {
for _, t := range podSpec.Affinity.PodAntiAffinity.RequiredDuringSchedulingIgnoredDuringExecution {
if t.NamespaceSelector != nil {
return true
}
}
for _, t := range podSpec.Affinity.PodAntiAffinity.PreferredDuringSchedulingIgnoredDuringExecution {
if t.PodAffinityTerm.NamespaceSelector == nil {
return true
}
}
}
return false
}
func ephemeralContainersInUse(podSpec *api.PodSpec) bool {
if podSpec == nil {
return false

View File

@@ -1375,7 +1375,163 @@ func TestValidatePodDeletionCostOption(t *testing.T) {
if tc.wantAllowInvalidPodDeletionCost != gotOptions.AllowInvalidPodDeletionCost {
t.Errorf("unexpected diff, want: %v, got: %v", tc.wantAllowInvalidPodDeletionCost, gotOptions.AllowInvalidPodDeletionCost)
}
})
}
}
func TestDropDisabledPodAffinityTermFields(t *testing.T) {
testCases := []struct {
name string
enabled bool
podSpec *api.PodSpec
oldPodSpec *api.PodSpec
wantPodSpec *api.PodSpec
}{
{
name: "nil affinity",
podSpec: &api.PodSpec{},
wantPodSpec: &api.PodSpec{},
},
{
name: "empty affinity",
podSpec: &api.PodSpec{Affinity: &api.Affinity{}},
wantPodSpec: &api.PodSpec{Affinity: &api.Affinity{}},
},
{
name: "NamespaceSelector cleared",
podSpec: &api.PodSpec{Affinity: &api.Affinity{
PodAffinity: &api.PodAffinity{
RequiredDuringSchedulingIgnoredDuringExecution: []api.PodAffinityTerm{
{LabelSelector: &metav1.LabelSelector{}, Namespaces: []string{"ns1"}, TopologyKey: "region1", NamespaceSelector: &metav1.LabelSelector{}},
},
PreferredDuringSchedulingIgnoredDuringExecution: []api.WeightedPodAffinityTerm{
{PodAffinityTerm: api.PodAffinityTerm{LabelSelector: &metav1.LabelSelector{}, Namespaces: []string{"ns2"}, TopologyKey: "region2", NamespaceSelector: &metav1.LabelSelector{}}},
},
},
PodAntiAffinity: &api.PodAntiAffinity{
RequiredDuringSchedulingIgnoredDuringExecution: []api.PodAffinityTerm{
{LabelSelector: &metav1.LabelSelector{}, Namespaces: []string{"ns3"}, TopologyKey: "region3", NamespaceSelector: &metav1.LabelSelector{}},
},
PreferredDuringSchedulingIgnoredDuringExecution: []api.WeightedPodAffinityTerm{
{PodAffinityTerm: api.PodAffinityTerm{LabelSelector: &metav1.LabelSelector{}, Namespaces: []string{"ns4"}, TopologyKey: "region4", NamespaceSelector: &metav1.LabelSelector{}}},
},
},
}},
oldPodSpec: &api.PodSpec{Affinity: &api.Affinity{}},
wantPodSpec: &api.PodSpec{Affinity: &api.Affinity{
PodAffinity: &api.PodAffinity{
RequiredDuringSchedulingIgnoredDuringExecution: []api.PodAffinityTerm{
{LabelSelector: &metav1.LabelSelector{}, Namespaces: []string{"ns1"}, TopologyKey: "region1"},
},
PreferredDuringSchedulingIgnoredDuringExecution: []api.WeightedPodAffinityTerm{
{PodAffinityTerm: api.PodAffinityTerm{LabelSelector: &metav1.LabelSelector{}, Namespaces: []string{"ns2"}, TopologyKey: "region2"}},
},
},
PodAntiAffinity: &api.PodAntiAffinity{
RequiredDuringSchedulingIgnoredDuringExecution: []api.PodAffinityTerm{
{LabelSelector: &metav1.LabelSelector{}, Namespaces: []string{"ns3"}, TopologyKey: "region3"},
},
PreferredDuringSchedulingIgnoredDuringExecution: []api.WeightedPodAffinityTerm{
{PodAffinityTerm: api.PodAffinityTerm{LabelSelector: &metav1.LabelSelector{}, Namespaces: []string{"ns4"}, TopologyKey: "region4"}},
},
},
}},
},
{
name: "NamespaceSelector not cleared since old spec already sets it",
podSpec: &api.PodSpec{Affinity: &api.Affinity{
PodAffinity: &api.PodAffinity{
RequiredDuringSchedulingIgnoredDuringExecution: []api.PodAffinityTerm{
{LabelSelector: &metav1.LabelSelector{}, Namespaces: []string{"ns1"}, TopologyKey: "region1", NamespaceSelector: &metav1.LabelSelector{}},
},
PreferredDuringSchedulingIgnoredDuringExecution: []api.WeightedPodAffinityTerm{
{PodAffinityTerm: api.PodAffinityTerm{LabelSelector: &metav1.LabelSelector{}, Namespaces: []string{"ns2"}, TopologyKey: "region2", NamespaceSelector: &metav1.LabelSelector{}}},
},
},
PodAntiAffinity: &api.PodAntiAffinity{
RequiredDuringSchedulingIgnoredDuringExecution: []api.PodAffinityTerm{
{LabelSelector: &metav1.LabelSelector{}, Namespaces: []string{"ns3"}, TopologyKey: "region3", NamespaceSelector: &metav1.LabelSelector{}},
},
PreferredDuringSchedulingIgnoredDuringExecution: []api.WeightedPodAffinityTerm{
{PodAffinityTerm: api.PodAffinityTerm{LabelSelector: &metav1.LabelSelector{}, Namespaces: []string{"ns4"}, TopologyKey: "region4", NamespaceSelector: &metav1.LabelSelector{}}},
},
},
}},
oldPodSpec: &api.PodSpec{Affinity: &api.Affinity{
PodAffinity: &api.PodAffinity{
RequiredDuringSchedulingIgnoredDuringExecution: []api.PodAffinityTerm{
{LabelSelector: &metav1.LabelSelector{}, Namespaces: []string{"ns1"}, TopologyKey: "region1", NamespaceSelector: &metav1.LabelSelector{}},
},
},
}},
wantPodSpec: &api.PodSpec{Affinity: &api.Affinity{
PodAffinity: &api.PodAffinity{
RequiredDuringSchedulingIgnoredDuringExecution: []api.PodAffinityTerm{
{LabelSelector: &metav1.LabelSelector{}, Namespaces: []string{"ns1"}, TopologyKey: "region1", NamespaceSelector: &metav1.LabelSelector{}},
},
PreferredDuringSchedulingIgnoredDuringExecution: []api.WeightedPodAffinityTerm{
{PodAffinityTerm: api.PodAffinityTerm{LabelSelector: &metav1.LabelSelector{}, Namespaces: []string{"ns2"}, TopologyKey: "region2", NamespaceSelector: &metav1.LabelSelector{}}},
},
},
PodAntiAffinity: &api.PodAntiAffinity{
RequiredDuringSchedulingIgnoredDuringExecution: []api.PodAffinityTerm{
{LabelSelector: &metav1.LabelSelector{}, Namespaces: []string{"ns3"}, TopologyKey: "region3", NamespaceSelector: &metav1.LabelSelector{}},
},
PreferredDuringSchedulingIgnoredDuringExecution: []api.WeightedPodAffinityTerm{
{PodAffinityTerm: api.PodAffinityTerm{LabelSelector: &metav1.LabelSelector{}, Namespaces: []string{"ns4"}, TopologyKey: "region4", NamespaceSelector: &metav1.LabelSelector{}}},
},
},
}},
},
{
name: "NamespaceSelector not cleared since feature is enabled",
enabled: true,
podSpec: &api.PodSpec{Affinity: &api.Affinity{
PodAffinity: &api.PodAffinity{
RequiredDuringSchedulingIgnoredDuringExecution: []api.PodAffinityTerm{
{LabelSelector: &metav1.LabelSelector{}, Namespaces: []string{"ns1"}, TopologyKey: "region1", NamespaceSelector: &metav1.LabelSelector{}},
},
PreferredDuringSchedulingIgnoredDuringExecution: []api.WeightedPodAffinityTerm{
{PodAffinityTerm: api.PodAffinityTerm{LabelSelector: &metav1.LabelSelector{}, Namespaces: []string{"ns2"}, TopologyKey: "region2", NamespaceSelector: &metav1.LabelSelector{}}},
},
},
PodAntiAffinity: &api.PodAntiAffinity{
RequiredDuringSchedulingIgnoredDuringExecution: []api.PodAffinityTerm{
{LabelSelector: &metav1.LabelSelector{}, Namespaces: []string{"ns3"}, TopologyKey: "region3", NamespaceSelector: &metav1.LabelSelector{}},
},
PreferredDuringSchedulingIgnoredDuringExecution: []api.WeightedPodAffinityTerm{
{PodAffinityTerm: api.PodAffinityTerm{LabelSelector: &metav1.LabelSelector{}, Namespaces: []string{"ns4"}, TopologyKey: "region4", NamespaceSelector: &metav1.LabelSelector{}}},
},
},
}},
wantPodSpec: &api.PodSpec{Affinity: &api.Affinity{
PodAffinity: &api.PodAffinity{
RequiredDuringSchedulingIgnoredDuringExecution: []api.PodAffinityTerm{
{LabelSelector: &metav1.LabelSelector{}, Namespaces: []string{"ns1"}, TopologyKey: "region1", NamespaceSelector: &metav1.LabelSelector{}},
},
PreferredDuringSchedulingIgnoredDuringExecution: []api.WeightedPodAffinityTerm{
{PodAffinityTerm: api.PodAffinityTerm{LabelSelector: &metav1.LabelSelector{}, Namespaces: []string{"ns2"}, TopologyKey: "region2", NamespaceSelector: &metav1.LabelSelector{}}},
},
},
PodAntiAffinity: &api.PodAntiAffinity{
RequiredDuringSchedulingIgnoredDuringExecution: []api.PodAffinityTerm{
{LabelSelector: &metav1.LabelSelector{}, Namespaces: []string{"ns3"}, TopologyKey: "region3", NamespaceSelector: &metav1.LabelSelector{}},
},
PreferredDuringSchedulingIgnoredDuringExecution: []api.WeightedPodAffinityTerm{
{PodAffinityTerm: api.PodAffinityTerm{LabelSelector: &metav1.LabelSelector{}, Namespaces: []string{"ns4"}, TopologyKey: "region4", NamespaceSelector: &metav1.LabelSelector{}}},
},
},
}},
},
}
for _, tc := range testCases {
t.Run(tc.name, func(t *testing.T) {
defer featuregatetesting.SetFeatureGateDuringTest(t, utilfeature.DefaultFeatureGate, features.PodAffinityNamespaceSelector, tc.enabled)()
dropDisabledPodAffinityTermFields(tc.podSpec, tc.oldPodSpec)
if diff := cmp.Diff(tc.wantPodSpec, tc.podSpec); diff != "" {
t.Errorf("unexpected pod spec (-want, +got):\n%s", diff)
}
})
}
}