mirror of
https://github.com/moby/moby.git
synced 2022-11-09 12:21:53 -05:00
Move ConvertVolumes to composetransform package.
Signed-off-by: Daniel Nephin <dnephin@docker.com>
This commit is contained in:
parent
c9ffd3586c
commit
732e19892d
7 changed files with 268 additions and 137 deletions
|
@ -50,6 +50,16 @@ const (
|
||||||
PropagationSlave Propagation = "slave"
|
PropagationSlave Propagation = "slave"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
// Propagations is the list of all valid mount propagations
|
||||||
|
var Propagations = []Propagation{
|
||||||
|
PropagationRPrivate,
|
||||||
|
PropagationPrivate,
|
||||||
|
PropagationRShared,
|
||||||
|
PropagationShared,
|
||||||
|
PropagationRSlave,
|
||||||
|
PropagationSlave,
|
||||||
|
}
|
||||||
|
|
||||||
// BindOptions defines options specific to mounts of type "bind".
|
// BindOptions defines options specific to mounts of type "bind".
|
||||||
type BindOptions struct {
|
type BindOptions struct {
|
||||||
Propagation Propagation `json:",omitempty"`
|
Propagation Propagation `json:",omitempty"`
|
||||||
|
|
|
@ -7,11 +7,12 @@ import (
|
||||||
"github.com/docker/docker/api/types/filters"
|
"github.com/docker/docker/api/types/filters"
|
||||||
"github.com/docker/docker/api/types/swarm"
|
"github.com/docker/docker/api/types/swarm"
|
||||||
"github.com/docker/docker/client"
|
"github.com/docker/docker/client"
|
||||||
|
"github.com/docker/docker/pkg/composetransform"
|
||||||
)
|
)
|
||||||
|
|
||||||
func getStackFilter(namespace string) filters.Args {
|
func getStackFilter(namespace string) filters.Args {
|
||||||
filter := filters.NewArgs()
|
filter := filters.NewArgs()
|
||||||
filter.Add("label", labelNamespace+"="+namespace)
|
filter.Add("label", composetransform.LabelNamespace+"="+namespace)
|
||||||
return filter
|
return filter
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -13,13 +13,12 @@ import (
|
||||||
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/container"
|
"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/api/types/swarm"
|
||||||
"github.com/docker/docker/cli"
|
"github.com/docker/docker/cli"
|
||||||
"github.com/docker/docker/cli/command"
|
"github.com/docker/docker/cli/command"
|
||||||
dockerclient "github.com/docker/docker/client"
|
dockerclient "github.com/docker/docker/client"
|
||||||
"github.com/docker/docker/opts"
|
"github.com/docker/docker/opts"
|
||||||
|
"github.com/docker/docker/pkg/composetransform"
|
||||||
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"
|
||||||
"github.com/spf13/cobra"
|
"github.com/spf13/cobra"
|
||||||
|
@ -120,10 +119,10 @@ func deployCompose(ctx context.Context, dockerCli *command.DockerCli, opts deplo
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
namespace := namespace{name: opts.namespace}
|
namespace := composetransform.NewNamespace(opts.namespace)
|
||||||
|
|
||||||
serviceNetworks := getServicesDeclaredNetworks(config.Services)
|
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 {
|
if err := validateExternalNetworks(ctx, dockerCli, externalNetworks); err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
@ -217,12 +216,12 @@ func validateExternalNetworks(
|
||||||
func createNetworks(
|
func createNetworks(
|
||||||
ctx context.Context,
|
ctx context.Context,
|
||||||
dockerCli *command.DockerCli,
|
dockerCli *command.DockerCli,
|
||||||
namespace namespace,
|
namespace composetransform.Namespace,
|
||||||
networks map[string]types.NetworkCreate,
|
networks map[string]types.NetworkCreate,
|
||||||
) error {
|
) error {
|
||||||
client := dockerCli.Client()
|
client := dockerCli.Client()
|
||||||
|
|
||||||
existingNetworks, err := getStackNetworks(ctx, client, namespace.name)
|
existingNetworks, err := getStackNetworks(ctx, client, namespace.Name())
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
@ -233,7 +232,7 @@ func createNetworks(
|
||||||
}
|
}
|
||||||
|
|
||||||
for internalName, createOpts := range networks {
|
for internalName, createOpts := range networks {
|
||||||
name := namespace.scope(internalName)
|
name := namespace.Scope(internalName)
|
||||||
if _, exists := existingNetworkMap[name]; exists {
|
if _, exists := existingNetworkMap[name]; exists {
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
|
@ -254,7 +253,7 @@ func createNetworks(
|
||||||
func convertServiceNetworks(
|
func convertServiceNetworks(
|
||||||
networks map[string]*composetypes.ServiceNetworkConfig,
|
networks map[string]*composetypes.ServiceNetworkConfig,
|
||||||
networkConfigs map[string]composetypes.NetworkConfig,
|
networkConfigs map[string]composetypes.NetworkConfig,
|
||||||
namespace namespace,
|
namespace composetransform.Namespace,
|
||||||
name string,
|
name string,
|
||||||
) ([]swarm.NetworkAttachmentConfig, error) {
|
) ([]swarm.NetworkAttachmentConfig, error) {
|
||||||
if len(networks) == 0 {
|
if len(networks) == 0 {
|
||||||
|
@ -288,128 +287,6 @@ func convertServiceNetworks(
|
||||||
return nets, nil
|
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(
|
func deployServices(
|
||||||
ctx context.Context,
|
ctx context.Context,
|
||||||
dockerCli *command.DockerCli,
|
dockerCli *command.DockerCli,
|
||||||
|
@ -519,7 +396,7 @@ func convertService(
|
||||||
return swarm.ServiceSpec{}, err
|
return swarm.ServiceSpec{}, err
|
||||||
}
|
}
|
||||||
|
|
||||||
mounts, err := convertVolumes(service.Volumes, volumes, namespace)
|
mounts, err := composetransform.ConvertVolumes(service.Volumes, volumes, namespace)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
// TODO: better error message (include service name)
|
// TODO: better error message (include service name)
|
||||||
return swarm.ServiceSpec{}, err
|
return swarm.ServiceSpec{}, err
|
||||||
|
|
|
@ -7,7 +7,8 @@ import (
|
||||||
)
|
)
|
||||||
|
|
||||||
const (
|
const (
|
||||||
labelNamespace = "com.docker.stack.namespace"
|
// LabelNamespace is the label used to track stack resources
|
||||||
|
LabelNamespace = "com.docker.stack.namespace"
|
||||||
)
|
)
|
||||||
|
|
||||||
// Namespace mangles names by prepending the name
|
// Namespace mangles names by prepending the name
|
||||||
|
@ -20,12 +21,22 @@ func (n Namespace) Scope(name string) string {
|
||||||
return n.name + "_" + name
|
return n.name + "_" + name
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Name returns the name of the namespace
|
||||||
|
func (n Namespace) Name() string {
|
||||||
|
return n.name
|
||||||
|
}
|
||||||
|
|
||||||
|
// NewNamespace returns a new Namespace for scoping of names
|
||||||
|
func NewNamespace(name string) Namespace {
|
||||||
|
return Namespace{name: name}
|
||||||
|
}
|
||||||
|
|
||||||
// AddStackLabel returns labels with the namespace label added
|
// AddStackLabel returns labels with the namespace label added
|
||||||
func AddStackLabel(namespace Namespace, labels map[string]string) map[string]string {
|
func AddStackLabel(namespace Namespace, labels map[string]string) map[string]string {
|
||||||
if labels == nil {
|
if labels == nil {
|
||||||
labels = make(map[string]string)
|
labels = make(map[string]string)
|
||||||
}
|
}
|
||||||
labels[labelNamespace] = namespace.name
|
labels[LabelNamespace] = namespace.name
|
||||||
return labels
|
return labels
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -21,7 +21,7 @@ func TestAddStackLabel(t *testing.T) {
|
||||||
actual := AddStackLabel(Namespace{name: "foo"}, labels)
|
actual := AddStackLabel(Namespace{name: "foo"}, labels)
|
||||||
expected := map[string]string{
|
expected := map[string]string{
|
||||||
"something": "labeled",
|
"something": "labeled",
|
||||||
labelNamespace: "foo",
|
LabelNamespace: "foo",
|
||||||
}
|
}
|
||||||
assert.DeepEqual(t, actual, expected)
|
assert.DeepEqual(t, actual, expected)
|
||||||
}
|
}
|
||||||
|
@ -56,7 +56,7 @@ func TestConvertNetworks(t *testing.T) {
|
||||||
expected := map[string]types.NetworkCreate{
|
expected := map[string]types.NetworkCreate{
|
||||||
"default": {
|
"default": {
|
||||||
Labels: map[string]string{
|
Labels: map[string]string{
|
||||||
labelNamespace: "foo",
|
LabelNamespace: "foo",
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
"normal": {
|
"normal": {
|
||||||
|
@ -73,7 +73,7 @@ func TestConvertNetworks(t *testing.T) {
|
||||||
"opt": "value",
|
"opt": "value",
|
||||||
},
|
},
|
||||||
Labels: map[string]string{
|
Labels: map[string]string{
|
||||||
labelNamespace: "foo",
|
LabelNamespace: "foo",
|
||||||
"something": "labeled",
|
"something": "labeled",
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
|
120
pkg/composetransform/volume.go
Normal file
120
pkg/composetransform/volume.go
Normal file
|
@ -0,0 +1,120 @@
|
||||||
|
package composetransform
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
"strings"
|
||||||
|
|
||||||
|
composetypes "github.com/aanand/compose-file/types"
|
||||||
|
"github.com/docker/docker/api/types/mount"
|
||||||
|
)
|
||||||
|
|
||||||
|
type volumes map[string]composetypes.VolumeConfig
|
||||||
|
|
||||||
|
// ConvertVolumes from compose-file types to engine api types
|
||||||
|
func ConvertVolumes(serviceVolumes []string, stackVolumes volumes, 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 volumes, 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]
|
||||||
|
}
|
||||||
|
|
||||||
|
// 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: AddStackLabel(namespace, 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 {
|
||||||
|
for _, propagation := range mount.Propagations {
|
||||||
|
if mount.Propagation(item) == propagation {
|
||||||
|
return &mount.BindOptions{Propagation: mount.Propagation(item)}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
112
pkg/composetransform/volume_test.go
Normal file
112
pkg/composetransform/volume_test.go
Normal file
|
@ -0,0 +1,112 @@
|
||||||
|
package composetransform
|
||||||
|
|
||||||
|
import (
|
||||||
|
"testing"
|
||||||
|
|
||||||
|
composetypes "github.com/aanand/compose-file/types"
|
||||||
|
"github.com/docker/docker/api/types/mount"
|
||||||
|
"github.com/docker/docker/pkg/testutil/assert"
|
||||||
|
)
|
||||||
|
|
||||||
|
func TestIsReadOnly(t *testing.T) {
|
||||||
|
assert.Equal(t, isReadOnly([]string{"foo", "bar", "ro"}), true)
|
||||||
|
assert.Equal(t, isReadOnly([]string{"ro"}), true)
|
||||||
|
assert.Equal(t, isReadOnly([]string{}), false)
|
||||||
|
assert.Equal(t, isReadOnly([]string{"foo", "rw"}), false)
|
||||||
|
assert.Equal(t, isReadOnly([]string{"foo"}), false)
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestIsNoCopy(t *testing.T) {
|
||||||
|
assert.Equal(t, isNoCopy([]string{"foo", "bar", "nocopy"}), true)
|
||||||
|
assert.Equal(t, isNoCopy([]string{"nocopy"}), true)
|
||||||
|
assert.Equal(t, isNoCopy([]string{}), false)
|
||||||
|
assert.Equal(t, isNoCopy([]string{"foo", "rw"}), false)
|
||||||
|
}
|
||||||
|
|
||||||
|
func TesTGetBindOptions(t *testing.T) {
|
||||||
|
opts := getBindOptions([]string{"slave"})
|
||||||
|
expected := &mount.BindOptions{Propagation: mount.PropagationSlave}
|
||||||
|
assert.Equal(t, opts, expected)
|
||||||
|
}
|
||||||
|
|
||||||
|
func TesTGetBindOptionsNone(t *testing.T) {
|
||||||
|
opts := getBindOptions([]string{"ro"})
|
||||||
|
assert.Equal(t, opts, nil)
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestConvertVolumeToMountNamedVolume(t *testing.T) {
|
||||||
|
stackVolumes := volumes{
|
||||||
|
"normal": composetypes.VolumeConfig{
|
||||||
|
Driver: "glusterfs",
|
||||||
|
DriverOpts: map[string]string{
|
||||||
|
"opt": "value",
|
||||||
|
},
|
||||||
|
Labels: map[string]string{
|
||||||
|
"something": "labeled",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
}
|
||||||
|
namespace := NewNamespace("foo")
|
||||||
|
expected := mount.Mount{
|
||||||
|
Type: mount.TypeVolume,
|
||||||
|
Source: "foo_normal",
|
||||||
|
Target: "/foo",
|
||||||
|
ReadOnly: true,
|
||||||
|
VolumeOptions: &mount.VolumeOptions{
|
||||||
|
Labels: map[string]string{
|
||||||
|
LabelNamespace: "foo",
|
||||||
|
"something": "labeled",
|
||||||
|
},
|
||||||
|
DriverConfig: &mount.Driver{
|
||||||
|
Name: "glusterfs",
|
||||||
|
Options: map[string]string{
|
||||||
|
"opt": "value",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
}
|
||||||
|
mount, err := convertVolumeToMount("normal:/foo:ro", stackVolumes, namespace)
|
||||||
|
assert.NilError(t, err)
|
||||||
|
assert.DeepEqual(t, mount, expected)
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestConvertVolumeToMountNamedVolumeExternal(t *testing.T) {
|
||||||
|
stackVolumes := volumes{
|
||||||
|
"outside": composetypes.VolumeConfig{
|
||||||
|
External: composetypes.External{
|
||||||
|
External: true,
|
||||||
|
Name: "special",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
}
|
||||||
|
namespace := NewNamespace("foo")
|
||||||
|
expected := mount.Mount{
|
||||||
|
Type: mount.TypeVolume,
|
||||||
|
Source: "special",
|
||||||
|
Target: "/foo",
|
||||||
|
}
|
||||||
|
mount, err := convertVolumeToMount("outside:/foo", stackVolumes, namespace)
|
||||||
|
assert.NilError(t, err)
|
||||||
|
assert.DeepEqual(t, mount, expected)
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestConvertVolumeToMountBind(t *testing.T) {
|
||||||
|
stackVolumes := volumes{}
|
||||||
|
namespace := NewNamespace("foo")
|
||||||
|
expected := mount.Mount{
|
||||||
|
Type: mount.TypeBind,
|
||||||
|
Source: "/bar",
|
||||||
|
Target: "/foo",
|
||||||
|
ReadOnly: true,
|
||||||
|
BindOptions: &mount.BindOptions{Propagation: mount.PropagationShared},
|
||||||
|
}
|
||||||
|
mount, err := convertVolumeToMount("/bar:/foo:ro,shared", stackVolumes, namespace)
|
||||||
|
assert.NilError(t, err)
|
||||||
|
assert.DeepEqual(t, mount, expected)
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestConvertVolumeToMountVolumeDoesNotExist(t *testing.T) {
|
||||||
|
namespace := NewNamespace("foo")
|
||||||
|
_, err := convertVolumeToMount("unknown:/foo:ro", volumes{}, namespace)
|
||||||
|
assert.Error(t, err, "undefined volume: unknown")
|
||||||
|
}
|
Loading…
Add table
Reference in a new issue