api: apply consistent behaviour of the reverse query parameter (#12244)

This commit is contained in:
Luiz Aoqui
2022-03-11 19:44:52 -05:00
committed by GitHub
parent 4a21dbcfaa
commit ddbbda6561
13 changed files with 321 additions and 189 deletions

View File

@@ -665,12 +665,12 @@ func (a *ACL) ListTokens(args *structs.ACLTokenListRequest, reply *structs.ACLTo
var opts paginator.StructsTokenizerOptions
if prefix := args.QueryOptions.Prefix; prefix != "" {
iter, err = state.ACLTokenByAccessorIDPrefix(ws, prefix)
iter, err = state.ACLTokenByAccessorIDPrefix(ws, prefix, sort)
opts = paginator.StructsTokenizerOptions{
WithID: true,
}
} else if args.GlobalOnly {
iter, err = state.ACLTokensByGlobal(ws, true)
iter, err = state.ACLTokensByGlobal(ws, true, sort)
opts = paginator.StructsTokenizerOptions{
WithID: true,
}

View File

@@ -80,7 +80,7 @@ func (a *Alloc) List(args *structs.AllocListRequest, reply *structs.AllocListRes
return err
} else {
if prefix := args.QueryOptions.Prefix; prefix != "" {
iter, err = state.AllocsByIDPrefix(ws, namespace, prefix)
iter, err = state.AllocsByIDPrefix(ws, namespace, prefix, sort)
opts = paginator.StructsTokenizerOptions{
WithID: true,
}

View File

@@ -414,7 +414,7 @@ func (d *Deployment) List(args *structs.DeploymentListRequest, reply *structs.De
var opts paginator.StructsTokenizerOptions
if prefix := args.QueryOptions.Prefix; prefix != "" {
iter, err = store.DeploymentsByIDPrefix(ws, namespace, prefix)
iter, err = store.DeploymentsByIDPrefix(ws, namespace, prefix, sort)
opts = paginator.StructsTokenizerOptions{
WithID: true,
}

View File

@@ -419,7 +419,7 @@ func (e *Eval) List(args *structs.EvalListRequest, reply *structs.EvalListRespon
var opts paginator.StructsTokenizerOptions
if prefix := args.QueryOptions.Prefix; prefix != "" {
iter, err = store.EvalsByIDPrefix(ws, namespace, prefix)
iter, err = store.EvalsByIDPrefix(ws, namespace, prefix, sort)
opts = paginator.StructsTokenizerOptions{
WithID: true,
}

View File

@@ -1524,13 +1524,13 @@ ERR_WAIT:
// diffACLTokens is used to perform a two-way diff between the local
// tokens and the remote tokens to determine which tokens need to
// be deleted or updated.
func diffACLTokens(state *state.StateStore, minIndex uint64, remoteList []*structs.ACLTokenListStub) (delete []string, update []string) {
func diffACLTokens(store *state.StateStore, minIndex uint64, remoteList []*structs.ACLTokenListStub) (delete []string, update []string) {
// Construct a set of the local and remote policies
local := make(map[string][]byte)
remote := make(map[string]struct{})
// Add all the local global tokens
iter, err := state.ACLTokensByGlobal(nil, true)
iter, err := store.ACLTokensByGlobal(nil, true, state.SortDefault)
if err != nil {
panic("failed to iterate local tokens")
}

View File

@@ -2063,11 +2063,11 @@ func TestClientEndpoint_GetClientAllocs_Blocking(t *testing.T) {
alloc := mock.Alloc()
alloc.NodeID = node.ID
alloc.ModifyTime = now
state := s1.fsm.State()
state.UpsertJobSummary(99, mock.JobSummary(alloc.JobID))
store := s1.fsm.State()
store.UpsertJobSummary(99, mock.JobSummary(alloc.JobID))
start := time.Now()
time.AfterFunc(100*time.Millisecond, func() {
err := state.UpsertAllocs(structs.MsgTypeTestSetup, 100, []*structs.Allocation{alloc})
err := store.UpsertAllocs(structs.MsgTypeTestSetup, 100, []*structs.Allocation{alloc})
if err != nil {
t.Fatalf("err: %v", err)
}
@@ -2101,7 +2101,7 @@ func TestClientEndpoint_GetClientAllocs_Blocking(t *testing.T) {
t.Fatalf("bad: %#v", resp2.Allocs)
}
iter, err := state.AllocsByIDPrefix(nil, structs.DefaultNamespace, alloc.ID)
iter, err := store.AllocsByIDPrefix(nil, structs.DefaultNamespace, alloc.ID, state.SortDefault)
if err != nil {
t.Fatalf("err: %v", err)
}
@@ -2133,8 +2133,8 @@ func TestClientEndpoint_GetClientAllocs_Blocking(t *testing.T) {
allocUpdate.NodeID = alloc.NodeID
allocUpdate.ID = alloc.ID
allocUpdate.ClientStatus = structs.AllocClientStatusRunning
state.UpsertJobSummary(199, mock.JobSummary(allocUpdate.JobID))
err := state.UpsertAllocs(structs.MsgTypeTestSetup, 200, []*structs.Allocation{allocUpdate})
store.UpsertJobSummary(199, mock.JobSummary(allocUpdate.JobID))
err := store.UpsertAllocs(structs.MsgTypeTestSetup, 200, []*structs.Allocation{allocUpdate})
if err != nil {
t.Fatalf("err: %v", err)
}

View File

@@ -355,26 +355,26 @@ func sortSet(matches []fuzzyMatch) {
// getResourceIter takes a context and returns a memdb iterator specific to
// that context
func getResourceIter(context structs.Context, aclObj *acl.ACL, namespace, prefix string, ws memdb.WatchSet, state *state.StateStore) (memdb.ResultIterator, error) {
func getResourceIter(context structs.Context, aclObj *acl.ACL, namespace, prefix string, ws memdb.WatchSet, store *state.StateStore) (memdb.ResultIterator, error) {
switch context {
case structs.Jobs:
return state.JobsByIDPrefix(ws, namespace, prefix)
return store.JobsByIDPrefix(ws, namespace, prefix)
case structs.Evals:
return state.EvalsByIDPrefix(ws, namespace, prefix)
return store.EvalsByIDPrefix(ws, namespace, prefix, state.SortDefault)
case structs.Allocs:
return state.AllocsByIDPrefix(ws, namespace, prefix)
return store.AllocsByIDPrefix(ws, namespace, prefix, state.SortDefault)
case structs.Nodes:
return state.NodesByIDPrefix(ws, prefix)
return store.NodesByIDPrefix(ws, prefix)
case structs.Deployments:
return state.DeploymentsByIDPrefix(ws, namespace, prefix)
return store.DeploymentsByIDPrefix(ws, namespace, prefix, state.SortDefault)
case structs.Plugins:
return state.CSIPluginsByIDPrefix(ws, prefix)
return store.CSIPluginsByIDPrefix(ws, prefix)
case structs.ScalingPolicies:
return state.ScalingPoliciesByIDPrefix(ws, namespace, prefix)
return store.ScalingPoliciesByIDPrefix(ws, namespace, prefix)
case structs.Volumes:
return state.CSIVolumesByIDPrefix(ws, namespace, prefix)
return store.CSIVolumesByIDPrefix(ws, namespace, prefix)
case structs.Namespaces:
iter, err := state.NamespacesByNamePrefix(ws, prefix)
iter, err := store.NamespacesByNamePrefix(ws, prefix)
if err != nil {
return nil, err
}
@@ -383,7 +383,7 @@ func getResourceIter(context structs.Context, aclObj *acl.ACL, namespace, prefix
}
return memdb.NewFilterIterator(iter, nsCapFilter(aclObj)), nil
default:
return getEnterpriseResourceIter(context, aclObj, namespace, prefix, ws, state)
return getEnterpriseResourceIter(context, aclObj, namespace, prefix, ws, store)
}
}

View File

@@ -617,11 +617,19 @@ func (s *StateStore) DeploymentsByNamespaceOrdered(ws memdb.WatchSet, namespace
return it, nil
}
func (s *StateStore) DeploymentsByIDPrefix(ws memdb.WatchSet, namespace, deploymentID string) (memdb.ResultIterator, error) {
func (s *StateStore) DeploymentsByIDPrefix(ws memdb.WatchSet, namespace, deploymentID string, sort SortOption) (memdb.ResultIterator, error) {
txn := s.db.ReadTxn()
var iter memdb.ResultIterator
var err error
// Walk the entire deployments table
iter, err := txn.Get("deployment", "id_prefix", deploymentID)
switch sort {
case SortReverse:
iter, err = txn.GetReverse("deployment", "id_prefix", deploymentID)
default:
iter, err = txn.Get("deployment", "id_prefix", deploymentID)
}
if err != nil {
return nil, err
}
@@ -3171,11 +3179,19 @@ func (s *StateStore) EvalByID(ws memdb.WatchSet, id string) (*structs.Evaluation
// EvalsByIDPrefix is used to lookup evaluations by prefix in a particular
// namespace
func (s *StateStore) EvalsByIDPrefix(ws memdb.WatchSet, namespace, id string) (memdb.ResultIterator, error) {
func (s *StateStore) EvalsByIDPrefix(ws memdb.WatchSet, namespace, id string, sort SortOption) (memdb.ResultIterator, error) {
txn := s.db.ReadTxn()
var iter memdb.ResultIterator
var err error
// Get an iterator over all evals by the id prefix
iter, err := txn.Get("evals", "id_prefix", id)
switch sort {
case SortReverse:
iter, err = txn.GetReverse("evals", "id_prefix", id)
default:
iter, err = txn.Get("evals", "id_prefix", id)
}
if err != nil {
return nil, fmt.Errorf("eval lookup failed: %v", err)
}
@@ -3631,10 +3647,18 @@ func (s *StateStore) allocByIDImpl(txn Txn, ws memdb.WatchSet, id string) (*stru
}
// AllocsByIDPrefix is used to lookup allocs by prefix
func (s *StateStore) AllocsByIDPrefix(ws memdb.WatchSet, namespace, id string) (memdb.ResultIterator, error) {
func (s *StateStore) AllocsByIDPrefix(ws memdb.WatchSet, namespace, id string, sort SortOption) (memdb.ResultIterator, error) {
txn := s.db.ReadTxn()
iter, err := txn.Get("allocs", "id_prefix", id)
var iter memdb.ResultIterator
var err error
switch sort {
case SortReverse:
iter, err = txn.GetReverse("allocs", "id_prefix", id)
default:
iter, err = txn.Get("allocs", "id_prefix", id)
}
if err != nil {
return nil, fmt.Errorf("alloc lookup failed: %v", err)
}
@@ -5535,13 +5559,22 @@ func (s *StateStore) ACLTokenBySecretID(ws memdb.WatchSet, secretID string) (*st
}
// ACLTokenByAccessorIDPrefix is used to lookup tokens by prefix
func (s *StateStore) ACLTokenByAccessorIDPrefix(ws memdb.WatchSet, prefix string) (memdb.ResultIterator, error) {
func (s *StateStore) ACLTokenByAccessorIDPrefix(ws memdb.WatchSet, prefix string, sort SortOption) (memdb.ResultIterator, error) {
txn := s.db.ReadTxn()
iter, err := txn.Get("acl_token", "id_prefix", prefix)
var iter memdb.ResultIterator
var err error
switch sort {
case SortReverse:
iter, err = txn.GetReverse("acl_token", "id_prefix", prefix)
default:
iter, err = txn.Get("acl_token", "id_prefix", prefix)
}
if err != nil {
return nil, fmt.Errorf("acl token lookup failed: %v", err)
}
ws.Add(iter.WatchCh())
return iter, nil
}
@@ -5568,14 +5601,23 @@ func (s *StateStore) ACLTokens(ws memdb.WatchSet, sort SortOption) (memdb.Result
}
// ACLTokensByGlobal returns an iterator over all the tokens filtered by global value
func (s *StateStore) ACLTokensByGlobal(ws memdb.WatchSet, globalVal bool) (memdb.ResultIterator, error) {
func (s *StateStore) ACLTokensByGlobal(ws memdb.WatchSet, globalVal bool, sort SortOption) (memdb.ResultIterator, error) {
txn := s.db.ReadTxn()
var iter memdb.ResultIterator
var err error
// Walk the entire table
iter, err := txn.Get("acl_token", "global", globalVal)
switch sort {
case SortReverse:
iter, err = txn.GetReverse("acl_token", "global", globalVal)
default:
iter, err = txn.Get("acl_token", "global", globalVal)
}
if err != nil {
return nil, err
}
ws.Add(iter.WatchCh())
return iter, nil
}

View File

@@ -694,16 +694,7 @@ func TestStateStore_DeploymentsByIDPrefix(t *testing.T) {
deploy.ID = "11111111-662e-d0ab-d1c9-3e434af7bdb4"
err := state.UpsertDeployment(1000, deploy)
if err != nil {
t.Fatalf("err: %v", err)
}
// Create a watchset so we can test that getters don't cause it to fire
ws := memdb.NewWatchSet()
iter, err := state.DeploymentsByIDPrefix(ws, deploy.Namespace, deploy.ID)
if err != nil {
t.Fatalf("err: %v", err)
}
require.NoError(t, err)
gatherDeploys := func(iter memdb.ResultIterator) []*structs.Deployment {
var deploys []*structs.Deployment
@@ -718,60 +709,67 @@ func TestStateStore_DeploymentsByIDPrefix(t *testing.T) {
return deploys
}
deploys := gatherDeploys(iter)
if len(deploys) != 1 {
t.Fatalf("err: %v", err)
}
t.Run("first deployment", func(t *testing.T) {
// Create a watchset so we can test that getters don't cause it to fire
ws := memdb.NewWatchSet()
iter, err := state.DeploymentsByIDPrefix(ws, deploy.Namespace, deploy.ID, SortDefault)
require.NoError(t, err)
if watchFired(ws) {
t.Fatalf("bad")
}
deploys := gatherDeploys(iter)
require.Len(t, deploys, 1)
require.False(t, watchFired(ws))
})
iter, err = state.DeploymentsByIDPrefix(ws, deploy.Namespace, "11")
if err != nil {
t.Fatalf("err: %v", err)
}
t.Run("using prefix", func(t *testing.T) {
ws := memdb.NewWatchSet()
iter, err := state.DeploymentsByIDPrefix(ws, deploy.Namespace, "11", SortDefault)
require.NoError(t, err)
deploys = gatherDeploys(iter)
if len(deploys) != 1 {
t.Fatalf("err: %v", err)
}
deploys := gatherDeploys(iter)
require.Len(t, deploys, 1)
require.False(t, watchFired(ws))
})
deploy = mock.Deployment()
deploy.ID = "11222222-662e-d0ab-d1c9-3e434af7bdb4"
err = state.UpsertDeployment(1001, deploy)
if err != nil {
t.Fatalf("err: %v", err)
}
require.NoError(t, err)
if !watchFired(ws) {
t.Fatalf("bad")
}
t.Run("more than one", func(t *testing.T) {
ws := memdb.NewWatchSet()
iter, err := state.DeploymentsByIDPrefix(ws, deploy.Namespace, "11", SortDefault)
require.NoError(t, err)
ws = memdb.NewWatchSet()
iter, err = state.DeploymentsByIDPrefix(ws, deploy.Namespace, "11")
if err != nil {
t.Fatalf("err: %v", err)
}
deploys := gatherDeploys(iter)
require.Len(t, deploys, 2)
})
deploys = gatherDeploys(iter)
if len(deploys) != 2 {
t.Fatalf("err: %v", err)
}
t.Run("filter to one", func(t *testing.T) {
ws := memdb.NewWatchSet()
iter, err := state.DeploymentsByIDPrefix(ws, deploy.Namespace, "1111", SortDefault)
require.NoError(t, err)
iter, err = state.DeploymentsByIDPrefix(ws, deploy.Namespace, "1111")
if err != nil {
t.Fatalf("err: %v", err)
}
deploys := gatherDeploys(iter)
require.Len(t, deploys, 1)
require.False(t, watchFired(ws))
})
deploys = gatherDeploys(iter)
if len(deploys) != 1 {
t.Fatalf("err: %v", err)
}
t.Run("reverse order", func(t *testing.T) {
ws := memdb.NewWatchSet()
iter, err := state.DeploymentsByIDPrefix(ws, deploy.Namespace, "11", SortReverse)
require.NoError(t, err)
if watchFired(ws) {
t.Fatalf("bad")
}
got := []string{}
for _, d := range gatherDeploys(iter) {
got = append(got, d.ID)
}
expected := []string{
"11222222-662e-d0ab-d1c9-3e434af7bdb4",
"11111111-662e-d0ab-d1c9-3e434af7bdb4",
}
require.Equal(t, expected, got)
require.False(t, watchFired(ws))
})
}
func TestStateStore_UpsertNode_Node(t *testing.T) {
@@ -3874,12 +3872,6 @@ func TestStateStore_EvalsByIDPrefix(t *testing.T) {
t.Fatalf("err: %v", err)
}
ws := memdb.NewWatchSet()
iter, err := state.EvalsByIDPrefix(ws, structs.DefaultNamespace, "aaaa")
if err != nil {
t.Fatalf("err: %v", err)
}
gatherEvals := func(iter memdb.ResultIterator) []*structs.Evaluation {
var evals []*structs.Evaluation
for {
@@ -3892,32 +3884,57 @@ func TestStateStore_EvalsByIDPrefix(t *testing.T) {
return evals
}
out := gatherEvals(iter)
if len(out) != 5 {
t.Fatalf("bad: expected five evaluations, got: %#v", out)
}
t.Run("list by prefix", func(t *testing.T) {
ws := memdb.NewWatchSet()
iter, err := state.EvalsByIDPrefix(ws, structs.DefaultNamespace, "aaaa", SortDefault)
require.NoError(t, err)
sort.Sort(EvalIDSort(evals))
for index, eval := range out {
if ids[index] != eval.ID {
t.Fatalf("bad: got unexpected id: %s", eval.ID)
got := []string{}
for _, e := range gatherEvals(iter) {
got = append(got, e.ID)
}
}
iter, err = state.EvalsByIDPrefix(ws, structs.DefaultNamespace, "b-a7bfb")
if err != nil {
t.Fatalf("err: %v", err)
}
expected := []string{
"aaaaaaaa-7bfb-395d-eb95-0685af2176b2",
"aaaaaaab-7bfb-395d-eb95-0685af2176b2",
"aaaaaabb-7bfb-395d-eb95-0685af2176b2",
"aaaaabbb-7bfb-395d-eb95-0685af2176b2",
"aaaabbbb-7bfb-395d-eb95-0685af2176b2",
}
require.Len(t, got, 5, "expected five evaluations")
require.Equal(t, expected, got) // Must be in this order.
})
out = gatherEvals(iter)
if len(out) != 0 {
t.Fatalf("bad: unexpected zero evaluations, got: %#v", out)
}
t.Run("invalid prefix", func(t *testing.T) {
ws := memdb.NewWatchSet()
iter, err := state.EvalsByIDPrefix(ws, structs.DefaultNamespace, "b-a7bfb", SortDefault)
require.NoError(t, err)
if watchFired(ws) {
t.Fatalf("bad")
}
out := gatherEvals(iter)
require.Len(t, out, 0, "expected zero evaluations")
require.False(t, watchFired(ws))
})
t.Run("reverse order", func(t *testing.T) {
ws := memdb.NewWatchSet()
iter, err := state.EvalsByIDPrefix(ws, structs.DefaultNamespace, "aaaa", SortReverse)
require.NoError(t, err)
got := []string{}
for _, e := range gatherEvals(iter) {
got = append(got, e.ID)
}
expected := []string{
"aaaabbbb-7bfb-395d-eb95-0685af2176b2",
"aaaaabbb-7bfb-395d-eb95-0685af2176b2",
"aaaaaabb-7bfb-395d-eb95-0685af2176b2",
"aaaaaaab-7bfb-395d-eb95-0685af2176b2",
"aaaaaaaa-7bfb-395d-eb95-0685af2176b2",
}
require.Len(t, got, 5, "expected five evaluations")
require.Equal(t, expected, got) // Must be in this order.
})
}
func TestStateStore_UpdateAllocsFromClient(t *testing.T) {
@@ -5362,15 +5379,7 @@ func TestStateStore_AllocsByIDPrefix(t *testing.T) {
}
err := state.UpsertAllocs(structs.MsgTypeTestSetup, 1000, allocs)
if err != nil {
t.Fatalf("err: %v", err)
}
ws := memdb.NewWatchSet()
iter, err := state.AllocsByIDPrefix(ws, structs.DefaultNamespace, "aaaa")
if err != nil {
t.Fatalf("err: %v", err)
}
require.NoError(t, err)
gatherAllocs := func(iter memdb.ResultIterator) []*structs.Allocation {
var allocs []*structs.Allocation
@@ -5384,32 +5393,61 @@ func TestStateStore_AllocsByIDPrefix(t *testing.T) {
return allocs
}
out := gatherAllocs(iter)
if len(out) != 5 {
t.Fatalf("bad: expected five allocations, got: %#v", out)
}
t.Run("allocs by prefix", func(t *testing.T) {
ws := memdb.NewWatchSet()
iter, err := state.AllocsByIDPrefix(ws, structs.DefaultNamespace, "aaaa", SortDefault)
require.NoError(t, err)
sort.Sort(AllocIDSort(allocs))
out := gatherAllocs(iter)
require.Len(t, out, 5, "expected five allocations")
for index, alloc := range out {
if ids[index] != alloc.ID {
t.Fatalf("bad: got unexpected id: %s", alloc.ID)
got := []string{}
for _, a := range out {
got = append(got, a.ID)
}
}
expected := []string{
"aaaaaaaa-7bfb-395d-eb95-0685af2176b2",
"aaaaaaab-7bfb-395d-eb95-0685af2176b2",
"aaaaaabb-7bfb-395d-eb95-0685af2176b2",
"aaaaabbb-7bfb-395d-eb95-0685af2176b2",
"aaaabbbb-7bfb-395d-eb95-0685af2176b2",
}
require.Equal(t, expected, got)
require.False(t, watchFired(ws))
})
iter, err = state.AllocsByIDPrefix(ws, structs.DefaultNamespace, "b-a7bfb")
if err != nil {
t.Fatalf("err: %v", err)
}
t.Run("invalid prefix", func(t *testing.T) {
ws := memdb.NewWatchSet()
iter, err := state.AllocsByIDPrefix(ws, structs.DefaultNamespace, "b-a7bfb", SortDefault)
require.NoError(t, err)
out = gatherAllocs(iter)
if len(out) != 0 {
t.Fatalf("bad: unexpected zero allocations, got: %#v", out)
}
out := gatherAllocs(iter)
require.Len(t, out, 0)
require.False(t, watchFired(ws))
})
if watchFired(ws) {
t.Fatalf("bad")
}
t.Run("reverse", func(t *testing.T) {
ws := memdb.NewWatchSet()
iter, err := state.AllocsByIDPrefix(ws, structs.DefaultNamespace, "aaaa", SortReverse)
require.NoError(t, err)
out := gatherAllocs(iter)
require.Len(t, out, 5, "expected five allocations")
got := []string{}
for _, a := range out {
got = append(got, a.ID)
}
expected := []string{
"aaaabbbb-7bfb-395d-eb95-0685af2176b2",
"aaaaabbb-7bfb-395d-eb95-0685af2176b2",
"aaaaaabb-7bfb-395d-eb95-0685af2176b2",
"aaaaaaab-7bfb-395d-eb95-0685af2176b2",
"aaaaaaaa-7bfb-395d-eb95-0685af2176b2",
}
require.Equal(t, expected, got)
require.False(t, watchFired(ws))
})
}
func TestStateStore_Allocs(t *testing.T) {
@@ -7717,36 +7755,54 @@ func TestStateStore_ACLTokenByAccessorIDPrefix(t *testing.T) {
for _, prefix := range prefixes {
tk := mock.ACLToken()
tk.AccessorID = prefix + tk.AccessorID[4:]
if err := state.UpsertACLTokens(structs.MsgTypeTestSetup, baseIndex, []*structs.ACLToken{tk}); err != nil {
t.Fatalf("err: %v", err)
}
err := state.UpsertACLTokens(structs.MsgTypeTestSetup, baseIndex, []*structs.ACLToken{tk})
require.NoError(t, err)
baseIndex++
}
// Scan by prefix
iter, err := state.ACLTokenByAccessorIDPrefix(nil, "aa")
if err != nil {
t.Fatalf("err: %v", err)
}
// Ensure we see both tokens
count := 0
out := []string{}
for {
raw := iter.Next()
if raw == nil {
break
gatherTokens := func(iter memdb.ResultIterator) []*structs.ACLToken {
var tokens []*structs.ACLToken
for {
raw := iter.Next()
if raw == nil {
break
}
tokens = append(tokens, raw.(*structs.ACLToken))
}
count++
out = append(out, raw.(*structs.ACLToken).AccessorID[:4])
return tokens
}
if count != 2 {
t.Fatalf("bad: %d %v", count, out)
}
sort.Strings(out)
expect := []string{"aaaa", "aabb"}
assert.Equal(t, expect, out)
t.Run("scan by prefix", func(t *testing.T) {
iter, err := state.ACLTokenByAccessorIDPrefix(nil, "aa", SortDefault)
require.NoError(t, err)
// Ensure we see both tokens
out := gatherTokens(iter)
require.Len(t, out, 2)
got := []string{}
for _, t := range out {
got = append(got, t.AccessorID[:4])
}
expect := []string{"aaaa", "aabb"}
require.Equal(t, expect, got)
})
t.Run("reverse order", func(t *testing.T) {
iter, err := state.ACLTokenByAccessorIDPrefix(nil, "aa", SortReverse)
require.NoError(t, err)
// Ensure we see both tokens
out := gatherTokens(iter)
require.Len(t, out, 2)
got := []string{}
for _, t := range out {
got = append(got, t.AccessorID[:4])
}
expect := []string{"aabb", "aaaa"}
require.Equal(t, expect, got)
})
}
func TestStateStore_ACLTokensByGlobal(t *testing.T) {
@@ -7754,32 +7810,51 @@ func TestStateStore_ACLTokensByGlobal(t *testing.T) {
state := testStateStore(t)
tk1 := mock.ACLToken()
tk1.AccessorID = "aaaa" + tk1.AccessorID[4:]
tk2 := mock.ACLToken()
tk2.AccessorID = "aabb" + tk2.AccessorID[4:]
tk3 := mock.ACLToken()
tk4 := mock.ACLToken()
tk3.AccessorID = "bbbb" + tk3.AccessorID[4:]
tk3.Global = true
if err := state.UpsertACLTokens(structs.MsgTypeTestSetup, 1000, []*structs.ACLToken{tk1, tk2, tk3, tk4}); err != nil {
t.Fatalf("err: %v", err)
}
tk4 := mock.ACLToken()
tk4.AccessorID = "ffff" + tk4.AccessorID[4:]
iter, err := state.ACLTokensByGlobal(nil, true)
if err != nil {
t.Fatalf("err: %v", err)
}
err := state.UpsertACLTokens(structs.MsgTypeTestSetup, 1000, []*structs.ACLToken{tk1, tk2, tk3, tk4})
require.NoError(t, err)
// Ensure we see the one global policies
count := 0
for {
raw := iter.Next()
if raw == nil {
break
gatherTokens := func(iter memdb.ResultIterator) []*structs.ACLToken {
var tokens []*structs.ACLToken
for {
raw := iter.Next()
if raw == nil {
break
}
tokens = append(tokens, raw.(*structs.ACLToken))
}
count++
}
if count != 1 {
t.Fatalf("bad: %d", count)
return tokens
}
t.Run("only global tokens", func(t *testing.T) {
iter, err := state.ACLTokensByGlobal(nil, true, SortDefault)
require.NoError(t, err)
got := gatherTokens(iter)
require.Len(t, got, 1)
require.Equal(t, tk3.AccessorID, got[0].AccessorID)
})
t.Run("reverse order", func(t *testing.T) {
iter, err := state.ACLTokensByGlobal(nil, false, SortReverse)
require.NoError(t, err)
expected := []*structs.ACLToken{tk4, tk2, tk1}
got := gatherTokens(iter)
require.Len(t, got, 3)
require.Equal(t, expected, got)
})
}
func TestStateStore_OneTimeTokens(t *testing.T) {

View File

@@ -70,11 +70,19 @@ The table below shows this endpoint's support for
### Parameters
- `global` `(bool: false)` - If true, only return ACL tokens that are
replicated globally to all regions.
- `prefix` `(string: "")` - Specifies a string to filter ACL tokens based on an
accessor ID prefix. Because the value is decoded to bytes, the prefix must
have an even number of hexadecimal characters (0-9a-f). This is specified as
a query string parameter.
- `reverse` `(bool: false)` - Specifies the list of returned ACL tokens should
be sorted in the reverse order. By default ACL tokens are returned sorted in
chronological order (older ACL tokens first), or in lexicographical order by
their ID if the `prefix` or `global` query parameters are used.
### Sample Request
```shell-session

View File

@@ -43,6 +43,11 @@ The table below shows this endpoint's support for
a large number of allocations may set `task_states=false` to significantly
reduce the size of the response.
- `reverse` `(bool: false)` - Specifies the list of returned allocations should
be sorted in the reverse order. By default allocations are returned sorted in
chronological order (older evaluations first), or in lexicographical order by
their ID if the `prefix` query parameter is used.
### Sample Request
```shell-session

View File

@@ -50,9 +50,10 @@ The table below shows this endpoint's support for
results. Consider using pagination or a query parameter to reduce resource
used to serve the request.
- `ascending` `(bool: false)` - Specifies the list of returned deployments should
be sorted in chronological order (oldest evaluations first). By default deployments
are returned sorted in reverse chronological order (newest deployments first).
- `reverse` `(bool: false)` - Specifies the list of returned deployments should
be sorted in the reverse order. By default deployments are returned sorted in
chronological order (older deployments first), or in lexicographical order
by their ID if the `prefix` query parameter is used.
### Sample Request

View File

@@ -57,9 +57,10 @@ The table below shows this endpoint's support for
Specifying `*` will return all evaluations across all authorized namespaces.
This parameter is used before any `filter` expression is applied.
- `ascending` `(bool: false)` - Specifies the list of returned evaluations should
be sorted in chronological order (oldest evaluations first). By default evaluations
are returned sorted in reverse chronological order (newest evaluations first).
- `reverse` `(bool: false)` - Specifies the list of returned evaluations should
be sorted in the reverse order. By default evaluations are returned sorted in
chronological order (older evaluations first), or in lexicographical order by
their ID if the `prefix` query parameter is used.
### Sample Request