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