Refactor key machine objects

In #20538, I was asked to consider refactoring the new OCI pull code
from within the generic machine directory.  This is something I had
tried when originally coding it but it became apparent that a much
larger refactor to prevent circular deps was needed.  Because I did not
want to pollute the initial PR with that refactor, I asked for the PR to
merge first.  This is the refactor that needed to be done.

Signed-off-by: Brent Baude <bbaude@redhat.com>
This commit is contained in:
Brent Baude
2023-11-02 12:32:50 -05:00
parent f47a85f4ff
commit a45ba06d02
33 changed files with 783 additions and 657 deletions

View File

@ -11,6 +11,8 @@ import (
"time" "time"
"github.com/containers/podman/v4/pkg/machine" "github.com/containers/podman/v4/pkg/machine"
"github.com/containers/podman/v4/pkg/machine/compression"
"github.com/containers/podman/v4/pkg/machine/define"
vfConfig "github.com/crc-org/vfkit/pkg/config" vfConfig "github.com/crc-org/vfkit/pkg/config"
"github.com/docker/go-units" "github.com/docker/go-units"
"golang.org/x/sys/unix" "golang.org/x/sys/unix"
@ -34,7 +36,7 @@ type MMHardwareConfig struct {
func VirtualizationProvider() machine.VirtProvider { func VirtualizationProvider() machine.VirtProvider {
return &AppleHVVirtualization{ return &AppleHVVirtualization{
machine.NewVirtualization(machine.AppleHV, machine.Xz, machine.Raw, vmtype), machine.NewVirtualization(define.AppleHV, compression.Xz, define.Raw, vmtype),
} }
} }
@ -116,7 +118,7 @@ func (v AppleHVVirtualization) NewMachine(opts machine.InitOptions) (machine.VM,
return nil, err return nil, err
} }
configPath, err := machine.NewMachineFile(getVMConfigPath(configDir, opts.Name), nil) configPath, err := define.NewMachineFile(getVMConfigPath(configDir, opts.Name), nil)
if err != nil { if err != nil {
return nil, err return nil, err
} }
@ -127,7 +129,7 @@ func (v AppleHVVirtualization) NewMachine(opts machine.InitOptions) (machine.VM,
return nil, err return nil, err
} }
ignitionPath, err := machine.NewMachineFile(filepath.Join(configDir, m.Name)+".ign", nil) ignitionPath, err := define.NewMachineFile(filepath.Join(configDir, m.Name)+".ign", nil)
if err != nil { if err != nil {
return nil, err return nil, err
} }

View File

@ -7,13 +7,13 @@ import (
"net" "net"
"net/http" "net/http"
"github.com/containers/podman/v4/pkg/machine" "github.com/containers/podman/v4/pkg/machine/define"
"github.com/sirupsen/logrus" "github.com/sirupsen/logrus"
) )
// serveIgnitionOverSock allows podman to open a small httpd instance on the vsock between the host // serveIgnitionOverSock allows podman to open a small httpd instance on the vsock between the host
// and guest to inject the ignitionfile into fcos // and guest to inject the ignitionfile into fcos
func (m *MacMachine) serveIgnitionOverSock(ignitionSocket *machine.VMFile) error { func (m *MacMachine) serveIgnitionOverSock(ignitionSocket *define.VMFile) error {
logrus.Debugf("reading ignition file: %s", m.IgnitionFile.GetPath()) logrus.Debugf("reading ignition file: %s", m.IgnitionFile.GetPath())
ignFile, err := m.IgnitionFile.Read() ignFile, err := m.IgnitionFile.Read()
if err != nil { if err != nil {

View File

@ -20,6 +20,7 @@ import (
"github.com/containers/common/pkg/config" "github.com/containers/common/pkg/config"
gvproxy "github.com/containers/gvisor-tap-vsock/pkg/types" gvproxy "github.com/containers/gvisor-tap-vsock/pkg/types"
"github.com/containers/podman/v4/pkg/machine" "github.com/containers/podman/v4/pkg/machine"
"github.com/containers/podman/v4/pkg/machine/define"
"github.com/containers/podman/v4/pkg/strongunits" "github.com/containers/podman/v4/pkg/strongunits"
"github.com/containers/podman/v4/pkg/util" "github.com/containers/podman/v4/pkg/util"
"github.com/containers/podman/v4/utils" "github.com/containers/podman/v4/utils"
@ -45,13 +46,13 @@ const (
type VfkitHelper struct { type VfkitHelper struct {
LogLevel logrus.Level LogLevel logrus.Level
Endpoint string Endpoint string
VfkitBinaryPath *machine.VMFile VfkitBinaryPath *define.VMFile
VirtualMachine *vfConfig.VirtualMachine VirtualMachine *vfConfig.VirtualMachine
} }
type MacMachine struct { type MacMachine struct {
// ConfigPath is the fully qualified path to the configuration file // ConfigPath is the fully qualified path to the configuration file
ConfigPath machine.VMFile ConfigPath define.VMFile
// HostUser contains info about host user // HostUser contains info about host user
machine.HostUser machine.HostUser
// ImageConfig describes the bootable image // ImageConfig describes the bootable image
@ -61,7 +62,7 @@ type MacMachine struct {
// Name of VM // Name of VM
Name string Name string
// ReadySocket tells host when vm is booted // ReadySocket tells host when vm is booted
ReadySocket machine.VMFile ReadySocket define.VMFile
// ResourceConfig is physical attrs of the VM // ResourceConfig is physical attrs of the VM
machine.ResourceConfig machine.ResourceConfig
// SSHConfig for accessing the remote vm // SSHConfig for accessing the remote vm
@ -74,9 +75,9 @@ type MacMachine struct {
LastUp time.Time LastUp time.Time
// The VFKit endpoint where we can interact with the VM // The VFKit endpoint where we can interact with the VM
Vfkit VfkitHelper Vfkit VfkitHelper
LogPath machine.VMFile LogPath define.VMFile
GvProxyPid machine.VMFile GvProxyPid define.VMFile
GvProxySock machine.VMFile GvProxySock define.VMFile
// Used at runtime for serializing write operations // Used at runtime for serializing write operations
lock *lockfile.LockFile lock *lockfile.LockFile
@ -84,7 +85,7 @@ type MacMachine struct {
// setGVProxyInfo sets the VM's gvproxy pid and socket files // setGVProxyInfo sets the VM's gvproxy pid and socket files
func (m *MacMachine) setGVProxyInfo(runtimeDir string) error { func (m *MacMachine) setGVProxyInfo(runtimeDir string) error {
gvProxyPid, err := machine.NewMachineFile(filepath.Join(runtimeDir, "gvproxy.pid"), nil) gvProxyPid, err := define.NewMachineFile(filepath.Join(runtimeDir, "gvproxy.pid"), nil)
if err != nil { if err != nil {
return err return err
} }
@ -95,7 +96,7 @@ func (m *MacMachine) setGVProxyInfo(runtimeDir string) error {
// setVfkitInfo stores the default devices, sets the vfkit endpoint, and // setVfkitInfo stores the default devices, sets the vfkit endpoint, and
// locates/stores the path to the binary // locates/stores the path to the binary
func (m *MacMachine) setVfkitInfo(cfg *config.Config, readySocket machine.VMFile) error { func (m *MacMachine) setVfkitInfo(cfg *config.Config, readySocket define.VMFile) error {
defaultDevices, err := getDefaultDevices(m.ImagePath.GetPath(), m.LogPath.GetPath(), readySocket.GetPath()) defaultDevices, err := getDefaultDevices(m.ImagePath.GetPath(), m.LogPath.GetPath(), readySocket.GetPath())
if err != nil { if err != nil {
return err return err
@ -105,7 +106,7 @@ func (m *MacMachine) setVfkitInfo(cfg *config.Config, readySocket machine.VMFile
if err != nil { if err != nil {
return err return err
} }
vfkitBinaryPath, err := machine.NewMachineFile(vfkitPath, nil) vfkitBinaryPath, err := define.NewMachineFile(vfkitPath, nil)
if err != nil { if err != nil {
return err return err
} }
@ -217,7 +218,7 @@ func (m *MacMachine) Init(opts machine.InitOptions) (bool, error) {
m.ImagePath = *imagePath m.ImagePath = *imagePath
m.ImageStream = strm.String() m.ImageStream = strm.String()
logPath, err := machine.NewMachineFile(filepath.Join(dataDir, fmt.Sprintf("%s.log", m.Name)), nil) logPath, err := define.NewMachineFile(filepath.Join(dataDir, fmt.Sprintf("%s.log", m.Name)), nil)
if err != nil { if err != nil {
return false, err return false, err
} }
@ -490,7 +491,7 @@ func (m *MacMachine) SSH(name string, opts machine.SSHOptions) error {
// deleteIgnitionSocket retrieves the ignition socket, deletes it, and returns a // deleteIgnitionSocket retrieves the ignition socket, deletes it, and returns a
// pointer to the `VMFile` // pointer to the `VMFile`
func (m *MacMachine) deleteIgnitionSocket() (*machine.VMFile, error) { func (m *MacMachine) deleteIgnitionSocket() (*define.VMFile, error) {
ignitionSocket, err := m.getIgnitionSock() ignitionSocket, err := m.getIgnitionSock()
if err != nil { if err != nil {
return nil, err return nil, err
@ -556,7 +557,7 @@ func (m *MacMachine) addVolumesToVfKit() error {
} }
func (m *MacMachine) Start(name string, opts machine.StartOptions) error { func (m *MacMachine) Start(name string, opts machine.StartOptions) error {
var ignitionSocket *machine.VMFile var ignitionSocket *define.VMFile
m.lock.Lock() m.lock.Lock()
defer m.lock.Unlock() defer m.lock.Unlock()
@ -996,13 +997,13 @@ func (m *MacMachine) dockerSock() (string, error) {
return filepath.Join(dd, "podman.sock"), nil return filepath.Join(dd, "podman.sock"), nil
} }
func (m *MacMachine) forwardSocketPath() (*machine.VMFile, error) { func (m *MacMachine) forwardSocketPath() (*define.VMFile, error) {
sockName := "podman.sock" sockName := "podman.sock"
path, err := machine.GetDataDir(machine.AppleHvVirt) path, err := machine.GetDataDir(machine.AppleHvVirt)
if err != nil { if err != nil {
return nil, fmt.Errorf("Resolving data dir: %s", err.Error()) return nil, fmt.Errorf("Resolving data dir: %s", err.Error())
} }
return machine.NewMachineFile(filepath.Join(path, sockName), &sockName) return define.NewMachineFile(filepath.Join(path, sockName), &sockName)
} }
// resizeDisk uses os truncate to resize (only larger) a raw disk. the input size // resizeDisk uses os truncate to resize (only larger) a raw disk. the input size
@ -1034,7 +1035,7 @@ func (m *MacMachine) isFirstBoot() (bool, error) {
return m.LastUp == never, nil return m.LastUp == never, nil
} }
func (m *MacMachine) getIgnitionSock() (*machine.VMFile, error) { func (m *MacMachine) getIgnitionSock() (*define.VMFile, error) {
dataDir, err := machine.GetDataDir(machine.AppleHvVirt) dataDir, err := machine.GetDataDir(machine.AppleHvVirt)
if err != nil { if err != nil {
return nil, err return nil, err
@ -1044,7 +1045,7 @@ func (m *MacMachine) getIgnitionSock() (*machine.VMFile, error) {
return nil, err return nil, err
} }
} }
return machine.NewMachineFile(filepath.Join(dataDir, ignitionSocketName), nil) return define.NewMachineFile(filepath.Join(dataDir, ignitionSocketName), nil)
} }
func (m *MacMachine) getRuntimeDir() (string, error) { func (m *MacMachine) getRuntimeDir() (string, error) {

View File

@ -0,0 +1,91 @@
package compression
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 := KindFromFile(tt.args.path); got != tt.want {
t.Errorf("KindFromFile() = %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)
}
})
}
}

View File

@ -0,0 +1,36 @@
package compression
import "strings"
type ImageCompression int64
const (
Xz ImageCompression = iota
Zip
Gz
Bz2
)
func KindFromFile(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
}
func (c ImageCompression) String() string {
switch c {
case Gz:
return "gz"
case Zip:
return "zip"
case Bz2:
return "bz2"
}
return "xz"
}

View File

@ -0,0 +1,184 @@
package compression
import (
"archive/zip"
"bufio"
"errors"
"io"
"os"
"os/exec"
"path/filepath"
"runtime"
"strings"
"github.com/containers/image/v5/pkg/compression"
"github.com/containers/podman/v4/pkg/machine/define"
"github.com/containers/podman/v4/utils"
"github.com/containers/storage/pkg/archive"
"github.com/sirupsen/logrus"
"github.com/ulikunitz/xz"
)
func Decompress(localPath *define.VMFile, uncompressedPath string) error {
var isZip bool
uncompressedFileWriter, err := os.OpenFile(uncompressedPath, os.O_CREATE|os.O_RDWR, 0600)
if err != nil {
return err
}
sourceFile, err := localPath.Read()
if err != nil {
return err
}
if strings.HasSuffix(localPath.GetPath(), ".zip") {
isZip = true
}
prefix := "Copying uncompressed file"
compressionType := archive.DetectCompression(sourceFile)
if compressionType != archive.Uncompressed || isZip {
prefix = "Extracting compressed file"
}
prefix += ": " + filepath.Base(uncompressedPath)
if compressionType == archive.Xz {
return decompressXZ(prefix, localPath.GetPath(), uncompressedFileWriter)
}
if isZip && runtime.GOOS == "windows" {
return decompressZip(prefix, localPath.GetPath(), uncompressedFileWriter)
}
return decompressEverythingElse(prefix, localPath.GetPath(), uncompressedFileWriter)
}
// 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
func decompressXZ(prefix string, src string, output io.WriteCloser) error {
var read io.Reader
var cmd *exec.Cmd
stat, err := os.Stat(src)
if err != nil {
return err
}
file, err := os.Open(src)
if err != nil {
return err
}
defer file.Close()
p, bar := utils.ProgressBar(prefix, stat.Size(), prefix+": done")
proxyReader := bar.ProxyReader(file)
defer func() {
if err := proxyReader.Close(); err != nil {
logrus.Error(err)
}
}()
// Prefer Xz utils for fastest performance, fallback to go xi2 impl
if _, err := exec.LookPath("xz"); err == nil {
cmd = exec.Command("xz", "-d", "-c")
cmd.Stdin = proxyReader
read, err = cmd.StdoutPipe()
if err != nil {
return err
}
cmd.Stderr = os.Stderr
} else {
// This XZ implementation is reliant on buffering. It is also 3x+ slower than XZ utils.
// Consider replacing with a faster implementation (e.g. xi2) if podman machine is
// updated with a larger image for the distribution base.
buf := bufio.NewReader(proxyReader)
read, err = xz.NewReader(buf)
if err != nil {
return err
}
}
done := make(chan bool)
go func() {
if _, err := io.Copy(output, read); err != nil {
logrus.Error(err)
}
output.Close()
done <- true
}()
if cmd != nil {
err := cmd.Start()
if err != nil {
return err
}
p.Wait()
return cmd.Wait()
}
<-done
p.Wait()
return nil
}
func decompressEverythingElse(prefix string, src string, output io.WriteCloser) error {
stat, err := os.Stat(src)
if err != nil {
return err
}
f, err := os.Open(src)
if err != nil {
return err
}
p, bar := utils.ProgressBar(prefix, stat.Size(), prefix+": done")
proxyReader := bar.ProxyReader(f)
defer func() {
if err := proxyReader.Close(); err != nil {
logrus.Error(err)
}
}()
uncompressStream, _, err := compression.AutoDecompress(proxyReader)
if err != nil {
return err
}
defer func() {
if err := uncompressStream.Close(); err != nil {
logrus.Error(err)
}
if err := output.Close(); err != nil {
logrus.Error(err)
}
}()
_, err = io.Copy(output, uncompressStream)
p.Wait()
return err
}
func decompressZip(prefix string, src string, output io.WriteCloser) error {
zipReader, err := zip.OpenReader(src)
if err != nil {
return err
}
if len(zipReader.File) != 1 {
return errors.New("machine image files should consist of a single compressed file")
}
f, err := zipReader.File[0].Open()
if err != nil {
return err
}
defer func() {
if err := f.Close(); err != nil {
logrus.Error(err)
}
}()
defer func() {
if err := output.Close(); err != nil {
logrus.Error(err)
}
}()
size := int64(zipReader.File[0].CompressedSize64)
p, bar := utils.ProgressBar(prefix, size, prefix+": done")
proxyReader := bar.ProxyReader(f)
defer func() {
if err := proxyReader.Close(); err != nil {
logrus.Error(err)
}
}()
_, err = io.Copy(output, proxyReader)
p.Wait()
return err
}

View File

@ -15,6 +15,8 @@ import (
"strings" "strings"
"time" "time"
"github.com/containers/podman/v4/pkg/machine/compression"
"github.com/containers/podman/v4/pkg/machine/define"
"github.com/containers/storage/pkg/homedir" "github.com/containers/storage/pkg/homedir"
"github.com/containers/storage/pkg/lockfile" "github.com/containers/storage/pkg/lockfile"
"github.com/sirupsen/logrus" "github.com/sirupsen/logrus"
@ -64,11 +66,11 @@ var (
type Download struct { type Download struct {
Arch string Arch string
Artifact Artifact Artifact define.Artifact
CacheDir string CacheDir string
CompressionType ImageCompression CompressionType compression.ImageCompression
DataDir string DataDir string
Format ImageFormat Format define.ImageFormat
ImageName string ImageName string
LocalPath string LocalPath string
LocalUncompressedFile string LocalUncompressedFile string
@ -162,7 +164,7 @@ type DistributionDownload interface {
CleanCache() error CleanCache() error
} }
type InspectInfo struct { type InspectInfo struct {
ConfigPath VMFile ConfigPath define.VMFile
ConnectionInfo ConnectionConfig ConnectionInfo ConnectionConfig
Created time.Time Created time.Time
Image ImageConfig Image ImageConfig
@ -269,15 +271,6 @@ func ConfDirPrefix() (string, error) {
return confDir, nil return confDir, nil
} }
// GuardedRemoveAll functions much like os.RemoveAll but
// will not delete certain catastrophic paths.
func GuardedRemoveAll(path string) error {
if path == "" || path == "/" {
return fmt.Errorf("refusing to recursively delete `%s`", path)
}
return os.RemoveAll(path)
}
// ResourceConfig describes physical attributes of the machine // ResourceConfig describes physical attributes of the machine
type ResourceConfig struct { type ResourceConfig struct {
// CPUs to be assigned to the VM // CPUs to be assigned to the VM
@ -288,77 +281,6 @@ type ResourceConfig struct {
Memory uint64 Memory uint64
} }
const maxSocketPathLength int = 103
type VMFile struct {
// Path is the fully qualified path to a file
Path string
// Symlink is a shortened version of Path by using
// a symlink
Symlink *string `json:"symlink,omitempty"`
}
// GetPath returns the working path for a machinefile. it returns
// the symlink unless one does not exist
func (m *VMFile) GetPath() string {
if m.Symlink == nil {
return m.Path
}
return *m.Symlink
}
// Delete removes the machinefile symlink (if it exists) and
// the actual path
func (m *VMFile) Delete() error {
if m.Symlink != nil {
if err := os.Remove(*m.Symlink); err != nil && !errors.Is(err, os.ErrNotExist) {
logrus.Errorf("unable to remove symlink %q", *m.Symlink)
}
}
if err := os.Remove(m.Path); err != nil && !errors.Is(err, os.ErrNotExist) {
return err
}
return nil
}
// Read the contents of a given file and return in []bytes
func (m *VMFile) Read() ([]byte, error) {
return os.ReadFile(m.GetPath())
}
// NewMachineFile is a constructor for VMFile
func NewMachineFile(path string, symlink *string) (*VMFile, error) {
if len(path) < 1 {
return nil, errors.New("invalid machine file path")
}
if symlink != nil && len(*symlink) < 1 {
return nil, errors.New("invalid symlink path")
}
mf := VMFile{Path: path}
if symlink != nil && len(path) > maxSocketPathLength {
if err := mf.makeSymlink(symlink); err != nil && !errors.Is(err, os.ErrExist) {
return nil, err
}
}
return &mf, nil
}
// makeSymlink for macOS creates a symlink in $HOME/.podman/
// for a machinefile like a socket
func (m *VMFile) makeSymlink(symlink *string) error {
homeDir, err := os.UserHomeDir()
if err != nil {
return err
}
sl := filepath.Join(homeDir, ".podman", *symlink)
// make the symlink dir and throw away if it already exists
if err := os.MkdirAll(filepath.Dir(sl), 0700); err != nil && !errors.Is(err, os.ErrNotExist) {
return err
}
m.Symlink = &sl
return os.Symlink(m.Path, sl)
}
type Mount struct { type Mount struct {
ReadOnly bool ReadOnly bool
Source string Source string
@ -371,11 +293,11 @@ type Mount struct {
type ImageConfig struct { type ImageConfig struct {
// IgnitionFile is the path to the filesystem where the // IgnitionFile is the path to the filesystem where the
// ignition file was written (if needs one) // ignition file was written (if needs one)
IgnitionFile VMFile `json:"IgnitionFilePath"` IgnitionFile define.VMFile `json:"IgnitionFilePath"`
// ImageStream is the update stream for the image // ImageStream is the update stream for the image
ImageStream string ImageStream string
// ImageFile is the fq path to // ImageFile is the fq path to
ImagePath VMFile `json:"ImagePath"` ImagePath define.VMFile `json:"ImagePath"`
} }
// HostUser describes the host user // HostUser describes the host user
@ -401,9 +323,9 @@ type SSHConfig struct {
// ConnectionConfig contains connections like sockets, etc. // ConnectionConfig contains connections like sockets, etc.
type ConnectionConfig struct { type ConnectionConfig struct {
// PodmanSocket is the exported podman service socket // PodmanSocket is the exported podman service socket
PodmanSocket *VMFile `json:"PodmanSocket"` PodmanSocket *define.VMFile `json:"PodmanSocket"`
// PodmanPipe is the exported podman service named pipe (Windows hosts only) // PodmanPipe is the exported podman service named pipe (Windows hosts only)
PodmanPipe *VMFile `json:"PodmanPipe"` PodmanPipe *define.VMFile `json:"PodmanPipe"`
} }
type VMType int64 type VMType int64
@ -455,10 +377,10 @@ func ParseVMType(input string, emptyFallback VMType) (VMType, error) {
} }
type VirtProvider interface { //nolint:interfacebloat type VirtProvider interface { //nolint:interfacebloat
Artifact() Artifact Artifact() define.Artifact
CheckExclusiveActiveVM() (bool, string, error) CheckExclusiveActiveVM() (bool, string, error)
Compression() ImageCompression Compression() compression.ImageCompression
Format() ImageFormat Format() define.ImageFormat
IsValidVMName(name string) (bool, error) IsValidVMName(name string) (bool, error)
List(opts ListOptions) ([]*ListResponse, error) List(opts ListOptions) ([]*ListResponse, error)
LoadVMByName(name string) (VM, error) LoadVMByName(name string) (VM, error)
@ -469,21 +391,21 @@ type VirtProvider interface { //nolint:interfacebloat
} }
type Virtualization struct { type Virtualization struct {
artifact Artifact artifact define.Artifact
compression ImageCompression compression compression.ImageCompression
format ImageFormat format define.ImageFormat
vmKind VMType vmKind VMType
} }
func (p *Virtualization) Artifact() Artifact { func (p *Virtualization) Artifact() define.Artifact {
return p.artifact return p.artifact
} }
func (p *Virtualization) Compression() ImageCompression { func (p *Virtualization) Compression() compression.ImageCompression {
return p.compression return p.compression
} }
func (p *Virtualization) Format() ImageFormat { func (p *Virtualization) Format() define.ImageFormat {
return p.format return p.format
} }
@ -513,7 +435,7 @@ func (p *Virtualization) NewDownload(vmName string) (Download, error) {
}, nil }, nil
} }
func NewVirtualization(artifact Artifact, compression ImageCompression, format ImageFormat, vmKind VMType) Virtualization { func NewVirtualization(artifact define.Artifact, compression compression.ImageCompression, format define.ImageFormat, vmKind VMType) Virtualization {
return Virtualization{ return Virtualization{
artifact, artifact,
compression, compression,
@ -579,10 +501,10 @@ func (dl Download) NewFcosDownloader(imageStream FCOSStream) (DistributionDownlo
// AcquireVMImage determines if the image is already in a FCOS stream. If so, // AcquireVMImage determines if the image is already in a FCOS stream. If so,
// retrieves the image path of the uncompressed file. Otherwise, the user has // retrieves the image path of the uncompressed file. Otherwise, the user has
// provided an alternative image, so we set the image path and download the image. // provided an alternative image, so we set the image path and download the image.
func (dl Download) AcquireVMImage(imagePath string) (*VMFile, FCOSStream, error) { func (dl Download) AcquireVMImage(imagePath string) (*define.VMFile, FCOSStream, error) {
var ( var (
err error err error
imageLocation *VMFile imageLocation *define.VMFile
fcosStream FCOSStream fcosStream FCOSStream
) )
@ -600,7 +522,7 @@ func (dl Download) AcquireVMImage(imagePath string) (*VMFile, FCOSStream, error)
return nil, 0, err return nil, 0, err
} }
imageLocation, err = NewMachineFile(dd.Get().LocalUncompressedFile, nil) imageLocation, err = define.NewMachineFile(dd.Get().LocalUncompressedFile, nil)
if err != nil { if err != nil {
return nil, 0, err return nil, 0, err
} }

View File

@ -0,0 +1,34 @@
package define
type ImageFormat int64
const (
Qcow ImageFormat = iota
Vhdx
Tar
Raw
)
func (imf ImageFormat) Kind() string {
switch imf {
case Vhdx:
return "vhdx"
case Tar:
return "tar"
case Raw:
return "raw"
}
return "qcow2"
}
func (imf ImageFormat) KindWithCompression() string {
switch imf {
case Vhdx:
return "vhdx.zip"
case Tar:
return "tar.xz"
case Raw:
return "raw.gz"
}
return "qcow2.xz"
}

View File

@ -0,0 +1,74 @@
package define
import "testing"
func TestImageFormat_Kind(t *testing.T) {
tests := []struct {
name string
imf ImageFormat
want string
}{
{
name: "vhdx",
imf: Vhdx,
want: "vhdx",
},
{
name: "qcow2",
imf: Qcow,
want: "qcow2",
},
{
name: "raw",
imf: Raw,
want: "raw",
},
{
name: "tar",
imf: Tar,
want: "tar",
},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
if got := tt.imf.Kind(); got != tt.want {
t.Errorf("Kind() = %v, want %v", got, tt.want)
}
})
}
}
func TestImageFormat_KindWithCompression(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",
},
{
name: "raw.gz",
imf: Raw,
want: "raw.gz",
}, {
name: "tar.xz",
imf: Tar,
want: "tar.xz",
},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
if got := tt.imf.KindWithCompression(); got != tt.want {
t.Errorf("String() = %v, want %v", got, tt.want)
}
})
}
}

View File

@ -0,0 +1,20 @@
package define
type Artifact int64
const (
Qemu Artifact = iota
HyperV
AppleHV
None
)
func (a Artifact) String() string {
switch a {
case HyperV:
return "hyperv"
case AppleHV:
return "applehv"
}
return "qemu"
}

View File

@ -0,0 +1,35 @@
package define
import (
"testing"
)
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",
}, {
name: "applehv",
a: AppleHV,
want: "applehv",
},
}
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)
}
})
}
}

View File

@ -0,0 +1,80 @@
package define
import (
"errors"
"os"
"path/filepath"
"github.com/sirupsen/logrus"
)
const MaxSocketPathLength int = 103
type VMFile struct {
// Path is the fully qualified path to a file
Path string
// Symlink is a shortened version of Path by using
// a symlink
Symlink *string `json:"symlink,omitempty"`
}
// GetPath returns the working path for a machinefile. it returns
// the symlink unless one does not exist
func (m *VMFile) GetPath() string {
if m.Symlink == nil {
return m.Path
}
return *m.Symlink
}
// Delete removes the machinefile symlink (if it exists) and
// the actual path
func (m *VMFile) Delete() error {
if m.Symlink != nil {
if err := os.Remove(*m.Symlink); err != nil && !errors.Is(err, os.ErrNotExist) {
logrus.Errorf("unable to remove symlink %q", *m.Symlink)
}
}
if err := os.Remove(m.Path); err != nil && !errors.Is(err, os.ErrNotExist) {
return err
}
return nil
}
// Read the contents of a given file and return in []bytes
func (m *VMFile) Read() ([]byte, error) {
return os.ReadFile(m.GetPath())
}
// NewMachineFile is a constructor for VMFile
func NewMachineFile(path string, symlink *string) (*VMFile, error) {
if len(path) < 1 {
return nil, errors.New("invalid machine file path")
}
if symlink != nil && len(*symlink) < 1 {
return nil, errors.New("invalid symlink path")
}
mf := VMFile{Path: path}
if symlink != nil && len(path) > MaxSocketPathLength {
if err := mf.makeSymlink(symlink); err != nil && !errors.Is(err, os.ErrExist) {
return nil, err
}
}
return &mf, nil
}
// makeSymlink for macOS creates a symlink in $HOME/.podman/
// for a machinefile like a socket
func (m *VMFile) makeSymlink(symlink *string) error {
homeDir, err := os.UserHomeDir()
if err != nil {
return err
}
sl := filepath.Join(homeDir, ".podman", *symlink)
// make the symlink dir and throw away if it already exists
if err := os.MkdirAll(filepath.Dir(sl), 0700); err != nil && !errors.Is(err, os.ErrNotExist) {
return err
}
m.Symlink = &sl
return os.Symlink(m.Path, sl)
}

View File

@ -1,7 +1,7 @@
//go:build (amd64 && !windows) || (arm64 && !windows) //go:build (amd64 && !windows) || (arm64 && !windows)
// +build amd64,!windows arm64,!windows // +build amd64,!windows arm64,!windows
package qemu package define
import ( import (
"os" "os"
@ -9,7 +9,6 @@ import (
"reflect" "reflect"
"testing" "testing"
"github.com/containers/podman/v4/pkg/machine"
"github.com/containers/podman/v4/test/utils" "github.com/containers/podman/v4/test/utils"
) )
@ -41,7 +40,7 @@ func TestMachineFile_GetPath(t *testing.T) {
} }
for _, tt := range tests { for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) { t.Run(tt.name, func(t *testing.T) {
m := &machine.VMFile{ m := &VMFile{
Path: tt.fields.Path, //nolint: scopelint Path: tt.fields.Path, //nolint: scopelint
Symlink: tt.fields.Symlink, //nolint: scopelint Symlink: tt.fields.Symlink, //nolint: scopelint
} }
@ -75,7 +74,7 @@ func TestNewMachineFile(t *testing.T) {
sym := "my.sock" sym := "my.sock"
longSym := filepath.Join(homedir, ".podman", sym) longSym := filepath.Join(homedir, ".podman", sym)
m := machine.VMFile{ m := VMFile{
Path: p, Path: p,
Symlink: nil, Symlink: nil,
} }
@ -86,7 +85,7 @@ func TestNewMachineFile(t *testing.T) {
tests := []struct { tests := []struct {
name string name string
args args args args
want *machine.VMFile want *VMFile
wantErr bool wantErr bool
}{ }{
{ {
@ -98,7 +97,7 @@ func TestNewMachineFile(t *testing.T) {
{ {
name: "Good with short symlink", name: "Good with short symlink",
args: args{p, &sym}, args: args{p, &sym},
want: &machine.VMFile{Path: p}, want: &VMFile{Path: p},
wantErr: false, wantErr: false,
}, },
{ {
@ -116,14 +115,14 @@ func TestNewMachineFile(t *testing.T) {
{ {
name: "Good with long symlink", name: "Good with long symlink",
args: args{longp, &sym}, args: args{longp, &sym},
want: &machine.VMFile{Path: longp, Symlink: &longSym}, want: &VMFile{Path: longp, Symlink: &longSym},
wantErr: false, wantErr: false,
}, },
} }
for _, tt := range tests { for _, tt := range tests {
tt := tt tt := tt
t.Run(tt.name, func(t *testing.T) { t.Run(tt.name, func(t *testing.T) {
got, err := machine.NewMachineFile(tt.args.path, tt.args.symlink) got, err := NewMachineFile(tt.args.path, tt.args.symlink)
if (err != nil) != tt.wantErr { if (err != nil) != tt.wantErr {
t.Errorf("NewMachineFile() error = %v, wantErr %v", err, tt.wantErr) t.Errorf("NewMachineFile() error = %v, wantErr %v", err, tt.wantErr)
return return

View File

@ -9,6 +9,7 @@ import (
"time" "time"
"github.com/containers/podman/v4/pkg/machine" "github.com/containers/podman/v4/pkg/machine"
"github.com/containers/podman/v4/utils"
. "github.com/onsi/ginkgo/v2" . "github.com/onsi/ginkgo/v2"
. "github.com/onsi/gomega" . "github.com/onsi/gomega"
. "github.com/onsi/gomega/gexec" . "github.com/onsi/gomega/gexec"
@ -188,7 +189,7 @@ var _ = Describe("podman machine init", func() {
_, err = os.CreateTemp(tmpDir, "example") _, err = os.CreateTemp(tmpDir, "example")
Expect(err).ToNot(HaveOccurred()) Expect(err).ToNot(HaveOccurred())
mount := tmpDir + ":/testmountdir" mount := tmpDir + ":/testmountdir"
defer func() { _ = machine.GuardedRemoveAll(tmpDir) }() defer func() { _ = utils.GuardedRemoveAll(tmpDir) }()
name := randomString() name := randomString()
i := new(initMachine) i := new(initMachine)

View File

@ -12,7 +12,10 @@ import (
"time" "time"
"github.com/containers/podman/v4/pkg/machine" "github.com/containers/podman/v4/pkg/machine"
"github.com/containers/podman/v4/pkg/machine/compression"
"github.com/containers/podman/v4/pkg/machine/define"
"github.com/containers/podman/v4/pkg/machine/provider" "github.com/containers/podman/v4/pkg/machine/provider"
"github.com/containers/podman/v4/utils"
. "github.com/onsi/ginkgo/v2" . "github.com/onsi/ginkgo/v2"
. "github.com/onsi/gomega" . "github.com/onsi/gomega"
) )
@ -77,11 +80,11 @@ var _ = BeforeSuite(func() {
Fail(fmt.Sprintf("unable to download machine image: %q", err)) Fail(fmt.Sprintf("unable to download machine image: %q", err))
} }
GinkgoWriter.Println("Download took: ", time.Since(now).String()) GinkgoWriter.Println("Download took: ", time.Since(now).String())
diskImage, err := machine.NewMachineFile(fqImageName+compressionExtension, nil) diskImage, err := define.NewMachineFile(fqImageName+compressionExtension, nil)
if err != nil { if err != nil {
Fail(fmt.Sprintf("unable to create vmfile %q: %v", fqImageName+compressionExtension, err)) Fail(fmt.Sprintf("unable to create vmfile %q: %v", fqImageName+compressionExtension, err))
} }
if err := machine.Decompress(diskImage, fqImageName); err != nil { if err := compression.Decompress(diskImage, fqImageName); err != nil {
Fail(fmt.Sprintf("unable to decompress image file: %q", err)) Fail(fmt.Sprintf("unable to decompress image file: %q", err))
} }
} else { } else {
@ -149,7 +152,7 @@ func teardown(origHomeDir string, testDir string, mb *machineTestBuilder) {
GinkgoWriter.Printf("error occurred rm'ing machine: %q\n", err) GinkgoWriter.Printf("error occurred rm'ing machine: %q\n", err)
} }
} }
if err := machine.GuardedRemoveAll(testDir); err != nil { if err := utils.GuardedRemoveAll(testDir); err != nil {
Fail(fmt.Sprintf("failed to remove test dir: %q", err)) Fail(fmt.Sprintf("failed to remove test dir: %q", err))
} }
// this needs to be last in teardown // this needs to be last in teardown

View File

@ -11,9 +11,10 @@ import (
url2 "net/url" url2 "net/url"
"os" "os"
"runtime" "runtime"
"strings"
"time" "time"
"github.com/containers/podman/v4/pkg/machine/compression"
"github.com/containers/podman/v4/pkg/machine/define"
"github.com/coreos/stream-metadata-go/fedoracoreos" "github.com/coreos/stream-metadata-go/fedoracoreos"
"github.com/coreos/stream-metadata-go/release" "github.com/coreos/stream-metadata-go/release"
"github.com/coreos/stream-metadata-go/stream" "github.com/coreos/stream-metadata-go/stream"
@ -21,31 +22,12 @@ import (
"github.com/sirupsen/logrus" "github.com/sirupsen/logrus"
) )
type ImageCompression int64
type Artifact int64
type ImageFormat int64
const ( const (
// Used for testing the latest podman in fcos // Used for testing the latest podman in fcos
// special builds // special builds
podmanTesting = "podman-testing" podmanTesting = "podman-testing"
PodmanTestingHost = "fedorapeople.org" PodmanTestingHost = "fedorapeople.org"
PodmanTestingURL = "groups/podman/testing" PodmanTestingURL = "groups/podman/testing"
Xz ImageCompression = iota
Zip
Gz
Bz2
Qemu Artifact = iota
HyperV
AppleHV
None
Qcow ImageFormat = iota
Vhdx
Tar
Raw
) )
// //
@ -56,64 +38,6 @@ const (
// typed strongly // typed strongly
// //
func (a Artifact) String() string {
switch a {
case HyperV:
return "hyperv"
case AppleHV:
return "applehv"
}
return "qemu"
}
func (imf ImageFormat) String() string {
switch imf {
case Vhdx:
return "vhdx.zip"
case Tar:
return "tar.xz"
case Raw:
return "raw.gz"
}
return "qcow2.xz"
}
func (imf ImageFormat) string() string {
switch imf {
case Vhdx:
return "vhdx"
case Tar:
return "tar"
case Raw:
return "raw"
}
return "qcow2"
}
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 { type FcosDownload struct {
Download Download
} }
@ -123,7 +47,7 @@ func (f FcosDownload) Get() *Download {
} }
type FcosDownloadInfo struct { type FcosDownloadInfo struct {
CompressionType ImageCompression CompressionType compression.ImageCompression
Location string Location string
Release string Release string
Sha256Sum string Sha256Sum string
@ -219,7 +143,7 @@ func (dl Download) GetFCOSDownload(imageStream FCOSStream) (*FcosDownloadInfo, e
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[define.Qcow.KindWithCompression()]
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")
} }
@ -251,9 +175,9 @@ func (dl Download) GetFCOSDownload(imageStream FCOSStream) (*FcosDownloadInfo, e
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")
} }
formatType, ok := formats[dl.Format.String()] formatType, ok := formats[dl.Format.KindWithCompression()]
if !ok { if !ok {
return nil, fmt.Errorf("unable to pull VM image: no %s format in stream", dl.Format.String()) return nil, fmt.Errorf("unable to pull VM image: no %s format in stream", dl.Format.KindWithCompression())
} }
disk := formatType.Disk disk := formatType.Disk
if disk == nil { if disk == nil {

View File

@ -7,146 +7,6 @@ import (
"testing" "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)
}
})
}
}
func TestFCOSStream_String(t *testing.T) { func TestFCOSStream_String(t *testing.T) {
tests := []struct { tests := []struct {
name string name string

View File

@ -8,6 +8,7 @@ import (
"syscall" "syscall"
"time" "time"
"github.com/containers/podman/v4/pkg/machine/define"
psutil "github.com/shirou/gopsutil/v3/process" psutil "github.com/shirou/gopsutil/v3/process"
"github.com/sirupsen/logrus" "github.com/sirupsen/logrus"
) )
@ -82,7 +83,7 @@ func waitOnProcess(processID int) error {
} }
// CleanupGVProxy reads the --pid-file for gvproxy attempts to stop it // CleanupGVProxy reads the --pid-file for gvproxy attempts to stop it
func CleanupGVProxy(f VMFile) error { func CleanupGVProxy(f define.VMFile) error {
gvPid, err := f.Read() gvPid, err := f.Read()
if err != nil { if err != nil {
return fmt.Errorf("unable to read gvproxy pid file %s: %v", f.GetPath(), err) return fmt.Errorf("unable to read gvproxy pid file %s: %v", f.GetPath(), err)

View File

@ -14,6 +14,8 @@ import (
"github.com/containers/libhvee/pkg/hypervctl" "github.com/containers/libhvee/pkg/hypervctl"
"github.com/containers/podman/v4/pkg/machine" "github.com/containers/podman/v4/pkg/machine"
"github.com/containers/podman/v4/pkg/machine/compression"
"github.com/containers/podman/v4/pkg/machine/define"
"github.com/docker/go-units" "github.com/docker/go-units"
"github.com/sirupsen/logrus" "github.com/sirupsen/logrus"
) )
@ -24,7 +26,7 @@ type HyperVVirtualization struct {
func VirtualizationProvider() machine.VirtProvider { func VirtualizationProvider() machine.VirtProvider {
return &HyperVVirtualization{ return &HyperVVirtualization{
machine.NewVirtualization(machine.HyperV, machine.Zip, machine.Vhdx, vmtype), machine.NewVirtualization(define.HyperV, compression.Zip, define.Vhdx, vmtype),
} }
} }
@ -117,14 +119,14 @@ func (v HyperVVirtualization) NewMachine(opts machine.InitOptions) (machine.VM,
return nil, err return nil, err
} }
configPath, err := machine.NewMachineFile(getVMConfigPath(configDir, opts.Name), nil) configPath, err := define.NewMachineFile(getVMConfigPath(configDir, opts.Name), nil)
if err != nil { if err != nil {
return nil, err return nil, err
} }
m.ConfigPath = *configPath m.ConfigPath = *configPath
ignitionPath, err := machine.NewMachineFile(filepath.Join(configDir, m.Name)+".ign", nil) ignitionPath, err := define.NewMachineFile(filepath.Join(configDir, m.Name)+".ign", nil)
if err != nil { if err != nil {
return nil, err return nil, err
} }
@ -139,7 +141,7 @@ func (v HyperVVirtualization) NewMachine(opts machine.InitOptions) (machine.VM,
} }
// Set the proxy pid file // Set the proxy pid file
gvProxyPid, err := machine.NewMachineFile(filepath.Join(dataDir, "gvproxy.pid"), nil) gvProxyPid, err := define.NewMachineFile(filepath.Join(dataDir, "gvproxy.pid"), nil)
if err != nil { if err != nil {
return nil, err return nil, err
} }

View File

@ -20,6 +20,7 @@ import (
gvproxy "github.com/containers/gvisor-tap-vsock/pkg/types" gvproxy "github.com/containers/gvisor-tap-vsock/pkg/types"
"github.com/containers/libhvee/pkg/hypervctl" "github.com/containers/libhvee/pkg/hypervctl"
"github.com/containers/podman/v4/pkg/machine" "github.com/containers/podman/v4/pkg/machine"
"github.com/containers/podman/v4/pkg/machine/define"
"github.com/containers/podman/v4/pkg/strongunits" "github.com/containers/podman/v4/pkg/strongunits"
"github.com/containers/podman/v4/pkg/util" "github.com/containers/podman/v4/pkg/util"
"github.com/containers/podman/v4/utils" "github.com/containers/podman/v4/utils"
@ -44,7 +45,7 @@ const (
type HyperVMachine struct { type HyperVMachine struct {
// ConfigPath is the fully qualified path to the configuration file // ConfigPath is the fully qualified path to the configuration file
ConfigPath machine.VMFile ConfigPath define.VMFile
// HostUser contains info about host user // HostUser contains info about host user
machine.HostUser machine.HostUser
// ImageConfig describes the bootable image // ImageConfig describes the bootable image
@ -68,7 +69,7 @@ type HyperVMachine struct {
// LastUp contains the last recorded uptime // LastUp contains the last recorded uptime
LastUp time.Time LastUp time.Time
// GVProxy will write its PID here // GVProxy will write its PID here
GvProxyPid machine.VMFile GvProxyPid define.VMFile
// MountVsocks contains the currently-active vsocks, mapped to the // MountVsocks contains the currently-active vsocks, mapped to the
// directory they should be mounted on. // directory they should be mounted on.
MountVsocks map[string]uint64 MountVsocks map[string]uint64
@ -873,13 +874,13 @@ func (m *HyperVMachine) dockerSock() (string, error) {
return filepath.Join(dd, "podman.sock"), nil return filepath.Join(dd, "podman.sock"), nil
} }
func (m *HyperVMachine) forwardSocketPath() (*machine.VMFile, error) { func (m *HyperVMachine) forwardSocketPath() (*define.VMFile, error) {
sockName := "podman.sock" sockName := "podman.sock"
path, err := machine.GetDataDir(machine.HyperVVirt) path, err := machine.GetDataDir(machine.HyperVVirt)
if err != nil { if err != nil {
return nil, fmt.Errorf("Resolving data dir: %s", err.Error()) return nil, fmt.Errorf("Resolving data dir: %s", err.Error())
} }
return machine.NewMachineFile(filepath.Join(path, sockName), &sockName) return define.NewMachineFile(filepath.Join(path, sockName), &sockName)
} }
func (m *HyperVMachine) writeConfig() error { func (m *HyperVMachine) writeConfig() error {

View File

@ -1,4 +1,4 @@
package machine package ocipull
import ( import (
"fmt" "fmt"
@ -6,14 +6,13 @@ import (
"path/filepath" "path/filepath"
"strings" "strings"
"github.com/containers/image/v5/types" "github.com/blang/semver/v4"
"github.com/containers/image/v5/pkg/compression" "github.com/containers/image/v5/pkg/compression"
"github.com/containers/image/v5/types"
"github.com/containers/podman/v4/pkg/machine/define"
"github.com/containers/podman/v4/version"
"github.com/containers/storage/pkg/archive" "github.com/containers/storage/pkg/archive"
"github.com/sirupsen/logrus" "github.com/sirupsen/logrus"
"github.com/blang/semver/v4"
"github.com/containers/podman/v4/version"
) )
// quay.io/libpod/podman-machine-images:4.6 // quay.io/libpod/podman-machine-images:4.6
@ -30,9 +29,9 @@ type OSVersion struct {
type Disker interface { type Disker interface {
Pull() error Pull() error
Decompress(compressedFile *VMFile) (*VMFile, error) Decompress(compressedFile *define.VMFile) (*define.VMFile, error)
DiskEndpoint() string DiskEndpoint() string
Unpack() (*VMFile, error) Unpack() (*define.VMFile, error)
} }
type OCIOpts struct { type OCIOpts struct {
@ -81,11 +80,11 @@ func (o *OSVersion) majorMinor() string {
return fmt.Sprintf("%d.%d", o.Major, o.Minor) return fmt.Sprintf("%d.%d", o.Major, o.Minor)
} }
func (o *OSVersion) diskImage(diskFlavor ImageFormat) string { func (o *OSVersion) diskImage(diskFlavor define.ImageFormat) string {
return fmt.Sprintf("%s/%s/%s:%s-%s", registry, repo, diskImages, o.majorMinor(), diskFlavor.string()) return fmt.Sprintf("%s/%s/%s:%s-%s", registry, repo, diskImages, o.majorMinor(), diskFlavor.Kind())
} }
func unpackOCIDir(ociTb, machineImageDir string) (*VMFile, error) { func unpackOCIDir(ociTb, machineImageDir string) (*define.VMFile, error) {
imageFileName, err := findTarComponent(ociTb) imageFileName, err := findTarComponent(ociTb)
if err != nil { if err != nil {
return nil, err return nil, err
@ -121,7 +120,7 @@ func unpackOCIDir(ociTb, machineImageDir string) (*VMFile, error) {
return nil, err return nil, err
} }
return NewMachineFile(unpackedFileName, nil) return define.NewMachineFile(unpackedFileName, nil)
} }
func localOCIDiskImageDir(blobDirPath string, localBlob *types.BlobInfo) string { func localOCIDiskImageDir(blobDirPath string, localBlob *types.BlobInfo) string {

View File

@ -1,4 +1,4 @@
package machine package ocipull
import ( import (
"archive/tar" "archive/tar"
@ -11,7 +11,8 @@ import (
"github.com/containers/image/v5/pkg/compression" "github.com/containers/image/v5/pkg/compression"
"github.com/containers/image/v5/types" "github.com/containers/image/v5/types"
"github.com/containers/podman/v4/pkg/machine/ocipull" diskcompression "github.com/containers/podman/v4/pkg/machine/compression"
"github.com/containers/podman/v4/pkg/machine/define"
"github.com/sirupsen/logrus" "github.com/sirupsen/logrus"
) )
@ -38,7 +39,7 @@ func NewOCIDir(ctx context.Context, inputDir, machineImageDir, vmName string) *L
} }
func (l *LocalBlobDir) Pull() error { func (l *LocalBlobDir) Pull() error {
localBlob, err := ocipull.GetLocalBlob(l.ctx, l.DiskEndpoint()) localBlob, err := GetLocalBlob(l.ctx, l.DiskEndpoint())
if err != nil { if err != nil {
return err return err
} }
@ -46,15 +47,15 @@ func (l *LocalBlobDir) Pull() error {
return nil return nil
} }
func (l *LocalBlobDir) Decompress(compressedFile *VMFile) (*VMFile, error) { func (l *LocalBlobDir) Decompress(compressedFile *define.VMFile) (*define.VMFile, error) {
finalName := finalFQImagePathName(l.vmName, l.imageName) finalName := finalFQImagePathName(l.vmName, l.imageName)
if err := Decompress(compressedFile, finalName); err != nil { if err := diskcompression.Decompress(compressedFile, finalName); err != nil {
return nil, err return nil, err
} }
return NewMachineFile(finalName, nil) return define.NewMachineFile(finalName, nil)
} }
func (l *LocalBlobDir) Unpack() (*VMFile, error) { func (l *LocalBlobDir) Unpack() (*define.VMFile, error) {
tbPath := localOCIDiskImageDir(l.blobDirPath, l.blob) tbPath := localOCIDiskImageDir(l.blobDirPath, l.blob)
unPackedFile, err := unpackOCIDir(tbPath, l.machineImageDir) unPackedFile, err := unpackOCIDir(tbPath, l.machineImageDir)
if err != nil { if err != nil {

View File

@ -1,4 +1,4 @@
package machine package ocipull
import ( import (
"context" "context"
@ -8,7 +8,9 @@ import (
"strings" "strings"
"github.com/containers/image/v5/types" "github.com/containers/image/v5/types"
"github.com/containers/podman/v4/pkg/machine/ocipull" "github.com/containers/podman/v4/pkg/machine/compression"
"github.com/containers/podman/v4/pkg/machine/define"
"github.com/containers/podman/v4/utils"
v1 "github.com/opencontainers/image-spec/specs-go/v1" v1 "github.com/opencontainers/image-spec/specs-go/v1"
"github.com/sirupsen/logrus" "github.com/sirupsen/logrus"
) )
@ -18,14 +20,14 @@ type Versioned struct {
blobDirPath string blobDirPath string
cacheDir string cacheDir string
ctx context.Context ctx context.Context
imageFormat ImageFormat imageFormat define.ImageFormat
imageName string imageName string
machineImageDir string machineImageDir string
machineVersion *OSVersion machineVersion *OSVersion
vmName string vmName string
} }
func newVersioned(ctx context.Context, machineImageDir, vmName string) (*Versioned, error) { func NewVersioned(ctx context.Context, machineImageDir, vmName string) (*Versioned, error) {
imageCacheDir := filepath.Join(machineImageDir, "cache") imageCacheDir := filepath.Join(machineImageDir, "cache")
if err := os.MkdirAll(imageCacheDir, 0777); err != nil { if err := os.MkdirAll(imageCacheDir, 0777); err != nil {
return nil, err return nil, err
@ -47,7 +49,7 @@ func (d *Versioned) versionedOCICacheDir() string {
} }
func (d *Versioned) identifyImageNameFromOCIDir() (string, error) { func (d *Versioned) identifyImageNameFromOCIDir() (string, error) {
imageManifest, err := ocipull.ReadImageManifestFromOCIPath(d.ctx, d.versionedOCICacheDir()) imageManifest, err := ReadImageManifestFromOCIPath(d.ctx, d.versionedOCICacheDir())
if err != nil { if err != nil {
return "", err return "", err
} }
@ -61,7 +63,7 @@ func (d *Versioned) identifyImageNameFromOCIDir() (string, error) {
func (d *Versioned) pull(path string) error { func (d *Versioned) pull(path string) error {
fmt.Printf("Pulling %s\n", d.DiskEndpoint()) fmt.Printf("Pulling %s\n", d.DiskEndpoint())
logrus.Debugf("pulling %s to %s", d.DiskEndpoint(), path) logrus.Debugf("pulling %s to %s", d.DiskEndpoint(), path)
return ocipull.Pull(d.ctx, d.DiskEndpoint(), path, ocipull.PullOptions{}) return Pull(d.ctx, d.DiskEndpoint(), path, PullOptions{})
} }
func (d *Versioned) Pull() error { func (d *Versioned) Pull() error {
@ -72,7 +74,7 @@ func (d *Versioned) Pull() error {
remoteDescriptor *v1.Descriptor remoteDescriptor *v1.Descriptor
) )
remoteDiskImage := d.machineVersion.diskImage(Qcow) remoteDiskImage := d.machineVersion.diskImage(define.Qcow)
logrus.Debugf("podman disk image name: %s", remoteDiskImage) logrus.Debugf("podman disk image name: %s", remoteDiskImage)
// is there a valid oci dir in our cache // is there a valid oci dir in our cache
@ -80,12 +82,12 @@ func (d *Versioned) Pull() error {
if hasCache { if hasCache {
logrus.Debug("checking remote registry") logrus.Debug("checking remote registry")
remoteDescriptor, err = ocipull.GetRemoteDescriptor(d.ctx, remoteDiskImage) remoteDescriptor, err = GetRemoteDescriptor(d.ctx, remoteDiskImage)
if err != nil { if err != nil {
return err return err
} }
logrus.Debugf("working with local cache: %s", d.versionedOCICacheDir()) logrus.Debugf("working with local cache: %s", d.versionedOCICacheDir())
localBlob, err = ocipull.GetLocalBlob(d.ctx, d.versionedOCICacheDir()) localBlob, err = GetLocalBlob(d.ctx, d.versionedOCICacheDir())
if err != nil { if err != nil {
return err return err
} }
@ -97,7 +99,7 @@ func (d *Versioned) Pull() error {
} }
if !hasCache || isUpdatable { if !hasCache || isUpdatable {
if hasCache { if hasCache {
if err := GuardedRemoveAll(d.versionedOCICacheDir()); err != nil { if err := utils.GuardedRemoveAll(d.versionedOCICacheDir()); err != nil {
return err return err
} }
} }
@ -113,7 +115,7 @@ func (d *Versioned) Pull() error {
d.imageName = imageName d.imageName = imageName
if localBlob == nil { if localBlob == nil {
localBlob, err = ocipull.GetLocalBlob(d.ctx, d.versionedOCICacheDir()) localBlob, err = GetLocalBlob(d.ctx, d.versionedOCICacheDir())
if err != nil { if err != nil {
return err return err
} }
@ -124,7 +126,7 @@ func (d *Versioned) Pull() error {
return nil return nil
} }
func (d *Versioned) Unpack() (*VMFile, error) { func (d *Versioned) Unpack() (*define.VMFile, error) {
tbPath := localOCIDiskImageDir(d.blobDirPath, d.blob) tbPath := localOCIDiskImageDir(d.blobDirPath, d.blob)
unpackedFile, err := unpackOCIDir(tbPath, d.machineImageDir) unpackedFile, err := unpackOCIDir(tbPath, d.machineImageDir)
if err != nil { if err != nil {
@ -134,14 +136,14 @@ func (d *Versioned) Unpack() (*VMFile, error) {
return unpackedFile, nil return unpackedFile, nil
} }
func (d *Versioned) Decompress(compressedFile *VMFile) (*VMFile, error) { func (d *Versioned) Decompress(compressedFile *define.VMFile) (*define.VMFile, error) {
imageCompression := compressionFromFile(d.imageName) imageCompression := compression.KindFromFile(d.imageName)
strippedImageName := strings.TrimSuffix(d.imageName, fmt.Sprintf(".%s", imageCompression.String())) strippedImageName := strings.TrimSuffix(d.imageName, fmt.Sprintf(".%s", imageCompression.String()))
finalName := finalFQImagePathName(d.vmName, strippedImageName) finalName := finalFQImagePathName(d.vmName, strippedImageName)
if err := Decompress(compressedFile, finalName); err != nil { if err := compression.Decompress(compressedFile, finalName); err != nil {
return nil, err return nil, err
} }
return NewMachineFile(finalName, nil) return define.NewMachineFile(finalName, nil)
} }
func (d *Versioned) localOCIDiskImageDir(localBlob *types.BlobInfo) string { func (d *Versioned) localOCIDiskImageDir(localBlob *types.BlobInfo) string {

View File

@ -4,8 +4,6 @@
package machine package machine
import ( import (
"archive/zip"
"bufio"
"context" "context"
"errors" "errors"
"fmt" "fmt"
@ -13,18 +11,15 @@ import (
"net/http" "net/http"
url2 "net/url" url2 "net/url"
"os" "os"
"os/exec"
"path/filepath" "path/filepath"
"runtime"
"strings" "strings"
"time" "time"
"github.com/containers/image/v5/pkg/compression" "github.com/containers/podman/v4/pkg/machine/compression"
"github.com/containers/storage/pkg/archive" "github.com/containers/podman/v4/pkg/machine/define"
"github.com/containers/podman/v4/pkg/machine/ocipull"
"github.com/containers/podman/v4/utils"
"github.com/sirupsen/logrus" "github.com/sirupsen/logrus"
"github.com/ulikunitz/xz"
"github.com/vbauerster/mpb/v8"
"github.com/vbauerster/mpb/v8/decor"
) )
// GenericDownload is used when a user provides a URL // GenericDownload is used when a user provides a URL
@ -90,7 +85,7 @@ func supportedURL(path string) (url *url2.URL) {
func (dl Download) GetLocalUncompressedFile(dataDir string) string { func (dl Download) GetLocalUncompressedFile(dataDir string) string {
compressedFilename := dl.VMName + "_" + dl.ImageName compressedFilename := dl.VMName + "_" + dl.ImageName
extension := compressionFromFile(compressedFilename) extension := compression.KindFromFile(compressedFilename)
uncompressedFile := strings.TrimSuffix(compressedFilename, fmt.Sprintf(".%s", extension.String())) uncompressedFile := strings.TrimSuffix(compressedFilename, fmt.Sprintf(".%s", extension.String()))
dl.LocalUncompressedFile = filepath.Join(dataDir, uncompressedFile) dl.LocalUncompressedFile = filepath.Join(dataDir, uncompressedFile)
return dl.LocalUncompressedFile return dl.LocalUncompressedFile
@ -134,33 +129,11 @@ func DownloadImage(d DistributionDownload) error {
} }
}() }()
} }
localPath, err := NewMachineFile(d.Get().LocalPath, nil) localPath, err := define.NewMachineFile(d.Get().LocalPath, nil)
if err != nil { if err != nil {
return err return err
} }
return Decompress(localPath, d.Get().LocalUncompressedFile) return compression.Decompress(localPath, d.Get().LocalUncompressedFile)
}
func progressBar(prefix string, size int64, onComplete string) (*mpb.Progress, *mpb.Bar) {
p := mpb.New(
mpb.WithWidth(80), // Do not go below 80, see bug #17718
mpb.WithRefreshRate(180*time.Millisecond),
)
bar := p.AddBar(size,
mpb.BarFillerClearOnComplete(),
mpb.PrependDecorators(
decor.OnComplete(decor.Name(prefix), onComplete),
),
mpb.AppendDecorators(
decor.OnComplete(decor.CountersKibiByte("%.1f / %.1f"), ""),
),
)
if size == 0 {
bar.SetTotal(0, true)
}
return p, bar
} }
// DownloadVMImage downloads a VM image from url to given path // DownloadVMImage downloads a VM image from url to given path
@ -193,7 +166,7 @@ func DownloadVMImage(downloadURL *url2.URL, imageName string, localImagePath str
prefix := "Downloading VM image: " + imageName prefix := "Downloading VM image: " + imageName
onComplete := prefix + ": done" onComplete := prefix + ": done"
p, bar := progressBar(prefix, size, onComplete) p, bar := utils.ProgressBar(prefix, size, onComplete)
proxyReader := bar.ProxyReader(resp.Body) proxyReader := bar.ProxyReader(resp.Body)
defer func() { defer func() {
@ -210,170 +183,6 @@ func DownloadVMImage(downloadURL *url2.URL, imageName string, localImagePath str
return nil return nil
} }
func Decompress(localPath *VMFile, uncompressedPath string) error {
var isZip bool
uncompressedFileWriter, err := os.OpenFile(uncompressedPath, os.O_CREATE|os.O_RDWR, 0600)
if err != nil {
return err
}
sourceFile, err := localPath.Read()
if err != nil {
return err
}
if strings.HasSuffix(localPath.GetPath(), ".zip") {
isZip = true
}
prefix := "Copying uncompressed file"
compressionType := archive.DetectCompression(sourceFile)
if compressionType != archive.Uncompressed || isZip {
prefix = "Extracting compressed file"
}
prefix += ": " + filepath.Base(uncompressedPath)
if compressionType == archive.Xz {
return decompressXZ(prefix, localPath.GetPath(), uncompressedFileWriter)
}
if isZip && runtime.GOOS == "windows" {
return decompressZip(prefix, localPath.GetPath(), uncompressedFileWriter)
}
return decompressEverythingElse(prefix, localPath.GetPath(), uncompressedFileWriter)
}
// 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
func decompressXZ(prefix string, src string, output io.WriteCloser) error {
var read io.Reader
var cmd *exec.Cmd
stat, err := os.Stat(src)
if err != nil {
return err
}
file, err := os.Open(src)
if err != nil {
return err
}
defer file.Close()
p, bar := progressBar(prefix, stat.Size(), prefix+": done")
proxyReader := bar.ProxyReader(file)
defer func() {
if err := proxyReader.Close(); err != nil {
logrus.Error(err)
}
}()
// Prefer Xz utils for fastest performance, fallback to go xi2 impl
if _, err := exec.LookPath("xz"); err == nil {
cmd = exec.Command("xz", "-d", "-c")
cmd.Stdin = proxyReader
read, err = cmd.StdoutPipe()
if err != nil {
return err
}
cmd.Stderr = os.Stderr
} else {
// This XZ implementation is reliant on buffering. It is also 3x+ slower than XZ utils.
// Consider replacing with a faster implementation (e.g. xi2) if podman machine is
// updated with a larger image for the distribution base.
buf := bufio.NewReader(proxyReader)
read, err = xz.NewReader(buf)
if err != nil {
return err
}
}
done := make(chan bool)
go func() {
if _, err := io.Copy(output, read); err != nil {
logrus.Error(err)
}
output.Close()
done <- true
}()
if cmd != nil {
err := cmd.Start()
if err != nil {
return err
}
p.Wait()
return cmd.Wait()
}
<-done
p.Wait()
return nil
}
func decompressEverythingElse(prefix string, src string, output io.WriteCloser) error {
stat, err := os.Stat(src)
if err != nil {
return err
}
f, err := os.Open(src)
if err != nil {
return err
}
p, bar := progressBar(prefix, stat.Size(), prefix+": done")
proxyReader := bar.ProxyReader(f)
defer func() {
if err := proxyReader.Close(); err != nil {
logrus.Error(err)
}
}()
uncompressStream, _, err := compression.AutoDecompress(proxyReader)
if err != nil {
return err
}
defer func() {
if err := uncompressStream.Close(); err != nil {
logrus.Error(err)
}
if err := output.Close(); err != nil {
logrus.Error(err)
}
}()
_, err = io.Copy(output, uncompressStream)
p.Wait()
return err
}
func decompressZip(prefix string, src string, output io.WriteCloser) error {
zipReader, err := zip.OpenReader(src)
if err != nil {
return err
}
if len(zipReader.File) != 1 {
return errors.New("machine image files should consist of a single compressed file")
}
f, err := zipReader.File[0].Open()
if err != nil {
return err
}
defer func() {
if err := f.Close(); err != nil {
logrus.Error(err)
}
}()
defer func() {
if err := output.Close(); err != nil {
logrus.Error(err)
}
}()
size := int64(zipReader.File[0].CompressedSize64)
p, bar := progressBar(prefix, size, prefix+": done")
proxyReader := bar.ProxyReader(f)
defer func() {
if err := proxyReader.Close(); err != nil {
logrus.Error(err)
}
}()
_, err = io.Copy(output, proxyReader)
p.Wait()
return err
}
func RemoveImageAfterExpire(dir string, expire time.Duration) error { func RemoveImageAfterExpire(dir string, expire time.Duration) error {
now := time.Now() now := time.Now()
err := filepath.Walk(dir, func(path string, info os.FileInfo, err error) error { err := filepath.Walk(dir, func(path string, info os.FileInfo, err error) error {
@ -393,13 +202,13 @@ func RemoveImageAfterExpire(dir string, expire time.Duration) error {
// AcquireAlternateImage downloads the alternate image the user provided, which // AcquireAlternateImage downloads the alternate image the user provided, which
// can be a file path or URL // can be a file path or URL
func (dl Download) AcquireAlternateImage(inputPath string) (*VMFile, error) { func (dl Download) AcquireAlternateImage(inputPath string) (*define.VMFile, error) {
g, err := NewGenericDownloader(dl.VMKind, dl.VMName, inputPath) g, err := NewGenericDownloader(dl.VMKind, dl.VMName, inputPath)
if err != nil { if err != nil {
return nil, err return nil, err
} }
imagePath, err := NewMachineFile(g.Get().LocalUncompressedFile, nil) imagePath, err := define.NewMachineFile(g.Get().LocalUncompressedFile, nil)
if err != nil { if err != nil {
return nil, err return nil, err
} }
@ -411,23 +220,23 @@ func (dl Download) AcquireAlternateImage(inputPath string) (*VMFile, error) {
return imagePath, nil return imagePath, nil
} }
func isOci(input string) (bool, *OCIKind, error) { func isOci(input string) (bool, *ocipull.OCIKind, error) {
inputURL, err := url2.Parse(input) inputURL, err := url2.Parse(input)
if err != nil { if err != nil {
return false, nil, err return false, nil, err
} }
switch inputURL.Scheme { switch inputURL.Scheme {
case OCIDir.String(): case ocipull.OCIDir.String():
return true, &OCIDir, nil return true, &ocipull.OCIDir, nil
case OCIRegistry.String(): case ocipull.OCIRegistry.String():
return true, &OCIRegistry, nil return true, &ocipull.OCIRegistry, nil
} }
return false, nil, nil return false, nil, nil
} }
func Pull(input, machineName string, vp VirtProvider) (*VMFile, FCOSStream, error) { func Pull(input, machineName string, vp VirtProvider) (*define.VMFile, FCOSStream, error) {
var ( var (
disk Disker disk ocipull.Disker
) )
ociBased, ociScheme, err := isOci(input) ociBased, ociScheme, err := isOci(input)
@ -442,7 +251,7 @@ func Pull(input, machineName string, vp VirtProvider) (*VMFile, FCOSStream, erro
} }
return dl.AcquireVMImage(input) return dl.AcquireVMImage(input)
} }
oopts := OCIOpts{ oopts := ocipull.OCIOpts{
Scheme: ociScheme, Scheme: ociScheme,
} }
dataDir, err := GetDataDir(vp.VMType()) dataDir, err := GetDataDir(vp.VMType())
@ -450,9 +259,9 @@ func Pull(input, machineName string, vp VirtProvider) (*VMFile, FCOSStream, erro
return nil, 0, err return nil, 0, err
} }
if ociScheme.IsOCIDir() { if ociScheme.IsOCIDir() {
strippedOCIDir := StripOCIReference(input) strippedOCIDir := ocipull.StripOCIReference(input)
oopts.Dir = &strippedOCIDir oopts.Dir = &strippedOCIDir
disk = NewOCIDir(context.Background(), input, dataDir, machineName) disk = ocipull.NewOCIDir(context.Background(), input, dataDir, machineName)
} else { } else {
// a use of a containers image type here might be // a use of a containers image type here might be
// tighter // tighter
@ -461,7 +270,7 @@ func Pull(input, machineName string, vp VirtProvider) (*VMFile, FCOSStream, erro
if len(strippedInput) > 0 { if len(strippedInput) > 0 {
return nil, 0, errors.New("image names are not supported yet") return nil, 0, errors.New("image names are not supported yet")
} }
disk, err = newVersioned(context.Background(), dataDir, machineName) disk, err = ocipull.NewVersioned(context.Background(), dataDir, machineName)
if err != nil { if err != nil {
return nil, 0, err return nil, 0, err
} }

View File

@ -4,7 +4,7 @@ import (
"fmt" "fmt"
"strconv" "strconv"
"github.com/containers/podman/v4/pkg/machine" "github.com/containers/podman/v4/pkg/machine/define"
) )
// QemuCmd is an alias around a string slice to prevent the need to migrate the // QemuCmd is an alias around a string slice to prevent the need to migrate the
@ -30,7 +30,7 @@ func (q *QemuCmd) SetCPUs(c uint64) {
} }
// SetIgnitionFile specifies the machine's ignition file // SetIgnitionFile specifies the machine's ignition file
func (q *QemuCmd) SetIgnitionFile(file machine.VMFile) { func (q *QemuCmd) SetIgnitionFile(file define.VMFile) {
*q = append(*q, "-fw_cfg", "name=opt/com.coreos/config,file="+file.GetPath()) *q = append(*q, "-fw_cfg", "name=opt/com.coreos/config,file="+file.GetPath())
} }
@ -47,7 +47,7 @@ func (q *QemuCmd) SetNetwork() {
} }
// SetSerialPort adds a serial port to the machine for readiness // SetSerialPort adds a serial port to the machine for readiness
func (q *QemuCmd) SetSerialPort(readySocket, vmPidFile machine.VMFile, name string) { func (q *QemuCmd) SetSerialPort(readySocket, vmPidFile define.VMFile, name string) {
*q = append(*q, *q = append(*q,
"-device", "virtio-serial", "-device", "virtio-serial",
// qemu needs to establish the long name; other connections can use the symlink'd // qemu needs to establish the long name; other connections can use the symlink'd

View File

@ -11,6 +11,8 @@ import (
"github.com/containers/common/pkg/config" "github.com/containers/common/pkg/config"
"github.com/containers/podman/v4/pkg/machine" "github.com/containers/podman/v4/pkg/machine"
"github.com/containers/podman/v4/pkg/machine/compression"
"github.com/containers/podman/v4/pkg/machine/define"
"github.com/containers/podman/v4/utils" "github.com/containers/podman/v4/utils"
"github.com/docker/go-units" "github.com/docker/go-units"
"github.com/sirupsen/logrus" "github.com/sirupsen/logrus"
@ -77,14 +79,14 @@ func (p *QEMUVirtualization) NewMachine(opts machine.InitOptions) (machine.VM, e
} }
// set VM ignition file // set VM ignition file
ignitionFile, err := machine.NewMachineFile(filepath.Join(vmConfigDir, vm.Name+".ign"), nil) ignitionFile, err := define.NewMachineFile(filepath.Join(vmConfigDir, vm.Name+".ign"), nil)
if err != nil { if err != nil {
return nil, err return nil, err
} }
vm.IgnitionFile = *ignitionFile vm.IgnitionFile = *ignitionFile
// set VM image file // set VM image file
imagePath, err := machine.NewMachineFile(opts.ImagePath, nil) imagePath, err := define.NewMachineFile(opts.ImagePath, nil)
if err != nil { if err != nil {
return nil, err return nil, err
} }
@ -299,7 +301,7 @@ func (p *QEMUVirtualization) RemoveAndCleanMachines() error {
} }
prevErr = err prevErr = err
} else { } else {
err := machine.GuardedRemoveAll(dataDir) err := utils.GuardedRemoveAll(dataDir)
if err != nil { if err != nil {
if prevErr != nil { if prevErr != nil {
logrus.Error(prevErr) logrus.Error(prevErr)
@ -316,7 +318,7 @@ func (p *QEMUVirtualization) RemoveAndCleanMachines() error {
} }
prevErr = err prevErr = err
} else { } else {
err := machine.GuardedRemoveAll(confDir) err := utils.GuardedRemoveAll(confDir)
if err != nil { if err != nil {
if prevErr != nil { if prevErr != nil {
logrus.Error(prevErr) logrus.Error(prevErr)
@ -333,7 +335,7 @@ func (p *QEMUVirtualization) VMType() machine.VMType {
func VirtualizationProvider() machine.VirtProvider { func VirtualizationProvider() machine.VirtProvider {
return &QEMUVirtualization{ return &QEMUVirtualization{
machine.NewVirtualization(machine.Qemu, machine.Xz, machine.Qcow, vmtype), machine.NewVirtualization(define.Qemu, compression.Xz, define.Qcow, vmtype),
} }
} }

View File

@ -24,6 +24,7 @@ import (
"github.com/containers/common/pkg/config" "github.com/containers/common/pkg/config"
gvproxy "github.com/containers/gvisor-tap-vsock/pkg/types" gvproxy "github.com/containers/gvisor-tap-vsock/pkg/types"
"github.com/containers/podman/v4/pkg/machine" "github.com/containers/podman/v4/pkg/machine"
"github.com/containers/podman/v4/pkg/machine/define"
"github.com/containers/podman/v4/pkg/rootless" "github.com/containers/podman/v4/pkg/rootless"
"github.com/containers/podman/v4/pkg/util" "github.com/containers/podman/v4/pkg/util"
"github.com/containers/storage/pkg/lockfile" "github.com/containers/storage/pkg/lockfile"
@ -46,7 +47,7 @@ const (
type MachineVM struct { type MachineVM struct {
// ConfigPath is the path to the configuration file // ConfigPath is the path to the configuration file
ConfigPath machine.VMFile ConfigPath define.VMFile
// The command line representation of the qemu command // The command line representation of the qemu command
CmdLine QemuCmd CmdLine QemuCmd
// HostUser contains info about host user // HostUser contains info about host user
@ -58,13 +59,13 @@ type MachineVM struct {
// Name of VM // Name of VM
Name string Name string
// PidFilePath is the where the Proxy PID file lives // PidFilePath is the where the Proxy PID file lives
PidFilePath machine.VMFile PidFilePath define.VMFile
// VMPidFilePath is the where the VM PID file lives // VMPidFilePath is the where the VM PID file lives
VMPidFilePath machine.VMFile VMPidFilePath define.VMFile
// QMPMonitor is the qemu monitor object for sending commands // QMPMonitor is the qemu monitor object for sending commands
QMPMonitor Monitor QMPMonitor Monitor
// ReadySocket tells host when vm is booted // ReadySocket tells host when vm is booted
ReadySocket machine.VMFile ReadySocket define.VMFile
// ResourceConfig is physical attrs of the VM // ResourceConfig is physical attrs of the VM
machine.ResourceConfig machine.ResourceConfig
// SSHConfig for accessing the remote vm // SSHConfig for accessing the remote vm
@ -82,7 +83,7 @@ type MachineVM struct {
type Monitor struct { type Monitor struct {
// Address portion of the qmp monitor (/tmp/tmp.sock) // Address portion of the qmp monitor (/tmp/tmp.sock)
Address machine.VMFile Address define.VMFile
// Network portion of the qmp monitor (unix) // Network portion of the qmp monitor (unix)
Network string Network string
// Timeout in seconds for qmp monitor transactions // Timeout in seconds for qmp monitor transactions
@ -105,9 +106,9 @@ func migrateVM(configPath string, config []byte, vm *MachineVM) error {
return err return err
} }
pidFilePath := machine.VMFile{Path: pidFile} pidFilePath := define.VMFile{Path: pidFile}
qmpMonitor := Monitor{ qmpMonitor := Monitor{
Address: machine.VMFile{Path: old.QMPMonitor.Address}, Address: define.VMFile{Path: old.QMPMonitor.Address},
Network: old.QMPMonitor.Network, Network: old.QMPMonitor.Network,
Timeout: old.QMPMonitor.Timeout, Timeout: old.QMPMonitor.Timeout,
} }
@ -116,18 +117,18 @@ func migrateVM(configPath string, config []byte, vm *MachineVM) error {
return err return err
} }
virtualSocketPath := filepath.Join(socketPath, "podman", vm.Name+"_ready.sock") virtualSocketPath := filepath.Join(socketPath, "podman", vm.Name+"_ready.sock")
readySocket := machine.VMFile{Path: virtualSocketPath} readySocket := define.VMFile{Path: virtualSocketPath}
vm.HostUser = machine.HostUser{} vm.HostUser = machine.HostUser{}
vm.ImageConfig = machine.ImageConfig{} vm.ImageConfig = machine.ImageConfig{}
vm.ResourceConfig = machine.ResourceConfig{} vm.ResourceConfig = machine.ResourceConfig{}
vm.SSHConfig = machine.SSHConfig{} vm.SSHConfig = machine.SSHConfig{}
ignitionFilePath, err := machine.NewMachineFile(old.IgnitionFilePath, nil) ignitionFilePath, err := define.NewMachineFile(old.IgnitionFilePath, nil)
if err != nil { if err != nil {
return err return err
} }
imagePath, err := machine.NewMachineFile(old.ImagePath, nil) imagePath, err := define.NewMachineFile(old.ImagePath, nil)
if err != nil { if err != nil {
return err return err
} }
@ -1030,7 +1031,7 @@ func NewQMPMonitor(network, name string, timeout time.Duration) (Monitor, error)
if timeout == 0 { if timeout == 0 {
timeout = defaultQMPTimeout timeout = defaultQMPTimeout
} }
address, err := machine.NewMachineFile(filepath.Join(rtDir, "qmp_"+name+".sock"), nil) address, err := define.NewMachineFile(filepath.Join(rtDir, "qmp_"+name+".sock"), nil)
if err != nil { if err != nil {
return Monitor{}, err return Monitor{}, err
} }
@ -1350,14 +1351,14 @@ func (v *MachineVM) userGlobalSocketLink() (string, error) {
return filepath.Join(filepath.Dir(path), "podman.sock"), err return filepath.Join(filepath.Dir(path), "podman.sock"), err
} }
func (v *MachineVM) forwardSocketPath() (*machine.VMFile, error) { func (v *MachineVM) forwardSocketPath() (*define.VMFile, error) {
sockName := "podman.sock" sockName := "podman.sock"
path, err := machine.GetDataDir(machine.QemuVirt) path, err := machine.GetDataDir(machine.QemuVirt)
if err != nil { if err != nil {
logrus.Errorf("Resolving data dir: %s", err.Error()) logrus.Errorf("Resolving data dir: %s", err.Error())
return nil, err return nil, err
} }
return machine.NewMachineFile(filepath.Join(path, sockName), &sockName) return define.NewMachineFile(filepath.Join(path, sockName), &sockName)
} }
func (v *MachineVM) setConfigPath() error { func (v *MachineVM) setConfigPath() error {
@ -1366,7 +1367,7 @@ func (v *MachineVM) setConfigPath() error {
return err return err
} }
configPath, err := machine.NewMachineFile(filepath.Join(vmConfigDir, v.Name)+".json", nil) configPath, err := define.NewMachineFile(filepath.Join(vmConfigDir, v.Name)+".json", nil)
if err != nil { if err != nil {
return err return err
} }
@ -1385,11 +1386,11 @@ func (v *MachineVM) setPIDSocket() error {
socketDir := filepath.Join(rtPath, "podman") socketDir := filepath.Join(rtPath, "podman")
vmPidFileName := fmt.Sprintf("%s_vm.pid", v.Name) vmPidFileName := fmt.Sprintf("%s_vm.pid", v.Name)
proxyPidFileName := fmt.Sprintf("%s_proxy.pid", v.Name) proxyPidFileName := fmt.Sprintf("%s_proxy.pid", v.Name)
vmPidFilePath, err := machine.NewMachineFile(filepath.Join(socketDir, vmPidFileName), &vmPidFileName) vmPidFilePath, err := define.NewMachineFile(filepath.Join(socketDir, vmPidFileName), &vmPidFileName)
if err != nil { if err != nil {
return err return err
} }
proxyPidFilePath, err := machine.NewMachineFile(filepath.Join(socketDir, proxyPidFileName), &proxyPidFileName) proxyPidFilePath, err := define.NewMachineFile(filepath.Join(socketDir, proxyPidFileName), &proxyPidFileName)
if err != nil { if err != nil {
return err return err
} }

View File

@ -7,22 +7,22 @@ import (
"fmt" "fmt"
"testing" "testing"
"github.com/containers/podman/v4/pkg/machine" "github.com/containers/podman/v4/pkg/machine/define"
"github.com/stretchr/testify/assert" "github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require" "github.com/stretchr/testify/require"
) )
func TestQemuCmd(t *testing.T) { func TestQemuCmd(t *testing.T) {
ignFile, err := machine.NewMachineFile(t.TempDir()+"demo-ignition-file.ign", nil) ignFile, err := define.NewMachineFile(t.TempDir()+"demo-ignition-file.ign", nil)
assert.NoError(t, err) assert.NoError(t, err)
machineAddrFile, err := machine.NewMachineFile(t.TempDir()+"tmp.sock", nil) machineAddrFile, err := define.NewMachineFile(t.TempDir()+"tmp.sock", nil)
assert.NoError(t, err) assert.NoError(t, err)
readySocket, err := machine.NewMachineFile(t.TempDir()+"readySocket.sock", nil) readySocket, err := define.NewMachineFile(t.TempDir()+"readySocket.sock", nil)
assert.NoError(t, err) assert.NoError(t, err)
vmPidFile, err := machine.NewMachineFile(t.TempDir()+"vmpidfile.pid", nil) vmPidFile, err := define.NewMachineFile(t.TempDir()+"vmpidfile.pid", nil)
assert.NoError(t, err) assert.NoError(t, err)
monitor := Monitor{ monitor := Monitor{

View File

@ -7,12 +7,14 @@ import (
"net" "net"
"path/filepath" "path/filepath"
"time" "time"
"github.com/containers/podman/v4/pkg/machine/define"
) )
// SetSocket creates a new machine file for the socket and assigns it to // SetSocket creates a new machine file for the socket and assigns it to
// `socketLoc` // `socketLoc`
func SetSocket(socketLoc *VMFile, path string, symlink *string) error { func SetSocket(socketLoc *define.VMFile, path string, symlink *string) error {
socket, err := NewMachineFile(path, symlink) socket, err := define.NewMachineFile(path, symlink)
if err != nil { if err != nil {
return err return err
} }

View File

@ -10,6 +10,9 @@ import (
"time" "time"
"github.com/containers/podman/v4/pkg/machine" "github.com/containers/podman/v4/pkg/machine"
"github.com/containers/podman/v4/pkg/machine/compression"
"github.com/containers/podman/v4/pkg/machine/define"
"github.com/containers/podman/v4/utils"
"github.com/sirupsen/logrus" "github.com/sirupsen/logrus"
) )
@ -19,7 +22,7 @@ type WSLVirtualization struct {
func VirtualizationProvider() machine.VirtProvider { func VirtualizationProvider() machine.VirtProvider {
return &WSLVirtualization{ return &WSLVirtualization{
machine.NewVirtualization(machine.None, machine.Xz, machine.Tar, vmtype), machine.NewVirtualization(define.None, compression.Xz, define.Tar, vmtype),
} }
} }
@ -196,7 +199,7 @@ func (p *WSLVirtualization) RemoveAndCleanMachines() error {
} }
prevErr = err prevErr = err
} else { } else {
err := machine.GuardedRemoveAll(dataDir) err := utils.GuardedRemoveAll(dataDir)
if err != nil { if err != nil {
if prevErr != nil { if prevErr != nil {
logrus.Error(prevErr) logrus.Error(prevErr)
@ -213,7 +216,7 @@ func (p *WSLVirtualization) RemoveAndCleanMachines() error {
} }
prevErr = err prevErr = err
} else { } else {
err := machine.GuardedRemoveAll(confDir) err := utils.GuardedRemoveAll(confDir)
if err != nil { if err != nil {
if prevErr != nil { if prevErr != nil {
logrus.Error(prevErr) logrus.Error(prevErr)

View File

@ -16,6 +16,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/define"
) )
const ( const (
@ -43,9 +44,9 @@ func NewFedoraDownloader(vmType machine.VMType, vmName, releaseStream string) (m
f := FedoraDownload{ f := FedoraDownload{
Download: machine.Download{ Download: machine.Download{
Arch: machine.GetFcosArch(), Arch: machine.GetFcosArch(),
Artifact: machine.None, Artifact: define.None,
CacheDir: cacheDir, CacheDir: cacheDir,
Format: machine.Tar, Format: define.Tar,
ImageName: imageName, ImageName: imageName,
LocalPath: filepath.Join(cacheDir, imageName), LocalPath: filepath.Join(cacheDir, imageName),
URL: downloadURL, URL: downloadURL,

View File

@ -19,8 +19,10 @@ import (
"github.com/containers/common/pkg/config" "github.com/containers/common/pkg/config"
"github.com/containers/podman/v4/pkg/machine" "github.com/containers/podman/v4/pkg/machine"
"github.com/containers/podman/v4/pkg/machine/define"
"github.com/containers/podman/v4/pkg/machine/wsl/wutil" "github.com/containers/podman/v4/pkg/machine/wsl/wutil"
"github.com/containers/podman/v4/pkg/util" "github.com/containers/podman/v4/pkg/util"
"github.com/containers/podman/v4/utils"
"github.com/containers/storage/pkg/homedir" "github.com/containers/storage/pkg/homedir"
"github.com/containers/storage/pkg/lockfile" "github.com/containers/storage/pkg/lockfile"
"github.com/sirupsen/logrus" "github.com/sirupsen/logrus"
@ -480,22 +482,22 @@ func (v *MachineVM) unprovisionWSL() error {
} }
distDir := filepath.Join(vmDataDir, "wsldist") distDir := filepath.Join(vmDataDir, "wsldist")
distTarget := filepath.Join(distDir, v.Name) distTarget := filepath.Join(distDir, v.Name)
return machine.GuardedRemoveAll(distTarget) return utils.GuardedRemoveAll(distTarget)
} }
func (v *MachineVM) removeMachineConfig() error { func (v *MachineVM) removeMachineConfig() error {
return machine.GuardedRemoveAll(v.ConfigPath) return utils.GuardedRemoveAll(v.ConfigPath)
} }
func (v *MachineVM) removeMachineImage() error { func (v *MachineVM) removeMachineImage() error {
return machine.GuardedRemoveAll(v.ImagePath) return utils.GuardedRemoveAll(v.ImagePath)
} }
func (v *MachineVM) removeSSHKeys() error { func (v *MachineVM) removeSSHKeys() error {
if err := machine.GuardedRemoveAll(fmt.Sprintf("%s.pub", v.IdentityPath)); err != nil { if err := utils.GuardedRemoveAll(fmt.Sprintf("%s.pub", v.IdentityPath)); err != nil {
logrus.Error(err) logrus.Error(err)
} }
return machine.GuardedRemoveAll(v.IdentityPath) return utils.GuardedRemoveAll(v.IdentityPath)
} }
func (v *MachineVM) removeSystemConnections() error { func (v *MachineVM) removeSystemConnections() error {
@ -1635,7 +1637,7 @@ func (v *MachineVM) Remove(name string, opts machine.RemoveOptions) (string, fun
logrus.Error(err) logrus.Error(err)
} }
for _, f := range files { for _, f := range files {
if err := machine.GuardedRemoveAll(f); err != nil { if err := utils.GuardedRemoveAll(f); err != nil {
logrus.Error(err) logrus.Error(err)
} }
} }
@ -1804,15 +1806,15 @@ func (v *MachineVM) Inspect() (*machine.InspectInfo, error) {
connInfo := new(machine.ConnectionConfig) connInfo := new(machine.ConnectionConfig)
machinePipe := toDist(v.Name) machinePipe := toDist(v.Name)
connInfo.PodmanPipe = &machine.VMFile{Path: `\\.\pipe\` + machinePipe} connInfo.PodmanPipe = &define.VMFile{Path: `\\.\pipe\` + machinePipe}
created, lastUp, _ := v.updateTimeStamps(state == machine.Running) created, lastUp, _ := v.updateTimeStamps(state == machine.Running)
return &machine.InspectInfo{ return &machine.InspectInfo{
ConfigPath: machine.VMFile{Path: v.ConfigPath}, ConfigPath: define.VMFile{Path: v.ConfigPath},
ConnectionInfo: *connInfo, ConnectionInfo: *connInfo,
Created: created, Created: created,
Image: machine.ImageConfig{ Image: machine.ImageConfig{
ImagePath: machine.VMFile{Path: v.ImagePath}, ImagePath: define.VMFile{Path: v.ImagePath},
ImageStream: v.ImageStream, ImageStream: v.ImageStream,
}, },
LastUp: lastUp, LastUp: lastUp,

View File

@ -10,12 +10,15 @@ import (
"strconv" "strconv"
"strings" "strings"
"sync" "sync"
"time"
"github.com/containers/common/pkg/cgroups" "github.com/containers/common/pkg/cgroups"
"github.com/containers/storage/pkg/archive" "github.com/containers/storage/pkg/archive"
"github.com/containers/storage/pkg/chrootarchive" "github.com/containers/storage/pkg/chrootarchive"
"github.com/godbus/dbus/v5" "github.com/godbus/dbus/v5"
"github.com/sirupsen/logrus" "github.com/sirupsen/logrus"
"github.com/vbauerster/mpb/v8"
"github.com/vbauerster/mpb/v8/decor"
) )
// ExecCmd executes a command with args and returns its output as a string along // ExecCmd executes a command with args and returns its output as a string along
@ -242,3 +245,34 @@ func MaybeMoveToSubCgroup() error {
}) })
return maybeMoveToSubCgroupSyncErr return maybeMoveToSubCgroupSyncErr
} }
// GuardedRemoveAll functions much like os.RemoveAll but
// will not delete certain catastrophic paths.
func GuardedRemoveAll(path string) error {
if path == "" || path == "/" {
return fmt.Errorf("refusing to recursively delete `%s`", path)
}
return os.RemoveAll(path)
}
func ProgressBar(prefix string, size int64, onComplete string) (*mpb.Progress, *mpb.Bar) {
p := mpb.New(
mpb.WithWidth(80), // Do not go below 80, see bug #17718
mpb.WithRefreshRate(180*time.Millisecond),
)
bar := p.AddBar(size,
mpb.BarFillerClearOnComplete(),
mpb.PrependDecorators(
decor.OnComplete(decor.Name(prefix), onComplete),
),
mpb.AppendDecorators(
decor.OnComplete(decor.CountersKibiByte("%.1f / %.1f"), ""),
),
)
if size == 0 {
bar.SetTotal(0, true)
}
return p, bar
}