@@ -3,6 +3,8 @@ package actions
33import (
44 "context"
55 "fmt"
6+ "os"
7+ "strings"
68 "time"
79
810 "github.com/OpenSlides/openslides-cli/internal/constants"
@@ -31,7 +33,15 @@ func getHealthStatus(ctx context.Context, k8sClient *client.Client, namespace st
3133 return nil , fmt .Errorf ("listing pods: %w" , err )
3234 }
3335
34- total := len (pods .Items )
36+ var filteredPods []corev1.Pod
37+ for _ , pod := range pods .Items {
38+ if pod .Status .Phase == corev1 .PodSucceeded {
39+ continue
40+ }
41+ filteredPods = append (filteredPods , pod )
42+ }
43+
44+ total := len (filteredPods )
3545 if total == 0 {
3646 return & HealthStatus {
3747 Healthy : false ,
@@ -42,17 +52,19 @@ func getHealthStatus(ctx context.Context, k8sClient *client.Client, namespace st
4252 }
4353
4454 ready := 0
45- for _ , pod := range pods . Items {
55+ for _ , pod := range filteredPods {
4656 if isPodReady (& pod ) {
4757 ready ++
4858 }
4959 }
5060
61+ healthy := ready == total
62+
5163 return & HealthStatus {
52- Healthy : ready == total ,
64+ Healthy : healthy ,
5365 Ready : ready ,
5466 Total : total ,
55- Pods : pods . Items ,
67+ Pods : filteredPods ,
5668 }, nil
5769}
5870
@@ -97,8 +109,6 @@ func checkHealth(ctx context.Context, k8sClient *client.Client, namespace string
97109
98110// waitForInstanceHealthy waits for instance to become healthy
99111func waitForInstanceHealthy (ctx context.Context , k8sClient * client.Client , namespace string , timeout time.Duration ) error {
100- logger .Info ("Waiting for instance to become healthy (timeout: %v)" , timeout )
101-
102112 ticker := time .NewTicker (constants .TickerDuration )
103113 defer ticker .Stop ()
104114
@@ -119,17 +129,27 @@ func waitForInstanceHealthy(ctx context.Context, k8sClient *client.Client, names
119129 lastStatus = status
120130
121131 if bar == nil && status .Total > 0 {
122- bar = createProgressBar (status .Total , "Pods ready" )
132+ bar = createProgressBar (status .Total , "Pods ready" , constants .AddDetailLineBuffer )
133+ } else if bar != nil {
134+ bar .ChangeMax (status .Total )
123135 }
124-
125- if bar != nil {
136+ if bar != nil && ! bar .IsFinished () {
137+ notReady := getNotReadyNames (status .Pods )
138+ if len (notReady ) > 0 {
139+ if err := bar .AddDetail (fmt .Sprintf ("%s Pending: %s" , constants .IconNotReady , strings .Join (notReady , ", " ))); err != nil {
140+ return fmt .Errorf ("adding pending pods detail: %w" , err )
141+ }
142+ } else {
143+ if err := bar .AddDetail ("" ); err != nil {
144+ return fmt .Errorf ("adding empty detail: %w" , err )
145+ }
146+ }
126147 if err := bar .Set (status .Ready ); err != nil {
127148 return fmt .Errorf ("setting progress bar: %w" , err )
128149 }
129150 }
130-
131151 if status .Healthy {
132- if bar != nil {
152+ if bar != nil && ! bar . IsFinished () {
133153 if err := bar .Finish (); err != nil {
134154 return fmt .Errorf ("finishing progress bar: %w" , err )
135155 }
@@ -139,7 +159,7 @@ func waitForInstanceHealthy(ctx context.Context, k8sClient *client.Client, names
139159 }
140160
141161 case <- timeoutCtx .Done ():
142- if bar != nil {
162+ if bar != nil && ! bar . IsFinished () {
143163 if err := bar .Finish (); err != nil {
144164 return fmt .Errorf ("finishing progress bar: %w" , err )
145165 }
@@ -153,32 +173,64 @@ func waitForInstanceHealthy(ctx context.Context, k8sClient *client.Client, names
153173 }
154174}
155175
156- func createProgressBar (max int , description string ) * progressbar.ProgressBar {
157- return progressbar .NewOptions ( max ,
176+ func createProgressBar (max int , description string , maxDetailRow int ) * progressbar.ProgressBar {
177+ opts := [] progressbar.Option {
158178 progressbar .OptionSetDescription (description ),
159179 progressbar .OptionSetWidth (constants .ProgressBarWidth ),
160- progressbar .OptionShowCount (),
180+ progressbar .OptionSetWriter (os .Stdout ),
181+ progressbar .OptionSetMaxDetailRow (maxDetailRow ),
161182 progressbar .OptionSetTheme (progressbar.Theme {
162183 Saucer : constants .Saucer ,
163184 SaucerPadding : constants .SaucerPadding ,
164185 BarStart : constants .BarStart ,
165186 BarEnd : constants .BarEnd ,
166187 }),
167188 progressbar .OptionThrottle (constants .ThrottleDuration ),
168- progressbar .OptionClearOnFinish (),
169- )
189+ progressbar .OptionOnCompletion (func () {
190+ fmt .Println ()
191+ }),
192+ }
193+
194+ if max > 0 {
195+ opts = append (opts , progressbar .OptionShowCount ())
196+ } else {
197+ opts = append (opts , progressbar .OptionSpinnerType (constants .SpinnerType ))
198+ }
199+
200+ return progressbar .NewOptions (max , opts ... )
170201}
171202
172203// isPodReady checks if a pod is ready
173204func isPodReady (pod * corev1.Pod ) bool {
205+ if pod .DeletionTimestamp != nil {
206+ return false
207+ }
208+
174209 for _ , condition := range pod .Status .Conditions {
175- if condition .Type == corev1 .PodReady {
176- return condition .Status == corev1 .ConditionTrue
210+ if condition .Type == corev1 .PodReady && condition .Status == corev1 .ConditionTrue {
211+ for _ , container := range pod .Status .ContainerStatuses {
212+ if ! container .Ready {
213+ return false
214+ }
215+ }
216+ return true
177217 }
178218 }
219+
179220 return false
180221}
181222
223+ // getNotReadyNames
224+ func getNotReadyNames (pods []corev1.Pod ) []string {
225+ var names []string
226+ for _ , pod := range pods {
227+ if ! isPodReady (& pod ) {
228+ names = append (names , pod .Name )
229+ }
230+ }
231+ return names
232+ }
233+
182234// namespaceIsActive checks if a namespace exists and is active
183235func namespaceIsActive (ctx context.Context , k8sClient * client.Client , namespace string ) (bool , error ) {
184236 ns , err := k8sClient .Clientset ().CoreV1 ().Namespaces ().Get (ctx , namespace , metav1.GetOptions {})
@@ -229,6 +281,8 @@ func waitForDeploymentReady(ctx context.Context, k8sClient *client.Client, names
229281 defer cancel ()
230282
231283 var lastDeployment * appsv1.Deployment
284+ var bar * progressbar.ProgressBar
285+
232286 for {
233287 select {
234288 case <- ticker .C :
@@ -240,56 +294,92 @@ func waitForDeploymentReady(ctx context.Context, k8sClient *client.Client, names
240294
241295 lastDeployment = deployment
242296
243- if deployment .Status .ObservedGeneration >= deployment .Generation &&
244- deployment .Status .UpdatedReplicas == * deployment .Spec .Replicas &&
245- deployment .Status .AvailableReplicas == * deployment .Spec .Replicas &&
246- deployment .Status .ReadyReplicas == * deployment .Spec .Replicas &&
247- deployment .Status .Replicas == * deployment .Spec .Replicas {
297+ desired := int (* deployment .Spec .Replicas )
298+ updated := int (deployment .Status .UpdatedReplicas )
299+ ready := int (deployment .Status .ReadyReplicas )
300+ available := int (deployment .Status .AvailableReplicas )
301+ total := int (deployment .Status .Replicas )
302+ observedGen := deployment .Status .ObservedGeneration
303+ gen := deployment .Generation
248304
249- logger .Info ("Deployment %s is ready with %d replicas" , deploymentName , * deployment .Spec .Replicas )
305+ if bar == nil && desired > 0 {
306+ bar = createProgressBar (- 1 , fmt .Sprintf ("Waiting for %s deployment rollout" , deploymentName ), 0 )
307+ }
308+
309+ if bar != nil {
310+ _ = bar .Add (1 )
311+ }
312+
313+ if observedGen >= gen &&
314+ updated == desired &&
315+ available == desired &&
316+ ready == desired &&
317+ total == desired {
318+ if bar != nil {
319+ if err := bar .Finish (); err != nil {
320+ return fmt .Errorf ("finishing progress bar: %w" , err )
321+ }
322+ }
323+ logger .Info ("Deployment %s is ready with %d replicas" , deploymentName , desired )
250324 return nil
251325 }
252326
253- logger .Debug ("Deployment %s: %d/%d replicas ready, %d total (generation: %d/%d)" ,
327+ logger .Debug ("Deployment %s: %d/%d updated, %d/%d ready, %d total (generation: %d/%d)" ,
254328 deploymentName ,
255- deployment .Status .ReadyReplicas ,
256- * deployment .Spec .Replicas ,
257- deployment .Status .Replicas ,
258- deployment .Status .ObservedGeneration ,
259- deployment .Generation )
329+ updated , desired ,
330+ ready , desired ,
331+ total ,
332+ observedGen , gen )
260333
261334 case <- timeoutCtx .Done ():
335+ if bar != nil {
336+ if err := bar .Finish (); err != nil {
337+ return fmt .Errorf ("finishing progress bar: %w" , err )
338+ }
339+ }
262340 logger .Warn ("Timeout reached. Deployment status:" )
263341 if lastDeployment != nil {
264342 printDeploymentStatus (namespace , deploymentName , lastDeployment )
265343 }
266344
267- return fmt .Errorf ("timeout waiting for deployment %s to become ready " , deploymentName )
345+ return fmt .Errorf ("timeout waiting for deployment %s rollout " , deploymentName )
268346 }
269347 }
270348}
271349
272350// waitForNamespaceDeletion waits for a namespace to be completely deleted
273351func waitForNamespaceDeletion (ctx context.Context , k8sClient * client.Client , namespace string , timeout time.Duration ) error {
274352 clientset := k8sClient .Clientset ()
275-
276353 ticker := time .NewTicker (constants .TickerDuration )
277354 defer ticker .Stop ()
278355
279356 timeoutCtx , cancel := context .WithTimeout (ctx , timeout )
280357 defer cancel ()
281358
359+ bar := createProgressBar (- 1 , fmt .Sprintf ("Stopping %s" , namespace ), 0 )
360+
282361 for {
283362 select {
284363 case <- ticker .C :
364+ _ = bar .Add (1 )
285365 _ , err := clientset .CoreV1 ().Namespaces ().Get (ctx , namespace , metav1.GetOptions {})
286366 if err != nil {
367+ if ! errors .IsNotFound (err ) {
368+ logger .Warn ("Error checking namespace: %v" , err )
369+ continue
370+ }
371+ if err := bar .Finish (); err != nil {
372+ return fmt .Errorf ("finishing progress bar: %w" , err )
373+ }
287374 logger .Debug ("Namespace %s successfully deleted" , namespace )
288375 return nil
289376 }
290377 logger .Debug ("Namespace %s still terminating..." , namespace )
291378
292379 case <- timeoutCtx .Done ():
380+ if err := bar .Finish (); err != nil {
381+ return fmt .Errorf ("finishing progress bar: %w" , err )
382+ }
293383 return fmt .Errorf ("timeout waiting for namespace %s to be deleted" , namespace )
294384 }
295385 }
0 commit comments