diff options
author | OpenShift Merge Robot <openshift-merge-robot@users.noreply.github.com> | 2021-09-30 08:35:07 -0400 |
---|---|---|
committer | GitHub <noreply@github.com> | 2021-09-30 08:35:07 -0400 |
commit | d8bdbf5b66c860a73f5d4e301535c0ee40d8d719 (patch) | |
tree | ee5e4c6705632064406f49147d2e5fb51947d300 | |
parent | f0ae84f5120f7ec8d2fc16c6ae9e52725b5d4958 (diff) | |
parent | 855746cc9258b85d390d68cd3c61ca0588dd0f8f (diff) | |
download | podman-d8bdbf5b66c860a73f5d4e301535c0ee40d8d719.tar.gz podman-d8bdbf5b66c860a73f5d4e301535c0ee40d8d719.tar.bz2 podman-d8bdbf5b66c860a73f5d4e301535c0ee40d8d719.zip |
Merge pull request #11792 from mheon/340_final
Backports + release notes for v3.4.0 final
46 files changed, 464 insertions, 148 deletions
diff --git a/RELEASE_NOTES.md b/RELEASE_NOTES.md index a5cfd9e2e..326991827 100644 --- a/RELEASE_NOTES.md +++ b/RELEASE_NOTES.md @@ -18,10 +18,12 @@ - Volumes created by `podman volume create` now support setting quotas when run atop XFS. The `size` and `inode` options allow the maximum size and maximum number of inodes consumed by a volume to be limited. - The `podman info` command now outputs information on what log drivers, network drivers, and volume plugins are available for use ([#11265](https://github.com/containers/podman/issues/11265)). - The `podman info` command now outputs the current log driver in use, and the variant and codename of the distribution in use. +- The parameters of the VM created by `podman machine init` (amount of disk space, memory, CPUs) can now be set in `containers.conf`. +- The `podman machine ls` command now shows additional information (CPUs, memory, disk size) about VMs managed by `podman machine`. +- The `podman ps` command now includes healthcheck status in container state for containers that have healthchecks ([#11527](https://github.com/containers/podman/issues/11527)). ### Changes - The `podman build` command has a new alias, `podman buildx`, to improve compatibility with Docker. We have already added support for many `docker buildx` flags to `podman build` and aim to continue to do so. -- Podman commands run as root now ignore `XDG_RUNTIME_DIR` when determining where to place temporary files, which should resolve a number of issues including [#10745](https://github.com/containers/podman/issues/10745) and [#10806](https://github.com/containers/podman/issues/10806). - Cases where Podman is run without a user session or a writable temporary files directory will now produce better error messages. - The default log driver has been changed from `file` to `journald`. The `file` driver did not properly support log rotation, so this should lead to a better experience. If journald is not available on the system, Podman will automatically revert to the `file`. - Podman no longer depends on `ip` for removing networks ([#11403](https://github.com/containers/podman/issues/11403)). @@ -51,6 +53,7 @@ - Fixed a bug where the `podman logs -f` command could hang when a container exited ([#11461](https://github.com/containers/podman/issues/11461)). - Fixed a bug where the `podman generate systemd` command could not be used on containers that specified a restart policy ([#11438](https://github.com/containers/podman/issues/11438)). - Fixed a bug where the remote Podman client's `podman build` command would fail to build containers if the UID and GID on the client were higher than 65536 ([#11474](https://github.com/containers/podman/issues/11474)). +- Fixed a bug where the remote Podman client's `podman build` command would fail to build containers if the context directory was a symlink ([#11732](https://github.com/containers/podman/issues/11732)). - Fixed a bug where the `--network` flag to `podman play kube` was not properly parsed when a non-bridge network configuration was specified. - Fixed a bug where the `podman inspect` command could error when the container being inspected was removed as it was being inspected ([#11392](https://github.com/containers/podman/issues/11392)). - Fixed a bug where the `podman play kube` command ignored the default pod infra image specified in `containers.conf`. @@ -68,6 +71,11 @@ - Fixed a bug where the `--filter` option to `podman ps` did not properly handle filtering by healthcheck status ([#11687](https://github.com/containers/podman/issues/11687)). - Fixed a bug where the `podman run` and `podman start --attach` commands could race when retrieving the exit code of a container that had already been removed resulting in an error (e.g. by an external `podman rm -f`) ([#11633](https://github.com/containers/podman/issues/11633)). - Fixed a bug where the `podman generate kube` command would add default environment variables to generated YAML. +- Fixed a bug where the `podman generate kube` command would add the default CMD from the image to generated YAML ([#11672](https://github.com/containers/podman/issues/11672)). +- Fixed a bug where the `podman rm --storage` command could fail to remove containers under some circumstances ([#11207](https://github.com/containers/podman/issues/11207)). +- Fixed a bug where the `podman machine ssh` command could fail when run on Linux ([#11731](https://github.com/containers/podman/issues/11731)). +- Fixed a bug where the `podman stop` command would error when used on a container that was already stopped ([#11740](https://github.com/containers/podman/issues/11740)). +- Fixed a bug where renaming a container in a pod using the `podman rename` command, then removing the pod using `podman pod rm`, could cause Podman to believe the new name of the container was permanently in use, despite the container being removed ([#11750](https://github.com/containers/podman/issues/11750)). ### API - The Libpod Pull endpoint for Images now has a new query parameter, `quiet`, which (when set to true) suppresses image pull progress reports ([#10612](https://github.com/containers/podman/issues/10612)). diff --git a/cmd/podman/common/completion.go b/cmd/podman/common/completion.go index e925fb4f1..04fb71c33 100644 --- a/cmd/podman/common/completion.go +++ b/cmd/podman/common/completion.go @@ -193,21 +193,14 @@ func getImages(cmd *cobra.Command, toComplete string) ([]string, cobra.ShellComp } else { // suggested "registry.fedoraproject.org/f29/httpd:latest" as // - "registry.fedoraproject.org/f29/httpd:latest" - // - "registry.fedoraproject.org/f29/httpd" // - "f29/httpd:latest" - // - "f29/httpd" // - "httpd:latest" - // - "httpd" paths := strings.Split(repo, "/") for i := range paths { suggestionWithTag := strings.Join(paths[i:], "/") if strings.HasPrefix(suggestionWithTag, toComplete) { suggestions = append(suggestions, suggestionWithTag) } - suggestionWithoutTag := strings.SplitN(strings.SplitN(suggestionWithTag, ":", 2)[0], "@", 2)[0] - if strings.HasPrefix(suggestionWithoutTag, toComplete) { - suggestions = append(suggestions, suggestionWithoutTag) - } } } } diff --git a/cmd/podman/containers/ps.go b/cmd/podman/containers/ps.go index 2566c57de..920ad0d4a 100644 --- a/cmd/podman/containers/ps.go +++ b/cmd/podman/containers/ps.go @@ -375,6 +375,10 @@ func (l psReporter) State() string { // Status is a synonym for State() func (l psReporter) Status() string { + hc := l.ListContainer.Status + if hc != "" { + return l.State() + " (" + hc + ")" + } return l.State() } diff --git a/cmd/podman/generate/generate.go b/cmd/podman/generate/generate.go index 6b48a342e..a42aa9f21 100644 --- a/cmd/podman/generate/generate.go +++ b/cmd/podman/generate/generate.go @@ -11,7 +11,7 @@ var ( // Command: podman _generate_ generateCmd = &cobra.Command{ Use: "generate", - Short: "Generate structured data based on containers, pods or volumes.", + Short: "Generate structured data based on containers, pods or volumes", Long: "Generate structured data (e.g., Kubernetes YAML or systemd units) based on containers, pods or volumes.", RunE: validate.SubCommandExists, } diff --git a/cmd/podman/images/prune.go b/cmd/podman/images/prune.go index 8a484495a..7e6a29d94 100644 --- a/cmd/podman/images/prune.go +++ b/cmd/podman/images/prune.go @@ -80,7 +80,7 @@ func prune(cmd *cobra.Command, args []string) error { func createPruneWarningMessage(pruneOpts entities.ImagePruneOptions) string { question := "Are you sure you want to continue? [y/N] " if pruneOpts.All { - return "WARNING! This will remove all images without at least one container associated to them.\n" + question + return "WARNING! This command removes all images without at least one container associated with them.\n" + question } - return "WARNING! This will remove all dangling images.\n" + question + return "WARNING! This command removes all dangling images.\n" + question } diff --git a/cmd/podman/machine/list.go b/cmd/podman/machine/list.go index d4360bb9b..fe9d712e3 100644 --- a/cmd/podman/machine/list.go +++ b/cmd/podman/machine/list.go @@ -40,10 +40,13 @@ type listFlagType struct { } type machineReporter struct { - Name string - Created string - LastUp string - VMType string + Name string + Created string + LastUp string + VMType string + CPUs uint64 + Memory string + DiskSize string } func init() { @@ -54,7 +57,7 @@ func init() { flags := lsCmd.Flags() formatFlagName := "format" - flags.StringVar(&listFlag.format, formatFlagName, "{{.Name}}\t{{.VMType}}\t{{.Created}}\t{{.LastUp}}\n", "Format volume output using Go template") + flags.StringVar(&listFlag.format, formatFlagName, "{{.Name}}\t{{.VMType}}\t{{.Created}}\t{{.LastUp}}\t{{.CPUs}}\t{{.Memory}}\t{{.DiskSize}}\n", "Format volume output using Go template") _ = lsCmd.RegisterFlagCompletionFunc(formatFlagName, completion.AutocompleteNone) flags.BoolVar(&listFlag.noHeading, "noheading", false, "Do not print headers") } @@ -85,8 +88,11 @@ func list(cmd *cobra.Command, args []string) error { func outputTemplate(cmd *cobra.Command, responses []*machineReporter) error { headers := report.Headers(machineReporter{}, map[string]string{ - "LastUp": "LAST UP", - "VmType": "VM TYPE", + "LastUp": "LAST UP", + "VmType": "VM TYPE", + "CPUs": "CPUS", + "Memory": "MEMORY", + "DiskSize": "DISK SIZE", }) row := report.NormalizeFormat(listFlag.format) @@ -136,6 +142,9 @@ func toHumanFormat(vms []*machine.ListResponse) ([]*machineReporter, error) { } response.Created = units.HumanDuration(time.Since(vm.CreatedAt)) + " ago" response.VMType = vm.VMType + response.CPUs = vm.CPUs + response.Memory = units.HumanSize(float64(vm.Memory) * units.MiB) + response.DiskSize = units.HumanSize(float64(vm.DiskSize) * units.GiB) humanResponses = append(humanResponses, response) } diff --git a/cmd/podman/play/play.go b/cmd/podman/play/play.go index f121d6a2d..d676bd701 100644 --- a/cmd/podman/play/play.go +++ b/cmd/podman/play/play.go @@ -10,7 +10,7 @@ var ( // Command: podman _play_ playCmd = &cobra.Command{ Use: "play", - Short: "Play containers, pods or volumes from a structured file.", + Short: "Play containers, pods or volumes from a structured file", Long: "Play structured data (e.g., Kubernetes YAML) based on containers, pods or volumes.", RunE: validate.SubCommandExists, } diff --git a/cmd/podman/registry/config.go b/cmd/podman/registry/config.go index 50e488b02..b512ba341 100644 --- a/cmd/podman/registry/config.go +++ b/cmd/podman/registry/config.go @@ -89,12 +89,7 @@ func newPodmanConfig() { // use for the containers.conf configuration file. func setXdgDirs() error { if !rootless.IsRootless() { - // unset XDG_RUNTIME_DIR for root - // Sometimes XDG_RUNTIME_DIR is set to /run/user/0 sometimes it is unset, - // the inconsistency is causing issues for the dnsname plugin. - // It is already set to an empty string for conmon so lets do the same - // for podman. see #10806 and #10745 - return os.Unsetenv("XDG_RUNTIME_DIR") + return nil } // Setup XDG_RUNTIME_DIR diff --git a/cmd/podman/root.go b/cmd/podman/root.go index c798e6634..2ab011631 100644 --- a/cmd/podman/root.go +++ b/cmd/podman/root.go @@ -92,6 +92,11 @@ func Execute() { if registry.GetExitCode() == 0 { registry.SetExitCode(define.ExecErrorCodeGeneric) } + if registry.IsRemote() { + if strings.Contains(err.Error(), "unable to connect to Podman") { + fmt.Fprintln(os.Stderr, "Cannot connect to Podman. Please verify your connection to the Linux system using `podman system connection list`, or try `podman machine init` and `podman machine start` to manage a new Linux VM") + } + } fmt.Fprintln(os.Stderr, formatError(err)) } os.Exit(registry.GetExitCode()) diff --git a/cmd/podman/system/prune.go b/cmd/podman/system/prune.go index e09e2d5e5..5565ea2f9 100644 --- a/cmd/podman/system/prune.go +++ b/cmd/podman/system/prune.go @@ -113,15 +113,15 @@ func prune(cmd *cobra.Command, args []string) error { func createPruneWarningMessage(pruneOpts entities.SystemPruneOptions) string { if pruneOpts.All { - return `WARNING! This will remove: + return `WARNING! This command removes: - all stopped containers - all networks not used by at least one container%s - - all images without at least one container associated to them + - all images without at least one container associated with them - all build cache %s` } - return `WARNING! This will remove: + return `WARNING! This command removes: - all stopped containers - all networks not used by at least one container%s - all dangling images diff --git a/docs/source/markdown/podman-build.1.md b/docs/source/markdown/podman-build.1.md index 3278436bd..0f52ad592 100644 --- a/docs/source/markdown/podman-build.1.md +++ b/docs/source/markdown/podman-build.1.md @@ -64,8 +64,10 @@ discarded when writing images in Docker formats. #### **--arch**=*arch* -Set the ARCH of the image to the provided value instead of the architecture of -the host. +Set the architecture of the image to be built, and that of the base image to be +pulled, if the build uses one, to the provided value instead of using the +architecture of the build host. (Examples: arm, arm64, 386, amd64, ppc64le, +s390x) #### **--authfile**=*path* @@ -321,7 +323,8 @@ Pass through HTTP Proxy environment variables. #### **--iidfile**=*ImageIDfile* -Write the image ID to the file. +Write the built image's ID to the file. When `--platform` is specified more +than once, attempting to use this option will trigger an error. #### **--ignorefile** @@ -389,6 +392,7 @@ Name of the manifest list to which the image will be added. Creates the manifest if it does not exist. This option is useful for building multi architecture images. #### **--memory**, **-m**=*LIMIT* + Memory limit (format: `<number>[<unit>]`, where unit = b (bytes), k (kilobytes), m (megabytes), or g (gigabytes)) @@ -430,8 +434,9 @@ with a new set of cached layers. #### **--os**=*string* -Set the OS to the provided value instead of the current operating system of the -host. +Set the OS of the image to be built, and that of the base image to be pulled, +if the build uses one, instead of using the current operating system of the +build host. #### **--pid**=*pid* @@ -442,11 +447,28 @@ that the PID namespace in which `podman` itself is being run should be reused, or it can be the path to a PID namespace which is already in use by another process. -#### **--platform**="Linux" +#### **--platform**="OS/ARCH[/VARIANT][,...]" + +Set the OS/ARCH of the built image (and its base image, if your build uses one) +to the provided value instead of using the current operating system and +architecture of the host (for example `linux/arm`). If `--platform` is set, +then the values of the `--arch`, `--os`, and `--variant` options will be +overridden. + +The `--platform` flag can be specified more than once, or given a +comma-separated list of values as its argument. When more than one platform is +specified, the `--manifest` option should be used instead of the `--tag` +option. + +OS/ARCH pairs are those used by the Go Programming Language. In several cases +the ARCH value for a platform differs from one produced by other tools such as +the `arch` command. Valid OS and architecture name combinations are listed as +values for $GOOS and $GOARCH at https://golang.org/doc/install/source#environment, +and can also be found by running `go tool dist list`. -This option has no effect on the build. Other container engines use this option -to control the execution platform for the build (e.g., Windows, Linux) which is -not required for Buildah as it supports only Linux. +While `podman build` is happy to use base images and build images for any +platform that exists, `RUN` instructions will not be able to succeed without +the help of emulation provided by packages like `qemu-user-static`. #### **--pull** @@ -486,7 +508,6 @@ commands specified by the **RUN** instruction. Note: You can also override the default runtime by setting the BUILDAH\_RUNTIME environment variable. `export BUILDAH_RUNTIME=/usr/local/bin/runc` - #### **--secret**=**id=id,src=path** Pass secret information to be used in the Containerfile for building images @@ -497,7 +518,6 @@ To later use the secret, use the --mount flag in a `RUN` instruction within a `C `RUN --mount=type=secret,id=mysecret cat /run/secrets/mysecret` - #### **--security-opt**=*option* Security Options @@ -697,7 +717,9 @@ process. #### **--variant**="" -Set the architecture variant of the image to be pulled. +Set the architecture variant of the image to be built, and that of the base +image to be pulled, if the build uses one, to the provided value instead of +using the architecture variant of the build host. #### **--volume**, **-v**[=*[HOST-DIR:CONTAINER-DIR[:OPTIONS]]*] @@ -858,7 +880,7 @@ $ podman build --layers --force-rm -t imageName . $ podman build --no-cache --rm=false -t imageName . ``` -### Building an multi-architecture image using a --manifest option (Requires emulation software) +### Building a multi-architecture image using the --manifest option (requires emulation software) ``` $ podman build --arch arm --manifest myimage /tmp/mysrc @@ -866,6 +888,10 @@ $ podman build --arch arm --manifest myimage /tmp/mysrc $ podman build --arch amd64 --manifest myimage /tmp/mysrc $ podman build --arch s390x --manifest myimage /tmp/mysrc + +$ podman build --platform linux/s390x,linux/ppc64le,linux/amd64 --manifest myimage /tmp/mysrc + +$ podman build --platform linux/arm64 --platform linux/amd64 --manifest myimage /tmp/mysrc ``` ### Building an image using a URL, Git repo, or archive diff --git a/libpod/boltdb_state.go b/libpod/boltdb_state.go index 5df3e8961..160f428d7 100644 --- a/libpod/boltdb_state.go +++ b/libpod/boltdb_state.go @@ -1756,6 +1756,23 @@ func (s *BoltState) SafeRewriteContainerConfig(ctr *Container, oldName, newName if err := allCtrsBkt.Put([]byte(ctr.ID()), []byte(newName)); err != nil { return errors.Wrapf(err, "error renaming container %s in all containers bucket in DB", ctr.ID()) } + if ctr.config.Pod != "" { + podsBkt, err := getPodBucket(tx) + if err != nil { + return err + } + podBkt := podsBkt.Bucket([]byte(ctr.config.Pod)) + if podBkt == nil { + return errors.Wrapf(define.ErrInternal, "bucket for pod %s does not exist", ctr.config.Pod) + } + podCtrBkt := podBkt.Bucket(containersBkt) + if podCtrBkt == nil { + return errors.Wrapf(define.ErrInternal, "pod %s does not have a containers bucket", ctr.config.Pod) + } + if err := podCtrBkt.Put([]byte(ctr.ID()), []byte(newName)); err != nil { + return errors.Wrapf(err, "error renaming container %s in pod %s members bucket", ctr.ID(), ctr.config.Pod) + } + } } } diff --git a/libpod/container_copy_linux.go b/libpod/container_copy_linux.go index a35824289..7d4dd0d46 100644 --- a/libpod/container_copy_linux.go +++ b/libpod/container_copy_linux.go @@ -174,7 +174,7 @@ func (c *Container) copyToArchive(ctx context.Context, path string, writer io.Wr // getContainerUser returns the specs.User and ID mappings of the container. func getContainerUser(container *Container, mountPoint string) (specs.User, error) { - userspec := container.Config().User + userspec := container.config.User uid, gid, _, err := chrootuser.GetUser(mountPoint, userspec) u := specs.User{ diff --git a/libpod/container_internal.go b/libpod/container_internal.go index 18b80475b..2ca49758d 100644 --- a/libpod/container_internal.go +++ b/libpod/container_internal.go @@ -982,12 +982,11 @@ func (c *Container) checkDependenciesRunning() ([]string, error) { } // Check the status - conf := depCtr.Config() state, err := depCtr.State() if err != nil { return nil, errors.Wrapf(err, "error retrieving state of dependency %s of container %s", dep, c.ID()) } - if state != define.ContainerStateRunning && !conf.IsInfra { + if state != define.ContainerStateRunning && !depCtr.config.IsInfra { notRunning = append(notRunning, dep) } depCtrs[dep] = depCtr @@ -1063,7 +1062,7 @@ func (c *Container) cniHosts() string { var hosts string if len(c.state.NetworkStatus) > 0 && len(c.state.NetworkStatus[0].IPs) > 0 { ipAddress := strings.Split(c.state.NetworkStatus[0].IPs[0].Address.String(), "/")[0] - hosts += fmt.Sprintf("%s\t%s %s\n", ipAddress, c.Hostname(), c.Config().Name) + hosts += fmt.Sprintf("%s\t%s %s\n", ipAddress, c.Hostname(), c.config.Name) } return hosts } @@ -2127,7 +2126,7 @@ func (c *Container) canWithPrevious() error { // JSON files for later export func (c *Container) prepareCheckpointExport() error { // save live config - if _, err := metadata.WriteJSONFile(c.Config(), c.bundlePath(), metadata.ConfigDumpFile); err != nil { + if _, err := metadata.WriteJSONFile(c.config, c.bundlePath(), metadata.ConfigDumpFile); err != nil { return err } diff --git a/libpod/container_path_resolution.go b/libpod/container_path_resolution.go index ec7306ca1..bb2ef1a73 100644 --- a/libpod/container_path_resolution.go +++ b/libpod/container_path_resolution.go @@ -112,7 +112,7 @@ func (c *Container) resolvePath(mountPoint string, containerPath string) (string func findVolume(c *Container, containerPath string) (*Volume, error) { runtime := c.Runtime() cleanedContainerPath := filepath.Clean(containerPath) - for _, vol := range c.Config().NamedVolumes { + for _, vol := range c.config.NamedVolumes { if cleanedContainerPath == filepath.Clean(vol.Dest) { return runtime.GetVolume(vol.Name) } @@ -124,7 +124,7 @@ func findVolume(c *Container, containerPath string) (*Volume, error) { // Volume's destination. func isPathOnVolume(c *Container, containerPath string) bool { cleanedContainerPath := filepath.Clean(containerPath) - for _, vol := range c.Config().NamedVolumes { + for _, vol := range c.config.NamedVolumes { if cleanedContainerPath == filepath.Clean(vol.Dest) { return true } @@ -141,7 +141,7 @@ func isPathOnVolume(c *Container, containerPath string) bool { // path of a Mount. Returns a matching Mount or nil. func findBindMount(c *Container, containerPath string) *specs.Mount { cleanedPath := filepath.Clean(containerPath) - for _, m := range c.Config().Spec.Mounts { + for _, m := range c.config.Spec.Mounts { if m.Type != "bind" { continue } @@ -157,7 +157,7 @@ func findBindMount(c *Container, containerPath string) *specs.Mount { // Mount's destination. func isPathOnBindMount(c *Container, containerPath string) bool { cleanedContainerPath := filepath.Clean(containerPath) - for _, m := range c.Config().Spec.Mounts { + for _, m := range c.config.Spec.Mounts { if cleanedContainerPath == filepath.Clean(m.Destination) { return true } diff --git a/libpod/kube.go b/libpod/kube.go index af3b0916e..25f672c28 100644 --- a/libpod/kube.go +++ b/libpod/kube.go @@ -1,9 +1,11 @@ package libpod import ( + "context" "fmt" "math/rand" "os" + "reflect" "sort" "strconv" "strings" @@ -27,14 +29,14 @@ import ( // GenerateForKube takes a slice of libpod containers and generates // one v1.Pod description that includes just a single container. -func GenerateForKube(ctrs []*Container) (*v1.Pod, error) { +func GenerateForKube(ctx context.Context, ctrs []*Container) (*v1.Pod, error) { // Generate the v1.Pod yaml description - return simplePodWithV1Containers(ctrs) + return simplePodWithV1Containers(ctx, ctrs) } // GenerateForKube takes a slice of libpod containers and generates // one v1.Pod description -func (p *Pod) GenerateForKube() (*v1.Pod, []v1.ServicePort, error) { +func (p *Pod) GenerateForKube(ctx context.Context) (*v1.Pod, []v1.ServicePort, error) { // Generate the v1.Pod yaml description var ( ports []v1.ContainerPort //nolint @@ -78,7 +80,7 @@ func (p *Pod) GenerateForKube() (*v1.Pod, []v1.ServicePort, error) { servicePorts = containerPortsToServicePorts(ports) hostNetwork = infraContainer.NetworkMode() == string(namespaces.NetworkMode(specgen.Host)) } - pod, err := p.podWithContainers(allContainers, ports, hostNetwork) + pod, err := p.podWithContainers(ctx, allContainers, ports, hostNetwork) if err != nil { return nil, servicePorts, err } @@ -88,7 +90,7 @@ func (p *Pod) GenerateForKube() (*v1.Pod, []v1.ServicePort, error) { // so set it at here for _, ctr := range allContainers { if !ctr.IsInfra() { - switch ctr.Config().RestartPolicy { + switch ctr.config.RestartPolicy { case define.RestartPolicyAlways: pod.Spec.RestartPolicy = v1.RestartPolicyAlways case define.RestartPolicyOnFailure: @@ -218,7 +220,7 @@ func containersToServicePorts(containers []v1.Container) []v1.ServicePort { return sps } -func (p *Pod) podWithContainers(containers []*Container, ports []v1.ContainerPort, hostNetwork bool) (*v1.Pod, error) { +func (p *Pod) podWithContainers(ctx context.Context, containers []*Container, ports []v1.ContainerPort, hostNetwork bool) (*v1.Pod, error) { deDupPodVolumes := make(map[string]*v1.Volume) first := true podContainers := make([]v1.Container, 0, len(containers)) @@ -239,7 +241,7 @@ func (p *Pod) podWithContainers(containers []*Container, ports []v1.ContainerPor isInit := ctr.IsInitCtr() - ctr, volumes, _, err := containerToV1Container(ctr) + ctr, volumes, _, err := containerToV1Container(ctx, ctr) if err != nil { return nil, err } @@ -251,7 +253,9 @@ func (p *Pod) podWithContainers(containers []*Container, ports []v1.ContainerPor // We add the original port declarations from the libpod infra container // to the first kubernetes container description because otherwise we loose // the original container/port bindings. - if first && len(ports) > 0 { + // Add the port configuration to the first regular container or the first + // init container if only init containers have been created in the pod. + if first && len(ports) > 0 && (!isInit || len(containers) == 2) { ctr.Ports = ports first = false } @@ -267,7 +271,7 @@ func (p *Pod) podWithContainers(containers []*Container, ports []v1.ContainerPor deDupPodVolumes[vol.Name] = &vol } } else { - _, _, infraDNS, err := containerToV1Container(ctr) + _, _, infraDNS, err := containerToV1Container(ctx, ctr) if err != nil { return nil, err } @@ -337,7 +341,7 @@ func newPodObject(podName string, annotations map[string]string, initCtrs, conta // simplePodWithV1Containers is a function used by inspect when kube yaml needs to be generated // for a single container. we "insert" that container description in a pod. -func simplePodWithV1Containers(ctrs []*Container) (*v1.Pod, error) { +func simplePodWithV1Containers(ctx context.Context, ctrs []*Container) (*v1.Pod, error) { kubeCtrs := make([]v1.Container, 0, len(ctrs)) kubeInitCtrs := []v1.Container{} kubeVolumes := make([]v1.Volume, 0) @@ -355,7 +359,7 @@ func simplePodWithV1Containers(ctrs []*Container) (*v1.Pod, error) { if !ctr.HostNetwork() { hostNetwork = false } - kubeCtr, kubeVols, ctrDNS, err := containerToV1Container(ctr) + kubeCtr, kubeVols, ctrDNS, err := containerToV1Container(ctx, ctr) if err != nil { return nil, err } @@ -411,7 +415,7 @@ func simplePodWithV1Containers(ctrs []*Container) (*v1.Pod, error) { // containerToV1Container converts information we know about a libpod container // to a V1.Container specification. -func containerToV1Container(c *Container) (v1.Container, []v1.Volume, *v1.PodDNSConfig, error) { +func containerToV1Container(ctx context.Context, c *Container) (v1.Container, []v1.Volume, *v1.PodDNSConfig, error) { kubeContainer := v1.Container{} kubeVolumes := []v1.Volume{} kubeSec, err := generateKubeSecurityContext(c) @@ -463,6 +467,17 @@ func containerToV1Container(c *Container) (v1.Container, []v1.Volume, *v1.PodDNS _, image := c.Image() kubeContainer.Image = image kubeContainer.Stdin = c.Stdin() + img, _, err := c.runtime.libimageRuntime.LookupImage(image, nil) + if err != nil { + return kubeContainer, kubeVolumes, nil, err + } + imgData, err := img.Inspect(ctx, false) + if err != nil { + return kubeContainer, kubeVolumes, nil, err + } + if reflect.DeepEqual(imgData.Config.Cmd, kubeContainer.Command) { + kubeContainer.Command = nil + } kubeContainer.WorkingDir = c.WorkingDir() kubeContainer.Ports = ports diff --git a/libpod/network/cni/cni_exec.go b/libpod/network/cni/cni_exec.go index c4d7f49f7..ae857bcfb 100644 --- a/libpod/network/cni/cni_exec.go +++ b/libpod/network/cni/cni_exec.go @@ -30,6 +30,7 @@ import ( "github.com/containernetworking/cni/pkg/invoke" "github.com/containernetworking/cni/pkg/version" + "github.com/containers/podman/v3/pkg/rootless" ) type cniExec struct { @@ -67,6 +68,17 @@ func (e *cniExec) ExecPlugin(ctx context.Context, pluginPath string, stdinData [ c.Stdout = stdout c.Stderr = stderr + // The dnsname plugin tries to use XDG_RUNTIME_DIR to store files. + // podman run will have XDG_RUNTIME_DIR set and thus the cni plugin can use + // it. The problem is that XDG_RUNTIME_DIR is unset for the conmon process + // for rootful users. This causes issues since the cleanup process is spawned + // by conmon and thus not have XDG_RUNTIME_DIR set to same value as podman run. + // Because of it dnsname will not find the config files and cannot correctly cleanup. + // To fix this we should also unset XDG_RUNTIME_DIR for the cni plugins as rootful. + if !rootless.IsRootless() { + c.Env = append(c.Env, "XDG_RUNTIME_DIR=") + } + err := c.Run() if err != nil { return nil, annotatePluginError(err, pluginPath, stdout.Bytes(), stderr.Bytes()) diff --git a/libpod/networking_slirp4netns.go b/libpod/networking_slirp4netns.go index a09027b72..07c3aae3c 100644 --- a/libpod/networking_slirp4netns.go +++ b/libpod/networking_slirp4netns.go @@ -222,7 +222,7 @@ func (r *Runtime) setupSlirp4netns(ctr *Container) error { defer errorhandling.CloseQuiet(syncR) defer errorhandling.CloseQuiet(syncW) - havePortMapping := len(ctr.Config().PortMappings) > 0 + havePortMapping := len(ctr.config.PortMappings) > 0 logPath := filepath.Join(ctr.runtime.config.Engine.TmpDir, fmt.Sprintf("slirp4netns-%s.log", ctr.config.ID)) ctrNetworkSlipOpts := []string{} diff --git a/libpod/oci_conmon_linux.go b/libpod/oci_conmon_linux.go index 8a823e4fc..c2b472f76 100644 --- a/libpod/oci_conmon_linux.go +++ b/libpod/oci_conmon_linux.go @@ -1148,7 +1148,7 @@ func (r *ConmonOCIRuntime) createOCIContainer(ctr *Container, restoreOptions *Co if ctr.config.NetMode.IsSlirp4netns() || rootless.IsRootless() { if ctr.config.PostConfigureNetNS { - havePortMapping := len(ctr.Config().PortMappings) > 0 + havePortMapping := len(ctr.config.PortMappings) > 0 if havePortMapping { ctr.rootlessPortSyncR, ctr.rootlessPortSyncW, err = os.Pipe() if err != nil { diff --git a/libpod/pod.go b/libpod/pod.go index 0d5d629cd..d9db06285 100644 --- a/libpod/pod.go +++ b/libpod/pod.go @@ -104,8 +104,7 @@ func (p *Pod) PidMode() string { if err != nil { return "" } - conf := infra.Config() - ctrSpec := conf.Spec + ctrSpec := infra.config.Spec if ctrSpec != nil && ctrSpec.Linux != nil { for _, ns := range ctrSpec.Linux.Namespaces { if ns.Type == specs.PIDNamespace { @@ -126,8 +125,7 @@ func (p *Pod) UserNSMode() string { if err != nil { return "" } - conf := infra.Config() - ctrSpec := conf.Spec + ctrSpec := infra.config.Spec if ctrSpec != nil && ctrSpec.Linux != nil { for _, ns := range ctrSpec.Linux.Namespaces { if ns.Type == specs.UserNamespace { diff --git a/libpod/pod_api.go b/libpod/pod_api.go index 4c3b1b0b7..cd0ac4ca6 100644 --- a/libpod/pod_api.go +++ b/libpod/pod_api.go @@ -34,7 +34,7 @@ func (p *Pod) startInitContainers(ctx context.Context) error { } // If the container is a once init container, we need to remove it // after it runs - if initCon.Config().InitContainerType == define.OneShotInitContainer { + if initCon.config.InitContainerType == define.OneShotInitContainer { icLock := initCon.lock icLock.Lock() if err := p.runtime.removeContainer(ctx, initCon, false, false, true); err != nil { @@ -588,37 +588,37 @@ func (p *Pod) Inspect() (*define.InspectPodData, error) { return nil, err } infraConfig = new(define.InspectPodInfraConfig) - infraConfig.HostNetwork = !infra.Config().ContainerNetworkConfig.UseImageHosts - infraConfig.StaticIP = infra.Config().ContainerNetworkConfig.StaticIP - infraConfig.NoManageResolvConf = infra.Config().UseImageResolvConf - infraConfig.NoManageHosts = infra.Config().UseImageHosts + infraConfig.HostNetwork = !infra.config.ContainerNetworkConfig.UseImageHosts + infraConfig.StaticIP = infra.config.ContainerNetworkConfig.StaticIP + infraConfig.NoManageResolvConf = infra.config.UseImageResolvConf + infraConfig.NoManageHosts = infra.config.UseImageHosts infraConfig.PidNS = p.PidMode() infraConfig.UserNS = p.UserNSMode() - if len(infra.Config().ContainerNetworkConfig.DNSServer) > 0 { - infraConfig.DNSServer = make([]string, 0, len(infra.Config().ContainerNetworkConfig.DNSServer)) - for _, entry := range infra.Config().ContainerNetworkConfig.DNSServer { + if len(infra.config.ContainerNetworkConfig.DNSServer) > 0 { + infraConfig.DNSServer = make([]string, 0, len(infra.config.ContainerNetworkConfig.DNSServer)) + for _, entry := range infra.config.ContainerNetworkConfig.DNSServer { infraConfig.DNSServer = append(infraConfig.DNSServer, entry.String()) } } - if len(infra.Config().ContainerNetworkConfig.DNSSearch) > 0 { - infraConfig.DNSSearch = make([]string, 0, len(infra.Config().ContainerNetworkConfig.DNSSearch)) - infraConfig.DNSSearch = append(infraConfig.DNSSearch, infra.Config().ContainerNetworkConfig.DNSSearch...) + if len(infra.config.ContainerNetworkConfig.DNSSearch) > 0 { + infraConfig.DNSSearch = make([]string, 0, len(infra.config.ContainerNetworkConfig.DNSSearch)) + infraConfig.DNSSearch = append(infraConfig.DNSSearch, infra.config.ContainerNetworkConfig.DNSSearch...) } - if len(infra.Config().ContainerNetworkConfig.DNSOption) > 0 { - infraConfig.DNSOption = make([]string, 0, len(infra.Config().ContainerNetworkConfig.DNSOption)) - infraConfig.DNSOption = append(infraConfig.DNSOption, infra.Config().ContainerNetworkConfig.DNSOption...) + if len(infra.config.ContainerNetworkConfig.DNSOption) > 0 { + infraConfig.DNSOption = make([]string, 0, len(infra.config.ContainerNetworkConfig.DNSOption)) + infraConfig.DNSOption = append(infraConfig.DNSOption, infra.config.ContainerNetworkConfig.DNSOption...) } - if len(infra.Config().HostAdd) > 0 { - infraConfig.HostAdd = make([]string, 0, len(infra.Config().HostAdd)) - infraConfig.HostAdd = append(infraConfig.HostAdd, infra.Config().HostAdd...) + if len(infra.config.HostAdd) > 0 { + infraConfig.HostAdd = make([]string, 0, len(infra.config.HostAdd)) + infraConfig.HostAdd = append(infraConfig.HostAdd, infra.config.HostAdd...) } - if len(infra.Config().ContainerNetworkConfig.Networks) > 0 { - infraConfig.Networks = make([]string, 0, len(infra.Config().ContainerNetworkConfig.Networks)) - infraConfig.Networks = append(infraConfig.Networks, infra.Config().ContainerNetworkConfig.Networks...) + if len(infra.config.ContainerNetworkConfig.Networks) > 0 { + infraConfig.Networks = make([]string, 0, len(infra.config.ContainerNetworkConfig.Networks)) + infraConfig.Networks = append(infraConfig.Networks, infra.config.ContainerNetworkConfig.Networks...) } - infraConfig.NetworkOptions = infra.Config().ContainerNetworkConfig.NetworkOptions - infraConfig.PortBindings = makeInspectPortBindings(infra.Config().ContainerNetworkConfig.PortMappings, nil) + infraConfig.NetworkOptions = infra.config.ContainerNetworkConfig.NetworkOptions + infraConfig.PortBindings = makeInspectPortBindings(infra.config.ContainerNetworkConfig.PortMappings, nil) } inspectData := define.InspectPodData{ diff --git a/libpod/runtime_cstorage.go b/libpod/runtime_cstorage.go index cd2f226af..58bd67e6d 100644 --- a/libpod/runtime_cstorage.go +++ b/libpod/runtime_cstorage.go @@ -106,18 +106,18 @@ func (r *Runtime) removeStorageContainer(idOrName string, force bool) error { logrus.Infof("Storage for container %s already removed", ctr.ID) return nil } - return errors.Wrapf(err, "error looking up container %q mounts", idOrName) + logrus.Warnf("Checking if container %q is mounted, attempting to delete: %v", idOrName, err) } if timesMounted > 0 { return errors.Wrapf(define.ErrCtrStateInvalid, "container %q is mounted and cannot be removed without using force", idOrName) } } else if _, err := r.store.Unmount(ctr.ID, true); err != nil { - if errors.Cause(err) == storage.ErrContainerUnknown { + if errors.Is(err, storage.ErrContainerUnknown) { // Container again gone, no error logrus.Infof("Storage for container %s already removed", ctr.ID) return nil } - return errors.Wrapf(err, "error unmounting container %q", idOrName) + logrus.Warnf("Unmounting container %q while attempting to delete storage: %v", idOrName, err) } if err := r.store.DeleteContainer(ctr.ID); err != nil { diff --git a/pkg/bindings/connection.go b/pkg/bindings/connection.go index 4127ad2f0..e2c46e481 100644 --- a/pkg/bindings/connection.go +++ b/pkg/bindings/connection.go @@ -112,12 +112,12 @@ func NewConnectionWithIdentity(ctx context.Context, uri string, identity string) return nil, errors.Errorf("unable to create connection. %q is not a supported schema", _url.Scheme) } if err != nil { - return nil, errors.Wrapf(err, "failed to create %sClient", _url.Scheme) + return nil, errors.Wrapf(err, "unable to connect to Podman. failed to create %sClient", _url.Scheme) } ctx = context.WithValue(ctx, clientKey, &connection) if err := pingNewConnection(ctx); err != nil { - return nil, errors.Wrap(err, "cannot connect to the Podman socket, please verify the connection to the Linux system, or use `podman machine` to create/start a Linux VM.") + return nil, errors.Wrap(err, "unable to connect to Podman socket") } return ctx, nil } diff --git a/pkg/bindings/images/build.go b/pkg/bindings/images/build.go index 9d5aad23b..4d667d90a 100644 --- a/pkg/bindings/images/build.go +++ b/pkg/bindings/images/build.go @@ -230,6 +230,9 @@ func Build(ctx context.Context, containerFiles []string, options entities.BuildO params.Add("platform", platform) } } + if contextDir, err := filepath.EvalSymlinks(options.ContextDirectory); err == nil { + options.ContextDirectory = contextDir + } params.Set("pullpolicy", options.PullPolicy.String()) diff --git a/pkg/domain/infra/abi/containers.go b/pkg/domain/infra/abi/containers.go index affed64d1..774362d03 100644 --- a/pkg/domain/infra/abi/containers.go +++ b/pkg/domain/infra/abi/containers.go @@ -169,6 +169,10 @@ func (ic *ContainerEngine) ContainerStop(ctx context.Context, namesOrIds []strin logrus.Debugf("Container %s is already stopped", c.ID()) case options.All && errors.Cause(err) == define.ErrCtrStateInvalid: logrus.Debugf("Container %s is not running, could not stop", c.ID()) + // container never created in OCI runtime + // docker parity: do nothing just return container id + case errors.Cause(err) == define.ErrCtrStateInvalid: + logrus.Debugf("Container %s is either not created on runtime or is in a invalid state", c.ID()) default: return err } diff --git a/pkg/domain/infra/abi/generate.go b/pkg/domain/infra/abi/generate.go index 1e614ce58..081a2464b 100644 --- a/pkg/domain/infra/abi/generate.go +++ b/pkg/domain/infra/abi/generate.go @@ -107,7 +107,7 @@ func (ic *ContainerEngine) GenerateKube(ctx context.Context, nameOrIDs []string, // Generate kube pods and services from pods. if len(pods) >= 1 { - pos, svcs, err := getKubePods(pods, options.Service) + pos, svcs, err := getKubePods(ctx, pods, options.Service) if err != nil { return nil, err } @@ -120,7 +120,7 @@ func (ic *ContainerEngine) GenerateKube(ctx context.Context, nameOrIDs []string, // Generate the kube pods from containers. if len(ctrs) >= 1 { - po, err := libpod.GenerateForKube(ctrs) + po, err := libpod.GenerateForKube(ctx, ctrs) if err != nil { return nil, err } @@ -153,12 +153,12 @@ func (ic *ContainerEngine) GenerateKube(ctx context.Context, nameOrIDs []string, } // getKubePods returns kube pod and service YAML files from podman pods. -func getKubePods(pods []*libpod.Pod, getService bool) ([][]byte, [][]byte, error) { +func getKubePods(ctx context.Context, pods []*libpod.Pod, getService bool) ([][]byte, [][]byte, error) { pos := [][]byte{} svcs := [][]byte{} for _, p := range pods { - po, sp, err := p.GenerateForKube() + po, sp, err := p.GenerateForKube(ctx) if err != nil { return nil, nil, err } diff --git a/pkg/machine/config.go b/pkg/machine/config.go index 8db2335aa..3ff5c7fe7 100644 --- a/pkg/machine/config.go +++ b/pkg/machine/config.go @@ -58,6 +58,9 @@ type ListResponse struct { LastUp time.Time Running bool VMType string + CPUs uint64 + Memory uint64 + DiskSize uint64 } type SSHOptions struct { diff --git a/pkg/machine/ignition.go b/pkg/machine/ignition.go index 89b556b14..e211f5ea6 100644 --- a/pkg/machine/ignition.go +++ b/pkg/machine/ignition.go @@ -6,6 +6,7 @@ import ( "encoding/json" "fmt" "io/ioutil" + "net/url" ) /* @@ -80,6 +81,7 @@ func NewIgnitionFile(ign DynamicIgnition) error { // so a listening host knows it can being interacting with it ready := `[Unit] Requires=dev-virtio\\x2dports-%s.device +After=remove-moby.service OnFailure=emergency.target OnFailureJobMode=isolate [Service] @@ -89,6 +91,23 @@ ExecStart=/bin/sh -c '/usr/bin/echo Ready >/dev/%s' [Install] RequiredBy=multi-user.target ` + deMoby := `[Unit] +Description=Remove moby-engine +# Run once for the machine +After=systemd-machine-id-commit.service +Before=zincati.service +ConditionPathExists=!/var/lib/%N.stamp + +[Service] +Type=oneshot +RemainAfterExit=yes +ExecStart=/usr/bin/rpm-ostree override remove moby-engine +ExecStart=/usr/bin/rpm-ostree ex apply-live --allow-replacement +ExecStartPost=/bin/touch /var/lib/%N.stamp + +[Install] +WantedBy=multi-user.target + ` _ = ready ignSystemd := Systemd{ Units: []Unit{ @@ -101,6 +120,21 @@ RequiredBy=multi-user.target Name: "ready.service", Contents: strToPtr(fmt.Sprintf(ready, "vport1p1", "vport1p1")), }, + { + Enabled: boolToPtr(false), + Name: "docker.service", + Mask: boolToPtr(true), + }, + { + Enabled: boolToPtr(false), + Name: "docker.socket", + Mask: boolToPtr(true), + }, + { + Enabled: boolToPtr(true), + Name: "remove-moby.service", + Contents: &deMoby, + }, }} ignConfig := Config{ Ignition: ignVersion, @@ -161,6 +195,22 @@ func getFiles(usrName string) []File { var ( files []File ) + + lingerExample := `[Unit] +Description=A systemd user unit demo +After=network-online.target +Wants=network-online.target podman.socket +[Service] +ExecStart=/usr/bin/sleep infinity +` + containers := `[containers] +netns="bridge" +rootless_networking="cni" +` + rootContainers := `[engine] +machine_enabled=true +` + // Add a fake systemd service to get the user socket rolling files = append(files, File{ Node: Node{ @@ -171,7 +221,7 @@ func getFiles(usrName string) []File { FileEmbedded1: FileEmbedded1{ Append: nil, Contents: Resource{ - Source: strToPtr("data:,%5BUnit%5D%0ADescription%3DA%20systemd%20user%20unit%20demo%0AAfter%3Dnetwork-online.target%0AWants%3Dnetwork-online.target%20podman.socket%0A%5BService%5D%0AExecStart%3D%2Fusr%2Fbin%2Fsleep%20infinity%0A"), + Source: encodeDataURLPtr(lingerExample), }, Mode: intToPtr(0744), }, @@ -188,7 +238,7 @@ func getFiles(usrName string) []File { FileEmbedded1: FileEmbedded1{ Append: nil, Contents: Resource{ - Source: strToPtr("data:,%5Bcontainers%5D%0D%0Anetns%3D%22bridge%22%0D%0Arootless_networking%3D%22cni%22"), + Source: encodeDataURLPtr(containers), }, Mode: intToPtr(0744), }, @@ -213,7 +263,7 @@ func getFiles(usrName string) []File { FileEmbedded1: FileEmbedded1{ Append: nil, Contents: Resource{ - Source: strToPtr("data:,%5Bengine%5D%0Amachine_enabled%3Dtrue%0A"), + Source: encodeDataURLPtr(rootContainers), }, Mode: intToPtr(0644), }, @@ -233,7 +283,22 @@ func getFiles(usrName string) []File { FileEmbedded1: FileEmbedded1{ Append: nil, Contents: Resource{ - Source: strToPtr("data:,unqualified-search-registries%3D%5B%22docker.io%22%5D"), + Source: encodeDataURLPtr("unqualified-search-registries=[\"docker.io\"]\n"), + }, + Mode: intToPtr(0644), + }, + }) + + files = append(files, File{ + Node: Node{ + Path: "/etc/tmpfiles.d/podman-docker.conf", + }, + FileEmbedded1: FileEmbedded1{ + Append: nil, + // Create a symlink from the docker socket to the podman socket. + // Taken from https://github.com/containers/podman/blob/main/contrib/systemd/system/podman-docker.conf + Contents: Resource{ + Source: encodeDataURLPtr("L+ /run/docker.sock - - - - /run/podman/podman.sock\n"), }, Mode: intToPtr(0644), }, @@ -253,5 +318,20 @@ func getLinks(usrName string) []Link { Hard: boolToPtr(false), Target: "/home/" + usrName + "/.config/systemd/user/linger-example.service", }, + }, { + Node: Node{ + Group: getNodeGrp("root"), + Path: "/usr/local/bin/docker", + Overwrite: boolToPtr(true), + User: getNodeUsr("root"), + }, + LinkEmbedded1: LinkEmbedded1{ + Hard: boolToPtr(false), + Target: "/usr/bin/podman", + }, }} } + +func encodeDataURLPtr(contents string) *string { + return strToPtr(fmt.Sprintf("data:,%s", url.PathEscape(contents))) +} diff --git a/pkg/machine/qemu/config.go b/pkg/machine/qemu/config.go index 3d0fa4094..9f5f45b58 100644 --- a/pkg/machine/qemu/config.go +++ b/pkg/machine/qemu/config.go @@ -17,6 +17,8 @@ type MachineVM struct { ImagePath string // Memory in megabytes assigned to the vm Memory uint64 + // Disk size in gigabytes assigned to the vm + DiskSize uint64 // Name of the vm Name string // SSH port for user networking diff --git a/pkg/machine/qemu/machine.go b/pkg/machine/qemu/machine.go index b9b66c123..d0f48da5f 100644 --- a/pkg/machine/qemu/machine.go +++ b/pkg/machine/qemu/machine.go @@ -64,6 +64,7 @@ func NewMachine(opts machine.InitOptions) (machine.VM, error) { vm.CPUs = opts.CPUS vm.Memory = opts.Memory + vm.DiskSize = opts.DiskSize // Look up the executable execPath, err := exec.LookPath(QemuCommand) @@ -574,6 +575,9 @@ func GetVMInfos() ([]*machine.ListResponse, error) { listEntry.Name = vm.Name listEntry.VMType = "qemu" + listEntry.CPUs = vm.CPUs + listEntry.Memory = vm.Memory + listEntry.DiskSize = vm.DiskSize fi, err := os.Stat(fullPath) if err != nil { return err diff --git a/pkg/ps/ps.go b/pkg/ps/ps.go index 54079baa1..e65400555 100644 --- a/pkg/ps/ps.go +++ b/pkg/ps/ps.go @@ -241,6 +241,13 @@ func ListContainerBatch(rt *libpod.Runtime, ctr *libpod.Container, opts entities UTS: uts, } } + + if hc, err := ctr.HealthCheckStatus(); err == nil { + ps.Status = hc + } else { + logrus.Debug(err) + } + return ps, nil } diff --git a/pkg/rootless/rootless_linux.c b/pkg/rootless/rootless_linux.c index 4d8443fcb..6ce4b1e29 100644 --- a/pkg/rootless/rootless_linux.c +++ b/pkg/rootless/rootless_linux.c @@ -212,6 +212,7 @@ can_use_shortcut () continue; if (strcmp (argv[argc], "mount") == 0 + || strcmp (argv[argc], "machine") == 0 || strcmp (argv[argc], "search") == 0 || (strcmp (argv[argc], "system") == 0 && argv[argc+1] && strcmp (argv[argc+1], "service") != 0)) { diff --git a/test/e2e/generate_kube_test.go b/test/e2e/generate_kube_test.go index bf89a0708..cb556991c 100644 --- a/test/e2e/generate_kube_test.go +++ b/test/e2e/generate_kube_test.go @@ -792,6 +792,45 @@ var _ = Describe("Podman generate kube", func() { Expect(containers[0].Args).To(Equal([]string{"10s"})) }) + It("podman generate kube - no command", func() { + session := podmanTest.Podman([]string{"create", "--name", "test", ALPINE}) + session.WaitWithDefaultTimeout() + Expect(session).Should(Exit(0)) + + kube := podmanTest.Podman([]string{"generate", "kube", "test"}) + kube.WaitWithDefaultTimeout() + Expect(kube).Should(Exit(0)) + + // Now make sure that the container's command is not set to the + // entrypoint and it's arguments to "10s". + pod := new(v1.Pod) + err := yaml.Unmarshal(kube.Out.Contents(), pod) + Expect(err).To(BeNil()) + + containers := pod.Spec.Containers + Expect(len(containers)).To(Equal(1)) + Expect(len(containers[0].Command)).To(Equal(0)) + + cmd := []string{"echo", "hi"} + session = podmanTest.Podman(append([]string{"create", "--name", "test1", ALPINE}, cmd...)) + session.WaitWithDefaultTimeout() + Expect(session).Should(Exit(0)) + + kube = podmanTest.Podman([]string{"generate", "kube", "test1"}) + kube.WaitWithDefaultTimeout() + Expect(kube).Should(Exit(0)) + + // Now make sure that the container's command is not set to the + // entrypoint and it's arguments to "10s". + pod = new(v1.Pod) + err = yaml.Unmarshal(kube.Out.Contents(), pod) + Expect(err).To(BeNil()) + + containers = pod.Spec.Containers + Expect(len(containers)).To(Equal(1)) + Expect(containers[0].Command).To(Equal(cmd)) + }) + It("podman generate kube - use entrypoint from image", func() { // Build an image with an entrypoint. containerfile := `FROM quay.io/libpod/alpine:latest diff --git a/test/e2e/healthcheck_run_test.go b/test/e2e/healthcheck_run_test.go index 1445a634b..b30e8b810 100644 --- a/test/e2e/healthcheck_run_test.go +++ b/test/e2e/healthcheck_run_test.go @@ -80,6 +80,11 @@ var _ = Describe("Podman healthcheck run", func() { time.Sleep(1 * time.Second) } Expect(exitCode).To(Equal(0)) + + ps := podmanTest.Podman([]string{"ps"}) + ps.WaitWithDefaultTimeout() + Expect(ps).Should(Exit(0)) + Expect(ps.OutputToString()).To(ContainSubstring("(healthy)")) }) It("podman healthcheck that should fail", func() { diff --git a/test/e2e/rename_test.go b/test/e2e/rename_test.go index 0bd1792c9..e5e69c25c 100644 --- a/test/e2e/rename_test.go +++ b/test/e2e/rename_test.go @@ -111,4 +111,29 @@ var _ = Describe("podman rename", func() { Expect(ps).Should(Exit(0)) Expect(ps.OutputToString()).To(ContainSubstring(newName)) }) + + It("Rename a container that is part of a pod", func() { + podName := "testPod" + infraName := "infra1" + pod := podmanTest.Podman([]string{"pod", "create", "--name", podName, "--infra-name", infraName}) + pod.WaitWithDefaultTimeout() + Expect(pod).Should(Exit(0)) + + infraName2 := "infra2" + rename := podmanTest.Podman([]string{"rename", infraName, infraName2}) + rename.WaitWithDefaultTimeout() + Expect(rename).Should(Exit(0)) + + remove := podmanTest.Podman([]string{"pod", "rm", "-f", podName}) + remove.WaitWithDefaultTimeout() + Expect(remove).Should(Exit(0)) + + create := podmanTest.Podman([]string{"create", "--name", infraName2, ALPINE, "top"}) + create.WaitWithDefaultTimeout() + Expect(create).Should(Exit(0)) + + create2 := podmanTest.Podman([]string{"create", "--name", infraName, ALPINE, "top"}) + create2.WaitWithDefaultTimeout() + Expect(create2).Should(Exit(0)) + }) }) diff --git a/test/e2e/stop_test.go b/test/e2e/stop_test.go index a984bf6d0..7f178d719 100644 --- a/test/e2e/stop_test.go +++ b/test/e2e/stop_test.go @@ -234,6 +234,17 @@ var _ = Describe("Podman stop", func() { Expect(strings.TrimSpace(finalCtrs.OutputToString())).To(Equal("")) }) + It("podman stop should return silent success on stopping configured containers", func() { + // following container is not created on OCI runtime + // so we return success and assume that is is stopped + session2 := podmanTest.Podman([]string{"create", "--name", "stopctr", ALPINE, "/bin/sh"}) + session2.WaitWithDefaultTimeout() + Expect(session2).Should(Exit(0)) + session3 := podmanTest.Podman([]string{"stop", "stopctr"}) + session3.WaitWithDefaultTimeout() + Expect(session3).Should(Exit(0)) + }) + It("podman stop --cidfile", func() { tmpDir, err := ioutil.TempDir("", "") diff --git a/test/system/035-logs.bats b/test/system/035-logs.bats index a04d2ac74..76ce12b81 100644 --- a/test/system/035-logs.bats +++ b/test/system/035-logs.bats @@ -135,31 +135,38 @@ function _log_test_until() { s_after="after_$(random_string)_${driver}" before=$(date --iso-8601=seconds) - sleep 5 + sleep 1 run_podman run --log-driver=$driver -d --name test $IMAGE sh -c \ "echo $s_before; trap 'echo $s_after; exit' SIGTERM; while :; do sleep 1; done" # sleep a second to make sure the date is after the first echo sleep 1 run_podman stop test - # sleep for 20 seconds to get the proper after time - sleep 20 + run_podman wait test - run_podman logs test - is "$output" \ - "$s_before + # Sigh. Stupid journald has a lag. Wait a few seconds for it to catch up. + retries=20 + s_both="$s_before $s_after" + while [[ $retries -gt 0 ]]; do + run_podman logs test + if [[ "$output" = "$s_both" ]]; then + break + fi + retries=$((retries - 1)) + sleep 0.1 + done + if [[ $retries -eq 0 ]]; then + die "Timed out waiting for before&after in podman logs: $output" + fi run_podman logs --until $before test - is "$output" \ - "" + is "$output" "" "podman logs --until before" - after=$(date --iso-8601=seconds) + after=$(date --date='+1 second' --iso-8601=seconds) run_podman logs --until $after test - is "$output" \ - "$s_before -$s_after" + is "$output" "$s_both" "podman logs --until after" run_podman rm -f test } diff --git a/test/system/040-ps.bats b/test/system/040-ps.bats index 182d75547..bb2971574 100644 --- a/test/system/040-ps.bats +++ b/test/system/040-ps.bats @@ -90,10 +90,18 @@ load helpers is "${#lines[@]}" "1" "setup check: no storage containers at start of test" # Force a buildah timeout; this leaves a buildah container behind + local t0=$SECONDS PODMAN_TIMEOUT=5 run_podman 124 build -t thiswillneverexist - <<EOF FROM $IMAGE RUN sleep 30 EOF + local t1=$SECONDS + local delta_t=$((t1 - t0)) + if [[ $delta_t -gt 10 ]]; then + # FIXME FIXME FIXME: when buildah issue 3544 gets fixed and vendored, + # change 'echo' to 'die' + echo "podman build did not get killed within 10 seconds (actual time: $delta_t seconds)" + fi run_podman ps -a is "${#lines[@]}" "1" "podman ps -a does not see buildah container" diff --git a/test/system/060-mount.bats b/test/system/060-mount.bats index 63a93e13b..ba37ea5e1 100644 --- a/test/system/060-mount.bats +++ b/test/system/060-mount.bats @@ -125,6 +125,7 @@ load helpers run_podman exec $cid find /image-mount/etc/ # Clean up + run_podman stop -t 0 $cid run_podman rm -f $cid } @@ -147,6 +148,7 @@ load helpers run_podman inspect --format "{{(index .Mounts 0).RW}}" $cid is "$output" "true" "inspect data includes image mount source" + run_podman stop -t 0 $cid run_podman rm -f $cid } diff --git a/test/system/065-cp.bats b/test/system/065-cp.bats index 39f439e7b..38c38d671 100644 --- a/test/system/065-cp.bats +++ b/test/system/065-cp.bats @@ -256,6 +256,7 @@ load helpers " # From RUNNING container + local -a destcontainers=() while read id src dest dest_fullname description; do # dest may be "''" for empty table cells if [[ $dest == "''" ]];then @@ -265,26 +266,25 @@ load helpers # To RUNNING container run_podman run -d $IMAGE sleep infinity destcontainer="$output" + destcontainers+=($destcontainer) run_podman cp cpcontainer:$src $destcontainer:"/$dest" run_podman exec $destcontainer cat "/$dest_fullname" is "$output" "${randomcontent[$id]}" "$description (cp ctr:$src to /$dest)" - run_podman kill $destcontainer - run_podman rm -f $destcontainer # To CREATED container run_podman create $IMAGE sleep infinity destcontainer="$output" + destcontainers+=($destcontainer) run_podman cp cpcontainer:$src $destcontainer:"/$dest" run_podman start $destcontainer run_podman exec $destcontainer cat "/$dest_fullname" is "$output" "${randomcontent[$id]}" "$description (cp ctr:$src to /$dest)" - run_podman kill $destcontainer - run_podman rm -f $destcontainer done < <(parse_table "$tests") - run_podman kill cpcontainer - run_podman rm -f cpcontainer + run_podman kill cpcontainer ${destcontainers[@]} + run_podman rm -f cpcontainer ${destcontainers[@]} # From CREATED container + destcontainers=() run_podman create --name cpcontainer --workdir=/srv $cpimage while read id src dest dest_fullname description; do # dest may be "''" for empty table cells @@ -295,23 +295,21 @@ load helpers # To RUNNING container run_podman run -d $IMAGE sleep infinity destcontainer="$output" + destcontainers+=($destcontainer) run_podman cp cpcontainer:$src $destcontainer:"/$dest" run_podman exec $destcontainer cat "/$dest_fullname" is "$output" "${randomcontent[$id]}" "$description (cp ctr:$src to /$dest)" - run_podman kill $destcontainer - run_podman rm -f $destcontainer - # To CREATED container run_podman create $IMAGE sleep infinity destcontainer="$output" + destcontainers+=($destcontainer) run_podman cp cpcontainer:$src $destcontainer:"/$dest" run_podman start $destcontainer run_podman exec $destcontainer cat "/$dest_fullname" is "$output" "${randomcontent[$id]}" "$description (cp ctr:$src to /$dest)" - run_podman kill $destcontainer - run_podman rm -f $destcontainer done < <(parse_table "$tests") - run_podman rm -f cpcontainer + run_podman kill ${destcontainers[@]} + run_podman rm -f cpcontainer ${destcontainers[@]} run_podman rmi -f $cpimage } @@ -496,6 +494,7 @@ load helpers " # From RUNNING container + local -a destcontainers=() while read src dest dest_fullname description; do if [[ $src == "''" ]];then unset src @@ -510,28 +509,27 @@ load helpers # To RUNNING container run_podman run -d $IMAGE sleep infinity destcontainer="$output" + destcontainers+=($destcontainer) run_podman cp cpcontainer:$src $destcontainer:"/$dest" run_podman exec $destcontainer cat "/$dest_fullname/containerfile0" "/$dest_fullname/containerfile1" is "$output" "${randomcontent[0]} ${randomcontent[1]}" "$description" - run_podman kill $destcontainer - run_podman rm -f $destcontainer # To CREATED container run_podman create $IMAGE sleep infinity destcontainer="$output" + destcontainers+=($destcontainer) run_podman cp cpcontainer:$src $destcontainer:"/$dest" run_podman start $destcontainer run_podman exec $destcontainer cat "/$dest_fullname/containerfile0" "/$dest_fullname/containerfile1" is "$output" "${randomcontent[0]} ${randomcontent[1]}" "$description" - run_podman kill $destcontainer - run_podman rm -f $destcontainer done < <(parse_table "$tests") - run_podman kill cpcontainer - run_podman rm -f cpcontainer + run_podman kill cpcontainer ${destcontainers[@]} + run_podman rm -f cpcontainer ${destcontainers[@]} # From CREATED container + destcontainers=() run_podman create --name cpcontainer --workdir=/srv $cpimage while read src dest dest_fullname description; do if [[ $src == "''" ]];then @@ -547,26 +545,25 @@ ${randomcontent[1]}" "$description" # To RUNNING container run_podman run -d $IMAGE sleep infinity destcontainer="$output" + destcontainers+=($destcontainer) run_podman cp cpcontainer:$src $destcontainer:"/$dest" run_podman exec $destcontainer cat "/$dest_fullname/containerfile0" "/$dest_fullname/containerfile1" is "$output" "${randomcontent[0]} ${randomcontent[1]}" "$description" - run_podman kill $destcontainer - run_podman rm -f $destcontainer # To CREATED container run_podman create $IMAGE sleep infinity destcontainer="$output" + destcontainers+=($destcontainer) run_podman start $destcontainer run_podman cp cpcontainer:$src $destcontainer:"/$dest" run_podman exec $destcontainer cat "/$dest_fullname/containerfile0" "/$dest_fullname/containerfile1" is "$output" "${randomcontent[0]} ${randomcontent[1]}" "$description" - run_podman kill $destcontainer - run_podman rm -f $destcontainer done < <(parse_table "$tests") - run_podman rm -f cpcontainer + run_podman kill ${destcontainers[@]} + run_podman rm -f cpcontainer ${destcontainers[@]} run_podman rmi -f $cpimage } diff --git a/test/system/070-build.bats b/test/system/070-build.bats index 0e1396fc6..4e89e299a 100644 --- a/test/system/070-build.bats +++ b/test/system/070-build.bats @@ -956,6 +956,15 @@ EOF run_podman build -t build_test $tmpdir } +@test "podman build build context is a symlink to a directory" { + tmpdir=$PODMAN_TMPDIR/build-test + mkdir -p $tmpdir/target + ln -s target $tmpdir/link + echo FROM $IMAGE > $tmpdir/link/Dockerfile + echo RUN echo hello >> $tmpdir/link/Dockerfile + run_podman build -t build_test $tmpdir/link +} + function teardown() { # A timeout or other error in 'build' can leave behind stale images # that podman can't even see and which will cascade into subsequent diff --git a/test/system/080-pause.bats b/test/system/080-pause.bats index 1eb47dcfb..2314324a9 100644 --- a/test/system/080-pause.bats +++ b/test/system/080-pause.bats @@ -48,6 +48,7 @@ load helpers # would imply that the container never paused. is "$max_delta" "[3456]" "delta t between paused and restarted" + run_podman stop -t 0 $cname run_podman rm -f $cname # Pause/unpause on nonexistent name or id - these should all fail @@ -73,6 +74,7 @@ load helpers is "$output" "$cid" "podman unpause output" run_podman ps --format '{{.ID}} {{.Names}} {{.Status}}' is "$output" "${cid:0:12} $cname Up.*" "podman ps on resumed container" + run_podman stop -t 0 $cname run_podman rm -f $cname run_podman rm -f notrunning } diff --git a/test/system/220-healthcheck.bats b/test/system/220-healthcheck.bats index e416629e6..391dbfa09 100644 --- a/test/system/220-healthcheck.bats +++ b/test/system/220-healthcheck.bats @@ -108,6 +108,7 @@ Log[-1].Output | is "$output" "unhealthy" "output from 'podman healthcheck run'" # Clean up + run_podman stop -t 0 healthcheck_c run_podman rm -f healthcheck_c run_podman rmi healthcheck_i } diff --git a/test/system/270-socket-activation.bats b/test/system/270-socket-activation.bats index 031ba161b..dd439d3ae 100644 --- a/test/system/270-socket-activation.bats +++ b/test/system/270-socket-activation.bats @@ -69,26 +69,36 @@ function teardown() { @test "podman system service - socket activation - no container" { run curl -s --max-time 3 --unix-socket $SERVICE_SOCK_ADDR http://podman/libpod/_ping - is "$output" "OK" "podman service responses normally" + is "$output" "OK" "podman service responds normally" } -@test "podman system service - socket activation - exist container " { - run_podman run $IMAGE sleep 90 +@test "podman system service - socket activation - existing container" { + run_podman run -d $IMAGE sleep 90 + cid="$output" + run curl -s --max-time 3 --unix-socket $SERVICE_SOCK_ADDR http://podman/libpod/_ping - is "$output" "OK" "podman service responses normally" + is "$output" "OK" "podman service responds normally" + + run_podman stop -t 0 $cid + run_podman rm -f $cid } -@test "podman system service - socket activation - kill rootless pause " { +@test "podman system service - socket activation - kill rootless pause" { if ! is_rootless; then skip "root podman no need pause process" fi - run_podman run $IMAGE sleep 90 + run_podman run -d $IMAGE sleep 90 + cid="$output" + local pause_pid="$XDG_RUNTIME_DIR/libpod/tmp/pause.pid" if [ -f $pause_pid ]; then kill -9 $(cat $pause_pid) 2> /dev/null fi run curl -s --max-time 3 --unix-socket $SERVICE_SOCK_ADDR http://podman/libpod/_ping - is "$output" "OK" "podman service responses normally" + is "$output" "OK" "podman service responds normally" + + run_podman stop -t 0 $cid + run_podman rm -f $cid } # vim: filetype=sh diff --git a/test/system/600-completion.bats b/test/system/600-completion.bats index fbb0da231..5f4610e9e 100644 --- a/test/system/600-completion.bats +++ b/test/system/600-completion.bats @@ -110,12 +110,10 @@ function check_shell_completion() { is "$output" ".*localhost/$random_image_name:$random_image_tag${nl}" \ "$* $cmd: actual image listed in suggestions" - # check that we complete the image with and without tag after at least one char is typed + # check that we complete the image with tag after at least one char is typed run_completion "$@" $cmd "${extra_args[@]}" "${random_image_name:0:1}" is "$output" ".*$random_image_name:$random_image_tag${nl}" \ "$* $cmd: image name:tag included in suggestions" - is "$output" ".*$random_image_name${nl}" \ - "$* $cmd: image name(w/o tag) included in suggestions" # check that we complete the image id after at least two chars are typed run_completion "$@" $cmd "${extra_args[@]}" "${random_image_id:0:2}" diff --git a/test/system/700-play.bats b/test/system/700-play.bats index 2b05cdd84..0785bffdf 100644 --- a/test/system/700-play.bats +++ b/test/system/700-play.bats @@ -69,11 +69,15 @@ RELABEL="system_u:object_r:container_file_t:s0" TESTDIR=$PODMAN_TMPDIR/testdir mkdir -p $TESTDIR echo "$testYaml" | sed "s|TESTDIR|${TESTDIR}|g" > $PODMAN_TMPDIR/test.yaml + run_podman play kube - < $PODMAN_TMPDIR/test.yaml if [ -e /usr/sbin/selinuxenabled -a /usr/sbin/selinuxenabled ]; then run ls -Zd $TESTDIR is "$output" ${RELABEL} "selinux relabel should have happened" fi + + run_podman stop -a -t 0 + run_podman pod stop test_pod run_podman pod rm -f test_pod } @@ -86,6 +90,9 @@ RELABEL="system_u:object_r:container_file_t:s0" run ls -Zd $TESTDIR is "$output" ${RELABEL} "selinux relabel should have happened" fi + + run_podman stop -a -t 0 + run_podman pod stop test_pod run_podman pod rm -f test_pod } @@ -102,12 +109,19 @@ RELABEL="system_u:object_r:container_file_t:s0" infraID="$output" run_podman container inspect --format "{{.HostConfig.NetworkMode}}" $infraID is "$output" "slirp4netns" "network mode slirp4netns is set for the container" + + run_podman stop -a -t 0 + run_podman pod stop test_pod run_podman pod rm -f test_pod + run_podman play kube --network none $PODMAN_TMPDIR/test.yaml run_podman pod inspect --format {{.InfraContainerID}} "${lines[1]}" infraID="$output" run_podman container inspect --format "{{.HostConfig.NetworkMode}}" $infraID is "$output" "none" "network mode none is set for the container" + + run_podman stop -a -t 0 + run_podman pod stop test_pod run_podman pod rm -f test_pod } @@ -149,6 +163,9 @@ _EOF run_podman play kube --start=false $PODMAN_TMPDIR/test.yaml run_podman inspect --format "{{ .Config.User }}" test_pod-test is "$output" bin "expect container within pod to run as the bin user" + + run_podman stop -a -t 0 + run_podman pod stop test_pod run_podman pod rm -f test_pod run_podman rmi -f userimage:latest } |