mirror of
https://github.com/juanfont/headscale.git
synced 2025-08-10 13:46:46 +02:00
integration/hsic: extract artifacts as directories instead of tar files
- Change SaveProfile to extract to hostname-pprof/ directory - Change SaveMapResponses to extract to hostname-mapresponses/ directory - Change SaveDatabase to extract .db file directly (not as tar) - Add extractTarToDirectory helper function for tar extraction - This makes test artifacts directly browsable without manual extraction
This commit is contained in:
parent
4205d3c1b0
commit
e822877215
@ -1,6 +1,8 @@
|
|||||||
package hsic
|
package hsic
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"archive/tar"
|
||||||
|
"bytes"
|
||||||
"cmp"
|
"cmp"
|
||||||
"crypto/tls"
|
"crypto/tls"
|
||||||
"encoding/json"
|
"encoding/json"
|
||||||
@ -12,6 +14,7 @@ import (
|
|||||||
"net/netip"
|
"net/netip"
|
||||||
"os"
|
"os"
|
||||||
"path"
|
"path"
|
||||||
|
"path/filepath"
|
||||||
"sort"
|
"sort"
|
||||||
"strconv"
|
"strconv"
|
||||||
"strings"
|
"strings"
|
||||||
@ -561,22 +564,67 @@ func (t *HeadscaleInContainer) SaveMetrics(savePath string) error {
|
|||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// extractTarToDirectory extracts a tar archive to a directory.
|
||||||
|
func extractTarToDirectory(tarData []byte, targetDir string) error {
|
||||||
|
if err := os.MkdirAll(targetDir, 0755); err != nil {
|
||||||
|
return fmt.Errorf("failed to create directory %s: %w", targetDir, err)
|
||||||
|
}
|
||||||
|
|
||||||
|
tarReader := tar.NewReader(bytes.NewReader(tarData))
|
||||||
|
for {
|
||||||
|
header, err := tarReader.Next()
|
||||||
|
if err == io.EOF {
|
||||||
|
break
|
||||||
|
}
|
||||||
|
if err != nil {
|
||||||
|
return fmt.Errorf("failed to read tar header: %w", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Clean the path to prevent directory traversal
|
||||||
|
cleanName := filepath.Clean(header.Name)
|
||||||
|
if strings.Contains(cleanName, "..") {
|
||||||
|
continue // Skip potentially dangerous paths
|
||||||
|
}
|
||||||
|
|
||||||
|
targetPath := filepath.Join(targetDir, filepath.Base(cleanName))
|
||||||
|
|
||||||
|
switch header.Typeflag {
|
||||||
|
case tar.TypeDir:
|
||||||
|
// Create directory
|
||||||
|
if err := os.MkdirAll(targetPath, os.FileMode(header.Mode)); err != nil {
|
||||||
|
return fmt.Errorf("failed to create directory %s: %w", targetPath, err)
|
||||||
|
}
|
||||||
|
case tar.TypeReg:
|
||||||
|
// Create file
|
||||||
|
outFile, err := os.Create(targetPath)
|
||||||
|
if err != nil {
|
||||||
|
return fmt.Errorf("failed to create file %s: %w", targetPath, err)
|
||||||
|
}
|
||||||
|
|
||||||
|
if _, err := io.Copy(outFile, tarReader); err != nil {
|
||||||
|
outFile.Close()
|
||||||
|
return fmt.Errorf("failed to copy file contents: %w", err)
|
||||||
|
}
|
||||||
|
outFile.Close()
|
||||||
|
|
||||||
|
// Set file permissions
|
||||||
|
if err := os.Chmod(targetPath, os.FileMode(header.Mode)); err != nil {
|
||||||
|
return fmt.Errorf("failed to set file permissions: %w", err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
func (t *HeadscaleInContainer) SaveProfile(savePath string) error {
|
func (t *HeadscaleInContainer) SaveProfile(savePath string) error {
|
||||||
tarFile, err := t.FetchPath("/tmp/profile")
|
tarFile, err := t.FetchPath("/tmp/profile")
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
err = os.WriteFile(
|
targetDir := path.Join(savePath, t.hostname+"-pprof")
|
||||||
path.Join(savePath, t.hostname+".pprof.tar"),
|
return extractTarToDirectory(tarFile, targetDir)
|
||||||
tarFile,
|
|
||||||
os.ModePerm,
|
|
||||||
)
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
return nil
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func (t *HeadscaleInContainer) SaveMapResponses(savePath string) error {
|
func (t *HeadscaleInContainer) SaveMapResponses(savePath string) error {
|
||||||
@ -585,16 +633,8 @@ func (t *HeadscaleInContainer) SaveMapResponses(savePath string) error {
|
|||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
err = os.WriteFile(
|
targetDir := path.Join(savePath, t.hostname+"-mapresponses")
|
||||||
path.Join(savePath, t.hostname+".maps.tar"),
|
return extractTarToDirectory(tarFile, targetDir)
|
||||||
tarFile,
|
|
||||||
os.ModePerm,
|
|
||||||
)
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
return nil
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func (t *HeadscaleInContainer) SaveDatabase(savePath string) error {
|
func (t *HeadscaleInContainer) SaveDatabase(savePath string) error {
|
||||||
@ -603,16 +643,35 @@ func (t *HeadscaleInContainer) SaveDatabase(savePath string) error {
|
|||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
err = os.WriteFile(
|
// For database, extract the single SQLite file directly
|
||||||
path.Join(savePath, t.hostname+".db.tar"),
|
tarReader := tar.NewReader(bytes.NewReader(tarFile))
|
||||||
tarFile,
|
for {
|
||||||
os.ModePerm,
|
header, err := tarReader.Next()
|
||||||
)
|
if err == io.EOF {
|
||||||
if err != nil {
|
break
|
||||||
return err
|
}
|
||||||
|
if err != nil {
|
||||||
|
return fmt.Errorf("failed to read tar header: %w", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
if header.Typeflag == tar.TypeReg && strings.Contains(header.Name, ".sqlite") {
|
||||||
|
dbPath := path.Join(savePath, t.hostname+".db")
|
||||||
|
outFile, err := os.Create(dbPath)
|
||||||
|
if err != nil {
|
||||||
|
return fmt.Errorf("failed to create database file: %w", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
if _, err := io.Copy(outFile, tarReader); err != nil {
|
||||||
|
outFile.Close()
|
||||||
|
return fmt.Errorf("failed to copy database file: %w", err)
|
||||||
|
}
|
||||||
|
outFile.Close()
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return nil
|
return fmt.Errorf("database file not found in tar archive")
|
||||||
}
|
}
|
||||||
|
|
||||||
// Execute runs a command inside the Headscale container and returns the
|
// Execute runs a command inside the Headscale container and returns the
|
||||||
|
Loading…
Reference in New Issue
Block a user