github.com/Ilhicas/nomad@v1.0.4-0.20210304152020-e86851182bc3/nomad/structs/csi_test.go (about) 1 package structs 2 3 import ( 4 "reflect" 5 "testing" 6 "time" 7 8 "github.com/stretchr/testify/require" 9 ) 10 11 func TestCSIVolumeClaim(t *testing.T) { 12 vol := NewCSIVolume("", 0) 13 vol.AccessMode = CSIVolumeAccessModeMultiNodeSingleWriter 14 vol.Schedulable = true 15 16 alloc := &Allocation{ID: "a1", Namespace: "n", JobID: "j"} 17 claim := &CSIVolumeClaim{ 18 AllocationID: alloc.ID, 19 NodeID: "foo", 20 Mode: CSIVolumeClaimRead, 21 } 22 23 require.NoError(t, vol.ClaimRead(claim, alloc)) 24 require.True(t, vol.ReadSchedulable()) 25 require.True(t, vol.WriteSchedulable()) 26 require.NoError(t, vol.ClaimRead(claim, alloc)) 27 28 claim.Mode = CSIVolumeClaimWrite 29 require.NoError(t, vol.ClaimWrite(claim, alloc)) 30 require.True(t, vol.ReadSchedulable()) 31 require.False(t, vol.WriteFreeClaims()) 32 33 vol.ClaimRelease(claim) 34 require.True(t, vol.ReadSchedulable()) 35 require.False(t, vol.WriteFreeClaims()) 36 37 claim.State = CSIVolumeClaimStateReadyToFree 38 vol.ClaimRelease(claim) 39 require.True(t, vol.ReadSchedulable()) 40 require.True(t, vol.WriteFreeClaims()) 41 42 vol.AccessMode = CSIVolumeAccessModeMultiNodeMultiWriter 43 require.NoError(t, vol.ClaimWrite(claim, alloc)) 44 require.NoError(t, vol.ClaimWrite(claim, alloc)) 45 require.True(t, vol.WriteFreeClaims()) 46 } 47 48 func TestVolume_Copy(t *testing.T) { 49 50 a1 := MockAlloc() 51 a2 := MockAlloc() 52 a3 := MockAlloc() 53 c1 := &CSIVolumeClaim{ 54 AllocationID: a1.ID, 55 NodeID: a1.NodeID, 56 ExternalNodeID: "c1", 57 Mode: CSIVolumeClaimRead, 58 State: CSIVolumeClaimStateTaken, 59 } 60 c2 := &CSIVolumeClaim{ 61 AllocationID: a2.ID, 62 NodeID: a2.NodeID, 63 ExternalNodeID: "c2", 64 Mode: CSIVolumeClaimRead, 65 State: CSIVolumeClaimStateNodeDetached, 66 } 67 c3 := &CSIVolumeClaim{ 68 AllocationID: a3.ID, 69 NodeID: a3.NodeID, 70 ExternalNodeID: "c3", 71 Mode: CSIVolumeClaimWrite, 72 State: CSIVolumeClaimStateTaken, 73 } 74 75 v1 := &CSIVolume{ 76 ID: "vol1", 77 Name: "vol1", 78 ExternalID: "vol-abcdef", 79 Namespace: "default", 80 Topologies: []*CSITopology{{Segments: map[string]string{"AZ1": "123"}}}, 81 AccessMode: CSIVolumeAccessModeSingleNodeWriter, 82 AttachmentMode: CSIVolumeAttachmentModeBlockDevice, 83 MountOptions: &CSIMountOptions{FSType: "ext4", MountFlags: []string{"ro", "noatime"}}, 84 Secrets: CSISecrets{"mysecret": "myvalue"}, 85 Parameters: map[string]string{"param1": "val1"}, 86 Context: map[string]string{"ctx1": "val1"}, 87 88 ReadAllocs: map[string]*Allocation{a1.ID: a1, a2.ID: nil}, 89 WriteAllocs: map[string]*Allocation{a3.ID: a3}, 90 91 ReadClaims: map[string]*CSIVolumeClaim{a1.ID: c1, a2.ID: c2}, 92 WriteClaims: map[string]*CSIVolumeClaim{a3.ID: c3}, 93 PastClaims: map[string]*CSIVolumeClaim{}, 94 95 Schedulable: true, 96 PluginID: "moosefs", 97 Provider: "n/a", 98 ProviderVersion: "1.0", 99 ControllerRequired: true, 100 ControllersHealthy: 2, 101 ControllersExpected: 2, 102 NodesHealthy: 4, 103 NodesExpected: 5, 104 ResourceExhausted: time.Now(), 105 } 106 107 v2 := v1.Copy() 108 if !reflect.DeepEqual(v1, v2) { 109 t.Fatalf("Copy() returned an unequal Volume; got %#v; want %#v", v1, v2) 110 } 111 112 v1.ReadClaims[a1.ID].State = CSIVolumeClaimStateReadyToFree 113 v1.ReadAllocs[a2.ID] = a2 114 v1.WriteAllocs[a3.ID].ClientStatus = AllocClientStatusComplete 115 v1.MountOptions.FSType = "zfs" 116 117 if v2.ReadClaims[a1.ID].State == CSIVolumeClaimStateReadyToFree { 118 t.Fatalf("Volume.Copy() failed; changes to original ReadClaims seen in copy") 119 } 120 if v2.ReadAllocs[a2.ID] != nil { 121 t.Fatalf("Volume.Copy() failed; changes to original ReadAllocs seen in copy") 122 } 123 if v2.WriteAllocs[a3.ID].ClientStatus == AllocClientStatusComplete { 124 t.Fatalf("Volume.Copy() failed; changes to original WriteAllocs seen in copy") 125 } 126 if v2.MountOptions.FSType == "zfs" { 127 t.Fatalf("Volume.Copy() failed; changes to original MountOptions seen in copy") 128 } 129 130 } 131 132 func TestCSIPluginJobs(t *testing.T) { 133 plug := NewCSIPlugin("foo", 1000) 134 controller := &Job{ 135 ID: "job", 136 Type: "service", 137 TaskGroups: []*TaskGroup{{ 138 Name: "foo", 139 Count: 11, 140 Tasks: []*Task{{ 141 CSIPluginConfig: &TaskCSIPluginConfig{ 142 ID: "foo", 143 Type: CSIPluginTypeController, 144 }, 145 }}, 146 }}, 147 } 148 149 summary := &JobSummary{} 150 151 plug.AddJob(controller, summary) 152 require.Equal(t, 11, plug.ControllersExpected) 153 154 // New job id & make it a system node plugin job 155 node := controller.Copy() 156 node.ID = "bar" 157 node.Type = "system" 158 node.TaskGroups[0].Tasks[0].CSIPluginConfig.Type = CSIPluginTypeNode 159 160 summary = &JobSummary{ 161 Summary: map[string]TaskGroupSummary{ 162 "foo": { 163 Queued: 1, 164 Running: 1, 165 Starting: 1, 166 }, 167 }, 168 } 169 170 plug.AddJob(node, summary) 171 require.Equal(t, 3, plug.NodesExpected) 172 173 plug.DeleteJob(node, summary) 174 require.Equal(t, 0, plug.NodesExpected) 175 require.Empty(t, plug.NodeJobs[""]) 176 177 plug.DeleteJob(controller, nil) 178 require.Equal(t, 0, plug.ControllersExpected) 179 require.Empty(t, plug.ControllerJobs[""]) 180 } 181 182 func TestCSIPluginCleanup(t *testing.T) { 183 plug := NewCSIPlugin("foo", 1000) 184 plug.AddPlugin("n0", &CSIInfo{ 185 PluginID: "foo", 186 AllocID: "a0", 187 Healthy: true, 188 Provider: "foo-provider", 189 RequiresControllerPlugin: true, 190 RequiresTopologies: false, 191 ControllerInfo: &CSIControllerInfo{}, 192 }) 193 194 plug.AddPlugin("n0", &CSIInfo{ 195 PluginID: "foo", 196 AllocID: "a0", 197 Healthy: true, 198 Provider: "foo-provider", 199 RequiresControllerPlugin: true, 200 RequiresTopologies: false, 201 NodeInfo: &CSINodeInfo{}, 202 }) 203 204 require.Equal(t, 1, plug.ControllersHealthy) 205 require.Equal(t, 1, plug.NodesHealthy) 206 207 err := plug.DeleteNode("n0") 208 require.NoError(t, err) 209 210 require.Equal(t, 0, plug.ControllersHealthy) 211 require.Equal(t, 0, plug.NodesHealthy) 212 213 require.Equal(t, 0, len(plug.Controllers)) 214 require.Equal(t, 0, len(plug.Nodes)) 215 } 216 217 func TestDeleteNodeForType_Controller(t *testing.T) { 218 info := &CSIInfo{ 219 PluginID: "foo", 220 AllocID: "a0", 221 Healthy: true, 222 Provider: "foo-provider", 223 RequiresControllerPlugin: true, 224 RequiresTopologies: false, 225 ControllerInfo: &CSIControllerInfo{}, 226 } 227 228 plug := NewCSIPlugin("foo", 1000) 229 230 plug.Controllers["n0"] = info 231 plug.ControllersHealthy = 1 232 233 err := plug.DeleteNodeForType("n0", CSIPluginTypeController) 234 require.NoError(t, err) 235 236 require.Equal(t, 0, plug.ControllersHealthy) 237 require.Equal(t, 0, len(plug.Controllers)) 238 } 239 240 func TestDeleteNodeForType_NilController(t *testing.T) { 241 plug := NewCSIPlugin("foo", 1000) 242 243 plug.Controllers["n0"] = nil 244 plug.ControllersHealthy = 1 245 246 err := plug.DeleteNodeForType("n0", CSIPluginTypeController) 247 require.Error(t, err) 248 require.Equal(t, 1, len(plug.Controllers)) 249 250 _, ok := plug.Controllers["foo"] 251 require.False(t, ok) 252 } 253 254 func TestDeleteNodeForType_Node(t *testing.T) { 255 info := &CSIInfo{ 256 PluginID: "foo", 257 AllocID: "a0", 258 Healthy: true, 259 Provider: "foo-provider", 260 RequiresControllerPlugin: true, 261 RequiresTopologies: false, 262 NodeInfo: &CSINodeInfo{}, 263 } 264 265 plug := NewCSIPlugin("foo", 1000) 266 267 plug.Nodes["n0"] = info 268 plug.NodesHealthy = 1 269 270 err := plug.DeleteNodeForType("n0", CSIPluginTypeNode) 271 require.NoError(t, err) 272 273 require.Equal(t, 0, plug.NodesHealthy) 274 require.Equal(t, 0, len(plug.Nodes)) 275 } 276 277 func TestDeleteNodeForType_NilNode(t *testing.T) { 278 plug := NewCSIPlugin("foo", 1000) 279 280 plug.Nodes["n0"] = nil 281 plug.NodesHealthy = 1 282 283 err := plug.DeleteNodeForType("n0", CSIPluginTypeNode) 284 require.Error(t, err) 285 require.Equal(t, 1, len(plug.Nodes)) 286 287 _, ok := plug.Nodes["foo"] 288 require.False(t, ok) 289 } 290 291 func TestDeleteNodeForType_Monolith(t *testing.T) { 292 controllerInfo := &CSIInfo{ 293 PluginID: "foo", 294 AllocID: "a0", 295 Healthy: true, 296 Provider: "foo-provider", 297 RequiresControllerPlugin: true, 298 RequiresTopologies: false, 299 ControllerInfo: &CSIControllerInfo{}, 300 } 301 302 nodeInfo := &CSIInfo{ 303 PluginID: "foo", 304 AllocID: "a0", 305 Healthy: true, 306 Provider: "foo-provider", 307 RequiresControllerPlugin: true, 308 RequiresTopologies: false, 309 NodeInfo: &CSINodeInfo{}, 310 } 311 312 plug := NewCSIPlugin("foo", 1000) 313 314 plug.Controllers["n0"] = controllerInfo 315 plug.ControllersHealthy = 1 316 317 plug.Nodes["n0"] = nodeInfo 318 plug.NodesHealthy = 1 319 320 err := plug.DeleteNodeForType("n0", CSIPluginTypeMonolith) 321 require.NoError(t, err) 322 323 require.Equal(t, 0, len(plug.Controllers)) 324 require.Equal(t, 0, len(plug.Nodes)) 325 326 _, ok := plug.Nodes["foo"] 327 require.False(t, ok) 328 329 _, ok = plug.Controllers["foo"] 330 require.False(t, ok) 331 } 332 333 func TestDeleteNodeForType_Monolith_NilController(t *testing.T) { 334 plug := NewCSIPlugin("foo", 1000) 335 336 plug.Controllers["n0"] = nil 337 plug.ControllersHealthy = 1 338 339 nodeInfo := &CSIInfo{ 340 PluginID: "foo", 341 AllocID: "a0", 342 Healthy: true, 343 Provider: "foo-provider", 344 RequiresControllerPlugin: true, 345 RequiresTopologies: false, 346 NodeInfo: &CSINodeInfo{}, 347 } 348 349 plug.Nodes["n0"] = nodeInfo 350 plug.NodesHealthy = 1 351 352 err := plug.DeleteNodeForType("n0", CSIPluginTypeMonolith) 353 require.Error(t, err) 354 355 require.Equal(t, 1, len(plug.Controllers)) 356 require.Equal(t, 0, len(plug.Nodes)) 357 358 _, ok := plug.Nodes["foo"] 359 require.False(t, ok) 360 361 _, ok = plug.Controllers["foo"] 362 require.False(t, ok) 363 } 364 365 func TestDeleteNodeForType_Monolith_NilNode(t *testing.T) { 366 plug := NewCSIPlugin("foo", 1000) 367 368 plug.Nodes["n0"] = nil 369 plug.NodesHealthy = 1 370 371 controllerInfo := &CSIInfo{ 372 PluginID: "foo", 373 AllocID: "a0", 374 Healthy: true, 375 Provider: "foo-provider", 376 RequiresControllerPlugin: true, 377 RequiresTopologies: false, 378 ControllerInfo: &CSIControllerInfo{}, 379 } 380 381 plug.Controllers["n0"] = controllerInfo 382 plug.ControllersHealthy = 1 383 384 err := plug.DeleteNodeForType("n0", CSIPluginTypeMonolith) 385 require.Error(t, err) 386 387 require.Equal(t, 0, len(plug.Controllers)) 388 require.Equal(t, 1, len(plug.Nodes)) 389 390 _, ok := plug.Nodes["foo"] 391 require.False(t, ok) 392 393 _, ok = plug.Controllers["foo"] 394 require.False(t, ok) 395 }