github.com/hanks177/podman/v4@v4.1.3-0.20220613032544-16d90015bc83/pkg/machine/config.go (about)

     1  //go:build amd64 || arm64
     2  // +build amd64 arm64
     3  
     4  package machine
     5  
     6  import (
     7  	errors2 "errors"
     8  	"io/ioutil"
     9  	"net"
    10  	"net/url"
    11  	"os"
    12  	"path/filepath"
    13  	"time"
    14  
    15  	"github.com/containers/storage/pkg/homedir"
    16  	"github.com/pkg/errors"
    17  	"github.com/sirupsen/logrus"
    18  )
    19  
    20  type InitOptions struct {
    21  	CPUS         uint64
    22  	DiskSize     uint64
    23  	IgnitionPath string
    24  	ImagePath    string
    25  	Volumes      []string
    26  	VolumeDriver string
    27  	IsDefault    bool
    28  	Memory       uint64
    29  	Name         string
    30  	TimeZone     string
    31  	URI          url.URL
    32  	Username     string
    33  	ReExec       bool
    34  	Rootful      bool
    35  	// The numerical userid of the user that called machine
    36  	UID string
    37  }
    38  
    39  type Status = string
    40  
    41  const (
    42  	// Running indicates the qemu vm is running.
    43  	Running Status = "running"
    44  	// Stopped indicates the vm has stopped.
    45  	Stopped            Status = "stopped"
    46  	DefaultMachineName string = "podman-machine-default"
    47  )
    48  
    49  type Provider interface {
    50  	NewMachine(opts InitOptions) (VM, error)
    51  	LoadVMByName(name string) (VM, error)
    52  	List(opts ListOptions) ([]*ListResponse, error)
    53  	IsValidVMName(name string) (bool, error)
    54  	CheckExclusiveActiveVM() (bool, string, error)
    55  	RemoveAndCleanMachines() error
    56  }
    57  
    58  type RemoteConnectionType string
    59  
    60  var (
    61  	SSHRemoteConnection     RemoteConnectionType = "ssh"
    62  	DefaultIgnitionUserName                      = "core"
    63  	ErrNoSuchVM                                  = errors.New("VM does not exist")
    64  	ErrVMAlreadyExists                           = errors.New("VM already exists")
    65  	ErrVMAlreadyRunning                          = errors.New("VM already running")
    66  	ErrMultipleActiveVM                          = errors.New("only one VM can be active at a time")
    67  	ForwarderBinaryName                          = "gvproxy"
    68  )
    69  
    70  type Download struct {
    71  	Arch                  string
    72  	Artifact              string
    73  	CompressionType       string
    74  	Format                string
    75  	ImageName             string
    76  	LocalPath             string
    77  	LocalUncompressedFile string
    78  	Sha256sum             string
    79  	URL                   *url.URL
    80  	VMName                string
    81  	Size                  int64
    82  }
    83  
    84  type ListOptions struct{}
    85  
    86  type ListResponse struct {
    87  	Name           string
    88  	CreatedAt      time.Time
    89  	LastUp         time.Time
    90  	Running        bool
    91  	Stream         string
    92  	VMType         string
    93  	CPUs           uint64
    94  	Memory         uint64
    95  	DiskSize       uint64
    96  	Port           int
    97  	RemoteUsername string
    98  	IdentityPath   string
    99  }
   100  
   101  type SetOptions struct {
   102  	CPUs     *uint64
   103  	DiskSize *uint64
   104  	Memory   *uint64
   105  	Rootful  *bool
   106  }
   107  
   108  type SSHOptions struct {
   109  	Username string
   110  	Args     []string
   111  }
   112  type StartOptions struct{}
   113  
   114  type StopOptions struct{}
   115  
   116  type RemoveOptions struct {
   117  	Force        bool
   118  	SaveKeys     bool
   119  	SaveImage    bool
   120  	SaveIgnition bool
   121  }
   122  
   123  type InspectOptions struct{}
   124  
   125  type VM interface {
   126  	Init(opts InitOptions) (bool, error)
   127  	Inspect() (*InspectInfo, error)
   128  	Remove(name string, opts RemoveOptions) (string, func() error, error)
   129  	Set(name string, opts SetOptions) ([]error, error)
   130  	SSH(name string, opts SSHOptions) error
   131  	Start(name string, opts StartOptions) error
   132  	State(bypass bool) (Status, error)
   133  	Stop(name string, opts StopOptions) error
   134  }
   135  
   136  type DistributionDownload interface {
   137  	HasUsableCache() (bool, error)
   138  	Get() *Download
   139  }
   140  type InspectInfo struct {
   141  	ConfigPath     VMFile
   142  	ConnectionInfo ConnectionConfig
   143  	Created        time.Time
   144  	Image          ImageConfig
   145  	LastUp         time.Time
   146  	Name           string
   147  	Resources      ResourceConfig
   148  	SSHConfig      SSHConfig
   149  	State          Status
   150  }
   151  
   152  func (rc RemoteConnectionType) MakeSSHURL(host, path, port, userName string) url.URL {
   153  	// TODO Should this function have input verification?
   154  	userInfo := url.User(userName)
   155  	uri := url.URL{
   156  		Scheme:     "ssh",
   157  		Opaque:     "",
   158  		User:       userInfo,
   159  		Host:       host,
   160  		Path:       path,
   161  		RawPath:    "",
   162  		ForceQuery: false,
   163  		RawQuery:   "",
   164  		Fragment:   "",
   165  	}
   166  	if len(port) > 0 {
   167  		uri.Host = net.JoinHostPort(uri.Hostname(), port)
   168  	}
   169  	return uri
   170  }
   171  
   172  // GetDataDir returns the filepath where vm images should
   173  // live for podman-machine.
   174  func GetDataDir(vmType string) (string, error) {
   175  	dataDirPrefix, err := DataDirPrefix()
   176  	if err != nil {
   177  		return "", err
   178  	}
   179  	dataDir := filepath.Join(dataDirPrefix, vmType)
   180  	if _, err := os.Stat(dataDir); !os.IsNotExist(err) {
   181  		return dataDir, nil
   182  	}
   183  	mkdirErr := os.MkdirAll(dataDir, 0755)
   184  	return dataDir, mkdirErr
   185  }
   186  
   187  // DataDirPrefix returns the path prefix for all machine data files
   188  func DataDirPrefix() (string, error) {
   189  	data, err := homedir.GetDataHome()
   190  	if err != nil {
   191  		return "", err
   192  	}
   193  	dataDir := filepath.Join(data, "containers", "podman", "machine")
   194  	return dataDir, nil
   195  }
   196  
   197  // GetConfigDir returns the filepath to where configuration
   198  // files for podman-machine should live
   199  func GetConfDir(vmType string) (string, error) {
   200  	confDirPrefix, err := ConfDirPrefix()
   201  	if err != nil {
   202  		return "", err
   203  	}
   204  	confDir := filepath.Join(confDirPrefix, vmType)
   205  	if _, err := os.Stat(confDir); !os.IsNotExist(err) {
   206  		return confDir, nil
   207  	}
   208  	mkdirErr := os.MkdirAll(confDir, 0755)
   209  	return confDir, mkdirErr
   210  }
   211  
   212  // ConfDirPrefix returns the path prefix for all machine config files
   213  func ConfDirPrefix() (string, error) {
   214  	conf, err := homedir.GetConfigHome()
   215  	if err != nil {
   216  		return "", err
   217  	}
   218  	confDir := filepath.Join(conf, "containers", "podman", "machine")
   219  	return confDir, nil
   220  }
   221  
   222  // ResourceConfig describes physical attributes of the machine
   223  type ResourceConfig struct {
   224  	// CPUs to be assigned to the VM
   225  	CPUs uint64
   226  	// Disk size in gigabytes assigned to the vm
   227  	DiskSize uint64
   228  	// Memory in megabytes assigned to the vm
   229  	Memory uint64
   230  }
   231  
   232  const maxSocketPathLength int = 103
   233  
   234  type VMFile struct {
   235  	// Path is the fully qualified path to a file
   236  	Path string
   237  	// Symlink is a shortened version of Path by using
   238  	// a symlink
   239  	Symlink *string `json:"symlink,omitempty"`
   240  }
   241  
   242  // GetPath returns the working path for a machinefile.  it returns
   243  // the symlink unless one does not exist
   244  func (m *VMFile) GetPath() string {
   245  	if m.Symlink == nil {
   246  		return m.Path
   247  	}
   248  	return *m.Symlink
   249  }
   250  
   251  // Delete removes the machinefile symlink (if it exists) and
   252  // the actual path
   253  func (m *VMFile) Delete() error {
   254  	if m.Symlink != nil {
   255  		if err := os.Remove(*m.Symlink); err != nil && !errors2.Is(err, os.ErrNotExist) {
   256  			logrus.Errorf("unable to remove symlink %q", *m.Symlink)
   257  		}
   258  	}
   259  	if err := os.Remove(m.Path); err != nil && !errors2.Is(err, os.ErrNotExist) {
   260  		return err
   261  	}
   262  	return nil
   263  }
   264  
   265  // Read the contents of a given file and return in []bytes
   266  func (m *VMFile) Read() ([]byte, error) {
   267  	return ioutil.ReadFile(m.GetPath())
   268  }
   269  
   270  // NewMachineFile is a constructor for VMFile
   271  func NewMachineFile(path string, symlink *string) (*VMFile, error) {
   272  	if len(path) < 1 {
   273  		return nil, errors2.New("invalid machine file path")
   274  	}
   275  	if symlink != nil && len(*symlink) < 1 {
   276  		return nil, errors2.New("invalid symlink path")
   277  	}
   278  	mf := VMFile{Path: path}
   279  	if symlink != nil && len(path) > maxSocketPathLength {
   280  		if err := mf.makeSymlink(symlink); err != nil && !errors2.Is(err, os.ErrExist) {
   281  			return nil, err
   282  		}
   283  	}
   284  	return &mf, nil
   285  }
   286  
   287  // makeSymlink for macOS creates a symlink in $HOME/.podman/
   288  // for a machinefile like a socket
   289  func (m *VMFile) makeSymlink(symlink *string) error {
   290  	homeDir, err := os.UserHomeDir()
   291  	if err != nil {
   292  		return err
   293  	}
   294  	sl := filepath.Join(homeDir, ".podman", *symlink)
   295  	// make the symlink dir and throw away if it already exists
   296  	if err := os.MkdirAll(filepath.Dir(sl), 0700); err != nil && !errors2.Is(err, os.ErrNotExist) {
   297  		return err
   298  	}
   299  	m.Symlink = &sl
   300  	return os.Symlink(m.Path, sl)
   301  }
   302  
   303  type Mount struct {
   304  	ReadOnly bool
   305  	Source   string
   306  	Tag      string
   307  	Target   string
   308  	Type     string
   309  }
   310  
   311  // ImageConfig describes the bootable image for the VM
   312  type ImageConfig struct {
   313  	// IgnitionFile is the path to the filesystem where the
   314  	// ignition file was written (if needs one)
   315  	IgnitionFile VMFile `json:"IgnitionFilePath"`
   316  	// ImageStream is the update stream for the image
   317  	ImageStream string
   318  	// ImageFile is the fq path to
   319  	ImagePath VMFile `json:"ImagePath"`
   320  }
   321  
   322  // HostUser describes the host user
   323  type HostUser struct {
   324  	// Whether this machine should run in a rootful or rootless manner
   325  	Rootful bool
   326  	// UID is the numerical id of the user that called machine
   327  	UID int
   328  }
   329  
   330  // SSHConfig contains remote access information for SSH
   331  type SSHConfig struct {
   332  	// IdentityPath is the fq path to the ssh priv key
   333  	IdentityPath string
   334  	// SSH port for user networking
   335  	Port int
   336  	// RemoteUsername of the vm user
   337  	RemoteUsername string
   338  }
   339  
   340  // ConnectionConfig contains connections like sockets, etc.
   341  type ConnectionConfig struct {
   342  	// PodmanSocket is the exported podman service socket
   343  	PodmanSocket *VMFile `json:"PodmanSocket"`
   344  }