|
|
@ -18,6 +18,7 @@ package kube // import "helm.sh/helm/v4/pkg/kube"
|
|
|
|
|
|
|
|
|
|
|
|
import (
|
|
|
|
import (
|
|
|
|
"bytes"
|
|
|
|
"bytes"
|
|
|
|
|
|
|
|
"context"
|
|
|
|
"encoding/json"
|
|
|
|
"encoding/json"
|
|
|
|
"fmt"
|
|
|
|
"fmt"
|
|
|
|
"io"
|
|
|
|
"io"
|
|
|
@ -687,150 +688,47 @@ func updateResource(c *Client, target *resource.Info, currentObj runtime.Object,
|
|
|
|
return nil
|
|
|
|
return nil
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// func (c *Client) watchUntilReady(timeout time.Duration, info *resource.Info) error {
|
|
|
|
// GetPodList uses the kubernetes interface to get the list of pods filtered by listOptions
|
|
|
|
// kind := info.Mapping.GroupVersionKind.Kind
|
|
|
|
func (c *Client) GetPodList(namespace string, listOptions metav1.ListOptions) (*v1.PodList, error) {
|
|
|
|
// switch kind {
|
|
|
|
podList, err := c.kubeClient.CoreV1().Pods(namespace).List(context.Background(), listOptions)
|
|
|
|
// case "Job", "Pod":
|
|
|
|
if err != nil {
|
|
|
|
// default:
|
|
|
|
return nil, fmt.Errorf("failed to get pod list with options: %+v with error: %v", listOptions, err)
|
|
|
|
// return nil
|
|
|
|
}
|
|
|
|
// }
|
|
|
|
return podList, nil
|
|
|
|
|
|
|
|
}
|
|
|
|
// c.Log("Watching for changes to %s %s with timeout of %v", kind, info.Name, timeout)
|
|
|
|
|
|
|
|
|
|
|
|
// OutputContainerLogsForPodList is a helper that outputs logs for a list of pods
|
|
|
|
// // Use a selector on the name of the resource. This should be unique for the
|
|
|
|
func (c *Client) OutputContainerLogsForPodList(podList *v1.PodList, namespace string, writerFunc func(namespace, pod, container string) io.Writer) error {
|
|
|
|
// // given version and kind
|
|
|
|
for _, pod := range podList.Items {
|
|
|
|
// selector, err := fields.ParseSelector(fmt.Sprintf("metadata.name=%s", info.Name))
|
|
|
|
for _, container := range pod.Spec.Containers {
|
|
|
|
// if err != nil {
|
|
|
|
options := &v1.PodLogOptions{
|
|
|
|
// return err
|
|
|
|
Container: container.Name,
|
|
|
|
// }
|
|
|
|
}
|
|
|
|
// lw := cachetools.NewListWatchFromClient(info.Client, info.Mapping.Resource.Resource, info.Namespace, selector)
|
|
|
|
request := c.kubeClient.CoreV1().Pods(namespace).GetLogs(pod.Name, options)
|
|
|
|
|
|
|
|
err2 := copyRequestStreamToWriter(request, pod.Name, container.Name, writerFunc(namespace, pod.Name, container.Name))
|
|
|
|
// // What we watch for depends on the Kind.
|
|
|
|
if err2 != nil {
|
|
|
|
// // - For a Job, we watch for completion.
|
|
|
|
return err2
|
|
|
|
// // - For all else, we watch until Ready.
|
|
|
|
}
|
|
|
|
// // In the future, we might want to add some special logic for types
|
|
|
|
}
|
|
|
|
// // like Ingress, Volume, etc.
|
|
|
|
}
|
|
|
|
|
|
|
|
return nil
|
|
|
|
// ctx, cancel := watchtools.ContextWithOptionalTimeout(context.Background(), timeout)
|
|
|
|
}
|
|
|
|
// defer cancel()
|
|
|
|
|
|
|
|
// _, err = watchtools.UntilWithSync(ctx, lw, &unstructured.Unstructured{}, nil, func(e watch.Event) (bool, error) {
|
|
|
|
func copyRequestStreamToWriter(request *rest.Request, podName, containerName string, writer io.Writer) error {
|
|
|
|
// // Make sure the incoming object is versioned as we use unstructured
|
|
|
|
readCloser, err := request.Stream(context.Background())
|
|
|
|
// // objects when we build manifests
|
|
|
|
if err != nil {
|
|
|
|
// obj := convertWithMapper(e.Object, info.Mapping)
|
|
|
|
return errors.Errorf("Failed to stream pod logs for pod: %s, container: %s", podName, containerName)
|
|
|
|
// switch e.Type {
|
|
|
|
}
|
|
|
|
// case watch.Added, watch.Modified:
|
|
|
|
defer readCloser.Close()
|
|
|
|
// // For things like a secret or a config map, this is the best indicator
|
|
|
|
_, err = io.Copy(writer, readCloser)
|
|
|
|
// // we get. We care mostly about jobs, where what we want to see is
|
|
|
|
if err != nil {
|
|
|
|
// // the status go into a good state. For other types, like ReplicaSet
|
|
|
|
return errors.Errorf("Failed to copy IO from logs for pod: %s, container: %s", podName, containerName)
|
|
|
|
// // we don't really do anything to support these as hooks.
|
|
|
|
}
|
|
|
|
// c.Log("Add/Modify event for %s: %v", info.Name, e.Type)
|
|
|
|
if err != nil {
|
|
|
|
// switch kind {
|
|
|
|
return errors.Errorf("Failed to close reader for pod: %s, container: %s", podName, containerName)
|
|
|
|
// case "Job":
|
|
|
|
}
|
|
|
|
// return c.waitForJob(obj, info.Name)
|
|
|
|
return nil
|
|
|
|
// case "Pod":
|
|
|
|
}
|
|
|
|
// return c.waitForPodSuccess(obj, info.Name)
|
|
|
|
|
|
|
|
// }
|
|
|
|
|
|
|
|
// return true, nil
|
|
|
|
|
|
|
|
// case watch.Deleted:
|
|
|
|
|
|
|
|
// c.Log("Deleted event for %s", info.Name)
|
|
|
|
|
|
|
|
// return true, nil
|
|
|
|
|
|
|
|
// case watch.Error:
|
|
|
|
|
|
|
|
// // Handle error and return with an error.
|
|
|
|
|
|
|
|
// c.Log("Error event for %s", info.Name)
|
|
|
|
|
|
|
|
// return true, errors.Errorf("failed to deploy %s", info.Name)
|
|
|
|
|
|
|
|
// 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.
|
|
|
|
|
|
|
|
// func (c *Client) waitForJob(obj runtime.Object, name string) (bool, error) {
|
|
|
|
|
|
|
|
// o, ok := obj.(*batch.Job)
|
|
|
|
|
|
|
|
// if !ok {
|
|
|
|
|
|
|
|
// return true, errors.Errorf("expected %s to be a *batch.Job, got %T", name, obj)
|
|
|
|
|
|
|
|
// }
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
// for _, c := range o.Status.Conditions {
|
|
|
|
|
|
|
|
// if c.Type == batch.JobComplete && c.Status == "True" {
|
|
|
|
|
|
|
|
// return true, nil
|
|
|
|
|
|
|
|
// } else if c.Type == batch.JobFailed && c.Status == "True" {
|
|
|
|
|
|
|
|
// return true, errors.Errorf("job %s failed: %s", name, c.Reason)
|
|
|
|
|
|
|
|
// }
|
|
|
|
|
|
|
|
// }
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
// c.Log("%s: Jobs active: %d, jobs failed: %d, jobs succeeded: %d", name, o.Status.Active, o.Status.Failed, o.Status.Succeeded)
|
|
|
|
|
|
|
|
// return false, nil
|
|
|
|
|
|
|
|
// }
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
// // waitForPodSuccess is a helper that waits for a pod to complete.
|
|
|
|
|
|
|
|
// //
|
|
|
|
|
|
|
|
// // This operates on an event returned from a watcher.
|
|
|
|
|
|
|
|
// func (c *Client) waitForPodSuccess(obj runtime.Object, name string) (bool, error) {
|
|
|
|
|
|
|
|
// o, ok := obj.(*v1.Pod)
|
|
|
|
|
|
|
|
// if !ok {
|
|
|
|
|
|
|
|
// return true, errors.Errorf("expected %s to be a *v1.Pod, got %T", name, obj)
|
|
|
|
|
|
|
|
// }
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
// switch o.Status.Phase {
|
|
|
|
|
|
|
|
// case v1.PodSucceeded:
|
|
|
|
|
|
|
|
// c.Log("Pod %s succeeded", o.Name)
|
|
|
|
|
|
|
|
// return true, nil
|
|
|
|
|
|
|
|
// case v1.PodFailed:
|
|
|
|
|
|
|
|
// return true, errors.Errorf("pod %s failed", o.Name)
|
|
|
|
|
|
|
|
// case v1.PodPending:
|
|
|
|
|
|
|
|
// c.Log("Pod %s pending", o.Name)
|
|
|
|
|
|
|
|
// case v1.PodRunning:
|
|
|
|
|
|
|
|
// c.Log("Pod %s running", o.Name)
|
|
|
|
|
|
|
|
// }
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
// return false, nil
|
|
|
|
|
|
|
|
// }
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
// // GetPodList uses the kubernetes interface to get the list of pods filtered by listOptions
|
|
|
|
|
|
|
|
// func (c *Client) GetPodList(namespace string, listOptions metav1.ListOptions) (*v1.PodList, error) {
|
|
|
|
|
|
|
|
// podList, err := c.kubeClient.CoreV1().Pods(namespace).List(context.Background(), listOptions)
|
|
|
|
|
|
|
|
// if err != nil {
|
|
|
|
|
|
|
|
// return nil, fmt.Errorf("failed to get pod list with options: %+v with error: %v", listOptions, err)
|
|
|
|
|
|
|
|
// }
|
|
|
|
|
|
|
|
// return podList, nil
|
|
|
|
|
|
|
|
// }
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
// // OutputContainerLogsForPodList is a helper that outputs logs for a list of pods
|
|
|
|
|
|
|
|
// func (c *Client) OutputContainerLogsForPodList(podList *v1.PodList, namespace string, writerFunc func(namespace, pod, container string) io.Writer) error {
|
|
|
|
|
|
|
|
// for _, pod := range podList.Items {
|
|
|
|
|
|
|
|
// for _, container := range pod.Spec.Containers {
|
|
|
|
|
|
|
|
// options := &v1.PodLogOptions{
|
|
|
|
|
|
|
|
// Container: container.Name,
|
|
|
|
|
|
|
|
// }
|
|
|
|
|
|
|
|
// request := c.kubeClient.CoreV1().Pods(namespace).GetLogs(pod.Name, options)
|
|
|
|
|
|
|
|
// err2 := copyRequestStreamToWriter(request, pod.Name, container.Name, writerFunc(namespace, pod.Name, container.Name))
|
|
|
|
|
|
|
|
// if err2 != nil {
|
|
|
|
|
|
|
|
// return err2
|
|
|
|
|
|
|
|
// }
|
|
|
|
|
|
|
|
// }
|
|
|
|
|
|
|
|
// }
|
|
|
|
|
|
|
|
// return nil
|
|
|
|
|
|
|
|
// }
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
// func copyRequestStreamToWriter(request *rest.Request, podName, containerName string, writer io.Writer) error {
|
|
|
|
|
|
|
|
// readCloser, err := request.Stream(context.Background())
|
|
|
|
|
|
|
|
// if err != nil {
|
|
|
|
|
|
|
|
// return errors.Errorf("Failed to stream pod logs for pod: %s, container: %s", podName, containerName)
|
|
|
|
|
|
|
|
// }
|
|
|
|
|
|
|
|
// defer readCloser.Close()
|
|
|
|
|
|
|
|
// _, err = io.Copy(writer, readCloser)
|
|
|
|
|
|
|
|
// if err != nil {
|
|
|
|
|
|
|
|
// return errors.Errorf("Failed to copy IO from logs for pod: %s, container: %s", podName, containerName)
|
|
|
|
|
|
|
|
// }
|
|
|
|
|
|
|
|
// if err != nil {
|
|
|
|
|
|
|
|
// return errors.Errorf("Failed to close reader for pod: %s, container: %s", podName, containerName)
|
|
|
|
|
|
|
|
// }
|
|
|
|
|
|
|
|
// return nil
|
|
|
|
|
|
|
|
// }
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
// scrubValidationError removes kubectl info from the message.
|
|
|
|
// scrubValidationError removes kubectl info from the message.
|
|
|
|
func scrubValidationError(err error) error {
|
|
|
|
func scrubValidationError(err error) error {
|
|
|
|