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.
This commit is contained in:
Michael Vogt 2025-06-03 13:08:36 +02:00 committed by Achilleas Koutsou
parent 03613a3fb3
commit dd13153b8b
2 changed files with 44 additions and 13 deletions

View file

@ -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/<disro>-<imgtype>-<arch>.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)

View file

@ -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)
}
}