* oss: feat(proto): statistics rpc added to server coordination service * oss: feat(session): add update statistics status service * oss: feat(handlers): add Statistics method to worker service server * oss: feat(worker): add statistics ticking * oss: internal/proto: define the new RoutingInfo RPC * oss: internal/server: repository changes for RoutingInfo RPC * oss: internal/daemon: add RoutingInfo handler to worker service The new RoutingInfo RPC is used to get information from the worker that is used to make routing decisions on the controller. It also handles the initial request from the worker. * oss: feat(db): server_worker_session_info table * oss: feat(proto): session info rpc for server coordination service * oss: feat(server): worker session info request data type * oss: feat(worker): session info ticker * oss: feat(handler): server coordination handler for session info * oss: feat(session): utilize session info request time for session cleanup * oss: proto: fix some gotags comments Some of these fields were missing comments, while others had incorrectly formatted comments. * oss: worker: send connection status to server This is required for the correct tracking of orphaned connections * oss: internal/daemon/worker: add function to wait for Statistics RPC * oss: internal/daemon/worker: add routing info RPC The new RoutingInfo RPC replaces part of the Status RPC, namely the part which is used by the controller and worker to perform decisions around the establishment of new sessions and new worker connections. * oss: internal/daemon/worker: remove unnecessary error check * oss: internal/daemon/worker: lock access to config and upstreams The new confLock protects access to the conf value and to the addressReceivers slice. * oss: worker: replace Status RPC with RoutingInfo * oss: worker: deprecate the Status RPC and related types The Status RPC and related messages have been replaced by the RoutingInfo/SessionInfo/Statistics RPCs. The controller will continue to support the Status RPC until two new minor versions have been released. * oss: internal/event: add default deny for new worker RPCs * oss: internal/daemon/worker: allow RPC interval to be set The new SessionInfo/RoutingInfo/Statistics RPCs run at long intervals, which make some of our cluster tests take a long time to complete. This allows their interval to be configured in tests. * oss: all: use TestWorkerRPCInterval in slow tests This should speed up these tests significantly. * oss: internal/tests/cluster: reset sys eventer in test --------- Co-authored-by: Damian Debkowski <damian.debkowski@hashicorp.com>
1760 lines
54 KiB
Go
1760 lines
54 KiB
Go
// Copyright (c) HashiCorp, Inc.
|
|
// SPDX-License-Identifier: BUSL-1.1
|
|
|
|
package server_test
|
|
|
|
import (
|
|
"context"
|
|
"crypto/rand"
|
|
"fmt"
|
|
"strings"
|
|
"testing"
|
|
"time"
|
|
|
|
"github.com/DATA-DOG/go-sqlmock"
|
|
"github.com/google/go-cmp/cmp"
|
|
"github.com/hashicorp/boundary/globals"
|
|
"github.com/hashicorp/boundary/internal/db"
|
|
"github.com/hashicorp/boundary/internal/db/timestamp"
|
|
"github.com/hashicorp/boundary/internal/errors"
|
|
"github.com/hashicorp/boundary/internal/iam"
|
|
"github.com/hashicorp/boundary/internal/kms"
|
|
"github.com/hashicorp/boundary/internal/server"
|
|
"github.com/hashicorp/boundary/internal/server/store"
|
|
"github.com/hashicorp/boundary/internal/session"
|
|
"github.com/hashicorp/boundary/internal/target"
|
|
"github.com/hashicorp/boundary/internal/target/targettest"
|
|
tgstore "github.com/hashicorp/boundary/internal/target/targettest/store"
|
|
"github.com/hashicorp/boundary/internal/types/scope"
|
|
"github.com/hashicorp/go-dbw"
|
|
"github.com/hashicorp/go-kms-wrapping/extras/kms/v2/migrations"
|
|
"github.com/hashicorp/nodeenrollment/rotation"
|
|
"github.com/hashicorp/nodeenrollment/storage/file"
|
|
"github.com/hashicorp/nodeenrollment/types"
|
|
"github.com/stretchr/testify/assert"
|
|
"github.com/stretchr/testify/require"
|
|
"google.golang.org/protobuf/testing/protocmp"
|
|
"google.golang.org/protobuf/types/known/timestamppb"
|
|
)
|
|
|
|
func TestDeleteWorker(t *testing.T) {
|
|
conn, _ := db.TestSetup(t, "postgres")
|
|
ctx := context.Background()
|
|
rw := db.New(conn)
|
|
wrapper := db.TestWrapper(t)
|
|
kms := kms.TestKms(t, conn, wrapper)
|
|
repo, err := server.NewRepository(ctx, rw, rw, kms)
|
|
require.NoError(t, err)
|
|
|
|
type args struct {
|
|
worker *server.Worker
|
|
}
|
|
tests := []struct {
|
|
name string
|
|
args args
|
|
wantRowsDeleted int
|
|
wantErr bool
|
|
wantErrMsg string
|
|
}{
|
|
{
|
|
name: "valid",
|
|
args: args{
|
|
worker: server.TestKmsWorker(t, conn, wrapper),
|
|
},
|
|
wantRowsDeleted: 1,
|
|
wantErr: false,
|
|
},
|
|
{
|
|
name: "no-public-id",
|
|
args: args{
|
|
worker: func() *server.Worker {
|
|
w := server.Worker{Worker: &store.Worker{}}
|
|
return &w
|
|
}(),
|
|
},
|
|
wantRowsDeleted: 0,
|
|
wantErr: true,
|
|
wantErrMsg: "server.(Repository).DeleteWorker: missing public id: parameter violation: error #100",
|
|
},
|
|
{
|
|
name: "not-found",
|
|
args: args{
|
|
worker: func() *server.Worker {
|
|
w := server.Worker{Worker: &store.Worker{}}
|
|
id, err := db.NewPublicId(ctx, "w")
|
|
require.NoError(t, err)
|
|
w.PublicId = id
|
|
return &w
|
|
}(),
|
|
},
|
|
wantRowsDeleted: 0,
|
|
wantErr: true,
|
|
wantErrMsg: "server.(Repository).DeleteWorker: delete failed for worker with workerId:",
|
|
},
|
|
}
|
|
for _, tt := range tests {
|
|
t.Run(tt.name, func(t *testing.T) {
|
|
assert := assert.New(t)
|
|
deletedRows, err := repo.DeleteWorker(ctx, tt.args.worker.Worker.PublicId)
|
|
if tt.wantErr {
|
|
assert.Error(err)
|
|
assert.Equal(0, deletedRows)
|
|
assert.Contains(err.Error(), tt.wantErrMsg)
|
|
|
|
return
|
|
}
|
|
assert.NoError(err)
|
|
assert.Equal(tt.wantRowsDeleted, deletedRows)
|
|
|
|
// Validate that the worker no longer exists
|
|
err = rw.LookupByPublicId(ctx, tt.args.worker)
|
|
assert.ErrorIs(err, dbw.ErrRecordNotFound)
|
|
})
|
|
}
|
|
}
|
|
|
|
func TestLookupWorkerIdByKeyId(t *testing.T) {
|
|
ctx := context.Background()
|
|
conn, _ := db.TestSetup(t, "postgres")
|
|
rw := db.New(conn)
|
|
wrapper := db.TestWrapper(t)
|
|
kmsCache := kms.TestKms(t, conn, wrapper)
|
|
require.NoError(t, kmsCache.CreateKeys(ctx, scope.Global.String(), kms.WithRandomReader(rand.Reader)))
|
|
repo, err := server.NewRepository(ctx, rw, rw, kmsCache)
|
|
require.NoError(t, err)
|
|
var workerKeyId string
|
|
w := server.TestPkiWorker(t, conn, wrapper, server.WithTestPkiWorkerAuthorizedKeyId(&workerKeyId))
|
|
t.Run("success", func(t *testing.T) {
|
|
got, err := repo.LookupWorkerIdByKeyId(ctx, workerKeyId)
|
|
require.NoError(t, err)
|
|
assert.Equal(t, w.PublicId, got)
|
|
})
|
|
t.Run("not found", func(t *testing.T) {
|
|
got, err := repo.LookupWorkerIdByKeyId(ctx, "unknown_key")
|
|
require.NoError(t, err)
|
|
assert.Empty(t, got)
|
|
})
|
|
t.Run("db error", func(t *testing.T) {
|
|
conn, mock := db.TestSetupWithMock(t)
|
|
rw := db.New(conn)
|
|
mock.ExpectQuery(`SELECT`).WillReturnError(errors.New(context.Background(), errors.Internal, "test", "lookup-error"))
|
|
r, err := server.NewRepository(ctx, rw, rw, kmsCache)
|
|
require.NoError(t, err)
|
|
got, err := r.LookupWorkerIdByKeyId(ctx, "somekey")
|
|
assert.NoError(t, mock.ExpectationsWereMet())
|
|
assert.Truef(t, errors.Match(errors.T(errors.Op("server.(Repository).LookupWorkerIdByKeyId")), err), "got error %v", err)
|
|
assert.Empty(t, got)
|
|
})
|
|
}
|
|
|
|
func TestLookupWorker(t *testing.T) {
|
|
ctx := context.Background()
|
|
conn, _ := db.TestSetup(t, "postgres")
|
|
rw := db.New(conn)
|
|
wrapper := db.TestWrapper(t)
|
|
kms := kms.TestKms(t, conn, wrapper)
|
|
repo, err := server.NewRepository(ctx, rw, rw, kms)
|
|
require.NoError(t, err)
|
|
|
|
w := server.TestKmsWorker(t, conn, wrapper,
|
|
server.WithName("name"),
|
|
server.WithDescription("description"),
|
|
server.WithAddress("address"),
|
|
server.WithWorkerTags(&server.Tag{"key", "val"}))
|
|
require.NoError(t, err)
|
|
|
|
sessRepo, err := session.NewRepository(ctx, rw, rw, kms)
|
|
require.NoError(t, err)
|
|
connRepo, err := session.NewConnectionRepository(ctx, rw, rw, kms, session.WithWorkerStateDelay(0))
|
|
require.NoError(t, err)
|
|
// 1 session with 2 connections
|
|
{
|
|
composedOf := session.TestSessionParams(t, conn, wrapper, iam.TestRepo(t, conn, wrapper))
|
|
future := timestamppb.New(time.Now().Add(time.Hour))
|
|
exp := ×tamp.Timestamp{Timestamp: future}
|
|
composedOf.ConnectionLimit = -1
|
|
sess := session.TestSession(t, conn, wrapper, composedOf, session.WithDbOpts(db.WithSkipVetForWrite(true)), session.WithExpirationTime(exp))
|
|
sess, _, err = sessRepo.ActivateSession(ctx, sess.GetPublicId(), sess.Version, []byte("foo"))
|
|
require.NoError(t, err)
|
|
c, err := connRepo.AuthorizeConnection(ctx, sess.GetPublicId(), w.GetPublicId())
|
|
require.NoError(t, err)
|
|
require.NotNil(t, c)
|
|
c, err = connRepo.AuthorizeConnection(ctx, sess.GetPublicId(), w.GetPublicId())
|
|
require.NoError(t, err)
|
|
require.NotNil(t, c)
|
|
}
|
|
|
|
// 1 session with 1 connection
|
|
{
|
|
sess2 := session.TestDefaultSession(t, conn, wrapper, iam.TestRepo(t, conn, wrapper),
|
|
session.WithDbOpts(db.WithSkipVetForWrite(true)))
|
|
sess2, _, err = sessRepo.ActivateSession(ctx, sess2.GetPublicId(), sess2.Version, []byte("foo"))
|
|
require.NoError(t, err)
|
|
c, err := connRepo.AuthorizeConnection(ctx, sess2.GetPublicId(), w.GetPublicId())
|
|
require.NoError(t, err)
|
|
require.NotNil(t, c)
|
|
}
|
|
|
|
t.Run("success", func(t *testing.T) {
|
|
got, err := repo.LookupWorker(ctx, w.GetPublicId())
|
|
require.NoError(t, err)
|
|
assert.Empty(t, cmp.Diff(w, got, protocmp.Transform()))
|
|
assert.Equal(t, uint32(3), got.ActiveConnectionCount)
|
|
assert.Equal(t, map[string][]string{
|
|
"key": {"val"},
|
|
}, got.CanonicalTags())
|
|
assert.Equal(t, got.CanonicalTags(), got.GetConfigTags())
|
|
})
|
|
t.Run("not found", func(t *testing.T) {
|
|
got, err := repo.LookupWorker(ctx, "w_unknownid")
|
|
require.NoError(t, err)
|
|
assert.Nil(t, got)
|
|
})
|
|
t.Run("db error", func(t *testing.T) {
|
|
conn, mock := db.TestSetupWithMock(t)
|
|
rw := db.New(conn)
|
|
mock.ExpectQuery(`with connection_count`).WillReturnError(errors.New(context.Background(), errors.Internal, "test", "lookup-error"))
|
|
r, err := server.NewRepository(ctx, rw, rw, kms)
|
|
require.NoError(t, err)
|
|
got, err := r.LookupWorker(ctx, w.GetPublicId())
|
|
assert.NoError(t, mock.ExpectationsWereMet())
|
|
assert.Truef(t, errors.Match(errors.T(errors.Op("server.(Repository).LookupWorker")), err), "got error %v", err)
|
|
assert.Nil(t, got)
|
|
})
|
|
}
|
|
|
|
func TestUpsertWorkerStatus(t *testing.T) {
|
|
ctx := context.Background()
|
|
conn, _ := db.TestSetup(t, "postgres")
|
|
rw := db.New(conn)
|
|
wrapper := db.TestWrapper(t)
|
|
kmsCache := kms.TestKms(t, conn, wrapper)
|
|
require.NoError(t, kmsCache.CreateKeys(context.Background(), scope.Global.String(), kms.WithRandomReader(rand.Reader)))
|
|
repo, err := server.NewRepository(ctx, rw, rw, kmsCache)
|
|
require.NoError(t, err)
|
|
|
|
t.Run("create an initial kms worker and update status", func(t *testing.T) {
|
|
wStatus1 := server.NewWorker(scope.Global.String(),
|
|
server.WithAddress("address"), server.WithName("config_name1"),
|
|
server.WithDescription("kms_description1"),
|
|
)
|
|
worker, err := server.TestUpsertAndReturnWorker(ctx, t, wStatus1, repo)
|
|
require.NoError(t, err)
|
|
|
|
assert.True(t, strings.HasPrefix(worker.GetPublicId(), "w_"))
|
|
assert.Equal(t, wStatus1.GetAddress(), worker.GetAddress())
|
|
assert.Equal(t, "config_name1", worker.Name)
|
|
assert.Equal(t, "kms_description1", worker.Description)
|
|
assert.Equal(t, worker.GetLastStatusTime().AsTime(), worker.GetUpdateTime().AsTime())
|
|
assert.Equal(t, uint32(1), worker.Version)
|
|
assert.Equal(t, "address", worker.GetAddress())
|
|
assert.NotNil(t, worker.ReleaseVersion)
|
|
|
|
// update again and see updated last status time
|
|
wStatus2 := server.NewWorker(scope.Global.String(),
|
|
server.WithAddress("new_address"), server.WithName("config_name1"), server.WithReleaseVersion("test-version"))
|
|
worker, err = server.TestUpsertAndReturnWorker(ctx, t, wStatus2, repo)
|
|
require.NoError(t, err)
|
|
assert.Greater(t, worker.GetLastStatusTime().AsTime(), worker.GetCreateTime().AsTime())
|
|
assert.Equal(t, "config_name1", worker.Name)
|
|
// Version does not change for status updates
|
|
assert.Equal(t, uint32(1), worker.Version)
|
|
assert.Equal(t, "test-version", wStatus2.ReleaseVersion)
|
|
assert.Equal(t, "new_address", worker.GetAddress())
|
|
|
|
// Expect this worker to be returned as it is active
|
|
workers, err := repo.ListWorkers(ctx, []string{scope.Global.String()})
|
|
require.NoError(t, err)
|
|
assert.Len(t, workers, 1)
|
|
assert.Equal(t, server.KmsWorkerType.String(), workers[0].Type)
|
|
|
|
// update again with a shutdown state
|
|
wStatus3 := server.NewWorker(scope.Global.String(),
|
|
server.WithAddress("new_address"), server.WithName("config_name1"),
|
|
server.WithOperationalState("shutdown"), server.WithReleaseVersion("Boundary v0.11.0"))
|
|
worker, err = server.TestUpsertAndReturnWorker(ctx, t, wStatus3, repo)
|
|
require.NoError(t, err)
|
|
assert.Greater(t, worker.GetLastStatusTime().AsTime(), worker.GetCreateTime().AsTime())
|
|
// Version does not change for status updates
|
|
assert.Equal(t, uint32(1), worker.Version)
|
|
assert.Equal(t, "shutdown", worker.GetOperationalState())
|
|
assert.Equal(t, server.UnknownLocalStorageState.String(), worker.GetLocalStorageState())
|
|
|
|
// update again with available local storage state
|
|
wStatus4 := server.NewWorker(scope.Global.String(),
|
|
server.WithAddress("new_address"), server.WithName("config_name1"),
|
|
server.WithOperationalState("shutdown"), server.WithReleaseVersion("Boundary v0.11.0"),
|
|
server.WithLocalStorageState("available"))
|
|
worker, err = server.TestUpsertAndReturnWorker(ctx, t, wStatus4, repo)
|
|
require.NoError(t, err)
|
|
assert.Greater(t, worker.GetLastStatusTime().AsTime(), worker.GetCreateTime().AsTime())
|
|
// Version does not change for status updates
|
|
assert.Equal(t, uint32(1), worker.Version)
|
|
assert.Equal(t, server.AvailableLocalStorageState.String(), worker.GetLocalStorageState())
|
|
|
|
// Check that this worker now returns in the shutdown state
|
|
workers, err = repo.ListWorkers(ctx, []string{scope.Global.String()})
|
|
require.NoError(t, err)
|
|
assert.Equal(t, "shutdown", workers[0].GetOperationalState())
|
|
})
|
|
|
|
// Setup and use a pki worker
|
|
var pkiWorkerKeyId string
|
|
pkiWorker := server.TestPkiWorker(t, conn, wrapper, server.WithName("pki"), server.WithDescription("pki_description1"), server.WithTestPkiWorkerAuthorizedKeyId(&pkiWorkerKeyId))
|
|
|
|
t.Run("update status for pki worker", func(t *testing.T) {
|
|
wStatus1 := server.NewWorker(scope.Global.String(),
|
|
server.WithAddress("pki_address"), server.WithDescription("pki_description2"),
|
|
server.WithReleaseVersion("test-version"))
|
|
worker, err := server.TestUpsertAndReturnWorker(ctx, t, wStatus1, repo, server.WithKeyId(pkiWorkerKeyId), server.WithReleaseVersion("test-version"))
|
|
require.NoError(t, err)
|
|
|
|
assert.True(t, strings.HasPrefix(worker.GetPublicId(), "w_"))
|
|
assert.Equal(t, wStatus1.GetAddress(), worker.GetAddress())
|
|
assert.Equal(t, "pki", worker.Name)
|
|
assert.Equal(t, "pki_description1", worker.Description) // PKI workers don't update description via status
|
|
assert.Greater(t, worker.GetLastStatusTime().AsTime(), worker.GetCreateTime().AsTime())
|
|
assert.Equal(t, worker.GetLastStatusTime().AsTime(), worker.GetUpdateTime().AsTime())
|
|
assert.Equal(t, uint32(1), worker.Version)
|
|
assert.Equal(t, "pki_address", worker.GetAddress())
|
|
assert.Equal(t, "test-version", worker.ReleaseVersion)
|
|
assert.Equal(t, server.PkiWorkerType.String(), worker.Type)
|
|
})
|
|
|
|
failureCases := []struct {
|
|
name string
|
|
repo *server.Repository
|
|
status *server.Worker
|
|
options []server.Option
|
|
errAssert func(*testing.T, error)
|
|
}{
|
|
{
|
|
name: "conflicting name with pki",
|
|
repo: repo,
|
|
status: server.NewWorker(scope.Global.String(),
|
|
server.WithName(pkiWorker.GetName()),
|
|
server.WithAddress("someaddress")),
|
|
errAssert: func(t *testing.T, err error) {
|
|
t.Helper()
|
|
assert.True(t, errors.Match(errors.T(errors.NotUnique), err), err)
|
|
},
|
|
},
|
|
{
|
|
name: "no address",
|
|
repo: repo,
|
|
status: server.NewWorker(scope.Global.String(),
|
|
server.WithName("worker_with_no_address")),
|
|
errAssert: func(t *testing.T, err error) {
|
|
t.Helper()
|
|
assert.True(t, errors.Match(errors.T(errors.InvalidParameter), err))
|
|
},
|
|
},
|
|
{
|
|
name: "cant specify public id",
|
|
repo: repo,
|
|
status: func() *server.Worker {
|
|
w := server.NewWorker(scope.Global.String(),
|
|
server.WithName("worker_with_no_address"),
|
|
server.WithAddress("workeraddress"))
|
|
w.PublicId = "w_specified"
|
|
return w
|
|
}(),
|
|
errAssert: func(t *testing.T, err error) {
|
|
t.Helper()
|
|
assert.True(t, errors.Match(errors.T(errors.InvalidParameter), err))
|
|
},
|
|
},
|
|
{
|
|
name: "no name or key id",
|
|
repo: repo,
|
|
status: server.NewWorker(scope.Global.String(),
|
|
server.WithAddress("no_name_address")),
|
|
errAssert: func(t *testing.T, err error) {
|
|
t.Helper()
|
|
assert.True(t, errors.Match(errors.T(errors.InvalidParameter), err))
|
|
},
|
|
},
|
|
{
|
|
name: "no status",
|
|
repo: repo,
|
|
status: nil,
|
|
errAssert: func(t *testing.T, err error) {
|
|
t.Helper()
|
|
assert.True(t, errors.Match(errors.T(errors.InvalidParameter), err))
|
|
},
|
|
},
|
|
{
|
|
name: "empty scope",
|
|
repo: repo,
|
|
status: server.NewWorker("",
|
|
server.WithAddress("address"),
|
|
server.WithName("config_name1")),
|
|
errAssert: func(t *testing.T, err error) {
|
|
t.Helper()
|
|
assert.True(t, errors.Match(errors.T(errors.InvalidParameter), err))
|
|
},
|
|
},
|
|
{
|
|
name: "database failure",
|
|
repo: func() *server.Repository {
|
|
conn, mock := db.TestSetupWithMock(t)
|
|
rw := db.New(conn)
|
|
mock.ExpectBegin()
|
|
mock.ExpectQuery(`INSERT`).WillReturnError(errors.New(context.Background(), errors.Internal, "test", "create-error"))
|
|
mock.ExpectRollback()
|
|
r, err := server.NewRepository(ctx, rw, rw, kmsCache)
|
|
require.NoError(t, err)
|
|
return r
|
|
}(),
|
|
status: server.NewWorker(scope.Global.String(),
|
|
server.WithName("database failure"),
|
|
server.WithAddress("address")),
|
|
errAssert: func(t *testing.T, err error) {
|
|
t.Helper()
|
|
assert.Error(t, err)
|
|
},
|
|
},
|
|
}
|
|
for _, tc := range failureCases {
|
|
t.Run(fmt.Sprintf("Failures %s", tc.name), func(t *testing.T) {
|
|
_, err = tc.repo.UpsertWorkerStatus(ctx, tc.status, tc.options...)
|
|
assert.Error(t, err)
|
|
tc.errAssert(t, err)
|
|
|
|
// Still only the original PKI and KMS workers exist.
|
|
workers, err := repo.ListWorkers(ctx, []string{scope.Global.String()})
|
|
require.NoError(t, err)
|
|
assert.Len(t, workers, 2)
|
|
})
|
|
}
|
|
|
|
t.Run("add another status", func(t *testing.T) {
|
|
anotherStatus := server.NewWorker(scope.Global.String(),
|
|
server.WithName("another_test_worker"),
|
|
server.WithAddress("address"),
|
|
server.WithReleaseVersion("Boundary v0.11.0"))
|
|
_, err = repo.UpsertWorkerStatus(ctx, anotherStatus)
|
|
require.NoError(t, err)
|
|
|
|
workers, err := repo.ListWorkers(ctx, []string{scope.Global.String()})
|
|
require.NoError(t, err)
|
|
assert.Len(t, workers, 3)
|
|
})
|
|
|
|
t.Run("name and key id provided", func(t *testing.T) {
|
|
anotherStatus := server.NewWorker(scope.Global.String(),
|
|
server.WithName("name-and-keyid"),
|
|
server.WithAddress("address2"),
|
|
server.WithReleaseVersion("Boundary v0.11.0"),
|
|
server.WithKeyId(pkiWorkerKeyId))
|
|
_, err = repo.UpsertWorkerStatus(ctx, anotherStatus)
|
|
require.NoError(t, err)
|
|
|
|
workers, err := repo.ListWorkers(ctx, []string{scope.Global.String()})
|
|
require.NoError(t, err)
|
|
assert.Len(t, workers, 4)
|
|
})
|
|
|
|
t.Run("send shutdown status", func(t *testing.T) {
|
|
anotherStatus := server.NewWorker(scope.Global.String(),
|
|
server.WithName("another_test_worker"),
|
|
server.WithAddress("address"),
|
|
server.WithOperationalState("shutdown"),
|
|
server.WithReleaseVersion("Boundary v0.11.0"))
|
|
_, err = repo.UpsertWorkerStatus(ctx, anotherStatus)
|
|
require.NoError(t, err)
|
|
|
|
// Ensure that we find two shutdown workers
|
|
workers, err := repo.ListWorkers(ctx, []string{scope.Global.String()})
|
|
require.NoError(t, err)
|
|
assert.Len(t, workers, 4)
|
|
numShutdown := 0
|
|
for _, w := range workers {
|
|
if w.OperationalState == "shutdown" {
|
|
numShutdown++
|
|
}
|
|
}
|
|
assert.Equal(t, 2, numShutdown)
|
|
})
|
|
|
|
t.Run("ipv4 address", func(t *testing.T) {
|
|
status := server.NewWorker(scope.Global.String(),
|
|
server.WithName("worker-with-ipv4-address"),
|
|
server.WithAddress("8.8.8.8"),
|
|
server.WithReleaseVersion("Boundary v0.11.0"))
|
|
_, err = repo.UpsertWorkerStatus(ctx, status)
|
|
require.NoError(t, err)
|
|
|
|
workers, err := repo.ListWorkers(ctx, []string{scope.Global.String()})
|
|
require.NoError(t, err)
|
|
require.NotEmpty(t, workers)
|
|
var actualWorker *server.Worker
|
|
for _, w := range workers {
|
|
if w.Name == "worker-with-ipv4-address" {
|
|
actualWorker = w
|
|
break
|
|
}
|
|
}
|
|
require.NotNil(t, actualWorker)
|
|
assert.Equal(t, actualWorker.Address, "8.8.8.8")
|
|
})
|
|
|
|
t.Run("ipv6 address", func(t *testing.T) {
|
|
status := server.NewWorker(scope.Global.String(),
|
|
server.WithName("worker-with-ipv6-address"),
|
|
server.WithAddress("2001:4860:4860:0:0:0:0:8888"),
|
|
server.WithReleaseVersion("Boundary v0.11.0"))
|
|
_, err = repo.UpsertWorkerStatus(ctx, status)
|
|
require.NoError(t, err)
|
|
|
|
workers, err := repo.ListWorkers(ctx, []string{scope.Global.String()})
|
|
require.NoError(t, err)
|
|
require.NotEmpty(t, workers)
|
|
var actualWorker *server.Worker
|
|
for _, w := range workers {
|
|
if w.Name == "worker-with-ipv6-address" {
|
|
actualWorker = w
|
|
break
|
|
}
|
|
}
|
|
require.NotNil(t, actualWorker)
|
|
assert.Equal(t, actualWorker.Address, "2001:4860:4860:0:0:0:0:8888")
|
|
})
|
|
|
|
t.Run("ipv6 abbreviated address", func(t *testing.T) {
|
|
status := server.NewWorker(scope.Global.String(),
|
|
server.WithName("worker-with-abbreviated-ipv6-address"),
|
|
server.WithAddress("2001:4860:4860::8888"),
|
|
server.WithReleaseVersion("Boundary v0.11.0"))
|
|
_, err = repo.UpsertWorkerStatus(ctx, status)
|
|
require.NoError(t, err)
|
|
|
|
workers, err := repo.ListWorkers(ctx, []string{scope.Global.String()})
|
|
require.NoError(t, err)
|
|
require.NotEmpty(t, workers)
|
|
var actualWorker *server.Worker
|
|
for _, w := range workers {
|
|
if w.Name == "worker-with-abbreviated-ipv6-address" {
|
|
actualWorker = w
|
|
break
|
|
}
|
|
}
|
|
require.NotNil(t, actualWorker)
|
|
assert.Equal(t, actualWorker.Address, "2001:4860:4860::8888")
|
|
})
|
|
}
|
|
|
|
func TestVerifyKnownWorkers(t *testing.T) {
|
|
ctx := context.Background()
|
|
conn, _ := db.TestSetup(t, "postgres")
|
|
rw := db.New(conn)
|
|
wrapper := db.TestWrapper(t)
|
|
kmsCache := kms.TestKms(t, conn, wrapper)
|
|
require.NoError(t, kmsCache.CreateKeys(context.Background(), scope.Global.String(), kms.WithRandomReader(rand.Reader)))
|
|
repo, err := server.NewRepository(ctx, rw, rw, kmsCache)
|
|
require.NoError(t, err)
|
|
|
|
workerIds := make([]string, 0, 10)
|
|
// Seed the repo with workers
|
|
for i := 0; i < 10; i++ {
|
|
w := server.TestPkiWorker(t, conn, wrapper)
|
|
workerIds = append(workerIds, w.GetPublicId())
|
|
}
|
|
|
|
tests := []struct {
|
|
name string
|
|
testIds []string
|
|
wantCnt int
|
|
}{
|
|
{
|
|
name: "empty-list",
|
|
testIds: []string{},
|
|
wantCnt: 0,
|
|
},
|
|
{
|
|
name: "full-list",
|
|
testIds: workerIds,
|
|
wantCnt: 10,
|
|
},
|
|
{
|
|
name: "bogus-list",
|
|
testIds: []string{"w_bogus1", "w_bogus2"},
|
|
wantCnt: 0,
|
|
},
|
|
{
|
|
name: "partial-bogus-list",
|
|
testIds: []string{"w_bogus1", "w_bogus2", workerIds[0], workerIds[1]},
|
|
wantCnt: 2,
|
|
},
|
|
{
|
|
name: "partial-list",
|
|
testIds: workerIds[:5],
|
|
wantCnt: 5,
|
|
},
|
|
}
|
|
for _, tt := range tests {
|
|
t.Run(tt.name, func(t *testing.T) {
|
|
ids, err := repo.VerifyKnownWorkers(ctx, tt.testIds)
|
|
require.NoError(t, err)
|
|
require.Equal(t, tt.wantCnt, len(ids))
|
|
})
|
|
}
|
|
}
|
|
|
|
func TestTagUpdatingListing(t *testing.T) {
|
|
ctx := context.Background()
|
|
require := require.New(t)
|
|
conn, _ := db.TestSetup(t, "postgres")
|
|
rw := db.New(conn)
|
|
wrapper := db.TestWrapper(t)
|
|
kms := kms.TestKms(t, conn, wrapper)
|
|
repo, err := server.NewRepository(ctx, rw, rw, kms)
|
|
require.NoError(err)
|
|
|
|
worker1 := server.TestKmsWorker(t, conn, wrapper)
|
|
wStatus := server.NewWorker(scope.Global.String(),
|
|
server.WithName(worker1.GetName()),
|
|
server.WithAddress("somethingnew"),
|
|
server.WithWorkerTags(
|
|
&server.Tag{
|
|
Key: "tag1",
|
|
Value: "value1",
|
|
},
|
|
&server.Tag{
|
|
Key: "tag1",
|
|
Value: "value2",
|
|
}))
|
|
|
|
worker1, err = server.TestUpsertAndReturnWorker(ctx, t, wStatus, repo, server.WithUpdateTags(true))
|
|
require.NoError(err)
|
|
assert.Len(t, worker1.CanonicalTags(), 1)
|
|
assert.ElementsMatch(t, []string{"value1", "value2"}, worker1.CanonicalTags()["tag1"])
|
|
|
|
// Update without saying to update tags
|
|
wStatus = server.NewWorker(scope.Global.String(),
|
|
server.WithName(worker1.GetName()),
|
|
server.WithAddress(worker1.GetAddress()),
|
|
server.WithWorkerTags(
|
|
&server.Tag{
|
|
Key: "tag22",
|
|
Value: "value21",
|
|
},
|
|
&server.Tag{
|
|
Key: "tag22",
|
|
Value: "value22",
|
|
}))
|
|
worker1, err = server.TestUpsertAndReturnWorker(ctx, t, wStatus, repo)
|
|
require.NoError(err)
|
|
assert.Len(t, worker1.CanonicalTags(), 1)
|
|
assert.ElementsMatch(t, []string{"value1", "value2"}, worker1.CanonicalTags()["tag1"])
|
|
|
|
// Update tags and test again
|
|
worker1, err = server.TestUpsertAndReturnWorker(ctx, t, wStatus, repo, server.WithUpdateTags(true))
|
|
require.NoError(err)
|
|
assert.Len(t, worker1.CanonicalTags(), 1)
|
|
assert.ElementsMatch(t, []string{"value21", "value22"}, worker1.CanonicalTags()["tag22"])
|
|
}
|
|
|
|
func TestListWorkers(t *testing.T) {
|
|
t.Parallel()
|
|
ctx := context.Background()
|
|
conn, _ := db.TestSetup(t, "postgres")
|
|
rw := db.New(conn)
|
|
wrapper := db.TestWrapper(t)
|
|
kmsCache := kms.TestKms(t, conn, wrapper)
|
|
require.NoError(t, kmsCache.CreateKeys(context.Background(), scope.Global.String(), kms.WithRandomReader(rand.Reader)))
|
|
|
|
const testLimit = 10
|
|
repo, err := server.NewRepository(ctx, rw, rw, kmsCache, server.WithLimit(testLimit))
|
|
require.NoError(t, err)
|
|
|
|
tests := []struct {
|
|
name string
|
|
createCnt int
|
|
reqScopes []string
|
|
opts []server.Option
|
|
wantCnt int
|
|
wantErr bool
|
|
}{
|
|
{
|
|
name: "no-limit",
|
|
createCnt: testLimit + 1,
|
|
reqScopes: []string{scope.Global.String()},
|
|
opts: []server.Option{server.WithLimit(-1)},
|
|
wantCnt: testLimit*2 + 2,
|
|
wantErr: false,
|
|
},
|
|
{
|
|
name: "default-limit",
|
|
createCnt: testLimit + 1,
|
|
reqScopes: []string{scope.Global.String()},
|
|
wantCnt: testLimit,
|
|
wantErr: false,
|
|
},
|
|
{
|
|
name: "custom-limit",
|
|
createCnt: testLimit + 1,
|
|
reqScopes: []string{scope.Global.String()},
|
|
opts: []server.Option{server.WithLimit(3)},
|
|
wantCnt: 3,
|
|
wantErr: false,
|
|
},
|
|
{
|
|
name: "no-scope",
|
|
createCnt: 1,
|
|
reqScopes: nil,
|
|
wantErr: true,
|
|
},
|
|
}
|
|
for _, tt := range tests {
|
|
t.Run(tt.name, func(t *testing.T) {
|
|
w := server.NewWorker(scope.Global.String())
|
|
db.TestDeleteWhere(t, conn, w, "true")
|
|
for i := 0; i < tt.createCnt; i++ {
|
|
server.TestKmsWorker(t, conn, wrapper, server.WithWorkerTags(&server.Tag{"key", "val"}))
|
|
}
|
|
for i := 0; i < tt.createCnt; i++ {
|
|
server.TestPkiWorker(t, conn, wrapper)
|
|
}
|
|
got, err := repo.ListWorkers(ctx, tt.reqScopes, tt.opts...)
|
|
if tt.wantErr {
|
|
assert.Error(t, err)
|
|
return
|
|
}
|
|
require.NoError(t, err)
|
|
assert.Len(t, got, tt.wantCnt)
|
|
for _, worker := range got {
|
|
switch worker.GetType() {
|
|
case server.KmsWorkerType.String():
|
|
canonicalTags := worker.CanonicalTags()
|
|
assert.Len(t, canonicalTags, 1)
|
|
require.Len(t, canonicalTags["key"], 1)
|
|
assert.Equal(t, canonicalTags["key"][0], "val")
|
|
case server.PkiWorkerType.String():
|
|
assert.Empty(t, worker.CanonicalTags())
|
|
}
|
|
}
|
|
})
|
|
}
|
|
}
|
|
|
|
func TestRepository_CreateWorker(t *testing.T) {
|
|
t.Parallel()
|
|
testCtx := context.Background()
|
|
conn, _ := db.TestSetup(t, "postgres")
|
|
rw := db.New(conn)
|
|
wrapper := db.TestWrapper(t)
|
|
testKms := kms.TestKms(t, conn, wrapper)
|
|
testRepo, err := server.NewRepository(testCtx, rw, rw, testKms)
|
|
require.NoError(t, err)
|
|
|
|
iamRepo := iam.TestRepo(t, conn, wrapper)
|
|
org, _ := iam.TestScopes(t, iamRepo)
|
|
|
|
testNewIdFn := func(context.Context) (string, error) {
|
|
return "", errors.New(testCtx, errors.Internal, "test", "testNewIdFn-error")
|
|
}
|
|
|
|
kmsWorker := server.TestKmsWorker(t, conn, wrapper)
|
|
|
|
rootStorage, err := server.NewRepositoryStorage(testCtx, rw, rw, testKms)
|
|
require.NoError(t, err)
|
|
_, err = rotation.RotateRootCertificates(testCtx, rootStorage)
|
|
require.NoError(t, err)
|
|
|
|
tests := []struct {
|
|
name string
|
|
setup func() *server.Worker
|
|
repo *server.Repository
|
|
reader db.Reader
|
|
opt []server.Option
|
|
wantErr bool
|
|
wantErrIs errors.Code
|
|
wantErrContains string
|
|
}{
|
|
{
|
|
name: "missing-worker",
|
|
setup: func() *server.Worker {
|
|
return nil
|
|
},
|
|
repo: testRepo,
|
|
wantErr: true,
|
|
wantErrIs: errors.InvalidParameter,
|
|
wantErrContains: "missing worker",
|
|
},
|
|
{
|
|
name: "public-id-not-empty",
|
|
setup: func() *server.Worker {
|
|
w := server.NewWorker(scope.Global.String())
|
|
var err error
|
|
w.PublicId, err = db.NewPublicId(testCtx, globals.WorkerPrefix)
|
|
require.NoError(t, err)
|
|
return w
|
|
},
|
|
repo: testRepo,
|
|
wantErr: true,
|
|
wantErrIs: errors.InvalidParameter,
|
|
wantErrContains: "public id is not empty",
|
|
},
|
|
{
|
|
name: "empty-scope-id",
|
|
setup: func() *server.Worker {
|
|
w := server.NewWorker(scope.Global.String())
|
|
w.ScopeId = ""
|
|
return w
|
|
},
|
|
repo: testRepo,
|
|
wantErr: true,
|
|
wantErrIs: errors.InvalidParameter,
|
|
wantErrContains: "missing scope id",
|
|
},
|
|
{
|
|
name: "org-scope-id",
|
|
setup: func() *server.Worker {
|
|
w := server.NewWorker(org.PublicId)
|
|
return w
|
|
},
|
|
repo: testRepo,
|
|
wantErr: true,
|
|
wantErrIs: errors.InvalidParameter,
|
|
wantErrContains: "scope id must be \"global\"",
|
|
},
|
|
{
|
|
name: "new-id-error",
|
|
setup: func() *server.Worker {
|
|
w := server.NewWorker(scope.Global.String())
|
|
return w
|
|
},
|
|
repo: testRepo,
|
|
opt: []server.Option{server.WithNewIdFunc(testNewIdFn)},
|
|
wantErr: true,
|
|
wantErrIs: errors.Internal,
|
|
wantErrContains: "testNewIdFn-error",
|
|
},
|
|
{
|
|
name: "create-error",
|
|
setup: func() *server.Worker {
|
|
w := server.NewWorker(scope.Global.String())
|
|
return w
|
|
},
|
|
repo: func() *server.Repository {
|
|
conn, mock := db.TestSetupWithMock(t)
|
|
writer := db.New(conn)
|
|
mock.ExpectBegin()
|
|
mock.ExpectQuery(`INSERT`).WillReturnError(errors.New(testCtx, errors.Internal, "test", "create-error"))
|
|
mock.ExpectRollback()
|
|
r, err := server.NewRepository(testCtx, rw, writer, testKms)
|
|
require.NoError(t, err)
|
|
return r
|
|
}(),
|
|
wantErr: true,
|
|
wantErrContains: "unable to create worker",
|
|
},
|
|
{
|
|
name: "no worker reported address",
|
|
setup: func() *server.Worker {
|
|
w := server.NewWorker(scope.Global.String())
|
|
w.Address = "foo"
|
|
return w
|
|
},
|
|
repo: testRepo,
|
|
wantErr: true,
|
|
wantErrIs: errors.InvalidParameter,
|
|
wantErrContains: "address is not empty",
|
|
},
|
|
{
|
|
name: "no last status update",
|
|
setup: func() *server.Worker {
|
|
w := server.NewWorker(scope.Global.String())
|
|
w.LastStatusTime = timestamp.Now()
|
|
return w
|
|
},
|
|
repo: testRepo,
|
|
wantErr: true,
|
|
wantErrIs: errors.InvalidParameter,
|
|
wantErrContains: "last status time is not nil",
|
|
},
|
|
{
|
|
name: "no-database-key",
|
|
setup: func() *server.Worker {
|
|
w := server.NewWorker(scope.Global.String())
|
|
return w
|
|
},
|
|
reader: rw,
|
|
opt: []server.Option{server.WithFetchNodeCredentialsRequest(
|
|
func() *types.FetchNodeCredentialsRequest {
|
|
// This happens on the worker
|
|
fileStorage, err := file.New(testCtx)
|
|
require.NoError(t, err)
|
|
defer func() { fileStorage.Cleanup(testCtx) }()
|
|
|
|
nodeCreds, err := types.NewNodeCredentials(testCtx, fileStorage)
|
|
require.NoError(t, err)
|
|
// Create request using worker id
|
|
fetchReq, err := nodeCreds.CreateFetchNodeCredentialsRequest(testCtx)
|
|
require.NoError(t, err)
|
|
return fetchReq
|
|
}(),
|
|
)},
|
|
repo: func() *server.Repository {
|
|
mockConn, mock := db.TestSetupWithMock(t)
|
|
mock.ExpectQuery(`SELECT`).WillReturnRows(sqlmock.NewRows([]string{"version", "create_time"}).AddRow(migrations.Version, time.Now()))
|
|
mock.ExpectQuery(`SELECT`).WillReturnRows(sqlmock.NewRows([]string{"version", "create_time"}).AddRow(migrations.Version, time.Now()))
|
|
mock.ExpectQuery(`SELECT`).WillReturnError(errors.New(testCtx, errors.Internal, "test", "no-database-key"))
|
|
k := kms.TestKms(t, mockConn, wrapper)
|
|
r, err := server.NewRepository(testCtx, rw, rw, k)
|
|
require.NoError(t, err)
|
|
return r
|
|
}(),
|
|
wantErr: true,
|
|
wantErrContains: "unable to get wrapper",
|
|
},
|
|
{
|
|
name: "bad-fetch-node-req",
|
|
setup: func() *server.Worker {
|
|
w := server.NewWorker(scope.Global.String())
|
|
return w
|
|
},
|
|
reader: rw,
|
|
opt: []server.Option{server.WithFetchNodeCredentialsRequest(&types.FetchNodeCredentialsRequest{})},
|
|
repo: testRepo,
|
|
wantErr: true,
|
|
wantErrContains: "unable to authorize node",
|
|
},
|
|
{
|
|
name: "unique name violation with kms worker",
|
|
setup: func() *server.Worker {
|
|
w := server.NewWorker(scope.Global.String())
|
|
w.Name = kmsWorker.GetName()
|
|
return w
|
|
},
|
|
reader: rw,
|
|
repo: testRepo,
|
|
wantErr: true,
|
|
wantErrIs: errors.NotUnique,
|
|
},
|
|
{
|
|
name: "success-no-options",
|
|
setup: func() *server.Worker {
|
|
w := server.NewWorker(scope.Global.String())
|
|
w.Name = "success"
|
|
return w
|
|
},
|
|
reader: rw,
|
|
repo: testRepo,
|
|
},
|
|
// This case must follow the one above it.
|
|
{
|
|
name: "unique name violation with pki worker",
|
|
setup: func() *server.Worker {
|
|
w := server.NewWorker(scope.Global.String())
|
|
w.Name = "success"
|
|
return w
|
|
},
|
|
reader: rw,
|
|
repo: testRepo,
|
|
wantErr: true,
|
|
wantErrIs: errors.NotUnique,
|
|
},
|
|
{
|
|
name: "success-with-state",
|
|
setup: func() *server.Worker {
|
|
w := server.NewWorker(scope.Global.String())
|
|
return w
|
|
},
|
|
reader: rw,
|
|
opt: []server.Option{server.WithOperationalState("shutdown")},
|
|
repo: testRepo,
|
|
},
|
|
{
|
|
name: "success-with-fetch-node-req",
|
|
setup: func() *server.Worker {
|
|
w := server.NewWorker(scope.Global.String())
|
|
w.Name = "success-with-fetch-node-req"
|
|
return w
|
|
},
|
|
reader: rw,
|
|
opt: []server.Option{server.WithFetchNodeCredentialsRequest(
|
|
func() *types.FetchNodeCredentialsRequest {
|
|
// This happens on the worker
|
|
fileStorage, err := file.New(testCtx)
|
|
require.NoError(t, err)
|
|
defer func() { fileStorage.Cleanup(testCtx) }()
|
|
|
|
nodeCreds, err := types.NewNodeCredentials(testCtx, fileStorage)
|
|
require.NoError(t, err)
|
|
// Create request using worker id
|
|
fetchReq, err := nodeCreds.CreateFetchNodeCredentialsRequest(testCtx)
|
|
require.NoError(t, err)
|
|
return fetchReq
|
|
}(),
|
|
)},
|
|
repo: testRepo,
|
|
},
|
|
{
|
|
name: "success-with-controller-activation-token",
|
|
setup: func() *server.Worker {
|
|
w := server.NewWorker(scope.Global.String())
|
|
w.Name = "success-with-controller-activation-token"
|
|
return w
|
|
},
|
|
reader: rw,
|
|
opt: []server.Option{server.WithCreateControllerLedActivationToken(true)},
|
|
repo: testRepo,
|
|
},
|
|
{
|
|
name: "success-with-valid-local-storage-state",
|
|
setup: func() *server.Worker {
|
|
w := server.NewWorker(scope.Global.String())
|
|
return w
|
|
},
|
|
reader: rw,
|
|
opt: []server.Option{server.WithLocalStorageState("available")},
|
|
repo: testRepo,
|
|
},
|
|
{
|
|
name: "failure-with-invalid-local-storage-state",
|
|
setup: func() *server.Worker {
|
|
w := server.NewWorker(scope.Global.String())
|
|
return w
|
|
},
|
|
reader: rw,
|
|
opt: []server.Option{server.WithLocalStorageState("invalid")},
|
|
repo: testRepo,
|
|
wantErr: true,
|
|
wantErrIs: errors.InvalidParameter,
|
|
wantErrContains: "invalid local storage state",
|
|
},
|
|
}
|
|
for _, tc := range tests {
|
|
t.Run(tc.name, func(t *testing.T) {
|
|
assert, require := assert.New(t), require.New(t)
|
|
testWorker := tc.setup()
|
|
|
|
got, err := tc.repo.CreateWorker(testCtx, testWorker, tc.opt...)
|
|
if tc.wantErr {
|
|
require.Error(err)
|
|
assert.Nil(got)
|
|
if tc.wantErrIs != errors.Unknown {
|
|
assert.True(errors.Match(errors.T(tc.wantErrIs), err))
|
|
}
|
|
if tc.wantErrContains != "" {
|
|
assert.Contains(err.Error(), tc.wantErrContains)
|
|
}
|
|
return
|
|
}
|
|
require.NoError(err)
|
|
assert.NotNil(got)
|
|
assert.NotEmpty(got.PublicId)
|
|
assert.NotEmpty(got.UpdateTime)
|
|
assert.NotEmpty(got.CreateTime)
|
|
|
|
found := &server.Worker{
|
|
Worker: &store.Worker{
|
|
PublicId: got.PublicId,
|
|
},
|
|
}
|
|
err = rw.LookupByPublicId(testCtx, found)
|
|
require.NoError(err)
|
|
assert.Empty(cmp.Diff(got, found, protocmp.Transform()))
|
|
|
|
opts := server.GetOpts(tc.opt...)
|
|
if opts.WithFetchNodeCredentialsRequest != nil {
|
|
worker := &server.WorkerAuth{
|
|
WorkerAuth: &store.WorkerAuth{},
|
|
}
|
|
require.NoError(tc.reader.LookupWhere(testCtx, worker, "worker_id = ?", []any{found.PublicId}))
|
|
}
|
|
if opts.WithCreateControllerLedActivationToken {
|
|
activationToken := &server.WorkerAuthServerLedActivationToken{
|
|
WorkerAuthServerLedActivationToken: &store.WorkerAuthServerLedActivationToken{},
|
|
}
|
|
require.NoError(tc.reader.LookupWhere(testCtx, activationToken, "worker_id = ?", []any{found.PublicId}))
|
|
}
|
|
})
|
|
}
|
|
}
|
|
|
|
func TestRepository_UpdateWorker(t *testing.T) {
|
|
t.Parallel()
|
|
ctx := context.Background()
|
|
conn, _ := db.TestSetup(t, "postgres")
|
|
rw := db.New(conn)
|
|
wrapper := db.TestWrapper(t)
|
|
kmsCache := kms.TestKms(t, conn, wrapper)
|
|
require.NoError(t, kmsCache.CreateKeys(context.Background(), scope.Global.String(), kms.WithRandomReader(rand.Reader)))
|
|
|
|
repo, err := server.NewRepository(ctx, rw, rw, kmsCache)
|
|
require.NoError(t, err)
|
|
|
|
pkiCases := []struct {
|
|
name string
|
|
modifyWorker func(*testing.T, *server.Worker)
|
|
newIdFunc func(context.Context, string, string) func(context.Context) (string, error)
|
|
path []string
|
|
assertGot func(*testing.T, *server.Worker)
|
|
wantErr bool
|
|
}{
|
|
{
|
|
name: "update name",
|
|
modifyWorker: func(t *testing.T, w *server.Worker) {
|
|
t.Helper()
|
|
w.Name = "foo"
|
|
},
|
|
path: []string{"Name"},
|
|
assertGot: func(t *testing.T, w *server.Worker) {
|
|
t.Helper()
|
|
assert.Equal(t, "foo", w.GetName())
|
|
assert.Equal(t, uint32(2), w.GetVersion())
|
|
assert.Nil(t, w.GetLastStatusTime())
|
|
assert.Greater(t, w.GetUpdateTime().AsTime(), w.GetCreateTime().AsTime())
|
|
},
|
|
},
|
|
{
|
|
name: "update name against kms-pki",
|
|
modifyWorker: func(t *testing.T, w *server.Worker) {
|
|
t.Helper()
|
|
w.Name = "foo"
|
|
},
|
|
newIdFunc: func(ctx context.Context, scopeId, name string) func(ctx context.Context) (string, error) {
|
|
return func(ctx context.Context) (string, error) {
|
|
return server.NewWorkerIdFromScopeAndName(ctx, scopeId, name)
|
|
}
|
|
},
|
|
path: []string{"Name"},
|
|
wantErr: true,
|
|
},
|
|
{
|
|
name: "update description",
|
|
modifyWorker: func(t *testing.T, w *server.Worker) {
|
|
t.Helper()
|
|
w.Description = "foo"
|
|
},
|
|
path: []string{"Description"},
|
|
assertGot: func(t *testing.T, w *server.Worker) {
|
|
t.Helper()
|
|
assert.Equal(t, "foo", w.GetDescription())
|
|
assert.Equal(t, uint32(2), w.GetVersion())
|
|
assert.Nil(t, w.GetLastStatusTime())
|
|
assert.Greater(t, w.GetUpdateTime().AsTime(), w.GetCreateTime().AsTime())
|
|
},
|
|
},
|
|
{
|
|
name: "update description against kms-pki",
|
|
modifyWorker: func(t *testing.T, w *server.Worker) {
|
|
t.Helper()
|
|
w.Description = "foo"
|
|
},
|
|
newIdFunc: func(ctx context.Context, scopeId, name string) func(ctx context.Context) (string, error) {
|
|
return func(ctx context.Context) (string, error) {
|
|
return server.NewWorkerIdFromScopeAndName(ctx, scopeId, name)
|
|
}
|
|
},
|
|
path: []string{"Description"},
|
|
wantErr: true,
|
|
},
|
|
}
|
|
for _, tt := range pkiCases {
|
|
t.Run(tt.name, func(t *testing.T) {
|
|
name := strings.ReplaceAll(tt.name, " ", "-")
|
|
opts := []server.Option{server.WithName(name)}
|
|
if tt.newIdFunc != nil {
|
|
opts = append(opts, server.WithNewIdFunc(tt.newIdFunc(ctx, scope.Global.String(), name)))
|
|
}
|
|
wkr := server.TestPkiWorker(t, conn, wrapper, opts...)
|
|
defer func() {
|
|
_, err := repo.DeleteWorker(ctx, wkr.GetPublicId())
|
|
require.NoError(t, err)
|
|
}()
|
|
tt.modifyWorker(t, wkr)
|
|
got, _, err := repo.UpdateWorker(ctx, wkr, 1, tt.path)
|
|
if tt.wantErr {
|
|
assert.Error(t, err)
|
|
return
|
|
} else {
|
|
require.NoError(t, err)
|
|
}
|
|
tt.assertGot(t, got)
|
|
})
|
|
}
|
|
|
|
// Clear cases
|
|
clearCases := []struct {
|
|
name string
|
|
modifyWorker func(*testing.T, *server.Worker)
|
|
path []string
|
|
assertGot func(*testing.T, *server.Worker)
|
|
wantErr bool
|
|
}{
|
|
{
|
|
name: "clear name",
|
|
modifyWorker: func(t *testing.T, w *server.Worker) {
|
|
t.Helper()
|
|
w.Name = ""
|
|
},
|
|
path: []string{"Name"},
|
|
assertGot: func(t *testing.T, w *server.Worker) {
|
|
t.Helper()
|
|
assert.Empty(t, w.GetName())
|
|
assert.Nil(t, w.GetLastStatusTime())
|
|
assert.Greater(t, w.GetUpdateTime().AsTime(), w.GetCreateTime().AsTime())
|
|
},
|
|
},
|
|
{
|
|
name: "clear description",
|
|
modifyWorker: func(t *testing.T, w *server.Worker) {
|
|
t.Helper()
|
|
w.Description = ""
|
|
},
|
|
path: []string{"Description"},
|
|
assertGot: func(t *testing.T, w *server.Worker) {
|
|
t.Helper()
|
|
assert.Empty(t, w.GetDescription())
|
|
assert.Nil(t, w.GetLastStatusTime())
|
|
assert.Greater(t, w.GetUpdateTime().AsTime(), w.GetCreateTime().AsTime())
|
|
},
|
|
},
|
|
}
|
|
for _, tt := range clearCases {
|
|
t.Run(tt.name, func(t *testing.T) {
|
|
wkr := server.TestPkiWorker(t, conn, wrapper)
|
|
wkr.Name = tt.name
|
|
wkr.Description = tt.name
|
|
wkr, _, err := repo.UpdateWorker(ctx, wkr, 1, []string{"name", "description"})
|
|
require.NoError(t, err)
|
|
|
|
tt.modifyWorker(t, wkr)
|
|
got, _, err := repo.UpdateWorker(ctx, wkr, 2, tt.path)
|
|
if tt.wantErr {
|
|
assert.Error(t, err)
|
|
return
|
|
} else {
|
|
require.NoError(t, err)
|
|
}
|
|
tt.assertGot(t, got)
|
|
})
|
|
}
|
|
|
|
t.Run("version is wrong", func(t *testing.T) {
|
|
wkr := server.TestPkiWorker(t, conn, wrapper)
|
|
wkr.Name = "version is wrong"
|
|
result, numUpdated, err := repo.UpdateWorker(ctx, wkr, 2, []string{"name"})
|
|
require.NoError(t, err)
|
|
assert.Zero(t, numUpdated)
|
|
assert.Equal(t, wkr.GetUpdateTime().AsTime(), result.GetUpdateTime().AsTime())
|
|
})
|
|
|
|
errorCases := []struct {
|
|
name string
|
|
input *server.Worker
|
|
path []string
|
|
version uint32
|
|
wantErr *errors.Template
|
|
}{
|
|
{
|
|
name: "nil worker",
|
|
path: []string{"name"},
|
|
version: 1,
|
|
wantErr: errors.T(errors.InvalidParameter),
|
|
},
|
|
{
|
|
name: "empty path",
|
|
input: server.TestPkiWorker(t, conn, wrapper),
|
|
version: 1,
|
|
wantErr: errors.T(errors.EmptyFieldMask),
|
|
},
|
|
{
|
|
name: "0 version",
|
|
input: server.TestPkiWorker(t, conn, wrapper),
|
|
path: []string{"name"},
|
|
version: 0,
|
|
wantErr: errors.T(errors.InvalidParameter),
|
|
},
|
|
{
|
|
name: "changing kms name",
|
|
input: func() *server.Worker {
|
|
w := server.TestKmsWorker(t, conn, wrapper)
|
|
w.Name = "some change"
|
|
w.Description = ""
|
|
w.Type = ""
|
|
return w
|
|
}(), path: []string{"name"},
|
|
version: 1,
|
|
wantErr: errors.T(errors.InvalidParameter),
|
|
},
|
|
{
|
|
name: "clearing kms name",
|
|
input: func() *server.Worker {
|
|
w := server.TestKmsWorker(t, conn, wrapper)
|
|
w.Name = ""
|
|
w.Description = ""
|
|
w.Type = ""
|
|
return w
|
|
}(), path: []string{"name"},
|
|
version: 1,
|
|
wantErr: errors.T(errors.InvalidParameter),
|
|
},
|
|
{
|
|
name: "changing kms description",
|
|
input: func() *server.Worker {
|
|
w := server.TestKmsWorker(t, conn, wrapper)
|
|
w.Description = "some change"
|
|
w.Name = ""
|
|
w.Type = ""
|
|
return w
|
|
}(), path: []string{"description"},
|
|
version: 1,
|
|
wantErr: errors.T(errors.InvalidParameter),
|
|
},
|
|
{
|
|
name: "clearing kms description",
|
|
input: func() *server.Worker {
|
|
w := server.TestKmsWorker(t, conn, wrapper)
|
|
w.Description = ""
|
|
w.Name = ""
|
|
w.Type = ""
|
|
return w
|
|
}(), path: []string{"description"},
|
|
version: 1,
|
|
wantErr: errors.T(errors.InvalidParameter),
|
|
},
|
|
{
|
|
name: "no public id",
|
|
input: func() *server.Worker {
|
|
w := server.TestPkiWorker(t, conn, wrapper)
|
|
w.PublicId = ""
|
|
return w
|
|
}(),
|
|
path: []string{"name"},
|
|
version: 0,
|
|
wantErr: errors.T(errors.InvalidParameter),
|
|
},
|
|
{
|
|
name: "unrecognized path",
|
|
input: server.TestPkiWorker(t, conn, wrapper),
|
|
path: []string{"UnrecognizedField"},
|
|
version: 1,
|
|
wantErr: errors.T(errors.InvalidFieldMask),
|
|
},
|
|
{
|
|
name: "not found worker",
|
|
input: func() *server.Worker {
|
|
w := server.TestPkiWorker(t, conn, wrapper)
|
|
w.PublicId = "w_notfoundworker"
|
|
return w
|
|
}(),
|
|
path: []string{"name"},
|
|
version: 1,
|
|
wantErr: errors.T(errors.RecordNotFound),
|
|
},
|
|
{
|
|
name: "duplicate name",
|
|
input: func() *server.Worker {
|
|
w1 := server.TestPkiWorker(t, conn, wrapper)
|
|
w1.Name = "somenamethatijustmadeup"
|
|
w1, _, err := repo.UpdateWorker(ctx, w1, w1.Version, []string{"name"}, nil)
|
|
require.NoError(t, err)
|
|
w2 := server.TestPkiWorker(t, conn, wrapper)
|
|
w2.Name = w1.Name
|
|
return w2
|
|
}(),
|
|
path: []string{"name"},
|
|
version: 1,
|
|
wantErr: errors.T("worker with name \"somenamethatijustmadeup\" already exists"),
|
|
},
|
|
}
|
|
for _, tt := range errorCases {
|
|
t.Run(tt.name, func(t *testing.T) {
|
|
_, updated, err := repo.UpdateWorker(ctx, tt.input, tt.version, tt.path)
|
|
assert.Equal(t, 0, updated)
|
|
assert.Truef(t, errors.Match(tt.wantErr, err), "Didn't match error %v", err)
|
|
})
|
|
}
|
|
}
|
|
|
|
func TestListHcpbManagedWorkers(t *testing.T) {
|
|
t.Parallel()
|
|
|
|
conn, _ := db.TestSetup(t, "postgres")
|
|
rw := db.New(conn)
|
|
wrapper := db.TestWrapper(t)
|
|
|
|
ctx := context.Background()
|
|
repo, err := server.NewRepository(ctx, rw, rw, kms.TestKms(t, conn, wrapper))
|
|
require.NoError(t, err)
|
|
|
|
hcpbTag := &server.Tag{Key: server.ManagedWorkerTag, Value: "true"}
|
|
|
|
t.Run("invalidLiveness", func(t *testing.T) {
|
|
worker := server.TestKmsWorker(t, conn, wrapper, server.WithWorkerTags(hcpbTag))
|
|
t.Cleanup(func() {
|
|
_, err = repo.DeleteWorker(ctx, worker.GetPublicId())
|
|
assert.NoError(t, err)
|
|
})
|
|
|
|
_, err := repo.UpsertWorkerStatus(ctx,
|
|
server.NewWorker(
|
|
scope.Global.String(),
|
|
server.WithName(worker.GetName()),
|
|
server.WithAddress(worker.GetAddress()),
|
|
),
|
|
server.WithPublicId(worker.GetPublicId()),
|
|
)
|
|
require.NoError(t, err)
|
|
|
|
workers, err := repo.ListHcpbManagedWorkers(ctx, -10)
|
|
require.NoError(t, err)
|
|
require.Len(t, workers, 1)
|
|
require.Equal(t, workers[0].PublicId, worker.GetPublicId())
|
|
require.Equal(t, workers[0].Address, worker.GetAddress())
|
|
})
|
|
|
|
t.Run("outsideLivenessThreshold", func(t *testing.T) {
|
|
worker := server.TestKmsWorker(t, conn, wrapper, server.WithWorkerTags(hcpbTag))
|
|
t.Cleanup(func() {
|
|
_, err = repo.DeleteWorker(ctx, worker.GetPublicId())
|
|
assert.NoError(t, err)
|
|
})
|
|
|
|
_, err := repo.UpsertWorkerStatus(ctx,
|
|
server.NewWorker(
|
|
scope.Global.String(),
|
|
server.WithName(worker.GetName()),
|
|
server.WithAddress(worker.GetAddress()),
|
|
),
|
|
server.WithPublicId(worker.GetPublicId()),
|
|
)
|
|
require.NoError(t, err)
|
|
|
|
<-time.After(2 * time.Second)
|
|
workers, err := repo.ListHcpbManagedWorkers(ctx, time.Second)
|
|
require.NoError(t, err)
|
|
require.Len(t, workers, 0)
|
|
})
|
|
|
|
t.Run("inLivenessThreshold", func(t *testing.T) {
|
|
worker := server.TestKmsWorker(t, conn, wrapper, server.WithWorkerTags(hcpbTag))
|
|
t.Cleanup(func() {
|
|
_, err = repo.DeleteWorker(ctx, worker.GetPublicId())
|
|
assert.NoError(t, err)
|
|
})
|
|
|
|
_, err := repo.UpsertWorkerStatus(ctx,
|
|
server.NewWorker(
|
|
scope.Global.String(),
|
|
server.WithName(worker.GetName()),
|
|
server.WithAddress(worker.GetAddress()),
|
|
),
|
|
server.WithPublicId(worker.GetPublicId()),
|
|
)
|
|
require.NoError(t, err)
|
|
|
|
workers, err := repo.ListHcpbManagedWorkers(ctx, time.Minute)
|
|
require.NoError(t, err)
|
|
require.Len(t, workers, 1)
|
|
require.Equal(t, workers[0].PublicId, worker.GetPublicId())
|
|
require.Equal(t, workers[0].Address, worker.GetAddress())
|
|
})
|
|
|
|
t.Run("multipleWorkers", func(t *testing.T) {
|
|
// Not HCPb managed but in liveness interval.
|
|
worker1 := server.TestKmsWorker(t, conn, wrapper)
|
|
t.Cleanup(func() {
|
|
_, err = repo.DeleteWorker(ctx, worker1.GetPublicId())
|
|
assert.NoError(t, err)
|
|
})
|
|
|
|
// HCPb managed not in liveness interval.
|
|
worker2 := server.TestKmsWorker(t, conn, wrapper, server.WithWorkerTags(hcpbTag))
|
|
t.Cleanup(func() {
|
|
_, err = repo.DeleteWorker(ctx, worker2.GetPublicId())
|
|
assert.NoError(t, err)
|
|
})
|
|
|
|
// HCPb managed in liveness interval.
|
|
worker3 := server.TestKmsWorker(t, conn, wrapper, server.WithWorkerTags(hcpbTag))
|
|
t.Cleanup(func() {
|
|
_, err = repo.DeleteWorker(ctx, worker3.GetPublicId())
|
|
assert.NoError(t, err)
|
|
})
|
|
|
|
// HCPb managed in liveness interval.
|
|
worker4 := server.TestKmsWorker(t, conn, wrapper, server.WithWorkerTags(hcpbTag))
|
|
t.Cleanup(func() {
|
|
_, err = repo.DeleteWorker(ctx, worker4.GetPublicId())
|
|
assert.NoError(t, err)
|
|
})
|
|
|
|
<-time.After(2 * time.Second)
|
|
|
|
// Update all the workers that are meant to be within the liveness
|
|
// interval (worker1, worker3, worker4).
|
|
_, err := repo.UpsertWorkerStatus(ctx,
|
|
server.NewWorker(
|
|
scope.Global.String(),
|
|
server.WithName(worker1.GetName()),
|
|
server.WithAddress(worker1.GetAddress()),
|
|
),
|
|
server.WithPublicId(worker1.GetPublicId()),
|
|
)
|
|
require.NoError(t, err)
|
|
|
|
_, err = repo.UpsertWorkerStatus(ctx,
|
|
server.NewWorker(
|
|
scope.Global.String(),
|
|
server.WithName(worker3.GetName()),
|
|
server.WithAddress(worker3.GetAddress()),
|
|
),
|
|
server.WithPublicId(worker3.GetPublicId()),
|
|
)
|
|
require.NoError(t, err)
|
|
|
|
_, err = repo.UpsertWorkerStatus(ctx,
|
|
server.NewWorker(
|
|
scope.Global.String(),
|
|
server.WithName(worker4.GetName()),
|
|
server.WithAddress(worker4.GetAddress()),
|
|
),
|
|
server.WithPublicId(worker4.GetPublicId()),
|
|
)
|
|
require.NoError(t, err)
|
|
|
|
// List should return worker3 and worker4.
|
|
workers, err := repo.ListHcpbManagedWorkers(ctx, time.Second)
|
|
require.NoError(t, err)
|
|
|
|
exp := []server.WorkerAddress{
|
|
{PublicId: worker3.GetPublicId(), Address: worker3.GetAddress()},
|
|
{PublicId: worker4.GetPublicId(), Address: worker4.GetAddress()},
|
|
}
|
|
require.ElementsMatch(t, exp, workers)
|
|
})
|
|
}
|
|
|
|
func TestFilterWorkers_EgressFilter(t *testing.T) {
|
|
ctx := context.Background()
|
|
// This prevents us from running tests in parallel.
|
|
server.TestUseCommunityFilterWorkersFn(t)
|
|
conn, _ := db.TestSetup(t, "postgres")
|
|
wrapper := db.TestWrapper(t)
|
|
kmsCache := kms.TestKms(t, conn, wrapper)
|
|
require.NoError(t, kmsCache.CreateKeys(context.Background(), scope.Global.String(), kms.WithRandomReader(rand.Reader)))
|
|
|
|
var workers []*server.Worker
|
|
for i := 0; i < 5; i++ {
|
|
switch {
|
|
case i%2 == 0:
|
|
workers = append(workers, server.TestKmsWorker(t, conn, wrapper,
|
|
server.WithName(fmt.Sprintf("test_worker_%d", i)),
|
|
server.WithWorkerTags(&server.Tag{
|
|
Key: fmt.Sprintf("key%d", i),
|
|
Value: fmt.Sprintf("value%d", i),
|
|
})))
|
|
default:
|
|
workers = append(workers, server.TestPkiWorker(t, conn, wrapper,
|
|
server.WithName(fmt.Sprintf("test_worker_%d", i)),
|
|
server.WithWorkerTags(&server.Tag{
|
|
Key: "key",
|
|
Value: "configvalue",
|
|
})))
|
|
}
|
|
}
|
|
|
|
cases := []struct {
|
|
name string
|
|
in []*server.Worker
|
|
out []*server.Worker
|
|
filter string
|
|
errContains string
|
|
}{
|
|
{
|
|
name: "no-workers",
|
|
in: []*server.Worker{},
|
|
out: []*server.Worker{},
|
|
filter: "",
|
|
errContains: "No workers are available to handle this session, or all have been filtered",
|
|
},
|
|
{
|
|
name: "no-filter",
|
|
in: workers,
|
|
out: workers,
|
|
},
|
|
{
|
|
name: "filter-no-matches",
|
|
in: workers,
|
|
out: workers,
|
|
filter: `"/name" matches "test_worker_[13]" and "configvalue2" in "/tags/key"`,
|
|
errContains: "No workers are available to handle this session, or all have been filtered",
|
|
},
|
|
{
|
|
name: "filter-one-match",
|
|
in: workers,
|
|
out: []*server.Worker{workers[1]},
|
|
filter: `"/name" matches "test_worker_[12]" and "configvalue" in "/tags/key"`,
|
|
},
|
|
{
|
|
name: "filter-two-matches",
|
|
in: workers,
|
|
out: []*server.Worker{workers[1], workers[3]},
|
|
filter: `"configvalue" in "/tags/key"`,
|
|
},
|
|
}
|
|
for _, tc := range cases {
|
|
t.Run(tc.name, func(t *testing.T) {
|
|
assert, require := assert.New(t), require.New(t)
|
|
target := &targettest.Target{
|
|
Target: &tgstore.Target{},
|
|
}
|
|
if len(tc.filter) > 0 {
|
|
target.EgressWorkerFilter = tc.filter
|
|
}
|
|
|
|
out, protocolWorker, err := server.FilterWorkersFn(
|
|
ctx, nil, target, tc.in, "", nil, nil, nil,
|
|
)
|
|
if tc.errContains != "" {
|
|
require.ErrorContains(err, tc.errContains)
|
|
assert.Nil(out)
|
|
return
|
|
}
|
|
|
|
require.NoError(err)
|
|
require.Len(out, len(tc.out))
|
|
for i, exp := range tc.out {
|
|
assert.Equal(exp.Name, out[i].Name)
|
|
}
|
|
require.Nil(protocolWorker)
|
|
})
|
|
}
|
|
}
|
|
|
|
func TestSelectSessionWorkers(t *testing.T) {
|
|
// This prevents us from running tests in parallel.
|
|
server.TestUseCommunityFilterWorkersFn(t)
|
|
conn, _ := db.TestSetup(t, "postgres")
|
|
rw := db.New(conn)
|
|
|
|
wrapper := db.TestWrapper(t)
|
|
kmsCache := kms.TestKms(t, conn, wrapper)
|
|
err := kmsCache.CreateKeys(context.Background(), scope.Global.String(), kms.WithRandomReader(rand.Reader))
|
|
require.NoError(t, err)
|
|
|
|
ctx := context.Background()
|
|
|
|
iamRepo, err := iam.NewRepository(ctx, rw, rw, kmsCache)
|
|
require.NoError(t, err)
|
|
_, proj := iam.TestScopes(t, iamRepo)
|
|
require.NotNil(t, proj)
|
|
|
|
repo, err := server.NewRepository(ctx, rw, rw, kmsCache)
|
|
require.NoError(t, err)
|
|
require.NotNil(t, repo)
|
|
|
|
t.Run("noAvailableWorkers", func(t *testing.T) {
|
|
tg, err := target.New(ctx, targettest.Subtype, proj.GetPublicId(), target.WithWorkerFilter(`"doesnt_exist" in "/tags/type"`))
|
|
require.NoError(t, err)
|
|
require.NotNil(t, tg)
|
|
|
|
was, protoWorkerId, err := repo.SelectSessionWorkers(ctx, server.DefaultLiveness, tg, "", nil, nil, nil)
|
|
require.Nil(t, was)
|
|
require.Empty(t, protoWorkerId)
|
|
require.ErrorContains(t, err, "No workers are available to handle this session.")
|
|
})
|
|
|
|
t.Run("invalidWorkerRPCGracePeriod", func(t *testing.T) {
|
|
w1 := server.TestKmsWorker(t, conn, wrapper, server.WithWorkerTags(&server.Tag{Key: "type", Value: "worker1"}))
|
|
require.NotNil(t, w1)
|
|
t.Cleanup(func() {
|
|
_, err := repo.DeleteWorker(ctx, w1.GetPublicId())
|
|
assert.NoError(t, err)
|
|
})
|
|
|
|
w2 := server.TestKmsWorker(t, conn, wrapper, server.WithWorkerTags(&server.Tag{Key: "type", Value: "worker2"}))
|
|
require.NotNil(t, w2)
|
|
t.Cleanup(func() {
|
|
_, err := repo.DeleteWorker(ctx, w2.GetPublicId())
|
|
assert.NoError(t, err)
|
|
})
|
|
|
|
tg, err := target.New(ctx, targettest.Subtype, proj.GetPublicId(), target.WithWorkerFilter(`"worker1" in "/tags/type"`))
|
|
require.NoError(t, err)
|
|
require.NotNil(t, tg)
|
|
|
|
was, protoWorkerId, err := repo.SelectSessionWorkers(ctx, -10, tg, "", nil, nil, nil)
|
|
require.NoError(t, err)
|
|
require.Empty(t, protoWorkerId)
|
|
require.Len(t, was, 1)
|
|
require.Equal(t, w1.GetPublicId(), was[0].PublicId)
|
|
require.Equal(t, w1.GetAddress(), was[0].Address)
|
|
})
|
|
|
|
t.Run("validWorkerRPCGracePeriod", func(t *testing.T) {
|
|
w1 := server.TestKmsWorker(t, conn, wrapper, server.WithWorkerTags(&server.Tag{Key: "type", Value: "prod"}))
|
|
require.NotNil(t, w1)
|
|
t.Cleanup(func() {
|
|
_, err := repo.DeleteWorker(ctx, w1.GetPublicId())
|
|
assert.NoError(t, err)
|
|
})
|
|
|
|
w2 := server.TestKmsWorker(t, conn, wrapper, server.WithWorkerTags(&server.Tag{Key: "type", Value: "prod"}))
|
|
require.NotNil(t, w2)
|
|
t.Cleanup(func() {
|
|
_, err := repo.DeleteWorker(ctx, w2.GetPublicId())
|
|
assert.NoError(t, err)
|
|
})
|
|
|
|
<-time.After(2 * time.Second)
|
|
_, err := repo.UpsertWorkerStatus(ctx,
|
|
server.NewWorker(
|
|
scope.Global.String(),
|
|
server.WithName(w1.GetName()),
|
|
server.WithAddress(w1.GetAddress()),
|
|
),
|
|
server.WithPublicId(w1.GetPublicId()),
|
|
)
|
|
require.NoError(t, err)
|
|
|
|
tg, err := target.New(ctx, targettest.Subtype, proj.GetPublicId(), target.WithWorkerFilter(`"prod" in "/tags/type"`))
|
|
require.NoError(t, err)
|
|
require.NotNil(t, tg)
|
|
|
|
was, protoWorkerId, err := repo.SelectSessionWorkers(ctx, time.Second, tg, "", nil, nil, nil)
|
|
require.NoError(t, err)
|
|
require.Empty(t, protoWorkerId)
|
|
require.Len(t, was, 1)
|
|
require.Equal(t, w1.GetPublicId(), was[0].PublicId)
|
|
require.Equal(t, w1.GetAddress(), was[0].Address)
|
|
})
|
|
|
|
t.Run("multipleWorkers", func(t *testing.T) {
|
|
w1 := server.TestKmsWorker(t, conn, wrapper, server.WithWorkerTags(&server.Tag{Key: "type", Value: "prod"}))
|
|
require.NotNil(t, w1)
|
|
t.Cleanup(func() {
|
|
_, err := repo.DeleteWorker(ctx, w1.GetPublicId())
|
|
assert.NoError(t, err)
|
|
})
|
|
|
|
w2 := server.TestKmsWorker(t, conn, wrapper, server.WithWorkerTags(&server.Tag{Key: "type", Value: "prod"}))
|
|
require.NotNil(t, w2)
|
|
t.Cleanup(func() {
|
|
_, err := repo.DeleteWorker(ctx, w2.GetPublicId())
|
|
assert.NoError(t, err)
|
|
})
|
|
|
|
tg, err := target.New(ctx, targettest.Subtype, proj.GetPublicId(), target.WithWorkerFilter(`"prod" in "/tags/type"`))
|
|
require.NoError(t, err)
|
|
require.NotNil(t, tg)
|
|
|
|
was, protoWorkerId, err := repo.SelectSessionWorkers(ctx, server.DefaultLiveness, tg, "", nil, nil, nil)
|
|
require.NoError(t, err)
|
|
require.Empty(t, protoWorkerId)
|
|
require.Len(t, was, 2)
|
|
|
|
exp := []server.WorkerAddress{
|
|
{PublicId: w1.GetPublicId(), Address: w1.GetAddress()},
|
|
{PublicId: w2.GetPublicId(), Address: w2.GetAddress()},
|
|
}
|
|
require.ElementsMatch(t, exp, was)
|
|
})
|
|
}
|