From 44d289497acb4c3f30b272828525cf3a8c68abe1 Mon Sep 17 00:00:00 2001 From: Juana De La Cuesta Date: Tue, 11 Apr 2023 13:13:18 +0200 Subject: [PATCH] func: add wildcard to namespace filter for deployments --- nomad/deployment_endpoint.go | 15 ++++--- nomad/deployment_endpoint_test.go | 65 +++++++++++++++++++++++++++---- nomad/state/state_store.go | 3 +- 3 files changed, 66 insertions(+), 17 deletions(-) diff --git a/nomad/deployment_endpoint.go b/nomad/deployment_endpoint.go index dc58023c9894..2faa674d71be 100644 --- a/nomad/deployment_endpoint.go +++ b/nomad/deployment_endpoint.go @@ -454,7 +454,7 @@ func (d *Deployment) List(args *structs.DeploymentListRequest, reply *structs.De if err != nil { return err } - if aclObj != nil && !aclObj.AllowNsOp(namespace, acl.NamespaceCapabilityReadJob) { + if !aclObj.AllowNsOp(namespace, acl.NamespaceCapabilityReadJob) { return structs.ErrPermissionDenied } @@ -466,21 +466,19 @@ func (d *Deployment) List(args *structs.DeploymentListRequest, reply *structs.De queryOpts: &args.QueryOptions, queryMeta: &reply.QueryMeta, run: func(ws memdb.WatchSet, store *state.StateStore) error { - // Capture all the deployments - var err error - var iter memdb.ResultIterator - var opts paginator.StructsTokenizerOptions - allowableNamespaces, err := allowedNSes(aclObj, store, allow) if err != nil { if err == structs.ErrPermissionDenied { - // return empty evals if token isn't authorized for any - // namespace, matching other endpoints reply.Deployments = make([]*structs.Deployment, 0) + return nil } return err } + // Capture all the deployments + var iter memdb.ResultIterator + var opts paginator.StructsTokenizerOptions + if prefix := args.QueryOptions.Prefix; prefix != "" { iter, err = store.DeploymentsByIDPrefix(ws, namespace, prefix, sort) opts = paginator.StructsTokenizerOptions{ @@ -504,6 +502,7 @@ func (d *Deployment) List(args *structs.DeploymentListRequest, reply *structs.De } tokenizer := paginator.NewStructsTokenizer(iter, opts) + filters := []paginator.Filter{ paginator.NamespaceFilter{ AllowableNamespaces: allowableNamespaces, diff --git a/nomad/deployment_endpoint_test.go b/nomad/deployment_endpoint_test.go index ed82c0c3d509..ec8d4a8c17cf 100644 --- a/nomad/deployment_endpoint_test.go +++ b/nomad/deployment_endpoint_test.go @@ -1152,6 +1152,37 @@ func TestDeploymentEndpoint_List(t *testing.T) { assert.Nil(msgpackrpc.CallWithCodec(codec, "Deployment.List", get, &resp), "RPC") assert.EqualValues(resp.Index, 1003, "Wrong Index") assert.Len(resp.Deployments, 2, "Deployments") + + // Lookup a deployment with wildcard namespace and prefix + var resp3 structs.DeploymentListResponse + get = &structs.DeploymentListRequest{ + QueryOptions: structs.QueryOptions{ + Region: "global", + Prefix: d.ID[:4], + Namespace: structs.AllNamespacesSentinel, + }, + } + + assert.Nil(msgpackrpc.CallWithCodec(codec, "Deployment.List", get, &resp3), "RPC") + assert.EqualValues(resp3.Index, 1003, "Wrong Index") + assert.Len(resp3.Deployments, 1, "Deployments") + assert.Equal(resp3.Deployments[0].ID, d.ID, "Deployment ID") + + // Lookup the other deployments with wildcard namespace and prefix + var resp4 structs.DeploymentListResponse + get = &structs.DeploymentListRequest{ + QueryOptions: structs.QueryOptions{ + Region: "global", + Prefix: d2.ID[:4], + Namespace: structs.AllNamespacesSentinel, + }, + } + + assert.Nil(msgpackrpc.CallWithCodec(codec, "Deployment.List", get, &resp4), "RPC") + assert.EqualValues(resp4.Index, 1003, "Wrong Index") + assert.Len(resp4.Deployments, 1, "Deployments") + assert.Equal(resp4.Deployments[0].ID, d2.ID, "Deployment ID") + } func TestDeploymentEndpoint_List_order(t *testing.T) { @@ -1415,6 +1446,12 @@ func TestDeploymentEndpoint_List_Pagination(t *testing.T) { codec := rpcClient(t, s1) testutil.WaitForLeader(t, s1.RPC) + // Create dev namespace + devNS := mock.Namespace() + devNS.Name = "non-default" + err := s1.fsm.State().UpsertNamespaces(999, []*structs.Namespace{devNS}) + require.NoError(t, err) + // create a set of deployments. these are in the order that the // state store will return them from the iterator (sorted by key), // for ease of writing tests @@ -1424,14 +1461,14 @@ func TestDeploymentEndpoint_List_Pagination(t *testing.T) { jobID string status string }{ - {id: "aaaa1111-3350-4b4b-d185-0e1992ed43e9"}, // 0 - {id: "aaaaaa22-3350-4b4b-d185-0e1992ed43e9"}, // 1 - {id: "aaaaaa33-3350-4b4b-d185-0e1992ed43e9", namespace: "non-default"}, // 2 - {id: "aaaaaaaa-3350-4b4b-d185-0e1992ed43e9"}, // 3 - {id: "aaaaaabb-3350-4b4b-d185-0e1992ed43e9"}, // 4 - {id: "aaaaaacc-3350-4b4b-d185-0e1992ed43e9"}, // 5 - {id: "aaaaaadd-3350-4b4b-d185-0e1992ed43e9"}, // 6 - {id: "00000111-3350-4b4b-d185-0e1992ed43e9"}, // 7 + {id: "aaaa1111-3350-4b4b-d185-0e1992ed43e9"}, // 0 + {id: "aaaaaa22-3350-4b4b-d185-0e1992ed43e9"}, // 1 + {id: "aaaaaa33-3350-4b4b-d185-0e1992ed43e9", namespace: devNS.Name}, // 2 + {id: "aaaaaaaa-3350-4b4b-d185-0e1992ed43e9"}, // 3 + {id: "aaaaaabb-3350-4b4b-d185-0e1992ed43e9"}, // 4 + {id: "aaaaaacc-3350-4b4b-d185-0e1992ed43e9"}, // 5 + {id: "aaaaaadd-3350-4b4b-d185-0e1992ed43e9"}, // 6 + {id: "00000111-3350-4b4b-d185-0e1992ed43e9"}, // 7 {}, // 8, index missing {id: "bbbb1111-3350-4b4b-d185-0e1992ed43e9"}, // 9 } @@ -1589,6 +1626,18 @@ func TestDeploymentEndpoint_List_Pagination(t *testing.T) { "bbbb1111-3350-4b4b-d185-0e1992ed43e9", }, }, + { + name: "test15 size-2 page-2 all namespaces with prefix", + namespace: "*", + prefix: "aaaa", + pageSize: 2, + nextToken: "aaaaaa33-3350-4b4b-d185-0e1992ed43e9", + expectedNextToken: "aaaaaabb-3350-4b4b-d185-0e1992ed43e9", + expectedIDs: []string{ + "aaaaaa33-3350-4b4b-d185-0e1992ed43e9", + "aaaaaaaa-3350-4b4b-d185-0e1992ed43e9", + }, + }, } for _, tc := range cases { diff --git a/nomad/state/state_store.go b/nomad/state/state_store.go index e5e1958d54cd..c1f3b0a768e8 100644 --- a/nomad/state/state_store.go +++ b/nomad/state/state_store.go @@ -679,7 +679,8 @@ func deploymentNamespaceFilter(namespace string) func(interface{}) bool { return true } - return d.Namespace != namespace + return namespace != structs.AllNamespacesSentinel && + d.Namespace != namespace } }