github.com/anth0d/nomad@v0.0.0-20221214183521-ae3a0a2cad06/client/allocrunner/taskrunner/volume_hook_test.go (about) 1 package taskrunner 2 3 import ( 4 "testing" 5 6 "github.com/hashicorp/nomad/ci" 7 "github.com/hashicorp/nomad/client/allocrunner/interfaces" 8 "github.com/hashicorp/nomad/client/pluginmanager/csimanager" 9 cstructs "github.com/hashicorp/nomad/client/structs" 10 "github.com/hashicorp/nomad/client/taskenv" 11 "github.com/hashicorp/nomad/helper/testlog" 12 "github.com/hashicorp/nomad/nomad/mock" 13 "github.com/hashicorp/nomad/nomad/structs" 14 "github.com/hashicorp/nomad/plugins/drivers" 15 dtu "github.com/hashicorp/nomad/plugins/drivers/testutils" 16 "github.com/stretchr/testify/require" 17 ) 18 19 func TestVolumeHook_PartitionMountsByVolume_Works(t *testing.T) { 20 ci.Parallel(t) 21 22 mounts := []*structs.VolumeMount{ 23 { 24 Volume: "foo", 25 Destination: "/tmp", 26 ReadOnly: false, 27 }, 28 { 29 Volume: "foo", 30 Destination: "/bar", 31 ReadOnly: false, 32 }, 33 { 34 Volume: "baz", 35 Destination: "/baz", 36 ReadOnly: false, 37 }, 38 } 39 40 expected := map[string][]*structs.VolumeMount{ 41 "foo": { 42 { 43 Volume: "foo", 44 Destination: "/tmp", 45 ReadOnly: false, 46 }, 47 { 48 Volume: "foo", 49 Destination: "/bar", 50 ReadOnly: false, 51 }, 52 }, 53 "baz": { 54 { 55 Volume: "baz", 56 Destination: "/baz", 57 ReadOnly: false, 58 }, 59 }, 60 } 61 62 // Test with a real collection 63 64 partitioned := partitionMountsByVolume(mounts) 65 require.Equal(t, expected, partitioned) 66 67 // Test with nil/emptylist 68 69 partitioned = partitionMountsByVolume(nil) 70 require.Equal(t, map[string][]*structs.VolumeMount{}, partitioned) 71 } 72 73 func TestVolumeHook_prepareCSIVolumes(t *testing.T) { 74 ci.Parallel(t) 75 76 req := &interfaces.TaskPrestartRequest{ 77 Task: &structs.Task{ 78 Name: "test", 79 Driver: "mock", 80 VolumeMounts: []*structs.VolumeMount{ 81 { 82 Volume: "foo", 83 Destination: "/bar", 84 }, 85 }, 86 }, 87 } 88 89 volumes := map[string]*structs.VolumeRequest{ 90 "foo": { 91 Type: "csi", 92 Source: "my-test-volume", 93 }, 94 } 95 96 cases := []struct { 97 Name string 98 Driver drivers.DriverPlugin 99 Expected []*drivers.MountConfig 100 ExpectedError string 101 }{ 102 { 103 Name: "supported driver", 104 Driver: &dtu.MockDriver{ 105 CapabilitiesF: func() (*drivers.Capabilities, error) { 106 return &drivers.Capabilities{ 107 MountConfigs: drivers.MountConfigSupportAll, 108 }, nil 109 }, 110 }, 111 Expected: []*drivers.MountConfig{ 112 { 113 HostPath: "/mnt/my-test-volume", 114 TaskPath: "/bar", 115 }, 116 }, 117 }, 118 { 119 Name: "unsupported driver", 120 Driver: &dtu.MockDriver{ 121 CapabilitiesF: func() (*drivers.Capabilities, error) { 122 return &drivers.Capabilities{ 123 MountConfigs: drivers.MountConfigSupportNone, 124 }, nil 125 }, 126 }, 127 ExpectedError: "task driver \"mock\" for \"test\" does not support CSI", 128 }, 129 } 130 131 for _, tc := range cases { 132 t.Run(tc.Name, func(t *testing.T) { 133 134 tr := &TaskRunner{ 135 task: req.Task, 136 driver: tc.Driver, 137 allocHookResources: &cstructs.AllocHookResources{ 138 CSIMounts: map[string]*csimanager.MountInfo{ 139 "foo": { 140 Source: "/mnt/my-test-volume", 141 }, 142 }, 143 }, 144 } 145 146 hook := &volumeHook{ 147 logger: testlog.HCLogger(t), 148 alloc: structs.MockAlloc(), 149 runner: tr, 150 } 151 mounts, err := hook.prepareCSIVolumes(req, volumes) 152 153 if tc.ExpectedError != "" { 154 require.EqualError(t, err, tc.ExpectedError) 155 } else { 156 require.NoError(t, err) 157 } 158 require.Equal(t, tc.Expected, mounts) 159 }) 160 } 161 } 162 163 func TestVolumeHook_Interpolation(t *testing.T) { 164 ci.Parallel(t) 165 166 alloc := mock.Alloc() 167 task := alloc.Job.TaskGroups[0].Tasks[0] 168 taskEnv := taskenv.NewBuilder(mock.Node(), alloc, task, "global").SetHookEnv("volume", 169 map[string]string{ 170 "PROPAGATION_MODE": "private", 171 "VOLUME_ID": "my-other-volume", 172 }, 173 ).Build() 174 175 mounts := []*structs.VolumeMount{ 176 { 177 Volume: "foo", 178 Destination: "/tmp", 179 ReadOnly: false, 180 PropagationMode: "bidirectional", 181 }, 182 { 183 Volume: "foo", 184 Destination: "/bar-${NOMAD_JOB_NAME}", 185 ReadOnly: false, 186 PropagationMode: "bidirectional", 187 }, 188 { 189 Volume: "${VOLUME_ID}", 190 Destination: "/baz", 191 ReadOnly: false, 192 PropagationMode: "bidirectional", 193 }, 194 { 195 Volume: "foo", 196 Destination: "/quux", 197 ReadOnly: false, 198 PropagationMode: "${PROPAGATION_MODE}", 199 }, 200 } 201 202 expected := []*structs.VolumeMount{ 203 { 204 Volume: "foo", 205 Destination: "/tmp", 206 ReadOnly: false, 207 PropagationMode: "bidirectional", 208 }, 209 { 210 Volume: "foo", 211 Destination: "/bar-my-job", 212 ReadOnly: false, 213 PropagationMode: "bidirectional", 214 }, 215 { 216 Volume: "my-other-volume", 217 Destination: "/baz", 218 ReadOnly: false, 219 PropagationMode: "bidirectional", 220 }, 221 { 222 Volume: "foo", 223 Destination: "/quux", 224 ReadOnly: false, 225 PropagationMode: "private", 226 }, 227 } 228 229 interpolateVolumeMounts(mounts, taskEnv) 230 require.Equal(t, expected, mounts) 231 }