aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--cmd/podman/pods/create.go15
-rw-r--r--cmd/podman/pods/ps.go2
-rw-r--r--cmd/podman/volumes/create.go4
-rw-r--r--docs/source/markdown/podman-network-create.1.md2
-rw-r--r--docs/source/markdown/podman-pod-create.1.md13
-rw-r--r--docs/source/markdown/podman-volume-create.1.md2
-rw-r--r--libpod/container_internal_linux.go2
-rw-r--r--libpod/runtime.go14
-rw-r--r--libpod/runtime_worker.go33
-rw-r--r--pkg/specgen/generate/validate.go9
-rw-r--r--test/system/015-help.bats58
-rw-r--r--test/system/200-pod.bats14
-rw-r--r--utils/testdata/cgroup.empty0
-rw-r--r--utils/testdata/cgroup.other1
-rw-r--r--utils/testdata/cgroup.root1
-rw-r--r--utils/utils_supported.go14
-rw-r--r--utils/utils_test.go26
-rw-r--r--utils/utils_windows.go4
18 files changed, 126 insertions, 88 deletions
diff --git a/cmd/podman/pods/create.go b/cmd/podman/pods/create.go
index 162379659..e2f80bdbc 100644
--- a/cmd/podman/pods/create.go
+++ b/cmd/podman/pods/create.go
@@ -16,7 +16,6 @@ import (
"github.com/containers/podman/v4/cmd/podman/containers"
"github.com/containers/podman/v4/cmd/podman/parse"
"github.com/containers/podman/v4/cmd/podman/registry"
- "github.com/containers/podman/v4/cmd/podman/validate"
"github.com/containers/podman/v4/libpod/define"
"github.com/containers/podman/v4/pkg/domain/entities"
"github.com/containers/podman/v4/pkg/errorhandling"
@@ -36,12 +35,14 @@ var (
You can then start it at any time with the podman pod start <pod_id> command. The pod will be created with the initial state 'created'.`
createCommand = &cobra.Command{
- Use: "create [options]",
- Args: validate.NoArgs,
+ Use: "create [options] [NAME]",
+ Args: cobra.MaximumNArgs(1),
Short: "Create a new empty pod",
Long: podCreateDescription,
RunE: create,
ValidArgsFunction: completion.AutocompleteNone,
+ Example: `podman pod create
+ podman pod create --label foo=bar mypod`,
}
)
@@ -115,6 +116,12 @@ func create(cmd *cobra.Command, args []string) error {
rawImageName string
podName string
)
+ if len(args) > 0 {
+ if len(createOptions.Name) > 0 {
+ return fmt.Errorf("cannot specify --name and NAME at the same time")
+ }
+ createOptions.Name = args[0]
+ }
labelFile = infraOptions.LabelFile
labels = infraOptions.Label
createOptions.Labels, err = parse.GetAllLabels(labelFile, labels)
@@ -128,7 +135,7 @@ func create(cmd *cobra.Command, args []string) error {
img := imageName
if !createOptions.Infra {
if cmd.Flag("no-hosts").Changed {
- return fmt.Errorf("cannot specify no-hosts without an infra container")
+ return fmt.Errorf("cannot specify --no-hosts without an infra container")
}
flags := cmd.Flags()
createOptions.Net, err = common.NetFlagsToNetOptions(nil, *flags)
diff --git a/cmd/podman/pods/ps.go b/cmd/podman/pods/ps.go
index 1275e65dc..a89448275 100644
--- a/cmd/podman/pods/ps.go
+++ b/cmd/podman/pods/ps.go
@@ -24,7 +24,7 @@ var (
// Command: podman pod _ps_
psCmd = &cobra.Command{
- Use: "ps [options]",
+ Use: "ps [options]",
Aliases: []string{"ls", "list"},
Short: "List pods",
Long: psDescription,
diff --git a/cmd/podman/volumes/create.go b/cmd/podman/volumes/create.go
index 1668c72de..b47ae16ce 100644
--- a/cmd/podman/volumes/create.go
+++ b/cmd/podman/volumes/create.go
@@ -17,6 +17,7 @@ var (
createCommand = &cobra.Command{
Use: "create [options] [NAME]",
+ Args: cobra.MaximumNArgs(1),
Short: "Create a new volume",
Long: createDescription,
RunE: create,
@@ -59,9 +60,6 @@ func create(cmd *cobra.Command, args []string) error {
var (
err error
)
- if len(args) > 1 {
- return errors.Errorf("too many arguments, create takes at most 1 argument")
- }
if len(args) > 0 {
createOpts.Name = args[0]
}
diff --git a/docs/source/markdown/podman-network-create.1.md b/docs/source/markdown/podman-network-create.1.md
index 0cdb6fe88..1d89b12e3 100644
--- a/docs/source/markdown/podman-network-create.1.md
+++ b/docs/source/markdown/podman-network-create.1.md
@@ -4,7 +4,7 @@
podman\-network-create - Create a Podman network
## SYNOPSIS
-**podman network create** [*options*] name
+**podman network create** [*options*] [*name*]
## DESCRIPTION
Create a CNI-network configuration for use with Podman. By default, Podman creates a bridge connection.
diff --git a/docs/source/markdown/podman-pod-create.1.md b/docs/source/markdown/podman-pod-create.1.md
index af8f9605e..cf749efda 100644
--- a/docs/source/markdown/podman-pod-create.1.md
+++ b/docs/source/markdown/podman-pod-create.1.md
@@ -4,14 +4,15 @@
podman\-pod\-create - Create a new pod
## SYNOPSIS
-**podman pod create** [*options*]
+**podman pod create** [*options*] [*name*]
## DESCRIPTION
Creates an empty pod, or unit of multiple containers, and prepares it to have
-containers added to it. The pod id is printed to STDOUT. You can then use
-**podman create --pod `<pod_id|pod_name>` ...** to add containers to the pod, and
-**podman pod start `<pod_id|pod_name>`** to start the pod.
+containers added to it. The pod can be created with a specific name. If a name
+is not given a random name is generated. The pod id is printed to STDOUT. You
+can then use **podman create --pod `<pod_id|pod_name>` ...** to add containers
+to the pod, and **podman pod start `<pod_id|pod_name>`** to start the pod.
The operator can identify a pod in three ways:
UUID long identifier (“f78375b1c487e03c9438c729345e54db9d20cfa2ac1fc3494b6eb60872e74778”)
@@ -549,9 +550,11 @@ that data on the target.
```
$ podman pod create --name test
+$ podman pod create mypod
+
$ podman pod create --infra=false
-$ podman pod create --infra-command /top
+$ podman pod create --infra-command /top toppod
$ podman pod create --publish 8443:443
diff --git a/docs/source/markdown/podman-volume-create.1.md b/docs/source/markdown/podman-volume-create.1.md
index 06fadcaa1..31e109791 100644
--- a/docs/source/markdown/podman-volume-create.1.md
+++ b/docs/source/markdown/podman-volume-create.1.md
@@ -4,7 +4,7 @@
podman\-volume\-create - Create a new volume
## SYNOPSIS
-**podman volume create** [*options*]
+**podman volume create** [*options*] [*name*]
## DESCRIPTION
diff --git a/libpod/container_internal_linux.go b/libpod/container_internal_linux.go
index 82e5fa992..298eb1947 100644
--- a/libpod/container_internal_linux.go
+++ b/libpod/container_internal_linux.go
@@ -3109,7 +3109,7 @@ func (c *Container) getOCICgroupPath() (string, error) {
case c.config.NoCgroups:
return "", nil
case c.config.CgroupsMode == cgroupSplit:
- selfCgroup, err := utils.GetOwnCgroup()
+ selfCgroup, err := utils.GetOwnCgroupDisallowRoot()
if err != nil {
return "", err
}
diff --git a/libpod/runtime.go b/libpod/runtime.go
index 4efa7b8e8..00fa2fe88 100644
--- a/libpod/runtime.go
+++ b/libpod/runtime.go
@@ -11,6 +11,7 @@ import (
"regexp"
"strconv"
"strings"
+ "sync"
"syscall"
"time"
@@ -87,8 +88,8 @@ type Runtime struct {
lockManager lock.Manager
// Worker
- workerShutdown chan bool
- workerChannel chan func()
+ workerChannel chan func()
+ workerGroup sync.WaitGroup
// syslog describes whenever logrus should log to the syslog as well.
// Note that the syslog hook will be enabled early in cmd/podman/syslog_linux.go
@@ -823,12 +824,9 @@ func (r *Runtime) Shutdown(force bool) error {
return define.ErrRuntimeStopped
}
- if r.workerShutdown != nil {
- // Signal the worker routine to shutdown. The routine will
- // process all pending work items and then read from the
- // channel; we're blocked until all work items have been
- // processed.
- r.workerShutdown <- true
+ if r.workerChannel != nil {
+ r.workerGroup.Wait()
+ close(r.workerChannel)
}
r.valid = false
diff --git a/libpod/runtime_worker.go b/libpod/runtime_worker.go
index ca44a27f7..9d41321b2 100644
--- a/libpod/runtime_worker.go
+++ b/libpod/runtime_worker.go
@@ -1,40 +1,17 @@
package libpod
-import (
- "time"
-)
-
func (r *Runtime) startWorker() {
- if r.workerChannel == nil {
- r.workerChannel = make(chan func(), 1)
- r.workerShutdown = make(chan bool)
- }
+ r.workerChannel = make(chan func(), 10)
go func() {
- for {
- // Make sure to read all workers before
- // checking if we're about to shutdown.
- for len(r.workerChannel) > 0 {
- w := <-r.workerChannel
- w()
- }
-
- select {
- // We'll read from the shutdown channel only when all
- // items above have been processed.
- //
- // (*Runtime).Shutdown() will block until until the
- // item is read.
- case <-r.workerShutdown:
- return
-
- default:
- time.Sleep(100 * time.Millisecond)
- }
+ for w := range r.workerChannel {
+ w()
+ r.workerGroup.Done()
}
}()
}
func (r *Runtime) queueWork(f func()) {
+ r.workerGroup.Add(1)
go func() {
r.workerChannel <- f
}()
diff --git a/pkg/specgen/generate/validate.go b/pkg/specgen/generate/validate.go
index 44c7818e7..a1affef31 100644
--- a/pkg/specgen/generate/validate.go
+++ b/pkg/specgen/generate/validate.go
@@ -1,6 +1,7 @@
package generate
import (
+ "io/ioutil"
"os"
"path/filepath"
@@ -166,6 +167,14 @@ func verifyContainerResourcesCgroupV2(s *specgen.SpecGenerator) ([]string, error
if err != nil {
return warnings, err
}
+
+ if own == "/" {
+ // If running under the root cgroup try to create or reuse a "probe" cgroup to read memory values
+ own = "podman_probe"
+ _ = os.MkdirAll(filepath.Join("/sys/fs/cgroup", own), 0o755)
+ _ = ioutil.WriteFile("/sys/fs/cgroup/cgroup.subtree_control", []byte("+memory"), 0o644)
+ }
+
memoryMax := filepath.Join("/sys/fs/cgroup", own, "memory.max")
memorySwapMax := filepath.Join("/sys/fs/cgroup", own, "memory.swap.max")
_, errMemoryMax := os.Stat(memoryMax)
diff --git a/test/system/015-help.bats b/test/system/015-help.bats
index 5757d51dc..1356c99a0 100644
--- a/test/system/015-help.bats
+++ b/test/system/015-help.bats
@@ -34,10 +34,16 @@ function check_help() {
# has no ' [options]'
is "$usage " " $command_string .*" "Usage string matches command"
+ # Strip off the leading command string; we no longer need it
+ usage=$(sed -e "s/^ $command_string \?//" <<<"$usage")
+
# If usage ends in '[command]', recurse into subcommands
- if expr "$usage" : '.*\[command\]$' >/dev/null; then
+ if expr "$usage" : '\[command\]' >/dev/null; then
found[subcommands]=1
- check_help "$@" $cmd
+ # (except for 'podman help', which is a special case)
+ if [[ $cmd != "help" ]]; then
+ check_help "$@" $cmd
+ fi
continue
fi
@@ -49,10 +55,26 @@ function check_help() {
assert "$usage" !~ '[A-Z].*\[option' \
"'options' must precede arguments in usage"
+ # Strip off '[options]' but remember if we've seen it.
+ local has_options=
+ if [[ $usage =~ \[options\] ]]; then
+ has_options=1
+ usage=$(sed -e 's/^\[options\] \?//' <<<"$usage")
+ fi
+
+ # From this point on, remaining argument descriptions must be UPPER CASE
+ # e.g., 'podman cmd [options] arg' or 'podman cmd [arg]' are invalid.
+ assert "$usage" !~ '[a-z]' \
+ "$command_string: argument names must be UPPER CASE"
+
+ # It makes no sense to have an optional arg followed by a mandatory one
+ assert "$usage" !~ '\[.*\] [A-Z]' \
+ "$command_string: optional args must be _after_ required ones"
+
# Cross-check: if usage includes '[options]', there must be a
# longer 'Options:' section in the full --help output; vice-versa,
# if 'Options:' is in full output, usage line must have '[options]'.
- if expr "$usage" : '.*\[option' >/dev/null; then
+ if [[ $has_options ]]; then
if ! expr "$full_help" : ".*Options:" >/dev/null; then
die "$command_string: Usage includes '[options]' but has no 'Options:' subsection"
fi
@@ -95,9 +117,7 @@ function check_help() {
fi
# If usage has required arguments, try running without them.
- # The expression here is 'first capital letter is not in [BRACKETS]'.
- # It is intended to handle 'podman foo [options] ARG' but not ' [ARG]'.
- if expr "$usage" : '[^A-Z]\+ [A-Z]' >/dev/null; then
+ if expr "$usage" : '[A-Z]' >/dev/null; then
# Exceptions: these commands don't work rootless
if is_rootless; then
# "pause is not supported for rootless containers"
@@ -126,25 +146,15 @@ function check_help() {
# the required args, then invoke with one extra. We should get a
# usage error.
if ! expr "$usage" : ".*\.\.\."; then
- # "podman help" can take infinite args, so skip that one
- if [ "$cmd" != "help" ]; then
- # Get the args part of the command line; this should be
- # everything from the first CAPITAL LETTER onward. We
- # don't actually care about the letter itself, so just
- # make it 'X'. And we don't care about [OPTIONAL] brackets
- # either. What we do care about is stuff like 'IMAGE | CTR'
- # which is actually one argument; convert to 'IMAGE-or-CTR'
- local rhs=$(sed -e 's/^[^A-Z]\+[A-Z]/X/' -e 's/ | /-or-/g' <<<"$usage")
- local n_args=$(wc -w <<<"$rhs")
-
- run_podman '?' "$@" $cmd $(seq --format='x%g' 0 $n_args)
- is "$status" 125 \
- "'$usage' indicates a maximum of $n_args args. I invoked it with more, and expected this exit status"
- is "$output" "Error:.* \(takes no arguments\|requires exactly $n_args arg\|accepts at most\|too many arguments\|accepts $n_args arg(s), received\|accepts between .* and .* arg(s), received \)" \
- "'$usage' indicates a maximum of $n_args args. I invoked it with more, and expected one of these error messages"
+ local n_args=$(wc -w <<<"$usage")
- found[fixed_args]=1
- fi
+ run_podman '?' "$@" $cmd $(seq --format='x%g' 0 $n_args)
+ is "$status" 125 \
+ "'$usage' indicates a maximum of $n_args args. I invoked it with more, and expected this exit status"
+ is "$output" "Error:.* \(takes no arguments\|requires exactly $n_args arg\|accepts at most\|too many arguments\|accepts $n_args arg(s), received\|accepts between .* and .* arg(s), received \)" \
+ "'$usage' indicates a maximum of $n_args args. I invoked it with more, and expected one of these error messages"
+
+ found[fixed_args]=1
fi
count=$(expr $count + 1)
diff --git a/test/system/200-pod.bats b/test/system/200-pod.bats
index 279e3f4d7..404ad67ec 100644
--- a/test/system/200-pod.bats
+++ b/test/system/200-pod.bats
@@ -394,20 +394,20 @@ EOF
is "$output" "false" "Default network sharing should be false"
run_podman pod rm test
- run_podman pod create --name test --share ipc --network private
+ run_podman pod create --share ipc --network private test
run_podman pod inspect test --format {{.InfraConfig.HostNetwork}}
is "$output" "false" "Private network sharing with only ipc should be false"
run_podman pod rm test
- run_podman pod create --name test --share net --network private
- run_podman pod inspect test --format {{.InfraConfig.HostNetwork}}
+ local name="$(random_string 10 | tr A-Z a-z)"
+ run_podman pod create --name $name --share net --network private
+ run_podman pod inspect $name --format {{.InfraConfig.HostNetwork}}
is "$output" "false" "Private network sharing with only net should be false"
- run_podman pod rm test
- run_podman pod create --name test --share net --network host
- run_podman pod inspect test --format {{.InfraConfig.HostNetwork}}
+ run_podman pod create --share net --network host --replace $name
+ run_podman pod inspect $name --format {{.InfraConfig.HostNetwork}}
is "$output" "true" "Host network sharing with only net should be true"
- run_podman pod rm test
+ run_podman pod rm $name
run_podman pod create --name test --share ipc --network host
run_podman pod inspect test --format {{.InfraConfig.HostNetwork}}
diff --git a/utils/testdata/cgroup.empty b/utils/testdata/cgroup.empty
new file mode 100644
index 000000000..e69de29bb
--- /dev/null
+++ b/utils/testdata/cgroup.empty
diff --git a/utils/testdata/cgroup.other b/utils/testdata/cgroup.other
new file mode 100644
index 000000000..239a7cded
--- /dev/null
+++ b/utils/testdata/cgroup.other
@@ -0,0 +1 @@
+0::/other
diff --git a/utils/testdata/cgroup.root b/utils/testdata/cgroup.root
new file mode 100644
index 000000000..1e027b2a3
--- /dev/null
+++ b/utils/testdata/cgroup.root
@@ -0,0 +1 @@
+0::/
diff --git a/utils/utils_supported.go b/utils/utils_supported.go
index 493ea61ce..c2dcc4631 100644
--- a/utils/utils_supported.go
+++ b/utils/utils_supported.go
@@ -64,7 +64,7 @@ func RunUnderSystemdScope(pid int, slice string, unitName string) error {
return nil
}
-func getCgroupProcess(procFile string) (string, error) {
+func getCgroupProcess(procFile string, allowRoot bool) (string, error) {
f, err := os.Open(procFile)
if err != nil {
return "", err
@@ -72,7 +72,7 @@ func getCgroupProcess(procFile string) (string, error) {
defer f.Close()
scanner := bufio.NewScanner(f)
- cgroup := "/"
+ cgroup := ""
for scanner.Scan() {
line := scanner.Text()
parts := strings.SplitN(line, ":", 3)
@@ -87,7 +87,7 @@ func getCgroupProcess(procFile string) (string, error) {
cgroup = parts[2]
}
}
- if cgroup == "/" {
+ if len(cgroup) == 0 || (!allowRoot && cgroup == "/") {
return "", errors.Errorf("could not find cgroup mount in %q", procFile)
}
return cgroup, nil
@@ -95,12 +95,16 @@ func getCgroupProcess(procFile string) (string, error) {
// GetOwnCgroup returns the cgroup for the current process.
func GetOwnCgroup() (string, error) {
- return getCgroupProcess("/proc/self/cgroup")
+ return getCgroupProcess("/proc/self/cgroup", true)
+}
+
+func GetOwnCgroupDisallowRoot() (string, error) {
+ return getCgroupProcess("/proc/self/cgroup", false)
}
// GetCgroupProcess returns the cgroup for the specified process process.
func GetCgroupProcess(pid int) (string, error) {
- return getCgroupProcess(fmt.Sprintf("/proc/%d/cgroup", pid))
+ return getCgroupProcess(fmt.Sprintf("/proc/%d/cgroup", pid), true)
}
// MoveUnderCgroupSubtree moves the PID under a cgroup subtree.
diff --git a/utils/utils_test.go b/utils/utils_test.go
new file mode 100644
index 000000000..f34dbdd7e
--- /dev/null
+++ b/utils/utils_test.go
@@ -0,0 +1,26 @@
+//go:build linux || darwin
+// +build linux darwin
+
+package utils
+
+import (
+ "testing"
+
+ "github.com/stretchr/testify/assert"
+)
+
+func TestCgroupProcess(t *testing.T) {
+ val, err := getCgroupProcess("testdata/cgroup.root", true)
+ assert.Nil(t, err)
+ assert.Equal(t, "/", val)
+
+ _, err = getCgroupProcess("testdata/cgroup.root", false)
+ assert.NotNil(t, err)
+
+ val, err = getCgroupProcess("testdata/cgroup.other", true)
+ assert.Nil(t, err)
+ assert.Equal(t, "/other", val)
+
+ _, err = getCgroupProcess("testdata/cgroup.empty", true)
+ assert.NotNil(t, err)
+}
diff --git a/utils/utils_windows.go b/utils/utils_windows.go
index 2c159ab06..1d017f5ae 100644
--- a/utils/utils_windows.go
+++ b/utils/utils_windows.go
@@ -17,6 +17,10 @@ func GetOwnCgroup() (string, error) {
return "", errors.New("not implemented for windows")
}
+func GetOwnCgroupDisallowRoot() (string, error) {
+ return "", errors.New("not implemented for windows")
+}
+
func GetCgroupProcess(pid int) (string, error) {
return "", errors.New("not implemented for windows")
}