Skip to content

Commit 870a8a6

Browse files
committed
add import/export command
1 parent 44b81d6 commit 870a8a6

2 files changed

Lines changed: 208 additions & 0 deletions

File tree

pkg/logs/import.go

Lines changed: 206 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,206 @@
1+
package logs
2+
3+
import (
4+
"archive/tar"
5+
"compress/gzip"
6+
"fmt"
7+
"io"
8+
"log"
9+
"os"
10+
"path/filepath"
11+
"strings"
12+
13+
"github.com/spf13/cobra"
14+
)
15+
16+
func newImportCmd() *cobra.Command {
17+
var importCmd = &cobra.Command{
18+
Use: "import",
19+
Short: "Import logs from a tar archive",
20+
Run: func(cmd *cobra.Command, args []string) {
21+
source, _ := cmd.Flags().GetString("source")
22+
dest, _ := cmd.Flags().GetString("dest")
23+
24+
// Create destination directory if it doesn't exist
25+
if err := os.MkdirAll(dest, 0755); err != nil {
26+
log.Fatal(err)
27+
}
28+
29+
// Implement your tar extraction logic here
30+
if err := extractTar(source, dest); err != nil {
31+
log.Fatal(err)
32+
}
33+
fmt.Println("Successfully imported logs to:", dest)
34+
},
35+
}
36+
// Import command flags
37+
importCmd.Flags().StringP("source", "s", "", "Source tar file to import (required)")
38+
importCmd.Flags().StringP("dest", "d", "/tmp/k8sdebug/logs", "Destination directory for extraction")
39+
importCmd.MarkFlagRequired("source")
40+
return importCmd
41+
}
42+
43+
func newExportCmd() *cobra.Command {
44+
var exportCmd = &cobra.Command{
45+
Use: "export",
46+
Short: "Export logs to a tar archive",
47+
Run: func(cmd *cobra.Command, args []string) {
48+
source, _ := cmd.Flags().GetString("source")
49+
dest, _ := cmd.Flags().GetString("dest")
50+
51+
// Implement your tar creation logic here
52+
if err := createTar(source, dest); err != nil {
53+
log.Fatal(err)
54+
}
55+
fmt.Println("Successfully exported logs to:", dest)
56+
},
57+
}
58+
// Export command flags
59+
exportCmd.Flags().StringP("source", "s", "/tmp/k8sdebug/logs", "Source directory to export")
60+
exportCmd.Flags().StringP("dest", "d", "", "Destination tar file path (required)")
61+
exportCmd.MarkFlagRequired("dest")
62+
return exportCmd
63+
}
64+
65+
func extractTar(source string, dest string) error {
66+
// Open source file
67+
file, err := os.Open(source)
68+
if err != nil {
69+
return fmt.Errorf("failed to open source file: %w", err)
70+
}
71+
defer file.Close()
72+
73+
// Create gzip reader
74+
gzr, err := gzip.NewReader(file)
75+
if err != nil {
76+
return fmt.Errorf("failed to create gzip reader: %w", err)
77+
}
78+
defer gzr.Close()
79+
80+
// Create tar reader
81+
tr := tar.NewReader(gzr)
82+
83+
// Process each entry in the tar archive
84+
for {
85+
header, err := tr.Next()
86+
if err == io.EOF {
87+
break // End of archive
88+
}
89+
if err != nil {
90+
return fmt.Errorf("tar read error: %w", err)
91+
}
92+
93+
// Sanitize file path to prevent path traversal
94+
targetPath := filepath.Join(dest, header.Name)
95+
if !strings.HasPrefix(targetPath, filepath.Clean(dest)+string(os.PathSeparator)) {
96+
return fmt.Errorf("illegal file path: %s", header.Name)
97+
}
98+
99+
switch header.Typeflag {
100+
case tar.TypeDir:
101+
// Create directory with original permissions
102+
if err := os.MkdirAll(targetPath, os.FileMode(header.Mode)); err != nil {
103+
return fmt.Errorf("failed to create directory: %w", err)
104+
}
105+
106+
case tar.TypeReg:
107+
// Create parent directories if needed
108+
if err := os.MkdirAll(filepath.Dir(targetPath), 0755); err != nil {
109+
return fmt.Errorf("failed to create parent directories: %w", err)
110+
}
111+
112+
// Create file with original permissions
113+
f, err := os.OpenFile(targetPath, os.O_CREATE|os.O_TRUNC|os.O_WRONLY, os.FileMode(header.Mode))
114+
if err != nil {
115+
return fmt.Errorf("failed to create file: %w", err)
116+
}
117+
118+
// Copy file contents
119+
if _, err := io.Copy(f, tr); err != nil {
120+
f.Close()
121+
return fmt.Errorf("failed to copy file contents: %w", err)
122+
}
123+
f.Close()
124+
125+
// Preserve modification time
126+
if err := os.Chtimes(targetPath, header.AccessTime, header.ModTime); err != nil {
127+
return fmt.Errorf("failed to set file times: %w", err)
128+
}
129+
130+
default:
131+
return fmt.Errorf("unsupported file type: %v", header.Typeflag)
132+
}
133+
}
134+
return nil
135+
}
136+
137+
func createTar(source string, dest string) error {
138+
// Create destination file
139+
file, err := os.Create(dest)
140+
if err != nil {
141+
return fmt.Errorf("failed to create destination file: %w", err)
142+
}
143+
defer file.Close()
144+
145+
// Create gzip writer
146+
gzw := gzip.NewWriter(file)
147+
defer gzw.Close()
148+
149+
// Create tar writer
150+
tw := tar.NewWriter(gzw)
151+
defer tw.Close()
152+
153+
// Walk the source directory
154+
baseDir := filepath.Clean(source)
155+
err = filepath.Walk(baseDir, func(path string, info os.FileInfo, err error) error {
156+
if err != nil {
157+
return err
158+
}
159+
160+
// Get relative path
161+
relPath, err := filepath.Rel(baseDir, path)
162+
if err != nil {
163+
return err
164+
}
165+
if relPath == "." {
166+
return nil // Skip root directory
167+
}
168+
169+
// Create tar header
170+
header, err := tar.FileInfoHeader(info, "")
171+
if err != nil {
172+
return fmt.Errorf("failed to create header: %w", err)
173+
}
174+
header.Name = relPath
175+
176+
// Write header
177+
if err := tw.WriteHeader(header); err != nil {
178+
return fmt.Errorf("failed to write header: %w", err)
179+
}
180+
181+
// Skip directories (header only)
182+
if info.IsDir() {
183+
return nil
184+
}
185+
186+
// Open file for reading
187+
f, err := os.Open(path)
188+
if err != nil {
189+
return fmt.Errorf("failed to open file: %w", err)
190+
}
191+
defer f.Close()
192+
193+
// Copy file contents
194+
if _, err := io.Copy(tw, f); err != nil {
195+
return fmt.Errorf("failed to copy file contents: %w", err)
196+
}
197+
198+
return nil
199+
})
200+
201+
if err != nil {
202+
return fmt.Errorf("directory walk failed: %w", err)
203+
}
204+
205+
return nil
206+
}

pkg/logs/logs.go

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -48,5 +48,7 @@ func NewCommand() *cobra.Command {
4848
cmd.AddCommand(newShowCommand())
4949
cmd.AddCommand(newCleanupCommand())
5050
cmd.AddCommand(newDiffCommand())
51+
cmd.AddCommand(newExportCmd())
52+
cmd.AddCommand(newImportCmd())
5153
return cmd
5254
}

0 commit comments

Comments
 (0)