123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263 |
- // +build dfrunmount
- package instructions
- import (
- "encoding/csv"
- "strconv"
- "strings"
- "github.com/pkg/errors"
- )
- const MountTypeBind = "bind"
- const MountTypeCache = "cache"
- const MountTypeTmpfs = "tmpfs"
- const MountTypeSecret = "secret"
- const MountTypeSSH = "ssh"
- var allowedMountTypes = map[string]struct{}{
- MountTypeBind: {},
- MountTypeCache: {},
- MountTypeTmpfs: {},
- MountTypeSecret: {},
- MountTypeSSH: {},
- }
- const MountSharingShared = "shared"
- const MountSharingPrivate = "private"
- const MountSharingLocked = "locked"
- var allowedSharingTypes = map[string]struct{}{
- MountSharingShared: {},
- MountSharingPrivate: {},
- MountSharingLocked: {},
- }
- type mountsKeyT string
- var mountsKey = mountsKeyT("dockerfile/run/mounts")
- func init() {
- parseRunPreHooks = append(parseRunPreHooks, runMountPreHook)
- parseRunPostHooks = append(parseRunPostHooks, runMountPostHook)
- }
- func isValidMountType(s string) bool {
- if s == "secret" {
- if !isSecretMountsSupported() {
- return false
- }
- }
- if s == "ssh" {
- if !isSSHMountsSupported() {
- return false
- }
- }
- _, ok := allowedMountTypes[s]
- return ok
- }
- func runMountPreHook(cmd *RunCommand, req parseRequest) error {
- st := &mountState{}
- st.flag = req.flags.AddStrings("mount")
- cmd.setExternalValue(mountsKey, st)
- return nil
- }
- func runMountPostHook(cmd *RunCommand, req parseRequest) error {
- st := getMountState(cmd)
- if st == nil {
- return errors.Errorf("no mount state")
- }
- var mounts []*Mount
- for _, str := range st.flag.StringValues {
- m, err := parseMount(str)
- if err != nil {
- return err
- }
- mounts = append(mounts, m)
- }
- st.mounts = mounts
- return nil
- }
- func getMountState(cmd *RunCommand) *mountState {
- v := cmd.getExternalValue(mountsKey)
- if v == nil {
- return nil
- }
- return v.(*mountState)
- }
- func GetMounts(cmd *RunCommand) []*Mount {
- return getMountState(cmd).mounts
- }
- type mountState struct {
- flag *Flag
- mounts []*Mount
- }
- type Mount struct {
- Type string
- From string
- Source string
- Target string
- ReadOnly bool
- CacheID string
- CacheSharing string
- Required bool
- Mode *uint64
- UID *uint64
- GID *uint64
- }
- 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{Type: MountTypeBind}
- roAuto := 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
- roAuto = false
- continue
- case "readwrite", "rw":
- m.ReadOnly = false
- roAuto = false
- continue
- case "required":
- if m.Type == "secret" || m.Type == "ssh" {
- m.Required = true
- continue
- } else {
- return nil, errors.Errorf("unexpected key '%s' for mount type '%s'", key, m.Type)
- }
- }
- }
- 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 !isValidMountType(strings.ToLower(value)) {
- return nil, errors.Errorf("unsupported 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)
- }
- roAuto = false
- 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
- roAuto = false
- case "required":
- if m.Type == "secret" || m.Type == "ssh" {
- v, err := strconv.ParseBool(value)
- if err != nil {
- return nil, errors.Errorf("invalid value for %s: %s", key, value)
- }
- m.Required = v
- } else {
- return nil, errors.Errorf("unexpected key '%s' for mount type '%s'", key, m.Type)
- }
- case "id":
- m.CacheID = value
- case "sharing":
- if _, ok := allowedSharingTypes[strings.ToLower(value)]; !ok {
- return nil, errors.Errorf("unsupported sharing value %q", value)
- }
- m.CacheSharing = strings.ToLower(value)
- case "mode":
- mode, err := strconv.ParseUint(value, 8, 32)
- if err != nil {
- return nil, errors.Errorf("invalid value %s for mode", value)
- }
- m.Mode = &mode
- case "uid":
- uid, err := strconv.ParseUint(value, 10, 32)
- if err != nil {
- return nil, errors.Errorf("invalid value %s for uid", value)
- }
- m.UID = &uid
- case "gid":
- gid, err := strconv.ParseUint(value, 10, 32)
- if err != nil {
- return nil, errors.Errorf("invalid value %s for gid", value)
- }
- m.GID = &gid
- default:
- return nil, errors.Errorf("unexpected key '%s' in '%s'", key, field)
- }
- }
- fileInfoAllowed := m.Type == MountTypeSecret || m.Type == MountTypeSSH || m.Type == MountTypeCache
- if m.Mode != nil && !fileInfoAllowed {
- return nil, errors.Errorf("mode not allowed for %q type mounts", m.Type)
- }
- if m.UID != nil && !fileInfoAllowed {
- return nil, errors.Errorf("uid not allowed for %q type mounts", m.Type)
- }
- if m.GID != nil && !fileInfoAllowed {
- return nil, errors.Errorf("gid not allowed for %q type mounts", m.Type)
- }
- if roAuto {
- if m.Type == MountTypeCache || m.Type == MountTypeTmpfs {
- m.ReadOnly = false
- } else {
- m.ReadOnly = true
- }
- }
- if m.CacheSharing != "" && m.Type != MountTypeCache {
- return nil, errors.Errorf("invalid cache sharing set for %v mount", m.Type)
- }
- if m.Type == MountTypeSecret {
- if m.From != "" {
- return nil, errors.Errorf("secret mount should not have a from")
- }
- if m.CacheSharing != "" {
- return nil, errors.Errorf("secret mount should not define sharing")
- }
- if m.Source == "" && m.Target == "" && m.CacheID == "" {
- return nil, errors.Errorf("invalid secret mount. one of source, target required")
- }
- if m.Source != "" && m.CacheID != "" {
- return nil, errors.Errorf("both source and id can't be set")
- }
- }
- return m, nil
- }
|