nomad: ACL endpoints check support enabled and redirect to authority

This commit is contained in:
Armon Dadgar
2017-08-20 19:59:25 -07:00
parent d7f3b896cd
commit ab5ce9b1a7
7 changed files with 159 additions and 41 deletions

View File

@@ -12,7 +12,7 @@ import (
func TestHTTP_ACLPolicyList(t *testing.T) {
t.Parallel()
httpTest(t, nil, func(s *TestAgent) {
httpACLTest(t, nil, func(s *TestAgent) {
p1 := mock.ACLPolicy()
p2 := mock.ACLPolicy()
p3 := mock.ACLPolicy()
@@ -59,7 +59,7 @@ func TestHTTP_ACLPolicyList(t *testing.T) {
func TestHTTP_ACLPolicyQuery(t *testing.T) {
t.Parallel()
httpTest(t, nil, func(s *TestAgent) {
httpACLTest(t, nil, func(s *TestAgent) {
p1 := mock.ACLPolicy()
args := structs.ACLPolicyUpsertRequest{
Policies: []*structs.ACLPolicy{p1},
@@ -104,7 +104,7 @@ func TestHTTP_ACLPolicyQuery(t *testing.T) {
func TestHTTP_ACLPolicyCreate(t *testing.T) {
t.Parallel()
httpTest(t, nil, func(s *TestAgent) {
httpACLTest(t, nil, func(s *TestAgent) {
// Make the HTTP request
p1 := mock.ACLPolicy()
buf := encodeReq(p1)
@@ -138,7 +138,7 @@ func TestHTTP_ACLPolicyCreate(t *testing.T) {
func TestHTTP_ACLPolicyDelete(t *testing.T) {
t.Parallel()
httpTest(t, nil, func(s *TestAgent) {
httpACLTest(t, nil, func(s *TestAgent) {
p1 := mock.ACLPolicy()
args := structs.ACLPolicyUpsertRequest{
Policies: []*structs.ACLPolicy{p1},
@@ -176,7 +176,11 @@ func TestHTTP_ACLPolicyDelete(t *testing.T) {
func TestHTTP_ACLTokenBootstrap(t *testing.T) {
t.Parallel()
httpTest(t, nil, func(s *TestAgent) {
conf := func(c *Config) {
c.ACL.Enabled = true
c.ACL.PolicyTTL = 0 // Special flag to disable auto-bootstrap
}
httpTest(t, conf, func(s *TestAgent) {
// Make the HTTP request
req, err := http.NewRequest("PUT", "/v1/acl/bootstrap", nil)
if err != nil {
@@ -204,7 +208,7 @@ func TestHTTP_ACLTokenBootstrap(t *testing.T) {
func TestHTTP_ACLTokenList(t *testing.T) {
t.Parallel()
httpTest(t, nil, func(s *TestAgent) {
httpACLTest(t, nil, func(s *TestAgent) {
p1 := mock.ACLToken()
p1.AccessorID = ""
p2 := mock.ACLToken()
@@ -244,9 +248,9 @@ func TestHTTP_ACLTokenList(t *testing.T) {
t.Fatalf("missing last contact")
}
// Check the output
// Check the output (includes boostrap token)
n := obj.([]*structs.ACLTokenListStub)
if len(n) != 3 {
if len(n) != 4 {
t.Fatalf("bad: %#v", n)
}
})
@@ -254,7 +258,7 @@ func TestHTTP_ACLTokenList(t *testing.T) {
func TestHTTP_ACLTokenQuery(t *testing.T) {
t.Parallel()
httpTest(t, nil, func(s *TestAgent) {
httpACLTest(t, nil, func(s *TestAgent) {
p1 := mock.ACLToken()
p1.AccessorID = ""
args := structs.ACLTokenUpsertRequest{
@@ -299,7 +303,7 @@ func TestHTTP_ACLTokenQuery(t *testing.T) {
func TestHTTP_ACLTokenCreate(t *testing.T) {
t.Parallel()
httpTest(t, nil, func(s *TestAgent) {
httpACLTest(t, nil, func(s *TestAgent) {
// Make the HTTP request
p1 := mock.ACLToken()
p1.AccessorID = ""
@@ -332,7 +336,7 @@ func TestHTTP_ACLTokenCreate(t *testing.T) {
func TestHTTP_ACLTokenDelete(t *testing.T) {
t.Parallel()
httpTest(t, nil, func(s *TestAgent) {
httpACLTest(t, nil, func(s *TestAgent) {
p1 := mock.ACLToken()
p1.AccessorID = ""
args := structs.ACLTokenUpsertRequest{

View File

@@ -496,6 +496,18 @@ func httpTest(t testing.TB, cb func(c *Config), f func(srv *TestAgent)) {
f(s)
}
func httpACLTest(t testing.TB, cb func(c *Config), f func(srv *TestAgent)) {
s := makeHTTPServer(t, func(c *Config) {
c.ACL.Enabled = true
if cb != nil {
cb(c)
}
})
defer s.Shutdown()
testutil.WaitForLeader(t, s.Agent.RPC)
f(s)
}
func encodeReq(obj interface{}) io.ReadCloser {
buf := bytes.NewBuffer(nil)
enc := json.NewEncoder(buf)

View File

@@ -16,6 +16,7 @@ import (
"github.com/hashicorp/nomad/api"
"github.com/hashicorp/nomad/client/fingerprint"
"github.com/hashicorp/nomad/nomad"
"github.com/hashicorp/nomad/nomad/mock"
"github.com/hashicorp/nomad/nomad/structs"
sconfig "github.com/hashicorp/nomad/nomad/structs/config"
"github.com/hashicorp/nomad/testutil"
@@ -66,6 +67,9 @@ type TestAgent struct {
// Agent is the embedded Nomad agent.
// It is valid after Start().
*Agent
// Token is auto-bootstrapped if ACLs are enabled
Token *structs.ACLToken
}
// NewTestAgent returns a started agent with the given name and
@@ -164,6 +168,17 @@ func (a *TestAgent) Start() *TestAgent {
panic(fmt.Sprintf("failed OK response: %v", err))
})
}
// Check if ACLs enabled. Use special value of PolicyTTL 0s
// to do a bypass of this step. This is so we can test bootstrap
// without having to pass down a special flag.
if a.Config.ACL.Enabled && a.Config.Server.Enabled && a.Config.ACL.PolicyTTL != 0 {
a.Token = mock.ACLManagementToken()
state := a.Agent.server.State()
if err := state.BootstrapACLTokens(1, a.Token); err != nil {
panic(fmt.Sprintf("token bootstrap failed: %v", err))
}
}
return a
}

View File

@@ -10,6 +10,11 @@ import (
"github.com/hashicorp/nomad/nomad/structs"
)
var (
// aclDisabled is returned when an ACL endpoint is hit but ACLs are not enabled
aclDisabled = fmt.Errorf("ACL support disabled")
)
// ACL endpoint is used for manipulating ACL tokens and policies
type ACL struct {
srv *Server
@@ -17,6 +22,12 @@ type ACL struct {
// UpsertPolicies is used to create or update a set of policies
func (a *ACL) UpsertPolicies(args *structs.ACLPolicyUpsertRequest, reply *structs.GenericResponse) error {
// Ensure ACLs are enabled, and always flow modification requests to the authoritative region
if !a.srv.config.ACLEnabled {
return aclDisabled
}
args.Region = a.srv.config.AuthoritativeRegion
if done, err := a.srv.forward("ACL.UpsertPolicies", args, args, reply); done {
return err
}
@@ -47,6 +58,12 @@ func (a *ACL) UpsertPolicies(args *structs.ACLPolicyUpsertRequest, reply *struct
// DeletePolicies is used to delete policies
func (a *ACL) DeletePolicies(args *structs.ACLPolicyDeleteRequest, reply *structs.GenericResponse) error {
// Ensure ACLs are enabled, and always flow modification requests to the authoritative region
if !a.srv.config.ACLEnabled {
return aclDisabled
}
args.Region = a.srv.config.AuthoritativeRegion
if done, err := a.srv.forward("ACL.DeletePolicies", args, args, reply); done {
return err
}
@@ -70,6 +87,9 @@ func (a *ACL) DeletePolicies(args *structs.ACLPolicyDeleteRequest, reply *struct
// ListPolicies is used to list the policies
func (a *ACL) ListPolicies(args *structs.ACLPolicyListRequest, reply *structs.ACLPolicyListResponse) error {
if !a.srv.config.ACLEnabled {
return aclDisabled
}
if done, err := a.srv.forward("ACL.ListPolicies", args, args, reply); done {
return err
}
@@ -122,6 +142,9 @@ func (a *ACL) ListPolicies(args *structs.ACLPolicyListRequest, reply *structs.AC
// GetPolicy is used to get a specific policy
func (a *ACL) GetPolicy(args *structs.ACLPolicySpecificRequest, reply *structs.SingleACLPolicyResponse) error {
if !a.srv.config.ACLEnabled {
return aclDisabled
}
if done, err := a.srv.forward("ACL.GetPolicy", args, args, reply); done {
return err
}
@@ -157,6 +180,9 @@ func (a *ACL) GetPolicy(args *structs.ACLPolicySpecificRequest, reply *structs.S
// GetPolicies is used to get a set of policies
func (a *ACL) GetPolicies(args *structs.ACLPolicySetRequest, reply *structs.ACLPolicySetResponse) error {
if !a.srv.config.ACLEnabled {
return aclDisabled
}
if done, err := a.srv.forward("ACL.GetPolicies", args, args, reply); done {
return err
}
@@ -194,6 +220,12 @@ func (a *ACL) GetPolicies(args *structs.ACLPolicySetRequest, reply *structs.ACLP
// Bootstrap is used to bootstrap the initial token
func (a *ACL) Bootstrap(args *structs.ACLTokenBootstrapRequest, reply *structs.ACLTokenUpsertResponse) error {
// Ensure ACLs are enabled, and always flow modification requests to the authoritative region
if !a.srv.config.ACLEnabled {
return aclDisabled
}
args.Region = a.srv.config.AuthoritativeRegion
if done, err := a.srv.forward("ACL.Bootstrap", args, args, reply); done {
return err
}
@@ -250,6 +282,12 @@ func (a *ACL) Bootstrap(args *structs.ACLTokenBootstrapRequest, reply *structs.A
// UpsertTokens is used to create or update a set of tokens
func (a *ACL) UpsertTokens(args *structs.ACLTokenUpsertRequest, reply *structs.ACLTokenUpsertResponse) error {
// Ensure ACLs are enabled, and always flow modification requests to the authoritative region
if !a.srv.config.ACLEnabled {
return aclDisabled
}
args.Region = a.srv.config.AuthoritativeRegion
if done, err := a.srv.forward("ACL.UpsertTokens", args, args, reply); done {
return err
}
@@ -322,6 +360,12 @@ func (a *ACL) UpsertTokens(args *structs.ACLTokenUpsertRequest, reply *structs.A
// DeleteTokens is used to delete tokens
func (a *ACL) DeleteTokens(args *structs.ACLTokenDeleteRequest, reply *structs.GenericResponse) error {
// Ensure ACLs are enabled, and always flow modification requests to the authoritative region
if !a.srv.config.ACLEnabled {
return aclDisabled
}
args.Region = a.srv.config.AuthoritativeRegion
if done, err := a.srv.forward("ACL.DeleteTokens", args, args, reply); done {
return err
}
@@ -345,6 +389,9 @@ func (a *ACL) DeleteTokens(args *structs.ACLTokenDeleteRequest, reply *structs.G
// ListTokens is used to list the tokens
func (a *ACL) ListTokens(args *structs.ACLTokenListRequest, reply *structs.ACLTokenListResponse) error {
if !a.srv.config.ACLEnabled {
return aclDisabled
}
if done, err := a.srv.forward("ACL.ListTokens", args, args, reply); done {
return err
}
@@ -393,6 +440,9 @@ func (a *ACL) ListTokens(args *structs.ACLTokenListRequest, reply *structs.ACLTo
// GetToken is used to get a specific token
func (a *ACL) GetToken(args *structs.ACLTokenSpecificRequest, reply *structs.SingleACLTokenResponse) error {
if !a.srv.config.ACLEnabled {
return aclDisabled
}
if done, err := a.srv.forward("ACL.GetToken", args, args, reply); done {
return err
}
@@ -428,6 +478,9 @@ func (a *ACL) GetToken(args *structs.ACLTokenSpecificRequest, reply *structs.Sin
// GetTokens is used to get a set of token
func (a *ACL) GetTokens(args *structs.ACLTokenSetRequest, reply *structs.ACLTokenSetResponse) error {
if !a.srv.config.ACLEnabled {
return aclDisabled
}
if done, err := a.srv.forward("ACL.GetTokens", args, args, reply); done {
return err
}
@@ -465,6 +518,9 @@ func (a *ACL) GetTokens(args *structs.ACLTokenSetRequest, reply *structs.ACLToke
// ResolveToken is used to lookup a specific token by a secret ID. This is used for enforcing ACLs by clients.
func (a *ACL) ResolveToken(args *structs.ResolveACLTokenRequest, reply *structs.ResolveACLTokenResponse) error {
if !a.srv.config.ACLEnabled {
return aclDisabled
}
if done, err := a.srv.forward("ACL.ResolveToken", args, args, reply); done {
return err
}

View File

@@ -14,7 +14,7 @@ import (
func TestACLEndpoint_GetPolicy(t *testing.T) {
t.Parallel()
s1 := testServer(t, nil)
s1, _ := testACLServer(t, nil)
defer s1.Shutdown()
codec := rpcClient(t, s1)
testutil.WaitForLeader(t, s1.RPC)
@@ -46,7 +46,7 @@ func TestACLEndpoint_GetPolicy(t *testing.T) {
func TestACLEndpoint_GetPolicy_Blocking(t *testing.T) {
t.Parallel()
s1 := testServer(t, nil)
s1, _ := testACLServer(t, nil)
defer s1.Shutdown()
state := s1.fsm.State()
codec := rpcClient(t, s1)
@@ -124,7 +124,7 @@ func TestACLEndpoint_GetPolicy_Blocking(t *testing.T) {
func TestACLEndpoint_GetPolicies(t *testing.T) {
t.Parallel()
s1 := testServer(t, nil)
s1, _ := testACLServer(t, nil)
defer s1.Shutdown()
codec := rpcClient(t, s1)
testutil.WaitForLeader(t, s1.RPC)
@@ -160,7 +160,7 @@ func TestACLEndpoint_GetPolicies(t *testing.T) {
func TestACLEndpoint_GetPolicies_Blocking(t *testing.T) {
t.Parallel()
s1 := testServer(t, nil)
s1, _ := testACLServer(t, nil)
defer s1.Shutdown()
state := s1.fsm.State()
codec := rpcClient(t, s1)
@@ -238,7 +238,7 @@ func TestACLEndpoint_GetPolicies_Blocking(t *testing.T) {
func TestACLEndpoint_ListPolicies(t *testing.T) {
t.Parallel()
s1 := testServer(t, nil)
s1, _ := testACLServer(t, nil)
defer s1.Shutdown()
codec := rpcClient(t, s1)
testutil.WaitForLeader(t, s1.RPC)
@@ -279,7 +279,7 @@ func TestACLEndpoint_ListPolicies(t *testing.T) {
func TestACLEndpoint_ListPolicies_Blocking(t *testing.T) {
t.Parallel()
s1 := testServer(t, nil)
s1, _ := testACLServer(t, nil)
defer s1.Shutdown()
state := s1.fsm.State()
codec := rpcClient(t, s1)
@@ -338,7 +338,7 @@ func TestACLEndpoint_ListPolicies_Blocking(t *testing.T) {
func TestACLEndpoint_DeletePolicies(t *testing.T) {
t.Parallel()
s1 := testServer(t, nil)
s1, _ := testACLServer(t, nil)
defer s1.Shutdown()
codec := rpcClient(t, s1)
testutil.WaitForLeader(t, s1.RPC)
@@ -361,7 +361,7 @@ func TestACLEndpoint_DeletePolicies(t *testing.T) {
func TestACLEndpoint_UpsertPolicies(t *testing.T) {
t.Parallel()
s1 := testServer(t, nil)
s1, _ := testACLServer(t, nil)
defer s1.Shutdown()
codec := rpcClient(t, s1)
testutil.WaitForLeader(t, s1.RPC)
@@ -388,7 +388,7 @@ func TestACLEndpoint_UpsertPolicies(t *testing.T) {
func TestACLEndpoint_UpsertPolicies_Invalid(t *testing.T) {
t.Parallel()
s1 := testServer(t, nil)
s1, _ := testACLServer(t, nil)
defer s1.Shutdown()
codec := rpcClient(t, s1)
testutil.WaitForLeader(t, s1.RPC)
@@ -412,7 +412,7 @@ func TestACLEndpoint_UpsertPolicies_Invalid(t *testing.T) {
func TestACLEndpoint_GetToken(t *testing.T) {
t.Parallel()
s1 := testServer(t, nil)
s1, _ := testACLServer(t, nil)
defer s1.Shutdown()
codec := rpcClient(t, s1)
testutil.WaitForLeader(t, s1.RPC)
@@ -444,7 +444,7 @@ func TestACLEndpoint_GetToken(t *testing.T) {
func TestACLEndpoint_GetToken_Blocking(t *testing.T) {
t.Parallel()
s1 := testServer(t, nil)
s1, _ := testACLServer(t, nil)
defer s1.Shutdown()
state := s1.fsm.State()
codec := rpcClient(t, s1)
@@ -522,7 +522,7 @@ func TestACLEndpoint_GetToken_Blocking(t *testing.T) {
func TestACLEndpoint_GetTokens(t *testing.T) {
t.Parallel()
s1 := testServer(t, nil)
s1, _ := testACLServer(t, nil)
defer s1.Shutdown()
codec := rpcClient(t, s1)
testutil.WaitForLeader(t, s1.RPC)
@@ -557,7 +557,7 @@ func TestACLEndpoint_GetTokens(t *testing.T) {
func TestACLEndpoint_GetTokens_Blocking(t *testing.T) {
t.Parallel()
s1 := testServer(t, nil)
s1, _ := testACLServer(t, nil)
defer s1.Shutdown()
state := s1.fsm.State()
codec := rpcClient(t, s1)
@@ -635,7 +635,7 @@ func TestACLEndpoint_GetTokens_Blocking(t *testing.T) {
func TestACLEndpoint_ListTokens(t *testing.T) {
t.Parallel()
s1 := testServer(t, nil)
s1, _ := testACLServer(t, nil)
defer s1.Shutdown()
codec := rpcClient(t, s1)
testutil.WaitForLeader(t, s1.RPC)
@@ -658,7 +658,7 @@ func TestACLEndpoint_ListTokens(t *testing.T) {
t.Fatalf("err: %v", err)
}
assert.Equal(t, uint64(1000), resp.Index)
assert.Equal(t, 2, len(resp.Tokens))
assert.Equal(t, 3, len(resp.Tokens))
// Lookup the tokens by prefix
get = &structs.ACLTokenListRequest{
@@ -686,12 +686,12 @@ func TestACLEndpoint_ListTokens(t *testing.T) {
t.Fatalf("err: %v", err)
}
assert.Equal(t, uint64(1000), resp3.Index)
assert.Equal(t, 1, len(resp3.Tokens))
assert.Equal(t, 2, len(resp3.Tokens))
}
func TestACLEndpoint_ListTokens_Blocking(t *testing.T) {
t.Parallel()
s1 := testServer(t, nil)
s1, _ := testACLServer(t, nil)
defer s1.Shutdown()
state := s1.fsm.State()
codec := rpcClient(t, s1)
@@ -702,7 +702,7 @@ func TestACLEndpoint_ListTokens_Blocking(t *testing.T) {
// Upsert eval triggers watches
time.AfterFunc(100*time.Millisecond, func() {
if err := state.UpsertACLTokens(2, []*structs.ACLToken{token}); err != nil {
if err := state.UpsertACLTokens(3, []*structs.ACLToken{token}); err != nil {
t.Fatalf("err: %v", err)
}
})
@@ -710,7 +710,7 @@ func TestACLEndpoint_ListTokens_Blocking(t *testing.T) {
req := &structs.ACLTokenListRequest{
QueryOptions: structs.QueryOptions{
Region: "global",
MinQueryIndex: 1,
MinQueryIndex: 2,
},
}
start := time.Now()
@@ -722,19 +722,19 @@ func TestACLEndpoint_ListTokens_Blocking(t *testing.T) {
if elapsed := time.Since(start); elapsed < 100*time.Millisecond {
t.Fatalf("should block (returned in %s) %#v", elapsed, resp)
}
assert.Equal(t, uint64(2), resp.Index)
if len(resp.Tokens) != 1 || resp.Tokens[0].AccessorID != token.AccessorID {
assert.Equal(t, uint64(3), resp.Index)
if len(resp.Tokens) != 2 {
t.Fatalf("bad: %#v", resp.Tokens)
}
// Eval deletion triggers watches
time.AfterFunc(100*time.Millisecond, func() {
if err := state.DeleteACLTokens(3, []string{token.AccessorID}); err != nil {
if err := state.DeleteACLTokens(4, []string{token.AccessorID}); err != nil {
t.Fatalf("err: %v", err)
}
})
req.MinQueryIndex = 2
req.MinQueryIndex = 3
start = time.Now()
var resp2 structs.ACLTokenListResponse
if err := msgpackrpc.CallWithCodec(codec, "ACL.ListTokens", req, &resp2); err != nil {
@@ -744,13 +744,13 @@ func TestACLEndpoint_ListTokens_Blocking(t *testing.T) {
if elapsed := time.Since(start); elapsed < 100*time.Millisecond {
t.Fatalf("should block (returned in %s) %#v", elapsed, resp2)
}
assert.Equal(t, uint64(3), resp2.Index)
assert.Equal(t, 0, len(resp2.Tokens))
assert.Equal(t, uint64(4), resp2.Index)
assert.Equal(t, 1, len(resp2.Tokens))
}
func TestACLEndpoint_DeleteTokens(t *testing.T) {
t.Parallel()
s1 := testServer(t, nil)
s1, _ := testACLServer(t, nil)
defer s1.Shutdown()
codec := rpcClient(t, s1)
testutil.WaitForLeader(t, s1.RPC)
@@ -773,7 +773,9 @@ func TestACLEndpoint_DeleteTokens(t *testing.T) {
func TestACLEndpoint_Bootstrap(t *testing.T) {
t.Parallel()
s1 := testServer(t, nil)
s1 := testServer(t, func(c *Config) {
c.ACLEnabled = true
})
defer s1.Shutdown()
codec := rpcClient(t, s1)
testutil.WaitForLeader(t, s1.RPC)
@@ -806,7 +808,7 @@ func TestACLEndpoint_Bootstrap(t *testing.T) {
func TestACLEndpoint_UpsertTokens(t *testing.T) {
t.Parallel()
s1 := testServer(t, nil)
s1, _ := testACLServer(t, nil)
defer s1.Shutdown()
codec := rpcClient(t, s1)
testutil.WaitForLeader(t, s1.RPC)
@@ -859,7 +861,7 @@ func TestACLEndpoint_UpsertTokens(t *testing.T) {
func TestACLEndpoint_UpsertTokens_Invalid(t *testing.T) {
t.Parallel()
s1 := testServer(t, nil)
s1, _ := testACLServer(t, nil)
defer s1.Shutdown()
codec := rpcClient(t, s1)
testutil.WaitForLeader(t, s1.RPC)
@@ -883,7 +885,7 @@ func TestACLEndpoint_UpsertTokens_Invalid(t *testing.T) {
func TestACLEndpoint_ResolveToken(t *testing.T) {
t.Parallel()
s1 := testServer(t, nil)
s1, _ := testACLServer(t, nil)
defer s1.Shutdown()
codec := rpcClient(t, s1)
testutil.WaitForLeader(t, s1.RPC)

View File

@@ -371,3 +371,16 @@ func ACLToken() *structs.ACLToken {
ModifyIndex: 20,
}
}
func ACLManagementToken() *structs.ACLToken {
return &structs.ACLToken{
AccessorID: structs.GenerateUUID(),
SecretID: structs.GenerateUUID(),
Name: "management " + structs.GenerateUUID(),
Type: "management",
Global: true,
CreateTime: time.Now().UTC(),
CreateIndex: 10,
ModifyIndex: 20,
}
}

View File

@@ -13,6 +13,7 @@ import (
"time"
"github.com/hashicorp/nomad/command/agent/consul"
"github.com/hashicorp/nomad/nomad/mock"
"github.com/hashicorp/nomad/nomad/structs"
"github.com/hashicorp/nomad/nomad/structs/config"
"github.com/hashicorp/nomad/testutil"
@@ -38,6 +39,21 @@ func tmpDir(t *testing.T) string {
return dir
}
func testACLServer(t *testing.T, cb func(*Config)) (*Server, *structs.ACLToken) {
server := testServer(t, func(c *Config) {
c.ACLEnabled = true
if cb != nil {
cb(c)
}
})
token := mock.ACLManagementToken()
err := server.State().BootstrapACLTokens(1, token)
if err != nil {
t.Fatalf("failed to bootstrap ACL token: %v", err)
}
return server, token
}
func testServer(t *testing.T, cb func(*Config)) *Server {
// Setup the default settings
config := DefaultConfig()