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 }