mirror of https://github.com/containers/podman.git
Merge pull request #13674 from baude/refactor1
Machine refactor - part 1
This commit is contained in:
commit
9cfa9695a0
|
@ -31,6 +31,10 @@ var (
|
|||
now bool
|
||||
)
|
||||
|
||||
// maxMachineNameSize is set to thirty to limit huge machine names primarily
|
||||
// because macos has a much smaller file size limit.
|
||||
const maxMachineNameSize = 30
|
||||
|
||||
func init() {
|
||||
registry.Commands = append(registry.Commands, registry.CliCommand{
|
||||
Command: initCmd,
|
||||
|
@ -111,10 +115,12 @@ func initMachine(cmd *cobra.Command, args []string) error {
|
|||
vm machine.VM
|
||||
err error
|
||||
)
|
||||
|
||||
provider := getSystemDefaultProvider()
|
||||
initOpts.Name = defaultMachineName
|
||||
if len(args) > 0 {
|
||||
if len(args[0]) > maxMachineNameSize {
|
||||
return errors.New("machine name must be 30 characters or less")
|
||||
}
|
||||
initOpts.Name = args[0]
|
||||
}
|
||||
if _, err := provider.LoadVMByName(initOpts.Name); err == nil {
|
||||
|
|
|
@ -29,7 +29,7 @@ type InitOptions struct {
|
|||
Username string
|
||||
ReExec bool
|
||||
Rootful bool
|
||||
// The numberical userid of the user that called machine
|
||||
// The numerical userid of the user that called machine
|
||||
UID string
|
||||
}
|
||||
|
||||
|
@ -128,6 +128,7 @@ type DistributionDownload interface {
|
|||
}
|
||||
|
||||
func (rc RemoteConnectionType) MakeSSHURL(host, path, port, userName string) url.URL {
|
||||
//TODO Should this function have input verification?
|
||||
userInfo := url.User(userName)
|
||||
uri := url.URL{
|
||||
Scheme: "ssh",
|
||||
|
|
|
@ -0,0 +1,71 @@
|
|||
package machine
|
||||
|
||||
import (
|
||||
"net"
|
||||
"net/url"
|
||||
"reflect"
|
||||
"testing"
|
||||
)
|
||||
|
||||
func TestRemoteConnectionType_MakeSSHURL(t *testing.T) {
|
||||
var (
|
||||
host = "foobar"
|
||||
path = "/path/to/socket"
|
||||
rc = "ssh"
|
||||
username = "core"
|
||||
)
|
||||
type args struct {
|
||||
host string
|
||||
path string
|
||||
port string
|
||||
userName string
|
||||
}
|
||||
tests := []struct {
|
||||
name string
|
||||
rc RemoteConnectionType
|
||||
args args
|
||||
want url.URL
|
||||
}{
|
||||
{
|
||||
name: "Good no port",
|
||||
rc: "ssh",
|
||||
args: args{
|
||||
host: host,
|
||||
path: path,
|
||||
port: "",
|
||||
userName: username,
|
||||
},
|
||||
want: url.URL{
|
||||
Scheme: rc,
|
||||
User: url.User(username),
|
||||
Host: host,
|
||||
Path: path,
|
||||
ForceQuery: false,
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "Good with port",
|
||||
rc: "ssh",
|
||||
args: args{
|
||||
host: host,
|
||||
path: path,
|
||||
port: "222",
|
||||
userName: username,
|
||||
},
|
||||
want: url.URL{
|
||||
Scheme: rc,
|
||||
User: url.User(username),
|
||||
Host: net.JoinHostPort(host, "222"),
|
||||
Path: path,
|
||||
ForceQuery: false,
|
||||
},
|
||||
},
|
||||
}
|
||||
for _, tt := range tests {
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
if got := tt.rc.MakeSSHURL(tt.args.host, tt.args.path, tt.args.port, tt.args.userName); !reflect.DeepEqual(got, tt.want) { //nolint: scopelint
|
||||
t.Errorf("MakeSSHURL() = %v, want %v", got, tt.want) //nolint: scopelint
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
|
@ -4,12 +4,28 @@
|
|||
package qemu
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"os"
|
||||
"time"
|
||||
|
||||
"github.com/sirupsen/logrus"
|
||||
)
|
||||
|
||||
const (
|
||||
// FCOS streams
|
||||
// Testing FCOS stream
|
||||
Testing string = "testing"
|
||||
// Next FCOS stream
|
||||
Next string = "next"
|
||||
// Stable FCOS stream
|
||||
Stable string = "stable"
|
||||
)
|
||||
|
||||
type Provider struct{}
|
||||
|
||||
type MachineVM struct {
|
||||
// Deprecated: MachineVMV1 is being deprecated in favor a more flexible and informative
|
||||
// structure
|
||||
type MachineVMV1 struct {
|
||||
// CPUs to be assigned to the VM
|
||||
CPUs uint64
|
||||
// The command line representation of the qemu command
|
||||
|
@ -42,6 +58,74 @@ type MachineVM struct {
|
|||
UID int
|
||||
}
|
||||
|
||||
type MachineVM struct {
|
||||
// The command line representation of the qemu command
|
||||
CmdLine []string
|
||||
// HostUser contains info about host user
|
||||
HostUser
|
||||
// ImageConfig describes the bootable image
|
||||
ImageConfig
|
||||
// Mounts is the list of remote filesystems to mount
|
||||
Mounts []Mount
|
||||
// Name of VM
|
||||
Name string
|
||||
// PidFilePath is the where the PID file lives
|
||||
PidFilePath MachineFile
|
||||
// QMPMonitor is the qemu monitor object for sending commands
|
||||
QMPMonitor Monitor
|
||||
// ReadySocket tells host when vm is booted
|
||||
ReadySocket MachineFile
|
||||
// ResourceConfig is physical attrs of the VM
|
||||
ResourceConfig
|
||||
// SSHConfig for accessing the remote vm
|
||||
SSHConfig
|
||||
}
|
||||
|
||||
// ImageConfig describes the bootable image for the VM
|
||||
type ImageConfig struct {
|
||||
IgnitionFilePath string
|
||||
// ImageStream is the update stream for the image
|
||||
ImageStream string
|
||||
// ImagePath is the fq path to
|
||||
ImagePath string
|
||||
}
|
||||
|
||||
// HostUser describes the host user
|
||||
type HostUser struct {
|
||||
// Whether this machine should run in a rootful or rootless manner
|
||||
Rootful bool
|
||||
// UID is the numerical id of the user that called machine
|
||||
UID int
|
||||
}
|
||||
|
||||
// SSHConfig contains remote access information for SSH
|
||||
type SSHConfig struct {
|
||||
// IdentityPath is the fq path to the ssh priv key
|
||||
IdentityPath string
|
||||
// SSH port for user networking
|
||||
Port int
|
||||
// RemoteUsername of the vm user
|
||||
RemoteUsername string
|
||||
}
|
||||
|
||||
// ResourceConfig describes physical attributes of the machine
|
||||
type ResourceConfig struct {
|
||||
// CPUs to be assigned to the VM
|
||||
CPUs uint64
|
||||
// Memory in megabytes assigned to the vm
|
||||
Memory uint64
|
||||
// Disk size in gigabytes assigned to the vm
|
||||
DiskSize uint64
|
||||
}
|
||||
|
||||
type MachineFile 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
|
||||
}
|
||||
|
||||
type Mount struct {
|
||||
Type string
|
||||
Tag string
|
||||
|
@ -52,7 +136,7 @@ type Mount struct {
|
|||
|
||||
type Monitor struct {
|
||||
// Address portion of the qmp monitor (/tmp/tmp.sock)
|
||||
Address string
|
||||
Address MachineFile
|
||||
// Network portion of the qmp monitor (unix)
|
||||
Network string
|
||||
// Timeout in seconds for qmp monitor transactions
|
||||
|
@ -64,3 +148,37 @@ var (
|
|||
// qmp monitor interactions.
|
||||
defaultQMPTimeout time.Duration = 2 * time.Second
|
||||
)
|
||||
|
||||
// GetPath returns the working path for a machinefile. it returns
|
||||
// the symlink unless one does not exist
|
||||
func (m *MachineFile) 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 *MachineFile) Delete() error {
|
||||
if m.Symlink != nil {
|
||||
if err := os.Remove(*m.Symlink); err != nil {
|
||||
logrus.Errorf("unable to remove symlink %q", *m.Symlink)
|
||||
}
|
||||
}
|
||||
return os.Remove(m.Path)
|
||||
}
|
||||
|
||||
// NewMachineFile is a constructor for MachineFile
|
||||
func NewMachineFile(path string, symlink *string) (*MachineFile, 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")
|
||||
}
|
||||
return &MachineFile{
|
||||
Path: path,
|
||||
Symlink: symlink,
|
||||
}, nil
|
||||
}
|
||||
|
|
|
@ -0,0 +1,103 @@
|
|||
package qemu
|
||||
|
||||
import (
|
||||
"reflect"
|
||||
"testing"
|
||||
)
|
||||
|
||||
func TestMachineFile_GetPath(t *testing.T) {
|
||||
path := "/var/tmp/podman/my.sock"
|
||||
sym := "/tmp/podman/my.sock"
|
||||
type fields struct {
|
||||
Path string
|
||||
Symlink *string
|
||||
}
|
||||
tests := []struct {
|
||||
name string
|
||||
fields fields
|
||||
want string
|
||||
}{
|
||||
{
|
||||
name: "Original path",
|
||||
fields: fields{path, nil},
|
||||
want: path,
|
||||
},
|
||||
{
|
||||
name: "Symlink over path",
|
||||
fields: fields{
|
||||
Path: path,
|
||||
Symlink: &sym,
|
||||
},
|
||||
want: sym,
|
||||
},
|
||||
}
|
||||
for _, tt := range tests {
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
m := &MachineFile{
|
||||
Path: tt.fields.Path, //nolint: scopelint
|
||||
Symlink: tt.fields.Symlink, //nolint: scopelint
|
||||
}
|
||||
if got := m.GetPath(); got != tt.want { //nolint: scopelint
|
||||
t.Errorf("GetPath() = %v, want %v", got, tt.want) //nolint: scopelint
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestNewMachineFile(t *testing.T) {
|
||||
p := "/var/tmp/podman/my.sock"
|
||||
sym := "/tmp/podman/my.sock"
|
||||
empty := ""
|
||||
|
||||
m := MachineFile{
|
||||
Path: p,
|
||||
Symlink: nil,
|
||||
}
|
||||
type args struct {
|
||||
path string
|
||||
symlink *string
|
||||
}
|
||||
tests := []struct {
|
||||
name string
|
||||
args args
|
||||
want *MachineFile
|
||||
wantErr bool
|
||||
}{
|
||||
{
|
||||
name: "Good",
|
||||
args: args{path: p},
|
||||
want: &m,
|
||||
wantErr: false,
|
||||
},
|
||||
{
|
||||
name: "Good with Symlink",
|
||||
args: args{p, &sym},
|
||||
want: &MachineFile{p, &sym},
|
||||
wantErr: false,
|
||||
},
|
||||
{
|
||||
name: "Bad path name",
|
||||
args: args{empty, nil},
|
||||
want: nil,
|
||||
wantErr: true,
|
||||
},
|
||||
{
|
||||
name: "Bad symlink name",
|
||||
args: args{p, &empty},
|
||||
want: nil,
|
||||
wantErr: true,
|
||||
},
|
||||
}
|
||||
for _, tt := range tests {
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
got, err := NewMachineFile(tt.args.path, tt.args.symlink) //nolint: scopelint
|
||||
if (err != nil) != tt.wantErr { //nolint: scopelint
|
||||
t.Errorf("NewMachineFile() error = %v, wantErr %v", err, tt.wantErr) //nolint: scopelint
|
||||
return
|
||||
}
|
||||
if !reflect.DeepEqual(got, tt.want) { //nolint: scopelint
|
||||
t.Errorf("NewMachineFile() got = %v, want %v", got, tt.want) //nolint: scopelint
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
|
@ -111,7 +111,7 @@ func (p *Provider) NewMachine(opts machine.InitOptions) (machine.VM, error) {
|
|||
return nil, err
|
||||
}
|
||||
vm.QMPMonitor = monitor
|
||||
cmd = append(cmd, []string{"-qmp", monitor.Network + ":/" + monitor.Address + ",server=on,wait=off"}...)
|
||||
cmd = append(cmd, []string{"-qmp", monitor.Network + ":/" + monitor.Address.GetPath() + ",server=on,wait=off"}...)
|
||||
|
||||
// Add network
|
||||
// Right now the mac address is hardcoded so that the host networking gives it a specific IP address. This is
|
||||
|
@ -134,7 +134,8 @@ func (p *Provider) NewMachine(opts machine.InitOptions) (machine.VM, 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) {
|
||||
vm := &MachineVM{UID: -1} // posix reserves -1, so use it to signify undefined
|
||||
vm := &MachineVM{Name: name}
|
||||
vm.HostUser = HostUser{UID: -1} // posix reserves -1, so use it to signify undefined
|
||||
vmConfigDir, err := machine.GetConfDir(vmtype)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
|
@ -176,7 +177,7 @@ func (v *MachineVM) Init(opts machine.InitOptions) (bool, error) {
|
|||
v.Rootful = opts.Rootful
|
||||
|
||||
switch opts.ImagePath {
|
||||
case "testing", "next", "stable", "":
|
||||
case Testing, Next, Stable, "":
|
||||
// Get image as usual
|
||||
v.ImageStream = opts.ImagePath
|
||||
dd, err := machine.NewFcosDownloader(vmtype, v.Name, opts.ImagePath)
|
||||
|
@ -576,12 +577,12 @@ func (v *MachineVM) checkStatus(monitor *qmp.SocketMonitor) (machine.QemuMachine
|
|||
func (v *MachineVM) Stop(name string, _ machine.StopOptions) error {
|
||||
var disconnected bool
|
||||
// check if the qmp socket is there. if not, qemu instance is gone
|
||||
if _, err := os.Stat(v.QMPMonitor.Address); os.IsNotExist(err) {
|
||||
if _, err := os.Stat(v.QMPMonitor.Address.GetPath()); os.IsNotExist(err) {
|
||||
// Right now it is NOT an error to stop a stopped machine
|
||||
logrus.Debugf("QMP monitor socket %v does not exist", v.QMPMonitor.Address)
|
||||
return nil
|
||||
}
|
||||
qmpMonitor, err := qmp.NewSocketMonitor(v.QMPMonitor.Network, v.QMPMonitor.Address, v.QMPMonitor.Timeout)
|
||||
qmpMonitor, err := qmp.NewSocketMonitor(v.QMPMonitor.Network, v.QMPMonitor.Address.GetPath(), v.QMPMonitor.Timeout)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
@ -684,20 +685,25 @@ func NewQMPMonitor(network, name string, timeout time.Duration) (Monitor, error)
|
|||
if timeout == 0 {
|
||||
timeout = defaultQMPTimeout
|
||||
}
|
||||
address, err := NewMachineFile(filepath.Join(rtDir, "qmp+"+name+".sock"), nil)
|
||||
if err != nil {
|
||||
return Monitor{}, err
|
||||
}
|
||||
monitor := Monitor{
|
||||
Network: network,
|
||||
Address: filepath.Join(rtDir, "qmp_"+name+".sock"),
|
||||
Address: *address,
|
||||
Timeout: timeout,
|
||||
}
|
||||
return monitor, nil
|
||||
}
|
||||
|
||||
// Remove deletes all the files associated with a machine including ssh keys, the image itself
|
||||
func (v *MachineVM) Remove(name string, opts machine.RemoveOptions) (string, func() error, error) {
|
||||
var (
|
||||
files []string
|
||||
)
|
||||
|
||||
// cannot remove a running vm
|
||||
// cannot remove a running vm unless --force is used
|
||||
running, err := v.isRunning()
|
||||
if err != nil {
|
||||
return "", nil, err
|
||||
|
@ -768,11 +774,11 @@ func (v *MachineVM) Remove(name string, opts machine.RemoveOptions) (string, fun
|
|||
|
||||
func (v *MachineVM) isRunning() (bool, error) {
|
||||
// Check if qmp socket path exists
|
||||
if _, err := os.Stat(v.QMPMonitor.Address); os.IsNotExist(err) {
|
||||
if _, err := os.Stat(v.QMPMonitor.Address.GetPath()); os.IsNotExist(err) {
|
||||
return false, nil
|
||||
}
|
||||
// Check if we can dial it
|
||||
monitor, err := qmp.NewSocketMonitor(v.QMPMonitor.Network, v.QMPMonitor.Address, v.QMPMonitor.Timeout)
|
||||
monitor, err := qmp.NewSocketMonitor(v.QMPMonitor.Network, v.QMPMonitor.Address.GetPath(), v.QMPMonitor.Timeout)
|
||||
if err != nil {
|
||||
// FIXME: this error should probably be returned
|
||||
return false, nil // nolint: nilerr
|
||||
|
|
Loading…
Reference in New Issue