From dd13153b8b044c14763e29f8f02075d2e85ef166 Mon Sep 17 00:00:00 2001 From: Michael Vogt Date: Tue, 3 Jun 2025 13:08:36 +0200 Subject: [PATCH] upload: try to auto-detect the upload arch from the filename This is another convenience feature for the `image-builder upload` command: when we do not know the target architecture try to guess it from the filename. This is not perfect but it will help a lot of users and should be fine until the day we go and inspect the image. --- cmd/image-builder/upload.go | 34 ++++++++++++++++++++++++++++---- cmd/image-builder/upload_test.go | 23 ++++++++++++--------- 2 files changed, 44 insertions(+), 13 deletions(-) diff --git a/cmd/image-builder/upload.go b/cmd/image-builder/upload.go index 5357ac6..64f09e2 100644 --- a/cmd/image-builder/upload.go +++ b/cmd/image-builder/upload.go @@ -6,6 +6,8 @@ import ( "fmt" "io" "os" + "path/filepath" + "strings" "github.com/cheggaaa/pb/v3" "github.com/spf13/cobra" @@ -123,6 +125,26 @@ func uploaderForCmdAWS(cmd *cobra.Command, targetArch string, bootMode *platform return awscloudNewUploader(region, bucketName, amiName, opts) } +func detectArchFromImagePath(imagePath string) string { + // This detection is currently rather naive, we just look for + // the file name and try to infer from that. We could extend + // this to smartz like inspect the image via libguestfs or + // add extra metadata to the image. But for now this is what + // we got. + + // imagePath by default looks like + // /path/to/--.img.xz + // so try to infer the arch + baseName := filepath.Base(imagePath) + nameNoEx := strings.SplitN(baseName, ".", -1)[0] + frags := strings.Split(nameNoEx, "-") + maybeArch := frags[len(frags)-1] + if a, err := arch.FromString(maybeArch); err == nil { + return a.String() + } + return "" +} + func cmdUpload(cmd *cobra.Command, args []string) error { imagePath := args[0] @@ -139,10 +161,14 @@ func cmdUpload(cmd *cobra.Command, args []string) error { return err } if targetArch == "" { - // we could try to inspect the image here and get a - // better guess - targetArch = arch.Current().String() - fmt.Fprintf(osStderr, "WARNING: no upload architecture specified, using %q (use --arch to override)\n", targetArch) + targetArch = detectArchFromImagePath(imagePath) + if targetArch != "" { + fmt.Fprintf(osStderr, "Note: using architecture %q based on image filename (use --arch to override)\n", targetArch) + } + if targetArch == "" { + targetArch = arch.Current().String() + fmt.Fprintf(osStderr, "WARNING: no upload architecture specified, using %q (use --arch to override)\n", targetArch) + } } uploader, err := uploaderFor(cmd, uploadTo, targetArch, nil) diff --git a/cmd/image-builder/upload_test.go b/cmd/image-builder/upload_test.go index 66a1e10..c20c4f4 100644 --- a/cmd/image-builder/upload_test.go +++ b/cmd/image-builder/upload_test.go @@ -48,20 +48,26 @@ func (fa *fakeAwsUploader) UploadAndRegister(r io.Reader, status io.Writer) erro func TestUploadWithAWSMock(t *testing.T) { fakeDiskContent := "fake-raw-img" - fakeImageFilePath := filepath.Join(t.TempDir(), "disk.raw") - err := os.WriteFile(fakeImageFilePath, []byte(fakeDiskContent), 0644) - assert.NoError(t, err) - var regionName, bucketName, amiName string var uploadOpts *awscloud.UploaderOptions for _, tc := range []struct { + fakeDiskName string targetArchArg string expectedUploadArch string + expectedWarning string }{ - {"", "x86_64"}, - {"aarch64", "aarch64"}, + // simple case: explicit target arch, no warning + {"fake-disk.img", "aarch64", "aarch64", ""}, + // no target arch, detectable from filename: add note + {"centos-9-ami-aarch64.img", "", "aarch64", `Note: using architecture "aarch64" based on image filename (use --arch to override)` + "\n"}, + // no target arch, not detectable form filename: we warn and expect host arch + {"fake-disk.img", "", arch.Current().String(), fmt.Sprintf("WARNING: no upload architecture specified, using %q (use --arch to override)\n", arch.Current().String())}, } { + fakeImageFilePath := filepath.Join(t.TempDir(), tc.fakeDiskName) + err := os.WriteFile(fakeImageFilePath, []byte(fakeDiskContent), 0644) + assert.NoError(t, err) + var fa fakeAwsUploader restore := main.MockAwscloudNewUploader(func(region string, bucket string, ami string, opts *awscloud.UploaderOptions) (cloud.Uploader, error) { regionName = region @@ -105,9 +111,8 @@ func TestUploadWithAWSMock(t *testing.T) { assert.Contains(t, fakeStdout.String(), "--] 100.00%") // warning was passed - if tc.targetArchArg == "" { - assert.Equal(t, fakeStderr.String(), `WARNING: no upload architecture specified, using "x86_64" (use --arch to override)`+"\n") - } + assert.Equal(t, fakeStderr.String(), tc.expectedWarning) + } }