diff options
-rw-r--r-- | docs/source/markdown/options/ip6.md | 8 | ||||
-rw-r--r-- | docs/source/markdown/podman-create.1.md.in | 12 | ||||
-rw-r--r-- | docs/source/markdown/podman-kube-play.1.md.in | 2 | ||||
-rw-r--r-- | docs/source/markdown/podman-pod-create.1.md.in | 11 | ||||
-rw-r--r-- | docs/source/markdown/podman-run.1.md.in | 11 | ||||
-rw-r--r-- | libpod/stats_common.go | 49 | ||||
-rw-r--r-- | libpod/stats_freebsd.go | 153 | ||||
-rw-r--r-- | libpod/stats_linux.go (renamed from libpod/stats.go) | 50 | ||||
-rw-r--r-- | libpod/stats_unsupported.go | 4 | ||||
-rw-r--r-- | pkg/rctl/rctl.go | 47 | ||||
-rw-r--r-- | pkg/systemd/generate/pods.go | 15 | ||||
-rw-r--r-- | pkg/systemd/generate/pods_test.go | 85 | ||||
-rw-r--r-- | test/e2e/generate_systemd_test.go | 38 |
13 files changed, 374 insertions, 111 deletions
diff --git a/docs/source/markdown/options/ip6.md b/docs/source/markdown/options/ip6.md new file mode 100644 index 000000000..feaae7c44 --- /dev/null +++ b/docs/source/markdown/options/ip6.md @@ -0,0 +1,8 @@ +#### **--ip6**=*ipv6* + +Specify a static IPv6 address for the <<container|pod>>, for example **fd46:db93:aa76:ac37::10**. +This option can only be used if the <<container|pod>> is joined to only a single network - i.e., **--network=network-name** is used at most once - +and if the <<container|pod>> is not joining another container's network namespace via **--network=container:_id_**. +The address must be within the network's IPv6 address pool. + +To specify multiple static IPv6 addresses per <<container|pod>>, set multiple networks using the **--network** option with a static IPv6 address specified for each using the `ip6` mode for that option. diff --git a/docs/source/markdown/podman-create.1.md.in b/docs/source/markdown/podman-create.1.md.in index 54afe3b16..e407ff4d4 100644 --- a/docs/source/markdown/podman-create.1.md.in +++ b/docs/source/markdown/podman-create.1.md.in @@ -230,15 +230,7 @@ Keep STDIN open even if not attached. The default is *false*. @@option ip -#### **--ip6**=*ipv6* - -Specify a static IPv6 address for the container, for example **fd46:db93:aa76:ac37::10**. -This option can only be used if the container is joined to only a single network - i.e., **--network=network-name** is used at most once - -and if the container is not joining another container's network namespace via **--network=container:_id_**. -The address must be within the network's IPv6 address pool. - -To specify multiple static IPv6 addresses per container, set multiple networks using the **--network** option with a static IPv6 address specified for each using the `ip6` mode for that option. - +@@option ip6 @@option ipc @@ -302,7 +294,7 @@ Valid _mode_ values are: - **ns:**_path_: Path to a network namespace to join. - **private**: Create a new namespace for the container. This will use the **bridge** mode for rootful containers and **slirp4netns** for rootless ones. - **slirp4netns[:OPTIONS,...]**: use **slirp4netns**(1) to create a user network stack. This is the default for rootless containers. It is possible to specify these additional options, they can also be set with `network_cmd_options` in containers.conf: - - **allow_host_loopback=true|false**: Allow the slirp4netns to reach the host loopback IP (`10.0.2.2`). Default is false. + - **allow_host_loopback=true|false**: Allow slirp4netns to reach the host loopback IP (default is 10.0.2.2 or the second IP from slirp4netns cidr subnet when changed, see the cidr option below). The default is false. - **mtu=MTU**: Specify the MTU to use for this network. (Default is `65520`). - **cidr=CIDR**: Specify ip range to use for this network. (Default is `10.0.2.0/24`). - **enable_ipv6=true|false**: Enable IPv6. Default is true. (Required for `outbound_addr6`). diff --git a/docs/source/markdown/podman-kube-play.1.md.in b/docs/source/markdown/podman-kube-play.1.md.in index e4e1199e6..3bbe8113a 100644 --- a/docs/source/markdown/podman-kube-play.1.md.in +++ b/docs/source/markdown/podman-kube-play.1.md.in @@ -187,7 +187,7 @@ Valid _mode_ values are: - **ns:**_path_: Path to a network namespace to join. - **private**: Create a new namespace for the container. This will use the **bridge** mode for rootful containers and **slirp4netns** for rootless ones. - **slirp4netns[:OPTIONS,...]**: use **slirp4netns**(1) to create a user network stack. This is the default for rootless containers. It is possible to specify these additional options, they can also be set with `network_cmd_options` in containers.conf: - - **allow_host_loopback=true|false**: Allow the slirp4netns to reach the host loopback IP (`10.0.2.2`). Default is false. + - **allow_host_loopback=true|false**: Allow slirp4netns to reach the host loopback IP (default is 10.0.2.2 or the second IP from slirp4netns cidr subnet when changed, see the cidr option below). The default is false. - **mtu=MTU**: Specify the MTU to use for this network. (Default is `65520`). - **cidr=CIDR**: Specify ip range to use for this network. (Default is `10.0.2.0/24`). - **enable_ipv6=true|false**: Enable IPv6. Default is true. (Required for `outbound_addr6`). diff --git a/docs/source/markdown/podman-pod-create.1.md.in b/docs/source/markdown/podman-pod-create.1.md.in index 7a2a3c78d..b3c13533a 100644 --- a/docs/source/markdown/podman-pod-create.1.md.in +++ b/docs/source/markdown/podman-pod-create.1.md.in @@ -101,14 +101,7 @@ The custom image that will be used for the infra container. Unless specified, P @@option ip -#### **--ip6**=*ipv6* - -Specify a static IPv6 address for the pod, for example **fd46:db93:aa76:ac37::10**. -This option can only be used if the pod is joined to only a single network - i.e., **--network=network-name** is used at most once - -and if the pod is not joining another container's network namespace via **--network=container:_id_**. -The address must be within the network's IPv6 address pool. - -To specify multiple static IPv6 addresses per pod, set multiple networks using the **--network** option with a static IPv6 address specified for each using the `ip6` mode for that option. +@@option ip6 @@option label @@ -145,7 +138,7 @@ Valid _mode_ values are: - **ns:**_path_: Path to a network namespace to join. - **private**: Create a new namespace for the container. This will use the **bridge** mode for rootful containers and **slirp4netns** for rootless ones. - **slirp4netns[:OPTIONS,...]**: use **slirp4netns**(1) to create a user network stack. This is the default for rootless containers. It is possible to specify these additional options, they can also be set with `network_cmd_options` in containers.conf: - - **allow_host_loopback=true|false**: Allow the slirp4netns to reach the host loopback IP (`10.0.2.2`). Default is false. + - **allow_host_loopback=true|false**: Allow slirp4netns to reach the host loopback IP (default is 10.0.2.2 or the second IP from slirp4netns cidr subnet when changed, see the cidr option below). The default is false. - **mtu=MTU**: Specify the MTU to use for this network. (Default is `65520`). - **cidr=CIDR**: Specify ip range to use for this network. (Default is `10.0.2.0/24`). - **enable_ipv6=true|false**: Enable IPv6. Default is true. (Required for `outbound_addr6`). diff --git a/docs/source/markdown/podman-run.1.md.in b/docs/source/markdown/podman-run.1.md.in index fe947b1d0..d9dcd60f9 100644 --- a/docs/source/markdown/podman-run.1.md.in +++ b/docs/source/markdown/podman-run.1.md.in @@ -251,14 +251,7 @@ When set to **true**, keep stdin open even if not attached. The default is **fal @@option ip -#### **--ip6**=*ipv6* - -Specify a static IPv6 address for the container, for example **fd46:db93:aa76:ac37::10**. -This option can only be used if the container is joined to only a single network - i.e., **--network=network-name** is used at most once - -and if the container is not joining another container's network namespace via **--network=container:_id_**. -The address must be within the network's IPv6 address pool. - -To specify multiple static IPv6 addresses per container, set multiple networks using the **--network** option with a static IPv6 address specified for each using the `ip6` mode for that option. +@@option ip6 @@option ipc @@ -322,7 +315,7 @@ Valid _mode_ values are: - **ns:**_path_: Path to a network namespace to join. - **private**: Create a new namespace for the container. This will use the **bridge** mode for rootful containers and **slirp4netns** for rootless ones. - **slirp4netns[:OPTIONS,...]**: use **slirp4netns**(1) to create a user network stack. This is the default for rootless containers. It is possible to specify these additional options, they can also be set with `network_cmd_options` in containers.conf: - - **allow_host_loopback=true|false**: Allow the slirp4netns to reach the host loopback IP (`10.0.2.2`). Default is false. + - **allow_host_loopback=true|false**: Allow slirp4netns to reach the host loopback IP (default is 10.0.2.2 or the second IP from slirp4netns cidr subnet when changed, see the cidr option below). The default is false. - **mtu=MTU**: Specify the MTU to use for this network. (Default is `65520`). - **cidr=CIDR**: Specify ip range to use for this network. (Default is `10.0.2.0/24`). - **enable_ipv6=true|false**: Enable IPv6. Default is true. (Required for `outbound_addr6`). diff --git a/libpod/stats_common.go b/libpod/stats_common.go new file mode 100644 index 000000000..122160bda --- /dev/null +++ b/libpod/stats_common.go @@ -0,0 +1,49 @@ +//go:build linux || freebsd +// +build linux freebsd + +package libpod + +import ( + "fmt" + + "github.com/containers/podman/v4/libpod/define" +) + +// GetContainerStats gets the running stats for a given container. +// The previousStats is used to correctly calculate cpu percentages. You +// should pass nil if there is no previous stat for this container. +func (c *Container) GetContainerStats(previousStats *define.ContainerStats) (*define.ContainerStats, error) { + stats := new(define.ContainerStats) + stats.ContainerID = c.ID() + stats.Name = c.Name() + + if c.config.NoCgroups { + return nil, fmt.Errorf("cannot run top on container %s as it did not create a cgroup: %w", c.ID(), define.ErrNoCgroups) + } + + if !c.batched { + c.lock.Lock() + defer c.lock.Unlock() + if err := c.syncContainer(); err != nil { + return stats, err + } + } + + // returns stats with the fields' default values respective of their type + if c.state.State != define.ContainerStateRunning && c.state.State != define.ContainerStatePaused { + return stats, nil + } + + if previousStats == nil { + previousStats = &define.ContainerStats{ + // if we have no prev stats use the container start time as prev time + // otherwise we cannot correctly calculate the CPU percentage + SystemNano: uint64(c.state.StartedTime.UnixNano()), + } + } + + if err := c.getPlatformContainerStats(stats, previousStats); err != nil { + return nil, err + } + return stats, nil +} diff --git a/libpod/stats_freebsd.go b/libpod/stats_freebsd.go new file mode 100644 index 000000000..53bc3f19a --- /dev/null +++ b/libpod/stats_freebsd.go @@ -0,0 +1,153 @@ +package libpod + +import ( + "fmt" + "math" + "strings" + "time" + + "github.com/containers/common/pkg/cgroups" + "github.com/containers/podman/v4/libpod/define" + "github.com/containers/podman/v4/pkg/rctl" + "github.com/containers/storage/pkg/system" + "github.com/sirupsen/logrus" +) + +// getPlatformContainerStats gets the platform-specific running stats +// for a given container. The previousStats is used to correctly +// calculate cpu percentages. You should pass nil if there is no +// previous stat for this container. +func (c *Container) getPlatformContainerStats(stats *define.ContainerStats, previousStats *define.ContainerStats) error { + now := uint64(time.Now().UnixNano()) + + jailName := c.ID() + if c.state.NetNS != nil { + jailName = c.state.NetNS.Name + "." + jailName + } + entries, err := rctl.GetRacct("jail:" + jailName) + if err != nil { + return fmt.Errorf("unable to read accounting for %s: %w", jailName, err) + } + + // If the current total usage is less than what was previously + // recorded then it means the container was restarted and runs + // in a new jail + if dur, ok := entries["wallclock"]; ok { + if previousStats.Duration > dur*1000000000 { + previousStats = &define.ContainerStats{} + } + } + + for key, val := range entries { + switch key { + case "cputime": // CPU time, in seconds + stats.CPUNano = val * 1000000000 + stats.AvgCPU = calculateCPUPercent(stats.CPUNano, 0, now, uint64(c.state.StartedTime.UnixNano())) + case "datasize": // data size, in bytes + case "stacksize": // stack size, in bytes + case "coredumpsize": // core dump size, in bytes + case "memoryuse": // resident set size, in bytes + stats.MemUsage = val + case "memorylocked": // locked memory, in bytes + case "maxproc": // number of processes + stats.PIDs = val + case "openfiles": // file descriptor table size + case "vmemoryuse": // address space limit, in bytes + case "pseudoterminals": // number of PTYs + case "swapuse": // swap space that may be reserved or used, in bytes + case "nthr": // number of threads + case "msgqqueued": // number of queued SysV messages + case "msgqsize": // SysV message queue size, in bytes + case "nmsgq": // number of SysV message queues + case "nsem": // number of SysV semaphores + case "nsemop": // number of SysV semaphores modified in a single semop(2) call + case "nshm": // number of SysV shared memory segments + case "shmsize": // SysV shared memory size, in bytes + case "wallclock": // wallclock time, in seconds + stats.Duration = val * 1000000000 + stats.UpTime = time.Duration(stats.Duration) + case "pcpu": // %CPU, in percents of a single CPU core + stats.CPU = float64(val) + case "readbps": // filesystem reads, in bytes per second + stats.BlockInput = val + case "writebps": // filesystem writes, in bytes per second + stats.BlockOutput = val + case "readiops": // filesystem reads, in operations per second + case "writeiops": // filesystem writes, in operations per second + } + } + stats.MemLimit = c.getMemLimit() + stats.SystemNano = now + + netStats, err := getContainerNetIO(c) + if err != nil { + return err + } + + // Handle case where the container is not in a network namespace + if netStats != nil { + stats.NetInput = netStats.TxBytes + stats.NetOutput = netStats.RxBytes + } else { + stats.NetInput = 0 + stats.NetOutput = 0 + } + + return nil +} + +// getMemory limit returns the memory limit for a container +func (c *Container) getMemLimit() uint64 { + memLimit := uint64(math.MaxUint64) + + if c.config.Spec.Linux != nil && c.config.Spec.Linux.Resources != nil && + c.config.Spec.Linux.Resources.Memory != nil && c.config.Spec.Linux.Resources.Memory.Limit != nil { + memLimit = uint64(*c.config.Spec.Linux.Resources.Memory.Limit) + } + + mi, err := system.ReadMemInfo() + if err != nil { + logrus.Errorf("ReadMemInfo error: %v", err) + return 0 + } + + //nolint:unconvert + physicalLimit := uint64(mi.MemTotal) + + if memLimit <= 0 || memLimit > physicalLimit { + return physicalLimit + } + + return memLimit +} + +// calculateCPUPercent calculates the cpu usage using the latest measurement in stats. +// previousCPU is the last value of stats.CPU.Usage.Total measured at the time previousSystem. +// +// (now - previousSystem) is the time delta in nanoseconds, between the measurement in previousCPU +// +// and the updated value in stats. +func calculateCPUPercent(currentCPU, previousCPU, now, previousSystem uint64) float64 { + var ( + cpuPercent = 0.0 + cpuDelta = float64(currentCPU - previousCPU) + systemDelta = float64(now - previousSystem) + ) + if systemDelta > 0.0 && cpuDelta > 0.0 { + // gets a ratio of container cpu usage total, and multiplies that by 100 to get a percentage + cpuPercent = (cpuDelta / systemDelta) * 100 + } + return cpuPercent +} + +func calculateBlockIO(stats *cgroups.Metrics) (read uint64, write uint64) { + for _, blkIOEntry := range stats.Blkio.IoServiceBytesRecursive { + switch strings.ToLower(blkIOEntry.Op) { + case "read": + read += blkIOEntry.Value + case "write": + write += blkIOEntry.Value + } + } + return +} diff --git a/libpod/stats.go b/libpod/stats_linux.go index c7e9e5128..ad8f33c91 100644 --- a/libpod/stats.go +++ b/libpod/stats_linux.go @@ -16,57 +16,33 @@ import ( "github.com/containers/podman/v4/libpod/define" ) -// GetContainerStats gets the running stats for a given container. -// The previousStats is used to correctly calculate cpu percentages. You -// should pass nil if there is no previous stat for this container. -func (c *Container) GetContainerStats(previousStats *define.ContainerStats) (*define.ContainerStats, error) { - stats := new(define.ContainerStats) - stats.ContainerID = c.ID() - stats.Name = c.Name() - +// getPlatformContainerStats gets the platform-specific running stats +// for a given container. The previousStats is used to correctly +// calculate cpu percentages. You should pass nil if there is no +// previous stat for this container. +func (c *Container) getPlatformContainerStats(stats *define.ContainerStats, previousStats *define.ContainerStats) error { if c.config.NoCgroups { - return nil, fmt.Errorf("cannot run top on container %s as it did not create a cgroup: %w", c.ID(), define.ErrNoCgroups) - } - - if !c.batched { - c.lock.Lock() - defer c.lock.Unlock() - if err := c.syncContainer(); err != nil { - return stats, err - } - } - - // returns stats with the fields' default values respective of their type - if c.state.State != define.ContainerStateRunning && c.state.State != define.ContainerStatePaused { - return stats, nil - } - - if previousStats == nil { - previousStats = &define.ContainerStats{ - // if we have no prev stats use the container start time as prev time - // otherwise we cannot correctly calculate the CPU percentage - SystemNano: uint64(c.state.StartedTime.UnixNano()), - } + return fmt.Errorf("cannot run top on container %s as it did not create a cgroup: %w", c.ID(), define.ErrNoCgroups) } cgroupPath, err := c.cGroupPath() if err != nil { - return nil, err + return err } cgroup, err := cgroups.Load(cgroupPath) if err != nil { - return stats, fmt.Errorf("unable to load cgroup at %s: %w", cgroupPath, err) + return fmt.Errorf("unable to load cgroup at %s: %w", cgroupPath, err) } // Ubuntu does not have swap memory in cgroups because swap is often not enabled. cgroupStats, err := cgroup.Stat() if err != nil { - return stats, fmt.Errorf("unable to obtain cgroup stats: %w", err) + return fmt.Errorf("unable to obtain cgroup stats: %w", err) } conState := c.state.State netStats, err := getContainerNetIO(c) if err != nil { - return nil, err + return err } // If the current total usage in the cgroup is less than what was previously @@ -103,7 +79,7 @@ func (c *Container) GetContainerStats(previousStats *define.ContainerStats) (*de stats.NetOutput = 0 } - return stats, nil + return nil } // getMemory limit returns the memory limit for a container @@ -133,7 +109,9 @@ func (c *Container) getMemLimit() uint64 { // calculateCPUPercent calculates the cpu usage using the latest measurement in stats. // previousCPU is the last value of stats.CPU.Usage.Total measured at the time previousSystem. -// (now - previousSystem) is the time delta in nanoseconds, between the measurement in previousCPU +// +// (now - previousSystem) is the time delta in nanoseconds, between the measurement in previousCPU +// // and the updated value in stats. func calculateCPUPercent(stats *runccgroup.Stats, previousCPU, now, previousSystem uint64) float64 { var ( diff --git a/libpod/stats_unsupported.go b/libpod/stats_unsupported.go index b23333c2e..3094e2eaa 100644 --- a/libpod/stats_unsupported.go +++ b/libpod/stats_unsupported.go @@ -1,5 +1,5 @@ -//go:build !linux -// +build !linux +//go:build !linux && !freebsd +// +build !linux,!freebsd package libpod diff --git a/pkg/rctl/rctl.go b/pkg/rctl/rctl.go new file mode 100644 index 000000000..135cc60cb --- /dev/null +++ b/pkg/rctl/rctl.go @@ -0,0 +1,47 @@ +//go:build freebsd +// +build freebsd + +package rctl + +// #include <sys/rctl.h> +import "C" + +import ( + "bytes" + "fmt" + "strconv" + "strings" + "syscall" + "unsafe" + + "github.com/sirupsen/logrus" +) + +func GetRacct(filter string) (map[string]uint64, error) { + bp, err := syscall.ByteSliceFromString(filter) + if err != nil { + return nil, err + } + var buf [1024]byte + _, _, errno := syscall.Syscall6(syscall.SYS_RCTL_GET_RACCT, + uintptr(unsafe.Pointer(&bp[0])), + uintptr(len(bp)), + uintptr(unsafe.Pointer(&buf[0])), + uintptr(len(buf)), 0, 0) + if errno != 0 { + return nil, fmt.Errorf("error calling rctl_get_racct with filter %s: %v", errno) + } + len := bytes.IndexByte(buf[:], byte(0)) + entries := strings.Split(string(buf[:len]), ",") + res := make(map[string]uint64) + for _, entry := range entries { + kv := strings.SplitN(entry, "=", 2) + key := kv[0] + val, err := strconv.ParseUint(kv[1], 10, 0) + if err != nil { + logrus.Warnf("unexpected rctl entry, ignoring: %s", entry) + } + res[key] = val + } + return res, nil +} diff --git a/pkg/systemd/generate/pods.go b/pkg/systemd/generate/pods.go index 22f568220..729a038a5 100644 --- a/pkg/systemd/generate/pods.go +++ b/pkg/systemd/generate/pods.go @@ -92,7 +92,7 @@ type podInfo struct { Requires []string } -const podTemplate = headerTemplate + `Requires={{{{- range $index, $value := .RequiredServices -}}}}{{{{if $index}}}} {{{{end}}}}{{{{ $value }}}}.service{{{{end}}}} +const podTemplate = headerTemplate + `Wants={{{{- range $index, $value := .RequiredServices -}}}}{{{{if $index}}}} {{{{end}}}}{{{{ $value }}}}.service{{{{end}}}} Before={{{{- range $index, $value := .RequiredServices -}}}}{{{{if $index}}}} {{{{end}}}}{{{{ $value }}}}.service{{{{end}}}} {{{{- if or .Wants .After .Requires }}}} @@ -252,18 +252,19 @@ func generatePodInfo(pod *libpod.Pod, options entities.GenerateSystemdOptions) ( StopTimeout: stopTimeout, GenerateTimestamp: true, CreateCommand: createCommand, + RunRoot: infraCtr.Runtime().RunRoot(), } return &info, nil } -// Unless already specified, the pod's exit policy to "stop". -func setPodExitPolicy(cmd []string) []string { +// Determine whether the command array includes an exit-policy setting +func hasPodExitPolicy(cmd []string) bool { for _, arg := range cmd { if strings.HasPrefix(arg, "--exit-policy=") || arg == "--exit-policy" { - return cmd + return true } } - return append(cmd, "--exit-policy=stop") + return false } // executePodTemplate executes the pod template on the specified podInfo. Note @@ -364,8 +365,10 @@ func executePodTemplate(info *podInfo, options entities.GenerateSystemdOptions) podCreateArgs = append(podCreateArgs, "--replace") } + if !hasPodExitPolicy(append(startCommand, podCreateArgs...)) { + startCommand = append(startCommand, "--exit-policy=stop") + } startCommand = append(startCommand, podCreateArgs...) - startCommand = setPodExitPolicy(startCommand) startCommand = escapeSystemdArguments(startCommand) info.ExecStartPre1 = "/bin/rm -f {{{{.PIDFile}}}} {{{{.PodIDFile}}}}" diff --git a/pkg/systemd/generate/pods_test.go b/pkg/systemd/generate/pods_test.go index 59f217256..000d73e9a 100644 --- a/pkg/systemd/generate/pods_test.go +++ b/pkg/systemd/generate/pods_test.go @@ -7,25 +7,26 @@ import ( "github.com/stretchr/testify/assert" ) -func TestSetPodExitPolicy(t *testing.T) { +func TestHasPodExitPolicy(t *testing.T) { tests := []struct { - input, expected []string + input []string + expected bool }{ { []string{"podman", "pod", "create"}, - []string{"podman", "pod", "create", "--exit-policy=stop"}, + false, }, { []string{"podman", "pod", "create", "--exit-policy=continue"}, - []string{"podman", "pod", "create", "--exit-policy=continue"}, + true, }, { []string{"podman", "pod", "create", "--exit-policy", "continue"}, - []string{"podman", "pod", "create", "--exit-policy", "continue"}, + true, }, } for _, test := range tests { - assert.Equalf(t, test.expected, setPodExitPolicy(test.input), "%v", test.input) + assert.Equalf(t, test.expected, hasPodExitPolicy(test.input), "%v", test.input) } } @@ -70,7 +71,7 @@ Documentation=man:podman-generate-systemd(1) Wants=network-online.target After=network-online.target RequiresMountsFor=/var/run/containers/storage -Requires=container-1.service container-2.service +Wants=container-1.service container-2.service Before=container-1.service container-2.service [Service] @@ -98,7 +99,7 @@ Documentation=man:podman-generate-systemd(1) Wants=network-online.target After=network-online.target RequiresMountsFor=/var/run/containers/storage -Requires=container-1.service container-2.service +Wants=container-1.service container-2.service Before=container-1.service container-2.service [Service] @@ -124,7 +125,7 @@ Documentation=man:podman-generate-systemd(1) Wants=network-online.target After=network-online.target RequiresMountsFor=/var/run/containers/storage -Requires=container-1.service container-2.service +Wants=container-1.service container-2.service Before=container-1.service container-2.service # User-defined dependencies @@ -152,7 +153,7 @@ Documentation=man:podman-generate-systemd(1) Wants=network-online.target After=network-online.target RequiresMountsFor=/var/run/containers/storage -Requires=container-1.service container-2.service +Wants=container-1.service container-2.service Before=container-1.service container-2.service # User-defined dependencies @@ -180,7 +181,7 @@ Documentation=man:podman-generate-systemd(1) Wants=network-online.target After=network-online.target RequiresMountsFor=/var/run/containers/storage -Requires=container-1.service container-2.service +Wants=container-1.service container-2.service Before=container-1.service container-2.service # User-defined dependencies @@ -208,7 +209,7 @@ Documentation=man:podman-generate-systemd(1) Wants=network-online.target After=network-online.target RequiresMountsFor=/var/run/containers/storage -Requires=container-1.service container-2.service +Wants=container-1.service container-2.service Before=container-1.service container-2.service # User-defined dependencies @@ -229,6 +230,33 @@ Type=forking [Install] WantedBy=default.target ` + podNoExplicitName := `# pod-123abc.service +# autogenerated by Podman CI + +[Unit] +Description=Podman pod-123abc.service +Documentation=man:podman-generate-systemd(1) +Wants=network-online.target +After=network-online.target +RequiresMountsFor=/var/run/containers/storage +Wants= +Before= + +[Service] +Environment=PODMAN_SYSTEMD_UNIT=%n +Restart=on-failure +TimeoutStopSec=70 +ExecStartPre=/bin/rm -f %t/pod-123abc.pid %t/pod-123abc.pod-id +ExecStartPre=/usr/bin/podman pod create --infra-conmon-pidfile %t/pod-123abc.pid --pod-id-file %t/pod-123abc.pod-id --exit-policy=stop foo +ExecStart=/usr/bin/podman pod start --pod-id-file %t/pod-123abc.pod-id +ExecStop=/usr/bin/podman pod stop --ignore --pod-id-file %t/pod-123abc.pod-id -t 10 +ExecStopPost=/usr/bin/podman pod rm --ignore -f --pod-id-file %t/pod-123abc.pod-id +PIDFile=%t/pod-123abc.pid +Type=forking + +[Install] +WantedBy=default.target +` podGoodRestartSec := `# pod-123abc.service # autogenerated by Podman CI @@ -239,7 +267,7 @@ Documentation=man:podman-generate-systemd(1) Wants=network-online.target After=network-online.target RequiresMountsFor=/var/run/containers/storage -Requires=container-1.service container-2.service +Wants=container-1.service container-2.service Before=container-1.service container-2.service [Service] @@ -266,7 +294,7 @@ Documentation=man:podman-generate-systemd(1) Wants=network-online.target After=network-online.target RequiresMountsFor=/var/run/containers/storage -Requires=container-1.service container-2.service +Wants=container-1.service container-2.service Before=container-1.service container-2.service [Service] @@ -274,7 +302,7 @@ Environment=PODMAN_SYSTEMD_UNIT=%n Restart=on-failure TimeoutStopSec=70 ExecStartPre=/bin/rm -f %t/pod-123abc.pid %t/pod-123abc.pod-id -ExecStartPre=/usr/bin/podman pod create --infra-conmon-pidfile %t/pod-123abc.pid --pod-id-file %t/pod-123abc.pod-id --name foo "bar=arg with space" --replace --exit-policy=stop +ExecStartPre=/usr/bin/podman pod create --infra-conmon-pidfile %t/pod-123abc.pid --pod-id-file %t/pod-123abc.pod-id --exit-policy=stop --name foo "bar=arg with space" --replace ExecStart=/usr/bin/podman pod start --pod-id-file %t/pod-123abc.pod-id ExecStop=/usr/bin/podman pod stop --ignore --pod-id-file %t/pod-123abc.pod-id -t 10 ExecStopPost=/usr/bin/podman pod rm --ignore -f --pod-id-file %t/pod-123abc.pod-id @@ -294,7 +322,7 @@ Documentation=man:podman-generate-systemd(1) Wants=network-online.target After=network-online.target RequiresMountsFor=/var/run/containers/storage -Requires=container-1.service container-2.service +Wants=container-1.service container-2.service Before=container-1.service container-2.service [Service] @@ -302,7 +330,7 @@ Environment=PODMAN_SYSTEMD_UNIT=%n Restart=on-failure TimeoutStopSec=70 ExecStartPre=/bin/rm -f %t/pod-123abc.pid %t/pod-123abc.pod-id -ExecStartPre=/usr/bin/podman --events-backend none --runroot /root pod create --infra-conmon-pidfile %t/pod-123abc.pid --pod-id-file %t/pod-123abc.pod-id --name foo "bar=arg with space" --replace --exit-policy=stop +ExecStartPre=/usr/bin/podman --events-backend none --runroot /root pod create --infra-conmon-pidfile %t/pod-123abc.pid --pod-id-file %t/pod-123abc.pod-id --exit-policy=stop --name foo "bar=arg with space" --replace ExecStart=/usr/bin/podman --events-backend none --runroot /root pod start --pod-id-file %t/pod-123abc.pod-id ExecStop=/usr/bin/podman --events-backend none --runroot /root pod stop --ignore --pod-id-file %t/pod-123abc.pod-id -t 10 ExecStopPost=/usr/bin/podman --events-backend none --runroot /root pod rm --ignore -f --pod-id-file %t/pod-123abc.pod-id @@ -322,7 +350,7 @@ Documentation=man:podman-generate-systemd(1) Wants=network-online.target After=network-online.target RequiresMountsFor=/var/run/containers/storage -Requires=container-1.service container-2.service +Wants=container-1.service container-2.service Before=container-1.service container-2.service [Service] @@ -330,7 +358,7 @@ Environment=PODMAN_SYSTEMD_UNIT=%n Restart=on-failure TimeoutStopSec=70 ExecStartPre=/bin/rm -f %t/pod-123abc.pid %t/pod-123abc.pod-id -ExecStartPre=/usr/bin/podman pod create --infra-conmon-pidfile %t/pod-123abc.pid --pod-id-file %t/pod-123abc.pod-id --name foo --replace --exit-policy=stop +ExecStartPre=/usr/bin/podman pod create --infra-conmon-pidfile %t/pod-123abc.pid --pod-id-file %t/pod-123abc.pod-id --exit-policy=stop --name foo --replace ExecStart=/usr/bin/podman pod start --pod-id-file %t/pod-123abc.pod-id ExecStop=/usr/bin/podman pod stop --ignore --pod-id-file %t/pod-123abc.pod-id -t 10 ExecStopPost=/usr/bin/podman pod rm --ignore -f --pod-id-file %t/pod-123abc.pod-id @@ -350,7 +378,7 @@ Documentation=man:podman-generate-systemd(1) Wants=network-online.target After=network-online.target RequiresMountsFor=/var/run/containers/storage -Requires=container-1.service container-2.service +Wants=container-1.service container-2.service Before=container-1.service container-2.service [Service] @@ -483,6 +511,23 @@ WantedBy=default.target false, false, }, + {"pod without --name", + podInfo{ + Executable: "/usr/bin/podman", + ServiceName: "pod-123abc", + InfraNameOrID: "jadda-jadda-infra", + PIDFile: "/run/containers/storage/overlay-containers/639c53578af4d84b8800b4635fa4e680ee80fd67e0e6a2d4eea48d1e3230f401/userdata/conmon.pid", + StopTimeout: 10, + PodmanVersion: "CI", + GraphRoot: "/var/lib/containers/storage", + RunRoot: "/var/run/containers/storage", + CreateCommand: []string{"podman", "pod", "create", "foo"}, + }, + podNoExplicitName, + true, + false, + false, + }, {"pod restartSec", podInfo{ Executable: "/usr/bin/podman", diff --git a/test/e2e/generate_systemd_test.go b/test/e2e/generate_systemd_test.go index f47abbc13..347440faf 100644 --- a/test/e2e/generate_systemd_test.go +++ b/test/e2e/generate_systemd_test.go @@ -3,6 +3,7 @@ package integration import ( "io/ioutil" "os" + "strings" . "github.com/containers/podman/v4/test/utils" . "github.com/onsi/ginkgo" @@ -220,19 +221,20 @@ var _ = Describe("Podman generate systemd", func() { Expect(session).Should(Exit(0)) // Grepping the output (in addition to unit tests) - Expect(session.OutputToString()).To(ContainSubstring("# pod-foo.service")) - Expect(session.OutputToString()).To(ContainSubstring("Requires=container-foo-1.service container-foo-2.service")) - Expect(session.OutputToString()).To(ContainSubstring("# container-foo-1.service")) - Expect(session.OutputToString()).To(ContainSubstring(" start foo-1")) - Expect(session.OutputToString()).To(ContainSubstring("-infra")) // infra container - Expect(session.OutputToString()).To(ContainSubstring("# container-foo-2.service")) - Expect(session.OutputToString()).To(ContainSubstring(" stop -t 42 foo-2")) - Expect(session.OutputToString()).To(ContainSubstring("BindsTo=pod-foo.service")) - Expect(session.OutputToString()).To(ContainSubstring("PIDFile=")) - Expect(session.OutputToString()).To(ContainSubstring("/userdata/conmon.pid")) - + output := session.OutputToString() + Expect(output).To(ContainSubstring("# pod-foo.service")) + Expect(output).To(ContainSubstring("Wants=container-foo-1.service container-foo-2.service")) + Expect(output).To(ContainSubstring("# container-foo-1.service")) + Expect(output).To(ContainSubstring(" start foo-1")) + Expect(output).To(ContainSubstring("-infra")) // infra container + Expect(output).To(ContainSubstring("# container-foo-2.service")) + Expect(output).To(ContainSubstring(" stop -t 42 foo-2")) + Expect(output).To(ContainSubstring("BindsTo=pod-foo.service")) + Expect(output).To(ContainSubstring("PIDFile=")) + Expect(output).To(ContainSubstring("/userdata/conmon.pid")) + Expect(strings.Count(output, "RequiresMountsFor="+podmanTest.RunRoot)).To(Equal(3)) // The podman commands in the unit should not contain the root flags - Expect(session.OutputToString()).ToNot(ContainSubstring(" --runroot")) + Expect(output).ToNot(ContainSubstring(" --runroot")) }) It("podman generate systemd pod --name --files", func() { @@ -468,7 +470,7 @@ var _ = Describe("Podman generate systemd", func() { // Grepping the output (in addition to unit tests) Expect(session.OutputToString()).To(ContainSubstring("# p-foo.service")) - Expect(session.OutputToString()).To(ContainSubstring("Requires=container-foo-1.service container-foo-2.service")) + Expect(session.OutputToString()).To(ContainSubstring("Wants=container-foo-1.service container-foo-2.service")) Expect(session.OutputToString()).To(ContainSubstring("# container-foo-1.service")) Expect(session.OutputToString()).To(ContainSubstring("BindsTo=p-foo.service")) }) @@ -492,7 +494,7 @@ var _ = Describe("Podman generate systemd", func() { // Grepping the output (in addition to unit tests) Expect(session.OutputToString()).To(ContainSubstring("# p_foo.service")) - Expect(session.OutputToString()).To(ContainSubstring("Requires=con_foo-1.service con_foo-2.service")) + Expect(session.OutputToString()).To(ContainSubstring("Wants=con_foo-1.service con_foo-2.service")) Expect(session.OutputToString()).To(ContainSubstring("# con_foo-1.service")) Expect(session.OutputToString()).To(ContainSubstring("# con_foo-2.service")) Expect(session.OutputToString()).To(ContainSubstring("BindsTo=p_foo.service")) @@ -518,7 +520,7 @@ var _ = Describe("Podman generate systemd", func() { // Grepping the output (in addition to unit tests) Expect(session1.OutputToString()).To(ContainSubstring("# foo.service")) - Expect(session1.OutputToString()).To(ContainSubstring("Requires=container-foo-1.service container-foo-2.service")) + Expect(session1.OutputToString()).To(ContainSubstring("Wants=container-foo-1.service container-foo-2.service")) Expect(session1.OutputToString()).To(ContainSubstring("# container-foo-1.service")) Expect(session1.OutputToString()).To(ContainSubstring("BindsTo=foo.service")) @@ -529,7 +531,7 @@ var _ = Describe("Podman generate systemd", func() { // Grepping the output (in addition to unit tests) Expect(session2.OutputToString()).To(ContainSubstring("# foo.service")) - Expect(session2.OutputToString()).To(ContainSubstring("Requires=foo-1.service foo-2.service")) + Expect(session2.OutputToString()).To(ContainSubstring("Wants=foo-1.service foo-2.service")) Expect(session2.OutputToString()).To(ContainSubstring("# foo-1.service")) Expect(session2.OutputToString()).To(ContainSubstring("# foo-2.service")) Expect(session2.OutputToString()).To(ContainSubstring("BindsTo=foo.service")) @@ -560,9 +562,9 @@ var _ = Describe("Podman generate systemd", func() { // Grepping the output (in addition to unit tests) Expect(session.OutputToString()).To(ContainSubstring("# pod-foo.service")) - Expect(session.OutputToString()).To(ContainSubstring("Requires=container-foo-1.service container-foo-2.service")) + Expect(session.OutputToString()).To(ContainSubstring("Wants=container-foo-1.service container-foo-2.service")) Expect(session.OutputToString()).To(ContainSubstring("BindsTo=pod-foo.service")) - Expect(session.OutputToString()).To(ContainSubstring("pod create --infra-conmon-pidfile %t/pod-foo.pid --pod-id-file %t/pod-foo.pod-id --name foo")) + Expect(session.OutputToString()).To(ContainSubstring("pod create --infra-conmon-pidfile %t/pod-foo.pid --pod-id-file %t/pod-foo.pod-id --exit-policy=stop --name foo")) Expect(session.OutputToString()).To(ContainSubstring("ExecStartPre=/bin/rm -f %t/pod-foo.pid %t/pod-foo.pod-id")) Expect(session.OutputToString()).To(ContainSubstring("pod stop --ignore --pod-id-file %t/pod-foo.pod-id -t 10")) Expect(session.OutputToString()).To(ContainSubstring("pod rm --ignore -f --pod-id-file %t/pod-foo.pod-id")) |