From f3493a427fed985f744d8f5207f139c0b7ea0d70 Mon Sep 17 00:00:00 2001 From: Fedor Korotkov Date: Thu, 16 Jul 2026 16:43:12 -0400 Subject: [PATCH] Select Linux test images by architecture --- .github/workflows/ci.yml | 6 +- internal/imageconstant/imageconstant.go | 35 ++++++++- internal/imageconstant/imageconstant_test.go | 78 +++++++++++++++++++ .../platformdependent/platformdependent.go | 74 +++++++++++++----- 4 files changed, 169 insertions(+), 24 deletions(-) create mode 100644 internal/imageconstant/imageconstant_test.go diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index db839d7..c8a1528 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -30,7 +30,7 @@ jobs: test-linux: name: Test (Linux) - runs-on: ubuntu-24.04-arm + runs-on: ubuntu-latest timeout-minutes: 60 steps: - uses: actions/checkout@v6 @@ -53,10 +53,10 @@ jobs: run: | curl --fail --location --retry 3 \ --output "$RUNNER_TEMP/cloud-hypervisor" \ - https://github.com/cloud-hypervisor/cloud-hypervisor/releases/latest/download/cloud-hypervisor-static-aarch64 + https://github.com/cloud-hypervisor/cloud-hypervisor/releases/latest/download/cloud-hypervisor-static chmod +x "$RUNNER_TEMP/cloud-hypervisor" - name: Pre-pull default Vetu image - run: vetu pull ghcr.io/cirruslabs/ubuntu:latest + run: vetu pull ghcr.io/cirruslabs/ubuntu-amd64:24.04 - name: Run tests run: go test -v -count=1 ./... diff --git a/internal/imageconstant/imageconstant.go b/internal/imageconstant/imageconstant.go index 0a11942..51502a5 100644 --- a/internal/imageconstant/imageconstant.go +++ b/internal/imageconstant/imageconstant.go @@ -1,6 +1,35 @@ package imageconstant -const ( - DefaultMacosImage = "ghcr.io/cirruslabs/macos-tahoe-base:latest" - DefaultLinuxImage = "ghcr.io/cirruslabs/ubuntu:latest" +import ( + "fmt" + + v1 "github.com/cirruslabs/orchard/pkg/resource/v1" ) + +const ( + DefaultMacosImage = "ghcr.io/cirruslabs/macos-tahoe-base:latest" + DefaultLinuxAMD64Image = "ghcr.io/cirruslabs/ubuntu-amd64:24.04" + DefaultLinuxARM64Image = "ghcr.io/cirruslabs/ubuntu:24.04" +) + +func DefaultImage(os v1.OS, architecture v1.Architecture) (string, error) { + switch os { + case v1.OSDarwin: + if architecture != v1.ArchitectureARM64 { + return "", fmt.Errorf("no default image for %s/%s", os, architecture) + } + + return DefaultMacosImage, nil + case v1.OSLinux: + switch architecture { + case v1.ArchitectureAMD64: + return DefaultLinuxAMD64Image, nil + case v1.ArchitectureARM64: + return DefaultLinuxARM64Image, nil + default: + return "", fmt.Errorf("no default image for %s/%s", os, architecture) + } + default: + return "", fmt.Errorf("no default image for %s/%s", os, architecture) + } +} diff --git a/internal/imageconstant/imageconstant_test.go b/internal/imageconstant/imageconstant_test.go new file mode 100644 index 0000000..01548fc --- /dev/null +++ b/internal/imageconstant/imageconstant_test.go @@ -0,0 +1,78 @@ +package imageconstant_test + +import ( + "testing" + + "github.com/cirruslabs/orchard/internal/imageconstant" + v1 "github.com/cirruslabs/orchard/pkg/resource/v1" + "github.com/stretchr/testify/require" +) + +func TestDefaultImage(t *testing.T) { + tests := []struct { + name string + os v1.OS + architecture v1.Architecture + expected string + }{ + { + name: "macOS ARM64", + os: v1.OSDarwin, + architecture: v1.ArchitectureARM64, + expected: imageconstant.DefaultMacosImage, + }, + { + name: "Linux AMD64", + os: v1.OSLinux, + architecture: v1.ArchitectureAMD64, + expected: imageconstant.DefaultLinuxAMD64Image, + }, + { + name: "Linux ARM64", + os: v1.OSLinux, + architecture: v1.ArchitectureARM64, + expected: imageconstant.DefaultLinuxARM64Image, + }, + } + + for _, test := range tests { + t.Run(test.name, func(t *testing.T) { + actual, err := imageconstant.DefaultImage(test.os, test.architecture) + + require.NoError(t, err) + require.Equal(t, test.expected, actual) + }) + } +} + +func TestDefaultImageRejectsUnsupportedPlatforms(t *testing.T) { + tests := []struct { + name string + os v1.OS + architecture v1.Architecture + }{ + { + name: "unsupported OS", + os: v1.OS("windows"), + architecture: v1.ArchitectureAMD64, + }, + { + name: "unsupported Linux architecture", + os: v1.OSLinux, + architecture: v1.Architecture("riscv64"), + }, + { + name: "unsupported macOS architecture", + os: v1.OSDarwin, + architecture: v1.ArchitectureAMD64, + }, + } + + for _, test := range tests { + t.Run(test.name, func(t *testing.T) { + _, err := imageconstant.DefaultImage(test.os, test.architecture) + + require.Error(t, err) + }) + } +} diff --git a/internal/tests/platformdependent/platformdependent.go b/internal/tests/platformdependent/platformdependent.go index 6646e68..0ea3fcc 100644 --- a/internal/tests/platformdependent/platformdependent.go +++ b/internal/tests/platformdependent/platformdependent.go @@ -2,6 +2,7 @@ package platformdependent import ( "context" + "fmt" "runtime" "github.com/cirruslabs/orchard/internal/imageconstant" @@ -13,20 +14,29 @@ import ( ) func VM(name string) *v1.VM { + hostOS, hostArchitecture, err := hostPlatform() + if err != nil { + panic(err) + } + + image, err := imageconstant.DefaultImage(hostOS, hostArchitecture) + if err != nil { + panic(err) + } + vm := &v1.VM{ Meta: v1.Meta{ Name: name, }, - Image: imageconstant.DefaultMacosImage, + Image: image, CPU: 4, Memory: 8 * 1024, Headless: true, } - if runtime.GOOS == "linux" { - vm.Image = imageconstant.DefaultLinuxImage - vm.OS = v1.OSLinux - vm.Arch = v1.Architecture(runtime.GOARCH) + if hostOS == v1.OSLinux { + vm.OS = hostOS + vm.Arch = hostArchitecture vm.Runtime = v1.RuntimeVetu } @@ -34,26 +44,54 @@ func VM(name string) *v1.VM { } func CloneDefaultImage(ctx context.Context, logger *zap.SugaredLogger, destination string) error { - var err error + hostOS, hostArchitecture, err := hostPlatform() + if err != nil { + return err + } - if runtime.GOOS == "linux" { - _, _, err = vetu.Vetu(ctx, logger, "clone", imageconstant.DefaultLinuxImage, destination) - } else { - _, _, err = tart.Tart(ctx, logger, "clone", imageconstant.DefaultMacosImage, destination) + image, err := imageconstant.DefaultImage(hostOS, hostArchitecture) + if err != nil { + return err + } + + switch hostOS { + case v1.OSLinux: + _, _, err = vetu.Vetu(ctx, logger, "clone", image, destination) + case v1.OSDarwin: + _, _, err = tart.Tart(ctx, logger, "clone", image, destination) + default: + return fmt.Errorf("unsupported host OS: %q", hostOS) } return err } func ListVMs(ctx context.Context, logger *zap.SugaredLogger) ([]vmmanager.VMInfo, error) { - var vms []vmmanager.VMInfo - var err error - - if runtime.GOOS == "linux" { - vms, err = vetu.List(ctx, logger) - } else { - vms, err = tart.List(ctx, logger) + hostOS, _, err := hostPlatform() + if err != nil { + return nil, err } - return vms, err + switch hostOS { + case v1.OSLinux: + return vetu.List(ctx, logger) + case v1.OSDarwin: + return tart.List(ctx, logger) + default: + return nil, fmt.Errorf("unsupported host OS: %q", hostOS) + } +} + +func hostPlatform() (v1.OS, v1.Architecture, error) { + hostOS, err := v1.NewOSFromString(runtime.GOOS) + if err != nil { + return "", "", err + } + + hostArchitecture, err := v1.NewArchitectureFromString(runtime.GOARCH) + if err != nil { + return "", "", err + } + + return hostOS, hostArchitecture, nil }