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 000000000..6e77c6c4f Binary files /dev/null and b/tests/unit-test-disk-image-bad.raw differ diff --git a/tests/unit-test-disk-image.raw b/tests/unit-test-disk-image.raw new file mode 100644 index 000000000..6b44f7f3d Binary files /dev/null and b/tests/unit-test-disk-image.raw differ diff --git a/tests/unit-test-disk-image.tar b/tests/unit-test-disk-image.tar new file mode 100644 index 000000000..2d2504dbe Binary files /dev/null and b/tests/unit-test-disk-image.tar differ diff --git a/tests/unit-test-disk-image.tar.gz b/tests/unit-test-disk-image.tar.gz new file mode 100644 index 000000000..c05dc5a94 Binary files /dev/null and b/tests/unit-test-disk-image.tar.gz differ diff --git a/tests/unit-test-disk-image.tar.xz b/tests/unit-test-disk-image.tar.xz new file mode 100644 index 000000000..63c2557ff Binary files /dev/null and b/tests/unit-test-disk-image.tar.xz differ diff --git a/tests/unit-test-disk-image.tgz b/tests/unit-test-disk-image.tgz new file mode 100644 index 000000000..c05dc5a94 Binary files /dev/null and b/tests/unit-test-disk-image.tgz differ diff --git a/tests/unit-test-disk-image2.tgz b/tests/unit-test-disk-image2.tgz new file mode 100644 index 000000000..e2ca06da8 Binary files /dev/null and b/tests/unit-test-disk-image2.tgz differ