daemon/volumes_unix_test.go
3cf18596
 // +build !windows
 
 package daemon
 
 import (
 	"encoding/json"
 	"fmt"
 	"reflect"
 	"testing"
 
 	containertypes "github.com/docker/docker/api/types/container"
 	mounttypes "github.com/docker/docker/api/types/mount"
 	"github.com/docker/docker/container"
 	"github.com/docker/docker/volume"
 )
 
 func TestBackportMountSpec(t *testing.T) {
 	d := Daemon{containers: container.NewMemoryStore()}
 
 	c := &container.Container{
55f8828e
 		State: &container.State{},
 		MountPoints: map[string]*volume.MountPoint{
 			"/apple":      {Destination: "/apple", Source: "/var/lib/docker/volumes/12345678", Name: "12345678", RW: true, CopyData: true}, // anonymous volume
 			"/banana":     {Destination: "/banana", Source: "/var/lib/docker/volumes/data", Name: "data", RW: true, CopyData: true},        // named volume
 			"/cherry":     {Destination: "/cherry", Source: "/var/lib/docker/volumes/data", Name: "data", CopyData: true},                  // RO named volume
 			"/dates":      {Destination: "/dates", Source: "/var/lib/docker/volumes/data", Name: "data"},                                   // named volume nocopy
 			"/elderberry": {Destination: "/elderberry", Source: "/var/lib/docker/volumes/data", Name: "data"},                              // masks anon vol
 			"/fig":        {Destination: "/fig", Source: "/data", RW: true},                                                                // RW bind
 			"/guava":      {Destination: "/guava", Source: "/data", RW: false, Propagation: "shared"},                                      // RO bind + propagation
 			"/kumquat":    {Destination: "/kumquat", Name: "data", RW: false, CopyData: true},                                              // volumes-from
3cf18596
 
55f8828e
 			// partially configured mountpoint due to #32613
 			// specifically, `mp.Spec.Source` is not set
 			"/honeydew": {
 				Type:        mounttypes.TypeVolume,
 				Destination: "/honeydew",
 				Name:        "data",
 				Source:      "/var/lib/docker/volumes/data",
 				Spec:        mounttypes.Mount{Type: mounttypes.TypeVolume, Target: "/honeydew", VolumeOptions: &mounttypes.VolumeOptions{NoCopy: true}},
 			},
3cf18596
 
55f8828e
 			// from hostconfig.Mounts
 			"/jambolan": {
 				Type:        mounttypes.TypeVolume,
 				Destination: "/jambolan",
 				Source:      "/var/lib/docker/volumes/data",
 				RW:          true,
 				Name:        "data",
 				Spec:        mounttypes.Mount{Type: mounttypes.TypeVolume, Target: "/jambolan", Source: "data"},
 			},
 		},
 		HostConfig: &containertypes.HostConfig{
 			Binds: []string{
 				"data:/banana",
 				"data:/cherry:ro",
 				"data:/dates:ro,nocopy",
 				"data:/elderberry:ro,nocopy",
 				"/data:/fig",
 				"/data:/guava:ro,shared",
 				"data:/honeydew:nocopy",
3cf18596
 			},
55f8828e
 			VolumesFrom: []string{"1:ro"},
 			Mounts: []mounttypes.Mount{
 				{Type: mounttypes.TypeVolume, Target: "/jambolan"},
3cf18596
 			},
55f8828e
 		},
 		Config: &containertypes.Config{Volumes: map[string]struct{}{
 			"/apple":      {},
 			"/elderberry": {},
 		}},
 	}
3cf18596
 
 	d.containers.Add("1", &container.Container{
55f8828e
 		State: &container.State{},
 		ID:    "1",
 		MountPoints: map[string]*volume.MountPoint{
 			"/kumquat": {Destination: "/kumquat", Name: "data", RW: false, CopyData: true},
 		},
 		HostConfig: &containertypes.HostConfig{
 			Binds: []string{
 				"data:/kumquat:ro",
3cf18596
 			},
 		},
 	})
 
 	type expected struct {
 		mp      *volume.MountPoint
 		comment string
 	}
 
 	pretty := func(mp *volume.MountPoint) string {
 		b, err := json.MarshalIndent(mp, "\t", "    ")
 		if err != nil {
 			return fmt.Sprintf("%#v", mp)
 		}
 		return string(b)
 	}
 
 	for _, x := range []expected{
 		{
 			mp: &volume.MountPoint{
 				Type:        mounttypes.TypeVolume,
 				Destination: "/apple",
 				RW:          true,
 				Name:        "12345678",
 				Source:      "/var/lib/docker/volumes/12345678",
 				CopyData:    true,
 				Spec: mounttypes.Mount{
 					Type:   mounttypes.TypeVolume,
 					Source: "",
 					Target: "/apple",
 				},
 			},
 			comment: "anonymous volume",
 		},
 		{
 			mp: &volume.MountPoint{
 				Type:        mounttypes.TypeVolume,
 				Destination: "/banana",
 				RW:          true,
 				Name:        "data",
 				Source:      "/var/lib/docker/volumes/data",
 				CopyData:    true,
 				Spec: mounttypes.Mount{
 					Type:   mounttypes.TypeVolume,
 					Source: "data",
 					Target: "/banana",
 				},
 			},
 			comment: "named volume",
 		},
 		{
 			mp: &volume.MountPoint{
 				Type:        mounttypes.TypeVolume,
 				Destination: "/cherry",
 				Name:        "data",
 				Source:      "/var/lib/docker/volumes/data",
 				CopyData:    true,
 				Spec: mounttypes.Mount{
 					Type:     mounttypes.TypeVolume,
 					Source:   "data",
 					Target:   "/cherry",
 					ReadOnly: true,
 				},
 			},
 			comment: "read-only named volume",
 		},
 		{
 			mp: &volume.MountPoint{
 				Type:        mounttypes.TypeVolume,
 				Destination: "/dates",
 				Name:        "data",
 				Source:      "/var/lib/docker/volumes/data",
 				Spec: mounttypes.Mount{
 					Type:          mounttypes.TypeVolume,
 					Source:        "data",
 					Target:        "/dates",
 					ReadOnly:      true,
 					VolumeOptions: &mounttypes.VolumeOptions{NoCopy: true},
 				},
 			},
 			comment: "named volume with nocopy",
 		},
 		{
 			mp: &volume.MountPoint{
 				Type:        mounttypes.TypeVolume,
 				Destination: "/elderberry",
 				Name:        "data",
 				Source:      "/var/lib/docker/volumes/data",
 				Spec: mounttypes.Mount{
 					Type:          mounttypes.TypeVolume,
 					Source:        "data",
 					Target:        "/elderberry",
 					ReadOnly:      true,
 					VolumeOptions: &mounttypes.VolumeOptions{NoCopy: true},
 				},
 			},
 			comment: "masks an anonymous volume",
 		},
 		{
 			mp: &volume.MountPoint{
 				Type:        mounttypes.TypeBind,
 				Destination: "/fig",
 				Source:      "/data",
 				RW:          true,
 				Spec: mounttypes.Mount{
 					Type:   mounttypes.TypeBind,
 					Source: "/data",
 					Target: "/fig",
 				},
 			},
 			comment: "bind mount with read/write",
 		},
 		{
 			mp: &volume.MountPoint{
 				Type:        mounttypes.TypeBind,
 				Destination: "/guava",
 				Source:      "/data",
 				RW:          false,
 				Propagation: "shared",
 				Spec: mounttypes.Mount{
 					Type:        mounttypes.TypeBind,
 					Source:      "/data",
 					Target:      "/guava",
 					ReadOnly:    true,
 					BindOptions: &mounttypes.BindOptions{Propagation: "shared"},
 				},
 			},
39bcaee4
 			comment: "bind mount with read/write + shared propagation",
3cf18596
 		},
 		{
 			mp: &volume.MountPoint{
 				Type:        mounttypes.TypeVolume,
 				Destination: "/honeydew",
 				Source:      "/var/lib/docker/volumes/data",
 				RW:          true,
 				Propagation: "shared",
 				Spec: mounttypes.Mount{
 					Type:          mounttypes.TypeVolume,
 					Source:        "data",
 					Target:        "/honeydew",
 					VolumeOptions: &mounttypes.VolumeOptions{NoCopy: true},
 				},
 			},
 			comment: "partially configured named volume caused by #32613",
 		},
 		{
 			mp:      &(*c.MountPoints["/jambolan"]), // copy the mountpoint, expect no changes
 			comment: "volume defined in mounts API",
 		},
 		{
 			mp: &volume.MountPoint{
 				Type:        mounttypes.TypeVolume,
 				Destination: "/kumquat",
 				Source:      "/var/lib/docker/volumes/data",
 				RW:          false,
 				Name:        "data",
 				Spec: mounttypes.Mount{
 					Type:     mounttypes.TypeVolume,
 					Source:   "data",
 					Target:   "/kumquat",
 					ReadOnly: true,
 				},
 			},
 			comment: "partially configured named volume caused by #32613",
 		},
 	} {
 
 		mp := c.MountPoints[x.mp.Destination]
 		d.backportMountSpec(c)
 
 		if !reflect.DeepEqual(mp.Spec, x.mp.Spec) {
 			t.Fatalf("%s\nexpected:\n\t%s\n\ngot:\n\t%s", x.comment, pretty(x.mp), pretty(mp))
 		}
 	}
 }