feat(api): return remote storage states for worker

This commit is contained in:
Damian Debkowski authored and Elim Tsiagbey committed 2024-07-17 14:23:11 -04:00
1 parent 11272b6c2a
commit c0cf5c4e1b
13 files changed
+787 -108

No files matched your search

@@ -27,6 +27,7 @@ import (
"github.com/hashicorp/boundary/internal/types/scope"
"github.com/hashicorp/boundary/internal/util"
pb "github.com/hashicorp/boundary/sdk/pbs/controller/api/resources/workers"
"github.com/hashicorp/boundary/sdk/pbs/plugin"
"github.com/hashicorp/go-secure-stdlib/strutil"
"github.com/hashicorp/nodeenrollment/types"
"github.com/mr-tron/base58"
@@ -924,10 +925,67 @@ func (s Service) toProto(ctx context.Context, in *server.Worker, opt ...handlers
return nil, errors.Wrap(ctx, err, op, errors.WithMsg("error preparing canonical tags proto"))
}
}
if outputFields.Has(globals.RemoteStorageStateField) && len(in.RemoteStorageStates) > 0 {
var err error
out.RemoteStorageState, err = remoteStorageStatesToMapProto(in.RemoteStorageStates)
if err != nil {
return nil, errors.Wrap(ctx, err, op, errors.WithMsg("error preparing remote storage state proto"))
}
}
return &out, nil
}
func remoteStorageStatesToMapProto(in map[string]*plugin.StorageBucketCredentialState) (map[string]*pb.RemoteStorageState, error) {
ret := make(map[string]*pb.RemoteStorageState)
for storageBucketId, sbcState := range in {
status := server.RemoteStorageStateAvailable
writeState := server.PermissionStateUnknown.String()
readState := server.PermissionStateUnknown.String()
deleteState := server.PermissionStateUnknown.String()
if sbcState.GetState() != nil {
if sbcState.GetState().GetWrite() != nil {
writePermissionState, err := server.ParsePermissionState(sbcState.GetState().GetWrite().GetState())
if err != nil {
return nil, err
}
writeState = writePermissionState
if writeState == server.PermissionStateError.String() {
status = server.RemoteStorageStateError
}
}
if sbcState.GetState().GetRead() != nil {
readPermissionState, err := server.ParsePermissionState(sbcState.GetState().GetRead().GetState())
if err != nil {
return nil, err
}
readState = readPermissionState
if readState == server.PermissionStateError.String() {
status = server.RemoteStorageStateError
}
}
if sbcState.GetState().GetDelete() != nil {
deletePermissionState, err := server.ParsePermissionState(sbcState.GetState().GetDelete().GetState())
if err != nil {
return nil, err
}
deleteState = deletePermissionState
if deleteState == server.PermissionStateError.String() {
status = server.RemoteStorageStateError
}
}
}
ret[storageBucketId] = &pb.RemoteStorageState{
Status: status.String(),
Permissions: &pb.RemoteStoragePermissions{
Write: writeState,
Read: readState,
Delete: deleteState,
},
}
}
return ret, nil
}
func tagsToMapProto(in map[string][]string) (map[string]*structpb.ListValue, error) {
b := make(map[string][]any)
for k, v := range in {
@@ -9,6 +9,7 @@ import (
"sort"
"strings"
"testing"
"time"
"github.com/google/go-cmp/cmp"
"github.com/google/go-cmp/cmp/cmpopts"
@@ -26,6 +27,7 @@ import (
"github.com/hashicorp/boundary/internal/types/scope"
"github.com/hashicorp/boundary/sdk/pbs/controller/api/resources/scopes"
pb "github.com/hashicorp/boundary/sdk/pbs/controller/api/resources/workers"
"github.com/hashicorp/boundary/sdk/pbs/plugin"
"github.com/hashicorp/go-secure-stdlib/strutil"
"github.com/hashicorp/nodeenrollment"
"github.com/hashicorp/nodeenrollment/rotation"
@@ -1597,6 +1599,7 @@ func TestCreateWorkerLed(t *testing.T) {
tc.res.Item.Scope = got.GetItem().Scope
tc.res.Item.AuthorizedActions = got.GetItem().GetAuthorizedActions()
tc.res.Item.LocalStorageState = got.GetItem().GetLocalStorageState()
tc.res.Item.DirectlyConnectedDownstreamWorkers = got.GetItem().GetDirectlyConnectedDownstreamWorkers()
}
}
assert.Equal(tc.res, got)
@@ -1945,6 +1948,7 @@ func TestCreateControllerLed(t *testing.T) {
tc.res.Item.AuthorizedActions = got.GetItem().GetAuthorizedActions()
tc.res.Item.ControllerGeneratedActivationToken = got.GetItem().GetControllerGeneratedActivationToken()
tc.res.Item.LocalStorageState = got.GetItem().GetLocalStorageState()
tc.res.Item.DirectlyConnectedDownstreamWorkers = got.GetItem().GetDirectlyConnectedDownstreamWorkers()
}
}
assert.Equal(tc.res, got)
@@ -2619,3 +2623,211 @@ func TestReinitializeCertificateAuthority(t *testing.T) {
})
}
}
func Test_RemoteStorageStatesToMapProto(t *testing.T) {
t.Parallel()
testTime := timestamppb.New(time.Now().UTC().Round(time.Second))
tests := []struct {
name string
input map[string]*plugin.StorageBucketCredentialState
expectedErrMsg string
expectedOutput map[string]*pb.RemoteStorageState
}{
{
name: "nil",
expectedOutput: map[string]*pb.RemoteStorageState{},
},
{
name: "empty",
expectedOutput: map[string]*pb.RemoteStorageState{},
},
{
name: "available",
input: map[string]*plugin.StorageBucketCredentialState{
"sb_1234567890": {
State: &plugin.Permissions{
Write: &plugin.Permission{
State: plugin.StateType_STATE_TYPE_OK,
CheckedAt: testTime,
},
Read: &plugin.Permission{
State: plugin.StateType_STATE_TYPE_OK,
CheckedAt: testTime,
},
Delete: &plugin.Permission{
State: plugin.StateType_STATE_TYPE_OK,
CheckedAt: testTime,
},
},
Version: 1,
},
},
expectedOutput: map[string]*pb.RemoteStorageState{
"sb_1234567890": {
Status: "available",
Permissions: &pb.RemoteStoragePermissions{
Write: "ok",
Read: "ok",
Delete: "ok",
},
},
},
},
{
name: "write error",
input: map[string]*plugin.StorageBucketCredentialState{
"sb_1234567890": {
State: &plugin.Permissions{
Write: &plugin.Permission{
State: plugin.StateType_STATE_TYPE_ERROR,
ErrorDetails: "invalid credentials",
CheckedAt: testTime,
},
Read: &plugin.Permission{
State: plugin.StateType_STATE_TYPE_OK,
CheckedAt: testTime,
},
Delete: &plugin.Permission{
State: plugin.StateType_STATE_TYPE_OK,
CheckedAt: testTime,
},
},
Version: 1,
},
},
expectedOutput: map[string]*pb.RemoteStorageState{
"sb_1234567890": {
Status: "error",
Permissions: &pb.RemoteStoragePermissions{
Write: "error",
Read: "ok",
Delete: "ok",
},
},
},
},
{
name: "read error",
input: map[string]*plugin.StorageBucketCredentialState{
"sb_1234567890": {
State: &plugin.Permissions{
Write: &plugin.Permission{
State: plugin.StateType_STATE_TYPE_OK,
CheckedAt: testTime,
},
Read: &plugin.Permission{
State: plugin.StateType_STATE_TYPE_ERROR,
ErrorDetails: "invalid credentials",
CheckedAt: testTime,
},
Delete: &plugin.Permission{
State: plugin.StateType_STATE_TYPE_OK,
CheckedAt: testTime,
},
},
Version: 1,
},
},
expectedOutput: map[string]*pb.RemoteStorageState{
"sb_1234567890": {
Status: "error",
Permissions: &pb.RemoteStoragePermissions{
Write: "ok",
Read: "error",
Delete: "ok",
},
},
},
},
{
name: "delete error",
input: map[string]*plugin.StorageBucketCredentialState{
"sb_1234567890": {
State: &plugin.Permissions{
Write: &plugin.Permission{
State: plugin.StateType_STATE_TYPE_OK,
CheckedAt: testTime,
},
Read: &plugin.Permission{
State: plugin.StateType_STATE_TYPE_OK,
CheckedAt: testTime,
},
Delete: &plugin.Permission{
State: plugin.StateType_STATE_TYPE_ERROR,
ErrorDetails: "invalid credentials",
CheckedAt: testTime,
},
},
Version: 1,
},
},
expectedOutput: map[string]*pb.RemoteStorageState{
"sb_1234567890": {
Status: "error",
Permissions: &pb.RemoteStoragePermissions{
Write: "ok",
Read: "ok",
Delete: "error",
},
},
},
},
{
name: "error",
input: map[string]*plugin.StorageBucketCredentialState{
"sb_1234567890": {
State: &plugin.Permissions{
Write: &plugin.Permission{
State: plugin.StateType_STATE_TYPE_ERROR,
ErrorDetails: "invalid credentials",
CheckedAt: testTime,
},
Read: &plugin.Permission{
State: plugin.StateType_STATE_TYPE_ERROR,
ErrorDetails: "invalid credentials",
CheckedAt: testTime,
},
Delete: &plugin.Permission{
State: plugin.StateType_STATE_TYPE_ERROR,
ErrorDetails: "invalid credentials",
CheckedAt: testTime,
},
},
Version: 1,
},
},
expectedOutput: map[string]*pb.RemoteStorageState{
"sb_1234567890": {
Status: "error",
Permissions: &pb.RemoteStoragePermissions{
Write: "error",
Read: "error",
Delete: "error",
},
},
},
},
}
for _, tc := range tests {
t.Run(tc.name, func(t *testing.T) {
require, assert := require.New(t), assert.New(t)
actualOutput, err := remoteStorageStatesToMapProto(tc.input)
if tc.expectedErrMsg != "" {
require.Error(err)
assert.ErrorContains(err, tc.expectedErrMsg)
assert.Nil(actualOutput)
return
}
require.NoError(err)
require.Len(actualOutput, len(tc.expectedOutput))
for storageBucketId, expectedState := range tc.expectedOutput {
actualState, ok := actualOutput[storageBucketId]
require.True(ok)
assert.Equal(expectedState.GetStatus(), actualState.GetStatus())
assert.Equal(expectedState.GetPermissions().GetWrite(), actualState.GetPermissions().GetWrite())
assert.Equal(expectedState.GetPermissions().GetRead(), actualState.GetPermissions().GetRead())
assert.Equal(expectedState.GetPermissions().GetDelete(), actualState.GetPermissions().GetDelete())
}
})
}
}