Merge pull request #6759 from travisn/aggregate-affinity

ceph: Merge placement node affinity for stretch clusters
This commit is contained in:
Sébastien Han authored and GitHub committed 2020-12-04 18:12:08 +01:00
commit 44305085df
4 files changed
+80 -6

No files matched your search

+48 -1
View File
@@ -29,7 +29,7 @@ func (p Placement) ApplyToPodSpec(t *v1.PodSpec) {
t.Affinity = &v1.Affinity{}
}
if p.NodeAffinity != nil {
t.Affinity.NodeAffinity = p.NodeAffinity.DeepCopy()
t.Affinity.NodeAffinity = p.mergeNodeAffinity(t.Affinity.NodeAffinity)
}
if p.PodAffinity != nil {
t.Affinity.PodAffinity = p.PodAffinity.DeepCopy()
@@ -45,6 +45,53 @@ func (p Placement) ApplyToPodSpec(t *v1.PodSpec) {
}
}
func (p Placement) mergeNodeAffinity(nodeAffinity *v1.NodeAffinity) *v1.NodeAffinity {
// no node affinity is specified yet, so return the placement's nodeAffinity
result := p.NodeAffinity.DeepCopy()
if nodeAffinity == nil {
return result
}
// merge the preferred node affinity that was already specified, and the placement's nodeAffinity
result.PreferredDuringSchedulingIgnoredDuringExecution = append(
nodeAffinity.PreferredDuringSchedulingIgnoredDuringExecution,
p.NodeAffinity.PreferredDuringSchedulingIgnoredDuringExecution...)
// nothing to merge if no affinity was passed in
if nodeAffinity.RequiredDuringSchedulingIgnoredDuringExecution == nil {
return result
}
// take the desired affinity if there was none on the placement
if p.NodeAffinity.RequiredDuringSchedulingIgnoredDuringExecution == nil {
result.RequiredDuringSchedulingIgnoredDuringExecution = nodeAffinity.RequiredDuringSchedulingIgnoredDuringExecution
return result
}
// take the desired affinity node selectors without the need to merge
if len(nodeAffinity.RequiredDuringSchedulingIgnoredDuringExecution.NodeSelectorTerms) == 0 {
return result
}
// take the placement affinity node selectors without the need to merge
if len(p.NodeAffinity.RequiredDuringSchedulingIgnoredDuringExecution.NodeSelectorTerms) == 0 {
// take the placement from the first option since the second isn't specified
result.RequiredDuringSchedulingIgnoredDuringExecution.NodeSelectorTerms =
nodeAffinity.RequiredDuringSchedulingIgnoredDuringExecution.NodeSelectorTerms
return result
}
// merge the match expressions together since they are defined in both placements
// this will only work if we want an "and" between all the expressions, more complex conditions won't work with this merge
var nodeTerm v1.NodeSelectorTerm
nodeTerm.MatchExpressions = append(
nodeAffinity.RequiredDuringSchedulingIgnoredDuringExecution.NodeSelectorTerms[0].MatchExpressions,
p.NodeAffinity.RequiredDuringSchedulingIgnoredDuringExecution.NodeSelectorTerms[0].MatchExpressions...)
nodeTerm.MatchFields = append(
nodeAffinity.RequiredDuringSchedulingIgnoredDuringExecution.NodeSelectorTerms[0].MatchFields,
p.NodeAffinity.RequiredDuringSchedulingIgnoredDuringExecution.NodeSelectorTerms[0].MatchFields...)
result.RequiredDuringSchedulingIgnoredDuringExecution.NodeSelectorTerms[0] = nodeTerm
return result
}
// Merge returns a Placement which results from merging the attributes of the
// original Placement with the attributes of the supplied one. The supplied
// Placement's attributes will override the original ones if defined.
+31 -2
View File
@@ -25,7 +25,7 @@ import (
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
)
func TestPlacement_spec(t *testing.T) {
func TestPlacementSpec(t *testing.T) {
specYaml := []byte(`
nodeAffinity:
requiredDuringSchedulingIgnoredDuringExecution:
@@ -91,6 +91,34 @@ topologySpreadConstraints:
assert.Equal(t, expected, placement)
}
func TestMergeNodeAffinity(t *testing.T) {
// affinity is nil
p := Placement{}
result := p.mergeNodeAffinity(nil)
assert.Nil(t, result)
// node affinity is only set on the placement and should remain unchanged
p.NodeAffinity = placementTestGenerateNodeAffinity()
result = p.mergeNodeAffinity(nil)
assert.Equal(t, p.NodeAffinity, result)
// preferred set, but required not set
affinityToMerge := placementTestGenerateNodeAffinity()
affinityToMerge.RequiredDuringSchedulingIgnoredDuringExecution = nil
result = p.mergeNodeAffinity(affinityToMerge)
assert.Equal(t, 2, len(result.PreferredDuringSchedulingIgnoredDuringExecution))
assert.Equal(t, p.NodeAffinity.RequiredDuringSchedulingIgnoredDuringExecution, result.RequiredDuringSchedulingIgnoredDuringExecution)
// preferred and required expressions set
affinityToMerge = placementTestGenerateNodeAffinity()
affinityToMerge.RequiredDuringSchedulingIgnoredDuringExecution.NodeSelectorTerms[0].MatchExpressions[0].Key = "baz"
result = p.mergeNodeAffinity(affinityToMerge)
assert.Equal(t, 2, len(result.PreferredDuringSchedulingIgnoredDuringExecution))
assert.Equal(t, 2, len(result.RequiredDuringSchedulingIgnoredDuringExecution.NodeSelectorTerms[0].MatchExpressions))
assert.Equal(t, "baz", result.RequiredDuringSchedulingIgnoredDuringExecution.NodeSelectorTerms[0].MatchExpressions[0].Key)
assert.Equal(t, "foo", result.RequiredDuringSchedulingIgnoredDuringExecution.NodeSelectorTerms[0].MatchExpressions[1].Key)
}
func TestPlacementApplyToPodSpec(t *testing.T) {
to := placementTestGetTolerations("foo", "bar")
na := placementTestGenerateNodeAffinity()
@@ -143,6 +171,7 @@ func TestPlacementApplyToPodSpec(t *testing.T) {
p.ApplyToPodSpec(ps)
assert.Equal(t, expected, ps)
// The preferred affinity is merged from both sources to result in two node affinities
p = Placement{NodeAffinity: na, PodAntiAffinity: antiaffinity}
nap := placementTestGenerateNodeAffinity()
nap.PreferredDuringSchedulingIgnoredDuringExecution[0].Weight = 5
@@ -152,7 +181,7 @@ func TestPlacementApplyToPodSpec(t *testing.T) {
TopologySpreadConstraints: tc,
}
p.ApplyToPodSpec(ps)
assert.Equal(t, expected, ps)
assert.Equal(t, 2, len(ps.Affinity.NodeAffinity.PreferredDuringSchedulingIgnoredDuringExecution))
}
func TestPlacementMerge(t *testing.T) {
@@ -55,7 +55,7 @@ func CheckForCancelledOrchestration(context *clusterd.Context) error {
// Check whether we need to cancel the orchestration
if context.RequestCancelOrchestration.IsSet() {
return errors.New("CANCELLING CURRENT ORCHESTATION")
return errors.New("CANCELLING CURRENT ORCHESTRATION")
}
return nil
-2
View File
@@ -289,7 +289,6 @@ func ExtractTopologyFromLabels(labels map[string]string, additionalTopologyIDs [
}
// check for the region k8s topology label that is GA in 1.17.
// TODO: Replace with a const when we update to the K8s 1.17 go-client.
region, ok = labels[corev1.LabelZoneRegionStable]
if ok {
topology[regionLabel] = region
@@ -303,7 +302,6 @@ func ExtractTopologyFromLabels(labels map[string]string, additionalTopologyIDs [
}
// check for the zone k8s topology label that is GA in 1.17.
// TODO: Replace with a const when we update to the K8s 1.17 go-client.
zone, ok = labels[corev1.LabelZoneFailureDomainStable]
if ok {
topology[zoneLabel] = zone