Add worker tagging (#862)
This allows workers to have tags assigned to them, which can then be filtered to control which targets they are allowed to serve.
This commit is contained in:
1 parent
595a86508e
commit
717a3b52ee
54 files changed
+2150
-666
No files matched your search
@@ -8,12 +8,11 @@ import (
|
||||
"github.com/hashicorp/boundary/internal/servers/controller"
|
||||
"github.com/hashicorp/boundary/internal/servers/worker"
|
||||
"github.com/hashicorp/go-hclog"
|
||||
"github.com/stretchr/testify/assert"
|
||||
"github.com/stretchr/testify/require"
|
||||
)
|
||||
|
||||
func TestMultiControllerMultiWorkerConnections(t *testing.T) {
|
||||
assert, require := assert.New(t), require.New(t)
|
||||
require := require.New(t)
|
||||
amId := "ampw_1234567890"
|
||||
user := "user"
|
||||
password := "passpass"
|
||||
@@ -38,29 +37,8 @@ func TestMultiControllerMultiWorkerConnections(t *testing.T) {
|
||||
})
|
||||
defer c2.Shutdown()
|
||||
|
||||
expectWorkers := func(c *controller.TestController, workers ...*worker.TestWorker) {
|
||||
updateTimes := c.Controller().WorkerStatusUpdateTimes()
|
||||
workerMap := map[string]*worker.TestWorker{}
|
||||
for _, w := range workers {
|
||||
workerMap[w.Name()] = w
|
||||
}
|
||||
updateTimes.Range(func(k, v interface{}) bool {
|
||||
require.NotNil(k)
|
||||
require.NotNil(v)
|
||||
if workerMap[k.(string)] == nil {
|
||||
// We don't remove from updateTimes currently so if we're not
|
||||
// expecting it we'll see an out-of-date entry
|
||||
return true
|
||||
}
|
||||
assert.WithinDuration(time.Now(), v.(time.Time), 30*time.Second)
|
||||
delete(workerMap, k.(string))
|
||||
return true
|
||||
})
|
||||
assert.Empty(workerMap)
|
||||
}
|
||||
|
||||
expectWorkers(c1)
|
||||
expectWorkers(c2)
|
||||
expectWorkers(t, c1)
|
||||
expectWorkers(t, c2)
|
||||
|
||||
w1 := worker.NewTestWorker(t, &worker.TestWorkerOpts{
|
||||
WorkerAuthKms: c1.Config().WorkerAuthKms,
|
||||
@@ -70,8 +48,8 @@ func TestMultiControllerMultiWorkerConnections(t *testing.T) {
|
||||
defer w1.Shutdown()
|
||||
|
||||
time.Sleep(10 * time.Second)
|
||||
expectWorkers(c1, w1)
|
||||
expectWorkers(c2, w1)
|
||||
expectWorkers(t, c1, w1)
|
||||
expectWorkers(t, c2, w1)
|
||||
|
||||
w2 := w1.AddClusterWorkerMember(t, &worker.TestWorkerOpts{
|
||||
Logger: logger.Named("w2"),
|
||||
@@ -79,25 +57,25 @@ func TestMultiControllerMultiWorkerConnections(t *testing.T) {
|
||||
defer w2.Shutdown()
|
||||
|
||||
time.Sleep(10 * time.Second)
|
||||
expectWorkers(c1, w1, w2)
|
||||
expectWorkers(c2, w1, w2)
|
||||
expectWorkers(t, c1, w1, w2)
|
||||
expectWorkers(t, c2, w1, w2)
|
||||
|
||||
require.NoError(w1.Worker().Shutdown(true))
|
||||
time.Sleep(10 * time.Second)
|
||||
expectWorkers(c1, w2)
|
||||
expectWorkers(c2, w2)
|
||||
expectWorkers(t, c1, w2)
|
||||
expectWorkers(t, c2, w2)
|
||||
|
||||
require.NoError(w1.Worker().Start())
|
||||
time.Sleep(10 * time.Second)
|
||||
expectWorkers(c1, w1, w2)
|
||||
expectWorkers(c2, w1, w2)
|
||||
expectWorkers(t, c1, w1, w2)
|
||||
expectWorkers(t, c2, w1, w2)
|
||||
|
||||
require.NoError(c1.Controller().Shutdown(true))
|
||||
time.Sleep(10 * time.Second)
|
||||
expectWorkers(c2, w1, w2)
|
||||
expectWorkers(t, c2, w1, w2)
|
||||
|
||||
require.NoError(c1.Controller().Start())
|
||||
time.Sleep(10 * time.Second)
|
||||
expectWorkers(c1, w1, w2)
|
||||
expectWorkers(c2, w1, w2)
|
||||
expectWorkers(t, c1, w1, w2)
|
||||
expectWorkers(t, c2, w1, w2)
|
||||
}
|
||||
@@ -0,0 +1,32 @@
|
||||
package cluster
|
||||
|
||||
import (
|
||||
"testing"
|
||||
"time"
|
||||
|
||||
"github.com/hashicorp/boundary/internal/servers/controller"
|
||||
"github.com/hashicorp/boundary/internal/servers/worker"
|
||||
"github.com/stretchr/testify/assert"
|
||||
"github.com/stretchr/testify/require"
|
||||
)
|
||||
|
||||
func expectWorkers(t *testing.T, c *controller.TestController, workers ...*worker.TestWorker) {
|
||||
updateTimes := c.Controller().WorkerStatusUpdateTimes()
|
||||
workerMap := map[string]*worker.TestWorker{}
|
||||
for _, w := range workers {
|
||||
workerMap[w.Name()] = w
|
||||
}
|
||||
updateTimes.Range(func(k, v interface{}) bool {
|
||||
require.NotNil(t, k)
|
||||
require.NotNil(t, v)
|
||||
if workerMap[k.(string)] == nil {
|
||||
// We don't remove from updateTimes currently so if we're not
|
||||
// expecting it we'll see an out-of-date entry
|
||||
return true
|
||||
}
|
||||
assert.WithinDuration(t, time.Now(), v.(time.Time), 30*time.Second)
|
||||
delete(workerMap, k.(string))
|
||||
return true
|
||||
})
|
||||
assert.Empty(t, workerMap)
|
||||
}
|
||||
@@ -0,0 +1,154 @@
|
||||
package cluster
|
||||
|
||||
import (
|
||||
"testing"
|
||||
"time"
|
||||
|
||||
"github.com/hashicorp/boundary/api/targets"
|
||||
"github.com/hashicorp/boundary/internal/cmd/config"
|
||||
pb "github.com/hashicorp/boundary/internal/gen/controller/api/resources/targets"
|
||||
"github.com/hashicorp/boundary/internal/servers/controller"
|
||||
"github.com/hashicorp/boundary/internal/servers/worker"
|
||||
"github.com/hashicorp/go-hclog"
|
||||
"github.com/mr-tron/base58"
|
||||
"github.com/stretchr/testify/assert"
|
||||
"github.com/stretchr/testify/require"
|
||||
"google.golang.org/protobuf/proto"
|
||||
)
|
||||
|
||||
func TestWorkerTagging(t *testing.T) {
|
||||
amId := "ampw_1234567890"
|
||||
user := "user"
|
||||
password := "passpass"
|
||||
logger := hclog.New(&hclog.LoggerOptions{
|
||||
Level: hclog.Trace,
|
||||
})
|
||||
|
||||
conf, err := config.DevController()
|
||||
require.NoError(t, err)
|
||||
|
||||
c1 := controller.NewTestController(t, &controller.TestControllerOpts{
|
||||
Config: conf,
|
||||
DefaultAuthMethodId: amId,
|
||||
DefaultLoginName: user,
|
||||
DefaultPassword: password,
|
||||
InitialResourcesSuffix: "1234567890",
|
||||
Logger: logger.Named("c1"),
|
||||
})
|
||||
defer c1.Shutdown()
|
||||
|
||||
ctx := c1.Context()
|
||||
|
||||
// No workers yet
|
||||
expectWorkers(t, c1)
|
||||
|
||||
// Ensure target is valid
|
||||
client := c1.Client()
|
||||
client.SetToken(c1.Token().Token)
|
||||
tcl := targets.NewClient(client)
|
||||
tgt, err := tcl.Read(ctx, "ttcp_1234567890")
|
||||
require.NoError(t, err)
|
||||
require.NotNil(t, tgt)
|
||||
|
||||
// Worker 1
|
||||
conf, err = config.DevWorker()
|
||||
require.NoError(t, err)
|
||||
conf.Worker.Name = "w1"
|
||||
conf.Worker.Tags = map[string][]string{
|
||||
"region": {"east"},
|
||||
}
|
||||
w1 := worker.NewTestWorker(t, &worker.TestWorkerOpts{
|
||||
Config: conf,
|
||||
WorkerAuthKms: c1.Config().WorkerAuthKms,
|
||||
InitialControllers: c1.ClusterAddrs(),
|
||||
Logger: logger.Named("w1"),
|
||||
})
|
||||
defer w1.Shutdown()
|
||||
w1Addr := w1.ProxyAddrs()[0]
|
||||
|
||||
// Worker 2
|
||||
conf, err = config.DevWorker()
|
||||
require.NoError(t, err)
|
||||
conf.Worker.Name = "w2"
|
||||
conf.Worker.Tags = map[string][]string{
|
||||
"region": {"west"},
|
||||
"az": {"one", "two", "three"},
|
||||
}
|
||||
w2 := worker.NewTestWorker(t, &worker.TestWorkerOpts{
|
||||
Config: conf,
|
||||
WorkerAuthKms: c1.Config().WorkerAuthKms,
|
||||
InitialControllers: c1.ClusterAddrs(),
|
||||
Logger: logger.Named("w2"),
|
||||
})
|
||||
defer w2.Shutdown()
|
||||
w2Addr := w2.ProxyAddrs()[0]
|
||||
|
||||
// Worker 3
|
||||
conf, err = config.DevWorker()
|
||||
require.NoError(t, err)
|
||||
conf.Worker.Name = "w3"
|
||||
conf.Worker.Tags = map[string][]string{
|
||||
"region": {"west"},
|
||||
"az": {"one", "three"},
|
||||
}
|
||||
w3 := worker.NewTestWorker(t, &worker.TestWorkerOpts{
|
||||
Config: conf,
|
||||
WorkerAuthKms: c1.Config().WorkerAuthKms,
|
||||
InitialControllers: c1.ClusterAddrs(),
|
||||
Logger: logger.Named("w3"),
|
||||
})
|
||||
defer w3.Shutdown()
|
||||
w3Addr := w3.ProxyAddrs()[0]
|
||||
|
||||
time.Sleep(10 * time.Second)
|
||||
expectWorkers(t, c1, w1, w2, w3)
|
||||
|
||||
cases := []struct {
|
||||
name string
|
||||
filter string
|
||||
expWorkers []string
|
||||
}{
|
||||
{
|
||||
name: "base case",
|
||||
expWorkers: []string{w1Addr, w2Addr, w3Addr},
|
||||
},
|
||||
{
|
||||
name: "name and region",
|
||||
filter: `"/name" matches "w[13]" and "west" in "/tags/region"`,
|
||||
expWorkers: []string{w3Addr},
|
||||
},
|
||||
{
|
||||
name: "name and az",
|
||||
filter: `"/name" matches "w[23]" and "three" in "/tags/az"`,
|
||||
expWorkers: []string{w2Addr, w3Addr},
|
||||
},
|
||||
}
|
||||
for _, tc := range cases {
|
||||
t.Run(tc.name, func(t *testing.T) {
|
||||
require, assert := require.New(t), assert.New(t)
|
||||
|
||||
opts := []targets.Option{targets.WithAutomaticVersioning(true), targets.WithDescription(tc.name)}
|
||||
if tc.filter != "" {
|
||||
opts = append(opts, targets.WithWorkerFilter(tc.filter))
|
||||
}
|
||||
tgt, err := tcl.Update(ctx, "ttcp_1234567890", 0, opts...)
|
||||
require.NoError(err)
|
||||
require.NotNil(tgt)
|
||||
|
||||
// Fetch the session and decode the token
|
||||
sar, err := tcl.AuthorizeSession(ctx, "ttcp_1234567890")
|
||||
require.NoError(err)
|
||||
sat := sar.GetItem().(*targets.SessionAuthorization).AuthorizationToken
|
||||
decodedSat, err := base58.FastBase58Decoding(sat)
|
||||
require.NoError(err)
|
||||
var sad pb.SessionAuthorizationData
|
||||
require.NoError(proto.Unmarshal(decodedSat, &sad))
|
||||
|
||||
var addrs []string
|
||||
for _, worker := range sad.GetWorkerInfo() {
|
||||
addrs = append(addrs, worker.GetAddress())
|
||||
}
|
||||
assert.ElementsMatch(tc.expWorkers, addrs)
|
||||
})
|
||||
}
|
||||
}
|
||||
Reference in new issue
Block a user