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>
This commit is contained in:
Aanand Prasad 2016-10-25 14:41:45 -07:00 committed by Daniel Nephin
parent f702b722d8
commit a9fc9b60fe
1 changed files with 99 additions and 9 deletions

View File

@ -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, Image: service.Image,
Command: service.Entrypoint, Command: service.Entrypoint,
Args: service.Command, Args: service.Command,
Env: convertEnvironment(service.Environment), Hostname: service.Hostname,
Labels: getStackLabels(namespace, service.Deploy.Labels), Env: convertEnvironment(service.Environment),
Dir: service.WorkingDir, Labels: getStackLabels(namespace, service.Deploy.Labels),
User: service.User, Dir: service.WorkingDir,
User: service.User,
Mounts: mounts,
}, },
Placement: &swarm.Placement{ Placement: &swarm.Placement{
Constraints: service.Deploy.Placement.Constraints, Constraints: service.Deploy.Placement.Constraints,