open-nomad/nomad/state/state_store_acl_binding_rul...

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)
}