mirror of
https://github.com/openfaas/faas.git
synced 2025-06-23 07:13:23 +00:00
**What** - Add the ability to specify secrets as a csv - Vendor the docker/cli/opts - Update the guide for secrets to use the `faas-cli` **Why** - Allowing the csv specification of secrets gives users more control about how those secrets are mounted into the container. This is good for things like key rotation and for developers that are building on top of OpenFaaS. Signed-off-by: Lucas Roesler <lucas.roesler@gmail.com>
174 lines
4.4 KiB
Go
174 lines
4.4 KiB
Go
package opts
|
|
|
|
import (
|
|
"encoding/csv"
|
|
"fmt"
|
|
"os"
|
|
"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
|
|
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
|
|
}
|
|
}
|
|
|
|
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
|
|
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 "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
|
|
}
|