github.com/ph/moby@v1.13.1/opts/mount.go (about) 1 package opts 2 3 import ( 4 "encoding/csv" 5 "fmt" 6 "os" 7 "strconv" 8 "strings" 9 10 mounttypes "github.com/docker/docker/api/types/mount" 11 "github.com/docker/go-units" 12 ) 13 14 // MountOpt is a Value type for parsing mounts 15 type MountOpt struct { 16 values []mounttypes.Mount 17 } 18 19 // Set a new mount value 20 func (m *MountOpt) Set(value string) error { 21 csvReader := csv.NewReader(strings.NewReader(value)) 22 fields, err := csvReader.Read() 23 if err != nil { 24 return err 25 } 26 27 mount := mounttypes.Mount{} 28 29 volumeOptions := func() *mounttypes.VolumeOptions { 30 if mount.VolumeOptions == nil { 31 mount.VolumeOptions = &mounttypes.VolumeOptions{ 32 Labels: make(map[string]string), 33 } 34 } 35 if mount.VolumeOptions.DriverConfig == nil { 36 mount.VolumeOptions.DriverConfig = &mounttypes.Driver{} 37 } 38 return mount.VolumeOptions 39 } 40 41 bindOptions := func() *mounttypes.BindOptions { 42 if mount.BindOptions == nil { 43 mount.BindOptions = new(mounttypes.BindOptions) 44 } 45 return mount.BindOptions 46 } 47 48 tmpfsOptions := func() *mounttypes.TmpfsOptions { 49 if mount.TmpfsOptions == nil { 50 mount.TmpfsOptions = new(mounttypes.TmpfsOptions) 51 } 52 return mount.TmpfsOptions 53 } 54 55 setValueOnMap := func(target map[string]string, value string) { 56 parts := strings.SplitN(value, "=", 2) 57 if len(parts) == 1 { 58 target[value] = "" 59 } else { 60 target[parts[0]] = parts[1] 61 } 62 } 63 64 mount.Type = mounttypes.TypeVolume // default to volume mounts 65 // Set writable as the default 66 for _, field := range fields { 67 parts := strings.SplitN(field, "=", 2) 68 key := strings.ToLower(parts[0]) 69 70 if len(parts) == 1 { 71 switch key { 72 case "readonly", "ro": 73 mount.ReadOnly = true 74 continue 75 case "volume-nocopy": 76 volumeOptions().NoCopy = true 77 continue 78 } 79 } 80 81 if len(parts) != 2 { 82 return fmt.Errorf("invalid field '%s' must be a key=value pair", field) 83 } 84 85 value := parts[1] 86 switch key { 87 case "type": 88 mount.Type = mounttypes.Type(strings.ToLower(value)) 89 case "source", "src": 90 mount.Source = value 91 case "target", "dst", "destination": 92 mount.Target = value 93 case "readonly", "ro": 94 mount.ReadOnly, err = strconv.ParseBool(value) 95 if err != nil { 96 return fmt.Errorf("invalid value for %s: %s", key, value) 97 } 98 case "bind-propagation": 99 bindOptions().Propagation = mounttypes.Propagation(strings.ToLower(value)) 100 case "volume-nocopy": 101 volumeOptions().NoCopy, err = strconv.ParseBool(value) 102 if err != nil { 103 return fmt.Errorf("invalid value for populate: %s", value) 104 } 105 case "volume-label": 106 setValueOnMap(volumeOptions().Labels, value) 107 case "volume-driver": 108 volumeOptions().DriverConfig.Name = value 109 case "volume-opt": 110 if volumeOptions().DriverConfig.Options == nil { 111 volumeOptions().DriverConfig.Options = make(map[string]string) 112 } 113 setValueOnMap(volumeOptions().DriverConfig.Options, value) 114 case "tmpfs-size": 115 sizeBytes, err := units.RAMInBytes(value) 116 if err != nil { 117 return fmt.Errorf("invalid value for %s: %s", key, value) 118 } 119 tmpfsOptions().SizeBytes = sizeBytes 120 case "tmpfs-mode": 121 ui64, err := strconv.ParseUint(value, 8, 32) 122 if err != nil { 123 return fmt.Errorf("invalid value for %s: %s", key, value) 124 } 125 tmpfsOptions().Mode = os.FileMode(ui64) 126 default: 127 return fmt.Errorf("unexpected key '%s' in '%s'", key, field) 128 } 129 } 130 131 if mount.Type == "" { 132 return fmt.Errorf("type is required") 133 } 134 135 if mount.Target == "" { 136 return fmt.Errorf("target is required") 137 } 138 139 if mount.VolumeOptions != nil && mount.Type != mounttypes.TypeVolume { 140 return fmt.Errorf("cannot mix 'volume-*' options with mount type '%s'", mount.Type) 141 } 142 if mount.BindOptions != nil && mount.Type != mounttypes.TypeBind { 143 return fmt.Errorf("cannot mix 'bind-*' options with mount type '%s'", mount.Type) 144 } 145 if mount.TmpfsOptions != nil && mount.Type != mounttypes.TypeTmpfs { 146 return fmt.Errorf("cannot mix 'tmpfs-*' options with mount type '%s'", mount.Type) 147 } 148 149 m.values = append(m.values, mount) 150 return nil 151 } 152 153 // Type returns the type of this option 154 func (m *MountOpt) Type() string { 155 return "mount" 156 } 157 158 // String returns a string repr of this option 159 func (m *MountOpt) String() string { 160 mounts := []string{} 161 for _, mount := range m.values { 162 repr := fmt.Sprintf("%s %s %s", mount.Type, mount.Source, mount.Target) 163 mounts = append(mounts, repr) 164 } 165 return strings.Join(mounts, ", ") 166 } 167 168 // Value returns the mounts 169 func (m *MountOpt) Value() []mounttypes.Mount { 170 return m.values 171 }