Add cluster volume support
- Write test for cluster volumes - Add inspect test, add update command - Add cluster volume opts to create - Add requisite and preferred topology flags - volume: move cluster bool in opts Signed-off-by: Drew Erny <derny@mirantis.com> Signed-off-by: Sebastiaan van Stijn <github@gone.nl>
This commit is contained in:
committed by
Sebastiaan van Stijn
parent
d0df532a25
commit
3455580ebc
@ -3,6 +3,7 @@ package volume
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"strings"
|
||||
|
||||
"github.com/docker/cli/cli"
|
||||
"github.com/docker/cli/cli/command"
|
||||
@ -11,6 +12,7 @@ import (
|
||||
"github.com/docker/docker/api/types/volume"
|
||||
"github.com/pkg/errors"
|
||||
"github.com/spf13/cobra"
|
||||
"github.com/spf13/pflag"
|
||||
)
|
||||
|
||||
type createOptions struct {
|
||||
@ -18,12 +20,28 @@ type createOptions struct {
|
||||
driver string
|
||||
driverOpts opts.MapOpts
|
||||
labels opts.ListOpts
|
||||
|
||||
// options for cluster volumes only
|
||||
cluster bool
|
||||
group string
|
||||
scope string
|
||||
sharing string
|
||||
availability string
|
||||
secrets opts.MapOpts
|
||||
requiredBytes opts.MemBytes
|
||||
limitBytes opts.MemBytes
|
||||
accessType string
|
||||
requisiteTopology opts.ListOpts
|
||||
preferredTopology opts.ListOpts
|
||||
}
|
||||
|
||||
func newCreateCommand(dockerCli command.Cli) *cobra.Command {
|
||||
options := createOptions{
|
||||
driverOpts: *opts.NewMapOpts(nil, nil),
|
||||
labels: opts.NewListOpts(opts.ValidateLabel),
|
||||
driverOpts: *opts.NewMapOpts(nil, nil),
|
||||
labels: opts.NewListOpts(opts.ValidateLabel),
|
||||
secrets: *opts.NewMapOpts(nil, nil),
|
||||
requisiteTopology: opts.NewListOpts(nil),
|
||||
preferredTopology: opts.NewListOpts(nil),
|
||||
}
|
||||
|
||||
cmd := &cobra.Command{
|
||||
@ -37,6 +55,7 @@ func newCreateCommand(dockerCli command.Cli) *cobra.Command {
|
||||
}
|
||||
options.name = args[0]
|
||||
}
|
||||
options.cluster = hasClusterVolumeOptionSet(cmd.Flags())
|
||||
return runCreate(dockerCli, options)
|
||||
},
|
||||
ValidArgsFunction: completion.NoComplete,
|
||||
@ -48,16 +67,110 @@ func newCreateCommand(dockerCli command.Cli) *cobra.Command {
|
||||
flags.VarP(&options.driverOpts, "opt", "o", "Set driver specific options")
|
||||
flags.Var(&options.labels, "label", "Set metadata for a volume")
|
||||
|
||||
// flags for cluster volumes only
|
||||
flags.StringVar(&options.group, "group", "", "Cluster Volume group (cluster volumes)")
|
||||
flags.StringVar(&options.scope, "scope", "single", `Cluster Volume access scope ("single"|"multi")`)
|
||||
flags.StringVar(&options.sharing, "sharing", "none", `Cluster Volume access sharing ("none"|"readonly"|"onewriter"|"all")`)
|
||||
flags.StringVar(&options.availability, "availability", "active", `Cluster Volume availability ("active"|"pause"|"drain")`)
|
||||
flags.StringVar(&options.accessType, "type", "block", `Cluster Volume access type ("mount"|"block")`)
|
||||
flags.Var(&options.secrets, "secret", "Cluster Volume secrets")
|
||||
flags.Var(&options.limitBytes, "limit-bytes", "Minimum size of the Cluster Volume in bytes")
|
||||
flags.Var(&options.requiredBytes, "required-bytes", "Maximum size of the Cluster Volume in bytes")
|
||||
flags.Var(&options.requisiteTopology, "topology-required", "A topology that the Cluster Volume must be accessible from")
|
||||
flags.Var(&options.preferredTopology, "topology-preferred", "A topology that the Cluster Volume would be preferred in")
|
||||
|
||||
return cmd
|
||||
}
|
||||
|
||||
// hasClusterVolumeOptionSet returns true if any of the cluster-specific
|
||||
// options are set.
|
||||
func hasClusterVolumeOptionSet(flags *pflag.FlagSet) bool {
|
||||
return flags.Changed("group") || flags.Changed("scope") ||
|
||||
flags.Changed("sharing") || flags.Changed("availability") ||
|
||||
flags.Changed("type") || flags.Changed("secrets") ||
|
||||
flags.Changed("limit-bytes") || flags.Changed("required-bytes")
|
||||
}
|
||||
|
||||
func runCreate(dockerCli command.Cli, options createOptions) error {
|
||||
vol, err := dockerCli.Client().VolumeCreate(context.Background(), volume.CreateOptions{
|
||||
volOpts := volume.CreateOptions{
|
||||
Driver: options.driver,
|
||||
DriverOpts: options.driverOpts.GetAll(),
|
||||
Name: options.name,
|
||||
Labels: opts.ConvertKVStringsToMap(options.labels.GetAll()),
|
||||
})
|
||||
}
|
||||
if options.cluster {
|
||||
volOpts.ClusterVolumeSpec = &volume.ClusterVolumeSpec{
|
||||
Group: options.group,
|
||||
AccessMode: &volume.AccessMode{
|
||||
Scope: volume.Scope(options.scope),
|
||||
Sharing: volume.SharingMode(options.sharing),
|
||||
},
|
||||
Availability: volume.Availability(options.availability),
|
||||
}
|
||||
|
||||
if options.accessType == "mount" {
|
||||
volOpts.ClusterVolumeSpec.AccessMode.MountVolume = &volume.TypeMount{}
|
||||
} else if options.accessType == "block" {
|
||||
volOpts.ClusterVolumeSpec.AccessMode.BlockVolume = &volume.TypeBlock{}
|
||||
}
|
||||
|
||||
vcr := &volume.CapacityRange{}
|
||||
if r := options.requiredBytes.Value(); r >= 0 {
|
||||
vcr.RequiredBytes = r
|
||||
}
|
||||
|
||||
if l := options.limitBytes.Value(); l >= 0 {
|
||||
vcr.LimitBytes = l
|
||||
}
|
||||
volOpts.ClusterVolumeSpec.CapacityRange = vcr
|
||||
|
||||
for key, secret := range options.secrets.GetAll() {
|
||||
volOpts.ClusterVolumeSpec.Secrets = append(
|
||||
volOpts.ClusterVolumeSpec.Secrets,
|
||||
volume.Secret{
|
||||
Key: key,
|
||||
Secret: secret,
|
||||
},
|
||||
)
|
||||
}
|
||||
|
||||
// TODO(dperny): ignore if no topology specified
|
||||
topology := &volume.TopologyRequirement{}
|
||||
for _, top := range options.requisiteTopology.GetAll() {
|
||||
// each topology takes the form segment=value,segment=value
|
||||
// comma-separated list of equal separated maps
|
||||
segments := map[string]string{}
|
||||
for _, segment := range strings.Split(top, ",") {
|
||||
parts := strings.SplitN(segment, "=", 2)
|
||||
// TODO(dperny): validate topology syntax
|
||||
segments[parts[0]] = parts[1]
|
||||
}
|
||||
topology.Requisite = append(
|
||||
topology.Requisite,
|
||||
volume.Topology{Segments: segments},
|
||||
)
|
||||
}
|
||||
|
||||
for _, top := range options.preferredTopology.GetAll() {
|
||||
// each topology takes the form segment=value,segment=value
|
||||
// comma-separated list of equal separated maps
|
||||
segments := map[string]string{}
|
||||
for _, segment := range strings.Split(top, ",") {
|
||||
parts := strings.SplitN(segment, "=", 2)
|
||||
// TODO(dperny): validate topology syntax
|
||||
segments[parts[0]] = parts[1]
|
||||
}
|
||||
|
||||
topology.Preferred = append(
|
||||
topology.Preferred,
|
||||
volume.Topology{Segments: segments},
|
||||
)
|
||||
}
|
||||
|
||||
volOpts.ClusterVolumeSpec.AccessibilityRequirements = topology
|
||||
}
|
||||
|
||||
vol, err := dockerCli.Client().VolumeCreate(context.Background(), volOpts)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user