Skip to content

Commit 1f5875f

Browse files
committed
Reduced the number of prints and updated how flags are handled
1 parent e45072b commit 1f5875f

File tree

8 files changed

+216
-174
lines changed

8 files changed

+216
-174
lines changed

README.md

+23
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,29 @@ For jobs it wait until the `Completed` condition is true.
2323

2424
For services it will wait until all pods that match the service selector are Ready and Available (like above).
2525

26+
## Example
27+
28+
```
29+
$ kube-wait-for-multi default,job,some-job default,service,some-service default,pod,some-pod-88bb5f7bb-wx4f7
30+
```
31+
Wait for the job `some-job` to complete, the service `some-service` to have all available pods and the pod `some-pod-88bb5f7bb-wx4f7` to be ready and available.
32+
33+
The program will also wait when a service does not exist yet.
34+
```
35+
$ kube-wait-for-multi default,job,some-job default,job,test default,service,service1 default,service,service2
36+
Starting with namespaces: [default]
37+
Starting informers...
38+
wait status
39+
└── [❔] namespace/default
40+
├── [✅] service/service1: Available
41+
├── [❔] service/service2: Unavailable
42+
├── [✅] job/some-job: Complete
43+
└── [❌] job/test: NotComplete
44+
[... some time later ...]
45+
wait status
46+
└── [✅] namespace/default
47+
```
48+
2649
## Docker
2750

2851
Hosted on Docker Hub: https://hub.docker.com/r/erayan/k8s-wait-for-multi

cmd/root.go

+6-8
Original file line numberDiff line numberDiff line change
@@ -18,7 +18,8 @@ package cmd
1818

1919
import (
2020
"os"
21-
"time"
21+
22+
"github.com/erayan/k8s-wait-for-multi/flags"
2223

2324
"github.com/spf13/cobra"
2425
"k8s.io/cli-runtime/pkg/genericclioptions"
@@ -30,6 +31,7 @@ var (
3031
KubeResourceBuilderFlags *genericclioptions.ResourceBuilderFlags
3132
KubernetesPrintFlags *genericclioptions.PrintFlags
3233
KubernetesGetPrintFlags *kubectlget.PrintFlags
34+
WaitForConfigFlags *flags.ConfigFlags
3335
)
3436

3537
// rootCmd represents the base command when called without any subcommands
@@ -40,7 +42,7 @@ var rootCmd = &cobra.Command{
4042
This is an implementation of k8s-wait-for that allows you to wait for multiple items in one process.
4143
This uses informers to get the status updates for all the items that this application is waiting for.
4244
43-
You can omit the NAMESPACE and KIND, they default to the value of the --namespace flag and pod respectively. Supported string for KIND are service, job and pod.`,
45+
You can omit the NAMESPACE and KIND, they default to the value of the --namespace flag and 'pod' respectively. Supported strings for KIND are service, job and pod.`,
4446
RunE: wait,
4547
Version: version,
4648
}
@@ -57,12 +59,8 @@ func init() {
5759

5860
KubernetesConfigFlags.AddFlags(rootCmd.PersistentFlags())
5961

60-
rootCmd.Flags().BoolP("version", "v", false, "Display version info")
61-
62-
rootCmd.Flags().Bool("no-collapse", false, "Do not collapse the status tree for done subtrees")
63-
64-
rootCmd.Flags().Bool("no-tree", false, "Do not print the status as a tree")
62+
WaitForConfigFlags = flags.NewConfigFlags()
6563

66-
rootCmd.PersistentFlags().DurationP("timeout", "t", time.Duration(600*time.Second), "The length of time to wait before ending watch, zero means never. Any other values should contain a corresponding time unit (e.g. 1s, 2m, 3h). Default is 10 minutes")
64+
WaitForConfigFlags.AddFlags(rootCmd.Flags())
6765

6866
}

cmd/wait.go

+18-44
Original file line numberDiff line numberDiff line change
@@ -44,28 +44,10 @@ var mu sync.Mutex
4444
var waits *pkg.Waitables
4545

4646
func wait(cmd *cobra.Command, args []string) error {
47-
isVersion, err := cmd.Flags().GetBool("version")
48-
49-
if err != nil {
50-
return err
51-
}
52-
53-
if isVersion {
47+
if *WaitForConfigFlags.PrintVersion {
5448
return printVersion(cmd, args)
5549
}
5650

57-
noCollapseTree, err := cmd.Flags().GetBool("no-collapse")
58-
59-
if err != nil {
60-
return err
61-
}
62-
63-
noTree, err := cmd.Flags().GetBool("no-tree")
64-
65-
if err != nil {
66-
return err
67-
}
68-
6951
if len(args) < 1 {
7052
return errors.New("command needs one or more arguments to wait for")
7153
}
@@ -74,7 +56,9 @@ func wait(cmd *cobra.Command, args []string) error {
7456
KubernetesConfigFlags.Namespace = pointer.String("default")
7557
}
7658

77-
waits = pkg.NewWaitables()
59+
waits = pkg.NewWaitables(WaitForConfigFlags)
60+
61+
waits.Start()
7862

7963
timeout, err := cmd.Flags().GetDuration("timeout")
8064

@@ -155,11 +139,7 @@ func wait(cmd *cobra.Command, args []string) error {
155139
return errors.New("not enough arguments")
156140
}
157141

158-
if noTree {
159-
log.Println(waits.GetStatusString())
160-
} else {
161-
log.Println(waits.GetStatusTreeString(noCollapseTree))
162-
}
142+
waits.PrintStatus()
163143

164144
if waits.HasServices() {
165145
svc_informer, err := cc.GetInformerForKind(timeoutCtx, schema.FromAPIVersionAndKind("v1", "Service"))
@@ -169,13 +149,13 @@ func wait(cmd *cobra.Command, args []string) error {
169149

170150
svc_informer.AddEventHandler(toolscache.ResourceEventHandlerFuncs{
171151
AddFunc: func(obj interface{}) {
172-
handleEvent(timeoutCtx, waits.ProcessEventAddService, obj.(*corev1.Service), !noCollapseTree, noTree)
152+
handleEvent(timeoutCtx, waits.ProcessEventAddService, obj.(*corev1.Service))
173153
},
174154
UpdateFunc: func(obj interface{}, newObj interface{}) {
175-
handleEvent(timeoutCtx, waits.ProcessEventUpdateService, newObj.(*corev1.Service), !noCollapseTree, noTree)
155+
handleEvent(timeoutCtx, waits.ProcessEventUpdateService, newObj.(*corev1.Service))
176156
},
177157
DeleteFunc: func(obj interface{}) {
178-
handleEvent(timeoutCtx, waits.ProcessEventDeleteService, obj.(*corev1.Service), !noCollapseTree, noTree)
158+
handleEvent(timeoutCtx, waits.ProcessEventDeleteService, obj.(*corev1.Service))
179159
},
180160
})
181161
}
@@ -188,13 +168,13 @@ func wait(cmd *cobra.Command, args []string) error {
188168

189169
pod_informer.AddEventHandler(toolscache.ResourceEventHandlerFuncs{
190170
AddFunc: func(obj interface{}) {
191-
handleEvent(timeoutCtx, waits.ProcessEventAddPod, obj.(*corev1.Pod), !noCollapseTree, noTree)
171+
handleEvent(timeoutCtx, waits.ProcessEventAddPod, obj.(*corev1.Pod))
192172
},
193173
UpdateFunc: func(obj interface{}, newObj interface{}) {
194-
handleEvent(timeoutCtx, waits.ProcessEventUpdatePod, newObj.(*corev1.Pod), !noCollapseTree, noTree)
174+
handleEvent(timeoutCtx, waits.ProcessEventUpdatePod, newObj.(*corev1.Pod))
195175
},
196176
DeleteFunc: func(obj interface{}) {
197-
handleEvent(timeoutCtx, waits.ProcessEventDeletePod, obj.(*corev1.Pod), !noCollapseTree, noTree)
177+
handleEvent(timeoutCtx, waits.ProcessEventDeletePod, obj.(*corev1.Pod))
198178
},
199179
})
200180
}
@@ -207,34 +187,32 @@ func wait(cmd *cobra.Command, args []string) error {
207187

208188
job_informer.AddEventHandler(toolscache.ResourceEventHandlerFuncs{
209189
AddFunc: func(obj interface{}) {
210-
handleEvent(timeoutCtx, waits.ProcessEventAddJob, obj.(*batchv1.Job), !noCollapseTree, noTree)
190+
handleEvent(timeoutCtx, waits.ProcessEventAddJob, obj.(*batchv1.Job))
211191
},
212192
UpdateFunc: func(obj interface{}, newObj interface{}) {
213-
handleEvent(timeoutCtx, waits.ProcessEventUpdateJob, newObj.(*batchv1.Job), !noCollapseTree, noTree)
193+
handleEvent(timeoutCtx, waits.ProcessEventUpdateJob, newObj.(*batchv1.Job))
214194
},
215195
DeleteFunc: func(obj interface{}) {
216-
handleEvent(timeoutCtx, waits.ProcessEventDeleteJob, obj.(*batchv1.Job), !noCollapseTree, noTree)
196+
handleEvent(timeoutCtx, waits.ProcessEventDeleteJob, obj.(*batchv1.Job))
217197
},
218198
})
219199
}
220200

221-
log.Printf("Starting informers...")
222-
223201
err = cc.Start(timeoutCtx)
224202
if err != nil {
225203
return err
226204
}
227-
log.Printf("Shutdown informers.")
205+
206+
waits.Done()
228207

229208
return nil
230209
}
231210

232211
func processCompletion() {
233-
log.Printf("All items have completed or are ready")
234212
cancelFn()
235213
}
236214

237-
func handleEvent[V *corev1.Pod | *corev1.Service | *batchv1.Job](ctx context.Context, f func(ctx context.Context, obj V) (bool, error), obj V, collapseTree bool, noTree bool) {
215+
func handleEvent[V *corev1.Pod | *corev1.Service | *batchv1.Job](ctx context.Context, f func(ctx context.Context, obj V) (bool, error), obj V) {
238216
mu.Lock()
239217
defer mu.Unlock()
240218

@@ -244,11 +222,7 @@ func handleEvent[V *corev1.Pod | *corev1.Service | *batchv1.Job](ctx context.Con
244222
}
245223

246224
if matches {
247-
if noTree {
248-
log.Println(waits.GetStatusString())
249-
} else {
250-
log.Println(waits.GetStatusTreeString(collapseTree))
251-
}
225+
waits.PrintStatus()
252226

253227
if waits.IsDone() {
254228
processCompletion()

flags/flags.go

+61
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,61 @@
1+
/*
2+
* Copyright 2023 The k8s-wait-for-multi authors.
3+
*
4+
* Licensed under the Apache License, Version 2.0 (the "License");
5+
* you may not use this file except in compliance with the License.
6+
* You may obtain a copy of the License at
7+
*
8+
* http://www.apache.org/licenses/LICENSE-2.0
9+
*
10+
* Unless required by applicable law or agreed to in writing, software
11+
* distributed under the License is distributed on an "AS IS" BASIS,
12+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
* See the License for the specific language governing permissions and
14+
* limitations under the License.
15+
*/
16+
17+
package flags
18+
19+
import (
20+
"time"
21+
22+
"github.com/spf13/pflag"
23+
24+
utilpointer "k8s.io/utils/pointer"
25+
)
26+
27+
type ConfigFlags struct {
28+
PrintVersion *bool
29+
PrintTree *bool
30+
PrintCollapsedTree *bool
31+
32+
Timeout *time.Duration
33+
}
34+
35+
func NewConfigFlags() *ConfigFlags {
36+
return &ConfigFlags{
37+
PrintVersion: utilpointer.Bool(false),
38+
PrintTree: utilpointer.Bool(true),
39+
PrintCollapsedTree: utilpointer.Bool(true),
40+
41+
Timeout: utilpointer.Duration(time.Duration(600 * time.Second)),
42+
}
43+
}
44+
45+
func (f *ConfigFlags) AddFlags(flags *pflag.FlagSet) {
46+
if f.Timeout != nil {
47+
flags.DurationVarP(f.Timeout, "timeout", "t", *f.Timeout, "The length of time to wait before ending watch, zero means never. Any other values should contain a corresponding time unit (e.g. 1s, 2m, 3h)")
48+
}
49+
50+
if f.PrintVersion != nil {
51+
flags.BoolVarP(f.PrintVersion, "version", "v", *f.PrintVersion, "Display version info")
52+
}
53+
54+
if f.PrintTree != nil {
55+
flags.BoolVar(f.PrintTree, "print-tree", *f.PrintTree, "Print the status as a tree")
56+
}
57+
58+
if f.PrintCollapsedTree != nil {
59+
flags.BoolVar(f.PrintCollapsedTree, "print-collapsed-tree", *f.PrintCollapsedTree, "Collapse the status tree for done subtrees")
60+
}
61+
}

kube/client.go

-94
This file was deleted.

pkg/utils.go

+23
Original file line numberDiff line numberDiff line change
@@ -34,3 +34,26 @@ func metaInSlice(a treeprint.MetaValue, list []treeprint.MetaValue) bool {
3434
}
3535
return false
3636
}
37+
38+
func getNodesStatus(item *treeprint.Node, collapseTree bool) treeprint.MetaValue {
39+
if len(item.Nodes) > 0 && item.Meta == TreeStatusUnknown {
40+
status := []treeprint.MetaValue{}
41+
42+
for _, node := range item.Nodes {
43+
status = append(status, getNodesStatus(node, collapseTree))
44+
}
45+
if metaInSlice(TreeStatusUnknown, status) {
46+
item.Meta = TreeStatusUnknown
47+
} else if metaInSlice(TreeStatusNotDone, status) {
48+
item.Meta = TreeStatusNotDone
49+
} else {
50+
if collapseTree {
51+
item.Nodes = nil
52+
}
53+
item.Meta = TreeStatusDone
54+
}
55+
// branch nodes
56+
}
57+
58+
return item.Meta
59+
}

0 commit comments

Comments
 (0)