github.com/rawahars/moby@v24.0.4+incompatible/volume/mounts/windows_parser_test.go (about)

     1  package mounts // import "github.com/docker/docker/volume/mounts"
     2  
     3  import (
     4  	"fmt"
     5  	"strings"
     6  	"testing"
     7  
     8  	"github.com/docker/docker/api/types/mount"
     9  	"gotest.tools/v3/assert"
    10  )
    11  
    12  func TestWindowsParseMountRaw(t *testing.T) {
    13  	valid := []string{
    14  		`d:\`,
    15  		`d:`,
    16  		`d:\path`,
    17  		`d:\path with space`,
    18  		`c:\:d:\`,
    19  		`c:\windows\:d:`,
    20  		`c:\windows:d:\s p a c e`,
    21  		`c:\windows:d:\s p a c e:RW`,
    22  		`c:\program files:d:\s p a c e i n h o s t d i r`,
    23  		`0123456789name:d:`,
    24  		`MiXeDcAsEnAmE:d:`,
    25  		`test-aux-volume:d:`, // includes reserved word, but is not one itself
    26  		`name:D:`,
    27  		`name:D::rW`,
    28  		`name:D::RW`,
    29  		`name:D::RO`,
    30  		`c:/:d:/forward/slashes/are/good/too`,
    31  		`c:/:d:/including with/spaces:ro`,
    32  		`c:\Windows`,                // With capital
    33  		`c:\Program Files (x86)`,    // With capitals and brackets
    34  		`\\?\c:\windows\:d:`,        // Long path handling (source)
    35  		`c:\windows\:\\?\d:\`,       // Long path handling (target)
    36  		`\\.\pipe\foo:\\.\pipe\foo`, // named pipe
    37  		`//./pipe/foo://./pipe/foo`, // named pipe forward slashes
    38  	}
    39  
    40  	invalid := map[string]string{
    41  		``:                                 "invalid volume specification: ",
    42  		`.`:                                "invalid volume specification: ",
    43  		`..\`:                              "invalid volume specification: ",
    44  		`c:\:..\`:                          "invalid volume specification: ",
    45  		`c:\:d:\:xyzzy`:                    "invalid volume specification: ",
    46  		`c:`:                               "cannot be `c:`",
    47  		`c:\`:                              "cannot be `c:`",
    48  		`c:\notexist:d:`:                   `source path does not exist: c:\notexist`,
    49  		`c:\windows\system32\ntdll.dll:d:`: `source path must be a directory`,
    50  		`name<:d:`:                         `invalid volume specification`,
    51  		`name>:d:`:                         `invalid volume specification`,
    52  		`name::d:`:                         `invalid volume specification`,
    53  		`name":d:`:                         `invalid volume specification`,
    54  		`name\:d:`:                         `invalid volume specification`,
    55  		`name*:d:`:                         `invalid volume specification`,
    56  		`name|:d:`:                         `invalid volume specification`,
    57  		`name?:d:`:                         `invalid volume specification`,
    58  		`name/:d:`:                         `invalid volume specification`,
    59  		`d:\pathandmode:rw`:                `invalid volume specification`,
    60  		`d:\pathandmode:ro`:                `invalid volume specification`,
    61  		`con:d:`:                           `cannot be a reserved word for Windows filenames`,
    62  		`PRN:d:`:                           `cannot be a reserved word for Windows filenames`,
    63  		`aUx:d:`:                           `cannot be a reserved word for Windows filenames`,
    64  		`nul:d:`:                           `cannot be a reserved word for Windows filenames`,
    65  		`com1:d:`:                          `cannot be a reserved word for Windows filenames`,
    66  		`com2:d:`:                          `cannot be a reserved word for Windows filenames`,
    67  		`com3:d:`:                          `cannot be a reserved word for Windows filenames`,
    68  		`com4:d:`:                          `cannot be a reserved word for Windows filenames`,
    69  		`com5:d:`:                          `cannot be a reserved word for Windows filenames`,
    70  		`com6:d:`:                          `cannot be a reserved word for Windows filenames`,
    71  		`com7:d:`:                          `cannot be a reserved word for Windows filenames`,
    72  		`com8:d:`:                          `cannot be a reserved word for Windows filenames`,
    73  		`com9:d:`:                          `cannot be a reserved word for Windows filenames`,
    74  		`lpt1:d:`:                          `cannot be a reserved word for Windows filenames`,
    75  		`lpt2:d:`:                          `cannot be a reserved word for Windows filenames`,
    76  		`lpt3:d:`:                          `cannot be a reserved word for Windows filenames`,
    77  		`lpt4:d:`:                          `cannot be a reserved word for Windows filenames`,
    78  		`lpt5:d:`:                          `cannot be a reserved word for Windows filenames`,
    79  		`lpt6:d:`:                          `cannot be a reserved word for Windows filenames`,
    80  		`lpt7:d:`:                          `cannot be a reserved word for Windows filenames`,
    81  		`lpt8:d:`:                          `cannot be a reserved word for Windows filenames`,
    82  		`lpt9:d:`:                          `cannot be a reserved word for Windows filenames`,
    83  		`c:\windows\system32\ntdll.dll`:    `Only directories can be mapped on this platform`,
    84  		`\\.\pipe\foo:c:\pipe`:             `'c:\pipe' is not a valid pipe path`,
    85  	}
    86  
    87  	parser := NewWindowsParser()
    88  	if p, ok := parser.(*windowsParser); ok {
    89  		p.fi = mockFiProvider{}
    90  	}
    91  
    92  	for _, path := range valid {
    93  		if _, err := parser.ParseMountRaw(path, "local"); err != nil {
    94  			t.Errorf("ParseMountRaw(`%q`) should succeed: error %q", path, err)
    95  		}
    96  	}
    97  
    98  	for path, expectedError := range invalid {
    99  		if mp, err := parser.ParseMountRaw(path, "local"); err == nil {
   100  			t.Errorf("ParseMountRaw(`%q`) should have failed validation. Err '%v' - MP: %v", path, err, mp)
   101  		} else {
   102  			if !strings.Contains(err.Error(), expectedError) {
   103  				t.Errorf("ParseMountRaw(`%q`) error should contain %q, got %v", path, expectedError, err.Error())
   104  			}
   105  		}
   106  	}
   107  }
   108  
   109  func TestWindowsParseMountRawSplit(t *testing.T) {
   110  	cases := []struct {
   111  		bind      string
   112  		driver    string
   113  		expType   mount.Type
   114  		expDest   string
   115  		expSource string
   116  		expName   string
   117  		expDriver string
   118  		expRW     bool
   119  		fail      bool
   120  	}{
   121  		{`c:\:d:`, "local", mount.TypeBind, `d:`, `c:\`, ``, "", true, false},
   122  		{`c:\:d:\`, "local", mount.TypeBind, `d:\`, `c:\`, ``, "", true, false},
   123  		{`c:\:d:\:ro`, "local", mount.TypeBind, `d:\`, `c:\`, ``, "", false, false},
   124  		{`c:\:d:\:rw`, "local", mount.TypeBind, `d:\`, `c:\`, ``, "", true, false},
   125  		{`c:\:d:\:foo`, "local", mount.TypeBind, `d:\`, `c:\`, ``, "", false, true},
   126  		{`name:d::rw`, "local", mount.TypeVolume, `d:`, ``, `name`, "local", true, false},
   127  		{`name:d:`, "local", mount.TypeVolume, `d:`, ``, `name`, "local", true, false},
   128  		{`name:d::ro`, "local", mount.TypeVolume, `d:`, ``, `name`, "local", false, false},
   129  		{`name:c:`, "", mount.TypeVolume, ``, ``, ``, "", true, true},
   130  		{`driver/name:c:`, "", mount.TypeVolume, ``, ``, ``, "", true, true},
   131  		{`\\.\pipe\foo:\\.\pipe\bar`, "local", mount.TypeNamedPipe, `\\.\pipe\bar`, `\\.\pipe\foo`, "", "", true, false},
   132  		{`\\.\pipe\foo:c:\foo\bar`, "local", mount.TypeNamedPipe, ``, ``, "", "", true, true},
   133  		{`c:\foo\bar:\\.\pipe\foo`, "local", mount.TypeNamedPipe, ``, ``, "", "", true, true},
   134  	}
   135  
   136  	parser := NewWindowsParser()
   137  	if p, ok := parser.(*windowsParser); ok {
   138  		p.fi = mockFiProvider{}
   139  	}
   140  
   141  	for i, c := range cases {
   142  		c := c
   143  		t.Run(fmt.Sprintf("%d_%s", i, c.bind), func(t *testing.T) {
   144  			m, err := parser.ParseMountRaw(c.bind, c.driver)
   145  			if c.fail {
   146  				assert.ErrorContains(t, err, "", "expected an error")
   147  				return
   148  			}
   149  
   150  			assert.NilError(t, err)
   151  			assert.Equal(t, m.Destination, c.expDest)
   152  			assert.Equal(t, m.Source, c.expSource)
   153  			assert.Equal(t, m.Name, c.expName)
   154  			assert.Equal(t, m.Driver, c.expDriver)
   155  			assert.Equal(t, m.RW, c.expRW)
   156  			assert.Equal(t, m.Type, c.expType)
   157  		})
   158  	}
   159  }
   160  
   161  // TestWindowsParseMountSpecBindWithFileinfoError makes sure that the parser returns
   162  // the error produced by the fileinfo provider.
   163  //
   164  // Some extra context for the future in case of changes and possible wtf are we
   165  // testing this for:
   166  //
   167  // Currently this "fileInfoProvider" returns (bool, bool, error)
   168  // The 1st bool is "does this path exist"
   169  // The 2nd bool is "is this path a dir"
   170  // Then of course the error is an error.
   171  //
   172  // The issue is the parser was ignoring the error and only looking at the
   173  // "does this path exist" boolean, which is always false if there is an error.
   174  // Then the error returned to the caller was a (slightly, maybe) friendlier
   175  // error string than what comes from `os.Stat`
   176  // So ...the caller was always getting an error saying the path doesn't exist
   177  // even if it does exist but got some other error (like a permission error).
   178  // This is confusing to users.
   179  func TestWindowsParseMountSpecBindWithFileinfoError(t *testing.T) {
   180  	parser := NewWindowsParser()
   181  	testErr := fmt.Errorf("some crazy error")
   182  	if pr, ok := parser.(*windowsParser); ok {
   183  		pr.fi = &mockFiProviderWithError{err: testErr}
   184  	}
   185  
   186  	_, err := parser.ParseMountSpec(mount.Mount{
   187  		Type:   mount.TypeBind,
   188  		Source: `c:\bananas`,
   189  		Target: `c:\bananas`,
   190  	})
   191  	assert.ErrorContains(t, err, testErr.Error())
   192  }