Cleanup: applyVolumesFrom
Docker-DCO-1.1-Signed-off-by: Brian Goff <cpuguy83@gmail.com> (github: cpuguy83)
This commit is contained in:
parent
bef33cf88a
commit
7495fbc0e3
3 changed files with 123 additions and 99 deletions
|
@ -1145,3 +1145,47 @@ func (container *Container) getNetworkedContainer() (*Container, error) {
|
|||
return nil, fmt.Errorf("network mode not set to container")
|
||||
}
|
||||
}
|
||||
|
||||
func (container *Container) GetVolumes() (map[string]*Volume, error) {
|
||||
// Get all the bind-mounts
|
||||
volumes, err := container.getBindMap()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
// Get all the normal volumes
|
||||
for volPath, hostPath := range container.Volumes {
|
||||
if _, exists := volumes[volPath]; exists {
|
||||
continue
|
||||
}
|
||||
volumes[volPath] = &Volume{VolPath: volPath, HostPath: hostPath, isReadWrite: container.VolumesRW[volPath]}
|
||||
}
|
||||
|
||||
return volumes, nil
|
||||
}
|
||||
|
||||
func (container *Container) getBindMap() (map[string]*Volume, error) {
|
||||
var (
|
||||
// Create the requested bind mounts
|
||||
volumes = map[string]*Volume{}
|
||||
// Define illegal container destinations
|
||||
illegalDsts = []string{"/", "."}
|
||||
)
|
||||
|
||||
for _, bind := range container.hostConfig.Binds {
|
||||
vol, err := parseBindVolumeSpec(bind)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
vol.isBindMount = true
|
||||
// Bail if trying to mount to an illegal destination
|
||||
for _, illegal := range illegalDsts {
|
||||
if vol.VolPath == illegal {
|
||||
return nil, fmt.Errorf("Illegal bind destination: %s", vol.VolPath)
|
||||
}
|
||||
}
|
||||
|
||||
volumes[filepath.Clean(vol.VolPath)] = &vol
|
||||
}
|
||||
return volumes, nil
|
||||
}
|
||||
|
|
|
@ -16,14 +16,10 @@ import (
|
|||
type Volume struct {
|
||||
HostPath string
|
||||
VolPath string
|
||||
Mode string
|
||||
isReadWrite bool
|
||||
isBindMount bool
|
||||
}
|
||||
|
||||
func (v *Volume) isRw() bool {
|
||||
return v.Mode == "" || strings.ToLower(v.Mode) == "rw"
|
||||
}
|
||||
|
||||
func (v *Volume) isDir() (bool, error) {
|
||||
stat, err := os.Stat(v.HostPath)
|
||||
if err != nil {
|
||||
|
@ -42,10 +38,7 @@ func prepareVolumesForContainer(container *Container) error {
|
|||
}
|
||||
}
|
||||
|
||||
if err := createVolumes(container); err != nil {
|
||||
return err
|
||||
}
|
||||
return nil
|
||||
return createVolumes(container)
|
||||
}
|
||||
|
||||
func setupMountsForContainer(container *Container) error {
|
||||
|
@ -74,87 +67,82 @@ func setupMountsForContainer(container *Container) error {
|
|||
return nil
|
||||
}
|
||||
|
||||
func parseVolumesFromSpec(container *Container, spec string) (map[string]*Volume, error) {
|
||||
specParts := strings.SplitN(spec, ":", 2)
|
||||
if len(specParts) == 0 {
|
||||
return nil, fmt.Errorf("Malformed volumes-from specification: %s", spec)
|
||||
}
|
||||
|
||||
c := container.daemon.Get(specParts[0])
|
||||
if c == nil {
|
||||
return nil, fmt.Errorf("Container %s not found. Impossible to mount its volumes", specParts[0])
|
||||
}
|
||||
|
||||
volumes, err := c.GetVolumes()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
if len(specParts) == 2 {
|
||||
mode := specParts[1]
|
||||
if validVolumeMode(mode) {
|
||||
return nil, fmt.Errorf("Invalid mode for volumes-from: %s", mode)
|
||||
}
|
||||
|
||||
// Set the mode for the inheritted volume
|
||||
for _, v := range volumes {
|
||||
v.isReadWrite = mode != "ro"
|
||||
}
|
||||
}
|
||||
|
||||
return volumes, nil
|
||||
}
|
||||
|
||||
func applyVolumesFrom(container *Container) error {
|
||||
volumesFrom := container.hostConfig.VolumesFrom
|
||||
if len(volumesFrom) > 0 {
|
||||
for _, containerSpec := range volumesFrom {
|
||||
var (
|
||||
mountRW = true
|
||||
specParts = strings.SplitN(containerSpec, ":", 2)
|
||||
)
|
||||
|
||||
switch len(specParts) {
|
||||
case 0:
|
||||
return fmt.Errorf("Malformed volumes-from specification: %s", containerSpec)
|
||||
case 2:
|
||||
switch specParts[1] {
|
||||
case "ro":
|
||||
mountRW = false
|
||||
case "rw": // mountRW is already true
|
||||
default:
|
||||
return fmt.Errorf("Malformed volumes-from specification: %s", containerSpec)
|
||||
}
|
||||
for _, spec := range volumesFrom {
|
||||
volumes, err := parseVolumesFromSpec(container, spec)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
for _, v := range volumes {
|
||||
if err = v.initialize(container); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
c := container.daemon.Get(specParts[0])
|
||||
if c == nil {
|
||||
return fmt.Errorf("Container %s not found. Impossible to mount its volumes", specParts[0])
|
||||
}
|
||||
|
||||
if err := c.Mount(); err != nil {
|
||||
return fmt.Errorf("Container %s failed to mount. Impossible to mount its volumes", specParts[0])
|
||||
}
|
||||
defer c.Unmount()
|
||||
|
||||
for volPath, id := range c.Volumes {
|
||||
if _, exists := container.Volumes[volPath]; exists {
|
||||
continue
|
||||
}
|
||||
|
||||
pth, err := c.getResourcePath(volPath)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
stat, err := os.Stat(pth)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if err := createIfNotExists(pth, stat.IsDir()); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
container.Volumes[volPath] = id
|
||||
if isRW, exists := c.VolumesRW[volPath]; exists {
|
||||
container.VolumesRW[volPath] = isRW && mountRW
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func validVolumeMode(mode string) bool {
|
||||
validModes := map[string]bool{
|
||||
"rw": true,
|
||||
"ro": true,
|
||||
}
|
||||
|
||||
return validModes[mode]
|
||||
}
|
||||
|
||||
func parseBindVolumeSpec(spec string) (Volume, error) {
|
||||
var (
|
||||
arr = strings.Split(spec, ":")
|
||||
vol Volume
|
||||
)
|
||||
|
||||
vol.isBindMount = true
|
||||
switch len(arr) {
|
||||
case 1:
|
||||
vol.VolPath = spec
|
||||
vol.Mode = "rw"
|
||||
vol.isReadWrite = true
|
||||
case 2:
|
||||
vol.HostPath = arr[0]
|
||||
vol.VolPath = arr[1]
|
||||
vol.Mode = "rw"
|
||||
vol.isReadWrite = true
|
||||
case 3:
|
||||
vol.HostPath = arr[0]
|
||||
vol.VolPath = arr[1]
|
||||
vol.Mode = arr[2]
|
||||
vol.isReadWrite = validVolumeMode(arr[2]) && arr[2] == "rw"
|
||||
default:
|
||||
return vol, fmt.Errorf("Invalid volume specification: %s", spec)
|
||||
}
|
||||
|
@ -166,34 +154,9 @@ func parseBindVolumeSpec(spec string) (Volume, error) {
|
|||
return vol, nil
|
||||
}
|
||||
|
||||
func getBindMap(container *Container) (map[string]Volume, error) {
|
||||
var (
|
||||
// Create the requested bind mounts
|
||||
volumes = map[string]Volume{}
|
||||
// Define illegal container destinations
|
||||
illegalDsts = []string{"/", "."}
|
||||
)
|
||||
|
||||
for _, bind := range container.hostConfig.Binds {
|
||||
vol, err := parseBindVolumeSpec(bind)
|
||||
if err != nil {
|
||||
return volumes, err
|
||||
}
|
||||
// Bail if trying to mount to an illegal destination
|
||||
for _, illegal := range illegalDsts {
|
||||
if vol.VolPath == illegal {
|
||||
return nil, fmt.Errorf("Illegal bind destination: %s", vol.VolPath)
|
||||
}
|
||||
}
|
||||
|
||||
volumes[filepath.Clean(vol.VolPath)] = vol
|
||||
}
|
||||
return volumes, nil
|
||||
}
|
||||
|
||||
func createVolumes(container *Container) error {
|
||||
// Get all the bindmounts
|
||||
volumes, err := getBindMap(container)
|
||||
volumes, err := container.GetVolumes()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
@ -202,9 +165,9 @@ func createVolumes(container *Container) error {
|
|||
for volPath := range container.Config.Volumes {
|
||||
// Make sure the the volume isn't already specified as a bindmount
|
||||
if _, exists := volumes[volPath]; !exists {
|
||||
volumes[volPath] = Volume{
|
||||
volumes[volPath] = &Volume{
|
||||
VolPath: volPath,
|
||||
Mode: "rw",
|
||||
isReadWrite: true,
|
||||
isBindMount: false,
|
||||
}
|
||||
}
|
||||
|
@ -215,8 +178,8 @@ func createVolumes(container *Container) error {
|
|||
return err
|
||||
}
|
||||
}
|
||||
return nil
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func createVolumeHostPath(container *Container) (string, error) {
|
||||
|
@ -247,7 +210,7 @@ func (v *Volume) initialize(container *Container) error {
|
|||
}
|
||||
|
||||
// If it's not a bindmount we need to create the dir on the host
|
||||
if !v.isBindMount {
|
||||
if !v.isBindMount && v.HostPath == "" {
|
||||
v.HostPath, err = createVolumeHostPath(container)
|
||||
if err != nil {
|
||||
return err
|
||||
|
@ -269,7 +232,7 @@ func (v *Volume) initialize(container *Container) error {
|
|||
}
|
||||
|
||||
container.Volumes[v.VolPath] = hostPath
|
||||
container.VolumesRW[v.VolPath] = v.isRw()
|
||||
container.VolumesRW[v.VolPath] = v.isReadWrite
|
||||
|
||||
volIsDir, err := v.isDir()
|
||||
if err != nil {
|
||||
|
@ -280,7 +243,7 @@ func (v *Volume) initialize(container *Container) error {
|
|||
}
|
||||
|
||||
// Do not copy or change permissions if we are mounting from the host
|
||||
if v.isRw() && !v.isBindMount {
|
||||
if v.isReadWrite && !v.isBindMount {
|
||||
return copyExistingContents(fullVolPath, hostPath)
|
||||
}
|
||||
return nil
|
||||
|
|
|
@ -374,6 +374,23 @@ func TestVolumesFromInReadWriteMode(t *testing.T) {
|
|||
logDone("run - volumes from as read write mount")
|
||||
}
|
||||
|
||||
func TestVolumesFromInheritsReadOnly(t *testing.T) {
|
||||
cmd := exec.Command(dockerBinary, "run", "--name", "parent", "-v", "/test:/test:ro", "busybox", "true")
|
||||
if _, err := runCommand(cmd); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
// Expect this "rw" mode to be be ignored since the inheritted volume is "ro"
|
||||
cmd = exec.Command(dockerBinary, "run", "--volumes-from", "parent:rw", "busybox", "touch", "/test/file")
|
||||
if _, err := runCommand(cmd); err == nil {
|
||||
t.Fatal("Expected to inherit read-only volume even when passing in `rw`")
|
||||
}
|
||||
|
||||
deleteAllContainers()
|
||||
|
||||
logDone("run - volumes from ignores `rw` if inherrited volume is `ro`")
|
||||
}
|
||||
|
||||
// Test for #1351
|
||||
func TestApplyVolumesFromBeforeVolumes(t *testing.T) {
|
||||
cmd := exec.Command(dockerBinary, "run", "--name", "parent", "-v", "/test", "busybox", "touch", "/test/foo")
|
||||
|
@ -1181,7 +1198,7 @@ func TestDockerRunWithVolumesIsRecursive(t *testing.T) {
|
|||
|
||||
deleteAllContainers()
|
||||
|
||||
logDone("run - volumes are bind mounted recuursively")
|
||||
logDone("run - volumes are bind mounted recursively")
|
||||
}
|
||||
|
||||
func TestDnsDefaultOptions(t *testing.T) {
|
||||
|
|
Loading…
Reference in a new issue