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  }