Browse code

Merge pull request #28876 from vdemeester/28835-better-handling-of-external-networks

stack deploy: handle external network when deploying

Victor Vieux authored on 2016/11/30 08:00:35
Showing 3 changed files
... ...
@@ -37,7 +37,7 @@ func getServices(
37 37
 		types.ServiceListOptions{Filters: getStackFilter(namespace)})
38 38
 }
39 39
 
40
-func getNetworks(
40
+func getStackNetworks(
41 41
 	ctx context.Context,
42 42
 	apiclient client.APIClient,
43 43
 	namespace string,
... ...
@@ -22,6 +22,7 @@ import (
22 22
 	"github.com/docker/docker/cli"
23 23
 	"github.com/docker/docker/cli/command"
24 24
 	servicecmd "github.com/docker/docker/cli/command/service"
25
+	dockerclient "github.com/docker/docker/client"
25 26
 	"github.com/docker/docker/opts"
26 27
 	runconfigopts "github.com/docker/docker/runconfig/opts"
27 28
 	"github.com/docker/go-connections/nat"
... ...
@@ -123,7 +124,10 @@ func deployCompose(ctx context.Context, dockerCli *command.DockerCli, opts deplo
123 123
 
124 124
 	namespace := namespace{name: opts.namespace}
125 125
 
126
-	networks := convertNetworks(namespace, config.Networks)
126
+	networks, externalNetworks := convertNetworks(namespace, config.Networks)
127
+	if err := validateExternalNetworks(ctx, dockerCli, externalNetworks); err != nil {
128
+		return err
129
+	}
127 130
 	if err := createNetworks(ctx, dockerCli, namespace, networks); err != nil {
128 131
 		return err
129 132
 	}
... ...
@@ -179,7 +183,7 @@ func getConfigFile(filename string) (*composetypes.ConfigFile, error) {
179 179
 func convertNetworks(
180 180
 	namespace namespace,
181 181
 	networks map[string]composetypes.NetworkConfig,
182
-) map[string]types.NetworkCreate {
182
+) (map[string]types.NetworkCreate, []string) {
183 183
 	if networks == nil {
184 184
 		networks = make(map[string]composetypes.NetworkConfig)
185 185
 	}
... ...
@@ -187,10 +191,12 @@ func convertNetworks(
187 187
 	// TODO: only add default network if it's used
188 188
 	networks["default"] = composetypes.NetworkConfig{}
189 189
 
190
+	externalNetworks := []string{}
190 191
 	result := make(map[string]types.NetworkCreate)
191 192
 
192 193
 	for internalName, network := range networks {
193
-		if network.External.Name != "" {
194
+		if network.External.External {
195
+			externalNetworks = append(externalNetworks, network.External.Name)
194 196
 			continue
195 197
 		}
196 198
 
... ...
@@ -216,7 +222,29 @@ func convertNetworks(
216 216
 		result[internalName] = createOpts
217 217
 	}
218 218
 
219
-	return result
219
+	return result, externalNetworks
220
+}
221
+
222
+func validateExternalNetworks(
223
+	ctx context.Context,
224
+	dockerCli *command.DockerCli,
225
+	externalNetworks []string) error {
226
+	client := dockerCli.Client()
227
+
228
+	for _, networkName := range externalNetworks {
229
+		network, err := client.NetworkInspect(ctx, networkName)
230
+		if err != nil {
231
+			if dockerclient.IsErrNetworkNotFound(err) {
232
+				return fmt.Errorf("network %q is declared as external, but could not be found. You need to create the network before the stack is deployed (with overlay driver)", networkName)
233
+			}
234
+			return err
235
+		}
236
+		if network.Scope != "swarm" {
237
+			return fmt.Errorf("network %q is declared as external, but it is not in the right scope: %q instead of %q", networkName, network.Scope, "swarm")
238
+		}
239
+	}
240
+
241
+	return nil
220 242
 }
221 243
 
222 244
 func createNetworks(
... ...
@@ -227,7 +255,7 @@ func createNetworks(
227 227
 ) error {
228 228
 	client := dockerCli.Client()
229 229
 
230
-	existingNetworks, err := getNetworks(ctx, client, namespace.name)
230
+	existingNetworks, err := getStackNetworks(ctx, client, namespace.name)
231 231
 	if err != nil {
232 232
 		return err
233 233
 	}
... ...
@@ -258,30 +286,39 @@ func createNetworks(
258 258
 
259 259
 func convertServiceNetworks(
260 260
 	networks map[string]*composetypes.ServiceNetworkConfig,
261
+	networkConfigs map[string]composetypes.NetworkConfig,
261 262
 	namespace namespace,
262 263
 	name string,
263
-) []swarm.NetworkAttachmentConfig {
264
+) ([]swarm.NetworkAttachmentConfig, error) {
264 265
 	if len(networks) == 0 {
265 266
 		return []swarm.NetworkAttachmentConfig{
266 267
 			{
267 268
 				Target:  namespace.scope("default"),
268 269
 				Aliases: []string{name},
269 270
 			},
270
-		}
271
+		}, nil
271 272
 	}
272 273
 
273 274
 	nets := []swarm.NetworkAttachmentConfig{}
274 275
 	for networkName, network := range networks {
276
+		networkConfig, ok := networkConfigs[networkName]
277
+		if !ok {
278
+			return []swarm.NetworkAttachmentConfig{}, fmt.Errorf("invalid network: %s", networkName)
279
+		}
275 280
 		var aliases []string
276 281
 		if network != nil {
277 282
 			aliases = network.Aliases
278 283
 		}
284
+		target := namespace.scope(networkName)
285
+		if networkConfig.External.External {
286
+			target = networkName
287
+		}
279 288
 		nets = append(nets, swarm.NetworkAttachmentConfig{
280
-			Target:  namespace.scope(networkName),
289
+			Target:  target,
281 290
 			Aliases: append(aliases, name),
282 291
 		})
283 292
 	}
284
-	return nets
293
+	return nets, nil
285 294
 }
286 295
 
287 296
 func convertVolumes(
... ...
@@ -472,9 +509,10 @@ func convertServices(
472 472
 
473 473
 	services := config.Services
474 474
 	volumes := config.Volumes
475
+	networks := config.Networks
475 476
 
476 477
 	for _, service := range services {
477
-		serviceSpec, err := convertService(namespace, service, volumes)
478
+		serviceSpec, err := convertService(namespace, service, networks, volumes)
478 479
 		if err != nil {
479 480
 			return nil, err
480 481
 		}
... ...
@@ -487,6 +525,7 @@ func convertServices(
487 487
 func convertService(
488 488
 	namespace namespace,
489 489
 	service composetypes.ServiceConfig,
490
+	networkConfigs map[string]composetypes.NetworkConfig,
490 491
 	volumes map[string]composetypes.VolumeConfig,
491 492
 ) (swarm.ServiceSpec, error) {
492 493
 	name := namespace.scope(service.Name)
... ...
@@ -523,6 +562,11 @@ func convertService(
523 523
 		return swarm.ServiceSpec{}, err
524 524
 	}
525 525
 
526
+	networks, err := convertServiceNetworks(service.Networks, networkConfigs, namespace, service.Name)
527
+	if err != nil {
528
+		return swarm.ServiceSpec{}, err
529
+	}
530
+
526 531
 	serviceSpec := swarm.ServiceSpec{
527 532
 		Annotations: swarm.Annotations{
528 533
 			Name:   name,
... ...
@@ -553,7 +597,7 @@ func convertService(
553 553
 		},
554 554
 		EndpointSpec: endpoint,
555 555
 		Mode:         mode,
556
-		Networks:     convertServiceNetworks(service.Networks, namespace, service.Name),
556
+		Networks:     networks,
557 557
 		UpdateConfig: convertUpdateConfig(service.Deploy.UpdateConfig),
558 558
 	}
559 559
 
... ...
@@ -49,7 +49,7 @@ func runRemove(dockerCli *command.DockerCli, opts removeOptions) error {
49 49
 		}
50 50
 	}
51 51
 
52
-	networks, err := getNetworks(ctx, client, namespace)
52
+	networks, err := getStackNetworks(ctx, client, namespace)
53 53
 	if err != nil {
54 54
 		return err
55 55
 	}