diff --git a/cmd/podman/machine/platform.go b/cmd/podman/machine/platform.go index 3a7e56ac75..9e951326fe 100644 --- a/cmd/podman/machine/platform.go +++ b/cmd/podman/machine/platform.go @@ -8,6 +8,6 @@ import ( "github.com/containers/podman/v4/pkg/machine/qemu" ) -func GetSystemDefaultProvider() machine.Provider { +func GetSystemDefaultProvider() machine.VirtProvider { return qemu.GetVirtualizationProvider() } diff --git a/cmd/podman/machine/platform_windows.go b/cmd/podman/machine/platform_windows.go index cdbc52459a..809725f8f2 100644 --- a/cmd/podman/machine/platform_windows.go +++ b/cmd/podman/machine/platform_windows.go @@ -5,6 +5,6 @@ import ( "github.com/containers/podman/v4/pkg/machine/wsl" ) -func GetSystemDefaultProvider() machine.Provider { +func GetSystemDefaultProvider() machine.VirtProvider { return wsl.GetWSLProvider() } diff --git a/pkg/machine/applehv/machine.go b/pkg/machine/applehv/machine.go index bc4a805d46..742bff6d51 100644 --- a/pkg/machine/applehv/machine.go +++ b/pkg/machine/applehv/machine.go @@ -17,7 +17,7 @@ var ( vmtype = "apple" ) -func GetVirtualizationProvider() machine.Provider { +func GetVirtualizationProvider() machine.VirtProvider { return hvProvider } diff --git a/pkg/machine/config.go b/pkg/machine/config.go index 3045fdd625..1d77ad4cc6 100644 --- a/pkg/machine/config.go +++ b/pkg/machine/config.go @@ -47,7 +47,7 @@ const ( DefaultMachineName string = "podman-machine-default" ) -type Provider interface { +type VirtProvider interface { NewMachine(opts InitOptions) (VM, error) LoadVMByName(name string) (VM, error) List(opts ListOptions) ([]*ListResponse, error) @@ -72,10 +72,10 @@ var ( type Download struct { Arch string - Artifact string + Artifact artifact CompressionType string CacheDir string - Format string + Format imageFormat ImageName string LocalPath string LocalUncompressedFile string diff --git a/pkg/machine/e2e/machine_test.go b/pkg/machine/e2e/machine_test.go index 6c460621d6..dd5031fde2 100644 --- a/pkg/machine/e2e/machine_test.go +++ b/pkg/machine/e2e/machine_test.go @@ -43,7 +43,7 @@ func TestMachine(t *testing.T) { } var _ = BeforeSuite(func() { - fcd, err := machine.GetFCOSDownload(defaultStream) + fcd, err := machine.GetFCOSDownload(defaultStream, machine.Xz) if err != nil { Fail("unable to get virtual machine image") } diff --git a/pkg/machine/fcos.go b/pkg/machine/fcos.go index 16f845a786..309235840a 100644 --- a/pkg/machine/fcos.go +++ b/pkg/machine/fcos.go @@ -18,17 +18,13 @@ import ( "github.com/coreos/stream-metadata-go/fedoracoreos" "github.com/coreos/stream-metadata-go/release" "github.com/coreos/stream-metadata-go/stream" - digest "github.com/opencontainers/go-digest" "github.com/sirupsen/logrus" ) -// These should eventually be moved into machine/qemu as -// they are specific to running qemu -var ( - artifact = "qemu" - Format = "qcow2.xz" -) +type imageCompression int64 +type artifact int64 +type imageFormat int64 const ( // Used for testing the latest podman in fcos @@ -36,14 +32,76 @@ const ( podmanTesting = "podman-testing" PodmanTestingHost = "fedorapeople.org" PodmanTestingURL = "groups/podman/testing" + + Xz imageCompression = iota + Zip + Gz + Bz2 + + Qemu artifact = iota + HyperV + None + + qcow imageFormat = iota + vhdx + Tar ) +// +// TODO artifact, imageformat, and imagecompression should be probably combined into some sort +// of object which can "produce" the correct output we are looking for bc things like +// image format contain both the image type AND the compression. This work can be done before +// or after the hyperv work. For now, my preference is to NOT change things and just get things +// typed strongly +// + +func (a artifact) String() string { + if a == HyperV { + return "hyperv" + } + return "qemu" +} + +func (imf imageFormat) String() string { + switch imf { + case vhdx: + return "vhdx.zip" + case Tar: + return "tar.xz" + } + return "qcow2.xz" +} + +func (c imageCompression) String() string { + switch c { + case Gz: + return "gz" + case Zip: + return "zip" + case Bz2: + return "bz2" + } + return "xz" +} + +func compressionFromFile(path string) imageCompression { + switch { + case strings.HasSuffix(path, Bz2.String()): + return Bz2 + case strings.HasSuffix(path, Gz.String()): + return Gz + case strings.HasSuffix(path, Zip.String()): + return Zip + } + return Xz +} + type FcosDownload struct { Download } func NewFcosDownloader(vmType, vmName, imageStream string) (DistributionDownload, error) { - info, err := GetFCOSDownload(imageStream) + info, err := GetFCOSDownload(imageStream, Xz) if err != nil { return nil, err } @@ -62,9 +120,9 @@ func NewFcosDownloader(vmType, vmName, imageStream string) (DistributionDownload fcd := FcosDownload{ Download: Download{ Arch: GetFcosArch(), - Artifact: artifact, + Artifact: Qemu, CacheDir: cacheDir, - Format: Format, + Format: qcow, ImageName: imageName, LocalPath: filepath.Join(cacheDir, imageName), Sha256sum: info.Sha256Sum, @@ -151,7 +209,7 @@ func getStreamURL(streamType string) url2.URL { // 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 -func GetFCOSDownload(imageStream string) (*FcosDownloadInfo, error) { +func GetFCOSDownload(imageStream string, compression imageCompression) (*FcosDownloadInfo, error) { var ( fcosstable stream.Stream altMeta release.Release @@ -193,7 +251,7 @@ func GetFCOSDownload(imageStream string) (*FcosDownloadInfo, error) { if !ok { return nil, fmt.Errorf("unable to pull VM image: no targetArch in stream") } - qcow2, ok := arches.Media.Qemu.Artifacts["qcow2.xz"] + qcow2, ok := arches.Media.Qemu.Artifacts[qcow.String()] if !ok { return nil, fmt.Errorf("unable to pull VM image: no qcow2.xz format in stream") } @@ -202,7 +260,7 @@ func GetFCOSDownload(imageStream string) (*FcosDownloadInfo, error) { return &FcosDownloadInfo{ Location: disk.Location, Sha256Sum: disk.Sha256, - CompressionType: "xz", + CompressionType: compression.String(), }, nil } @@ -217,7 +275,7 @@ func GetFCOSDownload(imageStream string) (*FcosDownloadInfo, error) { if artifacts == nil { return nil, fmt.Errorf("unable to pull VM image: no artifact in stream") } - qemu, ok := artifacts[artifact] + qemu, ok := artifacts[Qemu.String()] if !ok { return nil, fmt.Errorf("unable to pull VM image: no qemu artifact in stream") } @@ -225,7 +283,7 @@ func GetFCOSDownload(imageStream string) (*FcosDownloadInfo, error) { if formats == nil { return nil, fmt.Errorf("unable to pull VM image: no formats in stream") } - qcow, ok := formats[Format] + qcow, ok := formats[qcow.String()] if !ok { return nil, fmt.Errorf("unable to pull VM image: no qcow2.xz format in stream") } @@ -237,6 +295,6 @@ func GetFCOSDownload(imageStream string) (*FcosDownloadInfo, error) { Location: disk.Location, Release: qemu.Release, Sha256Sum: disk.Sha256, - CompressionType: "xz", + CompressionType: compression.String(), }, nil } diff --git a/pkg/machine/fcos_test.go b/pkg/machine/fcos_test.go new file mode 100644 index 0000000000..b9a8932baf --- /dev/null +++ b/pkg/machine/fcos_test.go @@ -0,0 +1,143 @@ +package machine + +import "testing" + +func Test_compressionFromFile(t *testing.T) { + type args struct { + path string + } + var tests = []struct { + name string + args args + want imageCompression + }{ + { + name: "xz", + args: args{ + path: "/tmp/foo.xz", + }, + want: Xz, + }, + { + name: "gzip", + args: args{ + path: "/tmp/foo.gz", + }, + want: Gz, + }, + { + name: "bz2", + args: args{ + path: "/tmp/foo.bz2", + }, + want: Bz2, + }, + { + name: "default is xz", + args: args{ + path: "/tmp/foo", + }, + want: Xz, + }, + } + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + if got := compressionFromFile(tt.args.path); got != tt.want { + t.Errorf("compressionFromFile() = %v, want %v", got, tt.want) + } + }) + } +} + +func TestImageCompression_String(t *testing.T) { + tests := []struct { + name string + c imageCompression + want string + }{ + { + name: "xz", + c: Xz, + want: "xz", + }, + { + name: "gz", + c: Gz, + want: "gz", + }, + { + name: "bz2", + c: Bz2, + want: "bz2", + }, + { + name: "zip", + c: Zip, + want: "zip", + }, + { + name: "xz is default", + c: 99, + want: "xz", + }, + } + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + if got := tt.c.String(); got != tt.want { + t.Errorf("String() = %v, want %v", got, tt.want) + } + }) + } +} + +func TestImageFormat_String(t *testing.T) { + tests := []struct { + name string + imf imageFormat + want string + }{ + { + name: "vhdx.zip", + imf: vhdx, + want: "vhdx.zip", + }, + { + name: "qcow2", + imf: qcow, + want: "qcow2.xz", + }, + } + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + if got := tt.imf.String(); got != tt.want { + t.Errorf("String() = %v, want %v", got, tt.want) + } + }) + } +} + +func Test_artifact_String(t *testing.T) { + tests := []struct { + name string + a artifact + want string + }{ + { + name: "qemu", + a: Qemu, + want: "qemu", + }, + { + name: "hyperv", + a: HyperV, + want: "hyperv", + }, + } + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + if got := tt.a.String(); got != tt.want { + t.Errorf("String() = %v, want %v", got, tt.want) + } + }) + } +} diff --git a/pkg/machine/pull.go b/pkg/machine/pull.go index b7289433b6..f59f681e1c 100644 --- a/pkg/machine/pull.go +++ b/pkg/machine/pull.go @@ -86,19 +86,9 @@ func supportedURL(path string) (url *url2.URL) { } func (d Download) GetLocalUncompressedFile(dataDir string) string { - var ( - extension string - ) - switch { - case strings.HasSuffix(d.LocalPath, ".bz2"): - extension = ".bz2" - case strings.HasSuffix(d.LocalPath, ".gz"): - extension = ".gz" - case strings.HasSuffix(d.LocalPath, ".xz"): - extension = ".xz" - } + extension := compressionFromFile(dataDir) uncompressedFilename := d.VMName + "_" + d.ImageName - return filepath.Join(dataDir, strings.TrimSuffix(uncompressedFilename, extension)) + return filepath.Join(dataDir, strings.TrimSuffix(uncompressedFilename, extension.String())) } func (g GenericDownload) Get() *Download { @@ -224,11 +214,11 @@ func Decompress(localPath, uncompressedPath string) error { // Will error out if file without .xz already exists // Maybe extracting then renaming is a good idea here.. -// depends on xz: not pre-installed on mac, so it becomes a brew dependency +// depends on Xz: not pre-installed on mac, so it becomes a brew dependency func decompressXZ(src string, output io.WriteCloser) error { var read io.Reader var cmd *exec.Cmd - // Prefer xz utils for fastest performance, fallback to go xi2 impl + // Prefer Xz utils for fastest performance, fallback to go xi2 impl if _, err := exec.LookPath("xz"); err == nil { cmd = exec.Command("xz", "-d", "-c", "-k", src) read, err = cmd.StdoutPipe() diff --git a/pkg/machine/qemu/config.go b/pkg/machine/qemu/config.go index 8081727f62..79f90ed901 100644 --- a/pkg/machine/qemu/config.go +++ b/pkg/machine/qemu/config.go @@ -19,7 +19,7 @@ const ( ) -type Provider struct{} +type Virtualization struct{} // Deprecated: MachineVMV1 is being deprecated in favor a more flexible and informative // structure diff --git a/pkg/machine/qemu/machine.go b/pkg/machine/qemu/machine.go index 4f25b4d26c..e87e6e789c 100644 --- a/pkg/machine/qemu/machine.go +++ b/pkg/machine/qemu/machine.go @@ -35,12 +35,12 @@ import ( ) var ( - qemuProvider = &Provider{} + qemuProvider = &Virtualization{} // vmtype refers to qemu (vs libvirt, krun, etc). vmtype = "qemu" ) -func GetVirtualizationProvider() machine.Provider { +func GetVirtualizationProvider() machine.VirtProvider { return qemuProvider } @@ -64,7 +64,7 @@ const ( // NewMachine initializes an instance of a virtual machine based on the qemu // virtualization. -func (p *Provider) NewMachine(opts machine.InitOptions) (machine.VM, error) { +func (p *Virtualization) NewMachine(opts machine.InitOptions) (machine.VM, error) { vmConfigDir, err := machine.GetConfDir(vmtype) if err != nil { return nil, err @@ -231,7 +231,7 @@ func migrateVM(configPath string, config []byte, vm *MachineVM) error { // LoadVMByName reads a json file that describes a known qemu vm // and returns a vm instance -func (p *Provider) LoadVMByName(name string) (machine.VM, error) { +func (p *Virtualization) LoadVMByName(name string) (machine.VM, error) { vm := &MachineVM{Name: name} vm.HostUser = machine.HostUser{UID: -1} // posix reserves -1, so use it to signify undefined if err := vm.update(); err != nil { @@ -1117,7 +1117,7 @@ func getDiskSize(path string) (uint64, error) { } // List lists all vm's that use qemu virtualization -func (p *Provider) List(_ machine.ListOptions) ([]*machine.ListResponse, error) { +func (p *Virtualization) List(_ machine.ListOptions) ([]*machine.ListResponse, error) { return getVMInfos() } @@ -1193,7 +1193,7 @@ func getVMInfos() ([]*machine.ListResponse, error) { return listed, err } -func (p *Provider) IsValidVMName(name string) (bool, error) { +func (p *Virtualization) IsValidVMName(name string) (bool, error) { infos, err := getVMInfos() if err != nil { return false, err @@ -1208,7 +1208,7 @@ func (p *Provider) IsValidVMName(name string) (bool, error) { // CheckExclusiveActiveVM checks if there is a VM already running // that does not allow other VMs to be running -func (p *Provider) CheckExclusiveActiveVM() (bool, string, error) { +func (p *Virtualization) CheckExclusiveActiveVM() (bool, string, error) { vms, err := getVMInfos() if err != nil { return false, "", fmt.Errorf("checking VM active: %w", err) @@ -1670,7 +1670,7 @@ func (v *MachineVM) editCmdLine(flag string, value string) { } // RemoveAndCleanMachines removes all machine and cleans up any other files associated with podman machine -func (p *Provider) RemoveAndCleanMachines() error { +func (p *Virtualization) RemoveAndCleanMachines() error { var ( vm machine.VM listResponse []*machine.ListResponse @@ -1745,7 +1745,7 @@ func (p *Provider) RemoveAndCleanMachines() error { return prevErr } -func (p *Provider) VMType() string { +func (p *Virtualization) VMType() string { return vmtype } diff --git a/pkg/machine/wsl/fedora.go b/pkg/machine/wsl/fedora.go index 8f97e505da..6888486383 100644 --- a/pkg/machine/wsl/fedora.go +++ b/pkg/machine/wsl/fedora.go @@ -43,9 +43,9 @@ func NewFedoraDownloader(vmType, vmName, releaseStream string) (machine.Distribu f := FedoraDownload{ Download: machine.Download{ Arch: machine.GetFcosArch(), - Artifact: "", + Artifact: machine.None, CacheDir: cacheDir, - Format: machine.Format, + Format: machine.Tar, ImageName: imageName, LocalPath: filepath.Join(cacheDir, imageName), URL: downloadURL, diff --git a/pkg/machine/wsl/machine.go b/pkg/machine/wsl/machine.go index 9eb9fd740b..0775837ad0 100644 --- a/pkg/machine/wsl/machine.go +++ b/pkg/machine/wsl/machine.go @@ -28,7 +28,7 @@ import ( ) var ( - wslProvider = &Provider{} + wslProvider = &Virtualization{} // vmtype refers to qemu (vs libvirt, krun, etc) vmtype = "wsl" ) @@ -204,7 +204,7 @@ const ( globalPipe = "docker_engine" ) -type Provider struct{} +type Virtualization struct{} type MachineVM struct { // ConfigPath is the path to the configuration file @@ -235,12 +235,12 @@ func (e *ExitCodeError) Error() string { return fmt.Sprintf("Process failed with exit code: %d", e.code) } -func GetWSLProvider() machine.Provider { +func GetWSLProvider() machine.VirtProvider { return wslProvider } // NewMachine initializes an instance of a wsl machine -func (p *Provider) NewMachine(opts machine.InitOptions) (machine.VM, error) { +func (p *Virtualization) NewMachine(opts machine.InitOptions) (machine.VM, error) { vm := new(MachineVM) if len(opts.Name) > 0 { vm.Name = opts.Name @@ -281,7 +281,7 @@ func getConfigPathExt(name string, extension string) (string, error) { // LoadByName reads a json file that describes a known qemu vm // and returns a vm instance -func (p *Provider) LoadVMByName(name string) (machine.VM, error) { +func (p *Virtualization) LoadVMByName(name string) (machine.VM, error) { configPath, err := getConfigPath(name) if err != nil { return nil, err @@ -1418,7 +1418,7 @@ func (v *MachineVM) SSH(name string, opts machine.SSHOptions) error { } // List lists all vm's that use qemu virtualization -func (p *Provider) List(_ machine.ListOptions) ([]*machine.ListResponse, error) { +func (p *Virtualization) List(_ machine.ListOptions) ([]*machine.ListResponse, error) { return GetVMInfos() } @@ -1547,7 +1547,7 @@ func getMem(vm *MachineVM) (uint64, error) { return total - available, err } -func (p *Provider) IsValidVMName(name string) (bool, error) { +func (p *Virtualization) IsValidVMName(name string) (bool, error) { infos, err := GetVMInfos() if err != nil { return false, err @@ -1560,7 +1560,7 @@ func (p *Provider) IsValidVMName(name string) (bool, error) { return false, nil } -func (p *Provider) CheckExclusiveActiveVM() (bool, string, error) { +func (p *Virtualization) CheckExclusiveActiveVM() (bool, string, error) { return false, "", nil } @@ -1619,7 +1619,7 @@ func (v *MachineVM) getResources() (resources machine.ResourceConfig) { } // RemoveAndCleanMachines removes all machine and cleans up any other files associated with podman machine -func (p *Provider) RemoveAndCleanMachines() error { +func (p *Virtualization) RemoveAndCleanMachines() error { var ( vm machine.VM listResponse []*machine.ListResponse @@ -1694,6 +1694,6 @@ func (p *Provider) RemoveAndCleanMachines() error { return prevErr } -func (p *Provider) VMType() string { +func (p *Virtualization) VMType() string { return vmtype }