You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
boundary/internal/server/worker_list_test.go

339 lines
8.7 KiB

// Copyright (c) HashiCorp, Inc.
// SPDX-License-Identifier: BUSL-1.1
package server
import (
"math/rand/v2"
"strconv"
"testing"
"github.com/hashicorp/boundary/sdk/pbs/plugin"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
)
func TestWorkerList_FilterWorkersByLocalStorageState(t *testing.T) {
t.Run("list of workers with unhealthy workers", func(t *testing.T) {
workers := WorkerList([]*Worker{
NewWorker("global",
WithName("worker1"),
WithReleaseVersion("Boundary v0.16.0"),
WithLocalStorageState(AvailableLocalStorageState.String())),
NewWorker("global",
WithName("worker2"),
WithReleaseVersion("Boundary v0.16.0"),
WithLocalStorageState(LowStorageLocalStorageState.String())),
NewWorker("global",
WithName("worker3"),
WithReleaseVersion("Boundary v0.16.0"),
WithLocalStorageState(CriticallyLowStorageLocalStorageState.String())),
})
healthyWorkers := FilterWorkersByLocalStorageState(workers)
assert.Equal(t, 1, len(healthyWorkers))
})
t.Run("list of workers with some unsupported worker versions", func(t *testing.T) {
workers := WorkerList([]*Worker{
NewWorker("global",
WithName("worker1"),
WithReleaseVersion("Boundary v0.16.0"),
WithLocalStorageState(AvailableLocalStorageState.String())),
NewWorker("global",
WithName("worker2"),
WithReleaseVersion("Boundary v0.16.0"),
WithLocalStorageState(UnknownLocalStorageState.String())),
NewWorker("global",
WithName("worker3"),
WithReleaseVersion("Boundary v0.16.0"),
WithLocalStorageState(CriticallyLowStorageLocalStorageState.String())),
NewWorker("global",
WithName("worker4"),
WithReleaseVersion("Boundary v0.15.0"),
WithLocalStorageState(CriticallyLowStorageLocalStorageState.String())),
})
healthyWorkers := FilterWorkersByLocalStorageState(workers)
assert.Equal(t, 2, len(healthyWorkers))
})
t.Run("list of workers with no healthy workers", func(t *testing.T) {
workers := WorkerList([]*Worker{
NewWorker("global",
WithName("worker1"),
WithReleaseVersion("Boundary v0.16.0"),
WithLocalStorageState(LowStorageLocalStorageState.String())),
NewWorker("global",
WithName("worker2"),
WithReleaseVersion("Boundary v0.16.0"),
WithLocalStorageState(OutOfStorageLocalStorageState.String())),
NewWorker("global",
WithName("worker3"),
WithReleaseVersion("Boundary v0.16.0"),
WithLocalStorageState(CriticallyLowStorageLocalStorageState.String())),
})
healthyWorkers := FilterWorkersByLocalStorageState(workers)
assert.Equal(t, 0, len(healthyWorkers))
})
t.Run("list of workers with all unknown local storage state", func(t *testing.T) {
workers := WorkerList([]*Worker{
NewWorker("global",
WithName("worker1"),
WithReleaseVersion("Boundary v0.16.0"),
WithLocalStorageState(UnknownLocalStorageState.String())),
NewWorker("global",
WithName("worker2"),
WithReleaseVersion("Boundary v0.16.0"),
WithLocalStorageState(UnknownLocalStorageState.String())),
NewWorker("global",
WithName("worker3"),
WithReleaseVersion("Boundary v0.16.0"),
WithLocalStorageState(UnknownLocalStorageState.String())),
})
healthyWorkers := FilterWorkersByLocalStorageState(workers)
assert.Equal(t, 3, len(healthyWorkers))
})
}
func Test_FilterStorageBucketCredentialByWriteAccess(t *testing.T) {
t.Parallel()
cases := []struct {
name string
sbcState *plugin.StorageBucketCredentialState
expectedFilter bool
}{
{
name: "nil sbcState",
expectedFilter: true,
},
{
name: "nil underlying state",
sbcState: &plugin.StorageBucketCredentialState{},
expectedFilter: true,
},
{
name: "nil write permission",
sbcState: &plugin.StorageBucketCredentialState{
State: &plugin.Permissions{},
},
expectedFilter: true,
},
{
name: "ok state",
sbcState: &plugin.StorageBucketCredentialState{
State: &plugin.Permissions{
Write: &plugin.Permission{
State: plugin.StateType_STATE_TYPE_OK,
},
},
},
expectedFilter: true,
},
{
name: "unknown state",
sbcState: &plugin.StorageBucketCredentialState{
State: &plugin.Permissions{
Write: &plugin.Permission{
State: plugin.StateType_STATE_TYPE_UNKNOWN,
},
},
},
expectedFilter: true,
},
{
name: "error state",
sbcState: &plugin.StorageBucketCredentialState{
State: &plugin.Permissions{
Write: &plugin.Permission{
State: plugin.StateType_STATE_TYPE_ERROR,
},
},
},
expectedFilter: false,
},
}
for _, tc := range cases {
t.Run(tc.name, func(t *testing.T) {
assert := assert.New(t)
assert.Equal(tc.expectedFilter, FilterStorageBucketCredentialByWriteAccess(tc.sbcState))
})
}
}
func Test_FilterStorageBucketCredentialByReadAccess(t *testing.T) {
t.Parallel()
cases := []struct {
name string
sbcState *plugin.StorageBucketCredentialState
expectedFilter bool
}{
{
name: "nil sbcState",
expectedFilter: true,
},
{
name: "nil underlying state",
sbcState: &plugin.StorageBucketCredentialState{},
expectedFilter: true,
},
{
name: "nil read permission",
sbcState: &plugin.StorageBucketCredentialState{
State: &plugin.Permissions{},
},
expectedFilter: true,
},
{
name: "ok state",
sbcState: &plugin.StorageBucketCredentialState{
State: &plugin.Permissions{
Read: &plugin.Permission{
State: plugin.StateType_STATE_TYPE_OK,
},
},
},
expectedFilter: true,
},
{
name: "unknown state",
sbcState: &plugin.StorageBucketCredentialState{
State: &plugin.Permissions{
Read: &plugin.Permission{
State: plugin.StateType_STATE_TYPE_UNKNOWN,
},
},
},
expectedFilter: true,
},
{
name: "error state",
sbcState: &plugin.StorageBucketCredentialState{
State: &plugin.Permissions{
Read: &plugin.Permission{
State: plugin.StateType_STATE_TYPE_ERROR,
},
},
},
expectedFilter: false,
},
}
for _, tc := range cases {
t.Run(tc.name, func(t *testing.T) {
assert := assert.New(t)
assert.Equal(tc.expectedFilter, FilterStorageBucketCredentialByReadAccess(tc.sbcState))
})
}
}
func Test_FilterStorageBucketCredentialByDeleteAccess(t *testing.T) {
t.Parallel()
cases := []struct {
name string
sbcState *plugin.StorageBucketCredentialState
expectedFilter bool
}{
{
name: "nil sbcState",
expectedFilter: true,
},
{
name: "nil underlying state",
sbcState: &plugin.StorageBucketCredentialState{},
expectedFilter: true,
},
{
name: "nil delete permission",
sbcState: &plugin.StorageBucketCredentialState{
State: &plugin.Permissions{},
},
expectedFilter: true,
},
{
name: "ok state",
sbcState: &plugin.StorageBucketCredentialState{
State: &plugin.Permissions{
Delete: &plugin.Permission{
State: plugin.StateType_STATE_TYPE_OK,
},
},
},
expectedFilter: true,
},
{
name: "unknown state",
sbcState: &plugin.StorageBucketCredentialState{
State: &plugin.Permissions{
Delete: &plugin.Permission{
State: plugin.StateType_STATE_TYPE_UNKNOWN,
},
},
},
expectedFilter: true,
},
{
name: "error state",
sbcState: &plugin.StorageBucketCredentialState{
State: &plugin.Permissions{
Delete: &plugin.Permission{
State: plugin.StateType_STATE_TYPE_ERROR,
},
},
},
expectedFilter: false,
},
}
for _, tc := range cases {
t.Run(tc.name, func(t *testing.T) {
assert := assert.New(t)
assert.Equal(tc.expectedFilter, FilterStorageBucketCredentialByDeleteAccess(tc.sbcState))
})
}
}
func TestShuffle(t *testing.T) {
t.Parallel()
t.Run("noElements", func(t *testing.T) {
in := WorkerList{}
out, err := in.Shuffle()
require.NoError(t, err)
require.Equal(t, in, out)
})
t.Run("oneElement", func(t *testing.T) {
in := WorkerList{NewWorker("test_scope", WithName("1"))}
out, err := in.Shuffle()
require.NoError(t, err)
require.Equal(t, in, out)
})
t.Run("multipleElements", func(t *testing.T) {
// We need a large amount of minimum workers here to statistically
// mitigate against the case where Shuffle just-so-happens to shuffle
// the elements into the same order they were in before.
n := rand.IntN(1000-99) + 100 // [100, 1000]
inOrder := make([]int, 0, n)
in := make(WorkerList, 0, n)
for i := 0; i < n; i++ {
inOrder = append(inOrder, i)
in = append(in, NewWorker("test", WithName(strconv.Itoa(i))))
}
out, err := in.Shuffle()
require.NoError(t, err)
require.ElementsMatch(t, in, out)
outOrder := make([]int, 0)
for _, w := range out {
i, _ := strconv.Atoi(w.Name)
outOrder = append(outOrder, i)
}
require.NotEqual(t, inOrder, outOrder)
})
}