Skip to content

Commit

Permalink
run: Add --uts flag
Browse files Browse the repository at this point in the history
Adds support for specifying the host UTS namespace (the only thing
docker supported). This also consolidates the namespace options
checks for linux on run into a single function.

Signed-off-by: Danny Canter <danny@dcantah.dev>
  • Loading branch information
dcantah committed Dec 27, 2022
1 parent b876456 commit b616ad0
Show file tree
Hide file tree
Showing 4 changed files with 93 additions and 25 deletions.
3 changes: 2 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -382,6 +382,7 @@ Basic flags:
- :whale: `--pull=(always|missing|never)`: Pull image before running
- Default: "missing"
- :whale: `--pid=(host|container:<container>)`: PID namespace to use
- :whale: `--uts=(host)` : UTS namespace to use
- :whale: `--stop-signal`: Signal to stop a container (default "SIGTERM")
- :whale: `--stop-timeout`: Timeout (in seconds) to stop a container

Expand Down Expand Up @@ -577,7 +578,7 @@ Unimplemented `docker run` flags:
`--attach`, `--blkio-weight-device`, `--cgroup-parent`, `--cpu-rt-*`, `--detach-keys`, `--device-*`,
`--disable-content-trust`, `--domainname`, `--expose`, `--health-*`, `--ip6`, `--isolation`, `--no-healthcheck`,
`--link*`, `--mac-address`, `--publish-all`, `--sig-proxy`, `--storage-opt`,
`--userns`, `--uts`, `--volume-driver`, `--volumes-from`
`--userns`, `--volume-driver`, `--volumes-from`

### :whale: :blue_square: nerdctl exec
Run a command in a running container.
Expand Down
29 changes: 21 additions & 8 deletions cmd/nerdctl/run.go
Original file line number Diff line number Diff line change
Expand Up @@ -160,6 +160,7 @@ func setCreateFlags(cmd *cobra.Command) {
cmd.Flags().Bool("oom-kill-disable", false, "Disable OOM Killer")
cmd.Flags().Int("oom-score-adj", 0, "Tune container’s OOM preferences (-1000 to 1000, rootless: 100 to 1000)")
cmd.Flags().String("pid", "", "PID namespace to use")
cmd.Flags().String("uts", "", "UTS namespace to use")
cmd.RegisterFlagCompletionFunc("pid", func(cmd *cobra.Command, args []string, toComplete string) ([]string, cobra.ShellCompDirective) {
return []string{"host"}, cobra.ShellCompDirectiveNoFileComp
})
Expand Down Expand Up @@ -566,18 +567,30 @@ func createContainer(ctx context.Context, cmd *cobra.Command, client *containerd
if err != nil {
return nil, nil, err
}

uts, err := cmd.Flags().GetString("uts")
if err != nil {
return nil, nil, err
}

if customHostname != "" {
// Docker considers this a validation error so keep compat.
if uts != "" {
return nil, nil, errors.New("conflicting options: hostname and UTS mode")
}
hostname = customHostname
}
opts = append(opts, oci.WithHostname(hostname))
internalLabels.hostname = hostname
// `/etc/hostname` does not exist on FreeBSD
if runtime.GOOS == "linux" {
hostnamePath := filepath.Join(stateDir, "hostname")
if err := os.WriteFile(hostnamePath, []byte(hostname+"\n"), 0644); err != nil {
return nil, nil, err
if uts == "" {
opts = append(opts, oci.WithHostname(hostname))
internalLabels.hostname = hostname
// `/etc/hostname` does not exist on FreeBSD
if runtime.GOOS == "linux" {
hostnamePath := filepath.Join(stateDir, "hostname")
if err := os.WriteFile(hostnamePath, []byte(hostname+"\n"), 0644); err != nil {
return nil, nil, err
}
opts = append(opts, withCustomEtcHostname(hostnamePath))
}
opts = append(opts, withCustomEtcHostname(hostnamePath))
}

netOpts, netSlice, ipAddress, ports, macAddress, err := generateNetOpts(cmd, dataStore, stateDir, ns, id)
Expand Down
71 changes: 55 additions & 16 deletions cmd/nerdctl/run_linux.go
Original file line number Diff line number Diff line change
Expand Up @@ -136,17 +136,6 @@ func setPlatformOptions(
opts = append(opts, oci.WithDevShmSize(shmBytes/1024))
}

pid, err := cmd.Flags().GetString("pid")
if err != nil {
return nil, err
}
pidOpts, pidInternalLabel, err := generatePIDOpts(ctx, client, pid)
if err != nil {
return nil, err
}
internalLabels.pidContainer = pidInternalLabel
opts = append(opts, pidOpts...)

ulimitOpts, err := generateUlimitsOpts(cmd)
if err != nil {
return nil, err
Expand Down Expand Up @@ -175,22 +164,72 @@ func setPlatformOptions(
opts = append(opts, oci.WithRdt(rdtClass, "", ""))
}

nsOpts, err := generateNamespaceOpts(ctx, cmd, client, internalLabels)
if err != nil {
return nil, err
}
opts = append(opts, nsOpts...)

opts, err = setOOMScoreAdj(opts, cmd)
if err != nil {
return nil, err
}

return opts, nil
}

// Helper to validate the namespace options exposed via run and return the correct
// opts.
func generateNamespaceOpts(
ctx context.Context,
cmd *cobra.Command,
client *containerd.Client,
internalLabels *internalLabels,
) ([]oci.SpecOpts, error) {
var opts []oci.SpecOpts

// UTS
uts, err := cmd.Flags().GetString("uts")
if err != nil {
return nil, err
}

switch uts {
case "host":
opts = append(opts, oci.WithHostNamespace(specs.UTSNamespace))
case "":
// Default, do nothing. Every container gets its own UTS ns by default.
default:
return nil, fmt.Errorf("unknown uts value. valid value(s) are 'host', got: %q", uts)
}

// IPC
ipc, err := cmd.Flags().GetString("ipc")
if err != nil {
return nil, err
}
// if nothing is specified, or if private, default to normal behavior
if ipc == "host" {

switch ipc {
case "host":
opts = append(opts, oci.WithHostNamespace(specs.IPCNamespace))
opts = append(opts, withBindMountHostIPC)
} else if ipc != "" && ipc != "private" {
return nil, fmt.Errorf("error: %v", "invalid ipc value, supported values are 'private' or 'host'")
case "private", "":
// If nothing is specified, or if private, default to normal behavior
default:
return nil, fmt.Errorf("unknown ipc value. valid values are 'private' or 'host', got: %q", ipc)
}

opts, err = setOOMScoreAdj(opts, cmd)
// PID
pid, err := cmd.Flags().GetString("pid")
if err != nil {
return nil, err
}
pidOpts, pidLabel, err := generatePIDOpts(ctx, client, pid)
if err != nil {
return nil, err
}
internalLabels.pidContainer = pidLabel
opts = append(opts, pidOpts...)

return opts, nil
}
Expand Down
15 changes: 15 additions & 0 deletions cmd/nerdctl/run_linux_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -75,6 +75,21 @@ func TestRunPidHost(t *testing.T) {
base.Cmd("run", "--rm", "--pid=host", testutil.AlpineImage, "ps", "auxw").AssertOutContains(strconv.Itoa(pid))
}

func TestRunUtsHost(t *testing.T) {
t.Parallel()
base := testutil.NewBase(t)

// Was thinking of os.ReadLink("/proc/1/ns/uts")
// but you'd get EPERM for rootless. Just validate the
// hostname is the same.
hostName, err := os.Hostname()
assert.NilError(base.T, err)

base.Cmd("run", "--rm", "--uts=host", testutil.AlpineImage, "hostname").AssertOutContains(hostName)
// Validate we can't provide a hostname with uts=host
base.Cmd("run", "--rm", "--uts=host", "--hostname=foobar", testutil.AlpineImage, "hostname").AssertFail()
}

func TestRunPidContainer(t *testing.T) {
t.Parallel()
base := testutil.NewBase(t)
Expand Down

0 comments on commit b616ad0

Please sign in to comment.