diff options
-rw-r--r-- | contrib/cirrus/git_authors_to_irc_nicks.csv | 8 | ||||
-rw-r--r-- | contrib/cirrus/lib.sh | 5 | ||||
-rwxr-xr-x | contrib/cirrus/success.sh | 48 | ||||
-rw-r--r-- | docs/tutorials/rootless_tutorial.md | 30 | ||||
-rw-r--r-- | pkg/spec/config_linux.go | 75 | ||||
-rw-r--r-- | test/e2e/run_device_test.go | 16 | ||||
-rw-r--r-- | troubleshooting.md | 19 |
7 files changed, 180 insertions, 21 deletions
diff --git a/contrib/cirrus/git_authors_to_irc_nicks.csv b/contrib/cirrus/git_authors_to_irc_nicks.csv new file mode 100644 index 000000000..4334b5cd2 --- /dev/null +++ b/contrib/cirrus/git_authors_to_irc_nicks.csv @@ -0,0 +1,8 @@ +# Comma separated mapping of author e-mail, to Freenode IRC nick. +# When no match is found here, the username portion of the e-mail is used. +# Sorting is done at runtime - first-found e-mail match wins. +# Comments (like this) and blank lines are ignored. + +rothberg@redhat.com,vrothberg +santiago@redhat.com,edsantiago +gscrivan@redhat.com,giuseppe diff --git a/contrib/cirrus/lib.sh b/contrib/cirrus/lib.sh index f66e63140..cd8b2ef61 100644 --- a/contrib/cirrus/lib.sh +++ b/contrib/cirrus/lib.sh @@ -28,11 +28,12 @@ CIRRUS_WORKING_DIR="${CIRRUS_WORKING_DIR:-$GOPATH/src/github.com/containers/libp export GOSRC="${GOSRC:-$CIRRUS_WORKING_DIR}" export PATH="$HOME/bin:$GOPATH/bin:/usr/local/bin:$PATH" export LD_LIBRARY_PATH="/usr/local/lib${LD_LIBRARY_PATH:+:${LD_LIBRARY_PATH}}" -TIMESTAMPS_FILEPATH="${TIMESTAMPS_FILEPATH:-/var/tmp/timestamps}" -SETUP_MARKER_FILEPATH="${SETUP_MARKER_FILEPATH:-/var/tmp/.setup_environment_sh_complete}" # Saves typing / in case location ever moves SCRIPT_BASE=${SCRIPT_BASE:-./contrib/cirrus} PACKER_BASE=${PACKER_BASE:-./contrib/cirrus/packer} +# Important filepaths +SETUP_MARKER_FILEPATH="${SETUP_MARKER_FILEPATH:-/var/tmp/.setup_environment_sh_complete}" +AUTHOR_NICKS_FILEPATH="${CIRRUS_WORKING_DIR}/${SCRIPT_BASE}/git_authors_to_irc_nicks.csv" cd $GOSRC if type -P git &> /dev/null diff --git a/contrib/cirrus/success.sh b/contrib/cirrus/success.sh index c4e150514..f2c9fbc7f 100755 --- a/contrib/cirrus/success.sh +++ b/contrib/cirrus/success.sh @@ -4,14 +4,52 @@ set -e source $(dirname $0)/lib.sh -req_env_var CIRRUS_BRANCH CIRRUS_BUILD_ID +req_env_var CIRRUS_BRANCH CIRRUS_BUILD_ID CIRRUS_REPO_FULL_NAME -REF=$(basename $CIRRUS_BRANCH) # PR number or branch named -URL="https://cirrus-ci.com/build/$CIRRUS_BUILD_ID" +cd $CIRRUS_WORKING_DIR if [[ "$CIRRUS_BRANCH" =~ "pull" ]] then - ircmsg "Cirrus-CI testing successful for PR #$REF: $URL" + echo "Finding commit authors for PR $CIRRUS_PR" + unset NICKS + if [[ -r "$AUTHOR_NICKS_FILEPATH" ]] + then + SHARANGE="${CIRRUS_BASE_SHA}..${CIRRUS_CHANGE_IN_REPO}" + EXCLUDE_RE='merge-robot' + AUTHOR_NICKS=$(egrep -v '(^[[:space:]]*$)|(^[[:space:]]*#)' "$AUTHOR_NICKS_FILEPATH" | sort -u) + # Depending on branch-state, it's possible SHARANGE could be _WAY_ too big + MAX_NICKS=10 + # newline separated + COMMIT_AUTHORS=$(git log --format='%ae' $SHARANGE | \ + sort -u | \ + egrep -v "$EXCLUDE_RE" | \ + tail -$MAX_NICKS) + + for c_email in $COMMIT_AUTHORS + do + echo -e "\tExamining $c_email" + NICK=$(echo "$AUTHOR_NICKS" | grep -m 1 "$c_email" | \ + awk --field-separator ',' '{print $2}' | tr -d '[[:blank:]]') + if [[ -n "$NICK" ]] + then + echo -e "\t\tFound $c_email -> $NICK in $(basename $AUTHOR_NICKS_FILEPATH)" + else + echo -e "\t\tNot found in $(basename $AUTHOR_NICKS_FILEPATH), using e-mail username." + NICK=$(echo "$c_email" | cut -d '@' -f 1) + fi + echo -e "\tUsing nick $NICK" + NICKS="${NICKS:+$NICKS, }$NICK" + done + fi + + unset MENTION_PREFIX + [[ -z "$NICKS" ]] || \ + MENTION_PREFIX="$NICKS: " + + URL="https://github.com/$CIRRUS_REPO_FULL_NAME/pull/$CIRRUS_PR" + PR_SUBJECT=$(echo "$CIRRUS_CHANGE_MESSAGE" | head -1) + ircmsg "${MENTION_PREFIX}Cirrus-CI testing successful for PR '$PR_SUBJECT': $URL" else - ircmsg "Cirrus-CI testing branch $REF successful: $URL" + URL="https://cirrus-ci.com/github/containers/libpod/$CIRRUS_BRANCH" + ircmsg "Cirrus-CI testing branch $(basename $CIRRUS_BRANCH) successful: $URL" fi diff --git a/docs/tutorials/rootless_tutorial.md b/docs/tutorials/rootless_tutorial.md index 92595dd02..91962fead 100644 --- a/docs/tutorials/rootless_tutorial.md +++ b/docs/tutorials/rootless_tutorial.md @@ -80,6 +80,36 @@ The Podman configuration files for root reside in /usr/share/containers with ove The default authorization file used by the `podman login` and `podman logout` commands reside in ${XDG\_RUNTIME\_DIR}/containers/auth.json. +## Systemd unit for rootless container + +``` +[Unit] +Description=nginx +Requires=user@1001.service +After=user@1001.service + +[Service] +Type=simple +KillMode=none +MemoryMax=200M +ExecStartPre=-/usr/bin/podman rm -f nginx +ExecStartPre=/usr/bin/podman pull nginx +ExecStart=/usr/bin/podman run --name=nginx -p 8080:80 -v /home/nginx/html:/usr/share/nginx/html:Z nginx +ExecStop=/usr/bin/podman stop nginx +Restart=always +User=nginx +Group=nginx + +[Install] +WantedBy=multi-user.target +``` + +This example unit will launch a nginx container using the existing user nginx with id 1001, serving static content from /home/nginx/html and limited to 200MB of RAM. + +You can use all the usual systemd flags to control the process, including capabilities and cgroup directives to limit memory or CPU. + +See #3866 for more details. + ## More information If you are still experiencing problems running Podman in a rootless environment, please refer to the [Shortcomings of Rootless Podman](https://github.com/containers/libpod/blob/master/rootless.md) page which lists known issues and solutions to known issues in this environment. diff --git a/pkg/spec/config_linux.go b/pkg/spec/config_linux.go index 60d31d78e..32d8cb4de 100644 --- a/pkg/spec/config_linux.go +++ b/pkg/spec/config_linux.go @@ -4,6 +4,7 @@ package createconfig import ( "fmt" + "io/ioutil" "os" "path/filepath" "strings" @@ -98,6 +99,26 @@ func addDevice(g *generate.Generator, device string) error { if err != nil { return errors.Wrapf(err, "%s is not a valid device", src) } + if rootless.IsRootless() { + if _, err := os.Stat(src); err != nil { + if os.IsNotExist(err) { + return errors.Wrapf(err, "the specified device %s doesn't exist", src) + } + return errors.Wrapf(err, "stat device %s exist", src) + } + perm := "ro" + if strings.Contains(permissions, "w") { + perm = "rw" + } + devMnt := spec.Mount{ + Destination: dst, + Type: TypeBind, + Source: src, + Options: []string{"slave", "nosuid", "noexec", perm, "rbind"}, + } + g.Config.Mounts = append(g.Config.Mounts, devMnt) + return nil + } dev.Path = dst linuxdev := spec.LinuxDevice{ Path: dev.Path, @@ -113,8 +134,53 @@ func addDevice(g *generate.Generator, device string) error { return nil } +// based on getDevices from runc (libcontainer/devices/devices.go) +func getDevices(path string) ([]*configs.Device, error) { + files, err := ioutil.ReadDir(path) + if err != nil { + if rootless.IsRootless() && os.IsPermission(err) { + return nil, nil + } + return nil, err + } + out := []*configs.Device{} + for _, f := range files { + switch { + case f.IsDir(): + switch f.Name() { + // ".lxc" & ".lxd-mounts" added to address https://github.com/lxc/lxd/issues/2825 + case "pts", "shm", "fd", "mqueue", ".lxc", ".lxd-mounts": + continue + default: + sub, err := getDevices(filepath.Join(path, f.Name())) + if err != nil { + return nil, err + } + if sub != nil { + out = append(out, sub...) + } + continue + } + case f.Name() == "console": + continue + } + device, err := devices.DeviceFromPath(filepath.Join(path, f.Name()), "rwm") + if err != nil { + if err == devices.ErrNotADevice { + continue + } + if os.IsNotExist(err) { + continue + } + return nil, err + } + out = append(out, device) + } + return out, nil +} + func (c *CreateConfig) addPrivilegedDevices(g *generate.Generator) error { - hostDevices, err := devices.HostDevices() + hostDevices, err := getDevices("/dev") if err != nil { return err } @@ -153,15 +219,16 @@ func (c *CreateConfig) addPrivilegedDevices(g *generate.Generator) error { newMounts = append(newMounts, devMnt) } g.Config.Mounts = append(newMounts, g.Config.Mounts...) + g.Config.Linux.Resources.Devices = nil } else { for _, d := range hostDevices { g.AddDevice(Device(d)) } + // Add resources device - need to clear the existing one first. + g.Config.Linux.Resources.Devices = nil + g.AddLinuxResourcesDevice(true, "", nil, nil, "rwm") } - // Add resources device - need to clear the existing one first. - g.Config.Linux.Resources.Devices = nil - g.AddLinuxResourcesDevice(true, "", nil, nil, "rwm") return nil } diff --git a/test/e2e/run_device_test.go b/test/e2e/run_device_test.go index cf7ce9cdf..d3b4b0e32 100644 --- a/test/e2e/run_device_test.go +++ b/test/e2e/run_device_test.go @@ -35,44 +35,40 @@ var _ = Describe("Podman run device", func() { }) It("podman run bad device test", func() { - session := podmanTest.Podman([]string{"run", "-q", "--device", "/dev/baddevice", ALPINE, "ls", "/dev/kmsg"}) + session := podmanTest.Podman([]string{"run", "-q", "--device", "/dev/baddevice", ALPINE, "true"}) session.WaitWithDefaultTimeout() Expect(session.ExitCode()).To(Not(Equal(0))) }) It("podman run device test", func() { - SkipIfRootless() - session := podmanTest.Podman([]string{"run", "-q", "--device", "/dev/kmsg", ALPINE, "ls", "--color=never", "/dev/kmsg"}) + session := podmanTest.Podman([]string{"run", "-q", "--security-opt", "label=disable", "--device", "/dev/kmsg", ALPINE, "ls", "--color=never", "/dev/kmsg"}) session.WaitWithDefaultTimeout() Expect(session.ExitCode()).To(Equal(0)) Expect(session.OutputToString()).To(Equal("/dev/kmsg")) }) It("podman run device rename test", func() { - SkipIfRootless() - session := podmanTest.Podman([]string{"run", "-q", "--device", "/dev/kmsg:/dev/kmsg1", ALPINE, "ls", "--color=never", "/dev/kmsg1"}) + session := podmanTest.Podman([]string{"run", "-q", "--security-opt", "label=disable", "--device", "/dev/kmsg:/dev/kmsg1", ALPINE, "ls", "--color=never", "/dev/kmsg1"}) session.WaitWithDefaultTimeout() Expect(session.ExitCode()).To(Equal(0)) Expect(session.OutputToString()).To(Equal("/dev/kmsg1")) }) It("podman run device permission test", func() { - SkipIfRootless() - session := podmanTest.Podman([]string{"run", "-q", "--device", "/dev/kmsg:r", ALPINE, "ls", "--color=never", "/dev/kmsg"}) + session := podmanTest.Podman([]string{"run", "-q", "--security-opt", "label=disable", "--device", "/dev/kmsg:r", ALPINE, "ls", "--color=never", "/dev/kmsg"}) session.WaitWithDefaultTimeout() Expect(session.ExitCode()).To(Equal(0)) Expect(session.OutputToString()).To(Equal("/dev/kmsg")) }) It("podman run device rename and permission test", func() { - SkipIfRootless() - session := podmanTest.Podman([]string{"run", "-q", "--device", "/dev/kmsg:/dev/kmsg1:r", ALPINE, "ls", "--color=never", "/dev/kmsg1"}) + session := podmanTest.Podman([]string{"run", "-q", "--security-opt", "label=disable", "--device", "/dev/kmsg:/dev/kmsg1:r", ALPINE, "ls", "--color=never", "/dev/kmsg1"}) session.WaitWithDefaultTimeout() Expect(session.ExitCode()).To(Equal(0)) Expect(session.OutputToString()).To(Equal("/dev/kmsg1")) }) It("podman run device rename and bad permission test", func() { - session := podmanTest.Podman([]string{"run", "-q", "--device", "/dev/kmsg:/dev/kmsg1:rd", ALPINE, "ls", "--color=never", "/dev/kmsg1"}) + session := podmanTest.Podman([]string{"run", "-q", "--security-opt", "label=disable", "--device", "/dev/kmsg:/dev/kmsg1:rd", ALPINE, "ls", "--color=never", "/dev/kmsg1"}) session.WaitWithDefaultTimeout() Expect(session.ExitCode()).To(Not(Equal(0))) }) diff --git a/troubleshooting.md b/troubleshooting.md index b88940dc8..9a5b38e01 100644 --- a/troubleshooting.md +++ b/troubleshooting.md @@ -391,3 +391,22 @@ Choose one of the following: * Install and configure fuse-overlayfs. * Install the fuse-overlayfs package for your Linux Distribution. * Add `mount_program = "/usr/bin/fuse-overlayfs` under `[storage.options]` in your `~/.config/containers/storage.conf` file. + +### 16) rhel7-init based images don't work with cgroups v2 + +The systemd version shipped in rhel7-init doesn't have support for cgroups v2. You'll need at least systemd 230. + +#### Symptom +```console + +sh# podman run --name test -d registry.access.redhat.com/rhel7-init:latest && sleep 10 && podman exec test systemctl status +c8567461948439bce72fad3076a91ececfb7b14d469bfa5fbc32c6403185beff +Failed to get D-Bus connection: Operation not permitted +Error: non zero exit code: 1: OCI runtime error +``` + +#### Solution +You'll need to either: + +* configure the host to use cgroups v1 +* update the image to use an updated version of systemd. |