@@ -33,6 +33,8 @@ import (
33
33
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
34
34
"k8s.io/apimachinery/pkg/labels"
35
35
"k8s.io/apimachinery/pkg/types"
36
+ "k8s.io/client-go/informers"
37
+ "k8s.io/client-go/tools/cache"
36
38
"knative.dev/pkg/logging"
37
39
"knative.dev/pkg/ptr"
38
40
"sigs.k8s.io/controller-runtime/pkg/client"
@@ -61,14 +63,29 @@ var (
61
63
}
62
64
)
63
65
66
+ const (
67
+ NoWatch = "NoWatch"
68
+ )
69
+
64
70
// if set, logs additional information that may be useful in debugging an E2E test failure
65
71
var debugE2E = true
72
+ var stop chan struct {}
66
73
74
+ // nolint:gocyclo
67
75
func (env * Environment ) BeforeEach () {
76
+ stop = make (chan struct {})
77
+
78
+ if debugE2E {
79
+ fmt .Println ("------- START BEFORE -------" )
80
+ defer fmt .Println ("------- END BEFORE -------" )
81
+ }
82
+
68
83
var nodes v1.NodeList
69
84
Expect (env .Client .List (env .Context , & nodes )).To (Succeed ())
70
85
if debugE2E {
71
- env .dumpNodeInformation (nodes )
86
+ for i := range nodes .Items {
87
+ fmt .Println (env .getNodeInformation (& nodes .Items [i ]))
88
+ }
72
89
}
73
90
for _ , node := range nodes .Items {
74
91
if len (node .Spec .Taints ) == 0 && ! node .Spec .Unschedulable {
@@ -79,41 +96,92 @@ func (env *Environment) BeforeEach() {
79
96
var pods v1.PodList
80
97
Expect (env .Client .List (env .Context , & pods )).To (Succeed ())
81
98
if debugE2E {
82
- env .dumpPodInformation (pods )
99
+ for i := range pods .Items {
100
+ fmt .Println (env .getPodInformation (& pods .Items [i ]))
101
+ }
83
102
}
84
103
for i := range pods .Items {
85
104
Expect (pod .IsProvisionable (& pods .Items [i ])).To (BeFalse (),
86
105
fmt .Sprintf ("expected to have no provisionable pods, found %s/%s" , pods .Items [i ].Namespace , pods .Items [i ].Name ))
87
106
Expect (pods .Items [i ].Namespace ).ToNot (Equal ("default" ),
88
107
fmt .Sprintf ("expected no pods in the `default` namespace, found %s/%s" , pods .Items [i ].Namespace , pods .Items [i ].Name ))
89
108
}
109
+ // If the test is labeled as NoWatch, then the node/pod monitor will just list at the beginning
110
+ // of the test rather than perform a watch during it
111
+ if debugE2E && ! lo .Contains (CurrentSpecReport ().Labels (), NoWatch ) {
112
+ env .startNodeMonitor (stop )
113
+ env .startPodMonitor (stop )
114
+ }
90
115
var provisioners v1alpha5.ProvisionerList
91
116
Expect (env .Client .List (env .Context , & provisioners )).To (Succeed ())
92
117
Expect (provisioners .Items ).To (HaveLen (0 ), "expected no provisioners to exist" )
93
118
env .Monitor .Reset ()
94
119
env .StartingNodeCount = env .Monitor .NodeCountAtReset ()
95
120
}
96
121
97
- func (env * Environment ) dumpNodeInformation (nodes v1.NodeList ) {
98
- for i := range nodes .Items {
99
- node := nodes .Items [i ]
100
- pods , _ := nodeutils .GetNodePods (env , env .Client , & node )
101
- fmt .Printf ("node %s ready=%s initialized=%s pods=%d taints = %v\n " , node .Name , nodeutils .GetCondition (& node , v1 .NodeReady ).Status , node .Labels [v1alpha5 .LabelNodeInitialized ], len (pods ), node .Spec .Taints )
102
- }
122
+ func (env * Environment ) getNodeInformation (n * v1.Node ) string {
123
+ pods , _ := nodeutils .GetNodePods (env , env .Client , n )
124
+ return fmt .Sprintf ("node %s ready=%s initialized=%s pods=%d taints=%v" , n .Name , nodeutils .GetCondition (n , v1 .NodeReady ).Status , n .Labels [v1alpha5 .LabelNodeInitialized ], len (pods ), n .Spec .Taints )
103
125
}
104
126
105
- func (env * Environment ) dumpPodInformation (pods v1.PodList ) {
106
- for i , p := range pods .Items {
107
- var containerInfo strings.Builder
108
- for _ , c := range p .Status .ContainerStatuses {
109
- if containerInfo .Len () > 0 {
110
- fmt .Fprintf (& containerInfo , ", " )
111
- }
112
- fmt .Fprintf (& containerInfo , "%s restarts=%d" , c .Name , c .RestartCount )
127
+ func (env * Environment ) getPodInformation (p * v1.Pod ) string {
128
+ var containerInfo strings.Builder
129
+ for _ , c := range p .Status .ContainerStatuses {
130
+ if containerInfo .Len () > 0 {
131
+ fmt .Fprintf (& containerInfo , ", " )
113
132
}
114
- fmt .Printf ("pods %s/%s provisionable=%v nodename=%s [%s]\n " , p .Namespace , p .Name ,
115
- pod .IsProvisionable (& pods .Items [i ]), p .Spec .NodeName , containerInfo .String ())
133
+ fmt .Fprintf (& containerInfo , "%s restarts=%d" , c .Name , c .RestartCount )
116
134
}
135
+ return fmt .Sprintf ("pods %s/%s provisionable=%v phase=%s nodename=%s [%s]" , p .Namespace , p .Name ,
136
+ pod .IsProvisionable (p ), p .Status .Phase , p .Spec .NodeName , containerInfo .String ())
137
+ }
138
+
139
+ // startPodMonitor monitors all pods that are provisioned in a namespace outside kube-system
140
+ // and karpenter namespaces during a test
141
+ func (env * Environment ) startPodMonitor (stop <- chan struct {}) {
142
+ factory := informers .NewSharedInformerFactoryWithOptions (env .KubeClient , time .Second * 30 ,
143
+ informers .WithTweakListOptions (func (l * metav1.ListOptions ) {
144
+ l .FieldSelector = "metadata.namespace!=kube-system,metadata.namespace!=karpenter"
145
+ }))
146
+ podInformer := factory .Core ().V1 ().Pods ().Informer ()
147
+ podInformer .AddEventHandler (cache.ResourceEventHandlerFuncs {
148
+ AddFunc : func (obj interface {}) {
149
+ fmt .Printf ("[CREATED] %s\n " , env .getPodInformation (obj .(* v1.Pod )))
150
+ },
151
+ UpdateFunc : func (oldObj interface {}, newObj interface {}) {
152
+ if env .getPodInformation (oldObj .(* v1.Pod )) != env .getPodInformation (newObj .(* v1.Pod )) {
153
+ fmt .Printf ("[UPDATED] %s\n " , env .getPodInformation (newObj .(* v1.Pod )))
154
+ }
155
+ },
156
+ DeleteFunc : func (obj interface {}) {
157
+ fmt .Printf ("[DELETED] %s\n " , env .getPodInformation (obj .(* v1.Pod )))
158
+ },
159
+ })
160
+ factory .Start (stop )
161
+ }
162
+
163
+ // startNodeMonitor monitors all nodes that are provisioned by any provisioners during a test
164
+ func (env * Environment ) startNodeMonitor (stop <- chan struct {}) {
165
+ factory := informers .NewSharedInformerFactoryWithOptions (env .KubeClient , time .Second * 30 ,
166
+ informers .WithTweakListOptions (func (l * metav1.ListOptions ) { l .LabelSelector = v1alpha5 .ProvisionerNameLabelKey }))
167
+ podInformer := factory .Core ().V1 ().Nodes ().Informer ()
168
+ podInformer .AddEventHandler (cache.ResourceEventHandlerFuncs {
169
+ AddFunc : func (obj interface {}) {
170
+ node := obj .(* v1.Node )
171
+ if _ , ok := node .Labels [TestLabelName ]; ok {
172
+ fmt .Printf ("[CREATED] %s\n " , env .getNodeInformation (obj .(* v1.Node )))
173
+ }
174
+ },
175
+ UpdateFunc : func (oldObj interface {}, newObj interface {}) {
176
+ if env .getNodeInformation (oldObj .(* v1.Node )) != env .getNodeInformation (newObj .(* v1.Node )) {
177
+ fmt .Printf ("[UPDATED] %s\n " , env .getNodeInformation (newObj .(* v1.Node )))
178
+ }
179
+ },
180
+ DeleteFunc : func (obj interface {}) {
181
+ fmt .Printf ("[DELETED] %s\n " , env .getNodeInformation (obj .(* v1.Node )))
182
+ },
183
+ })
184
+ factory .Start (stop )
117
185
}
118
186
119
187
func (env * Environment ) AfterEach () {
@@ -137,6 +205,7 @@ func (env *Environment) AfterEach() {
137
205
wg .Wait ()
138
206
env .eventuallyExpectScaleDown ()
139
207
env .expectNoCrashes ()
208
+ close (stop ) // close the pod/node monitor watch channel
140
209
env .printControllerLogs (& v1.PodLogOptions {Container : "controller" })
141
210
}
142
211
0 commit comments