open-consul/agent/consul/acl_token_exp_test.go
Daniel Nephin 047abdd73c acl: remove ACLDatacenter
This field has been unnecessary for a while now. It was always set to the same value
as PrimaryDatacenter. So we can remove the duplicate field and use PrimaryDatacenter
directly.

This change was made by GoLand refactor, which did most of the work for me.
2021-08-06 18:27:00 -04:00

225 lines
5.5 KiB
Go

package consul
import (
"os"
"testing"
"time"
"github.com/stretchr/testify/require"
"github.com/hashicorp/consul/agent/structs"
"github.com/hashicorp/consul/testrpc"
)
func TestACLTokenReap_Primary(t *testing.T) {
if testing.Short() {
t.Skip("too slow for testing.Short")
}
t.Parallel()
t.Run("global", func(t *testing.T) {
t.Parallel()
testACLTokenReap_Primary(t, false, true)
})
t.Run("local", func(t *testing.T) {
t.Parallel()
testACLTokenReap_Primary(t, true, false)
})
}
func testACLTokenReap_Primary(t *testing.T, local, global bool) {
// -------------------------------------------
// A word of caution when testing reapExpiredACLTokens():
//
// The underlying memdb index used for reaping has a minimum granularity of
// 1 second as it delegates to `time.Unix()`. This test will have to be
// deliberately slow to allow for necessary sleeps. If you try to make it
// operate faster (using expiration ttls of milliseconds) it will be flaky.
// -------------------------------------------
t.Helper()
require.NotEqual(t, local, global)
dir1, s1 := testServerWithConfig(t, func(c *Config) {
c.PrimaryDatacenter = "dc1"
c.ACLsEnabled = true
c.ACLMasterToken = "root"
c.ACLTokenMinExpirationTTL = 10 * time.Millisecond
c.ACLTokenMaxExpirationTTL = 8 * time.Second
})
defer os.RemoveAll(dir1)
defer s1.Shutdown()
testrpc.WaitForLeader(t, s1.RPC, "dc1")
codec := rpcClient(t, s1)
defer codec.Close()
acl := ACL{srv: s1}
masterTokenAccessorID, err := retrieveTestTokenAccessorForSecret(codec, "root", "dc1", "root")
require.NoError(t, err)
listTokens := func() (localTokens, globalTokens []string, err error) {
req := structs.ACLTokenListRequest{
Datacenter: "dc1",
QueryOptions: structs.QueryOptions{Token: "root"},
}
var res structs.ACLTokenListResponse
err = acl.TokenList(&req, &res)
if err != nil {
return nil, nil, err
}
for _, tok := range res.Tokens {
if tok.Local {
localTokens = append(localTokens, tok.AccessorID)
} else {
globalTokens = append(globalTokens, tok.AccessorID)
}
}
return localTokens, globalTokens, nil
}
requireTokenMatch := func(t *testing.T, expect []string) {
t.Helper()
var expectLocal, expectGlobal []string
// The master token and the anonymous token are always going to be
// present and global.
expectGlobal = append(expectGlobal, masterTokenAccessorID)
expectGlobal = append(expectGlobal, structs.ACLTokenAnonymousID)
if local {
expectLocal = append(expectLocal, expect...)
} else {
expectGlobal = append(expectGlobal, expect...)
}
localTokens, globalTokens, err := listTokens()
require.NoError(t, err)
require.ElementsMatch(t, expectLocal, localTokens)
require.ElementsMatch(t, expectGlobal, globalTokens)
}
// initial sanity check
requireTokenMatch(t, []string{})
t.Run("no tokens", func(t *testing.T) {
n, err := s1.reapExpiredACLTokens(local, global)
require.NoError(t, err)
require.Equal(t, 0, n)
requireTokenMatch(t, []string{})
})
// 2 normal
token1, err := upsertTestToken(codec, "root", "dc1", func(token *structs.ACLToken) {
token.Local = local
})
require.NoError(t, err)
token2, err := upsertTestToken(codec, "root", "dc1", func(token *structs.ACLToken) {
token.Local = local
})
require.NoError(t, err)
requireTokenMatch(t, []string{
token1.AccessorID,
token2.AccessorID,
})
t.Run("only normal tokens", func(t *testing.T) {
n, err := s1.reapExpiredACLTokens(local, global)
require.NoError(t, err)
require.Equal(t, 0, n)
requireTokenMatch(t, []string{
token1.AccessorID,
token2.AccessorID,
})
})
// 2 expiring
token3, err := upsertTestToken(codec, "root", "dc1", func(token *structs.ACLToken) {
token.ExpirationTTL = 1 * time.Second
token.Local = local
})
require.NoError(t, err)
token4, err := upsertTestToken(codec, "root", "dc1", func(token *structs.ACLToken) {
token.ExpirationTTL = 5 * time.Second
token.Local = local
})
require.NoError(t, err)
// 2 more normal
token5, err := upsertTestToken(codec, "root", "dc1", func(token *structs.ACLToken) {
token.Local = local
})
require.NoError(t, err)
token6, err := upsertTestToken(codec, "root", "dc1", func(token *structs.ACLToken) {
token.Local = local
})
require.NoError(t, err)
requireTokenMatch(t, []string{
token1.AccessorID,
token2.AccessorID,
token3.AccessorID,
token4.AccessorID,
token5.AccessorID,
token6.AccessorID,
})
t.Run("mixed but nothing expired yet", func(t *testing.T) {
n, err := s1.reapExpiredACLTokens(local, global)
require.NoError(t, err)
require.Equal(t, 0, n)
requireTokenMatch(t, []string{
token1.AccessorID,
token2.AccessorID,
token3.AccessorID,
token4.AccessorID,
token5.AccessorID,
token6.AccessorID,
})
})
time.Sleep(token3.ExpirationTime.Sub(time.Now()) + 10*time.Millisecond)
t.Run("one should be reaped", func(t *testing.T) {
n, err := s1.reapExpiredACLTokens(local, global)
require.NoError(t, err)
require.Equal(t, 1, n)
requireTokenMatch(t, []string{
token1.AccessorID,
token2.AccessorID,
// token3.AccessorID,
token4.AccessorID,
token5.AccessorID,
token6.AccessorID,
})
})
time.Sleep(token4.ExpirationTime.Sub(time.Now()) + 10*time.Millisecond)
t.Run("two should be reaped", func(t *testing.T) {
n, err := s1.reapExpiredACLTokens(local, global)
require.NoError(t, err)
require.Equal(t, 1, n)
requireTokenMatch(t, []string{
token1.AccessorID,
token2.AccessorID,
// token3.AccessorID,
// token4.AccessorID,
token5.AccessorID,
token6.AccessorID,
})
})
}