github.com/docker/engine@v22.0.0-20211208180946-d456264580cf+incompatible/daemon/volumes_unix_test.go (about)

     1  //go:build !windows
     2  // +build !windows
     3  
     4  package daemon // import "github.com/docker/docker/daemon"
     5  
     6  import (
     7  	"encoding/json"
     8  	"fmt"
     9  	"reflect"
    10  	"testing"
    11  
    12  	containertypes "github.com/docker/docker/api/types/container"
    13  	mounttypes "github.com/docker/docker/api/types/mount"
    14  	"github.com/docker/docker/container"
    15  	volumemounts "github.com/docker/docker/volume/mounts"
    16  )
    17  
    18  func TestBackportMountSpec(t *testing.T) {
    19  	d := Daemon{containers: container.NewMemoryStore()}
    20  
    21  	c := &container.Container{
    22  		State: &container.State{},
    23  		MountPoints: map[string]*volumemounts.MountPoint{
    24  			"/apple":      {Destination: "/apple", Source: "/var/lib/docker/volumes/12345678", Name: "12345678", RW: true, CopyData: true}, // anonymous volume
    25  			"/banana":     {Destination: "/banana", Source: "/var/lib/docker/volumes/data", Name: "data", RW: true, CopyData: true},        // named volume
    26  			"/cherry":     {Destination: "/cherry", Source: "/var/lib/docker/volumes/data", Name: "data", CopyData: true},                  // RO named volume
    27  			"/dates":      {Destination: "/dates", Source: "/var/lib/docker/volumes/data", Name: "data"},                                   // named volume nocopy
    28  			"/elderberry": {Destination: "/elderberry", Source: "/var/lib/docker/volumes/data", Name: "data"},                              // masks anon vol
    29  			"/fig":        {Destination: "/fig", Source: "/data", RW: true},                                                                // RW bind
    30  			"/guava":      {Destination: "/guava", Source: "/data", RW: false, Propagation: "shared"},                                      // RO bind + propagation
    31  			"/kumquat":    {Destination: "/kumquat", Name: "data", RW: false, CopyData: true},                                              // volumes-from
    32  
    33  			// partially configured mountpoint due to #32613
    34  			// specifically, `mp.Spec.Source` is not set
    35  			"/honeydew": {
    36  				Type:        mounttypes.TypeVolume,
    37  				Destination: "/honeydew",
    38  				Name:        "data",
    39  				Source:      "/var/lib/docker/volumes/data",
    40  				Spec:        mounttypes.Mount{Type: mounttypes.TypeVolume, Target: "/honeydew", VolumeOptions: &mounttypes.VolumeOptions{NoCopy: true}},
    41  			},
    42  
    43  			// from hostconfig.Mounts
    44  			"/jambolan": {
    45  				Type:        mounttypes.TypeVolume,
    46  				Destination: "/jambolan",
    47  				Source:      "/var/lib/docker/volumes/data",
    48  				RW:          true,
    49  				Name:        "data",
    50  				Spec:        mounttypes.Mount{Type: mounttypes.TypeVolume, Target: "/jambolan", Source: "data"},
    51  			},
    52  		},
    53  		HostConfig: &containertypes.HostConfig{
    54  			Binds: []string{
    55  				"data:/banana",
    56  				"data:/cherry:ro",
    57  				"data:/dates:ro,nocopy",
    58  				"data:/elderberry:ro,nocopy",
    59  				"/data:/fig",
    60  				"/data:/guava:ro,shared",
    61  				"data:/honeydew:nocopy",
    62  			},
    63  			VolumesFrom: []string{"1:ro"},
    64  			Mounts: []mounttypes.Mount{
    65  				{Type: mounttypes.TypeVolume, Target: "/jambolan"},
    66  			},
    67  		},
    68  		Config: &containertypes.Config{Volumes: map[string]struct{}{
    69  			"/apple":      {},
    70  			"/elderberry": {},
    71  		}},
    72  	}
    73  
    74  	d.containers.Add("1", &container.Container{
    75  		State: &container.State{},
    76  		ID:    "1",
    77  		MountPoints: map[string]*volumemounts.MountPoint{
    78  			"/kumquat": {Destination: "/kumquat", Name: "data", RW: false, CopyData: true},
    79  		},
    80  		HostConfig: &containertypes.HostConfig{
    81  			Binds: []string{
    82  				"data:/kumquat:ro",
    83  			},
    84  		},
    85  	})
    86  
    87  	type expected struct {
    88  		mp      *volumemounts.MountPoint
    89  		comment string
    90  	}
    91  
    92  	pretty := func(mp *volumemounts.MountPoint) string {
    93  		b, err := json.MarshalIndent(mp, "\t", "    ")
    94  		if err != nil {
    95  			return fmt.Sprintf("%#v", mp)
    96  		}
    97  		return string(b)
    98  	}
    99  
   100  	mpc := *c.MountPoints["/jambolan"]
   101  
   102  	for _, x := range []expected{
   103  		{
   104  			mp: &volumemounts.MountPoint{
   105  				Type:        mounttypes.TypeVolume,
   106  				Destination: "/apple",
   107  				RW:          true,
   108  				Name:        "12345678",
   109  				Source:      "/var/lib/docker/volumes/12345678",
   110  				CopyData:    true,
   111  				Spec: mounttypes.Mount{
   112  					Type:   mounttypes.TypeVolume,
   113  					Source: "",
   114  					Target: "/apple",
   115  				},
   116  			},
   117  			comment: "anonymous volume",
   118  		},
   119  		{
   120  			mp: &volumemounts.MountPoint{
   121  				Type:        mounttypes.TypeVolume,
   122  				Destination: "/banana",
   123  				RW:          true,
   124  				Name:        "data",
   125  				Source:      "/var/lib/docker/volumes/data",
   126  				CopyData:    true,
   127  				Spec: mounttypes.Mount{
   128  					Type:   mounttypes.TypeVolume,
   129  					Source: "data",
   130  					Target: "/banana",
   131  				},
   132  			},
   133  			comment: "named volume",
   134  		},
   135  		{
   136  			mp: &volumemounts.MountPoint{
   137  				Type:        mounttypes.TypeVolume,
   138  				Destination: "/cherry",
   139  				Name:        "data",
   140  				Source:      "/var/lib/docker/volumes/data",
   141  				CopyData:    true,
   142  				Spec: mounttypes.Mount{
   143  					Type:     mounttypes.TypeVolume,
   144  					Source:   "data",
   145  					Target:   "/cherry",
   146  					ReadOnly: true,
   147  				},
   148  			},
   149  			comment: "read-only named volume",
   150  		},
   151  		{
   152  			mp: &volumemounts.MountPoint{
   153  				Type:        mounttypes.TypeVolume,
   154  				Destination: "/dates",
   155  				Name:        "data",
   156  				Source:      "/var/lib/docker/volumes/data",
   157  				Spec: mounttypes.Mount{
   158  					Type:          mounttypes.TypeVolume,
   159  					Source:        "data",
   160  					Target:        "/dates",
   161  					ReadOnly:      true,
   162  					VolumeOptions: &mounttypes.VolumeOptions{NoCopy: true},
   163  				},
   164  			},
   165  			comment: "named volume with nocopy",
   166  		},
   167  		{
   168  			mp: &volumemounts.MountPoint{
   169  				Type:        mounttypes.TypeVolume,
   170  				Destination: "/elderberry",
   171  				Name:        "data",
   172  				Source:      "/var/lib/docker/volumes/data",
   173  				Spec: mounttypes.Mount{
   174  					Type:          mounttypes.TypeVolume,
   175  					Source:        "data",
   176  					Target:        "/elderberry",
   177  					ReadOnly:      true,
   178  					VolumeOptions: &mounttypes.VolumeOptions{NoCopy: true},
   179  				},
   180  			},
   181  			comment: "masks an anonymous volume",
   182  		},
   183  		{
   184  			mp: &volumemounts.MountPoint{
   185  				Type:        mounttypes.TypeBind,
   186  				Destination: "/fig",
   187  				Source:      "/data",
   188  				RW:          true,
   189  				Spec: mounttypes.Mount{
   190  					Type:   mounttypes.TypeBind,
   191  					Source: "/data",
   192  					Target: "/fig",
   193  				},
   194  			},
   195  			comment: "bind mount with read/write",
   196  		},
   197  		{
   198  			mp: &volumemounts.MountPoint{
   199  				Type:        mounttypes.TypeBind,
   200  				Destination: "/guava",
   201  				Source:      "/data",
   202  				RW:          false,
   203  				Propagation: "shared",
   204  				Spec: mounttypes.Mount{
   205  					Type:        mounttypes.TypeBind,
   206  					Source:      "/data",
   207  					Target:      "/guava",
   208  					ReadOnly:    true,
   209  					BindOptions: &mounttypes.BindOptions{Propagation: "shared"},
   210  				},
   211  			},
   212  			comment: "bind mount with read/write + shared propagation",
   213  		},
   214  		{
   215  			mp: &volumemounts.MountPoint{
   216  				Type:        mounttypes.TypeVolume,
   217  				Destination: "/honeydew",
   218  				Source:      "/var/lib/docker/volumes/data",
   219  				RW:          true,
   220  				Propagation: "shared",
   221  				Spec: mounttypes.Mount{
   222  					Type:          mounttypes.TypeVolume,
   223  					Source:        "data",
   224  					Target:        "/honeydew",
   225  					VolumeOptions: &mounttypes.VolumeOptions{NoCopy: true},
   226  				},
   227  			},
   228  			comment: "partially configured named volume caused by #32613",
   229  		},
   230  		{
   231  			mp:      &mpc,
   232  			comment: "volume defined in mounts API",
   233  		},
   234  		{
   235  			mp: &volumemounts.MountPoint{
   236  				Type:        mounttypes.TypeVolume,
   237  				Destination: "/kumquat",
   238  				Source:      "/var/lib/docker/volumes/data",
   239  				RW:          false,
   240  				Name:        "data",
   241  				Spec: mounttypes.Mount{
   242  					Type:     mounttypes.TypeVolume,
   243  					Source:   "data",
   244  					Target:   "/kumquat",
   245  					ReadOnly: true,
   246  				},
   247  			},
   248  			comment: "partially configured named volume caused by #32613",
   249  		},
   250  	} {
   251  
   252  		mp := c.MountPoints[x.mp.Destination]
   253  		d.backportMountSpec(c)
   254  
   255  		if !reflect.DeepEqual(mp.Spec, x.mp.Spec) {
   256  			t.Fatalf("%s\nexpected:\n\t%s\n\ngot:\n\t%s", x.comment, pretty(x.mp), pretty(mp))
   257  		}
   258  	}
   259  }