diff options
41 files changed, 496 insertions, 108 deletions
diff --git a/RELEASE_NOTES.md b/RELEASE_NOTES.md index 448cfed5a..1cd2f9381 100644 --- a/RELEASE_NOTES.md +++ b/RELEASE_NOTES.md @@ -23,6 +23,7 @@ - The `podman pod create` command now supports the `--device` option, allowing devices to be specified that will be mounted automatically to all containers in the pod. - The `podman pod create` command now supports the `--volumes-from` option, allowing volumes from an existing Podman container to be mounted automatically to all containers in the pod. - The `podman pod create` command now supports the `--security-opt` option, allowing security settings (e.g. disabling SELinux or Seccomp) to be configured automatically for all containers in the pod ([#12173](https://github.com/containers/podman/issues/12173)). +- The `podman pod create` command now supports the `--share-parent` option, which defaults to true, controlling whether containers in the pod will use a shared cgroup parent. - The `podman pod create` command now supports the `--sysctl` option, allowing sysctls to be configured automatically for all containers in the pod. - The `podman events` command now supports the `--no-trunc` option, which will allow short container IDs to be displayed instead of the default full IDs. The flag defaults to true, so full IDs remain the default ([#8941](https://github.com/containers/podman/issues/8941)). - The `podman machine init` command now supports a new VM type, `wsl`, available only on Windows; this uses WSL as a backend for `podman machine`, instead of creating a separate VM and managing it via QEMU ([#12503](https://github.com/containers/podman/pull/12503)). @@ -70,7 +71,7 @@ ### Breaking Changes - Podman v4.0 will perform several schema migrations in the Podman database when it is first run. These schema migrations will cause Podman v3.x and earlier to be unable to read certain network configuration information from the database, so downgrading from Podman v4.0 to an earlier version will cause containers to lose their static IP, MAC address, and port bindings. - All endpoints of the Docker-compatible API now enforce that all image shortnames will be resolved to the Docker Hub for improved Docker compatibility. This behavior can be turned off via the `compat_api_enforce_docker_hub` option in `containers.conf` ([#12320](https://github.com/containers/podman/issues/12320)). -- The Podman APIs for Manifest List and Network operations have been completely rewritten to address issues and inconsistencies in the previous APIs. +- The Podman APIs for Manifest List and Network operations have been completely rewritten to address issues and inconsistencies in the previous APIs. Incompatible APIs should warn if they are used with an older Podman client. - The `make install` makefile target no longer implicitly builds Podman, and will fail if `make` was not run prior to it. - The `podman rm --depends`, `podman rmi --force`, and `podman network rm --force` commands can now remove pods if a they need to remove an infra container (e.g. `podman rmi --force` on the infra image will remove all pods and infra containers). Previously, any command that tried to remove an infra container would error. - The `podman system reset` command now removes all networks on the system, in addition to all volumes, pods, containers, and images. @@ -113,6 +114,7 @@ - Fixed a bug where the `podman tag` command on a manifest list could tag an image in the manifest, and not the manifest list itself. - Fixed a bug where creating a volume using an invalid volume option that contained a format string would print a nonsensical error. - Fixed a bug where Podman would not create a healthcheck for containers created from images that specified a healthcheck in their configuration ([#12226](https://github.com/containers/podman/issues/12226)). +- Fixed a bug where the output of healthchecks was not shown in `podman inspect` ([#13083](https://github.com/containers/podman/issues/13083)). - Fixed a bug where rootless containers that used a custom user namespace (e.g. `--userns=keep-id`) could not have any ports forwarded to them. - Fixed a bug where the `podman system connection ls` command would not print any output (including headers) if no connections were present. - Fixed a bug where the `--memory-swappiness` option to `podman create` and `podman run` did not accept 0 as a valid value. @@ -156,6 +158,9 @@ - Fixed a bug where the `podman stats` command would not show network usage statistics on containers using `slirp4netns` for networking ([#11695](https://github.com/containers/podman/issues/11695)). - Fixed a bug where the `/dev/shm` mount in the container was not mounted with `nosuid`, `noexec`, and `nodev` mount options. - Fixed a bug where the `--shm-size` option to `podman create` and `podman run` interpeted human-readable sizes as KB instead of KiB, and GB instead of GiB (such that a kilobyte was interpreted as 1000 bytes, instead of 1024 bytes) ([#13096](https://github.com/containers/podman/issues/13096)). +- Fixed a bug where the `--share=cgroup` option to `podman pod create` controlled whether the pod used a shared Cgroup parent, not whether the Cgroup namespace was shared ([#12765](https://github.com/containers/podman/issues/12765)). +- Fixed a bug where, when a Podman container using the `slirp4netns` network mode was run inside a systemd unit file, systemd could kill the `slirp4netns` process, which is shared between all containers for a given user (thus causing all `slirp4netns`-mode containers for that user to be unable to connect to the internet) ([#13153](https://github.com/containers/podman/issues/13153)). +- Fixed a bug where the `podman network connect` and `podman network disconnect` commands would not update `/etc/resolv.conf` in the container to add or remove the DNS servers of the networks that were connected or disconnected ([#9603](https://github.com/containers/podman/issues/9603)). ### API - The Podman remote API version has been bumped to v4.0.0. @@ -173,6 +178,7 @@ - Fixed a bug where the Compat Load endpoint for Images would refuse to accept input archives that contained more than one image. - Fixed a bug where the Compat Build endpoint for Images ignored the `quiet` query parameter ([#12566](https://github.com/containers/podman/issues/12566)). - Fixed a bug where the Compat Build endpoint for Images did not include `aux` JSON (which included the ID of built images) in returned output ([#12063](https://github.com/containers/podman/issues/12063)). +- Fixed a bug where the Compat Build endpoint for Images did not set the correct `Content-Type` in its responses ([#13148](https://github.com/containers/podman/issues/13148)). - Fixed a bug where the Compat and Libpod List endpoints for Networks would sometimes not return networks created on the server by the Podman CLI after the API server had been started ([#11828](https://github.com/containers/podman/issues/11828)). - Fixed a bug where the Compat Inspect endpoint for Networks did not include the subnet CIDR in the returned IPv4 and IPv6 addresses. - Fixed a bug where the Compat Events endpoint did not properly set the Action field of `Died` events for containers to `die` (previously, `died` was used; this was incompatible with Docker's output). diff --git a/RELEASE_PROCESS.md b/RELEASE_PROCESS.md index d6414de73..e893d9e2e 100644 --- a/RELEASE_PROCESS.md +++ b/RELEASE_PROCESS.md @@ -166,6 +166,8 @@ spelled with complete minutiae. `git log -1 $(git tag | sort -V | tail -1)`. 1. Edit `version/version.go` and bump the `Version` value to the new release version. If there were API changes, also bump `APIVersion` value. + Make sure to also bump the version in the swagger.yaml `pkg/api/server/docs.go` + and to add a new entry in `docs/source/Reference.rst` for major and minor releases. 1. Commit this and sign the commit (`git commit -a -s -S`). The commit message should be `Bump to vX.Y.Z` (using the actual version numbers). 1. Push this single change to your github fork, and make a new PR, diff --git a/cmd/podman/pods/create.go b/cmd/podman/pods/create.go index 4b32e7bb7..1cd36008e 100644 --- a/cmd/podman/pods/create.go +++ b/cmd/podman/pods/create.go @@ -17,6 +17,7 @@ import ( "github.com/containers/podman/v4/cmd/podman/parse" "github.com/containers/podman/v4/cmd/podman/registry" "github.com/containers/podman/v4/cmd/podman/validate" + "github.com/containers/podman/v4/libpod/define" "github.com/containers/podman/v4/pkg/domain/entities" "github.com/containers/podman/v4/pkg/errorhandling" "github.com/containers/podman/v4/pkg/specgen" @@ -52,6 +53,7 @@ var ( podIDFile string replace bool share string + shareParent bool ) func init() { @@ -88,6 +90,9 @@ func init() { flags.StringVar(&share, shareFlagName, specgen.DefaultKernelNamespaces, "A comma delimited list of kernel namespaces the pod will share") _ = createCommand.RegisterFlagCompletionFunc(shareFlagName, common.AutocompletePodShareNamespace) + shareParentFlagName := "share-parent" + flags.BoolVar(&shareParent, shareParentFlagName, true, "Set the pod's cgroup as the cgroup parent for all containers joining the pod") + flags.SetNormalizeFunc(aliasNetworkFlag) } @@ -147,7 +152,11 @@ func create(cmd *cobra.Command, args []string) error { if err != nil { return err } + if strings.Contains(share, "cgroup") && shareParent { + return errors.Wrapf(define.ErrInvalidArg, "cannot define the pod as the cgroup parent at the same time as joining the infra container's cgroupNS") + } createOptions.Share = strings.Split(share, ",") + createOptions.ShareParent = &shareParent if cmd.Flag("infra-command").Changed { // Only send content to server side if user changed defaults cmdIn, err := cmd.Flags().GetString("infra-command") diff --git a/cmd/podman/registry/config.go b/cmd/podman/registry/config.go index 15bb7aee0..b5c9b359c 100644 --- a/cmd/podman/registry/config.go +++ b/cmd/podman/registry/config.go @@ -81,11 +81,6 @@ func newPodmanConfig() { mode = entities.TunnelMode } - cfg.Network.NetworkConfigDir = cfg.Network.CNIPluginDirs[0] - if rootless.IsRootless() { - cfg.Network.NetworkConfigDir = "" - } - podmanOptions = entities.PodmanConfig{Config: cfg, EngineMode: mode} } diff --git a/docs/source/Reference.rst b/docs/source/Reference.rst index d194c55a3..dca49a559 100644 --- a/docs/source/Reference.rst +++ b/docs/source/Reference.rst @@ -3,8 +3,16 @@ Reference ========= -To see full screen version please visit: `API documentation <https://docs.podman.io/en/latest/_static/api.html>`_ +Show the API documentation for version: -.. raw:: html +* `latest (main branch) <_static/api.html>`_ - <iframe src="_static/api.html" allowfullscreen="true" height="600px" width="120%"></iframe> +* `version 4.0 <_static/api.html?version=v4.0>`_ + +* `version 3.4 <_static/api.html?version=v3.4>`_ + +* `version 3.3 <_static/api.html?version=v3.3>`_ + +* `version 3.2 <_static/api.html?version=v3.2>`_ + +* `version 3.1 <_static/api.html?version=v3.1>`_ diff --git a/docs/source/_static/api.html b/docs/source/_static/api.html index 6d467d099..0d2d2a8a1 100644 --- a/docs/source/_static/api.html +++ b/docs/source/_static/api.html @@ -18,7 +18,22 @@ </style> </head> <body> - <redoc spec-url='https://storage.googleapis.com/libpod-master-releases/swagger-latest.yaml' sort-props-alphabetically sort-operations-alphabetically></redoc> + <script> + // get version from query (default to latest) + var queryString = window.location.search; + var query = new URLSearchParams(queryString); + var version = "latest"; + if (query.has("version")) { + version = query.get("version"); + } + + var redoc = document.createElement("redoc"); + redoc.setAttribute("sort-props-alphabetically",""); + redoc.setAttribute("sort-operations-alphabetically",""); + redoc.setAttribute("spec-url","https://storage.googleapis.com/libpod-master-releases/swagger-" + version + ".yaml"); + + document.body.appendChild(redoc); + </script> <script src="https://cdn.jsdelivr.net/npm/redoc@next/bundles/redoc.standalone.js"> </script> </body> </html> diff --git a/docs/source/markdown/podman-pod-create.1.md b/docs/source/markdown/podman-pod-create.1.md index 58d3b9d44..8088e1d62 100644 --- a/docs/source/markdown/podman-pod-create.1.md +++ b/docs/source/markdown/podman-pod-create.1.md @@ -265,7 +265,7 @@ Note: Labeling can be disabled for all containers by setting label=false in the #### **--share**=*namespace* -A comma-separated list of kernel namespaces to share. If none or "" is specified, no namespaces will be shared. The namespaces to choose from are ipc, net, pid, uts. +A comma-separated list of kernel namespaces to share. If none or "" is specified, no namespaces will be shared. The namespaces to choose from are cgroup, ipc, net, pid, uts. The operator can identify a pod in three ways: UUID long identifier (“f78375b1c487e03c9438c729345e54db9d20cfa2ac1fc3494b6eb60872e74778”) @@ -276,6 +276,12 @@ podman generates a UUID for each pod, and if a name is not assigned to the container with **--name** then a random string name will be generated for it. The name is useful any place you need to identify a pod. +#### **--share-parent** + +This boolean determines whether or not all containers entering the pod will use the pod as their cgroup parent. The default value of this flag is true. If you are looking to share the cgroup namespace rather than a cgroup parent in a pod, use **--share** + +Note: This options conflict with **--share=cgroup** since that would set the pod as the cgroup parent but enter the container into the same cgroupNS as the infra container. + #### **--sysctl**=_name_=_value_ Configure namespace kernel parameters for all containers in the pod. @@ -5,7 +5,7 @@ go 1.16 require ( github.com/BurntSushi/toml v1.0.0 github.com/blang/semver v3.5.1+incompatible - github.com/buger/goterm v1.0.1 + github.com/buger/goterm v1.0.4 github.com/checkpoint-restore/checkpointctl v0.0.0-20211204171957-54b4ebfdb681 github.com/checkpoint-restore/go-criu/v5 v5.3.0 github.com/container-orchestrated-devices/container-device-interface v0.0.0-20220111162300-46367ec063fd @@ -175,8 +175,8 @@ github.com/bmizerany/assert v0.0.0-20160611221934-b7ed37b82869/go.mod h1:Ekp36dR github.com/bombsimon/wsl/v3 v3.3.0/go.mod h1:st10JtZYLE4D5sC7b8xV4zTKZwAQjCH/Hy2Pm1FNZIc= github.com/breml/bidichk v0.1.1/go.mod h1:zbfeitpevDUGI7V91Uzzuwrn4Vls8MoBMrwtt78jmso= github.com/bshuster-repo/logrus-logstash-hook v0.4.1/go.mod h1:zsTqEiSzDgAa/8GZR7E1qaXrhYNDKBYy5/dWPTIflbk= -github.com/buger/goterm v1.0.1 h1:kSgw3jcjYUzC0Uh/eG8ULjccuz353solup27lUH8Zug= -github.com/buger/goterm v1.0.1/go.mod h1:HiFWV3xnkolgrBV3mY8m0X0Pumt4zg4QhbdOzQtB8tE= +github.com/buger/goterm v1.0.4 h1:Z9YvGmOih81P0FbVtEYTFF6YsSgxSUKEhf/f9bTMXbY= +github.com/buger/goterm v1.0.4/go.mod h1:HiFWV3xnkolgrBV3mY8m0X0Pumt4zg4QhbdOzQtB8tE= github.com/buger/jsonparser v0.0.0-20180808090653-f4dd9f5a6b44/go.mod h1:bbYlZJ7hK1yFx9hf58LP0zeX7UjIGs20ufpu3evjr+s= github.com/buger/jsonparser v1.1.1/go.mod h1:6RYKKt7H4d4+iWqouImQ9R2FZql3VbhNgx27UK13J/0= github.com/bugsnag/bugsnag-go v0.0.0-20141110184014-b1d153021fcd/go.mod h1:2oa8nejYd4cQ/b0hMIopN0lCRxU0bueqREvZLWFrtK8= diff --git a/libpod/container_internal_linux.go b/libpod/container_internal_linux.go index 86d8586d0..afa351c17 100644 --- a/libpod/container_internal_linux.go +++ b/libpod/container_internal_linux.go @@ -2045,19 +2045,8 @@ func (c *Container) generateResolvConf() (string, error) { } } - ipv6 := false - // If network status is set check for ipv6 and dns namesevers netStatus := c.getNetworkStatus() for _, status := range netStatus { - for _, netInt := range status.Interfaces { - for _, netAddress := range netInt.Subnets { - // Note: only using To16() does not work since it also returns a valid ip for ipv4 - if netAddress.IPNet.IP.To4() == nil && netAddress.IPNet.IP.To16() != nil { - ipv6 = true - } - } - } - if status.DNSServerIPs != nil { for _, nsIP := range status.DNSServerIPs { networkNameServers = append(networkNameServers, nsIP.String()) @@ -2070,16 +2059,9 @@ func (c *Container) generateResolvConf() (string, error) { } } - if c.config.NetMode.IsSlirp4netns() { - ctrNetworkSlipOpts := []string{} - if c.config.NetworkOptions != nil { - ctrNetworkSlipOpts = append(ctrNetworkSlipOpts, c.config.NetworkOptions["slirp4netns"]...) - } - slirpOpts, err := parseSlirp4netnsNetworkOptions(c.runtime, ctrNetworkSlipOpts) - if err != nil { - return "", err - } - ipv6 = slirpOpts.enableIPv6 + ipv6, err := c.checkForIPv6(netStatus) + if err != nil { + return "", err } // Ensure that the container's /etc/resolv.conf is compatible with its @@ -2099,38 +2081,38 @@ func (c *Container) generateResolvConf() (string, error) { } dnsServers := append(dns, c.config.DNSServer...) // If the user provided dns, it trumps all; then dns masq; then resolv.conf + var search []string switch { case len(dnsServers) > 0: - // We store DNS servers as net.IP, so need to convert to string for _, server := range dnsServers { nameservers = append(nameservers, server.String()) } - case len(networkNameServers) > 0: - nameservers = append(nameservers, networkNameServers...) default: // Make a new resolv.conf - nameservers = resolvconf.GetNameservers(resolv.Content) - // slirp4netns has a built in DNS server. + // first add the nameservers from the networks status + nameservers = append(nameservers, networkNameServers...) + // when we add network dns server we also have to add the search domains + search = networkSearchDomains + // slirp4netns has a built in DNS forwarder. if c.config.NetMode.IsSlirp4netns() { slirp4netnsDNS, err := GetSlirp4netnsDNS(c.slirp4netnsSubnet) if err != nil { logrus.Warn("Failed to determine Slirp4netns DNS: ", err.Error()) } else { - nameservers = append([]string{slirp4netnsDNS.String()}, nameservers...) + nameservers = append(nameservers, slirp4netnsDNS.String()) } } + nameservers = append(nameservers, resolvconf.GetNameservers(resolv.Content)...) } - var search []string - if len(c.config.DNSSearch) > 0 || len(c.runtime.config.Containers.DNSSearches) > 0 || len(networkSearchDomains) > 0 { + if len(c.config.DNSSearch) > 0 || len(c.runtime.config.Containers.DNSSearches) > 0 { if !util.StringInSlice(".", c.config.DNSSearch) { - search = c.runtime.config.Containers.DNSSearches + search = append(search, c.runtime.config.Containers.DNSSearches...) search = append(search, c.config.DNSSearch...) - search = append(search, networkSearchDomains...) } } else { - search = resolvconf.GetSearchDomains(resolv.Content) + search = append(search, resolvconf.GetSearchDomains(resolv.Content)...) } var options []string @@ -2160,6 +2142,116 @@ func (c *Container) generateResolvConf() (string, error) { return destPath, nil } +// Check if a container uses IPv6. +func (c *Container) checkForIPv6(netStatus map[string]types.StatusBlock) (bool, error) { + for _, status := range netStatus { + for _, netInt := range status.Interfaces { + for _, netAddress := range netInt.Subnets { + // Note: only using To16() does not work since it also returns a valid ip for ipv4 + if netAddress.IPNet.IP.To4() == nil && netAddress.IPNet.IP.To16() != nil { + return true, nil + } + } + } + } + + if c.config.NetMode.IsSlirp4netns() { + ctrNetworkSlipOpts := []string{} + if c.config.NetworkOptions != nil { + ctrNetworkSlipOpts = append(ctrNetworkSlipOpts, c.config.NetworkOptions["slirp4netns"]...) + } + slirpOpts, err := parseSlirp4netnsNetworkOptions(c.runtime, ctrNetworkSlipOpts) + if err != nil { + return false, err + } + return slirpOpts.enableIPv6, nil + } + + return false, nil +} + +// Add a new nameserver to the container's resolv.conf, ensuring that it is the +// first nameserver present. +// Usable only with running containers. +func (c *Container) addNameserver(ips []string) error { + // Take no action if container is not running. + if !c.ensureState(define.ContainerStateRunning, define.ContainerStateCreated) { + return nil + } + + // Do we have a resolv.conf at all? + path, ok := c.state.BindMounts["/etc/resolv.conf"] + if !ok { + return nil + } + + // Read in full contents, parse out existing nameservers + contents, err := ioutil.ReadFile(path) + if err != nil { + return err + } + ns := resolvconf.GetNameservers(contents) + options := resolvconf.GetOptions(contents) + search := resolvconf.GetSearchDomains(contents) + + // We could verify that it doesn't already exist + // but extra nameservers shouldn't harm anything. + // Ensure we are the first entry in resolv.conf though, otherwise we + // might be after user-added servers. + ns = append(ips, ns...) + + // We're rewriting the container's resolv.conf as part of this, but we + // hold the container lock, so there should be no risk of parallel + // modification. + if _, err := resolvconf.Build(path, ns, search, options); err != nil { + return errors.Wrapf(err, "error adding new nameserver to container %s resolv.conf", c.ID()) + } + + return nil +} + +// Remove an entry from the existing resolv.conf of the container. +// Usable only with running containers. +func (c *Container) removeNameserver(ips []string) error { + // Take no action if container is not running. + if !c.ensureState(define.ContainerStateRunning, define.ContainerStateCreated) { + return nil + } + + // Do we have a resolv.conf at all? + path, ok := c.state.BindMounts["/etc/resolv.conf"] + if !ok { + return nil + } + + // Read in full contents, parse out existing nameservers + contents, err := ioutil.ReadFile(path) + if err != nil { + return err + } + ns := resolvconf.GetNameservers(contents) + options := resolvconf.GetOptions(contents) + search := resolvconf.GetSearchDomains(contents) + + toRemove := make(map[string]bool) + for _, ip := range ips { + toRemove[ip] = true + } + + newNS := make([]string, 0, len(ns)) + for _, server := range ns { + if !toRemove[server] { + newNS = append(newNS, server) + } + } + + if _, err := resolvconf.Build(path, newNS, search, options); err != nil { + return errors.Wrapf(err, "error removing nameservers from container %s resolv.conf", c.ID()) + } + + return nil +} + // updateHosts updates the container's hosts file func (c *Container) updateHosts(path string) error { var hosts string diff --git a/libpod/container_log_linux.go b/libpod/container_log_linux.go index 6b7cb4aa6..6150973ca 100644 --- a/libpod/container_log_linux.go +++ b/libpod/container_log_linux.go @@ -226,7 +226,7 @@ func (c *Container) readFromJournal(ctx context.Context, options *logs.LogOption } if formatError != nil { - logrus.Errorf("Failed to parse journald log entry: %v", err) + logrus.Errorf("Failed to parse journald log entry: %v", formatError) return } diff --git a/libpod/healthcheck.go b/libpod/healthcheck.go index 53bad47b4..40af9aec3 100644 --- a/libpod/healthcheck.go +++ b/libpod/healthcheck.go @@ -2,7 +2,6 @@ package libpod import ( "bufio" - "bytes" "io/ioutil" "os" "path/filepath" @@ -22,16 +21,6 @@ const ( MaxHealthCheckLogLength = 500 ) -// hcWriteCloser allows us to use bufio as a WriteCloser -type hcWriteCloser struct { - *bufio.Writer -} - -// Used to add a closer to bufio -func (hcwc hcWriteCloser) Close() error { - return nil -} - // HealthCheck verifies the state and validity of the healthcheck configuration // on the container and then executes the healthcheck func (r *Runtime) HealthCheck(name string) (define.HealthCheckStatus, error) { @@ -51,7 +40,6 @@ func (c *Container) runHealthCheck() (define.HealthCheckStatus, error) { var ( newCommand []string returnCode int - capture bytes.Buffer inStartPeriod bool ) hcCommand := c.HealthCheckConfig().Test @@ -73,20 +61,30 @@ func (c *Container) runHealthCheck() (define.HealthCheckStatus, error) { if len(newCommand) < 1 || newCommand[0] == "" { return define.HealthCheckNotDefined, errors.Errorf("container %s has no defined healthcheck", c.ID()) } - captureBuffer := bufio.NewWriter(&capture) - hcw := hcWriteCloser{ - captureBuffer, + rPipe, wPipe, err := os.Pipe() + if err != nil { + return define.HealthCheckInternalError, errors.Wrapf(err, "unable to create pipe for healthcheck session") } + defer wPipe.Close() + defer rPipe.Close() + streams := new(define.AttachStreams) - streams.OutputStream = hcw - streams.ErrorStream = hcw streams.InputStream = bufio.NewReader(os.Stdin) - + streams.OutputStream = wPipe + streams.ErrorStream = wPipe streams.AttachOutput = true streams.AttachError = true streams.AttachInput = true + stdout := []string{} + go func() { + scanner := bufio.NewScanner(rPipe) + for scanner.Scan() { + stdout = append(stdout, scanner.Text()) + } + }() + logrus.Debugf("executing health check command %s for %s", strings.Join(newCommand, " "), c.ID()) timeStart := time.Now() hcResult := define.HealthCheckSuccess @@ -119,7 +117,7 @@ func (c *Container) runHealthCheck() (define.HealthCheckStatus, error) { } } - eventLog := capture.String() + eventLog := strings.Join(stdout, "\n") if len(eventLog) > MaxHealthCheckLogLength { eventLog = eventLog[:MaxHealthCheckLogLength] } diff --git a/libpod/networking_linux.go b/libpod/networking_linux.go index f490ac626..19d5c7f76 100644 --- a/libpod/networking_linux.go +++ b/libpod/networking_linux.go @@ -28,6 +28,7 @@ import ( "github.com/containers/podman/v4/pkg/resolvconf" "github.com/containers/podman/v4/pkg/rootless" "github.com/containers/podman/v4/pkg/util" + "github.com/containers/podman/v4/utils" "github.com/containers/storage/pkg/lockfile" "github.com/opencontainers/selinux/go-selinux/label" "github.com/pkg/errors" @@ -495,6 +496,12 @@ func (r *Runtime) GetRootlessNetNs(new bool) (*RootlessNetNS, error) { return nil, err } + // move to systemd scope to prevent systemd from killing it + err = utils.MoveRootlessNetnsSlirpProcessToUserSlice(cmd.Process.Pid) + if err != nil { + logrus.Errorf("failed to move the rootless netns slirp4netns process to the systemd user.slice: %v", err) + } + // build a new resolv.conf file which uses the slirp4netns dns server address resolveIP, err := GetSlirp4netnsDNS(nil) if err != nil { @@ -1163,6 +1170,7 @@ func (c *Container) NetworkDisconnect(nameOrID, netName string, force bool) erro } // update network status if container is running + oldStatus, statusExist := networkStatus[netName] delete(networkStatus, netName) c.state.NetworkStatus = networkStatus err = c.save() @@ -1173,8 +1181,26 @@ func (c *Container) NetworkDisconnect(nameOrID, netName string, force bool) erro // Reload ports when there are still connected networks, maybe we removed the network interface with the child ip. // Reloading without connected networks does not make sense, so we can skip this step. if rootless.IsRootless() && len(networkStatus) > 0 { - return c.reloadRootlessRLKPortMapping() + if err := c.reloadRootlessRLKPortMapping(); err != nil { + return err + } + } + + // Update resolv.conf if required + if statusExist { + stringIPs := make([]string, 0, len(oldStatus.DNSServerIPs)) + for _, ip := range oldStatus.DNSServerIPs { + stringIPs = append(stringIPs, ip.String()) + } + if len(stringIPs) == 0 { + return nil + } + logrus.Debugf("Removing DNS Servers %v from resolv.conf", stringIPs) + if err := c.removeNameserver(stringIPs); err != nil { + return err + } } + return nil } @@ -1256,11 +1282,36 @@ func (c *Container) NetworkConnect(nameOrID, netName string, netOpts types.PerNe if err != nil { return err } + // The first network needs a port reload to set the correct child ip for the rootlessport process. // Adding a second network does not require a port reload because the child ip is still valid. if rootless.IsRootless() && len(networks) == 0 { - return c.reloadRootlessRLKPortMapping() + if err := c.reloadRootlessRLKPortMapping(); err != nil { + return err + } } + + ipv6, err := c.checkForIPv6(networkStatus) + if err != nil { + return err + } + + // Update resolv.conf if required + stringIPs := make([]string, 0, len(results[netName].DNSServerIPs)) + for _, ip := range results[netName].DNSServerIPs { + if (ip.To4() == nil) && !ipv6 { + continue + } + stringIPs = append(stringIPs, ip.String()) + } + if len(stringIPs) == 0 { + return nil + } + logrus.Debugf("Adding DNS Servers %v to resolv.conf", stringIPs) + if err := c.addNameserver(stringIPs); err != nil { + return err + } + return nil } diff --git a/libpod/oci_conmon_exec_linux.go b/libpod/oci_conmon_exec_linux.go index 04deaac83..aa970bbde 100644 --- a/libpod/oci_conmon_exec_linux.go +++ b/libpod/oci_conmon_exec_linux.go @@ -257,7 +257,7 @@ func (r *ConmonOCIRuntime) ExecStopContainer(ctr *Container, sessionID string, t } // Wait for the PID to stop - if err := waitPidStop(pid, killContainerTimeout*time.Second); err != nil { + if err := waitPidStop(pid, killContainerTimeout); err != nil { return errors.Wrapf(err, "timed out waiting for container %s exec session %s PID %d to stop after SIGKILL", ctr.ID(), sessionID, pid) } diff --git a/libpod/options.go b/libpod/options.go index 4f9e49d0f..e0502a72d 100644 --- a/libpod/options.go +++ b/libpod/options.go @@ -1865,7 +1865,7 @@ func WithPodCgroupParent(path string) PodCreateOption { // this pod. // This can still be overridden at the container level by explicitly specifying // a Cgroup parent. -func WithPodCgroups() PodCreateOption { +func WithPodParent() PodCreateOption { return func(pod *Pod) error { if pod.valid { return define.ErrPodFinalized diff --git a/pkg/api/handlers/compat/images_build.go b/pkg/api/handlers/compat/images_build.go index cc9667202..0ebf74f98 100644 --- a/pkg/api/handlers/compat/images_build.go +++ b/pkg/api/handlers/compat/images_build.go @@ -605,8 +605,8 @@ func BuildImage(w http.ResponseWriter, r *http.Request) { } // Send headers and prime client for stream to come - w.WriteHeader(http.StatusOK) w.Header().Set("Content-Type", "application/json") + w.WriteHeader(http.StatusOK) flush() body := w.(io.Writer) diff --git a/pkg/api/handlers/libpod/pods.go b/pkg/api/handlers/libpod/pods.go index afbdf0e5f..d522631b7 100644 --- a/pkg/api/handlers/libpod/pods.go +++ b/pkg/api/handlers/libpod/pods.go @@ -45,6 +45,10 @@ func PodCreate(w http.ResponseWriter, r *http.Request) { infraOptions.Net = &entities.NetOptions{} infraOptions.Devices = psg.Devices infraOptions.SecurityOpt = psg.SecurityOpt + if psg.ShareParent == nil { + t := true + psg.ShareParent = &t + } err = specgenutil.FillOutSpecGen(psg.InfraContainerSpec, &infraOptions, []string{}) // necessary for default values in many cases (userns, idmappings) if err != nil { utils.Error(w, http.StatusInternalServerError, errors.Wrap(err, "error filling out specgen")) diff --git a/pkg/api/server/register_images.go b/pkg/api/server/register_images.go index f0b52dfc6..d7bc17093 100644 --- a/pkg/api/server/register_images.go +++ b/pkg/api/server/register_images.go @@ -61,7 +61,10 @@ func (s *APIServer) registerImagesHandlers(r *mux.Router) error { // description: Image content if fromSrc parameter was used // responses: // 200: - // $ref: "#/responses/ok" + // description: "no error" + // schema: + // type: "string" + // format: "binary" // 404: // $ref: "#/responses/NoSuchImage" // 500: diff --git a/pkg/domain/entities/pods.go b/pkg/domain/entities/pods.go index aeccc82b4..7922db4e6 100644 --- a/pkg/domain/entities/pods.go +++ b/pkg/domain/entities/pods.go @@ -132,6 +132,7 @@ type PodCreateOptions struct { Name string `json:"name,omitempty"` Net *NetOptions `json:"net,omitempty"` Share []string `json:"share,omitempty"` + ShareParent *bool `json:"share_parent,omitempty"` Pid string `json:"pid,omitempty"` Cpus float64 `json:"cpus,omitempty"` CpusetCpus string `json:"cpuset_cpus,omitempty"` @@ -324,6 +325,7 @@ func ToPodSpecGen(s specgen.PodSpecGenerator, p *PodCreateOptions) (*specgen.Pod } s.InfraImage = p.InfraImage s.SharedNamespaces = p.Share + s.ShareParent = p.ShareParent s.PodCreateCommand = p.CreateCommand s.VolumesFrom = p.VolumesFrom diff --git a/pkg/specgen/generate/namespaces.go b/pkg/specgen/generate/namespaces.go index 93d9caf4c..3f77cbe76 100644 --- a/pkg/specgen/generate/namespaces.go +++ b/pkg/specgen/generate/namespaces.go @@ -482,7 +482,7 @@ func GetNamespaceOptions(ns []string, netnsIsHost bool) ([]libpod.PodCreateOptio for _, toShare := range ns { switch toShare { case "cgroup": - options = append(options, libpod.WithPodCgroups()) + options = append(options, libpod.WithPodCgroup()) case "net": // share the netns setting with other containers in the pod only when it is not set to host if !netnsIsHost { diff --git a/pkg/specgen/generate/pod_create.go b/pkg/specgen/generate/pod_create.go index 03829e8cf..68fda3ad7 100644 --- a/pkg/specgen/generate/pod_create.go +++ b/pkg/specgen/generate/pod_create.go @@ -166,6 +166,9 @@ func createPodOptions(p *specgen.PodSpecGenerator, rt *libpod.Runtime, infraSpec ) if !p.NoInfra { //&& infraSpec != nil { options = append(options, libpod.WithInfraContainer()) + if p.ShareParent == nil || (p.ShareParent != nil && *p.ShareParent) { + options = append(options, libpod.WithPodParent()) + } nsOptions, err := GetNamespaceOptions(p.SharedNamespaces, p.InfraContainerSpec.NetNS.IsHost()) if err != nil { return nil, err diff --git a/pkg/specgen/namespaces.go b/pkg/specgen/namespaces.go index f61937078..e672bc65f 100644 --- a/pkg/specgen/namespaces.go +++ b/pkg/specgen/namespaces.go @@ -57,7 +57,7 @@ const ( // DefaultKernelNamespaces is a comma-separated list of default kernel // namespaces. - DefaultKernelNamespaces = "cgroup,ipc,net,uts" + DefaultKernelNamespaces = "ipc,net,uts" ) // Namespace describes the namespace diff --git a/pkg/specgen/podspecgen.go b/pkg/specgen/podspecgen.go index 91b2599cc..759caa0c0 100644 --- a/pkg/specgen/podspecgen.go +++ b/pkg/specgen/podspecgen.go @@ -63,6 +63,8 @@ type PodBasicConfig struct { // also be used by some tools that wish to recreate the pod // (e.g. `podman generate systemd --new`). // Optional. + // ShareParent determines if all containers in the pod will share the pod's cgroup as the cgroup parent + ShareParent *bool `json:"share_parent,omitempty"` PodCreateCommand []string `json:"pod_create_command,omitempty"` // Pid sets the process id namespace of the pod // Optional (defaults to private if unset). This sets the PID namespace of the infra container diff --git a/test/apiv2/01-basic.at b/test/apiv2/01-basic.at index 2747ccbd4..06db62785 100644 --- a/test/apiv2/01-basic.at +++ b/test/apiv2/01-basic.at @@ -19,7 +19,7 @@ for i in /version version; do t GET $i 200 \ .Components[0].Name="Podman Engine" \ .Components[0].Details.APIVersion~4[0-9.-]\\+ \ - .Components[0].Details.MinAPIVersion=3.1.0 \ + .Components[0].Details.MinAPIVersion=3.3.1 \ .Components[0].Details.Os=linux \ .ApiVersion=1.40 \ .MinAPIVersion=1.24 \ diff --git a/test/apiv2/10-images.at b/test/apiv2/10-images.at index 673858a3c..9526183e3 100644 --- a/test/apiv2/10-images.at +++ b/test/apiv2/10-images.at @@ -190,6 +190,11 @@ t POST "libpod/build?dockerfile=containerfile" $CONTAINERFILE_TAR application/js t POST "build?dockerfile=containerfile" $CONTAINERFILE_TAR application/json 200 \ .stream~"STEP 1/1: FROM $IMAGE" +# Build api response header must contain Content-type: application/json +t POST "build?dockerfile=containerfile" $CONTAINERFILE_TAR application/json 200 +response_headers=$(cat "$WORKDIR/curl.headers.out") +like "$response_headers" ".*application/json.*" "header does not contains application/json" + # PR #12091: output from compat API must now include {"aux":{"ID":"sha..."}} t POST "build?dockerfile=containerfile" $CONTAINERFILE_TAR 200 \ '.aux|select(has("ID")).ID~^sha256:[0-9a-f]\{64\}$' diff --git a/test/e2e/healthcheck_run_test.go b/test/e2e/healthcheck_run_test.go index c84488145..866edbf0e 100644 --- a/test/e2e/healthcheck_run_test.go +++ b/test/e2e/healthcheck_run_test.go @@ -54,6 +54,28 @@ var _ = Describe("Podman healthcheck run", func() { Expect(hc).Should(Exit(125)) }) + It("podman run healthcheck and logs should contain healthcheck output", func() { + session := podmanTest.Podman([]string{"run", "--name", "test-logs", "-dt", "--health-interval", "1s", "--health-cmd", "echo working", "busybox", "sleep", "3600"}) + session.WaitWithDefaultTimeout() + Expect(session).Should(Exit(0)) + + // Buy a little time to get container running + for i := 0; i < 5; i++ { + hc := podmanTest.Podman([]string{"healthcheck", "run", "test-logs"}) + hc.WaitWithDefaultTimeout() + exitCode := hc.ExitCode() + if exitCode == 0 || i == 4 { + break + } + time.Sleep(1 * time.Second) + } + + hc := podmanTest.Podman([]string{"container", "inspect", "--format", "{{.State.Healthcheck.Log}}", "test-logs"}) + hc.WaitWithDefaultTimeout() + Expect(hc).Should(Exit(0)) + Expect(hc.OutputToString()).To(ContainSubstring("working")) + }) + It("podman healthcheck from image's config (not container config)", func() { // Regression test for #12226: a health check may be defined in // the container or the container-config of an image. diff --git a/test/e2e/network_connect_disconnect_test.go b/test/e2e/network_connect_disconnect_test.go index 82b9dcd09..b200aa5d3 100644 --- a/test/e2e/network_connect_disconnect_test.go +++ b/test/e2e/network_connect_disconnect_test.go @@ -2,6 +2,7 @@ package integration import ( "os" + "strings" . "github.com/containers/podman/v4/test/utils" "github.com/containers/storage/pkg/stringid" @@ -77,6 +78,11 @@ var _ = Describe("Podman network connect and disconnect", func() { Expect(session).Should(Exit(0)) defer podmanTest.removeCNINetwork(netName) + gw := podmanTest.Podman([]string{"network", "inspect", netName, "--format", "{{(index .Subnets 0).Gateway}}"}) + gw.WaitWithDefaultTimeout() + Expect(gw).Should(Exit(0)) + ns := gw.OutputToString() + ctr := podmanTest.Podman([]string{"run", "-dt", "--name", "test", "--network", netName, ALPINE, "top"}) ctr.WaitWithDefaultTimeout() Expect(ctr).Should(Exit(0)) @@ -85,6 +91,11 @@ var _ = Describe("Podman network connect and disconnect", func() { exec.WaitWithDefaultTimeout() Expect(exec).Should(Exit(0)) + exec2 := podmanTest.Podman([]string{"exec", "-it", "test", "cat", "/etc/resolv.conf"}) + exec2.WaitWithDefaultTimeout() + Expect(exec2).Should(Exit(0)) + Expect(strings.Contains(exec2.OutputToString(), ns)).To(BeTrue()) + dis := podmanTest.Podman([]string{"network", "disconnect", netName, "test"}) dis.WaitWithDefaultTimeout() Expect(dis).Should(Exit(0)) @@ -98,6 +109,11 @@ var _ = Describe("Podman network connect and disconnect", func() { exec = podmanTest.Podman([]string{"exec", "-it", "test", "ip", "addr", "show", "eth0"}) exec.WaitWithDefaultTimeout() Expect(exec).Should(ExitWithError()) + + exec3 := podmanTest.Podman([]string{"exec", "-it", "test", "cat", "/etc/resolv.conf"}) + exec3.WaitWithDefaultTimeout() + Expect(exec3).Should(Exit(0)) + Expect(strings.Contains(exec3.OutputToString(), ns)).To(BeFalse()) }) It("bad network name in connect should result in error", func() { @@ -182,6 +198,16 @@ var _ = Describe("Podman network connect and disconnect", func() { Expect(session).Should(Exit(0)) defer podmanTest.removeCNINetwork(newNetName) + gw := podmanTest.Podman([]string{"network", "inspect", newNetName, "--format", "{{(index .Subnets 0).Gateway}}"}) + gw.WaitWithDefaultTimeout() + Expect(gw).Should(Exit(0)) + ns := gw.OutputToString() + + exec2 := podmanTest.Podman([]string{"exec", "-it", "test", "cat", "/etc/resolv.conf"}) + exec2.WaitWithDefaultTimeout() + Expect(exec2).Should(Exit(0)) + Expect(strings.Contains(exec2.OutputToString(), ns)).To(BeFalse()) + ip := "10.11.100.99" mac := "44:11:44:11:44:11" connect := podmanTest.Podman([]string{"network", "connect", "--ip", ip, "--mac-address", mac, newNetName, "test"}) @@ -206,6 +232,11 @@ var _ = Describe("Podman network connect and disconnect", func() { Expect(exec.OutputToString()).Should(ContainSubstring(ip)) Expect(exec.OutputToString()).Should(ContainSubstring(mac)) + exec3 := podmanTest.Podman([]string{"exec", "-it", "test", "cat", "/etc/resolv.conf"}) + exec3.WaitWithDefaultTimeout() + Expect(exec3).Should(Exit(0)) + Expect(strings.Contains(exec3.OutputToString(), ns)).To(BeTrue()) + // make sure no logrus errors are shown https://github.com/containers/podman/issues/9602 rm := podmanTest.Podman([]string{"rm", "--time=0", "-f", "test"}) rm.WaitWithDefaultTimeout() diff --git a/test/e2e/pod_create_test.go b/test/e2e/pod_create_test.go index f0abfd80c..cd7f72ac0 100644 --- a/test/e2e/pod_create_test.go +++ b/test/e2e/pod_create_test.go @@ -1068,4 +1068,47 @@ ENTRYPOINT ["sleep","99999"] }) + It("podman pod create --share-parent test", func() { + SkipIfRootlessCgroupsV1("rootless cannot use cgroups with cgroupsv1") + podCreate := podmanTest.Podman([]string{"pod", "create", "--share-parent=false"}) + podCreate.WaitWithDefaultTimeout() + Expect(podCreate).Should(Exit(0)) + + ctrCreate := podmanTest.Podman([]string{"run", "-dt", "--pod", podCreate.OutputToString(), ALPINE}) + ctrCreate.WaitWithDefaultTimeout() + Expect(ctrCreate).Should(Exit(0)) + + inspectPod := podmanTest.Podman([]string{"pod", "inspect", podCreate.OutputToString()}) + inspectPod.WaitWithDefaultTimeout() + Expect(inspectPod).Should(Exit(0)) + data := inspectPod.InspectPodToJSON() + + inspect := podmanTest.InspectContainer(ctrCreate.OutputToString()) + Expect(data.CgroupPath).To(HaveLen(0)) + if podmanTest.CgroupManager == "cgroupfs" || !rootless.IsRootless() { + Expect(inspect[0].HostConfig.CgroupParent).To(HaveLen(0)) + } else if podmanTest.CgroupManager == "systemd" { + Expect(inspect[0].HostConfig.CgroupParent).To(Equal("user.slice")) + } + + podCreate2 := podmanTest.Podman([]string{"pod", "create", "--share", "cgroup,ipc,net,uts", "--share-parent=false", "--infra-name", "cgroupCtr"}) + podCreate2.WaitWithDefaultTimeout() + Expect(podCreate2).Should(Exit(0)) + + ctrCreate2 := podmanTest.Podman([]string{"run", "-dt", "--pod", podCreate2.OutputToString(), ALPINE}) + ctrCreate2.WaitWithDefaultTimeout() + Expect(ctrCreate2).Should(Exit(0)) + + inspectInfra := podmanTest.InspectContainer("cgroupCtr") + + inspect2 := podmanTest.InspectContainer(ctrCreate2.OutputToString()) + + Expect(inspect2[0].HostConfig.CgroupMode).To(ContainSubstring(inspectInfra[0].ID)) + + podCreate3 := podmanTest.Podman([]string{"pod", "create", "--share", "cgroup"}) + podCreate3.WaitWithDefaultTimeout() + Expect(podCreate3).ShouldNot(Exit(0)) + + }) + }) diff --git a/test/system/200-pod.bats b/test/system/200-pod.bats index bccd04e8d..34dfaa8f6 100644 --- a/test/system/200-pod.bats +++ b/test/system/200-pod.bats @@ -340,7 +340,7 @@ EOF run_podman 125 pod create --share bogus --name $pod_name is "$output" ".*Invalid kernel namespace to share: bogus. Options are: cgroup, ipc, net, pid, uts or none" \ "pod test for bogus --share option" - run_podman pod create --share cgroup,ipc --name $pod_name + run_podman pod create --share ipc --name $pod_name run_podman run --rm --pod $pod_name --hostname foobar $IMAGE hostname is "$output" "foobar" "--hostname should work with non share UTS namespace" } diff --git a/test/system/220-healthcheck.bats b/test/system/220-healthcheck.bats index 1d4a2ea7e..c502ad669 100644 --- a/test/system/220-healthcheck.bats +++ b/test/system/220-healthcheck.bats @@ -15,10 +15,7 @@ function _check_health { run_podman inspect --format "{{json .State.Healthcheck}}" healthcheck_c parse_table "$tests" | while read field expect;do - # (kludge to deal with parse_table and empty strings) - if [ "$expect" = "''" ]; then expect=""; fi - - actual=$(jq -r ".$field" <<<"$output") + actual=$(jq ".$field" <<<"$output") is "$actual" "$expect" "$testname - .State.Healthcheck.$field" done } @@ -77,10 +74,10 @@ EOF is "$output" "" "output from 'podman healthcheck run'" _check_health "All healthy" " -Status | healthy +Status | \"healthy\" FailingStreak | 0 Log[-1].ExitCode | 0 -Log[-1].Output | +Log[-1].Output | \"Life is Good on stdout\\\nLife is Good on stderr\" " # Force a failure @@ -88,19 +85,19 @@ Log[-1].Output | sleep 2 _check_health "First failure" " -Status | healthy +Status | \"healthy\" FailingStreak | [123] Log[-1].ExitCode | 1 -Log[-1].Output | +Log[-1].Output | \"Uh-oh on stdout!\\\nUh-oh on stderr!\" " # After three successive failures, container should no longer be healthy sleep 5 _check_health "Three or more failures" " -Status | unhealthy +Status | \"unhealthy\" FailingStreak | [3456] Log[-1].ExitCode | 1 -Log[-1].Output | +Log[-1].Output | \"Uh-oh on stdout!\\\nUh-oh on stderr!\" " # healthcheck should now fail, with exit status 1 and 'unhealthy' output diff --git a/test/system/250-systemd.bats b/test/system/250-systemd.bats index c47679904..3847d9510 100644 --- a/test/system/250-systemd.bats +++ b/test/system/250-systemd.bats @@ -281,4 +281,34 @@ LISTEN_FDNAMES=listen_fdnames" | sort) is "$output" "" "output should be empty" } +# https://github.com/containers/podman/issues/13153 +@test "podman rootless-netns slirp4netns process should be in different cgroup" { + is_rootless || skip "only meaningful for rootless" + + cname=$(random_string) + local netname=testnet-$(random_string 10) + + # create network and container with network + run_podman network create $netname + run_podman create --name $cname --network $netname $IMAGE top + + # run container in systemd unit + service_setup + + # run second container with network + cname2=$(random_string) + run_podman run -d --name $cname2 --network $netname $IMAGE top + + # stop systemd container + service_cleanup + + # now check that the rootless netns slirp4netns process is still alive and working + run_podman unshare --rootless-netns ip addr + is "$output" ".*tap0.*" "slirp4netns interface exists in the netns" + run_podman exec $cname2 nslookup google.com + + run_podman rm -f -t0 $cname2 + run_podman network rm -f $netname +} + # vim: filetype=sh diff --git a/test/system/500-networking.bats b/test/system/500-networking.bats index 9f70c1c6c..b49f141dc 100644 --- a/test/system/500-networking.bats +++ b/test/system/500-networking.bats @@ -605,9 +605,27 @@ load helpers "8.8.8.8", ] EOF - CONTAINERS_CONF=$containersconf run_podman run --rm $IMAGE grep "example.com" /etc/resolv.conf - CONTAINERS_CONF=$containersconf run_podman run --rm $IMAGE grep $searchIP /etc/resolv.conf - is "$output" "nameserver $searchIP" "Should only be one $searchIP not multiple" + + local nl=" +" + + CONTAINERS_CONF=$containersconf run_podman run --rm $IMAGE cat /etc/resolv.conf + is "$output" "search example.com$nl.*" "correct seach domain" + is "$output" ".*nameserver 1.1.1.1${nl}nameserver $searchIP${nl}nameserver 1.0.0.1${nl}nameserver 8.8.8.8" "nameserver order is correct" + + # create network with dns + local netname=testnet-$(random_string 10) + local subnet=$(random_rfc1918_subnet) + run_podman network create --subnet "$subnet.0/24" $netname + # custom server overwrites the network dns server + CONTAINERS_CONF=$containersconf run_podman run --network $netname --rm $IMAGE cat /etc/resolv.conf + is "$output" "search example.com$nl.*" "correct seach domain" + is "$output" ".*nameserver 1.1.1.1${nl}nameserver $searchIP${nl}nameserver 1.0.0.1${nl}nameserver 8.8.8.8" "nameserver order is correct" + + # we should use the integrated dns server + run_podman run --network $netname --rm $IMAGE cat /etc/resolv.conf + is "$output" "search dns.podman.*" "correct seach domain" + is "$output" ".*nameserver $subnet.1.*" "integrated dns nameserver is set" } # vim: filetype=sh diff --git a/test/system/520-checkpoint.bats b/test/system/520-checkpoint.bats index fcb7fbb84..046dfd126 100644 --- a/test/system/520-checkpoint.bats +++ b/test/system/520-checkpoint.bats @@ -15,10 +15,6 @@ function setup() { skip "FIXME: checkpointing broken in Ubuntu 2004, 2104, 2110, ..." fi - if [[ "$(uname -r)" =~ "5.17" ]]; then - skip "FIXME: checkpointing broken on kernel 5.17 (#12949)" - fi - # None of these tests work rootless.... if is_rootless; then # ...however, is that a genuine cast-in-stone limitation, or one diff --git a/utils/utils.go b/utils/utils.go index 52586b937..22f0cb12f 100644 --- a/utils/utils.go +++ b/utils/utils.go @@ -174,7 +174,7 @@ func RunsOnSystemd() bool { return runsOnSystemd } -func moveProcessToScope(pidPath, slice, scope string) error { +func moveProcessPIDFileToScope(pidPath, slice, scope string) error { data, err := ioutil.ReadFile(pidPath) if err != nil { // do not raise an error if the file doesn't exist @@ -187,18 +187,32 @@ func moveProcessToScope(pidPath, slice, scope string) error { if err != nil { return errors.Wrapf(err, "cannot parse pid file %s", pidPath) } - err = RunUnderSystemdScope(int(pid), slice, scope) + return moveProcessToScope(int(pid), slice, scope) +} + +func moveProcessToScope(pid int, slice, scope string) error { + err := RunUnderSystemdScope(int(pid), slice, scope) // If the PID is not valid anymore, do not return an error. if dbusErr, ok := err.(dbus.Error); ok { if dbusErr.Name == "org.freedesktop.DBus.Error.UnixProcessIdUnknown" { return nil } } - return err } +// MoveRootlessNetnsSlirpProcessToUserSlice moves the slirp4netns process for the rootless netns +// into a different scope so that systemd does not kill it with a container. +func MoveRootlessNetnsSlirpProcessToUserSlice(pid int) error { + randBytes := make([]byte, 4) + _, err := rand.Read(randBytes) + if err != nil { + return err + } + return moveProcessToScope(pid, "user.slice", fmt.Sprintf("rootless-netns-%x.scope", randBytes)) +} + // MovePauseProcessToScope moves the pause process used for rootless mode to keep the namespaces alive to // a separate scope. func MovePauseProcessToScope(pausePidPath string) { @@ -211,7 +225,7 @@ func MovePauseProcessToScope(pausePidPath string) { logrus.Errorf("failed to read random bytes: %v", err) continue } - err = moveProcessToScope(pausePidPath, "user.slice", fmt.Sprintf("podman-pause-%x.scope", randBytes)) + err = moveProcessPIDFileToScope(pausePidPath, "user.slice", fmt.Sprintf("podman-pause-%x.scope", randBytes)) if err == nil { return } diff --git a/vendor/github.com/buger/goterm/.gitignore b/vendor/github.com/buger/goterm/.gitignore index 1377554eb..986544fb0 100644 --- a/vendor/github.com/buger/goterm/.gitignore +++ b/vendor/github.com/buger/goterm/.gitignore @@ -1 +1,2 @@ *.swp +.idea
\ No newline at end of file diff --git a/vendor/github.com/buger/goterm/terminal.go b/vendor/github.com/buger/goterm/terminal.go index 1ba6493ad..d1e82085e 100644 --- a/vendor/github.com/buger/goterm/terminal.go +++ b/vendor/github.com/buger/goterm/terminal.go @@ -199,15 +199,6 @@ func Width() int { return int(ws.Col) } -// Height gets console height -func Height() int { - ws, err := getWinsize() - if err != nil { - return -1 - } - return int(ws.Row) -} - // CurrentHeight gets current height. Line count in Screen buffer. func CurrentHeight() int { return strings.Count(Screen.String(), "\n") diff --git a/vendor/github.com/buger/goterm/terminal_nosysioctl.go b/vendor/github.com/buger/goterm/terminal_nosysioctl.go index 9b988ffd5..f4f4d5efc 100644 --- a/vendor/github.com/buger/goterm/terminal_nosysioctl.go +++ b/vendor/github.com/buger/goterm/terminal_nosysioctl.go @@ -1,3 +1,4 @@ +//go:build plan9 || solaris // +build plan9 solaris package goterm @@ -10,3 +11,12 @@ func getWinsize() (*winsize, error) { return ws, nil } + +// Height gets console height +func Height() int { + ws, err := getWinsize() + if err != nil { + return -1 + } + return int(ws.Row) +} diff --git a/vendor/github.com/buger/goterm/terminal_sysioctl.go b/vendor/github.com/buger/goterm/terminal_sysioctl.go index 33148ede0..8b48b405c 100644 --- a/vendor/github.com/buger/goterm/terminal_sysioctl.go +++ b/vendor/github.com/buger/goterm/terminal_sysioctl.go @@ -1,8 +1,11 @@ +//go:build !windows && !plan9 && !solaris // +build !windows,!plan9,!solaris package goterm import ( + "errors" + "math" "os" "golang.org/x/sys/unix" @@ -17,3 +20,17 @@ func getWinsize() (*unix.Winsize, error) { return ws, nil } + +// Height gets console height +func Height() int { + ws, err := getWinsize() + if err != nil { + // returns math.MinInt32 if we could not retrieve the height of console window, + // like VSCode debugging console + if errors.Is(err, unix.EOPNOTSUPP) { + return math.MinInt32 + } + return -1 + } + return int(ws.Row) +} diff --git a/vendor/github.com/buger/goterm/terminal_windows.go b/vendor/github.com/buger/goterm/terminal_windows.go index 37c56ae69..e8236b697 100644 --- a/vendor/github.com/buger/goterm/terminal_windows.go +++ b/vendor/github.com/buger/goterm/terminal_windows.go @@ -1,8 +1,11 @@ +//go:build windows // +build windows package goterm import ( + "errors" + "math" "os" "golang.org/x/sys/windows" @@ -21,3 +24,17 @@ func getWinsize() (*winsize, error) { return ws, nil } + +// Height gets console height +func Height() int { + ws, err := getWinsize() + if err != nil { + // returns math.MinInt32 if we could not retrieve the height of console window, + // like VSCode debugging console + if errors.Is(err, windows.WSAEOPNOTSUPP) { + return math.MinInt32 + } + return -1 + } + return int(ws.Row) +} diff --git a/vendor/modules.txt b/vendor/modules.txt index 3e5da922c..9260511f5 100644 --- a/vendor/modules.txt +++ b/vendor/modules.txt @@ -41,7 +41,7 @@ github.com/beorn7/perks/quantile # github.com/blang/semver v3.5.1+incompatible ## explicit github.com/blang/semver -# github.com/buger/goterm v1.0.1 +# github.com/buger/goterm v1.0.4 ## explicit github.com/buger/goterm # github.com/cespare/xxhash/v2 v2.1.2 diff --git a/version/version.go b/version/version.go index c6bd2c239..da7402967 100644 --- a/version/version.go +++ b/version/version.go @@ -38,7 +38,7 @@ var Version = semver.MustParse("4.0.0-dev") var APIVersion = map[Tree]map[Level]semver.Version{ Libpod: { CurrentAPI: Version, - MinimalAPI: semver.MustParse("3.1.0"), + MinimalAPI: semver.MustParse("3.3.1"), }, Compat: { CurrentAPI: semver.MustParse("1.40.0"), |