Skip to content

Commit 87cc074

Browse files
authored
Merge pull request #3885 from swagatbora90/add-domainname-option
feat: add support for 'domainname' option in container create
2 parents fd53b80 + 893a393 commit 87cc074

16 files changed

+160
-23
lines changed

cmd/nerdctl/container/container_run.go

+1
Original file line numberDiff line numberDiff line change
@@ -131,6 +131,7 @@ func setCreateFlags(cmd *cobra.Command) {
131131
cmd.Flags().String("ip", "", "IPv4 address to assign to the container")
132132
cmd.Flags().String("ip6", "", "IPv6 address to assign to the container")
133133
cmd.Flags().StringP("hostname", "h", "", "Container host name")
134+
cmd.Flags().String("domainname", "", "Container domain name")
134135
cmd.Flags().String("mac-address", "", "MAC address to assign to the container")
135136
// #endregion
136137

cmd/nerdctl/container/container_run_linux_test.go

+2
Original file line numberDiff line numberDiff line change
@@ -166,6 +166,8 @@ func TestRunUtsHost(t *testing.T) {
166166
base.Cmd("run", "--rm", "--uts=host", testutil.AlpineImage, "hostname").AssertOutContains(hostName)
167167
// Validate we can't provide a hostname with uts=host
168168
base.Cmd("run", "--rm", "--uts=host", "--hostname=foobar", testutil.AlpineImage, "hostname").AssertFail()
169+
// Validate we can't provide a domainname with uts=host
170+
base.Cmd("run", "--rm", "--uts=host", "--domainname=example.com", testutil.AlpineImage, "hostname").AssertFail()
169171
}
170172

171173
func TestRunPidContainer(t *testing.T) {

cmd/nerdctl/container/container_run_network.go

+7
Original file line numberDiff line numberDiff line change
@@ -93,6 +93,13 @@ func loadNetworkFlags(cmd *cobra.Command) (types.NetworkOptions, error) {
9393
}
9494
netOpts.Hostname = hostName
9595

96+
// --domainname=<container domainname>
97+
domainname, err := cmd.Flags().GetString("domainname")
98+
if err != nil {
99+
return netOpts, err
100+
}
101+
netOpts.Domainname = domainname
102+
96103
// --dns=<DNS host> ...
97104
dnsSlice, err := cmd.Flags().GetStringSlice("dns")
98105
if err != nil {

cmd/nerdctl/container/container_run_test.go

+51
Original file line numberDiff line numberDiff line change
@@ -719,3 +719,54 @@ func TestRunFromOCIArchive(t *testing.T) {
719719
base.Cmd("build", "--tag", tag, fmt.Sprintf("--output=type=oci,dest=%s", tarPath), buildCtx).AssertOK()
720720
base.Cmd("run", "--rm", fmt.Sprintf("oci-archive://%s", tarPath)).AssertOutContainsAll(fmt.Sprintf("Loaded image: %s", tag), sentinel)
721721
}
722+
723+
func TestRunDomainname(t *testing.T) {
724+
t.Parallel()
725+
726+
if runtime.GOOS == "windows" {
727+
t.Skip("run --hostname not implemented on Windows yet")
728+
}
729+
730+
testCases := []struct {
731+
name string
732+
hostname string
733+
domainname string
734+
Cmd string
735+
CmdFlag string
736+
expectedOut string
737+
}{
738+
{
739+
name: "Check domain name",
740+
hostname: "foobar",
741+
domainname: "example.com",
742+
Cmd: "hostname",
743+
CmdFlag: "-d",
744+
expectedOut: "example.com",
745+
},
746+
{
747+
name: "check fqdn",
748+
hostname: "foobar",
749+
domainname: "example.com",
750+
Cmd: "hostname",
751+
CmdFlag: "-f",
752+
expectedOut: "foobar.example.com",
753+
},
754+
}
755+
756+
for _, tc := range testCases {
757+
tc := tc // capture range variable
758+
t.Run(tc.name, func(t *testing.T) {
759+
t.Parallel()
760+
base := testutil.NewBase(t)
761+
762+
base.Cmd("run",
763+
"--rm",
764+
"--hostname", tc.hostname,
765+
"--domainname", tc.domainname,
766+
testutil.CommonImage,
767+
tc.Cmd,
768+
tc.CmdFlag,
769+
).AssertOutContains(tc.expectedOut)
770+
})
771+
}
772+
}

docs/command-reference.md

+2-1
Original file line numberDiff line numberDiff line change
@@ -186,6 +186,7 @@ Network flags:
186186
- :whale: `--dns-search`: Set custom DNS search domains
187187
- :whale: `--dns-opt, --dns-option`: Set DNS options
188188
- :whale: `-h, --hostname`: Container host name
189+
- :whale: `--domainname`: Container domain name
189190
- :whale: `--add-host`: Add a custom host-to-IP mapping (host:ip). `ip` could be a special string `host-gateway`,
190191
- which will be resolved to the `host-gateway-ip` in nerdctl.toml or global flag.
191192
- :whale: `--ip`: Specific static IP address(es) to use. Note that unlike docker, nerdctl allows specifying it with the default bridge network.
@@ -413,7 +414,7 @@ IPFS flags:
413414

414415
Unimplemented `docker run` flags:
415416
`--blkio-weight-device`, `--cpu-rt-*`, `--device-*`,
416-
`--disable-content-trust`, `--domainname`, `--expose`, `--health-*`, `--isolation`, `--no-healthcheck`,
417+
`--disable-content-trust`, `--expose`, `--health-*`, `--isolation`, `--no-healthcheck`,
417418
`--link*`, `--publish-all`, `--storage-opt`,
418419
`--userns`, `--volume-driver`
419420

pkg/api/types/container_network_types.go

+2
Original file line numberDiff line numberDiff line change
@@ -32,6 +32,8 @@ type NetworkOptions struct {
3232
IP6Address string
3333
// Hostname set container host name
3434
Hostname string
35+
// Domainname specifies the container's domain name
36+
Domainname string
3537
// DNSServers set custom DNS servers
3638
DNSServers []string
3739
// DNSResolvConfOptions set DNS options

pkg/cmd/container/create.go

+5-2
Original file line numberDiff line numberDiff line change
@@ -621,8 +621,9 @@ type internalLabels struct {
621621
extraHosts []string
622622
pidFile string
623623
// labels from cmd options or automatically set
624-
name string
625-
hostname string
624+
name string
625+
hostname string
626+
domainname string
626627
// automatically generated
627628
stateDir string
628629
// network
@@ -652,6 +653,7 @@ func withInternalLabels(internalLabels internalLabels) (containerd.NewContainerO
652653
m[labels.Name] = internalLabels.name
653654
}
654655
m[labels.Hostname] = internalLabels.hostname
656+
m[labels.Domainname] = internalLabels.domainname
655657
extraHostsJSON, err := json.Marshal(internalLabels.extraHosts)
656658
if err != nil {
657659
return nil, err
@@ -729,6 +731,7 @@ func withInternalLabels(internalLabels internalLabels) (containerd.NewContainerO
729731
// loadNetOpts loads network options into InternalLabels.
730732
func (il *internalLabels) loadNetOpts(opts types.NetworkOptions) {
731733
il.hostname = opts.Hostname
734+
il.domainname = opts.Domainname
732735
il.ports = opts.PortMappings
733736
il.ipAddress = opts.IPAddress
734737
il.ip6Address = opts.IP6Address

pkg/containerutil/container_network_manager.go

+8-4
Original file line numberDiff line numberDiff line change
@@ -375,7 +375,8 @@ func (m *containerNetworkManager) VerifyNetworkOptions(_ context.Context) error
375375

376376
// Note that mac-address is accepted, though it is a no-op
377377
nonZeroParams := nonZeroMapValues(map[string]interface{}{
378-
"--hostname": m.netOpts.Hostname,
378+
"--hostname": m.netOpts.Hostname,
379+
"--domainname": m.netOpts.Domainname,
379380
// NOTE: an empty slice still counts as a non-zero value so we check its length:
380381
"-p/--publish": len(m.netOpts.PortMappings) != 0,
381382
"--dns": len(m.netOpts.DNSServers) != 0,
@@ -736,6 +737,9 @@ func (m *hostNetworkManager) ContainerNetworkingOpts(_ context.Context, containe
736737
if hostnameOpts != nil {
737738
specs = append(specs, hostnameOpts...)
738739
}
740+
if m.netOpts.Domainname != "" {
741+
specs = append(specs, oci.WithDomainname(m.netOpts.Domainname))
742+
}
739743
}
740744

741745
if rootlessutil.IsRootless() {
@@ -803,9 +807,9 @@ func validateUtsSettings(netOpts types.NetworkOptions) error {
803807
}
804808

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

811815
return nil

pkg/containerutil/container_network_manager_linux.go

+3
Original file line numberDiff line numberDiff line change
@@ -129,6 +129,9 @@ func (m *cniNetworkManager) ContainerNetworkingOpts(_ context.Context, container
129129
if hostnameOpts != nil {
130130
opts = append(opts, hostnameOpts...)
131131
}
132+
if m.netOpts.Domainname != "" {
133+
opts = append(opts, oci.WithDomainname(m.netOpts.Domainname))
134+
}
132135
}
133136

134137
return opts, cOpts, nil

pkg/containerutil/container_network_manager_windows.go

+3-2
Original file line numberDiff line numberDiff line change
@@ -48,8 +48,9 @@ func (m *cniNetworkManager) VerifyNetworkOptions(_ context.Context) error {
4848
}
4949

5050
nonZeroArgs := nonZeroMapValues(map[string]interface{}{
51-
"--hostname": m.netOpts.Hostname,
52-
"--uts": m.netOpts.UTSNamespace,
51+
"--hostname": m.netOpts.Hostname,
52+
"--domainname": m.netOpts.Domainname,
53+
"--uts": m.netOpts.UTSNamespace,
5354
// NOTE: IP and MAC settings are currently ignored on Windows.
5455
"--ip-address": m.netOpts.IPAddress,
5556
"--mac-address": m.netOpts.MACAddress,

pkg/dnsutil/hostsstore/hostsstore.go

+1
Original file line numberDiff line numberDiff line change
@@ -82,6 +82,7 @@ type Meta struct {
8282
Hostname string
8383
ExtraHosts map[string]string // host:ip
8484
Name string
85+
Domainname string
8586
}
8687

8788
type Store interface {

pkg/dnsutil/hostsstore/updater.go

+11-2
Original file line numberDiff line numberDiff line change
@@ -21,8 +21,11 @@ import (
2121
)
2222

2323
// createLine returns a line string slice.
24-
// line is like "foo foo.nw0 bar bar.nw0\n"
25-
// for `nerdctl --name=foo --hostname=bar --network=n0`.
24+
// line is like "bar bar.nw0 foo foo.nw0\n"
25+
// for `nerdctl --name=foo --hostname=bar --network=nw0`.
26+
//
27+
// line is line "bar.example.com bar bar.nw0 foo foo.nw0\n"
28+
// for `nerdctl --name=foo --hostname=bar --domainname=example.com --network=n0`.
2629
//
2730
// May return an empty string slice
2831
func createLine(thatNetwork string, meta *Meta, myNetworks map[string]struct{}) []string {
@@ -31,7 +34,13 @@ func createLine(thatNetwork string, meta *Meta, myNetworks map[string]struct{})
3134
// Do not add lines for other networks
3235
return line
3336
}
37+
38+
if meta.Domainname != "" {
39+
line = append(line, meta.Hostname+"."+meta.Domainname)
40+
}
41+
3442
baseHostnames := []string{meta.Hostname}
43+
3544
if meta.Name != "" {
3645
baseHostnames = append(baseHostnames, meta.Name)
3746
}

pkg/dnsutil/hostsstore/updater_test.go

+54-10
Original file line numberDiff line numberDiff line change
@@ -23,16 +23,19 @@ import (
2323

2424
types100 "github.com/containernetworking/cni/pkg/types/100"
2525
"gotest.tools/v3/assert"
26+
27+
"github.com/containerd/nerdctl/v2/pkg/netutil"
2628
)
2729

2830
func TestCreateLine(t *testing.T) {
2931
type testCase struct {
30-
thatIP string
31-
thatNetwork string
32-
thatHostname string // nerdctl run --hostname
33-
thatName string // nerdctl run --name
34-
myNetwork string
35-
expected string
32+
thatIP string
33+
thatNetwork string
34+
thatHostname string // nerdctl run --hostname
35+
thatDomainname string // nerdctl run --domainname
36+
thatName string // nerdctl run --name
37+
myNetwork string
38+
expected string
3639
}
3740
testCases := []testCase{
3841
{
@@ -52,7 +55,7 @@ func TestCreateLine(t *testing.T) {
5255
},
5356
{
5457
thatIP: "10.4.2.4",
55-
thatNetwork: "bridge",
58+
thatNetwork: netutil.DefaultNetworkName,
5659
thatHostname: "bar",
5760
myNetwork: "n1",
5861
expected: "",
@@ -61,7 +64,7 @@ func TestCreateLine(t *testing.T) {
6164
thatIP: "10.4.2.5",
6265
thatNetwork: "n1",
6366
thatName: "foo",
64-
myNetwork: "bridge",
67+
myNetwork: netutil.DefaultNetworkName,
6568
expected: "",
6669
},
6770
{
@@ -71,6 +74,46 @@ func TestCreateLine(t *testing.T) {
7174
myNetwork: "n2",
7275
expected: "",
7376
},
77+
{
78+
thatIP: "10.4.2.3",
79+
thatNetwork: "n1",
80+
thatHostname: "bar.example.com", // using a fqdn as hostname
81+
myNetwork: "n1",
82+
expected: "bar.example.com bar.example.com.n1",
83+
},
84+
{
85+
thatIP: "10.4.2.7",
86+
thatNetwork: "n1",
87+
thatHostname: "bar", // unqualified hostname with separate domain name
88+
thatName: "foo",
89+
thatDomainname: "example.com",
90+
myNetwork: "n1",
91+
expected: "bar.example.com bar bar.n1 foo foo.n1",
92+
},
93+
{
94+
thatIP: "10.4.2.8",
95+
thatNetwork: "n1",
96+
thatHostname: "bar",
97+
thatDomainname: "example.com",
98+
myNetwork: "n1",
99+
expected: "bar.example.com bar bar.n1",
100+
},
101+
{
102+
thatIP: "10.4.2.9",
103+
thatNetwork: netutil.DefaultNetworkName,
104+
thatHostname: "bar",
105+
thatDomainname: "example.com",
106+
myNetwork: netutil.DefaultNetworkName,
107+
expected: "bar.example.com bar",
108+
},
109+
{
110+
thatIP: "10.4.2.9",
111+
thatNetwork: netutil.DefaultNetworkName,
112+
thatHostname: "bar.example.com",
113+
thatDomainname: "example.com",
114+
myNetwork: netutil.DefaultNetworkName,
115+
expected: "bar.example.com.example.com bar.example.com",
116+
},
74117
}
75118
for _, tc := range testCases {
76119
thatMeta := &Meta{
@@ -89,8 +132,9 @@ func TestCreateLine(t *testing.T) {
89132
},
90133
},
91134
},
92-
Hostname: tc.thatHostname,
93-
Name: tc.thatName,
135+
Hostname: tc.thatHostname,
136+
Domainname: tc.thatDomainname,
137+
Name: tc.thatName,
94138
}
95139

96140
myNetworks := map[string]struct{}{

pkg/inspecttypes/dockercompat/dockercompat.go

+6-2
Original file line numberDiff line numberDiff line change
@@ -142,8 +142,8 @@ type MountPoint struct {
142142

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

321+
if n.Labels[labels.Domainname] != "" {
322+
c.Config.Domainname = n.Labels[labels.Domainname]
323+
}
324+
321325
return c, nil
322326
}
323327

pkg/labels/labels.go

+3
Original file line numberDiff line numberDiff line change
@@ -44,6 +44,9 @@ const (
4444
// Hostname
4545
Hostname = Prefix + "hostname"
4646

47+
// Domainname
48+
Domainname = Prefix + "domainname"
49+
4750
// ExtraHosts are HostIPs to appended to /etc/hosts
4851
ExtraHosts = Prefix + "extraHosts"
4952

pkg/ocihook/ocihook.go

+1
Original file line numberDiff line numberDiff line change
@@ -457,6 +457,7 @@ func applyNetworkSettings(opts *handlerOpts) error {
457457
ID: opts.state.ID,
458458
Networks: make(map[string]*types100.Result, len(opts.cniNames)),
459459
Hostname: opts.state.Annotations[labels.Hostname],
460+
Domainname: opts.state.Annotations[labels.Domainname],
460461
ExtraHosts: opts.extraHosts,
461462
Name: opts.state.Annotations[labels.Name],
462463
}

0 commit comments

Comments
 (0)