summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--.cirrus.yml4
-rw-r--r--CONTRIBUTING.md73
-rw-r--r--Dockerfile2
-rw-r--r--cmd/podman/ps.go16
-rw-r--r--cmd/podman/runlabel.go5
-rw-r--r--cmd/podman/shared/container.go6
-rw-r--r--cmd/podman/shared/funcs.go41
-rw-r--r--cmd/podman/shared/funcs_test.go87
-rw-r--r--contrib/python/podman/test/test_images.py8
-rwxr-xr-xcontrib/python/podman/test/test_runner.sh4
-rw-r--r--libpod/container_api.go25
-rw-r--r--libpod/container_internal.go56
-rw-r--r--libpod/container_internal_linux.go44
-rw-r--r--libpod/runtime.go5
-rw-r--r--pkg/lookup/lookup.go156
-rw-r--r--pkg/spec/config_linux.go2
-rw-r--r--pkg/spec/parse.go14
-rw-r--r--test/e2e/run_selinux_test.go64
-rw-r--r--test/e2e/runlabel_test.go68
-rw-r--r--vendor.conf2
-rw-r--r--vendor/github.com/checkpoint-restore/go-criu/test/main.go133
-rw-r--r--vendor/github.com/checkpoint-restore/go-criu/test/phaul-main.go192
-rw-r--r--vendor/github.com/checkpoint-restore/go-criu/test/piggie.c57
-rw-r--r--vendor/github.com/containers/storage/drivers/overlay/check.go5
-rw-r--r--vendor/github.com/containers/storage/drivers/overlay/overlay.go14
25 files changed, 577 insertions, 506 deletions
diff --git a/.cirrus.yml b/.cirrus.yml
index 87842da74..ae660394b 100644
--- a/.cirrus.yml
+++ b/.cirrus.yml
@@ -20,7 +20,7 @@ env:
CNI_COMMIT: "7480240de9749f9a0a5c8614b17f1f03e0c06ab9"
CRIO_COMMIT: "662dbb31b5d4f5ed54511a47cde7190c61c28677"
CRIU_COMMIT: "584cbe4643c3fc7dc901ff08bf923ca0fe7326f9"
- RUNC_COMMIT: "ad0f5255060d36872be04de22f8731f38ef2d7b1"
+ RUNC_COMMIT: "78ef28e63bec2ee4c139b5e3e0d691eb9bdc748d"
# File to update in home-dir with task-specific env. var values
ENVLIB: ".bash_profile"
# Overrides default location (/tmp/cirrus) for repo clone
@@ -42,7 +42,7 @@ full_vm_testing_task:
# 'matrix' combinations. All run in parallel.
matrix:
# Images are generated separetly, from build_images_task (below)
- image_name: "ubuntu-1804-bionic-v20180911-libpod-fce09afe"
+ image_name: "ubuntu-1804-bionic-v20180911-libpod-63a86a18"
# TODO: Make these work (also build_images_task below)
#image_name: "rhel-server-ec2-7-5-165-1-libpod-fce09afe"
#image_name: "centos-7-v20180911-libpod-fce09afe"
diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md
index fa95bfe3a..c4e208894 100644
--- a/CONTRIBUTING.md
+++ b/CONTRIBUTING.md
@@ -52,11 +52,80 @@ too since in the end the entire PR will be reviewed anyway. When in doubt,
squash.
PRs that fix issues should include a reference like `Closes #XXXX` in the
-commit message so that github will automatically close the referenced issue
+commit message so that GitHub will automatically close the referenced issue
when the PR is merged.
PRs will be approved by an [approver][owners] listed in [`OWNERS`](OWNERS).
+### Describe your Changes in Commit Messages
+
+Describe your problem. Whether your patch is a one-line bug fix or 5000 lines
+of a new feature, there must be an underlying problem that motivated you to do
+this work. Convince the reviewer that there is a problem worth fixing and that
+it makes sense for them to read past the first paragraph.
+
+Describe user-visible impact. Straight up crashes and lockups are pretty
+convincing, but not all bugs are that blatant. Even if the problem was spotted
+during code review, describe the impact you think it can have on users. Keep in
+mind that the majority of users run packages provided by distributions, so
+include anything that could help route your change downstream.
+
+Quantify optimizations and trade-offs. If you claim improvements in
+performance, memory consumption, stack footprint, or binary size, include
+numbers that back them up. But also describe non-obvious costs. Optimizations
+usually aren’t free but trade-offs between CPU, memory, and readability; or,
+when it comes to heuristics, between different workloads. Describe the expected
+downsides of your optimization so that the reviewer can weigh costs against
+benefits.
+
+Once the problem is established, describe what you are actually doing about it
+in technical detail. It’s important to describe the change in plain English for
+the reviewer to verify that the code is behaving as you intend it to.
+
+Solve only one problem per patch. If your description starts to get long,
+that’s a sign that you probably need to split up your patch.
+
+If the patch fixes a logged bug entry, refer to that bug entry by number and
+URL. If the patch follows from a mailing list discussion, give a URL to the
+mailing list archive.
+
+However, try to make your explanation understandable without external
+resources. In addition to giving a URL to a mailing list archive or bug,
+summarize the relevant points of the discussion that led to the patch as
+submitted.
+
+If you want to refer to a specific commit, don’t just refer to the SHA-1 ID of
+the commit. Please also include the oneline summary of the commit, to make it
+easier for reviewers to know what it is about. Example:
+
+```
+Commit f641c2d9384e ("fix bug in rm -fa parallel deletes") [...]
+```
+
+You should also be sure to use at least the first twelve characters of the
+SHA-1 ID. The libpod repository holds a lot of objects, making collisions with
+shorter IDs a real possibility. Bear in mind that, even if there is no
+collision with your six-character ID now, that condition may change five years
+from now.
+
+If your patch fixes a bug in a specific commit, e.g. you found an issue using
+git bisect, please use the ‘Fixes:’ tag with the first 12 characters of the
+SHA-1 ID, and the one line summary. For example:
+
+```
+Fixes: f641c2d9384e ("fix bug in rm -fa parallel deletes")
+```
+
+The following git config settings can be used to add a pretty format for
+outputting the above style in the git log or git show commands:
+
+```
+[core]
+ abbrev = 12
+[pretty]
+ fixes = Fixes: %h (\"%s\")
+```
+
### Sign your PRs
The sign-off is a line at the end of the explanation for the patch. Your
@@ -127,7 +196,7 @@ Integration Tests [README.md](test/README.md).
For general questions and discussion, please use the
IRC `#podman` channel on `irc.freenode.net`.
-For discussions around issues/bugs and features, you can use the github
+For discussions around issues/bugs and features, you can use the GitHub
[issues](https://github.com/containers/libpod/issues)
and
[PRs](https://github.com/containers/libpod/pulls)
diff --git a/Dockerfile b/Dockerfile
index 62be638f2..70d1a7629 100644
--- a/Dockerfile
+++ b/Dockerfile
@@ -52,7 +52,7 @@ ADD . /go/src/github.com/containers/libpod
RUN set -x && cd /go/src/github.com/containers/libpod && make install.libseccomp.sudo
# Install runc
-ENV RUNC_COMMIT ad0f5255060d36872be04de22f8731f38ef2d7b1
+ENV RUNC_COMMIT 78ef28e63bec2ee4c139b5e3e0d691eb9bdc748d
RUN set -x \
&& export GOPATH="$(mktemp -d)" \
&& git clone https://github.com/opencontainers/runc.git "$GOPATH/src/github.com/opencontainers/runc" \
diff --git a/cmd/podman/ps.go b/cmd/podman/ps.go
index fa333f952..83274c9a8 100644
--- a/cmd/podman/ps.go
+++ b/cmd/podman/ps.go
@@ -329,16 +329,12 @@ func psCmd(c *cli.Context) error {
}
// Define a tab writer with stdout as the output
- w := tabwriter.NewWriter(os.Stdout, 0, 0, 1, ' ', 0)
+ w := tabwriter.NewWriter(os.Stdout, 0, 0, 2, ' ', 0)
defer w.Flush()
// Output standard PS headers
if !opts.Namespace {
- fmt.Fprintf(w, "\n%s\t%s\t%s\t%s\t%s\t%s\t%s", hid, himage, hcommand, hcreated, hstatus, hports, hnames)
- // If the user does not want size OR pod info, we print the isInfra bool
- if !opts.Size && !opts.Pod {
- fmt.Fprintf(w, "\t%s", hinfra)
- }
+ fmt.Fprintf(w, "%s\t%s\t%s\t%s\t%s\t%s\t%s", hid, himage, hcommand, hcreated, hstatus, hports, hnames)
// User wants pod info
if opts.Pod {
fmt.Fprintf(w, "\t%s", hpod)
@@ -349,7 +345,7 @@ func psCmd(c *cli.Context) error {
}
} else {
// Output Namespace headers
- fmt.Fprintf(w, "\n%s\t%s\t%s\t%s\t%s\t%s\t%s\t%s\t%s\t%s", hid, hnames, nspid, nscgroup, nsipc, nsmnt, nsnet, nspidns, nsuserns, nsuts)
+ fmt.Fprintf(w, "%s\t%s\t%s\t%s\t%s\t%s\t%s\t%s\t%s\t%s", hid, hnames, nspid, nscgroup, nsipc, nsmnt, nsnet, nspidns, nsuserns, nsuts)
}
// Now iterate each container and output its information
@@ -358,11 +354,6 @@ func psCmd(c *cli.Context) error {
// Standard PS output
if !opts.Namespace {
fmt.Fprintf(w, "\n%s\t%s\t%s\t%s\t%s\t%s\t%s", container.ID, container.Image, container.Command, container.Created, container.Status, container.Ports, container.Names)
-
- // If not size and not pod info, do isInfra
- if !opts.Size && !opts.Pod {
- fmt.Fprintf(w, "\t%t", container.IsInfra)
- }
// User wants pod info
if opts.Pod {
fmt.Fprintf(w, "\t%s", container.Pod)
@@ -385,6 +376,7 @@ func psCmd(c *cli.Context) error {
}
}
+ fmt.Fprint(w, "\n")
return nil
}
diff --git a/cmd/podman/runlabel.go b/cmd/podman/runlabel.go
index aa7411a5f..e1dee1fb2 100644
--- a/cmd/podman/runlabel.go
+++ b/cmd/podman/runlabel.go
@@ -196,7 +196,10 @@ func runlabelCmd(c *cli.Context) error {
runLabel = fmt.Sprintf("%s %s", runLabel, strings.Join(args[2:], " "))
}
- cmd := shared.GenerateCommand(runLabel, imageName, c.String("name"))
+ cmd, err := shared.GenerateCommand(runLabel, imageName, c.String("name"))
+ if err != nil {
+ return errors.Wrapf(err, "unable to generate command")
+ }
env := shared.GenerateRunEnvironment(c.String("name"), imageName, opts)
env = append(env, "PODMAN_RUNLABEL_NESTED=1")
diff --git a/cmd/podman/shared/container.go b/cmd/podman/shared/container.go
index b847314a4..4404268d4 100644
--- a/cmd/podman/shared/container.go
+++ b/cmd/podman/shared/container.go
@@ -24,6 +24,7 @@ import (
const (
cidTruncLength = 12
podTruncLength = 12
+ cmdTruncLength = 17
)
// PsOptions describes the struct being formed for ps
@@ -191,9 +192,12 @@ func NewBatchContainer(ctr *libpod.Container, opts PsOptions) (PsContainerOutput
pod := ctr.PodID()
if !opts.NoTrunc {
cid = cid[0:cidTruncLength]
- if len(pod) > 12 {
+ if len(pod) > podTruncLength {
pod = pod[0:podTruncLength]
}
+ if len(command) > cmdTruncLength {
+ command = command[0:cmdTruncLength] + "..."
+ }
}
pso.ID = cid
diff --git a/cmd/podman/shared/funcs.go b/cmd/podman/shared/funcs.go
index 485944f29..a92e0d547 100644
--- a/cmd/podman/shared/funcs.go
+++ b/cmd/podman/shared/funcs.go
@@ -3,11 +3,39 @@ package shared
import (
"fmt"
"os"
+ "path/filepath"
"strings"
)
+func substituteCommand(cmd string) (string, error) {
+ // If cmd is an absolute or relative path, check if the file exists.
+ // Throw an error if it doesn't exist.
+ if strings.Contains(cmd, "/") || strings.HasPrefix(cmd, ".") {
+ res, err := filepath.Abs(cmd)
+ if err != nil {
+ return "", err
+ }
+ if _, err := os.Stat(res); !os.IsNotExist(err) {
+ return res, nil
+ } else if err != nil {
+ return "", err
+ }
+ }
+
+ // Replace cmd with "/proc/self/exe" if "podman" or "docker" is being
+ // used. Otherwise, leave the command unchanged.
+ switch cmd {
+ case "podman":
+ fallthrough
+ case "docker":
+ return "/proc/self/exe", nil
+ default:
+ return cmd, nil
+ }
+}
+
// GenerateCommand takes a label (string) and converts it to an executable command
-func GenerateCommand(command, imageName, name string) []string {
+func GenerateCommand(command, imageName, name string) ([]string, error) {
var (
newCommand []string
)
@@ -15,8 +43,13 @@ func GenerateCommand(command, imageName, name string) []string {
name = imageName
}
cmd := strings.Split(command, " ")
- // Replace the first element of cmd with "/proc/self/exe"
- newCommand = append(newCommand, "/proc/self/exe")
+
+ prog, err := substituteCommand(cmd[0])
+ if err != nil {
+ return nil, err
+ }
+ newCommand = append(newCommand, prog)
+
for _, arg := range cmd[1:] {
var newArg string
switch arg {
@@ -37,7 +70,7 @@ func GenerateCommand(command, imageName, name string) []string {
}
newCommand = append(newCommand, newArg)
}
- return newCommand
+ return newCommand, nil
}
// GenerateRunEnvironment merges the current environment variables with optional
diff --git a/cmd/podman/shared/funcs_test.go b/cmd/podman/shared/funcs_test.go
index 612be480b..596df84e8 100644
--- a/cmd/podman/shared/funcs_test.go
+++ b/cmd/podman/shared/funcs_test.go
@@ -1,6 +1,10 @@
package shared
import (
+ "fmt"
+ "io/ioutil"
+ "os"
+ "path/filepath"
"strings"
"testing"
@@ -16,35 +20,106 @@ var (
func TestGenerateCommand(t *testing.T) {
inputCommand := "docker run -it --name NAME -e NAME=NAME -e IMAGE=IMAGE IMAGE echo install"
correctCommand := "/proc/self/exe run -it --name bar -e NAME=bar -e IMAGE=foo foo echo install"
- newCommand := GenerateCommand(inputCommand, "foo", "bar")
+ newCommand, err := GenerateCommand(inputCommand, "foo", "bar")
+ assert.Nil(t, err)
assert.Equal(t, correctCommand, strings.Join(newCommand, " "))
}
+func TestGenerateCommandCheckSubstitution(t *testing.T) {
+ type subsTest struct {
+ input string
+ expected string
+ shouldFail bool
+ }
+
+ absTmpFile, err := ioutil.TempFile("", "podmanRunlabelTestAbsolutePath")
+ assert.Nil(t, err, "error creating tempfile")
+ defer os.Remove(absTmpFile.Name())
+
+ relTmpFile, err := ioutil.TempFile("./", "podmanRunlabelTestRelativePath")
+ assert.Nil(t, err, "error creating tempfile")
+ defer os.Remove(relTmpFile.Name())
+ relTmpCmd, err := filepath.Abs(relTmpFile.Name())
+ assert.Nil(t, err, "error getting absolute path for relative tmpfile")
+
+ // this has a (low) potential of race conditions but no other way
+ removedTmpFile, err := ioutil.TempFile("", "podmanRunlabelTestRemove")
+ assert.Nil(t, err, "error creating tempfile")
+ os.Remove(removedTmpFile.Name())
+
+ absTmpCmd := fmt.Sprintf("%s --flag1 --flag2 --args=foo", absTmpFile.Name())
+ tests := []subsTest{
+ {
+ input: "docker run -it alpine:latest",
+ expected: "/proc/self/exe run -it alpine:latest",
+ shouldFail: false,
+ },
+ {
+ input: "podman run -it alpine:latest",
+ expected: "/proc/self/exe run -it alpine:latest",
+ shouldFail: false,
+ },
+ {
+ input: absTmpCmd,
+ expected: absTmpCmd,
+ shouldFail: false,
+ },
+ {
+ input: "./" + relTmpFile.Name(),
+ expected: relTmpCmd,
+ shouldFail: false,
+ },
+ {
+ input: "ls -la",
+ expected: "ls -la",
+ shouldFail: false,
+ },
+ {
+ input: removedTmpFile.Name(),
+ expected: "",
+ shouldFail: true,
+ },
+ }
+
+ for _, test := range tests {
+ newCommand, err := GenerateCommand(test.input, "foo", "bar")
+ if test.shouldFail {
+ assert.NotNil(t, err)
+ } else {
+ assert.Nil(t, err)
+ }
+ assert.Equal(t, test.expected, strings.Join(newCommand, " "))
+ }
+}
+
func TestGenerateCommandPath(t *testing.T) {
- inputCommand := "/usr/bin/docker run -it --name NAME -e NAME=NAME -e IMAGE=IMAGE IMAGE echo install"
+ inputCommand := "docker run -it --name NAME -e NAME=NAME -e IMAGE=IMAGE IMAGE echo install"
correctCommand := "/proc/self/exe run -it --name bar -e NAME=bar -e IMAGE=foo foo echo install"
- newCommand := GenerateCommand(inputCommand, "foo", "bar")
+ newCommand, _ := GenerateCommand(inputCommand, "foo", "bar")
assert.Equal(t, correctCommand, strings.Join(newCommand, " "))
}
func TestGenerateCommandNoSetName(t *testing.T) {
inputCommand := "docker run -it --name NAME -e NAME=NAME -e IMAGE=IMAGE IMAGE echo install"
correctCommand := "/proc/self/exe run -it --name foo -e NAME=foo -e IMAGE=foo foo echo install"
- newCommand := GenerateCommand(inputCommand, "foo", "")
+ newCommand, err := GenerateCommand(inputCommand, "foo", "")
+ assert.Nil(t, err)
assert.Equal(t, correctCommand, strings.Join(newCommand, " "))
}
func TestGenerateCommandNoName(t *testing.T) {
inputCommand := "docker run -it -e IMAGE=IMAGE IMAGE echo install"
correctCommand := "/proc/self/exe run -it -e IMAGE=foo foo echo install"
- newCommand := GenerateCommand(inputCommand, "foo", "")
+ newCommand, err := GenerateCommand(inputCommand, "foo", "")
+ assert.Nil(t, err)
assert.Equal(t, correctCommand, strings.Join(newCommand, " "))
}
func TestGenerateCommandAlreadyPodman(t *testing.T) {
inputCommand := "podman run -it --name NAME -e NAME=NAME -e IMAGE=IMAGE IMAGE echo install"
correctCommand := "/proc/self/exe run -it --name bar -e NAME=bar -e IMAGE=foo foo echo install"
- newCommand := GenerateCommand(inputCommand, "foo", "bar")
+ newCommand, err := GenerateCommand(inputCommand, "foo", "bar")
+ assert.Nil(t, err)
assert.Equal(t, correctCommand, strings.Join(newCommand, " "))
}
diff --git a/contrib/python/podman/test/test_images.py b/contrib/python/podman/test/test_images.py
index f6b95f98a..f97e13b4c 100644
--- a/contrib/python/podman/test/test_images.py
+++ b/contrib/python/podman/test/test_images.py
@@ -6,6 +6,7 @@ from datetime import datetime, timezone
from test.podman_testcase import PodmanTestCase
import podman
+from podman import FoldedString
class TestImages(PodmanTestCase):
@@ -44,6 +45,7 @@ class TestImages(PodmanTestCase):
self.assertGreaterEqual(len(actual), 2)
self.assertIsNotNone(self.alpine_image)
+ @unittest.skip('TODO: missing buildah json file')
def test_build(self):
path = os.path.join(self.tmpdir, 'ctnr', 'Dockerfile')
img, logs = self.pclient.images.build(
@@ -59,12 +61,12 @@ class TestImages(PodmanTestCase):
def test_create(self):
img_details = self.alpine_image.inspect()
- actual = self.alpine_image.container()
+ actual = self.alpine_image.container(command=['sleep', '1h'])
self.assertIsNotNone(actual)
- self.assertEqual(actual.status, 'configured')
+ self.assertEqual(FoldedString(actual.status), 'configured')
ctnr = actual.start()
- self.assertIn(ctnr.status, ['running', 'stopped', 'exited'])
+ self.assertEqual(FoldedString(ctnr.status), 'running')
ctnr_details = ctnr.inspect()
for e in img_details.containerconfig['env']:
diff --git a/contrib/python/podman/test/test_runner.sh b/contrib/python/podman/test/test_runner.sh
index 65cbd1e9c..bf097e2b2 100755
--- a/contrib/python/podman/test/test_runner.sh
+++ b/contrib/python/podman/test/test_runner.sh
@@ -31,7 +31,9 @@ function cleanup {
# aggressive cleanup as tests may crash leaving crap around
umount '^(shm|nsfs)'
umount '\/run\/netns'
- rm -r "$1"
+ if [[ $RETURNCODE -eq 0 ]]; then
+ rm -r "$1"
+ fi
}
# Create temporary directory for storage
diff --git a/libpod/container_api.go b/libpod/container_api.go
index 41a131ea2..30c67eb2a 100644
--- a/libpod/container_api.go
+++ b/libpod/container_api.go
@@ -10,8 +10,8 @@ import (
"time"
"github.com/containers/libpod/libpod/driver"
- "github.com/containers/libpod/pkg/chrootuser"
"github.com/containers/libpod/pkg/inspect"
+ "github.com/containers/libpod/pkg/lookup"
"github.com/containers/storage/pkg/stringid"
"github.com/docker/docker/daemon/caps"
"github.com/pkg/errors"
@@ -292,13 +292,13 @@ func (c *Container) Exec(tty, privileged bool, env, cmd []string, user string) e
// the host
hostUser := ""
if user != "" {
- uid, gid, err := chrootuser.GetUser(c.state.Mountpoint, user)
+ execUser, err := lookup.GetUserGroupInfo(c.state.Mountpoint, user, nil)
if err != nil {
- return errors.Wrapf(err, "error getting user to launch exec session as")
+ return err
}
// runc expects user formatted as uid:gid
- hostUser = fmt.Sprintf("%d:%d", uid, gid)
+ hostUser = fmt.Sprintf("%d:%d", execUser.Uid, execUser.Gid)
}
// Generate exec session ID
@@ -453,22 +453,19 @@ func (c *Container) Unmount(force bool) error {
}
}
- if c.state.State == ContainerStateRunning || c.state.State == ContainerStatePaused {
- return errors.Wrapf(ErrCtrStateInvalid, "cannot unmount storage for container %s as it is running or paused", c.ID())
- }
-
- // Check if we have active exec sessions
- if len(c.state.ExecSessions) != 0 {
- return errors.Wrapf(ErrCtrStateInvalid, "container %s has active exec sessions, refusing to unmount", c.ID())
- }
-
if c.state.Mounted {
mounted, err := c.runtime.storageService.MountedContainerImage(c.ID())
if err != nil {
return errors.Wrapf(err, "can't determine how many times %s is mounted, refusing to unmount", c.ID())
}
if mounted == 1 {
- return errors.Wrapf(err, "can't unmount %s last mount, it is still in use", c.ID())
+ if c.state.State == ContainerStateRunning || c.state.State == ContainerStatePaused {
+ return errors.Wrapf(ErrCtrStateInvalid, "cannot unmount storage for container %s as it is running or paused", c.ID())
+ }
+ if len(c.state.ExecSessions) != 0 {
+ return errors.Wrapf(ErrCtrStateInvalid, "container %s has active exec sessions, refusing to unmount", c.ID())
+ }
+ return errors.Wrapf(ErrInternal, "can't unmount %s last mount, it is still in use", c.ID())
}
}
return c.unmount(force)
diff --git a/libpod/container_internal.go b/libpod/container_internal.go
index 2af216358..d928c4aed 100644
--- a/libpod/container_internal.go
+++ b/libpod/container_internal.go
@@ -5,6 +5,7 @@ import (
"context"
"encoding/json"
"fmt"
+ "github.com/opencontainers/runc/libcontainer/user"
"io"
"io/ioutil"
"os"
@@ -14,9 +15,9 @@ import (
"syscall"
"github.com/containers/buildah/imagebuildah"
- "github.com/containers/libpod/pkg/chrootuser"
"github.com/containers/libpod/pkg/hooks"
"github.com/containers/libpod/pkg/hooks/exec"
+ "github.com/containers/libpod/pkg/lookup"
"github.com/containers/libpod/pkg/resolvconf"
"github.com/containers/libpod/pkg/rootless"
"github.com/containers/libpod/pkg/secrets"
@@ -1029,7 +1030,8 @@ func (c *Container) writeStringToRundir(destFile, output string) (string, error)
func (c *Container) generatePasswd() (string, error) {
var (
groupspec string
- gid uint32
+ group *user.Group
+ gid int
)
if c.config.User == "" {
return "", nil
@@ -1044,21 +1046,27 @@ func (c *Container) generatePasswd() (string, error) {
if err != nil {
return "", nil
}
- // if UID exists inside of container rootfs /etc/passwd then
- // don't generate passwd
- if _, _, err := chrootuser.LookupUIDInContainer(c.state.Mountpoint, uid); err == nil {
+ // Lookup the user to see if it exists in the container image
+ _, err = lookup.GetUser(c.state.Mountpoint, userspec)
+ if err != nil && err != user.ErrNoPasswdEntries {
+ return "", err
+ }
+ if err == nil {
return "", nil
}
- if err == nil && groupspec != "" {
+ if groupspec != "" {
if !c.state.Mounted {
return "", errors.Wrapf(ErrCtrStateInvalid, "container %s must be mounted in order to translate group field for passwd record", c.ID())
}
- gid, err = chrootuser.GetGroup(c.state.Mountpoint, groupspec)
+ group, err = lookup.GetGroup(c.state.Mountpoint, groupspec)
if err != nil {
- return "", errors.Wrapf(err, "unable to get gid from %s formporary passwd file")
+ if err == user.ErrNoGroupEntries {
+ return "", errors.Wrapf(err, "unable to get gid %s from group file", groupspec)
+ }
+ return "", err
}
+ gid = group.Gid
}
-
originPasswdFile := filepath.Join(c.state.Mountpoint, "/etc/passwd")
orig, err := ioutil.ReadFile(originPasswdFile)
if err != nil {
@@ -1153,6 +1161,7 @@ func (c *Container) generateHosts() (string, error) {
}
func (c *Container) addLocalVolumes(ctx context.Context, g *generate.Generator) error {
+ var uid, gid int
mountPoint := c.state.Mountpoint
if !c.state.Mounted {
return errors.Wrapf(ErrInternal, "container is not mounted")
@@ -1176,6 +1185,18 @@ func (c *Container) addLocalVolumes(ctx context.Context, g *generate.Generator)
}
}
+ if c.config.User != "" {
+ if !c.state.Mounted {
+ return errors.Wrapf(ErrCtrStateInvalid, "container %s must be mounted in order to translate User field", c.ID())
+ }
+ execUser, err := lookup.GetUserGroupInfo(c.state.Mountpoint, c.config.User, nil)
+ if err != nil {
+ return err
+ }
+ uid = execUser.Uid
+ gid = execUser.Gid
+ }
+
for k := range imageData.ContainerConfig.Volumes {
mount := spec.Mount{
Destination: k,
@@ -1186,19 +1207,6 @@ func (c *Container) addLocalVolumes(ctx context.Context, g *generate.Generator)
continue
}
volumePath := filepath.Join(c.config.StaticDir, "volumes", k)
- var (
- uid uint32
- gid uint32
- )
- if c.config.User != "" {
- if !c.state.Mounted {
- return errors.Wrapf(ErrCtrStateInvalid, "container %s must be mounted in order to translate User field", c.ID())
- }
- uid, gid, err = chrootuser.GetUser(c.state.Mountpoint, c.config.User)
- if err != nil {
- return err
- }
- }
// Ensure the symlinks are resolved
resolvedSymlink, err := imagebuildah.ResolveSymLink(mountPoint, k)
@@ -1218,7 +1226,7 @@ func (c *Container) addLocalVolumes(ctx context.Context, g *generate.Generator)
return errors.Wrapf(err, "error creating directory %q for volume %q in container %q", volumePath, k, c.ID())
}
- if err = os.Chown(srcPath, int(uid), int(gid)); err != nil {
+ if err = os.Chown(srcPath, uid, gid); err != nil {
return errors.Wrapf(err, "error chowning directory %q for volume %q in container %q", srcPath, k, c.ID())
}
}
@@ -1228,7 +1236,7 @@ func (c *Container) addLocalVolumes(ctx context.Context, g *generate.Generator)
return errors.Wrapf(err, "error creating directory %q for volume %q in container %q", volumePath, k, c.ID())
}
- if err = os.Chown(volumePath, int(uid), int(gid)); err != nil {
+ if err = os.Chown(volumePath, uid, gid); err != nil {
return errors.Wrapf(err, "error chowning directory %q for volume %q in container %q", volumePath, k, c.ID())
}
diff --git a/libpod/container_internal_linux.go b/libpod/container_internal_linux.go
index 0a1784ba7..7bf2c71ca 100644
--- a/libpod/container_internal_linux.go
+++ b/libpod/container_internal_linux.go
@@ -19,12 +19,10 @@ import (
cnitypes "github.com/containernetworking/cni/pkg/types/current"
"github.com/containernetworking/plugins/pkg/ns"
crioAnnotations "github.com/containers/libpod/pkg/annotations"
- "github.com/containers/libpod/pkg/chrootuser"
"github.com/containers/libpod/pkg/criu"
+ "github.com/containers/libpod/pkg/lookup"
"github.com/containers/libpod/pkg/rootless"
"github.com/containers/storage/pkg/idtools"
- "github.com/cyphar/filepath-securejoin"
- "github.com/opencontainers/runc/libcontainer/user"
spec "github.com/opencontainers/runtime-spec/specs-go"
"github.com/opencontainers/runtime-tools/generate"
"github.com/opencontainers/selinux/go-selinux/label"
@@ -135,6 +133,10 @@ func (c *Container) cleanupNetwork() error {
// Generate spec for a container
// Accepts a map of the container's dependencies
func (c *Container) generateSpec(ctx context.Context) (*spec.Spec, error) {
+ execUser, err := lookup.GetUserGroupInfo(c.state.Mountpoint, c.config.User, nil)
+ if err != nil {
+ return nil, err
+ }
g := generate.NewFromSpec(c.config.Spec)
// If network namespace was requested, add it now
@@ -188,7 +190,6 @@ func (c *Container) generateSpec(ctx context.Context) (*spec.Spec, error) {
}
}
- var err error
if !rootless.IsRootless() {
if c.state.ExtensionStageHooks, err = c.setupOCIHooks(ctx, g.Config); err != nil {
return nil, errors.Wrapf(err, "error setting up OCI Hooks")
@@ -206,13 +207,9 @@ func (c *Container) generateSpec(ctx context.Context) (*spec.Spec, error) {
if !c.state.Mounted {
return nil, errors.Wrapf(ErrCtrStateInvalid, "container %s must be mounted in order to translate User field", c.ID())
}
- uid, gid, err := chrootuser.GetUser(c.state.Mountpoint, c.config.User)
- if err != nil {
- return nil, err
- }
// User and Group must go together
- g.SetProcessUID(uid)
- g.SetProcessGID(gid)
+ g.SetProcessUID(uint32(execUser.Uid))
+ g.SetProcessGID(uint32(execUser.Gid))
}
// Add addition groups if c.config.GroupAdd is not empty
@@ -220,11 +217,8 @@ func (c *Container) generateSpec(ctx context.Context) (*spec.Spec, error) {
if !c.state.Mounted {
return nil, errors.Wrapf(ErrCtrStateInvalid, "container %s must be mounted in order to add additional groups", c.ID())
}
- for _, group := range c.config.Groups {
- gid, err := chrootuser.GetGroup(c.state.Mountpoint, group)
- if err != nil {
- return nil, err
- }
+ gids, _ := lookup.GetContainerGroups(c.config.Groups, c.state.Mountpoint, nil)
+ for _, gid := range gids {
g.AddProcessAdditionalGid(gid)
}
}
@@ -237,26 +231,6 @@ func (c *Container) generateSpec(ctx context.Context) (*spec.Spec, error) {
// Look up and add groups the user belongs to, if a group wasn't directly specified
if !rootless.IsRootless() && !strings.Contains(c.config.User, ":") {
- var groupDest, passwdDest string
- defaultExecUser := user.ExecUser{
- Uid: 0,
- Gid: 0,
- Home: "/",
- }
-
- // Make sure the /etc/group and /etc/passwd destinations are not a symlink to something naughty
- if groupDest, err = securejoin.SecureJoin(c.state.Mountpoint, "/etc/group"); err != nil {
- logrus.Debug(err)
- return nil, err
- }
- if passwdDest, err = securejoin.SecureJoin(c.state.Mountpoint, "/etc/passwd"); err != nil {
- logrus.Debug(err)
- return nil, err
- }
- execUser, err := user.GetExecUserPath(c.config.User, &defaultExecUser, passwdDest, groupDest)
- if err != nil {
- return nil, err
- }
for _, gid := range execUser.Sgids {
g.AddProcessAdditionalGid(uint32(gid))
}
diff --git a/libpod/runtime.go b/libpod/runtime.go
index 1b26f851f..318cd0369 100644
--- a/libpod/runtime.go
+++ b/libpod/runtime.go
@@ -184,6 +184,8 @@ var (
RuntimePath: []string{
"/usr/bin/runc",
"/usr/sbin/runc",
+ "/usr/local/bin/runc",
+ "/usr/local/sbin/runc",
"/sbin/runc",
"/bin/runc",
"/usr/lib/cri-o-runc/sbin/runc",
@@ -191,6 +193,7 @@ var (
ConmonPath: []string{
"/usr/libexec/podman/conmon",
"/usr/libexec/crio/conmon",
+ "/usr/local/lib/podman/conmon",
"/usr/local/libexec/crio/conmon",
"/usr/bin/conmon",
"/usr/sbin/conmon",
@@ -206,7 +209,7 @@ var (
MaxLogSize: -1,
NoPivotRoot: false,
CNIConfigDir: "/etc/cni/net.d/",
- CNIPluginDir: []string{"/usr/libexec/cni", "/usr/lib/cni", "/opt/cni/bin"},
+ CNIPluginDir: []string{"/usr/libexec/cni", "/usr/lib/cni", "/usr/local/lib/cni", "/opt/cni/bin"},
InfraCommand: DefaultInfraCommand,
InfraImage: DefaultInfraImage,
EnablePortReservation: true,
diff --git a/pkg/lookup/lookup.go b/pkg/lookup/lookup.go
new file mode 100644
index 000000000..b27e2a724
--- /dev/null
+++ b/pkg/lookup/lookup.go
@@ -0,0 +1,156 @@
+package lookup
+
+import (
+ "github.com/cyphar/filepath-securejoin"
+ "github.com/opencontainers/runc/libcontainer/user"
+ "github.com/sirupsen/logrus"
+ "strconv"
+)
+
+const (
+ etcpasswd = "/etc/passwd"
+ etcgroup = "/etc/group"
+)
+
+// Overrides allows you to override defaults in GetUserGroupInfo
+type Overrides struct {
+ DefaultUser *user.ExecUser
+ ContainerEtcPasswdPath string
+ ContainerEtcGroupPath string
+}
+
+// GetUserGroupInfo takes string forms of the the container's mount path and the container user and
+// returns a ExecUser with uid, gid, sgids, and home. And override can be provided for defaults.
+func GetUserGroupInfo(containerMount, containerUser string, override *Overrides) (*user.ExecUser, error) {
+ var (
+ passwdDest, groupDest string
+ defaultExecUser *user.ExecUser
+ err error
+ )
+ passwdPath := etcpasswd
+ groupPath := etcgroup
+
+ if override != nil {
+ // Check for an override /etc/passwd path
+ if override.ContainerEtcPasswdPath != "" {
+ passwdPath = override.ContainerEtcPasswdPath
+ }
+ // Check for an override for /etc/group path
+ if override.ContainerEtcGroupPath != "" {
+ groupPath = override.ContainerEtcGroupPath
+ }
+ }
+
+ // Check for an override default user
+ if override != nil && override.DefaultUser != nil {
+ defaultExecUser = override.DefaultUser
+ } else {
+ // Define a default container user
+ //defaultExecUser = &user.ExecUser{
+ // Uid: 0,
+ // Gid: 0,
+ // Home: "/",
+ defaultExecUser = nil
+
+ }
+
+ // Make sure the /etc/group and /etc/passwd destinations are not a symlink to something naughty
+ if passwdDest, err = securejoin.SecureJoin(containerMount, passwdPath); err != nil {
+ logrus.Debug(err)
+ return nil, err
+ }
+ if groupDest, err = securejoin.SecureJoin(containerMount, groupPath); err != nil {
+ logrus.Debug(err)
+ return nil, err
+ }
+ return user.GetExecUserPath(containerUser, defaultExecUser, passwdDest, groupDest)
+}
+
+// GetContainerGroups uses securejoin to get a list of numerical groupids from a container. Per the runc
+// function it calls: If a group name cannot be found, an error will be returned. If a group id cannot be found,
+// or the given group data is nil, the id will be returned as-is provided it is in the legal range.
+func GetContainerGroups(groups []string, containerMount string, override *Overrides) ([]uint32, error) {
+ var (
+ groupDest string
+ err error
+ uintgids []uint32
+ )
+
+ groupPath := etcgroup
+ if override != nil && override.ContainerEtcGroupPath != "" {
+ groupPath = override.ContainerEtcGroupPath
+ }
+
+ if groupDest, err = securejoin.SecureJoin(containerMount, groupPath); err != nil {
+ logrus.Debug(err)
+ return nil, err
+ }
+
+ gids, err := user.GetAdditionalGroupsPath(groups, groupDest)
+ if err != nil {
+ return nil, err
+ }
+ // For libpod, we want []uint32s
+ for _, gid := range gids {
+ uintgids = append(uintgids, uint32(gid))
+ }
+ return uintgids, nil
+}
+
+// GetUser takes a containermount path and user name or id and returns
+// a matching User structure from /etc/passwd. If it cannot locate a user
+// with the provided information, an ErrNoPasswdEntries is returned.
+func GetUser(containerMount, userIDorName string) (*user.User, error) {
+ var inputIsName bool
+ uid, err := strconv.Atoi(userIDorName)
+ if err != nil {
+ inputIsName = true
+ }
+ passwdDest, err := securejoin.SecureJoin(containerMount, etcpasswd)
+ if err != nil {
+ return nil, err
+ }
+ users, err := user.ParsePasswdFileFilter(passwdDest, func(u user.User) bool {
+ if inputIsName {
+ return u.Name == userIDorName
+ }
+ return u.Uid == uid
+ })
+ if err != nil {
+ return nil, err
+ }
+ if len(users) > 0 {
+ return &users[0], nil
+ }
+ return nil, user.ErrNoPasswdEntries
+}
+
+// GetGroup takes ac ontainermount path and a group name or id and returns
+// a match Group struct from /etc/group. if it cannot locate a group,
+// an ErrNoGroupEntries error is returned.
+func GetGroup(containerMount, groupIDorName string) (*user.Group, error) {
+ var inputIsName bool
+ gid, err := strconv.Atoi(groupIDorName)
+ if err != nil {
+ inputIsName = true
+ }
+
+ groupDest, err := securejoin.SecureJoin(containerMount, etcgroup)
+ if err != nil {
+ return nil, err
+ }
+
+ groups, err := user.ParseGroupFileFilter(groupDest, func(g user.Group) bool {
+ if inputIsName {
+ return g.Name == groupIDorName
+ }
+ return g.Gid == gid
+ })
+ if err != nil {
+ return nil, err
+ }
+ if len(groups) > 0 {
+ return &groups[0], nil
+ }
+ return nil, user.ErrNoGroupEntries
+}
diff --git a/pkg/spec/config_linux.go b/pkg/spec/config_linux.go
index 20cdcc458..5bf8eff43 100644
--- a/pkg/spec/config_linux.go
+++ b/pkg/spec/config_linux.go
@@ -28,7 +28,7 @@ func Device(d *configs.Device) spec.LinuxDevice {
}
func addDevice(g *generate.Generator, device string) error {
- src, dst, permissions, err := parseDevice(device)
+ src, dst, permissions, err := ParseDevice(device)
if err != nil {
return err
}
diff --git a/pkg/spec/parse.go b/pkg/spec/parse.go
index 9b2dd1347..4d20e35d4 100644
--- a/pkg/spec/parse.go
+++ b/pkg/spec/parse.go
@@ -148,21 +148,21 @@ func getLoggingPath(opts []string) string {
return ""
}
-// parseDevice parses device mapping string to a src, dest & permissions string
-func parseDevice(device string) (string, string, string, error) { //nolint
+// ParseDevice parses device mapping string to a src, dest & permissions string
+func ParseDevice(device string) (string, string, string, error) { //nolint
src := ""
dst := ""
permissions := "rwm"
arr := strings.Split(device, ":")
switch len(arr) {
case 3:
- if !validDeviceMode(arr[2]) {
+ if !IsValidDeviceMode(arr[2]) {
return "", "", "", fmt.Errorf("invalid device mode: %s", arr[2])
}
permissions = arr[2]
fallthrough
case 2:
- if validDeviceMode(arr[1]) {
+ if IsValidDeviceMode(arr[1]) {
permissions = arr[1]
} else {
if arr[1][0] != '/' {
@@ -183,9 +183,9 @@ func parseDevice(device string) (string, string, string, error) { //nolint
return src, dst, permissions, nil
}
-// validDeviceMode checks if the mode for device is valid or not.
-// Valid mode is a composition of r (read), w (write), and m (mknod).
-func validDeviceMode(mode string) bool {
+// IsValidDeviceMode checks if the mode for device is valid or not.
+// IsValid mode is a composition of r (read), w (write), and m (mknod).
+func IsValidDeviceMode(mode string) bool {
var legalDeviceMode = map[rune]bool{
'r': true,
'w': true,
diff --git a/test/e2e/run_selinux_test.go b/test/e2e/run_selinux_test.go
index ebe6604cc..a1a18c780 100644
--- a/test/e2e/run_selinux_test.go
+++ b/test/e2e/run_selinux_test.go
@@ -84,4 +84,68 @@ var _ = Describe("Podman run", func() {
Expect(match).Should(BeTrue())
})
+ It("podman test selinux label resolv.conf", func() {
+ session := podmanTest.Podman([]string{"run", fedoraMinimal, "ls", "-Z", "/etc/resolv.conf"})
+ session.WaitWithDefaultTimeout()
+ Expect(session.ExitCode()).To(Equal(0))
+ match, _ := session.GrepString("container_file_t")
+ Expect(match).Should(BeTrue())
+ })
+
+ It("podman test selinux label hosts", func() {
+ session := podmanTest.Podman([]string{"run", fedoraMinimal, "ls", "-Z", "/etc/hosts"})
+ session.WaitWithDefaultTimeout()
+ Expect(session.ExitCode()).To(Equal(0))
+ match, _ := session.GrepString("container_file_t")
+ Expect(match).Should(BeTrue())
+ })
+
+ It("podman test selinux label hostname", func() {
+ session := podmanTest.Podman([]string{"run", fedoraMinimal, "ls", "-Z", "/etc/hostname"})
+ session.WaitWithDefaultTimeout()
+ Expect(session.ExitCode()).To(Equal(0))
+ match, _ := session.GrepString("container_file_t")
+ Expect(match).Should(BeTrue())
+ })
+
+ It("podman test selinux label /run/secrets", func() {
+ session := podmanTest.Podman([]string{"run", fedoraMinimal, "ls", "-dZ", "/run/secrets"})
+ session.WaitWithDefaultTimeout()
+ Expect(session.ExitCode()).To(Equal(0))
+ match, _ := session.GrepString("container_file_t")
+ Expect(match).Should(BeTrue())
+ })
+
+ It("podman test selinux --privileged label resolv.conf", func() {
+ session := podmanTest.Podman([]string{"run", "--privileged", fedoraMinimal, "ls", "-Z", "/etc/resolv.conf"})
+ session.WaitWithDefaultTimeout()
+ Expect(session.ExitCode()).To(Equal(0))
+ match, _ := session.GrepString("container_file_t")
+ Expect(match).Should(BeTrue())
+ })
+
+ It("podman test selinux --privileged label hosts", func() {
+ session := podmanTest.Podman([]string{"run", "--privileged", fedoraMinimal, "ls", "-Z", "/etc/hosts"})
+ session.WaitWithDefaultTimeout()
+ Expect(session.ExitCode()).To(Equal(0))
+ match, _ := session.GrepString("container_file_t")
+ Expect(match).Should(BeTrue())
+ })
+
+ It("podman test selinux --privileged label hostname", func() {
+ session := podmanTest.Podman([]string{"run", "--privileged", fedoraMinimal, "ls", "-Z", "/etc/hostname"})
+ session.WaitWithDefaultTimeout()
+ Expect(session.ExitCode()).To(Equal(0))
+ match, _ := session.GrepString("container_file_t")
+ Expect(match).Should(BeTrue())
+ })
+
+ It("podman test selinux --privileged label /run/secrets", func() {
+ session := podmanTest.Podman([]string{"run", "--privileged", fedoraMinimal, "ls", "-dZ", "/run/secrets"})
+ session.WaitWithDefaultTimeout()
+ Expect(session.ExitCode()).To(Equal(0))
+ match, _ := session.GrepString("container_file_t")
+ Expect(match).Should(BeTrue())
+ })
+
})
diff --git a/test/e2e/runlabel_test.go b/test/e2e/runlabel_test.go
new file mode 100644
index 000000000..8d10d3c24
--- /dev/null
+++ b/test/e2e/runlabel_test.go
@@ -0,0 +1,68 @@
+package integration
+
+import (
+ "fmt"
+ "os"
+
+ . "github.com/onsi/ginkgo"
+ . "github.com/onsi/gomega"
+)
+
+var PodmanDockerfile = `
+FROM alpine:latest
+LABEL RUN podman --version`
+
+var LsDockerfile = `
+FROM alpine:latest
+LABEL RUN ls -la`
+
+var _ = Describe("podman container runlabel", func() {
+ var (
+ tempdir string
+ err error
+ podmanTest PodmanTest
+ )
+
+ BeforeEach(func() {
+ tempdir, err = CreateTempDirInTempDir()
+ if err != nil {
+ os.Exit(1)
+ }
+ podmanTest = PodmanCreate(tempdir)
+ podmanTest.RestoreAllArtifacts()
+ })
+
+ AfterEach(func() {
+ podmanTest.Cleanup()
+ f := CurrentGinkgoTestDescription()
+ timedResult := fmt.Sprintf("Test: %s completed in %f seconds", f.TestText, f.Duration.Seconds())
+ GinkgoWriter.Write([]byte(timedResult))
+
+ })
+
+ It("podman container runlabel (podman --version)", func() {
+ image := "podman-runlabel-test:podman"
+ podmanTest.BuildImage(PodmanDockerfile, image, "false")
+
+ result := podmanTest.Podman([]string{"container", "runlabel", "RUN", image})
+ result.WaitWithDefaultTimeout()
+ Expect(result.ExitCode()).To(Equal(0))
+
+ result = podmanTest.Podman([]string{"rmi", image})
+ result.WaitWithDefaultTimeout()
+ Expect(result.ExitCode()).To(Equal(0))
+ })
+
+ It("podman container runlabel (ls -la)", func() {
+ image := "podman-runlabel-test:ls"
+ podmanTest.BuildImage(LsDockerfile, image, "false")
+
+ result := podmanTest.Podman([]string{"container", "runlabel", "RUN", image})
+ result.WaitWithDefaultTimeout()
+ Expect(result.ExitCode()).To(Equal(0))
+
+ result = podmanTest.Podman([]string{"rmi", image})
+ result.WaitWithDefaultTimeout()
+ Expect(result.ExitCode()).To(Equal(0))
+ })
+})
diff --git a/vendor.conf b/vendor.conf
index dfcdbbe80..85b784d9b 100644
--- a/vendor.conf
+++ b/vendor.conf
@@ -12,7 +12,7 @@ github.com/containerd/continuity master
github.com/containernetworking/cni v0.7.0-alpha1
github.com/containernetworking/plugins 1562a1e60ed101aacc5e08ed9dbeba8e9f3d4ec1
github.com/containers/image bd10b1b53b2976f215b3f2f848fb8e7cad779aeb
-github.com/containers/storage bd5818eda84012cf1db4dafbddd4b7509bb77142
+github.com/containers/storage 09abf3a26b8a3aa69e29fd7faeb260b98d675759
github.com/containers/psgo 5dde6da0bc8831b35243a847625bcf18183bd1ee
github.com/coreos/go-systemd v14
github.com/cri-o/ocicni 2d2983e40c242322a56c22a903785e7f83eb378c
diff --git a/vendor/github.com/checkpoint-restore/go-criu/test/main.go b/vendor/github.com/checkpoint-restore/go-criu/test/main.go
deleted file mode 100644
index 418ebb843..000000000
--- a/vendor/github.com/checkpoint-restore/go-criu/test/main.go
+++ /dev/null
@@ -1,133 +0,0 @@
-package main
-
-import (
- "fmt"
- "github.com/checkpoint-restore/go-criu"
- "github.com/checkpoint-restore/go-criu/rpc"
- "github.com/golang/protobuf/proto"
- "os"
- "strconv"
-)
-
-// TestNfy struct
-type TestNfy struct {
- criu.NoNotify
-}
-
-// PreDump test function
-func (c TestNfy) PreDump() error {
- fmt.Printf("TEST PRE DUMP\n")
- return nil
-}
-
-func doDump(c *criu.Criu, pidS string, imgDir string, pre bool, prevImg string) error {
- fmt.Printf("Dumping\n")
- pid, _ := strconv.Atoi(pidS)
- img, err := os.Open(imgDir)
- if err != nil {
- return fmt.Errorf("can't open image dir (%s)", err)
- }
- defer img.Close()
-
- opts := rpc.CriuOpts{
- Pid: proto.Int32(int32(pid)),
- ImagesDirFd: proto.Int32(int32(img.Fd())),
- LogLevel: proto.Int32(4),
- LogFile: proto.String("dump.log"),
- }
-
- if prevImg != "" {
- opts.ParentImg = proto.String(prevImg)
- opts.TrackMem = proto.Bool(true)
- }
-
- if pre {
- err = c.PreDump(opts, TestNfy{})
- } else {
- err = c.Dump(opts, TestNfy{})
- }
- if err != nil {
- return fmt.Errorf("dump fail (%s)", err)
- }
-
- return nil
-}
-
-// Usage: test $act $pid $images_dir
-func main() {
- c := criu.MakeCriu()
- // Read out CRIU version
- version, err := c.GetCriuVersion()
- if err != nil {
- fmt.Println(err)
- os.Exit(1)
- }
- fmt.Println("CRIU version", version)
- // Check if version at least 3.2
- result, err := c.IsCriuAtLeast(30200)
- if err != nil {
- fmt.Println(err)
- os.Exit(1)
- }
- if !result {
- fmt.Println("CRIU too old")
- os.Exit(1)
- }
- act := os.Args[1]
- switch act {
- case "dump":
- err := doDump(c, os.Args[2], os.Args[3], false, "")
- if err != nil {
- fmt.Print(err)
- os.Exit(1)
- }
- case "dump2":
- err := c.Prepare()
- if err != nil {
- fmt.Print(err)
- os.Exit(1)
- }
-
- err = doDump(c, os.Args[2], os.Args[3]+"/pre", true, "")
- if err != nil {
- fmt.Printf("pre-dump failed")
- fmt.Print(err)
- os.Exit(1)
- }
- err = doDump(c, os.Args[2], os.Args[3], false, "./pre")
- if err != nil {
- fmt.Printf("dump failed")
- fmt.Print(err)
- os.Exit(1)
- }
-
- c.Cleanup()
- case "restore":
- fmt.Printf("Restoring\n")
- img, err := os.Open(os.Args[2])
- if err != nil {
- fmt.Printf("can't open image dir")
- os.Exit(1)
- }
- defer img.Close()
-
- opts := rpc.CriuOpts{
- ImagesDirFd: proto.Int32(int32(img.Fd())),
- LogLevel: proto.Int32(4),
- LogFile: proto.String("restore.log"),
- }
-
- err = c.Restore(opts, nil)
- if err != nil {
- fmt.Printf("Error:")
- fmt.Print(err)
- fmt.Printf("\n")
- os.Exit(1)
- }
- default:
- fmt.Printf("unknown action\n")
- os.Exit(1)
- }
-
- fmt.Printf("Success\n")
-}
diff --git a/vendor/github.com/checkpoint-restore/go-criu/test/phaul-main.go b/vendor/github.com/checkpoint-restore/go-criu/test/phaul-main.go
deleted file mode 100644
index f1bec2c55..000000000
--- a/vendor/github.com/checkpoint-restore/go-criu/test/phaul-main.go
+++ /dev/null
@@ -1,192 +0,0 @@
-package main
-
-import (
- "fmt"
- "os"
- "strconv"
- "strings"
- "syscall"
-
- "github.com/checkpoint-restore/go-criu"
- "github.com/checkpoint-restore/go-criu/phaul"
- "github.com/checkpoint-restore/go-criu/rpc"
- "github.com/golang/protobuf/proto"
-)
-
-type testLocal struct {
- criu.NoNotify
- r *testRemote
-}
-
-type testRemote struct {
- srv *phaul.Server
-}
-
-/* Dir where test will put dump images */
-const imagesDir = "image"
-
-func prepareImages() error {
- err := os.Mkdir(imagesDir, 0700)
- if err != nil {
- return err
- }
-
- /* Work dir for PhaulClient */
- err = os.Mkdir(imagesDir+"/local", 0700)
- if err != nil {
- return err
- }
-
- /* Work dir for PhaulServer */
- err = os.Mkdir(imagesDir+"/remote", 0700)
- if err != nil {
- return err
- }
-
- /* Work dir for DumpCopyRestore */
- err = os.Mkdir(imagesDir+"/test", 0700)
- if err != nil {
- return err
- }
-
- return nil
-}
-
-func mergeImages(dumpDir, lastPreDumpDir string) error {
- idir, err := os.Open(dumpDir)
- if err != nil {
- return err
- }
-
- defer idir.Close()
-
- imgs, err := idir.Readdirnames(0)
- if err != nil {
- return err
- }
-
- for _, fname := range imgs {
- if !strings.HasSuffix(fname, ".img") {
- continue
- }
-
- fmt.Printf("\t%s -> %s/\n", fname, lastPreDumpDir)
- err = syscall.Link(dumpDir+"/"+fname, lastPreDumpDir+"/"+fname)
- if err != nil {
- return err
- }
- }
-
- return nil
-}
-
-func (r *testRemote) doRestore() error {
- lastSrvImagesDir := r.srv.LastImagesDir()
- /*
- * In imagesDir we have images from dump, in the
- * lastSrvImagesDir -- where server-side images
- * (from page server, with pages and pagemaps) are.
- * Need to put former into latter and restore from
- * them.
- */
- err := mergeImages(imagesDir+"/test", lastSrvImagesDir)
- if err != nil {
- return err
- }
-
- imgDir, err := os.Open(lastSrvImagesDir)
- if err != nil {
- return err
- }
- defer imgDir.Close()
-
- opts := rpc.CriuOpts{
- LogLevel: proto.Int32(4),
- LogFile: proto.String("restore.log"),
- ImagesDirFd: proto.Int32(int32(imgDir.Fd())),
- }
-
- cr := r.srv.GetCriu()
- fmt.Printf("Do restore\n")
- return cr.Restore(opts, nil)
-}
-
-func (l *testLocal) PostDump() error {
- return l.r.doRestore()
-}
-
-func (l *testLocal) DumpCopyRestore(cr *criu.Criu, cfg phaul.Config, lastClnImagesDir string) error {
- fmt.Printf("Final stage\n")
-
- imgDir, err := os.Open(imagesDir + "/test")
- if err != nil {
- return err
- }
- defer imgDir.Close()
-
- psi := rpc.CriuPageServerInfo{
- Fd: proto.Int32(int32(cfg.Memfd)),
- }
-
- opts := rpc.CriuOpts{
- Pid: proto.Int32(int32(cfg.Pid)),
- LogLevel: proto.Int32(4),
- LogFile: proto.String("dump.log"),
- ImagesDirFd: proto.Int32(int32(imgDir.Fd())),
- TrackMem: proto.Bool(true),
- ParentImg: proto.String(lastClnImagesDir),
- Ps: &psi,
- }
-
- fmt.Printf("Do dump\n")
- return cr.Dump(opts, l)
-}
-
-func main() {
- pid, _ := strconv.Atoi(os.Args[1])
- fds, err := syscall.Socketpair(syscall.AF_LOCAL, syscall.SOCK_STREAM, 0)
- if err != nil {
- fmt.Printf("Can't make socketpair: %v\n", err)
- os.Exit(1)
- }
-
- err = prepareImages()
- if err != nil {
- fmt.Printf("Can't prepare dirs for images: %v\n", err)
- os.Exit(1)
- return
- }
-
- fmt.Printf("Make server part (socket %d)\n", fds[1])
- srv, err := phaul.MakePhaulServer(phaul.Config{
- Pid: pid,
- Memfd: fds[1],
- Wdir: imagesDir + "/remote"})
- if err != nil {
- fmt.Printf("Unable to run a server: %v", err)
- os.Exit(1)
- return
- }
-
- r := &testRemote{srv}
-
- fmt.Printf("Make client part (socket %d)\n", fds[0])
- cln, err := phaul.MakePhaulClient(&testLocal{r: r}, srv,
- phaul.Config{
- Pid: pid,
- Memfd: fds[0],
- Wdir: imagesDir + "/local"})
- if err != nil {
- fmt.Printf("Unable to run a client: %v\n", err)
- os.Exit(1)
- }
-
- fmt.Printf("Migrate\n")
- err = cln.Migrate()
- if err != nil {
- fmt.Printf("Failed: %v\n", err)
- os.Exit(1)
- }
-
- fmt.Printf("SUCCESS!\n")
-}
diff --git a/vendor/github.com/checkpoint-restore/go-criu/test/piggie.c b/vendor/github.com/checkpoint-restore/go-criu/test/piggie.c
deleted file mode 100644
index 1dc0801c0..000000000
--- a/vendor/github.com/checkpoint-restore/go-criu/test/piggie.c
+++ /dev/null
@@ -1,57 +0,0 @@
-#define _GNU_SOURCE
-#include <stdio.h>
-#include <signal.h>
-#include <unistd.h>
-#include <sys/mman.h>
-#include <fcntl.h>
-#include <sched.h>
-
-#define STKS (4*4096)
-
-#ifndef CLONE_NEWPID
-#define CLONE_NEWPID 0x20000000
-#endif
-
-static int do_test(void *logf)
-{
- int fd, i = 0;
-
- setsid();
-
- close(0);
- close(1);
- close(2);
-
- fd = open("/dev/null", O_RDONLY);
- if (fd != 0) {
- dup2(fd, 0);
- close(fd);
- }
-
- fd = open(logf, O_WRONLY | O_TRUNC | O_CREAT, 0600);
- dup2(fd, 1);
- dup2(fd, 2);
- if (fd != 1 && fd != 2)
- close(fd);
-
- while (1) {
- sleep(1);
- printf("%d\n", i++);
- fflush(stdout);
- }
-
- return 0;
-}
-
-int main(int argc, char **argv)
-{
- int pid;
- void *stk;
-
- stk = mmap(NULL, STKS, PROT_READ | PROT_WRITE,
- MAP_PRIVATE | MAP_ANON | MAP_GROWSDOWN, 0, 0);
- pid = clone(do_test, stk + STKS, SIGCHLD | CLONE_NEWPID, argv[1]);
- printf("Child forked, pid %d\n", pid);
-
- return 0;
-}
diff --git a/vendor/github.com/containers/storage/drivers/overlay/check.go b/vendor/github.com/containers/storage/drivers/overlay/check.go
index 2a096edf6..590d517fa 100644
--- a/vendor/github.com/containers/storage/drivers/overlay/check.go
+++ b/vendor/github.com/containers/storage/drivers/overlay/check.go
@@ -20,7 +20,7 @@ import (
// which copies up the opaque flag when copying up an opaque
// directory or the kernel enable CONFIG_OVERLAY_FS_REDIRECT_DIR.
// When these exist naive diff should be used.
-func doesSupportNativeDiff(d string) error {
+func doesSupportNativeDiff(d, mountOpts string) error {
td, err := ioutil.TempDir(d, "opaque-bug-check")
if err != nil {
return err
@@ -57,6 +57,9 @@ func doesSupportNativeDiff(d string) error {
}
opts := fmt.Sprintf("lowerdir=%s:%s,upperdir=%s,workdir=%s", path.Join(td, "l2"), path.Join(td, "l1"), path.Join(td, "l3"), path.Join(td, "work"))
+ if mountOpts != "" {
+ opts = fmt.Sprintf("%s,%s", opts, mountOpts)
+ }
if err := unix.Mount("overlay", filepath.Join(td, "merged"), "overlay", 0, opts); err != nil {
return errors.Wrap(err, "failed to mount overlay")
}
diff --git a/vendor/github.com/containers/storage/drivers/overlay/overlay.go b/vendor/github.com/containers/storage/drivers/overlay/overlay.go
index 2e0498f51..d2cc65bca 100644
--- a/vendor/github.com/containers/storage/drivers/overlay/overlay.go
+++ b/vendor/github.com/containers/storage/drivers/overlay/overlay.go
@@ -206,7 +206,7 @@ func Init(home string, options []string, uidMaps, gidMaps []idtools.IDMap) (grap
return nil, fmt.Errorf("Storage option overlay.size only supported for backingFS XFS. Found %v", backingFs)
}
- logrus.Debugf("backingFs=%s, projectQuotaSupported=%v, useNativeDiff=%v", backingFs, projectQuotaSupported, !useNaiveDiff(home))
+ logrus.Debugf("backingFs=%s, projectQuotaSupported=%v, useNativeDiff=%v", backingFs, projectQuotaSupported, !d.useNaiveDiff())
return d, nil
}
@@ -338,9 +338,9 @@ func supportsOverlay(home string, homeMagic graphdriver.FsMagic, rootUID, rootGI
return supportsDType, errors.Wrap(graphdriver.ErrNotSupported, "'overlay' not found as a supported filesystem on this host. Please ensure kernel is new enough and has overlay support loaded.")
}
-func useNaiveDiff(home string) bool {
+func (d *Driver) useNaiveDiff() bool {
useNaiveDiffLock.Do(func() {
- if err := doesSupportNativeDiff(home); err != nil {
+ if err := doesSupportNativeDiff(d.home, d.options.mountOptions); err != nil {
logrus.Warnf("Not using native diff for overlay, this may cause degraded performance for building images: %v", err)
useNaiveDiffOnly = true
}
@@ -358,7 +358,7 @@ func (d *Driver) Status() [][2]string {
return [][2]string{
{"Backing Filesystem", backingFs},
{"Supports d_type", strconv.FormatBool(d.supportsDType)},
- {"Native Overlay Diff", strconv.FormatBool(!useNaiveDiff(d.home))},
+ {"Native Overlay Diff", strconv.FormatBool(!d.useNaiveDiff())},
}
}
@@ -883,7 +883,7 @@ func (d *Driver) getDiffPath(id string) string {
// and its parent and returns the size in bytes of the changes
// relative to its base filesystem directory.
func (d *Driver) DiffSize(id string, idMappings *idtools.IDMappings, parent string, parentMappings *idtools.IDMappings, mountLabel string) (size int64, err error) {
- if useNaiveDiff(d.home) || !d.isParent(id, parent) {
+ if d.useNaiveDiff() || !d.isParent(id, parent) {
return d.naiveDiff.DiffSize(id, idMappings, parent, parentMappings, mountLabel)
}
return directory.Size(d.getDiffPath(id))
@@ -892,7 +892,7 @@ func (d *Driver) DiffSize(id string, idMappings *idtools.IDMappings, parent stri
// Diff produces an archive of the changes between the specified
// layer and its parent layer which may be "".
func (d *Driver) Diff(id string, idMappings *idtools.IDMappings, parent string, parentMappings *idtools.IDMappings, mountLabel string) (io.ReadCloser, error) {
- if useNaiveDiff(d.home) || !d.isParent(id, parent) {
+ if d.useNaiveDiff() || !d.isParent(id, parent) {
return d.naiveDiff.Diff(id, idMappings, parent, parentMappings, mountLabel)
}
@@ -919,7 +919,7 @@ func (d *Driver) Diff(id string, idMappings *idtools.IDMappings, parent string,
// Changes produces a list of changes between the specified layer
// and its parent layer. If parent is "", then all changes will be ADD changes.
func (d *Driver) Changes(id string, idMappings *idtools.IDMappings, parent string, parentMappings *idtools.IDMappings, mountLabel string) ([]archive.Change, error) {
- if useNaiveDiff(d.home) || !d.isParent(id, parent) {
+ if d.useNaiveDiff() || !d.isParent(id, parent) {
return d.naiveDiff.Changes(id, idMappings, parent, parentMappings, mountLabel)
}
// Overlay doesn't have snapshots, so we need to get changes from all parent