|
|
|
@ -19,7 +19,9 @@ package kube // import "helm.sh/helm/v3/pkg/kube"
|
|
|
|
|
import (
|
|
|
|
|
"context"
|
|
|
|
|
"fmt"
|
|
|
|
|
"strings"
|
|
|
|
|
|
|
|
|
|
"github.com/hashicorp/go-multierror"
|
|
|
|
|
appsv1 "k8s.io/api/apps/v1"
|
|
|
|
|
appsv1beta1 "k8s.io/api/apps/v1beta1"
|
|
|
|
|
appsv1beta2 "k8s.io/api/apps/v1beta2"
|
|
|
|
@ -38,13 +40,6 @@ import (
|
|
|
|
|
deploymentutil "helm.sh/helm/v3/internal/third_party/k8s.io/kubernetes/deployment/util"
|
|
|
|
|
)
|
|
|
|
|
|
|
|
|
|
type podError struct {
|
|
|
|
|
podName string
|
|
|
|
|
container string
|
|
|
|
|
reason string
|
|
|
|
|
message string
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// ReadyCheckerOption is a function that configures a ReadyChecker.
|
|
|
|
|
type ReadyCheckerOption func(*ReadyChecker)
|
|
|
|
|
|
|
|
|
@ -100,12 +95,11 @@ func (c *ReadyChecker) IsReady(ctx context.Context, v *resource.Info) (bool, err
|
|
|
|
|
switch value := AsVersioned(v).(type) {
|
|
|
|
|
case *corev1.Pod:
|
|
|
|
|
pod, err := c.client.CoreV1().Pods(v.Namespace).Get(ctx, v.Name, metav1.GetOptions{})
|
|
|
|
|
if err != nil || !c.isPodReady(pod) {
|
|
|
|
|
if err == nil {
|
|
|
|
|
err = c.getPodFailedError(pod)
|
|
|
|
|
}
|
|
|
|
|
if err != nil {
|
|
|
|
|
return false, err
|
|
|
|
|
}
|
|
|
|
|
ready, err := c.isPodReady(pod)
|
|
|
|
|
return ready, err
|
|
|
|
|
case *batchv1.Job:
|
|
|
|
|
if c.checkJobs {
|
|
|
|
|
job, err := c.client.BatchV1().Jobs(v.Namespace).Get(ctx, v.Name, metav1.GetOptions{})
|
|
|
|
@ -215,7 +209,7 @@ func (c *ReadyChecker) IsReady(ctx context.Context, v *resource.Info) (bool, err
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
func (c *ReadyChecker) getPodFailedError(pod *corev1.Pod) error {
|
|
|
|
|
podErrors := make([]podError, 0)
|
|
|
|
|
var err error
|
|
|
|
|
if pod.Status.Phase == corev1.PodFailed {
|
|
|
|
|
containerStatuses := pod.Status.ContainerStatuses
|
|
|
|
|
if pod.Status.InitContainerStatuses != nil {
|
|
|
|
@ -224,28 +218,32 @@ func (c *ReadyChecker) getPodFailedError(pod *corev1.Pod) error {
|
|
|
|
|
|
|
|
|
|
for _, cs := range containerStatuses {
|
|
|
|
|
if cs.State.Terminated != nil {
|
|
|
|
|
podErrors = append(podErrors, podError{
|
|
|
|
|
podName: pod.Name,
|
|
|
|
|
container: cs.Name,
|
|
|
|
|
reason: cs.State.Terminated.Reason,
|
|
|
|
|
message: cs.State.Terminated.Message,
|
|
|
|
|
})
|
|
|
|
|
err = multierror.Append(
|
|
|
|
|
err,
|
|
|
|
|
fmt.Errorf(
|
|
|
|
|
"pod: %q, container: %q. reason: %q. message: %q",
|
|
|
|
|
pod.Name,
|
|
|
|
|
cs.Name,
|
|
|
|
|
cs.State.Terminated.Reason,
|
|
|
|
|
strings.TrimSpace(cs.State.Terminated.Message),
|
|
|
|
|
),
|
|
|
|
|
)
|
|
|
|
|
} else if cs.State.Waiting != nil {
|
|
|
|
|
podErrors = append(podErrors, podError{
|
|
|
|
|
podName: pod.Name,
|
|
|
|
|
container: cs.Name,
|
|
|
|
|
reason: cs.State.Waiting.Reason,
|
|
|
|
|
message: cs.State.Waiting.Message,
|
|
|
|
|
})
|
|
|
|
|
}
|
|
|
|
|
err = multierror.Append(
|
|
|
|
|
err,
|
|
|
|
|
fmt.Errorf(
|
|
|
|
|
"pod: %q, container: %q. reason: %q. message: %q",
|
|
|
|
|
pod.Name,
|
|
|
|
|
cs.Name,
|
|
|
|
|
cs.State.Waiting.Reason,
|
|
|
|
|
strings.TrimSpace(cs.State.Waiting.Message),
|
|
|
|
|
),
|
|
|
|
|
)
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if len(podErrors) > 0 {
|
|
|
|
|
return fmt.Errorf("%+v", podErrors)
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return nil
|
|
|
|
|
return err
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
func (c *ReadyChecker) podsReadyForObject(ctx context.Context, namespace string, obj runtime.Object) (bool, error) {
|
|
|
|
@ -254,8 +252,8 @@ func (c *ReadyChecker) podsReadyForObject(ctx context.Context, namespace string,
|
|
|
|
|
return false, err
|
|
|
|
|
}
|
|
|
|
|
for _, pod := range pods {
|
|
|
|
|
if !c.isPodReady(&pod) {
|
|
|
|
|
return false, nil
|
|
|
|
|
if isReady, err := c.isPodReady(&pod); !isReady {
|
|
|
|
|
return false, err
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
return true, nil
|
|
|
|
@ -270,15 +268,26 @@ func (c *ReadyChecker) podsforObject(ctx context.Context, namespace string, obj
|
|
|
|
|
return list, err
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// isPodReady returns true if a pod is ready; false otherwise.
|
|
|
|
|
func (c *ReadyChecker) isPodReady(pod *corev1.Pod) bool {
|
|
|
|
|
for _, c := range pod.Status.Conditions {
|
|
|
|
|
if c.Type == corev1.PodReady && c.Status == corev1.ConditionTrue {
|
|
|
|
|
return true
|
|
|
|
|
// isPodReady returns true if the pod is ready, false if it is not ready, and an error if the pod is in a failed state.
|
|
|
|
|
func (c *ReadyChecker) isPodReady(pod *corev1.Pod) (bool, error) {
|
|
|
|
|
if pod.Status.Phase == corev1.PodSucceeded {
|
|
|
|
|
return true, nil
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
err := c.getPodFailedError(pod)
|
|
|
|
|
if err != nil {
|
|
|
|
|
c.log("Pod is failed: %s/%s", pod.GetNamespace(), pod.GetName())
|
|
|
|
|
return false, err
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
for _, condition := range pod.Status.Conditions {
|
|
|
|
|
if condition.Type == corev1.PodReady && condition.Status == corev1.ConditionTrue {
|
|
|
|
|
return true, nil
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
c.log("Pod is not ready: %s/%s", pod.GetNamespace(), pod.GetName())
|
|
|
|
|
return false
|
|
|
|
|
return false, nil
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
func (c *ReadyChecker) jobReady(job *batchv1.Job) (bool, error) {
|
|
|
|
|