@@ -19,6 +19,7 @@ package validators
1919import (
2020 "bytes"
2121 "context"
22+ "errors"
2223 "fmt"
2324 "os"
2425 "os/exec"
@@ -190,10 +191,10 @@ func (h *ValidatorHarness) TestNamespace() string {
190191 h .testNamespace = ns
191192
192193 h .t .Cleanup (func () {
193- h .dumpNamespaceResources (ns )
194+ ctx := context .WithoutCancel (h .Context ())
195+ h .dumpNamespaceResources (ctx , ns )
194196
195197 h .Logf ("Deleting test namespace %q" , ns )
196- ctx := context .WithoutCancel (h .Context ())
197198 err := h .DynamicClient ().Resource (namespaceGVR ).Delete (ctx , ns , metav1.DeleteOptions {})
198199 if err != nil {
199200 h .Logf ("failed to delete test namespace: %v" , err )
@@ -212,7 +213,7 @@ func (h *ValidatorHarness) ApplyManifest(namespace string, manifestPath string)
212213}
213214
214215// dumpNamespaceResources dumps key resources from the namespace to the artifacts directory for debugging.
215- func (h * ValidatorHarness ) dumpNamespaceResources (ns string ) {
216+ func (h * ValidatorHarness ) dumpNamespaceResources (ctx context. Context , ns string ) {
216217 artifactsDir := os .Getenv ("ARTIFACTS" )
217218 if artifactsDir == "" {
218219 artifactsDir = "_artifacts"
@@ -235,21 +236,25 @@ func (h *ValidatorHarness) dumpNamespaceResources(ns string) {
235236 }
236237
237238 for _ , resourceType := range resourceTypes {
238- if err := h .dumpResource (ns , resourceType , filepath .Join (clusterInfoDir , resourceType + ".yaml" )); err != nil {
239+ if err := h .dumpResource (ctx , ns , resourceType , filepath .Join (clusterInfoDir , resourceType + ".yaml" )); err != nil {
239240 h .Logf ("failed to dump resource %s: %v" , resourceType , err )
240241 }
241242 }
243+
244+ if err := h .dumpPodLogs (ctx , ns , clusterInfoDir ); err != nil {
245+ h .Logf ("failed to dump pod logs: %v" , err )
246+ }
242247}
243248
244249// dumpResource runs kubectl get for a resource type and writes the output to a file.
245250// Errors are logged but do not fail the test.
246- func (h * ValidatorHarness ) dumpResource (ns string , resourceType string , outputPath string ) error {
251+ func (h * ValidatorHarness ) dumpResource (ctx context. Context , ns string , resourceType string , outputPath string ) error {
247252 args := []string {"get" , resourceType }
248253 if ns != "" {
249254 args = append (args , "-n" , ns )
250255 }
251256 args = append (args , "-o" , "yaml" )
252- cmd := exec .CommandContext (context . WithoutCancel ( h . Context ()) , "kubectl" , args ... )
257+ cmd := exec .CommandContext (ctx , "kubectl" , args ... )
253258 var stdout bytes.Buffer
254259 var stderr bytes.Buffer
255260 cmd .Stdout = & stdout
@@ -265,3 +270,43 @@ func (h *ValidatorHarness) dumpResource(ns string, resourceType string, outputPa
265270
266271 return nil
267272}
273+
274+ // dumpPodLogs collects logs from all pods in the namespace and writes them to individual files.
275+ func (h * ValidatorHarness ) dumpPodLogs (ctx context.Context , ns string , clusterInfoDir string ) error {
276+ podLogsDir := filepath .Join (clusterInfoDir , "pod-logs" )
277+
278+ // List pods in the namespace
279+ cmd := exec .CommandContext (ctx , "kubectl" , "get" , "pods" , "-n" , ns , "-o" , "jsonpath={.items[*].metadata.name}" )
280+ var stdout bytes.Buffer
281+ cmd .Stdout = & stdout
282+ var stderr bytes.Buffer
283+ cmd .Stderr = & stderr
284+ if err := cmd .Run (); err != nil {
285+ return fmt .Errorf ("failed to list pods for log collection in namespace %s (stderr: %s): %w" , ns , stderr .String (), err )
286+ }
287+
288+ podNames := strings .Fields (stdout .String ())
289+
290+ if err := os .MkdirAll (podLogsDir , 0o755 ); err != nil {
291+ return fmt .Errorf ("failed to create pod-logs directory: %v" , err )
292+ }
293+
294+ var errs []error
295+ for _ , podName := range podNames {
296+ logCmd := exec .CommandContext (ctx , "kubectl" , "logs" , "-n" , ns , podName , "--all-containers" , "--ignore-errors" )
297+ var logOut bytes.Buffer
298+ logCmd .Stdout = & logOut
299+ var logErr bytes.Buffer
300+ logCmd .Stderr = & logErr
301+ if err := logCmd .Run (); err != nil {
302+ errs = append (errs , fmt .Errorf ("failed to get logs for pod %s (stderr: %s): %w" , podName , logErr .String (), err ))
303+ continue
304+ }
305+ logPath := filepath .Join (podLogsDir , podName + ".log" )
306+ if err := os .WriteFile (logPath , logOut .Bytes (), 0o644 ); err != nil {
307+ errs = append (errs , fmt .Errorf ("failed to write logs for pod %s to %s: %w" , podName , logPath , err ))
308+ }
309+ }
310+
311+ return errors .Join (errs ... )
312+ }
0 commit comments