diff options
28 files changed, 912 insertions, 528 deletions
diff --git a/cmd/podman/secrets/create.go b/cmd/podman/secrets/create.go index 01775f563..293da2103 100644 --- a/cmd/podman/secrets/create.go +++ b/cmd/podman/secrets/create.go @@ -10,6 +10,7 @@ import ( "github.com/containers/common/pkg/completion" "github.com/containers/podman/v4/cmd/podman/common" + "github.com/containers/podman/v4/cmd/podman/parse" "github.com/containers/podman/v4/cmd/podman/registry" "github.com/containers/podman/v4/pkg/domain/entities" "github.com/spf13/cobra" @@ -31,6 +32,7 @@ var ( var ( createOpts = entities.SecretCreateOptions{} env = false + labels []string ) func init() { @@ -38,21 +40,24 @@ func init() { Command: createCmd, Parent: secretCmd, }) + cfg := registry.PodmanConfig() flags := createCmd.Flags() driverFlagName := "driver" - optsFlagName := "driver-opts" - - cfg := registry.PodmanConfig() - flags.StringVarP(&createOpts.Driver, driverFlagName, "d", cfg.Secrets.Driver, "Specify secret driver") - flags.StringToStringVar(&createOpts.DriverOpts, optsFlagName, cfg.Secrets.Opts, "Specify driver specific options") _ = createCmd.RegisterFlagCompletionFunc(driverFlagName, completion.AutocompleteNone) + + optsFlagName := "driver-opts" + flags.StringToStringVar(&createOpts.DriverOpts, optsFlagName, cfg.Secrets.Opts, "Specify driver specific options") _ = createCmd.RegisterFlagCompletionFunc(optsFlagName, completion.AutocompleteNone) envFlagName := "env" flags.BoolVar(&env, envFlagName, false, "Read secret data from environment variable") + + labelFlagName := "label" + flags.StringArrayVarP(&labels, labelFlagName, "l", nil, "Specify labels on the secret") + _ = createCmd.RegisterFlagCompletionFunc(labelFlagName, completion.AutocompleteNone) } func create(cmd *cobra.Command, args []string) error { @@ -87,6 +92,11 @@ func create(cmd *cobra.Command, args []string) error { reader = file } + createOpts.Labels, err = parse.GetAllLabels([]string{}, labels) + if err != nil { + return fmt.Errorf("unable to process labels: %w", err) + } + report, err := registry.ContainerEngine().SecretCreate(context.Background(), name, reader, createOpts) if err != nil { return err diff --git a/docs/source/markdown/podman-generate-systemd.1.md b/docs/source/markdown/podman-generate-systemd.1.md index b733cff8d..190b21b71 100644 --- a/docs/source/markdown/podman-generate-systemd.1.md +++ b/docs/source/markdown/podman-generate-systemd.1.md @@ -141,7 +141,8 @@ RequiresMountsFor=/var/run/container/storage [Service] Restart=always ExecStart=/usr/bin/podman start de1e3223b1b888bc02d0962dd6cb5855eb00734061013ffdd3479d225abacdc6 -ExecStop=/usr/bin/podman stop -t 1 de1e3223b1b888bc02d0962dd6cb5855eb00734061013ffdd3479d225abacdc6 +ExecStop=/usr/bin/podman stop \ + -t 1 de1e3223b1b888bc02d0962dd6cb5855eb00734061013ffdd3479d225abacdc6 KillMode=none Type=forking PIDFile=/run/user/1000/overlay-containers/de1e3223b1b888bc02d0962dd6cb5855eb00734061013ffdd3479d225abacdc6/userdata/conmon.pid @@ -171,14 +172,19 @@ RequiresMountsFor=/var/run/container/storage Environment=PODMAN_SYSTEMD_UNIT=%n Restart=on-failure ExecStartPre=/bin/rm -f %t/%n-pid %t/%n-cid -ExecStart=/usr/local/bin/podman run - --conmon-pidfile %t/%n-pid - --cidfile %t/%n-cid - --cgroups=no-conmon - -d +ExecStart=/usr/local/bin/podman run \ + --conmon-pidfile %t/%n-pid \ + --cidfile %t/%n-cid \ + --cgroups=no-conmon \ + -d \ -dit alpine -ExecStop=/usr/local/bin/podman stop --ignore --cidfile %t/%n-cid -t 10 -ExecStopPost=/usr/local/bin/podman rm --ignore -f --cidfile %t/%n-cid +ExecStop=/usr/local/bin/podman stop \ + --ignore \ + --cidfile %t/%n-cid -t 10 +ExecStopPost=/usr/local/bin/podman rm \ + --ignore \ + -f \ + --cidfile %t/%n-cid PIDFile=%t/%n-pid KillMode=none Type=forking @@ -217,7 +223,8 @@ RequiresMountsFor=/var/run/container/storage [Service] Restart=on-failure ExecStart=/usr/bin/podman start 77a818221650-infra -ExecStop=/usr/bin/podman stop -t 10 77a818221650-infra +ExecStop=/usr/bin/podman stop \ + -t 10 77a818221650-infra KillMode=none Type=forking PIDFile=/run/user/1000/overlay-containers/ccfd5c71a088768774ca7bd05888d55cc287698dde06f475c8b02f696a25adcd/userdata/conmon.pid diff --git a/docs/source/markdown/podman-secret-create.1.md b/docs/source/markdown/podman-secret-create.1.md index 1aafc6c11..fc6d72efb 100644 --- a/docs/source/markdown/podman-secret-create.1.md +++ b/docs/source/markdown/podman-secret-create.1.md @@ -26,16 +26,20 @@ Specify the secret driver (default **file**, which is unencrypted). #### **--driver-opts**=*key1=val1,key2=val2* -Specify driver specific options +Specify driver specific options. #### **--env**=*false* -Read secret data from environment variable +Read secret data from environment variable. #### **--help** Print usage statement. +#### **--label**, **-l**=*key=val1,key2=val2* + +Add label to secret. These labels can be viewed in podman secrete inspect or ls. + ## EXAMPLES ``` diff --git a/libpod/container_copy_common.go b/libpod/container_copy_common.go new file mode 100644 index 000000000..d07b4c692 --- /dev/null +++ b/libpod/container_copy_common.go @@ -0,0 +1,213 @@ +//go:build linux || freebsd +// +build linux freebsd + +package libpod + +import ( + "errors" + "io" + "path/filepath" + "strings" + + buildahCopiah "github.com/containers/buildah/copier" + "github.com/containers/buildah/pkg/chrootuser" + "github.com/containers/buildah/util" + "github.com/containers/podman/v4/libpod/define" + "github.com/containers/podman/v4/pkg/rootless" + "github.com/containers/storage/pkg/archive" + "github.com/containers/storage/pkg/idtools" + "github.com/opencontainers/runtime-spec/specs-go" + "github.com/sirupsen/logrus" +) + +func (c *Container) copyFromArchive(path string, chown, noOverwriteDirNonDir bool, rename map[string]string, reader io.Reader) (func() error, error) { + var ( + mountPoint string + resolvedRoot string + resolvedPath string + unmount func() + err error + ) + + // Make sure that "/" copies the *contents* of the mount point and not + // the directory. + if path == "/" { + path = "/." + } + + // Optimization: only mount if the container is not already. + if c.state.Mounted { + mountPoint = c.state.Mountpoint + unmount = func() {} + } else { + // NOTE: make sure to unmount in error paths. + mountPoint, err = c.mount() + if err != nil { + return nil, err + } + unmount = func() { + if err := c.unmount(false); err != nil { + logrus.Errorf("Failed to unmount container: %v", err) + } + } + } + + resolvedRoot, resolvedPath, err = c.resolveCopyTarget(mountPoint, path) + if err != nil { + unmount() + return nil, err + } + + var idPair *idtools.IDPair + if chown { + // Make sure we chown the files to the container's main user and group ID. + user, err := getContainerUser(c, mountPoint) + if err != nil { + unmount() + return nil, err + } + idPair = &idtools.IDPair{UID: int(user.UID), GID: int(user.GID)} + } + + decompressed, err := archive.DecompressStream(reader) + if err != nil { + unmount() + return nil, err + } + + logrus.Debugf("Container copy *to* %q (resolved: %q) on container %q (ID: %s)", path, resolvedPath, c.Name(), c.ID()) + + return func() error { + defer unmount() + defer decompressed.Close() + putOptions := buildahCopiah.PutOptions{ + UIDMap: c.config.IDMappings.UIDMap, + GIDMap: c.config.IDMappings.GIDMap, + ChownDirs: idPair, + ChownFiles: idPair, + NoOverwriteDirNonDir: noOverwriteDirNonDir, + NoOverwriteNonDirDir: noOverwriteDirNonDir, + Rename: rename, + } + + return c.joinMountAndExec( + func() error { + return buildahCopiah.Put(resolvedRoot, resolvedPath, putOptions, decompressed) + }, + ) + }, nil +} + +func (c *Container) copyToArchive(path string, writer io.Writer) (func() error, error) { + var ( + mountPoint string + unmount func() + err error + ) + + // Optimization: only mount if the container is not already. + if c.state.Mounted { + mountPoint = c.state.Mountpoint + unmount = func() {} + } else { + // NOTE: make sure to unmount in error paths. + mountPoint, err = c.mount() + if err != nil { + return nil, err + } + unmount = func() { + if err := c.unmount(false); err != nil { + logrus.Errorf("Failed to unmount container: %v", err) + } + } + } + + statInfo, resolvedRoot, resolvedPath, err := c.stat(mountPoint, path) + if err != nil { + unmount() + return nil, err + } + + // We optimistically chown to the host user. In case of a hypothetical + // container-to-container copy, the reading side will chown back to the + // container user. + user, err := getContainerUser(c, mountPoint) + if err != nil { + unmount() + return nil, err + } + hostUID, hostGID, err := util.GetHostIDs( + idtoolsToRuntimeSpec(c.config.IDMappings.UIDMap), + idtoolsToRuntimeSpec(c.config.IDMappings.GIDMap), + user.UID, + user.GID, + ) + if err != nil { + unmount() + return nil, err + } + idPair := idtools.IDPair{UID: int(hostUID), GID: int(hostGID)} + + logrus.Debugf("Container copy *from* %q (resolved: %q) on container %q (ID: %s)", path, resolvedPath, c.Name(), c.ID()) + + return func() error { + defer unmount() + getOptions := buildahCopiah.GetOptions{ + // Unless the specified points to ".", we want to copy the base directory. + KeepDirectoryNames: statInfo.IsDir && filepath.Base(path) != ".", + UIDMap: c.config.IDMappings.UIDMap, + GIDMap: c.config.IDMappings.GIDMap, + ChownDirs: &idPair, + ChownFiles: &idPair, + Excludes: []string{"dev", "proc", "sys"}, + // Ignore EPERMs when copying from rootless containers + // since we cannot read TTY devices. Those are owned + // by the host's root and hence "nobody" inside the + // container's user namespace. + IgnoreUnreadable: rootless.IsRootless() && c.state.State == define.ContainerStateRunning, + } + return c.joinMountAndExec( + func() error { + return buildahCopiah.Get(resolvedRoot, "", getOptions, []string{resolvedPath}, writer) + }, + ) + }, nil +} + +// getContainerUser returns the specs.User and ID mappings of the container. +func getContainerUser(container *Container, mountPoint string) (specs.User, error) { + userspec := container.config.User + + uid, gid, _, err := chrootuser.GetUser(mountPoint, userspec) + u := specs.User{ + UID: uid, + GID: gid, + Username: userspec, + } + + if !strings.Contains(userspec, ":") { + groups, err2 := chrootuser.GetAdditionalGroupsForUser(mountPoint, uint64(u.UID)) + if err2 != nil { + if !errors.Is(err2, chrootuser.ErrNoSuchUser) && err == nil { + err = err2 + } + } else { + u.AdditionalGids = groups + } + } + + return u, err +} + +// idtoolsToRuntimeSpec converts idtools ID mapping to the one of the runtime spec. +func idtoolsToRuntimeSpec(idMaps []idtools.IDMap) (convertedIDMap []specs.LinuxIDMapping) { + for _, idmap := range idMaps { + tempIDMap := specs.LinuxIDMapping{ + ContainerID: uint32(idmap.ContainerID), + HostID: uint32(idmap.HostID), + Size: uint32(idmap.Size), + } + convertedIDMap = append(convertedIDMap, tempIDMap) + } + return convertedIDMap +} diff --git a/libpod/container_copy_freebsd.go b/libpod/container_copy_freebsd.go new file mode 100644 index 000000000..218f3917f --- /dev/null +++ b/libpod/container_copy_freebsd.go @@ -0,0 +1,13 @@ +package libpod + +// On FreeBSD, the container's mounts are in the global mount +// namespace so we can just execute the function directly. +func (c *Container) joinMountAndExec(f func() error) error { + return f() +} + +// Similarly, we can just use resolvePath for both running and stopped +// containers. +func (c *Container) resolveCopyTarget(mountPoint string, containerPath string) (string, string, error) { + return c.resolvePath(mountPoint, containerPath) +} diff --git a/libpod/container_copy_linux.go b/libpod/container_copy_linux.go index 557fead1e..3b029f08f 100644 --- a/libpod/container_copy_linux.go +++ b/libpod/container_copy_linux.go @@ -1,226 +1,14 @@ -//go:build linux -// +build linux - package libpod import ( - "errors" "fmt" - "io" "os" - "path/filepath" "runtime" - "strings" - buildahCopiah "github.com/containers/buildah/copier" - "github.com/containers/buildah/pkg/chrootuser" - "github.com/containers/buildah/util" "github.com/containers/podman/v4/libpod/define" - "github.com/containers/podman/v4/pkg/rootless" - "github.com/containers/storage/pkg/archive" - "github.com/containers/storage/pkg/idtools" - "github.com/opencontainers/runtime-spec/specs-go" - "github.com/sirupsen/logrus" "golang.org/x/sys/unix" ) -func (c *Container) copyFromArchive(path string, chown, noOverwriteDirNonDir bool, rename map[string]string, reader io.Reader) (func() error, error) { - var ( - mountPoint string - resolvedRoot string - resolvedPath string - unmount func() - err error - ) - - // Make sure that "/" copies the *contents* of the mount point and not - // the directory. - if path == "/" { - path = "/." - } - - // Optimization: only mount if the container is not already. - if c.state.Mounted { - mountPoint = c.state.Mountpoint - unmount = func() {} - } else { - // NOTE: make sure to unmount in error paths. - mountPoint, err = c.mount() - if err != nil { - return nil, err - } - unmount = func() { - if err := c.unmount(false); err != nil { - logrus.Errorf("Failed to unmount container: %v", err) - } - } - } - - if c.state.State == define.ContainerStateRunning { - resolvedRoot = "/" - resolvedPath = c.pathAbs(path) - } else { - resolvedRoot, resolvedPath, err = c.resolvePath(mountPoint, path) - if err != nil { - unmount() - return nil, err - } - } - - var idPair *idtools.IDPair - if chown { - // Make sure we chown the files to the container's main user and group ID. - user, err := getContainerUser(c, mountPoint) - if err != nil { - unmount() - return nil, err - } - idPair = &idtools.IDPair{UID: int(user.UID), GID: int(user.GID)} - } - - decompressed, err := archive.DecompressStream(reader) - if err != nil { - unmount() - return nil, err - } - - logrus.Debugf("Container copy *to* %q (resolved: %q) on container %q (ID: %s)", path, resolvedPath, c.Name(), c.ID()) - - return func() error { - defer unmount() - defer decompressed.Close() - putOptions := buildahCopiah.PutOptions{ - UIDMap: c.config.IDMappings.UIDMap, - GIDMap: c.config.IDMappings.GIDMap, - ChownDirs: idPair, - ChownFiles: idPair, - NoOverwriteDirNonDir: noOverwriteDirNonDir, - NoOverwriteNonDirDir: noOverwriteDirNonDir, - Rename: rename, - } - - return c.joinMountAndExec( - func() error { - return buildahCopiah.Put(resolvedRoot, resolvedPath, putOptions, decompressed) - }, - ) - }, nil -} - -func (c *Container) copyToArchive(path string, writer io.Writer) (func() error, error) { - var ( - mountPoint string - unmount func() - err error - ) - - // Optimization: only mount if the container is not already. - if c.state.Mounted { - mountPoint = c.state.Mountpoint - unmount = func() {} - } else { - // NOTE: make sure to unmount in error paths. - mountPoint, err = c.mount() - if err != nil { - return nil, err - } - unmount = func() { - if err := c.unmount(false); err != nil { - logrus.Errorf("Failed to unmount container: %v", err) - } - } - } - - statInfo, resolvedRoot, resolvedPath, err := c.stat(mountPoint, path) - if err != nil { - unmount() - return nil, err - } - - // We optimistically chown to the host user. In case of a hypothetical - // container-to-container copy, the reading side will chown back to the - // container user. - user, err := getContainerUser(c, mountPoint) - if err != nil { - unmount() - return nil, err - } - hostUID, hostGID, err := util.GetHostIDs( - idtoolsToRuntimeSpec(c.config.IDMappings.UIDMap), - idtoolsToRuntimeSpec(c.config.IDMappings.GIDMap), - user.UID, - user.GID, - ) - if err != nil { - unmount() - return nil, err - } - idPair := idtools.IDPair{UID: int(hostUID), GID: int(hostGID)} - - logrus.Debugf("Container copy *from* %q (resolved: %q) on container %q (ID: %s)", path, resolvedPath, c.Name(), c.ID()) - - return func() error { - defer unmount() - getOptions := buildahCopiah.GetOptions{ - // Unless the specified points to ".", we want to copy the base directory. - KeepDirectoryNames: statInfo.IsDir && filepath.Base(path) != ".", - UIDMap: c.config.IDMappings.UIDMap, - GIDMap: c.config.IDMappings.GIDMap, - ChownDirs: &idPair, - ChownFiles: &idPair, - Excludes: []string{"dev", "proc", "sys"}, - // Ignore EPERMs when copying from rootless containers - // since we cannot read TTY devices. Those are owned - // by the host's root and hence "nobody" inside the - // container's user namespace. - IgnoreUnreadable: rootless.IsRootless() && c.state.State == define.ContainerStateRunning, - } - return c.joinMountAndExec( - func() error { - return buildahCopiah.Get(resolvedRoot, "", getOptions, []string{resolvedPath}, writer) - }, - ) - }, nil -} - -// getContainerUser returns the specs.User and ID mappings of the container. -func getContainerUser(container *Container, mountPoint string) (specs.User, error) { - userspec := container.config.User - - uid, gid, _, err := chrootuser.GetUser(mountPoint, userspec) - u := specs.User{ - UID: uid, - GID: gid, - Username: userspec, - } - - if !strings.Contains(userspec, ":") { - groups, err2 := chrootuser.GetAdditionalGroupsForUser(mountPoint, uint64(u.UID)) - if err2 != nil { - if !errors.Is(err2, chrootuser.ErrNoSuchUser) && err == nil { - err = err2 - } - } else { - u.AdditionalGids = groups - } - } - - return u, err -} - -// idtoolsToRuntimeSpec converts idtools ID mapping to the one of the runtime spec. -func idtoolsToRuntimeSpec(idMaps []idtools.IDMap) (convertedIDMap []specs.LinuxIDMapping) { - for _, idmap := range idMaps { - tempIDMap := specs.LinuxIDMapping{ - ContainerID: uint32(idmap.ContainerID), - HostID: uint32(idmap.HostID), - Size: uint32(idmap.Size), - } - convertedIDMap = append(convertedIDMap, tempIDMap) - } - return convertedIDMap -} - // joinMountAndExec executes the specified function `f` inside the container's // mount and PID namespace. That allows for having the exact view on the // container's file system. @@ -288,3 +76,13 @@ func (c *Container) joinMountAndExec(f func() error) error { }() return <-errChan } + +func (c *Container) resolveCopyTarget(mountPoint string, containerPath string) (string, string, error) { + // If the container is running, we will execute the copy + // inside the container's mount namespace so we return a path + // relative to the container's root. + if c.state.State == define.ContainerStateRunning { + return "/", c.pathAbs(containerPath), nil + } + return c.resolvePath(mountPoint, containerPath) +} diff --git a/libpod/container_copy_unsupported.go b/libpod/container_copy_unsupported.go index 62937279a..703b0a74e 100644 --- a/libpod/container_copy_unsupported.go +++ b/libpod/container_copy_unsupported.go @@ -1,5 +1,5 @@ -//go:build !linux -// +build !linux +//go:build !linux && !freebsd +// +build !linux,!freebsd package libpod diff --git a/libpod/container_stat_common.go b/libpod/container_stat_common.go new file mode 100644 index 000000000..e59a52ede --- /dev/null +++ b/libpod/container_stat_common.go @@ -0,0 +1,155 @@ +//go:build linux || freebsd +// +build linux freebsd + +package libpod + +import ( + "errors" + "fmt" + "os" + "path/filepath" + "strings" + + "github.com/containers/buildah/copier" + "github.com/containers/podman/v4/libpod/define" + "github.com/containers/podman/v4/pkg/copy" +) + +// statOnHost stats the specified path *on the host*. It returns the file info +// along with the resolved root and the resolved path. Both paths are absolute +// to the host's root. Note that the paths may resolved outside the +// container's mount point (e.g., to a volume or bind mount). +func (c *Container) statOnHost(mountPoint string, containerPath string) (*copier.StatForItem, string, string, error) { + // Now resolve the container's path. It may hit a volume, it may hit a + // bind mount, it may be relative. + resolvedRoot, resolvedPath, err := c.resolvePath(mountPoint, containerPath) + if err != nil { + return nil, "", "", err + } + + statInfo, err := secureStat(resolvedRoot, resolvedPath) + return statInfo, resolvedRoot, resolvedPath, err +} + +func (c *Container) stat(containerMountPoint string, containerPath string) (*define.FileInfo, string, string, error) { + var ( + resolvedRoot string + resolvedPath string + absContainerPath string + statInfo *copier.StatForItem + statErr error + ) + + // Make sure that "/" copies the *contents* of the mount point and not + // the directory. + if containerPath == "/" { + containerPath = "/." + } + + // Wildcards are not allowed. + // TODO: it's now technically possible wildcards. + // We may consider enabling support in the future. + if strings.Contains(containerPath, "*") { + return nil, "", "", copy.ErrENOENT + } + + statInfo, resolvedRoot, resolvedPath, statErr = c.statInContainer(containerMountPoint, containerPath) + if statErr != nil { + if statInfo == nil { + return nil, "", "", statErr + } + // Not all errors from secureStat map to ErrNotExist, so we + // have to look into the error string. Turning it into an + // ENOENT let's the API handlers return the correct status code + // which is crucial for the remote client. + if os.IsNotExist(statErr) || strings.Contains(statErr.Error(), "o such file or directory") { + statErr = copy.ErrENOENT + } + } + + switch { + case statInfo.IsSymlink: + // Symlinks are already evaluated and always relative to the + // container's mount point. + absContainerPath = statInfo.ImmediateTarget + case strings.HasPrefix(resolvedPath, containerMountPoint): + // If the path is on the container's mount point, strip it off. + absContainerPath = strings.TrimPrefix(resolvedPath, containerMountPoint) + absContainerPath = filepath.Join("/", absContainerPath) + default: + // No symlink and not on the container's mount point, so let's + // move it back to the original input. It must have evaluated + // to a volume or bind mount but we cannot return host paths. + absContainerPath = containerPath + } + + // Preserve the base path as specified by the user. The `filepath` + // packages likes to remove trailing slashes and dots that are crucial + // to the copy logic. + absContainerPath = copy.PreserveBasePath(containerPath, absContainerPath) + resolvedPath = copy.PreserveBasePath(containerPath, resolvedPath) + + info := &define.FileInfo{ + IsDir: statInfo.IsDir, + Name: filepath.Base(absContainerPath), + Size: statInfo.Size, + Mode: statInfo.Mode, + ModTime: statInfo.ModTime, + LinkTarget: absContainerPath, + } + + return info, resolvedRoot, resolvedPath, statErr +} + +// secureStat extracts file info for path in a chroot'ed environment in root. +func secureStat(root string, path string) (*copier.StatForItem, error) { + var glob string + var err error + + // If root and path are equal, then dir must be empty and the glob must + // be ".". + if filepath.Clean(root) == filepath.Clean(path) { + glob = "." + } else { + glob, err = filepath.Rel(root, path) + if err != nil { + return nil, err + } + } + + globStats, err := copier.Stat(root, "", copier.StatOptions{}, []string{glob}) + if err != nil { + return nil, err + } + + if len(globStats) != 1 { + return nil, fmt.Errorf("internal error: secureStat: expected 1 item but got %d", len(globStats)) + } + if len(globStats) != 1 { + return nil, fmt.Errorf("internal error: secureStat: expected 1 result but got %d", len(globStats[0].Results)) + } + + // NOTE: the key in the map differ from `glob` when hitting symlink. + // Hence, we just take the first (and only) key/value pair. + for _, stat := range globStats[0].Results { + var statErr error + if stat.Error != "" { + statErr = errors.New(stat.Error) + } + // If necessary evaluate the symlink + if stat.IsSymlink { + target, err := copier.Eval(root, path, copier.EvalOptions{}) + if err != nil { + return nil, fmt.Errorf("evaluating symlink in container: %w", err) + } + // Need to make sure the symlink is relative to the root! + target = strings.TrimPrefix(target, root) + target = filepath.Join("/", target) + stat.ImmediateTarget = target + } + return stat, statErr + } + + // Nothing found! + return nil, copy.ErrENOENT +} diff --git a/libpod/container_stat_freebsd.go b/libpod/container_stat_freebsd.go new file mode 100644 index 000000000..d1e0db348 --- /dev/null +++ b/libpod/container_stat_freebsd.go @@ -0,0 +1,13 @@ +package libpod + +import ( + "github.com/containers/buildah/copier" +) + +// On FreeBSD, jails use the global mount namespace, filtered to only +// the mounts the jail should see. This means that we can use +// statOnHost whether the container is running or not. +// container is running +func (c *Container) statInContainer(mountPoint string, containerPath string) (*copier.StatForItem, string, string, error) { + return c.statOnHost(mountPoint, containerPath) +} diff --git a/libpod/container_stat_linux.go b/libpod/container_stat_linux.go index dc3a524f5..5e5ef3c1a 100644 --- a/libpod/container_stat_linux.go +++ b/libpod/container_stat_linux.go @@ -1,18 +1,8 @@ -//go:build linux -// +build linux - package libpod import ( - "errors" - "fmt" - "os" - "path/filepath" - "strings" - "github.com/containers/buildah/copier" "github.com/containers/podman/v4/libpod/define" - "github.com/containers/podman/v4/pkg/copy" ) // statInsideMount stats the specified path *inside* the container's mount and PID @@ -34,150 +24,15 @@ func (c *Container) statInsideMount(containerPath string) (*copier.StatForItem, return statInfo, resolvedRoot, resolvedPath, err } -// statOnHost stats the specified path *on the host*. It returns the file info -// along with the resolved root and the resolved path. Both paths are absolute -// to the host's root. Note that the paths may resolved outside the -// container's mount point (e.g., to a volume or bind mount). -func (c *Container) statOnHost(mountPoint string, containerPath string) (*copier.StatForItem, string, string, error) { - // Now resolve the container's path. It may hit a volume, it may hit a - // bind mount, it may be relative. - resolvedRoot, resolvedPath, err := c.resolvePath(mountPoint, containerPath) - if err != nil { - return nil, "", "", err - } - - statInfo, err := secureStat(resolvedRoot, resolvedPath) - return statInfo, resolvedRoot, resolvedPath, err -} - -func (c *Container) stat(containerMountPoint string, containerPath string) (*define.FileInfo, string, string, error) { - var ( - resolvedRoot string - resolvedPath string - absContainerPath string - statInfo *copier.StatForItem - statErr error - ) - - // Make sure that "/" copies the *contents* of the mount point and not - // the directory. - if containerPath == "/" { - containerPath = "/." - } - - // Wildcards are not allowed. - // TODO: it's now technically possible wildcards. - // We may consider enabling support in the future. - if strings.Contains(containerPath, "*") { - return nil, "", "", copy.ErrENOENT - } - +// Calls either statOnHost or statInsideMount depending on whether the +// container is running +func (c *Container) statInContainer(mountPoint string, containerPath string) (*copier.StatForItem, string, string, error) { if c.state.State == define.ContainerStateRunning { // If the container is running, we need to join it's mount namespace // and stat there. - statInfo, resolvedRoot, resolvedPath, statErr = c.statInsideMount(containerPath) - } else { - // If the container is NOT running, we need to resolve the path - // on the host. - statInfo, resolvedRoot, resolvedPath, statErr = c.statOnHost(containerMountPoint, containerPath) - } - - if statErr != nil { - if statInfo == nil { - return nil, "", "", statErr - } - // Not all errors from secureStat map to ErrNotExist, so we - // have to look into the error string. Turning it into an - // ENOENT let's the API handlers return the correct status code - // which is crucial for the remote client. - if os.IsNotExist(statErr) || strings.Contains(statErr.Error(), "o such file or directory") { - statErr = copy.ErrENOENT - } - } - - switch { - case statInfo.IsSymlink: - // Symlinks are already evaluated and always relative to the - // container's mount point. - absContainerPath = statInfo.ImmediateTarget - case strings.HasPrefix(resolvedPath, containerMountPoint): - // If the path is on the container's mount point, strip it off. - absContainerPath = strings.TrimPrefix(resolvedPath, containerMountPoint) - absContainerPath = filepath.Join("/", absContainerPath) - default: - // No symlink and not on the container's mount point, so let's - // move it back to the original input. It must have evaluated - // to a volume or bind mount but we cannot return host paths. - absContainerPath = containerPath + return c.statInsideMount(containerPath) } - - // Preserve the base path as specified by the user. The `filepath` - // packages likes to remove trailing slashes and dots that are crucial - // to the copy logic. - absContainerPath = copy.PreserveBasePath(containerPath, absContainerPath) - resolvedPath = copy.PreserveBasePath(containerPath, resolvedPath) - - info := &define.FileInfo{ - IsDir: statInfo.IsDir, - Name: filepath.Base(absContainerPath), - Size: statInfo.Size, - Mode: statInfo.Mode, - ModTime: statInfo.ModTime, - LinkTarget: absContainerPath, - } - - return info, resolvedRoot, resolvedPath, statErr -} - -// secureStat extracts file info for path in a chroot'ed environment in root. -func secureStat(root string, path string) (*copier.StatForItem, error) { - var glob string - var err error - - // If root and path are equal, then dir must be empty and the glob must - // be ".". - if filepath.Clean(root) == filepath.Clean(path) { - glob = "." - } else { - glob, err = filepath.Rel(root, path) - if err != nil { - return nil, err - } - } - - globStats, err := copier.Stat(root, "", copier.StatOptions{}, []string{glob}) - if err != nil { - return nil, err - } - - if len(globStats) != 1 { - return nil, fmt.Errorf("internal error: secureStat: expected 1 item but got %d", len(globStats)) - } - if len(globStats) != 1 { - return nil, fmt.Errorf("internal error: secureStat: expected 1 result but got %d", len(globStats[0].Results)) - } - - // NOTE: the key in the map differ from `glob` when hitting symlink. - // Hence, we just take the first (and only) key/value pair. - for _, stat := range globStats[0].Results { - var statErr error - if stat.Error != "" { - statErr = errors.New(stat.Error) - } - // If necessary evaluate the symlink - if stat.IsSymlink { - target, err := copier.Eval(root, path, copier.EvalOptions{}) - if err != nil { - return nil, fmt.Errorf("evaluating symlink in container: %w", err) - } - // Need to make sure the symlink is relative to the root! - target = strings.TrimPrefix(target, root) - target = filepath.Join("/", target) - stat.ImmediateTarget = target - } - return stat, statErr - } - - // Nothing found! - return nil, copy.ErrENOENT + // If the container is NOT running, we need to resolve the path + // on the host. + return c.statOnHost(mountPoint, containerPath) } diff --git a/libpod/container_stat_unsupported.go b/libpod/container_stat_unsupported.go index 2f1acd44d..e88b88bb1 100644 --- a/libpod/container_stat_unsupported.go +++ b/libpod/container_stat_unsupported.go @@ -1,5 +1,5 @@ -//go:build !linux -// +build !linux +//go:build !linux && !freebsd +// +build !linux,!freebsd package libpod diff --git a/pkg/api/handlers/compat/secrets.go b/pkg/api/handlers/compat/secrets.go index 13b3c4e24..847f05f27 100644 --- a/pkg/api/handlers/compat/secrets.go +++ b/pkg/api/handlers/compat/secrets.go @@ -111,14 +111,11 @@ func CreateSecret(w http.ResponseWriter, r *http.Request) { utils.Error(w, http.StatusInternalServerError, fmt.Errorf("Decode(): %w", err)) return } - if len(createParams.Labels) > 0 { - utils.Error(w, http.StatusBadRequest, fmt.Errorf("labels not supported: %w", errors.New("bad parameter"))) - return - } decoded, _ := base64.StdEncoding.DecodeString(createParams.Data) reader := bytes.NewReader(decoded) opts.Driver = createParams.Driver.Name + opts.Labels = createParams.Labels ic := abi.ContainerEngine{Libpod: runtime} report, err := ic.SecretCreate(r.Context(), createParams.Name, reader, opts) diff --git a/pkg/api/handlers/libpod/secrets.go b/pkg/api/handlers/libpod/secrets.go index 6eba65f2b..c24ac8563 100644 --- a/pkg/api/handlers/libpod/secrets.go +++ b/pkg/api/handlers/libpod/secrets.go @@ -22,6 +22,7 @@ func CreateSecret(w http.ResponseWriter, r *http.Request) { Name string `schema:"name"` Driver string `schema:"driver"` DriverOpts map[string]string `schema:"driveropts"` + Labels map[string]string `schema:"labels"` }{ // override any golang type defaults } @@ -33,6 +34,7 @@ func CreateSecret(w http.ResponseWriter, r *http.Request) { opts.Driver = query.Driver opts.DriverOpts = query.DriverOpts + opts.Labels = query.Labels ic := abi.ContainerEngine{Libpod: runtime} report, err := ic.SecretCreate(r.Context(), query.Name, r.Body, opts) diff --git a/pkg/api/server/register_secrets.go b/pkg/api/server/register_secrets.go index 8918ad238..a60145958 100644 --- a/pkg/api/server/register_secrets.go +++ b/pkg/api/server/register_secrets.go @@ -25,6 +25,14 @@ func (s *APIServer) registerSecretHandlers(r *mux.Router) error { // type: string // description: Secret driver // default: "file" + // - in: query + // name: driveropts + // type: string + // description: Secret driver options + // - in: query + // name: labels + // type: string + // description: Labels on the secret // - in: body // name: request // description: Secret diff --git a/pkg/bindings/secrets/types.go b/pkg/bindings/secrets/types.go index 01c3c248d..d2f449556 100644 --- a/pkg/bindings/secrets/types.go +++ b/pkg/bindings/secrets/types.go @@ -22,4 +22,5 @@ type CreateOptions struct { Name *string Driver *string DriverOpts map[string]string + Labels map[string]string } diff --git a/pkg/bindings/secrets/types_create_options.go b/pkg/bindings/secrets/types_create_options.go index 6b1666a42..c9c88e1f3 100644 --- a/pkg/bindings/secrets/types_create_options.go +++ b/pkg/bindings/secrets/types_create_options.go @@ -61,3 +61,18 @@ func (o *CreateOptions) GetDriverOpts() map[string]string { } return o.DriverOpts } + +// WithLabels set field Labels to given value +func (o *CreateOptions) WithLabels(value map[string]string) *CreateOptions { + o.Labels = value + return o +} + +// GetLabels returns value of field Labels +func (o *CreateOptions) GetLabels() map[string]string { + if o.Labels == nil { + var z map[string]string + return z + } + return o.Labels +} diff --git a/pkg/domain/entities/secrets.go b/pkg/domain/entities/secrets.go index d8af937a7..5686b90e9 100644 --- a/pkg/domain/entities/secrets.go +++ b/pkg/domain/entities/secrets.go @@ -13,6 +13,7 @@ type SecretCreateReport struct { type SecretCreateOptions struct { Driver string DriverOpts map[string]string + Labels map[string]string } type SecretListRequest struct { @@ -55,6 +56,7 @@ type SecretVersion struct { type SecretSpec struct { Name string Driver SecretDriverSpec + Labels map[string]string } type SecretDriverSpec struct { @@ -70,6 +72,8 @@ type SecretCreateRequest struct { Data string // Driver represents a driver (default "file") Driver SecretDriverSpec + // Labels are labels on the secret + Labels map[string]string } // Secret create response diff --git a/pkg/domain/infra/abi/secrets.go b/pkg/domain/infra/abi/secrets.go index 4867cbe1c..929858c5c 100644 --- a/pkg/domain/infra/abi/secrets.go +++ b/pkg/domain/infra/abi/secrets.go @@ -44,6 +44,7 @@ func (ic *ContainerEngine) SecretCreate(ctx context.Context, name string, reader storeOpts := secrets.StoreOptions{ DriverOpts: options.DriverOpts, + Labels: options.Labels, } secretID, err := manager.Store(name, data, options.Driver, storeOpts) @@ -73,6 +74,9 @@ func (ic *ContainerEngine) SecretInspect(ctx context.Context, nameOrIDs []string return nil, nil, fmt.Errorf("inspecting secret %s: %w", nameOrID, err) } } + if secret.Labels == nil { + secret.Labels = make(map[string]string) + } report := &entities.SecretInfoReport{ ID: secret.ID, CreatedAt: secret.CreatedAt, @@ -83,6 +87,7 @@ func (ic *ContainerEngine) SecretInspect(ctx context.Context, nameOrIDs []string Name: secret.Driver, Options: secret.DriverOptions, }, + Labels: secret.Labels, }, } reports = append(reports, report) diff --git a/pkg/domain/infra/tunnel/containers.go b/pkg/domain/infra/tunnel/containers.go index 0dc73081d..c82c9ba33 100644 --- a/pkg/domain/infra/tunnel/containers.go +++ b/pkg/domain/infra/tunnel/containers.go @@ -620,6 +620,9 @@ func (ic *ContainerEngine) ContainerExecDetached(ctx context.Context, nameOrID s } func startAndAttach(ic *ContainerEngine, name string, detachKeys *string, input, output, errput *os.File) error { + if output == nil && errput == nil { + fmt.Printf("%s\n", name) + } attachErr := make(chan error) attachReady := make(chan bool) options := new(containers.AttachOptions).WithStream(true) diff --git a/pkg/domain/infra/tunnel/secrets.go b/pkg/domain/infra/tunnel/secrets.go index d26718b12..aa48cb764 100644 --- a/pkg/domain/infra/tunnel/secrets.go +++ b/pkg/domain/infra/tunnel/secrets.go @@ -14,7 +14,8 @@ func (ic *ContainerEngine) SecretCreate(ctx context.Context, name string, reader opts := new(secrets.CreateOptions). WithDriver(options.Driver). WithDriverOpts(options.DriverOpts). - WithName(name) + WithName(name). + WithLabels(options.Labels) created, err := secrets.Create(ic.ClientCtx, reader, opts) if err != nil { return nil, err diff --git a/pkg/systemd/generate/containers.go b/pkg/systemd/generate/containers.go index 8510cfd42..71e9065ea 100644 --- a/pkg/systemd/generate/containers.go +++ b/pkg/systemd/generate/containers.go @@ -78,7 +78,7 @@ Requires={{{{- range $index, $value := .Requires }}}}{{{{ if $index}}}} {{{{end} {{{{- end}}}} [Service] -Environment={{{{.EnvVariable}}}}=%n{{{{- if (eq .IdentifySpecifier true) }}}}-%i{{{{- end}}}} +Environment={{{{.EnvVariable}}}}=%n{{{{- if (eq .IdentifySpecifier true) }}}}-%i {{{{- end}}}} {{{{- if .ExtraEnvs}}}} Environment={{{{- range $index, $value := .ExtraEnvs -}}}}{{{{if $index}}}} {{{{end}}}}{{{{ $value }}}}{{{{end}}}} {{{{- end}}}} @@ -254,6 +254,10 @@ func setContainerNameForTemplate(startCommand []string, info *containerInfo) ([] return startCommand, nil } +func formatOptionsString(cmd string) string { + return formatOptions(strings.Split(cmd, " ")) +} + func formatOptions(options []string) string { var formatted strings.Builder if len(options) == 0 { @@ -294,8 +298,8 @@ func executeContainerTemplate(info *containerInfo, options entities.GenerateSyst info.Type = "forking" info.EnvVariable = define.EnvVariable info.ExecStart = "{{{{.Executable}}}} start {{{{.ContainerNameOrID}}}}" - info.ExecStop = "{{{{.Executable}}}} stop {{{{if (ge .StopTimeout 0)}}}}-t {{{{.StopTimeout}}}}{{{{end}}}} {{{{.ContainerNameOrID}}}}" - info.ExecStopPost = "{{{{.Executable}}}} stop {{{{if (ge .StopTimeout 0)}}}}-t {{{{.StopTimeout}}}}{{{{end}}}} {{{{.ContainerNameOrID}}}}" + info.ExecStop = formatOptionsString("{{{{.Executable}}}} stop {{{{if (ge .StopTimeout 0)}}}} -t {{{{.StopTimeout}}}}{{{{end}}}} {{{{.ContainerNameOrID}}}}") + info.ExecStopPost = formatOptionsString("{{{{.Executable}}}} stop {{{{if (ge .StopTimeout 0)}}}} -t {{{{.StopTimeout}}}}{{{{end}}}} {{{{.ContainerNameOrID}}}}") for i, env := range info.AdditionalEnvVariables { info.AdditionalEnvVariables[i] = escapeSystemdArg(env) } @@ -312,9 +316,9 @@ func executeContainerTemplate(info *containerInfo, options entities.GenerateSyst info.NotifyAccess = "all" info.PIDFile = "" info.ContainerIDFile = "%t/%n.ctr-id" - info.ExecStartPre = "/bin/rm -f {{{{.ContainerIDFile}}}}" - info.ExecStop = "{{{{.Executable}}}} stop --ignore --cidfile={{{{.ContainerIDFile}}}}" - info.ExecStopPost = "{{{{.Executable}}}} rm -f --ignore --cidfile={{{{.ContainerIDFile}}}}" + info.ExecStartPre = formatOptionsString("/bin/rm -f {{{{.ContainerIDFile}}}}") + info.ExecStop = formatOptionsString("{{{{.Executable}}}} stop --ignore --cidfile={{{{.ContainerIDFile}}}}") + info.ExecStopPost = formatOptionsString("{{{{.Executable}}}} rm -f --ignore --cidfile={{{{.ContainerIDFile}}}}") // The create command must at least have three arguments: // /usr/bin/podman run $IMAGE index := 0 diff --git a/pkg/systemd/generate/containers_test.go b/pkg/systemd/generate/containers_test.go index 7f92e75b8..11e8f549e 100644 --- a/pkg/systemd/generate/containers_test.go +++ b/pkg/systemd/generate/containers_test.go @@ -57,8 +57,10 @@ Environment=PODMAN_SYSTEMD_UNIT=%n Restart=on-failure TimeoutStopSec=82 ExecStart=/usr/bin/podman start 639c53578af4d84b8800b4635fa4e680ee80fd67e0e6a2d4eea48d1e3230f401 -ExecStop=/usr/bin/podman stop -t 22 639c53578af4d84b8800b4635fa4e680ee80fd67e0e6a2d4eea48d1e3230f401 -ExecStopPost=/usr/bin/podman stop -t 22 639c53578af4d84b8800b4635fa4e680ee80fd67e0e6a2d4eea48d1e3230f401 +ExecStop=/usr/bin/podman stop \ + -t 22 639c53578af4d84b8800b4635fa4e680ee80fd67e0e6a2d4eea48d1e3230f401 +ExecStopPost=/usr/bin/podman stop \ + -t 22 639c53578af4d84b8800b4635fa4e680ee80fd67e0e6a2d4eea48d1e3230f401 PIDFile=/run/containers/storage/overlay-containers/639c53578af4d84b8800b4635fa4e680ee80fd67e0e6a2d4eea48d1e3230f401/userdata/conmon.pid Type=forking @@ -83,8 +85,10 @@ Environment=PODMAN_SYSTEMD_UNIT=%n Restart=on-failure TimeoutStopSec=70 ExecStart=/usr/bin/podman start foobar -ExecStop=/usr/bin/podman stop -t 10 foobar -ExecStopPost=/usr/bin/podman stop -t 10 foobar +ExecStop=/usr/bin/podman stop \ + -t 10 foobar +ExecStopPost=/usr/bin/podman stop \ + -t 10 foobar PIDFile=/run/containers/storage/overlay-containers/639c53578af4d84b8800b4635fa4e680ee80fd67e0e6a2d4eea48d1e3230f401/userdata/conmon.pid Type=forking @@ -107,8 +111,10 @@ Environment=PODMAN_SYSTEMD_UNIT=%n Restart=on-failure TimeoutStopSec=70 ExecStart=/usr/bin/podman start foobar -ExecStop=/usr/bin/podman stop -t 10 foobar -ExecStopPost=/usr/bin/podman stop -t 10 foobar +ExecStop=/usr/bin/podman stop \ + -t 10 foobar +ExecStopPost=/usr/bin/podman stop \ + -t 10 foobar PIDFile=/run/containers/storage/overlay-containers/639c53578af4d84b8800b4635fa4e680ee80fd67e0e6a2d4eea48d1e3230f401/userdata/conmon.pid Type=forking @@ -134,8 +140,10 @@ Environment=PODMAN_SYSTEMD_UNIT=%n Restart=on-failure TimeoutStopSec=70 ExecStart=/usr/bin/podman start foobar -ExecStop=/usr/bin/podman stop -t 10 foobar -ExecStopPost=/usr/bin/podman stop -t 10 foobar +ExecStop=/usr/bin/podman stop \ + -t 10 foobar +ExecStopPost=/usr/bin/podman stop \ + -t 10 foobar PIDFile=/run/containers/storage/overlay-containers/639c53578af4d84b8800b4635fa4e680ee80fd67e0e6a2d4eea48d1e3230f401/userdata/conmon.pid Type=forking @@ -161,8 +169,10 @@ Environment=PODMAN_SYSTEMD_UNIT=%n Restart=on-failure TimeoutStopSec=70 ExecStart=/usr/bin/podman start foobar -ExecStop=/usr/bin/podman stop -t 10 foobar -ExecStopPost=/usr/bin/podman stop -t 10 foobar +ExecStop=/usr/bin/podman stop \ + -t 10 foobar +ExecStopPost=/usr/bin/podman stop \ + -t 10 foobar PIDFile=/run/containers/storage/overlay-containers/639c53578af4d84b8800b4635fa4e680ee80fd67e0e6a2d4eea48d1e3230f401/userdata/conmon.pid Type=forking @@ -188,8 +198,10 @@ Environment=PODMAN_SYSTEMD_UNIT=%n Restart=on-failure TimeoutStopSec=70 ExecStart=/usr/bin/podman start foobar -ExecStop=/usr/bin/podman stop -t 10 foobar -ExecStopPost=/usr/bin/podman stop -t 10 foobar +ExecStop=/usr/bin/podman stop \ + -t 10 foobar +ExecStopPost=/usr/bin/podman stop \ + -t 10 foobar PIDFile=/run/containers/storage/overlay-containers/639c53578af4d84b8800b4635fa4e680ee80fd67e0e6a2d4eea48d1e3230f401/userdata/conmon.pid Type=forking @@ -217,8 +229,10 @@ Environment=PODMAN_SYSTEMD_UNIT=%n Restart=on-failure TimeoutStopSec=70 ExecStart=/usr/bin/podman start foobar -ExecStop=/usr/bin/podman stop -t 10 foobar -ExecStopPost=/usr/bin/podman stop -t 10 foobar +ExecStop=/usr/bin/podman stop \ + -t 10 foobar +ExecStopPost=/usr/bin/podman stop \ + -t 10 foobar PIDFile=/run/containers/storage/overlay-containers/639c53578af4d84b8800b4635fa4e680ee80fd67e0e6a2d4eea48d1e3230f401/userdata/conmon.pid Type=forking @@ -243,8 +257,10 @@ Environment=PODMAN_SYSTEMD_UNIT=%n Restart=on-failure TimeoutStopSec=70 ExecStart=/usr/bin/podman start foobar -ExecStop=/usr/bin/podman stop -t 10 foobar -ExecStopPost=/usr/bin/podman stop -t 10 foobar +ExecStop=/usr/bin/podman stop \ + -t 10 foobar +ExecStopPost=/usr/bin/podman stop \ + -t 10 foobar PIDFile=/run/containers/storage/overlay-containers/639c53578af4d84b8800b4635fa4e680ee80fd67e0e6a2d4eea48d1e3230f401/userdata/conmon.pid Type=forking @@ -266,7 +282,8 @@ RequiresMountsFor=/var/run/containers/storage Environment=PODMAN_SYSTEMD_UNIT=%n Restart=on-failure TimeoutStopSec=70 -ExecStartPre=/bin/rm -f %t/%n.ctr-id +ExecStartPre=/bin/rm \ + -f %t/%n.ctr-id ExecStart=/usr/bin/podman container run \ --cidfile=%t/%n.ctr-id \ --cgroups=no-conmon \ @@ -276,8 +293,13 @@ ExecStart=/usr/bin/podman container run \ --replace \ --name jadda-jadda \ --hostname hello-world awesome-image:latest command arg1 ... argN "foo=arg \"with \" space" -ExecStop=/usr/bin/podman stop --ignore --cidfile=%t/%n.ctr-id -ExecStopPost=/usr/bin/podman rm -f --ignore --cidfile=%t/%n.ctr-id +ExecStop=/usr/bin/podman stop \ + --ignore \ + --cidfile=%t/%n.ctr-id +ExecStopPost=/usr/bin/podman rm \ + -f \ + --ignore \ + --cidfile=%t/%n.ctr-id Type=notify NotifyAccess=all @@ -299,7 +321,8 @@ RequiresMountsFor=/var/run/containers/storage Environment=PODMAN_SYSTEMD_UNIT=%n Restart=on-failure TimeoutStopSec=70 -ExecStartPre=/bin/rm -f %t/%n.ctr-id +ExecStartPre=/bin/rm \ + -f %t/%n.ctr-id ExecStart=/usr/bin/podman container run \ --cidfile=%t/%n.ctr-id \ --cgroups=no-conmon \ @@ -309,8 +332,13 @@ ExecStart=/usr/bin/podman container run \ --sdnotify=container \ --name jadda-jadda \ --hostname hello-world awesome-image:latest command arg1 ... argN "foo=arg \"with \" space" -ExecStop=/usr/bin/podman stop --ignore --cidfile=%t/%n.ctr-id -ExecStopPost=/usr/bin/podman rm -f --ignore --cidfile=%t/%n.ctr-id +ExecStop=/usr/bin/podman stop \ + --ignore \ + --cidfile=%t/%n.ctr-id +ExecStopPost=/usr/bin/podman rm \ + -f \ + --ignore \ + --cidfile=%t/%n.ctr-id Type=notify NotifyAccess=all @@ -332,7 +360,8 @@ RequiresMountsFor=/var/run/containers/storage Environment=PODMAN_SYSTEMD_UNIT=%n Restart=on-failure TimeoutStopSec=70 -ExecStartPre=/bin/rm -f %t/%n.ctr-id +ExecStartPre=/bin/rm \ + -f %t/%n.ctr-id ExecStart=/usr/bin/podman container run \ --cidfile=%t/%n.ctr-id \ --cgroups=no-conmon \ @@ -342,8 +371,13 @@ ExecStart=/usr/bin/podman container run \ --replace \ --name jadda-jadda \ --hostname hello-world awesome-image:latest command arg1 ... argN "foo=arg \"with \" space" -ExecStop=/usr/bin/podman stop --ignore --cidfile=%t/%n.ctr-id -ExecStopPost=/usr/bin/podman rm -f --ignore --cidfile=%t/%n.ctr-id +ExecStop=/usr/bin/podman stop \ + --ignore \ + --cidfile=%t/%n.ctr-id +ExecStopPost=/usr/bin/podman rm \ + -f \ + --ignore \ + --cidfile=%t/%n.ctr-id Type=notify NotifyAccess=all @@ -365,7 +399,8 @@ RequiresMountsFor=/var/run/containers/storage Environment=PODMAN_SYSTEMD_UNIT=%n Restart=on-failure TimeoutStopSec=70 -ExecStartPre=/bin/rm -f %t/%n.ctr-id +ExecStartPre=/bin/rm \ + -f %t/%n.ctr-id ExecStart=/usr/bin/podman run \ --cidfile=%t/%n.ctr-id \ --cgroups=no-conmon \ @@ -375,8 +410,13 @@ ExecStart=/usr/bin/podman run \ -d \ --name jadda-jadda \ --hostname hello-world awesome-image:latest command arg1 ... argN -ExecStop=/usr/bin/podman stop --ignore --cidfile=%t/%n.ctr-id -ExecStopPost=/usr/bin/podman rm -f --ignore --cidfile=%t/%n.ctr-id +ExecStop=/usr/bin/podman stop \ + --ignore \ + --cidfile=%t/%n.ctr-id +ExecStopPost=/usr/bin/podman rm \ + -f \ + --ignore \ + --cidfile=%t/%n.ctr-id Type=notify NotifyAccess=all @@ -398,7 +438,8 @@ RequiresMountsFor=/var/run/containers/storage Environment=PODMAN_SYSTEMD_UNIT=%n Restart=on-failure TimeoutStopSec=70 -ExecStartPre=/bin/rm -f %t/%n.ctr-id +ExecStartPre=/bin/rm \ + -f %t/%n.ctr-id ExecStart=/usr/bin/podman run \ --cidfile=%t/%n.ctr-id \ --cgroups=no-conmon \ @@ -409,8 +450,13 @@ ExecStart=/usr/bin/podman run \ -d \ --name jadda-jadda \ --hostname hello-world awesome-image:latest command arg1 ... argN -ExecStop=/usr/bin/podman stop --ignore --cidfile=%t/%n.ctr-id -ExecStopPost=/usr/bin/podman rm -f --ignore --cidfile=%t/%n.ctr-id +ExecStop=/usr/bin/podman stop \ + --ignore \ + --cidfile=%t/%n.ctr-id +ExecStopPost=/usr/bin/podman rm \ + -f \ + --ignore \ + --cidfile=%t/%n.ctr-id Type=notify NotifyAccess=all @@ -432,7 +478,8 @@ RequiresMountsFor=/var/run/containers/storage Environment=PODMAN_SYSTEMD_UNIT=%n Restart=on-failure TimeoutStopSec=70 -ExecStartPre=/bin/rm -f %t/%n.ctr-id +ExecStartPre=/bin/rm \ + -f %t/%n.ctr-id ExecStart=/usr/bin/podman run \ --cidfile=%t/%n.ctr-id \ --cgroups=no-conmon \ @@ -442,8 +489,13 @@ ExecStart=/usr/bin/podman run \ --detach \ --name jadda-jadda \ --hostname hello-world awesome-image:latest command arg1 ... argN -ExecStop=/usr/bin/podman stop --ignore --cidfile=%t/%n.ctr-id -ExecStopPost=/usr/bin/podman rm -f --ignore --cidfile=%t/%n.ctr-id +ExecStop=/usr/bin/podman stop \ + --ignore \ + --cidfile=%t/%n.ctr-id +ExecStopPost=/usr/bin/podman rm \ + -f \ + --ignore \ + --cidfile=%t/%n.ctr-id Type=notify NotifyAccess=all @@ -465,15 +517,21 @@ RequiresMountsFor=/var/run/containers/storage Environment=PODMAN_SYSTEMD_UNIT=%n Restart=on-failure TimeoutStopSec=70 -ExecStartPre=/bin/rm -f %t/%n.ctr-id +ExecStartPre=/bin/rm \ + -f %t/%n.ctr-id ExecStart=/usr/bin/podman run \ --cidfile=%t/%n.ctr-id \ --cgroups=no-conmon \ --rm \ --sdnotify=conmon \ -d awesome-image:latest -ExecStop=/usr/bin/podman stop --ignore --cidfile=%t/%n.ctr-id -ExecStopPost=/usr/bin/podman rm -f --ignore --cidfile=%t/%n.ctr-id +ExecStop=/usr/bin/podman stop \ + --ignore \ + --cidfile=%t/%n.ctr-id +ExecStopPost=/usr/bin/podman rm \ + -f \ + --ignore \ + --cidfile=%t/%n.ctr-id Type=notify NotifyAccess=all @@ -496,7 +554,8 @@ RequiresMountsFor=/var/run/containers/storage Environment=PODMAN_SYSTEMD_UNIT=%n Restart=on-failure TimeoutStopSec=102 -ExecStartPre=/bin/rm -f %t/%n.ctr-id +ExecStartPre=/bin/rm \ + -f %t/%n.ctr-id ExecStart=/usr/bin/podman run \ --cidfile=%t/%n.ctr-id \ --cgroups=no-conmon \ @@ -505,8 +564,13 @@ ExecStart=/usr/bin/podman run \ ` + detachparam + ` awesome-image:latest -ExecStop=/usr/bin/podman stop --ignore --cidfile=%t/%n.ctr-id -ExecStopPost=/usr/bin/podman rm -f --ignore --cidfile=%t/%n.ctr-id +ExecStop=/usr/bin/podman stop \ + --ignore \ + --cidfile=%t/%n.ctr-id +ExecStopPost=/usr/bin/podman rm \ + -f \ + --ignore \ + --cidfile=%t/%n.ctr-id Type=notify NotifyAccess=all @@ -530,7 +594,8 @@ RequiresMountsFor=/var/run/containers/storage Environment=PODMAN_SYSTEMD_UNIT=%n Restart=on-failure TimeoutStopSec=102 -ExecStartPre=/bin/rm -f %t/%n.ctr-id +ExecStartPre=/bin/rm \ + -f %t/%n.ctr-id ExecStart=/usr/bin/podman run \ --cidfile=%t/%n.ctr-id \ --cgroups=no-conmon \ @@ -541,8 +606,13 @@ ExecStart=/usr/bin/podman run \ --name test \ -p 80:80 awesome-image:latest somecmd \ --detach=false -ExecStop=/usr/bin/podman stop --ignore --cidfile=%t/%n.ctr-id -ExecStopPost=/usr/bin/podman rm -f --ignore --cidfile=%t/%n.ctr-id +ExecStop=/usr/bin/podman stop \ + --ignore \ + --cidfile=%t/%n.ctr-id +ExecStopPost=/usr/bin/podman rm \ + -f \ + --ignore \ + --cidfile=%t/%n.ctr-id Type=notify NotifyAccess=all @@ -564,7 +634,8 @@ RequiresMountsFor=/var/run/containers/storage Environment=PODMAN_SYSTEMD_UNIT=%n Restart=on-failure TimeoutStopSec=102 -ExecStartPre=/bin/rm -f %t/%n.ctr-id +ExecStartPre=/bin/rm \ + -f %t/%n.ctr-id ExecStart=/usr/bin/podman \ --events-backend none \ --runroot /root run \ @@ -573,8 +644,13 @@ ExecStart=/usr/bin/podman \ --rm \ --sdnotify=conmon \ -d awesome-image:latest -ExecStop=/usr/bin/podman stop --ignore --cidfile=%t/%n.ctr-id -ExecStopPost=/usr/bin/podman rm -f --ignore --cidfile=%t/%n.ctr-id +ExecStop=/usr/bin/podman stop \ + --ignore \ + --cidfile=%t/%n.ctr-id +ExecStopPost=/usr/bin/podman rm \ + -f \ + --ignore \ + --cidfile=%t/%n.ctr-id Type=notify NotifyAccess=all @@ -596,15 +672,21 @@ RequiresMountsFor=/var/run/containers/storage Environment=PODMAN_SYSTEMD_UNIT=%n Restart=on-failure TimeoutStopSec=70 -ExecStartPre=/bin/rm -f %t/%n.ctr-id +ExecStartPre=/bin/rm \ + -f %t/%n.ctr-id ExecStart=/usr/bin/podman container run \ --cidfile=%t/%n.ctr-id \ --cgroups=no-conmon \ --rm \ --sdnotify=conmon \ -d awesome-image:latest -ExecStop=/usr/bin/podman stop --ignore --cidfile=%t/%n.ctr-id -ExecStopPost=/usr/bin/podman rm -f --ignore --cidfile=%t/%n.ctr-id +ExecStop=/usr/bin/podman stop \ + --ignore \ + --cidfile=%t/%n.ctr-id +ExecStopPost=/usr/bin/podman rm \ + -f \ + --ignore \ + --cidfile=%t/%n.ctr-id Type=notify NotifyAccess=all @@ -626,7 +708,8 @@ RequiresMountsFor=/var/run/containers/storage Environment=PODMAN_SYSTEMD_UNIT=%n Restart=on-failure TimeoutStopSec=70 -ExecStartPre=/bin/rm -f %t/%n.ctr-id +ExecStartPre=/bin/rm \ + -f %t/%n.ctr-id ExecStart=/usr/bin/podman run \ --cidfile=%t/%n.ctr-id \ --cgroups=no-conmon \ @@ -637,8 +720,13 @@ ExecStart=/usr/bin/podman run \ --name test \ --log-driver=journald \ --log-opt=tag={{.Name}} awesome-image:latest -ExecStop=/usr/bin/podman stop --ignore --cidfile=%t/%n.ctr-id -ExecStopPost=/usr/bin/podman rm -f --ignore --cidfile=%t/%n.ctr-id +ExecStop=/usr/bin/podman stop \ + --ignore \ + --cidfile=%t/%n.ctr-id +ExecStopPost=/usr/bin/podman rm \ + -f \ + --ignore \ + --cidfile=%t/%n.ctr-id Type=notify NotifyAccess=all @@ -660,7 +748,8 @@ RequiresMountsFor=/var/run/containers/storage Environment=PODMAN_SYSTEMD_UNIT=%n Restart=on-failure TimeoutStopSec=70 -ExecStartPre=/bin/rm -f %t/%n.ctr-id +ExecStartPre=/bin/rm \ + -f %t/%n.ctr-id ExecStart=/usr/bin/podman run \ --cidfile=%t/%n.ctr-id \ --cgroups=no-conmon \ @@ -670,8 +759,13 @@ ExecStart=/usr/bin/podman run \ --replace \ --name test awesome-image:latest sh \ -c "kill $$$$ && echo %%\\" -ExecStop=/usr/bin/podman stop --ignore --cidfile=%t/%n.ctr-id -ExecStopPost=/usr/bin/podman rm -f --ignore --cidfile=%t/%n.ctr-id +ExecStop=/usr/bin/podman stop \ + --ignore \ + --cidfile=%t/%n.ctr-id +ExecStopPost=/usr/bin/podman rm \ + -f \ + --ignore \ + --cidfile=%t/%n.ctr-id Type=notify NotifyAccess=all @@ -693,7 +787,8 @@ RequiresMountsFor=/var/run/containers/storage Environment=PODMAN_SYSTEMD_UNIT=%n Restart=on-failure TimeoutStopSec=70 -ExecStartPre=/bin/rm -f %t/%n.ctr-id +ExecStartPre=/bin/rm \ + -f %t/%n.ctr-id ExecStart=/usr/bin/podman run \ --cidfile=%t/%n.ctr-id \ --cgroups=no-conmon \ @@ -704,8 +799,13 @@ ExecStart=/usr/bin/podman run \ --cgroups=foo \ --conmon-pidfile=foo \ --cidfile=foo alpine -ExecStop=/usr/bin/podman stop --ignore --cidfile=%t/%n.ctr-id -ExecStopPost=/usr/bin/podman rm -f --ignore --cidfile=%t/%n.ctr-id +ExecStop=/usr/bin/podman stop \ + --ignore \ + --cidfile=%t/%n.ctr-id +ExecStopPost=/usr/bin/podman rm \ + -f \ + --ignore \ + --cidfile=%t/%n.ctr-id Type=notify NotifyAccess=all @@ -727,7 +827,8 @@ RequiresMountsFor=/var/run/containers/storage Environment=PODMAN_SYSTEMD_UNIT=%n Restart=on-failure TimeoutStopSec=70 -ExecStartPre=/bin/rm -f %t/%n.ctr-id +ExecStartPre=/bin/rm \ + -f %t/%n.ctr-id ExecStart=/usr/bin/podman run \ --cidfile=%t/%n.ctr-id \ --cgroups=no-conmon \ @@ -740,8 +841,13 @@ ExecStart=/usr/bin/podman run \ --conmon-pidfile=foo \ --cidfile=foo \ --pod-id-file /tmp/pod-foobar.pod-id-file alpine -ExecStop=/usr/bin/podman stop --ignore --cidfile=%t/%n.ctr-id -ExecStopPost=/usr/bin/podman rm -f --ignore --cidfile=%t/%n.ctr-id +ExecStop=/usr/bin/podman stop \ + --ignore \ + --cidfile=%t/%n.ctr-id +ExecStopPost=/usr/bin/podman rm \ + -f \ + --ignore \ + --cidfile=%t/%n.ctr-id Type=notify NotifyAccess=all @@ -764,7 +870,8 @@ Environment=PODMAN_SYSTEMD_UNIT=%n Environment=FOO=abc "BAR=my test" USER=%%a Restart=on-failure TimeoutStopSec=70 -ExecStartPre=/bin/rm -f %t/%n.ctr-id +ExecStartPre=/bin/rm \ + -f %t/%n.ctr-id ExecStart=/usr/bin/podman run \ --cidfile=%t/%n.ctr-id \ --cgroups=no-conmon \ @@ -775,8 +882,13 @@ ExecStart=/usr/bin/podman run \ --env=BAR \ --env=MYENV=2 \ -e USER awesome-image:latest -ExecStop=/usr/bin/podman stop --ignore --cidfile=%t/%n.ctr-id -ExecStopPost=/usr/bin/podman rm -f --ignore --cidfile=%t/%n.ctr-id +ExecStop=/usr/bin/podman stop \ + --ignore \ + --cidfile=%t/%n.ctr-id +ExecStopPost=/usr/bin/podman rm \ + -f \ + --ignore \ + --cidfile=%t/%n.ctr-id Type=notify NotifyAccess=all @@ -802,8 +914,10 @@ Environment=USER=%%a Restart=on-failure TimeoutStopSec=70 ExecStart=/usr/bin/podman start foobar -ExecStop=/usr/bin/podman stop -t 10 foobar -ExecStopPost=/usr/bin/podman stop -t 10 foobar +ExecStop=/usr/bin/podman stop \ + -t 10 foobar +ExecStopPost=/usr/bin/podman stop \ + -t 10 foobar PIDFile=/run/containers/storage/overlay-containers/639c53578af4d84b8800b4635fa4e680ee80fd67e0e6a2d4eea48d1e3230f401/userdata/conmon.pid Type=forking @@ -826,15 +940,21 @@ Environment=PODMAN_SYSTEMD_UNIT=%n Restart=on-failure StartLimitBurst=42 TimeoutStopSec=70 -ExecStartPre=/bin/rm -f %t/%n.ctr-id +ExecStartPre=/bin/rm \ + -f %t/%n.ctr-id ExecStart=/usr/bin/podman run \ --cidfile=%t/%n.ctr-id \ --cgroups=no-conmon \ --rm \ --sdnotify=conmon \ -d awesome-image:latest -ExecStop=/usr/bin/podman stop --ignore --cidfile=%t/%n.ctr-id -ExecStopPost=/usr/bin/podman rm -f --ignore --cidfile=%t/%n.ctr-id +ExecStop=/usr/bin/podman stop \ + --ignore \ + --cidfile=%t/%n.ctr-id +ExecStopPost=/usr/bin/podman rm \ + -f \ + --ignore \ + --cidfile=%t/%n.ctr-id Type=notify NotifyAccess=all @@ -856,7 +976,8 @@ RequiresMountsFor=/var/run/containers/storage Environment=PODMAN_SYSTEMD_UNIT=%n Restart=on-failure TimeoutStopSec=70 -ExecStartPre=/bin/rm -f %t/%n.ctr-id +ExecStartPre=/bin/rm \ + -f %t/%n.ctr-id ExecStart=/usr/bin/podman run \ --cidfile=%t/%n.ctr-id \ --cgroups=no-conmon \ @@ -864,8 +985,13 @@ ExecStart=/usr/bin/podman run \ --sdnotify=conmon \ -d \ -h hostname awesome-image:latest -ExecStop=/usr/bin/podman stop --ignore --cidfile=%t/%n.ctr-id -ExecStopPost=/usr/bin/podman rm -f --ignore --cidfile=%t/%n.ctr-id +ExecStop=/usr/bin/podman stop \ + --ignore \ + --cidfile=%t/%n.ctr-id +ExecStopPost=/usr/bin/podman rm \ + -f \ + --ignore \ + --cidfile=%t/%n.ctr-id Type=notify NotifyAccess=all @@ -888,7 +1014,8 @@ Environment=PODMAN_SYSTEMD_UNIT=%n-%i Restart=on-failure StartLimitBurst=42 TimeoutStopSec=70 -ExecStartPre=/bin/rm -f %t/%n.ctr-id +ExecStartPre=/bin/rm \ + -f %t/%n.ctr-id ExecStart=/usr/bin/podman run \ --name=container-foo-%i \ --cidfile=%t/%n.ctr-id \ @@ -896,8 +1023,13 @@ ExecStart=/usr/bin/podman run \ --rm \ --sdnotify=conmon \ -d awesome-image:latest -ExecStop=/usr/bin/podman stop --ignore --cidfile=%t/%n.ctr-id -ExecStopPost=/usr/bin/podman rm -f --ignore --cidfile=%t/%n.ctr-id +ExecStop=/usr/bin/podman stop \ + --ignore \ + --cidfile=%t/%n.ctr-id +ExecStopPost=/usr/bin/podman rm \ + -f \ + --ignore \ + --cidfile=%t/%n.ctr-id Type=notify NotifyAccess=all diff --git a/pkg/systemd/generate/pods.go b/pkg/systemd/generate/pods.go index 729a038a5..588bfb430 100644 --- a/pkg/systemd/generate/pods.go +++ b/pkg/systemd/generate/pods.go @@ -294,9 +294,9 @@ func executePodTemplate(info *podInfo, options entities.GenerateSystemdOptions) } info.EnvVariable = define.EnvVariable - info.ExecStart = "{{{{.Executable}}}} start {{{{.InfraNameOrID}}}}" - info.ExecStop = "{{{{.Executable}}}} stop {{{{if (ge .StopTimeout 0)}}}}-t {{{{.StopTimeout}}}}{{{{end}}}} {{{{.InfraNameOrID}}}}" - info.ExecStopPost = "{{{{.Executable}}}} stop {{{{if (ge .StopTimeout 0)}}}}-t {{{{.StopTimeout}}}}{{{{end}}}} {{{{.InfraNameOrID}}}}" + info.ExecStart = formatOptionsString("{{{{.Executable}}}} start {{{{.InfraNameOrID}}}}") + info.ExecStop = formatOptionsString("{{{{.Executable}}}} stop {{{{if (ge .StopTimeout 0)}}}} -t {{{{.StopTimeout}}}}{{{{end}}}} {{{{.InfraNameOrID}}}}") + info.ExecStopPost = formatOptionsString("{{{{.Executable}}}} stop {{{{if (ge .StopTimeout 0)}}}} -t {{{{.StopTimeout}}}}{{{{end}}}} {{{{.InfraNameOrID}}}}") // Assemble the ExecStart command when creating a new pod. // @@ -371,11 +371,11 @@ func executePodTemplate(info *podInfo, options entities.GenerateSystemdOptions) startCommand = append(startCommand, podCreateArgs...) startCommand = escapeSystemdArguments(startCommand) - info.ExecStartPre1 = "/bin/rm -f {{{{.PIDFile}}}} {{{{.PodIDFile}}}}" - info.ExecStartPre2 = strings.Join(startCommand, " ") - info.ExecStart = "{{{{.Executable}}}} {{{{if .RootFlags}}}}{{{{ .RootFlags}}}} {{{{end}}}}pod start --pod-id-file {{{{.PodIDFile}}}}" - info.ExecStop = "{{{{.Executable}}}} {{{{if .RootFlags}}}}{{{{ .RootFlags}}}} {{{{end}}}}pod stop --ignore --pod-id-file {{{{.PodIDFile}}}} {{{{if (ge .StopTimeout 0)}}}}-t {{{{.StopTimeout}}}}{{{{end}}}}" - info.ExecStopPost = "{{{{.Executable}}}} {{{{if .RootFlags}}}}{{{{ .RootFlags}}}} {{{{end}}}}pod rm --ignore -f --pod-id-file {{{{.PodIDFile}}}}" + info.ExecStartPre1 = formatOptionsString("/bin/rm -f {{{{.PIDFile}}}} {{{{.PodIDFile}}}}") + info.ExecStartPre2 = formatOptions(startCommand) + info.ExecStart = formatOptionsString("{{{{.Executable}}}} {{{{if .RootFlags}}}}{{{{ .RootFlags}}}} {{{{end}}}}pod start --pod-id-file {{{{.PodIDFile}}}}") + info.ExecStop = formatOptionsString("{{{{.Executable}}}} {{{{if .RootFlags}}}}{{{{ .RootFlags}}}} {{{{end}}}}pod stop --ignore --pod-id-file {{{{.PodIDFile}}}} {{{{if (ge .StopTimeout 0)}}}} -t {{{{.StopTimeout}}}}{{{{end}}}}") + info.ExecStopPost = formatOptionsString("{{{{.Executable}}}} {{{{if .RootFlags}}}}{{{{ .RootFlags}}}} {{{{end}}}}pod rm --ignore -f --pod-id-file {{{{.PodIDFile}}}}") } info.TimeoutStopSec = minTimeoutStopSec + info.StopTimeout diff --git a/pkg/systemd/generate/pods_test.go b/pkg/systemd/generate/pods_test.go index 000d73e9a..c44ab111e 100644 --- a/pkg/systemd/generate/pods_test.go +++ b/pkg/systemd/generate/pods_test.go @@ -79,8 +79,10 @@ Environment=PODMAN_SYSTEMD_UNIT=%n Restart=on-failure TimeoutStopSec=102 ExecStart=/usr/bin/podman start jadda-jadda-infra -ExecStop=/usr/bin/podman stop -t 42 jadda-jadda-infra -ExecStopPost=/usr/bin/podman stop -t 42 jadda-jadda-infra +ExecStop=/usr/bin/podman stop \ + -t 42 jadda-jadda-infra +ExecStopPost=/usr/bin/podman stop \ + -t 42 jadda-jadda-infra PIDFile=/run/containers/storage/overlay-containers/639c53578af4d84b8800b4635fa4e680ee80fd67e0e6a2d4eea48d1e3230f401/userdata/conmon.pid Type=forking @@ -107,8 +109,10 @@ Environment=PODMAN_SYSTEMD_UNIT=%n Restart=on-failure TimeoutStopSec=102 ExecStart=/usr/bin/podman start jadda-jadda-infra -ExecStop=/usr/bin/podman stop -t 42 jadda-jadda-infra -ExecStopPost=/usr/bin/podman stop -t 42 jadda-jadda-infra +ExecStop=/usr/bin/podman stop \ + -t 42 jadda-jadda-infra +ExecStopPost=/usr/bin/podman stop \ + -t 42 jadda-jadda-infra PIDFile=/run/containers/storage/overlay-containers/639c53578af4d84b8800b4635fa4e680ee80fd67e0e6a2d4eea48d1e3230f401/userdata/conmon.pid Type=forking @@ -136,8 +140,10 @@ Environment=PODMAN_SYSTEMD_UNIT=%n Restart=on-failure TimeoutStopSec=102 ExecStart=/usr/bin/podman start jadda-jadda-infra -ExecStop=/usr/bin/podman stop -t 42 jadda-jadda-infra -ExecStopPost=/usr/bin/podman stop -t 42 jadda-jadda-infra +ExecStop=/usr/bin/podman stop \ + -t 42 jadda-jadda-infra +ExecStopPost=/usr/bin/podman stop \ + -t 42 jadda-jadda-infra PIDFile=/run/containers/storage/overlay-containers/639c53578af4d84b8800b4635fa4e680ee80fd67e0e6a2d4eea48d1e3230f401/userdata/conmon.pid Type=forking @@ -164,8 +170,10 @@ Environment=PODMAN_SYSTEMD_UNIT=%n Restart=on-failure TimeoutStopSec=102 ExecStart=/usr/bin/podman start jadda-jadda-infra -ExecStop=/usr/bin/podman stop -t 42 jadda-jadda-infra -ExecStopPost=/usr/bin/podman stop -t 42 jadda-jadda-infra +ExecStop=/usr/bin/podman stop \ + -t 42 jadda-jadda-infra +ExecStopPost=/usr/bin/podman stop \ + -t 42 jadda-jadda-infra PIDFile=/run/containers/storage/overlay-containers/639c53578af4d84b8800b4635fa4e680ee80fd67e0e6a2d4eea48d1e3230f401/userdata/conmon.pid Type=forking @@ -192,8 +200,10 @@ Environment=PODMAN_SYSTEMD_UNIT=%n Restart=on-failure TimeoutStopSec=102 ExecStart=/usr/bin/podman start jadda-jadda-infra -ExecStop=/usr/bin/podman stop -t 42 jadda-jadda-infra -ExecStopPost=/usr/bin/podman stop -t 42 jadda-jadda-infra +ExecStop=/usr/bin/podman stop \ + -t 42 jadda-jadda-infra +ExecStopPost=/usr/bin/podman stop \ + -t 42 jadda-jadda-infra PIDFile=/run/containers/storage/overlay-containers/639c53578af4d84b8800b4635fa4e680ee80fd67e0e6a2d4eea48d1e3230f401/userdata/conmon.pid Type=forking @@ -222,8 +232,10 @@ Environment=PODMAN_SYSTEMD_UNIT=%n Restart=on-failure TimeoutStopSec=102 ExecStart=/usr/bin/podman start jadda-jadda-infra -ExecStop=/usr/bin/podman stop -t 42 jadda-jadda-infra -ExecStopPost=/usr/bin/podman stop -t 42 jadda-jadda-infra +ExecStop=/usr/bin/podman stop \ + -t 42 jadda-jadda-infra +ExecStopPost=/usr/bin/podman stop \ + -t 42 jadda-jadda-infra PIDFile=/run/containers/storage/overlay-containers/639c53578af4d84b8800b4635fa4e680ee80fd67e0e6a2d4eea48d1e3230f401/userdata/conmon.pid Type=forking @@ -246,11 +258,22 @@ Before= 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 +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 @@ -276,8 +299,10 @@ Restart=on-failure RestartSec=15 TimeoutStopSec=102 ExecStart=/usr/bin/podman start jadda-jadda-infra -ExecStop=/usr/bin/podman stop -t 42 jadda-jadda-infra -ExecStopPost=/usr/bin/podman stop -t 42 jadda-jadda-infra +ExecStop=/usr/bin/podman stop \ + -t 42 jadda-jadda-infra +ExecStopPost=/usr/bin/podman stop \ + -t 42 jadda-jadda-infra PIDFile=/run/containers/storage/overlay-containers/639c53578af4d84b8800b4635fa4e680ee80fd67e0e6a2d4eea48d1e3230f401/userdata/conmon.pid Type=forking @@ -301,11 +326,24 @@ Before=container-1.service container-2.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 --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 +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 \ + --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 PIDFile=%t/pod-123abc.pid Type=forking @@ -329,11 +367,26 @@ Before=container-1.service container-2.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 --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 +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 \ + --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 PIDFile=%t/pod-123abc.pid Type=forking @@ -357,11 +410,24 @@ Before=container-1.service container-2.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 --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 +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 \ + --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 PIDFile=%t/pod-123abc.pid Type=forking @@ -385,11 +451,25 @@ Before=container-1.service container-2.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 --name foo --label key={{someval}} --exit-policy=continue --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 +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 \ + --label key={{someval}} \ + --exit-policy=continue \ + --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 PIDFile=%t/pod-123abc.pid Type=forking diff --git a/test/apiv2/50-secrets.at b/test/apiv2/50-secrets.at index ed0e8fb6b..acd8f3de9 100644 --- a/test/apiv2/50-secrets.at +++ b/test/apiv2/50-secrets.at @@ -7,9 +7,6 @@ t POST secrets/create Name=mysecret Data=c2VjcmV0 200\ .ID~.* \ -# secret create unsupported labels -t POST secrets/create Name=mysecret Data=c2VjcmV0 Labels='{"fail":"fail"}' 400 - # secret create name already in use t POST secrets/create Name=mysecret Data=c2VjcmV0 409 @@ -59,8 +56,15 @@ t GET libpod/secrets/json?filters='garb1age}' 500 \ t GET libpod/secrets/json?filters='{"label":["testl' 500 \ .cause="unexpected end of JSON input" +# secret with labels +t POST secrets/create Name=labeledsecret Data=c2VjcmV0 Labels='{"foo":"bar"}' 200 +t GET secrets/labeledsecret 200 \ + .Spec.Labels.foo=bar + # secret rm t DELETE secrets/mysecret 204 +t DELETE secrets/labeledsecret 204 + # secret rm non-existent secret t DELETE secrets/bogus 404 diff --git a/test/e2e/generate_systemd_test.go b/test/e2e/generate_systemd_test.go index 49d7a66f2..01c0aefab 100644 --- a/test/e2e/generate_systemd_test.go +++ b/test/e2e/generate_systemd_test.go @@ -107,7 +107,7 @@ var _ = Describe("Podman generate systemd", func() { session.WaitWithDefaultTimeout() Expect(session).Should(Exit(0)) Expect(session.OutputToString()).To(ContainSubstring("TimeoutStopSec=1294")) - Expect(session.OutputToString()).To(ContainSubstring(" stop -t 1234 ")) + Expect(session.OutputToString()).To(ContainSubstring("-t 1234")) }) It("podman generate systemd", func() { @@ -148,14 +148,15 @@ var _ = Describe("Podman generate systemd", func() { Expect(session).Should(Exit(0)) Expect(session.OutputToString()).To(ContainSubstring("TimeoutStopSec=65")) Expect(session.OutputToString()).ToNot(ContainSubstring("TimeoutStartSec=")) - Expect(session.OutputToString()).To(ContainSubstring("podman stop -t 5")) + Expect(session.OutputToString()).To(ContainSubstring("podman stop")) + Expect(session.OutputToString()).To(ContainSubstring("-t 5")) session = podmanTest.Podman([]string{"generate", "systemd", "--stop-timeout", "5", "--start-timeout", "123", "nginx"}) session.WaitWithDefaultTimeout() Expect(session).Should(Exit(0)) Expect(session.OutputToString()).To(ContainSubstring("TimeoutStartSec=123")) Expect(session.OutputToString()).To(ContainSubstring("TimeoutStopSec=65")) - Expect(session.OutputToString()).To(ContainSubstring("podman stop -t 5")) + Expect(session.OutputToString()).To(ContainSubstring("-t 5")) }) It("podman generate systemd with user-defined dependencies", func() { @@ -227,7 +228,8 @@ var _ = Describe("Podman generate systemd", func() { 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("podman stop")) + Expect(output).To(ContainSubstring("-t 42 foo-2")) Expect(output).To(ContainSubstring("BindsTo=pod-foo.service")) Expect(output).To(ContainSubstring("PIDFile=")) Expect(output).To(ContainSubstring("/userdata/conmon.pid")) @@ -563,10 +565,21 @@ var _ = Describe("Podman generate systemd", func() { Expect(session.OutputToString()).To(ContainSubstring("# pod-foo.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 --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")) + Expect(session.OutputToString()).To(ContainSubstring("pod create")) + Expect(session.OutputToString()).To(ContainSubstring("--infra-conmon-pidfile %t/pod-foo.pid")) + Expect(session.OutputToString()).To(ContainSubstring("--pod-id-file %t/pod-foo.pod-id")) + Expect(session.OutputToString()).To(ContainSubstring("--exit-policy=stop")) + Expect(session.OutputToString()).To(ContainSubstring("--name foo")) + Expect(session.OutputToString()).To(ContainSubstring("ExecStartPre=/bin/rm")) + Expect(session.OutputToString()).To(ContainSubstring("-f %t/pod-foo.pid %t/pod-foo.pod-id")) + Expect(session.OutputToString()).To(ContainSubstring("pod stop")) + Expect(session.OutputToString()).To(ContainSubstring("--ignore")) + Expect(session.OutputToString()).To(ContainSubstring("--pod-id-file %t/pod-foo.pod-id")) + Expect(session.OutputToString()).To(ContainSubstring("-t 10")) + Expect(session.OutputToString()).To(ContainSubstring("pod rm")) + Expect(session.OutputToString()).To(ContainSubstring("--ignore")) + Expect(session.OutputToString()).To(ContainSubstring("-f")) + Expect(session.OutputToString()).To(ContainSubstring("--pod-id-file %t/pod-foo.pod-id")) }) It("podman generate systemd --format json", func() { diff --git a/test/e2e/secret_test.go b/test/e2e/secret_test.go index 776dab785..e28d415db 100644 --- a/test/e2e/secret_test.go +++ b/test/e2e/secret_test.go @@ -309,4 +309,41 @@ var _ = Describe("Podman secret", func() { Expect(inspect.OutputToString()).To(Equal(secrID)) }) + It("podman secret with labels", func() { + secretFilePath := filepath.Join(podmanTest.TempDir, "secret") + err := ioutil.WriteFile(secretFilePath, []byte("mysecret"), 0755) + Expect(err).To(BeNil()) + + session := podmanTest.Podman([]string{"secret", "create", "--label", "foo=bar", "a", secretFilePath}) + session.WaitWithDefaultTimeout() + secrID := session.OutputToString() + Expect(session).Should(Exit(0)) + + inspect := podmanTest.Podman([]string{"secret", "inspect", "--format", "{{.Spec.Labels}}", secrID}) + inspect.WaitWithDefaultTimeout() + Expect(inspect).Should(Exit(0)) + Expect(inspect.OutputToString()).To(ContainSubstring("foo:bar")) + + session = podmanTest.Podman([]string{"secret", "create", "--label", "foo=bar", "--label", "a:b", "b", secretFilePath}) + session.WaitWithDefaultTimeout() + secrID = session.OutputToString() + Expect(session).Should(Exit(0)) + + inspect = podmanTest.Podman([]string{"secret", "inspect", "--format", "{{.Spec.Labels}}", secrID}) + inspect.WaitWithDefaultTimeout() + Expect(inspect).Should(Exit(0)) + Expect(inspect.OutputToString()).To(ContainSubstring("foo:bar")) + Expect(inspect.OutputToString()).To(ContainSubstring("a:b")) + + session = podmanTest.Podman([]string{"secret", "create", "c", secretFilePath}) + session.WaitWithDefaultTimeout() + secrID = session.OutputToString() + Expect(session).Should(Exit(0)) + + inspect = podmanTest.Podman([]string{"secret", "inspect", "--format", "{{.Spec.Labels}}", secrID}) + inspect.WaitWithDefaultTimeout() + Expect(inspect).Should(Exit(0)) + Expect(inspect.OutputToString()).To(Equal("map[]")) + + }) }) diff --git a/test/system/030-run.bats b/test/system/030-run.bats index b1ce91d14..8cd29e744 100644 --- a/test/system/030-run.bats +++ b/test/system/030-run.bats @@ -892,4 +892,14 @@ $IMAGE--c_ok" \ run_podman container rm -f -t 0 c_ok c_fail_no_rm } +@test "podman run --attach stdin prints container ID" { + ctr_name="container-$(random_string 5)" + run_podman run --name $ctr_name --attach stdin $IMAGE echo hello + run_output=$output + run_podman inspect --format "{{.Id}}" $ctr_name + ctr_id=$output + is "$run_output" "$ctr_id" "Did not find container ID in the output" + run_podman rm $ctr_name +} + # vim: filetype=sh |