mirror of
https://github.com/k3s-io/kubernetes.git
synced 2025-09-20 09:33:52 +00:00
Add CrossNamespacePodAffinity quota scope and PodAffinityTerm.NamespaceSelector APIs, and CrossNamespacePodAffinity quota scope implementation.
This commit is contained in:
@@ -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
|
||||
|
@@ -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)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
Reference in New Issue
Block a user