Skip to content

Commit 7e07e0d

Browse files
committed
Enable setting DNS options through global nerdctl config
Signed-off-by: Swagat Bora <sbora@amazon.com>
1 parent 722de0a commit 7e07e0d

File tree

9 files changed

+167
-23
lines changed

9 files changed

+167
-23
lines changed

cmd/nerdctl/container/container_create.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -539,7 +539,7 @@ func createAction(cmd *cobra.Command, args []string) error {
539539
}
540540
defer cancel()
541541

542-
netFlags, err := loadNetworkFlags(cmd)
542+
netFlags, err := loadNetworkFlags(cmd, createOpt.GOptions)
543543
if err != nil {
544544
return fmt.Errorf("failed to load networking flags: %w", err)
545545
}

cmd/nerdctl/container/container_run.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -376,7 +376,7 @@ func runAction(cmd *cobra.Command, args []string) error {
376376
return errors.New("flags -d and -a cannot be specified together")
377377
}
378378

379-
netFlags, err := loadNetworkFlags(cmd)
379+
netFlags, err := loadNetworkFlags(cmd, createOpt.GOptions)
380380
if err != nil {
381381
return fmt.Errorf("failed to load networking flags: %w", err)
382382
}

cmd/nerdctl/container/container_run_network.go

Lines changed: 41 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -28,7 +28,7 @@ import (
2828
"github.com/containerd/nerdctl/v2/pkg/strutil"
2929
)
3030

31-
func loadNetworkFlags(cmd *cobra.Command) (types.NetworkOptions, error) {
31+
func loadNetworkFlags(cmd *cobra.Command, globalOpts types.GlobalCommandOptions) (types.NetworkOptions, error) {
3232
netOpts := types.NetworkOptions{}
3333

3434
// --net/--network=<net name> ...
@@ -101,33 +101,58 @@ func loadNetworkFlags(cmd *cobra.Command) (types.NetworkOptions, error) {
101101
netOpts.Domainname = domainname
102102

103103
// --dns=<DNS host> ...
104-
dnsSlice, err := cmd.Flags().GetStringSlice("dns")
105-
if err != nil {
106-
return netOpts, err
104+
// Use command flags if set, otherwise use global config is set
105+
var dnsSlice []string
106+
if cmd.Flags().Changed("dns") {
107+
var err error
108+
dnsSlice, err = cmd.Flags().GetStringSlice("dns")
109+
if err != nil {
110+
return netOpts, err
111+
}
112+
} else {
113+
dnsSlice = globalOpts.DNS
107114
}
108115
netOpts.DNSServers = strutil.DedupeStrSlice(dnsSlice)
109116

110117
// --dns-search=<domain name> ...
111-
dnsSearchSlice, err := cmd.Flags().GetStringSlice("dns-search")
112-
if err != nil {
113-
return netOpts, err
118+
// Use command flags if set, otherwise use global config is set
119+
var dnsSearchSlice []string
120+
if cmd.Flags().Changed("dns-search") {
121+
var err error
122+
dnsSearchSlice, err = cmd.Flags().GetStringSlice("dns-search")
123+
if err != nil {
124+
return netOpts, err
125+
}
126+
} else {
127+
dnsSearchSlice = globalOpts.DNSSearch
114128
}
115129
netOpts.DNSSearchDomains = strutil.DedupeStrSlice(dnsSearchSlice)
116130

117131
// --dns-opt/--dns-option=<resolv.conf line> ...
132+
// Use command flags if set, otherwise use global config if set
118133
dnsOptions := []string{}
119134

120-
dnsOptFlags, err := cmd.Flags().GetStringSlice("dns-opt")
121-
if err != nil {
122-
return netOpts, err
123-
}
124-
dnsOptions = append(dnsOptions, dnsOptFlags...)
135+
// Check if either dns-opt or dns-option flags were set
136+
dnsOptChanged := cmd.Flags().Changed("dns-opt")
137+
dnsOptionChanged := cmd.Flags().Changed("dns-option")
125138

126-
dnsOptionFlags, err := cmd.Flags().GetStringSlice("dns-option")
127-
if err != nil {
128-
return netOpts, err
139+
if dnsOptChanged || dnsOptionChanged {
140+
// Use command flags
141+
dnsOptFlags, err := cmd.Flags().GetStringSlice("dns-opt")
142+
if err != nil {
143+
return netOpts, err
144+
}
145+
dnsOptions = append(dnsOptions, dnsOptFlags...)
146+
147+
dnsOptionFlags, err := cmd.Flags().GetStringSlice("dns-option")
148+
if err != nil {
149+
return netOpts, err
150+
}
151+
dnsOptions = append(dnsOptions, dnsOptionFlags...)
152+
} else {
153+
// Use global config defaults
154+
dnsOptions = append(dnsOptions, globalOpts.DNSOpts...)
129155
}
130-
dnsOptions = append(dnsOptions, dnsOptionFlags...)
131156

132157
netOpts.DNSResolvConfOptions = strutil.DedupeStrSlice(dnsOptions)
133158

cmd/nerdctl/container/container_run_network_linux_test.go

Lines changed: 84 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -996,3 +996,87 @@ func TestHostNetworkDnsConfigs(t *testing.T) {
996996
}
997997
testCase.Run(t)
998998
}
999+
1000+
func TestDNSWithGlobalConfig(t *testing.T) {
1001+
var configContent test.ConfigValue = `debug = false
1002+
debug_full = false
1003+
dns = ["10.10.10.10", "20.20.20.20"]
1004+
dns_opts = ["ndots:2", "timeout:5"]
1005+
dns_search = ["example.com", "test.local"]`
1006+
1007+
nerdtest.Setup()
1008+
1009+
testCase := &test.Case{
1010+
Config: test.WithConfig(nerdtest.NerdctlToml, configContent),
1011+
// NERDCTL_TOML not supported in Docker
1012+
Require: require.Not(nerdtest.Docker),
1013+
SubTests: []*test.Case{
1014+
{
1015+
Description: "Global DNS settings are used when command line options are not provided",
1016+
Command: func(data test.Data, helpers test.Helpers) test.TestableCommand {
1017+
nerdctlTomlContent := string(helpers.Read(nerdtest.NerdctlToml))
1018+
helpers.T().Log("NERDCTL_TOML file content:\n%s", nerdctlTomlContent)
1019+
cmd := helpers.Command("run", "--rm", testutil.CommonImage, "cat", "/etc/resolv.conf")
1020+
return cmd
1021+
},
1022+
Expected: test.Expects(expect.ExitCodeSuccess, nil, expect.All(
1023+
expect.Contains("nameserver 10.10.10.10"),
1024+
expect.Contains("nameserver 20.20.20.20"),
1025+
expect.Contains("search example.com test.local"),
1026+
expect.Contains("options ndots:2 timeout:5"),
1027+
)),
1028+
},
1029+
{
1030+
Description: "Command line DNS options override global config",
1031+
Command: func(data test.Data, helpers test.Helpers) test.TestableCommand {
1032+
nerdctlTomlContent := string(helpers.Read(nerdtest.NerdctlToml))
1033+
helpers.T().Log("NERDCTL_TOML file content:\n%s", nerdctlTomlContent)
1034+
cmd := helpers.Command("run", "--rm",
1035+
"--dns", "9.9.9.9",
1036+
"--dns-search", "override.com",
1037+
"--dns-opt", "ndots:3",
1038+
testutil.CommonImage, "cat", "/etc/resolv.conf")
1039+
return cmd
1040+
},
1041+
Expected: test.Expects(expect.ExitCodeSuccess, nil, expect.All(
1042+
expect.Contains("nameserver 9.9.9.9"),
1043+
expect.Contains("search override.com"),
1044+
expect.Contains("options ndots:3"),
1045+
)),
1046+
},
1047+
{
1048+
Description: "Global DNS settings should also apply when using host network",
1049+
Command: func(data test.Data, helpers test.Helpers) test.TestableCommand {
1050+
nerdctlTomlContent := string(helpers.Read(nerdtest.NerdctlToml))
1051+
helpers.T().Log("NERDCTL_TOML file content:\n%s", nerdctlTomlContent)
1052+
cmd := helpers.Command("run", "--rm", "--network", "host",
1053+
testutil.CommonImage, "cat", "/etc/resolv.conf")
1054+
return cmd
1055+
},
1056+
Expected: test.Expects(expect.ExitCodeSuccess, nil, expect.All(
1057+
expect.Contains("nameserver 10.10.10.10"),
1058+
expect.Contains("nameserver 20.20.20.20"),
1059+
expect.Contains("search example.com test.local"),
1060+
expect.Contains("options ndots:2 timeout:5"),
1061+
)),
1062+
},
1063+
{
1064+
Description: "Global DNS settings should also apply when using none network",
1065+
Command: func(data test.Data, helpers test.Helpers) test.TestableCommand {
1066+
nerdctlTomlContent := string(helpers.Read(nerdtest.NerdctlToml))
1067+
helpers.T().Log("NERDCTL_TOML file content:\n%s", nerdctlTomlContent)
1068+
cmd := helpers.Command("run", "--rm", "--network", "none",
1069+
testutil.CommonImage, "cat", "/etc/resolv.conf")
1070+
return cmd
1071+
},
1072+
Expected: test.Expects(expect.ExitCodeSuccess, nil, expect.All(
1073+
expect.Contains("nameserver 10.10.10.10"),
1074+
expect.Contains("nameserver 20.20.20.20"),
1075+
expect.Contains("search example.com test.local"),
1076+
expect.Contains("options ndots:2 timeout:5"),
1077+
)),
1078+
},
1079+
},
1080+
}
1081+
testCase.Run(t)
1082+
}

cmd/nerdctl/helpers/cobra.go

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -283,3 +283,10 @@ func AddPersistentBoolFlag(cmd *cobra.Command, name string, aliases, nonPersiste
283283
}
284284
}
285285
}
286+
287+
// HiddenPersistentStringArrayFlag creates a persistent string slice flag and hides it.
288+
// Used mainly to pass global config values to individual commands.
289+
func HiddenPersistentStringArrayFlag(cmd *cobra.Command, name string, value []string, usage string) {
290+
cmd.PersistentFlags().StringSlice(name, value, usage)
291+
cmd.PersistentFlags().MarkHidden(name)
292+
}

cmd/nerdctl/helpers/flagutil.go

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -145,6 +145,18 @@ func ProcessRootCmdFlags(cmd *cobra.Command) (types.GlobalCommandOptions, error)
145145
if err != nil {
146146
return types.GlobalCommandOptions{}, err
147147
}
148+
dns, err := cmd.Flags().GetStringSlice("global-dns")
149+
if err != nil {
150+
return types.GlobalCommandOptions{}, err
151+
}
152+
dnsOpts, err := cmd.Flags().GetStringSlice("global-dns-opts")
153+
if err != nil {
154+
return types.GlobalCommandOptions{}, err
155+
}
156+
dnsSearch, err := cmd.Flags().GetStringSlice("global-dns-search")
157+
if err != nil {
158+
return types.GlobalCommandOptions{}, err
159+
}
148160

149161
// Point to dataRoot for filesystem-helpers implementing rollback / backups.
150162
err = pkg.InitFS(dataRoot)
@@ -169,6 +181,9 @@ func ProcessRootCmdFlags(cmd *cobra.Command) (types.GlobalCommandOptions, error)
169181
BridgeIP: bridgeIP,
170182
KubeHideDupe: kubeHideDupe,
171183
CDISpecDirs: cdiSpecDirs,
184+
DNS: dns,
185+
DNSOpts: dnsOpts,
186+
DNSSearch: dnsSearch,
172187
}, nil
173188
}
174189

cmd/nerdctl/main.go

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -188,6 +188,9 @@ func initRootCmdFlags(rootCmd *cobra.Command, tomlPath string) (*pflag.FlagSet,
188188
rootCmd.PersistentFlags().Bool("kube-hide-dupe", cfg.KubeHideDupe, "Deduplicate images for Kubernetes with namespace k8s.io")
189189
rootCmd.PersistentFlags().StringSlice("cdi-spec-dirs", cfg.CDISpecDirs, "The directories to search for CDI spec files. Defaults to /etc/cdi,/var/run/cdi")
190190
rootCmd.PersistentFlags().String("userns-remap", cfg.UsernsRemap, "Support idmapping for creating and running containers. This options is only supported on linux. If `host` is passed, no idmapping is done. if a user name is passed, it does idmapping based on the uidmap and gidmap ranges specified in /etc/subuid and /etc/subgid respectively")
191+
helpers.HiddenPersistentStringArrayFlag(rootCmd, "global-dns", cfg.DNS, "Global DNS servers for containers")
192+
helpers.HiddenPersistentStringArrayFlag(rootCmd, "global-dns-opts", cfg.DNSOpts, "Global DNS options for containers")
193+
helpers.HiddenPersistentStringArrayFlag(rootCmd, "global-dns-search", cfg.DNSSearch, "Global DNS search domains for containers")
191194
return aliasToBeInherited, nil
192195
}
193196

docs/config.md

Lines changed: 7 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -27,11 +27,14 @@ cgroup_manager = "cgroupfs"
2727
hosts_dir = ["/etc/containerd/certs.d", "/etc/docker/certs.d"]
2828
experimental = true
2929
userns_remap = ""
30+
dns = ["8.8.8.8", "1.1.1.1"]
31+
dns_opts = ["ndots:1", "timeout:2"]
32+
dns_search = ["example.com", "example.org"]
3033
```
3134

3235
## Properties
3336

34-
| TOML property | CLI flag | Env var | Description | Availability \*1 |
37+
| TOML property | CLI flag | Env var | Description | Availability |
3538
|---------------------|------------------------------------|---------------------------|------------------------------------------------------------------------------------------------------------------------------------------------------------------|------------------|
3639
| `debug` | `--debug` | | Debug mode | Since 0.16.0 |
3740
| `debug_full` | `--debug-full` | | Debug mode (with full output) | Since 0.16.0 |
@@ -50,14 +53,16 @@ userns_remap = ""
5053
| `kube_hide_dupe` | `--kube-hide-dupe` | | Deduplicate images for Kubernetes with namespace k8s.io, no more redundant <none> ones are displayed | Since 2.0.3 |
5154
| `cdi_spec_dirs` | `--cdi-spec-dirs` | | The folders to use when searching for CDI ([container-device-interface](https://github.com/cncf-tags/container-device-interface)) specifications. | Since 2.1.0 |
5255
| `userns_remap` | `--userns-remap` | | Support idmapping of containers. This options is only supported on rootful linux. If `host` is passed, no idmapping is done. if a user name is passed, it does idmapping based on the uidmap and gidmap ranges specified in /etc/subuid and /etc/subgid respectively. | Since 2.1.0 |
56+
| `dns` | | | Set global DNS servers for containers | Since 2.1.3 |
57+
| `dns_opts` | | | Set global DNS options for containers | Since 2.1.3 |
58+
| `dns_search` | | | Set global DNS search domains for containers | Since 2.1.3 |
5359

5460
The properties are parsed in the following precedence:
5561
1. CLI flag
5662
2. Env var
5763
3. TOML property
5864
4. Built-in default value (Run `nerdctl --help` to see the default values)
5965

60-
\*1: Availability of the TOML properties
6166

6267
## See also
6368
- [`registry.md`](registry.md)

pkg/config/config.go

Lines changed: 8 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -41,9 +41,11 @@ type Config struct {
4141
HostGatewayIP string `toml:"host_gateway_ip"`
4242
BridgeIP string `toml:"bridge_ip, omitempty"`
4343
KubeHideDupe bool `toml:"kube_hide_dupe"`
44-
// CDISpecDirs is a list of directories in which CDI specifications can be found.
45-
CDISpecDirs []string `toml:"cdi_spec_dirs,omitempty"`
46-
UsernsRemap string `toml:"userns_remap, omitempty"`
44+
CDISpecDirs []string `toml:"cdi_spec_dirs,omitempty"` // CDISpecDirs is a list of directories in which CDI specifications can be found.
45+
UsernsRemap string `toml:"userns_remap, omitempty"`
46+
DNS []string `toml:"dns,omitempty"`
47+
DNSOpts []string `toml:"dns_opts,omitempty"`
48+
DNSSearch []string `toml:"dns_search,omitempty"`
4749
}
4850

4951
// New creates a default Config object statically,
@@ -66,5 +68,8 @@ func New() *Config {
6668
KubeHideDupe: false,
6769
CDISpecDirs: ncdefaults.CDISpecDirs(),
6870
UsernsRemap: "",
71+
DNS: []string{},
72+
DNSOpts: []string{},
73+
DNSSearch: []string{},
6974
}
7075
}

0 commit comments

Comments
 (0)