@@ -133,6 +133,100 @@ func DeploymentBecomesAvailableWithin(d time.Duration, namespace, name string) f
133133 }
134134}
135135
136+ // DeploymentPodIsRunningMustNotChangeWithin fails a test if the supplied Deployment does
137+ // not have a running Pod that stays running for the supplied duration.
138+ func DeploymentPodIsRunningMustNotChangeWithin (d time.Duration , namespace , name string ) features.Func {
139+ return func (ctx context.Context , t * testing.T , c * envconf.Config ) context.Context {
140+ t .Helper ()
141+
142+ dp := & appsv1.Deployment {ObjectMeta : metav1.ObjectMeta {Namespace : namespace , Name : name }}
143+ t .Logf ("Ensuring deployment %s/%s has running pod that does not change within %s" , dp .GetNamespace (), dp .GetName (), d .String ())
144+ start := time .Now ()
145+
146+ pod , err := podForDeployment (ctx , t , c , dp )
147+ if err != nil {
148+ t .Errorf ("Failed to get pod for deployment %s/%s: %s" , dp .GetNamespace (), dp .GetName (), err )
149+ return ctx
150+ }
151+
152+ // first wait for pod to be running
153+ if err := wait .For (conditions .New (c .Client ().Resources ()).PodConditionMatch (pod , corev1 .PodReady , corev1 .ConditionTrue ), wait .WithTimeout (d ), wait .WithInterval (DefaultPollInterval )); err != nil {
154+ t .Errorf ("Deployment %s/%s never got a running pod after %s" , dp .GetNamespace (), dp .GetName (), since (start ))
155+ return ctx
156+ }
157+
158+ // now wait to make sure the pod stays running (does not change)
159+ start = time .Now ()
160+ if err := wait .For (conditions .New (c .Client ().Resources ()).PodConditionMatch (pod , corev1 .PodReady , corev1 .ConditionFalse ), wait .WithTimeout (d ), wait .WithInterval (DefaultPollInterval )); err != nil {
161+ if deadlineExceed (err ) {
162+ t .Logf ("Deployment %s/%s had running pod that did not change after %s" , dp .GetNamespace (), dp .GetName (), since (start ))
163+ } else {
164+ t .Errorf ("Error while observing pod for deployment %s/%s" , dp .GetNamespace (), dp .GetName ())
165+ }
166+ return ctx
167+ }
168+ t .Errorf ("Deployment %s/%s had pod that changed within %s, but it should not have" , dp .GetNamespace (), dp .GetName (), d .String ())
169+ return ctx
170+ }
171+ }
172+
173+ // ArgSetWithin fails a test if the supplied Deployment does not have a Pod with
174+ // the given argument set within the supplied duration.
175+ func ArgSetWithin (d time.Duration , arg , namespace , name string ) features.Func {
176+ return checkArgSetWithin (d , arg , true , namespace , name )
177+ }
178+
179+ // ArgUnsetWithin fails a test if the supplied Deployment does not have a Pod with
180+ // the given argument unset within the supplied duration.
181+ func ArgUnsetWithin (d time.Duration , arg , namespace , name string ) features.Func {
182+ return checkArgSetWithin (d , arg , false , namespace , name )
183+ }
184+
185+ // checkArgSetWithin implements a check for the supplied Deployment having a Pod
186+ // with the given argument being either set or unset within the supplied
187+ // duration.
188+ func checkArgSetWithin (d time.Duration , arg string , wantSet bool , namespace , name string ) features.Func {
189+ return func (ctx context.Context , t * testing.T , c * envconf.Config ) context.Context {
190+ t .Helper ()
191+
192+ dp := & appsv1.Deployment {ObjectMeta : metav1.ObjectMeta {Namespace : namespace , Name : name }}
193+ t .Logf ("Waiting %s for pod in deployment %s/%s to have arg %s set=%t..." , d , dp .GetNamespace (), dp .GetName (), arg , wantSet )
194+ start := time .Now ()
195+
196+ if err := wait .For (func (ctx context.Context ) (done bool , err error ) {
197+ pod , err := podForDeployment (ctx , t , c , dp )
198+ if err != nil {
199+ t .Logf ("failed to get pod for deployment %s/%s: %s" , dp .GetNamespace (), dp .GetName (), err )
200+ return false , nil
201+ }
202+
203+ found := false
204+ c := pod .Spec .Containers [0 ]
205+ for _ , a := range c .Args {
206+ if a == arg {
207+ found = true
208+ }
209+ }
210+
211+ if wantSet && ! found {
212+ t .Logf ("did not find arg %s within %s" , arg , c .Args )
213+ return false , nil
214+ } else if ! wantSet && found {
215+ t .Logf ("unexpectedly found arg %s within %s" , arg , c .Args )
216+ return false , nil
217+ }
218+
219+ return true , nil
220+ }, wait .WithTimeout (d ), wait .WithInterval (DefaultPollInterval )); err != nil {
221+ t .Fatal (err )
222+ return ctx
223+ }
224+
225+ t .Logf ("Deployment %s/%s has pod with arg %s set=%t after %s" , dp .GetNamespace (), dp .GetName (), arg , wantSet , since (start ))
226+ return ctx
227+ }
228+ }
229+
136230// ResourcesCreatedWithin fails a test if the supplied resources are not found
137231// to exist within the supplied duration.
138232func ResourcesCreatedWithin (d time.Duration , dir , pattern string , options ... decoder.DecodeOption ) features.Func {
@@ -727,7 +821,7 @@ func CompositeResourceHasFieldValueWithin(d time.Duration, dir, claimFile, path
727821 return got != ""
728822 }
729823
730- if err := wait .For (conditions .New (c .Client ().Resources ()).ResourceMatch (cm , hasResourceRef ), wait .WithTimeout (d ), wait .WithInterval (DefaultPollInterval )); err != nil {
824+ if err := wait .For (conditions .New (c .Client ().Resources ()).ResourceMatch (cm , hasResourceRef ), wait .WithTimeout (d ), wait .WithInterval (DefaultPollInterval ), wait . WithImmediate () ); err != nil {
731825 t .Errorf ("Claim %q does not have a resourceRef to an XR: %v" , cm .GetName (), err )
732826 return ctx
733827 }
@@ -1168,3 +1262,33 @@ func since(t time.Time) string {
11681262func deadlineExceed (err error ) bool {
11691263 return errors .Is (err , context .DeadlineExceeded ) || strings .Contains (err .Error (), "would exceed context deadline" )
11701264}
1265+
1266+ // podForDeployment returns the pod for a given Deployment. If the number of
1267+ // pods found is not exactly one, or that one pod does not have exactly one
1268+ // container, then this function returns an error.
1269+ func podForDeployment (ctx context.Context , t * testing.T , c * envconf.Config , dp * appsv1.Deployment ) (* corev1.Pod , error ) {
1270+ t .Helper ()
1271+ if err := c .Client ().Resources ().Get (ctx , dp .GetName (), dp .GetNamespace (), dp ); err != nil {
1272+ t .Logf ("failed to get deployment %s/%s: %s" , dp .GetNamespace (), dp .GetName (), err )
1273+ return nil , err
1274+ }
1275+
1276+ // use the deployment's selector to list all pods belonging to the deployment
1277+ selector := metav1 .FormatLabelSelector (dp .Spec .Selector )
1278+ pods := & corev1.PodList {}
1279+ if err := c .Client ().Resources ().List (ctx , pods , resources .WithLabelSelector (selector )); err != nil {
1280+ t .Logf ("failed to list pods for deployment %s/%s: %s" , dp .GetNamespace (), dp .GetName (), err )
1281+ return nil , err
1282+ }
1283+
1284+ if len (pods .Items ) != 1 {
1285+ return nil , errors .Errorf ("expected 1 pod, found %d" , len (pods .Items ))
1286+ }
1287+
1288+ pod := pods .Items [0 ]
1289+ if len (pod .Spec .Containers ) != 1 {
1290+ return nil , errors .Errorf ("expected 1 container, found %d" , len (pod .Spec .Containers ))
1291+ }
1292+
1293+ return & pod , nil
1294+ }
0 commit comments