Add a --dns-search parameter and a DnsSearch
configuration field for specifying dns search
domains.
Docker-DCO-1.1-Signed-off-by: Daniel Norberg <daniel.norberg@gmail.com> (github: danielnorberg)
| ... | ... |
@@ -18,6 +18,7 @@ type Config struct {
|
| 18 | 18 |
Root string |
| 19 | 19 |
AutoRestart bool |
| 20 | 20 |
Dns []string |
| 21 |
+ DnsSearch []string |
|
| 21 | 22 |
EnableIptables bool |
| 22 | 23 |
EnableIpForward bool |
| 23 | 24 |
DefaultIp net.IP |
| ... | ... |
@@ -49,6 +50,9 @@ func ConfigFromJob(job *engine.Job) *Config {
|
| 49 | 49 |
if dns := job.GetenvList("Dns"); dns != nil {
|
| 50 | 50 |
config.Dns = dns |
| 51 | 51 |
} |
| 52 |
+ if dnsSearch := job.GetenvList("DnsSearch"); dnsSearch != nil {
|
|
| 53 |
+ config.DnsSearch = dnsSearch |
|
| 54 |
+ } |
|
| 52 | 55 |
if mtu := job.GetenvInt("Mtu"); mtu != 0 {
|
| 53 | 56 |
config.Mtu = mtu |
| 54 | 57 |
} else {
|
| ... | ... |
@@ -35,6 +35,7 @@ func main() {
|
| 35 | 35 |
flSocketGroup = flag.String([]string{"G", "-group"}, "docker", "Group to assign the unix socket specified by -H when running in daemon mode; use '' (the empty string) to disable setting of a group")
|
| 36 | 36 |
flEnableCors = flag.Bool([]string{"#api-enable-cors", "-api-enable-cors"}, false, "Enable CORS headers in the remote API")
|
| 37 | 37 |
flDns = opts.NewListOpts(opts.ValidateIp4Address) |
| 38 |
+ flDnsSearch = opts.NewListOpts(opts.ValidateDomain) |
|
| 38 | 39 |
flEnableIptables = flag.Bool([]string{"#iptables", "-iptables"}, true, "Enable Docker's addition of iptables rules")
|
| 39 | 40 |
flEnableIpForward = flag.Bool([]string{"#ip-forward", "-ip-forward"}, true, "Enable net.ipv4.ip_forward")
|
| 40 | 41 |
flDefaultIp = flag.String([]string{"#ip", "-ip"}, "0.0.0.0", "Default IP address to use when binding container ports")
|
| ... | ... |
@@ -45,6 +46,7 @@ func main() {
|
| 45 | 45 |
flMtu = flag.Int([]string{"#mtu", "-mtu"}, 0, "Set the containers network MTU; if no value is provided: default to the default route MTU or 1500 if no default route is available")
|
| 46 | 46 |
) |
| 47 | 47 |
flag.Var(&flDns, []string{"#dns", "-dns"}, "Force docker to use specific DNS servers")
|
| 48 |
+ flag.Var(&flDnsSearch, []string{"-dns-search"}, "Force Docker to use specific DNS search domains")
|
|
| 48 | 49 |
flag.Var(&flHosts, []string{"H", "-host"}, "tcp://host:port, unix://path/to/socket, fd://* or fd://socketfd to use in daemon mode. Multiple sockets can be specified")
|
| 49 | 50 |
|
| 50 | 51 |
flag.Parse() |
| ... | ... |
@@ -115,6 +117,7 @@ func main() {
|
| 115 | 115 |
job.Setenv("Root", realRoot)
|
| 116 | 116 |
job.SetenvBool("AutoRestart", *flAutoRestart)
|
| 117 | 117 |
job.SetenvList("Dns", flDns.GetAll())
|
| 118 |
+ job.SetenvList("DnsSearch", flDnsSearch.GetAll())
|
|
| 118 | 119 |
job.SetenvBool("EnableIptables", *flEnableIptables)
|
| 119 | 120 |
job.SetenvBool("EnableIpForward", *flEnableIpForward)
|
| 120 | 121 |
job.Setenv("BridgeIface", *bridgeName)
|
| ... | ... |
@@ -77,6 +77,7 @@ Commands |
| 77 | 77 |
--bip="": Use this CIDR notation address for the network bridge's IP, not compatible with -b |
| 78 | 78 |
-d, --daemon=false: Enable daemon mode |
| 79 | 79 |
--dns=[]: Force docker to use specific DNS servers |
| 80 |
+ --dns-search=[]: Force Docker to use specific DNS search domains |
|
| 80 | 81 |
-g, --graph="/var/lib/docker": Path to use as the root of the docker runtime |
| 81 | 82 |
--icc=true: Enable inter-container communication |
| 82 | 83 |
--ip="0.0.0.0": Default IP address to use when binding container ports |
| ... | ... |
@@ -96,6 +97,8 @@ To force Docker to use devicemapper as the storage driver, use ``docker -d -s de |
| 96 | 96 |
|
| 97 | 97 |
To set the DNS server for all Docker containers, use ``docker -d --dns 8.8.8.8``. |
| 98 | 98 |
|
| 99 |
+To set the a DNS search domain for all Docker containers, use ``docker -d --dns-search example.com``. |
|
| 100 |
+ |
|
| 99 | 101 |
To run the daemon with debug output, use ``docker -d -D``. |
| 100 | 102 |
|
| 101 | 103 |
To use lxc as the execution driver, use ``docker -d -e lxc``. |
| ... | ... |
@@ -396,6 +399,7 @@ not overridden in the JSON hash will be merged in. |
| 396 | 396 |
"VolumesFrom" : "", |
| 397 | 397 |
"Cmd" : ["cat", "-e", "/etc/resolv.conf"], |
| 398 | 398 |
"Dns" : ["8.8.8.8", "8.8.4.4"], |
| 399 |
+ "DnsSearch" : ["example.com"], |
|
| 399 | 400 |
"MemorySwap" : 0, |
| 400 | 401 |
"AttachStdin" : false, |
| 401 | 402 |
"AttachStderr" : false, |
| ... | ... |
@@ -1131,6 +1135,7 @@ image is removed. |
| 1131 | 1131 |
-t, --tty=false: Allocate a pseudo-tty |
| 1132 | 1132 |
-u, --user="": Username or UID |
| 1133 | 1133 |
--dns=[]: Set custom dns servers for the container |
| 1134 |
+ --dns-search=[]: Set custom DNS search domains for the container |
|
| 1134 | 1135 |
-v, --volume=[]: Create a bind mount to a directory or file with: [host-path]:[container-path]:[rw|ro]. If a directory "container-path" is missing, then docker creates a new volume. |
| 1135 | 1136 |
--volumes-from="": Mount all volumes from the given container(s) |
| 1136 | 1137 |
--entrypoint="": Overwrite the default entrypoint set by the image |
| ... | ... |
@@ -1288,7 +1293,7 @@ A complete example |
| 1288 | 1288 |
$ sudo docker run -d --name static static-web-files sh |
| 1289 | 1289 |
$ sudo docker run -d --expose=8098 --name riak riakserver |
| 1290 | 1290 |
$ sudo docker run -d -m 100m -e DEVELOPMENT=1 -e BRANCH=example-code -v $(pwd):/app/bin:ro --name app appserver |
| 1291 |
- $ sudo docker run -d -p 1443:443 --dns=dns.dev.org -v /var/log/httpd --volumes-from static --link riak --link app -h www.sven.dev.org --name web webserver |
|
| 1291 |
+ $ sudo docker run -d -p 1443:443 --dns=dns.dev.org --dns-search=dev.org -v /var/log/httpd --volumes-from static --link riak --link app -h www.sven.dev.org --name web webserver |
|
| 1292 | 1292 |
$ sudo docker run -t -i --rm --volumes-from web -w /var/log/httpd busybox tail -f access.log |
| 1293 | 1293 |
|
| 1294 | 1294 |
This example shows 5 containers that might be set up to test a web application change: |
| ... | ... |
@@ -1296,7 +1301,7 @@ This example shows 5 containers that might be set up to test a web application c |
| 1296 | 1296 |
1. Start a pre-prepared volume image ``static-web-files`` (in the background) that has CSS, image and static HTML in it, (with a ``VOLUME`` instruction in the ``Dockerfile`` to allow the web server to use those files); |
| 1297 | 1297 |
2. Start a pre-prepared ``riakserver`` image, give the container name ``riak`` and expose port ``8098`` to any containers that link to it; |
| 1298 | 1298 |
3. Start the ``appserver`` image, restricting its memory usage to 100MB, setting two environment variables ``DEVELOPMENT`` and ``BRANCH`` and bind-mounting the current directory (``$(pwd)``) in the container in read-only mode as ``/app/bin``; |
| 1299 |
-4. Start the ``webserver``, mapping port ``443`` in the container to port ``1443`` on the Docker server, setting the DNS server to ``dns.dev.org``, creating a volume to put the log files into (so we can access it from another container), then importing the files from the volume exposed by the ``static`` container, and linking to all exposed ports from ``riak`` and ``app``. Lastly, we set the hostname to ``web.sven.dev.org`` so its consistent with the pre-generated SSL certificate; |
|
| 1299 |
+4. Start the ``webserver``, mapping port ``443`` in the container to port ``1443`` on the Docker server, setting the DNS server to ``dns.dev.org`` and DNS search domain to ``dev.org``, creating a volume to put the log files into (so we can access it from another container), then importing the files from the volume exposed by the ``static`` container, and linking to all exposed ports from ``riak`` and ``app``. Lastly, we set the hostname to ``web.sven.dev.org`` so its consistent with the pre-generated SSL certificate; |
|
| 1300 | 1300 |
5. Finally, we create a container that runs ``tail -f access.log`` using the logs volume from the ``web`` container, setting the workdir to ``/var/log/httpd``. The ``--rm`` option means that when the container exits, the container's layer is removed. |
| 1301 | 1301 |
|
| 1302 | 1302 |
|
| ... | ... |
@@ -136,3 +136,16 @@ func ValidateIp4Address(val string) (string, error) {
|
| 136 | 136 |
} |
| 137 | 137 |
return "", fmt.Errorf("%s is not an ip4 address", val)
|
| 138 | 138 |
} |
| 139 |
+ |
|
| 140 |
+func ValidateDomain(val string) (string, error) {
|
|
| 141 |
+ alpha := regexp.MustCompile(`[a-zA-Z]`) |
|
| 142 |
+ if alpha.FindString(val) == "" {
|
|
| 143 |
+ return "", fmt.Errorf("%s is not a valid domain", val)
|
|
| 144 |
+ } |
|
| 145 |
+ re := regexp.MustCompile(`^(:?(:?[a-zA-Z0-9]|(:?[a-zA-Z0-9][a-zA-Z0-9\-]*[a-zA-Z0-9]))(:?\.(:?[a-zA-Z0-9]|(:?[a-zA-Z0-9][a-zA-Z0-9\-]*[a-zA-Z0-9])))*)\.?\s*$`) |
|
| 146 |
+ var ns = re.FindSubmatch([]byte(val)) |
|
| 147 |
+ if len(ns) > 0 {
|
|
| 148 |
+ return string(ns[1]), nil |
|
| 149 |
+ } |
|
| 150 |
+ return "", fmt.Errorf("%s is not a valid domain", val)
|
|
| 151 |
+} |
| ... | ... |
@@ -22,3 +22,57 @@ func TestValidateIP4(t *testing.T) {
|
| 22 | 22 |
} |
| 23 | 23 |
|
| 24 | 24 |
} |
| 25 |
+ |
|
| 26 |
+func TestValidateDomain(t *testing.T) {
|
|
| 27 |
+ valid := []string{
|
|
| 28 |
+ `a`, |
|
| 29 |
+ `a.`, |
|
| 30 |
+ `1.foo`, |
|
| 31 |
+ `17.foo`, |
|
| 32 |
+ `foo.bar`, |
|
| 33 |
+ `foo.bar.baz`, |
|
| 34 |
+ `foo.bar.`, |
|
| 35 |
+ `foo.bar.baz`, |
|
| 36 |
+ `foo1.bar2`, |
|
| 37 |
+ `foo1.bar2.baz`, |
|
| 38 |
+ `1foo.2bar.`, |
|
| 39 |
+ `1foo.2bar.baz`, |
|
| 40 |
+ `foo-1.bar-2`, |
|
| 41 |
+ `foo-1.bar-2.baz`, |
|
| 42 |
+ `foo-1.bar-2.`, |
|
| 43 |
+ `foo-1.bar-2.baz`, |
|
| 44 |
+ `1-foo.2-bar`, |
|
| 45 |
+ `1-foo.2-bar.baz`, |
|
| 46 |
+ `1-foo.2-bar.`, |
|
| 47 |
+ `1-foo.2-bar.baz`, |
|
| 48 |
+ } |
|
| 49 |
+ |
|
| 50 |
+ invalid := []string{
|
|
| 51 |
+ ``, |
|
| 52 |
+ `.`, |
|
| 53 |
+ `17`, |
|
| 54 |
+ `17.`, |
|
| 55 |
+ `.17`, |
|
| 56 |
+ `17-.`, |
|
| 57 |
+ `17-.foo`, |
|
| 58 |
+ `.foo`, |
|
| 59 |
+ `foo-.bar`, |
|
| 60 |
+ `-foo.bar`, |
|
| 61 |
+ `foo.bar-`, |
|
| 62 |
+ `foo.bar-.baz`, |
|
| 63 |
+ `foo.-bar`, |
|
| 64 |
+ `foo.-bar.baz`, |
|
| 65 |
+ } |
|
| 66 |
+ |
|
| 67 |
+ for _, domain := range valid {
|
|
| 68 |
+ if ret, err := ValidateDomain(domain); err != nil || ret == "" {
|
|
| 69 |
+ t.Fatalf("ValidateDomain(`"+domain+"`) got %s %s", ret, err)
|
|
| 70 |
+ } |
|
| 71 |
+ } |
|
| 72 |
+ |
|
| 73 |
+ for _, domain := range invalid {
|
|
| 74 |
+ if ret, err := ValidateDomain(domain); err == nil || ret != "" {
|
|
| 75 |
+ t.Fatalf("ValidateDomain(`"+domain+"`) got %s %s", ret, err)
|
|
| 76 |
+ } |
|
| 77 |
+ } |
|
| 78 |
+} |
| ... | ... |
@@ -20,6 +20,7 @@ func Compare(a, b *Config) bool {
|
| 20 | 20 |
} |
| 21 | 21 |
if len(a.Cmd) != len(b.Cmd) || |
| 22 | 22 |
len(a.Dns) != len(b.Dns) || |
| 23 |
+ len(a.DnsSearch) != len(b.DnsSearch) || |
|
| 23 | 24 |
len(a.Env) != len(b.Env) || |
| 24 | 25 |
len(a.PortSpecs) != len(b.PortSpecs) || |
| 25 | 26 |
len(a.ExposedPorts) != len(b.ExposedPorts) || |
| ... | ... |
@@ -38,6 +39,11 @@ func Compare(a, b *Config) bool {
|
| 38 | 38 |
return false |
| 39 | 39 |
} |
| 40 | 40 |
} |
| 41 |
+ for i := 0; i < len(a.DnsSearch); i++ {
|
|
| 42 |
+ if a.DnsSearch[i] != b.DnsSearch[i] {
|
|
| 43 |
+ return false |
|
| 44 |
+ } |
|
| 45 |
+ } |
|
| 41 | 46 |
for i := 0; i < len(a.Env); i++ {
|
| 42 | 47 |
if a.Env[i] != b.Env[i] {
|
| 43 | 48 |
return false |
| ... | ... |
@@ -26,6 +26,7 @@ type Config struct {
|
| 26 | 26 |
Env []string |
| 27 | 27 |
Cmd []string |
| 28 | 28 |
Dns []string |
| 29 |
+ DnsSearch []string |
|
| 29 | 30 |
Image string // Name of the image as it was passed by the operator (eg. could be symbolic) |
| 30 | 31 |
Volumes map[string]struct{}
|
| 31 | 32 |
VolumesFrom string |
| ... | ... |
@@ -68,6 +69,9 @@ func ContainerConfigFromJob(job *engine.Job) *Config {
|
| 68 | 68 |
if Dns := job.GetenvList("Dns"); Dns != nil {
|
| 69 | 69 |
config.Dns = Dns |
| 70 | 70 |
} |
| 71 |
+ if DnsSearch := job.GetenvList("DnsSearch"); DnsSearch != nil {
|
|
| 72 |
+ config.DnsSearch = DnsSearch |
|
| 73 |
+ } |
|
| 71 | 74 |
if Entrypoint := job.GetenvList("Entrypoint"); Entrypoint != nil {
|
| 72 | 75 |
config.Entrypoint = Entrypoint |
| 73 | 76 |
} |
| ... | ... |
@@ -164,6 +164,7 @@ func TestCompare(t *testing.T) {
|
| 164 | 164 |
volumes1["/test1"] = struct{}{}
|
| 165 | 165 |
config1 := Config{
|
| 166 | 166 |
Dns: []string{"1.1.1.1", "2.2.2.2"},
|
| 167 |
+ DnsSearch: []string{"foo", "bar"},
|
|
| 167 | 168 |
PortSpecs: []string{"1111:1111", "2222:2222"},
|
| 168 | 169 |
Env: []string{"VAR1=1", "VAR2=2"},
|
| 169 | 170 |
VolumesFrom: "11111111", |
| ... | ... |
@@ -171,6 +172,7 @@ func TestCompare(t *testing.T) {
|
| 171 | 171 |
} |
| 172 | 172 |
config2 := Config{
|
| 173 | 173 |
Dns: []string{"0.0.0.0", "2.2.2.2"},
|
| 174 |
+ DnsSearch: []string{"foo", "bar"},
|
|
| 174 | 175 |
PortSpecs: []string{"1111:1111", "2222:2222"},
|
| 175 | 176 |
Env: []string{"VAR1=1", "VAR2=2"},
|
| 176 | 177 |
VolumesFrom: "11111111", |
| ... | ... |
@@ -178,6 +180,7 @@ func TestCompare(t *testing.T) {
|
| 178 | 178 |
} |
| 179 | 179 |
config3 := Config{
|
| 180 | 180 |
Dns: []string{"1.1.1.1", "2.2.2.2"},
|
| 181 |
+ DnsSearch: []string{"foo", "bar"},
|
|
| 181 | 182 |
PortSpecs: []string{"0000:0000", "2222:2222"},
|
| 182 | 183 |
Env: []string{"VAR1=1", "VAR2=2"},
|
| 183 | 184 |
VolumesFrom: "11111111", |
| ... | ... |
@@ -185,6 +188,7 @@ func TestCompare(t *testing.T) {
|
| 185 | 185 |
} |
| 186 | 186 |
config4 := Config{
|
| 187 | 187 |
Dns: []string{"1.1.1.1", "2.2.2.2"},
|
| 188 |
+ DnsSearch: []string{"foo", "bar"},
|
|
| 188 | 189 |
PortSpecs: []string{"0000:0000", "2222:2222"},
|
| 189 | 190 |
Env: []string{"VAR1=1", "VAR2=2"},
|
| 190 | 191 |
VolumesFrom: "22222222", |
| ... | ... |
@@ -194,11 +198,20 @@ func TestCompare(t *testing.T) {
|
| 194 | 194 |
volumes2["/test2"] = struct{}{}
|
| 195 | 195 |
config5 := Config{
|
| 196 | 196 |
Dns: []string{"1.1.1.1", "2.2.2.2"},
|
| 197 |
+ DnsSearch: []string{"foo", "bar"},
|
|
| 197 | 198 |
PortSpecs: []string{"0000:0000", "2222:2222"},
|
| 198 | 199 |
Env: []string{"VAR1=1", "VAR2=2"},
|
| 199 | 200 |
VolumesFrom: "11111111", |
| 200 | 201 |
Volumes: volumes2, |
| 201 | 202 |
} |
| 203 |
+ config6 := Config{
|
|
| 204 |
+ Dns: []string{"1.1.1.1", "2.2.2.2"},
|
|
| 205 |
+ DnsSearch: []string{"foos", "bars"},
|
|
| 206 |
+ PortSpecs: []string{"1111:1111", "2222:2222"},
|
|
| 207 |
+ Env: []string{"VAR1=1", "VAR2=2"},
|
|
| 208 |
+ VolumesFrom: "11111111", |
|
| 209 |
+ Volumes: volumes1, |
|
| 210 |
+ } |
|
| 202 | 211 |
if Compare(&config1, &config2) {
|
| 203 | 212 |
t.Fatalf("Compare should return false, Dns are different")
|
| 204 | 213 |
} |
| ... | ... |
@@ -211,6 +224,9 @@ func TestCompare(t *testing.T) {
|
| 211 | 211 |
if Compare(&config1, &config5) {
|
| 212 | 212 |
t.Fatalf("Compare should return false, Volumes are different")
|
| 213 | 213 |
} |
| 214 |
+ if Compare(&config1, &config6) {
|
|
| 215 |
+ t.Fatalf("Compare should return false, DnsSearch are different")
|
|
| 216 |
+ } |
|
| 214 | 217 |
if !Compare(&config1, &config1) {
|
| 215 | 218 |
t.Fatalf("Compare should return true")
|
| 216 | 219 |
} |
| ... | ... |
@@ -100,6 +100,12 @@ func Merge(userConf, imageConf *Config) error {
|
| 100 | 100 |
//duplicates aren't an issue here |
| 101 | 101 |
userConf.Dns = append(userConf.Dns, imageConf.Dns...) |
| 102 | 102 |
} |
| 103 |
+ if userConf.DnsSearch == nil || len(userConf.DnsSearch) == 0 {
|
|
| 104 |
+ userConf.DnsSearch = imageConf.DnsSearch |
|
| 105 |
+ } else {
|
|
| 106 |
+ //duplicates aren't an issue here |
|
| 107 |
+ userConf.DnsSearch = append(userConf.DnsSearch, imageConf.DnsSearch...) |
|
| 108 |
+ } |
|
| 103 | 109 |
if userConf.Entrypoint == nil || len(userConf.Entrypoint) == 0 {
|
| 104 | 110 |
userConf.Entrypoint = imageConf.Entrypoint |
| 105 | 111 |
} |
| ... | ... |
@@ -42,6 +42,7 @@ func parseRun(cmd *flag.FlagSet, args []string, sysInfo *sysinfo.SysInfo) (*Conf |
| 42 | 42 |
flPublish opts.ListOpts |
| 43 | 43 |
flExpose opts.ListOpts |
| 44 | 44 |
flDns opts.ListOpts |
| 45 |
+ flDnsSearch = opts.NewListOpts(opts.ValidateDomain) |
|
| 45 | 46 |
flVolumesFrom opts.ListOpts |
| 46 | 47 |
flLxcOpts opts.ListOpts |
| 47 | 48 |
|
| ... | ... |
@@ -73,6 +74,7 @@ func parseRun(cmd *flag.FlagSet, args []string, sysInfo *sysinfo.SysInfo) (*Conf |
| 73 | 73 |
cmd.Var(&flPublish, []string{"p", "-publish"}, fmt.Sprintf("Publish a container's port to the host (format: %s) (use 'docker port' to see the actual mapping)", nat.PortSpecTemplateFormat))
|
| 74 | 74 |
cmd.Var(&flExpose, []string{"#expose", "-expose"}, "Expose a port from the container without publishing it to your host")
|
| 75 | 75 |
cmd.Var(&flDns, []string{"#dns", "-dns"}, "Set custom dns servers")
|
| 76 |
+ cmd.Var(&flDnsSearch, []string{"-dns-search"}, "Set custom dns search domains")
|
|
| 76 | 77 |
cmd.Var(&flVolumesFrom, []string{"#volumes-from", "-volumes-from"}, "Mount volumes from the specified container(s)")
|
| 77 | 78 |
cmd.Var(&flLxcOpts, []string{"#lxc-conf", "-lxc-conf"}, "Add custom lxc options --lxc-conf=\"lxc.cgroup.cpuset.cpus = 0,1\"")
|
| 78 | 79 |
|
| ... | ... |
@@ -196,6 +198,7 @@ func parseRun(cmd *flag.FlagSet, args []string, sysInfo *sysinfo.SysInfo) (*Conf |
| 196 | 196 |
Env: flEnv.GetAll(), |
| 197 | 197 |
Cmd: runCmd, |
| 198 | 198 |
Dns: flDns.GetAll(), |
| 199 |
+ DnsSearch: flDnsSearch.GetAll(), |
|
| 199 | 200 |
Image: image, |
| 200 | 201 |
Volumes: flVolumes.GetMap(), |
| 201 | 202 |
VolumesFrom: strings.Join(flVolumesFrom.GetAll(), ","), |
| ... | ... |
@@ -493,13 +493,19 @@ func (runtime *Runtime) Create(config *runconfig.Config, name string) (*Containe |
| 493 | 493 |
} |
| 494 | 494 |
|
| 495 | 495 |
// If custom dns exists, then create a resolv.conf for the container |
| 496 |
- if len(config.Dns) > 0 || len(runtime.config.Dns) > 0 {
|
|
| 497 |
- var dns []string |
|
| 496 |
+ if len(config.Dns) > 0 || len(runtime.config.Dns) > 0 || len(config.DnsSearch) > 0 || len(runtime.config.DnsSearch) > 0 {
|
|
| 497 |
+ dns := utils.GetNameservers(resolvConf) |
|
| 498 |
+ dnsSearch := utils.GetSearchDomains(resolvConf) |
|
| 498 | 499 |
if len(config.Dns) > 0 {
|
| 499 | 500 |
dns = config.Dns |
| 500 |
- } else {
|
|
| 501 |
+ } else if len(runtime.config.Dns) > 0 {
|
|
| 501 | 502 |
dns = runtime.config.Dns |
| 502 | 503 |
} |
| 504 |
+ if len(config.DnsSearch) > 0 {
|
|
| 505 |
+ dnsSearch = config.DnsSearch |
|
| 506 |
+ } else if len(runtime.config.DnsSearch) > 0 {
|
|
| 507 |
+ dnsSearch = runtime.config.DnsSearch |
|
| 508 |
+ } |
|
| 503 | 509 |
container.ResolvConfPath = path.Join(container.root, "resolv.conf") |
| 504 | 510 |
f, err := os.Create(container.ResolvConfPath) |
| 505 | 511 |
if err != nil {
|
| ... | ... |
@@ -511,6 +517,11 @@ func (runtime *Runtime) Create(config *runconfig.Config, name string) (*Containe |
| 511 | 511 |
return nil, nil, err |
| 512 | 512 |
} |
| 513 | 513 |
} |
| 514 |
+ if len(dnsSearch) > 0 {
|
|
| 515 |
+ if _, err := f.Write([]byte("search " + strings.Join(dnsSearch, " ") + "\n")); err != nil {
|
|
| 516 |
+ return nil, nil, err |
|
| 517 |
+ } |
|
| 518 |
+ } |
|
| 514 | 519 |
} else {
|
| 515 | 520 |
container.ResolvConfPath = "/etc/resolv.conf" |
| 516 | 521 |
} |
| ... | ... |
@@ -731,54 +731,78 @@ func GetResolvConf() ([]byte, error) {
|
| 731 | 731 |
// CheckLocalDns looks into the /etc/resolv.conf, |
| 732 | 732 |
// it returns true if there is a local nameserver or if there is no nameserver. |
| 733 | 733 |
func CheckLocalDns(resolvConf []byte) bool {
|
| 734 |
- var parsedResolvConf = StripComments(resolvConf, []byte("#"))
|
|
| 735 |
- if !bytes.Contains(parsedResolvConf, []byte("nameserver")) {
|
|
| 736 |
- return true |
|
| 737 |
- } |
|
| 738 |
- for _, ip := range [][]byte{
|
|
| 739 |
- []byte("127.0.0.1"),
|
|
| 740 |
- []byte("127.0.1.1"),
|
|
| 741 |
- } {
|
|
| 742 |
- if bytes.Contains(parsedResolvConf, ip) {
|
|
| 743 |
- return true |
|
| 734 |
+ for _, line := range GetLines(resolvConf, []byte("#")) {
|
|
| 735 |
+ if !bytes.Contains(line, []byte("nameserver")) {
|
|
| 736 |
+ continue |
|
| 737 |
+ } |
|
| 738 |
+ for _, ip := range [][]byte{
|
|
| 739 |
+ []byte("127.0.0.1"),
|
|
| 740 |
+ []byte("127.0.1.1"),
|
|
| 741 |
+ } {
|
|
| 742 |
+ if bytes.Contains(line, ip) {
|
|
| 743 |
+ return true |
|
| 744 |
+ } |
|
| 744 | 745 |
} |
| 746 |
+ return false |
|
| 745 | 747 |
} |
| 746 |
- return false |
|
| 748 |
+ return true |
|
| 747 | 749 |
} |
| 748 | 750 |
|
| 749 |
-// StripComments parses input into lines and strips away comments. |
|
| 750 |
-func StripComments(input []byte, commentMarker []byte) []byte {
|
|
| 751 |
+// GetLines parses input into lines and strips away comments. |
|
| 752 |
+func GetLines(input []byte, commentMarker []byte) [][]byte {
|
|
| 751 | 753 |
lines := bytes.Split(input, []byte("\n"))
|
| 752 |
- var output []byte |
|
| 754 |
+ var output [][]byte |
|
| 753 | 755 |
for _, currentLine := range lines {
|
| 754 | 756 |
var commentIndex = bytes.Index(currentLine, commentMarker) |
| 755 | 757 |
if commentIndex == -1 {
|
| 756 |
- output = append(output, currentLine...) |
|
| 758 |
+ output = append(output, currentLine) |
|
| 757 | 759 |
} else {
|
| 758 |
- output = append(output, currentLine[:commentIndex]...) |
|
| 760 |
+ output = append(output, currentLine[:commentIndex]) |
|
| 759 | 761 |
} |
| 760 |
- output = append(output, []byte("\n")...)
|
|
| 761 | 762 |
} |
| 762 | 763 |
return output |
| 763 | 764 |
} |
| 764 | 765 |
|
| 765 |
-// GetNameserversAsCIDR returns nameservers (if any) listed in |
|
| 766 |
-// /etc/resolv.conf as CIDR blocks (e.g., "1.2.3.4/32") |
|
| 767 |
-// This function's output is intended for net.ParseCIDR |
|
| 768 |
-func GetNameserversAsCIDR(resolvConf []byte) []string {
|
|
| 769 |
- var parsedResolvConf = StripComments(resolvConf, []byte("#"))
|
|
| 766 |
+// GetNameservers returns nameservers (if any) listed in /etc/resolv.conf |
|
| 767 |
+func GetNameservers(resolvConf []byte) []string {
|
|
| 770 | 768 |
nameservers := []string{}
|
| 771 | 769 |
re := regexp.MustCompile(`^\s*nameserver\s*(([0-9]+\.){3}([0-9]+))\s*$`)
|
| 772 |
- for _, line := range bytes.Split(parsedResolvConf, []byte("\n")) {
|
|
| 770 |
+ for _, line := range GetLines(resolvConf, []byte("#")) {
|
|
| 773 | 771 |
var ns = re.FindSubmatch(line) |
| 774 | 772 |
if len(ns) > 0 {
|
| 775 |
- nameservers = append(nameservers, string(ns[1])+"/32") |
|
| 773 |
+ nameservers = append(nameservers, string(ns[1])) |
|
| 776 | 774 |
} |
| 777 | 775 |
} |
| 776 |
+ return nameservers |
|
| 777 |
+} |
|
| 778 | 778 |
|
| 779 |
+// GetNameserversAsCIDR returns nameservers (if any) listed in |
|
| 780 |
+// /etc/resolv.conf as CIDR blocks (e.g., "1.2.3.4/32") |
|
| 781 |
+// This function's output is intended for net.ParseCIDR |
|
| 782 |
+func GetNameserversAsCIDR(resolvConf []byte) []string {
|
|
| 783 |
+ nameservers := []string{}
|
|
| 784 |
+ for _, nameserver := range GetNameservers(resolvConf) {
|
|
| 785 |
+ nameservers = append(nameservers, nameserver+"/32") |
|
| 786 |
+ } |
|
| 779 | 787 |
return nameservers |
| 780 | 788 |
} |
| 781 | 789 |
|
| 790 |
+// GetSearchDomains returns search domains (if any) listed in /etc/resolv.conf |
|
| 791 |
+// If more than one search line is encountered, only the contents of the last |
|
| 792 |
+// one is returned. |
|
| 793 |
+func GetSearchDomains(resolvConf []byte) []string {
|
|
| 794 |
+ re := regexp.MustCompile(`^\s*search\s*(([^\s]+\s*)*)$`) |
|
| 795 |
+ domains := []string{}
|
|
| 796 |
+ for _, line := range GetLines(resolvConf, []byte("#")) {
|
|
| 797 |
+ match := re.FindSubmatch(line) |
|
| 798 |
+ if match == nil {
|
|
| 799 |
+ continue |
|
| 800 |
+ } |
|
| 801 |
+ domains = strings.Fields(string(match[1])) |
|
| 802 |
+ } |
|
| 803 |
+ return domains |
|
| 804 |
+} |
|
| 805 |
+ |
|
| 782 | 806 |
// FIXME: Change this not to receive default value as parameter |
| 783 | 807 |
func ParseHost(defaultHost string, defaultUnix, addr string) (string, error) {
|
| 784 | 808 |
var ( |
| ... | ... |
@@ -444,6 +444,30 @@ func TestParsePortMapping(t *testing.T) {
|
| 444 | 444 |
} |
| 445 | 445 |
} |
| 446 | 446 |
|
| 447 |
+func TestGetNameservers(t *testing.T) {
|
|
| 448 |
+ for resolv, result := range map[string][]string{`
|
|
| 449 |
+nameserver 1.2.3.4 |
|
| 450 |
+nameserver 40.3.200.10 |
|
| 451 |
+search example.com`: {"1.2.3.4", "40.3.200.10"},
|
|
| 452 |
+ `search example.com`: {},
|
|
| 453 |
+ `nameserver 1.2.3.4 |
|
| 454 |
+search example.com |
|
| 455 |
+nameserver 4.30.20.100`: {"1.2.3.4", "4.30.20.100"},
|
|
| 456 |
+ ``: {},
|
|
| 457 |
+ ` nameserver 1.2.3.4 `: {"1.2.3.4"},
|
|
| 458 |
+ `search example.com |
|
| 459 |
+nameserver 1.2.3.4 |
|
| 460 |
+#nameserver 4.3.2.1`: {"1.2.3.4"},
|
|
| 461 |
+ `search example.com |
|
| 462 |
+nameserver 1.2.3.4 # not 4.3.2.1`: {"1.2.3.4"},
|
|
| 463 |
+ } {
|
|
| 464 |
+ test := GetNameservers([]byte(resolv)) |
|
| 465 |
+ if !StrSlicesEqual(test, result) {
|
|
| 466 |
+ t.Fatalf("Wrong nameserver string {%s} should be %v. Input: %s", test, result, resolv)
|
|
| 467 |
+ } |
|
| 468 |
+ } |
|
| 469 |
+} |
|
| 470 |
+ |
|
| 447 | 471 |
func TestGetNameserversAsCIDR(t *testing.T) {
|
| 448 | 472 |
for resolv, result := range map[string][]string{`
|
| 449 | 473 |
nameserver 1.2.3.4 |
| ... | ... |
@@ -468,6 +492,33 @@ nameserver 1.2.3.4 # not 4.3.2.1`: {"1.2.3.4/32"},
|
| 468 | 468 |
} |
| 469 | 469 |
} |
| 470 | 470 |
|
| 471 |
+func TestGetSearchDomains(t *testing.T) {
|
|
| 472 |
+ for resolv, result := range map[string][]string{
|
|
| 473 |
+ `search example.com`: {"example.com"},
|
|
| 474 |
+ `search example.com # ignored`: {"example.com"},
|
|
| 475 |
+ ` search example.com `: {"example.com"},
|
|
| 476 |
+ ` search example.com # ignored`: {"example.com"},
|
|
| 477 |
+ `search foo.example.com example.com`: {"foo.example.com", "example.com"},
|
|
| 478 |
+ ` search foo.example.com example.com `: {"foo.example.com", "example.com"},
|
|
| 479 |
+ ` search foo.example.com example.com # ignored`: {"foo.example.com", "example.com"},
|
|
| 480 |
+ ``: {},
|
|
| 481 |
+ `# ignored`: {},
|
|
| 482 |
+ `nameserver 1.2.3.4 |
|
| 483 |
+search foo.example.com example.com`: {"foo.example.com", "example.com"},
|
|
| 484 |
+ `nameserver 1.2.3.4 |
|
| 485 |
+search dup1.example.com dup2.example.com |
|
| 486 |
+search foo.example.com example.com`: {"foo.example.com", "example.com"},
|
|
| 487 |
+ `nameserver 1.2.3.4 |
|
| 488 |
+search foo.example.com example.com |
|
| 489 |
+nameserver 4.30.20.100`: {"foo.example.com", "example.com"},
|
|
| 490 |
+ } {
|
|
| 491 |
+ test := GetSearchDomains([]byte(resolv)) |
|
| 492 |
+ if !StrSlicesEqual(test, result) {
|
|
| 493 |
+ t.Fatalf("Wrong search domain string {%s} should be %v. Input: %s", test, result, resolv)
|
|
| 494 |
+ } |
|
| 495 |
+ } |
|
| 496 |
+} |
|
| 497 |
+ |
|
| 471 | 498 |
func StrSlicesEqual(a, b []string) bool {
|
| 472 | 499 |
if len(a) != len(b) {
|
| 473 | 500 |
return false |