summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--.cirrus.yml7
-rw-r--r--docs/README.md15
-rw-r--r--docs/source/conf.py4
-rw-r--r--docs/source/markdown/.gitignore2
-rw-r--r--docs/source/markdown/options/dns.md11
-rw-r--r--docs/source/markdown/options/no-reset.md3
-rw-r--r--docs/source/markdown/options/no-stream.md3
-rw-r--r--docs/source/markdown/options/shm-size.md6
-rw-r--r--docs/source/markdown/podman-build.1.md.in22
-rw-r--r--docs/source/markdown/podman-create.1.md.in18
-rw-r--r--docs/source/markdown/podman-pod-clone.1.md.in6
-rw-r--r--docs/source/markdown/podman-pod-create.1.md.in6
-rw-r--r--docs/source/markdown/podman-pod-stats.1.md.in (renamed from docs/source/markdown/podman-pod-stats.1.md)8
-rw-r--r--docs/source/markdown/podman-run.1.md.in18
-rw-r--r--docs/source/markdown/podman-stats.1.md.in (renamed from docs/source/markdown/podman-stats.1.md)8
-rwxr-xr-xhack/bats4
-rwxr-xr-xhack/buildah-vendor-treadmill172
-rwxr-xr-xhack/check_root.sh2
-rwxr-xr-xhack/man-page-checker4
-rw-r--r--libpod/container_graph.go91
-rw-r--r--libpod/container_inspect.go297
-rw-r--r--libpod/container_inspect_freebsd.go17
-rw-r--r--libpod/container_inspect_linux.go306
-rw-r--r--libpod/pod_api.go2
-rw-r--r--libpod/runtime_ctr.go52
-rw-r--r--libpod/runtime_img.go2
-rw-r--r--libpod/runtime_pod_linux.go92
-rw-r--r--libpod/runtime_volume_linux.go2
-rw-r--r--pkg/api/handlers/compat/containers_stats.go13
-rw-r--r--test/apiv2/20-containers.at17
-rwxr-xr-xtest/apiv2/test-apiv236
-rw-r--r--test/buildah-bud/make-new-buildah-diffs2
-rwxr-xr-xtest/compose/test-compose6
-rw-r--r--test/e2e/pod_rm_test.go27
-rw-r--r--test/system/015-help.bats10
-rw-r--r--test/system/065-cp.bats4
-rw-r--r--test/system/070-build.bats2
-rw-r--r--test/system/160-volumes.bats4
-rw-r--r--test/system/400-unprivileged-access.bats4
39 files changed, 808 insertions, 497 deletions
diff --git a/.cirrus.yml b/.cirrus.yml
index 4d7fbde13..17902a430 100644
--- a/.cirrus.yml
+++ b/.cirrus.yml
@@ -503,8 +503,7 @@ docker-py_test_task:
always: *runner_stats
-# Does exactly what it says, execute the podman unit-tests on all primary
-# platforms and release versions.
+# Does exactly what it says, execute the podman unit-tests on Fedora.
unit_test_task:
name: "Unit tests on $DISTRO_NV"
alias: unit_test
@@ -515,9 +514,6 @@ unit_test_task:
- validate
matrix:
- env: *stdenvars
- # Fedora 35 skipped for podman4
- #- env: *priorfedora_envvars
- - env: *ubuntu_envvars
# Special-case: Rootless on latest Fedora (standard) VM
- name: "Rootless unit on $DISTRO_NV"
env:
@@ -1119,7 +1115,6 @@ win_installer_task:
env:
PATH: "${PATH};C:\\ProgramData\\chocolatey\\bin"
CIRRUS_SHELL: powershell
- CIRRUS_CLONE_DEPTH: 1
# Fake version, we are only testing the installer functions, so version doesn't matter
WIN_INST_VER: 9.9.9
CIRRUS_WORKING_DIR: "${CIRRUS_DEFAULT_WORK}"
diff --git a/docs/README.md b/docs/README.md
index 0f2af16d6..abe7d7758 100644
--- a/docs/README.md
+++ b/docs/README.md
@@ -13,7 +13,6 @@ link on that page.
| ------------------------------------ | --------------------------- |
| Markdown source for man pages | docs/source/markdown/ |
| man pages aliases as .so files | docs/source/markdown/links/ |
-| restructured text for readthedocs.io | docs/rst/ |
| target for output | docs/build |
| man pages | docs/build/man |
| remote linux man pages | docs/build/remote/linux |
@@ -60,16 +59,24 @@ incorrect, or the file isn't accessible for some other reason.
## Local Testing
-Assuming that you have the [dependencies](https://podman.io/getting-started/installation#build-and-run-dependencies)
+To build standard man pages, run `make docs`. Results will be in `docs/build/man`.
+
+To build HTMLized man pages: Assuming that you have the
+[dependencies](https://podman.io/getting-started/installation#build-and-run-dependencies)
installed, then also install (showing Fedora in the example):
```
-# dnf install python3-sphinx python3-recommonmark
-# pip install sphinx-markdown-tables
+$ sudo dnf install python3-sphinx python3-recommonmark
+$ pip install sphinx-markdown-tables myst_parser
```
+(The above dependencies are current as of 2022-09-15. If you experience problems,
+please see [requirements.txt](requirements.txt) in this directory, it will almost
+certainly be more up-to-date than this README.)
+
After that completes, cd to the `docs` directory in your Podman sandbox and then do `make html`.
You can then preview the html files in `docs/build/html` with:
```
python -m http.server 8000 --directory build/html
```
+...and point your web browser at `http://localhost:8000/`
diff --git a/docs/source/conf.py b/docs/source/conf.py
index b58bb3f46..345e289e1 100644
--- a/docs/source/conf.py
+++ b/docs/source/conf.py
@@ -86,12 +86,12 @@ def convert_markdown_title(app, docname, source):
if docpath.endswith(".md"):
# Convert pandoc title line into eval_rst block for myst_parser
#
- # Remove the ending "(1)" to avoid it from being displayed
+ # Remove the ending " 1" (section) to avoid it from being displayed
# in the web tab. Often such a text indicates that
# a web page got an update. For instance GitHub issues
# shows the number of new comments that have been written
# after the user's last visit.
- source[0] = re.sub(r"^% (.*)(\(\d\))", r"```{title} \g<1>\n```", source[0])
+ source[0] = re.sub(r"^% (.*)\s(\d)", r"```{title} \g<1>\n```", source[0])
def setup(app):
app.connect("source-read", convert_markdown_title)
diff --git a/docs/source/markdown/.gitignore b/docs/source/markdown/.gitignore
index 69911b5b8..af4c5360b 100644
--- a/docs/source/markdown/.gitignore
+++ b/docs/source/markdown/.gitignore
@@ -20,6 +20,7 @@ podman-pod-kill.1.md
podman-pod-logs.1.md
podman-pod-rm.1.md
podman-pod-start.1.md
+podman-pod-stats.1.md
podman-pod-stop.1.md
podman-pull.1.md
podman-push.1.md
@@ -27,6 +28,7 @@ podman-rm.1.md
podman-run.1.md
podman-search.1.md
podman-start.1.md
+podman-stats.1.md
podman-stop.1.md
podman-unpause.1.md
podman-update.1.md
diff --git a/docs/source/markdown/options/dns.md b/docs/source/markdown/options/dns.md
new file mode 100644
index 000000000..39380ace2
--- /dev/null
+++ b/docs/source/markdown/options/dns.md
@@ -0,0 +1,11 @@
+#### **--dns**=*ipaddr*
+
+Set custom DNS servers.
+
+This option can be used to override the DNS
+configuration passed to the container. Typically this is necessary when the
+host DNS configuration is invalid for the container (e.g., **127.0.0.1**). When this
+is the case the **--dns** flag is necessary for every run.
+
+The special value **none** can be specified to disable creation of _/etc/resolv.conf_ in the container by Podman.
+The _/etc/resolv.conf_ file in the image will be used without changes.
diff --git a/docs/source/markdown/options/no-reset.md b/docs/source/markdown/options/no-reset.md
new file mode 100644
index 000000000..ce5b95057
--- /dev/null
+++ b/docs/source/markdown/options/no-reset.md
@@ -0,0 +1,3 @@
+#### **--no-reset**
+
+Do not clear the terminal/screen in between reporting intervals
diff --git a/docs/source/markdown/options/no-stream.md b/docs/source/markdown/options/no-stream.md
new file mode 100644
index 000000000..a9d548ba0
--- /dev/null
+++ b/docs/source/markdown/options/no-stream.md
@@ -0,0 +1,3 @@
+#### **--no-stream**
+
+Disable streaming <<|pod >>stats and only pull the first result, default setting is false
diff --git a/docs/source/markdown/options/shm-size.md b/docs/source/markdown/options/shm-size.md
new file mode 100644
index 000000000..18cafcb86
--- /dev/null
+++ b/docs/source/markdown/options/shm-size.md
@@ -0,0 +1,6 @@
+#### **--shm-size**=*number[unit]*
+
+Size of _/dev/shm_. A _unit_ can be **b** (bytes), **k** (kibibytes), **m** (mebibytes), or **g** (gibibytes).
+If you omit the unit, the system uses bytes. If you omit the size entirely, the default is **64m**.
+When _size_ is **0**, there is no limit on the amount of memory used for IPC by the <<container|pod>>.
+This option conflicts with **--ipc=host**.
diff --git a/docs/source/markdown/podman-build.1.md.in b/docs/source/markdown/podman-build.1.md.in
index 2c636069f..e201806e5 100644
--- a/docs/source/markdown/podman-build.1.md.in
+++ b/docs/source/markdown/podman-build.1.md.in
@@ -223,18 +223,12 @@ specifying **--disable-compression=false**.
@@option disable-content-trust
-#### **--dns**=*dns*
+@@option dns
-Set custom DNS servers to be used during the build.
+This option cannot be combined with **--network** that is set to **none**.
-This option can be used to override the DNS configuration passed to the
-container. Typically this is necessary when the host DNS configuration is
-invalid for the container (e.g., 127.0.0.1). When this is the case the `--dns`
-option is necessary for every run.
-
-The special value **none** can be specified to disable creation of
-/etc/resolv.conf in the container by Podman. The /etc/resolv.conf file in the
-image will be used without changes.
+Note: this option takes effect only during *RUN* instructions in the build.
+It does not affect _/etc/resolv.conf_ in the final image.
#### **--dns-option**=*option*
@@ -564,13 +558,7 @@ container
- `seccomp=profile.json` : White listed syscalls seccomp Json file to be used
as a seccomp filter
-#### **--shm-size**=*size*
-
-Size of `/dev/shm`. The format is `<number><unit>`. `number` must be greater
-than `0`.
-Unit is optional and can be `b` (bytes), `k` (kibibytes), `m`(mebibytes), or
-`g` (gibibytes). If you omit the unit, the system uses bytes. If you omit the
-size entirely, the system uses `64m`.
+@@option shm-size
#### **--sign-by**=*fingerprint*
diff --git a/docs/source/markdown/podman-create.1.md.in b/docs/source/markdown/podman-create.1.md.in
index 020cdc00e..72f167a7e 100644
--- a/docs/source/markdown/podman-create.1.md.in
+++ b/docs/source/markdown/podman-create.1.md.in
@@ -141,17 +141,9 @@ flag to pass the user's supplementary group access into the container.
@@option disable-content-trust
-#### **--dns**=*dns*
+@@option dns
-Set custom DNS servers. Invalid if using **--dns** and **--network** that is set to 'none' or `container:<name|id>`.
-
-This option can be used to override the DNS
-configuration passed to the container. Typically this is necessary when the
-host DNS configuration is invalid for the container (e.g., 127.0.0.1). When this
-is the case the **--dns** flag is necessary for every run.
-
-The special value **none** can be specified to disable creation of **/etc/resolv.conf** in the container by Podman.
-The **/etc/resolv.conf** file in the image will be used without changes.
+This option cannot be combined with **--network** that is set to **none** or **container:**_id_.
@@option dns-opt.container
@@ -412,11 +404,7 @@ Note: Labeling can be disabled for all containers by setting label=false in the
Note: Labeling can be disabled for all containers by setting label=false in the **containers.conf** (`/etc/containers/containers.conf` or `$HOME/.config/containers/containers.conf`) file.
-#### **--shm-size**=*size*
-
-Size of `/dev/shm` (format: `<number>[<unit>]`, where unit = b (bytes), k (kibibytes), m (mebibytes), or g (gibibytes))
-If you omit the unit, the system uses bytes. If you omit the size entirely, the system uses `64m`.
-When size is `0`, there is no limit on the amount of memory used for IPC by the container.
+@@option shm-size
@@option stop-signal
diff --git a/docs/source/markdown/podman-pod-clone.1.md.in b/docs/source/markdown/podman-pod-clone.1.md.in
index 15f7ec208..90b829371 100644
--- a/docs/source/markdown/podman-pod-clone.1.md.in
+++ b/docs/source/markdown/podman-pod-clone.1.md.in
@@ -99,11 +99,7 @@ Note: Labeling can be disabled for all pods/containers by setting label=false in
Note: Labeling can be disabled for all containers by setting label=false in the **containers.conf** (`/etc/containers/containers.conf` or `$HOME/.config/containers/containers.conf`) file.
-#### **--shm-size**=*size*
-
-Size of `/dev/shm` (format: `<number>[<unit>]`, where unit = b (bytes), k (kibibytes), m (mebibytes), or g (gibibytes))
-If the unit is omitted, the system uses bytes. If the size is omitted, the system uses `64m`.
-When size is `0`, there is no limit on the amount of memory used for IPC by the pod. This option conflicts with **--ipc=host** when running containers.
+@@option shm-size
#### **--start**
diff --git a/docs/source/markdown/podman-pod-create.1.md.in b/docs/source/markdown/podman-pod-create.1.md.in
index b3c13533a..7700f5e62 100644
--- a/docs/source/markdown/podman-pod-create.1.md.in
+++ b/docs/source/markdown/podman-pod-create.1.md.in
@@ -213,11 +213,7 @@ This boolean determines whether or not all containers entering the pod will use
Note: This options conflict with **--share=cgroup** since that would set the pod as the cgroup parent but enter the container into the same cgroupNS as the infra container.
-#### **--shm-size**=*size*
-
-Size of `/dev/shm` (format: `<number>[<unit>]`, where unit = b (bytes), k (kibibytes), m (mebibytes), or g (gibibytes))
-If the unit is omitted, the system uses bytes. If the size is omitted, the system uses `64m`.
-When size is `0`, there is no limit on the amount of memory used for IPC by the pod. This option conflicts with **--ipc=host** when running containers.
+@@option shm-size
@@option subgidname
diff --git a/docs/source/markdown/podman-pod-stats.1.md b/docs/source/markdown/podman-pod-stats.1.md.in
index c71159f09..83a4b7a1e 100644
--- a/docs/source/markdown/podman-pod-stats.1.md
+++ b/docs/source/markdown/podman-pod-stats.1.md.in
@@ -40,13 +40,9 @@ When using a GO template, you may precede the format with `table` to print heade
Instead of providing the pod name or ID, use the last created pod. (This option is not available with the remote Podman client, including Mac and Windows (excluding WSL2) machines)
-#### **--no-reset**
+@@option no-reset
-Do not clear the terminal/screen in between reporting intervals
-
-#### **--no-stream**
-
-Disable streaming pod stats and only pull the first result, default setting is false
+@@option no-stream
## EXAMPLE
diff --git a/docs/source/markdown/podman-run.1.md.in b/docs/source/markdown/podman-run.1.md.in
index 227eb455c..37d052646 100644
--- a/docs/source/markdown/podman-run.1.md.in
+++ b/docs/source/markdown/podman-run.1.md.in
@@ -177,17 +177,9 @@ flag to pass the user's supplementary group access into the container.
@@option disable-content-trust
-#### **--dns**=*ipaddr*
+@@option dns
-Set custom DNS servers. Invalid if using **--dns** with **--network** that is set to **none** or **container:**_id_.
-
-This option can be used to override the DNS
-configuration passed to the container. Typically this is necessary when the
-host DNS configuration is invalid for the container (e.g., **127.0.0.1**). When this
-is the case the **--dns** flag is necessary for every run.
-
-The special value **none** can be specified to disable creation of _/etc/resolv.conf_ in the container by Podman.
-The _/etc/resolv.conf_ file in the image will be used without changes.
+This option cannot be combined with **--network** that is set to **none** or **container:**_id_.
@@option dns-opt.container
@@ -445,11 +437,7 @@ Note: Labeling can be disabled for all containers by setting label=false in the
Note: Labeling can be disabled for all containers by setting **label=false** in the **containers.conf**(5) file.
-#### **--shm-size**=*number[unit]*
-
-Size of _/dev/shm_. A _unit_ can be **b** (bytes), **k** (kibibytes), **m** (mebibytes), or **g** (gibibytes).
-If you omit the unit, the system uses bytes. If you omit the size entirely, the default is **64m**.
-When _size_ is **0**, there is no limit on the amount of memory used for IPC by the container.
+@@option shm-size
#### **--sig-proxy**
diff --git a/docs/source/markdown/podman-stats.1.md b/docs/source/markdown/podman-stats.1.md.in
index a1a0f6a93..f06bd3fcc 100644
--- a/docs/source/markdown/podman-stats.1.md
+++ b/docs/source/markdown/podman-stats.1.md.in
@@ -53,13 +53,9 @@ Time in seconds between stats reports, defaults to 5 seconds.
Instead of providing the container name or ID, use the last created container. If you use methods other than Podman
to run containers such as CRI-O, the last started container could be from either of those methods. (This option is not available with the remote Podman client, including Mac and Windows (excluding WSL2) machines)
-#### **--no-reset**
+@@option no-reset
-Do not clear the terminal/screen in between reporting intervals
-
-#### **--no-stream**
-
-Disable streaming stats and only pull the first result, default setting is false
+@@option no-stream
#### **--no-trunc**
diff --git a/hack/bats b/hack/bats
index ca9835a1f..9bc2d2c15 100755
--- a/hack/bats
+++ b/hack/bats
@@ -106,7 +106,7 @@ export PODMAN_ROOTLESS_USER=$(id -un)
# Root
if [ -z "$ROOTLESS_ONLY" ]; then
- echo "# bats ${bats_filter[@]} $TESTS"
+ echo "# bats ${bats_filter[*]} $TESTS"
sudo --preserve-env=PODMAN \
--preserve-env=PODMAN_TEST_DEBUG \
--preserve-env=OCI_RUNTIME \
@@ -119,7 +119,7 @@ fi
# Rootless. (Only if we're not already root)
if [[ -z "$ROOT_ONLY" && "$(id -u)" != 0 ]]; then
echo "--------------------------------------------------"
- echo "\$ bats ${bats_filter[@]} $TESTS"
+ echo "\$ bats ${bats_filter[*]} $TESTS"
bats "${bats_opts[@]}" "${bats_filter[@]}" $TESTS
rc=$((rc | $?))
fi
diff --git a/hack/buildah-vendor-treadmill b/hack/buildah-vendor-treadmill
index d579a180a..b95290841 100755
--- a/hack/buildah-vendor-treadmill
+++ b/hack/buildah-vendor-treadmill
@@ -38,10 +38,6 @@ our $Treadmill_PR_Title = 'DO NOT MERGE: buildah vendor treadmill';
# Github API; this is where we query to find out the active treadmill PR
our $API_URL = 'https://api.github.com/graphql';
-# Temporary file used to preserve current treadmill patches. This file
-# should only exist very briefly while we perform branch operations.
-our $Patch_File = "0000-$ME.patch";
-
# Use colors if available and if stdout is a tty
our $C_Highlight = '';
our $C_Warning = '';
@@ -66,14 +62,14 @@ eval '
sub usage {
print <<"END_USAGE";
-Usage: $ME [OPTIONS] [--sync | --pick | --reset ]
+Usage: $ME [OPTIONS] [--sync | --pick [PR] | --reset ]
$ME is (2022-04-20) **EXPERIMENTAL**
$ME is intended to solve the problem of vendoring
buildah into podman.
-Call me with one of two options:
+Call me with one of three options:
--sync The usual case. Mostly used by Ed. Called from a
development branch, this just updates everything so
@@ -81,7 +77,8 @@ Call me with one of two options:
latest-podman (main). With a few sanity checks.
--pick Used for really-truly vendoring in a new buildah; will
- cherry-pick a commit on your buildah-vendor working branch
+ cherry-pick a commit on your buildah-vendor working branch.
+ Optional PR arg is the ID of the treadmill PR on github.
--reset Used after vendoring buildah into main, when there
really aren't any buildah patches to keep rolling.
@@ -103,6 +100,7 @@ END_USAGE
our %action;
our $debug = 0;
our $force_old_main = 0; # in --pick, proceeds even if main is old
+our $force_retry = 0; # in --sync, continue despite saved checkpoint
our $force_testing = 0; # in --sync, test even no podman/buildah changes
our $verbose = 0;
our $NOT = ''; # print "blahing the blah$NOT\n" if $debug
@@ -114,6 +112,7 @@ sub handle_opts {
'reset' => sub { $action{reset}++ },
'force-old-main' => \$force_old_main,
+ 'force-retry' => \$force_retry,
'force-testing' => \$force_testing,
'debug!' => \$debug,
@@ -140,11 +139,6 @@ sub main {
# and there's no clean way to make it use @_.
handle_opts(); # will set package globals
- # Fetch command-line arguments. Barf if too many.
- # FIXME: if called with arg, that's the --sync branch?
- # FIXME: if called with --pick + arg, that's the PR?
- die "$ME: Too many arguments; try $ME --help\n" if @ARGV;
-
my @action = keys(%action);
die "$ME: Please invoke me with one of --sync or --pick\n"
if ! @action;
@@ -158,13 +152,15 @@ sub main {
# that repo is clean. None of our actions can be run on a dirty repo.
assert_clean_repo();
- $handler->();
+ $handler->(@ARGV);
}
###############################################################################
# BEGIN sync and its helpers
sub do_sync {
+ die "$ME: --sync takes no arguments; try $ME --help\n" if @_;
+
# Preserve current branch name, so we can come back after switching to main
my $current_branch = git_current_branch();
@@ -188,11 +184,13 @@ sub do_sync {
pull_main();
git('checkout', '-q', $current_branch);
- # Preserve local patches. --always will generate empty patches (e.g.,
- # after a buildah vendor when everything is copacetic); --no-signature
- # prevents a buildup of "-- 2.35" (git version) lines at the end.
- git('format-patch', '--always', '--no-signature', "--output=$Patch_File", 'HEAD^');
- progress("Treadmill patches saved to $Patch_File");
+ # Make a temporary copy of this branch
+ my $temp_branch = strftime("__buildah-treadmill-checkpoint/%Y%m%d-%H%M%S", localtime);
+ git('branch', $temp_branch, $current_branch);
+ progress("Current branch preserved as $temp_branch");
+
+ # Get the hash of the top (treadmill) commit, to cherry-pick later
+ my $treadmill_commit = git('rev-parse', 'HEAD');
#
# Danger Will Robinson! This is where it gets scary: a failure here
@@ -207,7 +205,11 @@ This is not something I can recover from. Your human judgment is needed.
You will need to recover from this manually. Your best option is to
look at the source code for this script.
-Your treadmill patches are here: $Patch_File
+Treadmill branch copy is preserved in $temp_branch
+
+To restore state to where you were before this sync:
+ \$ git checkout main
+ \$ git branch -f $current_branch $treadmill_commit
END_FAIL_INSTRUCTIONS
exit 1;
@@ -260,12 +262,34 @@ END_FAIL_INSTRUCTIONS
git_commit_buildah($buildah_new);
# And, finally, this has the highest possibility of failing
- progress('Reapplying preserved patches');
- git('am', '--empty=keep', $Patch_File);
+ local $SIG{__DIE__} = sub {
+ print STDERR $C_Warning, "@_", <<"END_FAIL_INSTRUCTIONS";
+
+This is not something I can recover from. Your human judgment is needed.
+
+Chances are, you might be able to run 'git status', look for
+merge conflicts, manually resolve those, 'git add', then
+'git cherry-pick --continue'. If that works, run this script
+again (you will probably need the --force-retry option).
- # It worked! Clean up: remove our local die() handler and the patch file
+If that DOES NOT work, your only option is to look at the source code
+for this script. Sorry. There's only so much that can be done automatically.
+
+Treadmill branch copy is preserved in $temp_branch
+
+To restore state to where you were before this sync:
+ \$ git checkout main
+ \$ git branch -f $current_branch $treadmill_commit
+END_FAIL_INSTRUCTIONS
+
+ exit 1;
+ };
+ progress('Reapplying treadmill patches');
+ git('cherry-pick', '--allow-empty', $treadmill_commit);
+
+ # It worked! Clean up: remove our local die() handler and the saved branch
undef $SIG{__DIE__};
- unlink $Patch_File;
+ git('branch', '-D', $temp_branch);
# if buildah is unchanged, and we did not pull main, exit cleanly
my $change_message = '';
@@ -295,6 +319,13 @@ END_FAIL_INSTRUCTIONS
progress("All OK. It's now up to you to 'git push --force'");
progress(" --- Reminder: $change_message");
+
+ # Kind of kludgy. If user had to retry a prior failed attempt, and
+ # things are now successful, remind them to delete old checkpoints.
+ # ($force_retry is a 'git branch -D' command string at this point.)
+ if ($force_retry) {
+ progress(" --- Retry worked! You may now $force_retry");
+ }
}
###############
@@ -429,8 +460,9 @@ sub do_pick {
assert_buildah_vendor_commit('HEAD');
progress("HEAD is a buildah vendor commit. Good.");
- # Identify and pull the treadmill PR
- my $treadmill_pr = treadmill_pr();
+ # Identify and pull the treadmill PR.
+ my $treadmill_pr = shift || treadmill_pr();
+
my $treadmill_branch = "$ME/pr$treadmill_pr/tmp$$";
progress("Fetching treadmill PR $treadmill_pr into $treadmill_branch");
git('fetch', '-q', git_upstream(), "pull/$treadmill_pr/head:$treadmill_branch");
@@ -465,6 +497,26 @@ sub do_pick {
# treadmill_pr # Returns ID of open podman PR with the desired subject
##################
sub treadmill_pr {
+ # Github API (or maybe just the search endpoint???) is restricted.
+ my $token = $ENV{GITHUB_TOKEN}
+ or do {
+ warn <<"END_NEED_PR";
+$ME: Cannot proceed without PR ID.
+
+If you have a github API token, please: export GITHUB_TOKEN=.......
+and re-run me.
+
+If you do not have a github API token, please go here:
+
+ https://github.com/containers/podman/pulls?q=is%3Apr+is%3Aopen+%22buildah+vendor+treadmill%22
+
+...then reinvoke me, adding that PR ID to the command line args.
+
+As of 2022-09-12 the treadmill PR is 13808, but that may change over time.
+END_NEED_PR
+ exit 1;
+ };
+
my $query = <<'END_QUERY';
{
search(
@@ -481,16 +533,10 @@ END_QUERY
$ua->agent("$ME " . $ua->agent); # Identify ourself
my %headers = (
+ 'Authorization' => "bearer $token",
'Accept' => "application/vnd.github.antiope-preview+json",
'Content-Type' => "application/json",
);
-
- # Use github token if available, but don't require it. (All it does is
- # bump up our throttling limit, which shouldn't be an issue) (unless
- # someone invokes this script hundreds of times per minute).
- if (my $token = $ENV{GITHUB_TOKEN}) {
- $headers{Authorization} = "bearer $token";
- }
$ua->default_header($_ => $headers{$_}) for keys %headers;
# Massage the query: escape quotes, put it all in one line, collapse spaces
@@ -503,7 +549,9 @@ END_QUERY
print $postquery, "\n" if $debug;
my $res = $ua->post($API_URL, Content => $postquery);
if ((my $code = $res->code) != 200) {
- print $code, " ", $res->message, "\n";
+ warn "$ME: GraphQL request failed on $API_URL:\n";
+ print STDERR " ", $code, " ", $res->message, "\n";
+ warn "Cannot continue.\n";
exit 1;
}
@@ -621,8 +669,8 @@ from the buildah vendor treadmill PR, #%s
EOF
# Strip the "DO NOT MERGE" header from the treadmill PR, print only
-# the "Changes as of YYYY-MM-DD" and subsequent lines
-sed -ne '/^Changes as of/,$ p' <$msgfile >>$tmpfile
+# the "Changes since YYYY-MM-DD" and subsequent lines
+sed -ne '/^Changes since /,$ p' <$msgfile >>$tmpfile
mv $tmpfile $msgfile
END_EDIT_SCRIPT
@@ -639,6 +687,8 @@ END_EDIT_SCRIPT
# BEGIN reset and its helpers
sub do_reset {
+ die "$ME: --sync takes no arguments; try $ME --help\n" if @_;
+
my $current_branch = git_current_branch();
# Make sure side branch == main (i.e., there are no commits on the branch)
@@ -681,20 +731,46 @@ sub progress {
# assert_clean_repo # Don't even think of running with local changes
#######################
sub assert_clean_repo {
- # Our patch file should only exist for brief moments during a sync run.
- # If it exists at any other time, something has gone very wrong.
- if (-e $Patch_File) {
- warn <<"END_WARN";
-$ME: File exists: $Patch_File
+ # During --sync we create a temporary copy of the treadmill branch,
+ # in case something goes wrong. The branch is deleted on success.
+ # If one exists, it means we may have lost work.
+ my @relics = grep {
+ m!^__buildah-treadmill-checkpoint/\d+-\d+$!
+ } git('branch', '--list', '--format=%(refname:lstrip=2)');
+ if (@relics) {
+ if ($force_retry) {
+ warn <<"END_WARN";
+$ME: WARNING: leftover checkpoint(s): @relics
+
+ ...continuing due to --force-retry.
+
+ If things work out, you can 'git branch -D @relics'
+END_WARN
+
+ # OK, ugly override of a binary flag, but it's OK because
+ # it helps with user-friendliness: offer a reminder upon
+ # successful completion of the script.
+ $force_retry = "git branch -D @relics";
+ }
+ else {
+ warn <<"END_WARN";
+$ME: FATAL: leftover checkpoint: @relics
This means that something went very wrong during an earlier sync run.
Your git branch may be in an inconsistent state. Your work to date
- may be lost. This file may be your only hope of recovering it.
+ may be lost. This branch may be your only hope of recovering it.
This is not something a script can resolve. You need to look at this
- file, compare to your git HEAD, and manually reconcile any differences.
+ branch, compare to your git HEAD, and manually reconcile any differences.
+
+ If you really know what you're doing, i.e., if you've reconciled
+ merge conflicts and have a pretty secure branch structure, try
+ rerunning me with --force-retry. Or, if that checkpoint is a
+ remnant from a past run, and you're ultra-certain that you don't
+ need it, you can git branch -D @relics
END_WARN
- exit 1;
+ exit 1;
+ }
}
# OK so far. Now check for modified files.
@@ -727,7 +803,15 @@ sub git_current_branch() {
# git_forkpoint # Hash at which branch (default: cur) branched from main
###################
sub git_forkpoint {
- return git('merge-base', '--fork-point', 'main', @_);
+ # '--fork-point vendor-branch' fails silently on Paul's git tree,
+ # but plain merge-base works fine. My head hurts from trying to
+ # understand the docs, so I give up. Just try fork-point first,
+ # and if it fails, try without. #cargocult #gitishard
+ my $forkpoint = eval { git('merge-base', '--fork-point', 'main', @_) };
+ if ($@) {
+ $forkpoint = git('merge-base', 'main', @_);
+ }
+ return $forkpoint;
}
#####################
diff --git a/hack/check_root.sh b/hack/check_root.sh
index 1f53887ff..2489a4edd 100755
--- a/hack/check_root.sh
+++ b/hack/check_root.sh
@@ -1,5 +1,5 @@
#!/usr/bin/env bash
if ! [ $(id -u) = 0 ]; then
- echo "Please run as root! '$@' requires root privileges."
+ echo "Please run as root! '$*' requires root privileges."
exit 1
fi
diff --git a/hack/man-page-checker b/hack/man-page-checker
index 8ee0aaf6d..83e0b8b1d 100755
--- a/hack/man-page-checker
+++ b/hack/man-page-checker
@@ -87,7 +87,7 @@ function compare_usage() {
# strip off command name from both
from_man=$(sed -e "s/\*\*$cmd\*\*[[:space:]]*//" <<<"$from_man")
- from_help=$(sed -e "s/^[[:space:]]*$cmd[[:space:]]*//" <<<"$from_help")
+ from_help=$(sed -e "s/^[[:space:]]*${cmd}[[:space:]]*//" <<<"$from_help")
# man page lists 'foo [*options*]', help msg shows 'foo [flags]'.
# Make sure if one has it, the other does too.
@@ -153,7 +153,7 @@ for md in *.1.md;do
# special case: the command is "auto-update", with a hyphen
md_nodash='podman auto-update'
fi
- if [ "$cmd" != "$md_nodash" -a "$cmd" != "podman-remote" ]; then
+ if [[ "$cmd" != "$md_nodash" ]] && [[ "$cmd" != "podman-remote" ]]; then
echo
printf "Inconsistent program name in SYNOPSIS in %s:\n" $md
printf " SYNOPSIS = %s (expected: '%s')\n" "$cmd" "$md_nodash"
diff --git a/libpod/container_graph.go b/libpod/container_graph.go
index 96d61b756..d43579e4a 100644
--- a/libpod/container_graph.go
+++ b/libpod/container_graph.go
@@ -281,3 +281,94 @@ func startNode(ctx context.Context, node *containerNode, setError bool, ctrError
startNode(ctx, successor, ctrErrored, ctrErrors, ctrsVisited, restart)
}
}
+
+// Visit a node on the container graph and remove it, or set an error if it
+// failed to remove. Only intended for use in pod removal; do *not* use when
+// removing individual containers.
+// All containers are assumed to be *UNLOCKED* on running this function.
+// Container locks will be acquired as necessary.
+// Pod and infraID are optional. If a pod is given it must be *LOCKED*.
+func removeNode(ctx context.Context, node *containerNode, pod *Pod, force bool, timeout *uint, setError bool, ctrErrors map[string]error, ctrsVisited map[string]bool, ctrNamedVolumes map[string]*ContainerNamedVolume) {
+ // If we already visited this node, we're done.
+ if ctrsVisited[node.id] {
+ return
+ }
+
+ // Someone who depends on us failed.
+ // Mark us as failed and recurse.
+ if setError {
+ ctrsVisited[node.id] = true
+ ctrErrors[node.id] = fmt.Errorf("a container that depends on container %s could not be removed: %w", node.id, define.ErrCtrStateInvalid)
+
+ // Hit anyone who depends on us, set errors there as well.
+ for _, successor := range node.dependsOn {
+ removeNode(ctx, successor, pod, force, timeout, true, ctrErrors, ctrsVisited, ctrNamedVolumes)
+ }
+ }
+
+ // Does anyone still depend on us?
+ // Cannot remove if true. Once all our dependencies have been removed,
+ // we will be removed.
+ for _, dep := range node.dependedOn {
+ // The container that depends on us hasn't been removed yet.
+ // OK to continue on
+ if ok := ctrsVisited[dep.id]; !ok {
+ return
+ }
+ }
+
+ // Going to try to remove the node, mark us as visited
+ ctrsVisited[node.id] = true
+
+ ctrErrored := false
+
+ // Verify that all that depend on us are gone.
+ // Graph traversal should guarantee this is true, but this isn't that
+ // expensive, and it's better to be safe.
+ for _, dep := range node.dependedOn {
+ if _, err := node.container.runtime.GetContainer(dep.id); err == nil {
+ ctrErrored = true
+ ctrErrors[node.id] = fmt.Errorf("a container that depends on container %s still exists: %w", node.id, define.ErrDepExists)
+ }
+ }
+
+ // Lock the container
+ node.container.lock.Lock()
+
+ // Gate all subsequent bits behind a ctrErrored check - we don't want to
+ // proceed if a previous step failed.
+ if !ctrErrored {
+ if err := node.container.syncContainer(); err != nil {
+ ctrErrored = true
+ ctrErrors[node.id] = err
+ }
+ }
+
+ if !ctrErrored {
+ for _, vol := range node.container.config.NamedVolumes {
+ ctrNamedVolumes[vol.Name] = vol
+ }
+
+ if pod != nil && pod.state.InfraContainerID == node.id {
+ pod.state.InfraContainerID = ""
+ if err := pod.save(); err != nil {
+ ctrErrored = true
+ ctrErrors[node.id] = fmt.Errorf("error removing infra container %s from pod %s: %w", node.id, pod.ID(), err)
+ }
+ }
+ }
+
+ if !ctrErrored {
+ if err := node.container.runtime.removeContainer(ctx, node.container, force, false, true, false, timeout); err != nil {
+ ctrErrored = true
+ ctrErrors[node.id] = err
+ }
+ }
+
+ node.container.lock.Unlock()
+
+ // Recurse to anyone who we depend on and remove them
+ for _, successor := range node.dependsOn {
+ removeNode(ctx, successor, pod, force, timeout, ctrErrored, ctrErrors, ctrsVisited, ctrNamedVolumes)
+ }
+}
diff --git a/libpod/container_inspect.go b/libpod/container_inspect.go
index b72d843b6..e4089efa6 100644
--- a/libpod/container_inspect.go
+++ b/libpod/container_inspect.go
@@ -3,20 +3,15 @@ package libpod
import (
"errors"
"fmt"
- "sort"
"strings"
- "github.com/containers/common/pkg/config"
"github.com/containers/podman/v4/libpod/define"
"github.com/containers/podman/v4/libpod/driver"
"github.com/containers/podman/v4/pkg/util"
"github.com/containers/storage/types"
units "github.com/docker/go-units"
spec "github.com/opencontainers/runtime-spec/specs-go"
- "github.com/opencontainers/runtime-tools/generate"
- "github.com/opencontainers/runtime-tools/validate"
"github.com/sirupsen/logrus"
- "github.com/syndtr/gocapability/capability"
)
// inspectLocked inspects a container for low-level information.
@@ -163,8 +158,6 @@ func (c *Container) getContainerInspectData(size bool, driverData *define.Driver
Driver: driverData.Name,
MountLabel: config.MountLabel,
ProcessLabel: config.ProcessLabel,
- EffectiveCaps: ctrSpec.Process.Capabilities.Effective,
- BoundingCaps: ctrSpec.Process.Capabilities.Bounding,
AppArmorProfile: ctrSpec.Process.ApparmorProfile,
ExecIDs: execIDs,
GraphDriver: driverData,
@@ -173,6 +166,10 @@ func (c *Container) getContainerInspectData(size bool, driverData *define.Driver
IsInfra: c.IsInfra(),
IsService: c.IsService(),
}
+ if ctrSpec.Process.Capabilities != nil {
+ data.EffectiveCaps = ctrSpec.Process.Capabilities.Effective
+ data.BoundingCaps = ctrSpec.Process.Capabilities.Bounding
+ }
if c.state.ConfigPath != "" {
data.OCIConfigPath = c.state.ConfigPath
@@ -484,11 +481,6 @@ func (c *Container) generateInspectContainerHostConfig(ctrSpec *spec.Spec, named
hostConfig.ShmSize = c.config.ShmSize
hostConfig.Runtime = "oci"
- // This is very expensive to initialize.
- // So we don't want to initialize it unless we absolutely have to - IE,
- // there are things that require a major:minor to path translation.
- var deviceNodes map[string]string
-
// Annotations
if ctrSpec.Annotations != nil {
hostConfig.ContainerIDFile = ctrSpec.Annotations[define.InspectAnnotationCIDFile]
@@ -506,109 +498,8 @@ func (c *Container) generateInspectContainerHostConfig(ctrSpec *spec.Spec, named
}
}
- // Resource limits
- if ctrSpec.Linux != nil {
- if ctrSpec.Linux.Resources != nil {
- if ctrSpec.Linux.Resources.CPU != nil {
- if ctrSpec.Linux.Resources.CPU.Shares != nil {
- hostConfig.CpuShares = *ctrSpec.Linux.Resources.CPU.Shares
- }
- if ctrSpec.Linux.Resources.CPU.Period != nil {
- hostConfig.CpuPeriod = *ctrSpec.Linux.Resources.CPU.Period
- }
- if ctrSpec.Linux.Resources.CPU.Quota != nil {
- hostConfig.CpuQuota = *ctrSpec.Linux.Resources.CPU.Quota
- }
- if ctrSpec.Linux.Resources.CPU.RealtimePeriod != nil {
- hostConfig.CpuRealtimePeriod = *ctrSpec.Linux.Resources.CPU.RealtimePeriod
- }
- if ctrSpec.Linux.Resources.CPU.RealtimeRuntime != nil {
- hostConfig.CpuRealtimeRuntime = *ctrSpec.Linux.Resources.CPU.RealtimeRuntime
- }
- hostConfig.CpusetCpus = ctrSpec.Linux.Resources.CPU.Cpus
- hostConfig.CpusetMems = ctrSpec.Linux.Resources.CPU.Mems
- }
- if ctrSpec.Linux.Resources.Memory != nil {
- if ctrSpec.Linux.Resources.Memory.Limit != nil {
- hostConfig.Memory = *ctrSpec.Linux.Resources.Memory.Limit
- }
- if ctrSpec.Linux.Resources.Memory.Reservation != nil {
- hostConfig.MemoryReservation = *ctrSpec.Linux.Resources.Memory.Reservation
- }
- if ctrSpec.Linux.Resources.Memory.Swap != nil {
- hostConfig.MemorySwap = *ctrSpec.Linux.Resources.Memory.Swap
- }
- if ctrSpec.Linux.Resources.Memory.Swappiness != nil {
- hostConfig.MemorySwappiness = int64(*ctrSpec.Linux.Resources.Memory.Swappiness)
- } else {
- // Swappiness has a default of -1
- hostConfig.MemorySwappiness = -1
- }
- if ctrSpec.Linux.Resources.Memory.DisableOOMKiller != nil {
- hostConfig.OomKillDisable = *ctrSpec.Linux.Resources.Memory.DisableOOMKiller
- }
- }
- if ctrSpec.Linux.Resources.Pids != nil {
- hostConfig.PidsLimit = ctrSpec.Linux.Resources.Pids.Limit
- }
- hostConfig.CgroupConf = ctrSpec.Linux.Resources.Unified
- if ctrSpec.Linux.Resources.BlockIO != nil {
- if ctrSpec.Linux.Resources.BlockIO.Weight != nil {
- hostConfig.BlkioWeight = *ctrSpec.Linux.Resources.BlockIO.Weight
- }
- hostConfig.BlkioWeightDevice = []define.InspectBlkioWeightDevice{}
- for _, dev := range ctrSpec.Linux.Resources.BlockIO.WeightDevice {
- key := fmt.Sprintf("%d:%d", dev.Major, dev.Minor)
- // TODO: how do we handle LeafWeight vs
- // Weight? For now, ignore anything
- // without Weight set.
- if dev.Weight == nil {
- logrus.Infof("Ignoring weight device %s as it lacks a weight", key)
- continue
- }
- if deviceNodes == nil {
- nodes, err := util.FindDeviceNodes()
- if err != nil {
- return nil, err
- }
- deviceNodes = nodes
- }
- path, ok := deviceNodes[key]
- if !ok {
- logrus.Infof("Could not locate weight device %s in system devices", key)
- continue
- }
- weightDev := define.InspectBlkioWeightDevice{}
- weightDev.Path = path
- weightDev.Weight = *dev.Weight
- hostConfig.BlkioWeightDevice = append(hostConfig.BlkioWeightDevice, weightDev)
- }
-
- readBps, err := blkioDeviceThrottle(deviceNodes, ctrSpec.Linux.Resources.BlockIO.ThrottleReadBpsDevice)
- if err != nil {
- return nil, err
- }
- hostConfig.BlkioDeviceReadBps = readBps
-
- writeBps, err := blkioDeviceThrottle(deviceNodes, ctrSpec.Linux.Resources.BlockIO.ThrottleWriteBpsDevice)
- if err != nil {
- return nil, err
- }
- hostConfig.BlkioDeviceWriteBps = writeBps
-
- readIops, err := blkioDeviceThrottle(deviceNodes, ctrSpec.Linux.Resources.BlockIO.ThrottleReadIOPSDevice)
- if err != nil {
- return nil, err
- }
- hostConfig.BlkioDeviceReadIOps = readIops
-
- writeIops, err := blkioDeviceThrottle(deviceNodes, ctrSpec.Linux.Resources.BlockIO.ThrottleWriteIOPSDevice)
- if err != nil {
- return nil, err
- }
- hostConfig.BlkioDeviceWriteIOps = writeIops
- }
- }
+ if err := c.platformInspectContainerHostConfig(ctrSpec, hostConfig); err != nil {
+ return nil, err
}
// NanoCPUs.
@@ -659,182 +550,6 @@ func (c *Container) generateInspectContainerHostConfig(ctrSpec *spec.Spec, named
hostConfig.PortBindings = make(map[string][]define.InspectHostPort)
}
- // Cap add and cap drop.
- // We need a default set of capabilities to compare against.
- // The OCI generate package has one, and is commonly used, so we'll
- // use it.
- // Problem: there are 5 sets of capabilities.
- // Use the bounding set for this computation, it's the most encompassing
- // (but still not perfect).
- capAdd := []string{}
- capDrop := []string{}
- // No point in continuing if we got a spec without a Process block...
- if ctrSpec.Process != nil {
- // Max an O(1) lookup table for default bounding caps.
- boundingCaps := make(map[string]bool)
- g, err := generate.New("linux")
- if err != nil {
- return nil, err
- }
- if !hostConfig.Privileged {
- for _, cap := range g.Config.Process.Capabilities.Bounding {
- boundingCaps[cap] = true
- }
- } else {
- // If we are privileged, use all caps.
- for _, cap := range capability.List() {
- if g.HostSpecific && cap > validate.LastCap() {
- continue
- }
- boundingCaps[fmt.Sprintf("CAP_%s", strings.ToUpper(cap.String()))] = true
- }
- }
- // Iterate through spec caps.
- // If it's not in default bounding caps, it was added.
- // If it is, delete from the default set. Whatever remains after
- // we finish are the dropped caps.
- for _, cap := range ctrSpec.Process.Capabilities.Bounding {
- if _, ok := boundingCaps[cap]; ok {
- delete(boundingCaps, cap)
- } else {
- capAdd = append(capAdd, cap)
- }
- }
- for cap := range boundingCaps {
- capDrop = append(capDrop, cap)
- }
- // Sort CapDrop so it displays in consistent order (GH #9490)
- sort.Strings(capDrop)
- }
- hostConfig.CapAdd = capAdd
- hostConfig.CapDrop = capDrop
- switch {
- case c.config.IPCNsCtr != "":
- hostConfig.IpcMode = fmt.Sprintf("container:%s", c.config.IPCNsCtr)
- case ctrSpec.Linux != nil:
- // Locate the spec's IPC namespace.
- // If there is none, it's ipc=host.
- // If there is one and it has a path, it's "ns:".
- // If no path, it's default - the empty string.
- for _, ns := range ctrSpec.Linux.Namespaces {
- if ns.Type == spec.IPCNamespace {
- if ns.Path != "" {
- hostConfig.IpcMode = fmt.Sprintf("ns:%s", ns.Path)
- } else {
- break
- }
- }
- }
- case c.config.NoShm:
- hostConfig.IpcMode = "none"
- case c.config.NoShmShare:
- hostConfig.IpcMode = "private"
- }
- if hostConfig.IpcMode == "" {
- hostConfig.IpcMode = "shareable"
- }
-
- // Cgroup namespace mode
- cgroupMode := ""
- if c.config.CgroupNsCtr != "" {
- cgroupMode = fmt.Sprintf("container:%s", c.config.CgroupNsCtr)
- } else if ctrSpec.Linux != nil {
- // Locate the spec's cgroup namespace
- // If there is none, it's cgroup=host.
- // If there is one and it has a path, it's "ns:".
- // If there is no path, it's private.
- for _, ns := range ctrSpec.Linux.Namespaces {
- if ns.Type == spec.CgroupNamespace {
- if ns.Path != "" {
- cgroupMode = fmt.Sprintf("ns:%s", ns.Path)
- } else {
- cgroupMode = "private"
- }
- }
- }
- if cgroupMode == "" {
- cgroupMode = "host"
- }
- }
- hostConfig.CgroupMode = cgroupMode
-
- // Cgroup parent
- // Need to check if it's the default, and not print if so.
- defaultCgroupParent := ""
- switch c.CgroupManager() {
- case config.CgroupfsCgroupsManager:
- defaultCgroupParent = CgroupfsDefaultCgroupParent
- case config.SystemdCgroupsManager:
- defaultCgroupParent = SystemdDefaultCgroupParent
- }
- if c.config.CgroupParent != defaultCgroupParent {
- hostConfig.CgroupParent = c.config.CgroupParent
- }
- hostConfig.CgroupManager = c.CgroupManager()
-
- // PID namespace mode
- pidMode := ""
- if c.config.PIDNsCtr != "" {
- pidMode = fmt.Sprintf("container:%s", c.config.PIDNsCtr)
- } else if ctrSpec.Linux != nil {
- // Locate the spec's PID namespace.
- // If there is none, it's pid=host.
- // If there is one and it has a path, it's "ns:".
- // If there is no path, it's default - the empty string.
- for _, ns := range ctrSpec.Linux.Namespaces {
- if ns.Type == spec.PIDNamespace {
- if ns.Path != "" {
- pidMode = fmt.Sprintf("ns:%s", ns.Path)
- } else {
- pidMode = "private"
- }
- break
- }
- }
- if pidMode == "" {
- pidMode = "host"
- }
- }
- hostConfig.PidMode = pidMode
-
- // UTS namespace mode
- utsMode := c.NamespaceMode(spec.UTSNamespace, ctrSpec)
-
- hostConfig.UTSMode = utsMode
-
- // User namespace mode
- usernsMode := ""
- if c.config.UserNsCtr != "" {
- usernsMode = fmt.Sprintf("container:%s", c.config.UserNsCtr)
- } else if ctrSpec.Linux != nil {
- // Locate the spec's user namespace.
- // If there is none, it's default - the empty string.
- // If there is one, it's "private" if no path, or "ns:" if
- // there's a path.
-
- for _, ns := range ctrSpec.Linux.Namespaces {
- if ns.Type == spec.UserNamespace {
- if ns.Path != "" {
- usernsMode = fmt.Sprintf("ns:%s", ns.Path)
- } else {
- usernsMode = "private"
- }
- }
- }
- }
- hostConfig.UsernsMode = usernsMode
- if c.config.IDMappings.UIDMap != nil && c.config.IDMappings.GIDMap != nil {
- hostConfig.IDMappings = generateIDMappings(c.config.IDMappings)
- }
- // Devices
- // Do not include if privileged - assumed that all devices will be
- // included.
- var err error
- hostConfig.Devices, err = c.GetDevices(hostConfig.Privileged, *ctrSpec, deviceNodes)
- if err != nil {
- return nil, err
- }
-
// Ulimits
hostConfig.Ulimits = []define.InspectUlimit{}
if ctrSpec.Process != nil {
diff --git a/libpod/container_inspect_freebsd.go b/libpod/container_inspect_freebsd.go
new file mode 100644
index 000000000..8b4e8df87
--- /dev/null
+++ b/libpod/container_inspect_freebsd.go
@@ -0,0 +1,17 @@
+package libpod
+
+import (
+ "github.com/containers/podman/v4/libpod/define"
+ spec "github.com/opencontainers/runtime-spec/specs-go"
+)
+
+func (c *Container) platformInspectContainerHostConfig(ctrSpec *spec.Spec, hostConfig *define.InspectContainerHostConfig) error {
+ // Not sure what to put here. FreeBSD jails use pids from the
+ // global pool but can only see their own pids.
+ hostConfig.PidMode = "host"
+
+ // UTS namespace mode
+ hostConfig.UTSMode = c.NamespaceMode(spec.UTSNamespace, ctrSpec)
+
+ return nil
+}
diff --git a/libpod/container_inspect_linux.go b/libpod/container_inspect_linux.go
new file mode 100644
index 000000000..355690d70
--- /dev/null
+++ b/libpod/container_inspect_linux.go
@@ -0,0 +1,306 @@
+package libpod
+
+import (
+ "fmt"
+ "sort"
+ "strings"
+
+ "github.com/containers/common/pkg/config"
+ "github.com/containers/podman/v4/libpod/define"
+ "github.com/containers/podman/v4/pkg/util"
+ spec "github.com/opencontainers/runtime-spec/specs-go"
+ "github.com/opencontainers/runtime-tools/generate"
+ "github.com/opencontainers/runtime-tools/validate"
+ "github.com/sirupsen/logrus"
+ "github.com/syndtr/gocapability/capability"
+)
+
+func (c *Container) platformInspectContainerHostConfig(ctrSpec *spec.Spec, hostConfig *define.InspectContainerHostConfig) error {
+ // This is very expensive to initialize.
+ // So we don't want to initialize it unless we absolutely have to - IE,
+ // there are things that require a major:minor to path translation.
+ var deviceNodes map[string]string
+
+ // Resource limits
+ if ctrSpec.Linux != nil {
+ if ctrSpec.Linux.Resources != nil {
+ if ctrSpec.Linux.Resources.CPU != nil {
+ if ctrSpec.Linux.Resources.CPU.Shares != nil {
+ hostConfig.CpuShares = *ctrSpec.Linux.Resources.CPU.Shares
+ }
+ if ctrSpec.Linux.Resources.CPU.Period != nil {
+ hostConfig.CpuPeriod = *ctrSpec.Linux.Resources.CPU.Period
+ }
+ if ctrSpec.Linux.Resources.CPU.Quota != nil {
+ hostConfig.CpuQuota = *ctrSpec.Linux.Resources.CPU.Quota
+ }
+ if ctrSpec.Linux.Resources.CPU.RealtimePeriod != nil {
+ hostConfig.CpuRealtimePeriod = *ctrSpec.Linux.Resources.CPU.RealtimePeriod
+ }
+ if ctrSpec.Linux.Resources.CPU.RealtimeRuntime != nil {
+ hostConfig.CpuRealtimeRuntime = *ctrSpec.Linux.Resources.CPU.RealtimeRuntime
+ }
+ hostConfig.CpusetCpus = ctrSpec.Linux.Resources.CPU.Cpus
+ hostConfig.CpusetMems = ctrSpec.Linux.Resources.CPU.Mems
+ }
+ if ctrSpec.Linux.Resources.Memory != nil {
+ if ctrSpec.Linux.Resources.Memory.Limit != nil {
+ hostConfig.Memory = *ctrSpec.Linux.Resources.Memory.Limit
+ }
+ if ctrSpec.Linux.Resources.Memory.Reservation != nil {
+ hostConfig.MemoryReservation = *ctrSpec.Linux.Resources.Memory.Reservation
+ }
+ if ctrSpec.Linux.Resources.Memory.Swap != nil {
+ hostConfig.MemorySwap = *ctrSpec.Linux.Resources.Memory.Swap
+ }
+ if ctrSpec.Linux.Resources.Memory.Swappiness != nil {
+ hostConfig.MemorySwappiness = int64(*ctrSpec.Linux.Resources.Memory.Swappiness)
+ } else {
+ // Swappiness has a default of -1
+ hostConfig.MemorySwappiness = -1
+ }
+ if ctrSpec.Linux.Resources.Memory.DisableOOMKiller != nil {
+ hostConfig.OomKillDisable = *ctrSpec.Linux.Resources.Memory.DisableOOMKiller
+ }
+ }
+ if ctrSpec.Linux.Resources.Pids != nil {
+ hostConfig.PidsLimit = ctrSpec.Linux.Resources.Pids.Limit
+ }
+ hostConfig.CgroupConf = ctrSpec.Linux.Resources.Unified
+ if ctrSpec.Linux.Resources.BlockIO != nil {
+ if ctrSpec.Linux.Resources.BlockIO.Weight != nil {
+ hostConfig.BlkioWeight = *ctrSpec.Linux.Resources.BlockIO.Weight
+ }
+ hostConfig.BlkioWeightDevice = []define.InspectBlkioWeightDevice{}
+ for _, dev := range ctrSpec.Linux.Resources.BlockIO.WeightDevice {
+ key := fmt.Sprintf("%d:%d", dev.Major, dev.Minor)
+ // TODO: how do we handle LeafWeight vs
+ // Weight? For now, ignore anything
+ // without Weight set.
+ if dev.Weight == nil {
+ logrus.Infof("Ignoring weight device %s as it lacks a weight", key)
+ continue
+ }
+ if deviceNodes == nil {
+ nodes, err := util.FindDeviceNodes()
+ if err != nil {
+ return err
+ }
+ deviceNodes = nodes
+ }
+ path, ok := deviceNodes[key]
+ if !ok {
+ logrus.Infof("Could not locate weight device %s in system devices", key)
+ continue
+ }
+ weightDev := define.InspectBlkioWeightDevice{}
+ weightDev.Path = path
+ weightDev.Weight = *dev.Weight
+ hostConfig.BlkioWeightDevice = append(hostConfig.BlkioWeightDevice, weightDev)
+ }
+
+ readBps, err := blkioDeviceThrottle(deviceNodes, ctrSpec.Linux.Resources.BlockIO.ThrottleReadBpsDevice)
+ if err != nil {
+ return err
+ }
+ hostConfig.BlkioDeviceReadBps = readBps
+
+ writeBps, err := blkioDeviceThrottle(deviceNodes, ctrSpec.Linux.Resources.BlockIO.ThrottleWriteBpsDevice)
+ if err != nil {
+ return err
+ }
+ hostConfig.BlkioDeviceWriteBps = writeBps
+
+ readIops, err := blkioDeviceThrottle(deviceNodes, ctrSpec.Linux.Resources.BlockIO.ThrottleReadIOPSDevice)
+ if err != nil {
+ return err
+ }
+ hostConfig.BlkioDeviceReadIOps = readIops
+
+ writeIops, err := blkioDeviceThrottle(deviceNodes, ctrSpec.Linux.Resources.BlockIO.ThrottleWriteIOPSDevice)
+ if err != nil {
+ return err
+ }
+ hostConfig.BlkioDeviceWriteIOps = writeIops
+ }
+ }
+ }
+
+ // Cap add and cap drop.
+ // We need a default set of capabilities to compare against.
+ // The OCI generate package has one, and is commonly used, so we'll
+ // use it.
+ // Problem: there are 5 sets of capabilities.
+ // Use the bounding set for this computation, it's the most encompassing
+ // (but still not perfect).
+ capAdd := []string{}
+ capDrop := []string{}
+ // No point in continuing if we got a spec without a Process block...
+ if ctrSpec.Process != nil {
+ // Max an O(1) lookup table for default bounding caps.
+ boundingCaps := make(map[string]bool)
+ g, err := generate.New("linux")
+ if err != nil {
+ return err
+ }
+ if !hostConfig.Privileged {
+ for _, cap := range g.Config.Process.Capabilities.Bounding {
+ boundingCaps[cap] = true
+ }
+ } else {
+ // If we are privileged, use all caps.
+ for _, cap := range capability.List() {
+ if g.HostSpecific && cap > validate.LastCap() {
+ continue
+ }
+ boundingCaps[fmt.Sprintf("CAP_%s", strings.ToUpper(cap.String()))] = true
+ }
+ }
+ // Iterate through spec caps.
+ // If it's not in default bounding caps, it was added.
+ // If it is, delete from the default set. Whatever remains after
+ // we finish are the dropped caps.
+ for _, cap := range ctrSpec.Process.Capabilities.Bounding {
+ if _, ok := boundingCaps[cap]; ok {
+ delete(boundingCaps, cap)
+ } else {
+ capAdd = append(capAdd, cap)
+ }
+ }
+ for cap := range boundingCaps {
+ capDrop = append(capDrop, cap)
+ }
+ // Sort CapDrop so it displays in consistent order (GH #9490)
+ sort.Strings(capDrop)
+ }
+ hostConfig.CapAdd = capAdd
+ hostConfig.CapDrop = capDrop
+ switch {
+ case c.config.IPCNsCtr != "":
+ hostConfig.IpcMode = fmt.Sprintf("container:%s", c.config.IPCNsCtr)
+ case ctrSpec.Linux != nil:
+ // Locate the spec's IPC namespace.
+ // If there is none, it's ipc=host.
+ // If there is one and it has a path, it's "ns:".
+ // If no path, it's default - the empty string.
+ for _, ns := range ctrSpec.Linux.Namespaces {
+ if ns.Type == spec.IPCNamespace {
+ if ns.Path != "" {
+ hostConfig.IpcMode = fmt.Sprintf("ns:%s", ns.Path)
+ } else {
+ break
+ }
+ }
+ }
+ case c.config.NoShm:
+ hostConfig.IpcMode = "none"
+ case c.config.NoShmShare:
+ hostConfig.IpcMode = "private"
+ }
+ if hostConfig.IpcMode == "" {
+ hostConfig.IpcMode = "shareable"
+ }
+
+ // Cgroup namespace mode
+ cgroupMode := ""
+ if c.config.CgroupNsCtr != "" {
+ cgroupMode = fmt.Sprintf("container:%s", c.config.CgroupNsCtr)
+ } else if ctrSpec.Linux != nil {
+ // Locate the spec's cgroup namespace
+ // If there is none, it's cgroup=host.
+ // If there is one and it has a path, it's "ns:".
+ // If there is no path, it's private.
+ for _, ns := range ctrSpec.Linux.Namespaces {
+ if ns.Type == spec.CgroupNamespace {
+ if ns.Path != "" {
+ cgroupMode = fmt.Sprintf("ns:%s", ns.Path)
+ } else {
+ cgroupMode = "private"
+ }
+ }
+ }
+ if cgroupMode == "" {
+ cgroupMode = "host"
+ }
+ }
+ hostConfig.CgroupMode = cgroupMode
+
+ // Cgroup parent
+ // Need to check if it's the default, and not print if so.
+ defaultCgroupParent := ""
+ switch c.CgroupManager() {
+ case config.CgroupfsCgroupsManager:
+ defaultCgroupParent = CgroupfsDefaultCgroupParent
+ case config.SystemdCgroupsManager:
+ defaultCgroupParent = SystemdDefaultCgroupParent
+ }
+ if c.config.CgroupParent != defaultCgroupParent {
+ hostConfig.CgroupParent = c.config.CgroupParent
+ }
+ hostConfig.CgroupManager = c.CgroupManager()
+
+ // PID namespace mode
+ pidMode := ""
+ if c.config.PIDNsCtr != "" {
+ pidMode = fmt.Sprintf("container:%s", c.config.PIDNsCtr)
+ } else if ctrSpec.Linux != nil {
+ // Locate the spec's PID namespace.
+ // If there is none, it's pid=host.
+ // If there is one and it has a path, it's "ns:".
+ // If there is no path, it's default - the empty string.
+ for _, ns := range ctrSpec.Linux.Namespaces {
+ if ns.Type == spec.PIDNamespace {
+ if ns.Path != "" {
+ pidMode = fmt.Sprintf("ns:%s", ns.Path)
+ } else {
+ pidMode = "private"
+ }
+ break
+ }
+ }
+ if pidMode == "" {
+ pidMode = "host"
+ }
+ }
+ hostConfig.PidMode = pidMode
+
+ // UTS namespace mode
+ utsMode := c.NamespaceMode(spec.UTSNamespace, ctrSpec)
+
+ hostConfig.UTSMode = utsMode
+
+ // User namespace mode
+ usernsMode := ""
+ if c.config.UserNsCtr != "" {
+ usernsMode = fmt.Sprintf("container:%s", c.config.UserNsCtr)
+ } else if ctrSpec.Linux != nil {
+ // Locate the spec's user namespace.
+ // If there is none, it's default - the empty string.
+ // If there is one, it's "private" if no path, or "ns:" if
+ // there's a path.
+
+ for _, ns := range ctrSpec.Linux.Namespaces {
+ if ns.Type == spec.UserNamespace {
+ if ns.Path != "" {
+ usernsMode = fmt.Sprintf("ns:%s", ns.Path)
+ } else {
+ usernsMode = "private"
+ }
+ }
+ }
+ }
+ hostConfig.UsernsMode = usernsMode
+ if c.config.IDMappings.UIDMap != nil && c.config.IDMappings.GIDMap != nil {
+ hostConfig.IDMappings = generateIDMappings(c.config.IDMappings)
+ }
+ // Devices
+ // Do not include if privileged - assumed that all devices will be
+ // included.
+ var err error
+ hostConfig.Devices, err = c.GetDevices(hostConfig.Privileged, *ctrSpec, deviceNodes)
+ if err != nil {
+ return err
+ }
+
+ return nil
+}
diff --git a/libpod/pod_api.go b/libpod/pod_api.go
index 1bd686ddc..924d43436 100644
--- a/libpod/pod_api.go
+++ b/libpod/pod_api.go
@@ -40,7 +40,7 @@ func (p *Pod) startInitContainers(ctx context.Context) error {
icLock := initCon.lock
icLock.Lock()
var time *uint
- if err := p.runtime.removeContainer(ctx, initCon, false, false, true, time); err != nil {
+ if err := p.runtime.removeContainer(ctx, initCon, false, false, true, false, time); err != nil {
icLock.Unlock()
return fmt.Errorf("failed to remove once init container %s: %w", initCon.ID(), err)
}
diff --git a/libpod/runtime_ctr.go b/libpod/runtime_ctr.go
index 1f032dd6b..7b3cbadfa 100644
--- a/libpod/runtime_ctr.go
+++ b/libpod/runtime_ctr.go
@@ -581,7 +581,7 @@ func (r *Runtime) setupContainer(ctx context.Context, ctr *Container) (_ *Contai
// be removed also if and only if the container is the sole user
// Otherwise, RemoveContainer will return an error if the container is running
func (r *Runtime) RemoveContainer(ctx context.Context, c *Container, force bool, removeVolume bool, timeout *uint) error {
- return r.removeContainer(ctx, c, force, removeVolume, false, timeout)
+ return r.removeContainer(ctx, c, force, removeVolume, false, false, timeout)
}
// Internal function to remove a container.
@@ -589,7 +589,9 @@ func (r *Runtime) RemoveContainer(ctx context.Context, c *Container, force bool,
// removePod is used only when removing pods. It instructs Podman to ignore
// infra container protections, and *not* remove from the database (as pod
// remove will handle that).
-func (r *Runtime) removeContainer(ctx context.Context, c *Container, force, removeVolume, removePod bool, timeout *uint) error {
+// ignoreDeps is *DANGEROUS* and should not be used outside of a very specific
+// context (alternate pod removal code, where graph traversal is not possible).
+func (r *Runtime) removeContainer(ctx context.Context, c *Container, force, removeVolume, removePod, ignoreDeps bool, timeout *uint) error {
if !c.valid {
if ok, _ := r.state.HasContainer(c.ID()); !ok {
// Container probably already removed
@@ -618,25 +620,27 @@ func (r *Runtime) removeContainer(ctx context.Context, c *Container, force, remo
// pod.
var pod *Pod
runtime := c.runtime
- if c.config.Pod != "" && !removePod {
+ if c.config.Pod != "" {
pod, err = r.state.Pod(c.config.Pod)
if err != nil {
return fmt.Errorf("container %s is in pod %s, but pod cannot be retrieved: %w", c.ID(), pod.ID(), err)
}
- // Lock the pod while we're removing container
- if pod.config.LockID == c.config.LockID {
- return fmt.Errorf("container %s and pod %s share lock ID %d: %w", c.ID(), pod.ID(), c.config.LockID, define.ErrWillDeadlock)
- }
- pod.lock.Lock()
- defer pod.lock.Unlock()
- if err := pod.updatePod(); err != nil {
- return err
- }
+ if !removePod {
+ // Lock the pod while we're removing container
+ if pod.config.LockID == c.config.LockID {
+ return fmt.Errorf("container %s and pod %s share lock ID %d: %w", c.ID(), pod.ID(), c.config.LockID, define.ErrWillDeadlock)
+ }
+ pod.lock.Lock()
+ defer pod.lock.Unlock()
+ if err := pod.updatePod(); err != nil {
+ return err
+ }
- infraID := pod.state.InfraContainerID
- if c.ID() == infraID {
- return fmt.Errorf("container %s is the infra container of pod %s and cannot be removed without removing the pod", c.ID(), pod.ID())
+ infraID := pod.state.InfraContainerID
+ if c.ID() == infraID {
+ return fmt.Errorf("container %s is the infra container of pod %s and cannot be removed without removing the pod", c.ID(), pod.ID())
+ }
}
}
@@ -696,7 +700,7 @@ func (r *Runtime) removeContainer(ctx context.Context, c *Container, force, remo
// Check that no other containers depend on the container.
// Only used if not removing a pod - pods guarantee that all
// deps will be evicted at the same time.
- if !removePod {
+ if !ignoreDeps {
deps, err := r.state.ContainerInUse(c)
if err != nil {
return err
@@ -777,13 +781,11 @@ func (r *Runtime) removeContainer(ctx context.Context, c *Container, force, remo
if c.config.Pod != "" {
// If we're removing the pod, the container will be evicted
// from the state elsewhere
- if !removePod {
- if err := r.state.RemoveContainerFromPod(pod, c); err != nil {
- if cleanupErr == nil {
- cleanupErr = err
- } else {
- logrus.Errorf("Removing container %s from database: %v", c.ID(), err)
- }
+ if err := r.state.RemoveContainerFromPod(pod, c); err != nil {
+ if cleanupErr == nil {
+ cleanupErr = err
+ } else {
+ logrus.Errorf("Removing container %s from database: %v", c.ID(), err)
}
}
} else {
@@ -872,7 +874,7 @@ func (r *Runtime) evictContainer(ctx context.Context, idOrName string, removeVol
if err == nil {
logrus.Infof("Container %s successfully retrieved from state, attempting normal removal", id)
// Assume force = true for the evict case
- err = r.removeContainer(ctx, tmpCtr, true, removeVolume, false, timeout)
+ err = r.removeContainer(ctx, tmpCtr, true, removeVolume, false, false, timeout)
if !tmpCtr.valid {
// If the container is marked invalid, remove succeeded
// in kicking it out of the state - no need to continue.
@@ -1034,7 +1036,7 @@ func (r *Runtime) RemoveDepend(ctx context.Context, rmCtr *Container, force bool
}
report := reports.RmReport{Id: rmCtr.ID(), RawInput: rmCtr.ID()}
- report.Err = r.removeContainer(ctx, rmCtr, force, removeVolume, false, timeout)
+ report.Err = r.removeContainer(ctx, rmCtr, force, removeVolume, false, false, timeout)
return append(rmReports, &report), nil
}
diff --git a/libpod/runtime_img.go b/libpod/runtime_img.go
index 5510b2af6..dacbd752f 100644
--- a/libpod/runtime_img.go
+++ b/libpod/runtime_img.go
@@ -47,7 +47,7 @@ func (r *Runtime) RemoveContainersForImageCallback(ctx context.Context) libimage
return fmt.Errorf("removing image %s: container %s using image could not be removed: %w", imageID, ctr.ID(), err)
}
} else {
- if err := r.removeContainer(ctx, ctr, true, false, false, timeout); err != nil {
+ if err := r.removeContainer(ctx, ctr, true, false, false, false, timeout); err != nil {
return fmt.Errorf("removing image %s: container %s using image could not be removed: %w", imageID, ctr.ID(), err)
}
}
diff --git a/libpod/runtime_pod_linux.go b/libpod/runtime_pod_linux.go
index 3eeef69d8..24e9f3da7 100644
--- a/libpod/runtime_pod_linux.go
+++ b/libpod/runtime_pod_linux.go
@@ -17,6 +17,7 @@ import (
"github.com/containers/podman/v4/libpod/events"
"github.com/containers/podman/v4/pkg/rootless"
"github.com/containers/podman/v4/pkg/specgen"
+ "github.com/hashicorp/go-multierror"
"github.com/sirupsen/logrus"
)
@@ -191,29 +192,9 @@ func (r *Runtime) SavePod(pod *Pod) error {
return nil
}
-func (r *Runtime) removePod(ctx context.Context, p *Pod, removeCtrs, force bool, timeout *uint) error {
- if err := p.updatePod(); err != nil {
- return err
- }
-
- ctrs, err := r.state.PodContainers(p)
- if err != nil {
- return err
- }
- numCtrs := len(ctrs)
-
- // If the only running container in the pod is the pause container, remove the pod and container unconditionally.
- pauseCtrID := p.state.InfraContainerID
- if numCtrs == 1 && ctrs[0].ID() == pauseCtrID {
- removeCtrs = true
- force = true
- }
- if !removeCtrs && numCtrs > 0 {
- return fmt.Errorf("pod %s contains containers and cannot be removed: %w", p.ID(), define.ErrCtrExists)
- }
-
- ctrNamedVolumes := make(map[string]*ContainerNamedVolume)
-
+// DO NOT USE THIS FUNCTION DIRECTLY. Use removePod(), below. It will call
+// removeMalformedPod() if necessary.
+func (r *Runtime) removeMalformedPod(ctx context.Context, p *Pod, ctrs []*Container, force bool, timeout *uint, ctrNamedVolumes map[string]*ContainerNamedVolume) error {
var removalErr error
for _, ctr := range ctrs {
err := func() error {
@@ -231,7 +212,7 @@ func (r *Runtime) removePod(ctx context.Context, p *Pod, removeCtrs, force bool,
ctrNamedVolumes[vol.Name] = vol
}
- return r.removeContainer(ctx, ctr, force, false, true, timeout)
+ return r.removeContainer(ctx, ctr, force, false, true, true, timeout)
}()
if removalErr == nil {
@@ -261,6 +242,69 @@ func (r *Runtime) removePod(ctx context.Context, p *Pod, removeCtrs, force bool,
return err
}
+ return nil
+}
+
+func (r *Runtime) removePod(ctx context.Context, p *Pod, removeCtrs, force bool, timeout *uint) error {
+ if err := p.updatePod(); err != nil {
+ return err
+ }
+
+ ctrs, err := r.state.PodContainers(p)
+ if err != nil {
+ return err
+ }
+ numCtrs := len(ctrs)
+
+ // If the only running container in the pod is the pause container, remove the pod and container unconditionally.
+ pauseCtrID := p.state.InfraContainerID
+ if numCtrs == 1 && ctrs[0].ID() == pauseCtrID {
+ removeCtrs = true
+ force = true
+ }
+ if !removeCtrs && numCtrs > 0 {
+ return fmt.Errorf("pod %s contains containers and cannot be removed: %w", p.ID(), define.ErrCtrExists)
+ }
+
+ var removalErr error
+ ctrNamedVolumes := make(map[string]*ContainerNamedVolume)
+
+ // Build a graph of all containers in the pod.
+ graph, err := BuildContainerGraph(ctrs)
+ if err != nil {
+ // We have to allow the pod to be removed.
+ // But let's only do it if force is set.
+ if !force {
+ return fmt.Errorf("cannot create container graph for pod %s: %w", p.ID(), err)
+ }
+
+ removalErr = fmt.Errorf("creating container graph for pod %s failed, fell back to loop removal: %w", p.ID(), err)
+
+ if err := r.removeMalformedPod(ctx, p, ctrs, force, timeout, ctrNamedVolumes); err != nil {
+ logrus.Errorf("Error creating container graph for pod %s: %v. Falling back to loop removal.", p.ID(), err)
+ return err
+ }
+ } else {
+ ctrErrors := make(map[string]error)
+ ctrsVisited := make(map[string]bool)
+
+ for _, node := range graph.notDependedOnNodes {
+ removeNode(ctx, node, p, force, timeout, false, ctrErrors, ctrsVisited, ctrNamedVolumes)
+ }
+
+ // This is gross, but I don't want to change the signature on
+ // removePod - especially since any change here eventually has
+ // to map down to one error unless we want to make a breaking
+ // API change.
+ if len(ctrErrors) > 0 {
+ var allErrs error
+ for id, err := range ctrErrors {
+ allErrs = multierror.Append(allErrs, fmt.Errorf("removing container %s from pod %s: %w", id, p.ID(), err))
+ }
+ return allErrs
+ }
+ }
+
for volName := range ctrNamedVolumes {
volume, err := r.state.Volume(volName)
if err != nil && !errors.Is(err, define.ErrNoSuchVolume) {
diff --git a/libpod/runtime_volume_linux.go b/libpod/runtime_volume_linux.go
index c9a4a7dc1..08fdbf977 100644
--- a/libpod/runtime_volume_linux.go
+++ b/libpod/runtime_volume_linux.go
@@ -324,7 +324,7 @@ func (r *Runtime) removeVolume(ctx context.Context, v *Volume, force bool, timeo
logrus.Debugf("Removing container %s (depends on volume %q)", ctr.ID(), v.Name())
- if err := r.removeContainer(ctx, ctr, force, false, false, timeout); err != nil {
+ if err := r.removeContainer(ctx, ctr, force, false, false, false, timeout); err != nil {
return fmt.Errorf("removing container %s that depends on volume %s: %w", ctr.ID(), v.Name(), err)
}
}
diff --git a/pkg/api/handlers/compat/containers_stats.go b/pkg/api/handlers/compat/containers_stats.go
index c115b4181..519661675 100644
--- a/pkg/api/handlers/compat/containers_stats.go
+++ b/pkg/api/handlers/compat/containers_stats.go
@@ -11,6 +11,7 @@ import (
"github.com/containers/podman/v4/libpod/define"
"github.com/containers/podman/v4/pkg/api/handlers/utils"
api "github.com/containers/podman/v4/pkg/api/types"
+ "github.com/containers/storage/pkg/system"
docker "github.com/docker/docker/api/types"
"github.com/gorilla/schema"
runccgroups "github.com/opencontainers/runc/libcontainer/cgroups"
@@ -139,6 +140,16 @@ streamLabel: // A label to flatten the scope
memoryLimit = uint64(*cfg.Spec.Linux.Resources.Memory.Limit)
}
+ memInfo, err := system.ReadMemInfo()
+ if err != nil {
+ logrus.Errorf("Unable to get cgroup stats: %v", err)
+ return
+ }
+ // cap the memory limit to the available memory.
+ if memInfo.MemTotal > 0 && memoryLimit > uint64(memInfo.MemTotal) {
+ memoryLimit = uint64(memInfo.MemTotal)
+ }
+
systemUsage, _ := cgroups.GetSystemCPUUsage()
s := StatsJSON{
Stats: Stats{
@@ -177,7 +188,7 @@ streamLabel: // A label to flatten the scope
PreCPUStats: preCPUStats,
MemoryStats: docker.MemoryStats{
Usage: cgroupStat.MemoryStats.Usage.Usage,
- MaxUsage: cgroupStat.MemoryStats.Usage.Limit,
+ MaxUsage: cgroupStat.MemoryStats.Usage.MaxUsage,
Stats: nil,
Failcnt: 0,
Limit: memoryLimit,
diff --git a/test/apiv2/20-containers.at b/test/apiv2/20-containers.at
index 9ace46b8b..cc238e27e 100644
--- a/test/apiv2/20-containers.at
+++ b/test/apiv2/20-containers.at
@@ -98,6 +98,12 @@ else
fi
fi
+# max_usage is not set for cgroupv2
+if have_cgroupsv2; then
+ t GET libpod/containers/stats?containers='[$cid]' 200 \
+ .memory_stats.max_usage=null
+fi
+
t DELETE libpod/containers/$cid 200 .[0].Id=$cid
# Issue #14676: make sure the stats show the memory limit specified for the container
@@ -111,6 +117,17 @@ if root; then
podman rm -f $CTRNAME
fi
+# Issue #15765: make sure the memory limit is capped
+if root; then
+ CTRNAME=ctr-with-limit
+ podman run --name $CTRNAME -d -m 512m -v /tmp:/tmp $IMAGE top
+
+ t GET libpod/containers/$CTRNAME/stats?stream=false 200 \
+ .memory_stats.limit!=18446744073709552000
+
+ podman rm -f $CTRNAME
+fi
+
# Issue #6799: it should be possible to start a container, even w/o args.
t POST libpod/containers/create?name=test_noargs Image=${IMAGE} 201 \
.Id~[0-9a-f]\\{64\\}
diff --git a/test/apiv2/test-apiv2 b/test/apiv2/test-apiv2
index aca7db0dd..8132e6432 100755
--- a/test/apiv2/test-apiv2
+++ b/test/apiv2/test-apiv2
@@ -107,6 +107,22 @@ function is() {
_show_ok 0 "$testname" "$expect" "$actual"
}
+############
+# is_not # Simple disequality
+############
+function is_not() {
+ local actual=$1
+ local expect_not=$2
+ local testname=$3
+
+ if [ "$actual" != "$expect_not" ]; then
+ # On success, include expected value; this helps readers understand
+ _show_ok 1 "$testname!=$expect"
+ return
+ fi
+ _show_ok 0 "$testname" "!= $expect" "$actual"
+}
+
##########
# like # Compare, but allowing patterns
##########
@@ -256,8 +272,8 @@ function t() {
esac
done
if [[ -z "$curl_args" ]]; then
- curl_args=(-d $(jsonify ${post_args[@]}))
- testname="$testname [${curl_args[@]}]"
+ curl_args=(-d $(jsonify ${post_args[*]}))
+ testname="$testname [${curl_args[*]}]"
fi
fi
@@ -320,7 +336,7 @@ function t() {
# Any error from curl is instant bad news, from which we can't recover
if [[ $rc -ne 0 ]]; then
- die "curl failure ($rc) on $url - cannot continue"
+ die "curl failure ($rc) on $url - cannot continue. args=${curl_args[*]}"
fi
# Show returned headers (without trailing ^M or empty lines) in log file.
@@ -368,7 +384,7 @@ function t() {
# Special case: if response code does not match, dump the response body
# and skip all further subtests.
- if [[ $actual_code != $expected_code ]]; then
+ if [[ "$actual_code" != "$expected_code" ]]; then
echo -e "# response: $output"
for i; do
_show_ok skip "$testname: $i # skip - wrong return code"
@@ -377,7 +393,13 @@ function t() {
fi
for i; do
- if expr "$i" : "[^=~]\+=.*" >/dev/null; then
+ if expr "$i" : '[^\!]\+\!=.\+' >/dev/null; then
+ # Disequality on json field
+ json_field=$(expr "$i" : '\([^!]*\)!')
+ expect_not=$(expr "$i" : '[^\!]*\!=\(.*\)')
+ actual=$(jq -r "$json_field" <<<"$output")
+ is_not "$actual" "$expect_not" "$testname : $json_field"
+ elif expr "$i" : "[^=~]\+=.*" >/dev/null; then
# Exact match on json field
json_field=$(expr "$i" : "\([^=]*\)=")
expect=$(expr "$i" : '[^=]*=\(.*\)')
@@ -649,11 +671,11 @@ echo -e "collected ${#tests_to_run[@]} items\n"
start_service
-for i in ${tests_to_run[@]}; do
+for i in "${tests_to_run[@]}"; do
TEST_CONTEXT="[$(basename $i .at)]"
# Clear output from 'podman' helper
- >| $WORKDIR/output.log
+ truncate --size=0 $WORKDIR/output.log
source $i
done
diff --git a/test/buildah-bud/make-new-buildah-diffs b/test/buildah-bud/make-new-buildah-diffs
index 3d0a77008..f6404fa51 100644
--- a/test/buildah-bud/make-new-buildah-diffs
+++ b/test/buildah-bud/make-new-buildah-diffs
@@ -17,7 +17,7 @@ if [[ ! $whereami =~ test-buildah-v ]]; then
fi
# FIXME: check that git repo is buildah
-git remote -v | grep -q [BUILDAHREPO] \
+git remote -v | grep -q '[BUILDAHREPO]' \
|| die "This does not look like a buildah repo (git remote -v)"
# We could do the commit automatically, but it's prudent to require human
diff --git a/test/compose/test-compose b/test/compose/test-compose
index 99d063c25..fe2da9532 100755
--- a/test/compose/test-compose
+++ b/test/compose/test-compose
@@ -64,7 +64,7 @@ function is() {
local expect=$2
local testname=$3
- if [[ $actual = $expect ]]; then
+ if [[ "$actual" = "$expect" ]]; then
# On success, include expected value; this helps readers understand
_show_ok 1 "$testname=$expect"
return
@@ -303,12 +303,12 @@ n_tests=0
# We aren't really TAP 13; this helps logformatter recognize our output as BATS
echo "TAP version 13"
-for t in ${tests_to_run[@]}; do
+for t in "${tests_to_run[@]}"; do
testdir="$(dirname $t)"
testname="$(basename $testdir)"
if [ -e $test_dir/SKIP ]; then
- local reason="$(<$test_dir/SKIP)"
+ reason="$(<$test_dir/SKIP)"
if [ -n "$reason" ]; then
reason=" - $reason"
fi
diff --git a/test/e2e/pod_rm_test.go b/test/e2e/pod_rm_test.go
index a5eab7eed..364ef54d5 100644
--- a/test/e2e/pod_rm_test.go
+++ b/test/e2e/pod_rm_test.go
@@ -318,4 +318,31 @@ var _ = Describe("Podman pod rm", func() {
result.WaitWithDefaultTimeout()
Expect(result).Should(Exit(0))
})
+
+ It("podman pod rm pod with infra container and running container", func() {
+ podName := "testPod"
+ ctrName := "testCtr"
+
+ ctrAndPod := podmanTest.Podman([]string{"run", "-d", "--pod", fmt.Sprintf("new:%s", podName), "--name", ctrName, ALPINE, "top"})
+ ctrAndPod.WaitWithDefaultTimeout()
+ Expect(ctrAndPod).Should(Exit(0))
+
+ removePod := podmanTest.Podman([]string{"pod", "rm", "-a"})
+ removePod.WaitWithDefaultTimeout()
+ Expect(removePod).Should(Not(Exit(0)))
+
+ ps := podmanTest.Podman([]string{"pod", "ps"})
+ ps.WaitWithDefaultTimeout()
+ Expect(ps).Should(Exit(0))
+ Expect(ps.OutputToString()).To(ContainSubstring(podName))
+
+ removePodForce := podmanTest.Podman([]string{"pod", "rm", "-af"})
+ removePodForce.WaitWithDefaultTimeout()
+ Expect(removePodForce).Should(Exit(0))
+
+ ps2 := podmanTest.Podman([]string{"pod", "ps"})
+ ps2.WaitWithDefaultTimeout()
+ Expect(ps2).Should(Exit(0))
+ Expect(ps2.OutputToString()).To(Not(ContainSubstring(podName)))
+ })
})
diff --git a/test/system/015-help.bats b/test/system/015-help.bats
index dd5a7ed44..927645f29 100644
--- a/test/system/015-help.bats
+++ b/test/system/015-help.bats
@@ -121,7 +121,7 @@ function check_help() {
# Exceptions: these commands don't work rootless
if is_rootless; then
# "pause is not supported for rootless containers"
- if [ "$cmd" = "pause" -o "$cmd" = "unpause" ]; then
+ if [[ "$cmd" = "pause" ]] || [[ "$cmd" = "unpause" ]]; then
continue
fi
# "network rm" too
@@ -162,17 +162,17 @@ function check_help() {
# Any command that takes subcommands, prints its help and errors if called
# without one.
- dprint "podman $@"
+ dprint "podman $*"
run_podman '?' "$@"
is "$status" 125 "'podman $*' without any subcommand - exit status"
- is "$output" ".*Usage:.*Error: missing command '.*$@ COMMAND'" \
+ is "$output" ".*Usage:.*Error: missing command '.*$* COMMAND'" \
"'podman $*' without any subcommand - expected error message"
# Assume that 'NoSuchCommand' is not a command
- dprint "podman $@ NoSuchCommand"
+ dprint "podman $* NoSuchCommand"
run_podman '?' "$@" NoSuchCommand
is "$status" 125 "'podman $* NoSuchCommand' - exit status"
- is "$output" "Error: unrecognized command .*$@ NoSuchCommand" \
+ is "$output" "Error: unrecognized command .*$* NoSuchCommand" \
"'podman $* NoSuchCommand' - expected error message"
# This can happen if the output of --help changes, such as between
diff --git a/test/system/065-cp.bats b/test/system/065-cp.bats
index 8f5abd228..c8ad8468c 100644
--- a/test/system/065-cp.bats
+++ b/test/system/065-cp.bats
@@ -436,7 +436,7 @@ load helpers
run_podman cp cpcontainer:$src $destdir$dest
is "$(< $destdir$dest_fullname/containerfile0)" "${randomcontent[0]}" "$description"
is "$(< $destdir$dest_fullname/containerfile1)" "${randomcontent[1]}" "$description"
- rm -rf $destdir/*
+ rm -rf ${destdir:?}/*
done < <(parse_table "$tests")
run_podman kill cpcontainer
run_podman rm -t 0 -f cpcontainer
@@ -456,7 +456,7 @@ load helpers
run_podman cp cpcontainer:$src $destdir$dest
is "$(< $destdir$dest_fullname/containerfile0)" "${randomcontent[0]}" "$description"
is "$(< $destdir$dest_fullname/containerfile1)" "${randomcontent[1]}" "$description"
- rm -rf $destdir/*
+ rm -rf ${destdir:?}/*
done < <(parse_table "$tests")
touch $destdir/testfile
diff --git a/test/system/070-build.bats b/test/system/070-build.bats
index 9fddbaa21..87979483e 100644
--- a/test/system/070-build.bats
+++ b/test/system/070-build.bats
@@ -541,7 +541,7 @@ Labels.$label_name | $label_value
this-file-does-not-match-anything-in-ignore-file
comment
)
- for f in ${files[@]}; do
+ for f in "${files[@]}"; do
# The magic '##-' strips off the '-' prefix
echo "$f" > $tmpdir/${f##-}
done
diff --git a/test/system/160-volumes.bats b/test/system/160-volumes.bats
index 6829c6a78..08baaf468 100644
--- a/test/system/160-volumes.bats
+++ b/test/system/160-volumes.bats
@@ -315,11 +315,11 @@ EOF
# List available volumes for pruning after using 1,2,3
run_podman volume prune <<< N
- is "$(echo $(sort <<<${lines[@]:1:3}))" "${v[4]} ${v[5]} ${v[6]}" "volume prune, with 1,2,3 in use, lists 4,5,6"
+ is "$(echo $(sort <<<${lines[*]:1:3}))" "${v[4]} ${v[5]} ${v[6]}" "volume prune, with 1,2,3 in use, lists 4,5,6"
# List available volumes for pruning after using 1,2,3 and filtering; see #8913
run_podman volume prune --filter label=mylabel <<< N
- is "$(echo $(sort <<<${lines[@]:1:2}))" "${v[5]} ${v[6]}" "volume prune, with 1,2,3 in use and 4 filtered out, lists 5,6"
+ is "$(echo $(sort <<<${lines[*]:1:2}))" "${v[5]} ${v[6]}" "volume prune, with 1,2,3 in use and 4 filtered out, lists 5,6"
# prune should remove v4
run_podman volume prune --force
diff --git a/test/system/400-unprivileged-access.bats b/test/system/400-unprivileged-access.bats
index 0d6be2d60..d70c95973 100644
--- a/test/system/400-unprivileged-access.bats
+++ b/test/system/400-unprivileged-access.bats
@@ -119,7 +119,7 @@ EOF
# Some of the above may not exist on our host. Find only the ones that do.
local -a subset=()
- for mp in ${mps[@]}; do
+ for mp in "${mps[@]}"; do
if [ -e $mp ]; then
subset+=($mp)
fi
@@ -128,7 +128,7 @@ EOF
# Run 'stat' on all the files, plus /dev/null. Get path, file type,
# number of links, major, and minor (see below for why). Do it all
# in one go, to avoid multiple podman-runs
- run_podman '?' run --rm $IMAGE stat -c'%n:%F:%h:%T:%t' /dev/null ${subset[@]}
+ run_podman '?' run --rm $IMAGE stat -c'%n:%F:%h:%T:%t' /dev/null "${subset[@]}"
assert $status -le 1 "stat exit status: expected 0 or 1"
local devnull=