Skip to content

Commit 1e00020

Browse files
authoredSep 19, 2022
test: Fix pod preemption in kubelet config tests (#2531)
1 parent fc6b899 commit 1e00020

File tree

6 files changed

+141
-40
lines changed

6 files changed

+141
-40
lines changed
 

‎test/pkg/environment/expectations.go

+77-2
Original file line numberDiff line numberDiff line change
@@ -33,6 +33,7 @@ import (
3333
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
3434
"k8s.io/apimachinery/pkg/labels"
3535
"k8s.io/apimachinery/pkg/types"
36+
"k8s.io/apimachinery/pkg/util/sets"
3637
"k8s.io/client-go/informers"
3738
"k8s.io/client-go/tools/cache"
3839
"knative.dev/pkg/logging"
@@ -64,16 +65,19 @@ var (
6465
)
6566

6667
const (
67-
NoWatch = "NoWatch"
68+
NoWatch = "NoWatch"
69+
NoEvents = "NoEvents"
6870
)
6971

7072
// if set, logs additional information that may be useful in debugging an E2E test failure
7173
var debugE2E = true
74+
var testStartTime time.Time
7275
var stop chan struct{}
7376

7477
// nolint:gocyclo
7578
func (env *Environment) BeforeEach() {
7679
stop = make(chan struct{})
80+
testStartTime = time.Now()
7781

7882
if debugE2E {
7983
fmt.Println("------- START BEFORE -------")
@@ -136,6 +140,29 @@ func (env *Environment) getPodInformation(p *v1.Pod) string {
136140
pod.IsProvisionable(p), p.Status.Phase, p.Spec.NodeName, containerInfo.String())
137141
}
138142

143+
// Partially copied from
144+
// https://github.com/kubernetes/kubernetes/blob/04ee339c7a4d36b4037ce3635993e2a9e395ebf3/staging/src/k8s.io/kubectl/pkg/describe/describe.go#L4232
145+
func getEventInformation(k types.NamespacedName, el *v1.EventList) string {
146+
sb := strings.Builder{}
147+
sb.WriteString(fmt.Sprintf("------- %s EVENTS -------\n", k))
148+
if len(el.Items) == 0 {
149+
return sb.String()
150+
}
151+
for _, e := range el.Items {
152+
source := e.Source.Component
153+
if source == "" {
154+
source = e.ReportingController
155+
}
156+
sb.WriteString(fmt.Sprintf("type=%s reason=%s from=%s message=%s\n",
157+
e.Type,
158+
e.Reason,
159+
source,
160+
strings.TrimSpace(e.Message)),
161+
)
162+
}
163+
return sb.String()
164+
}
165+
139166
// startPodMonitor monitors all pods that are provisioned in a namespace outside kube-system
140167
// and karpenter namespaces during a test
141168
func (env *Environment) startPodMonitor(stop <-chan struct{}) {
@@ -211,9 +238,47 @@ func (env *Environment) AfterEach() {
211238
env.eventuallyExpectScaleDown()
212239
env.expectNoCrashes()
213240
close(stop) // close the pod/node monitor watch channel
241+
if debugE2E && !lo.Contains(CurrentSpecReport().Labels(), NoEvents) {
242+
env.dumpPodEvents(testStartTime)
243+
}
214244
env.printControllerLogs(&v1.PodLogOptions{Container: "controller"})
215245
}
216246

247+
func (env *Environment) dumpPodEvents(testStartTime time.Time) {
248+
el := &v1.EventList{}
249+
ExpectWithOffset(1, env.Client.List(env, el)).To(Succeed())
250+
251+
eventMap := map[types.NamespacedName]*v1.EventList{}
252+
253+
filteredEvents := lo.Filter(el.Items, func(e v1.Event, _ int) bool {
254+
if !e.EventTime.IsZero() {
255+
if e.EventTime.BeforeTime(&metav1.Time{Time: testStartTime}) {
256+
return false
257+
}
258+
} else if e.FirstTimestamp.Before(&metav1.Time{Time: testStartTime}) {
259+
return false
260+
}
261+
if e.InvolvedObject.Kind != "Pod" {
262+
return false
263+
}
264+
if e.InvolvedObject.Namespace == "kube-system" || e.InvolvedObject.Namespace == "karpenter" {
265+
return false
266+
}
267+
return true
268+
})
269+
for i := range filteredEvents {
270+
elem := filteredEvents[i]
271+
objectKey := types.NamespacedName{Namespace: elem.InvolvedObject.Namespace, Name: elem.InvolvedObject.Name}
272+
if _, ok := eventMap[objectKey]; !ok {
273+
eventMap[objectKey] = &v1.EventList{}
274+
}
275+
eventMap[objectKey].Items = append(eventMap[objectKey].Items, elem)
276+
}
277+
for k, v := range eventMap {
278+
fmt.Print(getEventInformation(k, v))
279+
}
280+
}
281+
217282
func (env *Environment) ExpectCreated(objects ...client.Object) {
218283
for _, object := range objects {
219284
object.SetLabels(lo.Assign(object.GetLabels(), map[string]string{TestLabelName: env.ClusterName}))
@@ -271,10 +336,19 @@ func (env *Environment) EventuallyExpectKarpenterWithEnvVar(envVar v1.EnvVar) {
271336

272337
func (env *Environment) EventuallyExpectHealthyPodCount(selector labels.Selector, numPods int) {
273338
Eventually(func(g Gomega) {
274-
g.Expect(env.Monitor.RunningPods(selector)).To(Equal(numPods))
339+
g.Expect(env.Monitor.RunningPodsCount(selector)).To(Equal(numPods))
275340
}).Should(Succeed())
276341
}
277342

343+
func (env *Environment) ExpectUniqueNodeNames(selector labels.Selector, uniqueNames int) {
344+
pods := env.Monitor.RunningPods(selector)
345+
nodeNames := sets.NewString()
346+
for _, pod := range pods {
347+
nodeNames.Insert(pod.Spec.NodeName)
348+
}
349+
ExpectWithOffset(1, len(nodeNames)).To(BeNumerically("==", uniqueNames))
350+
}
351+
278352
func (env *Environment) eventuallyExpectScaleDown() {
279353
Eventually(func(g Gomega) {
280354
// expect the current node count to be what it was when the test started
@@ -368,6 +442,7 @@ var (
368442
)
369443

370444
func (env *Environment) printControllerLogs(options *v1.PodLogOptions) {
445+
fmt.Println("------- START CONTROLLER LOGS -------")
371446
if options.SinceTime == nil {
372447
options.SinceTime = lastLogged.DeepCopy()
373448
lastLogged = metav1.Now()

‎test/pkg/environment/monitor.go

+9-4
Original file line numberDiff line numberDiff line change
@@ -129,17 +129,22 @@ func (m *Monitor) GetCreatedNodes() []v1.Node {
129129
}
130130

131131
// RunningPods returns the number of running pods matching the given selector
132-
func (m *Monitor) RunningPods(selector labels.Selector) int {
133-
count := 0
132+
func (m *Monitor) RunningPods(selector labels.Selector) []*v1.Pod {
133+
var pods []*v1.Pod
134134
for _, pod := range m.poll().pods.Items {
135+
pod := pod
135136
if pod.Status.Phase != v1.PodRunning {
136137
continue
137138
}
138139
if selector.Matches(labels.Set(pod.Labels)) {
139-
count++
140+
pods = append(pods, &pod)
140141
}
141142
}
142-
return count
143+
return pods
144+
}
145+
146+
func (m *Monitor) RunningPodsCount(selector labels.Selector) int {
147+
return len(m.RunningPods(selector))
143148
}
144149

145150
func (m *Monitor) poll() state {

‎test/suites/consolidation/suite_test.go

+1-1
Original file line numberDiff line numberDiff line change
@@ -57,7 +57,7 @@ var _ = AfterEach(func() {
5757
})
5858

5959
var _ = Describe("Consolidation", func() {
60-
It("should consolidate nodes (delete)", Label(environment.NoWatch), func() {
60+
It("should consolidate nodes (delete)", Label(environment.NoWatch), Label(environment.NoEvents), func() {
6161
provider := test.AWSNodeTemplate(v1alpha1.AWSNodeTemplateSpec{AWS: awsv1alpha1.AWS{
6262
SecurityGroupSelector: map[string]string{"karpenter.sh/discovery": env.ClusterName},
6363
SubnetSelector: map[string]string{"karpenter.sh/discovery": env.ClusterName},

‎test/suites/integration/kubelet_config_test.go

+52-31
Original file line numberDiff line numberDiff line change
@@ -21,7 +21,9 @@ import (
2121
. "github.com/onsi/gomega"
2222
appsv1 "k8s.io/api/apps/v1"
2323
v1 "k8s.io/api/core/v1"
24-
"k8s.io/apimachinery/pkg/util/sets"
24+
"k8s.io/apimachinery/pkg/api/resource"
25+
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
26+
"k8s.io/apimachinery/pkg/labels"
2527
"knative.dev/pkg/ptr"
2628

2729
"github.com/aws/karpenter/pkg/apis/awsnodetemplate/v1alpha1"
@@ -56,19 +58,24 @@ var _ = Describe("KubeletConfiguration Overrides", func() {
5658
MaxPods: ptr.Int32(1 + int32(dsCount)),
5759
}
5860

59-
pods := []*v1.Pod{test.Pod(), test.Pod(), test.Pod()}
60-
env.ExpectCreated(provisioner, provider)
61-
for _, pod := range pods {
62-
env.ExpectCreated(pod)
63-
}
64-
env.EventuallyExpectHealthy(pods...)
65-
env.ExpectCreatedNodeCount("==", 3)
61+
numPods := 3
62+
dep := test.Deployment(test.DeploymentOptions{
63+
Replicas: int32(numPods),
64+
PodOptions: test.PodOptions{
65+
ObjectMeta: metav1.ObjectMeta{
66+
Labels: map[string]string{"app": "large-app"},
67+
},
68+
ResourceRequirements: v1.ResourceRequirements{
69+
Requests: v1.ResourceList{v1.ResourceCPU: resource.MustParse("100m")},
70+
},
71+
},
72+
})
73+
selector := labels.SelectorFromSet(dep.Spec.Selector.MatchLabels)
74+
env.ExpectCreated(provisioner, provider, dep)
6675

67-
nodeNames := sets.NewString()
68-
for _, pod := range pods {
69-
nodeNames.Insert(pod.Spec.NodeName)
70-
}
71-
Expect(len(nodeNames)).To(BeNumerically("==", 3))
76+
env.EventuallyExpectHealthyPodCount(selector, numPods)
77+
env.ExpectCreatedNodeCount("==", 3)
78+
env.ExpectUniqueNodeNames(selector, 3)
7279
})
7380
It("should schedule pods onto separate nodes when podsPerCore is set", func() {
7481
provider := test.AWSNodeTemplate(v1alpha1.AWSNodeTemplateSpec{AWS: awsv1alpha1.AWS{
@@ -92,6 +99,19 @@ var _ = Describe("KubeletConfiguration Overrides", func() {
9299
},
93100
},
94101
})
102+
numPods := 4
103+
dep := test.Deployment(test.DeploymentOptions{
104+
Replicas: int32(numPods),
105+
PodOptions: test.PodOptions{
106+
ObjectMeta: metav1.ObjectMeta{
107+
Labels: map[string]string{"app": "large-app"},
108+
},
109+
ResourceRequirements: v1.ResourceRequirements{
110+
Requests: v1.ResourceList{v1.ResourceCPU: resource.MustParse("100m")},
111+
},
112+
},
113+
})
114+
selector := labels.SelectorFromSet(dep.Spec.Selector.MatchLabels)
95115

96116
// Get the DS pod count and use it to calculate the DS pod overhead
97117
// We calculate podsPerCore to split the test pods and the DS pods between two nodes:
@@ -106,19 +126,10 @@ var _ = Describe("KubeletConfiguration Overrides", func() {
106126
PodsPerCore: ptr.Int32(int32(math.Ceil(float64(2+dsCount) / 2))),
107127
}
108128

109-
pods := []*v1.Pod{test.Pod(), test.Pod(), test.Pod(), test.Pod()}
110-
env.ExpectCreated(provisioner, provider)
111-
for _, pod := range pods {
112-
env.ExpectCreated(pod)
113-
}
114-
env.EventuallyExpectHealthy(pods...)
129+
env.ExpectCreated(provisioner, provider, dep)
130+
env.EventuallyExpectHealthyPodCount(selector, numPods)
115131
env.ExpectCreatedNodeCount("==", 2)
116-
117-
nodeNames := sets.NewString()
118-
for _, pod := range pods {
119-
nodeNames.Insert(pod.Spec.NodeName)
120-
}
121-
Expect(len(nodeNames)).To(BeNumerically("==", 2))
132+
env.ExpectUniqueNodeNames(selector, 2)
122133
})
123134
It("should ignore podsPerCore value when Bottlerocket is used", func() {
124135
provider := test.AWSNodeTemplate(v1alpha1.AWSNodeTemplateSpec{AWS: awsv1alpha1.AWS{
@@ -141,14 +152,24 @@ var _ = Describe("KubeletConfiguration Overrides", func() {
141152
},
142153
},
143154
})
155+
numPods := 6
156+
dep := test.Deployment(test.DeploymentOptions{
157+
Replicas: int32(numPods),
158+
PodOptions: test.PodOptions{
159+
ObjectMeta: metav1.ObjectMeta{
160+
Labels: map[string]string{"app": "large-app"},
161+
},
162+
ResourceRequirements: v1.ResourceRequirements{
163+
Requests: v1.ResourceList{v1.ResourceCPU: resource.MustParse("100m")},
164+
},
165+
},
166+
})
167+
selector := labels.SelectorFromSet(dep.Spec.Selector.MatchLabels)
144168

145-
pods := []*v1.Pod{test.Pod(), test.Pod(), test.Pod(), test.Pod(), test.Pod(), test.Pod()}
146-
env.ExpectCreated(provisioner, provider)
147-
for _, pod := range pods {
148-
env.ExpectCreated(pod)
149-
}
150-
env.EventuallyExpectHealthy(pods...)
169+
env.ExpectCreated(provisioner, provider, dep)
170+
env.EventuallyExpectHealthyPodCount(selector, numPods)
151171
env.ExpectCreatedNodeCount("==", 1)
172+
env.ExpectUniqueNodeNames(selector, 1)
152173
})
153174
})
154175

‎test/suites/integration/scheduling_test.go

+1-1
Original file line numberDiff line numberDiff line change
@@ -95,7 +95,7 @@ var _ = Describe("Scheduling", func() {
9595
env.EventuallyExpectHealthy(pod)
9696
env.ExpectCreatedNodeCount("==", 1)
9797
})
98-
It("should provision a node for a deployment", Label(environment.NoWatch), func() {
98+
It("should provision a node for a deployment", Label(environment.NoWatch), Label(environment.NoEvents), func() {
9999
provider := test.AWSNodeTemplate(v1alpha1.AWSNodeTemplateSpec{AWS: awsv1alpha1.AWS{
100100
SecurityGroupSelector: map[string]string{"karpenter.sh/discovery": env.ClusterName},
101101
SubnetSelector: map[string]string{"karpenter.sh/discovery": env.ClusterName},

‎test/suites/utilization/suite_test.go

+1-1
Original file line numberDiff line numberDiff line change
@@ -45,7 +45,7 @@ func TestUtilization(t *testing.T) {
4545
var _ = BeforeEach(func() { env.BeforeEach() })
4646
var _ = AfterEach(func() { env.AfterEach() })
4747

48-
var _ = Describe("Utilization", Label(environment.NoWatch), func() {
48+
var _ = Describe("Utilization", Label(environment.NoWatch), Label(environment.NoEvents), func() {
4949
It("should provision one pod per node", func() {
5050
provider := test.AWSNodeTemplate(v1alpha1.AWSNodeTemplateSpec{AWS: awsv1alpha1.AWS{
5151
SecurityGroupSelector: map[string]string{"karpenter.sh/discovery": env.ClusterName},

0 commit comments

Comments
 (0)
Please sign in to comment.