github.com/ssdev-go/moby@v17.12.1-ce-rc2+incompatible/daemon/cluster/executor/container/executor.go (about)

     1  package container
     2  
     3  import (
     4  	"fmt"
     5  	"sort"
     6  	"strings"
     7  	"sync"
     8  
     9  	"github.com/docker/docker/api/types"
    10  	"github.com/docker/docker/api/types/filters"
    11  	"github.com/docker/docker/api/types/network"
    12  	swarmtypes "github.com/docker/docker/api/types/swarm"
    13  	"github.com/docker/docker/daemon/cluster/controllers/plugin"
    14  	"github.com/docker/docker/daemon/cluster/convert"
    15  	executorpkg "github.com/docker/docker/daemon/cluster/executor"
    16  	clustertypes "github.com/docker/docker/daemon/cluster/provider"
    17  	networktypes "github.com/docker/libnetwork/types"
    18  	"github.com/docker/swarmkit/agent"
    19  	"github.com/docker/swarmkit/agent/exec"
    20  	"github.com/docker/swarmkit/api"
    21  	"github.com/docker/swarmkit/api/naming"
    22  	"github.com/sirupsen/logrus"
    23  	"golang.org/x/net/context"
    24  )
    25  
    26  type executor struct {
    27  	backend       executorpkg.Backend
    28  	pluginBackend plugin.Backend
    29  	dependencies  exec.DependencyManager
    30  	mutex         sync.Mutex // This mutex protects the following node field
    31  	node          *api.NodeDescription
    32  }
    33  
    34  // NewExecutor returns an executor from the docker client.
    35  func NewExecutor(b executorpkg.Backend, p plugin.Backend) exec.Executor {
    36  	return &executor{
    37  		backend:       b,
    38  		pluginBackend: p,
    39  		dependencies:  agent.NewDependencyManager(),
    40  	}
    41  }
    42  
    43  // Describe returns the underlying node description from the docker client.
    44  func (e *executor) Describe(ctx context.Context) (*api.NodeDescription, error) {
    45  	info, err := e.backend.SystemInfo()
    46  	if err != nil {
    47  		return nil, err
    48  	}
    49  
    50  	plugins := map[api.PluginDescription]struct{}{}
    51  	addPlugins := func(typ string, names []string) {
    52  		for _, name := range names {
    53  			plugins[api.PluginDescription{
    54  				Type: typ,
    55  				Name: name,
    56  			}] = struct{}{}
    57  		}
    58  	}
    59  
    60  	// add v1 plugins
    61  	addPlugins("Volume", info.Plugins.Volume)
    62  	// Add builtin driver "overlay" (the only builtin multi-host driver) to
    63  	// the plugin list by default.
    64  	addPlugins("Network", append([]string{"overlay"}, info.Plugins.Network...))
    65  	addPlugins("Authorization", info.Plugins.Authorization)
    66  	addPlugins("Log", info.Plugins.Log)
    67  
    68  	// add v2 plugins
    69  	v2Plugins, err := e.backend.PluginManager().List(filters.NewArgs())
    70  	if err == nil {
    71  		for _, plgn := range v2Plugins {
    72  			for _, typ := range plgn.Config.Interface.Types {
    73  				if typ.Prefix != "docker" || !plgn.Enabled {
    74  					continue
    75  				}
    76  				plgnTyp := typ.Capability
    77  				switch typ.Capability {
    78  				case "volumedriver":
    79  					plgnTyp = "Volume"
    80  				case "networkdriver":
    81  					plgnTyp = "Network"
    82  				case "logdriver":
    83  					plgnTyp = "Log"
    84  				}
    85  
    86  				plugins[api.PluginDescription{
    87  					Type: plgnTyp,
    88  					Name: plgn.Name,
    89  				}] = struct{}{}
    90  			}
    91  		}
    92  	}
    93  
    94  	pluginFields := make([]api.PluginDescription, 0, len(plugins))
    95  	for k := range plugins {
    96  		pluginFields = append(pluginFields, k)
    97  	}
    98  
    99  	sort.Sort(sortedPlugins(pluginFields))
   100  
   101  	// parse []string labels into a map[string]string
   102  	labels := map[string]string{}
   103  	for _, l := range info.Labels {
   104  		stringSlice := strings.SplitN(l, "=", 2)
   105  		// this will take the last value in the list for a given key
   106  		// ideally, one shouldn't assign multiple values to the same key
   107  		if len(stringSlice) > 1 {
   108  			labels[stringSlice[0]] = stringSlice[1]
   109  		}
   110  	}
   111  
   112  	description := &api.NodeDescription{
   113  		Hostname: info.Name,
   114  		Platform: &api.Platform{
   115  			Architecture: info.Architecture,
   116  			OS:           info.OSType,
   117  		},
   118  		Engine: &api.EngineDescription{
   119  			EngineVersion: info.ServerVersion,
   120  			Labels:        labels,
   121  			Plugins:       pluginFields,
   122  		},
   123  		Resources: &api.Resources{
   124  			NanoCPUs:    int64(info.NCPU) * 1e9,
   125  			MemoryBytes: info.MemTotal,
   126  			Generic:     convert.GenericResourcesToGRPC(info.GenericResources),
   127  		},
   128  	}
   129  
   130  	// Save the node information in the executor field
   131  	e.mutex.Lock()
   132  	e.node = description
   133  	e.mutex.Unlock()
   134  
   135  	return description, nil
   136  }
   137  
   138  func (e *executor) Configure(ctx context.Context, node *api.Node) error {
   139  	var ingressNA *api.NetworkAttachment
   140  	attachments := make(map[string]string)
   141  
   142  	for _, na := range node.Attachments {
   143  		if na.Network.Spec.Ingress {
   144  			ingressNA = na
   145  		}
   146  		attachments[na.Network.ID] = na.Addresses[0]
   147  	}
   148  
   149  	if (ingressNA == nil) && (node.Attachment != nil) {
   150  		ingressNA = node.Attachment
   151  		attachments[ingressNA.Network.ID] = ingressNA.Addresses[0]
   152  	}
   153  
   154  	if ingressNA == nil {
   155  		e.backend.ReleaseIngress()
   156  		return e.backend.GetAttachmentStore().ResetAttachments(attachments)
   157  	}
   158  
   159  	options := types.NetworkCreate{
   160  		Driver: ingressNA.Network.DriverState.Name,
   161  		IPAM: &network.IPAM{
   162  			Driver: ingressNA.Network.IPAM.Driver.Name,
   163  		},
   164  		Options:        ingressNA.Network.DriverState.Options,
   165  		Ingress:        true,
   166  		CheckDuplicate: true,
   167  	}
   168  
   169  	for _, ic := range ingressNA.Network.IPAM.Configs {
   170  		c := network.IPAMConfig{
   171  			Subnet:  ic.Subnet,
   172  			IPRange: ic.Range,
   173  			Gateway: ic.Gateway,
   174  		}
   175  		options.IPAM.Config = append(options.IPAM.Config, c)
   176  	}
   177  
   178  	_, err := e.backend.SetupIngress(clustertypes.NetworkCreateRequest{
   179  		ID: ingressNA.Network.ID,
   180  		NetworkCreateRequest: types.NetworkCreateRequest{
   181  			Name:          ingressNA.Network.Spec.Annotations.Name,
   182  			NetworkCreate: options,
   183  		},
   184  	}, ingressNA.Addresses[0])
   185  	if err != nil {
   186  		return err
   187  	}
   188  
   189  	return e.backend.GetAttachmentStore().ResetAttachments(attachments)
   190  }
   191  
   192  // Controller returns a docker container runner.
   193  func (e *executor) Controller(t *api.Task) (exec.Controller, error) {
   194  	dependencyGetter := agent.Restrict(e.dependencies, t)
   195  
   196  	// Get the node description from the executor field
   197  	e.mutex.Lock()
   198  	nodeDescription := e.node
   199  	e.mutex.Unlock()
   200  
   201  	if t.Spec.GetAttachment() != nil {
   202  		return newNetworkAttacherController(e.backend, t, nodeDescription, dependencyGetter)
   203  	}
   204  
   205  	var ctlr exec.Controller
   206  	switch r := t.Spec.GetRuntime().(type) {
   207  	case *api.TaskSpec_Generic:
   208  		logrus.WithFields(logrus.Fields{
   209  			"kind":     r.Generic.Kind,
   210  			"type_url": r.Generic.Payload.TypeUrl,
   211  		}).Debug("custom runtime requested")
   212  		runtimeKind, err := naming.Runtime(t.Spec)
   213  		if err != nil {
   214  			return ctlr, err
   215  		}
   216  		switch runtimeKind {
   217  		case string(swarmtypes.RuntimePlugin):
   218  			info, _ := e.backend.SystemInfo()
   219  			if !info.ExperimentalBuild {
   220  				return ctlr, fmt.Errorf("runtime type %q only supported in experimental", swarmtypes.RuntimePlugin)
   221  			}
   222  			c, err := plugin.NewController(e.pluginBackend, t)
   223  			if err != nil {
   224  				return ctlr, err
   225  			}
   226  			ctlr = c
   227  		default:
   228  			return ctlr, fmt.Errorf("unsupported runtime type: %q", runtimeKind)
   229  		}
   230  	case *api.TaskSpec_Container:
   231  		c, err := newController(e.backend, t, nodeDescription, dependencyGetter)
   232  		if err != nil {
   233  			return ctlr, err
   234  		}
   235  		ctlr = c
   236  	default:
   237  		return ctlr, fmt.Errorf("unsupported runtime: %q", r)
   238  	}
   239  
   240  	return ctlr, nil
   241  }
   242  
   243  func (e *executor) SetNetworkBootstrapKeys(keys []*api.EncryptionKey) error {
   244  	nwKeys := []*networktypes.EncryptionKey{}
   245  	for _, key := range keys {
   246  		nwKey := &networktypes.EncryptionKey{
   247  			Subsystem:   key.Subsystem,
   248  			Algorithm:   int32(key.Algorithm),
   249  			Key:         make([]byte, len(key.Key)),
   250  			LamportTime: key.LamportTime,
   251  		}
   252  		copy(nwKey.Key, key.Key)
   253  		nwKeys = append(nwKeys, nwKey)
   254  	}
   255  	e.backend.SetNetworkBootstrapKeys(nwKeys)
   256  
   257  	return nil
   258  }
   259  
   260  func (e *executor) Secrets() exec.SecretsManager {
   261  	return e.dependencies.Secrets()
   262  }
   263  
   264  func (e *executor) Configs() exec.ConfigsManager {
   265  	return e.dependencies.Configs()
   266  }
   267  
   268  type sortedPlugins []api.PluginDescription
   269  
   270  func (sp sortedPlugins) Len() int { return len(sp) }
   271  
   272  func (sp sortedPlugins) Swap(i, j int) { sp[i], sp[j] = sp[j], sp[i] }
   273  
   274  func (sp sortedPlugins) Less(i, j int) bool {
   275  	if sp[i].Type != sp[j].Type {
   276  		return sp[i].Type < sp[j].Type
   277  	}
   278  	return sp[i].Name < sp[j].Name
   279  }