* Remove the server_id from session table, change it to worker_id on session_connection This is step 1 in updating the worker table. The worker is becoming a public resource so it must have a public id. We drop the server_id column on the session_connection table and add the worker_id column so we can use the wt_public_key type. Since db migrations are applied while the controller is offline, it should be safe to assume all session connections are not in a running state, thus allowing us to null out all the worker ids in the session_connection table. For now we don't have very good enforcement of the wt_public_id constraints, since the id is currently defined in the worker config, but this will soon change and the worker's id will be generated by the controller.
156 lines
4.3 KiB
Go
156 lines
4.3 KiB
Go
//go:build !hsm
|
|
// +build !hsm
|
|
|
|
package server
|
|
|
|
import (
|
|
"context"
|
|
"encoding/json"
|
|
"fmt"
|
|
"testing"
|
|
"time"
|
|
|
|
"github.com/hashicorp/boundary/api"
|
|
"github.com/hashicorp/boundary/api/authmethods"
|
|
"github.com/hashicorp/boundary/api/authtokens"
|
|
"github.com/hashicorp/boundary/api/targets"
|
|
"github.com/hashicorp/boundary/internal/session"
|
|
"github.com/hashicorp/boundary/internal/tests/helper"
|
|
"github.com/hashicorp/boundary/internal/types/scope"
|
|
"github.com/hashicorp/boundary/testing/controller"
|
|
"github.com/mitchellh/cli"
|
|
"github.com/stretchr/testify/require"
|
|
"go.uber.org/atomic"
|
|
|
|
_ "github.com/hashicorp/boundary/internal/daemon/controller/handlers/targets/tcp"
|
|
)
|
|
|
|
const shutdownReloadWorkerConfig = `
|
|
disable_mlock = true
|
|
|
|
worker {
|
|
name = "w_1234567890"
|
|
description = "A default worker created in dev mode"
|
|
controllers = ["%s"]
|
|
}
|
|
|
|
kms "aead" {
|
|
purpose = "worker-auth"
|
|
aead_type = "aes-gcm"
|
|
key = "%s"
|
|
key_id = "global_worker-auth"
|
|
}
|
|
|
|
listener "tcp" {
|
|
purpose = "proxy"
|
|
address = "127.0.0.1:9205"
|
|
}
|
|
`
|
|
|
|
func TestServer_ShutdownWorker(t *testing.T) {
|
|
require := require.New(t)
|
|
|
|
rootWrapper, _ := wrapperWithKey(t)
|
|
recoveryWrapper, _ := wrapperWithKey(t)
|
|
workerAuthWrapper, key := wrapperWithKey(t)
|
|
testController := controller.NewTestController(t, controller.WithWorkerAuthKms(workerAuthWrapper), controller.WithRootKms(rootWrapper), controller.WithRecoveryKms(recoveryWrapper))
|
|
defer testController.Shutdown()
|
|
|
|
// Start the worker
|
|
workerCmd := testServerCommand(t, testServerCommandOpts{})
|
|
workerCmd.presetConfig = atomic.NewString(fmt.Sprintf(shutdownReloadWorkerConfig, testController.ClusterAddrs()[0], key))
|
|
|
|
workerCodeChan := make(chan int)
|
|
go func() {
|
|
workerCodeChan <- workerCmd.Run(nil)
|
|
}()
|
|
waitCh(t, workerCmd.startedCh)
|
|
|
|
// Give the worker time for initial status or we can fail because no worker
|
|
// is ready for the connection yet
|
|
time.Sleep(5 * time.Second)
|
|
|
|
// Set up the target
|
|
ctx := context.Background()
|
|
client := buildClient(t, testController.ApiAddrs()[0])
|
|
setAuthToken(ctx, t, client)
|
|
|
|
tcl := targets.NewClient(client)
|
|
tgtL, err := tcl.List(ctx, scope.Global.String(), targets.WithRecursive(true))
|
|
// tgt, err := tcl.Read(ctx, "ttcp_1234567890")
|
|
require.NoError(err)
|
|
require.Len(tgtL.Items, 1)
|
|
tgt := tgtL.Items[0]
|
|
require.NotNil(tgt)
|
|
|
|
// Create test server, update default port on target
|
|
ts := helper.NewTestTcpServer(t)
|
|
require.NotNil(ts)
|
|
defer ts.Close()
|
|
tgtR, err := tcl.Update(ctx, tgt.Id, tgt.Version, targets.WithTcpTargetDefaultPort(ts.Port()))
|
|
require.NoError(err)
|
|
require.NotNil(tgtR)
|
|
|
|
// Authorize and connect
|
|
sess := helper.NewTestSession(ctx, t, tcl, tgt.Id)
|
|
sConn := sess.Connect(ctx, t)
|
|
|
|
// Run initial send/receive test, make sure things are working
|
|
t.Log("running initial send/recv test")
|
|
sConn.TestSendRecvAll(t)
|
|
|
|
// Now, shut the worker down.
|
|
close(workerCmd.ShutdownCh)
|
|
if <-workerCodeChan != 0 {
|
|
output := workerCmd.UI.(*cli.MockUi).ErrorWriter.String() + workerCmd.UI.(*cli.MockUi).OutputWriter.String()
|
|
require.FailNow(output, "command exited with non-zero error code")
|
|
}
|
|
|
|
// Connection should fail, and the session should be closed on the DB.
|
|
sConn.TestSendRecvFail(t)
|
|
sess.ExpectConnectionStateOnController(ctx, t, testController.Controller().ConnectionRepoFn, session.StatusClosed)
|
|
}
|
|
|
|
// largely copied from controller/testing.go
|
|
func buildClient(t *testing.T, addr string) *api.Client {
|
|
require := require.New(t)
|
|
client, err := api.NewClient(nil)
|
|
require.NoError(err)
|
|
err = client.SetAddr(addr)
|
|
require.NoError(err)
|
|
// Because this is using the real lib it can pick up from stored locations
|
|
// like the system keychain. Explicitly clear the token to ensure we
|
|
// understand the client state at the start of each test.
|
|
client.SetToken("")
|
|
|
|
return client
|
|
}
|
|
|
|
func setAuthToken(ctx context.Context, t *testing.T, client *api.Client) {
|
|
require := require.New(t)
|
|
result, err := authmethods.NewClient(client).Authenticate(
|
|
ctx,
|
|
"ampw_1234567890",
|
|
"login",
|
|
map[string]interface{}{
|
|
"login_name": "admin",
|
|
"password": "passpass",
|
|
},
|
|
)
|
|
require.NoError(err)
|
|
token := new(authtokens.AuthToken)
|
|
err = json.Unmarshal(result.GetRawAttributes(), token)
|
|
require.NoError(err)
|
|
|
|
client.SetToken(token.Token)
|
|
}
|
|
|
|
func waitCh(t *testing.T, c chan struct{}) {
|
|
require := require.New(t)
|
|
select {
|
|
case <-c:
|
|
case <-time.After(15 * time.Second):
|
|
require.FailNow("timeout")
|
|
}
|
|
}
|