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  }