csi: allow `namespace` field to be passed in volume spec (#12400)

Use the volume spec's `namespace` field to override the value of the
`-namespace` and `NOMAD_NAMESPACE` field, just as we do with job spec.
This commit is contained in:
Tim Gross 2022-03-29 14:46:39 -04:00 committed by GitHub
parent 19703e3316
commit 03c1904112
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
7 changed files with 33 additions and 11 deletions

3
.changelog/12400.txt Normal file
View File

@ -0,0 +1,3 @@
```release-note:improvement
csi: allow namespace field to be passed in volume spec
```

View File

@ -110,6 +110,7 @@ func (c *VolumeInitCommand) Run(args []string) int {
var defaultHclVolumeSpec = strings.TrimSpace(` var defaultHclVolumeSpec = strings.TrimSpace(`
id = "ebs_prod_db1" id = "ebs_prod_db1"
namespace = "default"
name = "database" name = "database"
type = "csi" type = "csi"
plugin_id = "plugin_id" plugin_id = "plugin_id"
@ -183,6 +184,7 @@ context {
var defaultJsonVolumeSpec = strings.TrimSpace(` var defaultJsonVolumeSpec = strings.TrimSpace(`
{ {
"id": "ebs_prod_db1", "id": "ebs_prod_db1",
"namespace": "default",
"name": "database", "name": "database",
"type": "csi", "type": "csi",
"plugin_id": "plugin_id", "plugin_id": "plugin_id",

View File

@ -55,6 +55,7 @@ func TestCSIVolumeDecode(t *testing.T) {
name: "volume creation", name: "volume creation",
hcl: ` hcl: `
id = "testvolume" id = "testvolume"
namespace = "prod"
name = "test" name = "test"
type = "csi" type = "csi"
plugin_id = "myplugin" plugin_id = "myplugin"
@ -99,6 +100,7 @@ topology_request {
`, `,
expected: &api.CSIVolume{ expected: &api.CSIVolume{
ID: "testvolume", ID: "testvolume",
Namespace: "prod",
Name: "test", Name: "test",
PluginID: "myplugin", PluginID: "myplugin",
SnapshotID: "snap-12345", SnapshotID: "snap-12345",
@ -136,6 +138,7 @@ topology_request {
name: "volume registration", name: "volume registration",
hcl: ` hcl: `
id = "testvolume" id = "testvolume"
namespace = "prod"
external_id = "vol-12345" external_id = "vol-12345"
name = "test" name = "test"
type = "csi" type = "csi"
@ -162,6 +165,7 @@ topology_request {
`, `,
expected: &api.CSIVolume{ expected: &api.CSIVolume{
ID: "testvolume", ID: "testvolume",
Namespace: "prod",
ExternalID: "vol-12345", ExternalID: "vol-12345",
Name: "test", Name: "test",
PluginID: "myplugin", PluginID: "myplugin",

View File

@ -332,8 +332,6 @@ func (v *CSIVolume) Register(args *structs.CSIVolumeRegisterRequest, reply *stru
if err != nil { if err != nil {
return err return err
} }
// TODO: allow volume spec file to set namespace
// https://github.com/hashicorp/nomad/issues/11196
if vol.Namespace == "" { if vol.Namespace == "" {
vol.Namespace = args.RequestNamespace() vol.Namespace = args.RequestNamespace()
} }
@ -921,7 +919,9 @@ func (v *CSIVolume) Create(args *structs.CSIVolumeCreateRequest, reply *structs.
// We also validate that the plugin exists for each plugin, and validate the // We also validate that the plugin exists for each plugin, and validate the
// capabilities when the plugin has a controller. // capabilities when the plugin has a controller.
for _, vol := range args.Volumes { for _, vol := range args.Volumes {
vol.Namespace = args.RequestNamespace() if vol.Namespace == "" {
vol.Namespace = args.RequestNamespace()
}
if err = vol.Validate(); err != nil { if err = vol.Validate(); err != nil {
return err return err
} }

View File

@ -125,13 +125,15 @@ func TestCSIVolumeEndpoint_Register(t *testing.T) {
defer shutdown() defer shutdown()
testutil.WaitForLeader(t, srv.RPC) testutil.WaitForLeader(t, srv.RPC)
ns := structs.DefaultNamespace store := srv.fsm.State()
state := srv.fsm.State()
codec := rpcClient(t, srv) codec := rpcClient(t, srv)
id0 := uuid.Generate() id0 := uuid.Generate()
// Create the register request
ns := mock.Namespace()
store.UpsertNamespaces(900, []*structs.Namespace{ns})
// Create the node and plugin // Create the node and plugin
node := mock.Node() node := mock.Node()
node.CSINodePlugins = map[string]*structs.CSIInfo{ node.CSINodePlugins = map[string]*structs.CSIInfo{
@ -142,11 +144,12 @@ func TestCSIVolumeEndpoint_Register(t *testing.T) {
NodeInfo: &structs.CSINodeInfo{}, NodeInfo: &structs.CSINodeInfo{},
}, },
} }
require.NoError(t, state.UpsertNode(structs.MsgTypeTestSetup, 1000, node)) require.NoError(t, store.UpsertNode(structs.MsgTypeTestSetup, 1000, node))
// Create the volume // Create the volume
vols := []*structs.CSIVolume{{ vols := []*structs.CSIVolume{{
ID: id0, ID: id0,
Namespace: ns.Name,
PluginID: "minnie", PluginID: "minnie",
AccessMode: structs.CSIVolumeAccessModeSingleNodeReader, // legacy field ignored AccessMode: structs.CSIVolumeAccessModeSingleNodeReader, // legacy field ignored
AttachmentMode: structs.CSIVolumeAttachmentModeBlockDevice, // legacy field ignored AttachmentMode: structs.CSIVolumeAttachmentModeBlockDevice, // legacy field ignored
@ -166,7 +169,7 @@ func TestCSIVolumeEndpoint_Register(t *testing.T) {
Volumes: vols, Volumes: vols,
WriteRequest: structs.WriteRequest{ WriteRequest: structs.WriteRequest{
Region: "global", Region: "global",
Namespace: ns, Namespace: "",
}, },
} }
resp1 := &structs.CSIVolumeRegisterResponse{} resp1 := &structs.CSIVolumeRegisterResponse{}
@ -178,7 +181,8 @@ func TestCSIVolumeEndpoint_Register(t *testing.T) {
req2 := &structs.CSIVolumeGetRequest{ req2 := &structs.CSIVolumeGetRequest{
ID: id0, ID: id0,
QueryOptions: structs.QueryOptions{ QueryOptions: structs.QueryOptions{
Region: "global", Region: "global",
Namespace: ns.Name,
}, },
} }
resp2 := &structs.CSIVolumeGetResponse{} resp2 := &structs.CSIVolumeGetResponse{}
@ -201,7 +205,7 @@ func TestCSIVolumeEndpoint_Register(t *testing.T) {
VolumeIDs: []string{id0}, VolumeIDs: []string{id0},
WriteRequest: structs.WriteRequest{ WriteRequest: structs.WriteRequest{
Region: "global", Region: "global",
Namespace: ns, Namespace: ns.Name,
}, },
} }
resp3 := &structs.CSIVolumeDeregisterResponse{} resp3 := &structs.CSIVolumeDeregisterResponse{}
@ -1029,7 +1033,7 @@ func TestCSIVolumeEndpoint_Create(t *testing.T) {
vols := []*structs.CSIVolume{{ vols := []*structs.CSIVolume{{
ID: volID, ID: volID,
Name: "vol", Name: "vol",
Namespace: "notTheNamespace", // overriden by WriteRequest Namespace: "", // overriden by WriteRequest
PluginID: "minnie", PluginID: "minnie",
AccessMode: structs.CSIVolumeAccessModeSingleNodeReader, // legacy field ignored AccessMode: structs.CSIVolumeAccessModeSingleNodeReader, // legacy field ignored
AttachmentMode: structs.CSIVolumeAttachmentModeBlockDevice, // legacy field ignored AttachmentMode: structs.CSIVolumeAttachmentModeBlockDevice, // legacy field ignored

View File

@ -37,6 +37,7 @@ The file may be provided as either HCL or JSON. An example HCL configuration:
```hcl ```hcl
id = "ebs_prod_db1" id = "ebs_prod_db1"
namespace = "default"
name = "database" name = "database"
type = "csi" type = "csi"
plugin_id = "ebs-prod" plugin_id = "ebs-prod"
@ -84,6 +85,10 @@ parameters {
[`volume.source`][csi_volume_source] field in a job specification will refer [`volume.source`][csi_volume_source] field in a job specification will refer
to the volume. to the volume.
- `namespace` `(string: <optional>)` - The namespace of the volume. This field
overrides the namespace provided by the `-namespace` flag or `NOMAD_NAMESPACE`
environment variable. Defaults to `"default"` if unset.
- `name` `(string: <required>)` - The display name of the volume. This field - `name` `(string: <required>)` - The display name of the volume. This field
may be used by the external storage provider to tag the volume. may be used by the external storage provider to tag the volume.

View File

@ -87,6 +87,10 @@ context {
[`volume.source`][csi_volume_source] field in a job specification will refer [`volume.source`][csi_volume_source] field in a job specification will refer
to the volume. to the volume.
- `namespace` `(string: <optional>)` - The namespace of the volume. This field
overrides the namespace provided by the `-namespace` flag or `NOMAD_NAMESPACE`
environment variable. Defaults to `"default"` if unset.
- `name` `(string: <required>)` - The display name of the volume. - `name` `(string: <required>)` - The display name of the volume.
- `type` `(string: <required>)` - The type of volume. Currently only `"csi"` - `type` `(string: <required>)` - The type of volume. Currently only `"csi"`