stack deploy: handle external network when deploying

If the network is marked as external, don't use the namespace on
it. Otherwise, it's not found.

Signed-off-by: Vincent Demeester <vincent@sbr.pm>
This commit is contained in:
Vincent Demeester 2016-11-28 17:38:41 +01:00
parent a0f447d124
commit 8e63000bf3
3 changed files with 57 additions and 13 deletions

View File

@ -37,7 +37,7 @@ func getServices(
types.ServiceListOptions{Filters: getStackFilter(namespace)}) types.ServiceListOptions{Filters: getStackFilter(namespace)})
} }
func getNetworks( func getStackNetworks(
ctx context.Context, ctx context.Context,
apiclient client.APIClient, apiclient client.APIClient,
namespace string, namespace string,

View File

@ -22,6 +22,7 @@ import (
"github.com/docker/docker/cli" "github.com/docker/docker/cli"
"github.com/docker/docker/cli/command" "github.com/docker/docker/cli/command"
servicecmd "github.com/docker/docker/cli/command/service" servicecmd "github.com/docker/docker/cli/command/service"
dockerclient "github.com/docker/docker/client"
"github.com/docker/docker/opts" "github.com/docker/docker/opts"
runconfigopts "github.com/docker/docker/runconfig/opts" runconfigopts "github.com/docker/docker/runconfig/opts"
"github.com/docker/go-connections/nat" "github.com/docker/go-connections/nat"
@ -123,7 +124,10 @@ func deployCompose(ctx context.Context, dockerCli *command.DockerCli, opts deplo
namespace := namespace{name: opts.namespace} namespace := namespace{name: opts.namespace}
networks := convertNetworks(namespace, config.Networks) networks, externalNetworks := convertNetworks(namespace, config.Networks)
if err := validateExternalNetworks(ctx, dockerCli, externalNetworks); err != nil {
return err
}
if err := createNetworks(ctx, dockerCli, namespace, networks); err != nil { if err := createNetworks(ctx, dockerCli, namespace, networks); err != nil {
return err return err
} }
@ -179,7 +183,7 @@ func getConfigFile(filename string) (*composetypes.ConfigFile, error) {
func convertNetworks( func convertNetworks(
namespace namespace, namespace namespace,
networks map[string]composetypes.NetworkConfig, networks map[string]composetypes.NetworkConfig,
) map[string]types.NetworkCreate { ) (map[string]types.NetworkCreate, []string) {
if networks == nil { if networks == nil {
networks = make(map[string]composetypes.NetworkConfig) networks = make(map[string]composetypes.NetworkConfig)
} }
@ -187,10 +191,12 @@ func convertNetworks(
// TODO: only add default network if it's used // TODO: only add default network if it's used
networks["default"] = composetypes.NetworkConfig{} networks["default"] = composetypes.NetworkConfig{}
externalNetworks := []string{}
result := make(map[string]types.NetworkCreate) result := make(map[string]types.NetworkCreate)
for internalName, network := range networks { for internalName, network := range networks {
if network.External.Name != "" { if network.External.External {
externalNetworks = append(externalNetworks, network.External.Name)
continue continue
} }
@ -216,7 +222,29 @@ func convertNetworks(
result[internalName] = createOpts result[internalName] = createOpts
} }
return result return result, externalNetworks
}
func validateExternalNetworks(
ctx context.Context,
dockerCli *command.DockerCli,
externalNetworks []string) error {
client := dockerCli.Client()
for _, networkName := range externalNetworks {
network, err := client.NetworkInspect(ctx, networkName)
if err != nil {
if dockerclient.IsErrNetworkNotFound(err) {
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)
}
return err
}
if network.Scope != "swarm" {
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")
}
}
return nil
} }
func createNetworks( func createNetworks(
@ -227,7 +255,7 @@ func createNetworks(
) error { ) error {
client := dockerCli.Client() client := dockerCli.Client()
existingNetworks, err := getNetworks(ctx, client, namespace.name) existingNetworks, err := getStackNetworks(ctx, client, namespace.name)
if err != nil { if err != nil {
return err return err
} }
@ -258,30 +286,39 @@ func createNetworks(
func convertServiceNetworks( func convertServiceNetworks(
networks map[string]*composetypes.ServiceNetworkConfig, networks map[string]*composetypes.ServiceNetworkConfig,
networkConfigs map[string]composetypes.NetworkConfig,
namespace namespace, namespace namespace,
name string, name string,
) []swarm.NetworkAttachmentConfig { ) ([]swarm.NetworkAttachmentConfig, error) {
if len(networks) == 0 { if len(networks) == 0 {
return []swarm.NetworkAttachmentConfig{ return []swarm.NetworkAttachmentConfig{
{ {
Target: namespace.scope("default"), Target: namespace.scope("default"),
Aliases: []string{name}, Aliases: []string{name},
}, },
} }, nil
} }
nets := []swarm.NetworkAttachmentConfig{} nets := []swarm.NetworkAttachmentConfig{}
for networkName, network := range networks { for networkName, network := range networks {
networkConfig, ok := networkConfigs[networkName]
if !ok {
return []swarm.NetworkAttachmentConfig{}, fmt.Errorf("invalid network: %s", networkName)
}
var aliases []string var aliases []string
if network != nil { if network != nil {
aliases = network.Aliases aliases = network.Aliases
} }
target := namespace.scope(networkName)
if networkConfig.External.External {
target = networkName
}
nets = append(nets, swarm.NetworkAttachmentConfig{ nets = append(nets, swarm.NetworkAttachmentConfig{
Target: namespace.scope(networkName), Target: target,
Aliases: append(aliases, name), Aliases: append(aliases, name),
}) })
} }
return nets return nets, nil
} }
func convertVolumes( func convertVolumes(
@ -472,9 +509,10 @@ func convertServices(
services := config.Services services := config.Services
volumes := config.Volumes volumes := config.Volumes
networks := config.Networks
for _, service := range services { for _, service := range services {
serviceSpec, err := convertService(namespace, service, volumes) serviceSpec, err := convertService(namespace, service, networks, volumes)
if err != nil { if err != nil {
return nil, err return nil, err
} }
@ -487,6 +525,7 @@ func convertServices(
func convertService( func convertService(
namespace namespace, namespace namespace,
service composetypes.ServiceConfig, service composetypes.ServiceConfig,
networkConfigs map[string]composetypes.NetworkConfig,
volumes map[string]composetypes.VolumeConfig, volumes map[string]composetypes.VolumeConfig,
) (swarm.ServiceSpec, error) { ) (swarm.ServiceSpec, error) {
name := namespace.scope(service.Name) name := namespace.scope(service.Name)
@ -523,6 +562,11 @@ func convertService(
return swarm.ServiceSpec{}, err return swarm.ServiceSpec{}, err
} }
networks, err := convertServiceNetworks(service.Networks, networkConfigs, namespace, service.Name)
if err != nil {
return swarm.ServiceSpec{}, err
}
serviceSpec := swarm.ServiceSpec{ serviceSpec := swarm.ServiceSpec{
Annotations: swarm.Annotations{ Annotations: swarm.Annotations{
Name: name, Name: name,
@ -553,7 +597,7 @@ func convertService(
}, },
EndpointSpec: endpoint, EndpointSpec: endpoint,
Mode: mode, Mode: mode,
Networks: convertServiceNetworks(service.Networks, namespace, service.Name), Networks: networks,
UpdateConfig: convertUpdateConfig(service.Deploy.UpdateConfig), UpdateConfig: convertUpdateConfig(service.Deploy.UpdateConfig),
} }

View File

@ -49,7 +49,7 @@ func runRemove(dockerCli *command.DockerCli, opts removeOptions) error {
} }
} }
networks, err := getNetworks(ctx, client, namespace) networks, err := getStackNetworks(ctx, client, namespace)
if err != nil { if err != nil {
return err return err
} }