2013-12-11 22:57:40 +00:00
|
|
|
package consul
|
|
|
|
|
|
|
|
import (
|
2013-12-12 00:33:19 +00:00
|
|
|
"fmt"
|
2013-12-19 20:03:57 +00:00
|
|
|
"net/rpc"
|
2013-12-11 22:57:40 +00:00
|
|
|
"os"
|
2014-04-21 19:08:00 +00:00
|
|
|
"strings"
|
2013-12-11 22:57:40 +00:00
|
|
|
"testing"
|
|
|
|
"time"
|
2014-10-09 18:54:47 +00:00
|
|
|
|
2017-08-23 14:52:48 +00:00
|
|
|
"github.com/hashicorp/consul/acl"
|
2017-07-06 10:34:00 +00:00
|
|
|
"github.com/hashicorp/consul/agent/structs"
|
2017-04-19 23:00:11 +00:00
|
|
|
"github.com/hashicorp/consul/api"
|
2016-01-29 19:42:34 +00:00
|
|
|
"github.com/hashicorp/consul/lib"
|
2020-05-27 16:47:32 +00:00
|
|
|
"github.com/hashicorp/consul/lib/stringslice"
|
2019-03-27 12:54:56 +00:00
|
|
|
"github.com/hashicorp/consul/sdk/testutil/retry"
|
2019-04-16 16:00:15 +00:00
|
|
|
"github.com/hashicorp/consul/testrpc"
|
2017-01-18 22:26:42 +00:00
|
|
|
"github.com/hashicorp/consul/types"
|
2019-06-27 12:24:35 +00:00
|
|
|
msgpackrpc "github.com/hashicorp/net-rpc-msgpackrpc"
|
2018-03-07 01:32:41 +00:00
|
|
|
"github.com/stretchr/testify/assert"
|
2018-06-05 03:04:45 +00:00
|
|
|
"github.com/stretchr/testify/require"
|
2013-12-11 22:57:40 +00:00
|
|
|
)
|
|
|
|
|
2016-12-11 00:00:11 +00:00
|
|
|
func TestCatalog_Register(t *testing.T) {
|
2017-06-27 13:22:18 +00:00
|
|
|
t.Parallel()
|
2013-12-11 22:57:40 +00:00
|
|
|
dir1, s1 := testServer(t)
|
|
|
|
defer os.RemoveAll(dir1)
|
|
|
|
defer s1.Shutdown()
|
2015-10-13 23:43:52 +00:00
|
|
|
codec := rpcClient(t, s1)
|
|
|
|
defer codec.Close()
|
2013-12-11 22:57:40 +00:00
|
|
|
|
2013-12-19 20:03:57 +00:00
|
|
|
arg := structs.RegisterRequest{
|
2014-01-08 21:39:40 +00:00
|
|
|
Datacenter: "dc1",
|
|
|
|
Node: "foo",
|
|
|
|
Address: "127.0.0.1",
|
|
|
|
Service: &structs.NodeService{
|
|
|
|
Service: "db",
|
2014-04-03 19:03:10 +00:00
|
|
|
Tags: []string{"master"},
|
2014-01-08 21:39:40 +00:00
|
|
|
Port: 8000,
|
|
|
|
},
|
2016-12-09 00:01:01 +00:00
|
|
|
Check: &structs.HealthCheck{
|
2017-03-27 08:28:54 +00:00
|
|
|
CheckID: types.CheckID("db-check"),
|
2016-12-09 00:01:01 +00:00
|
|
|
ServiceID: "db",
|
|
|
|
},
|
2013-12-11 22:57:40 +00:00
|
|
|
}
|
|
|
|
var out struct{}
|
|
|
|
|
2015-10-13 23:43:52 +00:00
|
|
|
err := msgpackrpc.CallWithCodec(codec, "Catalog.Register", &arg, &out)
|
2016-07-10 17:24:18 +00:00
|
|
|
if err != nil {
|
2013-12-11 22:57:40 +00:00
|
|
|
t.Fatalf("err: %v", err)
|
|
|
|
}
|
2017-01-18 22:26:42 +00:00
|
|
|
}
|
|
|
|
|
2017-05-08 16:34:45 +00:00
|
|
|
func TestCatalog_RegisterService_InvalidAddress(t *testing.T) {
|
2017-06-27 13:22:18 +00:00
|
|
|
t.Parallel()
|
2017-05-08 16:34:45 +00:00
|
|
|
dir1, s1 := testServer(t)
|
|
|
|
defer os.RemoveAll(dir1)
|
|
|
|
defer s1.Shutdown()
|
|
|
|
codec := rpcClient(t, s1)
|
|
|
|
defer codec.Close()
|
|
|
|
|
2017-05-09 07:29:13 +00:00
|
|
|
for _, addr := range []string{"0.0.0.0", "::", "[::]"} {
|
|
|
|
t.Run("addr "+addr, func(t *testing.T) {
|
|
|
|
arg := structs.RegisterRequest{
|
|
|
|
Datacenter: "dc1",
|
|
|
|
Node: "foo",
|
|
|
|
Address: "127.0.0.1",
|
|
|
|
Service: &structs.NodeService{
|
|
|
|
Service: "db",
|
|
|
|
Address: addr,
|
|
|
|
Port: 8000,
|
|
|
|
},
|
|
|
|
}
|
|
|
|
var out struct{}
|
2017-05-08 16:34:45 +00:00
|
|
|
|
2017-05-09 07:29:13 +00:00
|
|
|
err := msgpackrpc.CallWithCodec(codec, "Catalog.Register", &arg, &out)
|
|
|
|
if err == nil || err.Error() != "Invalid service address" {
|
|
|
|
t.Fatalf("got error %v want 'Invalid service address'", err)
|
|
|
|
}
|
|
|
|
})
|
2017-05-08 16:34:45 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2017-11-01 21:25:46 +00:00
|
|
|
func TestCatalog_RegisterService_SkipNodeUpdate(t *testing.T) {
|
|
|
|
t.Parallel()
|
|
|
|
dir1, s1 := testServer(t)
|
|
|
|
defer os.RemoveAll(dir1)
|
|
|
|
defer s1.Shutdown()
|
|
|
|
codec := rpcClient(t, s1)
|
|
|
|
defer codec.Close()
|
|
|
|
|
|
|
|
// Register a node
|
|
|
|
arg := structs.RegisterRequest{
|
|
|
|
Datacenter: "dc1",
|
|
|
|
Node: "foo",
|
|
|
|
Address: "127.0.0.1",
|
|
|
|
}
|
|
|
|
var out struct{}
|
|
|
|
err := msgpackrpc.CallWithCodec(codec, "Catalog.Register", &arg, &out)
|
|
|
|
if err != nil {
|
|
|
|
t.Fatal(err)
|
|
|
|
}
|
|
|
|
|
|
|
|
// Update it with a blank address, should fail.
|
|
|
|
arg.Address = ""
|
|
|
|
arg.Service = &structs.NodeService{
|
|
|
|
Service: "db",
|
|
|
|
Port: 8000,
|
|
|
|
}
|
|
|
|
err = msgpackrpc.CallWithCodec(codec, "Catalog.Register", &arg, &out)
|
|
|
|
if err == nil || err.Error() != "Must provide address if SkipNodeUpdate is not set" {
|
|
|
|
t.Fatalf("got error %v want 'Must provide address...'", err)
|
|
|
|
}
|
|
|
|
|
|
|
|
// Set SkipNodeUpdate, should succeed
|
|
|
|
arg.SkipNodeUpdate = true
|
|
|
|
err = msgpackrpc.CallWithCodec(codec, "Catalog.Register", &arg, &out)
|
|
|
|
if err != nil {
|
|
|
|
t.Fatal(err)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2017-01-18 22:26:42 +00:00
|
|
|
func TestCatalog_Register_NodeID(t *testing.T) {
|
2017-06-27 13:22:18 +00:00
|
|
|
t.Parallel()
|
2017-01-18 22:26:42 +00:00
|
|
|
dir1, s1 := testServer(t)
|
|
|
|
defer os.RemoveAll(dir1)
|
|
|
|
defer s1.Shutdown()
|
|
|
|
codec := rpcClient(t, s1)
|
|
|
|
defer codec.Close()
|
|
|
|
|
|
|
|
arg := structs.RegisterRequest{
|
|
|
|
Datacenter: "dc1",
|
|
|
|
ID: "nope",
|
|
|
|
Node: "foo",
|
|
|
|
Address: "127.0.0.1",
|
|
|
|
Service: &structs.NodeService{
|
|
|
|
Service: "db",
|
|
|
|
Tags: []string{"master"},
|
|
|
|
Port: 8000,
|
|
|
|
},
|
|
|
|
Check: &structs.HealthCheck{
|
2017-03-27 08:28:54 +00:00
|
|
|
CheckID: types.CheckID("db-check"),
|
2017-01-18 22:26:42 +00:00
|
|
|
ServiceID: "db",
|
|
|
|
},
|
|
|
|
}
|
|
|
|
var out struct{}
|
2013-12-11 22:57:40 +00:00
|
|
|
|
2017-01-18 22:26:42 +00:00
|
|
|
err := msgpackrpc.CallWithCodec(codec, "Catalog.Register", &arg, &out)
|
|
|
|
if err == nil || !strings.Contains(err.Error(), "Bad node ID") {
|
2013-12-11 22:57:40 +00:00
|
|
|
t.Fatalf("err: %v", err)
|
2017-01-18 22:26:42 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
arg.ID = types.NodeID("adf4238a-882b-9ddc-4a9d-5b6758e4159e")
|
|
|
|
if err := msgpackrpc.CallWithCodec(codec, "Catalog.Register", &arg, &out); err != nil {
|
|
|
|
t.Fatalf("err: %v", err)
|
|
|
|
}
|
2013-12-11 22:57:40 +00:00
|
|
|
}
|
2013-12-11 23:34:10 +00:00
|
|
|
|
2016-12-11 00:00:11 +00:00
|
|
|
func TestCatalog_Register_ACLDeny(t *testing.T) {
|
2017-06-27 13:22:18 +00:00
|
|
|
t.Parallel()
|
2014-12-01 04:05:15 +00:00
|
|
|
dir1, s1 := testServerWithConfig(t, func(c *Config) {
|
|
|
|
c.ACLDatacenter = "dc1"
|
2018-10-19 16:04:07 +00:00
|
|
|
c.ACLsEnabled = true
|
2014-12-01 04:05:15 +00:00
|
|
|
c.ACLMasterToken = "root"
|
|
|
|
c.ACLDefaultPolicy = "deny"
|
|
|
|
})
|
|
|
|
defer os.RemoveAll(dir1)
|
|
|
|
defer s1.Shutdown()
|
2020-05-29 21:16:03 +00:00
|
|
|
testrpc.WaitForTestAgent(t, s1.RPC, "dc1", testrpc.WithToken("root"))
|
2015-10-13 23:43:52 +00:00
|
|
|
codec := rpcClient(t, s1)
|
|
|
|
defer codec.Close()
|
2014-12-01 04:05:15 +00:00
|
|
|
|
2016-12-10 03:15:44 +00:00
|
|
|
// Create the ACL.
|
2014-12-01 04:05:15 +00:00
|
|
|
arg := structs.ACLRequest{
|
|
|
|
Datacenter: "dc1",
|
|
|
|
Op: structs.ACLSet,
|
|
|
|
ACL: structs.ACL{
|
2016-12-10 03:15:44 +00:00
|
|
|
Name: "User token",
|
2018-10-19 16:04:07 +00:00
|
|
|
Type: structs.ACLTokenTypeClient,
|
2016-12-10 03:15:44 +00:00
|
|
|
Rules: `
|
|
|
|
service "foo" {
|
|
|
|
policy = "write"
|
|
|
|
}
|
2020-05-29 21:16:03 +00:00
|
|
|
node "foo" {
|
|
|
|
policy = "write"
|
|
|
|
}
|
2016-12-10 03:15:44 +00:00
|
|
|
`,
|
2014-12-01 04:05:15 +00:00
|
|
|
},
|
|
|
|
WriteRequest: structs.WriteRequest{Token: "root"},
|
|
|
|
}
|
2016-12-10 03:15:44 +00:00
|
|
|
var id string
|
|
|
|
if err := msgpackrpc.CallWithCodec(codec, "ACL.Apply", &arg, &id); err != nil {
|
2014-12-01 04:05:15 +00:00
|
|
|
t.Fatalf("err: %v", err)
|
|
|
|
}
|
|
|
|
|
|
|
|
argR := structs.RegisterRequest{
|
|
|
|
Datacenter: "dc1",
|
|
|
|
Node: "foo",
|
|
|
|
Address: "127.0.0.1",
|
|
|
|
Service: &structs.NodeService{
|
|
|
|
Service: "db",
|
|
|
|
Tags: []string{"master"},
|
|
|
|
Port: 8000,
|
|
|
|
},
|
|
|
|
WriteRequest: structs.WriteRequest{Token: id},
|
|
|
|
}
|
|
|
|
var outR struct{}
|
|
|
|
|
2016-12-10 03:15:44 +00:00
|
|
|
// This should fail since we are writing to the "db" service, which isn't
|
|
|
|
// allowed.
|
2015-10-13 23:43:52 +00:00
|
|
|
err := msgpackrpc.CallWithCodec(codec, "Catalog.Register", &argR, &outR)
|
2017-08-23 14:52:48 +00:00
|
|
|
if !acl.IsErrPermissionDenied(err) {
|
2014-12-01 04:05:15 +00:00
|
|
|
t.Fatalf("err: %v", err)
|
|
|
|
}
|
2014-12-01 04:10:42 +00:00
|
|
|
|
2016-12-10 03:15:44 +00:00
|
|
|
// The "foo" service should work, though.
|
2014-12-01 04:10:42 +00:00
|
|
|
argR.Service.Service = "foo"
|
2015-10-13 23:43:52 +00:00
|
|
|
err = msgpackrpc.CallWithCodec(codec, "Catalog.Register", &argR, &outR)
|
2014-12-01 04:10:42 +00:00
|
|
|
if err != nil {
|
|
|
|
t.Fatalf("err: %v", err)
|
|
|
|
}
|
2016-12-08 01:58:23 +00:00
|
|
|
|
2020-05-29 21:16:03 +00:00
|
|
|
// Try the former special case for the "consul" service.
|
2016-12-08 01:58:23 +00:00
|
|
|
argR.Service.Service = "consul"
|
|
|
|
err = msgpackrpc.CallWithCodec(codec, "Catalog.Register", &argR, &outR)
|
2017-08-23 14:52:48 +00:00
|
|
|
if !acl.IsErrPermissionDenied(err) {
|
2016-12-08 01:58:23 +00:00
|
|
|
t.Fatalf("err: %v", err)
|
|
|
|
}
|
2016-12-09 00:01:01 +00:00
|
|
|
|
|
|
|
// Register a db service using the root token.
|
|
|
|
argR.Service.Service = "db"
|
|
|
|
argR.Service.ID = "my-id"
|
|
|
|
argR.Token = "root"
|
|
|
|
err = msgpackrpc.CallWithCodec(codec, "Catalog.Register", &argR, &outR)
|
|
|
|
if err != nil {
|
|
|
|
t.Fatalf("err: %v", err)
|
|
|
|
}
|
|
|
|
|
|
|
|
// Prove that we are properly looking up the node services and passing
|
|
|
|
// that to the ACL helper. We can vet the helper independently in its
|
|
|
|
// own unit test after this. This is trying to register over the db
|
|
|
|
// service we created above, which is a check that depends on looking
|
2016-12-10 03:15:44 +00:00
|
|
|
// at the existing registration data with that service ID. This is a new
|
|
|
|
// check for version 8.
|
2016-12-09 00:01:01 +00:00
|
|
|
argR.Service.Service = "foo"
|
|
|
|
argR.Service.ID = "my-id"
|
|
|
|
argR.Token = id
|
|
|
|
err = msgpackrpc.CallWithCodec(codec, "Catalog.Register", &argR, &outR)
|
2017-08-23 14:52:48 +00:00
|
|
|
if !acl.IsErrPermissionDenied(err) {
|
2016-12-09 00:01:01 +00:00
|
|
|
t.Fatalf("err: %v", err)
|
|
|
|
}
|
2014-12-01 04:05:15 +00:00
|
|
|
}
|
|
|
|
|
2016-12-11 00:00:11 +00:00
|
|
|
func TestCatalog_Register_ForwardLeader(t *testing.T) {
|
2017-06-27 13:22:18 +00:00
|
|
|
t.Parallel()
|
2013-12-12 00:42:19 +00:00
|
|
|
dir1, s1 := testServer(t)
|
|
|
|
defer os.RemoveAll(dir1)
|
|
|
|
defer s1.Shutdown()
|
2015-10-13 23:43:52 +00:00
|
|
|
codec1 := rpcClient(t, s1)
|
|
|
|
defer codec1.Close()
|
2013-12-12 00:42:19 +00:00
|
|
|
|
|
|
|
dir2, s2 := testServer(t)
|
|
|
|
defer os.RemoveAll(dir2)
|
|
|
|
defer s2.Shutdown()
|
2015-10-13 23:43:52 +00:00
|
|
|
codec2 := rpcClient(t, s2)
|
|
|
|
defer codec2.Close()
|
2013-12-12 00:42:19 +00:00
|
|
|
|
|
|
|
// Try to join
|
2017-05-05 10:29:49 +00:00
|
|
|
joinLAN(t, s2, s1)
|
2013-12-12 00:42:19 +00:00
|
|
|
|
2017-04-19 23:00:11 +00:00
|
|
|
testrpc.WaitForLeader(t, s1.RPC, "dc1")
|
|
|
|
testrpc.WaitForLeader(t, s2.RPC, "dc1")
|
2013-12-12 00:42:19 +00:00
|
|
|
|
|
|
|
// Use the follower as the client
|
2015-10-13 23:43:52 +00:00
|
|
|
var codec rpc.ClientCodec
|
2013-12-12 00:42:19 +00:00
|
|
|
if !s1.IsLeader() {
|
2015-10-13 23:43:52 +00:00
|
|
|
codec = codec1
|
2013-12-12 00:42:19 +00:00
|
|
|
} else {
|
2015-10-13 23:43:52 +00:00
|
|
|
codec = codec2
|
2013-12-12 00:42:19 +00:00
|
|
|
}
|
|
|
|
|
2013-12-19 20:03:57 +00:00
|
|
|
arg := structs.RegisterRequest{
|
2014-01-08 21:39:40 +00:00
|
|
|
Datacenter: "dc1",
|
|
|
|
Node: "foo",
|
|
|
|
Address: "127.0.0.1",
|
|
|
|
Service: &structs.NodeService{
|
|
|
|
Service: "db",
|
2014-04-03 19:03:10 +00:00
|
|
|
Tags: []string{"master"},
|
2014-01-08 21:39:40 +00:00
|
|
|
Port: 8000,
|
|
|
|
},
|
2013-12-12 00:42:19 +00:00
|
|
|
}
|
|
|
|
var out struct{}
|
2015-10-13 23:43:52 +00:00
|
|
|
if err := msgpackrpc.CallWithCodec(codec, "Catalog.Register", &arg, &out); err != nil {
|
2013-12-12 00:42:19 +00:00
|
|
|
t.Fatalf("err: %v", err)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2016-12-11 00:00:11 +00:00
|
|
|
func TestCatalog_Register_ForwardDC(t *testing.T) {
|
2017-06-27 13:22:18 +00:00
|
|
|
t.Parallel()
|
2013-12-12 00:33:19 +00:00
|
|
|
dir1, s1 := testServer(t)
|
|
|
|
defer os.RemoveAll(dir1)
|
|
|
|
defer s1.Shutdown()
|
2015-10-13 23:43:52 +00:00
|
|
|
codec := rpcClient(t, s1)
|
|
|
|
defer codec.Close()
|
2013-12-12 00:33:19 +00:00
|
|
|
|
|
|
|
dir2, s2 := testServerDC(t, "dc2")
|
|
|
|
defer os.RemoveAll(dir2)
|
|
|
|
defer s2.Shutdown()
|
|
|
|
|
|
|
|
// Try to join
|
2017-05-05 10:29:49 +00:00
|
|
|
joinWAN(t, s2, s1)
|
2013-12-12 00:33:19 +00:00
|
|
|
|
2017-04-19 23:00:11 +00:00
|
|
|
testrpc.WaitForLeader(t, s1.RPC, "dc2")
|
2013-12-12 00:33:19 +00:00
|
|
|
|
2013-12-19 20:03:57 +00:00
|
|
|
arg := structs.RegisterRequest{
|
2015-09-15 12:22:08 +00:00
|
|
|
Datacenter: "dc2", // Should forward through s1
|
2014-01-08 21:39:40 +00:00
|
|
|
Node: "foo",
|
|
|
|
Address: "127.0.0.1",
|
|
|
|
Service: &structs.NodeService{
|
|
|
|
Service: "db",
|
2014-04-03 19:03:10 +00:00
|
|
|
Tags: []string{"master"},
|
2014-01-08 21:39:40 +00:00
|
|
|
Port: 8000,
|
|
|
|
},
|
2013-12-12 00:33:19 +00:00
|
|
|
}
|
|
|
|
var out struct{}
|
2015-10-13 23:43:52 +00:00
|
|
|
if err := msgpackrpc.CallWithCodec(codec, "Catalog.Register", &arg, &out); err != nil {
|
2013-12-12 00:33:19 +00:00
|
|
|
t.Fatalf("err: %v", err)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2018-03-09 06:13:35 +00:00
|
|
|
func TestCatalog_Register_ConnectProxy(t *testing.T) {
|
|
|
|
t.Parallel()
|
|
|
|
|
|
|
|
assert := assert.New(t)
|
|
|
|
dir1, s1 := testServer(t)
|
|
|
|
defer os.RemoveAll(dir1)
|
|
|
|
defer s1.Shutdown()
|
|
|
|
codec := rpcClient(t, s1)
|
|
|
|
defer codec.Close()
|
|
|
|
|
|
|
|
args := structs.TestRegisterRequestProxy(t)
|
|
|
|
|
|
|
|
// Register
|
|
|
|
var out struct{}
|
|
|
|
assert.Nil(msgpackrpc.CallWithCodec(codec, "Catalog.Register", &args, &out))
|
|
|
|
|
|
|
|
// List
|
|
|
|
req := structs.ServiceSpecificRequest{
|
|
|
|
Datacenter: "dc1",
|
|
|
|
ServiceName: args.Service.Service,
|
|
|
|
}
|
|
|
|
var resp structs.IndexedServiceNodes
|
|
|
|
assert.Nil(msgpackrpc.CallWithCodec(codec, "Catalog.ServiceNodes", &req, &resp))
|
|
|
|
assert.Len(resp.ServiceNodes, 1)
|
|
|
|
v := resp.ServiceNodes[0]
|
|
|
|
assert.Equal(structs.ServiceKindConnectProxy, v.ServiceKind)
|
2018-09-12 16:07:47 +00:00
|
|
|
assert.Equal(args.Service.Proxy.DestinationServiceName, v.ServiceProxy.DestinationServiceName)
|
|
|
|
}
|
|
|
|
|
2018-03-09 06:13:35 +00:00
|
|
|
// Test an invalid ConnectProxy. We don't need to exhaustively test because
|
|
|
|
// this is all tested in structs on the Validate method.
|
|
|
|
func TestCatalog_Register_ConnectProxy_invalid(t *testing.T) {
|
|
|
|
t.Parallel()
|
|
|
|
|
|
|
|
assert := assert.New(t)
|
|
|
|
dir1, s1 := testServer(t)
|
|
|
|
defer os.RemoveAll(dir1)
|
|
|
|
defer s1.Shutdown()
|
|
|
|
codec := rpcClient(t, s1)
|
|
|
|
defer codec.Close()
|
|
|
|
|
|
|
|
args := structs.TestRegisterRequestProxy(t)
|
2018-09-12 16:07:47 +00:00
|
|
|
args.Service.Proxy.DestinationServiceName = ""
|
2018-03-09 06:13:35 +00:00
|
|
|
|
|
|
|
// Register
|
|
|
|
var out struct{}
|
|
|
|
err := msgpackrpc.CallWithCodec(codec, "Catalog.Register", &args, &out)
|
|
|
|
assert.NotNil(err)
|
2018-09-12 16:07:47 +00:00
|
|
|
assert.Contains(err.Error(), "DestinationServiceName")
|
2018-03-09 06:13:35 +00:00
|
|
|
}
|
|
|
|
|
2018-03-09 06:31:44 +00:00
|
|
|
// Test that write is required for the proxy destination to register a proxy.
|
2019-08-09 19:19:30 +00:00
|
|
|
func TestCatalog_Register_ConnectProxy_ACLDestinationServiceName(t *testing.T) {
|
2018-03-09 06:31:44 +00:00
|
|
|
t.Parallel()
|
|
|
|
|
|
|
|
assert := assert.New(t)
|
|
|
|
dir1, s1 := testServerWithConfig(t, func(c *Config) {
|
|
|
|
c.ACLDatacenter = "dc1"
|
2018-10-19 16:04:07 +00:00
|
|
|
c.ACLsEnabled = true
|
2018-03-09 06:31:44 +00:00
|
|
|
c.ACLMasterToken = "root"
|
|
|
|
c.ACLDefaultPolicy = "deny"
|
|
|
|
})
|
|
|
|
defer os.RemoveAll(dir1)
|
|
|
|
defer s1.Shutdown()
|
|
|
|
codec := rpcClient(t, s1)
|
|
|
|
defer codec.Close()
|
|
|
|
|
|
|
|
testrpc.WaitForLeader(t, s1.RPC, "dc1")
|
|
|
|
|
|
|
|
// Create the ACL.
|
|
|
|
arg := structs.ACLRequest{
|
|
|
|
Datacenter: "dc1",
|
|
|
|
Op: structs.ACLSet,
|
|
|
|
ACL: structs.ACL{
|
|
|
|
Name: "User token",
|
2018-10-19 16:04:07 +00:00
|
|
|
Type: structs.ACLTokenTypeClient,
|
2018-03-09 06:31:44 +00:00
|
|
|
Rules: `
|
|
|
|
service "foo" {
|
|
|
|
policy = "write"
|
|
|
|
}
|
2020-05-29 21:16:03 +00:00
|
|
|
node "foo" {
|
|
|
|
policy = "write"
|
|
|
|
}
|
2018-03-09 06:31:44 +00:00
|
|
|
`,
|
|
|
|
},
|
|
|
|
WriteRequest: structs.WriteRequest{Token: "root"},
|
|
|
|
}
|
|
|
|
var token string
|
|
|
|
assert.Nil(msgpackrpc.CallWithCodec(codec, "ACL.Apply", &arg, &token))
|
|
|
|
|
|
|
|
// Register should fail because we don't have permission on the destination
|
|
|
|
args := structs.TestRegisterRequestProxy(t)
|
|
|
|
args.Service.Service = "foo"
|
2018-09-12 16:07:47 +00:00
|
|
|
args.Service.Proxy.DestinationServiceName = "bar"
|
2018-03-09 06:31:44 +00:00
|
|
|
args.WriteRequest.Token = token
|
|
|
|
var out struct{}
|
|
|
|
err := msgpackrpc.CallWithCodec(codec, "Catalog.Register", &args, &out)
|
|
|
|
assert.True(acl.IsErrPermissionDenied(err))
|
|
|
|
|
|
|
|
// Register should fail with the right destination but wrong name
|
|
|
|
args = structs.TestRegisterRequestProxy(t)
|
|
|
|
args.Service.Service = "bar"
|
2018-09-12 16:07:47 +00:00
|
|
|
args.Service.Proxy.DestinationServiceName = "foo"
|
2018-03-09 06:31:44 +00:00
|
|
|
args.WriteRequest.Token = token
|
|
|
|
err = msgpackrpc.CallWithCodec(codec, "Catalog.Register", &args, &out)
|
|
|
|
assert.True(acl.IsErrPermissionDenied(err))
|
|
|
|
|
|
|
|
// Register should work with the right destination
|
|
|
|
args = structs.TestRegisterRequestProxy(t)
|
|
|
|
args.Service.Service = "foo"
|
2018-09-12 16:07:47 +00:00
|
|
|
args.Service.Proxy.DestinationServiceName = "foo"
|
2018-03-09 06:31:44 +00:00
|
|
|
args.WriteRequest.Token = token
|
|
|
|
assert.Nil(msgpackrpc.CallWithCodec(codec, "Catalog.Register", &args, &out))
|
|
|
|
}
|
|
|
|
|
2018-06-04 05:14:01 +00:00
|
|
|
func TestCatalog_Register_ConnectNative(t *testing.T) {
|
|
|
|
t.Parallel()
|
|
|
|
|
|
|
|
assert := assert.New(t)
|
|
|
|
dir1, s1 := testServer(t)
|
|
|
|
defer os.RemoveAll(dir1)
|
|
|
|
defer s1.Shutdown()
|
|
|
|
codec := rpcClient(t, s1)
|
|
|
|
defer codec.Close()
|
|
|
|
|
|
|
|
args := structs.TestRegisterRequest(t)
|
2018-06-05 17:51:05 +00:00
|
|
|
args.Service.Connect.Native = true
|
2018-06-04 05:14:01 +00:00
|
|
|
|
|
|
|
// Register
|
|
|
|
var out struct{}
|
|
|
|
assert.Nil(msgpackrpc.CallWithCodec(codec, "Catalog.Register", &args, &out))
|
|
|
|
|
|
|
|
// List
|
|
|
|
req := structs.ServiceSpecificRequest{
|
|
|
|
Datacenter: "dc1",
|
|
|
|
ServiceName: args.Service.Service,
|
|
|
|
}
|
|
|
|
var resp structs.IndexedServiceNodes
|
|
|
|
assert.Nil(msgpackrpc.CallWithCodec(codec, "Catalog.ServiceNodes", &req, &resp))
|
|
|
|
assert.Len(resp.ServiceNodes, 1)
|
|
|
|
v := resp.ServiceNodes[0]
|
|
|
|
assert.Equal(structs.ServiceKindTypical, v.ServiceKind)
|
2018-06-05 17:51:05 +00:00
|
|
|
assert.True(v.ServiceConnect.Native)
|
2018-06-04 05:14:01 +00:00
|
|
|
}
|
|
|
|
|
2016-12-11 00:00:11 +00:00
|
|
|
func TestCatalog_Deregister(t *testing.T) {
|
2017-06-27 13:22:18 +00:00
|
|
|
t.Parallel()
|
2013-12-11 23:34:10 +00:00
|
|
|
dir1, s1 := testServer(t)
|
|
|
|
defer os.RemoveAll(dir1)
|
|
|
|
defer s1.Shutdown()
|
2015-10-13 23:43:52 +00:00
|
|
|
codec := rpcClient(t, s1)
|
|
|
|
defer codec.Close()
|
2013-12-11 23:34:10 +00:00
|
|
|
|
2013-12-19 20:03:57 +00:00
|
|
|
arg := structs.DeregisterRequest{
|
2013-12-11 23:34:10 +00:00
|
|
|
Datacenter: "dc1",
|
|
|
|
Node: "foo",
|
|
|
|
}
|
|
|
|
var out struct{}
|
|
|
|
|
2015-10-13 23:43:52 +00:00
|
|
|
err := msgpackrpc.CallWithCodec(codec, "Catalog.Deregister", &arg, &out)
|
2016-07-10 17:24:18 +00:00
|
|
|
if err != nil {
|
2013-12-11 23:34:10 +00:00
|
|
|
t.Fatalf("err: %v", err)
|
|
|
|
}
|
|
|
|
|
2017-04-19 23:00:11 +00:00
|
|
|
testrpc.WaitForLeader(t, s1.RPC, "dc1")
|
2013-12-11 23:34:10 +00:00
|
|
|
|
2015-10-13 23:43:52 +00:00
|
|
|
if err := msgpackrpc.CallWithCodec(codec, "Catalog.Deregister", &arg, &out); err != nil {
|
2013-12-11 23:34:10 +00:00
|
|
|
t.Fatalf("err: %v", err)
|
|
|
|
}
|
|
|
|
}
|
2013-12-12 18:35:50 +00:00
|
|
|
|
2016-12-11 00:00:11 +00:00
|
|
|
func TestCatalog_Deregister_ACLDeny(t *testing.T) {
|
2017-06-27 13:22:18 +00:00
|
|
|
t.Parallel()
|
2016-12-10 03:15:44 +00:00
|
|
|
dir1, s1 := testServerWithConfig(t, func(c *Config) {
|
|
|
|
c.ACLDatacenter = "dc1"
|
2018-10-19 16:04:07 +00:00
|
|
|
c.ACLsEnabled = true
|
2016-12-10 03:15:44 +00:00
|
|
|
c.ACLMasterToken = "root"
|
|
|
|
c.ACLDefaultPolicy = "deny"
|
|
|
|
})
|
|
|
|
defer os.RemoveAll(dir1)
|
|
|
|
defer s1.Shutdown()
|
|
|
|
codec := rpcClient(t, s1)
|
|
|
|
defer codec.Close()
|
|
|
|
|
2017-04-19 23:00:11 +00:00
|
|
|
testrpc.WaitForLeader(t, s1.RPC, "dc1")
|
2016-12-10 03:15:44 +00:00
|
|
|
|
|
|
|
// Create the ACL.
|
|
|
|
arg := structs.ACLRequest{
|
|
|
|
Datacenter: "dc1",
|
|
|
|
Op: structs.ACLSet,
|
|
|
|
ACL: structs.ACL{
|
|
|
|
Name: "User token",
|
2018-10-19 16:04:07 +00:00
|
|
|
Type: structs.ACLTokenTypeClient,
|
2016-12-10 03:15:44 +00:00
|
|
|
Rules: `
|
|
|
|
node "node" {
|
|
|
|
policy = "write"
|
|
|
|
}
|
|
|
|
|
|
|
|
service "service" {
|
|
|
|
policy = "write"
|
|
|
|
}
|
|
|
|
`,
|
|
|
|
},
|
|
|
|
WriteRequest: structs.WriteRequest{Token: "root"},
|
|
|
|
}
|
|
|
|
var id string
|
|
|
|
if err := msgpackrpc.CallWithCodec(codec, "ACL.Apply", &arg, &id); err != nil {
|
|
|
|
t.Fatalf("err: %v", err)
|
|
|
|
}
|
|
|
|
|
|
|
|
// Register a node, node check, service, and service check.
|
|
|
|
argR := structs.RegisterRequest{
|
|
|
|
Datacenter: "dc1",
|
|
|
|
Node: "node",
|
|
|
|
Address: "127.0.0.1",
|
|
|
|
Service: &structs.NodeService{
|
|
|
|
Service: "service",
|
|
|
|
Port: 8000,
|
|
|
|
},
|
|
|
|
Checks: structs.HealthChecks{
|
|
|
|
&structs.HealthCheck{
|
|
|
|
Node: "node",
|
|
|
|
CheckID: "node-check",
|
|
|
|
},
|
|
|
|
&structs.HealthCheck{
|
|
|
|
Node: "node",
|
|
|
|
CheckID: "service-check",
|
|
|
|
ServiceID: "service",
|
|
|
|
},
|
|
|
|
},
|
|
|
|
WriteRequest: structs.WriteRequest{Token: id},
|
|
|
|
}
|
|
|
|
var outR struct{}
|
|
|
|
if err := msgpackrpc.CallWithCodec(codec, "Catalog.Register", &argR, &outR); err != nil {
|
|
|
|
t.Fatalf("err: %v", err)
|
|
|
|
}
|
|
|
|
|
2020-05-29 21:16:03 +00:00
|
|
|
// We should be not be able to deregister everything without a token.
|
2016-12-10 03:15:44 +00:00
|
|
|
var err error
|
|
|
|
var out struct{}
|
|
|
|
err = msgpackrpc.CallWithCodec(codec, "Catalog.Deregister",
|
|
|
|
&structs.DeregisterRequest{
|
|
|
|
Datacenter: "dc1",
|
|
|
|
Node: "node",
|
|
|
|
CheckID: "service-check"}, &out)
|
2017-08-23 14:52:48 +00:00
|
|
|
if !acl.IsErrPermissionDenied(err) {
|
2016-12-10 03:15:44 +00:00
|
|
|
t.Fatalf("err: %v", err)
|
|
|
|
}
|
|
|
|
err = msgpackrpc.CallWithCodec(codec, "Catalog.Deregister",
|
|
|
|
&structs.DeregisterRequest{
|
|
|
|
Datacenter: "dc1",
|
|
|
|
Node: "node",
|
|
|
|
CheckID: "node-check"}, &out)
|
2017-08-23 14:52:48 +00:00
|
|
|
if !acl.IsErrPermissionDenied(err) {
|
2016-12-10 03:15:44 +00:00
|
|
|
t.Fatalf("err: %v", err)
|
|
|
|
}
|
|
|
|
err = msgpackrpc.CallWithCodec(codec, "Catalog.Deregister",
|
|
|
|
&structs.DeregisterRequest{
|
|
|
|
Datacenter: "dc1",
|
|
|
|
Node: "node",
|
|
|
|
ServiceID: "service"}, &out)
|
2017-08-23 14:52:48 +00:00
|
|
|
if !acl.IsErrPermissionDenied(err) {
|
2016-12-10 03:15:44 +00:00
|
|
|
t.Fatalf("err: %v", err)
|
|
|
|
}
|
|
|
|
err = msgpackrpc.CallWithCodec(codec, "Catalog.Deregister",
|
|
|
|
&structs.DeregisterRequest{
|
|
|
|
Datacenter: "dc1",
|
|
|
|
Node: "node"}, &out)
|
2017-08-23 14:52:48 +00:00
|
|
|
if !acl.IsErrPermissionDenied(err) {
|
2016-12-10 03:15:44 +00:00
|
|
|
t.Fatalf("err: %v", err)
|
|
|
|
}
|
|
|
|
|
2020-05-29 21:16:03 +00:00
|
|
|
// Second pass these should all go through with the token set.
|
2016-12-10 03:15:44 +00:00
|
|
|
err = msgpackrpc.CallWithCodec(codec, "Catalog.Deregister",
|
|
|
|
&structs.DeregisterRequest{
|
|
|
|
Datacenter: "dc1",
|
|
|
|
Node: "node",
|
|
|
|
CheckID: "service-check",
|
|
|
|
WriteRequest: structs.WriteRequest{
|
|
|
|
Token: id,
|
|
|
|
}}, &out)
|
|
|
|
if err != nil {
|
|
|
|
t.Fatalf("err: %v", err)
|
|
|
|
}
|
|
|
|
err = msgpackrpc.CallWithCodec(codec, "Catalog.Deregister",
|
|
|
|
&structs.DeregisterRequest{
|
|
|
|
Datacenter: "dc1",
|
|
|
|
Node: "node",
|
|
|
|
CheckID: "node-check",
|
|
|
|
WriteRequest: structs.WriteRequest{
|
|
|
|
Token: id,
|
|
|
|
}}, &out)
|
|
|
|
if err != nil {
|
|
|
|
t.Fatalf("err: %v", err)
|
|
|
|
}
|
|
|
|
err = msgpackrpc.CallWithCodec(codec, "Catalog.Deregister",
|
|
|
|
&structs.DeregisterRequest{
|
|
|
|
Datacenter: "dc1",
|
|
|
|
Node: "node",
|
|
|
|
ServiceID: "service",
|
|
|
|
WriteRequest: structs.WriteRequest{
|
|
|
|
Token: id,
|
|
|
|
}}, &out)
|
|
|
|
if err != nil {
|
|
|
|
t.Fatalf("err: %v", err)
|
|
|
|
}
|
|
|
|
err = msgpackrpc.CallWithCodec(codec, "Catalog.Deregister",
|
|
|
|
&structs.DeregisterRequest{
|
|
|
|
Datacenter: "dc1",
|
|
|
|
Node: "node",
|
|
|
|
WriteRequest: structs.WriteRequest{
|
|
|
|
Token: id,
|
|
|
|
}}, &out)
|
|
|
|
if err != nil {
|
|
|
|
t.Fatalf("err: %v", err)
|
|
|
|
}
|
|
|
|
|
|
|
|
// Try a few error cases.
|
|
|
|
err = msgpackrpc.CallWithCodec(codec, "Catalog.Deregister",
|
|
|
|
&structs.DeregisterRequest{
|
|
|
|
Datacenter: "dc1",
|
2019-06-27 12:24:35 +00:00
|
|
|
Node: "nope",
|
2016-12-10 03:15:44 +00:00
|
|
|
ServiceID: "nope",
|
|
|
|
WriteRequest: structs.WriteRequest{
|
|
|
|
Token: id,
|
|
|
|
}}, &out)
|
|
|
|
if err == nil || !strings.Contains(err.Error(), "Unknown service") {
|
|
|
|
t.Fatalf("err: %v", err)
|
|
|
|
}
|
|
|
|
err = msgpackrpc.CallWithCodec(codec, "Catalog.Deregister",
|
|
|
|
&structs.DeregisterRequest{
|
|
|
|
Datacenter: "dc1",
|
2019-06-27 12:24:35 +00:00
|
|
|
Node: "nope",
|
2016-12-10 03:15:44 +00:00
|
|
|
CheckID: "nope",
|
|
|
|
WriteRequest: structs.WriteRequest{
|
|
|
|
Token: id,
|
|
|
|
}}, &out)
|
|
|
|
if err == nil || !strings.Contains(err.Error(), "Unknown check") {
|
|
|
|
t.Fatalf("err: %v", err)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2016-12-11 00:00:11 +00:00
|
|
|
func TestCatalog_ListDatacenters(t *testing.T) {
|
2017-06-27 13:22:18 +00:00
|
|
|
t.Parallel()
|
2013-12-12 18:35:50 +00:00
|
|
|
dir1, s1 := testServer(t)
|
|
|
|
defer os.RemoveAll(dir1)
|
|
|
|
defer s1.Shutdown()
|
2015-10-13 23:43:52 +00:00
|
|
|
codec := rpcClient(t, s1)
|
|
|
|
defer codec.Close()
|
2013-12-12 18:35:50 +00:00
|
|
|
|
|
|
|
dir2, s2 := testServerDC(t, "dc2")
|
|
|
|
defer os.RemoveAll(dir2)
|
|
|
|
defer s2.Shutdown()
|
|
|
|
|
|
|
|
// Try to join
|
2017-05-05 10:29:49 +00:00
|
|
|
joinWAN(t, s2, s1)
|
2014-05-08 23:18:35 +00:00
|
|
|
|
2017-04-19 23:00:11 +00:00
|
|
|
testrpc.WaitForLeader(t, s1.RPC, "dc1")
|
2013-12-12 18:35:50 +00:00
|
|
|
|
|
|
|
var out []string
|
2015-10-13 23:43:52 +00:00
|
|
|
if err := msgpackrpc.CallWithCodec(codec, "Catalog.ListDatacenters", struct{}{}, &out); err != nil {
|
2013-12-12 18:35:50 +00:00
|
|
|
t.Fatalf("err: %v", err)
|
|
|
|
}
|
|
|
|
|
2015-07-02 22:36:59 +00:00
|
|
|
// The DCs should come out sorted by default.
|
2013-12-12 18:35:50 +00:00
|
|
|
if len(out) != 2 {
|
|
|
|
t.Fatalf("bad: %v", out)
|
|
|
|
}
|
|
|
|
if out[0] != "dc1" {
|
|
|
|
t.Fatalf("bad: %v", out)
|
|
|
|
}
|
|
|
|
if out[1] != "dc2" {
|
|
|
|
t.Fatalf("bad: %v", out)
|
|
|
|
}
|
|
|
|
}
|
2013-12-12 18:48:36 +00:00
|
|
|
|
2016-12-11 00:00:11 +00:00
|
|
|
func TestCatalog_ListDatacenters_DistanceSort(t *testing.T) {
|
2017-06-27 13:22:18 +00:00
|
|
|
t.Parallel()
|
2015-07-02 22:36:59 +00:00
|
|
|
dir1, s1 := testServer(t)
|
|
|
|
defer os.RemoveAll(dir1)
|
|
|
|
defer s1.Shutdown()
|
2015-10-15 23:07:16 +00:00
|
|
|
codec := rpcClient(t, s1)
|
|
|
|
defer codec.Close()
|
2015-07-02 22:36:59 +00:00
|
|
|
|
|
|
|
dir2, s2 := testServerDC(t, "dc2")
|
|
|
|
defer os.RemoveAll(dir2)
|
|
|
|
defer s2.Shutdown()
|
|
|
|
|
|
|
|
dir3, s3 := testServerDC(t, "acdc")
|
|
|
|
defer os.RemoveAll(dir3)
|
|
|
|
defer s3.Shutdown()
|
|
|
|
|
|
|
|
// Try to join
|
2017-05-05 10:29:49 +00:00
|
|
|
joinWAN(t, s2, s1)
|
|
|
|
joinWAN(t, s3, s1)
|
2017-04-19 23:00:11 +00:00
|
|
|
testrpc.WaitForLeader(t, s1.RPC, "dc1")
|
2015-07-02 22:36:59 +00:00
|
|
|
|
|
|
|
var out []string
|
2015-10-15 23:07:16 +00:00
|
|
|
if err := msgpackrpc.CallWithCodec(codec, "Catalog.ListDatacenters", struct{}{}, &out); err != nil {
|
2015-07-02 22:36:59 +00:00
|
|
|
t.Fatalf("err: %v", err)
|
|
|
|
}
|
|
|
|
|
|
|
|
// It's super hard to force the Serfs into a known configuration of
|
|
|
|
// coordinates, so the best we can do is make sure that the sorting
|
|
|
|
// function is getting called (it's tested extensively in rtt_test.go).
|
|
|
|
// Since this is relative to dc1, it will be listed first (proving we
|
2016-03-22 00:30:05 +00:00
|
|
|
// went into the sort fn).
|
2015-07-02 22:36:59 +00:00
|
|
|
if len(out) != 3 {
|
|
|
|
t.Fatalf("bad: %v", out)
|
|
|
|
}
|
|
|
|
if out[0] != "dc1" {
|
|
|
|
t.Fatalf("bad: %v", out)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2016-12-11 00:00:11 +00:00
|
|
|
func TestCatalog_ListNodes(t *testing.T) {
|
2017-06-27 13:22:18 +00:00
|
|
|
t.Parallel()
|
2013-12-12 18:48:36 +00:00
|
|
|
dir1, s1 := testServer(t)
|
|
|
|
defer os.RemoveAll(dir1)
|
|
|
|
defer s1.Shutdown()
|
2015-10-13 23:43:52 +00:00
|
|
|
codec := rpcClient(t, s1)
|
|
|
|
defer codec.Close()
|
2013-12-12 18:48:36 +00:00
|
|
|
|
2014-02-05 19:00:43 +00:00
|
|
|
args := structs.DCSpecificRequest{
|
|
|
|
Datacenter: "dc1",
|
|
|
|
}
|
|
|
|
var out structs.IndexedNodes
|
2015-10-13 23:43:52 +00:00
|
|
|
err := msgpackrpc.CallWithCodec(codec, "Catalog.ListNodes", &args, &out)
|
2016-07-10 17:24:18 +00:00
|
|
|
if err != nil {
|
2013-12-12 18:48:36 +00:00
|
|
|
t.Fatalf("err: %v", err)
|
|
|
|
}
|
|
|
|
|
2017-04-19 23:00:11 +00:00
|
|
|
testrpc.WaitForLeader(t, s1.RPC, "dc1")
|
2013-12-12 18:48:36 +00:00
|
|
|
|
|
|
|
// Just add a node
|
2015-10-13 05:21:39 +00:00
|
|
|
if err := s1.fsm.State().EnsureNode(1, &structs.Node{Node: "foo", Address: "127.0.0.1"}); err != nil {
|
2015-10-12 07:42:09 +00:00
|
|
|
t.Fatalf("err: %v", err)
|
|
|
|
}
|
2017-05-04 22:52:53 +00:00
|
|
|
retry.Run(t, func(r *retry.R) {
|
2015-10-13 23:43:52 +00:00
|
|
|
msgpackrpc.CallWithCodec(codec, "Catalog.ListNodes", &args, &out)
|
2017-04-29 16:34:02 +00:00
|
|
|
if got, want := len(out.Nodes), 2; got != want {
|
|
|
|
r.Fatalf("got %d nodes want %d", got, want)
|
|
|
|
}
|
|
|
|
})
|
2014-01-10 01:22:01 +00:00
|
|
|
|
|
|
|
// Server node is auto added from Serf
|
2014-07-23 08:34:03 +00:00
|
|
|
if out.Nodes[1].Node != s1.config.NodeName {
|
2013-12-12 18:48:36 +00:00
|
|
|
t.Fatalf("bad: %v", out)
|
|
|
|
}
|
2014-07-23 08:34:03 +00:00
|
|
|
if out.Nodes[0].Node != "foo" {
|
2013-12-12 18:48:36 +00:00
|
|
|
t.Fatalf("bad: %v", out)
|
|
|
|
}
|
2014-07-23 08:34:03 +00:00
|
|
|
if out.Nodes[0].Address != "127.0.0.1" {
|
2013-12-12 18:48:36 +00:00
|
|
|
t.Fatalf("bad: %v", out)
|
|
|
|
}
|
2020-06-30 18:11:43 +00:00
|
|
|
require.False(t, out.QueryMeta.NotModified)
|
|
|
|
|
|
|
|
t.Run("with option AllowNotModifiedResponse", func(t *testing.T) {
|
|
|
|
args.QueryOptions = structs.QueryOptions{
|
|
|
|
MinQueryIndex: out.QueryMeta.Index,
|
|
|
|
MaxQueryTime: 20 * time.Millisecond,
|
|
|
|
AllowNotModifiedResponse: true,
|
|
|
|
}
|
|
|
|
err := msgpackrpc.CallWithCodec(codec, "Catalog.ListNodes", &args, &out)
|
|
|
|
require.NoError(t, err)
|
|
|
|
|
|
|
|
require.Equal(t, out.Index, out.QueryMeta.Index)
|
|
|
|
require.Len(t, out.Nodes, 0)
|
|
|
|
require.True(t, out.QueryMeta.NotModified, "NotModified should be true")
|
|
|
|
})
|
2013-12-12 18:48:36 +00:00
|
|
|
}
|
2013-12-12 19:07:14 +00:00
|
|
|
|
2017-01-14 01:08:43 +00:00
|
|
|
func TestCatalog_ListNodes_NodeMetaFilter(t *testing.T) {
|
2017-06-27 13:22:18 +00:00
|
|
|
t.Parallel()
|
2017-01-06 01:21:56 +00:00
|
|
|
dir1, s1 := testServer(t)
|
|
|
|
defer os.RemoveAll(dir1)
|
|
|
|
defer s1.Shutdown()
|
|
|
|
codec := rpcClient(t, s1)
|
|
|
|
defer codec.Close()
|
|
|
|
|
2017-04-19 23:00:11 +00:00
|
|
|
testrpc.WaitForLeader(t, s1.RPC, "dc1")
|
2017-01-06 01:21:56 +00:00
|
|
|
|
|
|
|
// Add a new node with the right meta k/v pair
|
|
|
|
node := &structs.Node{Node: "foo", Address: "127.0.0.1", Meta: map[string]string{"somekey": "somevalue"}}
|
|
|
|
if err := s1.fsm.State().EnsureNode(1, node); err != nil {
|
|
|
|
t.Fatalf("err: %v", err)
|
|
|
|
}
|
|
|
|
|
2017-01-11 19:41:12 +00:00
|
|
|
// Filter by a specific meta k/v pair
|
|
|
|
args := structs.DCSpecificRequest{
|
|
|
|
Datacenter: "dc1",
|
|
|
|
NodeMetaFilters: map[string]string{
|
|
|
|
"somekey": "somevalue",
|
|
|
|
},
|
|
|
|
}
|
|
|
|
var out structs.IndexedNodes
|
2017-05-04 22:52:53 +00:00
|
|
|
retry.Run(t, func(r *retry.R) {
|
2017-01-06 01:21:56 +00:00
|
|
|
msgpackrpc.CallWithCodec(codec, "Catalog.ListNodes", &args, &out)
|
2017-04-29 16:34:02 +00:00
|
|
|
if got, want := len(out.Nodes), 1; got != want {
|
|
|
|
r.Fatalf("got %d nodes want %d", got, want)
|
|
|
|
}
|
|
|
|
})
|
2017-01-06 01:21:56 +00:00
|
|
|
|
|
|
|
// Verify that only the correct node was returned
|
|
|
|
if out.Nodes[0].Node != "foo" {
|
|
|
|
t.Fatalf("bad: %v", out)
|
|
|
|
}
|
|
|
|
if out.Nodes[0].Address != "127.0.0.1" {
|
|
|
|
t.Fatalf("bad: %v", out)
|
|
|
|
}
|
|
|
|
if v, ok := out.Nodes[0].Meta["somekey"]; !ok || v != "somevalue" {
|
|
|
|
t.Fatalf("bad: %v", out)
|
|
|
|
}
|
|
|
|
|
|
|
|
// Now filter on a nonexistent meta k/v pair
|
|
|
|
args = structs.DCSpecificRequest{
|
2017-01-11 19:41:12 +00:00
|
|
|
Datacenter: "dc1",
|
|
|
|
NodeMetaFilters: map[string]string{
|
|
|
|
"somekey": "invalid",
|
|
|
|
},
|
2017-01-06 01:21:56 +00:00
|
|
|
}
|
|
|
|
out = structs.IndexedNodes{}
|
2017-01-11 19:41:12 +00:00
|
|
|
err := msgpackrpc.CallWithCodec(codec, "Catalog.ListNodes", &args, &out)
|
2017-01-06 01:21:56 +00:00
|
|
|
if err != nil {
|
|
|
|
t.Fatalf("err: %v", err)
|
|
|
|
}
|
2017-05-05 10:14:43 +00:00
|
|
|
// Should get an empty list of nodes back
|
|
|
|
retry.Run(t, func(r *retry.R) {
|
|
|
|
msgpackrpc.CallWithCodec(codec, "Catalog.ListNodes", &args, &out)
|
|
|
|
if len(out.Nodes) != 0 {
|
|
|
|
r.Fatal(nil)
|
|
|
|
}
|
|
|
|
})
|
2017-01-06 01:21:56 +00:00
|
|
|
}
|
|
|
|
|
2019-04-16 16:00:15 +00:00
|
|
|
func TestCatalog_RPC_Filter(t *testing.T) {
|
|
|
|
t.Parallel()
|
|
|
|
dir1, s1 := testServer(t)
|
|
|
|
defer os.RemoveAll(dir1)
|
|
|
|
defer s1.Shutdown()
|
|
|
|
codec := rpcClient(t, s1)
|
|
|
|
defer codec.Close()
|
|
|
|
|
|
|
|
testrpc.WaitForLeader(t, s1.RPC, "dc1")
|
|
|
|
|
|
|
|
// prep the cluster with some data we can use in our filters
|
|
|
|
registerTestCatalogEntries(t, codec)
|
|
|
|
|
|
|
|
// Run the tests against the test server
|
|
|
|
|
|
|
|
t.Run("ListNodes", func(t *testing.T) {
|
|
|
|
args := structs.DCSpecificRequest{
|
|
|
|
Datacenter: "dc1",
|
|
|
|
QueryOptions: structs.QueryOptions{Filter: "Meta.os == linux"},
|
|
|
|
}
|
|
|
|
|
|
|
|
out := new(structs.IndexedNodes)
|
|
|
|
require.NoError(t, msgpackrpc.CallWithCodec(codec, "Catalog.ListNodes", &args, out))
|
|
|
|
require.Len(t, out.Nodes, 2)
|
|
|
|
require.Condition(t, func() bool {
|
|
|
|
return (out.Nodes[0].Node == "foo" && out.Nodes[1].Node == "baz") ||
|
|
|
|
(out.Nodes[0].Node == "baz" && out.Nodes[1].Node == "foo")
|
|
|
|
})
|
|
|
|
|
|
|
|
args.Filter = "Meta.os == linux and Meta.env == qa"
|
|
|
|
out = new(structs.IndexedNodes)
|
|
|
|
require.NoError(t, msgpackrpc.CallWithCodec(codec, "Catalog.ListNodes", &args, out))
|
|
|
|
require.Len(t, out.Nodes, 1)
|
|
|
|
require.Equal(t, "baz", out.Nodes[0].Node)
|
|
|
|
})
|
|
|
|
|
|
|
|
t.Run("ServiceNodes", func(t *testing.T) {
|
|
|
|
args := structs.ServiceSpecificRequest{
|
|
|
|
Datacenter: "dc1",
|
|
|
|
ServiceName: "redis",
|
|
|
|
QueryOptions: structs.QueryOptions{Filter: "ServiceMeta.version == 1"},
|
|
|
|
}
|
|
|
|
|
|
|
|
out := new(structs.IndexedServiceNodes)
|
|
|
|
require.NoError(t, msgpackrpc.CallWithCodec(codec, "Catalog.ServiceNodes", &args, &out))
|
|
|
|
require.Len(t, out.ServiceNodes, 2)
|
|
|
|
require.Condition(t, func() bool {
|
|
|
|
return (out.ServiceNodes[0].Node == "foo" && out.ServiceNodes[1].Node == "bar") ||
|
|
|
|
(out.ServiceNodes[0].Node == "bar" && out.ServiceNodes[1].Node == "foo")
|
|
|
|
})
|
|
|
|
|
|
|
|
args.Filter = "ServiceMeta.version == 2"
|
|
|
|
out = new(structs.IndexedServiceNodes)
|
|
|
|
require.NoError(t, msgpackrpc.CallWithCodec(codec, "Catalog.ServiceNodes", &args, &out))
|
|
|
|
require.Len(t, out.ServiceNodes, 1)
|
|
|
|
require.Equal(t, "foo", out.ServiceNodes[0].Node)
|
|
|
|
})
|
|
|
|
|
|
|
|
t.Run("NodeServices", func(t *testing.T) {
|
|
|
|
args := structs.NodeSpecificRequest{
|
|
|
|
Datacenter: "dc1",
|
|
|
|
Node: "baz",
|
|
|
|
QueryOptions: structs.QueryOptions{Filter: "Service == web"},
|
|
|
|
}
|
|
|
|
|
|
|
|
out := new(structs.IndexedNodeServices)
|
|
|
|
require.NoError(t, msgpackrpc.CallWithCodec(codec, "Catalog.NodeServices", &args, &out))
|
|
|
|
require.Len(t, out.NodeServices.Services, 2)
|
|
|
|
|
|
|
|
args.Filter = "Service == web and Meta.version == 2"
|
|
|
|
out = new(structs.IndexedNodeServices)
|
|
|
|
require.NoError(t, msgpackrpc.CallWithCodec(codec, "Catalog.NodeServices", &args, &out))
|
|
|
|
require.Len(t, out.NodeServices.Services, 1)
|
|
|
|
})
|
|
|
|
}
|
|
|
|
|
2017-06-26 12:22:09 +00:00
|
|
|
func TestCatalog_ListNodes_StaleRead(t *testing.T) {
|
2017-06-27 13:22:18 +00:00
|
|
|
t.Parallel()
|
2014-04-19 00:26:59 +00:00
|
|
|
dir1, s1 := testServer(t)
|
|
|
|
defer os.RemoveAll(dir1)
|
|
|
|
defer s1.Shutdown()
|
2015-10-13 23:43:52 +00:00
|
|
|
codec1 := rpcClient(t, s1)
|
|
|
|
defer codec1.Close()
|
2019-01-07 21:53:54 +00:00
|
|
|
testrpc.WaitForTestAgent(t, s1.RPC, "dc1")
|
2014-04-19 00:26:59 +00:00
|
|
|
|
2014-04-19 00:48:50 +00:00
|
|
|
dir2, s2 := testServerDCBootstrap(t, "dc1", false)
|
2014-04-19 00:26:59 +00:00
|
|
|
defer os.RemoveAll(dir2)
|
|
|
|
defer s2.Shutdown()
|
2015-10-13 23:43:52 +00:00
|
|
|
codec2 := rpcClient(t, s2)
|
|
|
|
defer codec2.Close()
|
2014-04-19 00:26:59 +00:00
|
|
|
|
|
|
|
// Try to join
|
2017-05-05 10:29:49 +00:00
|
|
|
joinLAN(t, s2, s1)
|
2014-04-19 00:26:59 +00:00
|
|
|
|
Added SOA configuration for DNS settings. (#4714)
This will allow to fine TUNE SOA settings sent by Consul in DNS responses,
for instance to be able to control negative ttl.
Will fix: https://github.com/hashicorp/consul/issues/4713
# Example
Override all settings:
* min_ttl: 0 => 60s
* retry: 600 (10m) => 300s (5 minutes),
* expire: 86400 (24h) => 43200 (12h)
* refresh: 3600 (1h) => 1800 (30 minutes)
```
consul agent -dev -hcl 'dns_config={soa={min_ttl=60,retry=300,expire=43200,refresh=1800}}'
```
Result:
```
dig +multiline @localhost -p 8600 service.consul
; <<>> DiG 9.12.1 <<>> +multiline @localhost -p 8600 service.consul
; (2 servers found)
;; global options: +cmd
;; Got answer:
;; ->>HEADER<<- opcode: QUERY, status: NXDOMAIN, id: 36557
;; flags: qr aa rd; QUERY: 1, ANSWER: 0, AUTHORITY: 1, ADDITIONAL: 1
;; WARNING: recursion requested but not available
;; OPT PSEUDOSECTION:
; EDNS: version: 0, flags:; udp: 4096
;; QUESTION SECTION:
;service.consul. IN A
;; AUTHORITY SECTION:
consul. 0 IN SOA ns.consul. hostmaster.consul. (
1537959133 ; serial
1800 ; refresh (30 minutes)
300 ; retry (5 minutes)
43200 ; expire (12 hours)
60 ; minimum (1 minute)
)
;; Query time: 4 msec
;; SERVER: 127.0.0.1#8600(127.0.0.1)
;; WHEN: Wed Sep 26 12:52:13 CEST 2018
;; MSG SIZE rcvd: 93
```
2018-10-10 19:50:56 +00:00
|
|
|
testrpc.WaitForTestAgent(t, s1.RPC, "dc1")
|
|
|
|
testrpc.WaitForTestAgent(t, s2.RPC, "dc1")
|
2014-05-07 21:47:16 +00:00
|
|
|
|
2014-04-19 00:26:59 +00:00
|
|
|
// Use the follower as the client
|
2015-10-13 23:43:52 +00:00
|
|
|
var codec rpc.ClientCodec
|
2014-04-19 00:26:59 +00:00
|
|
|
if !s1.IsLeader() {
|
2015-10-13 23:43:52 +00:00
|
|
|
codec = codec1
|
2014-04-19 00:26:59 +00:00
|
|
|
|
|
|
|
// Inject fake data on the follower!
|
2015-10-13 05:21:39 +00:00
|
|
|
if err := s1.fsm.State().EnsureNode(1, &structs.Node{Node: "foo", Address: "127.0.0.1"}); err != nil {
|
2015-10-12 07:42:09 +00:00
|
|
|
t.Fatalf("err: %v", err)
|
|
|
|
}
|
2014-04-19 00:26:59 +00:00
|
|
|
} else {
|
2015-10-13 23:43:52 +00:00
|
|
|
codec = codec2
|
2014-04-19 00:26:59 +00:00
|
|
|
|
|
|
|
// Inject fake data on the follower!
|
2015-10-13 05:21:39 +00:00
|
|
|
if err := s2.fsm.State().EnsureNode(1, &structs.Node{Node: "foo", Address: "127.0.0.1"}); err != nil {
|
2015-10-12 07:42:09 +00:00
|
|
|
t.Fatalf("err: %v", err)
|
|
|
|
}
|
2014-04-19 00:26:59 +00:00
|
|
|
}
|
|
|
|
|
2014-05-07 21:51:15 +00:00
|
|
|
args := structs.DCSpecificRequest{
|
|
|
|
Datacenter: "dc1",
|
|
|
|
QueryOptions: structs.QueryOptions{AllowStale: true},
|
|
|
|
}
|
2014-04-19 00:26:59 +00:00
|
|
|
var out structs.IndexedNodes
|
|
|
|
|
2019-07-12 15:52:26 +00:00
|
|
|
retry.Run(t, func(r *retry.R) {
|
|
|
|
if err := msgpackrpc.CallWithCodec(codec, "Catalog.ListNodes", &args, &out); err != nil {
|
2019-07-16 20:47:45 +00:00
|
|
|
r.Fatalf("err: %v", err)
|
2014-04-19 00:48:50 +00:00
|
|
|
}
|
|
|
|
|
2019-07-12 15:52:26 +00:00
|
|
|
found := false
|
|
|
|
for _, n := range out.Nodes {
|
|
|
|
if n.Node == "foo" {
|
|
|
|
found = true
|
|
|
|
}
|
|
|
|
}
|
|
|
|
if !found {
|
|
|
|
r.Fatalf("failed to find foo in %#v", out.Nodes)
|
|
|
|
}
|
|
|
|
if out.QueryMeta.LastContact == 0 {
|
|
|
|
r.Fatalf("should have a last contact time")
|
|
|
|
}
|
|
|
|
if !out.QueryMeta.KnownLeader {
|
|
|
|
r.Fatalf("should have known leader")
|
|
|
|
}
|
|
|
|
})
|
2014-04-21 19:08:00 +00:00
|
|
|
}
|
|
|
|
|
2016-12-11 00:00:11 +00:00
|
|
|
func TestCatalog_ListNodes_ConsistentRead_Fail(t *testing.T) {
|
2017-06-27 13:22:18 +00:00
|
|
|
t.Parallel()
|
2014-04-21 19:08:00 +00:00
|
|
|
dir1, s1 := testServer(t)
|
|
|
|
defer os.RemoveAll(dir1)
|
|
|
|
defer s1.Shutdown()
|
|
|
|
|
|
|
|
dir2, s2 := testServerDCBootstrap(t, "dc1", false)
|
|
|
|
defer os.RemoveAll(dir2)
|
|
|
|
defer s2.Shutdown()
|
|
|
|
|
2017-09-25 22:27:04 +00:00
|
|
|
dir3, s3 := testServerDCBootstrap(t, "dc1", false)
|
|
|
|
defer os.RemoveAll(dir3)
|
|
|
|
defer s3.Shutdown()
|
2014-04-21 19:08:00 +00:00
|
|
|
|
2017-09-25 22:27:04 +00:00
|
|
|
// Try to join and wait for all servers to get promoted to voters.
|
|
|
|
joinLAN(t, s2, s1)
|
|
|
|
joinLAN(t, s3, s2)
|
|
|
|
servers := []*Server{s1, s2, s3}
|
|
|
|
retry.Run(t, func(r *retry.R) {
|
|
|
|
r.Check(wantRaft(servers))
|
|
|
|
for _, s := range servers {
|
|
|
|
r.Check(wantPeers(s, 3))
|
|
|
|
}
|
|
|
|
})
|
2014-04-21 19:08:00 +00:00
|
|
|
|
2017-09-25 22:27:04 +00:00
|
|
|
// Use the leader as the client, kill the followers.
|
2015-10-13 23:43:52 +00:00
|
|
|
var codec rpc.ClientCodec
|
2017-09-25 22:27:04 +00:00
|
|
|
for _, s := range servers {
|
|
|
|
if s.IsLeader() {
|
|
|
|
codec = rpcClient(t, s)
|
|
|
|
defer codec.Close()
|
|
|
|
} else {
|
|
|
|
s.Shutdown()
|
|
|
|
}
|
|
|
|
}
|
|
|
|
if codec == nil {
|
|
|
|
t.Fatalf("no leader")
|
2014-04-21 19:08:00 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
args := structs.DCSpecificRequest{
|
|
|
|
Datacenter: "dc1",
|
|
|
|
QueryOptions: structs.QueryOptions{RequireConsistent: true},
|
|
|
|
}
|
|
|
|
var out structs.IndexedNodes
|
2017-09-14 04:22:53 +00:00
|
|
|
err := msgpackrpc.CallWithCodec(codec, "Catalog.ListNodes", &args, &out)
|
|
|
|
if err == nil || !strings.HasPrefix(err.Error(), "leadership lost") {
|
2014-04-21 19:08:00 +00:00
|
|
|
t.Fatalf("err: %v", err)
|
|
|
|
}
|
|
|
|
if out.QueryMeta.LastContact != 0 {
|
|
|
|
t.Fatalf("should not have a last contact time")
|
|
|
|
}
|
|
|
|
if out.QueryMeta.KnownLeader {
|
|
|
|
t.Fatalf("should have no known leader")
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2016-12-11 00:00:11 +00:00
|
|
|
func TestCatalog_ListNodes_ConsistentRead(t *testing.T) {
|
2017-06-27 13:22:18 +00:00
|
|
|
t.Parallel()
|
2014-04-21 19:08:00 +00:00
|
|
|
dir1, s1 := testServer(t)
|
|
|
|
defer os.RemoveAll(dir1)
|
|
|
|
defer s1.Shutdown()
|
2015-10-13 23:43:52 +00:00
|
|
|
codec1 := rpcClient(t, s1)
|
|
|
|
defer codec1.Close()
|
2014-04-21 19:08:00 +00:00
|
|
|
|
|
|
|
dir2, s2 := testServerDCBootstrap(t, "dc1", false)
|
|
|
|
defer os.RemoveAll(dir2)
|
|
|
|
defer s2.Shutdown()
|
2015-10-13 23:43:52 +00:00
|
|
|
codec2 := rpcClient(t, s2)
|
|
|
|
defer codec2.Close()
|
2014-04-21 19:08:00 +00:00
|
|
|
|
|
|
|
// Try to join
|
2017-05-05 10:29:49 +00:00
|
|
|
joinLAN(t, s2, s1)
|
2014-04-21 19:08:00 +00:00
|
|
|
|
2017-04-19 23:00:11 +00:00
|
|
|
testrpc.WaitForLeader(t, s1.RPC, "dc1")
|
|
|
|
testrpc.WaitForLeader(t, s2.RPC, "dc1")
|
2014-04-21 19:08:00 +00:00
|
|
|
|
|
|
|
// Use the leader as the client, kill the follower
|
2015-10-13 23:43:52 +00:00
|
|
|
var codec rpc.ClientCodec
|
2014-04-21 19:08:00 +00:00
|
|
|
if s1.IsLeader() {
|
2015-10-13 23:43:52 +00:00
|
|
|
codec = codec1
|
2014-04-21 19:08:00 +00:00
|
|
|
} else {
|
2015-10-13 23:43:52 +00:00
|
|
|
codec = codec2
|
2014-04-21 19:08:00 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
args := structs.DCSpecificRequest{
|
|
|
|
Datacenter: "dc1",
|
|
|
|
QueryOptions: structs.QueryOptions{RequireConsistent: true},
|
|
|
|
}
|
|
|
|
var out structs.IndexedNodes
|
2015-10-13 23:43:52 +00:00
|
|
|
if err := msgpackrpc.CallWithCodec(codec, "Catalog.ListNodes", &args, &out); err != nil {
|
2014-04-21 19:08:00 +00:00
|
|
|
t.Fatalf("err: %v", err)
|
|
|
|
}
|
|
|
|
|
|
|
|
if out.QueryMeta.LastContact != 0 {
|
|
|
|
t.Fatalf("should not have a last contact time")
|
|
|
|
}
|
|
|
|
if !out.QueryMeta.KnownLeader {
|
|
|
|
t.Fatalf("should have known leader")
|
2014-04-19 00:26:59 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2016-12-11 00:00:11 +00:00
|
|
|
func TestCatalog_ListNodes_DistanceSort(t *testing.T) {
|
2017-06-27 13:22:18 +00:00
|
|
|
t.Parallel()
|
2015-06-30 21:25:40 +00:00
|
|
|
dir1, s1 := testServer(t)
|
|
|
|
defer os.RemoveAll(dir1)
|
|
|
|
defer s1.Shutdown()
|
2015-10-15 23:07:16 +00:00
|
|
|
codec := rpcClient(t, s1)
|
|
|
|
defer codec.Close()
|
2015-06-30 21:25:40 +00:00
|
|
|
|
2017-04-19 23:00:11 +00:00
|
|
|
testrpc.WaitForLeader(t, s1.RPC, "dc1")
|
2015-10-23 22:19:14 +00:00
|
|
|
if err := s1.fsm.State().EnsureNode(1, &structs.Node{Node: "aaa", Address: "127.0.0.1"}); err != nil {
|
2015-06-30 21:25:40 +00:00
|
|
|
t.Fatalf("err: %v", err)
|
|
|
|
}
|
2015-10-23 22:19:14 +00:00
|
|
|
if err := s1.fsm.State().EnsureNode(2, &structs.Node{Node: "foo", Address: "127.0.0.2"}); err != nil {
|
2015-06-30 21:25:40 +00:00
|
|
|
t.Fatalf("err: %v", err)
|
|
|
|
}
|
2015-10-23 22:19:14 +00:00
|
|
|
if err := s1.fsm.State().EnsureNode(3, &structs.Node{Node: "bar", Address: "127.0.0.3"}); err != nil {
|
2015-06-30 21:25:40 +00:00
|
|
|
t.Fatalf("err: %v", err)
|
|
|
|
}
|
2015-10-23 22:19:14 +00:00
|
|
|
if err := s1.fsm.State().EnsureNode(4, &structs.Node{Node: "baz", Address: "127.0.0.4"}); err != nil {
|
2015-06-30 21:25:40 +00:00
|
|
|
t.Fatalf("err: %v", err)
|
|
|
|
}
|
|
|
|
|
|
|
|
// Set all but one of the nodes to known coordinates.
|
2015-10-23 22:19:14 +00:00
|
|
|
updates := structs.Coordinates{
|
2017-08-14 14:36:07 +00:00
|
|
|
{Node: "foo", Coord: lib.GenerateCoordinate(2 * time.Millisecond)},
|
|
|
|
{Node: "bar", Coord: lib.GenerateCoordinate(5 * time.Millisecond)},
|
|
|
|
{Node: "baz", Coord: lib.GenerateCoordinate(1 * time.Millisecond)},
|
2015-06-30 21:25:40 +00:00
|
|
|
}
|
|
|
|
if err := s1.fsm.State().CoordinateBatchUpdate(5, updates); err != nil {
|
|
|
|
t.Fatalf("err: %v", err)
|
|
|
|
}
|
|
|
|
|
|
|
|
// Query with no given source node, should get the natural order from
|
|
|
|
// the index.
|
|
|
|
args := structs.DCSpecificRequest{
|
|
|
|
Datacenter: "dc1",
|
|
|
|
}
|
|
|
|
var out structs.IndexedNodes
|
2017-05-04 22:52:53 +00:00
|
|
|
retry.Run(t, func(r *retry.R) {
|
2015-10-15 23:07:16 +00:00
|
|
|
msgpackrpc.CallWithCodec(codec, "Catalog.ListNodes", &args, &out)
|
2017-04-29 16:34:02 +00:00
|
|
|
if got, want := len(out.Nodes), 5; got != want {
|
|
|
|
r.Fatalf("got %d nodes want %d", got, want)
|
|
|
|
}
|
|
|
|
})
|
|
|
|
|
2015-06-30 21:25:40 +00:00
|
|
|
if out.Nodes[0].Node != "aaa" {
|
|
|
|
t.Fatalf("bad: %v", out)
|
|
|
|
}
|
|
|
|
if out.Nodes[1].Node != "bar" {
|
|
|
|
t.Fatalf("bad: %v", out)
|
|
|
|
}
|
|
|
|
if out.Nodes[2].Node != "baz" {
|
|
|
|
t.Fatalf("bad: %v", out)
|
|
|
|
}
|
|
|
|
if out.Nodes[3].Node != "foo" {
|
|
|
|
t.Fatalf("bad: %v", out)
|
|
|
|
}
|
|
|
|
if out.Nodes[4].Node != s1.config.NodeName {
|
|
|
|
t.Fatalf("bad: %v", out)
|
|
|
|
}
|
|
|
|
|
|
|
|
// Query relative to foo, note that there's no known coordinate for the
|
|
|
|
// default-added Serf node nor "aaa" so they will go at the end.
|
|
|
|
args = structs.DCSpecificRequest{
|
|
|
|
Datacenter: "dc1",
|
|
|
|
Source: structs.QuerySource{Datacenter: "dc1", Node: "foo"},
|
|
|
|
}
|
2017-05-04 22:52:53 +00:00
|
|
|
retry.Run(t, func(r *retry.R) {
|
2015-10-15 23:07:16 +00:00
|
|
|
msgpackrpc.CallWithCodec(codec, "Catalog.ListNodes", &args, &out)
|
2017-04-29 16:34:02 +00:00
|
|
|
if got, want := len(out.Nodes), 5; got != want {
|
|
|
|
r.Fatalf("got %d nodes want %d", got, want)
|
|
|
|
}
|
|
|
|
})
|
|
|
|
|
2015-06-30 21:25:40 +00:00
|
|
|
if out.Nodes[0].Node != "foo" {
|
|
|
|
t.Fatalf("bad: %v", out)
|
|
|
|
}
|
|
|
|
if out.Nodes[1].Node != "baz" {
|
|
|
|
t.Fatalf("bad: %v", out)
|
|
|
|
}
|
|
|
|
if out.Nodes[2].Node != "bar" {
|
|
|
|
t.Fatalf("bad: %v", out)
|
|
|
|
}
|
|
|
|
if out.Nodes[3].Node != "aaa" {
|
|
|
|
t.Fatalf("bad: %v", out)
|
|
|
|
}
|
|
|
|
if out.Nodes[4].Node != s1.config.NodeName {
|
|
|
|
t.Fatalf("bad: %v", out)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2016-12-11 00:00:11 +00:00
|
|
|
func TestCatalog_ListNodes_ACLFilter(t *testing.T) {
|
2017-06-27 13:22:18 +00:00
|
|
|
t.Parallel()
|
2016-12-11 00:00:11 +00:00
|
|
|
dir1, s1 := testServerWithConfig(t, func(c *Config) {
|
|
|
|
c.ACLDatacenter = "dc1"
|
2018-10-19 16:04:07 +00:00
|
|
|
c.ACLsEnabled = true
|
2016-12-11 00:00:11 +00:00
|
|
|
c.ACLMasterToken = "root"
|
|
|
|
c.ACLDefaultPolicy = "deny"
|
|
|
|
})
|
|
|
|
defer os.RemoveAll(dir1)
|
|
|
|
defer s1.Shutdown()
|
|
|
|
codec := rpcClient(t, s1)
|
|
|
|
defer codec.Close()
|
|
|
|
|
2017-04-19 23:00:11 +00:00
|
|
|
testrpc.WaitForLeader(t, s1.RPC, "dc1")
|
2016-12-11 00:00:11 +00:00
|
|
|
|
|
|
|
// We scope the reply in each of these since msgpack won't clear out an
|
|
|
|
// existing slice if the incoming one is nil, so it's best to start
|
|
|
|
// clean each time.
|
|
|
|
|
2020-05-29 21:16:03 +00:00
|
|
|
// The node policy should not be ignored.
|
2016-12-11 00:00:11 +00:00
|
|
|
args := structs.DCSpecificRequest{
|
|
|
|
Datacenter: "dc1",
|
|
|
|
}
|
|
|
|
{
|
|
|
|
reply := structs.IndexedNodes{}
|
|
|
|
if err := msgpackrpc.CallWithCodec(codec, "Catalog.ListNodes", &args, &reply); err != nil {
|
|
|
|
t.Fatalf("err: %v", err)
|
|
|
|
}
|
|
|
|
if len(reply.Nodes) != 0 {
|
|
|
|
t.Fatalf("bad: %v", reply.Nodes)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
// Create an ACL that can read the node.
|
|
|
|
arg := structs.ACLRequest{
|
|
|
|
Datacenter: "dc1",
|
|
|
|
Op: structs.ACLSet,
|
|
|
|
ACL: structs.ACL{
|
|
|
|
Name: "User token",
|
2018-10-19 16:04:07 +00:00
|
|
|
Type: structs.ACLTokenTypeClient,
|
2016-12-11 00:00:11 +00:00
|
|
|
Rules: fmt.Sprintf(`
|
|
|
|
node "%s" {
|
|
|
|
policy = "read"
|
|
|
|
}
|
|
|
|
`, s1.config.NodeName),
|
|
|
|
},
|
|
|
|
WriteRequest: structs.WriteRequest{Token: "root"},
|
|
|
|
}
|
|
|
|
var id string
|
|
|
|
if err := msgpackrpc.CallWithCodec(codec, "ACL.Apply", &arg, &id); err != nil {
|
|
|
|
t.Fatalf("err: %v", err)
|
|
|
|
}
|
|
|
|
|
|
|
|
// Now try with the token and it will go through.
|
|
|
|
args.Token = id
|
|
|
|
{
|
|
|
|
reply := structs.IndexedNodes{}
|
|
|
|
if err := msgpackrpc.CallWithCodec(codec, "Catalog.ListNodes", &args, &reply); err != nil {
|
|
|
|
t.Fatalf("err: %v", err)
|
|
|
|
}
|
|
|
|
if len(reply.Nodes) != 1 {
|
|
|
|
t.Fatalf("bad: %v", reply.Nodes)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
func Benchmark_Catalog_ListNodes(t *testing.B) {
|
2014-02-01 20:16:33 +00:00
|
|
|
dir1, s1 := testServer(nil)
|
|
|
|
defer os.RemoveAll(dir1)
|
|
|
|
defer s1.Shutdown()
|
2015-10-13 23:43:52 +00:00
|
|
|
codec := rpcClient(nil, s1)
|
|
|
|
defer codec.Close()
|
2014-02-01 20:16:33 +00:00
|
|
|
|
|
|
|
// Just add a node
|
2015-10-13 05:21:39 +00:00
|
|
|
if err := s1.fsm.State().EnsureNode(1, &structs.Node{Node: "foo", Address: "127.0.0.1"}); err != nil {
|
2015-10-12 07:42:09 +00:00
|
|
|
t.Fatalf("err: %v", err)
|
|
|
|
}
|
2014-02-01 20:16:33 +00:00
|
|
|
|
2014-02-05 19:00:43 +00:00
|
|
|
args := structs.DCSpecificRequest{
|
|
|
|
Datacenter: "dc1",
|
|
|
|
}
|
2014-02-01 20:16:33 +00:00
|
|
|
for i := 0; i < t.N; i++ {
|
2014-02-05 19:00:43 +00:00
|
|
|
var out structs.IndexedNodes
|
2015-10-13 23:43:52 +00:00
|
|
|
if err := msgpackrpc.CallWithCodec(codec, "Catalog.ListNodes", &args, &out); err != nil {
|
2014-02-01 20:16:33 +00:00
|
|
|
t.Fatalf("err: %v", err)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2016-12-11 00:00:11 +00:00
|
|
|
func TestCatalog_ListServices(t *testing.T) {
|
2017-06-27 13:22:18 +00:00
|
|
|
t.Parallel()
|
2013-12-12 19:07:14 +00:00
|
|
|
dir1, s1 := testServer(t)
|
|
|
|
defer os.RemoveAll(dir1)
|
|
|
|
defer s1.Shutdown()
|
2015-10-13 23:43:52 +00:00
|
|
|
codec := rpcClient(t, s1)
|
|
|
|
defer codec.Close()
|
2013-12-12 19:07:14 +00:00
|
|
|
|
2014-02-05 19:00:43 +00:00
|
|
|
args := structs.DCSpecificRequest{
|
|
|
|
Datacenter: "dc1",
|
|
|
|
}
|
|
|
|
var out structs.IndexedServices
|
2015-10-13 23:43:52 +00:00
|
|
|
err := msgpackrpc.CallWithCodec(codec, "Catalog.ListServices", &args, &out)
|
2016-07-10 17:24:18 +00:00
|
|
|
if err != nil {
|
2013-12-12 19:07:14 +00:00
|
|
|
t.Fatalf("err: %v", err)
|
|
|
|
}
|
|
|
|
|
2017-04-19 23:00:11 +00:00
|
|
|
testrpc.WaitForLeader(t, s1.RPC, "dc1")
|
2013-12-12 19:07:14 +00:00
|
|
|
|
|
|
|
// Just add a node
|
2015-10-13 05:21:39 +00:00
|
|
|
if err := s1.fsm.State().EnsureNode(1, &structs.Node{Node: "foo", Address: "127.0.0.1"}); err != nil {
|
2015-10-12 07:42:09 +00:00
|
|
|
t.Fatalf("err: %v", err)
|
|
|
|
}
|
2015-10-13 05:21:39 +00:00
|
|
|
if err := s1.fsm.State().EnsureService(2, "foo", &structs.NodeService{ID: "db", Service: "db", Tags: []string{"primary"}, Address: "127.0.0.1", Port: 5000}); err != nil {
|
2015-10-12 07:42:09 +00:00
|
|
|
t.Fatalf("err: %v", err)
|
|
|
|
}
|
2013-12-12 19:07:14 +00:00
|
|
|
|
2015-10-13 23:43:52 +00:00
|
|
|
if err := msgpackrpc.CallWithCodec(codec, "Catalog.ListServices", &args, &out); err != nil {
|
2013-12-12 19:07:14 +00:00
|
|
|
t.Fatalf("err: %v", err)
|
|
|
|
}
|
|
|
|
|
2014-02-05 19:00:43 +00:00
|
|
|
if len(out.Services) != 2 {
|
2014-01-10 01:57:13 +00:00
|
|
|
t.Fatalf("bad: %v", out)
|
|
|
|
}
|
2014-05-13 07:18:36 +00:00
|
|
|
for _, s := range out.Services {
|
|
|
|
if s == nil {
|
|
|
|
t.Fatalf("bad: %v", s)
|
|
|
|
}
|
|
|
|
}
|
2014-01-10 01:57:13 +00:00
|
|
|
// Consul service should auto-register
|
2014-04-03 19:03:10 +00:00
|
|
|
if _, ok := out.Services["consul"]; !ok {
|
2014-02-05 19:00:43 +00:00
|
|
|
t.Fatalf("bad: %v", out)
|
|
|
|
}
|
|
|
|
if len(out.Services["db"]) != 1 {
|
|
|
|
t.Fatalf("bad: %v", out)
|
|
|
|
}
|
|
|
|
if out.Services["db"][0] != "primary" {
|
|
|
|
t.Fatalf("bad: %v", out)
|
|
|
|
}
|
2020-06-30 18:11:43 +00:00
|
|
|
require.False(t, out.QueryMeta.NotModified)
|
|
|
|
|
|
|
|
t.Run("with option AllowNotModifiedResponse", func(t *testing.T) {
|
|
|
|
args.QueryOptions = structs.QueryOptions{
|
|
|
|
MinQueryIndex: out.QueryMeta.Index,
|
|
|
|
MaxQueryTime: 20 * time.Millisecond,
|
|
|
|
AllowNotModifiedResponse: true,
|
|
|
|
}
|
|
|
|
err := msgpackrpc.CallWithCodec(codec, "Catalog.ListServices", &args, &out)
|
|
|
|
require.NoError(t, err)
|
|
|
|
|
|
|
|
require.Equal(t, out.Index, out.QueryMeta.Index)
|
|
|
|
require.Len(t, out.Services, 0)
|
|
|
|
require.True(t, out.QueryMeta.NotModified, "NotModified should be true")
|
|
|
|
})
|
2014-02-05 19:00:43 +00:00
|
|
|
}
|
|
|
|
|
2017-01-14 01:08:43 +00:00
|
|
|
func TestCatalog_ListServices_NodeMetaFilter(t *testing.T) {
|
2017-06-27 13:22:18 +00:00
|
|
|
t.Parallel()
|
2017-01-09 19:21:49 +00:00
|
|
|
dir1, s1 := testServer(t)
|
|
|
|
defer os.RemoveAll(dir1)
|
|
|
|
defer s1.Shutdown()
|
|
|
|
codec := rpcClient(t, s1)
|
|
|
|
defer codec.Close()
|
|
|
|
|
2018-10-19 08:53:19 +00:00
|
|
|
testrpc.WaitForTestAgent(t, s1.RPC, "dc1")
|
2017-01-09 19:21:49 +00:00
|
|
|
|
|
|
|
// Add a new node with the right meta k/v pair
|
|
|
|
node := &structs.Node{Node: "foo", Address: "127.0.0.1", Meta: map[string]string{"somekey": "somevalue"}}
|
|
|
|
if err := s1.fsm.State().EnsureNode(1, node); err != nil {
|
|
|
|
t.Fatalf("err: %v", err)
|
|
|
|
}
|
|
|
|
// Add a service to the new node
|
|
|
|
if err := s1.fsm.State().EnsureService(2, "foo", &structs.NodeService{ID: "db", Service: "db", Tags: []string{"primary"}, Address: "127.0.0.1", Port: 5000}); err != nil {
|
|
|
|
t.Fatalf("err: %v", err)
|
|
|
|
}
|
|
|
|
|
2017-01-11 19:41:12 +00:00
|
|
|
// Filter by a specific meta k/v pair
|
|
|
|
args := structs.DCSpecificRequest{
|
|
|
|
Datacenter: "dc1",
|
|
|
|
NodeMetaFilters: map[string]string{
|
|
|
|
"somekey": "somevalue",
|
|
|
|
},
|
|
|
|
}
|
|
|
|
var out structs.IndexedServices
|
2017-01-09 19:21:49 +00:00
|
|
|
if err := msgpackrpc.CallWithCodec(codec, "Catalog.ListServices", &args, &out); err != nil {
|
|
|
|
t.Fatalf("err: %v", err)
|
|
|
|
}
|
|
|
|
|
|
|
|
if len(out.Services) != 1 {
|
|
|
|
t.Fatalf("bad: %v", out)
|
|
|
|
}
|
|
|
|
if out.Services["db"] == nil {
|
|
|
|
t.Fatalf("bad: %v", out.Services["db"])
|
|
|
|
}
|
|
|
|
if len(out.Services["db"]) != 1 {
|
|
|
|
t.Fatalf("bad: %v", out)
|
|
|
|
}
|
|
|
|
if out.Services["db"][0] != "primary" {
|
|
|
|
t.Fatalf("bad: %v", out)
|
|
|
|
}
|
|
|
|
|
|
|
|
// Now filter on a nonexistent meta k/v pair
|
|
|
|
args = structs.DCSpecificRequest{
|
2017-01-11 19:41:12 +00:00
|
|
|
Datacenter: "dc1",
|
|
|
|
NodeMetaFilters: map[string]string{
|
|
|
|
"somekey": "invalid",
|
|
|
|
},
|
2017-01-09 19:21:49 +00:00
|
|
|
}
|
|
|
|
out = structs.IndexedServices{}
|
2017-01-11 19:41:12 +00:00
|
|
|
err := msgpackrpc.CallWithCodec(codec, "Catalog.ListServices", &args, &out)
|
2017-01-09 19:21:49 +00:00
|
|
|
if err != nil {
|
|
|
|
t.Fatalf("err: %v", err)
|
|
|
|
}
|
|
|
|
|
|
|
|
// Should get an empty list of nodes back
|
|
|
|
if len(out.Services) != 0 {
|
|
|
|
t.Fatalf("bad: %v", out.Services)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2016-12-11 00:00:11 +00:00
|
|
|
func TestCatalog_ListServices_Blocking(t *testing.T) {
|
2017-06-27 13:22:18 +00:00
|
|
|
t.Parallel()
|
2014-02-05 19:00:43 +00:00
|
|
|
dir1, s1 := testServer(t)
|
|
|
|
defer os.RemoveAll(dir1)
|
|
|
|
defer s1.Shutdown()
|
2015-10-13 23:43:52 +00:00
|
|
|
codec := rpcClient(t, s1)
|
|
|
|
defer codec.Close()
|
2014-02-05 19:00:43 +00:00
|
|
|
|
|
|
|
args := structs.DCSpecificRequest{
|
|
|
|
Datacenter: "dc1",
|
|
|
|
}
|
|
|
|
var out structs.IndexedServices
|
|
|
|
|
2017-04-19 23:00:11 +00:00
|
|
|
testrpc.WaitForLeader(t, s1.RPC, "dc1")
|
2014-02-05 19:00:43 +00:00
|
|
|
|
|
|
|
// Run the query
|
2015-10-13 23:43:52 +00:00
|
|
|
if err := msgpackrpc.CallWithCodec(codec, "Catalog.ListServices", &args, &out); err != nil {
|
2014-02-05 19:00:43 +00:00
|
|
|
t.Fatalf("err: %v", err)
|
|
|
|
}
|
|
|
|
|
|
|
|
// Setup a blocking query
|
|
|
|
args.MinQueryIndex = out.Index
|
|
|
|
args.MaxQueryTime = time.Second
|
|
|
|
|
|
|
|
// Async cause a change
|
2015-10-12 07:42:09 +00:00
|
|
|
idx := out.Index
|
2014-02-05 19:00:43 +00:00
|
|
|
start := time.Now()
|
|
|
|
go func() {
|
2014-05-09 00:36:25 +00:00
|
|
|
time.Sleep(100 * time.Millisecond)
|
2015-10-13 05:21:39 +00:00
|
|
|
if err := s1.fsm.State().EnsureNode(idx+1, &structs.Node{Node: "foo", Address: "127.0.0.1"}); err != nil {
|
2020-06-05 20:52:31 +00:00
|
|
|
t.Errorf("err: %v", err)
|
|
|
|
return
|
2015-10-12 07:42:09 +00:00
|
|
|
}
|
2015-10-13 05:21:39 +00:00
|
|
|
if err := s1.fsm.State().EnsureService(idx+2, "foo", &structs.NodeService{ID: "db", Service: "db", Tags: []string{"primary"}, Address: "127.0.0.1", Port: 5000}); err != nil {
|
2020-06-05 20:52:31 +00:00
|
|
|
t.Errorf("err: %v", err)
|
2015-10-12 07:42:09 +00:00
|
|
|
}
|
2014-02-05 19:00:43 +00:00
|
|
|
}()
|
|
|
|
|
|
|
|
// Re-run the query
|
|
|
|
out = structs.IndexedServices{}
|
2015-10-13 23:43:52 +00:00
|
|
|
if err := msgpackrpc.CallWithCodec(codec, "Catalog.ListServices", &args, &out); err != nil {
|
2014-02-05 19:00:43 +00:00
|
|
|
t.Fatalf("err: %v", err)
|
|
|
|
}
|
|
|
|
|
|
|
|
// Should block at least 100ms
|
2017-10-17 18:38:24 +00:00
|
|
|
if time.Since(start) < 100*time.Millisecond {
|
2014-02-05 19:00:43 +00:00
|
|
|
t.Fatalf("too fast")
|
|
|
|
}
|
|
|
|
|
|
|
|
// Check the indexes
|
2015-10-12 07:42:09 +00:00
|
|
|
if out.Index != idx+2 {
|
2013-12-12 19:07:14 +00:00
|
|
|
t.Fatalf("bad: %v", out)
|
|
|
|
}
|
2014-02-05 19:00:43 +00:00
|
|
|
|
|
|
|
// Should find the service
|
|
|
|
if len(out.Services) != 2 {
|
2013-12-12 19:07:14 +00:00
|
|
|
t.Fatalf("bad: %v", out)
|
|
|
|
}
|
2014-02-05 19:00:43 +00:00
|
|
|
}
|
|
|
|
|
2016-12-11 00:00:11 +00:00
|
|
|
func TestCatalog_ListServices_Timeout(t *testing.T) {
|
2017-06-27 13:22:18 +00:00
|
|
|
t.Parallel()
|
2014-02-05 19:00:43 +00:00
|
|
|
dir1, s1 := testServer(t)
|
|
|
|
defer os.RemoveAll(dir1)
|
|
|
|
defer s1.Shutdown()
|
2015-10-13 23:43:52 +00:00
|
|
|
codec := rpcClient(t, s1)
|
|
|
|
defer codec.Close()
|
2014-02-05 19:00:43 +00:00
|
|
|
|
|
|
|
args := structs.DCSpecificRequest{
|
|
|
|
Datacenter: "dc1",
|
|
|
|
}
|
|
|
|
var out structs.IndexedServices
|
|
|
|
|
2017-04-19 23:00:11 +00:00
|
|
|
testrpc.WaitForLeader(t, s1.RPC, "dc1")
|
2014-02-05 19:00:43 +00:00
|
|
|
|
|
|
|
// Run the query
|
2015-10-13 23:43:52 +00:00
|
|
|
if err := msgpackrpc.CallWithCodec(codec, "Catalog.ListServices", &args, &out); err != nil {
|
2014-02-05 19:00:43 +00:00
|
|
|
t.Fatalf("err: %v", err)
|
|
|
|
}
|
|
|
|
|
|
|
|
// Setup a blocking query
|
|
|
|
args.MinQueryIndex = out.Index
|
2014-05-09 00:36:25 +00:00
|
|
|
args.MaxQueryTime = 100 * time.Millisecond
|
2014-02-05 19:00:43 +00:00
|
|
|
|
|
|
|
// Re-run the query
|
|
|
|
start := time.Now()
|
|
|
|
out = structs.IndexedServices{}
|
2015-10-13 23:43:52 +00:00
|
|
|
if err := msgpackrpc.CallWithCodec(codec, "Catalog.ListServices", &args, &out); err != nil {
|
2014-02-05 19:00:43 +00:00
|
|
|
t.Fatalf("err: %v", err)
|
|
|
|
}
|
|
|
|
|
|
|
|
// Should block at least 100ms
|
2017-10-17 18:38:24 +00:00
|
|
|
if time.Since(start) < 100*time.Millisecond {
|
2014-02-05 19:00:43 +00:00
|
|
|
t.Fatalf("too fast")
|
|
|
|
}
|
|
|
|
|
|
|
|
// Check the indexes, should not change
|
|
|
|
if out.Index != args.MinQueryIndex {
|
2013-12-12 19:07:14 +00:00
|
|
|
t.Fatalf("bad: %v", out)
|
|
|
|
}
|
|
|
|
}
|
2013-12-12 19:37:19 +00:00
|
|
|
|
2016-12-11 00:00:11 +00:00
|
|
|
func TestCatalog_ListServices_Stale(t *testing.T) {
|
2017-06-27 13:22:18 +00:00
|
|
|
t.Parallel()
|
2018-08-23 16:06:39 +00:00
|
|
|
dir1, s1 := testServerWithConfig(t, func(c *Config) {
|
|
|
|
c.ACLDatacenter = "dc1"
|
2018-10-19 16:04:07 +00:00
|
|
|
c.ACLsEnabled = true
|
2018-08-23 16:06:39 +00:00
|
|
|
})
|
2014-04-21 18:57:39 +00:00
|
|
|
defer os.RemoveAll(dir1)
|
|
|
|
defer s1.Shutdown()
|
2018-08-23 16:06:39 +00:00
|
|
|
|
2018-09-10 15:58:53 +00:00
|
|
|
testrpc.WaitForTestAgent(t, s1.RPC, "dc1")
|
2018-08-23 16:06:39 +00:00
|
|
|
dir2, s2 := testServerWithConfig(t, func(c *Config) {
|
|
|
|
c.ACLDatacenter = "dc1" // Enable ACLs!
|
2018-10-19 16:04:07 +00:00
|
|
|
c.ACLsEnabled = true
|
|
|
|
c.Bootstrap = false // Disable bootstrap
|
2018-08-23 16:06:39 +00:00
|
|
|
})
|
|
|
|
defer os.RemoveAll(dir2)
|
|
|
|
defer s2.Shutdown()
|
2014-04-21 18:57:39 +00:00
|
|
|
|
|
|
|
args := structs.DCSpecificRequest{
|
|
|
|
Datacenter: "dc1",
|
|
|
|
}
|
|
|
|
args.AllowStale = true
|
|
|
|
var out structs.IndexedServices
|
|
|
|
|
2018-08-23 16:06:39 +00:00
|
|
|
// Inject a node
|
|
|
|
if err := s1.fsm.State().EnsureNode(3, &structs.Node{Node: "foo", Address: "127.0.0.1"}); err != nil {
|
2015-10-12 07:42:09 +00:00
|
|
|
t.Fatalf("err: %v", err)
|
|
|
|
}
|
2018-08-23 16:06:39 +00:00
|
|
|
|
|
|
|
codec := rpcClient(t, s2)
|
|
|
|
defer codec.Close()
|
|
|
|
|
|
|
|
// Run the query, do not wait for leader, never any contact with leader, should fail
|
|
|
|
if err := msgpackrpc.CallWithCodec(codec, "Catalog.ListServices", &args, &out); err == nil || err.Error() != structs.ErrNoLeader.Error() {
|
|
|
|
t.Fatalf("expected %v but got err: %v and %v", structs.ErrNoLeader, err, out)
|
|
|
|
}
|
|
|
|
|
|
|
|
// Try to join
|
|
|
|
joinLAN(t, s2, s1)
|
|
|
|
retry.Run(t, func(r *retry.R) { r.Check(wantRaft([]*Server{s1, s2})) })
|
|
|
|
waitForLeader(s1, s2)
|
|
|
|
|
|
|
|
testrpc.WaitForLeader(t, s2.RPC, "dc1")
|
2014-04-21 18:57:39 +00:00
|
|
|
|
2019-07-12 15:52:26 +00:00
|
|
|
retry.Run(t, func(r *retry.R) {
|
|
|
|
if err := msgpackrpc.CallWithCodec(codec, "Catalog.ListServices", &args, &out); err != nil {
|
|
|
|
r.Fatalf("err: %v", err)
|
|
|
|
}
|
|
|
|
// Should find the services
|
|
|
|
if len(out.Services) != 1 {
|
|
|
|
r.Fatalf("bad: %#v", out.Services)
|
|
|
|
}
|
|
|
|
if !out.KnownLeader {
|
|
|
|
r.Fatalf("should have a leader: %v", out)
|
|
|
|
}
|
|
|
|
})
|
2018-08-23 16:06:39 +00:00
|
|
|
|
|
|
|
s1.Leave()
|
|
|
|
s1.Shutdown()
|
|
|
|
|
|
|
|
testrpc.WaitUntilNoLeader(t, s2.RPC, "dc1")
|
|
|
|
|
|
|
|
args.AllowStale = false
|
|
|
|
// Since the leader is now down, non-stale query should fail now
|
|
|
|
if err := msgpackrpc.CallWithCodec(codec, "Catalog.ListServices", &args, &out); err == nil || err.Error() != structs.ErrNoLeader.Error() {
|
|
|
|
t.Fatalf("expected %v but got err: %v and %v", structs.ErrNoLeader, err, out)
|
|
|
|
}
|
|
|
|
|
|
|
|
// With stale, request should still work
|
|
|
|
args.AllowStale = true
|
2015-10-13 23:43:52 +00:00
|
|
|
if err := msgpackrpc.CallWithCodec(codec, "Catalog.ListServices", &args, &out); err != nil {
|
2014-04-21 18:57:39 +00:00
|
|
|
t.Fatalf("err: %v", err)
|
|
|
|
}
|
|
|
|
|
2018-08-23 16:06:39 +00:00
|
|
|
// Should find old service
|
2014-04-21 18:57:39 +00:00
|
|
|
if len(out.Services) != 1 {
|
2018-08-23 16:06:39 +00:00
|
|
|
t.Fatalf("bad: %#v", out)
|
2014-04-21 18:57:39 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
if out.KnownLeader {
|
2018-08-23 16:06:39 +00:00
|
|
|
t.Fatalf("should not have a leader anymore: %#v", out)
|
2014-04-21 18:57:39 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2016-12-11 00:00:11 +00:00
|
|
|
func TestCatalog_ListServiceNodes(t *testing.T) {
|
2017-06-27 13:22:18 +00:00
|
|
|
t.Parallel()
|
2013-12-12 19:37:19 +00:00
|
|
|
dir1, s1 := testServer(t)
|
|
|
|
defer os.RemoveAll(dir1)
|
|
|
|
defer s1.Shutdown()
|
2015-10-13 23:43:52 +00:00
|
|
|
codec := rpcClient(t, s1)
|
|
|
|
defer codec.Close()
|
2013-12-12 19:37:19 +00:00
|
|
|
|
2014-01-08 21:52:09 +00:00
|
|
|
args := structs.ServiceSpecificRequest{
|
2013-12-12 19:37:19 +00:00
|
|
|
Datacenter: "dc1",
|
|
|
|
ServiceName: "db",
|
2018-10-11 11:50:05 +00:00
|
|
|
ServiceTags: []string{"slave"},
|
2013-12-12 19:37:19 +00:00
|
|
|
TagFilter: false,
|
|
|
|
}
|
2014-02-05 19:10:10 +00:00
|
|
|
var out structs.IndexedServiceNodes
|
2015-10-13 23:43:52 +00:00
|
|
|
err := msgpackrpc.CallWithCodec(codec, "Catalog.ServiceNodes", &args, &out)
|
2016-07-10 17:24:18 +00:00
|
|
|
if err != nil {
|
2013-12-12 19:37:19 +00:00
|
|
|
t.Fatalf("err: %v", err)
|
|
|
|
}
|
|
|
|
|
2017-04-19 23:00:11 +00:00
|
|
|
testrpc.WaitForLeader(t, s1.RPC, "dc1")
|
2013-12-12 19:37:19 +00:00
|
|
|
|
|
|
|
// Just add a node
|
2015-10-13 05:21:39 +00:00
|
|
|
if err := s1.fsm.State().EnsureNode(1, &structs.Node{Node: "foo", Address: "127.0.0.1"}); err != nil {
|
2015-10-12 07:42:09 +00:00
|
|
|
t.Fatalf("err: %v", err)
|
|
|
|
}
|
2015-10-13 05:21:39 +00:00
|
|
|
if err := s1.fsm.State().EnsureService(2, "foo", &structs.NodeService{ID: "db", Service: "db", Tags: []string{"primary"}, Address: "127.0.0.1", Port: 5000}); err != nil {
|
2015-10-12 07:42:09 +00:00
|
|
|
t.Fatalf("err: %v", err)
|
|
|
|
}
|
2013-12-12 19:37:19 +00:00
|
|
|
|
2015-10-13 23:43:52 +00:00
|
|
|
if err := msgpackrpc.CallWithCodec(codec, "Catalog.ServiceNodes", &args, &out); err != nil {
|
2013-12-12 19:37:19 +00:00
|
|
|
t.Fatalf("err: %v", err)
|
|
|
|
}
|
|
|
|
|
2014-02-05 19:10:10 +00:00
|
|
|
if len(out.ServiceNodes) != 1 {
|
2013-12-12 19:37:19 +00:00
|
|
|
t.Fatalf("bad: %v", out)
|
|
|
|
}
|
|
|
|
|
|
|
|
// Try with a filter
|
|
|
|
args.TagFilter = true
|
2014-02-05 19:10:10 +00:00
|
|
|
out = structs.IndexedServiceNodes{}
|
2013-12-12 19:37:19 +00:00
|
|
|
|
2015-10-13 23:43:52 +00:00
|
|
|
if err := msgpackrpc.CallWithCodec(codec, "Catalog.ServiceNodes", &args, &out); err != nil {
|
2013-12-12 19:37:19 +00:00
|
|
|
t.Fatalf("err: %v", err)
|
|
|
|
}
|
2014-02-05 19:10:10 +00:00
|
|
|
if len(out.ServiceNodes) != 0 {
|
2013-12-12 19:37:19 +00:00
|
|
|
t.Fatalf("bad: %v", out)
|
|
|
|
}
|
|
|
|
}
|
2013-12-12 19:46:25 +00:00
|
|
|
|
2019-06-21 17:16:17 +00:00
|
|
|
func TestCatalog_ListServiceNodes_ByAddress(t *testing.T) {
|
|
|
|
t.Parallel()
|
|
|
|
dir1, s1 := testServer(t)
|
|
|
|
defer os.RemoveAll(dir1)
|
|
|
|
defer s1.Shutdown()
|
|
|
|
codec := rpcClient(t, s1)
|
|
|
|
defer codec.Close()
|
|
|
|
|
|
|
|
testrpc.WaitForLeader(t, s1.RPC, "dc1")
|
|
|
|
|
|
|
|
args := structs.ServiceSpecificRequest{
|
|
|
|
Datacenter: "dc1",
|
|
|
|
ServiceName: "db",
|
|
|
|
}
|
|
|
|
var out structs.IndexedServiceNodes
|
|
|
|
require.NoError(t, msgpackrpc.CallWithCodec(codec, "Catalog.ServiceNodes", &args, &out))
|
|
|
|
|
|
|
|
fooAddress := "10.1.2.3"
|
|
|
|
fooPort := 1111
|
|
|
|
fooTaggedAddresses := map[string]structs.ServiceAddress{
|
2020-06-16 17:19:31 +00:00
|
|
|
"lan": {
|
2019-06-21 17:16:17 +00:00
|
|
|
Address: "10.1.2.3",
|
|
|
|
Port: fooPort,
|
|
|
|
},
|
2020-06-16 17:19:31 +00:00
|
|
|
"wan": {
|
2019-06-21 17:16:17 +00:00
|
|
|
Address: "198.18.1.2",
|
|
|
|
Port: fooPort,
|
|
|
|
},
|
|
|
|
}
|
|
|
|
barAddress := "10.1.2.3"
|
|
|
|
barPort := 2222
|
|
|
|
barTaggedAddresses := map[string]structs.ServiceAddress{
|
2020-06-16 17:19:31 +00:00
|
|
|
"lan": {
|
2019-06-21 17:16:17 +00:00
|
|
|
Address: "10.1.2.3",
|
|
|
|
Port: barPort,
|
|
|
|
},
|
2020-06-16 17:19:31 +00:00
|
|
|
"wan": {
|
2019-06-21 17:16:17 +00:00
|
|
|
Address: "198.18.2.3",
|
|
|
|
Port: barPort,
|
|
|
|
},
|
|
|
|
}
|
|
|
|
bazAddress := "192.168.1.35"
|
|
|
|
bazPort := 2222
|
|
|
|
bazTaggedAddresses := map[string]structs.ServiceAddress{
|
2020-06-16 17:19:31 +00:00
|
|
|
"lan": {
|
2019-06-21 17:16:17 +00:00
|
|
|
Address: "192.168.1.35",
|
|
|
|
Port: barPort,
|
|
|
|
},
|
2020-06-16 17:19:31 +00:00
|
|
|
"wan": {
|
2019-06-21 17:16:17 +00:00
|
|
|
Address: "198.18.2.4",
|
|
|
|
Port: barPort,
|
|
|
|
},
|
|
|
|
}
|
|
|
|
|
|
|
|
// Just add a node
|
|
|
|
require.NoError(t, s1.fsm.State().EnsureNode(1, &structs.Node{Node: "node", Address: "127.0.0.1"}))
|
|
|
|
require.NoError(t, s1.fsm.State().EnsureService(2, "node", &structs.NodeService{ID: "foo", Service: "db", Address: fooAddress, TaggedAddresses: fooTaggedAddresses, Port: fooPort}))
|
|
|
|
require.NoError(t, s1.fsm.State().EnsureService(2, "node", &structs.NodeService{ID: "bar", Service: "db", Address: barAddress, TaggedAddresses: barTaggedAddresses, Port: barPort}))
|
|
|
|
require.NoError(t, s1.fsm.State().EnsureService(2, "node", &structs.NodeService{ID: "baz", Service: "db", Address: bazAddress, TaggedAddresses: bazTaggedAddresses, Port: bazPort}))
|
|
|
|
require.NoError(t, msgpackrpc.CallWithCodec(codec, "Catalog.ServiceNodes", &args, &out))
|
|
|
|
require.Len(t, out.ServiceNodes, 3)
|
|
|
|
|
|
|
|
// Try with an address that would match foo & bar
|
|
|
|
args.ServiceAddress = "10.1.2.3"
|
|
|
|
out = structs.IndexedServiceNodes{}
|
|
|
|
|
|
|
|
require.NoError(t, msgpackrpc.CallWithCodec(codec, "Catalog.ServiceNodes", &args, &out))
|
|
|
|
require.Len(t, out.ServiceNodes, 2)
|
|
|
|
for _, sn := range out.ServiceNodes {
|
|
|
|
require.True(t, sn.ServiceID == "foo" || sn.ServiceID == "bar")
|
|
|
|
}
|
|
|
|
|
|
|
|
// Try with an address that would match just bar
|
|
|
|
args.ServiceAddress = "198.18.2.3"
|
|
|
|
out = structs.IndexedServiceNodes{}
|
|
|
|
|
|
|
|
require.NoError(t, msgpackrpc.CallWithCodec(codec, "Catalog.ServiceNodes", &args, &out))
|
|
|
|
require.Len(t, out.ServiceNodes, 1)
|
|
|
|
require.Equal(t, "bar", out.ServiceNodes[0].ServiceID)
|
|
|
|
}
|
|
|
|
|
2018-11-13 14:44:36 +00:00
|
|
|
// TestCatalog_ListServiceNodes_ServiceTags_V1_2_3Compat asserts the compatibility between <=v1.2.3 agents and >=v1.3.0 servers
|
|
|
|
// see https://github.com/hashicorp/consul/issues/4922
|
|
|
|
func TestCatalog_ListServiceNodes_ServiceTags_V1_2_3Compat(t *testing.T) {
|
|
|
|
t.Parallel()
|
|
|
|
dir1, s1 := testServer(t)
|
|
|
|
defer os.RemoveAll(dir1)
|
|
|
|
defer s1.Shutdown()
|
|
|
|
codec := rpcClient(t, s1)
|
|
|
|
defer codec.Close()
|
|
|
|
|
|
|
|
testrpc.WaitForTestAgent(t, s1.RPC, "dc1")
|
|
|
|
|
|
|
|
err := s1.fsm.State().EnsureNode(1, &structs.Node{Node: "foo", Address: "127.0.0.1"})
|
|
|
|
require.NoError(t, err)
|
|
|
|
|
|
|
|
// register two service instances with different tags
|
|
|
|
err = s1.fsm.State().EnsureService(2, "foo", &structs.NodeService{ID: "db1", Service: "db", Tags: []string{"primary"}, Address: "127.0.0.1", Port: 5000})
|
|
|
|
require.NoError(t, err)
|
|
|
|
err = s1.fsm.State().EnsureService(2, "foo", &structs.NodeService{ID: "db2", Service: "db", Tags: []string{"secondary"}, Address: "127.0.0.1", Port: 5001})
|
|
|
|
require.NoError(t, err)
|
|
|
|
|
2019-01-07 21:30:47 +00:00
|
|
|
// DEPRECATED (singular-service-tag) - remove this when backwards RPC compat
|
|
|
|
// with 1.2.x is not required.
|
2018-11-13 14:44:36 +00:00
|
|
|
// make a request with the <=1.2.3 ServiceTag tag field (vs ServiceTags)
|
|
|
|
args := structs.ServiceSpecificRequest{
|
|
|
|
Datacenter: "dc1",
|
|
|
|
ServiceName: "db",
|
|
|
|
ServiceTag: "primary",
|
|
|
|
TagFilter: true,
|
|
|
|
}
|
|
|
|
var out structs.IndexedServiceNodes
|
|
|
|
err = msgpackrpc.CallWithCodec(codec, "Catalog.ServiceNodes", &args, &out)
|
|
|
|
require.NoError(t, err)
|
|
|
|
|
|
|
|
// nodes should be filtered, even when using the old ServiceTag field
|
|
|
|
require.Equal(t, 1, len(out.ServiceNodes))
|
|
|
|
require.Equal(t, "db1", out.ServiceNodes[0].ServiceID)
|
|
|
|
|
2019-01-07 21:30:47 +00:00
|
|
|
// DEPRECATED (singular-service-tag) - remove this when backwards RPC compat
|
|
|
|
// with 1.2.x is not required.
|
2018-11-13 14:44:36 +00:00
|
|
|
// test with the other tag
|
|
|
|
args = structs.ServiceSpecificRequest{
|
|
|
|
Datacenter: "dc1",
|
|
|
|
ServiceName: "db",
|
|
|
|
ServiceTag: "secondary",
|
|
|
|
TagFilter: true,
|
|
|
|
}
|
|
|
|
out = structs.IndexedServiceNodes{}
|
|
|
|
err = msgpackrpc.CallWithCodec(codec, "Catalog.ServiceNodes", &args, &out)
|
|
|
|
require.NoError(t, err)
|
|
|
|
require.Equal(t, 1, len(out.ServiceNodes))
|
|
|
|
require.Equal(t, "db2", out.ServiceNodes[0].ServiceID)
|
|
|
|
|
|
|
|
// no tag, both instances
|
|
|
|
args = structs.ServiceSpecificRequest{
|
|
|
|
Datacenter: "dc1",
|
|
|
|
ServiceName: "db",
|
|
|
|
}
|
|
|
|
out = structs.IndexedServiceNodes{}
|
|
|
|
err = msgpackrpc.CallWithCodec(codec, "Catalog.ServiceNodes", &args, &out)
|
|
|
|
require.NoError(t, err)
|
|
|
|
require.Equal(t, 2, len(out.ServiceNodes))
|
|
|
|
|
2019-01-07 21:30:47 +00:00
|
|
|
// DEPRECATED (singular-service-tag) - remove this when backwards RPC compat
|
|
|
|
// with 1.2.x is not required.
|
2018-11-13 14:44:36 +00:00
|
|
|
// when both ServiceTag and ServiceTags fields are populated, use ServiceTag
|
|
|
|
args = structs.ServiceSpecificRequest{
|
|
|
|
Datacenter: "dc1",
|
|
|
|
ServiceName: "db",
|
|
|
|
ServiceTag: "primary",
|
|
|
|
ServiceTags: []string{"secondary"},
|
|
|
|
TagFilter: true,
|
|
|
|
}
|
|
|
|
out = structs.IndexedServiceNodes{}
|
|
|
|
err = msgpackrpc.CallWithCodec(codec, "Catalog.ServiceNodes", &args, &out)
|
|
|
|
require.NoError(t, err)
|
|
|
|
require.Equal(t, "db1", out.ServiceNodes[0].ServiceID)
|
|
|
|
}
|
|
|
|
|
2017-01-14 01:08:43 +00:00
|
|
|
func TestCatalog_ListServiceNodes_NodeMetaFilter(t *testing.T) {
|
2017-06-27 13:22:18 +00:00
|
|
|
t.Parallel()
|
2017-01-14 01:08:43 +00:00
|
|
|
dir1, s1 := testServer(t)
|
|
|
|
defer os.RemoveAll(dir1)
|
|
|
|
defer s1.Shutdown()
|
|
|
|
codec := rpcClient(t, s1)
|
|
|
|
defer codec.Close()
|
|
|
|
|
2017-04-19 23:00:11 +00:00
|
|
|
testrpc.WaitForLeader(t, s1.RPC, "dc1")
|
2017-01-14 01:08:43 +00:00
|
|
|
|
|
|
|
// Add 2 nodes with specific meta maps
|
|
|
|
node := &structs.Node{Node: "foo", Address: "127.0.0.1", Meta: map[string]string{"somekey": "somevalue", "common": "1"}}
|
|
|
|
if err := s1.fsm.State().EnsureNode(1, node); err != nil {
|
|
|
|
t.Fatalf("err: %v", err)
|
|
|
|
}
|
|
|
|
node2 := &structs.Node{Node: "bar", Address: "127.0.0.2", Meta: map[string]string{"common": "1"}}
|
|
|
|
if err := s1.fsm.State().EnsureNode(2, node2); err != nil {
|
|
|
|
t.Fatalf("err: %v", err)
|
|
|
|
}
|
2018-10-11 11:50:05 +00:00
|
|
|
if err := s1.fsm.State().EnsureService(3, "foo", &structs.NodeService{ID: "db", Service: "db", Tags: []string{"primary", "v2"}, Address: "127.0.0.1", Port: 5000}); err != nil {
|
2017-01-14 01:08:43 +00:00
|
|
|
t.Fatalf("err: %v", err)
|
|
|
|
}
|
2018-10-11 11:50:05 +00:00
|
|
|
if err := s1.fsm.State().EnsureService(4, "bar", &structs.NodeService{ID: "db2", Service: "db", Tags: []string{"secondary", "v2"}, Address: "127.0.0.2", Port: 5000}); err != nil {
|
2017-01-14 01:08:43 +00:00
|
|
|
t.Fatalf("err: %v", err)
|
|
|
|
}
|
|
|
|
|
|
|
|
cases := []struct {
|
|
|
|
filters map[string]string
|
2018-10-11 11:50:05 +00:00
|
|
|
tags []string
|
2017-01-14 01:08:43 +00:00
|
|
|
services structs.ServiceNodes
|
|
|
|
}{
|
|
|
|
// Basic meta filter
|
|
|
|
{
|
|
|
|
filters: map[string]string{"somekey": "somevalue"},
|
|
|
|
services: structs.ServiceNodes{&structs.ServiceNode{Node: "foo", ServiceID: "db"}},
|
|
|
|
},
|
|
|
|
// Basic meta filter, tag
|
|
|
|
{
|
|
|
|
filters: map[string]string{"somekey": "somevalue"},
|
2018-10-11 11:50:05 +00:00
|
|
|
tags: []string{"primary"},
|
2017-01-14 01:08:43 +00:00
|
|
|
services: structs.ServiceNodes{&structs.ServiceNode{Node: "foo", ServiceID: "db"}},
|
|
|
|
},
|
|
|
|
// Common meta filter
|
|
|
|
{
|
|
|
|
filters: map[string]string{"common": "1"},
|
|
|
|
services: structs.ServiceNodes{
|
|
|
|
&structs.ServiceNode{Node: "bar", ServiceID: "db2"},
|
|
|
|
&structs.ServiceNode{Node: "foo", ServiceID: "db"},
|
|
|
|
},
|
|
|
|
},
|
|
|
|
// Common meta filter, tag
|
|
|
|
{
|
|
|
|
filters: map[string]string{"common": "1"},
|
2018-10-11 11:50:05 +00:00
|
|
|
tags: []string{"secondary"},
|
2017-01-14 01:08:43 +00:00
|
|
|
services: structs.ServiceNodes{
|
|
|
|
&structs.ServiceNode{Node: "bar", ServiceID: "db2"},
|
|
|
|
},
|
|
|
|
},
|
|
|
|
// Invalid meta filter
|
|
|
|
{
|
|
|
|
filters: map[string]string{"invalid": "nope"},
|
|
|
|
services: structs.ServiceNodes{},
|
|
|
|
},
|
|
|
|
// Multiple filter values
|
|
|
|
{
|
|
|
|
filters: map[string]string{"somekey": "somevalue", "common": "1"},
|
|
|
|
services: structs.ServiceNodes{&structs.ServiceNode{Node: "foo", ServiceID: "db"}},
|
|
|
|
},
|
|
|
|
// Multiple filter values, tag
|
|
|
|
{
|
|
|
|
filters: map[string]string{"somekey": "somevalue", "common": "1"},
|
2018-10-11 11:50:05 +00:00
|
|
|
tags: []string{"primary"},
|
|
|
|
services: structs.ServiceNodes{&structs.ServiceNode{Node: "foo", ServiceID: "db"}},
|
|
|
|
},
|
|
|
|
// Common meta filter, single tag
|
|
|
|
{
|
|
|
|
filters: map[string]string{"common": "1"},
|
|
|
|
tags: []string{"v2"},
|
|
|
|
services: structs.ServiceNodes{
|
|
|
|
&structs.ServiceNode{Node: "bar", ServiceID: "db2"},
|
|
|
|
&structs.ServiceNode{Node: "foo", ServiceID: "db"},
|
|
|
|
},
|
|
|
|
},
|
|
|
|
// Common meta filter, multiple tags
|
|
|
|
{
|
|
|
|
filters: map[string]string{"common": "1"},
|
|
|
|
tags: []string{"v2", "primary"},
|
2017-01-14 01:08:43 +00:00
|
|
|
services: structs.ServiceNodes{&structs.ServiceNode{Node: "foo", ServiceID: "db"}},
|
|
|
|
},
|
|
|
|
}
|
|
|
|
|
|
|
|
for _, tc := range cases {
|
|
|
|
args := structs.ServiceSpecificRequest{
|
|
|
|
Datacenter: "dc1",
|
|
|
|
NodeMetaFilters: tc.filters,
|
|
|
|
ServiceName: "db",
|
2018-10-11 11:50:05 +00:00
|
|
|
ServiceTags: tc.tags,
|
|
|
|
TagFilter: len(tc.tags) > 0,
|
2017-01-14 01:08:43 +00:00
|
|
|
}
|
|
|
|
var out structs.IndexedServiceNodes
|
2018-10-11 11:50:05 +00:00
|
|
|
require.NoError(t, msgpackrpc.CallWithCodec(codec, "Catalog.ServiceNodes", &args, &out))
|
|
|
|
require.Len(t, out.ServiceNodes, len(tc.services))
|
2017-01-14 01:08:43 +00:00
|
|
|
|
|
|
|
for i, serviceNode := range out.ServiceNodes {
|
|
|
|
if serviceNode.Node != tc.services[i].Node || serviceNode.ServiceID != tc.services[i].ServiceID {
|
|
|
|
t.Fatalf("bad: %v, %v filters: %v", serviceNode, tc.services[i], tc.filters)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2016-12-11 00:00:11 +00:00
|
|
|
func TestCatalog_ListServiceNodes_DistanceSort(t *testing.T) {
|
2017-06-27 13:22:18 +00:00
|
|
|
t.Parallel()
|
2015-06-30 21:25:40 +00:00
|
|
|
dir1, s1 := testServer(t)
|
|
|
|
defer os.RemoveAll(dir1)
|
|
|
|
defer s1.Shutdown()
|
2015-10-15 23:07:16 +00:00
|
|
|
codec := rpcClient(t, s1)
|
|
|
|
defer codec.Close()
|
2015-06-30 21:25:40 +00:00
|
|
|
|
|
|
|
args := structs.ServiceSpecificRequest{
|
|
|
|
Datacenter: "dc1",
|
|
|
|
ServiceName: "db",
|
|
|
|
}
|
|
|
|
var out structs.IndexedServiceNodes
|
2015-10-15 23:07:16 +00:00
|
|
|
err := msgpackrpc.CallWithCodec(codec, "Catalog.ServiceNodes", &args, &out)
|
2016-07-10 17:24:18 +00:00
|
|
|
if err != nil {
|
2015-06-30 21:25:40 +00:00
|
|
|
t.Fatalf("err: %v", err)
|
|
|
|
}
|
|
|
|
|
2017-04-19 23:00:11 +00:00
|
|
|
testrpc.WaitForLeader(t, s1.RPC, "dc1")
|
2015-06-30 21:25:40 +00:00
|
|
|
|
|
|
|
// Add a few nodes for the associated services.
|
2015-10-23 22:19:14 +00:00
|
|
|
s1.fsm.State().EnsureNode(1, &structs.Node{Node: "aaa", Address: "127.0.0.1"})
|
|
|
|
s1.fsm.State().EnsureService(2, "aaa", &structs.NodeService{ID: "db", Service: "db", Tags: []string{"primary"}, Address: "127.0.0.1", Port: 5000})
|
|
|
|
s1.fsm.State().EnsureNode(3, &structs.Node{Node: "foo", Address: "127.0.0.2"})
|
|
|
|
s1.fsm.State().EnsureService(4, "foo", &structs.NodeService{ID: "db", Service: "db", Tags: []string{"primary"}, Address: "127.0.0.2", Port: 5000})
|
|
|
|
s1.fsm.State().EnsureNode(5, &structs.Node{Node: "bar", Address: "127.0.0.3"})
|
|
|
|
s1.fsm.State().EnsureService(6, "bar", &structs.NodeService{ID: "db", Service: "db", Tags: []string{"primary"}, Address: "127.0.0.3", Port: 5000})
|
|
|
|
s1.fsm.State().EnsureNode(7, &structs.Node{Node: "baz", Address: "127.0.0.4"})
|
|
|
|
s1.fsm.State().EnsureService(8, "baz", &structs.NodeService{ID: "db", Service: "db", Tags: []string{"primary"}, Address: "127.0.0.4", Port: 5000})
|
2015-06-30 21:25:40 +00:00
|
|
|
|
|
|
|
// Set all but one of the nodes to known coordinates.
|
2015-10-23 22:19:14 +00:00
|
|
|
updates := structs.Coordinates{
|
2017-08-14 14:36:07 +00:00
|
|
|
{Node: "foo", Coord: lib.GenerateCoordinate(2 * time.Millisecond)},
|
|
|
|
{Node: "bar", Coord: lib.GenerateCoordinate(5 * time.Millisecond)},
|
|
|
|
{Node: "baz", Coord: lib.GenerateCoordinate(1 * time.Millisecond)},
|
2015-06-30 21:25:40 +00:00
|
|
|
}
|
|
|
|
if err := s1.fsm.State().CoordinateBatchUpdate(9, updates); err != nil {
|
|
|
|
t.Fatalf("err: %v", err)
|
|
|
|
}
|
|
|
|
|
|
|
|
// Query with no given source node, should get the natural order from
|
|
|
|
// the index.
|
2015-10-15 23:07:16 +00:00
|
|
|
if err := msgpackrpc.CallWithCodec(codec, "Catalog.ServiceNodes", &args, &out); err != nil {
|
2015-06-30 21:25:40 +00:00
|
|
|
t.Fatalf("err: %v", err)
|
|
|
|
}
|
|
|
|
if len(out.ServiceNodes) != 4 {
|
|
|
|
t.Fatalf("bad: %v", out)
|
|
|
|
}
|
|
|
|
if out.ServiceNodes[0].Node != "aaa" {
|
|
|
|
t.Fatalf("bad: %v", out)
|
|
|
|
}
|
2015-10-23 22:19:14 +00:00
|
|
|
if out.ServiceNodes[1].Node != "bar" {
|
2015-06-30 21:25:40 +00:00
|
|
|
t.Fatalf("bad: %v", out)
|
|
|
|
}
|
2015-10-23 22:19:14 +00:00
|
|
|
if out.ServiceNodes[2].Node != "baz" {
|
2015-06-30 21:25:40 +00:00
|
|
|
t.Fatalf("bad: %v", out)
|
|
|
|
}
|
2015-10-23 22:19:14 +00:00
|
|
|
if out.ServiceNodes[3].Node != "foo" {
|
2015-06-30 21:25:40 +00:00
|
|
|
t.Fatalf("bad: %v", out)
|
|
|
|
}
|
|
|
|
|
|
|
|
// Query relative to foo, note that there's no known coordinate for "aaa"
|
|
|
|
// so it will go at the end.
|
|
|
|
args = structs.ServiceSpecificRequest{
|
|
|
|
Datacenter: "dc1",
|
|
|
|
ServiceName: "db",
|
|
|
|
Source: structs.QuerySource{Datacenter: "dc1", Node: "foo"},
|
|
|
|
}
|
2015-10-15 23:07:16 +00:00
|
|
|
if err := msgpackrpc.CallWithCodec(codec, "Catalog.ServiceNodes", &args, &out); err != nil {
|
2015-06-30 21:25:40 +00:00
|
|
|
t.Fatalf("err: %v", err)
|
|
|
|
}
|
|
|
|
if len(out.ServiceNodes) != 4 {
|
|
|
|
t.Fatalf("bad: %v", out)
|
|
|
|
}
|
|
|
|
if out.ServiceNodes[0].Node != "foo" {
|
|
|
|
t.Fatalf("bad: %v", out)
|
|
|
|
}
|
|
|
|
if out.ServiceNodes[1].Node != "baz" {
|
|
|
|
t.Fatalf("bad: %v", out)
|
|
|
|
}
|
|
|
|
if out.ServiceNodes[2].Node != "bar" {
|
|
|
|
t.Fatalf("bad: %v", out)
|
|
|
|
}
|
|
|
|
if out.ServiceNodes[3].Node != "aaa" {
|
|
|
|
t.Fatalf("bad: %v", out)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2018-03-07 01:32:41 +00:00
|
|
|
func TestCatalog_ListServiceNodes_ConnectProxy(t *testing.T) {
|
|
|
|
t.Parallel()
|
|
|
|
|
|
|
|
assert := assert.New(t)
|
|
|
|
dir1, s1 := testServer(t)
|
|
|
|
defer os.RemoveAll(dir1)
|
|
|
|
defer s1.Shutdown()
|
|
|
|
codec := rpcClient(t, s1)
|
|
|
|
defer codec.Close()
|
|
|
|
|
|
|
|
testrpc.WaitForLeader(t, s1.RPC, "dc1")
|
|
|
|
|
|
|
|
// Register the service
|
|
|
|
args := structs.TestRegisterRequestProxy(t)
|
|
|
|
var out struct{}
|
|
|
|
assert.Nil(msgpackrpc.CallWithCodec(codec, "Catalog.Register", args, &out))
|
|
|
|
|
|
|
|
// List
|
|
|
|
req := structs.ServiceSpecificRequest{
|
|
|
|
Datacenter: "dc1",
|
|
|
|
ServiceName: args.Service.Service,
|
|
|
|
TagFilter: false,
|
|
|
|
}
|
|
|
|
var resp structs.IndexedServiceNodes
|
|
|
|
assert.Nil(msgpackrpc.CallWithCodec(codec, "Catalog.ServiceNodes", &req, &resp))
|
|
|
|
assert.Len(resp.ServiceNodes, 1)
|
|
|
|
v := resp.ServiceNodes[0]
|
|
|
|
assert.Equal(structs.ServiceKindConnectProxy, v.ServiceKind)
|
2018-09-12 16:07:47 +00:00
|
|
|
assert.Equal(args.Service.Proxy.DestinationServiceName, v.ServiceProxy.DestinationServiceName)
|
2018-03-07 01:32:41 +00:00
|
|
|
}
|
|
|
|
|
2020-04-08 18:37:24 +00:00
|
|
|
func TestCatalog_ServiceNodes_Gateway(t *testing.T) {
|
|
|
|
t.Parallel()
|
|
|
|
|
|
|
|
dir1, s1 := testServer(t)
|
|
|
|
defer os.RemoveAll(dir1)
|
|
|
|
defer s1.Shutdown()
|
|
|
|
|
|
|
|
codec := rpcClient(t, s1)
|
|
|
|
defer codec.Close()
|
|
|
|
|
|
|
|
testrpc.WaitForTestAgent(t, s1.RPC, "dc1")
|
|
|
|
{
|
|
|
|
var out struct{}
|
|
|
|
|
|
|
|
// Register a service "api"
|
|
|
|
args := structs.TestRegisterRequest(t)
|
|
|
|
args.Service.Service = "api"
|
|
|
|
args.Check = &structs.HealthCheck{
|
|
|
|
Name: "api",
|
|
|
|
Status: api.HealthPassing,
|
|
|
|
ServiceID: args.Service.Service,
|
|
|
|
}
|
|
|
|
assert.Nil(t, msgpackrpc.CallWithCodec(codec, "Catalog.Register", &args, &out))
|
|
|
|
|
|
|
|
// Register a proxy for api
|
|
|
|
args = structs.TestRegisterRequestProxy(t)
|
|
|
|
args.Service.Service = "api-proxy"
|
|
|
|
args.Service.Proxy.DestinationServiceName = "api"
|
|
|
|
args.Check = &structs.HealthCheck{
|
|
|
|
Name: "api-proxy",
|
|
|
|
Status: api.HealthPassing,
|
|
|
|
ServiceID: args.Service.Service,
|
|
|
|
}
|
|
|
|
assert.Nil(t, msgpackrpc.CallWithCodec(codec, "Catalog.Register", &args, &out))
|
|
|
|
|
|
|
|
// Register a service "web"
|
|
|
|
args = structs.TestRegisterRequest(t)
|
|
|
|
args.Check = &structs.HealthCheck{
|
|
|
|
Name: "web",
|
|
|
|
Status: api.HealthPassing,
|
|
|
|
ServiceID: args.Service.Service,
|
|
|
|
}
|
|
|
|
assert.Nil(t, msgpackrpc.CallWithCodec(codec, "Catalog.Register", &args, &out))
|
|
|
|
|
|
|
|
// Register a proxy for web
|
|
|
|
args = structs.TestRegisterRequestProxy(t)
|
|
|
|
args.Check = &structs.HealthCheck{
|
|
|
|
Name: "web-proxy",
|
|
|
|
Status: api.HealthPassing,
|
|
|
|
ServiceID: args.Service.Service,
|
|
|
|
}
|
|
|
|
assert.Nil(t, msgpackrpc.CallWithCodec(codec, "Catalog.Register", &args, &out))
|
|
|
|
|
|
|
|
// Register a gateway for web
|
|
|
|
args = &structs.RegisterRequest{
|
|
|
|
Datacenter: "dc1",
|
|
|
|
Node: "foo",
|
|
|
|
Address: "127.0.0.1",
|
|
|
|
Service: &structs.NodeService{
|
|
|
|
Kind: structs.ServiceKindTerminatingGateway,
|
|
|
|
Service: "gateway",
|
|
|
|
Port: 443,
|
|
|
|
},
|
|
|
|
Check: &structs.HealthCheck{
|
|
|
|
Name: "gateway",
|
|
|
|
Status: api.HealthPassing,
|
|
|
|
ServiceID: args.Service.Service,
|
|
|
|
},
|
|
|
|
}
|
|
|
|
assert.Nil(t, msgpackrpc.CallWithCodec(codec, "Catalog.Register", &args, &out))
|
|
|
|
|
|
|
|
entryArgs := &structs.ConfigEntryRequest{
|
|
|
|
Op: structs.ConfigEntryUpsert,
|
|
|
|
Datacenter: "dc1",
|
|
|
|
Entry: &structs.TerminatingGatewayConfigEntry{
|
|
|
|
Kind: "terminating-gateway",
|
|
|
|
Name: "gateway",
|
|
|
|
Services: []structs.LinkedService{
|
|
|
|
{
|
|
|
|
Name: "web",
|
|
|
|
},
|
|
|
|
},
|
|
|
|
},
|
|
|
|
}
|
|
|
|
var entryResp bool
|
|
|
|
assert.Nil(t, msgpackrpc.CallWithCodec(codec, "ConfigEntry.Apply", &entryArgs, &entryResp))
|
|
|
|
}
|
|
|
|
|
|
|
|
retry.Run(t, func(r *retry.R) {
|
|
|
|
// List should return both the terminating-gateway and the connect-proxy associated with web
|
|
|
|
req := structs.ServiceSpecificRequest{
|
|
|
|
Connect: true,
|
|
|
|
Datacenter: "dc1",
|
|
|
|
ServiceName: "web",
|
|
|
|
}
|
|
|
|
var resp structs.IndexedServiceNodes
|
|
|
|
assert.Nil(r, msgpackrpc.CallWithCodec(codec, "Catalog.ServiceNodes", &req, &resp))
|
|
|
|
assert.Len(r, resp.ServiceNodes, 2)
|
|
|
|
|
|
|
|
// Check sidecar
|
|
|
|
assert.Equal(r, structs.ServiceKindConnectProxy, resp.ServiceNodes[0].ServiceKind)
|
|
|
|
assert.Equal(r, "foo", resp.ServiceNodes[0].Node)
|
|
|
|
assert.Equal(r, "web-proxy", resp.ServiceNodes[0].ServiceName)
|
|
|
|
assert.Equal(r, "web-proxy", resp.ServiceNodes[0].ServiceID)
|
|
|
|
assert.Equal(r, "web", resp.ServiceNodes[0].ServiceProxy.DestinationServiceName)
|
|
|
|
assert.Equal(r, 2222, resp.ServiceNodes[0].ServicePort)
|
|
|
|
|
|
|
|
// Check gateway
|
|
|
|
assert.Equal(r, structs.ServiceKindTerminatingGateway, resp.ServiceNodes[1].ServiceKind)
|
|
|
|
assert.Equal(r, "foo", resp.ServiceNodes[1].Node)
|
|
|
|
assert.Equal(r, "gateway", resp.ServiceNodes[1].ServiceName)
|
|
|
|
assert.Equal(r, "gateway", resp.ServiceNodes[1].ServiceID)
|
|
|
|
assert.Equal(r, 443, resp.ServiceNodes[1].ServicePort)
|
|
|
|
})
|
|
|
|
}
|
|
|
|
|
2018-03-09 16:34:55 +00:00
|
|
|
func TestCatalog_ListServiceNodes_ConnectDestination(t *testing.T) {
|
|
|
|
t.Parallel()
|
|
|
|
|
|
|
|
assert := assert.New(t)
|
|
|
|
dir1, s1 := testServer(t)
|
|
|
|
defer os.RemoveAll(dir1)
|
|
|
|
defer s1.Shutdown()
|
|
|
|
codec := rpcClient(t, s1)
|
|
|
|
defer codec.Close()
|
|
|
|
|
|
|
|
testrpc.WaitForLeader(t, s1.RPC, "dc1")
|
|
|
|
|
|
|
|
// Register the proxy service
|
|
|
|
args := structs.TestRegisterRequestProxy(t)
|
|
|
|
var out struct{}
|
|
|
|
assert.Nil(msgpackrpc.CallWithCodec(codec, "Catalog.Register", args, &out))
|
|
|
|
|
|
|
|
// Register the service
|
|
|
|
{
|
2018-09-12 16:07:47 +00:00
|
|
|
dst := args.Service.Proxy.DestinationServiceName
|
2018-03-09 16:34:55 +00:00
|
|
|
args := structs.TestRegisterRequest(t)
|
|
|
|
args.Service.Service = dst
|
|
|
|
var out struct{}
|
|
|
|
assert.Nil(msgpackrpc.CallWithCodec(codec, "Catalog.Register", args, &out))
|
|
|
|
}
|
|
|
|
|
|
|
|
// List
|
|
|
|
req := structs.ServiceSpecificRequest{
|
|
|
|
Connect: true,
|
|
|
|
Datacenter: "dc1",
|
2018-09-12 16:07:47 +00:00
|
|
|
ServiceName: args.Service.Proxy.DestinationServiceName,
|
2018-03-09 16:34:55 +00:00
|
|
|
}
|
|
|
|
var resp structs.IndexedServiceNodes
|
|
|
|
assert.Nil(msgpackrpc.CallWithCodec(codec, "Catalog.ServiceNodes", &req, &resp))
|
|
|
|
assert.Len(resp.ServiceNodes, 1)
|
|
|
|
v := resp.ServiceNodes[0]
|
|
|
|
assert.Equal(structs.ServiceKindConnectProxy, v.ServiceKind)
|
2018-09-12 16:07:47 +00:00
|
|
|
assert.Equal(args.Service.Proxy.DestinationServiceName, v.ServiceProxy.DestinationServiceName)
|
2018-03-09 16:34:55 +00:00
|
|
|
|
|
|
|
// List by non-Connect
|
|
|
|
req = structs.ServiceSpecificRequest{
|
|
|
|
Datacenter: "dc1",
|
2018-09-12 16:07:47 +00:00
|
|
|
ServiceName: args.Service.Proxy.DestinationServiceName,
|
2018-03-09 16:34:55 +00:00
|
|
|
}
|
|
|
|
assert.Nil(msgpackrpc.CallWithCodec(codec, "Catalog.ServiceNodes", &req, &resp))
|
|
|
|
assert.Len(resp.ServiceNodes, 1)
|
|
|
|
v = resp.ServiceNodes[0]
|
2018-09-12 16:07:47 +00:00
|
|
|
assert.Equal(args.Service.Proxy.DestinationServiceName, v.ServiceName)
|
|
|
|
assert.Equal("", v.ServiceProxy.DestinationServiceName)
|
2018-03-09 16:34:55 +00:00
|
|
|
}
|
|
|
|
|
2018-06-05 03:04:45 +00:00
|
|
|
// Test that calling ServiceNodes with Connect: true will return
|
|
|
|
// Connect native services.
|
|
|
|
func TestCatalog_ListServiceNodes_ConnectDestinationNative(t *testing.T) {
|
|
|
|
t.Parallel()
|
|
|
|
|
|
|
|
require := require.New(t)
|
|
|
|
dir1, s1 := testServer(t)
|
|
|
|
defer os.RemoveAll(dir1)
|
|
|
|
defer s1.Shutdown()
|
|
|
|
codec := rpcClient(t, s1)
|
|
|
|
defer codec.Close()
|
|
|
|
|
|
|
|
testrpc.WaitForLeader(t, s1.RPC, "dc1")
|
|
|
|
|
|
|
|
// Register the native service
|
|
|
|
args := structs.TestRegisterRequest(t)
|
2018-06-05 17:51:05 +00:00
|
|
|
args.Service.Connect.Native = true
|
2018-06-05 03:04:45 +00:00
|
|
|
var out struct{}
|
|
|
|
require.Nil(msgpackrpc.CallWithCodec(codec, "Catalog.Register", args, &out))
|
|
|
|
|
|
|
|
// List
|
|
|
|
req := structs.ServiceSpecificRequest{
|
|
|
|
Connect: true,
|
|
|
|
Datacenter: "dc1",
|
|
|
|
ServiceName: args.Service.Service,
|
|
|
|
}
|
|
|
|
var resp structs.IndexedServiceNodes
|
|
|
|
require.Nil(msgpackrpc.CallWithCodec(codec, "Catalog.ServiceNodes", &req, &resp))
|
|
|
|
require.Len(resp.ServiceNodes, 1)
|
|
|
|
v := resp.ServiceNodes[0]
|
|
|
|
require.Equal(args.Service.Service, v.ServiceName)
|
|
|
|
|
|
|
|
// List by non-Connect
|
|
|
|
req = structs.ServiceSpecificRequest{
|
|
|
|
Datacenter: "dc1",
|
|
|
|
ServiceName: args.Service.Service,
|
|
|
|
}
|
|
|
|
require.Nil(msgpackrpc.CallWithCodec(codec, "Catalog.ServiceNodes", &req, &resp))
|
|
|
|
require.Len(resp.ServiceNodes, 1)
|
|
|
|
v = resp.ServiceNodes[0]
|
|
|
|
require.Equal(args.Service.Service, v.ServiceName)
|
|
|
|
}
|
|
|
|
|
2018-03-11 16:31:39 +00:00
|
|
|
func TestCatalog_ListServiceNodes_ConnectProxy_ACL(t *testing.T) {
|
|
|
|
t.Parallel()
|
|
|
|
|
|
|
|
dir1, s1 := testServerWithConfig(t, func(c *Config) {
|
|
|
|
c.ACLDatacenter = "dc1"
|
2018-10-19 16:04:07 +00:00
|
|
|
c.ACLsEnabled = true
|
2018-03-11 16:31:39 +00:00
|
|
|
c.ACLMasterToken = "root"
|
|
|
|
c.ACLDefaultPolicy = "deny"
|
|
|
|
})
|
|
|
|
defer os.RemoveAll(dir1)
|
|
|
|
defer s1.Shutdown()
|
|
|
|
codec := rpcClient(t, s1)
|
|
|
|
defer codec.Close()
|
|
|
|
|
|
|
|
testrpc.WaitForLeader(t, s1.RPC, "dc1")
|
|
|
|
|
|
|
|
// Create the ACL.
|
|
|
|
arg := structs.ACLRequest{
|
|
|
|
Datacenter: "dc1",
|
|
|
|
Op: structs.ACLSet,
|
|
|
|
ACL: structs.ACL{
|
|
|
|
Name: "User token",
|
2018-10-19 16:04:07 +00:00
|
|
|
Type: structs.ACLTokenTypeClient,
|
2018-03-11 16:31:39 +00:00
|
|
|
Rules: `
|
|
|
|
service "foo" {
|
|
|
|
policy = "write"
|
|
|
|
}
|
2020-05-29 21:16:03 +00:00
|
|
|
node "" { policy = "read" }
|
2018-03-11 16:31:39 +00:00
|
|
|
`,
|
|
|
|
},
|
|
|
|
WriteRequest: structs.WriteRequest{Token: "root"},
|
|
|
|
}
|
|
|
|
var token string
|
2020-05-29 21:16:03 +00:00
|
|
|
require.NoError(t, msgpackrpc.CallWithCodec(codec, "ACL.Apply", &arg, &token))
|
2018-03-11 16:31:39 +00:00
|
|
|
|
|
|
|
{
|
|
|
|
// Register a proxy
|
|
|
|
args := structs.TestRegisterRequestProxy(t)
|
|
|
|
args.Service.Service = "foo-proxy"
|
2018-09-12 16:07:47 +00:00
|
|
|
args.Service.Proxy.DestinationServiceName = "bar"
|
2018-03-11 16:31:39 +00:00
|
|
|
args.WriteRequest.Token = "root"
|
|
|
|
var out struct{}
|
2020-05-29 21:16:03 +00:00
|
|
|
require.NoError(t, msgpackrpc.CallWithCodec(codec, "Catalog.Register", &args, &out))
|
2018-03-11 16:31:39 +00:00
|
|
|
|
|
|
|
// Register a proxy
|
|
|
|
args = structs.TestRegisterRequestProxy(t)
|
|
|
|
args.Service.Service = "foo-proxy"
|
2018-09-12 16:07:47 +00:00
|
|
|
args.Service.Proxy.DestinationServiceName = "foo"
|
2018-03-11 16:31:39 +00:00
|
|
|
args.WriteRequest.Token = "root"
|
2020-05-29 21:16:03 +00:00
|
|
|
require.NoError(t, msgpackrpc.CallWithCodec(codec, "Catalog.Register", &args, &out))
|
2018-03-11 16:31:39 +00:00
|
|
|
|
|
|
|
// Register a proxy
|
|
|
|
args = structs.TestRegisterRequestProxy(t)
|
|
|
|
args.Service.Service = "another-proxy"
|
2018-09-12 16:07:47 +00:00
|
|
|
args.Service.Proxy.DestinationServiceName = "foo"
|
2018-03-11 16:31:39 +00:00
|
|
|
args.WriteRequest.Token = "root"
|
2020-05-29 21:16:03 +00:00
|
|
|
require.NoError(t, msgpackrpc.CallWithCodec(codec, "Catalog.Register", &args, &out))
|
2018-03-11 16:31:39 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
// List w/ token. This should disallow because we don't have permission
|
|
|
|
// to read "bar"
|
|
|
|
req := structs.ServiceSpecificRequest{
|
|
|
|
Connect: true,
|
|
|
|
Datacenter: "dc1",
|
|
|
|
ServiceName: "bar",
|
|
|
|
QueryOptions: structs.QueryOptions{Token: token},
|
|
|
|
}
|
|
|
|
var resp structs.IndexedServiceNodes
|
2020-05-29 21:16:03 +00:00
|
|
|
require.NoError(t, msgpackrpc.CallWithCodec(codec, "Catalog.ServiceNodes", &req, &resp))
|
|
|
|
require.Len(t, resp.ServiceNodes, 0)
|
2018-03-11 16:31:39 +00:00
|
|
|
|
|
|
|
// List w/ token. This should work since we're requesting "foo", but should
|
|
|
|
// also only contain the proxies with names that adhere to our ACL.
|
|
|
|
req = structs.ServiceSpecificRequest{
|
|
|
|
Connect: true,
|
|
|
|
Datacenter: "dc1",
|
|
|
|
ServiceName: "foo",
|
|
|
|
QueryOptions: structs.QueryOptions{Token: token},
|
|
|
|
}
|
2020-05-29 21:16:03 +00:00
|
|
|
resp = structs.IndexedServiceNodes{}
|
|
|
|
require.NoError(t, msgpackrpc.CallWithCodec(codec, "Catalog.ServiceNodes", &req, &resp))
|
|
|
|
require.Len(t, resp.ServiceNodes, 1)
|
2018-03-11 16:31:39 +00:00
|
|
|
v := resp.ServiceNodes[0]
|
2020-05-29 21:16:03 +00:00
|
|
|
require.Equal(t, "foo-proxy", v.ServiceName)
|
2018-03-11 16:31:39 +00:00
|
|
|
}
|
|
|
|
|
2018-06-04 05:14:01 +00:00
|
|
|
func TestCatalog_ListServiceNodes_ConnectNative(t *testing.T) {
|
|
|
|
t.Parallel()
|
|
|
|
|
|
|
|
assert := assert.New(t)
|
|
|
|
dir1, s1 := testServer(t)
|
|
|
|
defer os.RemoveAll(dir1)
|
|
|
|
defer s1.Shutdown()
|
|
|
|
codec := rpcClient(t, s1)
|
|
|
|
defer codec.Close()
|
|
|
|
|
|
|
|
testrpc.WaitForLeader(t, s1.RPC, "dc1")
|
|
|
|
|
|
|
|
// Register the service
|
|
|
|
args := structs.TestRegisterRequest(t)
|
2018-06-05 17:51:05 +00:00
|
|
|
args.Service.Connect.Native = true
|
2018-06-04 05:14:01 +00:00
|
|
|
var out struct{}
|
|
|
|
assert.Nil(msgpackrpc.CallWithCodec(codec, "Catalog.Register", args, &out))
|
|
|
|
|
|
|
|
// List
|
|
|
|
req := structs.ServiceSpecificRequest{
|
|
|
|
Datacenter: "dc1",
|
|
|
|
ServiceName: args.Service.Service,
|
|
|
|
TagFilter: false,
|
|
|
|
}
|
|
|
|
var resp structs.IndexedServiceNodes
|
|
|
|
assert.Nil(msgpackrpc.CallWithCodec(codec, "Catalog.ServiceNodes", &req, &resp))
|
|
|
|
assert.Len(resp.ServiceNodes, 1)
|
|
|
|
v := resp.ServiceNodes[0]
|
2018-06-05 17:51:05 +00:00
|
|
|
assert.Equal(args.Service.Connect.Native, v.ServiceConnect.Native)
|
2018-06-04 05:14:01 +00:00
|
|
|
}
|
|
|
|
|
2016-12-10 05:04:00 +00:00
|
|
|
func TestCatalog_NodeServices(t *testing.T) {
|
2017-06-27 13:22:18 +00:00
|
|
|
t.Parallel()
|
2013-12-12 19:46:25 +00:00
|
|
|
dir1, s1 := testServer(t)
|
|
|
|
defer os.RemoveAll(dir1)
|
|
|
|
defer s1.Shutdown()
|
2015-10-13 23:43:52 +00:00
|
|
|
codec := rpcClient(t, s1)
|
|
|
|
defer codec.Close()
|
2019-01-07 21:53:54 +00:00
|
|
|
testrpc.WaitForTestAgent(t, s1.RPC, "dc1")
|
2013-12-12 19:46:25 +00:00
|
|
|
|
2014-01-08 21:52:09 +00:00
|
|
|
args := structs.NodeSpecificRequest{
|
2013-12-12 19:46:25 +00:00
|
|
|
Datacenter: "dc1",
|
|
|
|
Node: "foo",
|
|
|
|
}
|
2014-02-05 19:10:10 +00:00
|
|
|
var out structs.IndexedNodeServices
|
2015-10-13 23:43:52 +00:00
|
|
|
err := msgpackrpc.CallWithCodec(codec, "Catalog.NodeServices", &args, &out)
|
2016-07-10 17:24:18 +00:00
|
|
|
if err != nil {
|
2013-12-12 19:46:25 +00:00
|
|
|
t.Fatalf("err: %v", err)
|
|
|
|
}
|
|
|
|
|
2017-04-19 23:00:11 +00:00
|
|
|
testrpc.WaitForLeader(t, s1.RPC, "dc1")
|
2013-12-12 19:46:25 +00:00
|
|
|
|
|
|
|
// Just add a node
|
2015-10-13 05:21:39 +00:00
|
|
|
if err := s1.fsm.State().EnsureNode(1, &structs.Node{Node: "foo", Address: "127.0.0.1"}); err != nil {
|
2015-10-12 07:42:09 +00:00
|
|
|
t.Fatalf("err: %v", err)
|
|
|
|
}
|
2015-10-13 05:21:39 +00:00
|
|
|
if err := s1.fsm.State().EnsureService(2, "foo", &structs.NodeService{ID: "db", Service: "db", Tags: []string{"primary"}, Address: "127.0.0.1", Port: 5000}); err != nil {
|
2015-10-12 07:42:09 +00:00
|
|
|
t.Fatalf("err: %v", err)
|
|
|
|
}
|
2015-10-13 05:21:39 +00:00
|
|
|
if err := s1.fsm.State().EnsureService(3, "foo", &structs.NodeService{ID: "web", Service: "web", Tags: nil, Address: "127.0.0.1", Port: 80}); err != nil {
|
2015-10-12 07:42:09 +00:00
|
|
|
t.Fatalf("err: %v", err)
|
|
|
|
}
|
2013-12-12 19:46:25 +00:00
|
|
|
|
2015-10-13 23:43:52 +00:00
|
|
|
if err := msgpackrpc.CallWithCodec(codec, "Catalog.NodeServices", &args, &out); err != nil {
|
2013-12-12 19:46:25 +00:00
|
|
|
t.Fatalf("err: %v", err)
|
|
|
|
}
|
|
|
|
|
2014-02-05 19:10:10 +00:00
|
|
|
if out.NodeServices.Node.Address != "127.0.0.1" {
|
2014-01-03 01:29:39 +00:00
|
|
|
t.Fatalf("bad: %v", out)
|
|
|
|
}
|
2014-02-05 19:10:10 +00:00
|
|
|
if len(out.NodeServices.Services) != 2 {
|
2013-12-12 19:46:25 +00:00
|
|
|
t.Fatalf("bad: %v", out)
|
|
|
|
}
|
2014-02-05 19:10:10 +00:00
|
|
|
services := out.NodeServices.Services
|
2020-05-27 16:47:32 +00:00
|
|
|
if !stringslice.Contains(services["db"].Tags, "primary") || services["db"].Port != 5000 {
|
2013-12-12 19:46:25 +00:00
|
|
|
t.Fatalf("bad: %v", out)
|
|
|
|
}
|
2014-10-09 18:54:47 +00:00
|
|
|
if len(services["web"].Tags) != 0 || services["web"].Port != 80 {
|
2013-12-12 19:46:25 +00:00
|
|
|
t.Fatalf("bad: %v", out)
|
|
|
|
}
|
|
|
|
}
|
2014-01-01 02:31:17 +00:00
|
|
|
|
2018-03-07 01:41:39 +00:00
|
|
|
func TestCatalog_NodeServices_ConnectProxy(t *testing.T) {
|
|
|
|
t.Parallel()
|
|
|
|
|
|
|
|
assert := assert.New(t)
|
|
|
|
dir1, s1 := testServer(t)
|
|
|
|
defer os.RemoveAll(dir1)
|
|
|
|
defer s1.Shutdown()
|
|
|
|
codec := rpcClient(t, s1)
|
|
|
|
defer codec.Close()
|
|
|
|
|
2019-01-07 21:53:54 +00:00
|
|
|
testrpc.WaitForTestAgent(t, s1.RPC, "dc1")
|
2018-03-07 01:41:39 +00:00
|
|
|
|
|
|
|
// Register the service
|
|
|
|
args := structs.TestRegisterRequestProxy(t)
|
|
|
|
var out struct{}
|
|
|
|
assert.Nil(msgpackrpc.CallWithCodec(codec, "Catalog.Register", args, &out))
|
|
|
|
|
|
|
|
// List
|
|
|
|
req := structs.NodeSpecificRequest{
|
|
|
|
Datacenter: "dc1",
|
|
|
|
Node: args.Node,
|
|
|
|
}
|
|
|
|
var resp structs.IndexedNodeServices
|
|
|
|
assert.Nil(msgpackrpc.CallWithCodec(codec, "Catalog.NodeServices", &req, &resp))
|
|
|
|
|
|
|
|
assert.Len(resp.NodeServices.Services, 1)
|
|
|
|
v := resp.NodeServices.Services[args.Service.Service]
|
|
|
|
assert.Equal(structs.ServiceKindConnectProxy, v.Kind)
|
2018-09-12 16:07:47 +00:00
|
|
|
assert.Equal(args.Service.Proxy.DestinationServiceName, v.Proxy.DestinationServiceName)
|
2018-03-07 01:41:39 +00:00
|
|
|
}
|
|
|
|
|
2018-06-04 05:14:01 +00:00
|
|
|
func TestCatalog_NodeServices_ConnectNative(t *testing.T) {
|
|
|
|
t.Parallel()
|
|
|
|
|
|
|
|
assert := assert.New(t)
|
|
|
|
dir1, s1 := testServer(t)
|
|
|
|
defer os.RemoveAll(dir1)
|
|
|
|
defer s1.Shutdown()
|
|
|
|
codec := rpcClient(t, s1)
|
|
|
|
defer codec.Close()
|
|
|
|
|
2019-01-07 21:53:54 +00:00
|
|
|
testrpc.WaitForTestAgent(t, s1.RPC, "dc1")
|
2018-06-04 05:14:01 +00:00
|
|
|
|
|
|
|
// Register the service
|
|
|
|
args := structs.TestRegisterRequest(t)
|
|
|
|
var out struct{}
|
|
|
|
assert.Nil(msgpackrpc.CallWithCodec(codec, "Catalog.Register", args, &out))
|
|
|
|
|
|
|
|
// List
|
|
|
|
req := structs.NodeSpecificRequest{
|
|
|
|
Datacenter: "dc1",
|
|
|
|
Node: args.Node,
|
|
|
|
}
|
|
|
|
var resp structs.IndexedNodeServices
|
|
|
|
assert.Nil(msgpackrpc.CallWithCodec(codec, "Catalog.NodeServices", &req, &resp))
|
|
|
|
|
|
|
|
assert.Len(resp.NodeServices.Services, 1)
|
|
|
|
v := resp.NodeServices.Services[args.Service.Service]
|
2018-06-05 17:51:05 +00:00
|
|
|
assert.Equal(args.Service.Connect.Native, v.Connect.Native)
|
2018-06-04 05:14:01 +00:00
|
|
|
}
|
|
|
|
|
2014-01-01 02:31:17 +00:00
|
|
|
// Used to check for a regression against a known bug
|
2016-12-11 00:00:11 +00:00
|
|
|
func TestCatalog_Register_FailedCase1(t *testing.T) {
|
2017-06-27 13:22:18 +00:00
|
|
|
t.Parallel()
|
2014-01-01 02:31:17 +00:00
|
|
|
dir1, s1 := testServer(t)
|
|
|
|
defer os.RemoveAll(dir1)
|
|
|
|
defer s1.Shutdown()
|
2015-10-13 23:43:52 +00:00
|
|
|
codec := rpcClient(t, s1)
|
|
|
|
defer codec.Close()
|
2014-01-01 02:31:17 +00:00
|
|
|
|
|
|
|
arg := structs.RegisterRequest{
|
2014-01-08 21:39:40 +00:00
|
|
|
Datacenter: "dc1",
|
|
|
|
Node: "bar",
|
|
|
|
Address: "127.0.0.2",
|
|
|
|
Service: &structs.NodeService{
|
|
|
|
Service: "web",
|
2014-04-03 19:03:10 +00:00
|
|
|
Tags: nil,
|
2014-01-08 21:39:40 +00:00
|
|
|
Port: 8000,
|
|
|
|
},
|
2014-01-01 02:31:17 +00:00
|
|
|
}
|
|
|
|
var out struct{}
|
|
|
|
|
2015-10-13 23:43:52 +00:00
|
|
|
err := msgpackrpc.CallWithCodec(codec, "Catalog.Register", &arg, &out)
|
2016-07-10 17:24:18 +00:00
|
|
|
if err != nil {
|
2014-01-01 02:31:17 +00:00
|
|
|
t.Fatalf("err: %v", err)
|
|
|
|
}
|
|
|
|
|
2017-04-19 23:00:11 +00:00
|
|
|
testrpc.WaitForLeader(t, s1.RPC, "dc1")
|
2014-01-01 02:31:17 +00:00
|
|
|
|
2015-10-13 23:43:52 +00:00
|
|
|
if err := msgpackrpc.CallWithCodec(codec, "Catalog.Register", &arg, &out); err != nil {
|
2014-01-01 02:31:17 +00:00
|
|
|
t.Fatalf("err: %v", err)
|
|
|
|
}
|
|
|
|
|
|
|
|
// Check we can get this back
|
2014-01-08 21:52:09 +00:00
|
|
|
query := &structs.ServiceSpecificRequest{
|
2014-01-01 02:31:17 +00:00
|
|
|
Datacenter: "dc1",
|
|
|
|
ServiceName: "web",
|
|
|
|
}
|
2014-02-05 19:10:10 +00:00
|
|
|
var out2 structs.IndexedServiceNodes
|
2015-10-13 23:43:52 +00:00
|
|
|
if err := msgpackrpc.CallWithCodec(codec, "Catalog.ServiceNodes", query, &out2); err != nil {
|
2014-01-01 02:31:17 +00:00
|
|
|
t.Fatalf("err: %v", err)
|
|
|
|
}
|
|
|
|
|
|
|
|
// Check the output
|
2014-02-05 19:10:10 +00:00
|
|
|
if len(out2.ServiceNodes) != 1 {
|
|
|
|
t.Fatalf("Bad: %v", out2)
|
2014-01-01 02:31:17 +00:00
|
|
|
}
|
|
|
|
}
|
2014-12-01 04:05:15 +00:00
|
|
|
|
2015-10-13 23:43:52 +00:00
|
|
|
func testACLFilterServer(t *testing.T) (dir, token string, srv *Server, codec rpc.ClientCodec) {
|
2015-06-11 20:23:49 +00:00
|
|
|
dir, srv = testServerWithConfig(t, func(c *Config) {
|
2015-06-11 05:14:58 +00:00
|
|
|
c.ACLDatacenter = "dc1"
|
2018-10-19 16:04:07 +00:00
|
|
|
c.ACLsEnabled = true
|
2015-06-11 05:14:58 +00:00
|
|
|
c.ACLMasterToken = "root"
|
|
|
|
c.ACLDefaultPolicy = "deny"
|
|
|
|
})
|
|
|
|
|
2015-10-13 23:43:52 +00:00
|
|
|
codec = rpcClient(t, srv)
|
2020-05-29 21:16:03 +00:00
|
|
|
testrpc.WaitForTestAgent(t, srv.RPC, "dc1", testrpc.WithToken("root"))
|
2015-06-11 05:14:58 +00:00
|
|
|
|
|
|
|
// Create a new token
|
|
|
|
arg := structs.ACLRequest{
|
|
|
|
Datacenter: "dc1",
|
|
|
|
Op: structs.ACLSet,
|
|
|
|
ACL: structs.ACL{
|
2016-12-10 03:15:44 +00:00
|
|
|
Name: "User token",
|
2018-10-19 16:04:07 +00:00
|
|
|
Type: structs.ACLTokenTypeClient,
|
2016-12-10 03:15:44 +00:00
|
|
|
Rules: `
|
|
|
|
service "foo" {
|
|
|
|
policy = "write"
|
|
|
|
}
|
2020-05-29 21:16:03 +00:00
|
|
|
node "" {
|
|
|
|
policy = "read"
|
|
|
|
}
|
2016-12-10 03:15:44 +00:00
|
|
|
`,
|
2015-06-11 05:14:58 +00:00
|
|
|
},
|
|
|
|
WriteRequest: structs.WriteRequest{Token: "root"},
|
|
|
|
}
|
2015-10-13 23:43:52 +00:00
|
|
|
if err := msgpackrpc.CallWithCodec(codec, "ACL.Apply", &arg, &token); err != nil {
|
2015-06-11 05:14:58 +00:00
|
|
|
t.Fatalf("err: %v", err)
|
|
|
|
}
|
|
|
|
|
|
|
|
// Register a service
|
|
|
|
regArg := structs.RegisterRequest{
|
|
|
|
Datacenter: "dc1",
|
|
|
|
Node: srv.config.NodeName,
|
|
|
|
Address: "127.0.0.1",
|
|
|
|
Service: &structs.NodeService{
|
|
|
|
ID: "foo",
|
|
|
|
Service: "foo",
|
|
|
|
},
|
|
|
|
Check: &structs.HealthCheck{
|
|
|
|
CheckID: "service:foo",
|
|
|
|
Name: "service:foo",
|
|
|
|
ServiceID: "foo",
|
2017-04-19 23:00:11 +00:00
|
|
|
Status: api.HealthPassing,
|
2015-06-11 05:14:58 +00:00
|
|
|
},
|
|
|
|
WriteRequest: structs.WriteRequest{Token: "root"},
|
|
|
|
}
|
2015-10-13 23:43:52 +00:00
|
|
|
if err := msgpackrpc.CallWithCodec(codec, "Catalog.Register", ®Arg, nil); err != nil {
|
2015-06-11 05:14:58 +00:00
|
|
|
t.Fatalf("err: %s", err)
|
|
|
|
}
|
|
|
|
|
|
|
|
// Register a service which should be denied
|
|
|
|
regArg = structs.RegisterRequest{
|
|
|
|
Datacenter: "dc1",
|
|
|
|
Node: srv.config.NodeName,
|
|
|
|
Address: "127.0.0.1",
|
|
|
|
Service: &structs.NodeService{
|
|
|
|
ID: "bar",
|
|
|
|
Service: "bar",
|
|
|
|
},
|
|
|
|
Check: &structs.HealthCheck{
|
|
|
|
CheckID: "service:bar",
|
|
|
|
Name: "service:bar",
|
|
|
|
ServiceID: "bar",
|
|
|
|
},
|
|
|
|
WriteRequest: structs.WriteRequest{Token: "root"},
|
|
|
|
}
|
2015-10-13 23:43:52 +00:00
|
|
|
if err := msgpackrpc.CallWithCodec(codec, "Catalog.Register", ®Arg, nil); err != nil {
|
2015-06-11 05:14:58 +00:00
|
|
|
t.Fatalf("err: %s", err)
|
|
|
|
}
|
2015-06-11 20:23:49 +00:00
|
|
|
return
|
|
|
|
}
|
2015-06-11 05:14:58 +00:00
|
|
|
|
2015-06-11 20:23:49 +00:00
|
|
|
func TestCatalog_ListServices_FilterACL(t *testing.T) {
|
2017-06-27 13:22:18 +00:00
|
|
|
t.Parallel()
|
2015-10-13 23:43:52 +00:00
|
|
|
dir, token, srv, codec := testACLFilterServer(t)
|
2015-06-11 20:23:49 +00:00
|
|
|
defer os.RemoveAll(dir)
|
|
|
|
defer srv.Shutdown()
|
2015-10-13 23:43:52 +00:00
|
|
|
defer codec.Close()
|
2020-05-29 21:16:03 +00:00
|
|
|
testrpc.WaitForTestAgent(t, srv.RPC, "dc1", testrpc.WithToken("root"))
|
2015-06-11 20:23:49 +00:00
|
|
|
|
|
|
|
opt := structs.DCSpecificRequest{
|
|
|
|
Datacenter: "dc1",
|
|
|
|
QueryOptions: structs.QueryOptions{Token: token},
|
2015-06-11 05:14:58 +00:00
|
|
|
}
|
2015-06-11 20:23:49 +00:00
|
|
|
reply := structs.IndexedServices{}
|
2015-10-13 23:43:52 +00:00
|
|
|
if err := msgpackrpc.CallWithCodec(codec, "Catalog.ListServices", &opt, &reply); err != nil {
|
2015-06-11 20:23:49 +00:00
|
|
|
t.Fatalf("err: %s", err)
|
|
|
|
}
|
|
|
|
if _, ok := reply.Services["foo"]; !ok {
|
|
|
|
t.Fatalf("bad: %#v", reply.Services)
|
|
|
|
}
|
|
|
|
if _, ok := reply.Services["bar"]; ok {
|
|
|
|
t.Fatalf("bad: %#v", reply.Services)
|
|
|
|
}
|
|
|
|
}
|
2015-06-11 05:14:58 +00:00
|
|
|
|
2015-06-11 20:23:49 +00:00
|
|
|
func TestCatalog_ServiceNodes_FilterACL(t *testing.T) {
|
2017-06-27 13:22:18 +00:00
|
|
|
t.Parallel()
|
2015-10-13 23:43:52 +00:00
|
|
|
dir, token, srv, codec := testACLFilterServer(t)
|
2015-06-11 20:23:49 +00:00
|
|
|
defer os.RemoveAll(dir)
|
|
|
|
defer srv.Shutdown()
|
2015-10-13 23:43:52 +00:00
|
|
|
defer codec.Close()
|
2015-06-11 20:23:49 +00:00
|
|
|
|
|
|
|
opt := structs.ServiceSpecificRequest{
|
|
|
|
Datacenter: "dc1",
|
|
|
|
ServiceName: "foo",
|
|
|
|
QueryOptions: structs.QueryOptions{Token: token},
|
|
|
|
}
|
|
|
|
reply := structs.IndexedServiceNodes{}
|
2015-10-13 23:43:52 +00:00
|
|
|
if err := msgpackrpc.CallWithCodec(codec, "Catalog.ServiceNodes", &opt, &reply); err != nil {
|
2015-06-11 20:23:49 +00:00
|
|
|
t.Fatalf("err: %s", err)
|
|
|
|
}
|
|
|
|
found := false
|
|
|
|
for _, sn := range reply.ServiceNodes {
|
|
|
|
if sn.ServiceID == "foo" {
|
|
|
|
found = true
|
|
|
|
break
|
2015-06-11 05:14:58 +00:00
|
|
|
}
|
|
|
|
}
|
2015-06-11 20:23:49 +00:00
|
|
|
if !found {
|
|
|
|
t.Fatalf("bad: %#v", reply.ServiceNodes)
|
|
|
|
}
|
2015-06-11 05:14:58 +00:00
|
|
|
|
2015-06-11 20:23:49 +00:00
|
|
|
// Filters services we can't access
|
|
|
|
opt = structs.ServiceSpecificRequest{
|
|
|
|
Datacenter: "dc1",
|
|
|
|
ServiceName: "bar",
|
|
|
|
QueryOptions: structs.QueryOptions{Token: token},
|
|
|
|
}
|
|
|
|
reply = structs.IndexedServiceNodes{}
|
2015-10-13 23:43:52 +00:00
|
|
|
if err := msgpackrpc.CallWithCodec(codec, "Catalog.ServiceNodes", &opt, &reply); err != nil {
|
2015-06-11 20:23:49 +00:00
|
|
|
t.Fatalf("err: %s", err)
|
|
|
|
}
|
|
|
|
for _, sn := range reply.ServiceNodes {
|
|
|
|
if sn.ServiceID == "bar" {
|
|
|
|
t.Fatalf("bad: %#v", reply.ServiceNodes)
|
2015-06-11 05:14:58 +00:00
|
|
|
}
|
|
|
|
}
|
2016-12-11 21:22:14 +00:00
|
|
|
|
|
|
|
// We've already proven that we call the ACL filtering function so we
|
|
|
|
// test node filtering down in acl.go for node cases. This also proves
|
|
|
|
// that we respect the version 8 ACL flag, since the test server sets
|
|
|
|
// that to false (the regression value of *not* changing this is better
|
|
|
|
// for now until we change the sense of the version 8 ACL flag).
|
2015-06-11 20:23:49 +00:00
|
|
|
}
|
2015-06-11 05:14:58 +00:00
|
|
|
|
2016-12-10 05:04:00 +00:00
|
|
|
func TestCatalog_NodeServices_ACLDeny(t *testing.T) {
|
2017-06-27 13:22:18 +00:00
|
|
|
t.Parallel()
|
2016-12-10 05:04:00 +00:00
|
|
|
dir1, s1 := testServerWithConfig(t, func(c *Config) {
|
|
|
|
c.ACLDatacenter = "dc1"
|
2018-10-19 16:04:07 +00:00
|
|
|
c.ACLsEnabled = true
|
2016-12-10 05:04:00 +00:00
|
|
|
c.ACLMasterToken = "root"
|
|
|
|
c.ACLDefaultPolicy = "deny"
|
|
|
|
})
|
|
|
|
defer os.RemoveAll(dir1)
|
|
|
|
defer s1.Shutdown()
|
|
|
|
codec := rpcClient(t, s1)
|
|
|
|
defer codec.Close()
|
|
|
|
|
2020-05-29 21:16:03 +00:00
|
|
|
testrpc.WaitForTestAgent(t, s1.RPC, "dc1", testrpc.WithToken("root"))
|
2016-12-10 05:04:00 +00:00
|
|
|
|
2020-05-29 21:16:03 +00:00
|
|
|
// The node policy should not be ignored.
|
2016-12-10 05:04:00 +00:00
|
|
|
args := structs.NodeSpecificRequest{
|
|
|
|
Datacenter: "dc1",
|
|
|
|
Node: s1.config.NodeName,
|
|
|
|
}
|
|
|
|
reply := structs.IndexedNodeServices{}
|
|
|
|
if err := msgpackrpc.CallWithCodec(codec, "Catalog.NodeServices", &args, &reply); err != nil {
|
|
|
|
t.Fatalf("err: %v", err)
|
|
|
|
}
|
2016-12-13 00:53:31 +00:00
|
|
|
if reply.NodeServices != nil {
|
|
|
|
t.Fatalf("should not nil")
|
|
|
|
}
|
2016-12-10 05:04:00 +00:00
|
|
|
|
|
|
|
// Create an ACL that can read the node.
|
|
|
|
arg := structs.ACLRequest{
|
|
|
|
Datacenter: "dc1",
|
|
|
|
Op: structs.ACLSet,
|
|
|
|
ACL: structs.ACL{
|
|
|
|
Name: "User token",
|
2018-10-19 16:04:07 +00:00
|
|
|
Type: structs.ACLTokenTypeClient,
|
2016-12-10 05:04:00 +00:00
|
|
|
Rules: fmt.Sprintf(`
|
|
|
|
node "%s" {
|
2016-12-11 00:00:11 +00:00
|
|
|
policy = "read"
|
2016-12-10 05:04:00 +00:00
|
|
|
}
|
|
|
|
`, s1.config.NodeName),
|
|
|
|
},
|
|
|
|
WriteRequest: structs.WriteRequest{Token: "root"},
|
|
|
|
}
|
|
|
|
var id string
|
|
|
|
if err := msgpackrpc.CallWithCodec(codec, "ACL.Apply", &arg, &id); err != nil {
|
|
|
|
t.Fatalf("err: %v", err)
|
|
|
|
}
|
|
|
|
|
|
|
|
// Now try with the token and it will go through.
|
|
|
|
args.Token = id
|
|
|
|
if err := msgpackrpc.CallWithCodec(codec, "Catalog.NodeServices", &args, &reply); err != nil {
|
|
|
|
t.Fatalf("err: %v", err)
|
|
|
|
}
|
2016-12-11 00:00:11 +00:00
|
|
|
if reply.NodeServices == nil {
|
|
|
|
t.Fatalf("should not be nil")
|
|
|
|
}
|
2016-12-13 00:53:31 +00:00
|
|
|
|
|
|
|
// Make sure an unknown node doesn't cause trouble.
|
|
|
|
args.Node = "nope"
|
|
|
|
if err := msgpackrpc.CallWithCodec(codec, "Catalog.NodeServices", &args, &reply); err != nil {
|
|
|
|
t.Fatalf("err: %v", err)
|
|
|
|
}
|
|
|
|
if reply.NodeServices != nil {
|
|
|
|
t.Fatalf("should not nil")
|
|
|
|
}
|
2016-12-10 05:04:00 +00:00
|
|
|
}
|
|
|
|
|
2015-06-11 20:23:49 +00:00
|
|
|
func TestCatalog_NodeServices_FilterACL(t *testing.T) {
|
2017-06-27 13:22:18 +00:00
|
|
|
t.Parallel()
|
2015-10-13 23:43:52 +00:00
|
|
|
dir, token, srv, codec := testACLFilterServer(t)
|
2015-06-11 20:23:49 +00:00
|
|
|
defer os.RemoveAll(dir)
|
|
|
|
defer srv.Shutdown()
|
2015-10-13 23:43:52 +00:00
|
|
|
defer codec.Close()
|
2020-05-29 21:16:03 +00:00
|
|
|
testrpc.WaitForTestAgent(t, srv.RPC, "dc1", testrpc.WithToken("root"))
|
2015-06-11 20:23:49 +00:00
|
|
|
|
|
|
|
opt := structs.NodeSpecificRequest{
|
|
|
|
Datacenter: "dc1",
|
|
|
|
Node: srv.config.NodeName,
|
|
|
|
QueryOptions: structs.QueryOptions{Token: token},
|
|
|
|
}
|
2020-05-29 21:16:03 +00:00
|
|
|
|
|
|
|
var reply structs.IndexedNodeServices
|
|
|
|
require.NoError(t, msgpackrpc.CallWithCodec(codec, "Catalog.NodeServices", &opt, &reply))
|
|
|
|
|
|
|
|
require.NotNil(t, reply.NodeServices)
|
|
|
|
require.Len(t, reply.NodeServices.Services, 1)
|
|
|
|
|
|
|
|
svc, ok := reply.NodeServices.Services["foo"]
|
|
|
|
require.True(t, ok)
|
|
|
|
require.Equal(t, "foo", svc.ID)
|
2015-06-11 05:14:58 +00:00
|
|
|
}
|
2020-06-12 02:05:07 +00:00
|
|
|
|
|
|
|
func TestCatalog_GatewayServices_TerminatingGateway(t *testing.T) {
|
|
|
|
t.Parallel()
|
|
|
|
|
|
|
|
dir1, s1 := testServer(t)
|
|
|
|
defer os.RemoveAll(dir1)
|
|
|
|
defer s1.Shutdown()
|
|
|
|
|
|
|
|
codec := rpcClient(t, s1)
|
|
|
|
defer codec.Close()
|
|
|
|
|
|
|
|
testrpc.WaitForTestAgent(t, s1.RPC, "dc1")
|
|
|
|
{
|
|
|
|
var out struct{}
|
|
|
|
|
|
|
|
// Register a service "api"
|
|
|
|
args := structs.TestRegisterRequest(t)
|
|
|
|
args.Service.Service = "api"
|
|
|
|
args.Check = &structs.HealthCheck{
|
|
|
|
Name: "api",
|
|
|
|
Status: api.HealthPassing,
|
|
|
|
ServiceID: args.Service.Service,
|
|
|
|
}
|
|
|
|
assert.Nil(t, msgpackrpc.CallWithCodec(codec, "Catalog.Register", &args, &out))
|
|
|
|
|
|
|
|
// Register a service "db"
|
|
|
|
args = structs.TestRegisterRequest(t)
|
|
|
|
args.Service.Service = "db"
|
|
|
|
args.Check = &structs.HealthCheck{
|
|
|
|
Name: "db",
|
|
|
|
Status: api.HealthPassing,
|
|
|
|
ServiceID: args.Service.Service,
|
|
|
|
}
|
|
|
|
assert.Nil(t, msgpackrpc.CallWithCodec(codec, "Catalog.Register", &args, &out))
|
|
|
|
|
|
|
|
// Register a service "redis"
|
|
|
|
args = structs.TestRegisterRequest(t)
|
|
|
|
args.Service.Service = "redis"
|
|
|
|
args.Check = &structs.HealthCheck{
|
|
|
|
Name: "redis",
|
|
|
|
Status: api.HealthPassing,
|
|
|
|
ServiceID: args.Service.Service,
|
|
|
|
}
|
|
|
|
assert.Nil(t, msgpackrpc.CallWithCodec(codec, "Catalog.Register", &args, &out))
|
|
|
|
|
|
|
|
// Register a gateway
|
|
|
|
args = &structs.RegisterRequest{
|
|
|
|
Datacenter: "dc1",
|
|
|
|
Node: "foo",
|
|
|
|
Address: "127.0.0.1",
|
|
|
|
Service: &structs.NodeService{
|
|
|
|
Kind: structs.ServiceKindTerminatingGateway,
|
|
|
|
Service: "gateway",
|
|
|
|
Port: 443,
|
|
|
|
},
|
|
|
|
Check: &structs.HealthCheck{
|
|
|
|
Name: "gateway",
|
|
|
|
Status: api.HealthPassing,
|
|
|
|
ServiceID: "gateway",
|
|
|
|
},
|
|
|
|
}
|
|
|
|
assert.Nil(t, msgpackrpc.CallWithCodec(codec, "Catalog.Register", &args, &out))
|
|
|
|
|
|
|
|
entryArgs := &structs.ConfigEntryRequest{
|
|
|
|
Op: structs.ConfigEntryUpsert,
|
|
|
|
Datacenter: "dc1",
|
|
|
|
Entry: &structs.TerminatingGatewayConfigEntry{
|
|
|
|
Kind: "terminating-gateway",
|
|
|
|
Name: "gateway",
|
|
|
|
Services: []structs.LinkedService{
|
|
|
|
{
|
|
|
|
Name: "api",
|
|
|
|
CAFile: "api/ca.crt",
|
|
|
|
CertFile: "api/client.crt",
|
|
|
|
KeyFile: "api/client.key",
|
|
|
|
SNI: "my-domain",
|
|
|
|
},
|
|
|
|
{
|
|
|
|
Name: "db",
|
|
|
|
},
|
|
|
|
{
|
|
|
|
Name: "*",
|
|
|
|
CAFile: "ca.crt",
|
|
|
|
CertFile: "client.crt",
|
|
|
|
KeyFile: "client.key",
|
|
|
|
SNI: "my-alt-domain",
|
|
|
|
},
|
|
|
|
},
|
|
|
|
},
|
|
|
|
}
|
|
|
|
var entryResp bool
|
|
|
|
assert.Nil(t, msgpackrpc.CallWithCodec(codec, "ConfigEntry.Apply", &entryArgs, &entryResp))
|
|
|
|
}
|
|
|
|
|
|
|
|
retry.Run(t, func(r *retry.R) {
|
|
|
|
// List should return all three services
|
|
|
|
req := structs.ServiceSpecificRequest{
|
|
|
|
Datacenter: "dc1",
|
|
|
|
ServiceName: "gateway",
|
|
|
|
}
|
|
|
|
var resp structs.IndexedGatewayServices
|
|
|
|
assert.Nil(r, msgpackrpc.CallWithCodec(codec, "Catalog.GatewayServices", &req, &resp))
|
|
|
|
assert.Len(r, resp.Services, 3)
|
|
|
|
|
|
|
|
expect := structs.GatewayServices{
|
|
|
|
{
|
2020-06-12 14:57:41 +00:00
|
|
|
Service: structs.NewServiceName("api", nil),
|
|
|
|
Gateway: structs.NewServiceName("gateway", nil),
|
2020-06-12 02:05:07 +00:00
|
|
|
GatewayKind: structs.ServiceKindTerminatingGateway,
|
|
|
|
CAFile: "api/ca.crt",
|
|
|
|
CertFile: "api/client.crt",
|
|
|
|
KeyFile: "api/client.key",
|
|
|
|
SNI: "my-domain",
|
|
|
|
},
|
|
|
|
{
|
2020-06-12 14:57:41 +00:00
|
|
|
Service: structs.NewServiceName("db", nil),
|
|
|
|
Gateway: structs.NewServiceName("gateway", nil),
|
2020-06-12 02:05:07 +00:00
|
|
|
GatewayKind: structs.ServiceKindTerminatingGateway,
|
|
|
|
CAFile: "",
|
|
|
|
CertFile: "",
|
|
|
|
KeyFile: "",
|
|
|
|
},
|
|
|
|
{
|
2020-06-12 14:57:41 +00:00
|
|
|
Service: structs.NewServiceName("redis", nil),
|
|
|
|
Gateway: structs.NewServiceName("gateway", nil),
|
2020-06-12 02:05:07 +00:00
|
|
|
GatewayKind: structs.ServiceKindTerminatingGateway,
|
|
|
|
CAFile: "ca.crt",
|
|
|
|
CertFile: "client.crt",
|
|
|
|
KeyFile: "client.key",
|
|
|
|
SNI: "my-alt-domain",
|
|
|
|
FromWildcard: true,
|
|
|
|
},
|
|
|
|
}
|
|
|
|
|
|
|
|
// Ignore raft index for equality
|
|
|
|
for _, s := range resp.Services {
|
|
|
|
s.RaftIndex = structs.RaftIndex{}
|
|
|
|
}
|
|
|
|
assert.Equal(r, expect, resp.Services)
|
|
|
|
})
|
|
|
|
}
|
|
|
|
|
|
|
|
func TestCatalog_GatewayServices_BothGateways(t *testing.T) {
|
|
|
|
t.Parallel()
|
|
|
|
|
|
|
|
dir1, s1 := testServer(t)
|
|
|
|
defer os.RemoveAll(dir1)
|
|
|
|
defer s1.Shutdown()
|
|
|
|
|
|
|
|
codec := rpcClient(t, s1)
|
|
|
|
defer codec.Close()
|
|
|
|
|
|
|
|
testrpc.WaitForTestAgent(t, s1.RPC, "dc1")
|
|
|
|
{
|
|
|
|
var out struct{}
|
|
|
|
|
|
|
|
// Register a service "api"
|
|
|
|
args := structs.TestRegisterRequest(t)
|
|
|
|
args.Service.Service = "api"
|
|
|
|
args.Check = &structs.HealthCheck{
|
|
|
|
Name: "api",
|
|
|
|
Status: api.HealthPassing,
|
|
|
|
ServiceID: args.Service.Service,
|
|
|
|
}
|
|
|
|
assert.Nil(t, msgpackrpc.CallWithCodec(codec, "Catalog.Register", &args, &out))
|
|
|
|
|
|
|
|
// Register a terminating gateway
|
|
|
|
args = &structs.RegisterRequest{
|
|
|
|
Datacenter: "dc1",
|
|
|
|
Node: "foo",
|
|
|
|
Address: "127.0.0.1",
|
|
|
|
Service: &structs.NodeService{
|
|
|
|
Kind: structs.ServiceKindTerminatingGateway,
|
|
|
|
Service: "gateway",
|
|
|
|
Port: 443,
|
|
|
|
},
|
|
|
|
Check: &structs.HealthCheck{
|
|
|
|
Name: "gateway",
|
|
|
|
Status: api.HealthPassing,
|
|
|
|
ServiceID: "gateway",
|
|
|
|
},
|
|
|
|
}
|
|
|
|
assert.Nil(t, msgpackrpc.CallWithCodec(codec, "Catalog.Register", &args, &out))
|
|
|
|
|
|
|
|
entryArgs := &structs.ConfigEntryRequest{
|
|
|
|
Op: structs.ConfigEntryUpsert,
|
|
|
|
Datacenter: "dc1",
|
|
|
|
Entry: &structs.TerminatingGatewayConfigEntry{
|
|
|
|
Kind: "terminating-gateway",
|
|
|
|
Name: "gateway",
|
|
|
|
Services: []structs.LinkedService{
|
|
|
|
{
|
|
|
|
Name: "api",
|
|
|
|
},
|
|
|
|
},
|
|
|
|
},
|
|
|
|
}
|
|
|
|
var entryResp bool
|
|
|
|
assert.Nil(t, msgpackrpc.CallWithCodec(codec, "ConfigEntry.Apply", &entryArgs, &entryResp))
|
|
|
|
|
|
|
|
// Register a service "db"
|
|
|
|
args = structs.TestRegisterRequest(t)
|
|
|
|
args.Service.Service = "db"
|
|
|
|
args.Check = &structs.HealthCheck{
|
|
|
|
Name: "db",
|
|
|
|
Status: api.HealthPassing,
|
|
|
|
ServiceID: args.Service.Service,
|
|
|
|
}
|
|
|
|
assert.Nil(t, msgpackrpc.CallWithCodec(codec, "Catalog.Register", &args, &out))
|
|
|
|
|
|
|
|
// Register an ingress gateway
|
|
|
|
args = &structs.RegisterRequest{
|
|
|
|
Datacenter: "dc1",
|
|
|
|
Node: "foo",
|
|
|
|
Address: "127.0.0.2",
|
|
|
|
Service: &structs.NodeService{
|
|
|
|
Kind: structs.ServiceKindTerminatingGateway,
|
|
|
|
Service: "ingress",
|
|
|
|
Port: 444,
|
|
|
|
},
|
|
|
|
Check: &structs.HealthCheck{
|
|
|
|
Name: "ingress",
|
|
|
|
Status: api.HealthPassing,
|
|
|
|
ServiceID: "ingress",
|
|
|
|
},
|
|
|
|
}
|
|
|
|
assert.Nil(t, msgpackrpc.CallWithCodec(codec, "Catalog.Register", &args, &out))
|
|
|
|
|
|
|
|
entryArgs = &structs.ConfigEntryRequest{
|
|
|
|
Op: structs.ConfigEntryUpsert,
|
|
|
|
Datacenter: "dc1",
|
|
|
|
Entry: &structs.IngressGatewayConfigEntry{
|
|
|
|
Kind: "ingress-gateway",
|
|
|
|
Name: "ingress",
|
|
|
|
Listeners: []structs.IngressListener{
|
|
|
|
{
|
|
|
|
Port: 8888,
|
|
|
|
Services: []structs.IngressService{
|
|
|
|
{Name: "db"},
|
|
|
|
},
|
|
|
|
},
|
|
|
|
},
|
|
|
|
},
|
|
|
|
}
|
|
|
|
assert.Nil(t, msgpackrpc.CallWithCodec(codec, "ConfigEntry.Apply", &entryArgs, &entryResp))
|
|
|
|
}
|
|
|
|
|
|
|
|
retry.Run(t, func(r *retry.R) {
|
|
|
|
req := structs.ServiceSpecificRequest{
|
|
|
|
Datacenter: "dc1",
|
|
|
|
ServiceName: "gateway",
|
|
|
|
}
|
|
|
|
var resp structs.IndexedGatewayServices
|
|
|
|
assert.Nil(r, msgpackrpc.CallWithCodec(codec, "Catalog.GatewayServices", &req, &resp))
|
|
|
|
assert.Len(r, resp.Services, 1)
|
|
|
|
|
|
|
|
expect := structs.GatewayServices{
|
|
|
|
{
|
2020-06-12 14:57:41 +00:00
|
|
|
Service: structs.NewServiceName("api", nil),
|
|
|
|
Gateway: structs.NewServiceName("gateway", nil),
|
2020-06-12 02:05:07 +00:00
|
|
|
GatewayKind: structs.ServiceKindTerminatingGateway,
|
|
|
|
},
|
|
|
|
}
|
|
|
|
|
|
|
|
// Ignore raft index for equality
|
|
|
|
for _, s := range resp.Services {
|
|
|
|
s.RaftIndex = structs.RaftIndex{}
|
|
|
|
}
|
|
|
|
assert.Equal(r, expect, resp.Services)
|
|
|
|
|
|
|
|
req.ServiceName = "ingress"
|
|
|
|
assert.Nil(r, msgpackrpc.CallWithCodec(codec, "Catalog.GatewayServices", &req, &resp))
|
|
|
|
assert.Len(r, resp.Services, 1)
|
|
|
|
|
|
|
|
expect = structs.GatewayServices{
|
|
|
|
{
|
2020-06-12 14:57:41 +00:00
|
|
|
Service: structs.NewServiceName("db", nil),
|
|
|
|
Gateway: structs.NewServiceName("ingress", nil),
|
2020-06-12 02:05:07 +00:00
|
|
|
GatewayKind: structs.ServiceKindIngressGateway,
|
|
|
|
Protocol: "tcp",
|
|
|
|
Port: 8888,
|
|
|
|
},
|
|
|
|
}
|
|
|
|
|
|
|
|
// Ignore raft index for equality
|
|
|
|
for _, s := range resp.Services {
|
|
|
|
s.RaftIndex = structs.RaftIndex{}
|
|
|
|
}
|
|
|
|
assert.Equal(r, expect, resp.Services)
|
|
|
|
})
|
|
|
|
|
|
|
|
// Test a non-gateway service being requested
|
|
|
|
req := structs.ServiceSpecificRequest{
|
|
|
|
Datacenter: "dc1",
|
|
|
|
ServiceName: "api",
|
|
|
|
}
|
|
|
|
var resp structs.IndexedGatewayServices
|
|
|
|
err := msgpackrpc.CallWithCodec(codec, "Catalog.GatewayServices", &req, &resp)
|
|
|
|
assert.NoError(t, err)
|
|
|
|
assert.Empty(t, resp.Services)
|
|
|
|
// Ensure that the index is not zero so that a blocking query still gets the
|
|
|
|
// latest GatewayServices index
|
|
|
|
assert.NotEqual(t, 0, resp.Index)
|
|
|
|
}
|
|
|
|
|
|
|
|
func TestCatalog_GatewayServices_ACLFiltering(t *testing.T) {
|
|
|
|
t.Parallel()
|
|
|
|
|
|
|
|
dir1, s1 := testServerWithConfig(t, func(c *Config) {
|
|
|
|
c.ACLDatacenter = "dc1"
|
|
|
|
c.ACLsEnabled = true
|
|
|
|
c.ACLMasterToken = "root"
|
|
|
|
c.ACLDefaultPolicy = "deny"
|
|
|
|
})
|
|
|
|
defer os.RemoveAll(dir1)
|
|
|
|
defer s1.Shutdown()
|
|
|
|
codec := rpcClient(t, s1)
|
|
|
|
defer codec.Close()
|
|
|
|
|
|
|
|
testrpc.WaitForTestAgent(t, s1.RPC, "dc1", testrpc.WithToken("root"))
|
|
|
|
|
|
|
|
{
|
|
|
|
var out struct{}
|
|
|
|
|
|
|
|
// Register a service "api"
|
|
|
|
args := structs.TestRegisterRequest(t)
|
|
|
|
args.Service.Service = "api"
|
|
|
|
args.Check = &structs.HealthCheck{
|
|
|
|
Name: "api",
|
|
|
|
Status: api.HealthPassing,
|
|
|
|
ServiceID: args.Service.Service,
|
|
|
|
}
|
|
|
|
args.Token = "root"
|
|
|
|
assert.Nil(t, msgpackrpc.CallWithCodec(codec, "Catalog.Register", &args, &out))
|
|
|
|
|
|
|
|
// Register a service "db"
|
|
|
|
args = structs.TestRegisterRequest(t)
|
|
|
|
args.Service.Service = "db"
|
|
|
|
args.Check = &structs.HealthCheck{
|
|
|
|
Name: "db",
|
|
|
|
Status: api.HealthPassing,
|
|
|
|
ServiceID: args.Service.Service,
|
|
|
|
}
|
|
|
|
args.Token = "root"
|
|
|
|
assert.Nil(t, msgpackrpc.CallWithCodec(codec, "Catalog.Register", &args, &out))
|
|
|
|
|
|
|
|
// Register a service "redis"
|
|
|
|
args = structs.TestRegisterRequest(t)
|
|
|
|
args.Service.Service = "redis"
|
|
|
|
args.Check = &structs.HealthCheck{
|
|
|
|
Name: "redis",
|
|
|
|
Status: api.HealthPassing,
|
|
|
|
ServiceID: args.Service.Service,
|
|
|
|
}
|
|
|
|
args.Token = "root"
|
|
|
|
assert.Nil(t, msgpackrpc.CallWithCodec(codec, "Catalog.Register", &args, &out))
|
|
|
|
|
|
|
|
// Register a gateway
|
|
|
|
args = &structs.RegisterRequest{
|
|
|
|
Datacenter: "dc1",
|
|
|
|
Node: "foo",
|
|
|
|
Address: "127.0.0.1",
|
|
|
|
Service: &structs.NodeService{
|
|
|
|
Kind: structs.ServiceKindTerminatingGateway,
|
|
|
|
Service: "gateway",
|
|
|
|
Port: 443,
|
|
|
|
},
|
|
|
|
Check: &structs.HealthCheck{
|
|
|
|
Name: "gateway",
|
|
|
|
Status: api.HealthPassing,
|
|
|
|
ServiceID: "gateway",
|
|
|
|
},
|
|
|
|
}
|
|
|
|
args.Token = "root"
|
|
|
|
assert.Nil(t, msgpackrpc.CallWithCodec(codec, "Catalog.Register", &args, &out))
|
|
|
|
|
|
|
|
entryArgs := &structs.ConfigEntryRequest{
|
|
|
|
Op: structs.ConfigEntryUpsert,
|
|
|
|
Datacenter: "dc1",
|
|
|
|
Entry: &structs.TerminatingGatewayConfigEntry{
|
|
|
|
Kind: "terminating-gateway",
|
|
|
|
Name: "gateway",
|
|
|
|
Services: []structs.LinkedService{
|
|
|
|
{
|
|
|
|
Name: "api",
|
|
|
|
CAFile: "api/ca.crt",
|
|
|
|
CertFile: "api/client.crt",
|
|
|
|
KeyFile: "api/client.key",
|
|
|
|
},
|
|
|
|
{
|
|
|
|
Name: "db",
|
|
|
|
},
|
|
|
|
{
|
|
|
|
Name: "db_replica",
|
|
|
|
},
|
|
|
|
{
|
|
|
|
Name: "*",
|
|
|
|
CAFile: "ca.crt",
|
|
|
|
CertFile: "client.crt",
|
|
|
|
KeyFile: "client.key",
|
|
|
|
},
|
|
|
|
},
|
|
|
|
},
|
|
|
|
WriteRequest: structs.WriteRequest{Token: "root"},
|
|
|
|
}
|
|
|
|
|
|
|
|
var entryResp bool
|
|
|
|
assert.Nil(t, msgpackrpc.CallWithCodec(codec, "ConfigEntry.Apply", &entryArgs, &entryResp))
|
|
|
|
}
|
|
|
|
|
|
|
|
rules := `
|
|
|
|
service_prefix "db" {
|
|
|
|
policy = "read"
|
|
|
|
}
|
|
|
|
`
|
|
|
|
svcToken, err := upsertTestTokenWithPolicyRules(codec, "root", "dc1", rules)
|
|
|
|
require.NoError(t, err)
|
|
|
|
|
|
|
|
retry.Run(t, func(r *retry.R) {
|
|
|
|
// List should return an empty list, since we do not have read on the gateway
|
|
|
|
req := structs.ServiceSpecificRequest{
|
|
|
|
Datacenter: "dc1",
|
|
|
|
ServiceName: "gateway",
|
|
|
|
QueryOptions: structs.QueryOptions{Token: svcToken.SecretID},
|
|
|
|
}
|
|
|
|
var resp structs.IndexedGatewayServices
|
|
|
|
err := msgpackrpc.CallWithCodec(codec, "Catalog.GatewayServices", &req, &resp)
|
|
|
|
require.True(r, acl.IsErrPermissionDenied(err))
|
|
|
|
})
|
|
|
|
|
|
|
|
rules = `
|
|
|
|
service "gateway" {
|
|
|
|
policy = "read"
|
|
|
|
}
|
|
|
|
`
|
|
|
|
gwToken, err := upsertTestTokenWithPolicyRules(codec, "root", "dc1", rules)
|
|
|
|
require.NoError(t, err)
|
|
|
|
|
|
|
|
retry.Run(t, func(r *retry.R) {
|
|
|
|
// List should return an empty list, since we do not have read on db
|
|
|
|
req := structs.ServiceSpecificRequest{
|
|
|
|
Datacenter: "dc1",
|
|
|
|
ServiceName: "gateway",
|
|
|
|
QueryOptions: structs.QueryOptions{Token: gwToken.SecretID},
|
|
|
|
}
|
|
|
|
var resp structs.IndexedGatewayServices
|
|
|
|
assert.Nil(r, msgpackrpc.CallWithCodec(codec, "Catalog.GatewayServices", &req, &resp))
|
|
|
|
assert.Len(r, resp.Services, 0)
|
|
|
|
})
|
|
|
|
|
|
|
|
rules = `
|
|
|
|
service_prefix "db" {
|
|
|
|
policy = "read"
|
|
|
|
}
|
|
|
|
service "gateway" {
|
|
|
|
policy = "read"
|
|
|
|
}
|
|
|
|
`
|
|
|
|
validToken, err := upsertTestTokenWithPolicyRules(codec, "root", "dc1", rules)
|
|
|
|
require.NoError(t, err)
|
|
|
|
|
|
|
|
retry.Run(t, func(r *retry.R) {
|
|
|
|
// List should return db entry since we have read on db and gateway
|
|
|
|
req := structs.ServiceSpecificRequest{
|
|
|
|
Datacenter: "dc1",
|
|
|
|
ServiceName: "gateway",
|
|
|
|
QueryOptions: structs.QueryOptions{Token: validToken.SecretID},
|
|
|
|
}
|
|
|
|
var resp structs.IndexedGatewayServices
|
|
|
|
assert.Nil(r, msgpackrpc.CallWithCodec(codec, "Catalog.GatewayServices", &req, &resp))
|
|
|
|
assert.Len(r, resp.Services, 2)
|
|
|
|
|
|
|
|
expect := structs.GatewayServices{
|
|
|
|
{
|
2020-06-12 14:57:41 +00:00
|
|
|
Service: structs.NewServiceName("db", nil),
|
|
|
|
Gateway: structs.NewServiceName("gateway", nil),
|
2020-06-12 02:05:07 +00:00
|
|
|
GatewayKind: structs.ServiceKindTerminatingGateway,
|
|
|
|
},
|
|
|
|
{
|
2020-06-12 14:57:41 +00:00
|
|
|
Service: structs.NewServiceName("db_replica", nil),
|
|
|
|
Gateway: structs.NewServiceName("gateway", nil),
|
2020-06-12 02:05:07 +00:00
|
|
|
GatewayKind: structs.ServiceKindTerminatingGateway,
|
|
|
|
},
|
|
|
|
}
|
|
|
|
|
|
|
|
// Ignore raft index for equality
|
|
|
|
for _, s := range resp.Services {
|
|
|
|
s.RaftIndex = structs.RaftIndex{}
|
|
|
|
}
|
|
|
|
assert.Equal(r, expect, resp.Services)
|
|
|
|
})
|
|
|
|
}
|