Skip to content
Open
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
27 changes: 27 additions & 0 deletions pkg/container/container_podman.go
Original file line number Diff line number Diff line change
@@ -1,15 +1,40 @@
package container

import (
"bytes"
"encoding/base64"
"fmt"
"os"
"path/filepath"
"runtime"
"strings"

logger "github.com/sirupsen/logrus"
)

// checkRosettaEnabled verifies if Rosetta is enabled in Podman on macOS
// This is a non-blocking check that provides a hint to the user if Rosetta is not configured
func checkRosettaEnabled() {
// Rosetta is only relevant on Apple Silicon (arm64); skip on Intel Macs
if runtime.GOARCH != "arm64" {
return
}

checkCmd := createCommand(PODMAN, "machine", "ssh", "ls /proc/sys/fs/binfmt_misc/")
var out bytes.Buffer
checkCmd.Stdout = &out
checkCmd.Stderr = nil

if err := checkCmd.Run(); err != nil {
// Silently skip if we can't check
return
}
Comment thread
feichashao marked this conversation as resolved.

if !strings.Contains(out.String(), "rosetta") {
logger.Warnf("Rosetta does not appear to be enabled in Podman. For better compatibility with x86_64 images on Apple Silicon, please configure Rosetta. See https://github.com/openshift/backplane-cli/blob/main/docs/macOS.md for setup instructions.")
}
}

type podmanLinux struct {
fileMountDir string
}
Expand Down Expand Up @@ -82,6 +107,8 @@ func podmanRunConsoleContainer(containerName string, port string, consoleArgs []
}

func (ce *podmanMac) RunConsoleContainer(containerName string, port string, consoleArgs []string, envVars []EnvVar) error {
// Check if Rosetta is enabled for better compatibility
checkRosettaEnabled()
return podmanRunConsoleContainer(containerName, port, consoleArgs, envVars)
}

Expand Down
79 changes: 77 additions & 2 deletions pkg/container/container_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ package container
import (
"os"
"os/exec"
"runtime"
"strings"
"testing"

Expand Down Expand Up @@ -100,6 +101,79 @@ var _ = Describe("console container implementation", func() {
})
})

Context("when checking Rosetta on macOS Podman", func() {
It("should execute podman machine ssh command on arm64", func() {
if os.Getenv("GOARCH") == "arm64" || (os.Getenv("GOARCH") == "" && runtime.GOARCH == "arm64") {
capturedCommands = nil
checkRosettaEnabled()
Expect(len(capturedCommands)).To(Equal(1))
command := capturedCommands[0]
Expect(command[0]).To(Equal(PODMAN))
Expect(command[1]).To(Equal("machine"))
Expect(command[2]).To(Equal("ssh"))
Expect(strings.Join(command[3:], " ")).To(Equal("ls /proc/sys/fs/binfmt_misc/"))
} else {
Skip("Rosetta check only runs on arm64 architecture")
}
})
It("should skip the check on non-arm64 architectures", func() {
if os.Getenv("GOARCH") != "arm64" && (os.Getenv("GOARCH") != "" || runtime.GOARCH != "arm64") {
capturedCommands = nil
checkRosettaEnabled()
Expect(len(capturedCommands)).To(Equal(0))
} else {
Skip("This test only runs on non-arm64 architectures")
}
})
})

Context("when running console container on macOS", func() {
ce := podmanMac{}
It("should check Rosetta before running the container on arm64", func() {
if os.Getenv("GOARCH") == "arm64" || (os.Getenv("GOARCH") == "" && runtime.GOARCH == "arm64") {
mockOcmInterface.EXPECT().GetPullSecret().Return(pullSecret, nil).AnyTimes()
capturedCommands = nil
args := []string{"arg1"}
envvars := []EnvVar{{Key: "testkey", Value: "testval"}}
err := ce.RunConsoleContainer("console", "8888", args, envvars)
Expect(err).To(BeNil())
// Should have 2 commands: 1 for Rosetta check, 1 for running container
Expect(len(capturedCommands)).To(BeNumerically(">=", 2))
// First command should be Rosetta check
rosettaCheckCmd := capturedCommands[0]
Expect(rosettaCheckCmd[0]).To(Equal(PODMAN))
Expect(rosettaCheckCmd[1]).To(Equal("machine"))
Expect(rosettaCheckCmd[2]).To(Equal("ssh"))
// Last command should be the actual container run
runCmd := capturedCommands[len(capturedCommands)-1]
fullCommand := strings.Join(runCmd, " ")
Expect(fullCommand).To(ContainSubstring("arg1"))
Expect(fullCommand).To(ContainSubstring("--env"))
Expect(fullCommand).To(ContainSubstring("testkey=testval"))
} else {
Skip("Rosetta check only runs on arm64 architecture")
}
})
It("should skip Rosetta check on non-arm64 architectures", func() {
if os.Getenv("GOARCH") != "arm64" && (os.Getenv("GOARCH") != "" || runtime.GOARCH != "arm64") {
mockOcmInterface.EXPECT().GetPullSecret().Return(pullSecret, nil).AnyTimes()
capturedCommands = nil
args := []string{"arg1"}
envvars := []EnvVar{{Key: "testkey", Value: "testval"}}
err := ce.RunConsoleContainer("console", "8888", args, envvars)
Expect(err).To(BeNil())
// Should only have 1 command for running container (no Rosetta check)
Expect(len(capturedCommands)).To(Equal(1))
fullCommand := strings.Join(capturedCommands[0], " ")
Expect(fullCommand).To(ContainSubstring("arg1"))
Expect(fullCommand).To(ContainSubstring("--env"))
Expect(fullCommand).To(ContainSubstring("testkey=testval"))
} else {
Skip("This test only runs on non-arm64 architectures")
}
})
})

Context("when running console container", func() {
ce := podmanMac{}
It("should pass argments and environment variable if specified", func() {
Expand All @@ -109,8 +183,9 @@ var _ = Describe("console container implementation", func() {
envvars := []EnvVar{{Key: "testkey", Value: "testval"}}
err := ce.RunConsoleContainer("console", "8888", args, envvars)
Expect(err).To(BeNil())
Expect(len(capturedCommands)).To(Equal(1))
fullCommand := strings.Join(capturedCommands[0], " ")
Expect(len(capturedCommands)).To(BeNumerically(">=", 1))
// Find the run command (should be the last one)
fullCommand := strings.Join(capturedCommands[len(capturedCommands)-1], " ")
// arg
Expect(fullCommand).To(ContainSubstring("arg1"))
// env var
Expand Down