11package cli
22
33import (
4+ "archive/tar"
45 "bytes"
56 "context"
67 "crypto/tls"
78 "encoding/json"
89 "fmt"
10+ "io"
911 "io/ioutil"
1012 "net/http"
1113 "net/url"
1214 "os"
1315 "path/filepath"
16+ "sort"
1417 "strings"
1518 "time"
1619
@@ -395,7 +398,7 @@ func runCollectors(v *viper.Viper, collectors []*troubleshootv1beta1.Collect, ad
395398 }
396399
397400 if result != nil {
398- err = saveCollectorOutput (result , bundlePath )
401+ err = saveCollectorOutput (result , bundlePath , collector )
399402 if err != nil {
400403 progressChan <- fmt .Errorf ("failed to parse collector spec %q: %v" , collector .GetDisplayName (), err )
401404 continue
@@ -415,8 +418,15 @@ func runCollectors(v *viper.Viper, collectors []*troubleshootv1beta1.Collect, ad
415418 return filename , nil
416419}
417420
418- func saveCollectorOutput (output map [string ][]byte , bundlePath string ) error {
421+ func saveCollectorOutput (output map [string ][]byte , bundlePath string , c * collect. Collector ) error {
419422 for filename , maybeContents := range output {
423+ if c .Collect .Copy != nil {
424+ err := untarAndSave (maybeContents , filepath .Join (bundlePath , filepath .Dir (filename )))
425+ if err != nil {
426+ return errors .Wrap (err , "extract copied files" )
427+ }
428+ continue
429+ }
420430 fileDir , fileName := filepath .Split (filename )
421431 outPath := filepath .Join (bundlePath , fileDir )
422432
@@ -431,7 +441,59 @@ func saveCollectorOutput(output map[string][]byte, bundlePath string) error {
431441
432442 return nil
433443}
434-
444+ func untarAndSave (tarFile []byte , bundlePath string ) error {
445+ keys := make ([]string , 0 )
446+ dirs := make (map [string ]* tar.Header )
447+ files := make (map [string ][]byte )
448+ fileHeaders := make (map [string ]* tar.Header )
449+ tarReader := tar .NewReader (bytes .NewBuffer (tarFile ))
450+ //Extract and separate tar contentes in file and folders, keeping header info from each one.
451+ for {
452+ header , err := tarReader .Next ()
453+ if err != nil {
454+ if err != io .EOF {
455+ return err
456+ }
457+ break
458+ }
459+ switch header .Typeflag {
460+ case tar .TypeDir :
461+ dirs [header .Name ] = header
462+ case tar .TypeReg :
463+ file := new (bytes.Buffer )
464+ _ , err = io .Copy (file , tarReader )
465+ if err != nil {
466+ return err
467+ }
468+ files [header .Name ] = file .Bytes ()
469+ fileHeaders [header .Name ] = header
470+ default :
471+ return fmt .Errorf ("Tar file entry %s contained unsupported file type %v" , header .Name , header .FileInfo ().Mode ())
472+ }
473+ }
474+ //Create directories from base path: <namespace>/<pod name>/containerPath
475+ if err := os .MkdirAll (filepath .Join (bundlePath ), 0777 ); err != nil {
476+ return errors .Wrap (err , "create output file" )
477+ }
478+ //Order folders stored in variable keys to start always by parent folder. That way folder info is preserved.
479+ for k := range dirs {
480+ keys = append (keys , k )
481+ }
482+ sort .Strings (keys )
483+ //Orderly create folders.
484+ for _ , k := range keys {
485+ if err := os .Mkdir (filepath .Join (bundlePath , k ), dirs [k ].FileInfo ().Mode ().Perm ()); err != nil {
486+ return errors .Wrap (err , "create output file" )
487+ }
488+ }
489+ //Populate folders with respective files and its permissions stored in the header.
490+ for k , v := range files {
491+ if err := ioutil .WriteFile (filepath .Join (bundlePath , k ), v , fileHeaders [k ].FileInfo ().Mode ().Perm ()); err != nil {
492+ return err
493+ }
494+ }
495+ return nil
496+ }
435497func uploadSupportBundle (r * troubleshootv1beta1.ResultRequest , archivePath string ) error {
436498 contentType := getExpectedContentType (r .URI )
437499 if contentType != "" && contentType != "application/tar+gzip" {
0 commit comments