Older versions of Go do not format these comments, so we can already
reformat them ahead of time to prevent gofmt linting failing once
we update to Go 1.19 or up.
Result of:
gofmt -s -w $(find . -type f -name '*.go' | grep -v "/vendor/")
With some manual adjusting.
Signed-off-by: Sebastiaan van Stijn <github@gone.nl>
190 lines
4.9 KiB
Go
190 lines
4.9 KiB
Go
package opts
|
|
|
|
import (
|
|
"encoding/csv"
|
|
"fmt"
|
|
"os"
|
|
"path/filepath"
|
|
"strconv"
|
|
"strings"
|
|
|
|
mounttypes "github.com/docker/docker/api/types/mount"
|
|
"github.com/docker/go-units"
|
|
)
|
|
|
|
// MountOpt is a Value type for parsing mounts
|
|
type MountOpt struct {
|
|
values []mounttypes.Mount
|
|
}
|
|
|
|
// Set a new mount value
|
|
//
|
|
//nolint:gocyclo
|
|
func (m *MountOpt) Set(value string) error {
|
|
csvReader := csv.NewReader(strings.NewReader(value))
|
|
fields, err := csvReader.Read()
|
|
if err != nil {
|
|
return err
|
|
}
|
|
|
|
mount := mounttypes.Mount{}
|
|
|
|
volumeOptions := func() *mounttypes.VolumeOptions {
|
|
if mount.VolumeOptions == nil {
|
|
mount.VolumeOptions = &mounttypes.VolumeOptions{
|
|
Labels: make(map[string]string),
|
|
}
|
|
}
|
|
if mount.VolumeOptions.DriverConfig == nil {
|
|
mount.VolumeOptions.DriverConfig = &mounttypes.Driver{}
|
|
}
|
|
return mount.VolumeOptions
|
|
}
|
|
|
|
bindOptions := func() *mounttypes.BindOptions {
|
|
if mount.BindOptions == nil {
|
|
mount.BindOptions = new(mounttypes.BindOptions)
|
|
}
|
|
return mount.BindOptions
|
|
}
|
|
|
|
tmpfsOptions := func() *mounttypes.TmpfsOptions {
|
|
if mount.TmpfsOptions == nil {
|
|
mount.TmpfsOptions = new(mounttypes.TmpfsOptions)
|
|
}
|
|
return mount.TmpfsOptions
|
|
}
|
|
|
|
setValueOnMap := func(target map[string]string, value string) {
|
|
parts := strings.SplitN(value, "=", 2)
|
|
if len(parts) == 1 {
|
|
target[value] = ""
|
|
} else {
|
|
target[parts[0]] = parts[1]
|
|
}
|
|
}
|
|
|
|
mount.Type = mounttypes.TypeVolume // default to volume mounts
|
|
// Set writable as the default
|
|
for _, field := range fields {
|
|
parts := strings.SplitN(field, "=", 2)
|
|
key := strings.ToLower(parts[0])
|
|
|
|
if len(parts) == 1 {
|
|
switch key {
|
|
case "readonly", "ro":
|
|
mount.ReadOnly = true
|
|
continue
|
|
case "volume-nocopy":
|
|
volumeOptions().NoCopy = true
|
|
continue
|
|
case "bind-nonrecursive":
|
|
bindOptions().NonRecursive = true
|
|
continue
|
|
}
|
|
}
|
|
|
|
if len(parts) != 2 {
|
|
return fmt.Errorf("invalid field '%s' must be a key=value pair", field)
|
|
}
|
|
|
|
value := parts[1]
|
|
switch key {
|
|
case "type":
|
|
mount.Type = mounttypes.Type(strings.ToLower(value))
|
|
case "source", "src":
|
|
mount.Source = value
|
|
if strings.HasPrefix(value, "."+string(filepath.Separator)) || value == "." {
|
|
if abs, err := filepath.Abs(value); err == nil {
|
|
mount.Source = abs
|
|
}
|
|
}
|
|
case "target", "dst", "destination":
|
|
mount.Target = value
|
|
case "readonly", "ro":
|
|
mount.ReadOnly, err = strconv.ParseBool(value)
|
|
if err != nil {
|
|
return fmt.Errorf("invalid value for %s: %s", key, value)
|
|
}
|
|
case "consistency":
|
|
mount.Consistency = mounttypes.Consistency(strings.ToLower(value))
|
|
case "bind-propagation":
|
|
bindOptions().Propagation = mounttypes.Propagation(strings.ToLower(value))
|
|
case "bind-nonrecursive":
|
|
bindOptions().NonRecursive, err = strconv.ParseBool(value)
|
|
if err != nil {
|
|
return fmt.Errorf("invalid value for %s: %s", key, value)
|
|
}
|
|
case "volume-nocopy":
|
|
volumeOptions().NoCopy, err = strconv.ParseBool(value)
|
|
if err != nil {
|
|
return fmt.Errorf("invalid value for volume-nocopy: %s", value)
|
|
}
|
|
case "volume-label":
|
|
setValueOnMap(volumeOptions().Labels, value)
|
|
case "volume-driver":
|
|
volumeOptions().DriverConfig.Name = value
|
|
case "volume-opt":
|
|
if volumeOptions().DriverConfig.Options == nil {
|
|
volumeOptions().DriverConfig.Options = make(map[string]string)
|
|
}
|
|
setValueOnMap(volumeOptions().DriverConfig.Options, value)
|
|
case "tmpfs-size":
|
|
sizeBytes, err := units.RAMInBytes(value)
|
|
if err != nil {
|
|
return fmt.Errorf("invalid value for %s: %s", key, value)
|
|
}
|
|
tmpfsOptions().SizeBytes = sizeBytes
|
|
case "tmpfs-mode":
|
|
ui64, err := strconv.ParseUint(value, 8, 32)
|
|
if err != nil {
|
|
return fmt.Errorf("invalid value for %s: %s", key, value)
|
|
}
|
|
tmpfsOptions().Mode = os.FileMode(ui64)
|
|
default:
|
|
return fmt.Errorf("unexpected key '%s' in '%s'", key, field)
|
|
}
|
|
}
|
|
|
|
if mount.Type == "" {
|
|
return fmt.Errorf("type is required")
|
|
}
|
|
|
|
if mount.Target == "" {
|
|
return fmt.Errorf("target is required")
|
|
}
|
|
|
|
if mount.VolumeOptions != nil && mount.Type != mounttypes.TypeVolume {
|
|
return fmt.Errorf("cannot mix 'volume-*' options with mount type '%s'", mount.Type)
|
|
}
|
|
if mount.BindOptions != nil && mount.Type != mounttypes.TypeBind {
|
|
return fmt.Errorf("cannot mix 'bind-*' options with mount type '%s'", mount.Type)
|
|
}
|
|
if mount.TmpfsOptions != nil && mount.Type != mounttypes.TypeTmpfs {
|
|
return fmt.Errorf("cannot mix 'tmpfs-*' options with mount type '%s'", mount.Type)
|
|
}
|
|
|
|
m.values = append(m.values, mount)
|
|
return nil
|
|
}
|
|
|
|
// Type returns the type of this option
|
|
func (m *MountOpt) Type() string {
|
|
return "mount"
|
|
}
|
|
|
|
// String returns a string repr of this option
|
|
func (m *MountOpt) String() string {
|
|
mounts := []string{}
|
|
for _, mount := range m.values {
|
|
repr := fmt.Sprintf("%s %s %s", mount.Type, mount.Source, mount.Target)
|
|
mounts = append(mounts, repr)
|
|
}
|
|
return strings.Join(mounts, ", ")
|
|
}
|
|
|
|
// Value returns the mounts
|
|
func (m *MountOpt) Value() []mounttypes.Mount {
|
|
return m.values
|
|
}
|