Skip to content

feat: add support for 'domainname' option in container create #3885

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 1 commit into from
Feb 18, 2025
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions cmd/nerdctl/container/container_run.go
Original file line number Diff line number Diff line change
Expand Up @@ -131,6 +131,7 @@ func setCreateFlags(cmd *cobra.Command) {
cmd.Flags().String("ip", "", "IPv4 address to assign to the container")
cmd.Flags().String("ip6", "", "IPv6 address to assign to the container")
cmd.Flags().StringP("hostname", "h", "", "Container host name")
cmd.Flags().String("domainname", "", "Container domain name")
cmd.Flags().String("mac-address", "", "MAC address to assign to the container")
// #endregion

Expand Down
2 changes: 2 additions & 0 deletions cmd/nerdctl/container/container_run_linux_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -166,6 +166,8 @@ func TestRunUtsHost(t *testing.T) {
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()
// Validate we can't provide a domainname with uts=host
base.Cmd("run", "--rm", "--uts=host", "--domainname=example.com", testutil.AlpineImage, "hostname").AssertFail()
}

func TestRunPidContainer(t *testing.T) {
Expand Down
7 changes: 7 additions & 0 deletions cmd/nerdctl/container/container_run_network.go
Original file line number Diff line number Diff line change
Expand Up @@ -93,6 +93,13 @@ func loadNetworkFlags(cmd *cobra.Command) (types.NetworkOptions, error) {
}
netOpts.Hostname = hostName

// --domainname=<container domainname>
domainname, err := cmd.Flags().GetString("domainname")
if err != nil {
return netOpts, err
}
netOpts.Domainname = domainname

// --dns=<DNS host> ...
dnsSlice, err := cmd.Flags().GetStringSlice("dns")
if err != nil {
Expand Down
51 changes: 51 additions & 0 deletions cmd/nerdctl/container/container_run_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -722,3 +722,54 @@ func TestRunFromOCIArchive(t *testing.T) {
base.Cmd("build", "--tag", tag, fmt.Sprintf("--output=type=oci,dest=%s", tarPath), buildCtx).AssertOK()
base.Cmd("run", "--rm", fmt.Sprintf("oci-archive://%s", tarPath)).AssertOutContainsAll(fmt.Sprintf("Loaded image: %s", tag), sentinel)
}

func TestRunDomainname(t *testing.T) {
t.Parallel()

if runtime.GOOS == "windows" {
t.Skip("run --hostname not implemented on Windows yet")
}

testCases := []struct {
name string
hostname string
domainname string
Cmd string
CmdFlag string
expectedOut string
}{
{
name: "Check domain name",
hostname: "foobar",
domainname: "example.com",
Cmd: "hostname",
CmdFlag: "-d",
expectedOut: "example.com",
},
{
name: "check fqdn",
hostname: "foobar",
domainname: "example.com",
Cmd: "hostname",
CmdFlag: "-f",
expectedOut: "foobar.example.com",
},
}

for _, tc := range testCases {
tc := tc // capture range variable
t.Run(tc.name, func(t *testing.T) {
t.Parallel()
base := testutil.NewBase(t)

base.Cmd("run",
"--rm",
"--hostname", tc.hostname,
"--domainname", tc.domainname,
testutil.CommonImage,
tc.Cmd,
tc.CmdFlag,
).AssertOutContains(tc.expectedOut)
})
}
}
3 changes: 2 additions & 1 deletion docs/command-reference.md
Original file line number Diff line number Diff line change
Expand Up @@ -186,6 +186,7 @@ Network flags:
- :whale: `--dns-search`: Set custom DNS search domains
- :whale: `--dns-opt, --dns-option`: Set DNS options
- :whale: `-h, --hostname`: Container host name
- :whale: `--domainname`: Container domain name
- :whale: `--add-host`: Add a custom host-to-IP mapping (host:ip). `ip` could be a special string `host-gateway`,
- which will be resolved to the `host-gateway-ip` in nerdctl.toml or global flag.
- :whale: `--ip`: Specific static IP address(es) to use. Note that unlike docker, nerdctl allows specifying it with the default bridge network.
Expand Down Expand Up @@ -413,7 +414,7 @@ IPFS flags:

Unimplemented `docker run` flags:
`--blkio-weight-device`, `--cpu-rt-*`, `--device-*`,
`--disable-content-trust`, `--domainname`, `--expose`, `--health-*`, `--isolation`, `--no-healthcheck`,
`--disable-content-trust`, `--expose`, `--health-*`, `--isolation`, `--no-healthcheck`,
`--link*`, `--publish-all`, `--storage-opt`,
`--userns`, `--volume-driver`

Expand Down
2 changes: 2 additions & 0 deletions pkg/api/types/container_network_types.go
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,8 @@ type NetworkOptions struct {
IP6Address string
// Hostname set container host name
Hostname string
// Domainname specifies the container's domain name
Domainname string
// DNSServers set custom DNS servers
DNSServers []string
// DNSResolvConfOptions set DNS options
Expand Down
7 changes: 5 additions & 2 deletions pkg/cmd/container/create.go
Original file line number Diff line number Diff line change
Expand Up @@ -621,8 +621,9 @@ type internalLabels struct {
extraHosts []string
pidFile string
// labels from cmd options or automatically set
name string
hostname string
name string
hostname string
domainname string
// automatically generated
stateDir string
// network
Expand Down Expand Up @@ -652,6 +653,7 @@ func withInternalLabels(internalLabels internalLabels) (containerd.NewContainerO
m[labels.Name] = internalLabels.name
}
m[labels.Hostname] = internalLabels.hostname
m[labels.Domainname] = internalLabels.domainname
extraHostsJSON, err := json.Marshal(internalLabels.extraHosts)
if err != nil {
return nil, err
Expand Down Expand Up @@ -729,6 +731,7 @@ func withInternalLabels(internalLabels internalLabels) (containerd.NewContainerO
// loadNetOpts loads network options into InternalLabels.
func (il *internalLabels) loadNetOpts(opts types.NetworkOptions) {
il.hostname = opts.Hostname
il.domainname = opts.Domainname
il.ports = opts.PortMappings
il.ipAddress = opts.IPAddress
il.ip6Address = opts.IP6Address
Expand Down
12 changes: 8 additions & 4 deletions pkg/containerutil/container_network_manager.go
Original file line number Diff line number Diff line change
Expand Up @@ -209,7 +209,8 @@ func (m *containerNetworkManager) VerifyNetworkOptions(_ context.Context) error

// Note that mac-address is accepted, though it is a no-op
nonZeroParams := nonZeroMapValues(map[string]interface{}{
"--hostname": m.netOpts.Hostname,
"--hostname": m.netOpts.Hostname,
"--domainname": m.netOpts.Domainname,
// NOTE: an empty slice still counts as a non-zero value so we check its length:
"-p/--publish": len(m.netOpts.PortMappings) != 0,
"--dns": len(m.netOpts.DNSServers) != 0,
Expand Down Expand Up @@ -576,6 +577,9 @@ func (m *hostNetworkManager) ContainerNetworkingOpts(_ context.Context, containe
if hostnameOpts != nil {
specs = append(specs, hostnameOpts...)
}
if m.netOpts.Domainname != "" {
specs = append(specs, oci.WithDomainname(m.netOpts.Domainname))
}
}

if rootlessutil.IsRootless() {
Expand Down Expand Up @@ -643,9 +647,9 @@ func validateUtsSettings(netOpts types.NetworkOptions) error {
}

// Docker considers this a validation error so keep compat.
// https://docs.docker.com/engine/reference/run/#uts-settings---uts
if utsNamespace == UtsNamespaceHost && netOpts.Hostname != "" {
return fmt.Errorf("conflicting options: cannot set a --hostname with --uts=host")
// https://docs.docker.com/reference/cli/docker/container/run/#uts
if utsNamespace == UtsNamespaceHost && (netOpts.Hostname != "" || netOpts.Domainname != "") {
return fmt.Errorf("conflicting options: cannot set --hostname and/or --domainname with --uts=host")
}

return nil
Expand Down
3 changes: 3 additions & 0 deletions pkg/containerutil/container_network_manager_linux.go
Original file line number Diff line number Diff line change
Expand Up @@ -129,6 +129,9 @@ func (m *cniNetworkManager) ContainerNetworkingOpts(_ context.Context, container
if hostnameOpts != nil {
opts = append(opts, hostnameOpts...)
}
if m.netOpts.Domainname != "" {
opts = append(opts, oci.WithDomainname(m.netOpts.Domainname))
}
}

return opts, cOpts, nil
Expand Down
5 changes: 3 additions & 2 deletions pkg/containerutil/container_network_manager_windows.go
Original file line number Diff line number Diff line change
Expand Up @@ -48,8 +48,9 @@ func (m *cniNetworkManager) VerifyNetworkOptions(_ context.Context) error {
}

nonZeroArgs := nonZeroMapValues(map[string]interface{}{
"--hostname": m.netOpts.Hostname,
"--uts": m.netOpts.UTSNamespace,
"--hostname": m.netOpts.Hostname,
"--domainname": m.netOpts.Domainname,
"--uts": m.netOpts.UTSNamespace,
// NOTE: IP and MAC settings are currently ignored on Windows.
"--ip-address": m.netOpts.IPAddress,
"--mac-address": m.netOpts.MACAddress,
Expand Down
1 change: 1 addition & 0 deletions pkg/dnsutil/hostsstore/hostsstore.go
Original file line number Diff line number Diff line change
Expand Up @@ -82,6 +82,7 @@ type Meta struct {
Hostname string
ExtraHosts map[string]string // host:ip
Name string
Domainname string
}

type Store interface {
Expand Down
13 changes: 11 additions & 2 deletions pkg/dnsutil/hostsstore/updater.go
Original file line number Diff line number Diff line change
Expand Up @@ -21,8 +21,11 @@ import (
)

// createLine returns a line string slice.
// line is like "foo foo.nw0 bar bar.nw0\n"
// for `nerdctl --name=foo --hostname=bar --network=n0`.
// line is like "bar bar.nw0 foo foo.nw0\n"
// for `nerdctl --name=foo --hostname=bar --network=nw0`.
//
// line is line "bar.example.com bar bar.nw0 foo foo.nw0\n"
// for `nerdctl --name=foo --hostname=bar --domainname=example.com --network=n0`.
//
// May return an empty string slice
func createLine(thatNetwork string, meta *Meta, myNetworks map[string]struct{}) []string {
Expand All @@ -31,7 +34,13 @@ func createLine(thatNetwork string, meta *Meta, myNetworks map[string]struct{})
// Do not add lines for other networks
return line
}

if meta.Domainname != "" {
line = append(line, meta.Hostname+"."+meta.Domainname)
}

baseHostnames := []string{meta.Hostname}

if meta.Name != "" {
baseHostnames = append(baseHostnames, meta.Name)
}
Expand Down
64 changes: 54 additions & 10 deletions pkg/dnsutil/hostsstore/updater_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -23,16 +23,19 @@ import (

types100 "github.com/containernetworking/cni/pkg/types/100"
"gotest.tools/v3/assert"

"github.com/containerd/nerdctl/v2/pkg/netutil"
)

func TestCreateLine(t *testing.T) {
type testCase struct {
thatIP string
thatNetwork string
thatHostname string // nerdctl run --hostname
thatName string // nerdctl run --name
myNetwork string
expected string
thatIP string
thatNetwork string
thatHostname string // nerdctl run --hostname
thatDomainname string // nerdctl run --domainname
thatName string // nerdctl run --name
myNetwork string
expected string
}
testCases := []testCase{
{
Expand All @@ -52,7 +55,7 @@ func TestCreateLine(t *testing.T) {
},
{
thatIP: "10.4.2.4",
thatNetwork: "bridge",
thatNetwork: netutil.DefaultNetworkName,
thatHostname: "bar",
myNetwork: "n1",
expected: "",
Expand All @@ -61,7 +64,7 @@ func TestCreateLine(t *testing.T) {
thatIP: "10.4.2.5",
thatNetwork: "n1",
thatName: "foo",
myNetwork: "bridge",
myNetwork: netutil.DefaultNetworkName,
expected: "",
},
{
Expand All @@ -71,6 +74,46 @@ func TestCreateLine(t *testing.T) {
myNetwork: "n2",
expected: "",
},
{
thatIP: "10.4.2.3",
thatNetwork: "n1",
thatHostname: "bar.example.com", // using a fqdn as hostname
myNetwork: "n1",
expected: "bar.example.com bar.example.com.n1",
},
{
thatIP: "10.4.2.7",
thatNetwork: "n1",
thatHostname: "bar", // unqualified hostname with separate domain name
thatName: "foo",
thatDomainname: "example.com",
myNetwork: "n1",
expected: "bar.example.com bar bar.n1 foo foo.n1",
},
{
thatIP: "10.4.2.8",
thatNetwork: "n1",
thatHostname: "bar",
thatDomainname: "example.com",
myNetwork: "n1",
expected: "bar.example.com bar bar.n1",
},
{
thatIP: "10.4.2.9",
thatNetwork: netutil.DefaultNetworkName,
thatHostname: "bar",
thatDomainname: "example.com",
myNetwork: netutil.DefaultNetworkName,
expected: "bar.example.com bar",
},
{
thatIP: "10.4.2.9",
thatNetwork: netutil.DefaultNetworkName,
thatHostname: "bar.example.com",
thatDomainname: "example.com",
myNetwork: netutil.DefaultNetworkName,
expected: "bar.example.com.example.com bar.example.com",
},
}
for _, tc := range testCases {
thatMeta := &Meta{
Expand All @@ -89,8 +132,9 @@ func TestCreateLine(t *testing.T) {
},
},
},
Hostname: tc.thatHostname,
Name: tc.thatName,
Hostname: tc.thatHostname,
Domainname: tc.thatDomainname,
Name: tc.thatName,
}

myNetworks := map[string]struct{}{
Expand Down
8 changes: 6 additions & 2 deletions pkg/inspecttypes/dockercompat/dockercompat.go
Original file line number Diff line number Diff line change
Expand Up @@ -142,8 +142,8 @@ type MountPoint struct {

// config is from https://github.com/moby/moby/blob/8dbd90ec00daa26dc45d7da2431c965dec99e8b4/api/types/container/config.go#L37-L69
type Config struct {
Hostname string `json:",omitempty"` // Hostname
// TODO: Domainname string // Domainname
Hostname string `json:",omitempty"` // Hostname
Domainname string `json:",omitempty"` // Domainname
User string `json:",omitempty"` // User that will run the command(s) inside the container, also support user:group
AttachStdin bool // Attach the standard input, makes possible user interaction
// TODO: AttachStdout bool // Attach the standard output
Expand Down Expand Up @@ -318,6 +318,10 @@ func ContainerFromNative(n *native.Container) (*Container, error) {
}
c.Config.Hostname = hostname

if n.Labels[labels.Domainname] != "" {
c.Config.Domainname = n.Labels[labels.Domainname]
}

return c, nil
}

Expand Down
3 changes: 3 additions & 0 deletions pkg/labels/labels.go
Original file line number Diff line number Diff line change
Expand Up @@ -44,6 +44,9 @@ const (
// Hostname
Hostname = Prefix + "hostname"

// Domainname
Domainname = Prefix + "domainname"

// ExtraHosts are HostIPs to appended to /etc/hosts
ExtraHosts = Prefix + "extraHosts"

Expand Down
1 change: 1 addition & 0 deletions pkg/ocihook/ocihook.go
Original file line number Diff line number Diff line change
Expand Up @@ -457,6 +457,7 @@ func applyNetworkSettings(opts *handlerOpts) error {
ID: opts.state.ID,
Networks: make(map[string]*types100.Result, len(opts.cniNames)),
Hostname: opts.state.Annotations[labels.Hostname],
Domainname: opts.state.Annotations[labels.Domainname],
ExtraHosts: opts.extraHosts,
Name: opts.state.Annotations[labels.Name],
}
Expand Down
Loading