Machine refactor for QEMU/AppleHV

in preparation for adding hyper as a machine option, several common
functions needed to be moved specifically from qemu to a common area in
pkg/machine.  this usually involved functions and variables related to
using fcos as a machine image as well as its compression, artifact, and
image format.

[NO NEW TESTS NEEEDED]

Signed-off-by: Brent Baude <bbaude@redhat.com>
This commit is contained in:
Brent Baude
2023-02-20 13:39:42 -06:00
parent e838ad86b8
commit 43eb35a772
11 changed files with 241 additions and 117 deletions

View File

@ -0,0 +1,52 @@
//go:build arm64 && darwin
// +build arm64,darwin
package applehv
import "github.com/containers/podman/v4/pkg/machine"
type Virtualization struct {
artifact machine.Artifact
compression machine.ImageCompression
format machine.ImageFormat
}
func (v Virtualization) Artifact() machine.Artifact {
return machine.None
}
func (v Virtualization) CheckExclusiveActiveVM() (bool, string, error) {
return false, "", machine.ErrNotImplemented
}
func (v Virtualization) Compression() machine.ImageCompression {
return v.compression
}
func (v Virtualization) Format() machine.ImageFormat {
return v.format
}
func (v Virtualization) IsValidVMName(name string) (bool, error) {
return false, machine.ErrNotImplemented
}
func (v Virtualization) List(opts machine.ListOptions) ([]*machine.ListResponse, error) {
return nil, machine.ErrNotImplemented
}
func (v Virtualization) LoadVMByName(name string) (machine.VM, error) {
return nil, machine.ErrNotImplemented
}
func (v Virtualization) NewMachine(opts machine.InitOptions) (machine.VM, error) {
return nil, machine.ErrNotImplemented
}
func (v Virtualization) RemoveAndCleanMachines() error {
return machine.ErrNotImplemented
}
func (v Virtualization) VMType() string {
return vmtype
}

View File

@ -1,5 +1,5 @@
//go:build arm64 && !windows && !linux //go:build arm64 && darwin
// +build arm64,!windows,!linux // +build arm64,darwin
package applehv package applehv
@ -9,16 +9,17 @@ import (
"github.com/containers/podman/v4/pkg/machine" "github.com/containers/podman/v4/pkg/machine"
) )
type Provider struct{}
var ( var (
hvProvider = &Provider{}
// vmtype refers to qemu (vs libvirt, krun, etc). // vmtype refers to qemu (vs libvirt, krun, etc).
vmtype = "apple" vmtype = "apple"
) )
func GetVirtualizationProvider() machine.VirtProvider { func GetVirtualizationProvider() machine.VirtProvider {
return hvProvider return &Virtualization{
artifact: machine.None,
compression: machine.Xz,
format: machine.Qcow,
}
} }
const ( const (
@ -41,30 +42,4 @@ const (
dockerGlobal dockerGlobal
) )
func (p *Provider) NewMachine(opts machine.InitOptions) (machine.VM, error) {
return nil, machine.ErrNotImplemented
}
func (p *Provider) LoadVMByName(name string) (machine.VM, error) {
return nil, machine.ErrNotImplemented
}
func (p *Provider) List(opts machine.ListOptions) ([]*machine.ListResponse, error) {
return nil, machine.ErrNotImplemented
}
func (p *Provider) IsValidVMName(name string) (bool, error) {
return false, machine.ErrNotImplemented
}
func (p *Provider) CheckExclusiveActiveVM() (bool, string, error) {
return false, "", machine.ErrNotImplemented
}
func (p *Provider) RemoveAndCleanMachines() error {
return machine.ErrNotImplemented
}
func (p *Provider) VMType() string {
return vmtype
} }

View File

@ -48,11 +48,14 @@ const (
) )
type VirtProvider interface { type VirtProvider interface {
NewMachine(opts InitOptions) (VM, error) Artifact() Artifact
LoadVMByName(name string) (VM, error)
List(opts ListOptions) ([]*ListResponse, error)
IsValidVMName(name string) (bool, error)
CheckExclusiveActiveVM() (bool, string, error) CheckExclusiveActiveVM() (bool, string, error)
Compression() ImageCompression
Format() ImageFormat
IsValidVMName(name string) (bool, error)
List(opts ListOptions) ([]*ListResponse, error)
LoadVMByName(name string) (VM, error)
NewMachine(opts InitOptions) (VM, error)
RemoveAndCleanMachines() error RemoveAndCleanMachines() error
VMType() string VMType() string
} }
@ -72,10 +75,10 @@ var (
type Download struct { type Download struct {
Arch string Arch string
Artifact artifact Artifact Artifact
CompressionType string CompressionType string
CacheDir string CacheDir string
Format imageFormat Format ImageFormat
ImageName string ImageName string
LocalPath string LocalPath string
LocalUncompressedFile string LocalUncompressedFile string

View File

@ -12,6 +12,7 @@ import (
"time" "time"
"github.com/containers/podman/v4/pkg/machine" "github.com/containers/podman/v4/pkg/machine"
"github.com/containers/podman/v4/pkg/machine/qemu"
. "github.com/onsi/ginkgo" . "github.com/onsi/ginkgo"
. "github.com/onsi/gomega" . "github.com/onsi/gomega"
) )
@ -21,7 +22,7 @@ func TestMain(m *testing.M) {
} }
const ( const (
defaultStream string = "testing" defaultStream machine.FCOSStream = machine.Testing
) )
var ( var (
@ -43,7 +44,8 @@ func TestMachine(t *testing.T) {
} }
var _ = BeforeSuite(func() { var _ = BeforeSuite(func() {
fcd, err := machine.GetFCOSDownload(defaultStream, machine.Xz) qemuVP := qemu.GetVirtualizationProvider()
fcd, err := machine.GetFCOSDownload(qemuVP, defaultStream)
if err != nil { if err != nil {
Fail("unable to get virtual machine image") Fail("unable to get virtual machine image")
} }

View File

@ -22,9 +22,9 @@ import (
"github.com/sirupsen/logrus" "github.com/sirupsen/logrus"
) )
type imageCompression int64 type ImageCompression int64
type artifact int64 type Artifact int64
type imageFormat int64 type ImageFormat int64
const ( const (
// Used for testing the latest podman in fcos // Used for testing the latest podman in fcos
@ -33,17 +33,17 @@ const (
PodmanTestingHost = "fedorapeople.org" PodmanTestingHost = "fedorapeople.org"
PodmanTestingURL = "groups/podman/testing" PodmanTestingURL = "groups/podman/testing"
Xz imageCompression = iota Xz ImageCompression = iota
Zip Zip
Gz Gz
Bz2 Bz2
Qemu artifact = iota Qemu Artifact = iota
HyperV HyperV
None None
qcow imageFormat = iota Qcow ImageFormat = iota
vhdx Vhdx
Tar Tar
) )
@ -55,16 +55,16 @@ const (
// typed strongly // typed strongly
// //
func (a artifact) String() string { func (a Artifact) String() string {
if a == HyperV { if a == HyperV {
return "hyperv" return "hyperv"
} }
return "qemu" return "qemu"
} }
func (imf imageFormat) String() string { func (imf ImageFormat) String() string {
switch imf { switch imf {
case vhdx: case Vhdx:
return "vhdx.zip" return "vhdx.zip"
case Tar: case Tar:
return "tar.xz" return "tar.xz"
@ -72,7 +72,7 @@ func (imf imageFormat) String() string {
return "qcow2.xz" return "qcow2.xz"
} }
func (c imageCompression) String() string { func (c ImageCompression) String() string {
switch c { switch c {
case Gz: case Gz:
return "gz" return "gz"
@ -84,7 +84,7 @@ func (c imageCompression) String() string {
return "xz" return "xz"
} }
func compressionFromFile(path string) imageCompression { func compressionFromFile(path string) ImageCompression {
switch { switch {
case strings.HasSuffix(path, Bz2.String()): case strings.HasSuffix(path, Bz2.String()):
return Bz2 return Bz2
@ -100,8 +100,8 @@ type FcosDownload struct {
Download Download
} }
func NewFcosDownloader(vmType, vmName, imageStream string) (DistributionDownload, error) { func NewFcosDownloader(vmType, vmName string, imageStream FCOSStream, vp VirtProvider) (DistributionDownload, error) {
info, err := GetFCOSDownload(imageStream, Xz) info, err := GetFCOSDownload(vp, imageStream)
if err != nil { if err != nil {
return nil, err return nil, err
} }
@ -122,7 +122,7 @@ func NewFcosDownloader(vmType, vmName, imageStream string) (DistributionDownload
Arch: GetFcosArch(), Arch: GetFcosArch(),
Artifact: Qemu, Artifact: Qemu,
CacheDir: cacheDir, CacheDir: cacheDir,
Format: qcow, Format: Qcow,
ImageName: imageName, ImageName: imageName,
LocalPath: filepath.Join(cacheDir, imageName), LocalPath: filepath.Join(cacheDir, imageName),
Sha256sum: info.Sha256Sum, Sha256sum: info.Sha256Sum,
@ -194,41 +194,28 @@ func GetFcosArch() string {
// getStreamURL is a wrapper for the fcos.GetStream URL // getStreamURL is a wrapper for the fcos.GetStream URL
// so that we can inject a special stream and url for // so that we can inject a special stream and url for
// testing podman before it merges into fcos builds // testing podman before it merges into fcos builds
func getStreamURL(streamType string) url2.URL { func getStreamURL(streamType FCOSStream) url2.URL {
// For the podmanTesting stream type, we point to // For the podmanTesting stream type, we point to
// a custom url on fedorapeople.org // a custom url on fedorapeople.org
if streamType == podmanTesting { if streamType == PodmanTesting {
return url2.URL{ return url2.URL{
Scheme: "https", Scheme: "https",
Host: PodmanTestingHost, Host: PodmanTestingHost,
Path: fmt.Sprintf("%s/%s.json", PodmanTestingURL, "podman4"), Path: fmt.Sprintf("%s/%s.json", PodmanTestingURL, "podman4"),
} }
} }
return fedoracoreos.GetStreamURL(streamType) return fedoracoreos.GetStreamURL(streamType.String())
} }
// This should get Exported and stay put as it will apply to all fcos downloads // This should get Exported and stay put as it will apply to all fcos downloads
// getFCOS parses fedoraCoreOS's stream and returns the image download URL and the release version // getFCOS parses fedoraCoreOS's stream and returns the image download URL and the release version
func GetFCOSDownload(imageStream string, compression imageCompression) (*FcosDownloadInfo, error) { func GetFCOSDownload(vp VirtProvider, imageStream FCOSStream) (*FcosDownloadInfo, error) {
var ( var (
fcosstable stream.Stream fcosstable stream.Stream
altMeta release.Release altMeta release.Release
streamType string
) )
switch imageStream { streamurl := getStreamURL(imageStream)
case "podman-testing":
streamType = "podman-testing"
case "testing", "":
streamType = fedoracoreos.StreamTesting
case "next":
streamType = fedoracoreos.StreamNext
case "stable":
streamType = fedoracoreos.StreamStable
default:
return nil, fmt.Errorf("invalid stream %s: valid streams are `testing` and `stable`", imageStream)
}
streamurl := getStreamURL(streamType)
resp, err := http.Get(streamurl.String()) resp, err := http.Get(streamurl.String())
if err != nil { if err != nil {
return nil, err return nil, err
@ -242,7 +229,7 @@ func GetFCOSDownload(imageStream string, compression imageCompression) (*FcosDow
logrus.Error(err) logrus.Error(err)
} }
}() }()
if imageStream == podmanTesting { if imageStream == PodmanTesting {
if err := json.Unmarshal(body, &altMeta); err != nil { if err := json.Unmarshal(body, &altMeta); err != nil {
return nil, err return nil, err
} }
@ -251,7 +238,7 @@ func GetFCOSDownload(imageStream string, compression imageCompression) (*FcosDow
if !ok { if !ok {
return nil, fmt.Errorf("unable to pull VM image: no targetArch in stream") return nil, fmt.Errorf("unable to pull VM image: no targetArch in stream")
} }
qcow2, ok := arches.Media.Qemu.Artifacts[qcow.String()] qcow2, ok := arches.Media.Qemu.Artifacts[Qcow.String()]
if !ok { if !ok {
return nil, fmt.Errorf("unable to pull VM image: no qcow2.xz format in stream") return nil, fmt.Errorf("unable to pull VM image: no qcow2.xz format in stream")
} }
@ -260,7 +247,7 @@ func GetFCOSDownload(imageStream string, compression imageCompression) (*FcosDow
return &FcosDownloadInfo{ return &FcosDownloadInfo{
Location: disk.Location, Location: disk.Location,
Sha256Sum: disk.Sha256, Sha256Sum: disk.Sha256,
CompressionType: compression.String(), CompressionType: vp.Compression().String(),
}, nil }, nil
} }
@ -271,30 +258,69 @@ func GetFCOSDownload(imageStream string, compression imageCompression) (*FcosDow
if !ok { if !ok {
return nil, fmt.Errorf("unable to pull VM image: no targetArch in stream") return nil, fmt.Errorf("unable to pull VM image: no targetArch in stream")
} }
artifacts := arch.Artifacts upstreamArtifacts := arch.Artifacts
if artifacts == nil { if upstreamArtifacts == nil {
return nil, fmt.Errorf("unable to pull VM image: no artifact in stream") return nil, fmt.Errorf("unable to pull VM image: no artifact in stream")
} }
qemu, ok := artifacts[Qemu.String()] upstreamArtifact, ok := upstreamArtifacts[vp.Artifact().String()]
if !ok { if !ok {
return nil, fmt.Errorf("unable to pull VM image: no qemu artifact in stream") return nil, fmt.Errorf("unable to pull VM image: no %s artifact in stream", vp.Artifact().String())
} }
formats := qemu.Formats formats := upstreamArtifact.Formats
if formats == nil { if formats == nil {
return nil, fmt.Errorf("unable to pull VM image: no formats in stream") return nil, fmt.Errorf("unable to pull VM image: no formats in stream")
} }
qcow, ok := formats[qcow.String()] formatType, ok := formats[vp.Format().String()]
if !ok { if !ok {
return nil, fmt.Errorf("unable to pull VM image: no qcow2.xz format in stream") return nil, fmt.Errorf("unable to pull VM image: no %s format in stream", vp.Format().String())
} }
disk := qcow.Disk disk := formatType.Disk
if disk == nil { if disk == nil {
return nil, fmt.Errorf("unable to pull VM image: no disk in stream") return nil, fmt.Errorf("unable to pull VM image: no disk in stream")
} }
return &FcosDownloadInfo{ return &FcosDownloadInfo{
Location: disk.Location, Location: disk.Location,
Release: qemu.Release, Release: upstreamArtifact.Release,
Sha256Sum: disk.Sha256, Sha256Sum: disk.Sha256,
CompressionType: compression.String(), CompressionType: vp.Compression().String(),
}, nil }, nil
} }
type FCOSStream int64
const (
// FCOS streams
// Testing FCOS stream
Testing FCOSStream = iota
// Next FCOS stream
Next
// Stable FCOS stream
Stable
// Podman-Testing
PodmanTesting
)
// String is a helper func for fcos streams
func (st FCOSStream) String() string {
switch st {
case Testing:
return "testing"
case Next:
return "next"
case PodmanTesting:
return "podman-testing"
}
return "stable"
}
func FCOSStreamFromString(s string) FCOSStream {
switch s {
case Testing.String():
return Testing
case Next.String():
return Next
case PodmanTesting.String():
return PodmanTesting
}
return Stable
}

View File

@ -9,7 +9,7 @@ func Test_compressionFromFile(t *testing.T) {
var tests = []struct { var tests = []struct {
name string name string
args args args args
want imageCompression want ImageCompression
}{ }{
{ {
name: "xz", name: "xz",
@ -52,7 +52,7 @@ func Test_compressionFromFile(t *testing.T) {
func TestImageCompression_String(t *testing.T) { func TestImageCompression_String(t *testing.T) {
tests := []struct { tests := []struct {
name string name string
c imageCompression c ImageCompression
want string want string
}{ }{
{ {
@ -93,17 +93,17 @@ func TestImageCompression_String(t *testing.T) {
func TestImageFormat_String(t *testing.T) { func TestImageFormat_String(t *testing.T) {
tests := []struct { tests := []struct {
name string name string
imf imageFormat imf ImageFormat
want string want string
}{ }{
{ {
name: "vhdx.zip", name: "vhdx.zip",
imf: vhdx, imf: Vhdx,
want: "vhdx.zip", want: "vhdx.zip",
}, },
{ {
name: "qcow2", name: "qcow2",
imf: qcow, imf: Qcow,
want: "qcow2.xz", want: "qcow2.xz",
}, },
} }
@ -119,7 +119,7 @@ func TestImageFormat_String(t *testing.T) {
func Test_artifact_String(t *testing.T) { func Test_artifact_String(t *testing.T) {
tests := []struct { tests := []struct {
name string name string
a artifact a Artifact
want string want string
}{ }{
{ {
@ -141,3 +141,39 @@ func Test_artifact_String(t *testing.T) {
}) })
} }
} }
func TestFCOSStream_String(t *testing.T) {
tests := []struct {
name string
st FCOSStream
want string
}{
{
name: "testing",
st: Testing,
want: "testing",
},
{
name: "stable",
st: Stable,
want: "stable",
},
{
name: "next",
st: Next,
want: "next",
},
{
name: "default is stable",
st: 99,
want: "stable",
},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
if got := tt.st.String(); got != tt.want {
t.Errorf("String() = %v, want %v", got, tt.want)
}
})
}
}

View File

@ -6,20 +6,11 @@ import (
"github.com/containers/podman/v4/pkg/machine" "github.com/containers/podman/v4/pkg/machine"
) )
const ( type Virtualization struct {
// FCOS streams artifact machine.Artifact
// Testing FCOS stream compression machine.ImageCompression
Testing string = "testing" format machine.ImageFormat
// Next FCOS stream }
Next string = "next"
// Stable FCOS stream
Stable string = "stable"
// Max length of fully qualified socket path
)
type Virtualization struct{}
// Deprecated: MachineVMV1 is being deprecated in favor a more flexible and informative // Deprecated: MachineVMV1 is being deprecated in favor a more flexible and informative
// structure // structure

View File

@ -35,13 +35,17 @@ import (
) )
var ( var (
qemuProvider = &Virtualization{}
// vmtype refers to qemu (vs libvirt, krun, etc). // vmtype refers to qemu (vs libvirt, krun, etc).
// Could this be moved into Provider
vmtype = "qemu" vmtype = "qemu"
) )
func GetVirtualizationProvider() machine.VirtProvider { func GetVirtualizationProvider() machine.VirtProvider {
return qemuProvider return &Virtualization{
artifact: machine.Qemu,
compression: machine.Xz,
format: machine.Qcow,
}
} }
const ( const (
@ -252,10 +256,12 @@ func (v *MachineVM) Init(opts machine.InitOptions) (bool, error) {
v.Rootful = opts.Rootful v.Rootful = opts.Rootful
switch opts.ImagePath { switch opts.ImagePath {
case Testing, Next, Stable, "": // TODO these need to be re-typed as FCOSStreams
case machine.Testing.String(), machine.Next.String(), machine.Stable.String(), "":
// Get image as usual // Get image as usual
v.ImageStream = opts.ImagePath v.ImageStream = opts.ImagePath
dd, err := machine.NewFcosDownloader(vmtype, v.Name, opts.ImagePath) vp := GetVirtualizationProvider()
dd, err := machine.NewFcosDownloader(vmtype, v.Name, machine.FCOSStreamFromString(opts.ImagePath), vp)
if err != nil { if err != nil {
return false, err return false, err
@ -1221,6 +1227,18 @@ func (p *Virtualization) CheckExclusiveActiveVM() (bool, string, error) {
return false, "", nil return false, "", nil
} }
func (p *Virtualization) Artifact() machine.Artifact {
return p.artifact
}
func (p *Virtualization) Compression() machine.ImageCompression {
return p.compression
}
func (p *Virtualization) Format() machine.ImageFormat {
return p.format
}
// startHostNetworking runs a binary on the host system that allows users // startHostNetworking runs a binary on the host system that allows users
// to set up port forwarding to the podman virtual machine // to set up port forwarding to the podman virtual machine
func (v *MachineVM) startHostNetworking() (string, apiForwardingState, error) { func (v *MachineVM) startHostNetworking() (string, apiForwardingState, error) {

View File

@ -1,5 +1,5 @@
//go:build amd64 || arm64 //go:build windows
// +build amd64 arm64 // +build windows
package wsl package wsl

View File

@ -28,7 +28,6 @@ import (
) )
var ( var (
wslProvider = &Virtualization{}
// vmtype refers to qemu (vs libvirt, krun, etc) // vmtype refers to qemu (vs libvirt, krun, etc)
vmtype = "wsl" vmtype = "wsl"
) )
@ -204,7 +203,23 @@ const (
globalPipe = "docker_engine" globalPipe = "docker_engine"
) )
type Virtualization struct{} type Virtualization struct {
artifact machine.Artifact
compression machine.ImageCompression
format machine.ImageFormat
}
func (p *Virtualization) Artifact() machine.Artifact {
return p.artifact
}
func (p *Virtualization) Compression() machine.ImageCompression {
return p.compression
}
func (p *Virtualization) Format() machine.ImageFormat {
return p.format
}
type MachineVM struct { type MachineVM struct {
// ConfigPath is the path to the configuration file // ConfigPath is the path to the configuration file
@ -236,7 +251,11 @@ func (e *ExitCodeError) Error() string {
} }
func GetWSLProvider() machine.VirtProvider { func GetWSLProvider() machine.VirtProvider {
return wslProvider return &Virtualization{
artifact: machine.None,
compression: machine.Xz,
format: machine.Tar,
}
} }
// NewMachine initializes an instance of a wsl machine // NewMachine initializes an instance of a wsl machine

View File

@ -1,3 +1,6 @@
//go:build windows
// +build windows
package wsl package wsl
import ( import (
@ -12,11 +15,10 @@ import (
"unicode/utf16" "unicode/utf16"
"unsafe" "unsafe"
"github.com/containers/storage/pkg/homedir"
"github.com/sirupsen/logrus" "github.com/sirupsen/logrus"
"golang.org/x/sys/windows" "golang.org/x/sys/windows"
"golang.org/x/sys/windows/registry" "golang.org/x/sys/windows/registry"
"github.com/containers/storage/pkg/homedir"
) )
// nolint // nolint