stack deploy: handle external network when deploying
| ... | ... |
@@ -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 |
|