github.com/demonoid81/moby@v0.0.0-20200517203328-62dd8e17c460/daemon/cluster/executor/container/executor.go (about) 1 package container // import "github.com/demonoid81/moby/daemon/cluster/executor/container" 2 3 import ( 4 "context" 5 "fmt" 6 "sort" 7 "strings" 8 "sync" 9 10 "github.com/demonoid81/moby/api/types" 11 "github.com/demonoid81/moby/api/types/filters" 12 "github.com/demonoid81/moby/api/types/network" 13 swarmtypes "github.com/demonoid81/moby/api/types/swarm" 14 "github.com/demonoid81/moby/daemon/cluster/controllers/plugin" 15 "github.com/demonoid81/moby/daemon/cluster/convert" 16 executorpkg "github.com/demonoid81/moby/daemon/cluster/executor" 17 clustertypes "github.com/demonoid81/moby/daemon/cluster/provider" 18 networktypes "github.com/demonoid81/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 27 type 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. 38 func 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. 49 func (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 140 func (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. 204 func (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 253 func (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 270 func (e *executor) Secrets() exec.SecretsManager { 271 return e.dependencies.Secrets() 272 } 273 274 func (e *executor) Configs() exec.ConfigsManager { 275 return e.dependencies.Configs() 276 } 277 278 type sortedPlugins []api.PluginDescription 279 280 func (sp sortedPlugins) Len() int { return len(sp) } 281 282 func (sp sortedPlugins) Swap(i, j int) { sp[i], sp[j] = sp[j], sp[i] } 283 284 func (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 }