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 }