Skip to content

Commit b0a5031

Browse files
committed
feature: add bind mode
Add bind mode, it contains: "ro/rw/dr/rr/z/Z/nocopy/private/rprivate/slave/rslave/shared/rshared" common mode for all bind are: "ro/rw/z/Z", "dr/rr/nocopy" just for volume, "private/rprivate/slave/rslave/shared/rshared" just for rootfs. Signed-off-by: Rudy Zhang <rudyflyzhang@gmail.com>
1 parent 079a92d commit b0a5031

7 files changed

Lines changed: 268 additions & 80 deletions

File tree

apis/server/container_bridge.go

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -309,6 +309,13 @@ func (s *Server) getContainer(ctx context.Context, rw http.ResponseWriter, req *
309309
}
310310
}
311311

312+
if len(meta.Mounts) > 0 {
313+
container.Mounts = []types.MountPoint{}
314+
for _, mp := range meta.Mounts {
315+
container.Mounts = append(container.Mounts, *mp)
316+
}
317+
}
318+
312319
return EncodeResponse(rw, http.StatusOK, container)
313320
}
314321

apis/swagger.yml

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2791,6 +2791,8 @@ definitions:
27912791
properties:
27922792
Type:
27932793
type: "string"
2794+
ID:
2795+
type: "string"
27942796
Name:
27952797
type: "string"
27962798
Source:
@@ -2803,6 +2805,12 @@ definitions:
28032805
type: "string"
28042806
RW:
28052807
type: "boolean"
2808+
CopyData:
2809+
type: "boolean"
2810+
Named:
2811+
type: "boolean"
2812+
Replace:
2813+
type: "string"
28062814
Propagation:
28072815
type: "string"
28082816

apis/types/mount_point.go

Lines changed: 20 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

cli/common_flags.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -75,7 +75,7 @@ func addCommonFlags(flagSet *pflag.FlagSet) *container {
7575

7676
flagSet.StringVar(&c.utsMode, "uts", "", "UTS namespace to use")
7777

78-
flagSet.StringSliceVarP(&c.volume, "volume", "v", nil, "Bind mount volumes to container")
78+
flagSet.StringSliceVarP(&c.volume, "volume", "v", nil, "Bind mount volumes to container, format is: [source:]<destination>[:mode], [source] can be volume or host's path, <destination> is container's path, [mode] can be \"ro/rw/dr/rr/z/Z/nocopy/private/rprivate/slave/rslave/shared/rshared\"")
7979

8080
flagSet.StringVarP(&c.workdir, "workdir", "w", "", "Set the working directory in a container")
8181

daemon/mgr/container.go

Lines changed: 167 additions & 63 deletions
Original file line numberDiff line numberDiff line change
@@ -334,11 +334,6 @@ func (mgr *ContainerManager) Create(ctx context.Context, name string, config *ty
334334
return nil, errors.Wrap(errtypes.ErrAlreadyExisted, "container name: "+name)
335335
}
336336

337-
// parse volume config
338-
if err := mgr.parseVolumes(ctx, id, config); err != nil {
339-
return nil, errors.Wrap(err, "failed to parse volume argument")
340-
}
341-
342337
// check the image existed or not, and convert image id to image ref
343338
image, err := mgr.ImageMgr.GetImage(ctx, config.Image)
344339
if err != nil {
@@ -387,6 +382,11 @@ func (mgr *ContainerManager) Create(ctx context.Context, name string, config *ty
387382
HostConfig: config.HostConfig,
388383
}
389384

385+
// parse volume config
386+
if err := mgr.parseBinds(ctx, meta); err != nil {
387+
return nil, errors.Wrap(err, "failed to parse volume argument")
388+
}
389+
390390
// set container basefs
391391
mgr.setBaseFS(ctx, meta, id)
392392

@@ -1104,92 +1104,150 @@ func (mgr *ContainerManager) execExitedAndRelease(id string, m *ctrd.Message) er
11041104
return nil
11051105
}
11061106

1107-
func (mgr *ContainerManager) parseVolumes(ctx context.Context, id string, c *types.ContainerCreateConfig) error {
1108-
logrus.Debugf("bind volumes: %v", c.HostConfig.Binds)
1107+
func (mgr *ContainerManager) bindVolume(ctx context.Context, name string, meta *ContainerMeta) (string, string, error) {
1108+
id := meta.ID
1109+
1110+
ref := ""
1111+
driver := "local"
1112+
v, err := mgr.VolumeMgr.Get(ctx, name)
1113+
if err != nil || v == nil {
1114+
opts := map[string]string{
1115+
"backend": "local",
1116+
}
1117+
if err := mgr.VolumeMgr.Create(ctx, name, meta.HostConfig.VolumeDriver, opts, nil); err != nil {
1118+
logrus.Errorf("failed to create volume: %s, err: %v", name, err)
1119+
return "", "", errors.Wrap(err, "failed to create volume")
1120+
}
1121+
} else {
1122+
ref = v.Option("ref")
1123+
driver = v.Driver()
1124+
}
1125+
1126+
option := map[string]string{}
1127+
if ref == "" {
1128+
option["ref"] = id
1129+
} else {
1130+
option["ref"] = ref + "," + id
1131+
}
1132+
if _, err := mgr.VolumeMgr.Attach(ctx, name, option); err != nil {
1133+
logrus.Errorf("failed to attach volume: %s, err: %v", name, err)
1134+
return "", "", errors.Wrap(err, "failed to attach volume")
1135+
}
1136+
1137+
mountPath, err := mgr.VolumeMgr.Path(ctx, name)
1138+
if err != nil {
1139+
logrus.Errorf("failed to get the mount path of volume: %s, err: %v", name, err)
1140+
return "", "", errors.Wrap(err, "failed to get volume mount path")
1141+
}
1142+
1143+
return mountPath, driver, nil
1144+
}
1145+
1146+
func (mgr *ContainerManager) parseBinds(ctx context.Context, meta *ContainerMeta) error {
1147+
logrus.Debugf("bind volumes: %v", meta.HostConfig.Binds)
1148+
1149+
var err error
1150+
1151+
if meta.Config.Volumes == nil {
1152+
meta.Config.Volumes = make(map[string]interface{})
1153+
}
11091154

1110-
if c.Volumes == nil {
1111-
c.Volumes = make(map[string]interface{})
1155+
if meta.Mounts == nil {
1156+
meta.Mounts = make([]*types.MountPoint, 0)
11121157
}
11131158

1159+
defer func() {
1160+
if err != nil {
1161+
if err := mgr.detachVolumes(ctx, meta); err != nil {
1162+
logrus.Errorf("failed to detach volume, err: %v", err)
1163+
}
1164+
}
1165+
}()
1166+
11141167
// TODO: parse c.HostConfig.VolumesFrom
11151168

1116-
for i, b := range c.HostConfig.Binds {
1169+
for _, b := range meta.HostConfig.Binds {
1170+
var parts []string
11171171
// TODO: when caused error, how to rollback.
1118-
arr, err := checkBind(b)
1172+
parts, err = checkBind(b)
11191173
if err != nil {
11201174
return err
11211175
}
1122-
source := ""
1123-
destination := ""
1124-
switch len(arr) {
1176+
1177+
mode := ""
1178+
mp := new(types.MountPoint)
1179+
1180+
switch len(parts) {
11251181
case 1:
1126-
source = ""
1127-
destination = arr[0]
1128-
case 2, 3:
1129-
source = arr[0]
1130-
destination = arr[1]
1182+
mp.Source = ""
1183+
mp.Destination = parts[0]
1184+
case 2:
1185+
mp.Source = parts[0]
1186+
mp.Destination = parts[1]
1187+
case 3:
1188+
mp.Source = parts[0]
1189+
mp.Destination = parts[1]
1190+
mode = parts[2]
11311191
default:
11321192
return errors.Errorf("unknown bind: %s", b)
11331193
}
11341194

1135-
if source == "" {
1136-
source = randomid.Generate()
1195+
if mp.Source == "" {
1196+
mp.Source = randomid.Generate()
11371197
}
1138-
if !path.IsAbs(source) {
1139-
ref := ""
1140-
v, err := mgr.VolumeMgr.Get(ctx, source)
1141-
if err != nil || v == nil {
1142-
opts := map[string]string{
1143-
"backend": "local",
1144-
}
1145-
if err := mgr.VolumeMgr.Create(ctx, source, c.HostConfig.VolumeDriver, opts, nil); err != nil {
1146-
logrus.Errorf("failed to create volume: %s, err: %v", source, err)
1147-
return errors.Wrap(err, "failed to create volume")
1198+
1199+
err = parseBindMode(mp, mode)
1200+
if err != nil {
1201+
logrus.Errorf("failed to parse bind mode: %s, err: %v", mode, err)
1202+
return err
1203+
}
1204+
1205+
if !path.IsAbs(mp.Source) {
1206+
// volume bind.
1207+
name := mp.Source
1208+
if _, exist := meta.Config.Volumes[name]; !exist {
1209+
mp.Name = name
1210+
mp.Source, mp.Driver, err = mgr.bindVolume(ctx, name, meta)
1211+
if err != nil {
1212+
logrus.Errorf("failed to bind volume: %s, err: %v", name, err)
1213+
return errors.Wrap(err, "failed to bind volume")
11481214
}
1149-
} else {
1150-
ref = v.Option("ref")
1215+
meta.Config.Volumes[mp.Name] = mp.Destination
11511216
}
11521217

1153-
option := map[string]string{}
1154-
if ref == "" {
1155-
option["ref"] = id
1156-
} else {
1157-
option["ref"] = ref + "," + id
1158-
}
1159-
if _, err := mgr.VolumeMgr.Attach(ctx, source, option); err != nil {
1160-
logrus.Errorf("failed to attach volume: %s, err: %v", source, err)
1161-
return errors.Wrap(err, "failed to attach volume")
1162-
}
1218+
if mp.Replace != "" {
1219+
mp.Source, err = mgr.VolumeMgr.Path(ctx, name)
1220+
if err != nil {
1221+
return err
1222+
}
11631223

1164-
mountPath, err := mgr.VolumeMgr.Path(ctx, source)
1165-
if err != nil {
1166-
logrus.Errorf("failed to get the mount path of volume: %s, err: %v", source, err)
1167-
return errors.Wrap(err, "failed to get volume mount path")
1224+
switch mp.Replace {
1225+
case "dr":
1226+
mp.Source = path.Join(mp.Source, mp.Destination)
1227+
case "rr":
1228+
mp.Source = path.Join(mp.Source, randomid.Generate())
1229+
}
1230+
1231+
mp.Name = ""
1232+
mp.Named = false
1233+
mp.Driver = ""
11681234
}
1235+
}
11691236

1170-
c.Volumes[source] = destination
1171-
source = mountPath
1172-
} else if _, err := os.Stat(source); err != nil {
1237+
if _, err = os.Stat(mp.Source); err != nil {
1238+
// host directory bind into container.
11731239
if !os.IsNotExist(err) {
1174-
return errors.Errorf("failed to stat %q: %v", source, err)
1240+
return errors.Errorf("failed to stat %q: %v", mp.Source, err)
11751241
}
11761242
// Create the host path if it doesn't exist.
1177-
if err := os.MkdirAll(source, 0755); err != nil {
1178-
return errors.Errorf("failed to mkdir %q: %v", source, err)
1243+
if err = os.MkdirAll(mp.Source, 0755); err != nil {
1244+
return errors.Errorf("failed to mkdir %q: %v", mp.Source, err)
11791245
}
11801246
}
11811247

1182-
switch len(arr) {
1183-
case 1:
1184-
b = fmt.Sprintf("%s:%s", source, arr[0])
1185-
case 2, 3:
1186-
arr[0] = source
1187-
b = strings.Join(arr, ":")
1188-
default:
1189-
}
1190-
1191-
c.HostConfig.Binds[i] = b
1248+
meta.Mounts = append(meta.Mounts, mp)
11921249
}
1250+
11931251
return nil
11941252
}
11951253

@@ -1290,3 +1348,49 @@ func checkBind(b string) ([]string, error) {
12901348

12911349
return arr, nil
12921350
}
1351+
1352+
func parseBindMode(mp *types.MountPoint, mode string) error {
1353+
mp.RW = true
1354+
mp.CopyData = true
1355+
1356+
defaultMode := 0
1357+
rwMode := 0
1358+
labelMode := 0
1359+
replaceMode := 0
1360+
copyMode := 0
1361+
propagationMode := 0
1362+
1363+
for _, m := range strings.Split(mode, ",") {
1364+
switch m {
1365+
case "":
1366+
defaultMode++
1367+
case "ro":
1368+
mp.RW = false
1369+
rwMode++
1370+
case "rw":
1371+
mp.RW = true
1372+
rwMode++
1373+
case "dr", "rr":
1374+
// direct replace mode, random replace mode
1375+
mp.Replace = m
1376+
replaceMode++
1377+
case "z", "Z":
1378+
labelMode++
1379+
case "nocopy":
1380+
mp.CopyData = false
1381+
copyMode++
1382+
case "private", "rprivate", "slave", "rslave", "shared", "rshared":
1383+
mp.Propagation = m
1384+
propagationMode++
1385+
default:
1386+
return fmt.Errorf("known bind mode: %s", mode)
1387+
}
1388+
}
1389+
1390+
if defaultMode > 2 || rwMode > 2 || replaceMode > 2 || copyMode > 2 || propagationMode > 2 {
1391+
return fmt.Errorf("invalid bind mode: %s", mode)
1392+
}
1393+
1394+
mp.Mode = mode
1395+
return nil
1396+
}

0 commit comments

Comments
 (0)