548 lines
15 KiB
Go
548 lines
15 KiB
Go
package servers_test
|
|
|
|
import (
|
|
"context"
|
|
"fmt"
|
|
"testing"
|
|
"time"
|
|
|
|
"github.com/hashicorp/boundary/internal/db"
|
|
"github.com/hashicorp/boundary/internal/errors"
|
|
"github.com/hashicorp/boundary/internal/iam"
|
|
"github.com/hashicorp/boundary/internal/kms"
|
|
"github.com/hashicorp/boundary/internal/servers"
|
|
"github.com/hashicorp/boundary/internal/servers/store"
|
|
"github.com/hashicorp/boundary/internal/types/scope"
|
|
"github.com/hashicorp/go-dbw"
|
|
"github.com/stretchr/testify/assert"
|
|
"github.com/stretchr/testify/require"
|
|
)
|
|
|
|
func TestDeleteWorker(t *testing.T) {
|
|
conn, _ := db.TestSetup(t, "postgres")
|
|
rw := db.New(conn)
|
|
wrapper := db.TestWrapper(t)
|
|
kms := kms.TestKms(t, conn, wrapper)
|
|
repo, err := servers.NewRepository(rw, rw, kms)
|
|
require.NoError(t, err)
|
|
ctx := context.Background()
|
|
|
|
type args struct {
|
|
worker *servers.Worker
|
|
}
|
|
tests := []struct {
|
|
name string
|
|
args args
|
|
wantRowsDeleted int
|
|
wantErr bool
|
|
wantErrMsg string
|
|
}{
|
|
{
|
|
name: "valid",
|
|
args: args{
|
|
worker: servers.TestWorker(t, conn, wrapper),
|
|
},
|
|
wantRowsDeleted: 1,
|
|
wantErr: false,
|
|
},
|
|
{
|
|
name: "no-public-id",
|
|
args: args{
|
|
worker: func() *servers.Worker {
|
|
w := servers.Worker{Worker: &store.Worker{}}
|
|
return &w
|
|
}(),
|
|
},
|
|
wantRowsDeleted: 0,
|
|
wantErr: true,
|
|
wantErrMsg: "servers.(Repository).DeleteWorker: missing public id: parameter violation: error #100",
|
|
},
|
|
{
|
|
name: "not-found",
|
|
args: args{
|
|
worker: func() *servers.Worker {
|
|
w := servers.Worker{Worker: &store.Worker{}}
|
|
id, err := db.NewPublicId("w")
|
|
require.NoError(t, err)
|
|
w.PublicId = id
|
|
return &w
|
|
}(),
|
|
},
|
|
wantRowsDeleted: 0,
|
|
wantErr: true,
|
|
wantErrMsg: "servers.(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 TestUpsertWorkerStatus(t *testing.T) {
|
|
conn, _ := db.TestSetup(t, "postgres")
|
|
rw := db.New(conn)
|
|
wrapper := db.TestWrapper(t)
|
|
kms := kms.TestKms(t, conn, wrapper)
|
|
repo, err := servers.NewRepository(rw, rw, kms)
|
|
require.NoError(t, err)
|
|
ctx := context.Background()
|
|
|
|
wStatus1 := servers.NewWorkerStatus("test_worker_1",
|
|
servers.WithAddress("address"),
|
|
servers.WithName("config_name1"))
|
|
_, _, err = repo.UpsertWorkerStatus(ctx, wStatus1)
|
|
require.NoError(t, err)
|
|
{
|
|
workers, err := repo.ListWorkers(ctx)
|
|
require.NoError(t, err)
|
|
assert.Len(t, workers, 1)
|
|
worker := workers[0]
|
|
assert.Equal(t, wStatus1.GetWorkerId(), worker.GetPublicId())
|
|
assert.Equal(t, wStatus1.Address, worker.CanonicalAddress())
|
|
assert.Empty(t, worker.Name)
|
|
assert.Equal(t, worker.ReportedStatus.CreateTime, worker.CreateTime)
|
|
assert.Equal(t, worker.LastConnectionUpdate(), worker.UpdateTime)
|
|
assert.Equal(t, uint32(1), worker.Version)
|
|
assert.Empty(t, worker.Address)
|
|
assert.Empty(t, worker.Description)
|
|
}
|
|
|
|
// Remove the status name, verify that it doesn't remove the resource's name.
|
|
updatedStatus1 := servers.NewWorkerStatus(wStatus1.WorkerId,
|
|
servers.WithAddress(wStatus1.Address))
|
|
_, _, err = repo.UpsertWorkerStatus(ctx, updatedStatus1)
|
|
require.NoError(t, err)
|
|
{
|
|
workers, err := repo.ListWorkers(ctx)
|
|
require.NoError(t, err)
|
|
assert.Len(t, workers, 1)
|
|
worker := workers[0]
|
|
// The resource's name remains untouched
|
|
assert.Empty(t, worker.Name)
|
|
// The ReportedStatus's name is correctly cleared
|
|
assert.Empty(t, worker.ReportedStatus.Name)
|
|
// The last connection update time was increased
|
|
assert.Greater(t, worker.LastConnectionUpdate().AsTime().UnixMilli(), worker.UpdateTime.AsTime().UnixMilli())
|
|
assert.Equal(t, worker.UpdateTime, worker.CreateTime)
|
|
|
|
assert.Equal(t, updatedStatus1.GetWorkerId(), worker.GetPublicId())
|
|
assert.Equal(t, updatedStatus1.Address, worker.CanonicalAddress())
|
|
assert.Equal(t, worker.ReportedStatus.CreateTime, worker.CreateTime)
|
|
assert.Equal(t, uint32(1), worker.Version)
|
|
assert.Empty(t, worker.Address)
|
|
assert.Empty(t, worker.Description)
|
|
}
|
|
|
|
failureCases := []struct {
|
|
name string
|
|
status *servers.WorkerStatus
|
|
errAssert func(*testing.T, error)
|
|
}{
|
|
{
|
|
name: "no address",
|
|
status: servers.NewWorkerStatus("worker_with_no_address",
|
|
servers.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: "invalid id",
|
|
status: servers.NewWorkerStatus("short",
|
|
servers.WithAddress("someaddress"),
|
|
servers.WithName("worker_with_short_id")),
|
|
errAssert: func(t *testing.T, err error) {
|
|
t.Helper()
|
|
assert.True(t, errors.IsCheckConstraintError(err))
|
|
},
|
|
},
|
|
{
|
|
name: "no id",
|
|
status: servers.NewWorkerStatus("",
|
|
servers.WithAddress("anotheraddress"),
|
|
servers.WithName("no_address")),
|
|
errAssert: func(t *testing.T, err error) {
|
|
t.Helper()
|
|
assert.True(t, errors.Match(errors.T(errors.InvalidParameter), err))
|
|
},
|
|
},
|
|
{
|
|
name: "no status",
|
|
status: nil,
|
|
errAssert: func(t *testing.T, err error) {
|
|
t.Helper()
|
|
assert.True(t, errors.Match(errors.T(errors.InvalidParameter), err))
|
|
},
|
|
},
|
|
}
|
|
for _, tc := range failureCases {
|
|
t.Run(fmt.Sprintf("Failures %s", tc.name), func(t *testing.T) {
|
|
_, _, err = repo.UpsertWorkerStatus(ctx, tc.status)
|
|
assert.Error(t, err)
|
|
tc.errAssert(t, err)
|
|
|
|
// Still only the original worker exists.
|
|
workers, err := repo.ListWorkers(ctx)
|
|
require.NoError(t, err)
|
|
assert.Len(t, workers, 1)
|
|
})
|
|
}
|
|
|
|
{
|
|
anotherStatus := servers.NewWorkerStatus("another_test_worker",
|
|
servers.WithAddress("address"))
|
|
_, _, err = repo.UpsertWorkerStatus(ctx, anotherStatus)
|
|
require.NoError(t, err)
|
|
{
|
|
workers, err := repo.ListWorkers(ctx)
|
|
require.NoError(t, err)
|
|
assert.Len(t, workers, 2)
|
|
}
|
|
}
|
|
}
|
|
|
|
func TestTagUpdatingListing(t *testing.T) {
|
|
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 := servers.NewRepository(rw, rw, kms)
|
|
require.NoError(err)
|
|
ctx := context.Background()
|
|
|
|
wStatus := servers.NewWorkerStatus("test_worker_1",
|
|
servers.WithAddress("127.0.0.1"),
|
|
servers.WithWorkerTags(
|
|
&servers.Tag{
|
|
Key: "tag1",
|
|
Value: "value1",
|
|
},
|
|
&servers.Tag{
|
|
Key: "tag1",
|
|
Value: "value2",
|
|
}))
|
|
|
|
_, _, err = repo.UpsertWorkerStatus(ctx, wStatus, servers.WithUpdateTags(true))
|
|
require.NoError(err)
|
|
|
|
wStatus = servers.NewWorkerStatus("test_worker_2",
|
|
servers.WithAddress("127.0.0.1"),
|
|
servers.WithWorkerTags(
|
|
&servers.Tag{
|
|
Key: "tag2",
|
|
Value: "value1",
|
|
},
|
|
&servers.Tag{
|
|
Key: "tag2",
|
|
Value: "value2",
|
|
}))
|
|
_, _, err = repo.UpsertWorkerStatus(ctx, wStatus, servers.WithUpdateTags(true))
|
|
require.NoError(err)
|
|
|
|
tags, err := repo.ListTagsForWorkers(ctx, []string{"test_worker_1", "test_worker_2"})
|
|
require.NoError(err)
|
|
|
|
// Base case
|
|
exp := map[string][]*servers.Tag{
|
|
"test_worker_1": {
|
|
{
|
|
Key: "tag1",
|
|
Value: "value1",
|
|
}, {
|
|
Key: "tag1",
|
|
Value: "value2",
|
|
},
|
|
},
|
|
"test_worker_2": {
|
|
{
|
|
Key: "tag2",
|
|
Value: "value1",
|
|
},
|
|
{
|
|
Key: "tag2",
|
|
Value: "value2",
|
|
},
|
|
},
|
|
}
|
|
require.Equal(exp, tags)
|
|
|
|
// Update without saying to update tags
|
|
wStatus = servers.NewWorkerStatus("test_worker_2",
|
|
servers.WithAddress("192.168.1.1"),
|
|
servers.WithWorkerTags(
|
|
&servers.Tag{
|
|
Key: "tag22",
|
|
Value: "value21",
|
|
},
|
|
&servers.Tag{
|
|
Key: "tag22",
|
|
Value: "value22",
|
|
}))
|
|
_, _, err = repo.UpsertWorkerStatus(ctx, wStatus)
|
|
require.NoError(err)
|
|
tags, err = repo.ListTagsForWorkers(ctx, []string{"test_worker_1", "test_worker_2"})
|
|
require.NoError(err)
|
|
require.Equal(exp, tags)
|
|
|
|
// Update tags and test again
|
|
_, _, err = repo.UpsertWorkerStatus(ctx, wStatus, servers.WithUpdateTags(true))
|
|
require.NoError(err)
|
|
tags, err = repo.ListTagsForWorkers(ctx, []string{"test_worker_1", "test_worker_2"})
|
|
require.NoError(err)
|
|
require.NotEqual(exp, tags)
|
|
// Update and try again
|
|
exp["test_worker_2"] = []*servers.Tag{
|
|
{
|
|
Key: "tag22",
|
|
Value: "value21",
|
|
},
|
|
{
|
|
Key: "tag22",
|
|
Value: "value22",
|
|
},
|
|
}
|
|
require.Equal(exp, tags)
|
|
}
|
|
|
|
func TestListWorkersWithLiveness(t *testing.T) {
|
|
t.Parallel()
|
|
require := require.New(t)
|
|
conn, _ := db.TestSetup(t, "postgres")
|
|
rw := db.New(conn)
|
|
wrapper := db.TestWrapper(t)
|
|
kms := kms.TestKms(t, conn, wrapper)
|
|
serversRepo, err := servers.NewRepository(rw, rw, kms)
|
|
require.NoError(err)
|
|
ctx := context.Background()
|
|
|
|
newWorkerStatus := func(publicId string) *servers.WorkerStatus {
|
|
result := servers.NewWorkerStatus(publicId,
|
|
servers.WithAddress("127.0.0.1"))
|
|
_, rowsUpdated, err := serversRepo.UpsertWorkerStatus(ctx, result)
|
|
require.NoError(err)
|
|
require.Equal(1, rowsUpdated)
|
|
|
|
return result
|
|
}
|
|
|
|
workConf1 := newWorkerStatus("test_worker_1")
|
|
workConf2 := newWorkerStatus("test_worker_2")
|
|
workConf3 := newWorkerStatus("test_worker_3")
|
|
|
|
// Sleep the default liveness time (15sec currently) +1s
|
|
time.Sleep(time.Second * 16)
|
|
|
|
// Push an upsert to the first worker so that its status has been
|
|
// updated.
|
|
_, rowsUpdated, err := serversRepo.UpsertWorkerStatus(ctx, workConf1)
|
|
require.NoError(err)
|
|
require.Equal(1, rowsUpdated)
|
|
|
|
requireIds := func(expected []string, actual []*servers.Worker) {
|
|
require.Len(expected, len(actual))
|
|
want := make(map[string]struct{})
|
|
for _, v := range expected {
|
|
want[v] = struct{}{}
|
|
}
|
|
|
|
got := make(map[string]struct{})
|
|
for _, v := range actual {
|
|
got[v.PublicId] = struct{}{}
|
|
}
|
|
|
|
require.Equal(want, got)
|
|
}
|
|
|
|
// Default liveness, should only list 1
|
|
result, err := serversRepo.ListWorkers(ctx)
|
|
require.NoError(err)
|
|
require.Len(result, 1)
|
|
requireIds([]string{workConf1.WorkerId}, result)
|
|
|
|
// Upsert second server.
|
|
_, rowsUpdated, err = serversRepo.UpsertWorkerStatus(ctx, workConf2)
|
|
require.NoError(err)
|
|
require.Equal(1, rowsUpdated)
|
|
|
|
// Static liveness. Should get two, so long as this did not take
|
|
// more than 5s to execute.
|
|
result, err = serversRepo.ListWorkers(ctx, servers.WithLiveness(time.Second*5))
|
|
require.NoError(err)
|
|
require.Len(result, 2)
|
|
requireIds([]string{workConf1.WorkerId, workConf2.WorkerId}, result)
|
|
|
|
// Liveness disabled, should get all three workers.
|
|
result, err = serversRepo.ListWorkers(ctx, servers.WithLiveness(-1))
|
|
require.NoError(err)
|
|
require.Len(result, 3)
|
|
requireIds([]string{workConf1.WorkerId, workConf2.WorkerId, workConf3.WorkerId}, result)
|
|
}
|
|
|
|
func TestRepository_CreateWorker(t *testing.T) {
|
|
t.Parallel()
|
|
testCtx := context.Background()
|
|
conn, _ := db.TestSetup(t, "postgres")
|
|
rw := db.New(conn)
|
|
wrapper := db.TestWrapper(t)
|
|
kms := kms.TestKms(t, conn, wrapper)
|
|
testRepo, err := servers.NewRepository(rw, rw, kms)
|
|
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")
|
|
}
|
|
|
|
tests := []struct {
|
|
name string
|
|
setup func() *servers.Worker
|
|
repo *servers.Repository
|
|
opt []servers.Option
|
|
wantErr bool
|
|
wantErrIs errors.Code
|
|
wantErrContains string
|
|
}{
|
|
{
|
|
name: "missing-worker",
|
|
setup: func() *servers.Worker {
|
|
return nil
|
|
},
|
|
repo: testRepo,
|
|
wantErr: true,
|
|
wantErrIs: errors.InvalidParameter,
|
|
wantErrContains: "missing worker",
|
|
},
|
|
{
|
|
name: "public-id-not-empty",
|
|
setup: func() *servers.Worker {
|
|
w := servers.NewWorker(scope.Global.String())
|
|
var err error
|
|
w.PublicId, err = db.NewPublicId(servers.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() *servers.Worker {
|
|
w := servers.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() *servers.Worker {
|
|
w := servers.NewWorker(org.PublicId)
|
|
return w
|
|
},
|
|
repo: testRepo,
|
|
wantErr: true,
|
|
wantErrIs: errors.InvalidParameter,
|
|
wantErrContains: "scope id must be \"global\"",
|
|
},
|
|
{
|
|
name: "new-id-error",
|
|
setup: func() *servers.Worker {
|
|
w := servers.NewWorker(scope.Global.String())
|
|
return w
|
|
},
|
|
repo: testRepo,
|
|
opt: []servers.Option{servers.WithNewIdFunc(testNewIdFn)},
|
|
wantErr: true,
|
|
wantErrIs: errors.Internal,
|
|
wantErrContains: "testNewIdFn-error",
|
|
},
|
|
{
|
|
name: "create-error",
|
|
setup: func() *servers.Worker {
|
|
w := servers.NewWorker(scope.Global.String())
|
|
return w
|
|
},
|
|
repo: func() *servers.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 := servers.NewRepository(rw, writer, kms)
|
|
require.NoError(t, err)
|
|
return r
|
|
}(),
|
|
wantErr: true,
|
|
wantErrContains: "unable to create worker",
|
|
},
|
|
{
|
|
name: "success",
|
|
setup: func() *servers.Worker {
|
|
w := servers.NewWorker(scope.Global.String())
|
|
return w
|
|
},
|
|
repo: testRepo,
|
|
},
|
|
}
|
|
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 := &servers.Worker{
|
|
Worker: &store.Worker{
|
|
PublicId: got.PublicId,
|
|
},
|
|
}
|
|
err = rw.LookupByPublicId(testCtx, found)
|
|
require.NoError(err)
|
|
if found.Tags == nil {
|
|
found.Tags = []*servers.Tag{}
|
|
}
|
|
assert.Equal(got, found)
|
|
})
|
|
}
|
|
}
|