@ -18,6 +18,7 @@ package action
import (
import (
"context"
"context"
"errors"
"fmt"
"fmt"
"io"
"io"
"slices"
"slices"
@ -25,6 +26,8 @@ import (
"time"
"time"
v1 "k8s.io/api/core/v1"
v1 "k8s.io/api/core/v1"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
"k8s.io/client-go/kubernetes"
chartutil "helm.sh/helm/v4/pkg/chart/v2/util"
chartutil "helm.sh/helm/v4/pkg/chart/v2/util"
"helm.sh/helm/v4/pkg/kube"
"helm.sh/helm/v4/pkg/kube"
@ -124,9 +127,9 @@ func (r *ReleaseTesting) GetPodLogs(out io.Writer, rel *release.Release) error {
return fmt . Errorf ( "unable to get kubernetes client to fetch pod logs: %w" , err )
return fmt . Errorf ( "unable to get kubernetes client to fetch pod logs: %w" , err )
}
}
hooksByW ight := append ( [ ] * release . Hook { } , rel . Hooks ... )
hooksByW e ight := append ( [ ] * release . Hook { } , rel . Hooks ... )
sort . Stable ( hookByWeight ( hooksByW ight) )
sort . Stable ( hookByWeight ( hooksByW e ight) )
for _ , h := range hooksByW ight {
for _ , h := range hooksByW e ight {
for _ , e := range h . Events {
for _ , e := range h . Events {
if e == release . HookTest {
if e == release . HookTest {
if slices . Contains ( r . Filters [ ExcludeNameFilter ] , h . Name ) {
if slices . Contains ( r . Filters [ ExcludeNameFilter ] , h . Name ) {
@ -135,20 +138,42 @@ func (r *ReleaseTesting) GetPodLogs(out io.Writer, rel *release.Release) error {
if len ( r . Filters [ IncludeNameFilter ] ) > 0 && ! slices . Contains ( r . Filters [ IncludeNameFilter ] , h . Name ) {
if len ( r . Filters [ IncludeNameFilter ] ) > 0 && ! slices . Contains ( r . Filters [ IncludeNameFilter ] , h . Name ) {
continue
continue
}
}
req := client . CoreV1 ( ) . Pods ( r . Namespace ) . GetLogs ( h . Name , & v1 . PodLogOptions { } )
if err := r . getContainerLogs ( out , client , h . Name ) ; err != nil {
logReader , err := req . Stream ( context . Background ( ) )
return err
if err != nil {
return fmt . Errorf ( "unable to get pod logs for %s: %w" , h . Name , err )
}
fmt . Fprintf ( out , "POD LOGS: %s\n" , h . Name )
_ , err = io . Copy ( out , logReader )
fmt . Fprintln ( out )
if err != nil {
return fmt . Errorf ( "unable to write pod logs for %s: %w" , h . Name , err )
}
}
}
}
}
}
}
}
return nil
return nil
}
}
// getContainerLogs fetches logs from all containers (init and regular) in the
// named pod and writes them to out. It continues on per-container errors and
// returns all of them joined at the end.
func ( r * ReleaseTesting ) getContainerLogs ( out io . Writer , client kubernetes . Interface , podName string ) error {
pod , err := client . CoreV1 ( ) . Pods ( r . Namespace ) . Get ( context . Background ( ) , podName , metav1 . GetOptions { } )
if err != nil {
return fmt . Errorf ( "unable to get pod %s: %w" , podName , err )
}
allContainers := append ( pod . Spec . InitContainers , pod . Spec . Containers ... )
var errs [ ] error
for _ , c := range allContainers {
opts := & v1 . PodLogOptions { Container : c . Name }
req := client . CoreV1 ( ) . Pods ( r . Namespace ) . GetLogs ( podName , opts )
logReader , err := req . Stream ( context . Background ( ) )
if err != nil {
errs = append ( errs , fmt . Errorf ( "unable to get logs for pod %s, container %s: %w" , podName , c . Name , err ) )
continue
}
fmt . Fprintf ( out , "POD LOGS: %s (%s)\n" , podName , c . Name )
_ , err = io . Copy ( out , logReader )
fmt . Fprintln ( out )
if err != nil {
errs = append ( errs , fmt . Errorf ( "unable to write logs for pod %s, container %s: %w" , podName , c . Name , err ) )
}
}
return errors . Join ( errs ... )
}