| 
									
										
										
										
											2017-04-11 13:47:10 +08:00
										 |  |  | /* | 
					
						
							| 
									
										
										
										
											2018-08-25 03:03:55 +08:00
										 |  |  | Copyright The Helm Authors. | 
					
						
							| 
									
										
										
										
											2017-04-11 13:47:10 +08:00
										 |  |  | 
 | 
					
						
							|  |  |  | Licensed under the Apache License, Version 2.0 (the "License"); | 
					
						
							|  |  |  | you may not use this file except in compliance with the License. | 
					
						
							|  |  |  | You may obtain a copy of the License at | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     http://www.apache.org/licenses/LICENSE-2.0
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | Unless required by applicable law or agreed to in writing, software | 
					
						
							|  |  |  | distributed under the License is distributed on an "AS IS" BASIS, | 
					
						
							|  |  |  | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | 
					
						
							|  |  |  | See the License for the specific language governing permissions and | 
					
						
							|  |  |  | limitations under the License. | 
					
						
							|  |  |  | */ | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2024-12-27 05:33:51 +08:00
										 |  |  | package kube // import "helm.sh/helm/v4/pkg/kube"
 | 
					
						
							| 
									
										
										
										
											2017-04-11 13:47:10 +08:00
										 |  |  | 
 | 
					
						
							|  |  |  | import ( | 
					
						
							| 
									
										
										
										
											2020-04-13 23:40:38 +08:00
										 |  |  | 	"context" | 
					
						
							| 
									
										
										
										
											2019-05-16 03:31:47 +08:00
										 |  |  | 	"fmt" | 
					
						
							| 
									
										
										
										
											2025-03-21 16:54:44 +08:00
										 |  |  | 	"log/slog" | 
					
						
							| 
									
										
										
										
											2023-07-14 00:46:03 +08:00
										 |  |  | 	"net/http" | 
					
						
							| 
									
										
										
										
											2017-04-11 13:47:10 +08:00
										 |  |  | 	"time" | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2018-01-06 04:30:26 +08:00
										 |  |  | 	appsv1 "k8s.io/api/apps/v1" | 
					
						
							| 
									
										
										
										
											2017-10-12 06:05:27 +08:00
										 |  |  | 	appsv1beta1 "k8s.io/api/apps/v1beta1" | 
					
						
							|  |  |  | 	appsv1beta2 "k8s.io/api/apps/v1beta2" | 
					
						
							| 
									
										
										
										
											2019-05-16 03:31:47 +08:00
										 |  |  | 	batchv1 "k8s.io/api/batch/v1" | 
					
						
							|  |  |  | 	corev1 "k8s.io/api/core/v1" | 
					
						
							|  |  |  | 	extensionsv1beta1 "k8s.io/api/extensions/v1beta1" | 
					
						
							| 
									
										
										
										
											2021-05-16 01:40:19 +08:00
										 |  |  | 	apierrors "k8s.io/apimachinery/pkg/api/errors" | 
					
						
							| 
									
										
										
										
											2017-04-11 13:47:10 +08:00
										 |  |  | 	metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" | 
					
						
							| 
									
										
										
										
											2025-02-07 23:14:25 +08:00
										 |  |  | 	"k8s.io/apimachinery/pkg/apis/meta/v1/unstructured" | 
					
						
							|  |  |  | 	"k8s.io/apimachinery/pkg/fields" | 
					
						
							| 
									
										
										
										
											2017-04-11 13:47:10 +08:00
										 |  |  | 	"k8s.io/apimachinery/pkg/labels" | 
					
						
							| 
									
										
										
										
											2019-05-16 03:31:47 +08:00
										 |  |  | 	"k8s.io/apimachinery/pkg/runtime" | 
					
						
							| 
									
										
										
										
											2025-02-07 23:14:25 +08:00
										 |  |  | 	"k8s.io/apimachinery/pkg/watch" | 
					
						
							| 
									
										
										
										
											2023-02-03 22:02:12 +08:00
										 |  |  | 	"k8s.io/cli-runtime/pkg/resource" | 
					
						
							| 
									
										
										
										
											2025-01-05 22:45:18 +08:00
										 |  |  | 	"k8s.io/client-go/kubernetes" | 
					
						
							| 
									
										
										
										
											2025-02-07 23:14:25 +08:00
										 |  |  | 	cachetools "k8s.io/client-go/tools/cache" | 
					
						
							|  |  |  | 	watchtools "k8s.io/client-go/tools/watch" | 
					
						
							| 
									
										
										
										
											2022-10-12 07:53:14 +08:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2021-02-27 04:10:26 +08:00
										 |  |  | 	"k8s.io/apimachinery/pkg/util/wait" | 
					
						
							| 
									
										
										
										
											2017-04-11 13:47:10 +08:00
										 |  |  | ) | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2025-03-07 22:37:04 +08:00
										 |  |  | // legacyWaiter is the legacy implementation of the Waiter interface. This logic was used by default in Helm 3
 | 
					
						
							| 
									
										
										
										
											2025-01-07 01:13:59 +08:00
										 |  |  | // Helm 4 now uses the StatusWaiter implementation instead
 | 
					
						
							| 
									
										
										
										
											2025-03-07 22:37:04 +08:00
										 |  |  | type legacyWaiter struct { | 
					
						
							| 
									
										
										
										
											2025-01-05 22:45:18 +08:00
										 |  |  | 	c          ReadyChecker | 
					
						
							|  |  |  | 	kubeClient *kubernetes.Clientset | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2025-03-07 22:37:04 +08:00
										 |  |  | func (hw *legacyWaiter) Wait(resources ResourceList, timeout time.Duration) error { | 
					
						
							| 
									
										
										
										
											2025-04-10 21:06:03 +08:00
										 |  |  | 	hw.c = NewReadyChecker(hw.kubeClient, PausedAsReady(true)) | 
					
						
							| 
									
										
										
										
											2025-02-10 23:31:43 +08:00
										 |  |  | 	return hw.waitForResources(resources, timeout) | 
					
						
							| 
									
										
										
										
											2024-12-27 00:09:54 +08:00
										 |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2025-03-07 22:37:04 +08:00
										 |  |  | func (hw *legacyWaiter) WaitWithJobs(resources ResourceList, timeout time.Duration) error { | 
					
						
							| 
									
										
										
										
											2025-04-10 21:06:03 +08:00
										 |  |  | 	hw.c = NewReadyChecker(hw.kubeClient, PausedAsReady(true), CheckJobs(true)) | 
					
						
							| 
									
										
										
										
											2025-02-10 23:31:43 +08:00
										 |  |  | 	return hw.waitForResources(resources, timeout) | 
					
						
							| 
									
										
										
										
											2019-04-25 08:18:42 +08:00
										 |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2023-07-14 00:46:03 +08:00
										 |  |  | // waitForResources polls to get the current status of all pods, PVCs, Services and
 | 
					
						
							|  |  |  | // Jobs(optional) until all are ready or a timeout is reached
 | 
					
						
							| 
									
										
										
										
											2025-03-07 22:37:04 +08:00
										 |  |  | func (hw *legacyWaiter) waitForResources(created ResourceList, timeout time.Duration) error { | 
					
						
							| 
									
										
										
										
											2025-04-10 21:06:03 +08:00
										 |  |  | 	slog.Debug("beginning wait for resources", "count", len(created), "timeout", timeout) | 
					
						
							| 
									
										
										
										
											2017-04-12 13:53:34 +08:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2025-01-06 22:12:34 +08:00
										 |  |  | 	ctx, cancel := context.WithTimeout(context.Background(), timeout) | 
					
						
							| 
									
										
										
										
											2021-02-27 04:10:26 +08:00
										 |  |  | 	defer cancel() | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2023-02-03 22:02:12 +08:00
										 |  |  | 	numberOfErrors := make([]int, len(created)) | 
					
						
							|  |  |  | 	for i := range numberOfErrors { | 
					
						
							|  |  |  | 		numberOfErrors[i] = 0 | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2023-04-13 16:48:42 +08:00
										 |  |  | 	return wait.PollUntilContextCancel(ctx, 2*time.Second, true, func(ctx context.Context) (bool, error) { | 
					
						
							| 
									
										
										
										
											2023-07-14 00:46:03 +08:00
										 |  |  | 		waitRetries := 30 | 
					
						
							| 
									
										
										
										
											2023-02-03 22:02:12 +08:00
										 |  |  | 		for i, v := range created { | 
					
						
							| 
									
										
										
										
											2025-02-10 23:31:43 +08:00
										 |  |  | 			ready, err := hw.c.IsReady(ctx, v) | 
					
						
							| 
									
										
										
										
											2023-02-03 22:02:12 +08:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2025-02-10 23:31:43 +08:00
										 |  |  | 			if waitRetries > 0 && hw.isRetryableError(err, v) { | 
					
						
							| 
									
										
										
										
											2023-02-03 22:02:12 +08:00
										 |  |  | 				numberOfErrors[i]++ | 
					
						
							|  |  |  | 				if numberOfErrors[i] > waitRetries { | 
					
						
							| 
									
										
										
										
											2025-04-10 21:06:03 +08:00
										 |  |  | 					slog.Debug("max number of retries reached", "resource", v.Name, "retries", numberOfErrors[i]) | 
					
						
							| 
									
										
										
										
											2023-02-03 22:02:12 +08:00
										 |  |  | 					return false, err | 
					
						
							|  |  |  | 				} | 
					
						
							| 
									
										
										
										
											2025-04-10 21:06:03 +08:00
										 |  |  | 				slog.Debug("retrying resource readiness", "resource", v.Name, "currentRetries", numberOfErrors[i]-1, "maxRetries", waitRetries) | 
					
						
							| 
									
										
										
										
											2023-02-03 22:02:12 +08:00
										 |  |  | 				return false, nil | 
					
						
							|  |  |  | 			} | 
					
						
							|  |  |  | 			numberOfErrors[i] = 0 | 
					
						
							|  |  |  | 			if !ready { | 
					
						
							| 
									
										
										
										
											2019-05-16 03:31:47 +08:00
										 |  |  | 				return false, err | 
					
						
							| 
									
										
										
										
											2017-04-11 13:47:10 +08:00
										 |  |  | 			} | 
					
						
							|  |  |  | 		} | 
					
						
							| 
									
										
										
										
											2019-05-16 03:31:47 +08:00
										 |  |  | 		return true, nil | 
					
						
							| 
									
										
										
										
											2023-04-13 16:48:42 +08:00
										 |  |  | 	}) | 
					
						
							| 
									
										
										
										
											2017-04-11 13:47:10 +08:00
										 |  |  | } | 
					
						
							| 
									
										
										
										
											2019-05-16 03:31:47 +08:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2025-03-07 22:37:04 +08:00
										 |  |  | func (hw *legacyWaiter) isRetryableError(err error, resource *resource.Info) bool { | 
					
						
							| 
									
										
										
										
											2023-02-03 22:02:12 +08:00
										 |  |  | 	if err == nil { | 
					
						
							|  |  |  | 		return false | 
					
						
							|  |  |  | 	} | 
					
						
							| 
									
										
										
										
											2025-04-10 21:06:03 +08:00
										 |  |  | 	slog.Debug("error received when checking resource status", "resource", resource.Name, slog.Any("error", err)) | 
					
						
							| 
									
										
										
										
											2023-02-03 22:02:12 +08:00
										 |  |  | 	if ev, ok := err.(*apierrors.StatusError); ok { | 
					
						
							|  |  |  | 		statusCode := ev.Status().Code | 
					
						
							| 
									
										
										
										
											2025-02-10 23:31:43 +08:00
										 |  |  | 		retryable := hw.isRetryableHTTPStatusCode(statusCode) | 
					
						
							| 
									
										
										
										
											2025-04-10 21:06:03 +08:00
										 |  |  | 		slog.Debug("status code received", "resource", resource.Name, "statusCode", statusCode, "retryable", retryable) | 
					
						
							| 
									
										
										
										
											2023-02-03 22:02:12 +08:00
										 |  |  | 		return retryable | 
					
						
							|  |  |  | 	} | 
					
						
							| 
									
										
										
										
											2025-04-10 21:06:03 +08:00
										 |  |  | 	slog.Debug("retryable error assumed", "resource", resource.Name) | 
					
						
							| 
									
										
										
										
											2023-02-03 22:02:12 +08:00
										 |  |  | 	return true | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2025-03-07 22:37:04 +08:00
										 |  |  | func (hw *legacyWaiter) isRetryableHTTPStatusCode(httpStatusCode int32) bool { | 
					
						
							| 
									
										
										
										
											2023-07-14 00:46:03 +08:00
										 |  |  | 	return httpStatusCode == 0 || httpStatusCode == http.StatusTooManyRequests || (httpStatusCode >= 500 && httpStatusCode != http.StatusNotImplemented) | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2025-06-24 15:48:08 +08:00
										 |  |  | // WaitForDelete polls to check if all the resources are deleted or a timeout is reached
 | 
					
						
							| 
									
										
										
										
											2025-03-07 22:37:04 +08:00
										 |  |  | func (hw *legacyWaiter) WaitForDelete(deleted ResourceList, timeout time.Duration) error { | 
					
						
							| 
									
										
										
										
											2025-04-10 21:06:03 +08:00
										 |  |  | 	slog.Debug("beginning wait for resources to be deleted", "count", len(deleted), "timeout", timeout) | 
					
						
							| 
									
										
										
										
											2021-05-16 01:40:19 +08:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2025-03-21 16:54:44 +08:00
										 |  |  | 	startTime := time.Now() | 
					
						
							| 
									
										
										
										
											2025-01-06 22:12:34 +08:00
										 |  |  | 	ctx, cancel := context.WithTimeout(context.Background(), timeout) | 
					
						
							| 
									
										
										
										
											2021-05-16 01:40:19 +08:00
										 |  |  | 	defer cancel() | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2025-03-23 22:38:59 +08:00
										 |  |  | 	err := wait.PollUntilContextCancel(ctx, 2*time.Second, true, func(_ context.Context) (bool, error) { | 
					
						
							| 
									
										
										
										
											2021-05-16 01:40:19 +08:00
										 |  |  | 		for _, v := range deleted { | 
					
						
							|  |  |  | 			err := v.Get() | 
					
						
							|  |  |  | 			if err == nil || !apierrors.IsNotFound(err) { | 
					
						
							|  |  |  | 				return false, err | 
					
						
							|  |  |  | 			} | 
					
						
							|  |  |  | 		} | 
					
						
							|  |  |  | 		return true, nil | 
					
						
							| 
									
										
										
										
											2023-04-13 16:48:42 +08:00
										 |  |  | 	}) | 
					
						
							| 
									
										
										
										
											2025-03-21 16:54:44 +08:00
										 |  |  | 
 | 
					
						
							|  |  |  | 	elapsed := time.Since(startTime).Round(time.Second) | 
					
						
							|  |  |  | 	if err != nil { | 
					
						
							| 
									
										
										
										
											2025-04-10 21:06:03 +08:00
										 |  |  | 		slog.Debug("wait for resources failed", "elapsed", elapsed, slog.Any("error", err)) | 
					
						
							| 
									
										
										
										
											2025-03-21 16:54:44 +08:00
										 |  |  | 	} else { | 
					
						
							| 
									
										
										
										
											2025-04-10 21:06:03 +08:00
										 |  |  | 		slog.Debug("wait for resources succeeded", "elapsed", elapsed) | 
					
						
							| 
									
										
										
										
											2025-03-21 16:54:44 +08:00
										 |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	return err | 
					
						
							| 
									
										
										
										
											2021-05-16 01:40:19 +08:00
										 |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2019-07-25 04:24:32 +08:00
										 |  |  | // SelectorsForObject returns the pod label selector for a given object
 | 
					
						
							| 
									
										
										
										
											2019-05-16 03:31:47 +08:00
										 |  |  | //
 | 
					
						
							|  |  |  | // Modified version of https://github.com/kubernetes/kubernetes/blob/v1.14.1/pkg/kubectl/polymorphichelpers/helpers.go#L84
 | 
					
						
							| 
									
										
										
										
											2019-07-25 04:24:32 +08:00
										 |  |  | func SelectorsForObject(object runtime.Object) (selector labels.Selector, err error) { | 
					
						
							| 
									
										
										
										
											2019-05-16 03:31:47 +08:00
										 |  |  | 	switch t := object.(type) { | 
					
						
							|  |  |  | 	case *extensionsv1beta1.ReplicaSet: | 
					
						
							|  |  |  | 		selector, err = metav1.LabelSelectorAsSelector(t.Spec.Selector) | 
					
						
							|  |  |  | 	case *appsv1.ReplicaSet: | 
					
						
							|  |  |  | 		selector, err = metav1.LabelSelectorAsSelector(t.Spec.Selector) | 
					
						
							|  |  |  | 	case *appsv1beta2.ReplicaSet: | 
					
						
							|  |  |  | 		selector, err = metav1.LabelSelectorAsSelector(t.Spec.Selector) | 
					
						
							|  |  |  | 	case *corev1.ReplicationController: | 
					
						
							|  |  |  | 		selector = labels.SelectorFromSet(t.Spec.Selector) | 
					
						
							|  |  |  | 	case *appsv1.StatefulSet: | 
					
						
							|  |  |  | 		selector, err = metav1.LabelSelectorAsSelector(t.Spec.Selector) | 
					
						
							|  |  |  | 	case *appsv1beta1.StatefulSet: | 
					
						
							|  |  |  | 		selector, err = metav1.LabelSelectorAsSelector(t.Spec.Selector) | 
					
						
							|  |  |  | 	case *appsv1beta2.StatefulSet: | 
					
						
							|  |  |  | 		selector, err = metav1.LabelSelectorAsSelector(t.Spec.Selector) | 
					
						
							|  |  |  | 	case *extensionsv1beta1.DaemonSet: | 
					
						
							|  |  |  | 		selector, err = metav1.LabelSelectorAsSelector(t.Spec.Selector) | 
					
						
							|  |  |  | 	case *appsv1.DaemonSet: | 
					
						
							|  |  |  | 		selector, err = metav1.LabelSelectorAsSelector(t.Spec.Selector) | 
					
						
							|  |  |  | 	case *appsv1beta2.DaemonSet: | 
					
						
							|  |  |  | 		selector, err = metav1.LabelSelectorAsSelector(t.Spec.Selector) | 
					
						
							|  |  |  | 	case *extensionsv1beta1.Deployment: | 
					
						
							|  |  |  | 		selector, err = metav1.LabelSelectorAsSelector(t.Spec.Selector) | 
					
						
							|  |  |  | 	case *appsv1.Deployment: | 
					
						
							|  |  |  | 		selector, err = metav1.LabelSelectorAsSelector(t.Spec.Selector) | 
					
						
							|  |  |  | 	case *appsv1beta1.Deployment: | 
					
						
							|  |  |  | 		selector, err = metav1.LabelSelectorAsSelector(t.Spec.Selector) | 
					
						
							|  |  |  | 	case *appsv1beta2.Deployment: | 
					
						
							|  |  |  | 		selector, err = metav1.LabelSelectorAsSelector(t.Spec.Selector) | 
					
						
							|  |  |  | 	case *batchv1.Job: | 
					
						
							|  |  |  | 		selector, err = metav1.LabelSelectorAsSelector(t.Spec.Selector) | 
					
						
							|  |  |  | 	case *corev1.Service: | 
					
						
							| 
									
										
										
										
											2024-12-19 23:27:38 +08:00
										 |  |  | 		if len(t.Spec.Selector) == 0 { | 
					
						
							| 
									
										
										
										
											2019-05-16 03:31:47 +08:00
										 |  |  | 			return nil, fmt.Errorf("invalid service '%s': Service is defined without a selector", t.Name) | 
					
						
							|  |  |  | 		} | 
					
						
							|  |  |  | 		selector = labels.SelectorFromSet(t.Spec.Selector) | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	default: | 
					
						
							|  |  |  | 		return nil, fmt.Errorf("selector for %T not implemented", object) | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2024-11-19 01:00:28 +08:00
										 |  |  | 	if err != nil { | 
					
						
							|  |  |  | 		return selector, fmt.Errorf("invalid label selector: %w", err) | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	return selector, nil | 
					
						
							| 
									
										
										
										
											2019-05-16 03:31:47 +08:00
										 |  |  | } | 
					
						
							| 
									
										
										
										
											2025-02-07 23:14:25 +08:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2025-03-07 22:37:04 +08:00
										 |  |  | func (hw *legacyWaiter) watchTimeout(t time.Duration) func(*resource.Info) error { | 
					
						
							| 
									
										
										
										
											2025-02-07 23:14:25 +08:00
										 |  |  | 	return func(info *resource.Info) error { | 
					
						
							|  |  |  | 		return hw.watchUntilReady(t, info) | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | // WatchUntilReady watches the resources given and waits until it is ready.
 | 
					
						
							|  |  |  | //
 | 
					
						
							|  |  |  | // This method is mainly for hook implementations. It watches for a resource to
 | 
					
						
							|  |  |  | // hit a particular milestone. The milestone depends on the Kind.
 | 
					
						
							|  |  |  | //
 | 
					
						
							|  |  |  | // For most kinds, it checks to see if the resource is marked as Added or Modified
 | 
					
						
							|  |  |  | // by the Kubernetes event stream. For some kinds, it does more:
 | 
					
						
							|  |  |  | //
 | 
					
						
							|  |  |  | //   - Jobs: A job is marked "Ready" when it has successfully completed. This is
 | 
					
						
							|  |  |  | //     ascertained by watching the Status fields in a job's output.
 | 
					
						
							|  |  |  | //   - Pods: A pod is marked "Ready" when it has successfully completed. This is
 | 
					
						
							|  |  |  | //     ascertained by watching the status.phase field in a pod's output.
 | 
					
						
							|  |  |  | //
 | 
					
						
							|  |  |  | // Handling for other kinds will be added as necessary.
 | 
					
						
							| 
									
										
										
										
											2025-03-07 22:37:04 +08:00
										 |  |  | func (hw *legacyWaiter) WatchUntilReady(resources ResourceList, timeout time.Duration) error { | 
					
						
							| 
									
										
										
										
											2025-02-07 23:14:25 +08:00
										 |  |  | 	// For jobs, there's also the option to do poll c.Jobs(namespace).Get():
 | 
					
						
							|  |  |  | 	// https://github.com/adamreese/kubernetes/blob/master/test/e2e/job.go#L291-L300
 | 
					
						
							|  |  |  | 	return perform(resources, hw.watchTimeout(timeout)) | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2025-03-07 22:37:04 +08:00
										 |  |  | func (hw *legacyWaiter) watchUntilReady(timeout time.Duration, info *resource.Info) error { | 
					
						
							| 
									
										
										
										
											2025-02-07 23:14:25 +08:00
										 |  |  | 	kind := info.Mapping.GroupVersionKind.Kind | 
					
						
							|  |  |  | 	switch kind { | 
					
						
							|  |  |  | 	case "Job", "Pod": | 
					
						
							|  |  |  | 	default: | 
					
						
							|  |  |  | 		return nil | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2025-04-10 21:06:03 +08:00
										 |  |  | 	slog.Debug("watching for resource changes", "kind", kind, "resource", info.Name, "timeout", timeout) | 
					
						
							| 
									
										
										
										
											2025-02-07 23:14:25 +08:00
										 |  |  | 
 | 
					
						
							|  |  |  | 	// Use a selector on the name of the resource. This should be unique for the
 | 
					
						
							|  |  |  | 	// given version and kind
 | 
					
						
							|  |  |  | 	selector, err := fields.ParseSelector(fmt.Sprintf("metadata.name=%s", info.Name)) | 
					
						
							|  |  |  | 	if err != nil { | 
					
						
							|  |  |  | 		return err | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 	lw := cachetools.NewListWatchFromClient(info.Client, info.Mapping.Resource.Resource, info.Namespace, selector) | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	// What we watch for depends on the Kind.
 | 
					
						
							|  |  |  | 	// - For a Job, we watch for completion.
 | 
					
						
							|  |  |  | 	// - For all else, we watch until Ready.
 | 
					
						
							|  |  |  | 	// In the future, we might want to add some special logic for types
 | 
					
						
							|  |  |  | 	// like Ingress, Volume, etc.
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	ctx, cancel := watchtools.ContextWithOptionalTimeout(context.Background(), timeout) | 
					
						
							|  |  |  | 	defer cancel() | 
					
						
							|  |  |  | 	_, err = watchtools.UntilWithSync(ctx, lw, &unstructured.Unstructured{}, nil, func(e watch.Event) (bool, error) { | 
					
						
							|  |  |  | 		// Make sure the incoming object is versioned as we use unstructured
 | 
					
						
							|  |  |  | 		// objects when we build manifests
 | 
					
						
							|  |  |  | 		obj := convertWithMapper(e.Object, info.Mapping) | 
					
						
							|  |  |  | 		switch e.Type { | 
					
						
							|  |  |  | 		case watch.Added, watch.Modified: | 
					
						
							|  |  |  | 			// For things like a secret or a config map, this is the best indicator
 | 
					
						
							|  |  |  | 			// we get. We care mostly about jobs, where what we want to see is
 | 
					
						
							|  |  |  | 			// the status go into a good state. For other types, like ReplicaSet
 | 
					
						
							|  |  |  | 			// we don't really do anything to support these as hooks.
 | 
					
						
							| 
									
										
										
										
											2025-04-10 21:06:03 +08:00
										 |  |  | 			slog.Debug("add/modify event received", "resource", info.Name, "eventType", e.Type) | 
					
						
							| 
									
										
										
										
											2025-04-07 22:45:21 +08:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2025-02-07 23:14:25 +08:00
										 |  |  | 			switch kind { | 
					
						
							|  |  |  | 			case "Job": | 
					
						
							|  |  |  | 				return hw.waitForJob(obj, info.Name) | 
					
						
							|  |  |  | 			case "Pod": | 
					
						
							|  |  |  | 				return hw.waitForPodSuccess(obj, info.Name) | 
					
						
							|  |  |  | 			} | 
					
						
							|  |  |  | 			return true, nil | 
					
						
							|  |  |  | 		case watch.Deleted: | 
					
						
							| 
									
										
										
										
											2025-04-10 21:06:03 +08:00
										 |  |  | 			slog.Debug("deleted event received", "resource", info.Name) | 
					
						
							| 
									
										
										
										
											2025-02-07 23:14:25 +08:00
										 |  |  | 			return true, nil | 
					
						
							|  |  |  | 		case watch.Error: | 
					
						
							|  |  |  | 			// Handle error and return with an error.
 | 
					
						
							| 
									
										
										
										
											2025-04-10 21:06:03 +08:00
										 |  |  | 			slog.Error("error event received", "resource", info.Name) | 
					
						
							| 
									
										
										
										
											2025-04-22 00:13:10 +08:00
										 |  |  | 			return true, fmt.Errorf("failed to deploy %s", info.Name) | 
					
						
							| 
									
										
										
										
											2025-02-07 23:14:25 +08:00
										 |  |  | 		default: | 
					
						
							|  |  |  | 			return false, nil | 
					
						
							|  |  |  | 		} | 
					
						
							|  |  |  | 	}) | 
					
						
							|  |  |  | 	return err | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | // waitForJob is a helper that waits for a job to complete.
 | 
					
						
							|  |  |  | //
 | 
					
						
							|  |  |  | // This operates on an event returned from a watcher.
 | 
					
						
							| 
									
										
										
										
											2025-03-07 22:37:04 +08:00
										 |  |  | func (hw *legacyWaiter) waitForJob(obj runtime.Object, name string) (bool, error) { | 
					
						
							| 
									
										
										
										
											2025-02-17 05:11:48 +08:00
										 |  |  | 	o, ok := obj.(*batchv1.Job) | 
					
						
							| 
									
										
										
										
											2025-02-07 23:14:25 +08:00
										 |  |  | 	if !ok { | 
					
						
							| 
									
										
										
										
											2025-04-22 00:13:10 +08:00
										 |  |  | 		return true, fmt.Errorf("expected %s to be a *batch.Job, got %T", name, obj) | 
					
						
							| 
									
										
										
										
											2025-02-07 23:14:25 +08:00
										 |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	for _, c := range o.Status.Conditions { | 
					
						
							| 
									
										
										
										
											2025-02-17 05:11:48 +08:00
										 |  |  | 		if c.Type == batchv1.JobComplete && c.Status == "True" { | 
					
						
							| 
									
										
										
										
											2025-02-07 23:14:25 +08:00
										 |  |  | 			return true, nil | 
					
						
							| 
									
										
										
										
											2025-02-17 05:11:48 +08:00
										 |  |  | 		} else if c.Type == batchv1.JobFailed && c.Status == "True" { | 
					
						
							| 
									
										
										
										
											2025-04-10 21:06:03 +08:00
										 |  |  | 			slog.Error("job failed", "job", name, "reason", c.Reason) | 
					
						
							| 
									
										
										
										
											2025-04-22 00:13:10 +08:00
										 |  |  | 			return true, fmt.Errorf("job %s failed: %s", name, c.Reason) | 
					
						
							| 
									
										
										
										
											2025-02-07 23:14:25 +08:00
										 |  |  | 		} | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2025-04-10 21:06:03 +08:00
										 |  |  | 	slog.Debug("job status update", "job", name, "active", o.Status.Active, "failed", o.Status.Failed, "succeeded", o.Status.Succeeded) | 
					
						
							| 
									
										
										
										
											2025-02-07 23:14:25 +08:00
										 |  |  | 	return false, nil | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | // waitForPodSuccess is a helper that waits for a pod to complete.
 | 
					
						
							|  |  |  | //
 | 
					
						
							|  |  |  | // This operates on an event returned from a watcher.
 | 
					
						
							| 
									
										
										
										
											2025-03-07 22:37:04 +08:00
										 |  |  | func (hw *legacyWaiter) waitForPodSuccess(obj runtime.Object, name string) (bool, error) { | 
					
						
							| 
									
										
										
										
											2025-02-17 05:11:48 +08:00
										 |  |  | 	o, ok := obj.(*corev1.Pod) | 
					
						
							| 
									
										
										
										
											2025-02-07 23:14:25 +08:00
										 |  |  | 	if !ok { | 
					
						
							| 
									
										
										
										
											2025-04-22 00:13:10 +08:00
										 |  |  | 		return true, fmt.Errorf("expected %s to be a *v1.Pod, got %T", name, obj) | 
					
						
							| 
									
										
										
										
											2025-02-07 23:14:25 +08:00
										 |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	switch o.Status.Phase { | 
					
						
							| 
									
										
										
										
											2025-02-17 05:11:48 +08:00
										 |  |  | 	case corev1.PodSucceeded: | 
					
						
							| 
									
										
										
										
											2025-04-10 21:06:03 +08:00
										 |  |  | 		slog.Debug("pod succeeded", "pod", o.Name) | 
					
						
							| 
									
										
										
										
											2025-02-07 23:14:25 +08:00
										 |  |  | 		return true, nil | 
					
						
							| 
									
										
										
										
											2025-02-17 05:11:48 +08:00
										 |  |  | 	case corev1.PodFailed: | 
					
						
							| 
									
										
										
										
											2025-04-10 21:06:03 +08:00
										 |  |  | 		slog.Error("pod failed", "pod", o.Name) | 
					
						
							| 
									
										
										
										
											2025-04-22 00:13:10 +08:00
										 |  |  | 		return true, fmt.Errorf("pod %s failed", o.Name) | 
					
						
							| 
									
										
										
										
											2025-02-17 05:11:48 +08:00
										 |  |  | 	case corev1.PodPending: | 
					
						
							| 
									
										
										
										
											2025-04-10 21:06:03 +08:00
										 |  |  | 		slog.Debug("pod pending", "pod", o.Name) | 
					
						
							| 
									
										
										
										
											2025-02-17 05:11:48 +08:00
										 |  |  | 	case corev1.PodRunning: | 
					
						
							| 
									
										
										
										
											2025-04-10 21:06:03 +08:00
										 |  |  | 		slog.Debug("pod running", "pod", o.Name) | 
					
						
							| 
									
										
										
										
											2025-02-07 23:14:25 +08:00
										 |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	return false, nil | 
					
						
							|  |  |  | } |