diff --git a/.changelog/20551.txt b/.changelog/20551.txt new file mode 100644 index 000000000..a6f922fe6 --- /dev/null +++ b/.changelog/20551.txt @@ -0,0 +1,3 @@ +```release-note:improvement +csi: Added support for wildcard namespace to `plugin status` command +``` diff --git a/nomad/csi_endpoint.go b/nomad/csi_endpoint.go index afa7aaa0c..006b8c912 100644 --- a/nomad/csi_endpoint.go +++ b/nomad/csi_endpoint.go @@ -1797,7 +1797,8 @@ func (v *CSIPlugin) Get(args *structs.CSIPluginGetRequest, reply *structs.CSIPlu return structs.ErrPermissionDenied } - withAllocs := aclObj.AllowNsOp(args.RequestNamespace(), acl.NamespaceCapabilityReadJob) + ns := args.RequestNamespace() + withAllocs := aclObj.AllowNsOp(ns, acl.NamespaceCapabilityReadJob) if args.ID == "" { return fmt.Errorf("missing plugin ID") @@ -1821,18 +1822,22 @@ func (v *CSIPlugin) Get(args *structs.CSIPluginGetRequest, reply *structs.CSIPlu return nil } + // if we're not allowed access to the namespace at all, we skip this + // copy as an optimization. withAllocs will be true for the wildcard + // namespace if withAllocs { plug, err = snap.CSIPluginDenormalize(ws, plug.Copy()) if err != nil { return err } - // Filter the allocation stubs by our namespace. withAllocs - // means we're allowed + // Filter the allocation stubs by allowed namespace var as []*structs.AllocListStub for _, a := range plug.Allocations { - if a.Namespace == args.RequestNamespace() { - as = append(as, a) + if ns == structs.AllNamespacesSentinel || a.Namespace == ns { + if aclObj.AllowNsOp(a.Namespace, acl.NamespaceCapabilityReadJob) { + as = append(as, a) + } } } plug.Allocations = as diff --git a/nomad/csi_endpoint_test.go b/nomad/csi_endpoint_test.go index c1d1e9959..d22cce25e 100644 --- a/nomad/csi_endpoint_test.go +++ b/nomad/csi_endpoint_test.go @@ -2478,10 +2478,24 @@ func TestCSIPluginEndpoint_ACLNamespaceFilterAlloc(t *testing.T) { must.Eq(t, structs.DefaultNamespace, a.Namespace) } + // filter out all allocs p2 := mock.PluginPolicy("read") t2 := mock.CreatePolicyAndToken(t, s, 1004, "plugin-read2", p2) req.AuthToken = t2.SecretID err = msgpackrpc.CallWithCodec(codec, "CSIPlugin.Get", req, resp) must.NoError(t, err) must.Eq(t, 0, len(resp.Plugin.Allocations)) + + // wildcard namespace filter + p3 := mock.PluginPolicy("read") + + mock.NamespacePolicy(ns1.Name, "", []string{acl.NamespaceCapabilityReadJob}) + t3 := mock.CreatePolicyAndToken(t, s, 1005, "plugin-read", p3) + + req.Namespace = structs.AllNamespacesSentinel + req.AuthToken = t3.SecretID + resp = &structs.CSIPluginGetResponse{} + err = msgpackrpc.CallWithCodec(codec, "CSIPlugin.Get", req, resp) + must.NoError(t, err) + must.Eq(t, 1, len(resp.Plugin.Allocations)) + must.Eq(t, ns1.Name, resp.Plugin.Allocations[0].Namespace) }