Skip to content

Commit f5d2695

Browse files
kmarteauxzegl
authored andcommitted
[#495] implement feature request: topologySpreadConstraints
1 parent fa023cb commit f5d2695

9 files changed

+221
-0
lines changed
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,57 @@
1+
package podtopologyconstraints
2+
3+
import (
4+
ks "github.com/zegl/kube-score/domain"
5+
"github.com/zegl/kube-score/score/checks"
6+
"github.com/zegl/kube-score/scorecard"
7+
)
8+
9+
func Register(allChecks *checks.Checks) {
10+
allChecks.RegisterPodCheck("Pod Topology Spread Constraints", "Pod Topology Spread Constraints", podTopologySpreadConstraints)
11+
}
12+
13+
func podTopologySpreadConstraints(pod ks.PodSpecer) (score scorecard.TestScore, err error) {
14+
spreads := pod.GetPodTemplateSpec().Spec.TopologySpreadConstraints
15+
16+
if spreads == nil {
17+
score.Grade = scorecard.GradeAllOK
18+
score.AddComment("", "Pod Topology Spread Constraints", "No Pod Topology Spread Constraints set, kube-scheduler defaults assumed")
19+
return
20+
}
21+
22+
for _, spread := range spreads {
23+
if spread.LabelSelector == nil {
24+
score.Grade = scorecard.GradeCritical
25+
score.AddComment("", "Pod Topology Spread Constraints", "No labelSelector detected. A label selector is needed determine the number of pods in a topology domain")
26+
return
27+
}
28+
29+
if spread.MaxSkew == 0 {
30+
score.Grade = scorecard.GradeCritical
31+
score.AddComment("", "Pod Topology Spread Constraints", "MaxSkew is set to zero. This is not allowed.")
32+
return
33+
}
34+
35+
if spread.MinDomains != nil && *spread.MinDomains == 0 {
36+
score.Grade = scorecard.GradeCritical
37+
score.AddComment("", "Pod Topology Spread Constraints", "MaxDomain set to zero. This is not allowed. Constraint behaves if minDomains is set to 1 if nil")
38+
return
39+
}
40+
41+
if spread.TopologyKey == "" {
42+
score.Grade = scorecard.GradeCritical
43+
score.AddComment("", "Pod Topology Spread Constraints", "TopologyKey is not set. This is the key of node labels used to bucket nodes into a domain")
44+
return
45+
}
46+
47+
if spread.WhenUnsatisfiable != "DoNotSchedule" && spread.WhenUnsatisfiable != "ScheduleAnyway" {
48+
score.Grade = scorecard.GradeCritical
49+
score.AddComment("", "Pod Topology Spread Constraints", "Invalid WhenUnsatisfiable setting detected")
50+
return
51+
}
52+
}
53+
54+
score.Grade = scorecard.GradeAllOK
55+
score.AddComment("", "Pod Topology Spread Constraints", "Pod Topology Spread Constraints")
56+
return
57+
}
+42
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,42 @@
1+
package score
2+
3+
import (
4+
"testing"
5+
6+
"github.com/zegl/kube-score/scorecard"
7+
)
8+
9+
func TestPodTopologySpreadContraintsWithOneConstraint(t *testing.T) {
10+
t.Parallel()
11+
testExpectedScore(t, "pod-topology-spread-constraints-one-constraint.yaml", "Pod Topology Spread Constraints", scorecard.GradeAllOK)
12+
}
13+
14+
func TestPodTopologySpreadContraintsWithTwoConstraints(t *testing.T) {
15+
t.Parallel()
16+
testExpectedScore(t, "pod-topology-spread-constraints-two-constraints.yaml", "Pod Topology Spread Constraints", scorecard.GradeAllOK)
17+
}
18+
19+
func TestPodTopologySpreadContraintsNoLabelSelector(t *testing.T) {
20+
t.Parallel()
21+
testExpectedScore(t, "pod-topology-spread-constraints-no-labelselector.yaml", "Pod Topology Spread Constraints", scorecard.GradeCritical)
22+
}
23+
24+
func TestPodTopologySpreadContraintsInvalidMaxSkew(t *testing.T) {
25+
t.Parallel()
26+
testExpectedScore(t, "pod-topology-spread-constraints-invalid-maxskew.yaml", "Pod Topology Spread Constraints", scorecard.GradeCritical)
27+
}
28+
29+
func TestPodTopologySpreadContraintsInvalidMinDomains(t *testing.T) {
30+
t.Parallel()
31+
testExpectedScore(t, "pod-topology-spread-constraints-invalid-mindomains.yaml", "Pod Topology Spread Constraints", scorecard.GradeCritical)
32+
}
33+
34+
func TestPodTopologySpreadContraintsNoTopologyKey(t *testing.T) {
35+
t.Parallel()
36+
testExpectedScore(t, "pod-topology-spread-constraints-no-topologykey.yaml", "Pod Topology Spread Constraints", scorecard.GradeCritical)
37+
}
38+
39+
func TestPodTopologySpreadContraintsInvalidDirective(t *testing.T) {
40+
t.Parallel()
41+
testExpectedScore(t, "pod-topology-spread-constraints-invalid-whenunsatisfiable.yaml", "Pod Topology Spread Constraints", scorecard.GradeCritical)
42+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,17 @@
1+
kind: Pod
2+
apiVersion: v1
3+
metadata:
4+
name: mypod
5+
labels:
6+
foo: bar
7+
spec:
8+
topologySpreadConstraints:
9+
- maxSkew: 0
10+
topologyKey: zone
11+
whenUnsatisfiable: DoNotSchedule
12+
labelSelector:
13+
matchLabels:
14+
foo: bar
15+
containers:
16+
- name: pause
17+
image: registry.k8s.io/pause:3.1
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,18 @@
1+
kind: Pod
2+
apiVersion: v1
3+
metadata:
4+
name: mypod
5+
labels:
6+
foo: bar
7+
spec:
8+
topologySpreadConstraints:
9+
- maxSkew: 1
10+
minDomains: 0
11+
topologyKey: zone
12+
whenUnsatisfiable: DoNotSchedule
13+
labelSelector:
14+
matchLabels:
15+
foo: bar
16+
containers:
17+
- name: pause
18+
image: registry.k8s.io/pause:3.1
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,17 @@
1+
kind: Pod
2+
apiVersion: v1
3+
metadata:
4+
name: mypod
5+
labels:
6+
foo: bar
7+
spec:
8+
topologySpreadConstraints:
9+
- maxSkew: 1
10+
topologyKey: zone
11+
whenUnsatisfiable: DoNotSCHEDULE
12+
labelSelector:
13+
matchLabels:
14+
foo: bar
15+
containers:
16+
- name: pause
17+
image: registry.k8s.io/pause:3.1
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,14 @@
1+
kind: Pod
2+
apiVersion: v1
3+
metadata:
4+
name: mypod
5+
labels:
6+
foo: bar
7+
spec:
8+
topologySpreadConstraints:
9+
- maxSkew: 1
10+
topologyKey: zone
11+
whenUnsatisfiable: DoNotSchedule
12+
containers:
13+
- name: pause
14+
image: registry.k8s.io/pause:3.1
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,16 @@
1+
kind: Pod
2+
apiVersion: v1
3+
metadata:
4+
name: mypod
5+
labels:
6+
foo: bar
7+
spec:
8+
topologySpreadConstraints:
9+
- maxSkew: 1
10+
whenUnsatisfiable: DoNotSchedule
11+
labelSelector:
12+
matchLabels:
13+
foo: bar
14+
containers:
15+
- name: pause
16+
image: registry.k8s.io/pause:3.1
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,17 @@
1+
kind: Pod
2+
apiVersion: v1
3+
metadata:
4+
name: mypod
5+
labels:
6+
foo: bar
7+
spec:
8+
topologySpreadConstraints:
9+
- maxSkew: 1
10+
topologyKey: zone
11+
whenUnsatisfiable: DoNotSchedule
12+
labelSelector:
13+
matchLabels:
14+
foo: bar
15+
containers:
16+
- name: pause
17+
image: registry.k8s.io/pause:3.1
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,23 @@
1+
kind: Pod
2+
apiVersion: v1
3+
metadata:
4+
name: mypod
5+
labels:
6+
foo: bar
7+
spec:
8+
topologySpreadConstraints:
9+
- maxSkew: 1
10+
topologyKey: zone
11+
whenUnsatisfiable: DoNotSchedule
12+
labelSelector:
13+
matchLabels:
14+
foo: bar
15+
- maxSkew: 1
16+
topologyKey: node
17+
whenUnsatisfiable: DoNotSchedule
18+
labelSelector:
19+
matchLabels:
20+
foo: bar
21+
containers:
22+
- name: pause
23+
image: registry.k8s.io/pause:3.1

0 commit comments

Comments
 (0)