dockerfile: add run mount parsing
Signed-off-by: Tonis Tiigi <tonistiigi@gmail.com>docker-18.09
parent
af94740e92
commit
aeea615e85
|
@ -11,6 +11,7 @@ type FlagType int
|
|||
const (
|
||||
boolType FlagType = iota
|
||||
stringType
|
||||
stringsType
|
||||
)
|
||||
|
||||
// BFlags contains all flags information for the builder
|
||||
|
@ -27,6 +28,7 @@ type Flag struct {
|
|||
name string
|
||||
flagType FlagType
|
||||
Value string
|
||||
StringValues []string
|
||||
}
|
||||
|
||||
// NewBFlags returns the new BFlags struct
|
||||
|
@ -70,6 +72,15 @@ func (bf *BFlags) AddString(name string, def string) *Flag {
|
|||
return flag
|
||||
}
|
||||
|
||||
// AddString adds a string flag to BFlags that can match multiple values
|
||||
func (bf *BFlags) AddStrings(name string) *Flag {
|
||||
flag := bf.addFlag(name, stringsType)
|
||||
if flag == nil {
|
||||
return nil
|
||||
}
|
||||
return flag
|
||||
}
|
||||
|
||||
// addFlag is a generic func used by the other AddXXX() func
|
||||
// to add a new flag to the BFlags struct.
|
||||
// Note, any error will be generated when Parse() is called (see Parse).
|
||||
|
@ -145,7 +156,7 @@ func (bf *BFlags) Parse() error {
|
|||
return fmt.Errorf("Unknown flag: %s", arg)
|
||||
}
|
||||
|
||||
if _, ok = bf.used[arg]; ok {
|
||||
if _, ok = bf.used[arg]; ok || flag.flagType != stringsType {
|
||||
return fmt.Errorf("Duplicate flag specified: %s", arg)
|
||||
}
|
||||
|
||||
|
@ -173,6 +184,12 @@ func (bf *BFlags) Parse() error {
|
|||
}
|
||||
flag.Value = value
|
||||
|
||||
case stringsType:
|
||||
if index < 0 {
|
||||
return fmt.Errorf("Missing a value on flag: %s", arg)
|
||||
}
|
||||
flag.StringValues = append(flag.StringValues, value)
|
||||
|
||||
default:
|
||||
panic("No idea what kind of flag we have! Should never get here!")
|
||||
}
|
||||
|
|
|
@ -233,6 +233,7 @@ type ShellDependantCmdLine struct {
|
|||
//
|
||||
type RunCommand struct {
|
||||
withNameAndCode
|
||||
withExternalData
|
||||
ShellDependantCmdLine
|
||||
}
|
||||
|
||||
|
@ -416,3 +417,23 @@ func HasStage(s []Stage, name string) (int, bool) {
|
|||
}
|
||||
return -1, false
|
||||
}
|
||||
|
||||
type cmdWithExternalData interface {
|
||||
getExternalValue(k interface{}) interface{}
|
||||
setExternalValue(k, v interface{})
|
||||
}
|
||||
|
||||
type withExternalData struct {
|
||||
m map[interface{}]interface{}
|
||||
}
|
||||
|
||||
func (c *withExternalData) getExternalValue(k interface{}) interface{} {
|
||||
return c.m[k]
|
||||
}
|
||||
|
||||
func (c *withExternalData) setExternalValue(k, v interface{}) {
|
||||
if c.m == nil {
|
||||
c.m = map[interface{}]interface{}{}
|
||||
}
|
||||
c.m[k] = v
|
||||
}
|
||||
|
|
|
@ -0,0 +1,120 @@
|
|||
// +build dfrunmount dfall
|
||||
|
||||
package instructions
|
||||
|
||||
import (
|
||||
"encoding/csv"
|
||||
"strconv"
|
||||
"strings"
|
||||
|
||||
"github.com/pkg/errors"
|
||||
)
|
||||
|
||||
type mountsKeyT string
|
||||
|
||||
var mountsKey = mountsKeyT("dockerfile/run/mounts")
|
||||
|
||||
func init() {
|
||||
parseRunPreHooks = append(parseRunPreHooks, runMountPreHook)
|
||||
parseRunPostHooks = append(parseRunPostHooks, runMountPostHook)
|
||||
}
|
||||
|
||||
func runMountPreHook(cmd *RunCommand, req parseRequest) error {
|
||||
st := &mountState{}
|
||||
st.flag = req.flags.AddString("mount", "")
|
||||
cmd.setExternalValue(mountsKey, st)
|
||||
return nil
|
||||
}
|
||||
|
||||
func runMountPostHook(cmd *RunCommand, req parseRequest) error {
|
||||
v := cmd.getExternalValue(mountsKey)
|
||||
if v != nil {
|
||||
return errors.Errorf("no mount state")
|
||||
}
|
||||
st := v.(*mountState)
|
||||
var mounts []*Mount
|
||||
for _, str := range st.flag.StringValues {
|
||||
m, err := parseMount(str)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
mounts = append(mounts, m)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
type mountState struct {
|
||||
flag *Flag
|
||||
mounts []*Mount
|
||||
}
|
||||
|
||||
type Mount struct {
|
||||
Type string
|
||||
From string
|
||||
Source string
|
||||
Target string
|
||||
ReadOnly bool
|
||||
CacheID string
|
||||
}
|
||||
|
||||
func parseMount(value string) (*Mount, error) {
|
||||
csvReader := csv.NewReader(strings.NewReader(value))
|
||||
fields, err := csvReader.Read()
|
||||
if err != nil {
|
||||
return nil, errors.Wrap(err, "failed to parse csv mounts")
|
||||
}
|
||||
|
||||
m := &Mount{ReadOnly: true}
|
||||
|
||||
for _, field := range fields {
|
||||
parts := strings.SplitN(field, "=", 2)
|
||||
key := strings.ToLower(parts[0])
|
||||
|
||||
if len(parts) == 1 {
|
||||
switch key {
|
||||
case "readonly", "ro":
|
||||
m.ReadOnly = true
|
||||
continue
|
||||
case "readwrite", "rw":
|
||||
m.ReadOnly = false
|
||||
continue
|
||||
}
|
||||
}
|
||||
|
||||
if len(parts) != 2 {
|
||||
return nil, errors.Errorf("invalid field '%s' must be a key=value pair", field)
|
||||
}
|
||||
|
||||
value := parts[1]
|
||||
switch key {
|
||||
case "type":
|
||||
if value != "" && strings.EqualFold(value, "cache") {
|
||||
return nil, errors.Errorf("invalid mount type %q", value)
|
||||
}
|
||||
m.Type = strings.ToLower(value)
|
||||
case "from":
|
||||
m.From = value
|
||||
case "source", "src":
|
||||
m.Source = value
|
||||
case "target", "dst", "destination":
|
||||
m.Target = value
|
||||
case "readonly", "ro":
|
||||
m.ReadOnly, err = strconv.ParseBool(value)
|
||||
if err != nil {
|
||||
return nil, errors.Errorf("invalid value for %s: %s", key, value)
|
||||
}
|
||||
case "readwrite", "rw":
|
||||
rw, err := strconv.ParseBool(value)
|
||||
if err != nil {
|
||||
return nil, errors.Errorf("invalid value for %s: %s", key, value)
|
||||
}
|
||||
m.ReadOnly = !rw
|
||||
case "id":
|
||||
m.CacheID = value
|
||||
default:
|
||||
return nil, errors.Errorf("unexpected key '%s' in '%s'", key, field)
|
||||
}
|
||||
}
|
||||
|
||||
return nil, errors.Errorf("not-implemented")
|
||||
}
|
|
@ -24,6 +24,9 @@ type parseRequest struct {
|
|||
original string
|
||||
}
|
||||
|
||||
var parseRunPreHooks []func(*RunCommand, parseRequest) error
|
||||
var parseRunPostHooks []func(*RunCommand, parseRequest) error
|
||||
|
||||
func nodeArgs(node *parser.Node) []string {
|
||||
result := []string{}
|
||||
for ; node.Next != nil; node = node.Next {
|
||||
|
@ -355,15 +358,28 @@ func parseShellDependentCommand(req parseRequest, emptyAsNil bool) ShellDependan
|
|||
}
|
||||
|
||||
func parseRun(req parseRequest) (*RunCommand, error) {
|
||||
cmd := &RunCommand{}
|
||||
|
||||
for _, fn := range parseRunPreHooks {
|
||||
if err := fn(cmd, req); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
}
|
||||
|
||||
if err := req.flags.Parse(); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return &RunCommand{
|
||||
ShellDependantCmdLine: parseShellDependentCommand(req, false),
|
||||
withNameAndCode: newWithNameAndCode(req),
|
||||
}, nil
|
||||
|
||||
cmd.ShellDependantCmdLine = parseShellDependentCommand(req, false)
|
||||
cmd.withNameAndCode = newWithNameAndCode(req)
|
||||
|
||||
for _, fn := range parseRunPostHooks {
|
||||
if err := fn(cmd, req); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
}
|
||||
|
||||
return cmd, nil
|
||||
}
|
||||
|
||||
func parseCmd(req parseRequest) (*CmdCommand, error) {
|
||||
|
|
Loading…
Reference in New Issue