浏览代码

Add support for service-level 'volumes' key

Support volume driver + options
Support external volumes
Support hostname in Compose file

Signed-off-by: Aanand Prasad <aanand.prasad@gmail.com>
Aanand Prasad 8 年之前
父节点
当前提交
0884e3c868
共有 1 个文件被更改,包括 99 次插入9 次删除
  1. 99 9
      cli/command/stack/deploy.go

+ 99 - 9
cli/command/stack/deploy.go

@@ -4,6 +4,7 @@ import (
 	"fmt"
 	"fmt"
 	"io/ioutil"
 	"io/ioutil"
 	"os"
 	"os"
+	"strings"
 	"time"
 	"time"
 
 
 	"github.com/spf13/cobra"
 	"github.com/spf13/cobra"
@@ -12,6 +13,7 @@ import (
 	"github.com/aanand/compose-file/loader"
 	"github.com/aanand/compose-file/loader"
 	composetypes "github.com/aanand/compose-file/types"
 	composetypes "github.com/aanand/compose-file/types"
 	"github.com/docker/docker/api/types"
 	"github.com/docker/docker/api/types"
+	"github.com/docker/docker/api/types/mount"
 	networktypes "github.com/docker/docker/api/types/network"
 	networktypes "github.com/docker/docker/api/types/network"
 	"github.com/docker/docker/api/types/swarm"
 	"github.com/docker/docker/api/types/swarm"
 	"github.com/docker/docker/cli"
 	"github.com/docker/docker/cli"
@@ -92,7 +94,14 @@ func getConfigFile(filename string) (*composetypes.ConfigFile, error) {
 	if err != nil {
 	if err != nil {
 		return nil, err
 		return nil, err
 	}
 	}
-	return loader.ParseYAML(bytes, filename)
+	config, err := loader.ParseYAML(bytes)
+	if err != nil {
+		return nil, err
+	}
+	return &composetypes.ConfigFile{
+		Filename: filename,
+		Config:   config,
+	}, nil
 }
 }
 
 
 func createNetworks(
 func createNetworks(
@@ -114,7 +123,7 @@ func createNetworks(
 	}
 	}
 
 
 	for internalName, network := range networks {
 	for internalName, network := range networks {
-		if network.ExternalName != "" {
+		if network.External.Name != "" {
 			continue
 			continue
 		}
 		}
 
 
@@ -165,6 +174,80 @@ func convertNetworks(
 	return nets
 	return nets
 }
 }
 
 
+func convertVolumes(
+	serviceVolumes []string,
+	stackVolumes map[string]composetypes.VolumeConfig,
+	namespace string,
+) ([]mount.Mount, error) {
+	var mounts []mount.Mount
+
+	for _, volumeString := range serviceVolumes {
+		var (
+			source, target string
+			mountType      mount.Type
+			readOnly       bool
+			volumeOptions  *mount.VolumeOptions
+		)
+
+		// TODO: split Windows path mappings properly
+		parts := strings.SplitN(volumeString, ":", 3)
+
+		if len(parts) == 3 {
+			source = parts[0]
+			target = parts[1]
+			if parts[2] == "ro" {
+				readOnly = true
+			}
+		} else if len(parts) == 2 {
+			source = parts[0]
+			target = parts[1]
+		} else if len(parts) == 1 {
+			target = parts[0]
+		}
+
+		// TODO: catch Windows paths here
+		if strings.HasPrefix(source, "/") {
+			mountType = mount.TypeBind
+		} else {
+			mountType = mount.TypeVolume
+
+			stackVolume, exists := stackVolumes[source]
+			if !exists {
+				// TODO: better error message (include service name)
+				return nil, fmt.Errorf("Undefined volume: %s", source)
+			}
+
+			if stackVolume.External.Name != "" {
+				source = stackVolume.External.Name
+			} else {
+				volumeOptions = &mount.VolumeOptions{
+					Labels: stackVolume.Labels,
+				}
+
+				if stackVolume.Driver != "" {
+					volumeOptions.DriverConfig = &mount.Driver{
+						Name:    stackVolume.Driver,
+						Options: stackVolume.DriverOpts,
+					}
+				}
+
+				// TODO: remove this duplication
+				source = fmt.Sprintf("%s_%s", namespace, source)
+			}
+		}
+
+		mounts = append(mounts, mount.Mount{
+			Type:          mountType,
+			Source:        source,
+			Target:        target,
+			ReadOnly:      readOnly,
+			VolumeOptions: volumeOptions,
+		})
+	}
+
+	return mounts, nil
+}
+
 func deployServices(
 func deployServices(
 	ctx context.Context,
 	ctx context.Context,
 	dockerCli *command.DockerCli,
 	dockerCli *command.DockerCli,
@@ -255,6 +338,11 @@ func convertService(
 		return swarm.ServiceSpec{}, err
 		return swarm.ServiceSpec{}, err
 	}
 	}
 
 
+	mounts, err := convertVolumes(service.Volumes, volumes, namespace)
+	if err != nil {
+		return swarm.ServiceSpec{}, err
+	}
+
 	serviceSpec := swarm.ServiceSpec{
 	serviceSpec := swarm.ServiceSpec{
 		Annotations: swarm.Annotations{
 		Annotations: swarm.Annotations{
 			Name:   name,
 			Name:   name,
@@ -262,13 +350,15 @@ func convertService(
 		},
 		},
 		TaskTemplate: swarm.TaskSpec{
 		TaskTemplate: swarm.TaskSpec{
 			ContainerSpec: swarm.ContainerSpec{
 			ContainerSpec: swarm.ContainerSpec{
-				Image:   service.Image,
-				Command: service.Entrypoint,
-				Args:    service.Command,
-				Env:     convertEnvironment(service.Environment),
-				Labels:  getStackLabels(namespace, service.Deploy.Labels),
-				Dir:     service.WorkingDir,
-				User:    service.User,
+				Image:    service.Image,
+				Command:  service.Entrypoint,
+				Args:     service.Command,
+				Hostname: service.Hostname,
+				Env:      convertEnvironment(service.Environment),
+				Labels:   getStackLabels(namespace, service.Deploy.Labels),
+				Dir:      service.WorkingDir,
+				User:     service.User,
+				Mounts:   mounts,
 			},
 			},
 			Placement: &swarm.Placement{
 			Placement: &swarm.Placement{
 				Constraints: service.Deploy.Placement.Constraints,
 				Constraints: service.Deploy.Placement.Constraints,