Skip to content

Commit

Permalink
Merge pull request #1131 from rudyfly/volume
Browse files Browse the repository at this point in the history
feature: add volumes-from for container
  • Loading branch information
allencloud authored Apr 17, 2018
2 parents 5f57ba3 + d84951a commit dc75691
Show file tree
Hide file tree
Showing 6 changed files with 249 additions and 95 deletions.
1 change: 1 addition & 0 deletions cli/common_flags.go
Original file line number Diff line number Diff line change
Expand Up @@ -80,6 +80,7 @@ func addCommonFlags(flagSet *pflag.FlagSet) *container {
flagSet.StringVar(&c.utsMode, "uts", "", "UTS namespace to use")

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

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

Expand Down
30 changes: 16 additions & 14 deletions cli/container.go
Original file line number Diff line number Diff line change
Expand Up @@ -10,18 +10,19 @@ import (
)

type container struct {
labels []string
name string
tty bool
volume []string
runtime string
env []string
entrypoint string
workdir string
user string
groupAdd []string
hostname string
rm bool
labels []string
name string
tty bool
volume []string
volumesFrom []string
runtime string
env []string
entrypoint string
workdir string
user string
groupAdd []string
hostname string
rm bool

blkioWeight uint16
blkioWeightDevice WeightDevice
Expand Down Expand Up @@ -187,8 +188,9 @@ func (c *container) config() (*types.ContainerCreateConfig, error) {
},

HostConfig: &types.HostConfig{
Binds: c.volume,
Runtime: c.runtime,
Binds: c.volume,
VolumesFrom: c.volumesFrom,
Runtime: c.runtime,
Resources: types.Resources{
// cpu
CPUShares: c.cpushare,
Expand Down
125 changes: 47 additions & 78 deletions daemon/mgr/container.go
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@ import (
"github.com/alibaba/pouch/pkg/collect"
"github.com/alibaba/pouch/pkg/errtypes"
"github.com/alibaba/pouch/pkg/meta"
"github.com/alibaba/pouch/pkg/opts"
"github.com/alibaba/pouch/pkg/quota"
"github.com/alibaba/pouch/pkg/randomid"
"github.com/alibaba/pouch/pkg/reference"
Expand Down Expand Up @@ -1449,12 +1450,54 @@ func (mgr *ContainerManager) parseBinds(ctx context.Context, meta *ContainerMeta
}
}()

// TODO: parse c.HostConfig.VolumesFrom
for _, v := range meta.HostConfig.VolumesFrom {
var containerID, mode string
containerID, mode, err = opts.ParseVolumesFrom(v)
if err != nil {
return err
}

var oldMeta *ContainerMeta
oldMeta, err = mgr.Get(ctx, containerID)
if err != nil {
return err
}

for _, oldMountPoint := range oldMeta.Mounts {
mp := &types.MountPoint{
Name: oldMountPoint.Name,
Source: oldMountPoint.Source,
Destination: oldMountPoint.Destination,
Driver: oldMountPoint.Driver,
Named: oldMountPoint.Named,
RW: oldMountPoint.RW,
Propagation: oldMountPoint.Propagation,
}

if _, exist := meta.Config.Volumes[oldMountPoint.Name]; !exist {
mp.Name = oldMountPoint.Name
mp.Source, mp.Driver, err = mgr.bindVolume(ctx, oldMountPoint.Name, meta)
if err != nil {
logrus.Errorf("failed to bind volume: %s, err: %v", oldMountPoint.Name, err)
return errors.Wrap(err, "failed to bind volume")
}
meta.Config.Volumes[mp.Name] = oldMountPoint.Destination
}

err = opts.ParseBindMode(mp, mode)
if err != nil {
logrus.Errorf("failed to parse volumes-from mode: %s, err: %v", mode, err)
return err
}

meta.Mounts = append(meta.Mounts, mp)
}

}

for _, b := range meta.HostConfig.Binds {
var parts []string
// TODO: when caused error, how to rollback.
parts, err = checkBind(b)
parts, err = opts.CheckBind(b)
if err != nil {
return err
}
Expand All @@ -1481,7 +1524,7 @@ func (mgr *ContainerManager) parseBinds(ctx context.Context, meta *ContainerMeta
mp.Source = randomid.Generate()
}

err = parseBindMode(mp, mode)
err = opts.ParseBindMode(mp, mode)
if err != nil {
logrus.Errorf("failed to parse bind mode: %s, err: %v", mode, err)
return err
Expand Down Expand Up @@ -1699,77 +1742,3 @@ func (mgr *ContainerManager) setBaseFS(ctx context.Context, meta *ContainerMeta,
// io.containerd.runtime.v1.linux as a const used by runc
meta.BaseFS = filepath.Join(mgr.Config.HomeDir, "containerd/state", "io.containerd.runtime.v1.linux", namespaces.Default, info.Name, "rootfs")
}

func checkBind(b string) ([]string, error) {
if strings.Count(b, ":") > 2 {
return nil, fmt.Errorf("unknown volume bind: %s", b)
}

arr := strings.SplitN(b, ":", 3)
switch len(arr) {
case 1:
if arr[0] == "" {
return nil, fmt.Errorf("unknown volume bind: %s", b)
}
if arr[0][:1] != "/" {
return nil, fmt.Errorf("invalid bind path: %s", arr[0])
}
case 2, 3:
if arr[1] == "" {
return nil, fmt.Errorf("unknown volume bind: %s", b)
}
if arr[1][:1] != "/" {
return nil, fmt.Errorf("invalid bind path: %s", arr[1])
}
default:
return nil, fmt.Errorf("unknown volume bind: %s", b)
}

return arr, nil
}

func parseBindMode(mp *types.MountPoint, mode string) error {
mp.RW = true
mp.CopyData = true

defaultMode := 0
rwMode := 0
labelMode := 0
replaceMode := 0
copyMode := 0
propagationMode := 0

for _, m := range strings.Split(mode, ",") {
switch m {
case "":
defaultMode++
case "ro":
mp.RW = false
rwMode++
case "rw":
mp.RW = true
rwMode++
case "dr", "rr":
// direct replace mode, random replace mode
mp.Replace = m
replaceMode++
case "z", "Z":
labelMode++
case "nocopy":
mp.CopyData = false
copyMode++
case "private", "rprivate", "slave", "rslave", "shared", "rshared":
mp.Propagation = m
propagationMode++
default:
return fmt.Errorf("unknown bind mode: %s", mode)
}
}

if defaultMode > 1 || rwMode > 1 || replaceMode > 1 || copyMode > 1 || propagationMode > 1 {
return fmt.Errorf("invalid bind mode: %s", mode)
}

mp.Mode = mode
return nil
}
104 changes: 104 additions & 0 deletions pkg/opts/mountpoint.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,104 @@
package opts

import (
"fmt"
"strings"

"github.com/alibaba/pouch/apis/types"
)

// CheckBind is used to check the volume bind information.
func CheckBind(b string) ([]string, error) {
if strings.Count(b, ":") > 2 {
return nil, fmt.Errorf("unknown volume bind: %s", b)
}

arr := strings.SplitN(b, ":", 3)
switch len(arr) {
case 1:
if arr[0] == "" {
return nil, fmt.Errorf("unknown volume bind: %s", b)
}
if arr[0][:1] != "/" {
return nil, fmt.Errorf("invalid bind path: %s", arr[0])
}
case 2, 3:
if arr[1] == "" {
return nil, fmt.Errorf("unknown volume bind: %s", b)
}
if arr[1][:1] != "/" {
return nil, fmt.Errorf("invalid bind path: %s", arr[1])
}
default:
return nil, fmt.Errorf("unknown volume bind: %s", b)
}

return arr, nil
}

// ParseVolumesFrom is used to parse the parameter of VolumesFrom.
func ParseVolumesFrom(volume string) (string, string, error) {
if len(volume) == 0 {
return "", "", fmt.Errorf("invalid argument volumes-from")
}

parts := strings.Split(volume, ":")
containerID := parts[0]
mode := ""
if len(parts) > 1 {
mode = parts[1]
}

if containerID == "" {
return "", "", fmt.Errorf("failed to parse container's id")
}

return containerID, mode, nil
}

// ParseBindMode is used to parse the bind's mode.
func ParseBindMode(mp *types.MountPoint, mode string) error {
mp.RW = true
mp.CopyData = true

defaultMode := 0
rwMode := 0
labelMode := 0
replaceMode := 0
copyMode := 0
propagationMode := 0

for _, m := range strings.Split(mode, ",") {
switch m {
case "":
defaultMode++
case "ro":
mp.RW = false
rwMode++
case "rw":
mp.RW = true
rwMode++
case "dr", "rr":
// direct replace mode, random replace mode
mp.Replace = m
replaceMode++
case "z", "Z":
labelMode++
case "nocopy":
mp.CopyData = false
copyMode++
case "private", "rprivate", "slave", "rslave", "shared", "rshared":
mp.Propagation = m
propagationMode++
default:
return fmt.Errorf("unknown bind mode: %s", mode)
}
}

if defaultMode > 1 || rwMode > 1 || replaceMode > 1 || copyMode > 1 || propagationMode > 1 {
return fmt.Errorf("invalid bind mode: %s", mode)
}

mp.Mode = mode
return nil
}
37 changes: 34 additions & 3 deletions daemon/mgr/container_test.go → pkg/opts/mountpoint_test.go
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
package mgr
package opts

import (
"fmt"
Expand Down Expand Up @@ -32,7 +32,7 @@ func TestCheckBind(t *testing.T) {
}

for _, p := range parseds {
arr, err := checkBind(p.bind)
arr, err := CheckBind(p.bind)
if p.err {
assert.Equal(err, p.expectErr)
} else {
Expand Down Expand Up @@ -63,7 +63,7 @@ func TestParseBindMode(t *testing.T) {

for _, p := range parseds {
mp := &types.MountPoint{}
err := parseBindMode(mp, p.mode)
err := ParseBindMode(mp, p.mode)
if p.err {
assert.Equal(err, p.expectErr)
} else {
Expand All @@ -74,3 +74,34 @@ func TestParseBindMode(t *testing.T) {
}
}
}

func TestParseVolumesFrom(t *testing.T) {
assert := assert.New(t)

type parsed struct {
volumesFrom string
expectID string
expectMode string
err bool
expectErr error
}

parseds := []parsed{
{volumesFrom: "123456789", expectID: "123456789", expectMode: "", err: false, expectErr: nil},
{volumesFrom: "123456789:nocopy", expectID: "123456789", expectMode: "nocopy", err: false, expectErr: nil},
{volumesFrom: "123456789:", expectID: "123456789", expectMode: "", err: false, expectErr: nil},
{volumesFrom: "", expectID: "", expectMode: "", err: true, expectErr: fmt.Errorf("invalid argument volumes-from")},
{volumesFrom: ":", expectID: "", expectMode: "", err: true, expectErr: fmt.Errorf("failed to parse container's id")},
}

for _, p := range parseds {
containerID, mode, err := ParseVolumesFrom(p.volumesFrom)
if p.err {
assert.Equal(err, p.expectErr)
} else {
assert.NoError(err, p.expectErr)
assert.Equal(p.expectID, containerID)
assert.Equal(p.expectMode, mode)
}
}
}
Loading

0 comments on commit dc75691

Please sign in to comment.