open-nomad/client/allocrunner/taskrunner/volume_hook_test.go
2023-04-10 15:36:59 +00:00

234 lines
5 KiB
Go

// Copyright (c) HashiCorp, Inc.
// SPDX-License-Identifier: MPL-2.0
package taskrunner
import (
"testing"
"github.com/hashicorp/nomad/ci"
"github.com/hashicorp/nomad/client/allocrunner/interfaces"
"github.com/hashicorp/nomad/client/pluginmanager/csimanager"
cstructs "github.com/hashicorp/nomad/client/structs"
"github.com/hashicorp/nomad/client/taskenv"
"github.com/hashicorp/nomad/helper/testlog"
"github.com/hashicorp/nomad/nomad/mock"
"github.com/hashicorp/nomad/nomad/structs"
"github.com/hashicorp/nomad/plugins/drivers"
dtu "github.com/hashicorp/nomad/plugins/drivers/testutils"
"github.com/stretchr/testify/require"
)
func TestVolumeHook_PartitionMountsByVolume_Works(t *testing.T) {
ci.Parallel(t)
mounts := []*structs.VolumeMount{
{
Volume: "foo",
Destination: "/tmp",
ReadOnly: false,
},
{
Volume: "foo",
Destination: "/bar",
ReadOnly: false,
},
{
Volume: "baz",
Destination: "/baz",
ReadOnly: false,
},
}
expected := map[string][]*structs.VolumeMount{
"foo": {
{
Volume: "foo",
Destination: "/tmp",
ReadOnly: false,
},
{
Volume: "foo",
Destination: "/bar",
ReadOnly: false,
},
},
"baz": {
{
Volume: "baz",
Destination: "/baz",
ReadOnly: false,
},
},
}
// Test with a real collection
partitioned := partitionMountsByVolume(mounts)
require.Equal(t, expected, partitioned)
// Test with nil/emptylist
partitioned = partitionMountsByVolume(nil)
require.Equal(t, map[string][]*structs.VolumeMount{}, partitioned)
}
func TestVolumeHook_prepareCSIVolumes(t *testing.T) {
ci.Parallel(t)
req := &interfaces.TaskPrestartRequest{
Task: &structs.Task{
Name: "test",
Driver: "mock",
VolumeMounts: []*structs.VolumeMount{
{
Volume: "foo",
Destination: "/bar",
},
},
},
}
volumes := map[string]*structs.VolumeRequest{
"foo": {
Type: "csi",
Source: "my-test-volume",
},
}
cases := []struct {
Name string
Driver drivers.DriverPlugin
Expected []*drivers.MountConfig
ExpectedError string
}{
{
Name: "supported driver",
Driver: &dtu.MockDriver{
CapabilitiesF: func() (*drivers.Capabilities, error) {
return &drivers.Capabilities{
MountConfigs: drivers.MountConfigSupportAll,
}, nil
},
},
Expected: []*drivers.MountConfig{
{
HostPath: "/mnt/my-test-volume",
TaskPath: "/bar",
},
},
},
{
Name: "unsupported driver",
Driver: &dtu.MockDriver{
CapabilitiesF: func() (*drivers.Capabilities, error) {
return &drivers.Capabilities{
MountConfigs: drivers.MountConfigSupportNone,
}, nil
},
},
ExpectedError: "task driver \"mock\" for \"test\" does not support CSI",
},
}
for _, tc := range cases {
t.Run(tc.Name, func(t *testing.T) {
tr := &TaskRunner{
task: req.Task,
driver: tc.Driver,
allocHookResources: cstructs.NewAllocHookResources(),
}
tr.allocHookResources.SetCSIMounts(map[string]*csimanager.MountInfo{
"foo": {
Source: "/mnt/my-test-volume",
},
})
hook := &volumeHook{
logger: testlog.HCLogger(t),
alloc: structs.MockAlloc(),
runner: tr,
}
mounts, err := hook.prepareCSIVolumes(req, volumes)
if tc.ExpectedError != "" {
require.EqualError(t, err, tc.ExpectedError)
} else {
require.NoError(t, err)
}
require.Equal(t, tc.Expected, mounts)
})
}
}
func TestVolumeHook_Interpolation(t *testing.T) {
ci.Parallel(t)
alloc := mock.Alloc()
task := alloc.Job.TaskGroups[0].Tasks[0]
taskEnv := taskenv.NewBuilder(mock.Node(), alloc, task, "global").SetHookEnv("volume",
map[string]string{
"PROPAGATION_MODE": "private",
"VOLUME_ID": "my-other-volume",
},
).Build()
mounts := []*structs.VolumeMount{
{
Volume: "foo",
Destination: "/tmp",
ReadOnly: false,
PropagationMode: "bidirectional",
},
{
Volume: "foo",
Destination: "/bar-${NOMAD_JOB_NAME}",
ReadOnly: false,
PropagationMode: "bidirectional",
},
{
Volume: "${VOLUME_ID}",
Destination: "/baz",
ReadOnly: false,
PropagationMode: "bidirectional",
},
{
Volume: "foo",
Destination: "/quux",
ReadOnly: false,
PropagationMode: "${PROPAGATION_MODE}",
},
}
expected := []*structs.VolumeMount{
{
Volume: "foo",
Destination: "/tmp",
ReadOnly: false,
PropagationMode: "bidirectional",
},
{
Volume: "foo",
Destination: "/bar-my-job",
ReadOnly: false,
PropagationMode: "bidirectional",
},
{
Volume: "my-other-volume",
Destination: "/baz",
ReadOnly: false,
PropagationMode: "bidirectional",
},
{
Volume: "foo",
Destination: "/quux",
ReadOnly: false,
PropagationMode: "private",
},
}
interpolateVolumeMounts(mounts, taskEnv)
require.Equal(t, expected, mounts)
}