82
82
83
83
# Wait for the pod "busybox1" to be deleted, with a timeout of 60s, after having issued the "delete" command
84
84
kubectl delete pod/busybox1
85
- kubectl wait --for=delete pod/busybox1 --timeout=60s` ))
85
+ kubectl wait --for=delete pod/busybox1 --timeout=60s
86
+
87
+ # Wait for the creation of the service "loadbalancer" in addition to wait to have ingress
88
+ kubectl wait --for=jsonpath='{.status.loadBalancer.ingress}' service/loadbalancer --wait-for-creation` ))
86
89
)
87
90
88
91
// errNoMatchingResources is returned when there is no resources matching a query.
@@ -96,8 +99,9 @@ type WaitFlags struct {
96
99
PrintFlags * genericclioptions.PrintFlags
97
100
ResourceBuilderFlags * genericclioptions.ResourceBuilderFlags
98
101
99
- Timeout time.Duration
100
- ForCondition string
102
+ Timeout time.Duration
103
+ ForCondition string
104
+ WaitForCreation bool
101
105
102
106
genericiooptions.IOStreams
103
107
}
@@ -115,7 +119,8 @@ func NewWaitFlags(restClientGetter genericclioptions.RESTClientGetter, streams g
115
119
WithLocal (false ).
116
120
WithLatest (),
117
121
118
- Timeout : 30 * time .Second ,
122
+ Timeout : 30 * time .Second ,
123
+ WaitForCreation : true ,
119
124
120
125
IOStreams : streams ,
121
126
}
@@ -152,6 +157,7 @@ func (flags *WaitFlags) AddFlags(cmd *cobra.Command) {
152
157
153
158
cmd .Flags ().DurationVar (& flags .Timeout , "timeout" , flags .Timeout , "The length of time to wait before giving up. Zero means check once and don't wait, negative means wait for a week." )
154
159
cmd .Flags ().StringVar (& flags .ForCondition , "for" , flags .ForCondition , "The condition to wait on: [delete|condition=condition-name[=condition-value]|jsonpath='{JSONPath expression}'=[JSONPath value]]. The default condition-value is true. Condition values are compared after Unicode simple case folding, which is a more general form of case-insensitivity." )
160
+ cmd .Flags ().BoolVar (& flags .WaitForCreation , "wait-for-creation" , flags .WaitForCreation , "The default value is true. If set to true, also wait for creation of objects if they do not already exist. This flag is ignored in --for=delete" )
155
161
}
156
162
157
163
// ToOptions converts from CLI inputs to runtime inputs
@@ -180,10 +186,11 @@ func (flags *WaitFlags) ToOptions(args []string) (*WaitOptions, error) {
180
186
}
181
187
182
188
o := & WaitOptions {
183
- ResourceFinder : builder ,
184
- DynamicClient : dynamicClient ,
185
- Timeout : effectiveTimeout ,
186
- ForCondition : flags .ForCondition ,
189
+ ResourceFinder : builder ,
190
+ DynamicClient : dynamicClient ,
191
+ Timeout : effectiveTimeout ,
192
+ ForCondition : flags .ForCondition ,
193
+ WaitForCreation : flags .WaitForCreation ,
187
194
188
195
Printer : printer ,
189
196
ConditionFn : conditionFn ,
@@ -302,10 +309,11 @@ type WaitOptions struct {
302
309
ResourceFinder genericclioptions.ResourceFinder
303
310
// UIDMap maps a resource location to a UID. It is optional, but ConditionFuncs may choose to use it to make the result
304
311
// more reliable. For instance, delete can look for UID consistency during delegated calls.
305
- UIDMap UIDMap
306
- DynamicClient dynamic.Interface
307
- Timeout time.Duration
308
- ForCondition string
312
+ UIDMap UIDMap
313
+ DynamicClient dynamic.Interface
314
+ Timeout time.Duration
315
+ ForCondition string
316
+ WaitForCreation bool
309
317
310
318
Printer printers.ResourcePrinter
311
319
ConditionFn ConditionFunc
@@ -320,6 +328,40 @@ func (o *WaitOptions) RunWait() error {
320
328
ctx , cancel := watchtools .ContextWithOptionalTimeout (context .Background (), o .Timeout )
321
329
defer cancel ()
322
330
331
+ isForDelete := strings .ToLower (o .ForCondition ) == "delete"
332
+ if o .WaitForCreation && o .Timeout == 0 {
333
+ return fmt .Errorf ("--wait-for-creation requires a timeout value greater than 0" )
334
+ }
335
+
336
+ if o .WaitForCreation && ! isForDelete {
337
+ err := func () error {
338
+ for {
339
+ select {
340
+ case <- ctx .Done ():
341
+ return fmt .Errorf ("context deadline is exceeded while waiting for the creation of the resources" )
342
+ default :
343
+ err := o .ResourceFinder .Do ().Visit (func (info * resource.Info , err error ) error {
344
+ // We don't need to do anything after we assure that the resources exist. Because
345
+ // actual logic will be incorporated after we wait all the resources' existence.
346
+ return nil
347
+ })
348
+ // It is verified that all the resources exist.
349
+ if err == nil {
350
+ return nil
351
+ }
352
+ // We specifically wait for the creation of resources and all the errors
353
+ // other than not found means that this is something we cannot handle.
354
+ if ! apierrors .IsNotFound (err ) {
355
+ return err
356
+ }
357
+ }
358
+ }
359
+ }()
360
+ if err != nil {
361
+ return err
362
+ }
363
+ }
364
+
323
365
visitCount := 0
324
366
visitFunc := func (info * resource.Info , err error ) error {
325
367
if err != nil {
@@ -338,7 +380,6 @@ func (o *WaitOptions) RunWait() error {
338
380
return err
339
381
}
340
382
visitor := o .ResourceFinder .Do ()
341
- isForDelete := strings .ToLower (o .ForCondition ) == "delete"
342
383
if visitor , ok := visitor .(* resource.Result ); ok && isForDelete {
343
384
visitor .IgnoreErrors (apierrors .IsNotFound )
344
385
}
0 commit comments