...

Source file src/github.com/Microsoft/hcsshim/internal/guest/storage/scsi/scsi_test.go

Documentation: github.com/Microsoft/hcsshim/internal/guest/storage/scsi

     1  //go:build linux
     2  // +build linux
     3  
     4  package scsi
     5  
     6  import (
     7  	"context"
     8  	"errors"
     9  	"fmt"
    10  	"os"
    11  	"testing"
    12  
    13  	"github.com/Microsoft/hcsshim/internal/protocol/guestresource"
    14  	"golang.org/x/sys/unix"
    15  )
    16  
    17  func clearTestDependencies() {
    18  	osMkdirAll = nil
    19  	osRemoveAll = nil
    20  	unixMount = nil
    21  	controllerLunToName = nil
    22  	createVerityTarget = nil
    23  	encryptDevice = nil
    24  	cleanupCryptDevice = nil
    25  	storageUnmountPath = nil
    26  }
    27  
    28  func Test_Mount_Mkdir_Fails_Error(t *testing.T) {
    29  	clearTestDependencies()
    30  
    31  	expectedErr := errors.New("mkdir : no such file or directory")
    32  	osMkdirAll = func(path string, perm os.FileMode) error {
    33  		return expectedErr
    34  	}
    35  
    36  	controllerLunToName = func(ctx context.Context, controller, lun uint8) (string, error) {
    37  		return "", nil
    38  	}
    39  
    40  	if err := Mount(
    41  		context.Background(),
    42  		0,
    43  		0,
    44  		"",
    45  		false,
    46  		false,
    47  		nil,
    48  		nil,
    49  	); !errors.Is(err, expectedErr) {
    50  		t.Fatalf("expected err: %v, got: %v", expectedErr, err)
    51  	}
    52  }
    53  
    54  func Test_Mount_Mkdir_ExpectedPath(t *testing.T) {
    55  	clearTestDependencies()
    56  
    57  	// NOTE: Do NOT set osRemoveAll because the mount succeeds. Expect it not to
    58  	// be called.
    59  
    60  	target := "/fake/path"
    61  	osMkdirAll = func(path string, perm os.FileMode) error {
    62  		if path != target {
    63  			t.Errorf("expected path: %v, got: %v", target, path)
    64  			return errors.New("unexpected path")
    65  		}
    66  		return nil
    67  	}
    68  	controllerLunToName = func(ctx context.Context, controller, lun uint8) (string, error) {
    69  		return "", nil
    70  	}
    71  	unixMount = func(source string, target string, fstype string, flags uintptr, data string) error {
    72  		// Fake the mount success
    73  		return nil
    74  	}
    75  
    76  	if err := Mount(
    77  		context.Background(),
    78  		0,
    79  		0,
    80  		target,
    81  		false,
    82  		false,
    83  		nil,
    84  		nil,
    85  	); err != nil {
    86  		t.Fatalf("expected nil error got: %v", err)
    87  	}
    88  }
    89  
    90  func Test_Mount_Mkdir_ExpectedPerm(t *testing.T) {
    91  	clearTestDependencies()
    92  
    93  	// NOTE: Do NOT set osRemoveAll because the mount succeeds. Expect it not to
    94  	// be called.
    95  
    96  	target := "/fake/path"
    97  	osMkdirAll = func(path string, perm os.FileMode) error {
    98  		if perm != os.FileMode(0700) {
    99  			t.Errorf("expected perm: %v, got: %v", os.FileMode(0700), perm)
   100  			return errors.New("unexpected perm")
   101  		}
   102  		return nil
   103  	}
   104  	controllerLunToName = func(ctx context.Context, controller, lun uint8) (string, error) {
   105  		return "", nil
   106  	}
   107  	unixMount = func(source string, target string, fstype string, flags uintptr, data string) error {
   108  		// Fake the mount success
   109  		return nil
   110  	}
   111  
   112  	if err := Mount(
   113  		context.Background(),
   114  		0,
   115  		0,
   116  		target,
   117  		false,
   118  		false,
   119  		nil,
   120  		nil,
   121  	); err != nil {
   122  		t.Fatalf("expected nil error got: %v", err)
   123  	}
   124  }
   125  
   126  func Test_Mount_ControllerLunToName_Valid_Controller(t *testing.T) {
   127  	clearTestDependencies()
   128  
   129  	// NOTE: Do NOT set osRemoveAll because the mount succeeds. Expect it not to
   130  	// be called.
   131  
   132  	osMkdirAll = func(path string, perm os.FileMode) error {
   133  		return nil
   134  	}
   135  	expectedController := uint8(2)
   136  	controllerLunToName = func(ctx context.Context, controller, lun uint8) (string, error) {
   137  		if expectedController != controller {
   138  			t.Errorf("expected controller: %v, got: %v", expectedController, controller)
   139  			return "", errors.New("unexpected controller")
   140  		}
   141  		return "", nil
   142  	}
   143  	unixMount = func(source string, target string, fstype string, flags uintptr, data string) error {
   144  		// Fake the mount success
   145  		return nil
   146  	}
   147  
   148  	if err := Mount(
   149  		context.Background(),
   150  		expectedController,
   151  		0,
   152  		"/fake/path",
   153  		false,
   154  		false,
   155  		nil,
   156  		nil,
   157  	); err != nil {
   158  		t.Fatalf("expected nil error got: %v", err)
   159  	}
   160  }
   161  
   162  func Test_Mount_ControllerLunToName_Valid_Lun(t *testing.T) {
   163  	clearTestDependencies()
   164  
   165  	// NOTE: Do NOT set osRemoveAll because the mount succeeds. Expect it not to
   166  	// be called.
   167  
   168  	osMkdirAll = func(path string, perm os.FileMode) error {
   169  		return nil
   170  	}
   171  	expectedLun := uint8(2)
   172  	controllerLunToName = func(ctx context.Context, controller, lun uint8) (string, error) {
   173  		if expectedLun != lun {
   174  			t.Errorf("expected lun: %v, got: %v", expectedLun, lun)
   175  			return "", errors.New("unexpected lun")
   176  		}
   177  		return "", nil
   178  	}
   179  	unixMount = func(source string, target string, fstype string, flags uintptr, data string) error {
   180  		// Fake the mount success
   181  		return nil
   182  	}
   183  
   184  	if err := Mount(
   185  		context.Background(),
   186  		0,
   187  		expectedLun,
   188  		"/fake/path",
   189  		false,
   190  		false,
   191  		nil,
   192  		nil,
   193  	); err != nil {
   194  		t.Fatalf("expected nil error got: %v", err)
   195  	}
   196  }
   197  
   198  func Test_Mount_Calls_RemoveAll_OnMountFailure(t *testing.T) {
   199  	clearTestDependencies()
   200  
   201  	osMkdirAll = func(path string, perm os.FileMode) error {
   202  		return nil
   203  	}
   204  	controllerLunToName = func(ctx context.Context, controller, lun uint8) (string, error) {
   205  		return "", nil
   206  	}
   207  	target := "/fake/path"
   208  	removeAllCalled := false
   209  	osRemoveAll = func(path string) error {
   210  		removeAllCalled = true
   211  		if path != target {
   212  			t.Errorf("expected path: %v, got: %v", target, path)
   213  			return errors.New("unexpected path")
   214  		}
   215  		return nil
   216  	}
   217  	expectedErr := errors.New("unexpected mount failure")
   218  	unixMount = func(source string, target string, fstype string, flags uintptr, data string) error {
   219  		// Fake the mount failure to test remove is called
   220  		return expectedErr
   221  	}
   222  
   223  	if err := Mount(
   224  		context.Background(),
   225  		0,
   226  		0,
   227  		target,
   228  		false,
   229  		false,
   230  		nil,
   231  		nil,
   232  	); !errors.Is(err, expectedErr) {
   233  		t.Fatalf("expected err: %v, got: %v", expectedErr, err)
   234  	}
   235  	if !removeAllCalled {
   236  		t.Fatal("expected os.RemoveAll to be called on mount failure")
   237  	}
   238  }
   239  
   240  func Test_Mount_Valid_Source(t *testing.T) {
   241  	clearTestDependencies()
   242  
   243  	// NOTE: Do NOT set osRemoveAll because the mount succeeds. Expect it not to
   244  	// be called.
   245  
   246  	osMkdirAll = func(path string, perm os.FileMode) error {
   247  		return nil
   248  	}
   249  	expectedSource := "/dev/sdz"
   250  	controllerLunToName = func(ctx context.Context, controller, lun uint8) (string, error) {
   251  		return expectedSource, nil
   252  	}
   253  	unixMount = func(source string, target string, fstype string, flags uintptr, data string) error {
   254  		if expectedSource != source {
   255  			t.Errorf("expected source: %s, got: %s", expectedSource, source)
   256  			return errors.New("unexpected source")
   257  		}
   258  		return nil
   259  	}
   260  	err := Mount(context.Background(), 0, 0, "/fake/path", false, false, nil, nil)
   261  	if err != nil {
   262  		t.Fatalf("expected nil err, got: %v", err)
   263  	}
   264  }
   265  
   266  func Test_Mount_Valid_Target(t *testing.T) {
   267  	clearTestDependencies()
   268  
   269  	// NOTE: Do NOT set osRemoveAll because the mount succeeds. Expect it not to
   270  	// be called.
   271  
   272  	osMkdirAll = func(path string, perm os.FileMode) error {
   273  		return nil
   274  	}
   275  	controllerLunToName = func(ctx context.Context, controller, lun uint8) (string, error) {
   276  		return "", nil
   277  	}
   278  	expectedTarget := "/fake/path"
   279  	unixMount = func(source string, target string, fstype string, flags uintptr, data string) error {
   280  		if expectedTarget != target {
   281  			t.Errorf("expected target: %s, got: %s", expectedTarget, target)
   282  			return errors.New("unexpected target")
   283  		}
   284  		return nil
   285  	}
   286  
   287  	if err := Mount(
   288  		context.Background(),
   289  		0,
   290  		0,
   291  		expectedTarget,
   292  		false,
   293  		false,
   294  		nil,
   295  		nil,
   296  	); err != nil {
   297  		t.Fatalf("expected nil err, got: %v", err)
   298  	}
   299  }
   300  
   301  func Test_Mount_Valid_FSType(t *testing.T) {
   302  	clearTestDependencies()
   303  
   304  	// NOTE: Do NOT set osRemoveAll because the mount succeeds. Expect it not to
   305  	// be called.
   306  
   307  	osMkdirAll = func(path string, perm os.FileMode) error {
   308  		return nil
   309  	}
   310  	controllerLunToName = func(ctx context.Context, controller, lun uint8) (string, error) {
   311  		return "", nil
   312  	}
   313  	unixMount = func(source string, target string, fstype string, flags uintptr, data string) error {
   314  		expectedFSType := "ext4"
   315  		if expectedFSType != fstype {
   316  			t.Errorf("expected fstype: %s, got: %s", expectedFSType, fstype)
   317  			return errors.New("unexpected fstype")
   318  		}
   319  		return nil
   320  	}
   321  
   322  	if err := Mount(
   323  		context.Background(),
   324  		0,
   325  		0,
   326  		"/fake/path",
   327  		false,
   328  		false,
   329  		nil,
   330  		nil,
   331  	); err != nil {
   332  		t.Fatalf("expected nil err, got: %v", err)
   333  	}
   334  }
   335  
   336  func Test_Mount_Valid_Flags(t *testing.T) {
   337  	clearTestDependencies()
   338  
   339  	// NOTE: Do NOT set osRemoveAll because the mount succeeds. Expect it not to
   340  	// be called.
   341  
   342  	osMkdirAll = func(path string, perm os.FileMode) error {
   343  		return nil
   344  	}
   345  	controllerLunToName = func(ctx context.Context, controller, lun uint8) (string, error) {
   346  		return "", nil
   347  	}
   348  	unixMount = func(source string, target string, fstype string, flags uintptr, data string) error {
   349  		expectedFlags := uintptr(0)
   350  		if expectedFlags != flags {
   351  			t.Errorf("expected flags: %v, got: %v", expectedFlags, flags)
   352  			return errors.New("unexpected flags")
   353  		}
   354  		return nil
   355  	}
   356  
   357  	if err := Mount(
   358  		context.Background(),
   359  		0,
   360  		0,
   361  		"/fake/path",
   362  		false,
   363  		false,
   364  		nil,
   365  		nil,
   366  	); err != nil {
   367  		t.Fatalf("expected nil err, got: %v", err)
   368  	}
   369  }
   370  
   371  func Test_Mount_Readonly_Valid_Flags(t *testing.T) {
   372  	clearTestDependencies()
   373  
   374  	// NOTE: Do NOT set osRemoveAll because the mount succeeds. Expect it not to
   375  	// be called.
   376  
   377  	osMkdirAll = func(path string, perm os.FileMode) error {
   378  		return nil
   379  	}
   380  	controllerLunToName = func(ctx context.Context, controller, lun uint8) (string, error) {
   381  		return "", nil
   382  	}
   383  	unixMount = func(source string, target string, fstype string, flags uintptr, data string) error {
   384  		expectedFlags := uintptr(unix.MS_RDONLY)
   385  		if expectedFlags != flags {
   386  			t.Errorf("expected flags: %v, got: %v", expectedFlags, flags)
   387  			return errors.New("unexpected flags")
   388  		}
   389  		return nil
   390  	}
   391  
   392  	if err := Mount(
   393  		context.Background(),
   394  		0,
   395  		0,
   396  		"/fake/path",
   397  		true,
   398  		false,
   399  		nil,
   400  		nil,
   401  	); err != nil {
   402  		t.Fatalf("expected nil err, got: %v", err)
   403  	}
   404  }
   405  
   406  func Test_Mount_Valid_Data(t *testing.T) {
   407  	clearTestDependencies()
   408  
   409  	// NOTE: Do NOT set osRemoveAll because the mount succeeds. Expect it not to
   410  	// be called.
   411  
   412  	osMkdirAll = func(path string, perm os.FileMode) error {
   413  		return nil
   414  	}
   415  	controllerLunToName = func(ctx context.Context, controller, lun uint8) (string, error) {
   416  		return "", nil
   417  	}
   418  	unixMount = func(source string, target string, fstype string, flags uintptr, data string) error {
   419  		if data != "" {
   420  			t.Errorf("expected empty data, got: %s", data)
   421  			return errors.New("unexpected data")
   422  		}
   423  		return nil
   424  	}
   425  
   426  	if err := Mount(
   427  		context.Background(),
   428  		0,
   429  		0,
   430  		"/fake/path",
   431  		false,
   432  		false,
   433  		nil,
   434  		nil,
   435  	); err != nil {
   436  		t.Fatalf("expected nil err, got: %v", err)
   437  	}
   438  }
   439  
   440  func Test_Mount_Readonly_Valid_Data(t *testing.T) {
   441  	clearTestDependencies()
   442  
   443  	// NOTE: Do NOT set osRemoveAll because the mount succeeds. Expect it not to
   444  	// be called.
   445  
   446  	osMkdirAll = func(path string, perm os.FileMode) error {
   447  		return nil
   448  	}
   449  	controllerLunToName = func(ctx context.Context, controller, lun uint8) (string, error) {
   450  		return "", nil
   451  	}
   452  	unixMount = func(source string, target string, fstype string, flags uintptr, data string) error {
   453  		expectedData := "noload"
   454  		if expectedData != data {
   455  			t.Errorf("expected data: %s, got: %s", expectedData, data)
   456  			return errors.New("unexpected data")
   457  		}
   458  		return nil
   459  	}
   460  
   461  	if err := Mount(
   462  		context.Background(),
   463  		0,
   464  		0,
   465  		"/fake/path",
   466  		true,
   467  		false,
   468  		nil,
   469  		nil,
   470  	); err != nil {
   471  		t.Fatalf("expected nil err, got: %v", err)
   472  	}
   473  }
   474  
   475  // dm-verity tests
   476  
   477  func Test_CreateVerityTarget_And_Mount_Called_With_Correct_Parameters(t *testing.T) {
   478  	clearTestDependencies()
   479  
   480  	expectedVerityName := fmt.Sprintf(verityDeviceFmt, 0, 0, "hash")
   481  	expectedSource := "/dev/sdb"
   482  	expectedMapperPath := fmt.Sprintf("/dev/mapper/%s", expectedVerityName)
   483  	expectedTarget := "/foo"
   484  	createVerityTargetCalled := false
   485  
   486  	controllerLunToName = func(_ context.Context, _, _ uint8) (string, error) {
   487  		return expectedSource, nil
   488  	}
   489  
   490  	osMkdirAll = func(_ string, _ os.FileMode) error {
   491  		return nil
   492  	}
   493  
   494  	vInfo := &guestresource.DeviceVerityInfo{
   495  		RootDigest: "hash",
   496  	}
   497  	createVerityTarget = func(_ context.Context, source, name string, verityInfo *guestresource.DeviceVerityInfo) (string, error) {
   498  		createVerityTargetCalled = true
   499  		if source != expectedSource {
   500  			t.Errorf("expected source %s, got %s", expectedSource, source)
   501  		}
   502  		if name != expectedVerityName {
   503  			t.Errorf("expected verity target name %s, got %s", expectedVerityName, name)
   504  		}
   505  		return expectedMapperPath, nil
   506  	}
   507  
   508  	unixMount = func(source string, target string, fstype string, flags uintptr, data string) error {
   509  		if source != expectedMapperPath {
   510  			t.Errorf("expected unixMount source %s, got %s", expectedMapperPath, source)
   511  		}
   512  		if target != expectedTarget {
   513  			t.Errorf("expected unixMount target %s, got %s", expectedTarget, target)
   514  		}
   515  		return nil
   516  	}
   517  
   518  	if err := Mount(
   519  		context.Background(),
   520  		0,
   521  		0,
   522  		expectedTarget,
   523  		true,
   524  		false,
   525  		nil,
   526  		vInfo,
   527  	); err != nil {
   528  		t.Fatalf("unexpected error during Mount: %s", err)
   529  	}
   530  	if !createVerityTargetCalled {
   531  		t.Fatalf("expected createVerityTargetCalled to be called")
   532  	}
   533  }
   534  
   535  func Test_osMkdirAllFails_And_RemoveDevice_Called(t *testing.T) {
   536  	clearTestDependencies()
   537  
   538  	expectedError := errors.New("osMkdirAll error")
   539  	expectedVerityName := fmt.Sprintf(verityDeviceFmt, 0, 0, "hash")
   540  	removeDeviceCalled := false
   541  
   542  	controllerLunToName = func(_ context.Context, _, _ uint8) (string, error) {
   543  		return "/dev/sdb", nil
   544  	}
   545  
   546  	osMkdirAll = func(_ string, _ os.FileMode) error {
   547  		return expectedError
   548  	}
   549  
   550  	verityInfo := &guestresource.DeviceVerityInfo{
   551  		RootDigest: "hash",
   552  	}
   553  
   554  	createVerityTarget = func(_ context.Context, _, _ string, _ *guestresource.DeviceVerityInfo) (string, error) {
   555  		return fmt.Sprintf("/dev/mapper/%s", expectedVerityName), nil
   556  	}
   557  
   558  	removeDevice = func(name string) error {
   559  		removeDeviceCalled = true
   560  		if name != expectedVerityName {
   561  			t.Errorf("expected RemoveDevice name %s, got %s", expectedVerityName, name)
   562  		}
   563  		return nil
   564  	}
   565  
   566  	if err := Mount(
   567  		context.Background(),
   568  		0,
   569  		0,
   570  		"/foo",
   571  		true,
   572  		false,
   573  		nil,
   574  		verityInfo,
   575  	); err != expectedError {
   576  		t.Fatalf("expected Mount error %s, got %s", expectedError, err)
   577  	}
   578  	if !removeDeviceCalled {
   579  		t.Fatal("expected removeDevice to be called")
   580  	}
   581  }
   582  
   583  func Test_Mount_EncryptDevice_Called(t *testing.T) {
   584  	clearTestDependencies()
   585  
   586  	osMkdirAll = func(string, os.FileMode) error {
   587  		return nil
   588  	}
   589  	controllerLunToName = func(context.Context, uint8, uint8) (string, error) {
   590  		return "", nil
   591  	}
   592  	unixMount = func(string, string, string, uintptr, string) error {
   593  		return nil
   594  	}
   595  	encryptDeviceCalled := false
   596  	encryptDevice = func(_ context.Context, source string, devName string) (string, error) {
   597  		expectedCryptTarget := fmt.Sprintf(cryptDeviceFmt, 0, 0)
   598  		if devName != expectedCryptTarget {
   599  			t.Fatalf("expected crypt device %q got %q", expectedCryptTarget, devName)
   600  		}
   601  		encryptDeviceCalled = true
   602  		return "", nil
   603  	}
   604  	if err := Mount(
   605  		context.Background(),
   606  		0,
   607  		0,
   608  		"/fake/path",
   609  		false,
   610  		true,
   611  		nil,
   612  		nil,
   613  	); err != nil {
   614  		t.Fatalf("expected nil error, got: %s", err)
   615  	}
   616  	if !encryptDeviceCalled {
   617  		t.Fatal("expected encryptDevice to be called")
   618  	}
   619  }
   620  
   621  func Test_Mount_RemoveAllCalled_When_EncryptDevice_Fails(t *testing.T) {
   622  	clearTestDependencies()
   623  
   624  	osMkdirAll = func(string, os.FileMode) error {
   625  		return nil
   626  	}
   627  	controllerLunToName = func(context.Context, uint8, uint8) (string, error) {
   628  		return "", nil
   629  	}
   630  	unixMount = func(string, string, string, uintptr, string) error {
   631  		return nil
   632  	}
   633  	encryptDeviceError := errors.New("encrypt device error")
   634  	encryptDevice = func(context.Context, string, string) (string, error) {
   635  		return "", encryptDeviceError
   636  	}
   637  	removeAllCalled := false
   638  	osRemoveAll = func(string) error {
   639  		removeAllCalled = true
   640  		return nil
   641  	}
   642  
   643  	err := Mount(
   644  		context.Background(),
   645  		0,
   646  		0,
   647  		"/fake/path",
   648  		false,
   649  		true,
   650  		nil,
   651  		nil,
   652  	)
   653  	if err == nil {
   654  		t.Fatalf("expected to fail")
   655  	}
   656  	if errors.Unwrap(err) != encryptDeviceError {
   657  		t.Fatalf("expected error %q, got %q", encryptDeviceError, err)
   658  	}
   659  	if !removeAllCalled {
   660  		t.Fatal("osRemoveAll was not called")
   661  	}
   662  }
   663  
   664  func Test_Unmount_CleanupCryptDevice_Called(t *testing.T) {
   665  	clearTestDependencies()
   666  
   667  	storageUnmountPath = func(context.Context, string, bool) error {
   668  		return nil
   669  	}
   670  	cleanupCryptDeviceCalled := false
   671  	cleanupCryptDevice = func(devName string) error {
   672  		expectedDevName := fmt.Sprintf(cryptDeviceFmt, 0, 0)
   673  		if devName != expectedDevName {
   674  			t.Fatalf("expected crypt target %q, got %q", expectedDevName, devName)
   675  		}
   676  		cleanupCryptDeviceCalled = true
   677  		return nil
   678  	}
   679  
   680  	if err := Unmount(context.Background(), 0, 0, "/fake/path", true, nil); err != nil {
   681  		t.Fatalf("unexpected error: %s", err)
   682  	}
   683  	if !cleanupCryptDeviceCalled {
   684  		t.Fatal("cleanupCryptDevice not called")
   685  	}
   686  }
   687  

View as plain text