Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion .github/workflows/ci.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@ permissions:
contents: read

env:
DAGGER_VERSION: "0.20.1"
DAGGER_VERSION: "0.20.6"

concurrency:
group: ${{ github.workflow }}-${{ github.ref }}
Expand Down
2 changes: 1 addition & 1 deletion .github/workflows/cut-release.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ permissions:
contents: write

env:
DAGGER_VERSION: "0.20.1"
DAGGER_VERSION: "0.20.6"

jobs:
create-release:
Expand Down
2 changes: 1 addition & 1 deletion .github/workflows/nightly.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@ permissions:
contents: write

env:
DAGGER_VERSION: "0.20.1"
DAGGER_VERSION: "0.20.6"

jobs:
check:
Expand Down
2 changes: 1 addition & 1 deletion .github/workflows/pr.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@ permissions:
pull-requests: read

env:
DAGGER_VERSION: "0.20.1"
DAGGER_VERSION: "0.20.6"

concurrency:
group: ${{ github.workflow }}-${{ github.ref }}
Expand Down
2 changes: 1 addition & 1 deletion .github/workflows/release.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ on:
types: [published]

env:
DAGGER_VERSION: "0.20.1"
DAGGER_VERSION: "0.20.6"

jobs:
build-linux:
Expand Down
2 changes: 1 addition & 1 deletion cmd/vmhost/platform_darwin_arm64.go
Original file line number Diff line number Diff line change
Expand Up @@ -63,7 +63,7 @@ func getPlatformConfig(_ string) *vm.QEMUPlatformConfig {
return &vm.QEMUPlatformConfig{
Accelerator: "hvf",
Binary: "qemu-system-aarch64",
MachineType: "virt",
MachineType: "virt,highmem=on",
EFISearchPaths: []string{
"{qemu_prefix}/share/qemu/edk2-aarch64-code.fd",
"/opt/homebrew/share/qemu/edk2-aarch64-code.fd",
Expand Down
36 changes: 0 additions & 36 deletions cmd/vmhost/platform_linux.go

This file was deleted.

22 changes: 22 additions & 0 deletions cmd/vmhost/platform_linux_amd64.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
//go:build linux && amd64

package vmhostcmder

import (
"context"
"fmt"
"log"

"github.com/papercomputeco/masterblaster/pkg/vm"
"github.com/papercomputeco/masterblaster/pkg/vmhost"
)

func bootAppleVirt(_ context.Context, _ string, _ *vm.Instance, _ *log.Logger) (vmhost.VMController, error) {
return nil, fmt.Errorf("apple Virtualization.framework is only available on macOS/Apple Silicon")
}

// getPlatformConfig returns the QEMU platform configuration for linux/amd64.
// Delegates to pkg/vm so the config lives in exactly one place.
func getPlatformConfig(_ string) *vm.QEMUPlatformConfig {
return vm.LinuxAMD64PlatformConfig()
}
22 changes: 22 additions & 0 deletions cmd/vmhost/platform_linux_arm64.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
//go:build linux && arm64

package vmhostcmder

import (
"context"
"fmt"
"log"

"github.com/papercomputeco/masterblaster/pkg/vm"
"github.com/papercomputeco/masterblaster/pkg/vmhost"
)

func bootAppleVirt(_ context.Context, _ string, _ *vm.Instance, _ *log.Logger) (vmhost.VMController, error) {
return nil, fmt.Errorf("apple Virtualization.framework is only available on macOS/Apple Silicon")
}

// getPlatformConfig returns the QEMU platform configuration for linux/arm64.
// Delegates to pkg/vm so the config lives in exactly one place.
func getPlatformConfig(_ string) *vm.QEMUPlatformConfig {
return vm.LinuxARM64PlatformConfig()
}
2 changes: 1 addition & 1 deletion dagger.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "masterblaster",
"engineVersion": "v0.20.1",
"engineVersion": "v0.20.6",
"sdk": {
"source": "go"
},
Expand Down
12 changes: 6 additions & 6 deletions flake.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 2 additions & 0 deletions go.mod
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ require (
github.com/google/go-containerregistry v0.21.0
github.com/google/uuid v1.6.0
github.com/klauspost/compress v1.18.4
github.com/mdlayher/vsock v1.2.1
github.com/onsi/ginkgo/v2 v2.28.1
github.com/onsi/gomega v1.39.1
github.com/posthog/posthog-go v1.10.0
Expand Down Expand Up @@ -51,6 +52,7 @@ require (
github.com/mattn/go-isatty v0.0.20 // indirect
github.com/mattn/go-localereader v0.0.1 // indirect
github.com/mattn/go-runewidth v0.0.16 // indirect
github.com/mdlayher/socket v0.4.1 // indirect
github.com/mitchellh/go-homedir v1.1.0 // indirect
github.com/mitchellh/hashstructure/v2 v2.0.2 // indirect
github.com/muesli/ansi v0.0.0-20230316100256-276c6243b2f6 // indirect
Expand Down
4 changes: 4 additions & 0 deletions go.sum
Original file line number Diff line number Diff line change
Expand Up @@ -112,6 +112,10 @@ github.com/mattn/go-localereader v0.0.1 h1:ygSAOl7ZXTx4RdPYinUpg6W99U8jWvWi9Ye2J
github.com/mattn/go-localereader v0.0.1/go.mod h1:8fBrzywKY7BI3czFoHkuzRoWE9C+EiG4R1k4Cjx5p88=
github.com/mattn/go-runewidth v0.0.16 h1:E5ScNMtiwvlvB5paMFdw9p4kSQzbXFikJ5SQO6TULQc=
github.com/mattn/go-runewidth v0.0.16/go.mod h1:Jdepj2loyihRzMpdS35Xk/zdY8IAYHsh153qUoGf23w=
github.com/mdlayher/socket v0.4.1 h1:eM9y2/jlbs1M615oshPQOHZzj6R6wMT7bX5NPiQvn2U=
github.com/mdlayher/socket v0.4.1/go.mod h1:cAqeGjoufqdxWkD7DkpyS+wcefOtmu5OQ8KuoJGIReA=
github.com/mdlayher/vsock v1.2.1 h1:pC1mTJTvjo1r9n9fbm7S1j04rCgCzhCOS5DY0zqHlnQ=
github.com/mdlayher/vsock v1.2.1/go.mod h1:NRfCibel++DgeMD8z/hP+PPTjlNJsdPOmxcnENvE+SE=
github.com/mfridman/tparse v0.18.0 h1:wh6dzOKaIwkUGyKgOntDW4liXSo37qg5AXbIhkMV3vE=
github.com/mfridman/tparse v0.18.0/go.mod h1:gEvqZTuCgEhPbYk/2lS3Kcxg1GmTxxU7kTC8DvP0i/A=
github.com/mitchellh/go-homedir v1.1.0 h1:lukF9ziXFxDFPkA1vsr5zpc1XuPDn/wFntq5mG+4E0Y=
Expand Down
111 changes: 94 additions & 17 deletions pkg/mixtapes/pull.go
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@ import (
"io"
"os"
"path/filepath"
"runtime"
"strings"

"github.com/klauspost/compress/zstd"
Expand Down Expand Up @@ -134,8 +135,14 @@ func resolveImage(desc *remote.Descriptor) (v1.Image, error) {
}
}

// resolveFromIndex parses an OCI index and selects the raw-format manifest.
// Falls back to the first manifest if no raw format is found.
// resolveFromIndex parses an OCI index and selects the best manifest for the
// running host, preferring raw disk format.
//
// Selection priority (see selectIndexManifest for the pure policy):
// 1. Platform matches host GOOS/GOARCH AND has a raw disk layer
// 2. Platform matches host GOOS/GOARCH (any format)
// 3. Any manifest with a raw disk layer (index didn't set Platform)
// 4. First manifest (last-resort fallback)
func resolveFromIndex(desc *remote.Descriptor) (v1.Image, error) {
idx, err := desc.ImageIndex()
if err != nil {
Expand All @@ -147,27 +154,97 @@ func resolveFromIndex(desc *remote.Descriptor) (v1.Image, error) {
return nil, fmt.Errorf("reading index manifest: %w", err)
}

if len(indexManifest.Manifests) == 0 {
return nil, fmt.Errorf("index manifest contains no entries")
hasRaw := func(h v1.Hash) (bool, error) {
img, err := idx.Image(h)
if err != nil {
return false, err
}
return hasRawDiskLayer(img), nil
}

// Try each manifest to find the raw format.
// The raw format is listed first by convention, but we check explicitly.
for _, m := range indexManifest.Manifests {
img, err := idx.Image(m.Digest)
if err != nil {
continue
digest, err := selectIndexManifest(indexManifest.Manifests, runtime.GOOS, runtime.GOARCH, hasRaw)
if err != nil {
return nil, err
}
return idx.Image(digest)
}

// hasRawDiskLayerFunc reports whether the manifest addressed by a given digest
// contains a raw disk layer. Parameterised so selectIndexManifest stays
// testable without a real OCI client.
type hasRawDiskLayerFunc func(v1.Hash) (bool, error)

// selectIndexManifest picks the best manifest digest from an OCI index's
// manifest descriptors for the given host GOOS/GOARCH. See resolveFromIndex
// for priority order.
func selectIndexManifest(
manifests []v1.Descriptor,
goos, goarch string,
hasRaw hasRawDiskLayerFunc,
) (v1.Hash, error) {
if len(manifests) == 0 {
return v1.Hash{}, fmt.Errorf("index manifest contains no entries")
}

platformMatch := func(p *v1.Platform) bool {
return p != nil && p.OS == goos && p.Architecture == goarch
}

var (
platformRaw, platformAny, anyRaw v1.Hash
hasT1, hasT2, hasT3 bool
)

for _, m := range manifests {
// Cache per-manifest raw-probe so we only load each manifest once.
var (
rawChecked, rawOK bool
)
checkRaw := func() bool {
if rawChecked {
return rawOK
}
rawChecked = true
ok, err := hasRaw(m.Digest)
rawOK = err == nil && ok
return rawOK
}

if platformMatch(m.Platform) {
if !hasT2 {
platformAny = m.Digest
hasT2 = true
}
if !hasT1 && checkRaw() {
platformRaw = m.Digest
hasT1 = true
}
}
if hasRawDiskLayer(img) {
ui.Info("Selected raw disk format from index")
return img, nil
if !hasT3 && checkRaw() {
anyRaw = m.Digest
hasT3 = true
}

// Early exit: we have the top-priority match.
if hasT1 {
break
}
}

// Fallback: use the first manifest.
ui.Warn("No raw disk format found in index, using first manifest")
firstDigest := indexManifest.Manifests[0].Digest
return idx.Image(firstDigest)
switch {
case hasT1:
ui.Info("Selected raw disk manifest for %s/%s from index", goos, goarch)
return platformRaw, nil
case hasT2:
ui.Warn("No raw format for %s/%s in index; using platform-matched manifest", goos, goarch)
return platformAny, nil
case hasT3:
ui.Warn("No %s/%s manifest in index; using any raw-format manifest (may not run on this host)", goos, goarch)
return anyRaw, nil
default:
ui.Warn("No raw format in index, using first manifest")
return manifests[0].Digest, nil
}
}

// hasRawDiskLayer checks whether an image contains a raw disk layer.
Expand Down
Loading
Loading