diff options
-rw-r--r-- | docs/podman-generate-systemd.1.md | 4 | ||||
-rw-r--r-- | libpod/container.go | 3 | ||||
-rw-r--r-- | libpod/container_internal.go | 10 | ||||
-rw-r--r-- | libpod/runtime_ctr.go | 11 | ||||
-rw-r--r-- | pkg/adapter/containers.go | 9 | ||||
-rw-r--r-- | pkg/systemdgen/systemdgen.go | 28 | ||||
-rw-r--r-- | pkg/systemdgen/systemdgen_test.go | 18 | ||||
-rw-r--r-- | test/e2e/checkpoint_test.go | 39 | ||||
-rw-r--r-- | test/e2e/push_test.go | 4 | ||||
-rw-r--r-- | test/e2e/rmi_test.go | 4 | ||||
-rw-r--r-- | test/e2e/run_signal_test.go | 8 | ||||
-rw-r--r-- | test/e2e/tree_test.go | 6 | ||||
-rw-r--r-- | test/system/250-generate-systemd.bats | 46 | ||||
-rw-r--r-- | test/system/helpers.bash | 11 |
14 files changed, 172 insertions, 29 deletions
diff --git a/docs/podman-generate-systemd.1.md b/docs/podman-generate-systemd.1.md index 09752480d..64e68a69a 100644 --- a/docs/podman-generate-systemd.1.md +++ b/docs/podman-generate-systemd.1.md @@ -39,7 +39,7 @@ ExecStart=/usr/bin/podman start c21da63c4783be2ac2cd3487ef8d2ec15ee2a28f63dd8f14 ExecStop=/usr/bin/podman stop -t 10 c21da63c4783be2ac2cd3487ef8d2ec15ee2a28f63dd8f145e3b05607f31cffc KillMode=none Type=forking -PIDFile=/var/lib/containers/storage/overlay-containers/c21da63c4783be2ac2cd3487ef8d2ec15ee2a28f63dd8f145e3b05607f31cffc/userdata/c21da63c4783be2ac2cd3487ef8d2ec15ee2a28f63dd8f145e3b05607f31cffc.pid +PIDFile=/var/run/containers/storage/overlay-containers/c21da63c4783be2ac2cd3487ef8d2ec15ee2a28f63dd8f145e3b05607f31cffc/userdata/conmon.pid [Install] WantedBy=multi-user.target ``` @@ -55,7 +55,7 @@ ExecStart=/usr/bin/podman start c21da63c4783be2ac2cd3487ef8d2ec15ee2a28f63dd8f14 ExecStop=/usr/bin/podman stop -t 1 c21da63c4783be2ac2cd3487ef8d2ec15ee2a28f63dd8f145e3b05607f31cffc KillMode=none Type=forking -PIDFile=/var/lib/containers/storage/overlay-containers/c21da63c4783be2ac2cd3487ef8d2ec15ee2a28f63dd8f145e3b05607f31cffc/userdata/c21da63c4783be2ac2cd3487ef8d2ec15ee2a28f63dd8f145e3b05607f31cffc.pid +PIDFile=/var/run/containers/storage/overlay-containers/c21da63c4783be2ac2cd3487ef8d2ec15ee2a28f63dd8f145e3b05607f31cffc/userdata/conmon.pid [Install] WantedBy=multi-user.target ``` diff --git a/libpod/container.go b/libpod/container.go index bfbc47d76..a9b512de9 100644 --- a/libpod/container.go +++ b/libpod/container.go @@ -138,6 +138,9 @@ type Container struct { // being checkpointed. If requestedIP is set it will be used instead // of config.StaticIP. requestedIP net.IP + + // This is true if a container is restored from a checkpoint. + restoreFromCheckpoint bool } // ContainerState contains the current state of the container diff --git a/libpod/container_internal.go b/libpod/container_internal.go index 1cac7b003..c409da96a 100644 --- a/libpod/container_internal.go +++ b/libpod/container_internal.go @@ -352,6 +352,16 @@ func (c *Container) setupStorage(ctx context.Context) error { }, LabelOpts: c.config.LabelOpts, } + if c.restoreFromCheckpoint { + // If restoring from a checkpoint, the root file-system + // needs to be mounted with the same SELinux labels as + // it was mounted previously. + if options.Flags == nil { + options.Flags = make(map[string]interface{}) + } + options.Flags["ProcessLabel"] = c.config.ProcessLabel + options.Flags["MountLabel"] = c.config.MountLabel + } if c.config.Privileged { privOpt := func(opt string) bool { for _, privopt := range []string{"nodev", "nosuid", "noexec"} { diff --git a/libpod/runtime_ctr.go b/libpod/runtime_ctr.go index d022478b1..ae9b3e5bc 100644 --- a/libpod/runtime_ctr.go +++ b/libpod/runtime_ctr.go @@ -52,7 +52,7 @@ func (r *Runtime) RestoreContainer(ctx context.Context, rSpec *spec.Spec, config if err != nil { return nil, errors.Wrapf(err, "error initializing container variables") } - return r.setupContainer(ctx, ctr, true) + return r.setupContainer(ctx, ctr) } func (r *Runtime) initContainerVariables(rSpec *spec.Spec, config *ContainerConfig) (c *Container, err error) { @@ -68,6 +68,7 @@ func (r *Runtime) initContainerVariables(rSpec *spec.Spec, config *ContainerConf ctr.config.ShmSize = DefaultShmSize } else { // This is a restore from an imported checkpoint + ctr.restoreFromCheckpoint = true if err := JSONDeepCopy(config, ctr.config); err != nil { return nil, errors.Wrapf(err, "error copying container config for restore") } @@ -119,10 +120,10 @@ func (r *Runtime) newContainer(ctx context.Context, rSpec *spec.Spec, options .. return nil, errors.Wrapf(err, "error running container create option") } } - return r.setupContainer(ctx, ctr, false) + return r.setupContainer(ctx, ctr) } -func (r *Runtime) setupContainer(ctx context.Context, ctr *Container, restore bool) (c *Container, err error) { +func (r *Runtime) setupContainer(ctx context.Context, ctr *Container) (c *Container, err error) { // Allocate a lock for the container lock, err := r.lockManager.AllocateLock() if err != nil { @@ -211,7 +212,7 @@ func (r *Runtime) setupContainer(ctx context.Context, ctr *Container, restore bo return nil, errors.Wrapf(config2.ErrInvalidArg, "unsupported CGroup manager: %s - cannot validate cgroup parent", r.config.CgroupManager) } - if restore { + if ctr.restoreFromCheckpoint { // Remove information about bind mount // for new container from imported checkpoint g := generate.Generator{Config: ctr.config.Spec} @@ -236,7 +237,7 @@ func (r *Runtime) setupContainer(ctx context.Context, ctr *Container, restore bo } }() - if rootless.IsRootless() && ctr.config.ConmonPidFile == "" { + if ctr.config.ConmonPidFile == "" { ctr.config.ConmonPidFile = filepath.Join(ctr.state.RunDir, "conmon.pid") } diff --git a/pkg/adapter/containers.go b/pkg/adapter/containers.go index 10720886b..0ea89a72c 100644 --- a/pkg/adapter/containers.go +++ b/pkg/adapter/containers.go @@ -1058,7 +1058,14 @@ func (r *LocalRuntime) GenerateSystemd(c *cliconfig.GenerateSystemdValues) (stri if c.Name { name = ctr.Name() } - return systemdgen.CreateSystemdUnitAsString(name, ctr.ID(), c.RestartPolicy, ctr.Config().StaticDir, timeout) + + config := ctr.Config() + conmonPidFile := config.ConmonPidFile + if conmonPidFile == "" { + return "", errors.Errorf("conmon PID file path is empty, try to recreate the container with --conmon-pidfile flag") + } + + return systemdgen.CreateSystemdUnitAsString(name, ctr.ID(), c.RestartPolicy, conmonPidFile, timeout) } // GetNamespaces returns namespace information about a container for PS diff --git a/pkg/systemdgen/systemdgen.go b/pkg/systemdgen/systemdgen.go index 3d1c31b5d..06c5ebde5 100644 --- a/pkg/systemdgen/systemdgen.go +++ b/pkg/systemdgen/systemdgen.go @@ -2,17 +2,18 @@ package systemdgen import ( "fmt" - "path/filepath" + "os" "github.com/pkg/errors" + "github.com/sirupsen/logrus" ) var template = `[Unit] Description=%s Podman Container [Service] Restart=%s -ExecStart=/usr/bin/podman start %s -ExecStop=/usr/bin/podman stop -t %d %s +ExecStart=%s start %s +ExecStop=%s stop -t %d %s KillMode=none Type=forking PIDFile=%s @@ -33,11 +34,26 @@ func ValidateRestartPolicy(restart string) error { // CreateSystemdUnitAsString takes variables to create a systemd unit file used to control // a libpod container -func CreateSystemdUnitAsString(name, cid, restart, pidPath string, stopTimeout int) (string, error) { +func CreateSystemdUnitAsString(name, cid, restart, pidFile string, stopTimeout int) (string, error) { + podmanExe := getPodmanExecutable() + return createSystemdUnitAsString(podmanExe, name, cid, restart, pidFile, stopTimeout) +} + +func createSystemdUnitAsString(exe, name, cid, restart, pidFile string, stopTimeout int) (string, error) { if err := ValidateRestartPolicy(restart); err != nil { return "", err } - pidFile := filepath.Join(pidPath, fmt.Sprintf("%s.pid", cid)) - unit := fmt.Sprintf(template, name, restart, name, stopTimeout, name, pidFile) + + unit := fmt.Sprintf(template, name, restart, exe, name, exe, stopTimeout, name, pidFile) return unit, nil } + +func getPodmanExecutable() string { + podmanExe, err := os.Executable() + if err != nil { + podmanExe = "/usr/bin/podman" + logrus.Warnf("Could not obtain podman executable location, using default %s", podmanExe) + } + + return podmanExe +} diff --git a/pkg/systemdgen/systemdgen_test.go b/pkg/systemdgen/systemdgen_test.go index f2f49e750..e413b24ce 100644 --- a/pkg/systemdgen/systemdgen_test.go +++ b/pkg/systemdgen/systemdgen_test.go @@ -41,7 +41,7 @@ ExecStart=/usr/bin/podman start 639c53578af4d84b8800b4635fa4e680ee80fd67e0e6a2d4 ExecStop=/usr/bin/podman stop -t 10 639c53578af4d84b8800b4635fa4e680ee80fd67e0e6a2d4eea48d1e3230f401 KillMode=none Type=forking -PIDFile=/var/lib/containers/storage/overlay-containers/639c53578af4d84b8800b4635fa4e680ee80fd67e0e6a2d4eea48d1e3230f401/userdata/639c53578af4d84b8800b4635fa4e680ee80fd67e0e6a2d4eea48d1e3230f401.pid +PIDFile=/var/run/containers/storage/overlay-containers/639c53578af4d84b8800b4635fa4e680ee80fd67e0e6a2d4eea48d1e3230f401/userdata/conmon.pid [Install] WantedBy=multi-user.target` @@ -53,15 +53,16 @@ ExecStart=/usr/bin/podman start foobar ExecStop=/usr/bin/podman stop -t 10 foobar KillMode=none Type=forking -PIDFile=/var/lib/containers/storage/overlay-containers/639c53578af4d84b8800b4635fa4e680ee80fd67e0e6a2d4eea48d1e3230f401/userdata/639c53578af4d84b8800b4635fa4e680ee80fd67e0e6a2d4eea48d1e3230f401.pid +PIDFile=/var/run/containers/storage/overlay-containers/639c53578af4d84b8800b4635fa4e680ee80fd67e0e6a2d4eea48d1e3230f401/userdata/conmon.pid [Install] WantedBy=multi-user.target` type args struct { + exe string name string cid string restart string - pidPath string + pidFile string stopTimeout int } tests := []struct { @@ -73,10 +74,11 @@ WantedBy=multi-user.target` {"good with id", args{ + "/usr/bin/podman", "639c53578af4d84b8800b4635fa4e680ee80fd67e0e6a2d4eea48d1e3230f401", "639c53578af4d84b8800b4635fa4e680ee80fd67e0e6a2d4eea48d1e3230f401", "always", - "/var/lib/containers/storage/overlay-containers/639c53578af4d84b8800b4635fa4e680ee80fd67e0e6a2d4eea48d1e3230f401/userdata/", + "/var/run/containers/storage/overlay-containers/639c53578af4d84b8800b4635fa4e680ee80fd67e0e6a2d4eea48d1e3230f401/userdata/conmon.pid", 10, }, goodID, @@ -84,10 +86,11 @@ WantedBy=multi-user.target` }, {"good with name", args{ + "/usr/bin/podman", "foobar", "639c53578af4d84b8800b4635fa4e680ee80fd67e0e6a2d4eea48d1e3230f401", "always", - "/var/lib/containers/storage/overlay-containers/639c53578af4d84b8800b4635fa4e680ee80fd67e0e6a2d4eea48d1e3230f401/userdata/", + "/var/run/containers/storage/overlay-containers/639c53578af4d84b8800b4635fa4e680ee80fd67e0e6a2d4eea48d1e3230f401/userdata/conmon.pid", 10, }, goodName, @@ -95,10 +98,11 @@ WantedBy=multi-user.target` }, {"bad restart policy", args{ + "/usr/bin/podman", "639c53578af4d84b8800b4635fa4e680ee80fd67e0e6a2d4eea48d1e3230f401", "639c53578af4d84b8800b4635fa4e680ee80fd67e0e6a2d4eea48d1e3230f401", "never", - "/var/lib/containers/storage/overlay-containers/639c53578af4d84b8800b4635fa4e680ee80fd67e0e6a2d4eea48d1e3230f401/userdata/", + "/var/run/containers/storage/overlay-containers/639c53578af4d84b8800b4635fa4e680ee80fd67e0e6a2d4eea48d1e3230f401/userdata/conmon.pid", 10, }, "", @@ -107,7 +111,7 @@ WantedBy=multi-user.target` } for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { - got, err := CreateSystemdUnitAsString(tt.args.name, tt.args.cid, tt.args.restart, tt.args.pidPath, tt.args.stopTimeout) + got, err := createSystemdUnitAsString(tt.args.exe, tt.args.name, tt.args.cid, tt.args.restart, tt.args.pidFile, tt.args.stopTimeout) if (err != nil) != tt.wantErr { t.Errorf("CreateSystemdUnitAsString() error = %v, wantErr %v", err, tt.wantErr) return diff --git a/test/e2e/checkpoint_test.go b/test/e2e/checkpoint_test.go index d452a062b..c60a99386 100644 --- a/test/e2e/checkpoint_test.go +++ b/test/e2e/checkpoint_test.go @@ -392,4 +392,43 @@ var _ = Describe("Podman checkpoint", func() { // Remove exported checkpoint os.Remove("/tmp/checkpoint.tar.gz") }) + + It("podman checkpoint and run exec in restored container", func() { + // Start the container + session := podmanTest.Podman([]string{"run", "-it", "--rm", "-d", ALPINE, "top"}) + session.WaitWithDefaultTimeout() + Expect(session.ExitCode()).To(Equal(0)) + Expect(podmanTest.NumberOfContainersRunning()).To(Equal(1)) + cid := session.OutputToString() + + // Checkpoint the container + result := podmanTest.Podman([]string{"container", "checkpoint", "-l", "-e", "/tmp/checkpoint.tar.gz"}) + result.WaitWithDefaultTimeout() + + Expect(result.ExitCode()).To(Equal(0)) + Expect(podmanTest.NumberOfContainersRunning()).To(Equal(0)) + Expect(podmanTest.NumberOfContainers()).To(Equal(0)) + + // Restore the container + result = podmanTest.Podman([]string{"container", "restore", "-i", "/tmp/checkpoint.tar.gz"}) + result.WaitWithDefaultTimeout() + + Expect(result.ExitCode()).To(Equal(0)) + Expect(podmanTest.NumberOfContainersRunning()).To(Equal(1)) + Expect(podmanTest.NumberOfContainers()).To(Equal(1)) + Expect(podmanTest.GetContainerStatus()).To(ContainSubstring("Up")) + + // Exec in the container + result = podmanTest.Podman([]string{"exec", "-l", "/bin/sh", "-c", "echo " + cid + " > /test.output"}) + result.WaitWithDefaultTimeout() + Expect(result.ExitCode()).To(Equal(0)) + + result = podmanTest.Podman([]string{"exec", "-l", "cat", "/test.output"}) + result.WaitWithDefaultTimeout() + Expect(result.ExitCode()).To(Equal(0)) + Expect(result.OutputToString()).To(ContainSubstring(cid)) + + // Remove exported checkpoint + os.Remove("/tmp/checkpoint.tar.gz") + }) }) diff --git a/test/e2e/push_test.go b/test/e2e/push_test.go index de2416868..cf6279f2f 100644 --- a/test/e2e/push_test.go +++ b/test/e2e/push_test.go @@ -8,6 +8,7 @@ import ( "path/filepath" "strings" + "github.com/containers/libpod/pkg/rootless" . "github.com/containers/libpod/test/utils" . "github.com/onsi/ginkgo" . "github.com/onsi/gomega" @@ -59,6 +60,9 @@ var _ = Describe("Podman push", func() { if podmanTest.Host.Arch == "ppc64le" { Skip("No registry image for ppc64le") } + if rootless.IsRootless() { + podmanTest.RestoreArtifact(registry) + } lock := GetPortLock("5000") defer lock.Unlock() session := podmanTest.PodmanNoCache([]string{"run", "-d", "--name", "registry", "-p", "5000:5000", registry, "/entrypoint.sh", "/etc/docker/registry/config.yml"}) diff --git a/test/e2e/rmi_test.go b/test/e2e/rmi_test.go index 1687bf764..1b0329a83 100644 --- a/test/e2e/rmi_test.go +++ b/test/e2e/rmi_test.go @@ -55,7 +55,7 @@ var _ = Describe("Podman rmi", func() { }) It("podman rmi all images", func() { - podmanTest.PullImages([]string{nginx}) + podmanTest.RestoreArtifact(nginx) session := podmanTest.PodmanNoCache([]string{"rmi", "-a"}) session.WaitWithDefaultTimeout() images := podmanTest.PodmanNoCache([]string{"images"}) @@ -66,7 +66,7 @@ var _ = Describe("Podman rmi", func() { }) It("podman rmi all images forcibly with short options", func() { - podmanTest.PullImages([]string{nginx}) + podmanTest.RestoreArtifact(nginx) session := podmanTest.PodmanNoCache([]string{"rmi", "-fa"}) session.WaitWithDefaultTimeout() Expect(session.ExitCode()).To(Equal(0)) diff --git a/test/e2e/run_signal_test.go b/test/e2e/run_signal_test.go index 3a5ed483c..1dbac1dc9 100644 --- a/test/e2e/run_signal_test.go +++ b/test/e2e/run_signal_test.go @@ -11,6 +11,7 @@ import ( "syscall" "time" + "github.com/containers/libpod/pkg/rootless" . "github.com/containers/libpod/test/utils" . "github.com/onsi/ginkgo" . "github.com/onsi/gomega" @@ -53,7 +54,9 @@ var _ = Describe("Podman run with --sig-proxy", func() { os.Mkdir(udsDir, 0700) udsPath := filepath.Join(udsDir, "fifo") syscall.Mkfifo(udsPath, 0600) - + if rootless.IsRootless() { + podmanTest.RestoreArtifact(fedoraMinimal) + } _, pid := podmanTest.PodmanPID([]string{"run", "-it", "-v", fmt.Sprintf("%s:/h:Z", udsDir), fedoraMinimal, "bash", "-c", sigCatch}) uds, _ := os.OpenFile(udsPath, os.O_RDONLY|syscall.O_NONBLOCK, 0600) @@ -108,6 +111,9 @@ var _ = Describe("Podman run with --sig-proxy", func() { Specify("signals are not forwarded to container with sig-proxy false", func() { signal := syscall.SIGPOLL + if rootless.IsRootless() { + podmanTest.RestoreArtifact(fedoraMinimal) + } session, pid := podmanTest.PodmanPID([]string{"run", "--name", "test2", "--sig-proxy=false", fedoraMinimal, "bash", "-c", sigCatch}) ok := WaitForContainer(podmanTest) diff --git a/test/e2e/tree_test.go b/test/e2e/tree_test.go index 2db7aeb5e..c445328fa 100644 --- a/test/e2e/tree_test.go +++ b/test/e2e/tree_test.go @@ -37,10 +37,6 @@ var _ = Describe("Podman image tree", func() { if podmanTest.RemoteTest { Skip("Does not work on remote client") } - session := podmanTest.PodmanNoCache([]string{"pull", "docker.io/library/busybox:latest"}) - session.WaitWithDefaultTimeout() - Expect(session.ExitCode()).To(Equal(0)) - dockerfile := `FROM docker.io/library/busybox:latest RUN mkdir hello RUN touch test.txt @@ -48,7 +44,7 @@ ENV foo=bar ` podmanTest.BuildImage(dockerfile, "test:latest", "true") - session = podmanTest.PodmanNoCache([]string{"image", "tree", "test:latest"}) + session := podmanTest.PodmanNoCache([]string{"image", "tree", "test:latest"}) session.WaitWithDefaultTimeout() Expect(session.ExitCode()).To(Equal(0)) session = podmanTest.PodmanNoCache([]string{"image", "tree", "--whatrequires", "docker.io/library/busybox:latest"}) diff --git a/test/system/250-generate-systemd.bats b/test/system/250-generate-systemd.bats new file mode 100644 index 000000000..80199af5f --- /dev/null +++ b/test/system/250-generate-systemd.bats @@ -0,0 +1,46 @@ +#!/usr/bin/env bats -*- bats -*- +# +# Tests generated configurations for systemd. +# + +load helpers + +# Be extra paranoid in naming to avoid collisions. +SERVICE_NAME="podman_test_$(random_string)" +UNIT_DIR="$HOME/.config/systemd/user" +UNIT_FILE="$UNIT_DIR/$SERVICE_NAME.service" + +function setup() { + skip_if_not_systemd + skip_if_remote + + basic_setup + + if [ ! -d "$UNIT_DIR" ]; then + mkdir -p "$UNIT_DIR" + systemctl --user daemon-reload + fi +} + +function teardown() { + rm -f "$UNIT_FILE" + systemctl --user stop "$SERVICE_NAME" + basic_teardown +} + +@test "podman generate - systemd - basic" { + run_podman create $IMAGE echo "I'm alive!" + cid="$output" + + run_podman generate systemd $cid > "$UNIT_FILE" + + run systemctl --user start "$SERVICE_NAME" + if [ $status -ne 0 ]; then + die "The systemd service $SERVICE_NAME did not start correctly, output: $output" + fi + + run_podman logs $cid + is "$output" "I'm alive!" "Container output" +} + +# vim: filetype=sh diff --git a/test/system/helpers.bash b/test/system/helpers.bash index 29ef19ecc..1db80f111 100644 --- a/test/system/helpers.bash +++ b/test/system/helpers.bash @@ -236,6 +236,17 @@ function skip_if_remote() { skip "${1:-test does not work with podman-remote}" } +######################### +# skip_if_not_systemd # ...with an optional message +######################### +function skip_if_not_systemd() { + if systemctl --user >/dev/null 2>&1; then + return + fi + + skip "${1:-no systemd or daemon does not respond}" +} + ######### # die # Abort with helpful message ######### |