summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--cmd/podman/common.go4
-rw-r--r--cmd/podman/create.go15
-rw-r--r--completions/bash/podman1
-rw-r--r--docs/podman-create.1.md13
-rw-r--r--docs/podman-run.1.md13
-rw-r--r--libpod/container_internal.go4
-rw-r--r--libpod/image/image_test.go2
-rw-r--r--libpod/image/parts.go25
-rw-r--r--libpod/image/parts_test.go6
-rw-r--r--libpod/image/pull_test.go12
-rw-r--r--pkg/spec/createconfig.go7
-rw-r--r--pkg/spec/spec.go43
12 files changed, 124 insertions, 21 deletions
diff --git a/cmd/podman/common.go b/cmd/podman/common.go
index 93b16f4e3..43a0fe061 100644
--- a/cmd/podman/common.go
+++ b/cmd/podman/common.go
@@ -383,6 +383,10 @@ var createFlags = []cli.Flag{
Name: "sysctl",
Usage: "Sysctl options (default [])",
},
+ cli.BoolTFlag{
+ Name: "systemd",
+ Usage: "Run container in systemd mode if the command executable is systemd or init",
+ },
cli.StringSliceFlag{
Name: "tmpfs",
Usage: "Mount a temporary filesystem (`tmpfs`) into a container (default [])",
diff --git a/cmd/podman/create.go b/cmd/podman/create.go
index 209064eff..e7e349306 100644
--- a/cmd/podman/create.go
+++ b/cmd/podman/create.go
@@ -5,6 +5,7 @@ import (
"encoding/json"
"fmt"
"os"
+ "path/filepath"
"strconv"
"strings"
"syscall"
@@ -509,7 +510,7 @@ func parseCreateOpts(ctx context.Context, c *cli.Context, runtime *libpod.Runtim
// STOP SIGNAL
stopSignal := syscall.SIGTERM
- signalString := "SIGTERM"
+ signalString := ""
if data != nil {
signalString = data.ContainerConfig.StopSignal
}
@@ -648,6 +649,17 @@ func parseCreateOpts(ctx context.Context, c *cli.Context, runtime *libpod.Runtim
return nil, errors.Errorf("invalid image-volume type %q. Pick one of bind, tmpfs, or ignore", c.String("image-volume"))
}
+ var systemd bool
+ if c.BoolT("systemd") && ((filepath.Base(command[0]) == "init") || (filepath.Base(command[0]) == "systemd")) {
+ systemd = true
+ if signalString == "" {
+ stopSignal, err = signal.ParseSignal("RTMIN+3")
+ if err != nil {
+ return nil, errors.Wrapf(err, "error parsing systemd signal")
+ }
+ }
+ }
+
config := &cc.CreateConfig{
Runtime: runtime,
Annotations: annotations,
@@ -726,6 +738,7 @@ func parseCreateOpts(ctx context.Context, c *cli.Context, runtime *libpod.Runtim
StopSignal: stopSignal,
StopTimeout: c.Uint("stop-timeout"),
Sysctl: sysctl,
+ Systemd: systemd,
Tmpfs: c.StringSlice("tmpfs"),
Tty: tty,
User: user,
diff --git a/completions/bash/podman b/completions/bash/podman
index 083211fc1..91c08a78c 100644
--- a/completions/bash/podman
+++ b/completions/bash/podman
@@ -1494,6 +1494,7 @@ _podman_container_run() {
--subgidname
--subuidname
--sysctl
+ --systemd
--uidmap
--ulimit
--user -u
diff --git a/docs/podman-create.1.md b/docs/podman-create.1.md
index f27a84b1d..8523d0c78 100644
--- a/docs/podman-create.1.md
+++ b/docs/podman-create.1.md
@@ -544,6 +544,19 @@ Network Namespace - current sysctls allowed:
Note: if you use the --network=host option these sysctls will not be allowed.
+**--systemd**=*true*|*false*
+
+Run container in systemd mode. The default is *true*.
+
+If the command you running inside of the container is systemd or init, podman
+will setup tmpfs mount points in the following directories:
+
+/run, /run/lock, /tmp, /sys/fs/cgroup/systemd, /var/lib/journal
+
+It will also set the default stop signal to SIGRTMIN+3.
+
+This allow systemd to run in a confined container without any modifications.
+
**--tmpfs**=[] Create a tmpfs mount
Mount a temporary filesystem (`tmpfs`) mount into a container, for example:
diff --git a/docs/podman-run.1.md b/docs/podman-run.1.md
index 7b23f356f..59b9d4b01 100644
--- a/docs/podman-run.1.md
+++ b/docs/podman-run.1.md
@@ -578,6 +578,19 @@ Network Namespace - current sysctls allowed:
Note: if you use the `--network=host` option these sysctls will not be allowed.
+**--systemd**=*true*|*false*
+
+Run container in systemd mode. The default is *true*.
+
+If the command you running inside of the container is systemd or init, podman
+will setup tmpfs mount points in the following directories:
+
+/run, /run/lock, /tmp, /sys/fs/cgroup/systemd, /var/lib/journal
+
+It will also set the default stop signal to SIGRTMIN+3.
+
+This allow systemd to run in a confined container without any modifications.
+
**--tmpfs**=[] Create a tmpfs mount
Mount a temporary filesystem (`tmpfs`) mount into a container, for example:
diff --git a/libpod/container_internal.go b/libpod/container_internal.go
index 1c6143cd2..79bc49c37 100644
--- a/libpod/container_internal.go
+++ b/libpod/container_internal.go
@@ -1295,6 +1295,10 @@ func (c *Container) mount() (string, error) {
if err != nil {
return "", errors.Wrapf(err, "error mounting storage for container %s", c.ID())
}
+ mountPoint, err = filepath.EvalSymlinks(mountPoint)
+ if err != nil {
+ return "", errors.Wrapf(err, "error resolving storage path for container %s", c.ID())
+ }
return mountPoint, nil
}
diff --git a/libpod/image/image_test.go b/libpod/image/image_test.go
index 0aa637dab..f187631b4 100644
--- a/libpod/image/image_test.go
+++ b/libpod/image/image_test.go
@@ -221,7 +221,7 @@ func TestNormalizeTag(t *testing.T) {
{"example.com/busybox" + digestSuffix, "example.com/busybox" + digestSuffix + ":none"}, // Qualified name@digest; FIXME: The result is not even syntactically valid!
{"example.com/busybox:notlatest" + digestSuffix, "example.com/busybox:notlatest" + digestSuffix}, // Qualified name:tag@digest
{"busybox:latest", "localhost/busybox:latest"}, // Unqualified name-only
- {"ns/busybox:latest", "ns/busybox:latest"}, // Unqualified with a dot-less namespace FIXME: "ns" is treated as a registry
+ {"ns/busybox:latest", "localhost/ns/busybox:latest"}, // Unqualified with a dot-less namespace
} {
res, err := normalizeTag(c.input)
if c.expected == "" {
diff --git a/libpod/image/parts.go b/libpod/image/parts.go
index 127f723a8..1509005e5 100644
--- a/libpod/image/parts.go
+++ b/libpod/image/parts.go
@@ -2,6 +2,7 @@ package image
import (
"fmt"
+ "strings"
"github.com/containers/image/docker/reference"
)
@@ -16,6 +17,11 @@ type imageParts struct {
hasRegistry bool
}
+// Registries must contain a ":" or a "." or be localhost
+func isRegistry(name string) bool {
+ return strings.ContainsAny(name, ".:") || name == "localhost"
+}
+
// decompose breaks an input name into an imageParts description
func decompose(input string) (imageParts, error) {
var (
@@ -37,10 +43,16 @@ func decompose(input string) (imageParts, error) {
tag = ntag.Tag()
}
registry := reference.Domain(imgRef.(reference.Named))
- if registry != "" {
+ imageName := reference.Path(imgRef.(reference.Named))
+ // Is this a registry or a repo?
+ if isRegistry(registry) {
hasRegistry = true
+ } else {
+ if registry != "" {
+ imageName = registry + "/" + imageName
+ registry = ""
+ }
}
- imageName := reference.Path(imgRef.(reference.Named))
return imageParts{
registry: registry,
hasRegistry: hasRegistry,
@@ -53,10 +65,15 @@ func decompose(input string) (imageParts, error) {
// assemble concatenates an image's parts into a string
func (ip *imageParts) assemble() string {
- return fmt.Sprintf("%s/%s:%s", ip.registry, ip.name, ip.tag)
+ spec := fmt.Sprintf("%s:%s", ip.name, ip.tag)
+
+ if ip.registry != "" {
+ spec = fmt.Sprintf("%s/%s", ip.registry, spec)
+ }
+ return spec
}
// assemble concatenates an image's parts with transport into a string
func (ip *imageParts) assembleWithTransport() string {
- return fmt.Sprintf("%s%s/%s:%s", ip.transport, ip.registry, ip.name, ip.tag)
+ return fmt.Sprintf("%s%s", ip.transport, ip.assemble())
}
diff --git a/libpod/image/parts_test.go b/libpod/image/parts_test.go
index eba938cc2..518538f0b 100644
--- a/libpod/image/parts_test.go
+++ b/libpod/image/parts_test.go
@@ -27,12 +27,10 @@ func TestDecompose(t *testing.T) {
},
{ // Unqualified single-name input
"busybox", "docker://", "", "busybox", "latest", false, false,
- // FIXME? The [empty]/busybox syntax is surprising.
- "/busybox:latest", "docker:///busybox:latest",
+ "busybox:latest", "docker://busybox:latest",
},
{ // Unqualified namespaced input
- // FIXME: .registry == "ns" !!
- "ns/busybox", "docker://", "ns", "busybox", "latest", false, true,
+ "ns/busybox", "docker://", "", "ns/busybox", "latest", false, false,
"ns/busybox:latest", "docker://ns/busybox:latest",
},
{ // name:tag
diff --git a/libpod/image/pull_test.go b/libpod/image/pull_test.go
index 5ef8c47a5..37b45dc83 100644
--- a/libpod/image/pull_test.go
+++ b/libpod/image/pull_test.go
@@ -98,9 +98,8 @@ func TestGetPullRefPair(t *testing.T) {
"example.com/from-directory" + digestSuffix, "example.com/from-directory" + digestSuffix,
},
{ // ns/name:tag, no registry:
- // FIXME: This is interpreted as "registry == ns"
"dir:/dev/this-does-not-exist", "ns/from-directory:notlatest",
- "ns/from-directory:notlatest", "docker.io/ns/from-directory:notlatest",
+ "localhost/ns/from-directory:notlatest", "localhost/ns/from-directory:notlatest",
},
{ // containers-storage image ID
"dir:/dev/this-does-not-exist", imageID,
@@ -218,9 +217,8 @@ func TestPullGoalFromImageReference(t *testing.T) {
false,
},
{ // Relative path, multiple elements.
- // FIXME: This does not add localhost/, so dstName is normalized to docker.io/testdata.
"dir:testdata/this-does-not-exist",
- []expected{{"testdata/this-does-not-exist", "docker.io/testdata/this-does-not-exist:latest"}},
+ []expected{{"localhost/testdata/this-does-not-exist:latest", "localhost/testdata/this-does-not-exist:latest"}},
false,
},
@@ -343,12 +341,10 @@ func TestPullGoalFromPossiblyUnqualifiedName(t *testing.T) {
},
{ // Unqualified, namespaced, name-only
"ns/busybox",
- // FIXME: This is interpreted as "registry == ns", and actual pull happens from docker.io/ns/busybox:latest;
- // example.com should be first in the list but isn't used at all.
[]pullRefStrings{
- {"ns/busybox", "docker://ns/busybox:latest", "docker.io/ns/busybox:latest"},
+ {"example.com/ns/busybox:latest", "docker://example.com/ns/busybox:latest", "example.com/ns/busybox:latest"},
},
- false,
+ true,
},
{ // Unqualified, name:tag
"busybox:notlatest",
diff --git a/pkg/spec/createconfig.go b/pkg/spec/createconfig.go
index c7eaeb9f7..486281200 100644
--- a/pkg/spec/createconfig.go
+++ b/pkg/spec/createconfig.go
@@ -115,9 +115,10 @@ type CreateConfig struct {
Resources CreateResourceConfig
Rm bool //rm
ShmDir string
- StopSignal syscall.Signal // stop-signal
- StopTimeout uint // stop-timeout
- Sysctl map[string]string //sysctl
+ StopSignal syscall.Signal // stop-signal
+ StopTimeout uint // stop-timeout
+ Sysctl map[string]string //sysctl
+ Systemd bool
Tmpfs []string // tmpfs
Tty bool //tty
UsernsMode container.UsernsMode //userns
diff --git a/pkg/spec/spec.go b/pkg/spec/spec.go
index 26b93f5fe..11bc880cb 100644
--- a/pkg/spec/spec.go
+++ b/pkg/spec/spec.go
@@ -4,6 +4,7 @@ import (
"os"
"strings"
+ "github.com/containers/libpod/libpod"
"github.com/containers/libpod/pkg/rootless"
"github.com/docker/docker/daemon/caps"
"github.com/docker/docker/pkg/mount"
@@ -221,6 +222,12 @@ func CreateConfigToOCISpec(config *CreateConfig) (*spec.Spec, error) { //nolint
}
}
+ if config.Systemd && (strings.HasSuffix(config.Command[0], "init") ||
+ strings.HasSuffix(config.Command[0], "systemd")) {
+ if err := setupSystemd(config, &g); err != nil {
+ return nil, errors.Wrap(err, "failed to setup systemd")
+ }
+ }
for _, i := range config.Tmpfs {
// Default options if nothing passed
options := []string{"rw", "private", "noexec", "nosuid", "nodev", "size=65536k"}
@@ -353,6 +360,42 @@ func blockAccessToKernelFilesystems(config *CreateConfig, g *generate.Generator)
}
}
+// systemd expects to have /run, /run/lock and /tmp on tmpfs
+// It also expects to be able to write to /sys/fs/cgroup/systemd and /var/log/journal
+
+func setupSystemd(config *CreateConfig, g *generate.Generator) error {
+ mounts, err := config.GetVolumeMounts([]spec.Mount{})
+ if err != nil {
+ return err
+ }
+ options := []string{"rw", "private", "noexec", "nosuid", "nodev"}
+ for _, dest := range []string{"/run", "/run/lock", "/sys/fs/cgroup/systemd"} {
+ if libpod.MountExists(mounts, dest) {
+ continue
+ }
+ tmpfsMnt := spec.Mount{
+ Destination: dest,
+ Type: "tmpfs",
+ Source: "tmpfs",
+ Options: append(options, "tmpcopyup", "size=65536k"),
+ }
+ g.AddMount(tmpfsMnt)
+ }
+ for _, dest := range []string{"/tmp", "/var/log/journal"} {
+ if libpod.MountExists(mounts, dest) {
+ continue
+ }
+ tmpfsMnt := spec.Mount{
+ Destination: dest,
+ Type: "tmpfs",
+ Source: "tmpfs",
+ Options: append(options, "tmpcopyup"),
+ }
+ g.AddMount(tmpfsMnt)
+ }
+ return nil
+}
+
func addPidNS(config *CreateConfig, g *generate.Generator) error {
pidMode := config.PidMode
if IsNS(string(pidMode)) {