Skip to content

Commit

Permalink
rosetta: add option to ignore rosetta failure when installation cance…
Browse files Browse the repository at this point in the history
…lled

    this adds a ignore-if-missing option that allows vfkit to not fail if the rosetta installation is cancelled by the user.
    So far, if the rosetta installation was cancelled vfkit exited with an error. This behavior still exists if ignore-if-missing option is not set.
    This will be used by podman bc there could be a fallback and rosetta could also be not mandatory containers/podman#23153
  • Loading branch information
Luca Stocchi authored and Luca Stocchi committed Oct 4, 2024
1 parent 41bbe77 commit 39cc8c3
Show file tree
Hide file tree
Showing 3 changed files with 133 additions and 6 deletions.
8 changes: 7 additions & 1 deletion pkg/config/virtio.go
Original file line number Diff line number Diff line change
Expand Up @@ -75,7 +75,8 @@ type VirtioFs struct {
// RosettaShare configures rosetta support in the guest to run Intel binaries on Apple CPUs
type RosettaShare struct {
DirectorySharingConfig
InstallRosetta bool `json:"installRosetta"`
InstallRosetta bool `json:"installRosetta"`
IgnoreIfMissing bool `json:"ignoreIfMissing"`
}

// NVMExpressController configures a NVMe controller in the guest
Expand Down Expand Up @@ -639,6 +640,9 @@ func (dev *RosettaShare) ToCmdLine() ([]string, error) {
if dev.InstallRosetta {
builder.WriteString(",install")
}
if dev.IgnoreIfMissing {
builder.WriteString(",ignore-if-missing")
}

return []string{"--device", builder.String()}, nil
}
Expand All @@ -650,6 +654,8 @@ func (dev *RosettaShare) FromOptions(options []option) error {
dev.MountTag = option.value
case "install":
dev.InstallRosetta = true
case "ignore-if-missing":
dev.IgnoreIfMissing = true
default:
return fmt.Errorf("unknown option for rosetta share: %s", option.key)
}
Expand Down
20 changes: 15 additions & 5 deletions pkg/vf/rosetta_arm64.go
Original file line number Diff line number Diff line change
Expand Up @@ -2,22 +2,32 @@ package vf

import (
"fmt"
"strings"

"github.com/Code-Hex/vz/v3"
log "github.com/sirupsen/logrus"
)

func checkRosettaAvailability(install bool) error {
availability := vz.LinuxRosettaDirectoryShareAvailability()
var (
checkRosettaDirectoryShareAvailability = vz.LinuxRosettaDirectoryShareAvailability
doInstallRosetta = vz.LinuxRosettaDirectoryShareInstallRosetta
)

func (dev *RosettaShare) checkRosettaAvailability() error {
availability := checkRosettaDirectoryShareAvailability()
switch availability {
case vz.LinuxRosettaAvailabilityNotSupported:
return fmt.Errorf("rosetta is not supported")
case vz.LinuxRosettaAvailabilityNotInstalled:
if !install {
if !dev.InstallRosetta {
return fmt.Errorf("rosetta is not installed")
}
log.Debugf("installing rosetta")
if err := vz.LinuxRosettaDirectoryShareInstallRosetta(); err != nil {
if err := doInstallRosetta(); err != nil {
if dev.IgnoreIfMissing && strings.Contains(err.Error(), fmt.Sprintf("VZErrorDomain Code=%d", vz.ErrorOperationCancelled)) {
log.Debugf("user cancelled rosetta installation but ignoreIfMissing option is true")
return nil
}
return fmt.Errorf("failed to install rosetta: %w", err)
}
log.Debugf("rosetta installed")
Expand All @@ -32,7 +42,7 @@ func (dev *RosettaShare) toVz() (vz.DirectorySharingDeviceConfiguration, error)
if dev.MountTag == "" {
return nil, fmt.Errorf("missing mandatory 'mountTage' option for rosetta share")
}
if err := checkRosettaAvailability(dev.InstallRosetta); err != nil {
if err := dev.checkRosettaAvailability(); err != nil {
return nil, err
}

Expand Down
111 changes: 111 additions & 0 deletions pkg/vf/rosetta_arm64_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,111 @@
package vf

import (
"fmt"
"testing"

"github.com/Code-Hex/vz/v3"
"github.com/crc-org/vfkit/pkg/config"
"github.com/stretchr/testify/require"
)

type checkRosettaAvailabilityTest struct {
installRosetta bool
ignoreIfMissing bool
checkRosettaDirectoryShareAvailability func() vz.LinuxRosettaAvailability
doInstallRosetta func() error
errorValue string
}

var checkRosettaAvailabilityTests = map[string]checkRosettaAvailabilityTest{
"TestRosettaIsNotSupported": {
checkRosettaDirectoryShareAvailability: func() vz.LinuxRosettaAvailability {
return vz.LinuxRosettaAvailabilityNotSupported
},
errorValue: "rosetta is not supported",
},
"TestRosettaInstalled": {
checkRosettaDirectoryShareAvailability: func() vz.LinuxRosettaAvailability {
return vz.LinuxRosettaAvailabilityInstalled
},
},
"TestRosettaNotInstalled-NotToBeInstalled": {
installRosetta: false,
checkRosettaDirectoryShareAvailability: func() vz.LinuxRosettaAvailability {
return vz.LinuxRosettaAvailabilityNotInstalled
},
errorValue: "rosetta is not installed",
},
"TestRosettaNotInstalled-InstallationCancelledButIgnoreIfMissingFalse": {
installRosetta: true,
ignoreIfMissing: false,
checkRosettaDirectoryShareAvailability: func() vz.LinuxRosettaAvailability {
return vz.LinuxRosettaAvailabilityNotInstalled
},
doInstallRosetta: func() error {
return fmt.Errorf("VZErrorDomain Code=%d", vz.ErrorOperationCancelled)
},
errorValue: fmt.Sprintf("failed to install rosetta: VZErrorDomain Code=%d", vz.ErrorOperationCancelled),
},
"TestRosettaNotInstalled-InstallationCancelledButIgnoreIfMissingTrue": {
installRosetta: true,
ignoreIfMissing: true,
checkRosettaDirectoryShareAvailability: func() vz.LinuxRosettaAvailability {
return vz.LinuxRosettaAvailabilityNotInstalled
},
doInstallRosetta: func() error {
return fmt.Errorf("VZErrorDomain Code=%d", vz.ErrorOperationCancelled)
},
},
"TestRosettaNotInstalled-InstallationFailed": {
installRosetta: true,
ignoreIfMissing: true,
checkRosettaDirectoryShareAvailability: func() vz.LinuxRosettaAvailability {
return vz.LinuxRosettaAvailabilityNotInstalled
},
doInstallRosetta: func() error {
return fmt.Errorf("VZErrorDomain Code=%d", vz.ErrorInstallationFailed)
},
errorValue: fmt.Sprintf("failed to install rosetta: VZErrorDomain Code=%d", vz.ErrorInstallationFailed),
},
}

func TestCheckRosettaAvailability(t *testing.T) {
t.Run("name", func(t *testing.T) {
for name := range checkRosettaAvailabilityTests {
t.Run(name, func(t *testing.T) {
test := checkRosettaAvailabilityTests[name]
testCheckRosettaAvailability(t, &test)
})
}
})
}

func testCheckRosettaAvailability(t *testing.T, test *checkRosettaAvailabilityTest) {
rosetta :=
RosettaShare{
InstallRosetta: test.installRosetta,
IgnoreIfMissing: test.ignoreIfMissing,
DirectorySharingConfig: config.DirectorySharingConfig{
MountTag: "mount",
},
}

origCheckRosettaDirectoryShareAvailability := checkRosettaDirectoryShareAvailability
checkRosettaDirectoryShareAvailability = test.checkRosettaDirectoryShareAvailability
origDoInstallRosetta := doInstallRosetta
doInstallRosetta = test.doInstallRosetta
defer func() {
checkRosettaDirectoryShareAvailability = origCheckRosettaDirectoryShareAvailability
doInstallRosetta = origDoInstallRosetta
}()

err := rosetta.checkRosettaAvailability()

if test.errorValue != "" {
require.Error(t, err)
require.ErrorContains(t, err, test.errorValue)
} else {
require.NoError(t, err)
}
}

0 comments on commit 39cc8c3

Please sign in to comment.