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" "fmt"
"io" "io"
"os" "os"
"path/filepath"
"strings"
"github.com/cheggaaa/pb/v3" "github.com/cheggaaa/pb/v3"
"github.com/spf13/cobra" "github.com/spf13/cobra"
@ -123,6 +125,26 @@ func uploaderForCmdAWS(cmd *cobra.Command, targetArch string, bootMode *platform
return awscloudNewUploader(region, bucketName, amiName, opts) 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 { func cmdUpload(cmd *cobra.Command, args []string) error {
imagePath := args[0] imagePath := args[0]
@ -139,10 +161,14 @@ func cmdUpload(cmd *cobra.Command, args []string) error {
return err return err
} }
if targetArch == "" { if targetArch == "" {
// we could try to inspect the image here and get a targetArch = detectArchFromImagePath(imagePath)
// better guess if targetArch != "" {
targetArch = arch.Current().String() fmt.Fprintf(osStderr, "Note: using architecture %q based on image filename (use --arch to override)\n", targetArch)
fmt.Fprintf(osStderr, "WARNING: no upload architecture specified, using %q (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) 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) { func TestUploadWithAWSMock(t *testing.T) {
fakeDiskContent := "fake-raw-img" 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 regionName, bucketName, amiName string
var uploadOpts *awscloud.UploaderOptions var uploadOpts *awscloud.UploaderOptions
for _, tc := range []struct { for _, tc := range []struct {
fakeDiskName string
targetArchArg string targetArchArg string
expectedUploadArch string expectedUploadArch string
expectedWarning string
}{ }{
{"", "x86_64"}, // simple case: explicit target arch, no warning
{"aarch64", "aarch64"}, {"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 var fa fakeAwsUploader
restore := main.MockAwscloudNewUploader(func(region string, bucket string, ami string, opts *awscloud.UploaderOptions) (cloud.Uploader, error) { restore := main.MockAwscloudNewUploader(func(region string, bucket string, ami string, opts *awscloud.UploaderOptions) (cloud.Uploader, error) {
regionName = region regionName = region
@ -105,9 +111,8 @@ func TestUploadWithAWSMock(t *testing.T) {
assert.Contains(t, fakeStdout.String(), "--] 100.00%") assert.Contains(t, fakeStdout.String(), "--] 100.00%")
// warning was passed // warning was passed
if tc.targetArchArg == "" { assert.Equal(t, fakeStderr.String(), tc.expectedWarning)
assert.Equal(t, fakeStderr.String(), `WARNING: no upload architecture specified, using "x86_64" (use --arch to override)`+"\n")
}
} }
} }