diff options
Diffstat (limited to 'pkg')
-rw-r--r-- | pkg/bindings/images/build.go | 9 | ||||
-rw-r--r-- | pkg/bindings/system/system.go | 1 | ||||
-rw-r--r-- | pkg/domain/entities/pods.go | 2 | ||||
-rw-r--r-- | pkg/domain/infra/abi/containers.go | 2 | ||||
-rw-r--r-- | pkg/domain/infra/abi/play.go | 11 | ||||
-rw-r--r-- | pkg/machine/qemu/config.go | 19 | ||||
-rw-r--r-- | pkg/machine/qemu/machine.go | 269 | ||||
-rw-r--r-- | pkg/machine/qemu/options_darwin_arm64.go | 6 | ||||
-rw-r--r-- | pkg/machine/wsl/machine.go | 30 | ||||
-rw-r--r-- | pkg/specgen/container_validate.go | 4 | ||||
-rw-r--r-- | pkg/specgen/generate/container.go | 43 | ||||
-rw-r--r-- | pkg/specgen/generate/container_create.go | 33 | ||||
-rw-r--r-- | pkg/specgen/generate/oci.go | 4 | ||||
-rw-r--r-- | pkg/specgenutil/specgen.go | 4 | ||||
-rw-r--r-- | pkg/specgenutil/volumes.go | 12 |
15 files changed, 294 insertions, 155 deletions
diff --git a/pkg/bindings/images/build.go b/pkg/bindings/images/build.go index f6739b7ca..ab562377f 100644 --- a/pkg/bindings/images/build.go +++ b/pkg/bindings/images/build.go @@ -367,20 +367,20 @@ func Build(ctx context.Context, containerFiles []string, options entities.BuildO return nil, err } - // Check if Containerfile is in the context directory, if so truncate the contextdirectory off path + // Check if Containerfile is in the context directory, if so truncate the context directory off path // Do NOT add to tarfile if strings.HasPrefix(containerfile, contextDir+string(filepath.Separator)) { containerfile = strings.TrimPrefix(containerfile, contextDir+string(filepath.Separator)) dontexcludes = append(dontexcludes, "!"+containerfile) } else { - // If Containerfile does not exists assume it is in context directory, do Not add to tarfile + // If Containerfile does not exist, assume it is in context directory and do Not add to tarfile if _, err := os.Lstat(containerfile); err != nil { if !os.IsNotExist(err) { return nil, err } containerfile = c } else { - // If Containerfile does exists but is not in context directory add it to the tarfile + // If Containerfile does exist and not in the context directory, add it to the tarfile tarContent = append(tarContent, containerfile) } } @@ -586,6 +586,9 @@ func nTar(excludes []string, sources ...string) (io.ReadCloser, error) { return errors.Wrapf(err, "error checking if %q is excluded", name) } if excluded { + // Note: filepath.SkipDir is not possible to use given .dockerignore semantics. + // An exception to exclusions may include an excluded directory, therefore we + // are required to visit all files. :( return nil } diff --git a/pkg/bindings/system/system.go b/pkg/bindings/system/system.go index 1eb90ab54..5ef78e444 100644 --- a/pkg/bindings/system/system.go +++ b/pkg/bindings/system/system.go @@ -120,6 +120,7 @@ func Version(ctx context.Context, options *VersionOptions) (*entities.SystemVers BuiltTime: time.Unix(b.Unix(), 0).Format(time.ANSIC), Built: b.Unix(), OsArch: fmt.Sprintf("%s/%s", component.Os, component.Arch), + Os: component.Os, } for _, c := range component.Components { diff --git a/pkg/domain/entities/pods.go b/pkg/domain/entities/pods.go index a483064ab..f1d445c4b 100644 --- a/pkg/domain/entities/pods.go +++ b/pkg/domain/entities/pods.go @@ -212,7 +212,7 @@ type ContainerCreateOptions struct { Name string `json:"container_name"` NoHealthCheck bool OOMKillDisable bool - OOMScoreAdj int + OOMScoreAdj *int Arch string OS string Variant string diff --git a/pkg/domain/infra/abi/containers.go b/pkg/domain/infra/abi/containers.go index 35689092a..100842c69 100644 --- a/pkg/domain/infra/abi/containers.go +++ b/pkg/domain/infra/abi/containers.go @@ -1492,7 +1492,7 @@ func (ic *ContainerEngine) ContainerRename(ctx context.Context, nameOrID string, func (ic *ContainerEngine) ContainerClone(ctx context.Context, ctrCloneOpts entities.ContainerCloneOptions) (*entities.ContainerCreateReport, error) { spec := specgen.NewSpecGenerator(ctrCloneOpts.Image, ctrCloneOpts.CreateOpts.RootFS) var c *libpod.Container - c, err := generate.ConfigToSpec(ic.Libpod, spec, ctrCloneOpts.ID) + c, _, err := generate.ConfigToSpec(ic.Libpod, spec, ctrCloneOpts.ID) if err != nil { return nil, err } diff --git a/pkg/domain/infra/abi/play.go b/pkg/domain/infra/abi/play.go index 1423ab06e..c3f6bb17d 100644 --- a/pkg/domain/infra/abi/play.go +++ b/pkg/domain/infra/abi/play.go @@ -290,7 +290,16 @@ func (ic *ContainerEngine) playKubePod(ctx context.Context, podName string, podY if v.Type == kube.KubeVolumeTypeConfigMap && !v.Optional { vol, err := ic.Libpod.NewVolume(ctx, libpod.WithVolumeName(v.Source)) if err != nil { - return nil, errors.Wrapf(err, "cannot create a local volume for volume from configmap %q", v.Source) + if errors.Is(err, define.ErrVolumeExists) { + // Volume for this configmap already exists do not + // error out instead reuse the current volume. + vol, err = ic.Libpod.GetVolume(v.Source) + if err != nil { + return nil, errors.Wrapf(err, "cannot re-use local volume for volume from configmap %q", v.Source) + } + } else { + return nil, errors.Wrapf(err, "cannot create a local volume for volume from configmap %q", v.Source) + } } mountPoint, err := vol.MountPoint() if err != nil || mountPoint == "" { diff --git a/pkg/machine/qemu/config.go b/pkg/machine/qemu/config.go index 05a1d74d3..3d1032fba 100644 --- a/pkg/machine/qemu/config.go +++ b/pkg/machine/qemu/config.go @@ -5,6 +5,7 @@ package qemu import ( "errors" + "io/ioutil" "os" "time" @@ -59,6 +60,8 @@ type MachineVMV1 struct { } type MachineVM struct { + // ConfigPath is the path to the configuration file + ConfigPath MachineFile // The command line representation of the qemu command CmdLine []string // HostUser contains info about host user @@ -83,11 +86,11 @@ type MachineVM struct { // ImageConfig describes the bootable image for the VM type ImageConfig struct { - IgnitionFilePath string + IgnitionFilePath MachineFile // ImageStream is the update stream for the image ImageStream string // ImagePath is the fq path to - ImagePath string + ImagePath MachineFile } // HostUser describes the host user @@ -171,11 +174,19 @@ func (m *MachineFile) GetPath() string { // the actual path func (m *MachineFile) Delete() error { if m.Symlink != nil { - if err := os.Remove(*m.Symlink); err != nil { + if err := os.Remove(*m.Symlink); err != nil && !errors.Is(err, os.ErrNotExist) { logrus.Errorf("unable to remove symlink %q", *m.Symlink) } } - return os.Remove(m.Path) + 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 *MachineFile) Read() ([]byte, error) { + return ioutil.ReadFile(m.GetPath()) } // NewMachineFile is a constructor for MachineFile diff --git a/pkg/machine/qemu/machine.go b/pkg/machine/qemu/machine.go index 07155bbcf..65980129d 100644 --- a/pkg/machine/qemu/machine.go +++ b/pkg/machine/qemu/machine.go @@ -71,10 +71,17 @@ func (p *Provider) NewMachine(opts machine.InitOptions) (machine.VM, error) { if len(opts.Name) > 0 { vm.Name = opts.Name } - ignitionFile := filepath.Join(vmConfigDir, vm.Name+".ign") - vm.IgnitionFilePath = ignitionFile + ignitionFile, err := NewMachineFile(filepath.Join(vmConfigDir, vm.Name+".ign"), nil) + if err != nil { + return nil, err + } + vm.IgnitionFilePath = *ignitionFile - vm.ImagePath = opts.ImagePath + imagePath, err := NewMachineFile(opts.ImagePath, nil) + if err != nil { + return nil, err + } + vm.ImagePath = *imagePath vm.RemoteUsername = opts.Username // Add a random port for ssh @@ -104,7 +111,7 @@ func (p *Provider) NewMachine(opts machine.InitOptions) (machine.VM, error) { // Add cpus cmd = append(cmd, []string{"-smp", strconv.Itoa(int(vm.CPUs))}...) // Add ignition file - cmd = append(cmd, []string{"-fw_cfg", "name=opt/com.coreos/config,file=" + vm.IgnitionFilePath}...) + cmd = append(cmd, []string{"-fw_cfg", "name=opt/com.coreos/config,file=" + vm.IgnitionFilePath.GetPath()}...) // Add qmp socket monitor, err := NewQMPMonitor("unix", vm.Name, defaultQMPTimeout) if err != nil { @@ -117,17 +124,19 @@ func (p *Provider) NewMachine(opts machine.InitOptions) (machine.VM, error) { // Right now the mac address is hardcoded so that the host networking gives it a specific IP address. This is // why we can only run one vm at a time right now cmd = append(cmd, []string{"-netdev", "socket,id=vlan,fd=3", "-device", "virtio-net-pci,netdev=vlan,mac=5a:94:ef:e4:0c:ee"}...) - socketPath, err := getRuntimeDir() - if err != nil { + if err := vm.setReadySocket(); err != nil { return nil, err } - virtualSocketPath := filepath.Join(socketPath, "podman", vm.Name+"_ready.sock") + // Add serial port for readiness cmd = append(cmd, []string{ "-device", "virtio-serial", - "-chardev", "socket,path=" + virtualSocketPath + ",server=on,wait=off,id=" + vm.Name + "_ready", + "-chardev", "socket,path=" + vm.getReadySocket() + ",server=on,wait=off,id=" + vm.Name + "_ready", "-device", "virtserialport,chardev=" + vm.Name + "_ready" + ",name=org.fedoraproject.port.0"}...) vm.CmdLine = cmd + if err := vm.setQEMUAndPIDSocket(); err != nil { + return nil, err + } return vm, nil } @@ -165,12 +174,26 @@ func migrateVM(configPath string, config []byte, vm *MachineVM) error { vm.ResourceConfig = ResourceConfig{} vm.SSHConfig = SSHConfig{} + ignitionFilePath, err := NewMachineFile(old.IgnitionFilePath, nil) + if err != nil { + return err + } + imagePath, err := NewMachineFile(old.ImagePath, nil) + if err != nil { + return err + } + + // setReadySocket will stick the entry into the new struct + if err := vm.setReadySocket(); err != nil { + return err + } + vm.CPUs = old.CPUs vm.CmdLine = old.CmdLine vm.DiskSize = old.DiskSize vm.IdentityPath = old.IdentityPath - vm.IgnitionFilePath = old.IgnitionFilePath - vm.ImagePath = old.ImagePath + vm.IgnitionFilePath = *ignitionFilePath + vm.ImagePath = *imagePath vm.ImageStream = old.ImageStream vm.Memory = old.Memory vm.Mounts = old.Mounts @@ -200,31 +223,15 @@ func migrateVM(configPath string, config []byte, vm *MachineVM) error { return os.Remove(configPath + ".orig") } -// LoadByName reads a json file that describes a known qemu vm +// LoadVMByName reads a json file that describes a known qemu vm // and returns a vm instance func (p *Provider) LoadVMByName(name string) (machine.VM, error) { 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 { + if err := vm.update(); err != nil { return nil, err } - path := filepath.Join(vmConfigDir, name+".json") - b, err := ioutil.ReadFile(path) - if os.IsNotExist(err) { - return nil, errors.Wrap(machine.ErrNoSuchVM, name) - } - if err != nil { - return nil, err - } - err = json.Unmarshal(b, vm) - if err != nil { - migrateErr := migrateVM(path, b, vm) - if migrateErr != nil { - return nil, migrateErr - } - err = migrateErr - } + // It is here for providing the ability to propagate // proxy settings (e.g. HTTP_PROXY and others) on a start // and avoid a need of re-creating/re-initiating a VM @@ -239,7 +246,7 @@ func (p *Provider) LoadVMByName(name string) (machine.VM, error) { } logrus.Debug(vm.CmdLine) - return vm, err + return vm, nil } // Init writes the json configuration file to the filesystem for @@ -261,7 +268,11 @@ func (v *MachineVM) Init(opts machine.InitOptions) (bool, error) { if err != nil { return false, err } - v.ImagePath = dd.Get().LocalUncompressedFile + uncompressedFile, err := NewMachineFile(dd.Get().LocalUncompressedFile, nil) + if err != nil { + return false, err + } + v.ImagePath = *uncompressedFile if err := machine.DownloadImage(dd); err != nil { return false, err } @@ -273,14 +284,17 @@ func (v *MachineVM) Init(opts machine.InitOptions) (bool, error) { if err != nil { return false, err } - v.ImagePath = g.Get().LocalUncompressedFile + imagePath, err := NewMachineFile(g.Get().LocalUncompressedFile, nil) + if err != nil { + return false, err + } + v.ImagePath = *imagePath if err := machine.DownloadImage(g); err != nil { return false, err } } // Add arch specific options including image location v.CmdLine = append(v.CmdLine, v.addArchOptions()...) - var volumeType string switch opts.VolumeDriver { case "virtfs": @@ -330,7 +344,7 @@ func (v *MachineVM) Init(opts machine.InitOptions) (bool, error) { v.UID = os.Getuid() // Add location of bootable image - v.CmdLine = append(v.CmdLine, "-drive", "if=virtio,file="+v.ImagePath) + v.CmdLine = append(v.CmdLine, "-drive", "if=virtio,file="+v.getImageFile()) // This kind of stinks but no other way around this r/n if len(opts.IgnitionPath) < 1 { uri := machine.SSHRemoteConnection.MakeSSHURL("localhost", fmt.Sprintf("/run/user/%d/podman/podman.sock", v.UID), strconv.Itoa(v.Port), v.RemoteUsername) @@ -373,7 +387,7 @@ func (v *MachineVM) Init(opts machine.InitOptions) (bool, error) { return false, err } - originalDiskSize, err := getDiskSize(v.ImagePath) + originalDiskSize, err := getDiskSize(v.getImageFile()) if err != nil { return false, err } @@ -390,7 +404,7 @@ func (v *MachineVM) Init(opts machine.InitOptions) (bool, error) { if err != nil { return false, err } - resize := exec.Command(resizePath, []string{"resize", v.ImagePath, strconv.Itoa(int(opts.DiskSize)) + "G"}...) + resize := exec.Command(resizePath, []string{"resize", v.getImageFile(), strconv.Itoa(int(opts.DiskSize)) + "G"}...) resize.Stdout = os.Stdout resize.Stderr = os.Stderr if err := resize.Run(); err != nil { @@ -404,7 +418,7 @@ func (v *MachineVM) Init(opts machine.InitOptions) (bool, error) { if err != nil { return false, err } - return false, ioutil.WriteFile(v.IgnitionFilePath, inputIgnition, 0644) + return false, ioutil.WriteFile(v.getIgnitionFile(), inputIgnition, 0644) } // Write the ignition file ign := machine.DynamicIgnition{ @@ -412,14 +426,14 @@ func (v *MachineVM) Init(opts machine.InitOptions) (bool, error) { Key: key, VMName: v.Name, TimeZone: opts.TimeZone, - WritePath: v.IgnitionFilePath, + WritePath: v.getIgnitionFile(), UID: v.UID, } err = machine.NewIgnitionFile(ign) return err == nil, err } -func (v *MachineVM) Set(name string, opts machine.SetOptions) error { +func (v *MachineVM) Set(_ string, opts machine.SetOptions) error { if v.Rootful == opts.Rootful { return nil } @@ -473,17 +487,16 @@ func (v *MachineVM) Start(name string, _ machine.StartOptions) error { return err } } - qemuSocketPath, _, err := v.getSocketandPid() - if err != nil { + if err := v.setQEMUAndPIDSocket(); err != nil { return err } // If the qemusocketpath exists and the vm is off/down, we should rm // it before the dial as to avoid a segv - if err := os.Remove(qemuSocketPath); err != nil && !errors.Is(err, os.ErrNotExist) { - logrus.Warn(err) + if err := v.QMPMonitor.Address.Delete(); err != nil { + return err } for i := 0; i < 6; i++ { - qemuSocketConn, err = net.Dial("unix", qemuSocketPath) + qemuSocketConn, err = net.Dial("unix", v.QMPMonitor.Address.GetPath()) if err == nil { break } @@ -650,7 +663,7 @@ func (v *MachineVM) checkStatus(monitor *qmp.SocketMonitor) (machine.QemuMachine } // Stop uses the qmp monitor to call a system_powerdown -func (v *MachineVM) Stop(name string, _ machine.StopOptions) error { +func (v *MachineVM) Stop(_ 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.GetPath()); os.IsNotExist(err) { @@ -687,14 +700,10 @@ func (v *MachineVM) Stop(name string, _ machine.StopOptions) error { return err } - qemuSocketFile, pidFile, err := v.getSocketandPid() - if err != nil { - return err - } - if _, err := os.Stat(pidFile); os.IsNotExist(err) { + if _, err := os.Stat(v.getPidFile()); os.IsNotExist(err) { return nil } - pidString, err := ioutil.ReadFile(pidFile) + pidString, err := v.PidFilePath.Read() if err != nil { return err } @@ -712,11 +721,11 @@ func (v *MachineVM) Stop(name string, _ machine.StopOptions) error { return err } // Remove the pidfile - if err := os.Remove(pidFile); err != nil && !errors.Is(err, os.ErrNotExist) { - logrus.Warn(err) + if err := v.PidFilePath.Delete(); err != nil { + return err } // Remove socket - if err := os.Remove(qemuSocketFile); err != nil { + if err := v.QMPMonitor.Address.Delete(); err != nil { return err } @@ -774,7 +783,7 @@ func NewQMPMonitor(network, name string, timeout time.Duration) (Monitor, error) } // 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) { +func (v *MachineVM) Remove(_ string, opts machine.RemoveOptions) (string, func() error, error) { var ( files []string ) @@ -793,10 +802,10 @@ func (v *MachineVM) Remove(name string, opts machine.RemoveOptions) (string, fun files = append(files, v.IdentityPath, v.IdentityPath+".pub") } if !opts.SaveIgnition { - files = append(files, v.IgnitionFilePath) + files = append(files, v.getIgnitionFile()) } if !opts.SaveImage { - files = append(files, v.ImagePath) + files = append(files, v.getImageFile()) } socketPath, err := v.getForwardSocketPath() if err != nil { @@ -822,19 +831,15 @@ func (v *MachineVM) Remove(name string, opts machine.RemoveOptions) (string, fun confirmationMessage += msg + "\n" } - // Get path to socket and pidFile before we do any cleanups - qemuSocketFile, pidFile, errSocketFile := v.getSocketandPid() //silently try to delete socket and pid file //remove socket and pid file if any: warn at low priority if things fail - if errSocketFile == nil { - // Remove the pidfile - if err := os.Remove(pidFile); err != nil && !errors.Is(err, os.ErrNotExist) { - logrus.Debugf("Error while removing pidfile: %v", err) - } - // Remove socket - if err := os.Remove(qemuSocketFile); err != nil && !errors.Is(err, os.ErrNotExist) { - logrus.Debugf("Error while removing podman-machine-socket: %v", err) - } + // Remove the pidfile + if err := v.PidFilePath.Delete(); err != nil { + logrus.Debugf("Error while removing pidfile: %v", err) + } + // Remove socket + if err := v.QMPMonitor.Address.Delete(); err != nil { + logrus.Debugf("Error while removing podman-machine-socket: %v", err) } confirmationMessage += "\n" @@ -890,7 +895,7 @@ func (v *MachineVM) isListening() bool { // SSH opens an interactive SSH session to the vm specified. // Added ssh function to VM interface: pkg/machine/config/go : line 58 -func (v *MachineVM) SSH(name string, opts machine.SSHOptions) error { +func (v *MachineVM) SSH(_ string, opts machine.SSHOptions) error { running, err := v.isRunning() if err != nil { return err @@ -966,10 +971,10 @@ func getDiskSize(path string) (uint64, error) { // List lists all vm's that use qemu virtualization func (p *Provider) List(_ machine.ListOptions) ([]*machine.ListResponse, error) { - return GetVMInfos() + return getVMInfos() } -func GetVMInfos() ([]*machine.ListResponse, error) { +func getVMInfos() ([]*machine.ListResponse, error) { vmConfigDir, err := machine.GetConfDir(vmtype) if err != nil { return nil, err @@ -1011,7 +1016,7 @@ func GetVMInfos() ([]*machine.ListResponse, error) { } listEntry.CreatedAt = fi.ModTime() - fi, err = os.Stat(vm.ImagePath) + fi, err = os.Stat(vm.getImageFile()) if err != nil { return err } @@ -1034,7 +1039,7 @@ func GetVMInfos() ([]*machine.ListResponse, error) { } func (p *Provider) IsValidVMName(name string) (bool, error) { - infos, err := GetVMInfos() + infos, err := getVMInfos() if err != nil { return false, err } @@ -1049,7 +1054,7 @@ func (p *Provider) IsValidVMName(name string) (bool, error) { // CheckExclusiveActiveVM checks if there is a VM already running // that does not allow other VMs to be running func (p *Provider) CheckExclusiveActiveVM() (bool, string, error) { - vms, err := GetVMInfos() + vms, err := getVMInfos() if err != nil { return false, "", errors.Wrap(err, "error checking VM active") } @@ -1073,16 +1078,12 @@ func (v *MachineVM) startHostNetworking() (string, apiForwardingState, error) { return "", noForwarding, err } - qemuSocket, pidFile, err := v.getSocketandPid() - if err != nil { - return "", noForwarding, err - } attr := new(os.ProcAttr) // Pass on stdin, stdout, stderr files := []*os.File{os.Stdin, os.Stdout, os.Stderr} attr.Files = files cmd := []string{binary} - cmd = append(cmd, []string{"-listen-qemu", fmt.Sprintf("unix://%s", qemuSocket), "-pid-file", pidFile}...) + cmd = append(cmd, []string{"-listen-qemu", fmt.Sprintf("unix://%s", v.getQMPMonitorSocket()), "-pid-file", v.getPidFile()}...) // Add the ssh port cmd = append(cmd, []string{"-ssh-port", fmt.Sprintf("%d", v.Port)}...) @@ -1171,6 +1172,52 @@ func (v *MachineVM) getForwardSocketPath() (string, error) { return filepath.Join(path, "podman.sock"), nil } +func (v *MachineVM) setConfigPath() error { + vmConfigDir, err := machine.GetConfDir(vmtype) + if err != nil { + return err + } + + configPath, err := NewMachineFile(filepath.Join(vmConfigDir, v.Name)+".json", nil) + if err != nil { + return err + } + v.ConfigPath = *configPath + return nil +} + +func (v *MachineVM) setReadySocket() error { + rtPath, err := getRuntimeDir() + if err != nil { + return err + } + virtualSocketPath, err := NewMachineFile(filepath.Join(rtPath, "podman", v.Name+"_ready.sock"), nil) + if err != nil { + return err + } + v.ReadySocket = *virtualSocketPath + return nil +} + +func (v *MachineVM) setQEMUAndPIDSocket() error { + rtPath, err := getRuntimeDir() + if err != nil { + return err + } + if !rootless.IsRootless() { + rtPath = "/run" + } + socketDir := filepath.Join(rtPath, "podman") + pidFilePath, err := NewMachineFile(filepath.Join(socketDir, fmt.Sprintf("%s.pid", v.Name)), nil) + if err != nil { + return err + } + v.PidFilePath = *pidFilePath + return nil +} + +// Deprecated: getSocketandPid is being replace by setQEMUAndPIDSocket and +// machinefiles. func (v *MachineVM) getSocketandPid() (string, string, error) { rtPath, err := getRuntimeDir() if err != nil { @@ -1287,23 +1334,73 @@ func (v *MachineVM) waitAPIAndPrintInfo(forwardState apiForwardingState, forward } } -func (v *MachineVM) writeConfig() error { - // GetConfDir creates the directory so no need to check for - // its existence - vmConfigDir, err := machine.GetConfDir(vmtype) +// update returns the content of the VM's +// configuration file in json +func (v *MachineVM) update() error { + if err := v.setConfigPath(); err != nil { + return err + } + b, err := v.ConfigPath.Read() if err != nil { + if os.IsNotExist(err) { + return errors.Wrap(machine.ErrNoSuchVM, v.Name) + } return err } + if err != nil { + return err + } + err = json.Unmarshal(b, v) + if err != nil { + err = migrateVM(v.ConfigPath.GetPath(), b, v) + if err != nil { + return err + } + } + return err +} - jsonFile := filepath.Join(vmConfigDir, v.Name) + ".json" +func (v *MachineVM) writeConfig() error { + // Set the path of the configfile before writing to make + // life easier down the line + if err := v.setConfigPath(); err != nil { + return err + } // Write the JSON file b, err := json.MarshalIndent(v, "", " ") if err != nil { return err } - if err := ioutil.WriteFile(jsonFile, b, 0644); err != nil { + if err := ioutil.WriteFile(v.ConfigPath.GetPath(), b, 0644); err != nil { return err } - return nil } + +// getPidFile gets the file where the machine pid is stored +func (v *MachineVM) getPidFile() string { + return v.PidFilePath.GetPath() +} + +// getQMPMonitorSocket gets the socket used by qemu to interact +// with the instance +func (v *MachineVM) getQMPMonitorSocket() string { + return v.QMPMonitor.Address.GetPath() +} + +// getReadySocket returns the socket used to communicate +// with the machinevm and report when it is booted +func (v *MachineVM) getReadySocket() string { + return v.ReadySocket.GetPath() +} + +// getImageFile returns the path to the image used +// to boot the VM +func (v *MachineVM) getImageFile() string { + return v.ImagePath.GetPath() +} + +// getIgnitionFile returns the path to the ignition file +func (v *MachineVM) getIgnitionFile() string { + return v.IgnitionFilePath.GetPath() +} diff --git a/pkg/machine/qemu/options_darwin_arm64.go b/pkg/machine/qemu/options_darwin_arm64.go index 5b6cdc86d..4c954af00 100644 --- a/pkg/machine/qemu/options_darwin_arm64.go +++ b/pkg/machine/qemu/options_darwin_arm64.go @@ -11,7 +11,7 @@ var ( ) func (v *MachineVM) addArchOptions() []string { - ovmfDir := getOvmfDir(v.ImagePath, v.Name) + ovmfDir := getOvmfDir(v.ImagePath.GetPath(), v.Name) opts := []string{ "-accel", "hvf", "-accel", "tcg", @@ -23,13 +23,13 @@ func (v *MachineVM) addArchOptions() []string { } func (v *MachineVM) prepare() error { - ovmfDir := getOvmfDir(v.ImagePath, v.Name) + ovmfDir := getOvmfDir(v.ImagePath.GetPath(), v.Name) cmd := []string{"/bin/dd", "if=/dev/zero", "conv=sync", "bs=1m", "count=64", "of=" + ovmfDir} return exec.Command(cmd[0], cmd[1:]...).Run() } func (v *MachineVM) archRemovalFiles() []string { - ovmDir := getOvmfDir(v.ImagePath, v.Name) + ovmDir := getOvmfDir(v.ImagePath.GetPath(), v.Name) return []string{ovmDir} } diff --git a/pkg/machine/wsl/machine.go b/pkg/machine/wsl/machine.go index 5128fa313..fdda45ca6 100644 --- a/pkg/machine/wsl/machine.go +++ b/pkg/machine/wsl/machine.go @@ -145,6 +145,8 @@ http://docs.microsoft.com/en-us/windows/wsl/install\ const ( winSShProxy = "win-sshproxy.exe" winSshProxyTid = "win-sshproxy.tid" + pipePrefix = "npipe:////./pipe/" + globalPipe = "docker_engine" ) type Provider struct{} @@ -801,16 +803,15 @@ func (v *MachineVM) Start(name string, _ machine.StartOptions) error { } func launchWinProxy(v *MachineVM) (bool, string, error) { - globalName := true - pipeName := "docker_engine" - if !pipeAvailable(pipeName) { - pipeName = toDist(v.Name) - globalName = false - if !pipeAvailable(pipeName) { - return globalName, "", errors.Errorf("could not start api proxy since expected pipe is not available: %s", pipeName) - } + machinePipe := toDist(v.Name) + if !pipeAvailable(machinePipe) { + return false, "", errors.Errorf("could not start api proxy since expected pipe is not available: %s", machinePipe) + } + + globalName := false + if pipeAvailable(globalPipe) { + globalName = true } - fullPipeName := "npipe:////./pipe/" + pipeName exe, err := os.Executable() if err != nil { @@ -829,12 +830,19 @@ func launchWinProxy(v *MachineVM) (bool, string, error) { } dest := fmt.Sprintf("ssh://root@localhost:%d/run/podman/podman.sock", v.Port) - cmd := exec.Command(command, v.Name, stateDir, fullPipeName, dest, v.IdentityPath) + args := []string{v.Name, stateDir, pipePrefix + machinePipe, dest, v.IdentityPath} + waitPipe := machinePipe + if globalName { + args = append(args, pipePrefix+globalPipe, dest, v.IdentityPath) + waitPipe = globalPipe + } + + cmd := exec.Command(command, args...) if err := cmd.Start(); err != nil { return globalName, "", err } - return globalName, fullPipeName, waitPipeExists(pipeName, 30, func() error { + return globalName, pipePrefix + waitPipe, waitPipeExists(waitPipe, 30, func() error { active, exitCode := getProcessState(cmd.Process.Pid) if !active { return errors.Errorf("win-sshproxy.exe failed to start, exit code: %d (see windows event logs)", exitCode) diff --git a/pkg/specgen/container_validate.go b/pkg/specgen/container_validate.go index e71eafb75..42b70e334 100644 --- a/pkg/specgen/container_validate.go +++ b/pkg/specgen/container_validate.go @@ -83,10 +83,6 @@ func (s *SpecGenerator) Validate() error { // // ContainerSecurityConfig // - // capadd and privileged are exclusive - if len(s.CapAdd) > 0 && s.Privileged { - return exclusiveOptions("CapAdd", "privileged") - } // userns and idmappings conflict if s.UserNS.IsPrivate() && s.IDMappings == nil { return errors.Wrap(ErrInvalidSpecConfig, "IDMappings are required when not creating a User namespace") diff --git a/pkg/specgen/generate/container.go b/pkg/specgen/generate/container.go index 118d80e2c..b38b0e695 100644 --- a/pkg/specgen/generate/container.go +++ b/pkg/specgen/generate/container.go @@ -337,11 +337,11 @@ func FinishThrottleDevices(s *specgen.SpecGenerator) error { return nil } -// ConfigToSpec takes a completed container config and converts it back into a specgenerator for purposes of cloning an existing container -func ConfigToSpec(rt *libpod.Runtime, specg *specgen.SpecGenerator, containerID string) (*libpod.Container, error) { - c, err := rt.LookupContainer(containerID) +// ConfigToSpec takes a completed container config and converts it back into a specgenerator for purposes of cloning an exisiting container +func ConfigToSpec(rt *libpod.Runtime, specg *specgen.SpecGenerator, contaierID string) (*libpod.Container, *libpod.InfraInherit, error) { + c, err := rt.LookupContainer(contaierID) if err != nil { - return nil, err + return nil, nil, err } conf := c.Config() @@ -351,17 +351,22 @@ func ConfigToSpec(rt *libpod.Runtime, specg *specgen.SpecGenerator, containerID conf.Systemd = nil conf.Mounts = []string{} + if specg == nil { + specg = &specgen.SpecGenerator{} + } + specg.Pod = conf.Pod matching, err := json.Marshal(conf) if err != nil { - return nil, err + return nil, nil, err } err = json.Unmarshal(matching, specg) if err != nil { - return nil, err + return nil, nil, err } + conf.Systemd = tmpSystemd conf.Mounts = tmpMounts @@ -481,7 +486,29 @@ func ConfigToSpec(rt *libpod.Runtime, specg *specgen.SpecGenerator, containerID } } specg.OverlayVolumes = overlay - specg.Mounts = conf.Spec.Mounts + _, mounts := c.SortUserVolumes(c.Spec()) + specg.Mounts = mounts specg.HostDeviceList = conf.DeviceHostSrc - return c, nil + mapSecurityConfig(conf, specg) + + if c.IsInfra() { // if we are creating this spec for a pod's infra ctr, map the compatible options + spec, err := json.Marshal(specg) + if err != nil { + return nil, nil, err + } + infraInherit := &libpod.InfraInherit{} + err = json.Unmarshal(spec, infraInherit) + return c, infraInherit, err + } + // else just return the container + return c, nil, nil +} + +// mapSecurityConfig takes a libpod.ContainerSecurityConfig and converts it to a specgen.ContinerSecurityConfig +func mapSecurityConfig(c *libpod.ContainerConfig, s *specgen.SpecGenerator) { + s.Privileged = c.Privileged + s.SelinuxOpts = append(s.SelinuxOpts, c.LabelOpts...) + s.User = c.User + s.Groups = c.Groups + s.HostUsers = c.HostUsers } diff --git a/pkg/specgen/generate/container_create.go b/pkg/specgen/generate/container_create.go index a014f5047..6a611e854 100644 --- a/pkg/specgen/generate/container_create.go +++ b/pkg/specgen/generate/container_create.go @@ -49,7 +49,7 @@ func MakeContainer(ctx context.Context, rt *libpod.Runtime, s *specgen.SpecGener compatibleOptions := &libpod.InfraInherit{} var infraSpec *spec.Spec if infra != nil { - options, infraSpec, compatibleOptions, err = Inherit(*infra) + options, infraSpec, compatibleOptions, err = Inherit(*infra, s, rt) if err != nil { return nil, nil, nil, err } @@ -152,8 +152,8 @@ func MakeContainer(ctx context.Context, rt *libpod.Runtime, s *specgen.SpecGener return nil, nil, nil, err } - infraVolumes := (len(compatibleOptions.InfraVolumes) > 0 || len(compatibleOptions.InfraUserVolumes) > 0 || len(compatibleOptions.InfraImageVolumes) > 0) - opts, err := createContainerOptions(ctx, rt, s, pod, finalVolumes, finalOverlays, imageData, command, infraVolumes, *compatibleOptions) + infraVol := (len(compatibleOptions.Mounts) > 0 || len(compatibleOptions.Volumes) > 0 || len(compatibleOptions.ImageVolumes) > 0 || len(compatibleOptions.OverlayVolumes) > 0) + opts, err := createContainerOptions(ctx, rt, s, pod, finalVolumes, finalOverlays, imageData, command, infraVol, *compatibleOptions) if err != nil { return nil, nil, nil, err } @@ -446,7 +446,7 @@ func createContainerOptions(ctx context.Context, rt *libpod.Runtime, s *specgen. if len(s.SelinuxOpts) > 0 { options = append(options, libpod.WithSecLabels(s.SelinuxOpts)) } else { - if pod != nil && len(compatibleOptions.InfraLabels) == 0 { + if pod != nil && len(compatibleOptions.SelinuxOpts) == 0 { // duplicate the security options from the pod processLabel, err := pod.ProcessLabel() if err != nil { @@ -544,32 +544,23 @@ func createContainerOptions(ctx context.Context, rt *libpod.Runtime, s *specgen. return options, nil } -func Inherit(infra libpod.Container) (opts []libpod.CtrCreateOption, infraS *spec.Spec, compat *libpod.InfraInherit, err error) { +func Inherit(infra libpod.Container, s *specgen.SpecGenerator, rt *libpod.Runtime) (opts []libpod.CtrCreateOption, infraS *spec.Spec, compat *libpod.InfraInherit, err error) { + inheritSpec := &specgen.SpecGenerator{} + _, compatibleOptions, err := ConfigToSpec(rt, inheritSpec, infra.ID()) + if err != nil { + return nil, nil, nil, err + } options := []libpod.CtrCreateOption{} - compatibleOptions := &libpod.InfraInherit{} infraConf := infra.Config() infraSpec := infraConf.Spec - config, err := json.Marshal(infraConf) + compatByte, err := json.Marshal(compatibleOptions) if err != nil { return nil, nil, nil, err } - err = json.Unmarshal(config, compatibleOptions) + err = json.Unmarshal(compatByte, s) if err != nil { return nil, nil, nil, err } - if infraSpec.Linux != nil && infraSpec.Linux.Resources != nil { - resources, err := json.Marshal(infraSpec.Linux.Resources) - if err != nil { - return nil, nil, nil, err - } - err = json.Unmarshal(resources, &compatibleOptions.InfraResources) - if err != nil { - return nil, nil, nil, err - } - } - if compatibleOptions != nil { - options = append(options, libpod.WithInfraConfig(*compatibleOptions)) - } return options, infraSpec, compatibleOptions, nil } diff --git a/pkg/specgen/generate/oci.go b/pkg/specgen/generate/oci.go index 1cc3a463f..961cea933 100644 --- a/pkg/specgen/generate/oci.go +++ b/pkg/specgen/generate/oci.go @@ -352,8 +352,8 @@ func SpecGenToOCI(ctx context.Context, s *specgen.SpecGenerator, rt *libpod.Runt return nil, err } } - if len(compatibleOptions.InfraDevices) > 0 && len(s.Devices) == 0 { - userDevices = compatibleOptions.InfraDevices + if len(compatibleOptions.HostDeviceList) > 0 && len(s.Devices) == 0 { + userDevices = compatibleOptions.HostDeviceList } else { userDevices = s.Devices } diff --git a/pkg/specgenutil/specgen.go b/pkg/specgenutil/specgen.go index 186d3862b..7d4fca846 100644 --- a/pkg/specgenutil/specgen.go +++ b/pkg/specgenutil/specgen.go @@ -753,8 +753,8 @@ func FillOutSpecGen(s *specgen.SpecGenerator, c *entities.ContainerCreateOptions s.PreserveFDs = c.PreserveFDs } - if s.OOMScoreAdj == nil || c.OOMScoreAdj != 0 { - s.OOMScoreAdj = &c.OOMScoreAdj + if s.OOMScoreAdj == nil || c.OOMScoreAdj != nil { + s.OOMScoreAdj = c.OOMScoreAdj } if c.Restart != "" { splitRestart := strings.Split(c.Restart, ":") diff --git a/pkg/specgenutil/volumes.go b/pkg/specgenutil/volumes.go index 2bd79b186..8a861077a 100644 --- a/pkg/specgenutil/volumes.go +++ b/pkg/specgenutil/volumes.go @@ -28,7 +28,7 @@ var ( // TODO: handle options parsing/processing via containers/storage/pkg/mount func parseVolumes(volumeFlag, mountFlag, tmpfsFlag []string, addReadOnlyTmpfs bool) ([]spec.Mount, []*specgen.NamedVolume, []*specgen.OverlayVolume, []*specgen.ImageVolume, error) { // Get mounts from the --mounts flag. - unifiedMounts, unifiedVolumes, unifiedImageVolumes, err := getMounts(mountFlag) + unifiedMounts, unifiedVolumes, unifiedImageVolumes, err := Mounts(mountFlag) if err != nil { return nil, nil, nil, nil, err } @@ -167,12 +167,12 @@ func findMountType(input string) (mountType string, tokens []string, err error) return } -// getMounts takes user-provided input from the --mount flag and creates OCI +// Mounts takes user-provided input from the --mount flag and creates OCI // spec mounts and Libpod named volumes. // podman run --mount type=bind,src=/etc/resolv.conf,target=/etc/resolv.conf ... // podman run --mount type=tmpfs,target=/dev/shm ... // podman run --mount type=volume,source=test-volume, ... -func getMounts(mountFlag []string) (map[string]spec.Mount, map[string]*specgen.NamedVolume, map[string]*specgen.ImageVolume, error) { +func Mounts(mountFlag []string) (map[string]spec.Mount, map[string]*specgen.NamedVolume, map[string]*specgen.ImageVolume, error) { finalMounts := make(map[string]spec.Mount) finalNamedVolumes := make(map[string]*specgen.NamedVolume) finalImageVolumes := make(map[string]*specgen.ImageVolume) @@ -518,7 +518,7 @@ func getDevptsMount(args []string) (spec.Mount, error) { func getNamedVolume(args []string) (*specgen.NamedVolume, error) { newVolume := new(specgen.NamedVolume) - var setSource, setDest, setRORW, setSuid, setDev, setExec, setOwnership bool + var setDest, setRORW, setSuid, setDev, setExec, setOwnership bool for _, val := range args { kv := strings.SplitN(val, "=", 2) @@ -554,7 +554,6 @@ func getNamedVolume(args []string) (*specgen.NamedVolume, error) { return nil, errors.Wrapf(optionArgError, kv[0]) } newVolume.Name = kv[1] - setSource = true case "target", "dst", "destination": if len(kv) == 1 { return nil, errors.Wrapf(optionArgError, kv[0]) @@ -585,9 +584,6 @@ func getNamedVolume(args []string) (*specgen.NamedVolume, error) { } } - if !setSource { - return nil, errors.Errorf("must set source volume") - } if !setDest { return nil, noDestError } |