|
@@ -13,13 +13,12 @@ import (
|
|
|
composetypes "github.com/aanand/compose-file/types"
|
|
|
"github.com/docker/docker/api/types"
|
|
|
"github.com/docker/docker/api/types/container"
|
|
|
- "github.com/docker/docker/api/types/mount"
|
|
|
- networktypes "github.com/docker/docker/api/types/network"
|
|
|
"github.com/docker/docker/api/types/swarm"
|
|
|
"github.com/docker/docker/cli"
|
|
|
"github.com/docker/docker/cli/command"
|
|
|
dockerclient "github.com/docker/docker/client"
|
|
|
"github.com/docker/docker/opts"
|
|
|
+ "github.com/docker/docker/pkg/composetransform"
|
|
|
runconfigopts "github.com/docker/docker/runconfig/opts"
|
|
|
"github.com/docker/go-connections/nat"
|
|
|
"github.com/spf13/cobra"
|
|
@@ -120,10 +119,10 @@ func deployCompose(ctx context.Context, dockerCli *command.DockerCli, opts deplo
|
|
|
return err
|
|
|
}
|
|
|
|
|
|
- namespace := namespace{name: opts.namespace}
|
|
|
+ namespace := composetransform.NewNamespace(opts.namespace)
|
|
|
|
|
|
serviceNetworks := getServicesDeclaredNetworks(config.Services)
|
|
|
- networks, externalNetworks := convertNetworks(namespace, config.Networks, serviceNetworks)
|
|
|
+ networks, externalNetworks := composetransform.ConvertNetworks(namespace, config.Networks, serviceNetworks)
|
|
|
if err := validateExternalNetworks(ctx, dockerCli, externalNetworks); err != nil {
|
|
|
return err
|
|
|
}
|
|
@@ -217,12 +216,12 @@ func validateExternalNetworks(
|
|
|
func createNetworks(
|
|
|
ctx context.Context,
|
|
|
dockerCli *command.DockerCli,
|
|
|
- namespace namespace,
|
|
|
+ namespace composetransform.Namespace,
|
|
|
networks map[string]types.NetworkCreate,
|
|
|
) error {
|
|
|
client := dockerCli.Client()
|
|
|
|
|
|
- existingNetworks, err := getStackNetworks(ctx, client, namespace.name)
|
|
|
+ existingNetworks, err := getStackNetworks(ctx, client, namespace.Name())
|
|
|
if err != nil {
|
|
|
return err
|
|
|
}
|
|
@@ -233,7 +232,7 @@ func createNetworks(
|
|
|
}
|
|
|
|
|
|
for internalName, createOpts := range networks {
|
|
|
- name := namespace.scope(internalName)
|
|
|
+ name := namespace.Scope(internalName)
|
|
|
if _, exists := existingNetworkMap[name]; exists {
|
|
|
continue
|
|
|
}
|
|
@@ -254,7 +253,7 @@ func createNetworks(
|
|
|
func convertServiceNetworks(
|
|
|
networks map[string]*composetypes.ServiceNetworkConfig,
|
|
|
networkConfigs map[string]composetypes.NetworkConfig,
|
|
|
- namespace namespace,
|
|
|
+ namespace composetransform.Namespace,
|
|
|
name string,
|
|
|
) ([]swarm.NetworkAttachmentConfig, error) {
|
|
|
if len(networks) == 0 {
|
|
@@ -288,128 +287,6 @@ func convertServiceNetworks(
|
|
|
return nets, nil
|
|
|
}
|
|
|
|
|
|
-func convertVolumes(
|
|
|
- serviceVolumes []string,
|
|
|
- stackVolumes map[string]composetypes.VolumeConfig,
|
|
|
- namespace namespace,
|
|
|
-) ([]mount.Mount, error) {
|
|
|
- var mounts []mount.Mount
|
|
|
-
|
|
|
- for _, volumeSpec := range serviceVolumes {
|
|
|
- mount, err := convertVolumeToMount(volumeSpec, stackVolumes, namespace)
|
|
|
- if err != nil {
|
|
|
- return nil, err
|
|
|
- }
|
|
|
- mounts = append(mounts, mount)
|
|
|
- }
|
|
|
- return mounts, nil
|
|
|
-}
|
|
|
-
|
|
|
-func convertVolumeToMount(
|
|
|
- volumeSpec string,
|
|
|
- stackVolumes map[string]composetypes.VolumeConfig,
|
|
|
- namespace namespace,
|
|
|
-) (mount.Mount, error) {
|
|
|
- var source, target string
|
|
|
- var mode []string
|
|
|
-
|
|
|
- // TODO: split Windows path mappings properly
|
|
|
- parts := strings.SplitN(volumeSpec, ":", 3)
|
|
|
-
|
|
|
- for _, part := range parts {
|
|
|
- if strings.TrimSpace(part) == "" {
|
|
|
- return mount.Mount{}, fmt.Errorf("invalid volume: %s", volumeSpec)
|
|
|
- }
|
|
|
- }
|
|
|
-
|
|
|
- switch len(parts) {
|
|
|
- case 3:
|
|
|
- source = parts[0]
|
|
|
- target = parts[1]
|
|
|
- mode = strings.Split(parts[2], ",")
|
|
|
- case 2:
|
|
|
- source = parts[0]
|
|
|
- target = parts[1]
|
|
|
- case 1:
|
|
|
- target = parts[0]
|
|
|
- }
|
|
|
-
|
|
|
- if source == "" {
|
|
|
- // Anonymous volume
|
|
|
- return mount.Mount{
|
|
|
- Type: mount.TypeVolume,
|
|
|
- Target: target,
|
|
|
- }, nil
|
|
|
- }
|
|
|
-
|
|
|
- // TODO: catch Windows paths here
|
|
|
- if strings.HasPrefix(source, "/") {
|
|
|
- return mount.Mount{
|
|
|
- Type: mount.TypeBind,
|
|
|
- Source: source,
|
|
|
- Target: target,
|
|
|
- ReadOnly: isReadOnly(mode),
|
|
|
- BindOptions: getBindOptions(mode),
|
|
|
- }, nil
|
|
|
- }
|
|
|
-
|
|
|
- stackVolume, exists := stackVolumes[source]
|
|
|
- if !exists {
|
|
|
- return mount.Mount{}, fmt.Errorf("undefined volume: %s", source)
|
|
|
- }
|
|
|
-
|
|
|
- var volumeOptions *mount.VolumeOptions
|
|
|
- if stackVolume.External.Name != "" {
|
|
|
- source = stackVolume.External.Name
|
|
|
- } else {
|
|
|
- volumeOptions = &mount.VolumeOptions{
|
|
|
- Labels: getStackLabels(namespace.name, stackVolume.Labels),
|
|
|
- NoCopy: isNoCopy(mode),
|
|
|
- }
|
|
|
-
|
|
|
- if stackVolume.Driver != "" {
|
|
|
- volumeOptions.DriverConfig = &mount.Driver{
|
|
|
- Name: stackVolume.Driver,
|
|
|
- Options: stackVolume.DriverOpts,
|
|
|
- }
|
|
|
- }
|
|
|
- source = namespace.scope(source)
|
|
|
- }
|
|
|
- return mount.Mount{
|
|
|
- Type: mount.TypeVolume,
|
|
|
- Source: source,
|
|
|
- Target: target,
|
|
|
- ReadOnly: isReadOnly(mode),
|
|
|
- VolumeOptions: volumeOptions,
|
|
|
- }, nil
|
|
|
-}
|
|
|
-
|
|
|
-func modeHas(mode []string, field string) bool {
|
|
|
- for _, item := range mode {
|
|
|
- if item == field {
|
|
|
- return true
|
|
|
- }
|
|
|
- }
|
|
|
- return false
|
|
|
-}
|
|
|
-
|
|
|
-func isReadOnly(mode []string) bool {
|
|
|
- return modeHas(mode, "ro")
|
|
|
-}
|
|
|
-
|
|
|
-func isNoCopy(mode []string) bool {
|
|
|
- return modeHas(mode, "nocopy")
|
|
|
-}
|
|
|
-
|
|
|
-func getBindOptions(mode []string) *mount.BindOptions {
|
|
|
- for _, item := range mode {
|
|
|
- if strings.Contains(item, "private") || strings.Contains(item, "shared") || strings.Contains(item, "slave") {
|
|
|
- return &mount.BindOptions{Propagation: mount.Propagation(item)}
|
|
|
- }
|
|
|
- }
|
|
|
- return nil
|
|
|
-}
|
|
|
-
|
|
|
func deployServices(
|
|
|
ctx context.Context,
|
|
|
dockerCli *command.DockerCli,
|
|
@@ -519,7 +396,7 @@ func convertService(
|
|
|
return swarm.ServiceSpec{}, err
|
|
|
}
|
|
|
|
|
|
- mounts, err := convertVolumes(service.Volumes, volumes, namespace)
|
|
|
+ mounts, err := composetransform.ConvertVolumes(service.Volumes, volumes, namespace)
|
|
|
if err != nil {
|
|
|
// TODO: better error message (include service name)
|
|
|
return swarm.ServiceSpec{}, err
|