diff --git a/.changelog/12400.txt b/.changelog/12400.txt new file mode 100644 index 000000000..52663f9fd --- /dev/null +++ b/.changelog/12400.txt @@ -0,0 +1,3 @@ +```release-note:improvement +csi: allow namespace field to be passed in volume spec +``` diff --git a/command/volume_init.go b/command/volume_init.go index ce0ffba3c..c2901e384 100644 --- a/command/volume_init.go +++ b/command/volume_init.go @@ -110,6 +110,7 @@ func (c *VolumeInitCommand) Run(args []string) int { var defaultHclVolumeSpec = strings.TrimSpace(` id = "ebs_prod_db1" +namespace = "default" name = "database" type = "csi" plugin_id = "plugin_id" @@ -183,6 +184,7 @@ context { var defaultJsonVolumeSpec = strings.TrimSpace(` { "id": "ebs_prod_db1", + "namespace": "default", "name": "database", "type": "csi", "plugin_id": "plugin_id", diff --git a/command/volume_register_test.go b/command/volume_register_test.go index 29bf025e5..3257aadf4 100644 --- a/command/volume_register_test.go +++ b/command/volume_register_test.go @@ -55,6 +55,7 @@ func TestCSIVolumeDecode(t *testing.T) { name: "volume creation", hcl: ` id = "testvolume" +namespace = "prod" name = "test" type = "csi" plugin_id = "myplugin" @@ -99,6 +100,7 @@ topology_request { `, expected: &api.CSIVolume{ ID: "testvolume", + Namespace: "prod", Name: "test", PluginID: "myplugin", SnapshotID: "snap-12345", @@ -136,6 +138,7 @@ topology_request { name: "volume registration", hcl: ` id = "testvolume" +namespace = "prod" external_id = "vol-12345" name = "test" type = "csi" @@ -162,6 +165,7 @@ topology_request { `, expected: &api.CSIVolume{ ID: "testvolume", + Namespace: "prod", ExternalID: "vol-12345", Name: "test", PluginID: "myplugin", diff --git a/nomad/csi_endpoint.go b/nomad/csi_endpoint.go index 0cb457761..e0f364432 100644 --- a/nomad/csi_endpoint.go +++ b/nomad/csi_endpoint.go @@ -332,8 +332,6 @@ func (v *CSIVolume) Register(args *structs.CSIVolumeRegisterRequest, reply *stru if err != nil { return err } - // TODO: allow volume spec file to set namespace - // https://github.com/hashicorp/nomad/issues/11196 if vol.Namespace == "" { 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 // capabilities when the plugin has a controller. for _, vol := range args.Volumes { - vol.Namespace = args.RequestNamespace() + if vol.Namespace == "" { + vol.Namespace = args.RequestNamespace() + } if err = vol.Validate(); err != nil { return err } diff --git a/nomad/csi_endpoint_test.go b/nomad/csi_endpoint_test.go index 254daea50..19cd30d01 100644 --- a/nomad/csi_endpoint_test.go +++ b/nomad/csi_endpoint_test.go @@ -125,13 +125,15 @@ func TestCSIVolumeEndpoint_Register(t *testing.T) { defer shutdown() testutil.WaitForLeader(t, srv.RPC) - ns := structs.DefaultNamespace - - state := srv.fsm.State() + store := srv.fsm.State() codec := rpcClient(t, srv) id0 := uuid.Generate() + // Create the register request + ns := mock.Namespace() + store.UpsertNamespaces(900, []*structs.Namespace{ns}) + // Create the node and plugin node := mock.Node() node.CSINodePlugins = map[string]*structs.CSIInfo{ @@ -142,11 +144,12 @@ func TestCSIVolumeEndpoint_Register(t *testing.T) { NodeInfo: &structs.CSINodeInfo{}, }, } - require.NoError(t, state.UpsertNode(structs.MsgTypeTestSetup, 1000, node)) + require.NoError(t, store.UpsertNode(structs.MsgTypeTestSetup, 1000, node)) // Create the volume vols := []*structs.CSIVolume{{ ID: id0, + Namespace: ns.Name, PluginID: "minnie", AccessMode: structs.CSIVolumeAccessModeSingleNodeReader, // legacy field ignored AttachmentMode: structs.CSIVolumeAttachmentModeBlockDevice, // legacy field ignored @@ -166,7 +169,7 @@ func TestCSIVolumeEndpoint_Register(t *testing.T) { Volumes: vols, WriteRequest: structs.WriteRequest{ Region: "global", - Namespace: ns, + Namespace: "", }, } resp1 := &structs.CSIVolumeRegisterResponse{} @@ -178,7 +181,8 @@ func TestCSIVolumeEndpoint_Register(t *testing.T) { req2 := &structs.CSIVolumeGetRequest{ ID: id0, QueryOptions: structs.QueryOptions{ - Region: "global", + Region: "global", + Namespace: ns.Name, }, } resp2 := &structs.CSIVolumeGetResponse{} @@ -201,7 +205,7 @@ func TestCSIVolumeEndpoint_Register(t *testing.T) { VolumeIDs: []string{id0}, WriteRequest: structs.WriteRequest{ Region: "global", - Namespace: ns, + Namespace: ns.Name, }, } resp3 := &structs.CSIVolumeDeregisterResponse{} @@ -1029,7 +1033,7 @@ func TestCSIVolumeEndpoint_Create(t *testing.T) { vols := []*structs.CSIVolume{{ ID: volID, Name: "vol", - Namespace: "notTheNamespace", // overriden by WriteRequest + Namespace: "", // overriden by WriteRequest PluginID: "minnie", AccessMode: structs.CSIVolumeAccessModeSingleNodeReader, // legacy field ignored AttachmentMode: structs.CSIVolumeAttachmentModeBlockDevice, // legacy field ignored diff --git a/website/content/docs/commands/volume/create.mdx b/website/content/docs/commands/volume/create.mdx index 0ffc78da0..cbf37e83a 100644 --- a/website/content/docs/commands/volume/create.mdx +++ b/website/content/docs/commands/volume/create.mdx @@ -37,6 +37,7 @@ The file may be provided as either HCL or JSON. An example HCL configuration: ```hcl id = "ebs_prod_db1" +namespace = "default" name = "database" type = "csi" plugin_id = "ebs-prod" @@ -84,6 +85,10 @@ parameters { [`volume.source`][csi_volume_source] field in a job specification will refer to the volume. +- `namespace` `(string: )` - 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: )` - The display name of the volume. This field may be used by the external storage provider to tag the volume. diff --git a/website/content/docs/commands/volume/register.mdx b/website/content/docs/commands/volume/register.mdx index 343f8b942..65a76333d 100644 --- a/website/content/docs/commands/volume/register.mdx +++ b/website/content/docs/commands/volume/register.mdx @@ -87,6 +87,10 @@ context { [`volume.source`][csi_volume_source] field in a job specification will refer to the volume. +- `namespace` `(string: )` - 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: )` - The display name of the volume. - `type` `(string: )` - The type of volume. Currently only `"csi"`