Skip to content

Commit d70df79

Browse files
committed
feature: add volumes-from for container
Add volumes-from for container, container can use the volumes of another containers. The format is "<containerID>[:mode]" Signed-off-by: Rudy Zhang <[email protected]>
1 parent 9ed2a9a commit d70df79

File tree

6 files changed

+248
-94
lines changed

6 files changed

+248
-94
lines changed

cli/common_flags.go

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -80,6 +80,7 @@ func addCommonFlags(flagSet *pflag.FlagSet) *container {
8080
flagSet.StringVar(&c.utsMode, "uts", "", "UTS namespace to use")
8181

8282
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\"")
83+
flagSet.StringSliceVar(&c.volumesFrom, "volumes-from", nil, "set volumes from other containers, format is <container>[:mode]")
8384

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

cli/container.go

Lines changed: 15 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -10,17 +10,18 @@ import (
1010
)
1111

1212
type container struct {
13-
labels []string
14-
name string
15-
tty bool
16-
volume []string
17-
runtime string
18-
env []string
19-
entrypoint string
20-
workdir string
21-
user string
22-
groupAdd []string
23-
hostname string
13+
labels []string
14+
name string
15+
tty bool
16+
volume []string
17+
volumesFrom []string
18+
runtime string
19+
env []string
20+
entrypoint string
21+
workdir string
22+
user string
23+
groupAdd []string
24+
hostname string
2425

2526
blkioWeight uint16
2627
blkioWeightDevice WeightDevice
@@ -186,8 +187,9 @@ func (c *container) config() (*types.ContainerCreateConfig, error) {
186187
},
187188

188189
HostConfig: &types.HostConfig{
189-
Binds: c.volume,
190-
Runtime: c.runtime,
190+
Binds: c.volume,
191+
VolumesFrom: c.volumesFrom,
192+
Runtime: c.runtime,
191193
Resources: types.Resources{
192194
// cpu
193195
CPUShares: c.cpushare,

daemon/mgr/container.go

Lines changed: 47 additions & 78 deletions
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,7 @@ import (
2222
"github.com/alibaba/pouch/pkg/collect"
2323
"github.com/alibaba/pouch/pkg/errtypes"
2424
"github.com/alibaba/pouch/pkg/meta"
25+
"github.com/alibaba/pouch/pkg/opts"
2526
"github.com/alibaba/pouch/pkg/quota"
2627
"github.com/alibaba/pouch/pkg/randomid"
2728
"github.com/alibaba/pouch/pkg/reference"
@@ -1447,12 +1448,54 @@ func (mgr *ContainerManager) parseBinds(ctx context.Context, meta *ContainerMeta
14471448
}
14481449
}()
14491450

1450-
// TODO: parse c.HostConfig.VolumesFrom
1451+
for _, v := range meta.HostConfig.VolumesFrom {
1452+
var containerID, mode string
1453+
containerID, mode, err = opts.ParseVolumesFrom(v)
1454+
if err != nil {
1455+
return err
1456+
}
1457+
1458+
var oldMeta *ContainerMeta
1459+
oldMeta, err = mgr.Get(ctx, containerID)
1460+
if err != nil {
1461+
return err
1462+
}
1463+
1464+
for _, oldMountPoint := range oldMeta.Mounts {
1465+
mp := &types.MountPoint{
1466+
Name: oldMountPoint.Name,
1467+
Source: oldMountPoint.Source,
1468+
Destination: oldMountPoint.Destination,
1469+
Driver: oldMountPoint.Driver,
1470+
Named: oldMountPoint.Named,
1471+
RW: oldMountPoint.RW,
1472+
Propagation: oldMountPoint.Propagation,
1473+
}
1474+
1475+
if _, exist := meta.Config.Volumes[oldMountPoint.Name]; !exist {
1476+
mp.Name = oldMountPoint.Name
1477+
mp.Source, mp.Driver, err = mgr.bindVolume(ctx, oldMountPoint.Name, meta)
1478+
if err != nil {
1479+
logrus.Errorf("failed to bind volume: %s, err: %v", oldMountPoint.Name, err)
1480+
return errors.Wrap(err, "failed to bind volume")
1481+
}
1482+
meta.Config.Volumes[mp.Name] = oldMountPoint.Destination
1483+
}
1484+
1485+
err = opts.ParseBindMode(mp, mode)
1486+
if err != nil {
1487+
logrus.Errorf("failed to parse volumes-from mode: %s, err: %v", mode, err)
1488+
return err
1489+
}
1490+
1491+
meta.Mounts = append(meta.Mounts, mp)
1492+
}
1493+
1494+
}
14511495

14521496
for _, b := range meta.HostConfig.Binds {
14531497
var parts []string
1454-
// TODO: when caused error, how to rollback.
1455-
parts, err = checkBind(b)
1498+
parts, err = opts.CheckBind(b)
14561499
if err != nil {
14571500
return err
14581501
}
@@ -1479,7 +1522,7 @@ func (mgr *ContainerManager) parseBinds(ctx context.Context, meta *ContainerMeta
14791522
mp.Source = randomid.Generate()
14801523
}
14811524

1482-
err = parseBindMode(mp, mode)
1525+
err = opts.ParseBindMode(mp, mode)
14831526
if err != nil {
14841527
logrus.Errorf("failed to parse bind mode: %s, err: %v", mode, err)
14851528
return err
@@ -1697,77 +1740,3 @@ func (mgr *ContainerManager) setBaseFS(ctx context.Context, meta *ContainerMeta,
16971740
// io.containerd.runtime.v1.linux as a const used by runc
16981741
meta.BaseFS = filepath.Join(mgr.Config.HomeDir, "containerd/state", "io.containerd.runtime.v1.linux", namespaces.Default, info.Name, "rootfs")
16991742
}
1700-
1701-
func checkBind(b string) ([]string, error) {
1702-
if strings.Count(b, ":") > 2 {
1703-
return nil, fmt.Errorf("unknown volume bind: %s", b)
1704-
}
1705-
1706-
arr := strings.SplitN(b, ":", 3)
1707-
switch len(arr) {
1708-
case 1:
1709-
if arr[0] == "" {
1710-
return nil, fmt.Errorf("unknown volume bind: %s", b)
1711-
}
1712-
if arr[0][:1] != "/" {
1713-
return nil, fmt.Errorf("invalid bind path: %s", arr[0])
1714-
}
1715-
case 2, 3:
1716-
if arr[1] == "" {
1717-
return nil, fmt.Errorf("unknown volume bind: %s", b)
1718-
}
1719-
if arr[1][:1] != "/" {
1720-
return nil, fmt.Errorf("invalid bind path: %s", arr[1])
1721-
}
1722-
default:
1723-
return nil, fmt.Errorf("unknown volume bind: %s", b)
1724-
}
1725-
1726-
return arr, nil
1727-
}
1728-
1729-
func parseBindMode(mp *types.MountPoint, mode string) error {
1730-
mp.RW = true
1731-
mp.CopyData = true
1732-
1733-
defaultMode := 0
1734-
rwMode := 0
1735-
labelMode := 0
1736-
replaceMode := 0
1737-
copyMode := 0
1738-
propagationMode := 0
1739-
1740-
for _, m := range strings.Split(mode, ",") {
1741-
switch m {
1742-
case "":
1743-
defaultMode++
1744-
case "ro":
1745-
mp.RW = false
1746-
rwMode++
1747-
case "rw":
1748-
mp.RW = true
1749-
rwMode++
1750-
case "dr", "rr":
1751-
// direct replace mode, random replace mode
1752-
mp.Replace = m
1753-
replaceMode++
1754-
case "z", "Z":
1755-
labelMode++
1756-
case "nocopy":
1757-
mp.CopyData = false
1758-
copyMode++
1759-
case "private", "rprivate", "slave", "rslave", "shared", "rshared":
1760-
mp.Propagation = m
1761-
propagationMode++
1762-
default:
1763-
return fmt.Errorf("unknown bind mode: %s", mode)
1764-
}
1765-
}
1766-
1767-
if defaultMode > 1 || rwMode > 1 || replaceMode > 1 || copyMode > 1 || propagationMode > 1 {
1768-
return fmt.Errorf("invalid bind mode: %s", mode)
1769-
}
1770-
1771-
mp.Mode = mode
1772-
return nil
1773-
}

pkg/opts/mountpoint.go

Lines changed: 104 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,104 @@
1+
package opts
2+
3+
import (
4+
"fmt"
5+
"strings"
6+
7+
"github.com/alibaba/pouch/apis/types"
8+
)
9+
10+
// CheckBind is used to check the volume bind information.
11+
func CheckBind(b string) ([]string, error) {
12+
if strings.Count(b, ":") > 2 {
13+
return nil, fmt.Errorf("unknown volume bind: %s", b)
14+
}
15+
16+
arr := strings.SplitN(b, ":", 3)
17+
switch len(arr) {
18+
case 1:
19+
if arr[0] == "" {
20+
return nil, fmt.Errorf("unknown volume bind: %s", b)
21+
}
22+
if arr[0][:1] != "/" {
23+
return nil, fmt.Errorf("invalid bind path: %s", arr[0])
24+
}
25+
case 2, 3:
26+
if arr[1] == "" {
27+
return nil, fmt.Errorf("unknown volume bind: %s", b)
28+
}
29+
if arr[1][:1] != "/" {
30+
return nil, fmt.Errorf("invalid bind path: %s", arr[1])
31+
}
32+
default:
33+
return nil, fmt.Errorf("unknown volume bind: %s", b)
34+
}
35+
36+
return arr, nil
37+
}
38+
39+
// ParseVolumesFrom is used to parse the parameter of VolumesFrom.
40+
func ParseVolumesFrom(volume string) (string, string, error) {
41+
if len(volume) == 0 {
42+
return "", "", fmt.Errorf("invalid argument volumes-from")
43+
}
44+
45+
parts := strings.Split(volume, ":")
46+
containerID := parts[0]
47+
mode := ""
48+
if len(parts) > 1 {
49+
mode = parts[1]
50+
}
51+
52+
if containerID == "" {
53+
return "", "", fmt.Errorf("failed to parse container's id")
54+
}
55+
56+
return containerID, mode, nil
57+
}
58+
59+
// ParseBindMode is used to parse the bind's mode.
60+
func ParseBindMode(mp *types.MountPoint, mode string) error {
61+
mp.RW = true
62+
mp.CopyData = true
63+
64+
defaultMode := 0
65+
rwMode := 0
66+
labelMode := 0
67+
replaceMode := 0
68+
copyMode := 0
69+
propagationMode := 0
70+
71+
for _, m := range strings.Split(mode, ",") {
72+
switch m {
73+
case "":
74+
defaultMode++
75+
case "ro":
76+
mp.RW = false
77+
rwMode++
78+
case "rw":
79+
mp.RW = true
80+
rwMode++
81+
case "dr", "rr":
82+
// direct replace mode, random replace mode
83+
mp.Replace = m
84+
replaceMode++
85+
case "z", "Z":
86+
labelMode++
87+
case "nocopy":
88+
mp.CopyData = false
89+
copyMode++
90+
case "private", "rprivate", "slave", "rslave", "shared", "rshared":
91+
mp.Propagation = m
92+
propagationMode++
93+
default:
94+
return fmt.Errorf("unknown bind mode: %s", mode)
95+
}
96+
}
97+
98+
if defaultMode > 1 || rwMode > 1 || replaceMode > 1 || copyMode > 1 || propagationMode > 1 {
99+
return fmt.Errorf("invalid bind mode: %s", mode)
100+
}
101+
102+
mp.Mode = mode
103+
return nil
104+
}

daemon/mgr/container_test.go renamed to pkg/opts/mountpoint_test.go

Lines changed: 34 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
package mgr
1+
package opts
22

33
import (
44
"fmt"
@@ -32,7 +32,7 @@ func TestCheckBind(t *testing.T) {
3232
}
3333

3434
for _, p := range parseds {
35-
arr, err := checkBind(p.bind)
35+
arr, err := CheckBind(p.bind)
3636
if p.err {
3737
assert.Equal(err, p.expectErr)
3838
} else {
@@ -63,7 +63,7 @@ func TestParseBindMode(t *testing.T) {
6363

6464
for _, p := range parseds {
6565
mp := &types.MountPoint{}
66-
err := parseBindMode(mp, p.mode)
66+
err := ParseBindMode(mp, p.mode)
6767
if p.err {
6868
assert.Equal(err, p.expectErr)
6969
} else {
@@ -74,3 +74,34 @@ func TestParseBindMode(t *testing.T) {
7474
}
7575
}
7676
}
77+
78+
func TestParseVolumesFrom(t *testing.T) {
79+
assert := assert.New(t)
80+
81+
type parsed struct {
82+
volumesFrom string
83+
expectID string
84+
expectMode string
85+
err bool
86+
expectErr error
87+
}
88+
89+
parseds := []parsed{
90+
{volumesFrom: "123456789", expectID: "123456789", expectMode: "", err: false, expectErr: nil},
91+
{volumesFrom: "123456789:nocopy", expectID: "123456789", expectMode: "nocopy", err: false, expectErr: nil},
92+
{volumesFrom: "123456789:", expectID: "123456789", expectMode: "", err: false, expectErr: nil},
93+
{volumesFrom: "", expectID: "", expectMode: "", err: true, expectErr: fmt.Errorf("invalid argument volumes-from")},
94+
{volumesFrom: ":", expectID: "", expectMode: "", err: true, expectErr: fmt.Errorf("failed to parse container's id")},
95+
}
96+
97+
for _, p := range parseds {
98+
containerID, mode, err := ParseVolumesFrom(p.volumesFrom)
99+
if p.err {
100+
assert.Equal(err, p.expectErr)
101+
} else {
102+
assert.NoError(err, p.expectErr)
103+
assert.Equal(p.expectID, containerID)
104+
assert.Equal(p.expectMode, mode)
105+
}
106+
}
107+
}

0 commit comments

Comments
 (0)