mirror of
				https://github.com/moby/moby.git
				synced 2022-11-09 12:21:53 -05:00 
			
		
		
		
	Merge pull request #27567 from yongtang/24391-dns-setting
Add custom DNS settings to service definition
This commit is contained in:
		
						commit
						462e57f05e
					
				
					 12 changed files with 276 additions and 13 deletions
				
			
		| 
						 | 
				
			
			@ -7,6 +7,20 @@ import (
 | 
			
		|||
	"github.com/docker/docker/api/types/mount"
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
// DNSConfig specifies DNS related configurations in resolver configuration file (resolv.conf)
 | 
			
		||||
// Detailed documentation is available in:
 | 
			
		||||
// http://man7.org/linux/man-pages/man5/resolv.conf.5.html
 | 
			
		||||
// `nameserver`, `search`, `options` have been supported.
 | 
			
		||||
// TODO: `domain` is not supported yet.
 | 
			
		||||
type DNSConfig struct {
 | 
			
		||||
	// Nameservers specifies the IP addresses of the name servers
 | 
			
		||||
	Nameservers []string `json:",omitempty"`
 | 
			
		||||
	// Search specifies the search list for host-name lookup
 | 
			
		||||
	Search []string `json:",omitempty"`
 | 
			
		||||
	// Options allows certain internal resolver variables to be modified
 | 
			
		||||
	Options []string `json:",omitempty"`
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// ContainerSpec represents the spec of a container.
 | 
			
		||||
type ContainerSpec struct {
 | 
			
		||||
	Image           string                  `json:",omitempty"`
 | 
			
		||||
| 
						 | 
				
			
			@ -22,4 +36,5 @@ type ContainerSpec struct {
 | 
			
		|||
	Mounts          []mount.Mount           `json:",omitempty"`
 | 
			
		||||
	StopGracePeriod *time.Duration          `json:",omitempty"`
 | 
			
		||||
	Healthcheck     *container.HealthConfig `json:",omitempty"`
 | 
			
		||||
	DNSConfig       *DNSConfig              `json:",omitempty"`
 | 
			
		||||
}
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -41,6 +41,9 @@ func newCreateCommand(dockerCli *command.DockerCli) *cobra.Command {
 | 
			
		|||
	flags.StringSliceVar(&opts.networks, flagNetwork, []string{}, "Network attachments")
 | 
			
		||||
	flags.VarP(&opts.endpoint.ports, flagPublish, "p", "Publish a port as a node port")
 | 
			
		||||
	flags.StringSliceVar(&opts.groups, flagGroup, []string{}, "Set one or more supplementary user groups for the container")
 | 
			
		||||
	flags.Var(&opts.dns, flagDNS, "Set custom DNS servers")
 | 
			
		||||
	flags.Var(&opts.dnsOptions, flagDNSOptions, "Set DNS options")
 | 
			
		||||
	flags.Var(&opts.dnsSearch, flagDNSSearch, "Set custom DNS search domains")
 | 
			
		||||
 | 
			
		||||
	flags.SetInterspersed(false)
 | 
			
		||||
	return cmd
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -296,6 +296,9 @@ type serviceOptions struct {
 | 
			
		|||
	groups          []string
 | 
			
		||||
	tty             bool
 | 
			
		||||
	mounts          opts.MountOpt
 | 
			
		||||
	dns             opts.ListOpts
 | 
			
		||||
	dnsSearch       opts.ListOpts
 | 
			
		||||
	dnsOptions      opts.ListOpts
 | 
			
		||||
 | 
			
		||||
	resources resourceOptions
 | 
			
		||||
	stopGrace DurationOpt
 | 
			
		||||
| 
						 | 
				
			
			@ -325,7 +328,10 @@ func newServiceOptions() *serviceOptions {
 | 
			
		|||
		endpoint: endpointOptions{
 | 
			
		||||
			ports: opts.NewListOpts(ValidatePort),
 | 
			
		||||
		},
 | 
			
		||||
		logDriver: newLogDriverOptions(),
 | 
			
		||||
		logDriver:  newLogDriverOptions(),
 | 
			
		||||
		dns:        opts.NewListOpts(opts.ValidateIPAddress),
 | 
			
		||||
		dnsOptions: opts.NewListOpts(nil),
 | 
			
		||||
		dnsSearch:  opts.NewListOpts(opts.ValidateDNSSearch),
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			@ -358,16 +364,21 @@ func (opts *serviceOptions) ToService() (swarm.ServiceSpec, error) {
 | 
			
		|||
		},
 | 
			
		||||
		TaskTemplate: swarm.TaskSpec{
 | 
			
		||||
			ContainerSpec: swarm.ContainerSpec{
 | 
			
		||||
				Image:           opts.image,
 | 
			
		||||
				Args:            opts.args,
 | 
			
		||||
				Env:             currentEnv,
 | 
			
		||||
				Hostname:        opts.hostname,
 | 
			
		||||
				Labels:          runconfigopts.ConvertKVStringsToMap(opts.containerLabels.GetAll()),
 | 
			
		||||
				Dir:             opts.workdir,
 | 
			
		||||
				User:            opts.user,
 | 
			
		||||
				Groups:          opts.groups,
 | 
			
		||||
				TTY:             opts.tty,
 | 
			
		||||
				Mounts:          opts.mounts.Value(),
 | 
			
		||||
				Image:    opts.image,
 | 
			
		||||
				Args:     opts.args,
 | 
			
		||||
				Env:      currentEnv,
 | 
			
		||||
				Hostname: opts.hostname,
 | 
			
		||||
				Labels:   runconfigopts.ConvertKVStringsToMap(opts.containerLabels.GetAll()),
 | 
			
		||||
				Dir:      opts.workdir,
 | 
			
		||||
				User:     opts.user,
 | 
			
		||||
				Groups:   opts.groups,
 | 
			
		||||
				TTY:      opts.tty,
 | 
			
		||||
				Mounts:   opts.mounts.Value(),
 | 
			
		||||
				DNSConfig: &swarm.DNSConfig{
 | 
			
		||||
					Nameservers: opts.dns.GetAll(),
 | 
			
		||||
					Search:      opts.dnsSearch.GetAll(),
 | 
			
		||||
					Options:     opts.dnsOptions.GetAll(),
 | 
			
		||||
				},
 | 
			
		||||
				StopGracePeriod: opts.stopGrace.Value(),
 | 
			
		||||
			},
 | 
			
		||||
			Networks:      convertNetworks(opts.networks),
 | 
			
		||||
| 
						 | 
				
			
			@ -463,6 +474,15 @@ const (
 | 
			
		|||
	flagContainerLabel        = "container-label"
 | 
			
		||||
	flagContainerLabelRemove  = "container-label-rm"
 | 
			
		||||
	flagContainerLabelAdd     = "container-label-add"
 | 
			
		||||
	flagDNS                   = "dns"
 | 
			
		||||
	flagDNSRemove             = "dns-rm"
 | 
			
		||||
	flagDNSAdd                = "dns-add"
 | 
			
		||||
	flagDNSOptions            = "dns-options"
 | 
			
		||||
	flagDNSOptionsRemove      = "dns-options-rm"
 | 
			
		||||
	flagDNSOptionsAdd         = "dns-options-add"
 | 
			
		||||
	flagDNSSearch             = "dns-search"
 | 
			
		||||
	flagDNSSearchRemove       = "dns-search-rm"
 | 
			
		||||
	flagDNSSearchAdd          = "dns-search-add"
 | 
			
		||||
	flagEndpointMode          = "endpoint-mode"
 | 
			
		||||
	flagHostname              = "hostname"
 | 
			
		||||
	flagEnv                   = "env"
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -48,6 +48,9 @@ func newUpdateCommand(dockerCli *command.DockerCli) *cobra.Command {
 | 
			
		|||
	flags.Var(newListOptsVar(), flagMountRemove, "Remove a mount by its target path")
 | 
			
		||||
	flags.Var(newListOptsVar(), flagPublishRemove, "Remove a published port by its target port")
 | 
			
		||||
	flags.Var(newListOptsVar(), flagConstraintRemove, "Remove a constraint")
 | 
			
		||||
	flags.Var(newListOptsVar(), flagDNSRemove, "Remove custom DNS servers")
 | 
			
		||||
	flags.Var(newListOptsVar(), flagDNSOptionsRemove, "Remove DNS options")
 | 
			
		||||
	flags.Var(newListOptsVar(), flagDNSSearchRemove, "Remove DNS search domains")
 | 
			
		||||
	flags.Var(&opts.labels, flagLabelAdd, "Add or update a service label")
 | 
			
		||||
	flags.Var(&opts.containerLabels, flagContainerLabelAdd, "Add or update a container label")
 | 
			
		||||
	flags.Var(&opts.env, flagEnvAdd, "Add or update an environment variable")
 | 
			
		||||
| 
						 | 
				
			
			@ -55,6 +58,10 @@ func newUpdateCommand(dockerCli *command.DockerCli) *cobra.Command {
 | 
			
		|||
	flags.StringSliceVar(&opts.constraints, flagConstraintAdd, []string{}, "Add or update a placement constraint")
 | 
			
		||||
	flags.Var(&opts.endpoint.ports, flagPublishAdd, "Add or update a published port")
 | 
			
		||||
	flags.StringSliceVar(&opts.groups, flagGroupAdd, []string{}, "Add an additional supplementary user group to the container")
 | 
			
		||||
	flags.Var(&opts.dns, flagDNSAdd, "Add or update custom DNS servers")
 | 
			
		||||
	flags.Var(&opts.dnsOptions, flagDNSOptionsAdd, "Add or update DNS options")
 | 
			
		||||
	flags.Var(&opts.dnsSearch, flagDNSSearchAdd, "Add or update custom DNS search domains")
 | 
			
		||||
 | 
			
		||||
	return cmd
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			@ -257,6 +264,15 @@ func updateService(flags *pflag.FlagSet, spec *swarm.ServiceSpec) error {
 | 
			
		|||
		}
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	if anyChanged(flags, flagDNSAdd, flagDNSRemove, flagDNSOptionsAdd, flagDNSOptionsRemove, flagDNSSearchAdd, flagDNSSearchRemove) {
 | 
			
		||||
		if cspec.DNSConfig == nil {
 | 
			
		||||
			cspec.DNSConfig = &swarm.DNSConfig{}
 | 
			
		||||
		}
 | 
			
		||||
		if err := updateDNSConfig(flags, &cspec.DNSConfig); err != nil {
 | 
			
		||||
			return err
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	if err := updateLogDriver(flags, &spec.TaskTemplate); err != nil {
 | 
			
		||||
		return err
 | 
			
		||||
	}
 | 
			
		||||
| 
						 | 
				
			
			@ -484,6 +500,71 @@ func updateGroups(flags *pflag.FlagSet, groups *[]string) error {
 | 
			
		|||
	return nil
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func removeDuplicates(entries []string) []string {
 | 
			
		||||
	hit := map[string]bool{}
 | 
			
		||||
	newEntries := []string{}
 | 
			
		||||
	for _, v := range entries {
 | 
			
		||||
		if !hit[v] {
 | 
			
		||||
			newEntries = append(newEntries, v)
 | 
			
		||||
			hit[v] = true
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
	return newEntries
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func updateDNSConfig(flags *pflag.FlagSet, config **swarm.DNSConfig) error {
 | 
			
		||||
	newConfig := &swarm.DNSConfig{}
 | 
			
		||||
 | 
			
		||||
	nameservers := (*config).Nameservers
 | 
			
		||||
	if flags.Changed(flagDNSAdd) {
 | 
			
		||||
		values := flags.Lookup(flagDNSAdd).Value.(*opts.ListOpts).GetAll()
 | 
			
		||||
		nameservers = append(nameservers, values...)
 | 
			
		||||
	}
 | 
			
		||||
	nameservers = removeDuplicates(nameservers)
 | 
			
		||||
	toRemove := buildToRemoveSet(flags, flagDNSRemove)
 | 
			
		||||
	for _, nameserver := range nameservers {
 | 
			
		||||
		if _, exists := toRemove[nameserver]; !exists {
 | 
			
		||||
			newConfig.Nameservers = append(newConfig.Nameservers, nameserver)
 | 
			
		||||
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
	// Sort so that result is predictable.
 | 
			
		||||
	sort.Strings(newConfig.Nameservers)
 | 
			
		||||
 | 
			
		||||
	search := (*config).Search
 | 
			
		||||
	if flags.Changed(flagDNSSearchAdd) {
 | 
			
		||||
		values := flags.Lookup(flagDNSSearchAdd).Value.(*opts.ListOpts).GetAll()
 | 
			
		||||
		search = append(search, values...)
 | 
			
		||||
	}
 | 
			
		||||
	search = removeDuplicates(search)
 | 
			
		||||
	toRemove = buildToRemoveSet(flags, flagDNSSearchRemove)
 | 
			
		||||
	for _, entry := range search {
 | 
			
		||||
		if _, exists := toRemove[entry]; !exists {
 | 
			
		||||
			newConfig.Search = append(newConfig.Search, entry)
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
	// Sort so that result is predictable.
 | 
			
		||||
	sort.Strings(newConfig.Search)
 | 
			
		||||
 | 
			
		||||
	options := (*config).Options
 | 
			
		||||
	if flags.Changed(flagDNSOptionsAdd) {
 | 
			
		||||
		values := flags.Lookup(flagDNSOptionsAdd).Value.(*opts.ListOpts).GetAll()
 | 
			
		||||
		options = append(options, values...)
 | 
			
		||||
	}
 | 
			
		||||
	options = removeDuplicates(options)
 | 
			
		||||
	toRemove = buildToRemoveSet(flags, flagDNSOptionsRemove)
 | 
			
		||||
	for _, option := range options {
 | 
			
		||||
		if _, exists := toRemove[option]; !exists {
 | 
			
		||||
			newConfig.Options = append(newConfig.Options, option)
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
	// Sort so that result is predictable.
 | 
			
		||||
	sort.Strings(newConfig.Options)
 | 
			
		||||
 | 
			
		||||
	*config = newConfig
 | 
			
		||||
	return nil
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
type byPortConfig []swarm.PortConfig
 | 
			
		||||
 | 
			
		||||
func (r byPortConfig) Len() int      { return len(r) }
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -120,6 +120,52 @@ func TestUpdateGroups(t *testing.T) {
 | 
			
		|||
	assert.Equal(t, groups[2], "wheel")
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func TestUpdateDNSConfig(t *testing.T) {
 | 
			
		||||
	flags := newUpdateCommand(nil).Flags()
 | 
			
		||||
 | 
			
		||||
	// IPv4, with duplicates
 | 
			
		||||
	flags.Set("dns-add", "1.1.1.1")
 | 
			
		||||
	flags.Set("dns-add", "1.1.1.1")
 | 
			
		||||
	flags.Set("dns-add", "2.2.2.2")
 | 
			
		||||
	flags.Set("dns-rm", "3.3.3.3")
 | 
			
		||||
	flags.Set("dns-rm", "2.2.2.2")
 | 
			
		||||
	// IPv6
 | 
			
		||||
	flags.Set("dns-add", "2001:db8:abc8::1")
 | 
			
		||||
	// Invalid dns record
 | 
			
		||||
	assert.Error(t, flags.Set("dns-add", "x.y.z.w"), "x.y.z.w is not an ip address")
 | 
			
		||||
 | 
			
		||||
	// domains with duplicates
 | 
			
		||||
	flags.Set("dns-search-add", "example.com")
 | 
			
		||||
	flags.Set("dns-search-add", "example.com")
 | 
			
		||||
	flags.Set("dns-search-add", "example.org")
 | 
			
		||||
	flags.Set("dns-search-rm", "example.org")
 | 
			
		||||
	// Invalid dns search domain
 | 
			
		||||
	assert.Error(t, flags.Set("dns-search-add", "example$com"), "example$com is not a valid domain")
 | 
			
		||||
 | 
			
		||||
	flags.Set("dns-options-add", "ndots:9")
 | 
			
		||||
	flags.Set("dns-options-rm", "timeout:3")
 | 
			
		||||
 | 
			
		||||
	config := &swarm.DNSConfig{
 | 
			
		||||
		Nameservers: []string{"3.3.3.3", "5.5.5.5"},
 | 
			
		||||
		Search:      []string{"localdomain"},
 | 
			
		||||
		Options:     []string{"timeout:3"},
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	updateDNSConfig(flags, &config)
 | 
			
		||||
 | 
			
		||||
	assert.Equal(t, len(config.Nameservers), 3)
 | 
			
		||||
	assert.Equal(t, config.Nameservers[0], "1.1.1.1")
 | 
			
		||||
	assert.Equal(t, config.Nameservers[1], "2001:db8:abc8::1")
 | 
			
		||||
	assert.Equal(t, config.Nameservers[2], "5.5.5.5")
 | 
			
		||||
 | 
			
		||||
	assert.Equal(t, len(config.Search), 2)
 | 
			
		||||
	assert.Equal(t, config.Search[0], "example.com")
 | 
			
		||||
	assert.Equal(t, config.Search[1], "localdomain")
 | 
			
		||||
 | 
			
		||||
	assert.Equal(t, len(config.Options), 1)
 | 
			
		||||
	assert.Equal(t, config.Options[0], "ndots:9")
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func TestUpdateMounts(t *testing.T) {
 | 
			
		||||
	flags := newUpdateCommand(nil).Flags()
 | 
			
		||||
	flags.Set("mount-add", "type=volume,source=vol2,target=/toadd")
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -25,6 +25,14 @@ func containerSpecFromGRPC(c *swarmapi.ContainerSpec) types.ContainerSpec {
 | 
			
		|||
		TTY:      c.TTY,
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	if c.DNSConfig != nil {
 | 
			
		||||
		containerSpec.DNSConfig = &types.DNSConfig{
 | 
			
		||||
			Nameservers: c.DNSConfig.Nameservers,
 | 
			
		||||
			Search:      c.DNSConfig.Search,
 | 
			
		||||
			Options:     c.DNSConfig.Options,
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	// Mounts
 | 
			
		||||
	for _, m := range c.Mounts {
 | 
			
		||||
		mount := mounttypes.Mount{
 | 
			
		||||
| 
						 | 
				
			
			@ -81,6 +89,14 @@ func containerToGRPC(c types.ContainerSpec) (*swarmapi.ContainerSpec, error) {
 | 
			
		|||
		TTY:      c.TTY,
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	if c.DNSConfig != nil {
 | 
			
		||||
		containerSpec.DNSConfig = &swarmapi.ContainerSpec_DNSConfig{
 | 
			
		||||
			Nameservers: c.DNSConfig.Nameservers,
 | 
			
		||||
			Search:      c.DNSConfig.Search,
 | 
			
		||||
			Options:     c.DNSConfig.Options,
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	if c.StopGracePeriod != nil {
 | 
			
		||||
		containerSpec.StopGracePeriod = ptypes.DurationProto(*c.StopGracePeriod)
 | 
			
		||||
	}
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -327,6 +327,12 @@ func (c *containerConfig) hostConfig() *enginecontainer.HostConfig {
 | 
			
		|||
		GroupAdd:  c.spec().Groups,
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	if c.spec().DNSConfig != nil {
 | 
			
		||||
		hc.DNS = c.spec().DNSConfig.Nameservers
 | 
			
		||||
		hc.DNSSearch = c.spec().DNSConfig.Search
 | 
			
		||||
		hc.DNSOptions = c.spec().DNSConfig.Options
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	if c.task.LogDriver != nil {
 | 
			
		||||
		hc.LogConfig = enginecontainer.LogConfig{
 | 
			
		||||
			Type:   c.task.LogDriver.Name,
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -169,6 +169,7 @@ This section lists each version from latest to oldest.  Each listing includes a
 | 
			
		|||
* `GET /info` now returns more structured information about security options.
 | 
			
		||||
* The `HostConfig` field now includes `CpuCount` that represents the number of CPUs available for execution by the container. Windows daemon only.
 | 
			
		||||
* `POST /services/create` and `POST /services/(id or name)/update` now accept the `TTY` parameter, which allocate a pseudo-TTY in container.
 | 
			
		||||
* `POST /services/create` and `POST /services/(id or name)/update` now accept the `DNSConfig` parameter, which specifies DNS related configurations in resolver configuration file (resolv.conf) through `Nameservers`, `Search`, and `Options`.
 | 
			
		||||
 | 
			
		||||
### v1.24 API changes
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -5114,7 +5114,12 @@ image](#create-an-image) section for more details.
 | 
			
		|||
            }
 | 
			
		||||
          ],
 | 
			
		||||
          "User": "33",
 | 
			
		||||
          "TTY": false
 | 
			
		||||
          "TTY": false,
 | 
			
		||||
          "DNSConfig": {
 | 
			
		||||
            "Nameservers": ["8.8.8.8"],
 | 
			
		||||
            "Search": ["example.org"],
 | 
			
		||||
            "Options": ["timeout:3"]
 | 
			
		||||
          }
 | 
			
		||||
        },
 | 
			
		||||
        "LogDriver": {
 | 
			
		||||
          "Name": "json-file",
 | 
			
		||||
| 
						 | 
				
			
			@ -5209,6 +5214,11 @@ image](#create-an-image) section for more details.
 | 
			
		|||
                  - **Options** - key/value map of driver specific options.
 | 
			
		||||
        - **StopGracePeriod** – Amount of time to wait for the container to terminate before
 | 
			
		||||
          forcefully killing it.
 | 
			
		||||
        - **DNSConfig** – Specification for DNS related configurations in
 | 
			
		||||
          resolver configuration file (resolv.conf).
 | 
			
		||||
            - **Nameservers** – A list of the IP addresses of the name servers.
 | 
			
		||||
            - **Search** – A search list for host-name lookup.
 | 
			
		||||
            - **Options** – A list of internal resolver variables to be modified (e.g., `debug`, `ndots:3`, etc.).
 | 
			
		||||
    - **LogDriver** - Log configuration for containers created as part of the
 | 
			
		||||
      service.
 | 
			
		||||
        - **Name** - Name of the logging driver to use (`json-file`, `syslog`,
 | 
			
		||||
| 
						 | 
				
			
			@ -5394,7 +5404,12 @@ image](#create-an-image) section for more details.
 | 
			
		|||
          "Args": [
 | 
			
		||||
            "top"
 | 
			
		||||
          ],
 | 
			
		||||
          "TTY": true
 | 
			
		||||
          "TTY": true,
 | 
			
		||||
          "DNSConfig": {
 | 
			
		||||
            "Nameservers": ["8.8.8.8"],
 | 
			
		||||
            "Search": ["example.org"],
 | 
			
		||||
            "Options": ["timeout:3"]
 | 
			
		||||
          }
 | 
			
		||||
        },
 | 
			
		||||
        "Resources": {
 | 
			
		||||
          "Limits": {},
 | 
			
		||||
| 
						 | 
				
			
			@ -5460,6 +5475,11 @@ image](#create-an-image) section for more details.
 | 
			
		|||
                  - **Options** - key/value map of driver specific options
 | 
			
		||||
        - **StopGracePeriod** – Amount of time to wait for the container to terminate before
 | 
			
		||||
          forcefully killing it.
 | 
			
		||||
        - **DNSConfig** – Specification for DNS related configurations in
 | 
			
		||||
          resolver configuration file (resolv.conf).
 | 
			
		||||
            - **Nameservers** – A list of the IP addresses of the name servers.
 | 
			
		||||
            - **Search** – A search list for host-name lookup.
 | 
			
		||||
            - **Options** – A list of internal resolver variables to be modified (e.g., `debug`, `ndots:3`, etc.).
 | 
			
		||||
    - **Resources** – Resource requirements which apply to each individual container created as part
 | 
			
		||||
      of the service.
 | 
			
		||||
        - **Limits** – Define resources limits.
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -23,6 +23,9 @@ Create a new service
 | 
			
		|||
Options:
 | 
			
		||||
      --constraint value                 Placement constraints (default [])
 | 
			
		||||
      --container-label value            Service container labels (default [])
 | 
			
		||||
      --dns list                         Set custom DNS servers (default [])
 | 
			
		||||
      --dns-options list                 Set DNS options (default [])
 | 
			
		||||
      --dns-search list                  Set custom DNS search domains (default [])
 | 
			
		||||
      --endpoint-mode string             Endpoint mode (vip or dnsrr)
 | 
			
		||||
  -e, --env value                        Set environment variables (default [])
 | 
			
		||||
      --env-file value                   Read in a file of environment variables (default [])
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -26,6 +26,12 @@ Options:
 | 
			
		|||
      --constraint-rm list                 Remove a constraint (default [])
 | 
			
		||||
      --container-label-add list           Add or update a container label (default [])
 | 
			
		||||
      --container-label-rm list            Remove a container label by its key (default [])
 | 
			
		||||
      --dns-add list                       Add or update custom DNS servers (default [])
 | 
			
		||||
      --dns-options-add list               Add or update DNS options (default [])
 | 
			
		||||
      --dns-options-rm list                Remove DNS options (default [])
 | 
			
		||||
      --dns-rm list                        Remove custom DNS servers (default [])
 | 
			
		||||
      --dns-search-add list                Add or update custom DNS search domains (default [])
 | 
			
		||||
      --dns-search-rm list                 Remove DNS search domains (default [])
 | 
			
		||||
      --endpoint-mode string               Endpoint mode (vip or dnsrr)
 | 
			
		||||
      --env-add list                       Add or update an environment variable (default [])
 | 
			
		||||
      --env-rm list                        Remove an environment variable (default [])
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -789,3 +789,49 @@ func (s *DockerSwarmSuite) TestSwarmServiceTTYUpdate(c *check.C) {
 | 
			
		|||
	c.Assert(err, checker.IsNil)
 | 
			
		||||
	c.Assert(strings.TrimSpace(out), checker.Equals, "true")
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (s *DockerSwarmSuite) TestDNSConfig(c *check.C) {
 | 
			
		||||
	d := s.AddDaemon(c, true, true)
 | 
			
		||||
 | 
			
		||||
	// Create a service
 | 
			
		||||
	name := "top"
 | 
			
		||||
	_, err := d.Cmd("service", "create", "--name", name, "--dns=1.2.3.4", "--dns-search=example.com", "--dns-options=timeout:3", "busybox", "top")
 | 
			
		||||
	c.Assert(err, checker.IsNil)
 | 
			
		||||
 | 
			
		||||
	// Make sure task has been deployed.
 | 
			
		||||
	waitAndAssert(c, defaultReconciliationTimeout, d.checkActiveContainerCount, checker.Equals, 1)
 | 
			
		||||
 | 
			
		||||
	// We need to get the container id.
 | 
			
		||||
	out, err := d.Cmd("ps", "-a", "-q", "--no-trunc")
 | 
			
		||||
	c.Assert(err, checker.IsNil)
 | 
			
		||||
	id := strings.TrimSpace(out)
 | 
			
		||||
 | 
			
		||||
	// Compare against expected output.
 | 
			
		||||
	expectedOutput1 := "nameserver 1.2.3.4"
 | 
			
		||||
	expectedOutput2 := "search example.com"
 | 
			
		||||
	expectedOutput3 := "options timeout:3"
 | 
			
		||||
	out, err = d.Cmd("exec", id, "cat", "/etc/resolv.conf")
 | 
			
		||||
	c.Assert(err, checker.IsNil)
 | 
			
		||||
	c.Assert(out, checker.Contains, expectedOutput1, check.Commentf("Expected '%s', but got %q", expectedOutput1, out))
 | 
			
		||||
	c.Assert(out, checker.Contains, expectedOutput2, check.Commentf("Expected '%s', but got %q", expectedOutput2, out))
 | 
			
		||||
	c.Assert(out, checker.Contains, expectedOutput3, check.Commentf("Expected '%s', but got %q", expectedOutput3, out))
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (s *DockerSwarmSuite) TestDNSConfigUpdate(c *check.C) {
 | 
			
		||||
	d := s.AddDaemon(c, true, true)
 | 
			
		||||
 | 
			
		||||
	// Create a service
 | 
			
		||||
	name := "top"
 | 
			
		||||
	_, err := d.Cmd("service", "create", "--name", name, "busybox", "top")
 | 
			
		||||
	c.Assert(err, checker.IsNil)
 | 
			
		||||
 | 
			
		||||
	// Make sure task has been deployed.
 | 
			
		||||
	waitAndAssert(c, defaultReconciliationTimeout, d.checkActiveContainerCount, checker.Equals, 1)
 | 
			
		||||
 | 
			
		||||
	_, err = d.Cmd("service", "update", "--dns-add=1.2.3.4", "--dns-search-add=example.com", "--dns-options-add=timeout:3", name)
 | 
			
		||||
	c.Assert(err, checker.IsNil)
 | 
			
		||||
 | 
			
		||||
	out, err := d.Cmd("service", "inspect", "--format", "{{ .Spec.TaskTemplate.ContainerSpec.DNSConfig }}", name)
 | 
			
		||||
	c.Assert(err, checker.IsNil)
 | 
			
		||||
	c.Assert(strings.TrimSpace(out), checker.Equals, "{[1.2.3.4] [example.com] [timeout:3]}")
 | 
			
		||||
}
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
		Loading…
	
	Add table
		Add a link
		
	
		Reference in a new issue