From 69151be3bb09462aefa183caa7bb67f126f1f972 Mon Sep 17 00:00:00 2001 From: benduncan <5178243+benduncan@users.noreply.github.co> Date: Wed, 5 Nov 2025 22:28:05 +1100 Subject: [PATCH] Util helpers for image/AMI importer --- cmd/hive/cmd/admin.go | 6 +- hive/utils/utils.go | 146 +++++++++++++++++++++++++++++ hive/utils/utils_test.go | 103 ++++++++++++++++++-- tests/unit-test-disk-image-bad.raw | Bin 0 -> 1024 bytes tests/unit-test-disk-image.raw | Bin 0 -> 1024 bytes tests/unit-test-disk-image.tar | Bin 0 -> 10240 bytes tests/unit-test-disk-image.tar.gz | Bin 0 -> 268 bytes tests/unit-test-disk-image.tar.xz | Bin 0 -> 292 bytes tests/unit-test-disk-image.tgz | Bin 0 -> 268 bytes tests/unit-test-disk-image2.tgz | Bin 0 -> 394 bytes 10 files changed, 247 insertions(+), 8 deletions(-) create mode 100644 tests/unit-test-disk-image-bad.raw create mode 100644 tests/unit-test-disk-image.raw create mode 100644 tests/unit-test-disk-image.tar create mode 100644 tests/unit-test-disk-image.tar.gz create mode 100644 tests/unit-test-disk-image.tar.xz create mode 100644 tests/unit-test-disk-image.tgz create mode 100644 tests/unit-test-disk-image2.tgz diff --git a/cmd/hive/cmd/admin.go b/cmd/hive/cmd/admin.go index ac06d332b..d18de249d 100644 --- a/cmd/hive/cmd/admin.go +++ b/cmd/hive/cmd/admin.go @@ -127,7 +127,7 @@ func runimagesImportCmd(cmd *cobra.Command, args []string) { imageStat, err := os.Stat(imageFile) if err != nil { - fmt.Fprintf(os.Stderr, "File could not be found", err) + fmt.Fprintf(os.Stderr, "File could not be found %s", err) os.Exit(1) } @@ -138,7 +138,7 @@ func runimagesImportCmd(cmd *cobra.Command, args []string) { imageDir := fmt.Sprintf("%s/images", baseDir) if !fileExists(imageDir) { - fmt.Fprintf(os.Stderr, "Image directory does not exist. Base path specified correctly?", imageDir) + fmt.Fprintf(os.Stderr, "Image directory does not exist. Base path specified correctly? %s", imageDir) os.Exit(1) } @@ -220,6 +220,8 @@ func runimagesImportCmd(cmd *cobra.Command, args []string) { os.Exit(1) } + // Next, validate if the image is raw, tar, gz, xv, etc. We need to upload the raw image + } func runAdminInit(cmd *cobra.Command, args []string) { diff --git a/hive/utils/utils.go b/hive/utils/utils.go index ae2e34484..7c5ef74da 100644 --- a/hive/utils/utils.go +++ b/hive/utils/utils.go @@ -1,6 +1,7 @@ package utils import ( + "bufio" "bytes" "encoding/json" "encoding/xml" @@ -8,9 +9,11 @@ import ( "fmt" "log/slog" "os" + "os/exec" "path/filepath" "reflect" "strconv" + "strings" "syscall" "time" @@ -340,3 +343,146 @@ func ValidateKeyPairName(name string) error { return nil } + +// AMI / image extraction utils +func ExtractDiskImageFromFile(imagepath string, tmpdir string) (diskimage string, err error) { + + var args []string + var execCmd string + + // Confirm file exists + _, err = os.Stat(imagepath) + + if err != nil { + return + } + + // Extract the filepath + imagefile := filepath.Base(imagepath) + + // Provide the full path to the specified file + //imagedir, err := filepath.Abs(filepath.Dir(imagepath)) + + //if err != nil { + // return + //} + + // Already in raw/image formt, confirm the file contains a valid disk image/MBR + if strings.HasSuffix(imagefile, ".raw") || strings.HasSuffix(imagefile, ".img") { + + path, err := filepath.Abs(imagepath) + + if err != nil { + return path, err + } + + // Validate the specified filename is indeed a disk image / MBR + err = validateDiskImagePath(path) + + return path, err + + } else if strings.HasSuffix(imagefile, ".tar.xz") { + + args = []string{ + "xfvJ", + imagepath, + "-C", + tmpdir, + } + + execCmd = "tar" + + } else if strings.HasSuffix(imagefile, ".tar.gz") || strings.HasSuffix(imagefile, ".tgz") { + + args = []string{ + "xfvz", + imagepath, + "-C", + tmpdir, + } + + execCmd = "tar" + + } else if strings.HasSuffix(imagefile, ".tar") { + + args = []string{ + "xfv", + imagepath, + "-C", + tmpdir, + } + + execCmd = "tar" + + } else if strings.HasSuffix(imagefile, ".xz") { + + args = []string{ + "-d", + imagepath, + } + + execCmd = "xz" + + } else { + err = errors.New("unsupported filetype") + return + } + + cmd := exec.Command(execCmd, args...) + output, _ := cmd.Output() + + diskimage, err = extractDiskImagePath(tmpdir, output) + + return + +} + +func extractDiskImagePath(imagedir string, output []byte) (diskimage string, err error) { + + reader := bytes.NewReader(output) + + r := bufio.NewReader(reader) + + for { + line, err := r.ReadString('\n') + line = strings.Replace(line, "\n", "", 1) + + if strings.HasSuffix(line, ".raw") || strings.HasSuffix(line, ".img") { + diskimage := fmt.Sprintf("%s/%s", imagedir, line) + + err = validateDiskImagePath(diskimage) + + return diskimage, err + } + + if err != nil && err.Error() == "EOF" { + break + } + } + + return + +} + +func validateDiskImagePath(diskimage string) (err error) { + + args := []string{ + diskimage, + } + + cmd := exec.Command("file", args...) + output, _ := cmd.Output() + + filetype := strings.Split(string(output), ":") + + if len(filetype) > 1 { + + if strings.Contains(filetype[1], "DOS/MBR boot sector") || strings.Contains(filetype[1], "Linux ") { + return nil + } + + } + + return errors.New("no valid disk image found") + +} diff --git a/hive/utils/utils_test.go b/hive/utils/utils_test.go index 1429144c8..b1852e281 100644 --- a/hive/utils/utils_test.go +++ b/hive/utils/utils_test.go @@ -267,15 +267,15 @@ func TestGenerateErrorPayload(t *testing.T) { func TestValidateErrorPayload(t *testing.T) { tests := []struct { - name string - payload string - expectError bool + name string + payload string + expectError bool expectedCode string }{ { - name: "Valid error payload", - payload: `{"Code":"ValidationError","Message":null}`, - expectError: true, + name: "Valid error payload", + payload: `{"Code":"ValidationError","Message":null}`, + expectError: true, expectedCode: "ValidationError", }, { @@ -427,3 +427,94 @@ func TestStopProcess(t *testing.T) { err = StopProcess("nonexistent-process") assert.Error(t, err, "Should error when stopping non-existent process") } + +// Test file extraction process + +func TestExtractDiskImageFromFile(t *testing.T) { + + tmpDir, err := os.MkdirTemp("", "hive-utils-test-*") + + t.Log("Temp dir:", tmpDir) + + assert.NoError(t, err, "Temp dir should be created") + + // Sample .xz (fail) + imagePath, err := ExtractDiskImageFromFile("/tmp/file.xz", tmpDir) + + assert.Empty(t, imagePath, "Should be blank") + assert.Error(t, err, "Should error") + + // Sample incorrect image (fail) + imagePath, err = ExtractDiskImageFromFile("../../tests/ebs.json", tmpDir) + + assert.Empty(t, imagePath, "Should be blank") + assert.Error(t, err, "Should error") + assert.ErrorContains(t, err, "unsupported filetype") + + // Sample incorrect image (fail) + imagePath, err = ExtractDiskImageFromFile("../../tests/unit-test-disk-image-bad.raw", tmpDir) + + assert.NotEmpty(t, imagePath, "Should be blank") + assert.Error(t, err, "Should error") + assert.ErrorContains(t, err, "no valid disk image found") + + // Sample raw + imagePath, err = ExtractDiskImageFromFile("../../tests/unit-test-disk-image.raw", tmpDir) + + assert.NotEmpty(t, imagePath, "Should not be blank") + assert.Contains(t, imagePath, ".raw") + + assert.NoError(t, err, "Should not error") + + _, err = exec.LookPath("tar") + + if err == nil { + + // Sample .tgz + imagePath, err = ExtractDiskImageFromFile("../../tests/unit-test-disk-image2.tgz", tmpDir) + + assert.NotEmpty(t, imagePath, "Should not be blank") + assert.Contains(t, imagePath, ".img") + + assert.NoError(t, err, "Should not error") + + // Sample .tar + imagePath, err = ExtractDiskImageFromFile("../../tests/unit-test-disk-image.tar", tmpDir) + + assert.NotEmpty(t, imagePath, "Should not be blank") + assert.Contains(t, imagePath, ".raw") + + assert.NoError(t, err, "Should not error") + + // Sample .tar.gz + imagePath, err = ExtractDiskImageFromFile("../../tests/unit-test-disk-image.tar.gz", tmpDir) + + assert.NotEmpty(t, imagePath, "Should not be blank") + assert.Contains(t, imagePath, ".raw") + + assert.NoError(t, err, "Should not error") + + // Sample xz + imagePath, err = ExtractDiskImageFromFile("../../tests/unit-test-disk-image.tar.xz", tmpDir) + + assert.NotEmpty(t, imagePath, "Should not be blank") + assert.Contains(t, imagePath, ".raw") + + assert.NoError(t, err, "Should not error") + + // Sample tgz + imagePath, err = ExtractDiskImageFromFile("../../tests/unit-test-disk-image.tgz", tmpDir) + + assert.NotEmpty(t, imagePath, "Should not be blank") + assert.Contains(t, imagePath, ".raw") + + assert.NoError(t, err, "Should not error") + + } else { + t.Skip("tar command not found, skipping archive extraction tests") + } + + err = os.RemoveAll(tmpDir) + assert.NoError(t, err, "Could not remove temp dir") + +} diff --git a/tests/unit-test-disk-image-bad.raw b/tests/unit-test-disk-image-bad.raw new file mode 100644 index 0000000000000000000000000000000000000000..6e77c6c4f8f9e206c0e25074a3a8156964ef7677 GIT binary patch literal 1024 zcmYevEY8*|N-SrfAus>}GcywgAZ2K7YJg0Gq>O-=!O+yuz{u3d%+$n)!NAbK%+QcQ z!GMNtf*W00T#{I%pumunng^#>1-)$4=jgX$H$@w* zViQ}F#GcE(N&xPnMPLM#GlA_*ui z26mNzl~=G7Z{Pt0Phf9jXDt?jo!vT%8?iDN*rfUYV0iQK=Bs(blkBvS4kl6|?Ku|} z(QI1%I~GFU#8{(48#)_>qK$UOh|*O)YU(08AEZjj(dg{YyjknO64c~9&-1|deE9e7 z!@GEQPaU*6U6DZgekOaoelAvmGUMmemGAIUj&CkEp4w~sZ?~`aTif-_BC;tH3T>vr~O3h&D zPl?KDdp=rF>HFRcck8#R=PSLMzFtrc{_d)CqTJaVs|Uwk4kK(kqXxU*TIT16c%0f+ zU!ro>G%NBBgT2o_m^HuB?kr2%iKk?_Pa^_jAeUTil5&+8fxWOBVBHa!mceVvnm9G% zrokBf(MJtSY04p}ipQxidCi=4K!K#IOkU|Kp;w}8%>*mYwLP|Br%rMpX73hH>k3{2 zR2AoL_&dMHo^Q00023leDYgQdzhF0sjGjPyhfb&J>%m#Ao{g000001X)_vM1cE(N&xPnMPLM#GlA_*ui z26mNzl~=G7Z{Pt0Phf9jXDt?jo!vT%8?iDN*rfUYV0iQK=Bs(blkBvS4kl6|?Ku|} z(QI1%I~GFU#8{(48#)_>qK$UOh|*O)YU(08AEZjj(dg{YyjknO64c~9&-1|deE9e7 z!@GEQPaU*6U6DZgekOaoelAvmGUMmemGAIUj&CkEp4w~sZ?~`aTif-*L2%4j|_x`%&p0bhES-@iE z1~UVzx>3s&OKX9QS<;jsuhA$-oTe#uDMwYQx3nodNLFPjtcIguB|?&{M57vs(nxk6 zpk`GwrYKUDmA`y%&^Q3Ve{<(1y7soK)5k>n2d;MA7f;<@eEe!m!>Hyh3T10WJr~Lsb!Kf@ zRaOdCOO>H7)B4mi(NnLV-OT%Av4=)-*L&pOt_wnprkpYR9==C`JiU`e`{5qu0IvS& zVt