github.com/hernad/nomad@v1.6.112/nomad/structs/volume_test.go (about)

     1  // Copyright (c) HashiCorp, Inc.
     2  // SPDX-License-Identifier: MPL-2.0
     3  
     4  package structs
     5  
     6  import (
     7  	"testing"
     8  
     9  	"github.com/hernad/nomad/ci"
    10  	"github.com/shoenig/test/must"
    11  	"github.com/stretchr/testify/require"
    12  )
    13  
    14  func TestVolumeRequest_Validate(t *testing.T) {
    15  	ci.Parallel(t)
    16  
    17  	testCases := []struct {
    18  		name           string
    19  		expected       []string
    20  		canariesCount  int
    21  		taskGroupCount int
    22  		req            *VolumeRequest
    23  	}{
    24  		{
    25  			name:     "host volume with empty source",
    26  			expected: []string{"volume has an empty source"},
    27  			req: &VolumeRequest{
    28  				Type: VolumeTypeHost,
    29  			},
    30  		},
    31  		{
    32  			name: "host volume with CSI volume config",
    33  			expected: []string{
    34  				"host volumes cannot have an access mode",
    35  				"host volumes cannot have an attachment mode",
    36  				"host volumes cannot have mount options",
    37  				"volume cannot be per_alloc for system or sysbatch jobs",
    38  				"volume cannot be per_alloc when canaries are in use",
    39  			},
    40  			canariesCount: 1,
    41  			req: &VolumeRequest{
    42  				Type:           VolumeTypeHost,
    43  				ReadOnly:       false,
    44  				AccessMode:     CSIVolumeAccessModeSingleNodeReader,
    45  				AttachmentMode: CSIVolumeAttachmentModeBlockDevice,
    46  				MountOptions: &CSIMountOptions{
    47  					FSType:     "ext4",
    48  					MountFlags: []string{"ro"},
    49  				},
    50  				PerAlloc: true,
    51  			},
    52  		},
    53  		{
    54  			name: "CSI volume multi-reader-single-writer access mode",
    55  			expected: []string{
    56  				"volume with multi-node-single-writer access mode allows only one writer",
    57  			},
    58  			taskGroupCount: 2,
    59  			req: &VolumeRequest{
    60  				Type:       VolumeTypeCSI,
    61  				AccessMode: CSIVolumeAccessModeMultiNodeSingleWriter,
    62  			},
    63  		},
    64  		{
    65  			name: "CSI volume single reader access mode",
    66  			expected: []string{
    67  				"volume with single-node-reader-only access mode allows only one reader",
    68  			},
    69  			taskGroupCount: 2,
    70  			req: &VolumeRequest{
    71  				Type:       VolumeTypeCSI,
    72  				AccessMode: CSIVolumeAccessModeSingleNodeReader,
    73  				ReadOnly:   true,
    74  			},
    75  		},
    76  		{
    77  			name: "CSI volume per-alloc with canaries",
    78  			expected: []string{
    79  				"volume cannot be per_alloc for system or sysbatch jobs",
    80  				"volume cannot be per_alloc when canaries are in use",
    81  			},
    82  			canariesCount: 1,
    83  			req: &VolumeRequest{
    84  				Type:     VolumeTypeCSI,
    85  				PerAlloc: true,
    86  			},
    87  		},
    88  	}
    89  
    90  	for _, tc := range testCases {
    91  		t.Run(tc.name, func(t *testing.T) {
    92  			err := tc.req.Validate(JobTypeSystem, tc.taskGroupCount, tc.canariesCount)
    93  			for _, expected := range tc.expected {
    94  				require.Contains(t, err.Error(), expected)
    95  			}
    96  		})
    97  	}
    98  
    99  }
   100  
   101  func TestVolumeRequest_Equal(t *testing.T) {
   102  	ci.Parallel(t)
   103  
   104  	must.Equal[*VolumeRequest](t, nil, nil)
   105  	must.NotEqual[*VolumeRequest](t, nil, new(VolumeRequest))
   106  
   107  	must.StructEqual(t, &VolumeRequest{
   108  		Name:           "name",
   109  		Type:           "type",
   110  		Source:         "source",
   111  		ReadOnly:       true,
   112  		AccessMode:     "access",
   113  		AttachmentMode: "attachment",
   114  		MountOptions: &CSIMountOptions{
   115  			FSType:     "fs1",
   116  			MountFlags: []string{"flag1"},
   117  		},
   118  		PerAlloc: true,
   119  	}, []must.Tweak[*VolumeRequest]{{
   120  		Field: "Name",
   121  		Apply: func(vr *VolumeRequest) { vr.Name = "name2" },
   122  	}, {
   123  		Field: "Type",
   124  		Apply: func(vr *VolumeRequest) { vr.Type = "type2" },
   125  	}, {
   126  		Field: "Source",
   127  		Apply: func(vr *VolumeRequest) { vr.Source = "source2" },
   128  	}, {
   129  		Field: "ReadOnly",
   130  		Apply: func(vr *VolumeRequest) { vr.ReadOnly = false },
   131  	}, {
   132  		Field: "AccessMode",
   133  		Apply: func(vr *VolumeRequest) { vr.AccessMode = "access2" },
   134  	}, {
   135  		Field: "AttachmentMode",
   136  		Apply: func(vr *VolumeRequest) { vr.AttachmentMode = "attachment2" },
   137  	}, {
   138  		Field: "MountOptions",
   139  		Apply: func(vr *VolumeRequest) { vr.MountOptions = nil },
   140  	}, {
   141  		Field: "PerAlloc",
   142  		Apply: func(vr *VolumeRequest) { vr.PerAlloc = false },
   143  	}})
   144  }
   145  
   146  func TestVolumeMount_Equal(t *testing.T) {
   147  	ci.Parallel(t)
   148  
   149  	must.Equal[*VolumeMount](t, nil, nil)
   150  	must.NotEqual[*VolumeMount](t, nil, new(VolumeMount))
   151  
   152  	must.StructEqual(t, &VolumeMount{
   153  		Volume:          "volume",
   154  		Destination:     "destination",
   155  		ReadOnly:        true,
   156  		PropagationMode: "mode",
   157  	}, []must.Tweak[*VolumeMount]{{
   158  		Field: "Volume",
   159  		Apply: func(vm *VolumeMount) { vm.Volume = "vol2" },
   160  	}, {
   161  		Field: "Destination",
   162  		Apply: func(vm *VolumeMount) { vm.Destination = "dest2" },
   163  	}, {
   164  		Field: "ReadOnly",
   165  		Apply: func(vm *VolumeMount) { vm.ReadOnly = false },
   166  	}, {
   167  		Field: "PropogationMode",
   168  		Apply: func(vm *VolumeMount) { vm.PropagationMode = "mode2" },
   169  	}})
   170  }