github.com/kobeld/docker@v1.12.0-rc1/daemon/cluster/executor/container/container.go (about)

     1  package container
     2  
     3  import (
     4  	"errors"
     5  	"fmt"
     6  	"log"
     7  	"net"
     8  	"strings"
     9  	"time"
    10  
    11  	clustertypes "github.com/docker/docker/daemon/cluster/provider"
    12  	"github.com/docker/docker/reference"
    13  	"github.com/docker/engine-api/types"
    14  	enginecontainer "github.com/docker/engine-api/types/container"
    15  	"github.com/docker/engine-api/types/network"
    16  	"github.com/docker/swarmkit/agent/exec"
    17  	"github.com/docker/swarmkit/api"
    18  )
    19  
    20  const (
    21  	// Explictly use the kernel's default setting for CPU quota of 100ms.
    22  	// https://www.kernel.org/doc/Documentation/scheduler/sched-bwc.txt
    23  	cpuQuotaPeriod = 100 * time.Millisecond
    24  
    25  	// systemLabelPrefix represents the reserved namespace for system labels.
    26  	systemLabelPrefix = "com.docker.swarm"
    27  )
    28  
    29  // containerConfig converts task properties into docker container compatible
    30  // components.
    31  type containerConfig struct {
    32  	task                *api.Task
    33  	networksAttachments map[string]*api.NetworkAttachment
    34  }
    35  
    36  // newContainerConfig returns a validated container config. No methods should
    37  // return an error if this function returns without error.
    38  func newContainerConfig(t *api.Task) (*containerConfig, error) {
    39  	var c containerConfig
    40  	return &c, c.setTask(t)
    41  }
    42  
    43  func (c *containerConfig) setTask(t *api.Task) error {
    44  	container := t.Spec.GetContainer()
    45  	if container == nil {
    46  		return exec.ErrRuntimeUnsupported
    47  	}
    48  
    49  	if container.Image == "" {
    50  		return ErrImageRequired
    51  	}
    52  
    53  	// index the networks by name
    54  	c.networksAttachments = make(map[string]*api.NetworkAttachment, len(t.Networks))
    55  	for _, attachment := range t.Networks {
    56  		c.networksAttachments[attachment.Network.Spec.Annotations.Name] = attachment
    57  	}
    58  
    59  	c.task = t
    60  	return nil
    61  }
    62  
    63  func (c *containerConfig) endpoint() *api.Endpoint {
    64  	return c.task.Endpoint
    65  }
    66  
    67  func (c *containerConfig) spec() *api.ContainerSpec {
    68  	return c.task.Spec.GetContainer()
    69  }
    70  
    71  func (c *containerConfig) name() string {
    72  	if c.task.Annotations.Name != "" {
    73  		// if set, use the container Annotations.Name field, set in the orchestrator.
    74  		return c.task.Annotations.Name
    75  	}
    76  
    77  	// fallback to service.slot.id.
    78  	return strings.Join([]string{c.task.ServiceAnnotations.Name, fmt.Sprint(c.task.Slot), c.task.ID}, ".")
    79  }
    80  
    81  func (c *containerConfig) image() string {
    82  	raw := c.spec().Image
    83  	ref, err := reference.ParseNamed(raw)
    84  	if err != nil {
    85  		return raw
    86  	}
    87  	return reference.WithDefaultTag(ref).String()
    88  }
    89  
    90  func (c *containerConfig) volumes() map[string]struct{} {
    91  	r := make(map[string]struct{})
    92  
    93  	for _, mount := range c.spec().Mounts {
    94  		// pick off all the volume mounts.
    95  		if mount.Type != api.MountTypeVolume {
    96  			continue
    97  		}
    98  
    99  		r[fmt.Sprintf("%s:%s", mount.Target, getMountMask(&mount))] = struct{}{}
   100  	}
   101  
   102  	return r
   103  }
   104  
   105  func (c *containerConfig) config() *enginecontainer.Config {
   106  	config := &enginecontainer.Config{
   107  		Labels:     c.labels(),
   108  		User:       c.spec().User,
   109  		Env:        c.spec().Env,
   110  		WorkingDir: c.spec().Dir,
   111  		Image:      c.image(),
   112  		Volumes:    c.volumes(),
   113  	}
   114  
   115  	if len(c.spec().Command) > 0 {
   116  		// If Command is provided, we replace the whole invocation with Command
   117  		// by replacing Entrypoint and specifying Cmd. Args is ignored in this
   118  		// case.
   119  		config.Entrypoint = append(config.Entrypoint, c.spec().Command[0])
   120  		config.Cmd = append(config.Cmd, c.spec().Command[1:]...)
   121  	} else if len(c.spec().Args) > 0 {
   122  		// In this case, we assume the image has an Entrypoint and Args
   123  		// specifies the arguments for that entrypoint.
   124  		config.Cmd = c.spec().Args
   125  	}
   126  
   127  	return config
   128  }
   129  
   130  func (c *containerConfig) labels() map[string]string {
   131  	var (
   132  		system = map[string]string{
   133  			"task":         "", // mark as cluster task
   134  			"task.id":      c.task.ID,
   135  			"task.name":    fmt.Sprintf("%v.%v", c.task.ServiceAnnotations.Name, c.task.Slot),
   136  			"node.id":      c.task.NodeID,
   137  			"service.id":   c.task.ServiceID,
   138  			"service.name": c.task.ServiceAnnotations.Name,
   139  		}
   140  		labels = make(map[string]string)
   141  	)
   142  
   143  	// base labels are those defined in the spec.
   144  	for k, v := range c.spec().Labels {
   145  		labels[k] = v
   146  	}
   147  
   148  	// we then apply the overrides from the task, which may be set via the
   149  	// orchestrator.
   150  	for k, v := range c.task.Annotations.Labels {
   151  		labels[k] = v
   152  	}
   153  
   154  	// finally, we apply the system labels, which override all labels.
   155  	for k, v := range system {
   156  		labels[strings.Join([]string{systemLabelPrefix, k}, ".")] = v
   157  	}
   158  
   159  	return labels
   160  }
   161  
   162  func (c *containerConfig) bindMounts() []string {
   163  	var r []string
   164  
   165  	for _, val := range c.spec().Mounts {
   166  		mask := getMountMask(&val)
   167  		if val.Type == api.MountTypeBind {
   168  			r = append(r, fmt.Sprintf("%s:%s:%s", val.Source, val.Target, mask))
   169  		}
   170  	}
   171  
   172  	return r
   173  }
   174  
   175  func getMountMask(m *api.Mount) string {
   176  	maskOpts := []string{"ro"}
   177  	if m.Writable {
   178  		maskOpts[0] = "rw"
   179  	}
   180  
   181  	if m.BindOptions != nil {
   182  		switch m.BindOptions.Propagation {
   183  		case api.MountPropagationPrivate:
   184  			maskOpts = append(maskOpts, "private")
   185  		case api.MountPropagationRPrivate:
   186  			maskOpts = append(maskOpts, "rprivate")
   187  		case api.MountPropagationShared:
   188  			maskOpts = append(maskOpts, "shared")
   189  		case api.MountPropagationRShared:
   190  			maskOpts = append(maskOpts, "rshared")
   191  		case api.MountPropagationSlave:
   192  			maskOpts = append(maskOpts, "slave")
   193  		case api.MountPropagationRSlave:
   194  			maskOpts = append(maskOpts, "rslave")
   195  		}
   196  	}
   197  
   198  	if m.VolumeOptions != nil {
   199  		if !m.VolumeOptions.Populate {
   200  			maskOpts = append(maskOpts, "nocopy")
   201  		}
   202  	}
   203  	return strings.Join(maskOpts, ",")
   204  }
   205  
   206  func (c *containerConfig) hostConfig() *enginecontainer.HostConfig {
   207  	return &enginecontainer.HostConfig{
   208  		Resources: c.resources(),
   209  		Binds:     c.bindMounts(),
   210  	}
   211  }
   212  
   213  // This handles the case of volumes that are defined inside a service Mount
   214  func (c *containerConfig) volumeCreateRequest(mount *api.Mount) *types.VolumeCreateRequest {
   215  	var (
   216  		driverName string
   217  		driverOpts map[string]string
   218  		labels     map[string]string
   219  	)
   220  
   221  	if mount.VolumeOptions != nil && mount.VolumeOptions.DriverConfig != nil {
   222  		driverName = mount.VolumeOptions.DriverConfig.Name
   223  		driverOpts = mount.VolumeOptions.DriverConfig.Options
   224  		labels = mount.VolumeOptions.Labels
   225  	}
   226  
   227  	if mount.VolumeOptions != nil {
   228  		return &types.VolumeCreateRequest{
   229  			Name:       mount.Source,
   230  			Driver:     driverName,
   231  			DriverOpts: driverOpts,
   232  			Labels:     labels,
   233  		}
   234  	}
   235  	return nil
   236  }
   237  
   238  func (c *containerConfig) resources() enginecontainer.Resources {
   239  	resources := enginecontainer.Resources{}
   240  
   241  	// If no limits are specified let the engine use its defaults.
   242  	//
   243  	// TODO(aluzzardi): We might want to set some limits anyway otherwise
   244  	// "unlimited" tasks will step over the reservation of other tasks.
   245  	r := c.task.Spec.Resources
   246  	if r == nil || r.Limits == nil {
   247  		return resources
   248  	}
   249  
   250  	if r.Limits.MemoryBytes > 0 {
   251  		resources.Memory = r.Limits.MemoryBytes
   252  	}
   253  
   254  	if r.Limits.NanoCPUs > 0 {
   255  		// CPU Period must be set in microseconds.
   256  		resources.CPUPeriod = int64(cpuQuotaPeriod / time.Microsecond)
   257  		resources.CPUQuota = r.Limits.NanoCPUs * resources.CPUPeriod / 1e9
   258  	}
   259  
   260  	return resources
   261  }
   262  
   263  // Docker daemon supports just 1 network during container create.
   264  func (c *containerConfig) createNetworkingConfig() *network.NetworkingConfig {
   265  	var networks []*api.NetworkAttachment
   266  	if c.task.Spec.GetContainer() != nil {
   267  		networks = c.task.Networks
   268  	}
   269  
   270  	epConfig := make(map[string]*network.EndpointSettings)
   271  	if len(networks) > 0 {
   272  		epConfig[networks[0].Network.Spec.Annotations.Name] = getEndpointConfig(networks[0])
   273  	}
   274  
   275  	return &network.NetworkingConfig{EndpointsConfig: epConfig}
   276  }
   277  
   278  // TODO: Merge this function with createNetworkingConfig after daemon supports multiple networks in container create
   279  func (c *containerConfig) connectNetworkingConfig() *network.NetworkingConfig {
   280  	var networks []*api.NetworkAttachment
   281  	if c.task.Spec.GetContainer() != nil {
   282  		networks = c.task.Networks
   283  	}
   284  
   285  	// First network is used during container create. Other networks are used in "docker network connect"
   286  	if len(networks) < 2 {
   287  		return nil
   288  	}
   289  
   290  	epConfig := make(map[string]*network.EndpointSettings)
   291  	for _, na := range networks[1:] {
   292  		epConfig[na.Network.Spec.Annotations.Name] = getEndpointConfig(na)
   293  	}
   294  	return &network.NetworkingConfig{EndpointsConfig: epConfig}
   295  }
   296  
   297  func getEndpointConfig(na *api.NetworkAttachment) *network.EndpointSettings {
   298  	var ipv4, ipv6 string
   299  	for _, addr := range na.Addresses {
   300  		ip, _, err := net.ParseCIDR(addr)
   301  		if err != nil {
   302  			continue
   303  		}
   304  
   305  		if ip.To4() != nil {
   306  			ipv4 = ip.String()
   307  			continue
   308  		}
   309  
   310  		if ip.To16() != nil {
   311  			ipv6 = ip.String()
   312  		}
   313  	}
   314  
   315  	return &network.EndpointSettings{
   316  		IPAMConfig: &network.EndpointIPAMConfig{
   317  			IPv4Address: ipv4,
   318  			IPv6Address: ipv6,
   319  		},
   320  	}
   321  }
   322  
   323  func (c *containerConfig) virtualIP(networkID string) string {
   324  	if c.task.Endpoint == nil {
   325  		return ""
   326  	}
   327  
   328  	for _, eVip := range c.task.Endpoint.VirtualIPs {
   329  		// We only support IPv4 VIPs for now.
   330  		if eVip.NetworkID == networkID {
   331  			vip, _, err := net.ParseCIDR(eVip.Addr)
   332  			if err != nil {
   333  				return ""
   334  			}
   335  
   336  			return vip.String()
   337  		}
   338  	}
   339  
   340  	return ""
   341  }
   342  
   343  func (c *containerConfig) serviceConfig() *clustertypes.ServiceConfig {
   344  	if len(c.task.Networks) == 0 {
   345  		return nil
   346  	}
   347  
   348  	log.Printf("Creating service config in agent for t = %+v", c.task)
   349  	svcCfg := &clustertypes.ServiceConfig{
   350  		Name:             c.task.ServiceAnnotations.Name,
   351  		ID:               c.task.ServiceID,
   352  		VirtualAddresses: make(map[string]*clustertypes.VirtualAddress),
   353  	}
   354  
   355  	for _, na := range c.task.Networks {
   356  		svcCfg.VirtualAddresses[na.Network.ID] = &clustertypes.VirtualAddress{
   357  			// We support only IPv4 virtual IP for now.
   358  			IPv4: c.virtualIP(na.Network.ID),
   359  		}
   360  	}
   361  
   362  	if c.task.Endpoint != nil {
   363  		for _, ePort := range c.task.Endpoint.Ports {
   364  			svcCfg.ExposedPorts = append(svcCfg.ExposedPorts, &clustertypes.PortConfig{
   365  				Name:          ePort.Name,
   366  				Protocol:      int32(ePort.Protocol),
   367  				TargetPort:    ePort.TargetPort,
   368  				PublishedPort: ePort.PublishedPort,
   369  			})
   370  		}
   371  	}
   372  
   373  	return svcCfg
   374  }
   375  
   376  // networks returns a list of network names attached to the container. The
   377  // returned name can be used to lookup the corresponding network create
   378  // options.
   379  func (c *containerConfig) networks() []string {
   380  	var networks []string
   381  
   382  	for name := range c.networksAttachments {
   383  		networks = append(networks, name)
   384  	}
   385  
   386  	return networks
   387  }
   388  
   389  func (c *containerConfig) networkCreateRequest(name string) (clustertypes.NetworkCreateRequest, error) {
   390  	na, ok := c.networksAttachments[name]
   391  	if !ok {
   392  		return clustertypes.NetworkCreateRequest{}, errors.New("container: unknown network referenced")
   393  	}
   394  
   395  	options := types.NetworkCreate{
   396  		// ID:     na.Network.ID,
   397  		Driver: na.Network.DriverState.Name,
   398  		IPAM: network.IPAM{
   399  			Driver: na.Network.IPAM.Driver.Name,
   400  		},
   401  		Options:        na.Network.DriverState.Options,
   402  		CheckDuplicate: true,
   403  	}
   404  
   405  	for _, ic := range na.Network.IPAM.Configs {
   406  		c := network.IPAMConfig{
   407  			Subnet:  ic.Subnet,
   408  			IPRange: ic.Range,
   409  			Gateway: ic.Gateway,
   410  		}
   411  		options.IPAM.Config = append(options.IPAM.Config, c)
   412  	}
   413  
   414  	return clustertypes.NetworkCreateRequest{na.Network.ID, types.NetworkCreateRequest{Name: name, NetworkCreate: options}}, nil
   415  }