260 lines
9.1 KiB
Go
260 lines
9.1 KiB
Go
// Copyright (c) HashiCorp, Inc.
|
|
// SPDX-License-Identifier: MPL-2.0
|
|
|
|
package state
|
|
|
|
import (
|
|
"testing"
|
|
|
|
"github.com/hashicorp/go-memdb"
|
|
"github.com/hashicorp/nomad/ci"
|
|
"github.com/hashicorp/nomad/helper/uuid"
|
|
"github.com/hashicorp/nomad/nomad/mock"
|
|
"github.com/hashicorp/nomad/nomad/structs"
|
|
"github.com/shoenig/test/must"
|
|
)
|
|
|
|
func TestStateStore_UpsertACLBindingRules(t *testing.T) {
|
|
ci.Parallel(t)
|
|
testState := testStateStore(t)
|
|
|
|
// Generate a mocked ACL binding rule for testing and attempt to upsert
|
|
// this straight into state. It should fail because the auth method does
|
|
// not exist.
|
|
mockedACLBindingRules := []*structs.ACLBindingRule{mock.ACLBindingRule()}
|
|
err := testState.UpsertACLBindingRules(10, mockedACLBindingRules, false)
|
|
must.EqError(t, err, "ACL binding rule insert failed: ACL auth method not found")
|
|
|
|
// Create an auth method and ensure the binding rule is updated, so it is
|
|
// related to it.
|
|
authMethod := mock.ACLOIDCAuthMethod()
|
|
mockedACLBindingRules[0].AuthMethod = authMethod.Name
|
|
|
|
must.NoError(t, testState.UpsertACLAuthMethods(10, []*structs.ACLAuthMethod{authMethod}))
|
|
must.NoError(t, testState.UpsertACLBindingRules(20, mockedACLBindingRules, false))
|
|
|
|
// Check that the index for the table was modified as expected.
|
|
initialIndex, err := testState.Index(TableACLBindingRules)
|
|
must.NoError(t, err)
|
|
must.Eq(t, 20, initialIndex)
|
|
|
|
// List all the ACL binding rules in the table, so we can perform a number
|
|
// of tests on the return array.
|
|
ws := memdb.NewWatchSet()
|
|
iter, err := testState.GetACLBindingRules(ws)
|
|
must.NoError(t, err)
|
|
|
|
// Count how many table entries we have, to ensure it is the expected
|
|
// number.
|
|
var count int
|
|
|
|
for raw := iter.Next(); raw != nil; raw = iter.Next() {
|
|
count++
|
|
|
|
// Ensure the create and modify indexes are populated correctly.
|
|
aclRole := raw.(*structs.ACLBindingRule)
|
|
must.Eq(t, 20, aclRole.CreateIndex)
|
|
must.Eq(t, 20, aclRole.ModifyIndex)
|
|
}
|
|
must.Eq(t, 1, count)
|
|
|
|
// Try writing the same ACL binding rule to state which should not result
|
|
// in an update to the table index.
|
|
must.NoError(t, testState.UpsertACLBindingRules(20, mockedACLBindingRules, false))
|
|
reInsertActualIndex, err := testState.Index(TableACLBindingRules)
|
|
must.NoError(t, err)
|
|
must.Eq(t, 20, reInsertActualIndex)
|
|
|
|
// Make a change to the binding rule and ensure this update is accepted and
|
|
// the table index is updated.
|
|
updatedMockedACLBindingRule := mockedACLBindingRules[0].Copy()
|
|
updatedMockedACLBindingRule.BindType = "role-name"
|
|
updatedMockedACLBindingRule.SetHash()
|
|
must.NoError(t, testState.UpsertACLBindingRules(
|
|
30, []*structs.ACLBindingRule{updatedMockedACLBindingRule}, false))
|
|
|
|
// Check that the index for the table was modified as expected.
|
|
updatedIndex, err := testState.Index(TableACLBindingRules)
|
|
must.NoError(t, err)
|
|
must.Eq(t, 30, updatedIndex)
|
|
|
|
// List the ACL roles in state.
|
|
iter, err = testState.GetACLBindingRules(ws)
|
|
must.NoError(t, err)
|
|
|
|
// Count how many table entries we have, to ensure it is the expected
|
|
// number.
|
|
count = 0
|
|
|
|
for raw := iter.Next(); raw != nil; raw = iter.Next() {
|
|
count++
|
|
|
|
// Ensure the create and modify indexes are populated correctly.
|
|
aclRole := raw.(*structs.ACLBindingRule)
|
|
must.Eq(t, 20, aclRole.CreateIndex)
|
|
must.Eq(t, 30, aclRole.ModifyIndex)
|
|
}
|
|
must.Eq(t, 1, count)
|
|
|
|
// Now try inserting an ACL binding rule using the missing auth methods
|
|
// argument to simulate replication.
|
|
replicatedACLBindingRule := []*structs.ACLBindingRule{mock.ACLBindingRule()}
|
|
must.NoError(t, testState.UpsertACLBindingRules(40, replicatedACLBindingRule, true))
|
|
|
|
replicatedACLBindingRuleResp, err := testState.GetACLBindingRule(ws, replicatedACLBindingRule[0].ID)
|
|
must.NoError(t, err)
|
|
must.Eq(t, replicatedACLBindingRule[0].Hash, replicatedACLBindingRuleResp.Hash)
|
|
}
|
|
|
|
func TestStateStore_DeleteACLBindingRules(t *testing.T) {
|
|
ci.Parallel(t)
|
|
testState := testStateStore(t)
|
|
|
|
// Generate a some mocked ACL binding rules for testing and upsert these
|
|
// straight into state.
|
|
mockedACLBindingRoles := []*structs.ACLBindingRule{mock.ACLBindingRule(), mock.ACLBindingRule()}
|
|
must.NoError(t, testState.UpsertACLBindingRules(10, mockedACLBindingRoles, true))
|
|
|
|
// Try and delete a binding rule using an ID that doesn't exist. This
|
|
// should return an error and not change the index for the table.
|
|
err := testState.DeleteACLBindingRules(20, []string{uuid.Generate()})
|
|
must.EqError(t, err, "ACL binding rule not found")
|
|
|
|
tableIndex, err := testState.Index(TableACLBindingRules)
|
|
must.NoError(t, err)
|
|
must.Eq(t, 10, tableIndex)
|
|
|
|
// Delete one of the previously upserted ACL binding rules. This should
|
|
// succeed and modify the table index.
|
|
must.NoError(t, testState.DeleteACLBindingRules(20, []string{mockedACLBindingRoles[0].ID}))
|
|
|
|
tableIndex, err = testState.Index(TableACLBindingRules)
|
|
must.NoError(t, err)
|
|
must.Eq(t, 20, tableIndex)
|
|
|
|
// List the ACL binding rules and ensure we now only have one present and
|
|
// that it is the one we expect.
|
|
ws := memdb.NewWatchSet()
|
|
iter, err := testState.GetACLBindingRules(ws)
|
|
must.NoError(t, err)
|
|
|
|
var aclBindingRules []*structs.ACLBindingRule
|
|
|
|
for raw := iter.Next(); raw != nil; raw = iter.Next() {
|
|
aclBindingRules = append(aclBindingRules, raw.(*structs.ACLBindingRule))
|
|
}
|
|
|
|
must.Len(t, 1, aclBindingRules)
|
|
must.True(t, aclBindingRules[0].Equal(mockedACLBindingRoles[1]))
|
|
|
|
// Delete the final remaining ACL binding rule. This should succeed and
|
|
// modify the table index.
|
|
must.NoError(t, testState.DeleteACLBindingRules(30, []string{mockedACLBindingRoles[1].ID}))
|
|
|
|
tableIndex, err = testState.Index(TableACLBindingRules)
|
|
must.NoError(t, err)
|
|
must.Eq(t, 30, tableIndex)
|
|
|
|
// List the ACL binding rules and ensure we have zero entries.
|
|
iter, err = testState.GetACLBindingRules(ws)
|
|
must.NoError(t, err)
|
|
|
|
aclBindingRules = make([]*structs.ACLBindingRule, 0)
|
|
|
|
for raw := iter.Next(); raw != nil; raw = iter.Next() {
|
|
aclBindingRules = append(aclBindingRules, raw.(*structs.ACLBindingRule))
|
|
}
|
|
must.Len(t, 0, aclBindingRules)
|
|
}
|
|
|
|
func TestStateStore_GetACLBindingRules(t *testing.T) {
|
|
ci.Parallel(t)
|
|
testState := testStateStore(t)
|
|
|
|
// Generate a some mocked ACL binding rules for testing and upsert these
|
|
// straight into state.
|
|
mockedACLBindingRoles := []*structs.ACLBindingRule{mock.ACLBindingRule(), mock.ACLBindingRule()}
|
|
must.NoError(t, testState.UpsertACLBindingRules(10, mockedACLBindingRoles, true))
|
|
|
|
// List the ACL binding rules and ensure they are exactly as we expect.
|
|
ws := memdb.NewWatchSet()
|
|
iter, err := testState.GetACLBindingRules(ws)
|
|
must.NoError(t, err)
|
|
|
|
var aclBindingRules []*structs.ACLBindingRule
|
|
|
|
for raw := iter.Next(); raw != nil; raw = iter.Next() {
|
|
aclBindingRules = append(aclBindingRules, raw.(*structs.ACLBindingRule))
|
|
}
|
|
|
|
expected := mockedACLBindingRoles
|
|
for i := range expected {
|
|
expected[i].CreateIndex = 10
|
|
expected[i].ModifyIndex = 10
|
|
}
|
|
|
|
must.SliceContainsAll(t, aclBindingRules, expected)
|
|
}
|
|
|
|
func TestStateStore_GetACLBindingRule(t *testing.T) {
|
|
ci.Parallel(t)
|
|
testState := testStateStore(t)
|
|
|
|
// Generate a some mocked ACL binding rules for testing and upsert these
|
|
// straight into state.
|
|
mockedACLBindingRoles := []*structs.ACLBindingRule{mock.ACLBindingRule(), mock.ACLBindingRule()}
|
|
must.NoError(t, testState.UpsertACLBindingRules(10, mockedACLBindingRoles, true))
|
|
|
|
ws := memdb.NewWatchSet()
|
|
|
|
// Try reading an ACL binding rule that does not exist.
|
|
aclBindingRule, err := testState.GetACLBindingRule(ws, uuid.Generate())
|
|
must.NoError(t, err)
|
|
must.Nil(t, aclBindingRule)
|
|
|
|
// Read the two ACL binding rules that we should find.
|
|
aclBindingRule, err = testState.GetACLBindingRule(ws, mockedACLBindingRoles[0].ID)
|
|
must.NoError(t, err)
|
|
must.Eq(t, mockedACLBindingRoles[0], aclBindingRule)
|
|
|
|
aclBindingRule, err = testState.GetACLBindingRule(ws, mockedACLBindingRoles[1].ID)
|
|
must.NoError(t, err)
|
|
must.Eq(t, mockedACLBindingRoles[1], aclBindingRule)
|
|
}
|
|
|
|
func TestStateStore_GetACLBindingRulesByAuthMethod(t *testing.T) {
|
|
ci.Parallel(t)
|
|
testState := testStateStore(t)
|
|
|
|
// Generate a some mocked ACL binding rules for testing and upsert these
|
|
// straight into state.
|
|
mockedACLBindingRoles := []*structs.ACLBindingRule{mock.ACLBindingRule(), mock.ACLBindingRule()}
|
|
must.NoError(t, testState.UpsertACLBindingRules(10, mockedACLBindingRoles, true))
|
|
|
|
ws := memdb.NewWatchSet()
|
|
|
|
// Lookup ACL binding rules using an auth method that is not referenced. We
|
|
// should not get any results within the iterator.
|
|
iter, err := testState.GetACLBindingRulesByAuthMethod(ws, "not-an-auth-method")
|
|
must.NoError(t, err)
|
|
|
|
var aclBindingRules []*structs.ACLBindingRule
|
|
|
|
for raw := iter.Next(); raw != nil; raw = iter.Next() {
|
|
aclBindingRules = append(aclBindingRules, raw.(*structs.ACLBindingRule))
|
|
}
|
|
must.Len(t, 0, aclBindingRules)
|
|
|
|
// Lookup ACL binding rules using an auth method that is referenced by both
|
|
// mocked rules. Ensure the results are as expected.
|
|
iter, err = testState.GetACLBindingRulesByAuthMethod(ws, mockedACLBindingRoles[0].AuthMethod)
|
|
must.NoError(t, err)
|
|
|
|
aclBindingRules = make([]*structs.ACLBindingRule, 0)
|
|
|
|
for raw := iter.Next(); raw != nil; raw = iter.Next() {
|
|
aclBindingRules = append(aclBindingRules, raw.(*structs.ACLBindingRule))
|
|
}
|
|
must.Len(t, 2, aclBindingRules)
|
|
}
|