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:
parent
19703e3316
commit
03c1904112
|
@ -0,0 +1,3 @@
|
||||||
|
```release-note:improvement
|
||||||
|
csi: allow namespace field to be passed in volume spec
|
||||||
|
```
|
|
@ -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",
|
||||||
|
|
|
@ -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",
|
||||||
|
|
|
@ -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 {
|
||||||
|
if vol.Namespace == "" {
|
||||||
vol.Namespace = args.RequestNamespace()
|
vol.Namespace = args.RequestNamespace()
|
||||||
|
}
|
||||||
if err = vol.Validate(); err != nil {
|
if err = vol.Validate(); err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
|
@ -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{}
|
||||||
|
@ -179,6 +182,7 @@ func TestCSIVolumeEndpoint_Register(t *testing.T) {
|
||||||
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
|
||||||
|
|
|
@ -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.
|
||||||
|
|
||||||
|
|
|
@ -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"`
|
||||||
|
|
Loading…
Reference in New Issue