open-nomad/nomad/structs/volume_test.go

171 lines
4.3 KiB
Go

// Copyright (c) HashiCorp, Inc.
// SPDX-License-Identifier: MPL-2.0
package structs
import (
"testing"
"github.com/hashicorp/nomad/ci"
"github.com/shoenig/test/must"
"github.com/stretchr/testify/require"
)
func TestVolumeRequest_Validate(t *testing.T) {
ci.Parallel(t)
testCases := []struct {
name string
expected []string
canariesCount int
taskGroupCount int
req *VolumeRequest
}{
{
name: "host volume with empty source",
expected: []string{"volume has an empty source"},
req: &VolumeRequest{
Type: VolumeTypeHost,
},
},
{
name: "host volume with CSI volume config",
expected: []string{
"host volumes cannot have an access mode",
"host volumes cannot have an attachment mode",
"host volumes cannot have mount options",
"volume cannot be per_alloc for system or sysbatch jobs",
"volume cannot be per_alloc when canaries are in use",
},
canariesCount: 1,
req: &VolumeRequest{
Type: VolumeTypeHost,
ReadOnly: false,
AccessMode: CSIVolumeAccessModeSingleNodeReader,
AttachmentMode: CSIVolumeAttachmentModeBlockDevice,
MountOptions: &CSIMountOptions{
FSType: "ext4",
MountFlags: []string{"ro"},
},
PerAlloc: true,
},
},
{
name: "CSI volume multi-reader-single-writer access mode",
expected: []string{
"volume with multi-node-single-writer access mode allows only one writer",
},
taskGroupCount: 2,
req: &VolumeRequest{
Type: VolumeTypeCSI,
AccessMode: CSIVolumeAccessModeMultiNodeSingleWriter,
},
},
{
name: "CSI volume single reader access mode",
expected: []string{
"volume with single-node-reader-only access mode allows only one reader",
},
taskGroupCount: 2,
req: &VolumeRequest{
Type: VolumeTypeCSI,
AccessMode: CSIVolumeAccessModeSingleNodeReader,
ReadOnly: true,
},
},
{
name: "CSI volume per-alloc with canaries",
expected: []string{
"volume cannot be per_alloc for system or sysbatch jobs",
"volume cannot be per_alloc when canaries are in use",
},
canariesCount: 1,
req: &VolumeRequest{
Type: VolumeTypeCSI,
PerAlloc: true,
},
},
}
for _, tc := range testCases {
t.Run(tc.name, func(t *testing.T) {
err := tc.req.Validate(JobTypeSystem, tc.taskGroupCount, tc.canariesCount)
for _, expected := range tc.expected {
require.Contains(t, err.Error(), expected)
}
})
}
}
func TestVolumeRequest_Equal(t *testing.T) {
ci.Parallel(t)
must.Equal[*VolumeRequest](t, nil, nil)
must.NotEqual[*VolumeRequest](t, nil, new(VolumeRequest))
must.StructEqual(t, &VolumeRequest{
Name: "name",
Type: "type",
Source: "source",
ReadOnly: true,
AccessMode: "access",
AttachmentMode: "attachment",
MountOptions: &CSIMountOptions{
FSType: "fs1",
MountFlags: []string{"flag1"},
},
PerAlloc: true,
}, []must.Tweak[*VolumeRequest]{{
Field: "Name",
Apply: func(vr *VolumeRequest) { vr.Name = "name2" },
}, {
Field: "Type",
Apply: func(vr *VolumeRequest) { vr.Type = "type2" },
}, {
Field: "Source",
Apply: func(vr *VolumeRequest) { vr.Source = "source2" },
}, {
Field: "ReadOnly",
Apply: func(vr *VolumeRequest) { vr.ReadOnly = false },
}, {
Field: "AccessMode",
Apply: func(vr *VolumeRequest) { vr.AccessMode = "access2" },
}, {
Field: "AttachmentMode",
Apply: func(vr *VolumeRequest) { vr.AttachmentMode = "attachment2" },
}, {
Field: "MountOptions",
Apply: func(vr *VolumeRequest) { vr.MountOptions = nil },
}, {
Field: "PerAlloc",
Apply: func(vr *VolumeRequest) { vr.PerAlloc = false },
}})
}
func TestVolumeMount_Equal(t *testing.T) {
ci.Parallel(t)
must.Equal[*VolumeMount](t, nil, nil)
must.NotEqual[*VolumeMount](t, nil, new(VolumeMount))
must.StructEqual(t, &VolumeMount{
Volume: "volume",
Destination: "destination",
ReadOnly: true,
PropagationMode: "mode",
}, []must.Tweak[*VolumeMount]{{
Field: "Volume",
Apply: func(vm *VolumeMount) { vm.Volume = "vol2" },
}, {
Field: "Destination",
Apply: func(vm *VolumeMount) { vm.Destination = "dest2" },
}, {
Field: "ReadOnly",
Apply: func(vm *VolumeMount) { vm.ReadOnly = false },
}, {
Field: "PropogationMode",
Apply: func(vm *VolumeMount) { vm.PropagationMode = "mode2" },
}})
}