2015-11-07 06:18:11 +00:00
|
|
|
package consul
|
|
|
|
|
|
|
|
import (
|
2015-11-11 05:16:04 +00:00
|
|
|
"bytes"
|
2015-11-10 22:46:08 +00:00
|
|
|
"fmt"
|
2015-11-11 05:16:04 +00:00
|
|
|
"log"
|
2015-11-10 22:46:08 +00:00
|
|
|
"net/rpc"
|
2015-11-07 06:18:11 +00:00
|
|
|
"os"
|
2015-11-10 18:29:55 +00:00
|
|
|
"reflect"
|
2015-11-11 02:23:37 +00:00
|
|
|
"sort"
|
2015-11-10 07:03:20 +00:00
|
|
|
"strings"
|
2015-11-07 06:18:11 +00:00
|
|
|
"testing"
|
2015-11-11 01:42:52 +00:00
|
|
|
"time"
|
2015-11-07 06:18:11 +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"
|
|
|
|
"github.com/hashicorp/consul/testrpc"
|
2017-04-29 16:34:02 +00:00
|
|
|
"github.com/hashicorp/consul/testutil/retry"
|
2018-04-10 12:28:27 +00:00
|
|
|
"github.com/hashicorp/consul/types"
|
2015-11-07 06:18:11 +00:00
|
|
|
"github.com/hashicorp/net-rpc-msgpackrpc"
|
2015-11-11 01:42:52 +00:00
|
|
|
"github.com/hashicorp/serf/coordinate"
|
2018-06-05 21:17:19 +00:00
|
|
|
"github.com/stretchr/testify/require"
|
2015-11-07 06:18:11 +00:00
|
|
|
)
|
|
|
|
|
2015-11-10 04:37:41 +00:00
|
|
|
func TestPreparedQuery_Apply(t *testing.T) {
|
2017-06-27 13:22:18 +00:00
|
|
|
t.Parallel()
|
2015-11-07 06:18:11 +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")
|
2015-11-07 06:18:11 +00:00
|
|
|
|
2015-11-10 07:03:20 +00:00
|
|
|
// Set up a bare bones query.
|
2015-11-10 19:16:17 +00:00
|
|
|
query := structs.PreparedQueryRequest{
|
2015-11-07 06:18:11 +00:00
|
|
|
Datacenter: "dc1",
|
2015-11-10 04:37:41 +00:00
|
|
|
Op: structs.PreparedQueryCreate,
|
2015-11-10 18:29:55 +00:00
|
|
|
Query: &structs.PreparedQuery{
|
2016-12-10 19:19:08 +00:00
|
|
|
Name: "test",
|
2015-11-07 06:18:11 +00:00
|
|
|
Service: structs.ServiceQuery{
|
|
|
|
Service: "redis",
|
|
|
|
},
|
|
|
|
},
|
|
|
|
}
|
|
|
|
var reply string
|
2015-11-10 07:03:20 +00:00
|
|
|
|
|
|
|
// Set an ID which should fail the create.
|
2015-11-10 19:16:17 +00:00
|
|
|
query.Query.ID = "nope"
|
|
|
|
err := msgpackrpc.CallWithCodec(codec, "PreparedQuery.Apply", &query, &reply)
|
2015-11-10 07:03:20 +00:00
|
|
|
if err == nil || !strings.Contains(err.Error(), "ID must be empty") {
|
|
|
|
t.Fatalf("bad: %v", err)
|
|
|
|
}
|
|
|
|
|
|
|
|
// Change it to a bogus modify which should also fail.
|
2015-11-10 19:16:17 +00:00
|
|
|
query.Op = structs.PreparedQueryUpdate
|
|
|
|
query.Query.ID = generateUUID()
|
|
|
|
err = msgpackrpc.CallWithCodec(codec, "PreparedQuery.Apply", &query, &reply)
|
2015-11-10 07:03:20 +00:00
|
|
|
if err == nil || !strings.Contains(err.Error(), "Cannot modify non-existent prepared query") {
|
|
|
|
t.Fatalf("bad: %v", err)
|
|
|
|
}
|
|
|
|
|
|
|
|
// Fix up the ID but invalidate the query itself. This proves we call
|
|
|
|
// parseQuery for a create, but that function is checked in detail as
|
Creates new "prepared-query" ACL type and new token capture behavior.
Prior to this change, prepared queries had the following behavior for
ACLs, which will need to change to support templates:
1. A management token, or a token with read access to the service being
queried needed to be provided in order to create a prepared query.
2. The token used to create the prepared query was stored with the query
in the state store and used to execute the query.
3. A management token, or the token used to create the query needed to be
supplied to perform and CRUD operations on an existing prepared query.
This was pretty subtle and complicated behavior, and won't work for
templates since the service name is computed at execution time. To solve
this, we introduce a new "prepared-query" ACL type, where the prefix
applies to the query name for static prepared query types and to the
prefix for template prepared query types.
With this change, the new behavior is:
1. A management token, or a token with "prepared-query" write access to
the query name or (soon) the given template prefix is required to do
any CRUD operations on a prepared query, or to list prepared queries
(the list is filtered by this ACL).
2. You will no longer need a management token to list prepared queries,
but you will only be able to see prepared queries that you have access
to (you get an empty list instead of permission denied).
3. When listing or getting a query, because it was easy to capture
management tokens given the past behavior, this will always blank out
the "Token" field (replacing the contents as <hidden>) for all tokens
unless a management token is supplied. Going forward, we should
discourage people from binding tokens for execution unless strictly
necessary.
4. No token will be captured by default when a prepared query is created.
If the user wishes to supply an execution token then can pass it in via
the "Token" field in the prepared query definition. Otherwise, this
field will default to empty.
5. At execution time, we will use the captured token if it exists with the
prepared query definition, otherwise we will use the token that's passed
in with the request, just like we do for other RPCs (or you can use the
agent's configured token for DNS).
6. Prepared queries with no name (accessible only by ID) will not require
ACLs to create or modify (execution time will depend on the service ACL
configuration). Our argument here is that these are designed to be
ephemeral and the IDs are as good as an ACL. Management tokens will be
able to list all of these.
These changes enable templates, but also enable delegation of authority to
manage the prepared query namespace.
2016-02-23 08:12:58 +00:00
|
|
|
// part of another test so we don't have to exercise all the checks
|
|
|
|
// here.
|
2015-11-10 19:16:17 +00:00
|
|
|
query.Op = structs.PreparedQueryCreate
|
|
|
|
query.Query.ID = ""
|
|
|
|
query.Query.Service.Failover.NearestN = -1
|
|
|
|
err = msgpackrpc.CallWithCodec(codec, "PreparedQuery.Apply", &query, &reply)
|
2015-11-10 07:03:20 +00:00
|
|
|
if err == nil || !strings.Contains(err.Error(), "Bad NearestN") {
|
|
|
|
t.Fatalf("bad: %v", err)
|
|
|
|
}
|
|
|
|
|
|
|
|
// Fix that and make sure it propagates an error from the Raft apply.
|
2015-11-10 19:16:17 +00:00
|
|
|
query.Query.Service.Failover.NearestN = 0
|
Creates new "prepared-query" ACL type and new token capture behavior.
Prior to this change, prepared queries had the following behavior for
ACLs, which will need to change to support templates:
1. A management token, or a token with read access to the service being
queried needed to be provided in order to create a prepared query.
2. The token used to create the prepared query was stored with the query
in the state store and used to execute the query.
3. A management token, or the token used to create the query needed to be
supplied to perform and CRUD operations on an existing prepared query.
This was pretty subtle and complicated behavior, and won't work for
templates since the service name is computed at execution time. To solve
this, we introduce a new "prepared-query" ACL type, where the prefix
applies to the query name for static prepared query types and to the
prefix for template prepared query types.
With this change, the new behavior is:
1. A management token, or a token with "prepared-query" write access to
the query name or (soon) the given template prefix is required to do
any CRUD operations on a prepared query, or to list prepared queries
(the list is filtered by this ACL).
2. You will no longer need a management token to list prepared queries,
but you will only be able to see prepared queries that you have access
to (you get an empty list instead of permission denied).
3. When listing or getting a query, because it was easy to capture
management tokens given the past behavior, this will always blank out
the "Token" field (replacing the contents as <hidden>) for all tokens
unless a management token is supplied. Going forward, we should
discourage people from binding tokens for execution unless strictly
necessary.
4. No token will be captured by default when a prepared query is created.
If the user wishes to supply an execution token then can pass it in via
the "Token" field in the prepared query definition. Otherwise, this
field will default to empty.
5. At execution time, we will use the captured token if it exists with the
prepared query definition, otherwise we will use the token that's passed
in with the request, just like we do for other RPCs (or you can use the
agent's configured token for DNS).
6. Prepared queries with no name (accessible only by ID) will not require
ACLs to create or modify (execution time will depend on the service ACL
configuration). Our argument here is that these are designed to be
ephemeral and the IDs are as good as an ACL. Management tokens will be
able to list all of these.
These changes enable templates, but also enable delegation of authority to
manage the prepared query namespace.
2016-02-23 08:12:58 +00:00
|
|
|
query.Query.Session = "nope"
|
2015-11-10 19:16:17 +00:00
|
|
|
err = msgpackrpc.CallWithCodec(codec, "PreparedQuery.Apply", &query, &reply)
|
Creates new "prepared-query" ACL type and new token capture behavior.
Prior to this change, prepared queries had the following behavior for
ACLs, which will need to change to support templates:
1. A management token, or a token with read access to the service being
queried needed to be provided in order to create a prepared query.
2. The token used to create the prepared query was stored with the query
in the state store and used to execute the query.
3. A management token, or the token used to create the query needed to be
supplied to perform and CRUD operations on an existing prepared query.
This was pretty subtle and complicated behavior, and won't work for
templates since the service name is computed at execution time. To solve
this, we introduce a new "prepared-query" ACL type, where the prefix
applies to the query name for static prepared query types and to the
prefix for template prepared query types.
With this change, the new behavior is:
1. A management token, or a token with "prepared-query" write access to
the query name or (soon) the given template prefix is required to do
any CRUD operations on a prepared query, or to list prepared queries
(the list is filtered by this ACL).
2. You will no longer need a management token to list prepared queries,
but you will only be able to see prepared queries that you have access
to (you get an empty list instead of permission denied).
3. When listing or getting a query, because it was easy to capture
management tokens given the past behavior, this will always blank out
the "Token" field (replacing the contents as <hidden>) for all tokens
unless a management token is supplied. Going forward, we should
discourage people from binding tokens for execution unless strictly
necessary.
4. No token will be captured by default when a prepared query is created.
If the user wishes to supply an execution token then can pass it in via
the "Token" field in the prepared query definition. Otherwise, this
field will default to empty.
5. At execution time, we will use the captured token if it exists with the
prepared query definition, otherwise we will use the token that's passed
in with the request, just like we do for other RPCs (or you can use the
agent's configured token for DNS).
6. Prepared queries with no name (accessible only by ID) will not require
ACLs to create or modify (execution time will depend on the service ACL
configuration). Our argument here is that these are designed to be
ephemeral and the IDs are as good as an ACL. Management tokens will be
able to list all of these.
These changes enable templates, but also enable delegation of authority to
manage the prepared query namespace.
2016-02-23 08:12:58 +00:00
|
|
|
if err == nil || !strings.Contains(err.Error(), "failed session lookup") {
|
2015-11-10 07:03:20 +00:00
|
|
|
t.Fatalf("bad: %v", err)
|
|
|
|
}
|
|
|
|
|
|
|
|
// Fix that and make sure the apply goes through.
|
Creates new "prepared-query" ACL type and new token capture behavior.
Prior to this change, prepared queries had the following behavior for
ACLs, which will need to change to support templates:
1. A management token, or a token with read access to the service being
queried needed to be provided in order to create a prepared query.
2. The token used to create the prepared query was stored with the query
in the state store and used to execute the query.
3. A management token, or the token used to create the query needed to be
supplied to perform and CRUD operations on an existing prepared query.
This was pretty subtle and complicated behavior, and won't work for
templates since the service name is computed at execution time. To solve
this, we introduce a new "prepared-query" ACL type, where the prefix
applies to the query name for static prepared query types and to the
prefix for template prepared query types.
With this change, the new behavior is:
1. A management token, or a token with "prepared-query" write access to
the query name or (soon) the given template prefix is required to do
any CRUD operations on a prepared query, or to list prepared queries
(the list is filtered by this ACL).
2. You will no longer need a management token to list prepared queries,
but you will only be able to see prepared queries that you have access
to (you get an empty list instead of permission denied).
3. When listing or getting a query, because it was easy to capture
management tokens given the past behavior, this will always blank out
the "Token" field (replacing the contents as <hidden>) for all tokens
unless a management token is supplied. Going forward, we should
discourage people from binding tokens for execution unless strictly
necessary.
4. No token will be captured by default when a prepared query is created.
If the user wishes to supply an execution token then can pass it in via
the "Token" field in the prepared query definition. Otherwise, this
field will default to empty.
5. At execution time, we will use the captured token if it exists with the
prepared query definition, otherwise we will use the token that's passed
in with the request, just like we do for other RPCs (or you can use the
agent's configured token for DNS).
6. Prepared queries with no name (accessible only by ID) will not require
ACLs to create or modify (execution time will depend on the service ACL
configuration). Our argument here is that these are designed to be
ephemeral and the IDs are as good as an ACL. Management tokens will be
able to list all of these.
These changes enable templates, but also enable delegation of authority to
manage the prepared query namespace.
2016-02-23 08:12:58 +00:00
|
|
|
query.Query.Session = ""
|
2015-11-10 19:16:17 +00:00
|
|
|
if err = msgpackrpc.CallWithCodec(codec, "PreparedQuery.Apply", &query, &reply); err != nil {
|
2015-11-10 07:03:20 +00:00
|
|
|
t.Fatalf("err: %v", err)
|
|
|
|
}
|
|
|
|
|
2015-11-10 18:29:55 +00:00
|
|
|
// Capture the ID and read the query back to verify.
|
2015-11-10 19:16:17 +00:00
|
|
|
query.Query.ID = reply
|
2015-11-10 18:29:55 +00:00
|
|
|
{
|
|
|
|
req := &structs.PreparedQuerySpecificRequest{
|
2015-11-11 20:20:40 +00:00
|
|
|
Datacenter: "dc1",
|
|
|
|
QueryID: query.Query.ID,
|
2015-11-10 18:29:55 +00:00
|
|
|
}
|
|
|
|
var resp structs.IndexedPreparedQueries
|
2015-11-11 20:20:40 +00:00
|
|
|
if err = msgpackrpc.CallWithCodec(codec, "PreparedQuery.Get", req, &resp); err != nil {
|
2015-11-10 18:29:55 +00:00
|
|
|
t.Fatalf("err: %v", err)
|
|
|
|
}
|
|
|
|
|
|
|
|
if len(resp.Queries) != 1 {
|
|
|
|
t.Fatalf("bad: %v", resp)
|
|
|
|
}
|
|
|
|
actual := resp.Queries[0]
|
|
|
|
if resp.Index != actual.ModifyIndex {
|
|
|
|
t.Fatalf("bad index: %d", resp.Index)
|
|
|
|
}
|
|
|
|
actual.CreateIndex, actual.ModifyIndex = 0, 0
|
2015-11-10 19:16:17 +00:00
|
|
|
if !reflect.DeepEqual(actual, query.Query) {
|
2015-11-10 18:29:55 +00:00
|
|
|
t.Fatalf("bad: %v", actual)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
// Make the op an update. This should go through now that we have an ID.
|
2015-11-10 19:16:17 +00:00
|
|
|
query.Op = structs.PreparedQueryUpdate
|
|
|
|
query.Query.Service.Failover.NearestN = 2
|
|
|
|
if err = msgpackrpc.CallWithCodec(codec, "PreparedQuery.Apply", &query, &reply); err != nil {
|
2015-11-10 07:03:20 +00:00
|
|
|
t.Fatalf("err: %v", err)
|
|
|
|
}
|
|
|
|
|
2015-11-10 18:29:55 +00:00
|
|
|
// Read back again to verify the update worked.
|
|
|
|
{
|
|
|
|
req := &structs.PreparedQuerySpecificRequest{
|
2015-11-11 20:20:40 +00:00
|
|
|
Datacenter: "dc1",
|
|
|
|
QueryID: query.Query.ID,
|
2015-11-10 18:29:55 +00:00
|
|
|
}
|
|
|
|
var resp structs.IndexedPreparedQueries
|
2015-11-11 20:20:40 +00:00
|
|
|
if err = msgpackrpc.CallWithCodec(codec, "PreparedQuery.Get", req, &resp); err != nil {
|
2015-11-10 18:29:55 +00:00
|
|
|
t.Fatalf("err: %v", err)
|
|
|
|
}
|
|
|
|
|
|
|
|
if len(resp.Queries) != 1 {
|
|
|
|
t.Fatalf("bad: %v", resp)
|
|
|
|
}
|
|
|
|
actual := resp.Queries[0]
|
|
|
|
if resp.Index != actual.ModifyIndex {
|
|
|
|
t.Fatalf("bad index: %d", resp.Index)
|
|
|
|
}
|
|
|
|
actual.CreateIndex, actual.ModifyIndex = 0, 0
|
2015-11-10 19:16:17 +00:00
|
|
|
if !reflect.DeepEqual(actual, query.Query) {
|
2015-11-10 18:29:55 +00:00
|
|
|
t.Fatalf("bad: %v", actual)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2015-11-10 07:03:20 +00:00
|
|
|
// Give a bogus op and make sure it fails.
|
2015-11-10 19:16:17 +00:00
|
|
|
query.Op = "nope"
|
|
|
|
err = msgpackrpc.CallWithCodec(codec, "PreparedQuery.Apply", &query, &reply)
|
2015-11-10 07:03:20 +00:00
|
|
|
if err == nil || !strings.Contains(err.Error(), "Unknown prepared query operation:") {
|
|
|
|
t.Fatalf("bad: %v", err)
|
|
|
|
}
|
|
|
|
|
|
|
|
// Prove that an update also goes through the parseQuery validation.
|
2015-11-10 19:16:17 +00:00
|
|
|
query.Op = structs.PreparedQueryUpdate
|
|
|
|
query.Query.Service.Failover.NearestN = -1
|
|
|
|
err = msgpackrpc.CallWithCodec(codec, "PreparedQuery.Apply", &query, &reply)
|
2015-11-10 07:03:20 +00:00
|
|
|
if err == nil || !strings.Contains(err.Error(), "Bad NearestN") {
|
|
|
|
t.Fatalf("bad: %v", err)
|
|
|
|
}
|
|
|
|
|
|
|
|
// Now change the op to delete; the bad query field should be ignored
|
|
|
|
// because all we care about for a delete op is the ID.
|
2015-11-10 19:16:17 +00:00
|
|
|
query.Op = structs.PreparedQueryDelete
|
|
|
|
if err = msgpackrpc.CallWithCodec(codec, "PreparedQuery.Apply", &query, &reply); err != nil {
|
2015-11-10 07:03:20 +00:00
|
|
|
t.Fatalf("err: %v", err)
|
|
|
|
}
|
|
|
|
|
2015-11-10 18:29:55 +00:00
|
|
|
// Verify that this query is deleted.
|
|
|
|
{
|
|
|
|
req := &structs.PreparedQuerySpecificRequest{
|
2015-11-11 20:20:40 +00:00
|
|
|
Datacenter: "dc1",
|
|
|
|
QueryID: query.Query.ID,
|
2015-11-10 19:16:17 +00:00
|
|
|
}
|
|
|
|
var resp structs.IndexedPreparedQueries
|
2015-11-11 20:20:40 +00:00
|
|
|
if err = msgpackrpc.CallWithCodec(codec, "PreparedQuery.Get", req, &resp); err != nil {
|
2015-11-13 20:57:06 +00:00
|
|
|
if err.Error() != ErrQueryNotFound.Error() {
|
|
|
|
t.Fatalf("err: %v", err)
|
|
|
|
}
|
2015-11-10 19:16:17 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
if len(resp.Queries) != 0 {
|
|
|
|
t.Fatalf("bad: %v", resp)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
func TestPreparedQuery_Apply_ACLDeny(t *testing.T) {
|
2017-06-27 13:22:18 +00:00
|
|
|
t.Parallel()
|
2015-11-10 19:16:17 +00:00
|
|
|
dir1, s1 := testServerWithConfig(t, func(c *Config) {
|
|
|
|
c.ACLDatacenter = "dc1"
|
|
|
|
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")
|
2015-11-10 19:16:17 +00:00
|
|
|
|
Creates new "prepared-query" ACL type and new token capture behavior.
Prior to this change, prepared queries had the following behavior for
ACLs, which will need to change to support templates:
1. A management token, or a token with read access to the service being
queried needed to be provided in order to create a prepared query.
2. The token used to create the prepared query was stored with the query
in the state store and used to execute the query.
3. A management token, or the token used to create the query needed to be
supplied to perform and CRUD operations on an existing prepared query.
This was pretty subtle and complicated behavior, and won't work for
templates since the service name is computed at execution time. To solve
this, we introduce a new "prepared-query" ACL type, where the prefix
applies to the query name for static prepared query types and to the
prefix for template prepared query types.
With this change, the new behavior is:
1. A management token, or a token with "prepared-query" write access to
the query name or (soon) the given template prefix is required to do
any CRUD operations on a prepared query, or to list prepared queries
(the list is filtered by this ACL).
2. You will no longer need a management token to list prepared queries,
but you will only be able to see prepared queries that you have access
to (you get an empty list instead of permission denied).
3. When listing or getting a query, because it was easy to capture
management tokens given the past behavior, this will always blank out
the "Token" field (replacing the contents as <hidden>) for all tokens
unless a management token is supplied. Going forward, we should
discourage people from binding tokens for execution unless strictly
necessary.
4. No token will be captured by default when a prepared query is created.
If the user wishes to supply an execution token then can pass it in via
the "Token" field in the prepared query definition. Otherwise, this
field will default to empty.
5. At execution time, we will use the captured token if it exists with the
prepared query definition, otherwise we will use the token that's passed
in with the request, just like we do for other RPCs (or you can use the
agent's configured token for DNS).
6. Prepared queries with no name (accessible only by ID) will not require
ACLs to create or modify (execution time will depend on the service ACL
configuration). Our argument here is that these are designed to be
ephemeral and the IDs are as good as an ACL. Management tokens will be
able to list all of these.
These changes enable templates, but also enable delegation of authority to
manage the prepared query namespace.
2016-02-23 08:12:58 +00:00
|
|
|
// Create an ACL with write permissions for redis queries.
|
|
|
|
var token string
|
2015-11-10 19:16:17 +00:00
|
|
|
{
|
|
|
|
var rules = `
|
2016-02-25 00:57:55 +00:00
|
|
|
query "redis" {
|
Creates new "prepared-query" ACL type and new token capture behavior.
Prior to this change, prepared queries had the following behavior for
ACLs, which will need to change to support templates:
1. A management token, or a token with read access to the service being
queried needed to be provided in order to create a prepared query.
2. The token used to create the prepared query was stored with the query
in the state store and used to execute the query.
3. A management token, or the token used to create the query needed to be
supplied to perform and CRUD operations on an existing prepared query.
This was pretty subtle and complicated behavior, and won't work for
templates since the service name is computed at execution time. To solve
this, we introduce a new "prepared-query" ACL type, where the prefix
applies to the query name for static prepared query types and to the
prefix for template prepared query types.
With this change, the new behavior is:
1. A management token, or a token with "prepared-query" write access to
the query name or (soon) the given template prefix is required to do
any CRUD operations on a prepared query, or to list prepared queries
(the list is filtered by this ACL).
2. You will no longer need a management token to list prepared queries,
but you will only be able to see prepared queries that you have access
to (you get an empty list instead of permission denied).
3. When listing or getting a query, because it was easy to capture
management tokens given the past behavior, this will always blank out
the "Token" field (replacing the contents as <hidden>) for all tokens
unless a management token is supplied. Going forward, we should
discourage people from binding tokens for execution unless strictly
necessary.
4. No token will be captured by default when a prepared query is created.
If the user wishes to supply an execution token then can pass it in via
the "Token" field in the prepared query definition. Otherwise, this
field will default to empty.
5. At execution time, we will use the captured token if it exists with the
prepared query definition, otherwise we will use the token that's passed
in with the request, just like we do for other RPCs (or you can use the
agent's configured token for DNS).
6. Prepared queries with no name (accessible only by ID) will not require
ACLs to create or modify (execution time will depend on the service ACL
configuration). Our argument here is that these are designed to be
ephemeral and the IDs are as good as an ACL. Management tokens will be
able to list all of these.
These changes enable templates, but also enable delegation of authority to
manage the prepared query namespace.
2016-02-23 08:12:58 +00:00
|
|
|
policy = "write"
|
2015-11-10 19:16:17 +00:00
|
|
|
}
|
|
|
|
`
|
|
|
|
|
|
|
|
req := structs.ACLRequest{
|
|
|
|
Datacenter: "dc1",
|
|
|
|
Op: structs.ACLSet,
|
|
|
|
ACL: structs.ACL{
|
|
|
|
Name: "User token",
|
|
|
|
Type: structs.ACLTypeClient,
|
|
|
|
Rules: rules,
|
|
|
|
},
|
|
|
|
WriteRequest: structs.WriteRequest{Token: "root"},
|
|
|
|
}
|
Creates new "prepared-query" ACL type and new token capture behavior.
Prior to this change, prepared queries had the following behavior for
ACLs, which will need to change to support templates:
1. A management token, or a token with read access to the service being
queried needed to be provided in order to create a prepared query.
2. The token used to create the prepared query was stored with the query
in the state store and used to execute the query.
3. A management token, or the token used to create the query needed to be
supplied to perform and CRUD operations on an existing prepared query.
This was pretty subtle and complicated behavior, and won't work for
templates since the service name is computed at execution time. To solve
this, we introduce a new "prepared-query" ACL type, where the prefix
applies to the query name for static prepared query types and to the
prefix for template prepared query types.
With this change, the new behavior is:
1. A management token, or a token with "prepared-query" write access to
the query name or (soon) the given template prefix is required to do
any CRUD operations on a prepared query, or to list prepared queries
(the list is filtered by this ACL).
2. You will no longer need a management token to list prepared queries,
but you will only be able to see prepared queries that you have access
to (you get an empty list instead of permission denied).
3. When listing or getting a query, because it was easy to capture
management tokens given the past behavior, this will always blank out
the "Token" field (replacing the contents as <hidden>) for all tokens
unless a management token is supplied. Going forward, we should
discourage people from binding tokens for execution unless strictly
necessary.
4. No token will be captured by default when a prepared query is created.
If the user wishes to supply an execution token then can pass it in via
the "Token" field in the prepared query definition. Otherwise, this
field will default to empty.
5. At execution time, we will use the captured token if it exists with the
prepared query definition, otherwise we will use the token that's passed
in with the request, just like we do for other RPCs (or you can use the
agent's configured token for DNS).
6. Prepared queries with no name (accessible only by ID) will not require
ACLs to create or modify (execution time will depend on the service ACL
configuration). Our argument here is that these are designed to be
ephemeral and the IDs are as good as an ACL. Management tokens will be
able to list all of these.
These changes enable templates, but also enable delegation of authority to
manage the prepared query namespace.
2016-02-23 08:12:58 +00:00
|
|
|
if err := msgpackrpc.CallWithCodec(codec, "ACL.Apply", &req, &token); err != nil {
|
2015-11-10 19:16:17 +00:00
|
|
|
t.Fatalf("err: %v", err)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
// Set up a bare bones query.
|
|
|
|
query := structs.PreparedQueryRequest{
|
|
|
|
Datacenter: "dc1",
|
|
|
|
Op: structs.PreparedQueryCreate,
|
|
|
|
Query: &structs.PreparedQuery{
|
2016-02-24 09:26:16 +00:00
|
|
|
Name: "redis-master",
|
2015-11-10 19:16:17 +00:00
|
|
|
Service: structs.ServiceQuery{
|
2016-02-24 09:26:16 +00:00
|
|
|
Service: "the-redis",
|
2015-11-10 19:16:17 +00:00
|
|
|
},
|
|
|
|
},
|
|
|
|
}
|
|
|
|
var reply string
|
|
|
|
|
|
|
|
// Creating without a token should fail since the default policy is to
|
|
|
|
// deny.
|
|
|
|
err := msgpackrpc.CallWithCodec(codec, "PreparedQuery.Apply", &query, &reply)
|
2017-08-23 14:52:48 +00:00
|
|
|
if !acl.IsErrPermissionDenied(err) {
|
2015-11-10 19:16:17 +00:00
|
|
|
t.Fatalf("bad: %v", err)
|
|
|
|
}
|
|
|
|
|
|
|
|
// Now add the token and try again.
|
Creates new "prepared-query" ACL type and new token capture behavior.
Prior to this change, prepared queries had the following behavior for
ACLs, which will need to change to support templates:
1. A management token, or a token with read access to the service being
queried needed to be provided in order to create a prepared query.
2. The token used to create the prepared query was stored with the query
in the state store and used to execute the query.
3. A management token, or the token used to create the query needed to be
supplied to perform and CRUD operations on an existing prepared query.
This was pretty subtle and complicated behavior, and won't work for
templates since the service name is computed at execution time. To solve
this, we introduce a new "prepared-query" ACL type, where the prefix
applies to the query name for static prepared query types and to the
prefix for template prepared query types.
With this change, the new behavior is:
1. A management token, or a token with "prepared-query" write access to
the query name or (soon) the given template prefix is required to do
any CRUD operations on a prepared query, or to list prepared queries
(the list is filtered by this ACL).
2. You will no longer need a management token to list prepared queries,
but you will only be able to see prepared queries that you have access
to (you get an empty list instead of permission denied).
3. When listing or getting a query, because it was easy to capture
management tokens given the past behavior, this will always blank out
the "Token" field (replacing the contents as <hidden>) for all tokens
unless a management token is supplied. Going forward, we should
discourage people from binding tokens for execution unless strictly
necessary.
4. No token will be captured by default when a prepared query is created.
If the user wishes to supply an execution token then can pass it in via
the "Token" field in the prepared query definition. Otherwise, this
field will default to empty.
5. At execution time, we will use the captured token if it exists with the
prepared query definition, otherwise we will use the token that's passed
in with the request, just like we do for other RPCs (or you can use the
agent's configured token for DNS).
6. Prepared queries with no name (accessible only by ID) will not require
ACLs to create or modify (execution time will depend on the service ACL
configuration). Our argument here is that these are designed to be
ephemeral and the IDs are as good as an ACL. Management tokens will be
able to list all of these.
These changes enable templates, but also enable delegation of authority to
manage the prepared query namespace.
2016-02-23 08:12:58 +00:00
|
|
|
query.WriteRequest.Token = token
|
2015-11-10 19:16:17 +00:00
|
|
|
if err = msgpackrpc.CallWithCodec(codec, "PreparedQuery.Apply", &query, &reply); err != nil {
|
|
|
|
t.Fatalf("err: %v", err)
|
|
|
|
}
|
|
|
|
|
|
|
|
// Capture the ID and set the token, then read back the query to verify.
|
Creates new "prepared-query" ACL type and new token capture behavior.
Prior to this change, prepared queries had the following behavior for
ACLs, which will need to change to support templates:
1. A management token, or a token with read access to the service being
queried needed to be provided in order to create a prepared query.
2. The token used to create the prepared query was stored with the query
in the state store and used to execute the query.
3. A management token, or the token used to create the query needed to be
supplied to perform and CRUD operations on an existing prepared query.
This was pretty subtle and complicated behavior, and won't work for
templates since the service name is computed at execution time. To solve
this, we introduce a new "prepared-query" ACL type, where the prefix
applies to the query name for static prepared query types and to the
prefix for template prepared query types.
With this change, the new behavior is:
1. A management token, or a token with "prepared-query" write access to
the query name or (soon) the given template prefix is required to do
any CRUD operations on a prepared query, or to list prepared queries
(the list is filtered by this ACL).
2. You will no longer need a management token to list prepared queries,
but you will only be able to see prepared queries that you have access
to (you get an empty list instead of permission denied).
3. When listing or getting a query, because it was easy to capture
management tokens given the past behavior, this will always blank out
the "Token" field (replacing the contents as <hidden>) for all tokens
unless a management token is supplied. Going forward, we should
discourage people from binding tokens for execution unless strictly
necessary.
4. No token will be captured by default when a prepared query is created.
If the user wishes to supply an execution token then can pass it in via
the "Token" field in the prepared query definition. Otherwise, this
field will default to empty.
5. At execution time, we will use the captured token if it exists with the
prepared query definition, otherwise we will use the token that's passed
in with the request, just like we do for other RPCs (or you can use the
agent's configured token for DNS).
6. Prepared queries with no name (accessible only by ID) will not require
ACLs to create or modify (execution time will depend on the service ACL
configuration). Our argument here is that these are designed to be
ephemeral and the IDs are as good as an ACL. Management tokens will be
able to list all of these.
These changes enable templates, but also enable delegation of authority to
manage the prepared query namespace.
2016-02-23 08:12:58 +00:00
|
|
|
// Note that unlike previous versions of Consul, we DO NOT capture the
|
|
|
|
// token. We will set that here just to be explicit about it.
|
2015-11-10 19:16:17 +00:00
|
|
|
query.Query.ID = reply
|
Creates new "prepared-query" ACL type and new token capture behavior.
Prior to this change, prepared queries had the following behavior for
ACLs, which will need to change to support templates:
1. A management token, or a token with read access to the service being
queried needed to be provided in order to create a prepared query.
2. The token used to create the prepared query was stored with the query
in the state store and used to execute the query.
3. A management token, or the token used to create the query needed to be
supplied to perform and CRUD operations on an existing prepared query.
This was pretty subtle and complicated behavior, and won't work for
templates since the service name is computed at execution time. To solve
this, we introduce a new "prepared-query" ACL type, where the prefix
applies to the query name for static prepared query types and to the
prefix for template prepared query types.
With this change, the new behavior is:
1. A management token, or a token with "prepared-query" write access to
the query name or (soon) the given template prefix is required to do
any CRUD operations on a prepared query, or to list prepared queries
(the list is filtered by this ACL).
2. You will no longer need a management token to list prepared queries,
but you will only be able to see prepared queries that you have access
to (you get an empty list instead of permission denied).
3. When listing or getting a query, because it was easy to capture
management tokens given the past behavior, this will always blank out
the "Token" field (replacing the contents as <hidden>) for all tokens
unless a management token is supplied. Going forward, we should
discourage people from binding tokens for execution unless strictly
necessary.
4. No token will be captured by default when a prepared query is created.
If the user wishes to supply an execution token then can pass it in via
the "Token" field in the prepared query definition. Otherwise, this
field will default to empty.
5. At execution time, we will use the captured token if it exists with the
prepared query definition, otherwise we will use the token that's passed
in with the request, just like we do for other RPCs (or you can use the
agent's configured token for DNS).
6. Prepared queries with no name (accessible only by ID) will not require
ACLs to create or modify (execution time will depend on the service ACL
configuration). Our argument here is that these are designed to be
ephemeral and the IDs are as good as an ACL. Management tokens will be
able to list all of these.
These changes enable templates, but also enable delegation of authority to
manage the prepared query namespace.
2016-02-23 08:12:58 +00:00
|
|
|
query.Query.Token = ""
|
2015-11-10 19:16:17 +00:00
|
|
|
{
|
|
|
|
req := &structs.PreparedQuerySpecificRequest{
|
2015-11-11 20:20:40 +00:00
|
|
|
Datacenter: "dc1",
|
|
|
|
QueryID: query.Query.ID,
|
|
|
|
QueryOptions: structs.QueryOptions{Token: "root"},
|
2015-11-10 19:16:17 +00:00
|
|
|
}
|
|
|
|
var resp structs.IndexedPreparedQueries
|
2015-11-11 20:20:40 +00:00
|
|
|
if err = msgpackrpc.CallWithCodec(codec, "PreparedQuery.Get", req, &resp); err != nil {
|
2015-11-10 19:16:17 +00:00
|
|
|
t.Fatalf("err: %v", err)
|
|
|
|
}
|
|
|
|
|
|
|
|
if len(resp.Queries) != 1 {
|
|
|
|
t.Fatalf("bad: %v", resp)
|
|
|
|
}
|
|
|
|
actual := resp.Queries[0]
|
|
|
|
if resp.Index != actual.ModifyIndex {
|
|
|
|
t.Fatalf("bad index: %d", resp.Index)
|
|
|
|
}
|
|
|
|
actual.CreateIndex, actual.ModifyIndex = 0, 0
|
|
|
|
if !reflect.DeepEqual(actual, query.Query) {
|
|
|
|
t.Fatalf("bad: %v", actual)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
Creates new "prepared-query" ACL type and new token capture behavior.
Prior to this change, prepared queries had the following behavior for
ACLs, which will need to change to support templates:
1. A management token, or a token with read access to the service being
queried needed to be provided in order to create a prepared query.
2. The token used to create the prepared query was stored with the query
in the state store and used to execute the query.
3. A management token, or the token used to create the query needed to be
supplied to perform and CRUD operations on an existing prepared query.
This was pretty subtle and complicated behavior, and won't work for
templates since the service name is computed at execution time. To solve
this, we introduce a new "prepared-query" ACL type, where the prefix
applies to the query name for static prepared query types and to the
prefix for template prepared query types.
With this change, the new behavior is:
1. A management token, or a token with "prepared-query" write access to
the query name or (soon) the given template prefix is required to do
any CRUD operations on a prepared query, or to list prepared queries
(the list is filtered by this ACL).
2. You will no longer need a management token to list prepared queries,
but you will only be able to see prepared queries that you have access
to (you get an empty list instead of permission denied).
3. When listing or getting a query, because it was easy to capture
management tokens given the past behavior, this will always blank out
the "Token" field (replacing the contents as <hidden>) for all tokens
unless a management token is supplied. Going forward, we should
discourage people from binding tokens for execution unless strictly
necessary.
4. No token will be captured by default when a prepared query is created.
If the user wishes to supply an execution token then can pass it in via
the "Token" field in the prepared query definition. Otherwise, this
field will default to empty.
5. At execution time, we will use the captured token if it exists with the
prepared query definition, otherwise we will use the token that's passed
in with the request, just like we do for other RPCs (or you can use the
agent's configured token for DNS).
6. Prepared queries with no name (accessible only by ID) will not require
ACLs to create or modify (execution time will depend on the service ACL
configuration). Our argument here is that these are designed to be
ephemeral and the IDs are as good as an ACL. Management tokens will be
able to list all of these.
These changes enable templates, but also enable delegation of authority to
manage the prepared query namespace.
2016-02-23 08:12:58 +00:00
|
|
|
// Try to do an update without a token; this should get rejected.
|
2015-11-10 19:16:17 +00:00
|
|
|
query.Op = structs.PreparedQueryUpdate
|
|
|
|
query.WriteRequest.Token = ""
|
|
|
|
err = msgpackrpc.CallWithCodec(codec, "PreparedQuery.Apply", &query, &reply)
|
2017-08-23 14:52:48 +00:00
|
|
|
if !acl.IsErrPermissionDenied(err) {
|
2015-11-10 19:16:17 +00:00
|
|
|
t.Fatalf("bad: %v", err)
|
|
|
|
}
|
|
|
|
|
Creates new "prepared-query" ACL type and new token capture behavior.
Prior to this change, prepared queries had the following behavior for
ACLs, which will need to change to support templates:
1. A management token, or a token with read access to the service being
queried needed to be provided in order to create a prepared query.
2. The token used to create the prepared query was stored with the query
in the state store and used to execute the query.
3. A management token, or the token used to create the query needed to be
supplied to perform and CRUD operations on an existing prepared query.
This was pretty subtle and complicated behavior, and won't work for
templates since the service name is computed at execution time. To solve
this, we introduce a new "prepared-query" ACL type, where the prefix
applies to the query name for static prepared query types and to the
prefix for template prepared query types.
With this change, the new behavior is:
1. A management token, or a token with "prepared-query" write access to
the query name or (soon) the given template prefix is required to do
any CRUD operations on a prepared query, or to list prepared queries
(the list is filtered by this ACL).
2. You will no longer need a management token to list prepared queries,
but you will only be able to see prepared queries that you have access
to (you get an empty list instead of permission denied).
3. When listing or getting a query, because it was easy to capture
management tokens given the past behavior, this will always blank out
the "Token" field (replacing the contents as <hidden>) for all tokens
unless a management token is supplied. Going forward, we should
discourage people from binding tokens for execution unless strictly
necessary.
4. No token will be captured by default when a prepared query is created.
If the user wishes to supply an execution token then can pass it in via
the "Token" field in the prepared query definition. Otherwise, this
field will default to empty.
5. At execution time, we will use the captured token if it exists with the
prepared query definition, otherwise we will use the token that's passed
in with the request, just like we do for other RPCs (or you can use the
agent's configured token for DNS).
6. Prepared queries with no name (accessible only by ID) will not require
ACLs to create or modify (execution time will depend on the service ACL
configuration). Our argument here is that these are designed to be
ephemeral and the IDs are as good as an ACL. Management tokens will be
able to list all of these.
These changes enable templates, but also enable delegation of authority to
manage the prepared query namespace.
2016-02-23 08:12:58 +00:00
|
|
|
// Try again with the original token; this should go through.
|
|
|
|
query.WriteRequest.Token = token
|
2015-11-10 19:16:17 +00:00
|
|
|
if err = msgpackrpc.CallWithCodec(codec, "PreparedQuery.Apply", &query, &reply); err != nil {
|
|
|
|
t.Fatalf("err: %v", err)
|
|
|
|
}
|
|
|
|
|
Creates new "prepared-query" ACL type and new token capture behavior.
Prior to this change, prepared queries had the following behavior for
ACLs, which will need to change to support templates:
1. A management token, or a token with read access to the service being
queried needed to be provided in order to create a prepared query.
2. The token used to create the prepared query was stored with the query
in the state store and used to execute the query.
3. A management token, or the token used to create the query needed to be
supplied to perform and CRUD operations on an existing prepared query.
This was pretty subtle and complicated behavior, and won't work for
templates since the service name is computed at execution time. To solve
this, we introduce a new "prepared-query" ACL type, where the prefix
applies to the query name for static prepared query types and to the
prefix for template prepared query types.
With this change, the new behavior is:
1. A management token, or a token with "prepared-query" write access to
the query name or (soon) the given template prefix is required to do
any CRUD operations on a prepared query, or to list prepared queries
(the list is filtered by this ACL).
2. You will no longer need a management token to list prepared queries,
but you will only be able to see prepared queries that you have access
to (you get an empty list instead of permission denied).
3. When listing or getting a query, because it was easy to capture
management tokens given the past behavior, this will always blank out
the "Token" field (replacing the contents as <hidden>) for all tokens
unless a management token is supplied. Going forward, we should
discourage people from binding tokens for execution unless strictly
necessary.
4. No token will be captured by default when a prepared query is created.
If the user wishes to supply an execution token then can pass it in via
the "Token" field in the prepared query definition. Otherwise, this
field will default to empty.
5. At execution time, we will use the captured token if it exists with the
prepared query definition, otherwise we will use the token that's passed
in with the request, just like we do for other RPCs (or you can use the
agent's configured token for DNS).
6. Prepared queries with no name (accessible only by ID) will not require
ACLs to create or modify (execution time will depend on the service ACL
configuration). Our argument here is that these are designed to be
ephemeral and the IDs are as good as an ACL. Management tokens will be
able to list all of these.
These changes enable templates, but also enable delegation of authority to
manage the prepared query namespace.
2016-02-23 08:12:58 +00:00
|
|
|
// Try to do a delete with no token; this should get rejected.
|
2015-11-10 19:16:17 +00:00
|
|
|
query.Op = structs.PreparedQueryDelete
|
|
|
|
query.WriteRequest.Token = ""
|
|
|
|
err = msgpackrpc.CallWithCodec(codec, "PreparedQuery.Apply", &query, &reply)
|
2017-08-23 14:52:48 +00:00
|
|
|
if !acl.IsErrPermissionDenied(err) {
|
2015-11-10 19:16:17 +00:00
|
|
|
t.Fatalf("bad: %v", err)
|
|
|
|
}
|
|
|
|
|
|
|
|
// Try again with the original token. This should go through.
|
Creates new "prepared-query" ACL type and new token capture behavior.
Prior to this change, prepared queries had the following behavior for
ACLs, which will need to change to support templates:
1. A management token, or a token with read access to the service being
queried needed to be provided in order to create a prepared query.
2. The token used to create the prepared query was stored with the query
in the state store and used to execute the query.
3. A management token, or the token used to create the query needed to be
supplied to perform and CRUD operations on an existing prepared query.
This was pretty subtle and complicated behavior, and won't work for
templates since the service name is computed at execution time. To solve
this, we introduce a new "prepared-query" ACL type, where the prefix
applies to the query name for static prepared query types and to the
prefix for template prepared query types.
With this change, the new behavior is:
1. A management token, or a token with "prepared-query" write access to
the query name or (soon) the given template prefix is required to do
any CRUD operations on a prepared query, or to list prepared queries
(the list is filtered by this ACL).
2. You will no longer need a management token to list prepared queries,
but you will only be able to see prepared queries that you have access
to (you get an empty list instead of permission denied).
3. When listing or getting a query, because it was easy to capture
management tokens given the past behavior, this will always blank out
the "Token" field (replacing the contents as <hidden>) for all tokens
unless a management token is supplied. Going forward, we should
discourage people from binding tokens for execution unless strictly
necessary.
4. No token will be captured by default when a prepared query is created.
If the user wishes to supply an execution token then can pass it in via
the "Token" field in the prepared query definition. Otherwise, this
field will default to empty.
5. At execution time, we will use the captured token if it exists with the
prepared query definition, otherwise we will use the token that's passed
in with the request, just like we do for other RPCs (or you can use the
agent's configured token for DNS).
6. Prepared queries with no name (accessible only by ID) will not require
ACLs to create or modify (execution time will depend on the service ACL
configuration). Our argument here is that these are designed to be
ephemeral and the IDs are as good as an ACL. Management tokens will be
able to list all of these.
These changes enable templates, but also enable delegation of authority to
manage the prepared query namespace.
2016-02-23 08:12:58 +00:00
|
|
|
query.WriteRequest.Token = token
|
2015-11-10 19:16:17 +00:00
|
|
|
if err = msgpackrpc.CallWithCodec(codec, "PreparedQuery.Apply", &query, &reply); err != nil {
|
|
|
|
t.Fatalf("err: %v", err)
|
|
|
|
}
|
|
|
|
|
|
|
|
// Make sure the query got deleted.
|
|
|
|
{
|
|
|
|
req := &structs.PreparedQuerySpecificRequest{
|
2015-11-11 20:20:40 +00:00
|
|
|
Datacenter: "dc1",
|
|
|
|
QueryID: query.Query.ID,
|
|
|
|
QueryOptions: structs.QueryOptions{Token: "root"},
|
2015-11-10 19:16:17 +00:00
|
|
|
}
|
|
|
|
var resp structs.IndexedPreparedQueries
|
2015-11-11 20:20:40 +00:00
|
|
|
if err = msgpackrpc.CallWithCodec(codec, "PreparedQuery.Get", req, &resp); err != nil {
|
2015-11-13 20:57:06 +00:00
|
|
|
if err.Error() != ErrQueryNotFound.Error() {
|
|
|
|
t.Fatalf("err: %v", err)
|
|
|
|
}
|
2015-11-10 19:16:17 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
if len(resp.Queries) != 0 {
|
|
|
|
t.Fatalf("bad: %v", resp)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
// Make the query again.
|
|
|
|
query.Op = structs.PreparedQueryCreate
|
|
|
|
query.Query.ID = ""
|
Creates new "prepared-query" ACL type and new token capture behavior.
Prior to this change, prepared queries had the following behavior for
ACLs, which will need to change to support templates:
1. A management token, or a token with read access to the service being
queried needed to be provided in order to create a prepared query.
2. The token used to create the prepared query was stored with the query
in the state store and used to execute the query.
3. A management token, or the token used to create the query needed to be
supplied to perform and CRUD operations on an existing prepared query.
This was pretty subtle and complicated behavior, and won't work for
templates since the service name is computed at execution time. To solve
this, we introduce a new "prepared-query" ACL type, where the prefix
applies to the query name for static prepared query types and to the
prefix for template prepared query types.
With this change, the new behavior is:
1. A management token, or a token with "prepared-query" write access to
the query name or (soon) the given template prefix is required to do
any CRUD operations on a prepared query, or to list prepared queries
(the list is filtered by this ACL).
2. You will no longer need a management token to list prepared queries,
but you will only be able to see prepared queries that you have access
to (you get an empty list instead of permission denied).
3. When listing or getting a query, because it was easy to capture
management tokens given the past behavior, this will always blank out
the "Token" field (replacing the contents as <hidden>) for all tokens
unless a management token is supplied. Going forward, we should
discourage people from binding tokens for execution unless strictly
necessary.
4. No token will be captured by default when a prepared query is created.
If the user wishes to supply an execution token then can pass it in via
the "Token" field in the prepared query definition. Otherwise, this
field will default to empty.
5. At execution time, we will use the captured token if it exists with the
prepared query definition, otherwise we will use the token that's passed
in with the request, just like we do for other RPCs (or you can use the
agent's configured token for DNS).
6. Prepared queries with no name (accessible only by ID) will not require
ACLs to create or modify (execution time will depend on the service ACL
configuration). Our argument here is that these are designed to be
ephemeral and the IDs are as good as an ACL. Management tokens will be
able to list all of these.
These changes enable templates, but also enable delegation of authority to
manage the prepared query namespace.
2016-02-23 08:12:58 +00:00
|
|
|
query.WriteRequest.Token = token
|
2015-11-10 19:16:17 +00:00
|
|
|
if err = msgpackrpc.CallWithCodec(codec, "PreparedQuery.Apply", &query, &reply); err != nil {
|
|
|
|
t.Fatalf("err: %v", err)
|
|
|
|
}
|
|
|
|
|
Creates new "prepared-query" ACL type and new token capture behavior.
Prior to this change, prepared queries had the following behavior for
ACLs, which will need to change to support templates:
1. A management token, or a token with read access to the service being
queried needed to be provided in order to create a prepared query.
2. The token used to create the prepared query was stored with the query
in the state store and used to execute the query.
3. A management token, or the token used to create the query needed to be
supplied to perform and CRUD operations on an existing prepared query.
This was pretty subtle and complicated behavior, and won't work for
templates since the service name is computed at execution time. To solve
this, we introduce a new "prepared-query" ACL type, where the prefix
applies to the query name for static prepared query types and to the
prefix for template prepared query types.
With this change, the new behavior is:
1. A management token, or a token with "prepared-query" write access to
the query name or (soon) the given template prefix is required to do
any CRUD operations on a prepared query, or to list prepared queries
(the list is filtered by this ACL).
2. You will no longer need a management token to list prepared queries,
but you will only be able to see prepared queries that you have access
to (you get an empty list instead of permission denied).
3. When listing or getting a query, because it was easy to capture
management tokens given the past behavior, this will always blank out
the "Token" field (replacing the contents as <hidden>) for all tokens
unless a management token is supplied. Going forward, we should
discourage people from binding tokens for execution unless strictly
necessary.
4. No token will be captured by default when a prepared query is created.
If the user wishes to supply an execution token then can pass it in via
the "Token" field in the prepared query definition. Otherwise, this
field will default to empty.
5. At execution time, we will use the captured token if it exists with the
prepared query definition, otherwise we will use the token that's passed
in with the request, just like we do for other RPCs (or you can use the
agent's configured token for DNS).
6. Prepared queries with no name (accessible only by ID) will not require
ACLs to create or modify (execution time will depend on the service ACL
configuration). Our argument here is that these are designed to be
ephemeral and the IDs are as good as an ACL. Management tokens will be
able to list all of these.
These changes enable templates, but also enable delegation of authority to
manage the prepared query namespace.
2016-02-23 08:12:58 +00:00
|
|
|
// Check that it's there, and again make sure that the token did not get
|
|
|
|
// captured.
|
2015-11-10 19:16:17 +00:00
|
|
|
query.Query.ID = reply
|
Creates new "prepared-query" ACL type and new token capture behavior.
Prior to this change, prepared queries had the following behavior for
ACLs, which will need to change to support templates:
1. A management token, or a token with read access to the service being
queried needed to be provided in order to create a prepared query.
2. The token used to create the prepared query was stored with the query
in the state store and used to execute the query.
3. A management token, or the token used to create the query needed to be
supplied to perform and CRUD operations on an existing prepared query.
This was pretty subtle and complicated behavior, and won't work for
templates since the service name is computed at execution time. To solve
this, we introduce a new "prepared-query" ACL type, where the prefix
applies to the query name for static prepared query types and to the
prefix for template prepared query types.
With this change, the new behavior is:
1. A management token, or a token with "prepared-query" write access to
the query name or (soon) the given template prefix is required to do
any CRUD operations on a prepared query, or to list prepared queries
(the list is filtered by this ACL).
2. You will no longer need a management token to list prepared queries,
but you will only be able to see prepared queries that you have access
to (you get an empty list instead of permission denied).
3. When listing or getting a query, because it was easy to capture
management tokens given the past behavior, this will always blank out
the "Token" field (replacing the contents as <hidden>) for all tokens
unless a management token is supplied. Going forward, we should
discourage people from binding tokens for execution unless strictly
necessary.
4. No token will be captured by default when a prepared query is created.
If the user wishes to supply an execution token then can pass it in via
the "Token" field in the prepared query definition. Otherwise, this
field will default to empty.
5. At execution time, we will use the captured token if it exists with the
prepared query definition, otherwise we will use the token that's passed
in with the request, just like we do for other RPCs (or you can use the
agent's configured token for DNS).
6. Prepared queries with no name (accessible only by ID) will not require
ACLs to create or modify (execution time will depend on the service ACL
configuration). Our argument here is that these are designed to be
ephemeral and the IDs are as good as an ACL. Management tokens will be
able to list all of these.
These changes enable templates, but also enable delegation of authority to
manage the prepared query namespace.
2016-02-23 08:12:58 +00:00
|
|
|
query.Query.Token = ""
|
2015-11-10 19:16:17 +00:00
|
|
|
{
|
|
|
|
req := &structs.PreparedQuerySpecificRequest{
|
2015-11-11 20:20:40 +00:00
|
|
|
Datacenter: "dc1",
|
|
|
|
QueryID: query.Query.ID,
|
|
|
|
QueryOptions: structs.QueryOptions{Token: "root"},
|
2015-11-10 19:16:17 +00:00
|
|
|
}
|
|
|
|
var resp structs.IndexedPreparedQueries
|
2015-11-11 20:20:40 +00:00
|
|
|
if err = msgpackrpc.CallWithCodec(codec, "PreparedQuery.Get", req, &resp); err != nil {
|
2015-11-10 19:16:17 +00:00
|
|
|
t.Fatalf("err: %v", err)
|
|
|
|
}
|
|
|
|
|
|
|
|
if len(resp.Queries) != 1 {
|
|
|
|
t.Fatalf("bad: %v", resp)
|
|
|
|
}
|
|
|
|
actual := resp.Queries[0]
|
|
|
|
if resp.Index != actual.ModifyIndex {
|
|
|
|
t.Fatalf("bad index: %d", resp.Index)
|
|
|
|
}
|
|
|
|
actual.CreateIndex, actual.ModifyIndex = 0, 0
|
|
|
|
if !reflect.DeepEqual(actual, query.Query) {
|
|
|
|
t.Fatalf("bad: %v", actual)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
// A management token should be able to update the query no matter what.
|
|
|
|
query.Op = structs.PreparedQueryUpdate
|
|
|
|
query.WriteRequest.Token = "root"
|
|
|
|
if err = msgpackrpc.CallWithCodec(codec, "PreparedQuery.Apply", &query, &reply); err != nil {
|
|
|
|
t.Fatalf("err: %v", err)
|
|
|
|
}
|
|
|
|
|
Creates new "prepared-query" ACL type and new token capture behavior.
Prior to this change, prepared queries had the following behavior for
ACLs, which will need to change to support templates:
1. A management token, or a token with read access to the service being
queried needed to be provided in order to create a prepared query.
2. The token used to create the prepared query was stored with the query
in the state store and used to execute the query.
3. A management token, or the token used to create the query needed to be
supplied to perform and CRUD operations on an existing prepared query.
This was pretty subtle and complicated behavior, and won't work for
templates since the service name is computed at execution time. To solve
this, we introduce a new "prepared-query" ACL type, where the prefix
applies to the query name for static prepared query types and to the
prefix for template prepared query types.
With this change, the new behavior is:
1. A management token, or a token with "prepared-query" write access to
the query name or (soon) the given template prefix is required to do
any CRUD operations on a prepared query, or to list prepared queries
(the list is filtered by this ACL).
2. You will no longer need a management token to list prepared queries,
but you will only be able to see prepared queries that you have access
to (you get an empty list instead of permission denied).
3. When listing or getting a query, because it was easy to capture
management tokens given the past behavior, this will always blank out
the "Token" field (replacing the contents as <hidden>) for all tokens
unless a management token is supplied. Going forward, we should
discourage people from binding tokens for execution unless strictly
necessary.
4. No token will be captured by default when a prepared query is created.
If the user wishes to supply an execution token then can pass it in via
the "Token" field in the prepared query definition. Otherwise, this
field will default to empty.
5. At execution time, we will use the captured token if it exists with the
prepared query definition, otherwise we will use the token that's passed
in with the request, just like we do for other RPCs (or you can use the
agent's configured token for DNS).
6. Prepared queries with no name (accessible only by ID) will not require
ACLs to create or modify (execution time will depend on the service ACL
configuration). Our argument here is that these are designed to be
ephemeral and the IDs are as good as an ACL. Management tokens will be
able to list all of these.
These changes enable templates, but also enable delegation of authority to
manage the prepared query namespace.
2016-02-23 08:12:58 +00:00
|
|
|
// That last update should not have captured a token.
|
|
|
|
query.Query.Token = ""
|
2015-11-10 19:16:17 +00:00
|
|
|
{
|
|
|
|
req := &structs.PreparedQuerySpecificRequest{
|
2015-11-11 20:20:40 +00:00
|
|
|
Datacenter: "dc1",
|
|
|
|
QueryID: query.Query.ID,
|
|
|
|
QueryOptions: structs.QueryOptions{Token: "root"},
|
2015-11-10 19:16:17 +00:00
|
|
|
}
|
|
|
|
var resp structs.IndexedPreparedQueries
|
2015-11-11 20:20:40 +00:00
|
|
|
if err = msgpackrpc.CallWithCodec(codec, "PreparedQuery.Get", req, &resp); err != nil {
|
2015-11-10 19:16:17 +00:00
|
|
|
t.Fatalf("err: %v", err)
|
|
|
|
}
|
|
|
|
|
|
|
|
if len(resp.Queries) != 1 {
|
|
|
|
t.Fatalf("bad: %v", resp)
|
|
|
|
}
|
|
|
|
actual := resp.Queries[0]
|
|
|
|
if resp.Index != actual.ModifyIndex {
|
|
|
|
t.Fatalf("bad index: %d", resp.Index)
|
|
|
|
}
|
|
|
|
actual.CreateIndex, actual.ModifyIndex = 0, 0
|
|
|
|
if !reflect.DeepEqual(actual, query.Query) {
|
|
|
|
t.Fatalf("bad: %v", actual)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
// A management token should be able to delete the query no matter what.
|
|
|
|
query.Op = structs.PreparedQueryDelete
|
|
|
|
query.WriteRequest.Token = "root"
|
|
|
|
if err = msgpackrpc.CallWithCodec(codec, "PreparedQuery.Apply", &query, &reply); err != nil {
|
|
|
|
t.Fatalf("err: %v", err)
|
|
|
|
}
|
|
|
|
|
|
|
|
// Make sure the query got deleted.
|
|
|
|
{
|
|
|
|
req := &structs.PreparedQuerySpecificRequest{
|
2015-11-11 20:20:40 +00:00
|
|
|
Datacenter: "dc1",
|
|
|
|
QueryID: query.Query.ID,
|
|
|
|
QueryOptions: structs.QueryOptions{Token: "root"},
|
2015-11-10 18:29:55 +00:00
|
|
|
}
|
|
|
|
var resp structs.IndexedPreparedQueries
|
2015-11-11 20:20:40 +00:00
|
|
|
if err = msgpackrpc.CallWithCodec(codec, "PreparedQuery.Get", req, &resp); err != nil {
|
2015-11-13 20:57:06 +00:00
|
|
|
if err.Error() != ErrQueryNotFound.Error() {
|
|
|
|
t.Fatalf("err: %v", err)
|
|
|
|
}
|
2015-11-10 18:29:55 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
if len(resp.Queries) != 0 {
|
|
|
|
t.Fatalf("bad: %v", resp)
|
|
|
|
}
|
2015-11-10 07:03:20 +00:00
|
|
|
}
|
Creates new "prepared-query" ACL type and new token capture behavior.
Prior to this change, prepared queries had the following behavior for
ACLs, which will need to change to support templates:
1. A management token, or a token with read access to the service being
queried needed to be provided in order to create a prepared query.
2. The token used to create the prepared query was stored with the query
in the state store and used to execute the query.
3. A management token, or the token used to create the query needed to be
supplied to perform and CRUD operations on an existing prepared query.
This was pretty subtle and complicated behavior, and won't work for
templates since the service name is computed at execution time. To solve
this, we introduce a new "prepared-query" ACL type, where the prefix
applies to the query name for static prepared query types and to the
prefix for template prepared query types.
With this change, the new behavior is:
1. A management token, or a token with "prepared-query" write access to
the query name or (soon) the given template prefix is required to do
any CRUD operations on a prepared query, or to list prepared queries
(the list is filtered by this ACL).
2. You will no longer need a management token to list prepared queries,
but you will only be able to see prepared queries that you have access
to (you get an empty list instead of permission denied).
3. When listing or getting a query, because it was easy to capture
management tokens given the past behavior, this will always blank out
the "Token" field (replacing the contents as <hidden>) for all tokens
unless a management token is supplied. Going forward, we should
discourage people from binding tokens for execution unless strictly
necessary.
4. No token will be captured by default when a prepared query is created.
If the user wishes to supply an execution token then can pass it in via
the "Token" field in the prepared query definition. Otherwise, this
field will default to empty.
5. At execution time, we will use the captured token if it exists with the
prepared query definition, otherwise we will use the token that's passed
in with the request, just like we do for other RPCs (or you can use the
agent's configured token for DNS).
6. Prepared queries with no name (accessible only by ID) will not require
ACLs to create or modify (execution time will depend on the service ACL
configuration). Our argument here is that these are designed to be
ephemeral and the IDs are as good as an ACL. Management tokens will be
able to list all of these.
These changes enable templates, but also enable delegation of authority to
manage the prepared query namespace.
2016-02-23 08:12:58 +00:00
|
|
|
|
2016-02-24 09:26:16 +00:00
|
|
|
// Use the root token to make a query under a different name.
|
Creates new "prepared-query" ACL type and new token capture behavior.
Prior to this change, prepared queries had the following behavior for
ACLs, which will need to change to support templates:
1. A management token, or a token with read access to the service being
queried needed to be provided in order to create a prepared query.
2. The token used to create the prepared query was stored with the query
in the state store and used to execute the query.
3. A management token, or the token used to create the query needed to be
supplied to perform and CRUD operations on an existing prepared query.
This was pretty subtle and complicated behavior, and won't work for
templates since the service name is computed at execution time. To solve
this, we introduce a new "prepared-query" ACL type, where the prefix
applies to the query name for static prepared query types and to the
prefix for template prepared query types.
With this change, the new behavior is:
1. A management token, or a token with "prepared-query" write access to
the query name or (soon) the given template prefix is required to do
any CRUD operations on a prepared query, or to list prepared queries
(the list is filtered by this ACL).
2. You will no longer need a management token to list prepared queries,
but you will only be able to see prepared queries that you have access
to (you get an empty list instead of permission denied).
3. When listing or getting a query, because it was easy to capture
management tokens given the past behavior, this will always blank out
the "Token" field (replacing the contents as <hidden>) for all tokens
unless a management token is supplied. Going forward, we should
discourage people from binding tokens for execution unless strictly
necessary.
4. No token will be captured by default when a prepared query is created.
If the user wishes to supply an execution token then can pass it in via
the "Token" field in the prepared query definition. Otherwise, this
field will default to empty.
5. At execution time, we will use the captured token if it exists with the
prepared query definition, otherwise we will use the token that's passed
in with the request, just like we do for other RPCs (or you can use the
agent's configured token for DNS).
6. Prepared queries with no name (accessible only by ID) will not require
ACLs to create or modify (execution time will depend on the service ACL
configuration). Our argument here is that these are designed to be
ephemeral and the IDs are as good as an ACL. Management tokens will be
able to list all of these.
These changes enable templates, but also enable delegation of authority to
manage the prepared query namespace.
2016-02-23 08:12:58 +00:00
|
|
|
query.Op = structs.PreparedQueryCreate
|
|
|
|
query.Query.ID = ""
|
2016-02-24 09:26:16 +00:00
|
|
|
query.Query.Name = "cassandra"
|
Creates new "prepared-query" ACL type and new token capture behavior.
Prior to this change, prepared queries had the following behavior for
ACLs, which will need to change to support templates:
1. A management token, or a token with read access to the service being
queried needed to be provided in order to create a prepared query.
2. The token used to create the prepared query was stored with the query
in the state store and used to execute the query.
3. A management token, or the token used to create the query needed to be
supplied to perform and CRUD operations on an existing prepared query.
This was pretty subtle and complicated behavior, and won't work for
templates since the service name is computed at execution time. To solve
this, we introduce a new "prepared-query" ACL type, where the prefix
applies to the query name for static prepared query types and to the
prefix for template prepared query types.
With this change, the new behavior is:
1. A management token, or a token with "prepared-query" write access to
the query name or (soon) the given template prefix is required to do
any CRUD operations on a prepared query, or to list prepared queries
(the list is filtered by this ACL).
2. You will no longer need a management token to list prepared queries,
but you will only be able to see prepared queries that you have access
to (you get an empty list instead of permission denied).
3. When listing or getting a query, because it was easy to capture
management tokens given the past behavior, this will always blank out
the "Token" field (replacing the contents as <hidden>) for all tokens
unless a management token is supplied. Going forward, we should
discourage people from binding tokens for execution unless strictly
necessary.
4. No token will be captured by default when a prepared query is created.
If the user wishes to supply an execution token then can pass it in via
the "Token" field in the prepared query definition. Otherwise, this
field will default to empty.
5. At execution time, we will use the captured token if it exists with the
prepared query definition, otherwise we will use the token that's passed
in with the request, just like we do for other RPCs (or you can use the
agent's configured token for DNS).
6. Prepared queries with no name (accessible only by ID) will not require
ACLs to create or modify (execution time will depend on the service ACL
configuration). Our argument here is that these are designed to be
ephemeral and the IDs are as good as an ACL. Management tokens will be
able to list all of these.
These changes enable templates, but also enable delegation of authority to
manage the prepared query namespace.
2016-02-23 08:12:58 +00:00
|
|
|
query.WriteRequest.Token = "root"
|
|
|
|
if err = msgpackrpc.CallWithCodec(codec, "PreparedQuery.Apply", &query, &reply); err != nil {
|
|
|
|
t.Fatalf("err: %v", err)
|
|
|
|
}
|
|
|
|
|
|
|
|
// Check that it's there and that the token did not get captured.
|
|
|
|
query.Query.ID = reply
|
|
|
|
query.Query.Token = ""
|
|
|
|
{
|
|
|
|
req := &structs.PreparedQuerySpecificRequest{
|
|
|
|
Datacenter: "dc1",
|
|
|
|
QueryID: query.Query.ID,
|
|
|
|
QueryOptions: structs.QueryOptions{Token: "root"},
|
|
|
|
}
|
|
|
|
var resp structs.IndexedPreparedQueries
|
|
|
|
if err = msgpackrpc.CallWithCodec(codec, "PreparedQuery.Get", req, &resp); err != nil {
|
|
|
|
t.Fatalf("err: %v", err)
|
|
|
|
}
|
|
|
|
|
|
|
|
if len(resp.Queries) != 1 {
|
|
|
|
t.Fatalf("bad: %v", resp)
|
|
|
|
}
|
|
|
|
actual := resp.Queries[0]
|
|
|
|
if resp.Index != actual.ModifyIndex {
|
|
|
|
t.Fatalf("bad index: %d", resp.Index)
|
|
|
|
}
|
|
|
|
actual.CreateIndex, actual.ModifyIndex = 0, 0
|
|
|
|
if !reflect.DeepEqual(actual, query.Query) {
|
|
|
|
t.Fatalf("bad: %v", actual)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
// Now try to change that to redis with the valid redis token. This will
|
|
|
|
// fail because that token can't change cassandra queries.
|
|
|
|
query.Op = structs.PreparedQueryUpdate
|
2016-02-24 09:26:16 +00:00
|
|
|
query.Query.Name = "redis"
|
Creates new "prepared-query" ACL type and new token capture behavior.
Prior to this change, prepared queries had the following behavior for
ACLs, which will need to change to support templates:
1. A management token, or a token with read access to the service being
queried needed to be provided in order to create a prepared query.
2. The token used to create the prepared query was stored with the query
in the state store and used to execute the query.
3. A management token, or the token used to create the query needed to be
supplied to perform and CRUD operations on an existing prepared query.
This was pretty subtle and complicated behavior, and won't work for
templates since the service name is computed at execution time. To solve
this, we introduce a new "prepared-query" ACL type, where the prefix
applies to the query name for static prepared query types and to the
prefix for template prepared query types.
With this change, the new behavior is:
1. A management token, or a token with "prepared-query" write access to
the query name or (soon) the given template prefix is required to do
any CRUD operations on a prepared query, or to list prepared queries
(the list is filtered by this ACL).
2. You will no longer need a management token to list prepared queries,
but you will only be able to see prepared queries that you have access
to (you get an empty list instead of permission denied).
3. When listing or getting a query, because it was easy to capture
management tokens given the past behavior, this will always blank out
the "Token" field (replacing the contents as <hidden>) for all tokens
unless a management token is supplied. Going forward, we should
discourage people from binding tokens for execution unless strictly
necessary.
4. No token will be captured by default when a prepared query is created.
If the user wishes to supply an execution token then can pass it in via
the "Token" field in the prepared query definition. Otherwise, this
field will default to empty.
5. At execution time, we will use the captured token if it exists with the
prepared query definition, otherwise we will use the token that's passed
in with the request, just like we do for other RPCs (or you can use the
agent's configured token for DNS).
6. Prepared queries with no name (accessible only by ID) will not require
ACLs to create or modify (execution time will depend on the service ACL
configuration). Our argument here is that these are designed to be
ephemeral and the IDs are as good as an ACL. Management tokens will be
able to list all of these.
These changes enable templates, but also enable delegation of authority to
manage the prepared query namespace.
2016-02-23 08:12:58 +00:00
|
|
|
query.WriteRequest.Token = token
|
|
|
|
err = msgpackrpc.CallWithCodec(codec, "PreparedQuery.Apply", &query, &reply)
|
2017-08-23 14:52:48 +00:00
|
|
|
if !acl.IsErrPermissionDenied(err) {
|
Creates new "prepared-query" ACL type and new token capture behavior.
Prior to this change, prepared queries had the following behavior for
ACLs, which will need to change to support templates:
1. A management token, or a token with read access to the service being
queried needed to be provided in order to create a prepared query.
2. The token used to create the prepared query was stored with the query
in the state store and used to execute the query.
3. A management token, or the token used to create the query needed to be
supplied to perform and CRUD operations on an existing prepared query.
This was pretty subtle and complicated behavior, and won't work for
templates since the service name is computed at execution time. To solve
this, we introduce a new "prepared-query" ACL type, where the prefix
applies to the query name for static prepared query types and to the
prefix for template prepared query types.
With this change, the new behavior is:
1. A management token, or a token with "prepared-query" write access to
the query name or (soon) the given template prefix is required to do
any CRUD operations on a prepared query, or to list prepared queries
(the list is filtered by this ACL).
2. You will no longer need a management token to list prepared queries,
but you will only be able to see prepared queries that you have access
to (you get an empty list instead of permission denied).
3. When listing or getting a query, because it was easy to capture
management tokens given the past behavior, this will always blank out
the "Token" field (replacing the contents as <hidden>) for all tokens
unless a management token is supplied. Going forward, we should
discourage people from binding tokens for execution unless strictly
necessary.
4. No token will be captured by default when a prepared query is created.
If the user wishes to supply an execution token then can pass it in via
the "Token" field in the prepared query definition. Otherwise, this
field will default to empty.
5. At execution time, we will use the captured token if it exists with the
prepared query definition, otherwise we will use the token that's passed
in with the request, just like we do for other RPCs (or you can use the
agent's configured token for DNS).
6. Prepared queries with no name (accessible only by ID) will not require
ACLs to create or modify (execution time will depend on the service ACL
configuration). Our argument here is that these are designed to be
ephemeral and the IDs are as good as an ACL. Management tokens will be
able to list all of these.
These changes enable templates, but also enable delegation of authority to
manage the prepared query namespace.
2016-02-23 08:12:58 +00:00
|
|
|
t.Fatalf("bad: %v", err)
|
|
|
|
}
|
2015-11-07 06:18:11 +00:00
|
|
|
}
|
2015-11-10 19:33:00 +00:00
|
|
|
|
2015-11-10 22:46:08 +00:00
|
|
|
func TestPreparedQuery_Apply_ForwardLeader(t *testing.T) {
|
2017-06-27 13:22:18 +00:00
|
|
|
t.Parallel()
|
2016-10-26 02:20:24 +00:00
|
|
|
dir1, s1 := testServerWithConfig(t, func(c *Config) {
|
|
|
|
c.Bootstrap = false
|
|
|
|
})
|
2015-11-10 22:46:08 +00:00
|
|
|
defer os.RemoveAll(dir1)
|
|
|
|
defer s1.Shutdown()
|
|
|
|
codec1 := rpcClient(t, s1)
|
|
|
|
defer codec1.Close()
|
|
|
|
|
|
|
|
dir2, s2 := testServer(t)
|
|
|
|
defer os.RemoveAll(dir2)
|
|
|
|
defer s2.Shutdown()
|
|
|
|
codec2 := rpcClient(t, s2)
|
|
|
|
defer codec2.Close()
|
|
|
|
|
|
|
|
// Try to join.
|
2017-05-05 10:29:49 +00:00
|
|
|
joinLAN(t, s2, s1)
|
2015-11-10 22:46:08 +00:00
|
|
|
|
2017-04-19 23:00:11 +00:00
|
|
|
testrpc.WaitForLeader(t, s1.RPC, "dc1")
|
|
|
|
testrpc.WaitForLeader(t, s2.RPC, "dc1")
|
2015-11-10 22:46:08 +00:00
|
|
|
|
|
|
|
// Use the follower as the client.
|
|
|
|
var codec rpc.ClientCodec
|
|
|
|
if !s1.IsLeader() {
|
|
|
|
codec = codec1
|
|
|
|
} else {
|
|
|
|
codec = codec2
|
|
|
|
}
|
|
|
|
|
|
|
|
// Set up a node and service in the catalog.
|
|
|
|
{
|
|
|
|
req := structs.RegisterRequest{
|
|
|
|
Datacenter: "dc1",
|
|
|
|
Node: "foo",
|
|
|
|
Address: "127.0.0.1",
|
|
|
|
Service: &structs.NodeService{
|
|
|
|
Service: "redis",
|
|
|
|
Tags: []string{"master"},
|
|
|
|
Port: 8000,
|
|
|
|
},
|
|
|
|
}
|
|
|
|
var reply struct{}
|
|
|
|
err := msgpackrpc.CallWithCodec(codec, "Catalog.Register", &req, &reply)
|
|
|
|
if err != nil {
|
|
|
|
t.Fatalf("err: %v", err)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
// Set up a bare bones query.
|
|
|
|
query := structs.PreparedQueryRequest{
|
|
|
|
Datacenter: "dc1",
|
|
|
|
Op: structs.PreparedQueryCreate,
|
|
|
|
Query: &structs.PreparedQuery{
|
2016-12-10 19:19:08 +00:00
|
|
|
Name: "test",
|
2015-11-10 22:46:08 +00:00
|
|
|
Service: structs.ServiceQuery{
|
|
|
|
Service: "redis",
|
|
|
|
},
|
|
|
|
},
|
|
|
|
}
|
|
|
|
|
|
|
|
// Make sure the apply works even when forwarded through the non-leader.
|
|
|
|
var reply string
|
|
|
|
if err := msgpackrpc.CallWithCodec(codec, "PreparedQuery.Apply", &query, &reply); err != nil {
|
|
|
|
t.Fatalf("err: %v", err)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2015-11-10 19:33:00 +00:00
|
|
|
func TestPreparedQuery_parseQuery(t *testing.T) {
|
2017-06-27 13:22:18 +00:00
|
|
|
t.Parallel()
|
2015-11-10 19:33:00 +00:00
|
|
|
query := &structs.PreparedQuery{}
|
|
|
|
|
2016-12-10 19:19:08 +00:00
|
|
|
err := parseQuery(query, true)
|
|
|
|
if err == nil || !strings.Contains(err.Error(), "Must be bound to a session") {
|
2015-11-10 19:33:00 +00:00
|
|
|
t.Fatalf("bad: %v", err)
|
|
|
|
}
|
|
|
|
|
2016-12-10 19:19:08 +00:00
|
|
|
query.Session = "adf4238a-882b-9ddc-4a9d-5b6758e4159e"
|
|
|
|
err = parseQuery(query, true)
|
|
|
|
if err == nil || !strings.Contains(err.Error(), "Must provide a Service") {
|
2016-02-25 01:23:09 +00:00
|
|
|
t.Fatalf("bad: %v", err)
|
|
|
|
}
|
|
|
|
|
2016-12-10 19:19:08 +00:00
|
|
|
query.Session = ""
|
|
|
|
query.Template.Type = "some-kind-of-template"
|
|
|
|
err = parseQuery(query, true)
|
|
|
|
if err == nil || !strings.Contains(err.Error(), "Must provide a Service") {
|
|
|
|
t.Fatalf("bad: %v", err)
|
2016-02-25 01:23:09 +00:00
|
|
|
}
|
|
|
|
|
2016-12-10 19:19:08 +00:00
|
|
|
query.Template.Type = ""
|
|
|
|
err = parseQuery(query, false)
|
|
|
|
if err == nil || !strings.Contains(err.Error(), "Must provide a Service") {
|
2015-11-10 19:33:00 +00:00
|
|
|
t.Fatalf("bad: %v", err)
|
|
|
|
}
|
|
|
|
|
2016-12-10 19:19:08 +00:00
|
|
|
// None of the rest of these care about version 8 ACL enforcement.
|
|
|
|
for _, version8 := range []bool{true, false} {
|
|
|
|
query = &structs.PreparedQuery{}
|
|
|
|
query.Session = "adf4238a-882b-9ddc-4a9d-5b6758e4159e"
|
|
|
|
query.Service.Service = "foo"
|
|
|
|
if err := parseQuery(query, version8); err != nil {
|
|
|
|
t.Fatalf("err: %v", err)
|
|
|
|
}
|
2015-11-10 19:33:00 +00:00
|
|
|
|
2016-12-10 19:19:08 +00:00
|
|
|
query.Token = redactedToken
|
|
|
|
err = parseQuery(query, version8)
|
|
|
|
if err == nil || !strings.Contains(err.Error(), "Bad Token") {
|
|
|
|
t.Fatalf("bad: %v", err)
|
|
|
|
}
|
2015-11-10 19:33:00 +00:00
|
|
|
|
2016-12-10 19:19:08 +00:00
|
|
|
query.Token = "adf4238a-882b-9ddc-4a9d-5b6758e4159e"
|
|
|
|
if err := parseQuery(query, version8); err != nil {
|
|
|
|
t.Fatalf("err: %v", err)
|
|
|
|
}
|
2015-11-10 19:33:00 +00:00
|
|
|
|
2016-12-10 19:19:08 +00:00
|
|
|
query.Service.Failover.NearestN = -1
|
|
|
|
err = parseQuery(query, version8)
|
|
|
|
if err == nil || !strings.Contains(err.Error(), "Bad NearestN") {
|
|
|
|
t.Fatalf("bad: %v", err)
|
|
|
|
}
|
|
|
|
|
|
|
|
query.Service.Failover.NearestN = 3
|
|
|
|
if err := parseQuery(query, version8); err != nil {
|
|
|
|
t.Fatalf("err: %v", err)
|
|
|
|
}
|
|
|
|
|
|
|
|
query.DNS.TTL = "two fortnights"
|
|
|
|
err = parseQuery(query, version8)
|
|
|
|
if err == nil || !strings.Contains(err.Error(), "Bad DNS TTL") {
|
|
|
|
t.Fatalf("bad: %v", err)
|
|
|
|
}
|
|
|
|
|
|
|
|
query.DNS.TTL = "-3s"
|
|
|
|
err = parseQuery(query, version8)
|
|
|
|
if err == nil || !strings.Contains(err.Error(), "must be >=0") {
|
|
|
|
t.Fatalf("bad: %v", err)
|
|
|
|
}
|
|
|
|
|
|
|
|
query.DNS.TTL = "3s"
|
|
|
|
if err := parseQuery(query, version8); err != nil {
|
|
|
|
t.Fatalf("err: %v", err)
|
|
|
|
}
|
2017-01-23 23:53:45 +00:00
|
|
|
|
|
|
|
query.Service.NodeMeta = map[string]string{"": "somevalue"}
|
|
|
|
err = parseQuery(query, version8)
|
|
|
|
if err == nil || !strings.Contains(err.Error(), "cannot be blank") {
|
|
|
|
t.Fatalf("bad: %v", err)
|
|
|
|
}
|
|
|
|
|
|
|
|
query.Service.NodeMeta = map[string]string{"somekey": "somevalue"}
|
|
|
|
if err := parseQuery(query, version8); err != nil {
|
|
|
|
t.Fatalf("err: %v", err)
|
|
|
|
}
|
2015-11-10 19:33:00 +00:00
|
|
|
}
|
|
|
|
}
|
2015-11-10 20:13:40 +00:00
|
|
|
|
2016-03-03 09:04:12 +00:00
|
|
|
func TestPreparedQuery_ACLDeny_Catchall_Template(t *testing.T) {
|
2017-06-27 13:22:18 +00:00
|
|
|
t.Parallel()
|
2016-03-03 07:59:35 +00:00
|
|
|
dir1, s1 := testServerWithConfig(t, func(c *Config) {
|
|
|
|
c.ACLDatacenter = "dc1"
|
|
|
|
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-03-03 07:59:35 +00:00
|
|
|
|
|
|
|
// Create an ACL with write permissions for any prefix.
|
|
|
|
var token string
|
|
|
|
{
|
|
|
|
var rules = `
|
|
|
|
query "" {
|
|
|
|
policy = "write"
|
|
|
|
}
|
|
|
|
`
|
|
|
|
|
|
|
|
req := structs.ACLRequest{
|
|
|
|
Datacenter: "dc1",
|
|
|
|
Op: structs.ACLSet,
|
|
|
|
ACL: structs.ACL{
|
|
|
|
Name: "User token",
|
|
|
|
Type: structs.ACLTypeClient,
|
|
|
|
Rules: rules,
|
|
|
|
},
|
|
|
|
WriteRequest: structs.WriteRequest{Token: "root"},
|
|
|
|
}
|
|
|
|
if err := msgpackrpc.CallWithCodec(codec, "ACL.Apply", &req, &token); err != nil {
|
|
|
|
t.Fatalf("err: %v", err)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
// Set up a catch-all template.
|
|
|
|
query := structs.PreparedQueryRequest{
|
|
|
|
Datacenter: "dc1",
|
|
|
|
Op: structs.PreparedQueryCreate,
|
|
|
|
Query: &structs.PreparedQuery{
|
|
|
|
Name: "",
|
|
|
|
Token: "5e1e24e5-1329-f86f-18c6-3d3734edb2cd",
|
|
|
|
Template: structs.QueryTemplateOptions{
|
|
|
|
Type: structs.QueryTemplateTypeNamePrefixMatch,
|
|
|
|
},
|
|
|
|
Service: structs.ServiceQuery{
|
|
|
|
Service: "${name.full}",
|
|
|
|
},
|
|
|
|
},
|
|
|
|
}
|
|
|
|
var reply string
|
|
|
|
|
|
|
|
// Creating without a token should fail since the default policy is to
|
|
|
|
// deny.
|
|
|
|
err := msgpackrpc.CallWithCodec(codec, "PreparedQuery.Apply", &query, &reply)
|
2017-08-23 14:52:48 +00:00
|
|
|
if !acl.IsErrPermissionDenied(err) {
|
2016-03-03 07:59:35 +00:00
|
|
|
t.Fatalf("bad: %v", err)
|
|
|
|
}
|
|
|
|
|
|
|
|
// Now add the token and try again.
|
|
|
|
query.WriteRequest.Token = token
|
|
|
|
if err = msgpackrpc.CallWithCodec(codec, "PreparedQuery.Apply", &query, &reply); err != nil {
|
|
|
|
t.Fatalf("err: %v", err)
|
|
|
|
}
|
|
|
|
|
|
|
|
// Capture the ID and read back the query to verify. Note that the token
|
|
|
|
// will be redacted since this isn't a management token.
|
|
|
|
query.Query.ID = reply
|
|
|
|
query.Query.Token = redactedToken
|
|
|
|
{
|
|
|
|
req := &structs.PreparedQuerySpecificRequest{
|
|
|
|
Datacenter: "dc1",
|
|
|
|
QueryID: query.Query.ID,
|
|
|
|
QueryOptions: structs.QueryOptions{Token: token},
|
|
|
|
}
|
|
|
|
var resp structs.IndexedPreparedQueries
|
|
|
|
if err = msgpackrpc.CallWithCodec(codec, "PreparedQuery.Get", req, &resp); err != nil {
|
|
|
|
t.Fatalf("err: %v", err)
|
|
|
|
}
|
|
|
|
|
|
|
|
if len(resp.Queries) != 1 {
|
|
|
|
t.Fatalf("bad: %v", resp)
|
|
|
|
}
|
|
|
|
actual := resp.Queries[0]
|
|
|
|
if resp.Index != actual.ModifyIndex {
|
|
|
|
t.Fatalf("bad index: %d", resp.Index)
|
|
|
|
}
|
|
|
|
actual.CreateIndex, actual.ModifyIndex = 0, 0
|
|
|
|
if !reflect.DeepEqual(actual, query.Query) {
|
|
|
|
t.Fatalf("bad: %v", actual)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
// Try to query by ID without a token and make sure it gets denied, even
|
|
|
|
// though this has an empty name and would normally be shown.
|
|
|
|
{
|
|
|
|
req := &structs.PreparedQuerySpecificRequest{
|
|
|
|
Datacenter: "dc1",
|
|
|
|
QueryID: query.Query.ID,
|
|
|
|
}
|
|
|
|
var resp structs.IndexedPreparedQueries
|
|
|
|
err := msgpackrpc.CallWithCodec(codec, "PreparedQuery.Get", req, &resp)
|
2017-08-23 14:52:48 +00:00
|
|
|
if !acl.IsErrPermissionDenied(err) {
|
2016-03-03 07:59:35 +00:00
|
|
|
t.Fatalf("bad: %v", err)
|
|
|
|
}
|
|
|
|
|
|
|
|
if len(resp.Queries) != 0 {
|
|
|
|
t.Fatalf("bad: %v", resp)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
// We should get the same result listing all the queries without a
|
|
|
|
// token.
|
|
|
|
{
|
|
|
|
req := &structs.DCSpecificRequest{
|
|
|
|
Datacenter: "dc1",
|
|
|
|
}
|
|
|
|
var resp structs.IndexedPreparedQueries
|
|
|
|
if err = msgpackrpc.CallWithCodec(codec, "PreparedQuery.List", req, &resp); err != nil {
|
|
|
|
t.Fatalf("err: %v", err)
|
|
|
|
}
|
|
|
|
|
|
|
|
if len(resp.Queries) != 0 {
|
|
|
|
t.Fatalf("bad: %v", resp)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
// But a management token should be able to see it, and the real token.
|
|
|
|
query.Query.Token = "5e1e24e5-1329-f86f-18c6-3d3734edb2cd"
|
|
|
|
{
|
|
|
|
req := &structs.PreparedQuerySpecificRequest{
|
|
|
|
Datacenter: "dc1",
|
|
|
|
QueryID: query.Query.ID,
|
|
|
|
QueryOptions: structs.QueryOptions{Token: "root"},
|
|
|
|
}
|
|
|
|
var resp structs.IndexedPreparedQueries
|
|
|
|
if err = msgpackrpc.CallWithCodec(codec, "PreparedQuery.Get", req, &resp); err != nil {
|
|
|
|
t.Fatalf("err: %v", err)
|
|
|
|
}
|
|
|
|
|
|
|
|
if len(resp.Queries) != 1 {
|
|
|
|
t.Fatalf("bad: %v", resp)
|
|
|
|
}
|
|
|
|
actual := resp.Queries[0]
|
|
|
|
if resp.Index != actual.ModifyIndex {
|
|
|
|
t.Fatalf("bad index: %d", resp.Index)
|
|
|
|
}
|
|
|
|
actual.CreateIndex, actual.ModifyIndex = 0, 0
|
|
|
|
if !reflect.DeepEqual(actual, query.Query) {
|
|
|
|
t.Fatalf("bad: %v", actual)
|
|
|
|
}
|
|
|
|
}
|
2016-03-03 09:04:12 +00:00
|
|
|
|
2016-03-04 01:30:36 +00:00
|
|
|
// Explaining should also be denied without a token.
|
2016-03-03 09:04:12 +00:00
|
|
|
{
|
|
|
|
req := &structs.PreparedQueryExecuteRequest{
|
|
|
|
Datacenter: "dc1",
|
|
|
|
QueryIDOrName: "anything",
|
|
|
|
}
|
2016-03-04 01:30:36 +00:00
|
|
|
var resp structs.PreparedQueryExplainResponse
|
|
|
|
err := msgpackrpc.CallWithCodec(codec, "PreparedQuery.Explain", req, &resp)
|
2017-08-23 14:52:48 +00:00
|
|
|
if !acl.IsErrPermissionDenied(err) {
|
2016-03-03 09:04:12 +00:00
|
|
|
t.Fatalf("bad: %v", err)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2016-03-04 01:30:36 +00:00
|
|
|
// The user can explain and see the redacted token.
|
2016-03-03 09:04:12 +00:00
|
|
|
query.Query.Token = redactedToken
|
|
|
|
query.Query.Service.Service = "anything"
|
|
|
|
{
|
|
|
|
req := &structs.PreparedQueryExecuteRequest{
|
|
|
|
Datacenter: "dc1",
|
|
|
|
QueryIDOrName: "anything",
|
|
|
|
QueryOptions: structs.QueryOptions{Token: token},
|
|
|
|
}
|
2016-03-04 01:30:36 +00:00
|
|
|
var resp structs.PreparedQueryExplainResponse
|
|
|
|
err := msgpackrpc.CallWithCodec(codec, "PreparedQuery.Explain", req, &resp)
|
2016-03-03 09:04:12 +00:00
|
|
|
if err != nil {
|
|
|
|
t.Fatalf("err: %v", err)
|
|
|
|
}
|
|
|
|
|
|
|
|
actual := &resp.Query
|
|
|
|
actual.CreateIndex, actual.ModifyIndex = 0, 0
|
|
|
|
if !reflect.DeepEqual(actual, query.Query) {
|
|
|
|
t.Fatalf("bad: %v", actual)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2016-03-04 01:30:36 +00:00
|
|
|
// Make sure the management token can also explain and see the token.
|
2016-03-03 09:04:12 +00:00
|
|
|
query.Query.Token = "5e1e24e5-1329-f86f-18c6-3d3734edb2cd"
|
|
|
|
query.Query.Service.Service = "anything"
|
|
|
|
{
|
|
|
|
req := &structs.PreparedQueryExecuteRequest{
|
|
|
|
Datacenter: "dc1",
|
|
|
|
QueryIDOrName: "anything",
|
|
|
|
QueryOptions: structs.QueryOptions{Token: "root"},
|
|
|
|
}
|
2016-03-04 01:30:36 +00:00
|
|
|
var resp structs.PreparedQueryExplainResponse
|
|
|
|
err := msgpackrpc.CallWithCodec(codec, "PreparedQuery.Explain", req, &resp)
|
2016-03-03 09:04:12 +00:00
|
|
|
if err != nil {
|
|
|
|
t.Fatalf("err: %v", err)
|
|
|
|
}
|
|
|
|
|
|
|
|
actual := &resp.Query
|
|
|
|
actual.CreateIndex, actual.ModifyIndex = 0, 0
|
|
|
|
if !reflect.DeepEqual(actual, query.Query) {
|
|
|
|
t.Fatalf("bad: %v", actual)
|
|
|
|
}
|
|
|
|
}
|
2016-03-03 07:59:35 +00:00
|
|
|
}
|
|
|
|
|
2015-11-11 20:20:40 +00:00
|
|
|
func TestPreparedQuery_Get(t *testing.T) {
|
2017-06-27 13:22:18 +00:00
|
|
|
t.Parallel()
|
2015-11-10 20:13:40 +00:00
|
|
|
dir1, s1 := testServerWithConfig(t, func(c *Config) {
|
|
|
|
c.ACLDatacenter = "dc1"
|
|
|
|
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")
|
2015-11-10 20:13:40 +00:00
|
|
|
|
Creates new "prepared-query" ACL type and new token capture behavior.
Prior to this change, prepared queries had the following behavior for
ACLs, which will need to change to support templates:
1. A management token, or a token with read access to the service being
queried needed to be provided in order to create a prepared query.
2. The token used to create the prepared query was stored with the query
in the state store and used to execute the query.
3. A management token, or the token used to create the query needed to be
supplied to perform and CRUD operations on an existing prepared query.
This was pretty subtle and complicated behavior, and won't work for
templates since the service name is computed at execution time. To solve
this, we introduce a new "prepared-query" ACL type, where the prefix
applies to the query name for static prepared query types and to the
prefix for template prepared query types.
With this change, the new behavior is:
1. A management token, or a token with "prepared-query" write access to
the query name or (soon) the given template prefix is required to do
any CRUD operations on a prepared query, or to list prepared queries
(the list is filtered by this ACL).
2. You will no longer need a management token to list prepared queries,
but you will only be able to see prepared queries that you have access
to (you get an empty list instead of permission denied).
3. When listing or getting a query, because it was easy to capture
management tokens given the past behavior, this will always blank out
the "Token" field (replacing the contents as <hidden>) for all tokens
unless a management token is supplied. Going forward, we should
discourage people from binding tokens for execution unless strictly
necessary.
4. No token will be captured by default when a prepared query is created.
If the user wishes to supply an execution token then can pass it in via
the "Token" field in the prepared query definition. Otherwise, this
field will default to empty.
5. At execution time, we will use the captured token if it exists with the
prepared query definition, otherwise we will use the token that's passed
in with the request, just like we do for other RPCs (or you can use the
agent's configured token for DNS).
6. Prepared queries with no name (accessible only by ID) will not require
ACLs to create or modify (execution time will depend on the service ACL
configuration). Our argument here is that these are designed to be
ephemeral and the IDs are as good as an ACL. Management tokens will be
able to list all of these.
These changes enable templates, but also enable delegation of authority to
manage the prepared query namespace.
2016-02-23 08:12:58 +00:00
|
|
|
// Create an ACL with write permissions for redis queries.
|
|
|
|
var token string
|
2015-11-10 20:13:40 +00:00
|
|
|
{
|
|
|
|
var rules = `
|
2016-02-25 00:57:55 +00:00
|
|
|
query "redis" {
|
Creates new "prepared-query" ACL type and new token capture behavior.
Prior to this change, prepared queries had the following behavior for
ACLs, which will need to change to support templates:
1. A management token, or a token with read access to the service being
queried needed to be provided in order to create a prepared query.
2. The token used to create the prepared query was stored with the query
in the state store and used to execute the query.
3. A management token, or the token used to create the query needed to be
supplied to perform and CRUD operations on an existing prepared query.
This was pretty subtle and complicated behavior, and won't work for
templates since the service name is computed at execution time. To solve
this, we introduce a new "prepared-query" ACL type, where the prefix
applies to the query name for static prepared query types and to the
prefix for template prepared query types.
With this change, the new behavior is:
1. A management token, or a token with "prepared-query" write access to
the query name or (soon) the given template prefix is required to do
any CRUD operations on a prepared query, or to list prepared queries
(the list is filtered by this ACL).
2. You will no longer need a management token to list prepared queries,
but you will only be able to see prepared queries that you have access
to (you get an empty list instead of permission denied).
3. When listing or getting a query, because it was easy to capture
management tokens given the past behavior, this will always blank out
the "Token" field (replacing the contents as <hidden>) for all tokens
unless a management token is supplied. Going forward, we should
discourage people from binding tokens for execution unless strictly
necessary.
4. No token will be captured by default when a prepared query is created.
If the user wishes to supply an execution token then can pass it in via
the "Token" field in the prepared query definition. Otherwise, this
field will default to empty.
5. At execution time, we will use the captured token if it exists with the
prepared query definition, otherwise we will use the token that's passed
in with the request, just like we do for other RPCs (or you can use the
agent's configured token for DNS).
6. Prepared queries with no name (accessible only by ID) will not require
ACLs to create or modify (execution time will depend on the service ACL
configuration). Our argument here is that these are designed to be
ephemeral and the IDs are as good as an ACL. Management tokens will be
able to list all of these.
These changes enable templates, but also enable delegation of authority to
manage the prepared query namespace.
2016-02-23 08:12:58 +00:00
|
|
|
policy = "write"
|
2015-11-10 20:13:40 +00:00
|
|
|
}
|
|
|
|
`
|
|
|
|
|
|
|
|
req := structs.ACLRequest{
|
|
|
|
Datacenter: "dc1",
|
|
|
|
Op: structs.ACLSet,
|
|
|
|
ACL: structs.ACL{
|
|
|
|
Name: "User token",
|
|
|
|
Type: structs.ACLTypeClient,
|
|
|
|
Rules: rules,
|
|
|
|
},
|
|
|
|
WriteRequest: structs.WriteRequest{Token: "root"},
|
|
|
|
}
|
Creates new "prepared-query" ACL type and new token capture behavior.
Prior to this change, prepared queries had the following behavior for
ACLs, which will need to change to support templates:
1. A management token, or a token with read access to the service being
queried needed to be provided in order to create a prepared query.
2. The token used to create the prepared query was stored with the query
in the state store and used to execute the query.
3. A management token, or the token used to create the query needed to be
supplied to perform and CRUD operations on an existing prepared query.
This was pretty subtle and complicated behavior, and won't work for
templates since the service name is computed at execution time. To solve
this, we introduce a new "prepared-query" ACL type, where the prefix
applies to the query name for static prepared query types and to the
prefix for template prepared query types.
With this change, the new behavior is:
1. A management token, or a token with "prepared-query" write access to
the query name or (soon) the given template prefix is required to do
any CRUD operations on a prepared query, or to list prepared queries
(the list is filtered by this ACL).
2. You will no longer need a management token to list prepared queries,
but you will only be able to see prepared queries that you have access
to (you get an empty list instead of permission denied).
3. When listing or getting a query, because it was easy to capture
management tokens given the past behavior, this will always blank out
the "Token" field (replacing the contents as <hidden>) for all tokens
unless a management token is supplied. Going forward, we should
discourage people from binding tokens for execution unless strictly
necessary.
4. No token will be captured by default when a prepared query is created.
If the user wishes to supply an execution token then can pass it in via
the "Token" field in the prepared query definition. Otherwise, this
field will default to empty.
5. At execution time, we will use the captured token if it exists with the
prepared query definition, otherwise we will use the token that's passed
in with the request, just like we do for other RPCs (or you can use the
agent's configured token for DNS).
6. Prepared queries with no name (accessible only by ID) will not require
ACLs to create or modify (execution time will depend on the service ACL
configuration). Our argument here is that these are designed to be
ephemeral and the IDs are as good as an ACL. Management tokens will be
able to list all of these.
These changes enable templates, but also enable delegation of authority to
manage the prepared query namespace.
2016-02-23 08:12:58 +00:00
|
|
|
if err := msgpackrpc.CallWithCodec(codec, "ACL.Apply", &req, &token); err != nil {
|
2015-11-10 20:13:40 +00:00
|
|
|
t.Fatalf("err: %v", err)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
// Set up a bare bones query.
|
|
|
|
query := structs.PreparedQueryRequest{
|
|
|
|
Datacenter: "dc1",
|
|
|
|
Op: structs.PreparedQueryCreate,
|
|
|
|
Query: &structs.PreparedQuery{
|
2016-02-24 09:26:16 +00:00
|
|
|
Name: "redis-master",
|
2015-11-10 20:13:40 +00:00
|
|
|
Service: structs.ServiceQuery{
|
2016-02-24 09:26:16 +00:00
|
|
|
Service: "the-redis",
|
2015-11-10 20:13:40 +00:00
|
|
|
},
|
|
|
|
},
|
Creates new "prepared-query" ACL type and new token capture behavior.
Prior to this change, prepared queries had the following behavior for
ACLs, which will need to change to support templates:
1. A management token, or a token with read access to the service being
queried needed to be provided in order to create a prepared query.
2. The token used to create the prepared query was stored with the query
in the state store and used to execute the query.
3. A management token, or the token used to create the query needed to be
supplied to perform and CRUD operations on an existing prepared query.
This was pretty subtle and complicated behavior, and won't work for
templates since the service name is computed at execution time. To solve
this, we introduce a new "prepared-query" ACL type, where the prefix
applies to the query name for static prepared query types and to the
prefix for template prepared query types.
With this change, the new behavior is:
1. A management token, or a token with "prepared-query" write access to
the query name or (soon) the given template prefix is required to do
any CRUD operations on a prepared query, or to list prepared queries
(the list is filtered by this ACL).
2. You will no longer need a management token to list prepared queries,
but you will only be able to see prepared queries that you have access
to (you get an empty list instead of permission denied).
3. When listing or getting a query, because it was easy to capture
management tokens given the past behavior, this will always blank out
the "Token" field (replacing the contents as <hidden>) for all tokens
unless a management token is supplied. Going forward, we should
discourage people from binding tokens for execution unless strictly
necessary.
4. No token will be captured by default when a prepared query is created.
If the user wishes to supply an execution token then can pass it in via
the "Token" field in the prepared query definition. Otherwise, this
field will default to empty.
5. At execution time, we will use the captured token if it exists with the
prepared query definition, otherwise we will use the token that's passed
in with the request, just like we do for other RPCs (or you can use the
agent's configured token for DNS).
6. Prepared queries with no name (accessible only by ID) will not require
ACLs to create or modify (execution time will depend on the service ACL
configuration). Our argument here is that these are designed to be
ephemeral and the IDs are as good as an ACL. Management tokens will be
able to list all of these.
These changes enable templates, but also enable delegation of authority to
manage the prepared query namespace.
2016-02-23 08:12:58 +00:00
|
|
|
WriteRequest: structs.WriteRequest{Token: token},
|
2015-11-10 20:13:40 +00:00
|
|
|
}
|
|
|
|
var reply string
|
|
|
|
if err := msgpackrpc.CallWithCodec(codec, "PreparedQuery.Apply", &query, &reply); err != nil {
|
|
|
|
t.Fatalf("err: %v", err)
|
|
|
|
}
|
|
|
|
|
Creates new "prepared-query" ACL type and new token capture behavior.
Prior to this change, prepared queries had the following behavior for
ACLs, which will need to change to support templates:
1. A management token, or a token with read access to the service being
queried needed to be provided in order to create a prepared query.
2. The token used to create the prepared query was stored with the query
in the state store and used to execute the query.
3. A management token, or the token used to create the query needed to be
supplied to perform and CRUD operations on an existing prepared query.
This was pretty subtle and complicated behavior, and won't work for
templates since the service name is computed at execution time. To solve
this, we introduce a new "prepared-query" ACL type, where the prefix
applies to the query name for static prepared query types and to the
prefix for template prepared query types.
With this change, the new behavior is:
1. A management token, or a token with "prepared-query" write access to
the query name or (soon) the given template prefix is required to do
any CRUD operations on a prepared query, or to list prepared queries
(the list is filtered by this ACL).
2. You will no longer need a management token to list prepared queries,
but you will only be able to see prepared queries that you have access
to (you get an empty list instead of permission denied).
3. When listing or getting a query, because it was easy to capture
management tokens given the past behavior, this will always blank out
the "Token" field (replacing the contents as <hidden>) for all tokens
unless a management token is supplied. Going forward, we should
discourage people from binding tokens for execution unless strictly
necessary.
4. No token will be captured by default when a prepared query is created.
If the user wishes to supply an execution token then can pass it in via
the "Token" field in the prepared query definition. Otherwise, this
field will default to empty.
5. At execution time, we will use the captured token if it exists with the
prepared query definition, otherwise we will use the token that's passed
in with the request, just like we do for other RPCs (or you can use the
agent's configured token for DNS).
6. Prepared queries with no name (accessible only by ID) will not require
ACLs to create or modify (execution time will depend on the service ACL
configuration). Our argument here is that these are designed to be
ephemeral and the IDs are as good as an ACL. Management tokens will be
able to list all of these.
These changes enable templates, but also enable delegation of authority to
manage the prepared query namespace.
2016-02-23 08:12:58 +00:00
|
|
|
// Capture the ID, then read back the query to verify.
|
2015-11-10 20:13:40 +00:00
|
|
|
query.Query.ID = reply
|
|
|
|
{
|
|
|
|
req := &structs.PreparedQuerySpecificRequest{
|
2015-11-11 20:20:40 +00:00
|
|
|
Datacenter: "dc1",
|
|
|
|
QueryID: query.Query.ID,
|
Creates new "prepared-query" ACL type and new token capture behavior.
Prior to this change, prepared queries had the following behavior for
ACLs, which will need to change to support templates:
1. A management token, or a token with read access to the service being
queried needed to be provided in order to create a prepared query.
2. The token used to create the prepared query was stored with the query
in the state store and used to execute the query.
3. A management token, or the token used to create the query needed to be
supplied to perform and CRUD operations on an existing prepared query.
This was pretty subtle and complicated behavior, and won't work for
templates since the service name is computed at execution time. To solve
this, we introduce a new "prepared-query" ACL type, where the prefix
applies to the query name for static prepared query types and to the
prefix for template prepared query types.
With this change, the new behavior is:
1. A management token, or a token with "prepared-query" write access to
the query name or (soon) the given template prefix is required to do
any CRUD operations on a prepared query, or to list prepared queries
(the list is filtered by this ACL).
2. You will no longer need a management token to list prepared queries,
but you will only be able to see prepared queries that you have access
to (you get an empty list instead of permission denied).
3. When listing or getting a query, because it was easy to capture
management tokens given the past behavior, this will always blank out
the "Token" field (replacing the contents as <hidden>) for all tokens
unless a management token is supplied. Going forward, we should
discourage people from binding tokens for execution unless strictly
necessary.
4. No token will be captured by default when a prepared query is created.
If the user wishes to supply an execution token then can pass it in via
the "Token" field in the prepared query definition. Otherwise, this
field will default to empty.
5. At execution time, we will use the captured token if it exists with the
prepared query definition, otherwise we will use the token that's passed
in with the request, just like we do for other RPCs (or you can use the
agent's configured token for DNS).
6. Prepared queries with no name (accessible only by ID) will not require
ACLs to create or modify (execution time will depend on the service ACL
configuration). Our argument here is that these are designed to be
ephemeral and the IDs are as good as an ACL. Management tokens will be
able to list all of these.
These changes enable templates, but also enable delegation of authority to
manage the prepared query namespace.
2016-02-23 08:12:58 +00:00
|
|
|
QueryOptions: structs.QueryOptions{Token: token},
|
2015-11-10 20:13:40 +00:00
|
|
|
}
|
|
|
|
var resp structs.IndexedPreparedQueries
|
2015-11-11 20:20:40 +00:00
|
|
|
if err := msgpackrpc.CallWithCodec(codec, "PreparedQuery.Get", req, &resp); err != nil {
|
2015-11-10 20:13:40 +00:00
|
|
|
t.Fatalf("err: %v", err)
|
|
|
|
}
|
|
|
|
|
|
|
|
if len(resp.Queries) != 1 {
|
|
|
|
t.Fatalf("bad: %v", resp)
|
|
|
|
}
|
|
|
|
actual := resp.Queries[0]
|
|
|
|
if resp.Index != actual.ModifyIndex {
|
|
|
|
t.Fatalf("bad index: %d", resp.Index)
|
|
|
|
}
|
|
|
|
actual.CreateIndex, actual.ModifyIndex = 0, 0
|
|
|
|
if !reflect.DeepEqual(actual, query.Query) {
|
|
|
|
t.Fatalf("bad: %v", actual)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
Creates new "prepared-query" ACL type and new token capture behavior.
Prior to this change, prepared queries had the following behavior for
ACLs, which will need to change to support templates:
1. A management token, or a token with read access to the service being
queried needed to be provided in order to create a prepared query.
2. The token used to create the prepared query was stored with the query
in the state store and used to execute the query.
3. A management token, or the token used to create the query needed to be
supplied to perform and CRUD operations on an existing prepared query.
This was pretty subtle and complicated behavior, and won't work for
templates since the service name is computed at execution time. To solve
this, we introduce a new "prepared-query" ACL type, where the prefix
applies to the query name for static prepared query types and to the
prefix for template prepared query types.
With this change, the new behavior is:
1. A management token, or a token with "prepared-query" write access to
the query name or (soon) the given template prefix is required to do
any CRUD operations on a prepared query, or to list prepared queries
(the list is filtered by this ACL).
2. You will no longer need a management token to list prepared queries,
but you will only be able to see prepared queries that you have access
to (you get an empty list instead of permission denied).
3. When listing or getting a query, because it was easy to capture
management tokens given the past behavior, this will always blank out
the "Token" field (replacing the contents as <hidden>) for all tokens
unless a management token is supplied. Going forward, we should
discourage people from binding tokens for execution unless strictly
necessary.
4. No token will be captured by default when a prepared query is created.
If the user wishes to supply an execution token then can pass it in via
the "Token" field in the prepared query definition. Otherwise, this
field will default to empty.
5. At execution time, we will use the captured token if it exists with the
prepared query definition, otherwise we will use the token that's passed
in with the request, just like we do for other RPCs (or you can use the
agent's configured token for DNS).
6. Prepared queries with no name (accessible only by ID) will not require
ACLs to create or modify (execution time will depend on the service ACL
configuration). Our argument here is that these are designed to be
ephemeral and the IDs are as good as an ACL. Management tokens will be
able to list all of these.
These changes enable templates, but also enable delegation of authority to
manage the prepared query namespace.
2016-02-23 08:12:58 +00:00
|
|
|
// Try again with no token, which should return an error.
|
2015-11-10 20:13:40 +00:00
|
|
|
{
|
|
|
|
req := &structs.PreparedQuerySpecificRequest{
|
2015-11-11 20:20:40 +00:00
|
|
|
Datacenter: "dc1",
|
|
|
|
QueryID: query.Query.ID,
|
|
|
|
QueryOptions: structs.QueryOptions{Token: ""},
|
2015-11-10 20:13:40 +00:00
|
|
|
}
|
|
|
|
var resp structs.IndexedPreparedQueries
|
2015-11-11 20:20:40 +00:00
|
|
|
err := msgpackrpc.CallWithCodec(codec, "PreparedQuery.Get", req, &resp)
|
2017-08-23 14:52:48 +00:00
|
|
|
if !acl.IsErrPermissionDenied(err) {
|
2015-11-10 20:13:40 +00:00
|
|
|
t.Fatalf("bad: %v", err)
|
|
|
|
}
|
|
|
|
|
|
|
|
if len(resp.Queries) != 0 {
|
|
|
|
t.Fatalf("bad: %v", resp)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
// A management token should be able to read no matter what.
|
|
|
|
{
|
|
|
|
req := &structs.PreparedQuerySpecificRequest{
|
2015-11-11 20:20:40 +00:00
|
|
|
Datacenter: "dc1",
|
|
|
|
QueryID: query.Query.ID,
|
|
|
|
QueryOptions: structs.QueryOptions{Token: "root"},
|
2015-11-10 20:13:40 +00:00
|
|
|
}
|
|
|
|
var resp structs.IndexedPreparedQueries
|
2015-11-11 20:20:40 +00:00
|
|
|
if err := msgpackrpc.CallWithCodec(codec, "PreparedQuery.Get", req, &resp); err != nil {
|
2015-11-10 20:13:40 +00:00
|
|
|
t.Fatalf("err: %v", err)
|
|
|
|
}
|
|
|
|
|
|
|
|
if len(resp.Queries) != 1 {
|
|
|
|
t.Fatalf("bad: %v", resp)
|
|
|
|
}
|
|
|
|
actual := resp.Queries[0]
|
|
|
|
if resp.Index != actual.ModifyIndex {
|
|
|
|
t.Fatalf("bad index: %d", resp.Index)
|
|
|
|
}
|
|
|
|
actual.CreateIndex, actual.ModifyIndex = 0, 0
|
|
|
|
if !reflect.DeepEqual(actual, query.Query) {
|
|
|
|
t.Fatalf("bad: %v", actual)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2016-12-10 19:19:08 +00:00
|
|
|
// Create a session.
|
|
|
|
var session string
|
|
|
|
{
|
|
|
|
req := structs.SessionRequest{
|
|
|
|
Datacenter: "dc1",
|
|
|
|
Op: structs.SessionCreate,
|
|
|
|
Session: structs.Session{
|
|
|
|
Node: s1.config.NodeName,
|
|
|
|
},
|
|
|
|
}
|
|
|
|
if err := msgpackrpc.CallWithCodec(codec, "Session.Apply", &req, &session); err != nil {
|
|
|
|
t.Fatalf("err: %v", err)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2016-02-24 09:26:16 +00:00
|
|
|
// Now update the query to take away its name.
|
|
|
|
query.Op = structs.PreparedQueryUpdate
|
|
|
|
query.Query.Name = ""
|
2016-12-10 19:19:08 +00:00
|
|
|
query.Query.Session = session
|
2016-02-24 09:26:16 +00:00
|
|
|
if err := msgpackrpc.CallWithCodec(codec, "PreparedQuery.Apply", &query, &reply); err != nil {
|
|
|
|
t.Fatalf("err: %v", err)
|
|
|
|
}
|
|
|
|
|
|
|
|
// Try again with no token, this should work since this query is only
|
|
|
|
// managed by an ID (no name) so no ACLs apply to it.
|
|
|
|
{
|
|
|
|
req := &structs.PreparedQuerySpecificRequest{
|
|
|
|
Datacenter: "dc1",
|
|
|
|
QueryID: query.Query.ID,
|
|
|
|
QueryOptions: structs.QueryOptions{Token: ""},
|
|
|
|
}
|
|
|
|
var resp structs.IndexedPreparedQueries
|
|
|
|
if err := msgpackrpc.CallWithCodec(codec, "PreparedQuery.Get", req, &resp); err != nil {
|
|
|
|
t.Fatalf("err: %v", err)
|
|
|
|
}
|
|
|
|
|
|
|
|
if len(resp.Queries) != 1 {
|
|
|
|
t.Fatalf("bad: %v", resp)
|
|
|
|
}
|
|
|
|
actual := resp.Queries[0]
|
|
|
|
if resp.Index != actual.ModifyIndex {
|
|
|
|
t.Fatalf("bad index: %d", resp.Index)
|
|
|
|
}
|
|
|
|
actual.CreateIndex, actual.ModifyIndex = 0, 0
|
|
|
|
if !reflect.DeepEqual(actual, query.Query) {
|
|
|
|
t.Fatalf("bad: %v", actual)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2016-02-26 23:59:00 +00:00
|
|
|
// Capture a token.
|
|
|
|
query.Op = structs.PreparedQueryUpdate
|
|
|
|
query.Query.Token = "le-token"
|
|
|
|
if err := msgpackrpc.CallWithCodec(codec, "PreparedQuery.Apply", &query, &reply); err != nil {
|
|
|
|
t.Fatalf("err: %v", err)
|
|
|
|
}
|
|
|
|
|
|
|
|
// This should get redacted when we read it back without a token.
|
|
|
|
query.Query.Token = redactedToken
|
|
|
|
{
|
|
|
|
req := &structs.PreparedQuerySpecificRequest{
|
|
|
|
Datacenter: "dc1",
|
|
|
|
QueryID: query.Query.ID,
|
|
|
|
QueryOptions: structs.QueryOptions{Token: ""},
|
|
|
|
}
|
|
|
|
var resp structs.IndexedPreparedQueries
|
|
|
|
if err := msgpackrpc.CallWithCodec(codec, "PreparedQuery.Get", req, &resp); err != nil {
|
|
|
|
t.Fatalf("err: %v", err)
|
|
|
|
}
|
|
|
|
|
|
|
|
if len(resp.Queries) != 1 {
|
|
|
|
t.Fatalf("bad: %v", resp)
|
|
|
|
}
|
|
|
|
actual := resp.Queries[0]
|
|
|
|
if resp.Index != actual.ModifyIndex {
|
|
|
|
t.Fatalf("bad index: %d", resp.Index)
|
|
|
|
}
|
|
|
|
actual.CreateIndex, actual.ModifyIndex = 0, 0
|
|
|
|
if !reflect.DeepEqual(actual, query.Query) {
|
|
|
|
t.Fatalf("bad: %v", actual)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
// But a management token should be able to see it.
|
|
|
|
query.Query.Token = "le-token"
|
|
|
|
{
|
|
|
|
req := &structs.PreparedQuerySpecificRequest{
|
|
|
|
Datacenter: "dc1",
|
|
|
|
QueryID: query.Query.ID,
|
|
|
|
QueryOptions: structs.QueryOptions{Token: "root"},
|
|
|
|
}
|
|
|
|
var resp structs.IndexedPreparedQueries
|
|
|
|
if err := msgpackrpc.CallWithCodec(codec, "PreparedQuery.Get", req, &resp); err != nil {
|
|
|
|
t.Fatalf("err: %v", err)
|
|
|
|
}
|
|
|
|
|
|
|
|
if len(resp.Queries) != 1 {
|
|
|
|
t.Fatalf("bad: %v", resp)
|
|
|
|
}
|
|
|
|
actual := resp.Queries[0]
|
|
|
|
if resp.Index != actual.ModifyIndex {
|
|
|
|
t.Fatalf("bad index: %d", resp.Index)
|
|
|
|
}
|
|
|
|
actual.CreateIndex, actual.ModifyIndex = 0, 0
|
|
|
|
if !reflect.DeepEqual(actual, query.Query) {
|
|
|
|
t.Fatalf("bad: %v", actual)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2015-11-11 20:20:40 +00:00
|
|
|
// Try to get an unknown ID.
|
2015-11-10 20:13:40 +00:00
|
|
|
{
|
|
|
|
req := &structs.PreparedQuerySpecificRequest{
|
2015-11-11 20:20:40 +00:00
|
|
|
Datacenter: "dc1",
|
|
|
|
QueryID: generateUUID(),
|
Creates new "prepared-query" ACL type and new token capture behavior.
Prior to this change, prepared queries had the following behavior for
ACLs, which will need to change to support templates:
1. A management token, or a token with read access to the service being
queried needed to be provided in order to create a prepared query.
2. The token used to create the prepared query was stored with the query
in the state store and used to execute the query.
3. A management token, or the token used to create the query needed to be
supplied to perform and CRUD operations on an existing prepared query.
This was pretty subtle and complicated behavior, and won't work for
templates since the service name is computed at execution time. To solve
this, we introduce a new "prepared-query" ACL type, where the prefix
applies to the query name for static prepared query types and to the
prefix for template prepared query types.
With this change, the new behavior is:
1. A management token, or a token with "prepared-query" write access to
the query name or (soon) the given template prefix is required to do
any CRUD operations on a prepared query, or to list prepared queries
(the list is filtered by this ACL).
2. You will no longer need a management token to list prepared queries,
but you will only be able to see prepared queries that you have access
to (you get an empty list instead of permission denied).
3. When listing or getting a query, because it was easy to capture
management tokens given the past behavior, this will always blank out
the "Token" field (replacing the contents as <hidden>) for all tokens
unless a management token is supplied. Going forward, we should
discourage people from binding tokens for execution unless strictly
necessary.
4. No token will be captured by default when a prepared query is created.
If the user wishes to supply an execution token then can pass it in via
the "Token" field in the prepared query definition. Otherwise, this
field will default to empty.
5. At execution time, we will use the captured token if it exists with the
prepared query definition, otherwise we will use the token that's passed
in with the request, just like we do for other RPCs (or you can use the
agent's configured token for DNS).
6. Prepared queries with no name (accessible only by ID) will not require
ACLs to create or modify (execution time will depend on the service ACL
configuration). Our argument here is that these are designed to be
ephemeral and the IDs are as good as an ACL. Management tokens will be
able to list all of these.
These changes enable templates, but also enable delegation of authority to
manage the prepared query namespace.
2016-02-23 08:12:58 +00:00
|
|
|
QueryOptions: structs.QueryOptions{Token: token},
|
2015-11-10 20:13:40 +00:00
|
|
|
}
|
|
|
|
var resp structs.IndexedPreparedQueries
|
2015-11-11 20:20:40 +00:00
|
|
|
if err := msgpackrpc.CallWithCodec(codec, "PreparedQuery.Get", req, &resp); err != nil {
|
2015-11-13 20:57:06 +00:00
|
|
|
if err.Error() != ErrQueryNotFound.Error() {
|
|
|
|
t.Fatalf("err: %v", err)
|
|
|
|
}
|
2015-11-10 20:13:40 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
if len(resp.Queries) != 0 {
|
|
|
|
t.Fatalf("bad: %v", resp)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
func TestPreparedQuery_List(t *testing.T) {
|
2017-06-27 13:22:18 +00:00
|
|
|
t.Parallel()
|
2015-11-10 20:13:40 +00:00
|
|
|
dir1, s1 := testServerWithConfig(t, func(c *Config) {
|
|
|
|
c.ACLDatacenter = "dc1"
|
|
|
|
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")
|
2015-11-10 20:13:40 +00:00
|
|
|
|
Creates new "prepared-query" ACL type and new token capture behavior.
Prior to this change, prepared queries had the following behavior for
ACLs, which will need to change to support templates:
1. A management token, or a token with read access to the service being
queried needed to be provided in order to create a prepared query.
2. The token used to create the prepared query was stored with the query
in the state store and used to execute the query.
3. A management token, or the token used to create the query needed to be
supplied to perform and CRUD operations on an existing prepared query.
This was pretty subtle and complicated behavior, and won't work for
templates since the service name is computed at execution time. To solve
this, we introduce a new "prepared-query" ACL type, where the prefix
applies to the query name for static prepared query types and to the
prefix for template prepared query types.
With this change, the new behavior is:
1. A management token, or a token with "prepared-query" write access to
the query name or (soon) the given template prefix is required to do
any CRUD operations on a prepared query, or to list prepared queries
(the list is filtered by this ACL).
2. You will no longer need a management token to list prepared queries,
but you will only be able to see prepared queries that you have access
to (you get an empty list instead of permission denied).
3. When listing or getting a query, because it was easy to capture
management tokens given the past behavior, this will always blank out
the "Token" field (replacing the contents as <hidden>) for all tokens
unless a management token is supplied. Going forward, we should
discourage people from binding tokens for execution unless strictly
necessary.
4. No token will be captured by default when a prepared query is created.
If the user wishes to supply an execution token then can pass it in via
the "Token" field in the prepared query definition. Otherwise, this
field will default to empty.
5. At execution time, we will use the captured token if it exists with the
prepared query definition, otherwise we will use the token that's passed
in with the request, just like we do for other RPCs (or you can use the
agent's configured token for DNS).
6. Prepared queries with no name (accessible only by ID) will not require
ACLs to create or modify (execution time will depend on the service ACL
configuration). Our argument here is that these are designed to be
ephemeral and the IDs are as good as an ACL. Management tokens will be
able to list all of these.
These changes enable templates, but also enable delegation of authority to
manage the prepared query namespace.
2016-02-23 08:12:58 +00:00
|
|
|
// Create an ACL with write permissions for redis queries.
|
2015-11-10 20:13:40 +00:00
|
|
|
var token string
|
|
|
|
{
|
|
|
|
var rules = `
|
2016-02-25 00:57:55 +00:00
|
|
|
query "redis" {
|
Creates new "prepared-query" ACL type and new token capture behavior.
Prior to this change, prepared queries had the following behavior for
ACLs, which will need to change to support templates:
1. A management token, or a token with read access to the service being
queried needed to be provided in order to create a prepared query.
2. The token used to create the prepared query was stored with the query
in the state store and used to execute the query.
3. A management token, or the token used to create the query needed to be
supplied to perform and CRUD operations on an existing prepared query.
This was pretty subtle and complicated behavior, and won't work for
templates since the service name is computed at execution time. To solve
this, we introduce a new "prepared-query" ACL type, where the prefix
applies to the query name for static prepared query types and to the
prefix for template prepared query types.
With this change, the new behavior is:
1. A management token, or a token with "prepared-query" write access to
the query name or (soon) the given template prefix is required to do
any CRUD operations on a prepared query, or to list prepared queries
(the list is filtered by this ACL).
2. You will no longer need a management token to list prepared queries,
but you will only be able to see prepared queries that you have access
to (you get an empty list instead of permission denied).
3. When listing or getting a query, because it was easy to capture
management tokens given the past behavior, this will always blank out
the "Token" field (replacing the contents as <hidden>) for all tokens
unless a management token is supplied. Going forward, we should
discourage people from binding tokens for execution unless strictly
necessary.
4. No token will be captured by default when a prepared query is created.
If the user wishes to supply an execution token then can pass it in via
the "Token" field in the prepared query definition. Otherwise, this
field will default to empty.
5. At execution time, we will use the captured token if it exists with the
prepared query definition, otherwise we will use the token that's passed
in with the request, just like we do for other RPCs (or you can use the
agent's configured token for DNS).
6. Prepared queries with no name (accessible only by ID) will not require
ACLs to create or modify (execution time will depend on the service ACL
configuration). Our argument here is that these are designed to be
ephemeral and the IDs are as good as an ACL. Management tokens will be
able to list all of these.
These changes enable templates, but also enable delegation of authority to
manage the prepared query namespace.
2016-02-23 08:12:58 +00:00
|
|
|
policy = "write"
|
2015-11-10 20:13:40 +00:00
|
|
|
}
|
|
|
|
`
|
|
|
|
|
|
|
|
req := structs.ACLRequest{
|
|
|
|
Datacenter: "dc1",
|
|
|
|
Op: structs.ACLSet,
|
|
|
|
ACL: structs.ACL{
|
|
|
|
Name: "User token",
|
|
|
|
Type: structs.ACLTypeClient,
|
|
|
|
Rules: rules,
|
|
|
|
},
|
|
|
|
WriteRequest: structs.WriteRequest{Token: "root"},
|
|
|
|
}
|
2015-11-11 01:42:52 +00:00
|
|
|
if err := msgpackrpc.CallWithCodec(codec, "ACL.Apply", &req, &token); err != nil {
|
2015-11-10 20:13:40 +00:00
|
|
|
t.Fatalf("err: %v", err)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
Creates new "prepared-query" ACL type and new token capture behavior.
Prior to this change, prepared queries had the following behavior for
ACLs, which will need to change to support templates:
1. A management token, or a token with read access to the service being
queried needed to be provided in order to create a prepared query.
2. The token used to create the prepared query was stored with the query
in the state store and used to execute the query.
3. A management token, or the token used to create the query needed to be
supplied to perform and CRUD operations on an existing prepared query.
This was pretty subtle and complicated behavior, and won't work for
templates since the service name is computed at execution time. To solve
this, we introduce a new "prepared-query" ACL type, where the prefix
applies to the query name for static prepared query types and to the
prefix for template prepared query types.
With this change, the new behavior is:
1. A management token, or a token with "prepared-query" write access to
the query name or (soon) the given template prefix is required to do
any CRUD operations on a prepared query, or to list prepared queries
(the list is filtered by this ACL).
2. You will no longer need a management token to list prepared queries,
but you will only be able to see prepared queries that you have access
to (you get an empty list instead of permission denied).
3. When listing or getting a query, because it was easy to capture
management tokens given the past behavior, this will always blank out
the "Token" field (replacing the contents as <hidden>) for all tokens
unless a management token is supplied. Going forward, we should
discourage people from binding tokens for execution unless strictly
necessary.
4. No token will be captured by default when a prepared query is created.
If the user wishes to supply an execution token then can pass it in via
the "Token" field in the prepared query definition. Otherwise, this
field will default to empty.
5. At execution time, we will use the captured token if it exists with the
prepared query definition, otherwise we will use the token that's passed
in with the request, just like we do for other RPCs (or you can use the
agent's configured token for DNS).
6. Prepared queries with no name (accessible only by ID) will not require
ACLs to create or modify (execution time will depend on the service ACL
configuration). Our argument here is that these are designed to be
ephemeral and the IDs are as good as an ACL. Management tokens will be
able to list all of these.
These changes enable templates, but also enable delegation of authority to
manage the prepared query namespace.
2016-02-23 08:12:58 +00:00
|
|
|
// Query with a legit token but no queries.
|
2015-11-10 20:13:40 +00:00
|
|
|
{
|
|
|
|
req := &structs.DCSpecificRequest{
|
|
|
|
Datacenter: "dc1",
|
Creates new "prepared-query" ACL type and new token capture behavior.
Prior to this change, prepared queries had the following behavior for
ACLs, which will need to change to support templates:
1. A management token, or a token with read access to the service being
queried needed to be provided in order to create a prepared query.
2. The token used to create the prepared query was stored with the query
in the state store and used to execute the query.
3. A management token, or the token used to create the query needed to be
supplied to perform and CRUD operations on an existing prepared query.
This was pretty subtle and complicated behavior, and won't work for
templates since the service name is computed at execution time. To solve
this, we introduce a new "prepared-query" ACL type, where the prefix
applies to the query name for static prepared query types and to the
prefix for template prepared query types.
With this change, the new behavior is:
1. A management token, or a token with "prepared-query" write access to
the query name or (soon) the given template prefix is required to do
any CRUD operations on a prepared query, or to list prepared queries
(the list is filtered by this ACL).
2. You will no longer need a management token to list prepared queries,
but you will only be able to see prepared queries that you have access
to (you get an empty list instead of permission denied).
3. When listing or getting a query, because it was easy to capture
management tokens given the past behavior, this will always blank out
the "Token" field (replacing the contents as <hidden>) for all tokens
unless a management token is supplied. Going forward, we should
discourage people from binding tokens for execution unless strictly
necessary.
4. No token will be captured by default when a prepared query is created.
If the user wishes to supply an execution token then can pass it in via
the "Token" field in the prepared query definition. Otherwise, this
field will default to empty.
5. At execution time, we will use the captured token if it exists with the
prepared query definition, otherwise we will use the token that's passed
in with the request, just like we do for other RPCs (or you can use the
agent's configured token for DNS).
6. Prepared queries with no name (accessible only by ID) will not require
ACLs to create or modify (execution time will depend on the service ACL
configuration). Our argument here is that these are designed to be
ephemeral and the IDs are as good as an ACL. Management tokens will be
able to list all of these.
These changes enable templates, but also enable delegation of authority to
manage the prepared query namespace.
2016-02-23 08:12:58 +00:00
|
|
|
QueryOptions: structs.QueryOptions{Token: token},
|
2015-11-10 20:13:40 +00:00
|
|
|
}
|
|
|
|
var resp structs.IndexedPreparedQueries
|
|
|
|
if err := msgpackrpc.CallWithCodec(codec, "PreparedQuery.List", req, &resp); err != nil {
|
|
|
|
t.Fatalf("err: %v", err)
|
|
|
|
}
|
|
|
|
|
|
|
|
if len(resp.Queries) != 0 {
|
|
|
|
t.Fatalf("bad: %v", resp)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
// Set up a bare bones query.
|
|
|
|
query := structs.PreparedQueryRequest{
|
|
|
|
Datacenter: "dc1",
|
|
|
|
Op: structs.PreparedQueryCreate,
|
|
|
|
Query: &structs.PreparedQuery{
|
2016-02-26 23:59:00 +00:00
|
|
|
Name: "redis-master",
|
|
|
|
Token: "le-token",
|
2015-11-10 20:13:40 +00:00
|
|
|
Service: structs.ServiceQuery{
|
2016-02-24 09:26:16 +00:00
|
|
|
Service: "the-redis",
|
2015-11-10 20:13:40 +00:00
|
|
|
},
|
|
|
|
},
|
|
|
|
WriteRequest: structs.WriteRequest{Token: token},
|
|
|
|
}
|
|
|
|
var reply string
|
|
|
|
if err := msgpackrpc.CallWithCodec(codec, "PreparedQuery.Apply", &query, &reply); err != nil {
|
|
|
|
t.Fatalf("err: %v", err)
|
|
|
|
}
|
|
|
|
|
2016-02-26 23:59:00 +00:00
|
|
|
// Capture the ID and read back the query to verify. We also make sure
|
|
|
|
// the captured token gets redacted.
|
2015-11-10 20:13:40 +00:00
|
|
|
query.Query.ID = reply
|
2016-02-26 23:59:00 +00:00
|
|
|
query.Query.Token = redactedToken
|
2015-11-10 20:13:40 +00:00
|
|
|
{
|
|
|
|
req := &structs.DCSpecificRequest{
|
|
|
|
Datacenter: "dc1",
|
|
|
|
QueryOptions: structs.QueryOptions{Token: token},
|
|
|
|
}
|
|
|
|
var resp structs.IndexedPreparedQueries
|
Creates new "prepared-query" ACL type and new token capture behavior.
Prior to this change, prepared queries had the following behavior for
ACLs, which will need to change to support templates:
1. A management token, or a token with read access to the service being
queried needed to be provided in order to create a prepared query.
2. The token used to create the prepared query was stored with the query
in the state store and used to execute the query.
3. A management token, or the token used to create the query needed to be
supplied to perform and CRUD operations on an existing prepared query.
This was pretty subtle and complicated behavior, and won't work for
templates since the service name is computed at execution time. To solve
this, we introduce a new "prepared-query" ACL type, where the prefix
applies to the query name for static prepared query types and to the
prefix for template prepared query types.
With this change, the new behavior is:
1. A management token, or a token with "prepared-query" write access to
the query name or (soon) the given template prefix is required to do
any CRUD operations on a prepared query, or to list prepared queries
(the list is filtered by this ACL).
2. You will no longer need a management token to list prepared queries,
but you will only be able to see prepared queries that you have access
to (you get an empty list instead of permission denied).
3. When listing or getting a query, because it was easy to capture
management tokens given the past behavior, this will always blank out
the "Token" field (replacing the contents as <hidden>) for all tokens
unless a management token is supplied. Going forward, we should
discourage people from binding tokens for execution unless strictly
necessary.
4. No token will be captured by default when a prepared query is created.
If the user wishes to supply an execution token then can pass it in via
the "Token" field in the prepared query definition. Otherwise, this
field will default to empty.
5. At execution time, we will use the captured token if it exists with the
prepared query definition, otherwise we will use the token that's passed
in with the request, just like we do for other RPCs (or you can use the
agent's configured token for DNS).
6. Prepared queries with no name (accessible only by ID) will not require
ACLs to create or modify (execution time will depend on the service ACL
configuration). Our argument here is that these are designed to be
ephemeral and the IDs are as good as an ACL. Management tokens will be
able to list all of these.
These changes enable templates, but also enable delegation of authority to
manage the prepared query namespace.
2016-02-23 08:12:58 +00:00
|
|
|
if err := msgpackrpc.CallWithCodec(codec, "PreparedQuery.List", req, &resp); err != nil {
|
|
|
|
t.Fatalf("err: %v", err)
|
2015-11-10 20:13:40 +00:00
|
|
|
}
|
|
|
|
|
Creates new "prepared-query" ACL type and new token capture behavior.
Prior to this change, prepared queries had the following behavior for
ACLs, which will need to change to support templates:
1. A management token, or a token with read access to the service being
queried needed to be provided in order to create a prepared query.
2. The token used to create the prepared query was stored with the query
in the state store and used to execute the query.
3. A management token, or the token used to create the query needed to be
supplied to perform and CRUD operations on an existing prepared query.
This was pretty subtle and complicated behavior, and won't work for
templates since the service name is computed at execution time. To solve
this, we introduce a new "prepared-query" ACL type, where the prefix
applies to the query name for static prepared query types and to the
prefix for template prepared query types.
With this change, the new behavior is:
1. A management token, or a token with "prepared-query" write access to
the query name or (soon) the given template prefix is required to do
any CRUD operations on a prepared query, or to list prepared queries
(the list is filtered by this ACL).
2. You will no longer need a management token to list prepared queries,
but you will only be able to see prepared queries that you have access
to (you get an empty list instead of permission denied).
3. When listing or getting a query, because it was easy to capture
management tokens given the past behavior, this will always blank out
the "Token" field (replacing the contents as <hidden>) for all tokens
unless a management token is supplied. Going forward, we should
discourage people from binding tokens for execution unless strictly
necessary.
4. No token will be captured by default when a prepared query is created.
If the user wishes to supply an execution token then can pass it in via
the "Token" field in the prepared query definition. Otherwise, this
field will default to empty.
5. At execution time, we will use the captured token if it exists with the
prepared query definition, otherwise we will use the token that's passed
in with the request, just like we do for other RPCs (or you can use the
agent's configured token for DNS).
6. Prepared queries with no name (accessible only by ID) will not require
ACLs to create or modify (execution time will depend on the service ACL
configuration). Our argument here is that these are designed to be
ephemeral and the IDs are as good as an ACL. Management tokens will be
able to list all of these.
These changes enable templates, but also enable delegation of authority to
manage the prepared query namespace.
2016-02-23 08:12:58 +00:00
|
|
|
if len(resp.Queries) != 1 {
|
2015-11-10 20:13:40 +00:00
|
|
|
t.Fatalf("bad: %v", resp)
|
|
|
|
}
|
Creates new "prepared-query" ACL type and new token capture behavior.
Prior to this change, prepared queries had the following behavior for
ACLs, which will need to change to support templates:
1. A management token, or a token with read access to the service being
queried needed to be provided in order to create a prepared query.
2. The token used to create the prepared query was stored with the query
in the state store and used to execute the query.
3. A management token, or the token used to create the query needed to be
supplied to perform and CRUD operations on an existing prepared query.
This was pretty subtle and complicated behavior, and won't work for
templates since the service name is computed at execution time. To solve
this, we introduce a new "prepared-query" ACL type, where the prefix
applies to the query name for static prepared query types and to the
prefix for template prepared query types.
With this change, the new behavior is:
1. A management token, or a token with "prepared-query" write access to
the query name or (soon) the given template prefix is required to do
any CRUD operations on a prepared query, or to list prepared queries
(the list is filtered by this ACL).
2. You will no longer need a management token to list prepared queries,
but you will only be able to see prepared queries that you have access
to (you get an empty list instead of permission denied).
3. When listing or getting a query, because it was easy to capture
management tokens given the past behavior, this will always blank out
the "Token" field (replacing the contents as <hidden>) for all tokens
unless a management token is supplied. Going forward, we should
discourage people from binding tokens for execution unless strictly
necessary.
4. No token will be captured by default when a prepared query is created.
If the user wishes to supply an execution token then can pass it in via
the "Token" field in the prepared query definition. Otherwise, this
field will default to empty.
5. At execution time, we will use the captured token if it exists with the
prepared query definition, otherwise we will use the token that's passed
in with the request, just like we do for other RPCs (or you can use the
agent's configured token for DNS).
6. Prepared queries with no name (accessible only by ID) will not require
ACLs to create or modify (execution time will depend on the service ACL
configuration). Our argument here is that these are designed to be
ephemeral and the IDs are as good as an ACL. Management tokens will be
able to list all of these.
These changes enable templates, but also enable delegation of authority to
manage the prepared query namespace.
2016-02-23 08:12:58 +00:00
|
|
|
actual := resp.Queries[0]
|
|
|
|
if resp.Index != actual.ModifyIndex {
|
|
|
|
t.Fatalf("bad index: %d", resp.Index)
|
|
|
|
}
|
|
|
|
actual.CreateIndex, actual.ModifyIndex = 0, 0
|
|
|
|
if !reflect.DeepEqual(actual, query.Query) {
|
|
|
|
t.Fatalf("bad: %v", actual)
|
|
|
|
}
|
2015-11-10 20:13:40 +00:00
|
|
|
}
|
|
|
|
|
Creates new "prepared-query" ACL type and new token capture behavior.
Prior to this change, prepared queries had the following behavior for
ACLs, which will need to change to support templates:
1. A management token, or a token with read access to the service being
queried needed to be provided in order to create a prepared query.
2. The token used to create the prepared query was stored with the query
in the state store and used to execute the query.
3. A management token, or the token used to create the query needed to be
supplied to perform and CRUD operations on an existing prepared query.
This was pretty subtle and complicated behavior, and won't work for
templates since the service name is computed at execution time. To solve
this, we introduce a new "prepared-query" ACL type, where the prefix
applies to the query name for static prepared query types and to the
prefix for template prepared query types.
With this change, the new behavior is:
1. A management token, or a token with "prepared-query" write access to
the query name or (soon) the given template prefix is required to do
any CRUD operations on a prepared query, or to list prepared queries
(the list is filtered by this ACL).
2. You will no longer need a management token to list prepared queries,
but you will only be able to see prepared queries that you have access
to (you get an empty list instead of permission denied).
3. When listing or getting a query, because it was easy to capture
management tokens given the past behavior, this will always blank out
the "Token" field (replacing the contents as <hidden>) for all tokens
unless a management token is supplied. Going forward, we should
discourage people from binding tokens for execution unless strictly
necessary.
4. No token will be captured by default when a prepared query is created.
If the user wishes to supply an execution token then can pass it in via
the "Token" field in the prepared query definition. Otherwise, this
field will default to empty.
5. At execution time, we will use the captured token if it exists with the
prepared query definition, otherwise we will use the token that's passed
in with the request, just like we do for other RPCs (or you can use the
agent's configured token for DNS).
6. Prepared queries with no name (accessible only by ID) will not require
ACLs to create or modify (execution time will depend on the service ACL
configuration). Our argument here is that these are designed to be
ephemeral and the IDs are as good as an ACL. Management tokens will be
able to list all of these.
These changes enable templates, but also enable delegation of authority to
manage the prepared query namespace.
2016-02-23 08:12:58 +00:00
|
|
|
// An empty token should result in an empty list because of ACL
|
|
|
|
// filtering.
|
2015-11-10 20:13:40 +00:00
|
|
|
{
|
|
|
|
req := &structs.DCSpecificRequest{
|
|
|
|
Datacenter: "dc1",
|
|
|
|
QueryOptions: structs.QueryOptions{Token: ""},
|
|
|
|
}
|
|
|
|
var resp structs.IndexedPreparedQueries
|
Creates new "prepared-query" ACL type and new token capture behavior.
Prior to this change, prepared queries had the following behavior for
ACLs, which will need to change to support templates:
1. A management token, or a token with read access to the service being
queried needed to be provided in order to create a prepared query.
2. The token used to create the prepared query was stored with the query
in the state store and used to execute the query.
3. A management token, or the token used to create the query needed to be
supplied to perform and CRUD operations on an existing prepared query.
This was pretty subtle and complicated behavior, and won't work for
templates since the service name is computed at execution time. To solve
this, we introduce a new "prepared-query" ACL type, where the prefix
applies to the query name for static prepared query types and to the
prefix for template prepared query types.
With this change, the new behavior is:
1. A management token, or a token with "prepared-query" write access to
the query name or (soon) the given template prefix is required to do
any CRUD operations on a prepared query, or to list prepared queries
(the list is filtered by this ACL).
2. You will no longer need a management token to list prepared queries,
but you will only be able to see prepared queries that you have access
to (you get an empty list instead of permission denied).
3. When listing or getting a query, because it was easy to capture
management tokens given the past behavior, this will always blank out
the "Token" field (replacing the contents as <hidden>) for all tokens
unless a management token is supplied. Going forward, we should
discourage people from binding tokens for execution unless strictly
necessary.
4. No token will be captured by default when a prepared query is created.
If the user wishes to supply an execution token then can pass it in via
the "Token" field in the prepared query definition. Otherwise, this
field will default to empty.
5. At execution time, we will use the captured token if it exists with the
prepared query definition, otherwise we will use the token that's passed
in with the request, just like we do for other RPCs (or you can use the
agent's configured token for DNS).
6. Prepared queries with no name (accessible only by ID) will not require
ACLs to create or modify (execution time will depend on the service ACL
configuration). Our argument here is that these are designed to be
ephemeral and the IDs are as good as an ACL. Management tokens will be
able to list all of these.
These changes enable templates, but also enable delegation of authority to
manage the prepared query namespace.
2016-02-23 08:12:58 +00:00
|
|
|
if err := msgpackrpc.CallWithCodec(codec, "PreparedQuery.List", req, &resp); err != nil {
|
|
|
|
t.Fatalf("err: %v", err)
|
2015-11-10 20:13:40 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
if len(resp.Queries) != 0 {
|
|
|
|
t.Fatalf("bad: %v", resp)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2016-02-26 23:59:00 +00:00
|
|
|
// But a management token should work, and be able to see the captured
|
|
|
|
// token.
|
|
|
|
query.Query.Token = "le-token"
|
2015-11-10 20:13:40 +00:00
|
|
|
{
|
|
|
|
req := &structs.DCSpecificRequest{
|
|
|
|
Datacenter: "dc1",
|
|
|
|
QueryOptions: structs.QueryOptions{Token: "root"},
|
|
|
|
}
|
|
|
|
var resp structs.IndexedPreparedQueries
|
|
|
|
if err := msgpackrpc.CallWithCodec(codec, "PreparedQuery.List", req, &resp); err != nil {
|
|
|
|
t.Fatalf("err: %v", err)
|
|
|
|
}
|
|
|
|
|
|
|
|
if len(resp.Queries) != 1 {
|
|
|
|
t.Fatalf("bad: %v", resp)
|
|
|
|
}
|
|
|
|
actual := resp.Queries[0]
|
|
|
|
if resp.Index != actual.ModifyIndex {
|
|
|
|
t.Fatalf("bad index: %d", resp.Index)
|
|
|
|
}
|
|
|
|
actual.CreateIndex, actual.ModifyIndex = 0, 0
|
|
|
|
if !reflect.DeepEqual(actual, query.Query) {
|
|
|
|
t.Fatalf("bad: %v", actual)
|
|
|
|
}
|
|
|
|
}
|
2016-02-24 09:26:16 +00:00
|
|
|
|
2016-12-10 19:19:08 +00:00
|
|
|
// Create a session.
|
|
|
|
var session string
|
|
|
|
{
|
|
|
|
req := structs.SessionRequest{
|
|
|
|
Datacenter: "dc1",
|
|
|
|
Op: structs.SessionCreate,
|
|
|
|
Session: structs.Session{
|
|
|
|
Node: s1.config.NodeName,
|
|
|
|
},
|
|
|
|
}
|
|
|
|
if err := msgpackrpc.CallWithCodec(codec, "Session.Apply", &req, &session); err != nil {
|
|
|
|
t.Fatalf("err: %v", err)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2016-02-24 09:26:16 +00:00
|
|
|
// Now take away the query name.
|
|
|
|
query.Op = structs.PreparedQueryUpdate
|
|
|
|
query.Query.Name = ""
|
2016-12-10 19:19:08 +00:00
|
|
|
query.Query.Session = session
|
2016-02-24 09:26:16 +00:00
|
|
|
if err := msgpackrpc.CallWithCodec(codec, "PreparedQuery.Apply", &query, &reply); err != nil {
|
|
|
|
t.Fatalf("err: %v", err)
|
|
|
|
}
|
|
|
|
|
|
|
|
// A query with the redis token shouldn't show anything since it doesn't
|
|
|
|
// match any un-named queries.
|
|
|
|
{
|
|
|
|
req := &structs.DCSpecificRequest{
|
|
|
|
Datacenter: "dc1",
|
|
|
|
QueryOptions: structs.QueryOptions{Token: token},
|
|
|
|
}
|
|
|
|
var resp structs.IndexedPreparedQueries
|
|
|
|
if err := msgpackrpc.CallWithCodec(codec, "PreparedQuery.List", req, &resp); err != nil {
|
|
|
|
t.Fatalf("err: %v", err)
|
|
|
|
}
|
|
|
|
|
|
|
|
if len(resp.Queries) != 0 {
|
|
|
|
t.Fatalf("bad: %v", resp)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
// But a management token should work.
|
|
|
|
{
|
|
|
|
req := &structs.DCSpecificRequest{
|
|
|
|
Datacenter: "dc1",
|
|
|
|
QueryOptions: structs.QueryOptions{Token: "root"},
|
|
|
|
}
|
|
|
|
var resp structs.IndexedPreparedQueries
|
|
|
|
if err := msgpackrpc.CallWithCodec(codec, "PreparedQuery.List", req, &resp); err != nil {
|
|
|
|
t.Fatalf("err: %v", err)
|
|
|
|
}
|
|
|
|
|
|
|
|
if len(resp.Queries) != 1 {
|
|
|
|
t.Fatalf("bad: %v", resp)
|
|
|
|
}
|
|
|
|
actual := resp.Queries[0]
|
|
|
|
if resp.Index != actual.ModifyIndex {
|
|
|
|
t.Fatalf("bad index: %d", resp.Index)
|
|
|
|
}
|
|
|
|
actual.CreateIndex, actual.ModifyIndex = 0, 0
|
|
|
|
if !reflect.DeepEqual(actual, query.Query) {
|
|
|
|
t.Fatalf("bad: %v", actual)
|
|
|
|
}
|
|
|
|
}
|
2015-11-10 20:13:40 +00:00
|
|
|
}
|
2015-11-10 23:16:41 +00:00
|
|
|
|
2016-03-04 01:30:36 +00:00
|
|
|
func TestPreparedQuery_Explain(t *testing.T) {
|
2017-06-27 13:22:18 +00:00
|
|
|
t.Parallel()
|
2016-03-03 09:04:12 +00:00
|
|
|
dir1, s1 := testServerWithConfig(t, func(c *Config) {
|
|
|
|
c.ACLDatacenter = "dc1"
|
|
|
|
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-03-03 09:04:12 +00:00
|
|
|
|
|
|
|
// Create an ACL with write permissions for prod- queries.
|
|
|
|
var token string
|
|
|
|
{
|
|
|
|
var rules = `
|
|
|
|
query "prod-" {
|
|
|
|
policy = "write"
|
|
|
|
}
|
|
|
|
`
|
|
|
|
|
|
|
|
req := structs.ACLRequest{
|
|
|
|
Datacenter: "dc1",
|
|
|
|
Op: structs.ACLSet,
|
|
|
|
ACL: structs.ACL{
|
|
|
|
Name: "User token",
|
|
|
|
Type: structs.ACLTypeClient,
|
|
|
|
Rules: rules,
|
|
|
|
},
|
|
|
|
WriteRequest: structs.WriteRequest{Token: "root"},
|
|
|
|
}
|
|
|
|
if err := msgpackrpc.CallWithCodec(codec, "ACL.Apply", &req, &token); err != nil {
|
|
|
|
t.Fatalf("err: %v", err)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
// Set up a template.
|
|
|
|
query := structs.PreparedQueryRequest{
|
|
|
|
Datacenter: "dc1",
|
|
|
|
Op: structs.PreparedQueryCreate,
|
|
|
|
Query: &structs.PreparedQuery{
|
|
|
|
Name: "prod-",
|
|
|
|
Token: "5e1e24e5-1329-f86f-18c6-3d3734edb2cd",
|
|
|
|
Template: structs.QueryTemplateOptions{
|
|
|
|
Type: structs.QueryTemplateTypeNamePrefixMatch,
|
|
|
|
},
|
|
|
|
Service: structs.ServiceQuery{
|
|
|
|
Service: "${name.full}",
|
|
|
|
},
|
|
|
|
},
|
|
|
|
WriteRequest: structs.WriteRequest{Token: token},
|
|
|
|
}
|
|
|
|
var reply string
|
|
|
|
if err := msgpackrpc.CallWithCodec(codec, "PreparedQuery.Apply", &query, &reply); err != nil {
|
|
|
|
t.Fatalf("err: %v", err)
|
|
|
|
}
|
|
|
|
|
2016-03-04 01:30:36 +00:00
|
|
|
// Explain via the management token.
|
2016-03-03 09:04:12 +00:00
|
|
|
query.Query.ID = reply
|
|
|
|
query.Query.Service.Service = "prod-redis"
|
|
|
|
{
|
|
|
|
req := &structs.PreparedQueryExecuteRequest{
|
|
|
|
Datacenter: "dc1",
|
|
|
|
QueryIDOrName: "prod-redis",
|
|
|
|
QueryOptions: structs.QueryOptions{Token: "root"},
|
|
|
|
}
|
2016-03-04 01:30:36 +00:00
|
|
|
var resp structs.PreparedQueryExplainResponse
|
|
|
|
err := msgpackrpc.CallWithCodec(codec, "PreparedQuery.Explain", req, &resp)
|
2016-03-03 09:04:12 +00:00
|
|
|
if err != nil {
|
|
|
|
t.Fatalf("err: %v", err)
|
|
|
|
}
|
|
|
|
|
|
|
|
actual := &resp.Query
|
|
|
|
actual.CreateIndex, actual.ModifyIndex = 0, 0
|
|
|
|
if !reflect.DeepEqual(actual, query.Query) {
|
|
|
|
t.Fatalf("bad: %v", actual)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2016-03-04 01:30:36 +00:00
|
|
|
// Explain via the user token, which will redact the captured token.
|
2016-03-03 09:04:12 +00:00
|
|
|
query.Query.Token = redactedToken
|
|
|
|
query.Query.Service.Service = "prod-redis"
|
|
|
|
{
|
|
|
|
req := &structs.PreparedQueryExecuteRequest{
|
|
|
|
Datacenter: "dc1",
|
|
|
|
QueryIDOrName: "prod-redis",
|
|
|
|
QueryOptions: structs.QueryOptions{Token: token},
|
|
|
|
}
|
2016-03-04 01:30:36 +00:00
|
|
|
var resp structs.PreparedQueryExplainResponse
|
|
|
|
err := msgpackrpc.CallWithCodec(codec, "PreparedQuery.Explain", req, &resp)
|
2016-03-03 09:04:12 +00:00
|
|
|
if err != nil {
|
|
|
|
t.Fatalf("err: %v", err)
|
|
|
|
}
|
|
|
|
|
|
|
|
actual := &resp.Query
|
|
|
|
actual.CreateIndex, actual.ModifyIndex = 0, 0
|
|
|
|
if !reflect.DeepEqual(actual, query.Query) {
|
|
|
|
t.Fatalf("bad: %v", actual)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2016-03-04 01:30:36 +00:00
|
|
|
// Explaining should be denied without a token, since the user isn't
|
2016-03-03 09:04:12 +00:00
|
|
|
// allowed to see the query.
|
|
|
|
{
|
|
|
|
req := &structs.PreparedQueryExecuteRequest{
|
|
|
|
Datacenter: "dc1",
|
|
|
|
QueryIDOrName: "prod-redis",
|
|
|
|
}
|
2016-03-04 01:30:36 +00:00
|
|
|
var resp structs.PreparedQueryExplainResponse
|
|
|
|
err := msgpackrpc.CallWithCodec(codec, "PreparedQuery.Explain", req, &resp)
|
2017-08-23 14:52:48 +00:00
|
|
|
if !acl.IsErrPermissionDenied(err) {
|
2016-03-03 09:04:12 +00:00
|
|
|
t.Fatalf("bad: %v", err)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2016-03-04 01:30:36 +00:00
|
|
|
// Try to explain a bogus ID.
|
2016-03-03 09:04:12 +00:00
|
|
|
{
|
|
|
|
req := &structs.PreparedQueryExecuteRequest{
|
|
|
|
Datacenter: "dc1",
|
|
|
|
QueryIDOrName: generateUUID(),
|
|
|
|
QueryOptions: structs.QueryOptions{Token: "root"},
|
|
|
|
}
|
|
|
|
var resp structs.IndexedPreparedQueries
|
2016-03-04 01:30:36 +00:00
|
|
|
if err := msgpackrpc.CallWithCodec(codec, "PreparedQuery.Explain", req, &resp); err != nil {
|
2016-03-03 09:04:12 +00:00
|
|
|
if err.Error() != ErrQueryNotFound.Error() {
|
|
|
|
t.Fatalf("err: %v", err)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2015-11-11 01:42:52 +00:00
|
|
|
// This is a beast of a test, but the setup is so extensive it makes sense to
|
|
|
|
// walk through the different cases once we have it up. This is broken into
|
|
|
|
// sections so it's still pretty easy to read.
|
|
|
|
func TestPreparedQuery_Execute(t *testing.T) {
|
2017-06-27 13:22:18 +00:00
|
|
|
t.Parallel()
|
2015-11-11 01:42:52 +00:00
|
|
|
dir1, s1 := testServerWithConfig(t, func(c *Config) {
|
|
|
|
c.ACLDatacenter = "dc1"
|
|
|
|
c.ACLMasterToken = "root"
|
|
|
|
c.ACLDefaultPolicy = "deny"
|
2016-12-13 01:27:22 +00:00
|
|
|
c.ACLEnforceVersion8 = false
|
2015-11-11 01:42:52 +00:00
|
|
|
})
|
|
|
|
defer os.RemoveAll(dir1)
|
|
|
|
defer s1.Shutdown()
|
|
|
|
codec1 := rpcClient(t, s1)
|
|
|
|
defer codec1.Close()
|
|
|
|
|
|
|
|
dir2, s2 := testServerWithConfig(t, func(c *Config) {
|
|
|
|
c.Datacenter = "dc2"
|
|
|
|
c.ACLDatacenter = "dc1"
|
|
|
|
})
|
|
|
|
defer os.RemoveAll(dir2)
|
|
|
|
defer s2.Shutdown()
|
|
|
|
codec2 := rpcClient(t, s2)
|
|
|
|
defer codec2.Close()
|
|
|
|
|
2017-04-19 23:00:11 +00:00
|
|
|
testrpc.WaitForLeader(t, s1.RPC, "dc1")
|
|
|
|
testrpc.WaitForLeader(t, s2.RPC, "dc2")
|
2015-11-11 01:42:52 +00:00
|
|
|
|
|
|
|
// Try to WAN join.
|
2017-05-05 10:29:49 +00:00
|
|
|
joinWAN(t, s2, s1)
|
2017-05-04 22:52:53 +00:00
|
|
|
retry.Run(t, func(r *retry.R) {
|
2017-04-29 16:34:02 +00:00
|
|
|
if got, want := len(s1.WANMembers()), 2; got != want {
|
|
|
|
r.Fatalf("got %d WAN members want %d", got, want)
|
|
|
|
}
|
|
|
|
})
|
2015-11-11 01:42:52 +00:00
|
|
|
|
|
|
|
// Create an ACL with read permission to the service.
|
Creates new "prepared-query" ACL type and new token capture behavior.
Prior to this change, prepared queries had the following behavior for
ACLs, which will need to change to support templates:
1. A management token, or a token with read access to the service being
queried needed to be provided in order to create a prepared query.
2. The token used to create the prepared query was stored with the query
in the state store and used to execute the query.
3. A management token, or the token used to create the query needed to be
supplied to perform and CRUD operations on an existing prepared query.
This was pretty subtle and complicated behavior, and won't work for
templates since the service name is computed at execution time. To solve
this, we introduce a new "prepared-query" ACL type, where the prefix
applies to the query name for static prepared query types and to the
prefix for template prepared query types.
With this change, the new behavior is:
1. A management token, or a token with "prepared-query" write access to
the query name or (soon) the given template prefix is required to do
any CRUD operations on a prepared query, or to list prepared queries
(the list is filtered by this ACL).
2. You will no longer need a management token to list prepared queries,
but you will only be able to see prepared queries that you have access
to (you get an empty list instead of permission denied).
3. When listing or getting a query, because it was easy to capture
management tokens given the past behavior, this will always blank out
the "Token" field (replacing the contents as <hidden>) for all tokens
unless a management token is supplied. Going forward, we should
discourage people from binding tokens for execution unless strictly
necessary.
4. No token will be captured by default when a prepared query is created.
If the user wishes to supply an execution token then can pass it in via
the "Token" field in the prepared query definition. Otherwise, this
field will default to empty.
5. At execution time, we will use the captured token if it exists with the
prepared query definition, otherwise we will use the token that's passed
in with the request, just like we do for other RPCs (or you can use the
agent's configured token for DNS).
6. Prepared queries with no name (accessible only by ID) will not require
ACLs to create or modify (execution time will depend on the service ACL
configuration). Our argument here is that these are designed to be
ephemeral and the IDs are as good as an ACL. Management tokens will be
able to list all of these.
These changes enable templates, but also enable delegation of authority to
manage the prepared query namespace.
2016-02-23 08:12:58 +00:00
|
|
|
var execToken string
|
2015-11-11 01:42:52 +00:00
|
|
|
{
|
|
|
|
var rules = `
|
|
|
|
service "foo" {
|
|
|
|
policy = "read"
|
|
|
|
}
|
|
|
|
`
|
|
|
|
|
|
|
|
req := structs.ACLRequest{
|
|
|
|
Datacenter: "dc1",
|
|
|
|
Op: structs.ACLSet,
|
|
|
|
ACL: structs.ACL{
|
|
|
|
Name: "User token",
|
|
|
|
Type: structs.ACLTypeClient,
|
|
|
|
Rules: rules,
|
|
|
|
},
|
|
|
|
WriteRequest: structs.WriteRequest{Token: "root"},
|
|
|
|
}
|
Creates new "prepared-query" ACL type and new token capture behavior.
Prior to this change, prepared queries had the following behavior for
ACLs, which will need to change to support templates:
1. A management token, or a token with read access to the service being
queried needed to be provided in order to create a prepared query.
2. The token used to create the prepared query was stored with the query
in the state store and used to execute the query.
3. A management token, or the token used to create the query needed to be
supplied to perform and CRUD operations on an existing prepared query.
This was pretty subtle and complicated behavior, and won't work for
templates since the service name is computed at execution time. To solve
this, we introduce a new "prepared-query" ACL type, where the prefix
applies to the query name for static prepared query types and to the
prefix for template prepared query types.
With this change, the new behavior is:
1. A management token, or a token with "prepared-query" write access to
the query name or (soon) the given template prefix is required to do
any CRUD operations on a prepared query, or to list prepared queries
(the list is filtered by this ACL).
2. You will no longer need a management token to list prepared queries,
but you will only be able to see prepared queries that you have access
to (you get an empty list instead of permission denied).
3. When listing or getting a query, because it was easy to capture
management tokens given the past behavior, this will always blank out
the "Token" field (replacing the contents as <hidden>) for all tokens
unless a management token is supplied. Going forward, we should
discourage people from binding tokens for execution unless strictly
necessary.
4. No token will be captured by default when a prepared query is created.
If the user wishes to supply an execution token then can pass it in via
the "Token" field in the prepared query definition. Otherwise, this
field will default to empty.
5. At execution time, we will use the captured token if it exists with the
prepared query definition, otherwise we will use the token that's passed
in with the request, just like we do for other RPCs (or you can use the
agent's configured token for DNS).
6. Prepared queries with no name (accessible only by ID) will not require
ACLs to create or modify (execution time will depend on the service ACL
configuration). Our argument here is that these are designed to be
ephemeral and the IDs are as good as an ACL. Management tokens will be
able to list all of these.
These changes enable templates, but also enable delegation of authority to
manage the prepared query namespace.
2016-02-23 08:12:58 +00:00
|
|
|
if err := msgpackrpc.CallWithCodec(codec1, "ACL.Apply", &req, &execToken); err != nil {
|
|
|
|
t.Fatalf("err: %v", err)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2015-11-11 01:42:52 +00:00
|
|
|
// Set up some nodes in each DC that host the service.
|
|
|
|
{
|
|
|
|
for i := 0; i < 10; i++ {
|
|
|
|
for _, dc := range []string{"dc1", "dc2"} {
|
|
|
|
req := structs.RegisterRequest{
|
|
|
|
Datacenter: dc,
|
|
|
|
Node: fmt.Sprintf("node%d", i+1),
|
|
|
|
Address: fmt.Sprintf("127.0.0.%d", i+1),
|
2017-01-18 23:40:19 +00:00
|
|
|
NodeMeta: map[string]string{
|
|
|
|
"group": fmt.Sprintf("%d", i/5),
|
2017-01-18 21:23:33 +00:00
|
|
|
"instance_type": "t2.micro",
|
|
|
|
},
|
2015-11-11 01:42:52 +00:00
|
|
|
Service: &structs.NodeService{
|
|
|
|
Service: "foo",
|
|
|
|
Port: 8000,
|
|
|
|
Tags: []string{dc, fmt.Sprintf("tag%d", i+1)},
|
|
|
|
},
|
|
|
|
WriteRequest: structs.WriteRequest{Token: "root"},
|
|
|
|
}
|
2017-01-18 21:23:33 +00:00
|
|
|
if i == 0 {
|
|
|
|
req.NodeMeta["unique"] = "true"
|
|
|
|
}
|
2015-11-11 01:42:52 +00:00
|
|
|
|
|
|
|
var codec rpc.ClientCodec
|
|
|
|
if dc == "dc1" {
|
|
|
|
codec = codec1
|
|
|
|
} else {
|
|
|
|
codec = codec2
|
|
|
|
}
|
|
|
|
|
|
|
|
var reply struct{}
|
|
|
|
if err := msgpackrpc.CallWithCodec(codec, "Catalog.Register", &req, &reply); err != nil {
|
|
|
|
t.Fatalf("err: %v", err)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
// Set up a service query.
|
|
|
|
query := structs.PreparedQueryRequest{
|
|
|
|
Datacenter: "dc1",
|
|
|
|
Op: structs.PreparedQueryCreate,
|
|
|
|
Query: &structs.PreparedQuery{
|
2016-12-10 19:19:08 +00:00
|
|
|
Name: "test",
|
2015-11-11 01:42:52 +00:00
|
|
|
Service: structs.ServiceQuery{
|
|
|
|
Service: "foo",
|
|
|
|
},
|
|
|
|
DNS: structs.QueryDNSOptions{
|
|
|
|
TTL: "10s",
|
|
|
|
},
|
|
|
|
},
|
2016-02-24 09:26:16 +00:00
|
|
|
WriteRequest: structs.WriteRequest{Token: "root"},
|
2015-11-11 01:42:52 +00:00
|
|
|
}
|
|
|
|
if err := msgpackrpc.CallWithCodec(codec1, "PreparedQuery.Apply", &query, &query.Query.ID); err != nil {
|
|
|
|
t.Fatalf("err: %v", err)
|
|
|
|
}
|
|
|
|
|
|
|
|
// Run a query that doesn't exist.
|
|
|
|
{
|
|
|
|
req := structs.PreparedQueryExecuteRequest{
|
|
|
|
Datacenter: "dc1",
|
|
|
|
QueryIDOrName: "nope",
|
|
|
|
}
|
|
|
|
|
|
|
|
var reply structs.PreparedQueryExecuteResponse
|
|
|
|
err := msgpackrpc.CallWithCodec(codec1, "PreparedQuery.Execute", &req, &reply)
|
2015-11-13 18:38:44 +00:00
|
|
|
if err == nil || err.Error() != ErrQueryNotFound.Error() {
|
2015-11-11 01:42:52 +00:00
|
|
|
t.Fatalf("bad: %v", err)
|
|
|
|
}
|
|
|
|
|
|
|
|
if len(reply.Nodes) != 0 {
|
|
|
|
t.Fatalf("bad: %v", reply)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
// Run the registered query.
|
|
|
|
{
|
|
|
|
req := structs.PreparedQueryExecuteRequest{
|
|
|
|
Datacenter: "dc1",
|
|
|
|
QueryIDOrName: query.Query.ID,
|
Creates new "prepared-query" ACL type and new token capture behavior.
Prior to this change, prepared queries had the following behavior for
ACLs, which will need to change to support templates:
1. A management token, or a token with read access to the service being
queried needed to be provided in order to create a prepared query.
2. The token used to create the prepared query was stored with the query
in the state store and used to execute the query.
3. A management token, or the token used to create the query needed to be
supplied to perform and CRUD operations on an existing prepared query.
This was pretty subtle and complicated behavior, and won't work for
templates since the service name is computed at execution time. To solve
this, we introduce a new "prepared-query" ACL type, where the prefix
applies to the query name for static prepared query types and to the
prefix for template prepared query types.
With this change, the new behavior is:
1. A management token, or a token with "prepared-query" write access to
the query name or (soon) the given template prefix is required to do
any CRUD operations on a prepared query, or to list prepared queries
(the list is filtered by this ACL).
2. You will no longer need a management token to list prepared queries,
but you will only be able to see prepared queries that you have access
to (you get an empty list instead of permission denied).
3. When listing or getting a query, because it was easy to capture
management tokens given the past behavior, this will always blank out
the "Token" field (replacing the contents as <hidden>) for all tokens
unless a management token is supplied. Going forward, we should
discourage people from binding tokens for execution unless strictly
necessary.
4. No token will be captured by default when a prepared query is created.
If the user wishes to supply an execution token then can pass it in via
the "Token" field in the prepared query definition. Otherwise, this
field will default to empty.
5. At execution time, we will use the captured token if it exists with the
prepared query definition, otherwise we will use the token that's passed
in with the request, just like we do for other RPCs (or you can use the
agent's configured token for DNS).
6. Prepared queries with no name (accessible only by ID) will not require
ACLs to create or modify (execution time will depend on the service ACL
configuration). Our argument here is that these are designed to be
ephemeral and the IDs are as good as an ACL. Management tokens will be
able to list all of these.
These changes enable templates, but also enable delegation of authority to
manage the prepared query namespace.
2016-02-23 08:12:58 +00:00
|
|
|
QueryOptions: structs.QueryOptions{Token: execToken},
|
2015-11-11 01:42:52 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
var reply structs.PreparedQueryExecuteResponse
|
|
|
|
if err := msgpackrpc.CallWithCodec(codec1, "PreparedQuery.Execute", &req, &reply); err != nil {
|
|
|
|
t.Fatalf("err: %v", err)
|
|
|
|
}
|
|
|
|
|
|
|
|
if len(reply.Nodes) != 10 ||
|
|
|
|
reply.Datacenter != "dc1" || reply.Failovers != 0 ||
|
2015-11-13 18:38:44 +00:00
|
|
|
reply.Service != query.Query.Service.Service ||
|
2015-11-12 06:34:46 +00:00
|
|
|
!reflect.DeepEqual(reply.DNS, query.Query.DNS) ||
|
|
|
|
!reply.QueryMeta.KnownLeader {
|
2015-11-11 01:42:52 +00:00
|
|
|
t.Fatalf("bad: %v", reply)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
// Try with a limit.
|
|
|
|
{
|
|
|
|
req := structs.PreparedQueryExecuteRequest{
|
|
|
|
Datacenter: "dc1",
|
|
|
|
QueryIDOrName: query.Query.ID,
|
|
|
|
Limit: 3,
|
Creates new "prepared-query" ACL type and new token capture behavior.
Prior to this change, prepared queries had the following behavior for
ACLs, which will need to change to support templates:
1. A management token, or a token with read access to the service being
queried needed to be provided in order to create a prepared query.
2. The token used to create the prepared query was stored with the query
in the state store and used to execute the query.
3. A management token, or the token used to create the query needed to be
supplied to perform and CRUD operations on an existing prepared query.
This was pretty subtle and complicated behavior, and won't work for
templates since the service name is computed at execution time. To solve
this, we introduce a new "prepared-query" ACL type, where the prefix
applies to the query name for static prepared query types and to the
prefix for template prepared query types.
With this change, the new behavior is:
1. A management token, or a token with "prepared-query" write access to
the query name or (soon) the given template prefix is required to do
any CRUD operations on a prepared query, or to list prepared queries
(the list is filtered by this ACL).
2. You will no longer need a management token to list prepared queries,
but you will only be able to see prepared queries that you have access
to (you get an empty list instead of permission denied).
3. When listing or getting a query, because it was easy to capture
management tokens given the past behavior, this will always blank out
the "Token" field (replacing the contents as <hidden>) for all tokens
unless a management token is supplied. Going forward, we should
discourage people from binding tokens for execution unless strictly
necessary.
4. No token will be captured by default when a prepared query is created.
If the user wishes to supply an execution token then can pass it in via
the "Token" field in the prepared query definition. Otherwise, this
field will default to empty.
5. At execution time, we will use the captured token if it exists with the
prepared query definition, otherwise we will use the token that's passed
in with the request, just like we do for other RPCs (or you can use the
agent's configured token for DNS).
6. Prepared queries with no name (accessible only by ID) will not require
ACLs to create or modify (execution time will depend on the service ACL
configuration). Our argument here is that these are designed to be
ephemeral and the IDs are as good as an ACL. Management tokens will be
able to list all of these.
These changes enable templates, but also enable delegation of authority to
manage the prepared query namespace.
2016-02-23 08:12:58 +00:00
|
|
|
QueryOptions: structs.QueryOptions{Token: execToken},
|
2015-11-11 01:42:52 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
var reply structs.PreparedQueryExecuteResponse
|
|
|
|
if err := msgpackrpc.CallWithCodec(codec1, "PreparedQuery.Execute", &req, &reply); err != nil {
|
|
|
|
t.Fatalf("err: %v", err)
|
|
|
|
}
|
|
|
|
|
|
|
|
if len(reply.Nodes) != 3 ||
|
|
|
|
reply.Datacenter != "dc1" || reply.Failovers != 0 ||
|
2015-11-13 18:38:44 +00:00
|
|
|
reply.Service != query.Query.Service.Service ||
|
2015-11-12 06:34:46 +00:00
|
|
|
!reflect.DeepEqual(reply.DNS, query.Query.DNS) ||
|
|
|
|
!reply.QueryMeta.KnownLeader {
|
2015-11-11 01:42:52 +00:00
|
|
|
t.Fatalf("bad: %v", reply)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2017-01-18 21:23:33 +00:00
|
|
|
// Run various service queries with node metadata filters.
|
|
|
|
if false {
|
2017-01-18 23:40:19 +00:00
|
|
|
cases := []struct {
|
|
|
|
filters map[string]string
|
2017-01-18 21:23:33 +00:00
|
|
|
numNodes int
|
|
|
|
}{
|
|
|
|
{
|
2017-01-18 23:40:19 +00:00
|
|
|
filters: map[string]string{},
|
2017-01-18 21:23:33 +00:00
|
|
|
numNodes: 10,
|
|
|
|
},
|
|
|
|
{
|
2017-01-18 23:40:19 +00:00
|
|
|
filters: map[string]string{"instance_type": "t2.micro"},
|
2017-01-18 21:23:33 +00:00
|
|
|
numNodes: 10,
|
|
|
|
},
|
|
|
|
{
|
2017-01-18 23:40:19 +00:00
|
|
|
filters: map[string]string{"group": "1"},
|
2017-01-18 21:23:33 +00:00
|
|
|
numNodes: 5,
|
|
|
|
},
|
|
|
|
{
|
2017-01-18 23:40:19 +00:00
|
|
|
filters: map[string]string{"group": "0", "unique": "true"},
|
2017-01-18 21:23:33 +00:00
|
|
|
numNodes: 1,
|
|
|
|
},
|
|
|
|
}
|
|
|
|
|
|
|
|
for _, tc := range cases {
|
|
|
|
nodeMetaQuery := structs.PreparedQueryRequest{
|
|
|
|
Datacenter: "dc1",
|
|
|
|
Op: structs.PreparedQueryCreate,
|
|
|
|
Query: &structs.PreparedQuery{
|
|
|
|
Service: structs.ServiceQuery{
|
2017-01-18 23:40:19 +00:00
|
|
|
Service: "foo",
|
2017-01-18 21:23:33 +00:00
|
|
|
NodeMeta: tc.filters,
|
|
|
|
},
|
|
|
|
DNS: structs.QueryDNSOptions{
|
|
|
|
TTL: "10s",
|
|
|
|
},
|
|
|
|
},
|
|
|
|
WriteRequest: structs.WriteRequest{Token: "root"},
|
|
|
|
}
|
|
|
|
if err := msgpackrpc.CallWithCodec(codec1, "PreparedQuery.Apply", &nodeMetaQuery, &nodeMetaQuery.Query.ID); err != nil {
|
|
|
|
t.Fatalf("err: %v", err)
|
|
|
|
}
|
|
|
|
|
|
|
|
req := structs.PreparedQueryExecuteRequest{
|
|
|
|
Datacenter: "dc1",
|
|
|
|
QueryIDOrName: nodeMetaQuery.Query.ID,
|
|
|
|
QueryOptions: structs.QueryOptions{Token: execToken},
|
|
|
|
}
|
|
|
|
|
|
|
|
var reply structs.PreparedQueryExecuteResponse
|
|
|
|
if err := msgpackrpc.CallWithCodec(codec1, "PreparedQuery.Execute", &req, &reply); err != nil {
|
|
|
|
t.Fatalf("err: %v", err)
|
|
|
|
}
|
|
|
|
|
|
|
|
if len(reply.Nodes) != tc.numNodes {
|
|
|
|
t.Fatalf("bad: %v, %v", len(reply.Nodes), tc.numNodes)
|
|
|
|
}
|
|
|
|
|
|
|
|
for _, node := range reply.Nodes {
|
|
|
|
if !structs.SatisfiesMetaFilters(node.Node.Meta, tc.filters) {
|
|
|
|
t.Fatalf("bad: %v", node.Node.Meta)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2015-11-11 01:42:52 +00:00
|
|
|
// Push a coordinate for one of the nodes so we can try an RTT sort. We
|
|
|
|
// have to sleep a little while for the coordinate batch to get flushed.
|
|
|
|
{
|
|
|
|
req := structs.CoordinateUpdateRequest{
|
|
|
|
Datacenter: "dc1",
|
|
|
|
Node: "node3",
|
|
|
|
Coord: coordinate.NewCoordinate(coordinate.DefaultConfig()),
|
|
|
|
}
|
|
|
|
var out struct{}
|
|
|
|
if err := msgpackrpc.CallWithCodec(codec1, "Coordinate.Update", &req, &out); err != nil {
|
|
|
|
t.Fatalf("err: %v", err)
|
|
|
|
}
|
2016-03-21 23:44:35 +00:00
|
|
|
time.Sleep(3 * s1.config.CoordinateUpdatePeriod)
|
2015-11-11 01:42:52 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
// Try an RTT sort. We don't have any other coordinates in there but
|
|
|
|
// showing that the node with a coordinate is always first proves we
|
|
|
|
// call the RTT sorting function, which is tested elsewhere.
|
|
|
|
for i := 0; i < 100; i++ {
|
|
|
|
req := structs.PreparedQueryExecuteRequest{
|
|
|
|
Datacenter: "dc1",
|
|
|
|
QueryIDOrName: query.Query.ID,
|
|
|
|
Source: structs.QuerySource{
|
2016-06-30 23:51:18 +00:00
|
|
|
Datacenter: "dc1",
|
|
|
|
Node: "node3",
|
2015-11-11 01:42:52 +00:00
|
|
|
},
|
Creates new "prepared-query" ACL type and new token capture behavior.
Prior to this change, prepared queries had the following behavior for
ACLs, which will need to change to support templates:
1. A management token, or a token with read access to the service being
queried needed to be provided in order to create a prepared query.
2. The token used to create the prepared query was stored with the query
in the state store and used to execute the query.
3. A management token, or the token used to create the query needed to be
supplied to perform and CRUD operations on an existing prepared query.
This was pretty subtle and complicated behavior, and won't work for
templates since the service name is computed at execution time. To solve
this, we introduce a new "prepared-query" ACL type, where the prefix
applies to the query name for static prepared query types and to the
prefix for template prepared query types.
With this change, the new behavior is:
1. A management token, or a token with "prepared-query" write access to
the query name or (soon) the given template prefix is required to do
any CRUD operations on a prepared query, or to list prepared queries
(the list is filtered by this ACL).
2. You will no longer need a management token to list prepared queries,
but you will only be able to see prepared queries that you have access
to (you get an empty list instead of permission denied).
3. When listing or getting a query, because it was easy to capture
management tokens given the past behavior, this will always blank out
the "Token" field (replacing the contents as <hidden>) for all tokens
unless a management token is supplied. Going forward, we should
discourage people from binding tokens for execution unless strictly
necessary.
4. No token will be captured by default when a prepared query is created.
If the user wishes to supply an execution token then can pass it in via
the "Token" field in the prepared query definition. Otherwise, this
field will default to empty.
5. At execution time, we will use the captured token if it exists with the
prepared query definition, otherwise we will use the token that's passed
in with the request, just like we do for other RPCs (or you can use the
agent's configured token for DNS).
6. Prepared queries with no name (accessible only by ID) will not require
ACLs to create or modify (execution time will depend on the service ACL
configuration). Our argument here is that these are designed to be
ephemeral and the IDs are as good as an ACL. Management tokens will be
able to list all of these.
These changes enable templates, but also enable delegation of authority to
manage the prepared query namespace.
2016-02-23 08:12:58 +00:00
|
|
|
QueryOptions: structs.QueryOptions{Token: execToken},
|
2015-11-11 01:42:52 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
var reply structs.PreparedQueryExecuteResponse
|
|
|
|
if err := msgpackrpc.CallWithCodec(codec1, "PreparedQuery.Execute", &req, &reply); err != nil {
|
|
|
|
t.Fatalf("err: %v", err)
|
|
|
|
}
|
|
|
|
|
|
|
|
if len(reply.Nodes) != 10 ||
|
|
|
|
reply.Datacenter != "dc1" || reply.Failovers != 0 ||
|
2015-11-13 18:38:44 +00:00
|
|
|
reply.Service != query.Query.Service.Service ||
|
2015-11-12 06:34:46 +00:00
|
|
|
!reflect.DeepEqual(reply.DNS, query.Query.DNS) ||
|
|
|
|
!reply.QueryMeta.KnownLeader {
|
2015-11-11 01:42:52 +00:00
|
|
|
t.Fatalf("bad: %v", reply)
|
|
|
|
}
|
|
|
|
if reply.Nodes[0].Node.Node != "node3" {
|
|
|
|
t.Fatalf("bad: %v", reply)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
// Make sure the shuffle looks like it's working.
|
|
|
|
uniques := make(map[string]struct{})
|
|
|
|
for i := 0; i < 100; i++ {
|
|
|
|
req := structs.PreparedQueryExecuteRequest{
|
|
|
|
Datacenter: "dc1",
|
|
|
|
QueryIDOrName: query.Query.ID,
|
Creates new "prepared-query" ACL type and new token capture behavior.
Prior to this change, prepared queries had the following behavior for
ACLs, which will need to change to support templates:
1. A management token, or a token with read access to the service being
queried needed to be provided in order to create a prepared query.
2. The token used to create the prepared query was stored with the query
in the state store and used to execute the query.
3. A management token, or the token used to create the query needed to be
supplied to perform and CRUD operations on an existing prepared query.
This was pretty subtle and complicated behavior, and won't work for
templates since the service name is computed at execution time. To solve
this, we introduce a new "prepared-query" ACL type, where the prefix
applies to the query name for static prepared query types and to the
prefix for template prepared query types.
With this change, the new behavior is:
1. A management token, or a token with "prepared-query" write access to
the query name or (soon) the given template prefix is required to do
any CRUD operations on a prepared query, or to list prepared queries
(the list is filtered by this ACL).
2. You will no longer need a management token to list prepared queries,
but you will only be able to see prepared queries that you have access
to (you get an empty list instead of permission denied).
3. When listing or getting a query, because it was easy to capture
management tokens given the past behavior, this will always blank out
the "Token" field (replacing the contents as <hidden>) for all tokens
unless a management token is supplied. Going forward, we should
discourage people from binding tokens for execution unless strictly
necessary.
4. No token will be captured by default when a prepared query is created.
If the user wishes to supply an execution token then can pass it in via
the "Token" field in the prepared query definition. Otherwise, this
field will default to empty.
5. At execution time, we will use the captured token if it exists with the
prepared query definition, otherwise we will use the token that's passed
in with the request, just like we do for other RPCs (or you can use the
agent's configured token for DNS).
6. Prepared queries with no name (accessible only by ID) will not require
ACLs to create or modify (execution time will depend on the service ACL
configuration). Our argument here is that these are designed to be
ephemeral and the IDs are as good as an ACL. Management tokens will be
able to list all of these.
These changes enable templates, but also enable delegation of authority to
manage the prepared query namespace.
2016-02-23 08:12:58 +00:00
|
|
|
QueryOptions: structs.QueryOptions{Token: execToken},
|
2015-11-11 01:42:52 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
var reply structs.PreparedQueryExecuteResponse
|
|
|
|
if err := msgpackrpc.CallWithCodec(codec1, "PreparedQuery.Execute", &req, &reply); err != nil {
|
|
|
|
t.Fatalf("err: %v", err)
|
|
|
|
}
|
|
|
|
|
|
|
|
if len(reply.Nodes) != 10 ||
|
|
|
|
reply.Datacenter != "dc1" || reply.Failovers != 0 ||
|
2015-11-13 18:38:44 +00:00
|
|
|
reply.Service != query.Query.Service.Service ||
|
2015-11-12 06:34:46 +00:00
|
|
|
!reflect.DeepEqual(reply.DNS, query.Query.DNS) ||
|
|
|
|
!reply.QueryMeta.KnownLeader {
|
2015-11-11 01:42:52 +00:00
|
|
|
t.Fatalf("bad: %v", reply)
|
|
|
|
}
|
|
|
|
var names []string
|
|
|
|
for _, node := range reply.Nodes {
|
|
|
|
names = append(names, node.Node.Node)
|
|
|
|
}
|
|
|
|
key := strings.Join(names, "|")
|
|
|
|
uniques[key] = struct{}{}
|
|
|
|
}
|
|
|
|
|
|
|
|
// We have to allow for the fact that there won't always be a unique
|
|
|
|
// shuffle each pass, so we just look for smell here without the test
|
|
|
|
// being flaky.
|
|
|
|
if len(uniques) < 50 {
|
|
|
|
t.Fatalf("unique shuffle ratio too low: %d/100", len(uniques))
|
|
|
|
}
|
|
|
|
|
2016-06-30 19:11:20 +00:00
|
|
|
// Set the query to return results nearest to node3. This is the only
|
|
|
|
// node with coordinates, and it carries the service we are asking for,
|
|
|
|
// so node3 should always show up first.
|
2016-06-20 21:53:13 +00:00
|
|
|
query.Op = structs.PreparedQueryUpdate
|
2016-06-30 19:11:20 +00:00
|
|
|
query.Query.Service.Near = "node3"
|
2016-06-20 21:53:13 +00:00
|
|
|
if err := msgpackrpc.CallWithCodec(codec1, "PreparedQuery.Apply", &query, &query.Query.ID); err != nil {
|
|
|
|
t.Fatalf("err: %v", err)
|
|
|
|
}
|
|
|
|
|
2016-06-30 23:51:18 +00:00
|
|
|
// Now run the query and make sure the sort looks right.
|
2016-06-20 21:53:13 +00:00
|
|
|
{
|
|
|
|
req := structs.PreparedQueryExecuteRequest{
|
2016-06-30 23:51:18 +00:00
|
|
|
Agent: structs.QuerySource{
|
|
|
|
Datacenter: "dc1",
|
|
|
|
Node: "node3",
|
2016-06-21 22:34:26 +00:00
|
|
|
},
|
2016-06-20 21:53:13 +00:00
|
|
|
Datacenter: "dc1",
|
|
|
|
QueryIDOrName: query.Query.ID,
|
|
|
|
QueryOptions: structs.QueryOptions{Token: execToken},
|
|
|
|
}
|
|
|
|
|
|
|
|
for i := 0; i < 10; i++ {
|
2017-01-18 23:40:19 +00:00
|
|
|
var reply structs.PreparedQueryExecuteResponse
|
2016-06-20 21:53:13 +00:00
|
|
|
if err := msgpackrpc.CallWithCodec(codec1, "PreparedQuery.Execute", &req, &reply); err != nil {
|
|
|
|
t.Fatalf("err: %v", err)
|
|
|
|
}
|
|
|
|
if n := len(reply.Nodes); n != 10 {
|
|
|
|
t.Fatalf("expect 10 nodes, got: %d", n)
|
|
|
|
}
|
2016-06-30 19:11:20 +00:00
|
|
|
if node := reply.Nodes[0].Node.Node; node != "node3" {
|
|
|
|
t.Fatalf("expect node3 first, got: %q", node)
|
2016-06-20 21:53:13 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2016-06-30 23:51:18 +00:00
|
|
|
// Query again, but this time set a client-supplied query source. This
|
|
|
|
// proves that we allow overriding the baked-in value with ?near.
|
2016-06-21 19:39:40 +00:00
|
|
|
{
|
2016-06-30 19:11:20 +00:00
|
|
|
// Set up the query with a non-existent node. This will cause the
|
|
|
|
// nodes to be shuffled if the passed node is respected, proving
|
|
|
|
// that we allow the override to happen.
|
2016-06-21 19:39:40 +00:00
|
|
|
req := structs.PreparedQueryExecuteRequest{
|
2016-06-30 19:11:20 +00:00
|
|
|
Source: structs.QuerySource{
|
2016-06-30 23:51:18 +00:00
|
|
|
Datacenter: "dc1",
|
|
|
|
Node: "foo",
|
|
|
|
},
|
|
|
|
Agent: structs.QuerySource{
|
|
|
|
Datacenter: "dc1",
|
|
|
|
Node: "node3",
|
2016-06-21 22:34:26 +00:00
|
|
|
},
|
2016-06-21 19:39:40 +00:00
|
|
|
Datacenter: "dc1",
|
|
|
|
QueryIDOrName: query.Query.ID,
|
|
|
|
QueryOptions: structs.QueryOptions{Token: execToken},
|
|
|
|
}
|
|
|
|
|
2016-06-30 19:11:20 +00:00
|
|
|
shuffled := false
|
|
|
|
for i := 0; i < 10; i++ {
|
2017-01-24 02:11:13 +00:00
|
|
|
var reply structs.PreparedQueryExecuteResponse
|
2016-06-30 19:11:20 +00:00
|
|
|
if err := msgpackrpc.CallWithCodec(codec1, "PreparedQuery.Execute", &req, &reply); err != nil {
|
|
|
|
t.Fatalf("err: %v", err)
|
|
|
|
}
|
|
|
|
if n := len(reply.Nodes); n != 10 {
|
|
|
|
t.Fatalf("expect 10 nodes, got: %d", n)
|
|
|
|
}
|
|
|
|
if node := reply.Nodes[0].Node.Node; node != "node3" {
|
|
|
|
shuffled = true
|
|
|
|
break
|
|
|
|
}
|
2016-06-21 19:39:40 +00:00
|
|
|
}
|
2016-06-30 19:11:20 +00:00
|
|
|
|
|
|
|
if !shuffled {
|
|
|
|
t.Fatalf("expect nodes to be shuffled")
|
2016-06-21 19:39:40 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2016-07-01 21:28:58 +00:00
|
|
|
// If the exact node we are sorting near appears in the list, make sure it
|
|
|
|
// gets popped to the front of the result.
|
|
|
|
{
|
|
|
|
req := structs.PreparedQueryExecuteRequest{
|
|
|
|
Source: structs.QuerySource{
|
|
|
|
Datacenter: "dc1",
|
|
|
|
Node: "node1",
|
|
|
|
},
|
|
|
|
Datacenter: "dc1",
|
|
|
|
QueryIDOrName: query.Query.ID,
|
|
|
|
QueryOptions: structs.QueryOptions{Token: execToken},
|
|
|
|
}
|
|
|
|
|
|
|
|
for i := 0; i < 10; i++ {
|
2017-01-18 23:40:19 +00:00
|
|
|
var reply structs.PreparedQueryExecuteResponse
|
2016-07-01 21:28:58 +00:00
|
|
|
if err := msgpackrpc.CallWithCodec(codec1, "PreparedQuery.Execute", &req, &reply); err != nil {
|
|
|
|
t.Fatalf("err: %v", err)
|
|
|
|
}
|
|
|
|
if n := len(reply.Nodes); n != 10 {
|
|
|
|
t.Fatalf("expect 10 nodes, got: %d", n)
|
|
|
|
}
|
|
|
|
if node := reply.Nodes[0].Node.Node; node != "node1" {
|
|
|
|
t.Fatalf("expect node1 first, got: %q", node)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2016-06-30 23:51:18 +00:00
|
|
|
// Bake the magic "_agent" flag into the query.
|
|
|
|
query.Query.Service.Near = "_agent"
|
|
|
|
if err := msgpackrpc.CallWithCodec(codec1, "PreparedQuery.Apply", &query, &query.Query.ID); err != nil {
|
|
|
|
t.Fatalf("err: %v", err)
|
|
|
|
}
|
|
|
|
|
|
|
|
// Check that we sort the local agent first when the magic flag is set.
|
2016-06-21 22:34:26 +00:00
|
|
|
{
|
|
|
|
req := structs.PreparedQueryExecuteRequest{
|
2016-06-30 23:51:18 +00:00
|
|
|
Agent: structs.QuerySource{
|
|
|
|
Datacenter: "dc1",
|
|
|
|
Node: "node3",
|
2016-06-21 22:34:26 +00:00
|
|
|
},
|
|
|
|
Datacenter: "dc1",
|
|
|
|
QueryIDOrName: query.Query.ID,
|
|
|
|
QueryOptions: structs.QueryOptions{Token: execToken},
|
|
|
|
}
|
|
|
|
|
|
|
|
for i := 0; i < 10; i++ {
|
2017-01-18 23:40:19 +00:00
|
|
|
var reply structs.PreparedQueryExecuteResponse
|
2016-06-21 22:34:26 +00:00
|
|
|
if err := msgpackrpc.CallWithCodec(codec1, "PreparedQuery.Execute", &req, &reply); err != nil {
|
|
|
|
t.Fatalf("err: %v", err)
|
|
|
|
}
|
|
|
|
if n := len(reply.Nodes); n != 10 {
|
|
|
|
t.Fatalf("expect 10 nodes, got: %d", n)
|
|
|
|
}
|
2016-06-30 19:11:20 +00:00
|
|
|
if node := reply.Nodes[0].Node.Node; node != "node3" {
|
2016-06-30 23:51:18 +00:00
|
|
|
t.Fatalf("expect node3 first, got: %q", node)
|
2016-06-21 22:34:26 +00:00
|
|
|
}
|
|
|
|
}
|
2016-06-21 19:54:18 +00:00
|
|
|
}
|
|
|
|
|
2016-06-30 23:51:18 +00:00
|
|
|
// Check that the query isn't just sorting "node3" first because we
|
|
|
|
// provided it in the Agent query source. Proves that we use the
|
|
|
|
// Agent source when the magic "_agent" flag is passed.
|
2016-06-21 19:54:18 +00:00
|
|
|
{
|
|
|
|
req := structs.PreparedQueryExecuteRequest{
|
2016-06-30 23:51:18 +00:00
|
|
|
Agent: structs.QuerySource{
|
2016-06-21 22:34:26 +00:00
|
|
|
Datacenter: "dc1",
|
2016-06-30 23:51:18 +00:00
|
|
|
Node: "foo",
|
2016-06-21 22:34:26 +00:00
|
|
|
},
|
2016-06-21 19:54:18 +00:00
|
|
|
Datacenter: "dc1",
|
|
|
|
QueryIDOrName: query.Query.ID,
|
|
|
|
QueryOptions: structs.QueryOptions{Token: execToken},
|
|
|
|
}
|
|
|
|
|
2016-06-30 23:51:18 +00:00
|
|
|
// Expect the set to be shuffled since we have no coordinates
|
|
|
|
// on the "foo" node.
|
|
|
|
shuffled := false
|
2016-06-21 19:54:18 +00:00
|
|
|
for i := 0; i < 10; i++ {
|
2017-01-18 23:40:19 +00:00
|
|
|
var reply structs.PreparedQueryExecuteResponse
|
2016-06-21 19:54:18 +00:00
|
|
|
if err := msgpackrpc.CallWithCodec(codec1, "PreparedQuery.Execute", &req, &reply); err != nil {
|
|
|
|
t.Fatalf("err: %v", err)
|
|
|
|
}
|
|
|
|
if n := len(reply.Nodes); n != 10 {
|
|
|
|
t.Fatalf("expect 10 nodes, got: %d", n)
|
|
|
|
}
|
|
|
|
if node := reply.Nodes[0].Node.Node; node != "node3" {
|
2016-06-30 23:51:18 +00:00
|
|
|
shuffled = true
|
|
|
|
break
|
2016-06-30 19:11:20 +00:00
|
|
|
}
|
|
|
|
}
|
2016-06-30 23:51:18 +00:00
|
|
|
|
|
|
|
if !shuffled {
|
|
|
|
t.Fatal("expect nodes to be shuffled")
|
|
|
|
}
|
2016-06-30 19:11:20 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
// Shuffles if the response comes from a non-local DC. Proves that the
|
2016-06-30 23:51:18 +00:00
|
|
|
// agent query source does not interfere with the order.
|
2016-06-30 19:11:20 +00:00
|
|
|
{
|
|
|
|
req := structs.PreparedQueryExecuteRequest{
|
|
|
|
Source: structs.QuerySource{
|
|
|
|
Datacenter: "dc2",
|
|
|
|
Node: "node3",
|
|
|
|
},
|
2016-06-30 23:51:18 +00:00
|
|
|
Agent: structs.QuerySource{
|
|
|
|
Datacenter: "dc1",
|
|
|
|
Node: "node3",
|
|
|
|
},
|
2016-06-30 19:11:20 +00:00
|
|
|
Datacenter: "dc1",
|
|
|
|
QueryIDOrName: query.Query.ID,
|
|
|
|
QueryOptions: structs.QueryOptions{Token: execToken},
|
|
|
|
}
|
|
|
|
|
|
|
|
shuffled := false
|
|
|
|
for i := 0; i < 10; i++ {
|
2017-01-18 23:40:19 +00:00
|
|
|
var reply structs.PreparedQueryExecuteResponse
|
2016-06-30 19:11:20 +00:00
|
|
|
if err := msgpackrpc.CallWithCodec(codec1, "PreparedQuery.Execute", &req, &reply); err != nil {
|
|
|
|
t.Fatalf("err: %v", err)
|
|
|
|
}
|
|
|
|
if n := len(reply.Nodes); n != 10 {
|
|
|
|
t.Fatalf("expect 10 nodes, got: %d", n)
|
2016-06-21 19:54:18 +00:00
|
|
|
}
|
2016-06-30 19:11:20 +00:00
|
|
|
if reply.Nodes[0].Node.Node != "node3" {
|
|
|
|
shuffled = true
|
|
|
|
break
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
if !shuffled {
|
|
|
|
t.Fatal("expect node shuffle for remote results")
|
2016-06-21 19:54:18 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2016-06-30 19:11:20 +00:00
|
|
|
// Un-bake the near parameter.
|
2016-06-21 19:39:40 +00:00
|
|
|
query.Query.Service.Near = ""
|
2016-06-20 21:53:13 +00:00
|
|
|
if err := msgpackrpc.CallWithCodec(codec1, "PreparedQuery.Apply", &query, &query.Query.ID); err != nil {
|
2016-06-30 19:11:20 +00:00
|
|
|
t.Fatalf("err: %v", err)
|
2016-06-20 21:53:13 +00:00
|
|
|
}
|
|
|
|
|
2015-11-11 01:42:52 +00:00
|
|
|
// Update the health of a node to mark it critical.
|
|
|
|
setHealth := func(node string, health string) {
|
|
|
|
req := structs.RegisterRequest{
|
|
|
|
Datacenter: "dc1",
|
|
|
|
Node: node,
|
|
|
|
Address: "127.0.0.1",
|
|
|
|
Service: &structs.NodeService{
|
|
|
|
Service: "foo",
|
|
|
|
Port: 8000,
|
|
|
|
Tags: []string{"dc1", "tag1"},
|
|
|
|
},
|
|
|
|
Check: &structs.HealthCheck{
|
|
|
|
Name: "failing",
|
|
|
|
Status: health,
|
|
|
|
ServiceID: "foo",
|
|
|
|
},
|
|
|
|
WriteRequest: structs.WriteRequest{Token: "root"},
|
|
|
|
}
|
|
|
|
var reply struct{}
|
|
|
|
if err := msgpackrpc.CallWithCodec(codec1, "Catalog.Register", &req, &reply); err != nil {
|
|
|
|
t.Fatalf("err: %v", err)
|
|
|
|
}
|
|
|
|
}
|
2017-04-19 23:00:11 +00:00
|
|
|
setHealth("node1", api.HealthCritical)
|
2015-11-11 01:42:52 +00:00
|
|
|
|
|
|
|
// The failing node should be filtered.
|
|
|
|
{
|
|
|
|
req := structs.PreparedQueryExecuteRequest{
|
|
|
|
Datacenter: "dc1",
|
|
|
|
QueryIDOrName: query.Query.ID,
|
Creates new "prepared-query" ACL type and new token capture behavior.
Prior to this change, prepared queries had the following behavior for
ACLs, which will need to change to support templates:
1. A management token, or a token with read access to the service being
queried needed to be provided in order to create a prepared query.
2. The token used to create the prepared query was stored with the query
in the state store and used to execute the query.
3. A management token, or the token used to create the query needed to be
supplied to perform and CRUD operations on an existing prepared query.
This was pretty subtle and complicated behavior, and won't work for
templates since the service name is computed at execution time. To solve
this, we introduce a new "prepared-query" ACL type, where the prefix
applies to the query name for static prepared query types and to the
prefix for template prepared query types.
With this change, the new behavior is:
1. A management token, or a token with "prepared-query" write access to
the query name or (soon) the given template prefix is required to do
any CRUD operations on a prepared query, or to list prepared queries
(the list is filtered by this ACL).
2. You will no longer need a management token to list prepared queries,
but you will only be able to see prepared queries that you have access
to (you get an empty list instead of permission denied).
3. When listing or getting a query, because it was easy to capture
management tokens given the past behavior, this will always blank out
the "Token" field (replacing the contents as <hidden>) for all tokens
unless a management token is supplied. Going forward, we should
discourage people from binding tokens for execution unless strictly
necessary.
4. No token will be captured by default when a prepared query is created.
If the user wishes to supply an execution token then can pass it in via
the "Token" field in the prepared query definition. Otherwise, this
field will default to empty.
5. At execution time, we will use the captured token if it exists with the
prepared query definition, otherwise we will use the token that's passed
in with the request, just like we do for other RPCs (or you can use the
agent's configured token for DNS).
6. Prepared queries with no name (accessible only by ID) will not require
ACLs to create or modify (execution time will depend on the service ACL
configuration). Our argument here is that these are designed to be
ephemeral and the IDs are as good as an ACL. Management tokens will be
able to list all of these.
These changes enable templates, but also enable delegation of authority to
manage the prepared query namespace.
2016-02-23 08:12:58 +00:00
|
|
|
QueryOptions: structs.QueryOptions{Token: execToken},
|
2015-11-11 01:42:52 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
var reply structs.PreparedQueryExecuteResponse
|
|
|
|
if err := msgpackrpc.CallWithCodec(codec1, "PreparedQuery.Execute", &req, &reply); err != nil {
|
|
|
|
t.Fatalf("err: %v", err)
|
|
|
|
}
|
|
|
|
|
|
|
|
if len(reply.Nodes) != 9 ||
|
|
|
|
reply.Datacenter != "dc1" || reply.Failovers != 0 ||
|
2015-11-13 18:38:44 +00:00
|
|
|
reply.Service != query.Query.Service.Service ||
|
2015-11-12 06:34:46 +00:00
|
|
|
!reflect.DeepEqual(reply.DNS, query.Query.DNS) ||
|
|
|
|
!reply.QueryMeta.KnownLeader {
|
2015-11-11 01:42:52 +00:00
|
|
|
t.Fatalf("bad: %v", reply)
|
|
|
|
}
|
|
|
|
for _, node := range reply.Nodes {
|
|
|
|
if node.Node.Node == "node1" {
|
|
|
|
t.Fatalf("bad: %v", node)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
// Upgrade it to a warning and re-query, should be 10 nodes again.
|
2017-04-19 23:00:11 +00:00
|
|
|
setHealth("node1", api.HealthWarning)
|
2015-11-11 01:42:52 +00:00
|
|
|
{
|
|
|
|
req := structs.PreparedQueryExecuteRequest{
|
|
|
|
Datacenter: "dc1",
|
|
|
|
QueryIDOrName: query.Query.ID,
|
Creates new "prepared-query" ACL type and new token capture behavior.
Prior to this change, prepared queries had the following behavior for
ACLs, which will need to change to support templates:
1. A management token, or a token with read access to the service being
queried needed to be provided in order to create a prepared query.
2. The token used to create the prepared query was stored with the query
in the state store and used to execute the query.
3. A management token, or the token used to create the query needed to be
supplied to perform and CRUD operations on an existing prepared query.
This was pretty subtle and complicated behavior, and won't work for
templates since the service name is computed at execution time. To solve
this, we introduce a new "prepared-query" ACL type, where the prefix
applies to the query name for static prepared query types and to the
prefix for template prepared query types.
With this change, the new behavior is:
1. A management token, or a token with "prepared-query" write access to
the query name or (soon) the given template prefix is required to do
any CRUD operations on a prepared query, or to list prepared queries
(the list is filtered by this ACL).
2. You will no longer need a management token to list prepared queries,
but you will only be able to see prepared queries that you have access
to (you get an empty list instead of permission denied).
3. When listing or getting a query, because it was easy to capture
management tokens given the past behavior, this will always blank out
the "Token" field (replacing the contents as <hidden>) for all tokens
unless a management token is supplied. Going forward, we should
discourage people from binding tokens for execution unless strictly
necessary.
4. No token will be captured by default when a prepared query is created.
If the user wishes to supply an execution token then can pass it in via
the "Token" field in the prepared query definition. Otherwise, this
field will default to empty.
5. At execution time, we will use the captured token if it exists with the
prepared query definition, otherwise we will use the token that's passed
in with the request, just like we do for other RPCs (or you can use the
agent's configured token for DNS).
6. Prepared queries with no name (accessible only by ID) will not require
ACLs to create or modify (execution time will depend on the service ACL
configuration). Our argument here is that these are designed to be
ephemeral and the IDs are as good as an ACL. Management tokens will be
able to list all of these.
These changes enable templates, but also enable delegation of authority to
manage the prepared query namespace.
2016-02-23 08:12:58 +00:00
|
|
|
QueryOptions: structs.QueryOptions{Token: execToken},
|
2015-11-11 01:42:52 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
var reply structs.PreparedQueryExecuteResponse
|
|
|
|
if err := msgpackrpc.CallWithCodec(codec1, "PreparedQuery.Execute", &req, &reply); err != nil {
|
|
|
|
t.Fatalf("err: %v", err)
|
|
|
|
}
|
|
|
|
|
|
|
|
if len(reply.Nodes) != 10 ||
|
|
|
|
reply.Datacenter != "dc1" || reply.Failovers != 0 ||
|
2015-11-13 18:38:44 +00:00
|
|
|
reply.Service != query.Query.Service.Service ||
|
2015-11-12 06:34:46 +00:00
|
|
|
!reflect.DeepEqual(reply.DNS, query.Query.DNS) ||
|
|
|
|
!reply.QueryMeta.KnownLeader {
|
2015-11-11 01:42:52 +00:00
|
|
|
t.Fatalf("bad: %v", reply)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
// Make the query more picky so it excludes warning nodes.
|
|
|
|
query.Query.Service.OnlyPassing = true
|
|
|
|
if err := msgpackrpc.CallWithCodec(codec1, "PreparedQuery.Apply", &query, &query.Query.ID); err != nil {
|
|
|
|
t.Fatalf("err: %v", err)
|
|
|
|
}
|
|
|
|
|
|
|
|
// The node in the warning state should be filtered.
|
|
|
|
{
|
|
|
|
req := structs.PreparedQueryExecuteRequest{
|
|
|
|
Datacenter: "dc1",
|
|
|
|
QueryIDOrName: query.Query.ID,
|
Creates new "prepared-query" ACL type and new token capture behavior.
Prior to this change, prepared queries had the following behavior for
ACLs, which will need to change to support templates:
1. A management token, or a token with read access to the service being
queried needed to be provided in order to create a prepared query.
2. The token used to create the prepared query was stored with the query
in the state store and used to execute the query.
3. A management token, or the token used to create the query needed to be
supplied to perform and CRUD operations on an existing prepared query.
This was pretty subtle and complicated behavior, and won't work for
templates since the service name is computed at execution time. To solve
this, we introduce a new "prepared-query" ACL type, where the prefix
applies to the query name for static prepared query types and to the
prefix for template prepared query types.
With this change, the new behavior is:
1. A management token, or a token with "prepared-query" write access to
the query name or (soon) the given template prefix is required to do
any CRUD operations on a prepared query, or to list prepared queries
(the list is filtered by this ACL).
2. You will no longer need a management token to list prepared queries,
but you will only be able to see prepared queries that you have access
to (you get an empty list instead of permission denied).
3. When listing or getting a query, because it was easy to capture
management tokens given the past behavior, this will always blank out
the "Token" field (replacing the contents as <hidden>) for all tokens
unless a management token is supplied. Going forward, we should
discourage people from binding tokens for execution unless strictly
necessary.
4. No token will be captured by default when a prepared query is created.
If the user wishes to supply an execution token then can pass it in via
the "Token" field in the prepared query definition. Otherwise, this
field will default to empty.
5. At execution time, we will use the captured token if it exists with the
prepared query definition, otherwise we will use the token that's passed
in with the request, just like we do for other RPCs (or you can use the
agent's configured token for DNS).
6. Prepared queries with no name (accessible only by ID) will not require
ACLs to create or modify (execution time will depend on the service ACL
configuration). Our argument here is that these are designed to be
ephemeral and the IDs are as good as an ACL. Management tokens will be
able to list all of these.
These changes enable templates, but also enable delegation of authority to
manage the prepared query namespace.
2016-02-23 08:12:58 +00:00
|
|
|
QueryOptions: structs.QueryOptions{Token: execToken},
|
2015-11-11 01:42:52 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
var reply structs.PreparedQueryExecuteResponse
|
|
|
|
if err := msgpackrpc.CallWithCodec(codec1, "PreparedQuery.Execute", &req, &reply); err != nil {
|
|
|
|
t.Fatalf("err: %v", err)
|
|
|
|
}
|
|
|
|
|
|
|
|
if len(reply.Nodes) != 9 ||
|
|
|
|
reply.Datacenter != "dc1" || reply.Failovers != 0 ||
|
2015-11-13 18:38:44 +00:00
|
|
|
reply.Service != query.Query.Service.Service ||
|
2015-11-12 06:34:46 +00:00
|
|
|
!reflect.DeepEqual(reply.DNS, query.Query.DNS) ||
|
|
|
|
!reply.QueryMeta.KnownLeader {
|
2015-11-11 01:42:52 +00:00
|
|
|
t.Fatalf("bad: %v", reply)
|
|
|
|
}
|
|
|
|
for _, node := range reply.Nodes {
|
|
|
|
if node.Node.Node == "node1" {
|
|
|
|
t.Fatalf("bad: %v", node)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2018-04-10 12:28:27 +00:00
|
|
|
// Make the query ignore all our health checks (which have "failing" ID
|
|
|
|
// implicitly from their name).
|
|
|
|
query.Query.Service.IgnoreCheckIDs = []types.CheckID{"failing"}
|
|
|
|
if err := msgpackrpc.CallWithCodec(codec1, "PreparedQuery.Apply", &query, &query.Query.ID); err != nil {
|
|
|
|
t.Fatalf("err: %v", err)
|
|
|
|
}
|
|
|
|
|
|
|
|
// We should end up with 10 nodes again
|
|
|
|
{
|
|
|
|
req := structs.PreparedQueryExecuteRequest{
|
|
|
|
Datacenter: "dc1",
|
|
|
|
QueryIDOrName: query.Query.ID,
|
|
|
|
QueryOptions: structs.QueryOptions{Token: execToken},
|
|
|
|
}
|
|
|
|
|
|
|
|
var reply structs.PreparedQueryExecuteResponse
|
|
|
|
if err := msgpackrpc.CallWithCodec(codec1, "PreparedQuery.Execute", &req, &reply); err != nil {
|
|
|
|
t.Fatalf("err: %v", err)
|
|
|
|
}
|
|
|
|
|
|
|
|
if len(reply.Nodes) != 10 ||
|
|
|
|
reply.Datacenter != "dc1" ||
|
|
|
|
reply.Service != query.Query.Service.Service ||
|
|
|
|
!reflect.DeepEqual(reply.DNS, query.Query.DNS) ||
|
|
|
|
!reply.QueryMeta.KnownLeader {
|
|
|
|
t.Fatalf("bad: %v", reply)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
// Undo that so all the following tests aren't broken!
|
|
|
|
query.Query.Service.IgnoreCheckIDs = nil
|
|
|
|
if err := msgpackrpc.CallWithCodec(codec1, "PreparedQuery.Apply", &query, &query.Query.ID); err != nil {
|
|
|
|
t.Fatalf("err: %v", err)
|
|
|
|
}
|
|
|
|
|
2015-11-11 01:42:52 +00:00
|
|
|
// Make the query more picky by adding a tag filter. This just proves we
|
|
|
|
// call into the tag filter, it is tested more thoroughly in a separate
|
|
|
|
// test.
|
|
|
|
query.Query.Service.Tags = []string{"!tag3"}
|
|
|
|
if err := msgpackrpc.CallWithCodec(codec1, "PreparedQuery.Apply", &query, &query.Query.ID); err != nil {
|
|
|
|
t.Fatalf("err: %v", err)
|
|
|
|
}
|
|
|
|
|
|
|
|
// The node in the warning state should be filtered as well as the node
|
|
|
|
// with the filtered tag.
|
|
|
|
{
|
|
|
|
req := structs.PreparedQueryExecuteRequest{
|
|
|
|
Datacenter: "dc1",
|
|
|
|
QueryIDOrName: query.Query.ID,
|
Creates new "prepared-query" ACL type and new token capture behavior.
Prior to this change, prepared queries had the following behavior for
ACLs, which will need to change to support templates:
1. A management token, or a token with read access to the service being
queried needed to be provided in order to create a prepared query.
2. The token used to create the prepared query was stored with the query
in the state store and used to execute the query.
3. A management token, or the token used to create the query needed to be
supplied to perform and CRUD operations on an existing prepared query.
This was pretty subtle and complicated behavior, and won't work for
templates since the service name is computed at execution time. To solve
this, we introduce a new "prepared-query" ACL type, where the prefix
applies to the query name for static prepared query types and to the
prefix for template prepared query types.
With this change, the new behavior is:
1. A management token, or a token with "prepared-query" write access to
the query name or (soon) the given template prefix is required to do
any CRUD operations on a prepared query, or to list prepared queries
(the list is filtered by this ACL).
2. You will no longer need a management token to list prepared queries,
but you will only be able to see prepared queries that you have access
to (you get an empty list instead of permission denied).
3. When listing or getting a query, because it was easy to capture
management tokens given the past behavior, this will always blank out
the "Token" field (replacing the contents as <hidden>) for all tokens
unless a management token is supplied. Going forward, we should
discourage people from binding tokens for execution unless strictly
necessary.
4. No token will be captured by default when a prepared query is created.
If the user wishes to supply an execution token then can pass it in via
the "Token" field in the prepared query definition. Otherwise, this
field will default to empty.
5. At execution time, we will use the captured token if it exists with the
prepared query definition, otherwise we will use the token that's passed
in with the request, just like we do for other RPCs (or you can use the
agent's configured token for DNS).
6. Prepared queries with no name (accessible only by ID) will not require
ACLs to create or modify (execution time will depend on the service ACL
configuration). Our argument here is that these are designed to be
ephemeral and the IDs are as good as an ACL. Management tokens will be
able to list all of these.
These changes enable templates, but also enable delegation of authority to
manage the prepared query namespace.
2016-02-23 08:12:58 +00:00
|
|
|
QueryOptions: structs.QueryOptions{Token: execToken},
|
|
|
|
}
|
|
|
|
|
|
|
|
var reply structs.PreparedQueryExecuteResponse
|
|
|
|
if err := msgpackrpc.CallWithCodec(codec1, "PreparedQuery.Execute", &req, &reply); err != nil {
|
|
|
|
t.Fatalf("err: %v", err)
|
|
|
|
}
|
|
|
|
|
|
|
|
if len(reply.Nodes) != 8 ||
|
|
|
|
reply.Datacenter != "dc1" || reply.Failovers != 0 ||
|
|
|
|
reply.Service != query.Query.Service.Service ||
|
|
|
|
!reflect.DeepEqual(reply.DNS, query.Query.DNS) ||
|
|
|
|
!reply.QueryMeta.KnownLeader {
|
|
|
|
t.Fatalf("bad: %v", reply)
|
|
|
|
}
|
|
|
|
for _, node := range reply.Nodes {
|
|
|
|
if node.Node.Node == "node1" || node.Node.Node == "node3" {
|
|
|
|
t.Fatalf("bad: %v", node)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
// Make a new exec token that can't read the service.
|
|
|
|
var denyToken string
|
|
|
|
{
|
|
|
|
var rules = `
|
|
|
|
service "foo" {
|
|
|
|
policy = "deny"
|
|
|
|
}
|
|
|
|
`
|
|
|
|
|
|
|
|
req := structs.ACLRequest{
|
|
|
|
Datacenter: "dc1",
|
|
|
|
Op: structs.ACLSet,
|
|
|
|
ACL: structs.ACL{
|
|
|
|
Name: "User token",
|
|
|
|
Type: structs.ACLTypeClient,
|
|
|
|
Rules: rules,
|
|
|
|
},
|
|
|
|
WriteRequest: structs.WriteRequest{Token: "root"},
|
|
|
|
}
|
|
|
|
if err := msgpackrpc.CallWithCodec(codec1, "ACL.Apply", &req, &denyToken); err != nil {
|
|
|
|
t.Fatalf("err: %v", err)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
// Make sure the query gets denied with this token.
|
|
|
|
{
|
|
|
|
req := structs.PreparedQueryExecuteRequest{
|
|
|
|
Datacenter: "dc1",
|
|
|
|
QueryIDOrName: query.Query.ID,
|
|
|
|
QueryOptions: structs.QueryOptions{Token: denyToken},
|
|
|
|
}
|
|
|
|
|
|
|
|
var reply structs.PreparedQueryExecuteResponse
|
|
|
|
if err := msgpackrpc.CallWithCodec(codec1, "PreparedQuery.Execute", &req, &reply); err != nil {
|
|
|
|
t.Fatalf("err: %v", err)
|
|
|
|
}
|
|
|
|
|
|
|
|
if len(reply.Nodes) != 0 ||
|
|
|
|
reply.Datacenter != "dc1" || reply.Failovers != 0 ||
|
|
|
|
reply.Service != query.Query.Service.Service ||
|
|
|
|
!reflect.DeepEqual(reply.DNS, query.Query.DNS) ||
|
|
|
|
!reply.QueryMeta.KnownLeader {
|
|
|
|
t.Fatalf("bad: %v", reply)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
// Bake the exec token into the query.
|
|
|
|
query.Query.Token = execToken
|
|
|
|
if err := msgpackrpc.CallWithCodec(codec1, "PreparedQuery.Apply", &query, &query.Query.ID); err != nil {
|
|
|
|
t.Fatalf("err: %v", err)
|
|
|
|
}
|
|
|
|
|
|
|
|
// Now even querying with the deny token should work.
|
|
|
|
{
|
|
|
|
req := structs.PreparedQueryExecuteRequest{
|
|
|
|
Datacenter: "dc1",
|
|
|
|
QueryIDOrName: query.Query.ID,
|
|
|
|
QueryOptions: structs.QueryOptions{Token: denyToken},
|
2015-11-11 01:42:52 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
var reply structs.PreparedQueryExecuteResponse
|
|
|
|
if err := msgpackrpc.CallWithCodec(codec1, "PreparedQuery.Execute", &req, &reply); err != nil {
|
|
|
|
t.Fatalf("err: %v", err)
|
|
|
|
}
|
|
|
|
|
|
|
|
if len(reply.Nodes) != 8 ||
|
|
|
|
reply.Datacenter != "dc1" || reply.Failovers != 0 ||
|
2015-11-13 18:38:44 +00:00
|
|
|
reply.Service != query.Query.Service.Service ||
|
2015-11-12 06:34:46 +00:00
|
|
|
!reflect.DeepEqual(reply.DNS, query.Query.DNS) ||
|
|
|
|
!reply.QueryMeta.KnownLeader {
|
2015-11-11 01:42:52 +00:00
|
|
|
t.Fatalf("bad: %v", reply)
|
|
|
|
}
|
|
|
|
for _, node := range reply.Nodes {
|
|
|
|
if node.Node.Node == "node1" || node.Node.Node == "node3" {
|
|
|
|
t.Fatalf("bad: %v", node)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
Creates new "prepared-query" ACL type and new token capture behavior.
Prior to this change, prepared queries had the following behavior for
ACLs, which will need to change to support templates:
1. A management token, or a token with read access to the service being
queried needed to be provided in order to create a prepared query.
2. The token used to create the prepared query was stored with the query
in the state store and used to execute the query.
3. A management token, or the token used to create the query needed to be
supplied to perform and CRUD operations on an existing prepared query.
This was pretty subtle and complicated behavior, and won't work for
templates since the service name is computed at execution time. To solve
this, we introduce a new "prepared-query" ACL type, where the prefix
applies to the query name for static prepared query types and to the
prefix for template prepared query types.
With this change, the new behavior is:
1. A management token, or a token with "prepared-query" write access to
the query name or (soon) the given template prefix is required to do
any CRUD operations on a prepared query, or to list prepared queries
(the list is filtered by this ACL).
2. You will no longer need a management token to list prepared queries,
but you will only be able to see prepared queries that you have access
to (you get an empty list instead of permission denied).
3. When listing or getting a query, because it was easy to capture
management tokens given the past behavior, this will always blank out
the "Token" field (replacing the contents as <hidden>) for all tokens
unless a management token is supplied. Going forward, we should
discourage people from binding tokens for execution unless strictly
necessary.
4. No token will be captured by default when a prepared query is created.
If the user wishes to supply an execution token then can pass it in via
the "Token" field in the prepared query definition. Otherwise, this
field will default to empty.
5. At execution time, we will use the captured token if it exists with the
prepared query definition, otherwise we will use the token that's passed
in with the request, just like we do for other RPCs (or you can use the
agent's configured token for DNS).
6. Prepared queries with no name (accessible only by ID) will not require
ACLs to create or modify (execution time will depend on the service ACL
configuration). Our argument here is that these are designed to be
ephemeral and the IDs are as good as an ACL. Management tokens will be
able to list all of these.
These changes enable templates, but also enable delegation of authority to
manage the prepared query namespace.
2016-02-23 08:12:58 +00:00
|
|
|
// Un-bake the token.
|
|
|
|
query.Query.Token = ""
|
|
|
|
if err := msgpackrpc.CallWithCodec(codec1, "PreparedQuery.Apply", &query, &query.Query.ID); err != nil {
|
|
|
|
t.Fatalf("err: %v", err)
|
|
|
|
}
|
|
|
|
|
|
|
|
// Make sure the query gets denied again with the deny token.
|
|
|
|
{
|
|
|
|
req := structs.PreparedQueryExecuteRequest{
|
|
|
|
Datacenter: "dc1",
|
|
|
|
QueryIDOrName: query.Query.ID,
|
|
|
|
QueryOptions: structs.QueryOptions{Token: denyToken},
|
|
|
|
}
|
|
|
|
|
|
|
|
var reply structs.PreparedQueryExecuteResponse
|
|
|
|
if err := msgpackrpc.CallWithCodec(codec1, "PreparedQuery.Execute", &req, &reply); err != nil {
|
|
|
|
t.Fatalf("err: %v", err)
|
|
|
|
}
|
|
|
|
|
|
|
|
if len(reply.Nodes) != 0 ||
|
|
|
|
reply.Datacenter != "dc1" || reply.Failovers != 0 ||
|
|
|
|
reply.Service != query.Query.Service.Service ||
|
|
|
|
!reflect.DeepEqual(reply.DNS, query.Query.DNS) ||
|
|
|
|
!reply.QueryMeta.KnownLeader {
|
|
|
|
t.Fatalf("bad: %v", reply)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2016-12-13 01:27:22 +00:00
|
|
|
// Turn on version 8 ACLs, which will start to filter even with the exec
|
|
|
|
// token.
|
|
|
|
s1.config.ACLEnforceVersion8 = true
|
|
|
|
{
|
|
|
|
req := structs.PreparedQueryExecuteRequest{
|
|
|
|
Datacenter: "dc1",
|
|
|
|
QueryIDOrName: query.Query.ID,
|
|
|
|
QueryOptions: structs.QueryOptions{Token: execToken},
|
|
|
|
}
|
|
|
|
|
|
|
|
var reply structs.PreparedQueryExecuteResponse
|
|
|
|
if err := msgpackrpc.CallWithCodec(codec1, "PreparedQuery.Execute", &req, &reply); err != nil {
|
|
|
|
t.Fatalf("err: %v", err)
|
|
|
|
}
|
|
|
|
|
|
|
|
if len(reply.Nodes) != 0 ||
|
|
|
|
reply.Datacenter != "dc1" || reply.Failovers != 0 ||
|
|
|
|
reply.Service != query.Query.Service.Service ||
|
|
|
|
!reflect.DeepEqual(reply.DNS, query.Query.DNS) ||
|
|
|
|
!reply.QueryMeta.KnownLeader {
|
|
|
|
t.Fatalf("bad: %v", reply)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
// Revert version 8 ACLs and make sure the query works again.
|
|
|
|
s1.config.ACLEnforceVersion8 = false
|
|
|
|
{
|
|
|
|
req := structs.PreparedQueryExecuteRequest{
|
|
|
|
Datacenter: "dc1",
|
|
|
|
QueryIDOrName: query.Query.ID,
|
|
|
|
QueryOptions: structs.QueryOptions{Token: execToken},
|
|
|
|
}
|
|
|
|
|
|
|
|
var reply structs.PreparedQueryExecuteResponse
|
|
|
|
if err := msgpackrpc.CallWithCodec(codec1, "PreparedQuery.Execute", &req, &reply); err != nil {
|
|
|
|
t.Fatalf("err: %v", err)
|
|
|
|
}
|
|
|
|
|
|
|
|
if len(reply.Nodes) != 8 ||
|
|
|
|
reply.Datacenter != "dc1" || reply.Failovers != 0 ||
|
|
|
|
reply.Service != query.Query.Service.Service ||
|
|
|
|
!reflect.DeepEqual(reply.DNS, query.Query.DNS) ||
|
|
|
|
!reply.QueryMeta.KnownLeader {
|
|
|
|
t.Fatalf("bad: %v", reply)
|
|
|
|
}
|
|
|
|
for _, node := range reply.Nodes {
|
|
|
|
if node.Node.Node == "node1" || node.Node.Node == "node3" {
|
|
|
|
t.Fatalf("bad: %v", node)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2015-11-11 01:42:52 +00:00
|
|
|
// Now fail everything in dc1 and we should get an empty list back.
|
|
|
|
for i := 0; i < 10; i++ {
|
2017-04-19 23:00:11 +00:00
|
|
|
setHealth(fmt.Sprintf("node%d", i+1), api.HealthCritical)
|
2015-11-11 01:42:52 +00:00
|
|
|
}
|
|
|
|
{
|
|
|
|
req := structs.PreparedQueryExecuteRequest{
|
|
|
|
Datacenter: "dc1",
|
|
|
|
QueryIDOrName: query.Query.ID,
|
Creates new "prepared-query" ACL type and new token capture behavior.
Prior to this change, prepared queries had the following behavior for
ACLs, which will need to change to support templates:
1. A management token, or a token with read access to the service being
queried needed to be provided in order to create a prepared query.
2. The token used to create the prepared query was stored with the query
in the state store and used to execute the query.
3. A management token, or the token used to create the query needed to be
supplied to perform and CRUD operations on an existing prepared query.
This was pretty subtle and complicated behavior, and won't work for
templates since the service name is computed at execution time. To solve
this, we introduce a new "prepared-query" ACL type, where the prefix
applies to the query name for static prepared query types and to the
prefix for template prepared query types.
With this change, the new behavior is:
1. A management token, or a token with "prepared-query" write access to
the query name or (soon) the given template prefix is required to do
any CRUD operations on a prepared query, or to list prepared queries
(the list is filtered by this ACL).
2. You will no longer need a management token to list prepared queries,
but you will only be able to see prepared queries that you have access
to (you get an empty list instead of permission denied).
3. When listing or getting a query, because it was easy to capture
management tokens given the past behavior, this will always blank out
the "Token" field (replacing the contents as <hidden>) for all tokens
unless a management token is supplied. Going forward, we should
discourage people from binding tokens for execution unless strictly
necessary.
4. No token will be captured by default when a prepared query is created.
If the user wishes to supply an execution token then can pass it in via
the "Token" field in the prepared query definition. Otherwise, this
field will default to empty.
5. At execution time, we will use the captured token if it exists with the
prepared query definition, otherwise we will use the token that's passed
in with the request, just like we do for other RPCs (or you can use the
agent's configured token for DNS).
6. Prepared queries with no name (accessible only by ID) will not require
ACLs to create or modify (execution time will depend on the service ACL
configuration). Our argument here is that these are designed to be
ephemeral and the IDs are as good as an ACL. Management tokens will be
able to list all of these.
These changes enable templates, but also enable delegation of authority to
manage the prepared query namespace.
2016-02-23 08:12:58 +00:00
|
|
|
QueryOptions: structs.QueryOptions{Token: execToken},
|
2015-11-11 01:42:52 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
var reply structs.PreparedQueryExecuteResponse
|
|
|
|
if err := msgpackrpc.CallWithCodec(codec1, "PreparedQuery.Execute", &req, &reply); err != nil {
|
|
|
|
t.Fatalf("err: %v", err)
|
|
|
|
}
|
|
|
|
|
|
|
|
if len(reply.Nodes) != 0 ||
|
|
|
|
reply.Datacenter != "dc1" || reply.Failovers != 0 ||
|
2015-11-13 18:38:44 +00:00
|
|
|
reply.Service != query.Query.Service.Service ||
|
2015-11-12 06:34:46 +00:00
|
|
|
!reflect.DeepEqual(reply.DNS, query.Query.DNS) ||
|
|
|
|
!reply.QueryMeta.KnownLeader {
|
2015-11-11 01:42:52 +00:00
|
|
|
t.Fatalf("bad: %v", reply)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
// Modify the query to have it fail over to a bogus DC and then dc2.
|
|
|
|
query.Query.Service.Failover.Datacenters = []string{"bogus", "dc2"}
|
|
|
|
if err := msgpackrpc.CallWithCodec(codec1, "PreparedQuery.Apply", &query, &query.Query.ID); err != nil {
|
|
|
|
t.Fatalf("err: %v", err)
|
|
|
|
}
|
|
|
|
|
|
|
|
// Now we should see 9 nodes from dc2 (we have the tag filter still).
|
|
|
|
{
|
|
|
|
req := structs.PreparedQueryExecuteRequest{
|
|
|
|
Datacenter: "dc1",
|
|
|
|
QueryIDOrName: query.Query.ID,
|
Creates new "prepared-query" ACL type and new token capture behavior.
Prior to this change, prepared queries had the following behavior for
ACLs, which will need to change to support templates:
1. A management token, or a token with read access to the service being
queried needed to be provided in order to create a prepared query.
2. The token used to create the prepared query was stored with the query
in the state store and used to execute the query.
3. A management token, or the token used to create the query needed to be
supplied to perform and CRUD operations on an existing prepared query.
This was pretty subtle and complicated behavior, and won't work for
templates since the service name is computed at execution time. To solve
this, we introduce a new "prepared-query" ACL type, where the prefix
applies to the query name for static prepared query types and to the
prefix for template prepared query types.
With this change, the new behavior is:
1. A management token, or a token with "prepared-query" write access to
the query name or (soon) the given template prefix is required to do
any CRUD operations on a prepared query, or to list prepared queries
(the list is filtered by this ACL).
2. You will no longer need a management token to list prepared queries,
but you will only be able to see prepared queries that you have access
to (you get an empty list instead of permission denied).
3. When listing or getting a query, because it was easy to capture
management tokens given the past behavior, this will always blank out
the "Token" field (replacing the contents as <hidden>) for all tokens
unless a management token is supplied. Going forward, we should
discourage people from binding tokens for execution unless strictly
necessary.
4. No token will be captured by default when a prepared query is created.
If the user wishes to supply an execution token then can pass it in via
the "Token" field in the prepared query definition. Otherwise, this
field will default to empty.
5. At execution time, we will use the captured token if it exists with the
prepared query definition, otherwise we will use the token that's passed
in with the request, just like we do for other RPCs (or you can use the
agent's configured token for DNS).
6. Prepared queries with no name (accessible only by ID) will not require
ACLs to create or modify (execution time will depend on the service ACL
configuration). Our argument here is that these are designed to be
ephemeral and the IDs are as good as an ACL. Management tokens will be
able to list all of these.
These changes enable templates, but also enable delegation of authority to
manage the prepared query namespace.
2016-02-23 08:12:58 +00:00
|
|
|
QueryOptions: structs.QueryOptions{Token: execToken},
|
2015-11-11 01:42:52 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
var reply structs.PreparedQueryExecuteResponse
|
|
|
|
if err := msgpackrpc.CallWithCodec(codec1, "PreparedQuery.Execute", &req, &reply); err != nil {
|
|
|
|
t.Fatalf("err: %v", err)
|
|
|
|
}
|
|
|
|
|
|
|
|
if len(reply.Nodes) != 9 ||
|
|
|
|
reply.Datacenter != "dc2" || reply.Failovers != 1 ||
|
2015-11-13 18:38:44 +00:00
|
|
|
reply.Service != query.Query.Service.Service ||
|
2015-11-12 06:34:46 +00:00
|
|
|
!reflect.DeepEqual(reply.DNS, query.Query.DNS) ||
|
|
|
|
!reply.QueryMeta.KnownLeader {
|
2015-11-11 01:42:52 +00:00
|
|
|
t.Fatalf("bad: %v", reply)
|
|
|
|
}
|
|
|
|
for _, node := range reply.Nodes {
|
|
|
|
if node.Node.Node == "node3" {
|
|
|
|
t.Fatalf("bad: %v", node)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
// Make sure the limit and query options are forwarded.
|
|
|
|
{
|
|
|
|
req := structs.PreparedQueryExecuteRequest{
|
|
|
|
Datacenter: "dc1",
|
|
|
|
QueryIDOrName: query.Query.ID,
|
|
|
|
Limit: 3,
|
Creates new "prepared-query" ACL type and new token capture behavior.
Prior to this change, prepared queries had the following behavior for
ACLs, which will need to change to support templates:
1. A management token, or a token with read access to the service being
queried needed to be provided in order to create a prepared query.
2. The token used to create the prepared query was stored with the query
in the state store and used to execute the query.
3. A management token, or the token used to create the query needed to be
supplied to perform and CRUD operations on an existing prepared query.
This was pretty subtle and complicated behavior, and won't work for
templates since the service name is computed at execution time. To solve
this, we introduce a new "prepared-query" ACL type, where the prefix
applies to the query name for static prepared query types and to the
prefix for template prepared query types.
With this change, the new behavior is:
1. A management token, or a token with "prepared-query" write access to
the query name or (soon) the given template prefix is required to do
any CRUD operations on a prepared query, or to list prepared queries
(the list is filtered by this ACL).
2. You will no longer need a management token to list prepared queries,
but you will only be able to see prepared queries that you have access
to (you get an empty list instead of permission denied).
3. When listing or getting a query, because it was easy to capture
management tokens given the past behavior, this will always blank out
the "Token" field (replacing the contents as <hidden>) for all tokens
unless a management token is supplied. Going forward, we should
discourage people from binding tokens for execution unless strictly
necessary.
4. No token will be captured by default when a prepared query is created.
If the user wishes to supply an execution token then can pass it in via
the "Token" field in the prepared query definition. Otherwise, this
field will default to empty.
5. At execution time, we will use the captured token if it exists with the
prepared query definition, otherwise we will use the token that's passed
in with the request, just like we do for other RPCs (or you can use the
agent's configured token for DNS).
6. Prepared queries with no name (accessible only by ID) will not require
ACLs to create or modify (execution time will depend on the service ACL
configuration). Our argument here is that these are designed to be
ephemeral and the IDs are as good as an ACL. Management tokens will be
able to list all of these.
These changes enable templates, but also enable delegation of authority to
manage the prepared query namespace.
2016-02-23 08:12:58 +00:00
|
|
|
QueryOptions: structs.QueryOptions{
|
|
|
|
Token: execToken,
|
|
|
|
RequireConsistent: true,
|
|
|
|
},
|
2015-11-11 01:42:52 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
var reply structs.PreparedQueryExecuteResponse
|
|
|
|
if err := msgpackrpc.CallWithCodec(codec1, "PreparedQuery.Execute", &req, &reply); err != nil {
|
|
|
|
t.Fatalf("err: %v", err)
|
|
|
|
}
|
|
|
|
|
|
|
|
if len(reply.Nodes) != 3 ||
|
|
|
|
reply.Datacenter != "dc2" || reply.Failovers != 1 ||
|
2015-11-13 18:38:44 +00:00
|
|
|
reply.Service != query.Query.Service.Service ||
|
2015-11-12 06:34:46 +00:00
|
|
|
!reflect.DeepEqual(reply.DNS, query.Query.DNS) ||
|
|
|
|
!reply.QueryMeta.KnownLeader {
|
2015-11-11 01:42:52 +00:00
|
|
|
t.Fatalf("bad: %v", reply)
|
|
|
|
}
|
|
|
|
for _, node := range reply.Nodes {
|
|
|
|
if node.Node.Node == "node3" {
|
|
|
|
t.Fatalf("bad: %v", node)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
// Make sure the remote shuffle looks like it's working.
|
|
|
|
uniques = make(map[string]struct{})
|
|
|
|
for i := 0; i < 100; i++ {
|
|
|
|
req := structs.PreparedQueryExecuteRequest{
|
|
|
|
Datacenter: "dc1",
|
|
|
|
QueryIDOrName: query.Query.ID,
|
Creates new "prepared-query" ACL type and new token capture behavior.
Prior to this change, prepared queries had the following behavior for
ACLs, which will need to change to support templates:
1. A management token, or a token with read access to the service being
queried needed to be provided in order to create a prepared query.
2. The token used to create the prepared query was stored with the query
in the state store and used to execute the query.
3. A management token, or the token used to create the query needed to be
supplied to perform and CRUD operations on an existing prepared query.
This was pretty subtle and complicated behavior, and won't work for
templates since the service name is computed at execution time. To solve
this, we introduce a new "prepared-query" ACL type, where the prefix
applies to the query name for static prepared query types and to the
prefix for template prepared query types.
With this change, the new behavior is:
1. A management token, or a token with "prepared-query" write access to
the query name or (soon) the given template prefix is required to do
any CRUD operations on a prepared query, or to list prepared queries
(the list is filtered by this ACL).
2. You will no longer need a management token to list prepared queries,
but you will only be able to see prepared queries that you have access
to (you get an empty list instead of permission denied).
3. When listing or getting a query, because it was easy to capture
management tokens given the past behavior, this will always blank out
the "Token" field (replacing the contents as <hidden>) for all tokens
unless a management token is supplied. Going forward, we should
discourage people from binding tokens for execution unless strictly
necessary.
4. No token will be captured by default when a prepared query is created.
If the user wishes to supply an execution token then can pass it in via
the "Token" field in the prepared query definition. Otherwise, this
field will default to empty.
5. At execution time, we will use the captured token if it exists with the
prepared query definition, otherwise we will use the token that's passed
in with the request, just like we do for other RPCs (or you can use the
agent's configured token for DNS).
6. Prepared queries with no name (accessible only by ID) will not require
ACLs to create or modify (execution time will depend on the service ACL
configuration). Our argument here is that these are designed to be
ephemeral and the IDs are as good as an ACL. Management tokens will be
able to list all of these.
These changes enable templates, but also enable delegation of authority to
manage the prepared query namespace.
2016-02-23 08:12:58 +00:00
|
|
|
QueryOptions: structs.QueryOptions{Token: execToken},
|
2015-11-11 01:42:52 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
var reply structs.PreparedQueryExecuteResponse
|
|
|
|
if err := msgpackrpc.CallWithCodec(codec1, "PreparedQuery.Execute", &req, &reply); err != nil {
|
|
|
|
t.Fatalf("err: %v", err)
|
|
|
|
}
|
|
|
|
|
|
|
|
if len(reply.Nodes) != 9 ||
|
|
|
|
reply.Datacenter != "dc2" || reply.Failovers != 1 ||
|
2015-11-13 18:38:44 +00:00
|
|
|
reply.Service != query.Query.Service.Service ||
|
2015-11-12 06:34:46 +00:00
|
|
|
!reflect.DeepEqual(reply.DNS, query.Query.DNS) ||
|
|
|
|
!reply.QueryMeta.KnownLeader {
|
2015-11-11 01:42:52 +00:00
|
|
|
t.Fatalf("bad: %v", reply)
|
|
|
|
}
|
|
|
|
var names []string
|
|
|
|
for _, node := range reply.Nodes {
|
|
|
|
names = append(names, node.Node.Node)
|
|
|
|
}
|
|
|
|
key := strings.Join(names, "|")
|
|
|
|
uniques[key] = struct{}{}
|
|
|
|
}
|
|
|
|
|
|
|
|
// We have to allow for the fact that there won't always be a unique
|
|
|
|
// shuffle each pass, so we just look for smell here without the test
|
|
|
|
// being flaky.
|
|
|
|
if len(uniques) < 50 {
|
|
|
|
t.Fatalf("unique shuffle ratio too low: %d/100", len(uniques))
|
|
|
|
}
|
|
|
|
|
Creates new "prepared-query" ACL type and new token capture behavior.
Prior to this change, prepared queries had the following behavior for
ACLs, which will need to change to support templates:
1. A management token, or a token with read access to the service being
queried needed to be provided in order to create a prepared query.
2. The token used to create the prepared query was stored with the query
in the state store and used to execute the query.
3. A management token, or the token used to create the query needed to be
supplied to perform and CRUD operations on an existing prepared query.
This was pretty subtle and complicated behavior, and won't work for
templates since the service name is computed at execution time. To solve
this, we introduce a new "prepared-query" ACL type, where the prefix
applies to the query name for static prepared query types and to the
prefix for template prepared query types.
With this change, the new behavior is:
1. A management token, or a token with "prepared-query" write access to
the query name or (soon) the given template prefix is required to do
any CRUD operations on a prepared query, or to list prepared queries
(the list is filtered by this ACL).
2. You will no longer need a management token to list prepared queries,
but you will only be able to see prepared queries that you have access
to (you get an empty list instead of permission denied).
3. When listing or getting a query, because it was easy to capture
management tokens given the past behavior, this will always blank out
the "Token" field (replacing the contents as <hidden>) for all tokens
unless a management token is supplied. Going forward, we should
discourage people from binding tokens for execution unless strictly
necessary.
4. No token will be captured by default when a prepared query is created.
If the user wishes to supply an execution token then can pass it in via
the "Token" field in the prepared query definition. Otherwise, this
field will default to empty.
5. At execution time, we will use the captured token if it exists with the
prepared query definition, otherwise we will use the token that's passed
in with the request, just like we do for other RPCs (or you can use the
agent's configured token for DNS).
6. Prepared queries with no name (accessible only by ID) will not require
ACLs to create or modify (execution time will depend on the service ACL
configuration). Our argument here is that these are designed to be
ephemeral and the IDs are as good as an ACL. Management tokens will be
able to list all of these.
These changes enable templates, but also enable delegation of authority to
manage the prepared query namespace.
2016-02-23 08:12:58 +00:00
|
|
|
// Make sure the query response from dc2 gets denied with the deny token.
|
2015-11-11 01:42:52 +00:00
|
|
|
{
|
Creates new "prepared-query" ACL type and new token capture behavior.
Prior to this change, prepared queries had the following behavior for
ACLs, which will need to change to support templates:
1. A management token, or a token with read access to the service being
queried needed to be provided in order to create a prepared query.
2. The token used to create the prepared query was stored with the query
in the state store and used to execute the query.
3. A management token, or the token used to create the query needed to be
supplied to perform and CRUD operations on an existing prepared query.
This was pretty subtle and complicated behavior, and won't work for
templates since the service name is computed at execution time. To solve
this, we introduce a new "prepared-query" ACL type, where the prefix
applies to the query name for static prepared query types and to the
prefix for template prepared query types.
With this change, the new behavior is:
1. A management token, or a token with "prepared-query" write access to
the query name or (soon) the given template prefix is required to do
any CRUD operations on a prepared query, or to list prepared queries
(the list is filtered by this ACL).
2. You will no longer need a management token to list prepared queries,
but you will only be able to see prepared queries that you have access
to (you get an empty list instead of permission denied).
3. When listing or getting a query, because it was easy to capture
management tokens given the past behavior, this will always blank out
the "Token" field (replacing the contents as <hidden>) for all tokens
unless a management token is supplied. Going forward, we should
discourage people from binding tokens for execution unless strictly
necessary.
4. No token will be captured by default when a prepared query is created.
If the user wishes to supply an execution token then can pass it in via
the "Token" field in the prepared query definition. Otherwise, this
field will default to empty.
5. At execution time, we will use the captured token if it exists with the
prepared query definition, otherwise we will use the token that's passed
in with the request, just like we do for other RPCs (or you can use the
agent's configured token for DNS).
6. Prepared queries with no name (accessible only by ID) will not require
ACLs to create or modify (execution time will depend on the service ACL
configuration). Our argument here is that these are designed to be
ephemeral and the IDs are as good as an ACL. Management tokens will be
able to list all of these.
These changes enable templates, but also enable delegation of authority to
manage the prepared query namespace.
2016-02-23 08:12:58 +00:00
|
|
|
req := structs.PreparedQueryExecuteRequest{
|
|
|
|
Datacenter: "dc1",
|
|
|
|
QueryIDOrName: query.Query.ID,
|
|
|
|
QueryOptions: structs.QueryOptions{Token: denyToken},
|
2015-11-11 01:42:52 +00:00
|
|
|
}
|
Creates new "prepared-query" ACL type and new token capture behavior.
Prior to this change, prepared queries had the following behavior for
ACLs, which will need to change to support templates:
1. A management token, or a token with read access to the service being
queried needed to be provided in order to create a prepared query.
2. The token used to create the prepared query was stored with the query
in the state store and used to execute the query.
3. A management token, or the token used to create the query needed to be
supplied to perform and CRUD operations on an existing prepared query.
This was pretty subtle and complicated behavior, and won't work for
templates since the service name is computed at execution time. To solve
this, we introduce a new "prepared-query" ACL type, where the prefix
applies to the query name for static prepared query types and to the
prefix for template prepared query types.
With this change, the new behavior is:
1. A management token, or a token with "prepared-query" write access to
the query name or (soon) the given template prefix is required to do
any CRUD operations on a prepared query, or to list prepared queries
(the list is filtered by this ACL).
2. You will no longer need a management token to list prepared queries,
but you will only be able to see prepared queries that you have access
to (you get an empty list instead of permission denied).
3. When listing or getting a query, because it was easy to capture
management tokens given the past behavior, this will always blank out
the "Token" field (replacing the contents as <hidden>) for all tokens
unless a management token is supplied. Going forward, we should
discourage people from binding tokens for execution unless strictly
necessary.
4. No token will be captured by default when a prepared query is created.
If the user wishes to supply an execution token then can pass it in via
the "Token" field in the prepared query definition. Otherwise, this
field will default to empty.
5. At execution time, we will use the captured token if it exists with the
prepared query definition, otherwise we will use the token that's passed
in with the request, just like we do for other RPCs (or you can use the
agent's configured token for DNS).
6. Prepared queries with no name (accessible only by ID) will not require
ACLs to create or modify (execution time will depend on the service ACL
configuration). Our argument here is that these are designed to be
ephemeral and the IDs are as good as an ACL. Management tokens will be
able to list all of these.
These changes enable templates, but also enable delegation of authority to
manage the prepared query namespace.
2016-02-23 08:12:58 +00:00
|
|
|
|
|
|
|
var reply structs.PreparedQueryExecuteResponse
|
|
|
|
if err := msgpackrpc.CallWithCodec(codec1, "PreparedQuery.Execute", &req, &reply); err != nil {
|
2015-11-11 01:42:52 +00:00
|
|
|
t.Fatalf("err: %v", err)
|
|
|
|
}
|
Creates new "prepared-query" ACL type and new token capture behavior.
Prior to this change, prepared queries had the following behavior for
ACLs, which will need to change to support templates:
1. A management token, or a token with read access to the service being
queried needed to be provided in order to create a prepared query.
2. The token used to create the prepared query was stored with the query
in the state store and used to execute the query.
3. A management token, or the token used to create the query needed to be
supplied to perform and CRUD operations on an existing prepared query.
This was pretty subtle and complicated behavior, and won't work for
templates since the service name is computed at execution time. To solve
this, we introduce a new "prepared-query" ACL type, where the prefix
applies to the query name for static prepared query types and to the
prefix for template prepared query types.
With this change, the new behavior is:
1. A management token, or a token with "prepared-query" write access to
the query name or (soon) the given template prefix is required to do
any CRUD operations on a prepared query, or to list prepared queries
(the list is filtered by this ACL).
2. You will no longer need a management token to list prepared queries,
but you will only be able to see prepared queries that you have access
to (you get an empty list instead of permission denied).
3. When listing or getting a query, because it was easy to capture
management tokens given the past behavior, this will always blank out
the "Token" field (replacing the contents as <hidden>) for all tokens
unless a management token is supplied. Going forward, we should
discourage people from binding tokens for execution unless strictly
necessary.
4. No token will be captured by default when a prepared query is created.
If the user wishes to supply an execution token then can pass it in via
the "Token" field in the prepared query definition. Otherwise, this
field will default to empty.
5. At execution time, we will use the captured token if it exists with the
prepared query definition, otherwise we will use the token that's passed
in with the request, just like we do for other RPCs (or you can use the
agent's configured token for DNS).
6. Prepared queries with no name (accessible only by ID) will not require
ACLs to create or modify (execution time will depend on the service ACL
configuration). Our argument here is that these are designed to be
ephemeral and the IDs are as good as an ACL. Management tokens will be
able to list all of these.
These changes enable templates, but also enable delegation of authority to
manage the prepared query namespace.
2016-02-23 08:12:58 +00:00
|
|
|
|
|
|
|
if len(reply.Nodes) != 0 ||
|
|
|
|
reply.Datacenter != "dc2" || reply.Failovers != 1 ||
|
|
|
|
reply.Service != query.Query.Service.Service ||
|
|
|
|
!reflect.DeepEqual(reply.DNS, query.Query.DNS) ||
|
|
|
|
!reply.QueryMeta.KnownLeader {
|
|
|
|
t.Fatalf("bad: %v", reply)
|
|
|
|
}
|
2015-11-11 01:42:52 +00:00
|
|
|
}
|
|
|
|
|
Creates new "prepared-query" ACL type and new token capture behavior.
Prior to this change, prepared queries had the following behavior for
ACLs, which will need to change to support templates:
1. A management token, or a token with read access to the service being
queried needed to be provided in order to create a prepared query.
2. The token used to create the prepared query was stored with the query
in the state store and used to execute the query.
3. A management token, or the token used to create the query needed to be
supplied to perform and CRUD operations on an existing prepared query.
This was pretty subtle and complicated behavior, and won't work for
templates since the service name is computed at execution time. To solve
this, we introduce a new "prepared-query" ACL type, where the prefix
applies to the query name for static prepared query types and to the
prefix for template prepared query types.
With this change, the new behavior is:
1. A management token, or a token with "prepared-query" write access to
the query name or (soon) the given template prefix is required to do
any CRUD operations on a prepared query, or to list prepared queries
(the list is filtered by this ACL).
2. You will no longer need a management token to list prepared queries,
but you will only be able to see prepared queries that you have access
to (you get an empty list instead of permission denied).
3. When listing or getting a query, because it was easy to capture
management tokens given the past behavior, this will always blank out
the "Token" field (replacing the contents as <hidden>) for all tokens
unless a management token is supplied. Going forward, we should
discourage people from binding tokens for execution unless strictly
necessary.
4. No token will be captured by default when a prepared query is created.
If the user wishes to supply an execution token then can pass it in via
the "Token" field in the prepared query definition. Otherwise, this
field will default to empty.
5. At execution time, we will use the captured token if it exists with the
prepared query definition, otherwise we will use the token that's passed
in with the request, just like we do for other RPCs (or you can use the
agent's configured token for DNS).
6. Prepared queries with no name (accessible only by ID) will not require
ACLs to create or modify (execution time will depend on the service ACL
configuration). Our argument here is that these are designed to be
ephemeral and the IDs are as good as an ACL. Management tokens will be
able to list all of these.
These changes enable templates, but also enable delegation of authority to
manage the prepared query namespace.
2016-02-23 08:12:58 +00:00
|
|
|
// Bake the exec token into the query.
|
|
|
|
query.Query.Token = execToken
|
|
|
|
if err := msgpackrpc.CallWithCodec(codec1, "PreparedQuery.Apply", &query, &query.Query.ID); err != nil {
|
|
|
|
t.Fatalf("err: %v", err)
|
|
|
|
}
|
|
|
|
|
|
|
|
// Now even querying with the deny token should work.
|
2015-11-11 01:42:52 +00:00
|
|
|
{
|
|
|
|
req := structs.PreparedQueryExecuteRequest{
|
|
|
|
Datacenter: "dc1",
|
|
|
|
QueryIDOrName: query.Query.ID,
|
Creates new "prepared-query" ACL type and new token capture behavior.
Prior to this change, prepared queries had the following behavior for
ACLs, which will need to change to support templates:
1. A management token, or a token with read access to the service being
queried needed to be provided in order to create a prepared query.
2. The token used to create the prepared query was stored with the query
in the state store and used to execute the query.
3. A management token, or the token used to create the query needed to be
supplied to perform and CRUD operations on an existing prepared query.
This was pretty subtle and complicated behavior, and won't work for
templates since the service name is computed at execution time. To solve
this, we introduce a new "prepared-query" ACL type, where the prefix
applies to the query name for static prepared query types and to the
prefix for template prepared query types.
With this change, the new behavior is:
1. A management token, or a token with "prepared-query" write access to
the query name or (soon) the given template prefix is required to do
any CRUD operations on a prepared query, or to list prepared queries
(the list is filtered by this ACL).
2. You will no longer need a management token to list prepared queries,
but you will only be able to see prepared queries that you have access
to (you get an empty list instead of permission denied).
3. When listing or getting a query, because it was easy to capture
management tokens given the past behavior, this will always blank out
the "Token" field (replacing the contents as <hidden>) for all tokens
unless a management token is supplied. Going forward, we should
discourage people from binding tokens for execution unless strictly
necessary.
4. No token will be captured by default when a prepared query is created.
If the user wishes to supply an execution token then can pass it in via
the "Token" field in the prepared query definition. Otherwise, this
field will default to empty.
5. At execution time, we will use the captured token if it exists with the
prepared query definition, otherwise we will use the token that's passed
in with the request, just like we do for other RPCs (or you can use the
agent's configured token for DNS).
6. Prepared queries with no name (accessible only by ID) will not require
ACLs to create or modify (execution time will depend on the service ACL
configuration). Our argument here is that these are designed to be
ephemeral and the IDs are as good as an ACL. Management tokens will be
able to list all of these.
These changes enable templates, but also enable delegation of authority to
manage the prepared query namespace.
2016-02-23 08:12:58 +00:00
|
|
|
QueryOptions: structs.QueryOptions{Token: denyToken},
|
2015-11-11 01:42:52 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
var reply structs.PreparedQueryExecuteResponse
|
Creates new "prepared-query" ACL type and new token capture behavior.
Prior to this change, prepared queries had the following behavior for
ACLs, which will need to change to support templates:
1. A management token, or a token with read access to the service being
queried needed to be provided in order to create a prepared query.
2. The token used to create the prepared query was stored with the query
in the state store and used to execute the query.
3. A management token, or the token used to create the query needed to be
supplied to perform and CRUD operations on an existing prepared query.
This was pretty subtle and complicated behavior, and won't work for
templates since the service name is computed at execution time. To solve
this, we introduce a new "prepared-query" ACL type, where the prefix
applies to the query name for static prepared query types and to the
prefix for template prepared query types.
With this change, the new behavior is:
1. A management token, or a token with "prepared-query" write access to
the query name or (soon) the given template prefix is required to do
any CRUD operations on a prepared query, or to list prepared queries
(the list is filtered by this ACL).
2. You will no longer need a management token to list prepared queries,
but you will only be able to see prepared queries that you have access
to (you get an empty list instead of permission denied).
3. When listing or getting a query, because it was easy to capture
management tokens given the past behavior, this will always blank out
the "Token" field (replacing the contents as <hidden>) for all tokens
unless a management token is supplied. Going forward, we should
discourage people from binding tokens for execution unless strictly
necessary.
4. No token will be captured by default when a prepared query is created.
If the user wishes to supply an execution token then can pass it in via
the "Token" field in the prepared query definition. Otherwise, this
field will default to empty.
5. At execution time, we will use the captured token if it exists with the
prepared query definition, otherwise we will use the token that's passed
in with the request, just like we do for other RPCs (or you can use the
agent's configured token for DNS).
6. Prepared queries with no name (accessible only by ID) will not require
ACLs to create or modify (execution time will depend on the service ACL
configuration). Our argument here is that these are designed to be
ephemeral and the IDs are as good as an ACL. Management tokens will be
able to list all of these.
These changes enable templates, but also enable delegation of authority to
manage the prepared query namespace.
2016-02-23 08:12:58 +00:00
|
|
|
if err := msgpackrpc.CallWithCodec(codec1, "PreparedQuery.Execute", &req, &reply); err != nil {
|
|
|
|
t.Fatalf("err: %v", err)
|
2015-11-11 01:42:52 +00:00
|
|
|
}
|
|
|
|
|
Creates new "prepared-query" ACL type and new token capture behavior.
Prior to this change, prepared queries had the following behavior for
ACLs, which will need to change to support templates:
1. A management token, or a token with read access to the service being
queried needed to be provided in order to create a prepared query.
2. The token used to create the prepared query was stored with the query
in the state store and used to execute the query.
3. A management token, or the token used to create the query needed to be
supplied to perform and CRUD operations on an existing prepared query.
This was pretty subtle and complicated behavior, and won't work for
templates since the service name is computed at execution time. To solve
this, we introduce a new "prepared-query" ACL type, where the prefix
applies to the query name for static prepared query types and to the
prefix for template prepared query types.
With this change, the new behavior is:
1. A management token, or a token with "prepared-query" write access to
the query name or (soon) the given template prefix is required to do
any CRUD operations on a prepared query, or to list prepared queries
(the list is filtered by this ACL).
2. You will no longer need a management token to list prepared queries,
but you will only be able to see prepared queries that you have access
to (you get an empty list instead of permission denied).
3. When listing or getting a query, because it was easy to capture
management tokens given the past behavior, this will always blank out
the "Token" field (replacing the contents as <hidden>) for all tokens
unless a management token is supplied. Going forward, we should
discourage people from binding tokens for execution unless strictly
necessary.
4. No token will be captured by default when a prepared query is created.
If the user wishes to supply an execution token then can pass it in via
the "Token" field in the prepared query definition. Otherwise, this
field will default to empty.
5. At execution time, we will use the captured token if it exists with the
prepared query definition, otherwise we will use the token that's passed
in with the request, just like we do for other RPCs (or you can use the
agent's configured token for DNS).
6. Prepared queries with no name (accessible only by ID) will not require
ACLs to create or modify (execution time will depend on the service ACL
configuration). Our argument here is that these are designed to be
ephemeral and the IDs are as good as an ACL. Management tokens will be
able to list all of these.
These changes enable templates, but also enable delegation of authority to
manage the prepared query namespace.
2016-02-23 08:12:58 +00:00
|
|
|
if len(reply.Nodes) != 9 ||
|
|
|
|
reply.Datacenter != "dc2" || reply.Failovers != 1 ||
|
|
|
|
reply.Service != query.Query.Service.Service ||
|
|
|
|
!reflect.DeepEqual(reply.DNS, query.Query.DNS) ||
|
|
|
|
!reply.QueryMeta.KnownLeader {
|
2015-11-11 01:42:52 +00:00
|
|
|
t.Fatalf("bad: %v", reply)
|
|
|
|
}
|
Creates new "prepared-query" ACL type and new token capture behavior.
Prior to this change, prepared queries had the following behavior for
ACLs, which will need to change to support templates:
1. A management token, or a token with read access to the service being
queried needed to be provided in order to create a prepared query.
2. The token used to create the prepared query was stored with the query
in the state store and used to execute the query.
3. A management token, or the token used to create the query needed to be
supplied to perform and CRUD operations on an existing prepared query.
This was pretty subtle and complicated behavior, and won't work for
templates since the service name is computed at execution time. To solve
this, we introduce a new "prepared-query" ACL type, where the prefix
applies to the query name for static prepared query types and to the
prefix for template prepared query types.
With this change, the new behavior is:
1. A management token, or a token with "prepared-query" write access to
the query name or (soon) the given template prefix is required to do
any CRUD operations on a prepared query, or to list prepared queries
(the list is filtered by this ACL).
2. You will no longer need a management token to list prepared queries,
but you will only be able to see prepared queries that you have access
to (you get an empty list instead of permission denied).
3. When listing or getting a query, because it was easy to capture
management tokens given the past behavior, this will always blank out
the "Token" field (replacing the contents as <hidden>) for all tokens
unless a management token is supplied. Going forward, we should
discourage people from binding tokens for execution unless strictly
necessary.
4. No token will be captured by default when a prepared query is created.
If the user wishes to supply an execution token then can pass it in via
the "Token" field in the prepared query definition. Otherwise, this
field will default to empty.
5. At execution time, we will use the captured token if it exists with the
prepared query definition, otherwise we will use the token that's passed
in with the request, just like we do for other RPCs (or you can use the
agent's configured token for DNS).
6. Prepared queries with no name (accessible only by ID) will not require
ACLs to create or modify (execution time will depend on the service ACL
configuration). Our argument here is that these are designed to be
ephemeral and the IDs are as good as an ACL. Management tokens will be
able to list all of these.
These changes enable templates, but also enable delegation of authority to
manage the prepared query namespace.
2016-02-23 08:12:58 +00:00
|
|
|
for _, node := range reply.Nodes {
|
|
|
|
if node.Node.Node == "node3" {
|
|
|
|
t.Fatalf("bad: %v", node)
|
|
|
|
}
|
|
|
|
}
|
2015-11-11 01:42:52 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2015-11-10 23:16:41 +00:00
|
|
|
func TestPreparedQuery_Execute_ForwardLeader(t *testing.T) {
|
2017-06-27 13:22:18 +00:00
|
|
|
t.Parallel()
|
2015-11-10 23:16:41 +00:00
|
|
|
dir1, s1 := testServer(t)
|
|
|
|
defer os.RemoveAll(dir1)
|
|
|
|
defer s1.Shutdown()
|
|
|
|
codec1 := rpcClient(t, s1)
|
|
|
|
defer codec1.Close()
|
|
|
|
|
|
|
|
dir2, s2 := testServer(t)
|
|
|
|
defer os.RemoveAll(dir2)
|
|
|
|
defer s2.Shutdown()
|
|
|
|
codec2 := rpcClient(t, s2)
|
|
|
|
defer codec2.Close()
|
|
|
|
|
|
|
|
// Try to join.
|
2017-05-05 10:29:49 +00:00
|
|
|
joinLAN(t, s2, s1)
|
2015-11-10 23:16:41 +00:00
|
|
|
|
2017-04-19 23:00:11 +00:00
|
|
|
testrpc.WaitForLeader(t, s1.RPC, "dc1")
|
|
|
|
testrpc.WaitForLeader(t, s2.RPC, "dc1")
|
2015-11-10 23:16:41 +00:00
|
|
|
|
|
|
|
// Use the follower as the client.
|
|
|
|
var codec rpc.ClientCodec
|
|
|
|
if !s1.IsLeader() {
|
|
|
|
codec = codec1
|
|
|
|
} else {
|
|
|
|
codec = codec2
|
|
|
|
}
|
|
|
|
|
|
|
|
// Set up a node and service in the catalog.
|
|
|
|
{
|
|
|
|
req := structs.RegisterRequest{
|
|
|
|
Datacenter: "dc1",
|
|
|
|
Node: "foo",
|
|
|
|
Address: "127.0.0.1",
|
|
|
|
Service: &structs.NodeService{
|
|
|
|
Service: "redis",
|
|
|
|
Tags: []string{"master"},
|
|
|
|
Port: 8000,
|
|
|
|
},
|
|
|
|
}
|
|
|
|
var reply struct{}
|
|
|
|
if err := msgpackrpc.CallWithCodec(codec, "Catalog.Register", &req, &reply); err != nil {
|
|
|
|
t.Fatalf("err: %v", err)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
// Set up a bare bones query.
|
|
|
|
query := structs.PreparedQueryRequest{
|
|
|
|
Datacenter: "dc1",
|
|
|
|
Op: structs.PreparedQueryCreate,
|
|
|
|
Query: &structs.PreparedQuery{
|
2016-12-10 19:19:08 +00:00
|
|
|
Name: "test",
|
2015-11-10 23:16:41 +00:00
|
|
|
Service: structs.ServiceQuery{
|
|
|
|
Service: "redis",
|
|
|
|
},
|
|
|
|
},
|
|
|
|
}
|
|
|
|
var reply string
|
|
|
|
if err := msgpackrpc.CallWithCodec(codec, "PreparedQuery.Apply", &query, &reply); err != nil {
|
|
|
|
t.Fatalf("err: %v", err)
|
|
|
|
}
|
|
|
|
|
|
|
|
// Execute it through the follower.
|
|
|
|
{
|
|
|
|
req := structs.PreparedQueryExecuteRequest{
|
|
|
|
Datacenter: "dc1",
|
|
|
|
QueryIDOrName: reply,
|
|
|
|
}
|
|
|
|
var reply structs.PreparedQueryExecuteResponse
|
|
|
|
if err := msgpackrpc.CallWithCodec(codec, "PreparedQuery.Execute", &req, &reply); err != nil {
|
|
|
|
t.Fatalf("err: %v", err)
|
|
|
|
}
|
|
|
|
|
|
|
|
if len(reply.Nodes) != 1 {
|
|
|
|
t.Fatalf("bad: %v", reply)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
// Execute it through the follower with consistency turned on.
|
|
|
|
{
|
|
|
|
req := structs.PreparedQueryExecuteRequest{
|
|
|
|
Datacenter: "dc1",
|
|
|
|
QueryIDOrName: reply,
|
|
|
|
QueryOptions: structs.QueryOptions{RequireConsistent: true},
|
|
|
|
}
|
|
|
|
var reply structs.PreparedQueryExecuteResponse
|
|
|
|
if err := msgpackrpc.CallWithCodec(codec, "PreparedQuery.Execute", &req, &reply); err != nil {
|
|
|
|
t.Fatalf("err: %v", err)
|
|
|
|
}
|
|
|
|
|
|
|
|
if len(reply.Nodes) != 1 {
|
|
|
|
t.Fatalf("bad: %v", reply)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
// Remote execute it through the follower.
|
|
|
|
{
|
|
|
|
req := structs.PreparedQueryExecuteRemoteRequest{
|
|
|
|
Datacenter: "dc1",
|
|
|
|
Query: *query.Query,
|
|
|
|
}
|
|
|
|
var reply structs.PreparedQueryExecuteResponse
|
|
|
|
if err := msgpackrpc.CallWithCodec(codec, "PreparedQuery.ExecuteRemote", &req, &reply); err != nil {
|
|
|
|
t.Fatalf("err: %v", err)
|
|
|
|
}
|
|
|
|
|
|
|
|
if len(reply.Nodes) != 1 {
|
|
|
|
t.Fatalf("bad: %v", reply)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
// Remote execute it through the follower with consistency turned on.
|
|
|
|
{
|
|
|
|
req := structs.PreparedQueryExecuteRemoteRequest{
|
|
|
|
Datacenter: "dc1",
|
|
|
|
Query: *query.Query,
|
|
|
|
QueryOptions: structs.QueryOptions{RequireConsistent: true},
|
|
|
|
}
|
|
|
|
var reply structs.PreparedQueryExecuteResponse
|
|
|
|
if err := msgpackrpc.CallWithCodec(codec, "PreparedQuery.ExecuteRemote", &req, &reply); err != nil {
|
|
|
|
t.Fatalf("err: %v", err)
|
|
|
|
}
|
|
|
|
|
|
|
|
if len(reply.Nodes) != 1 {
|
|
|
|
t.Fatalf("bad: %v", reply)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
2015-11-11 02:23:37 +00:00
|
|
|
|
2018-06-05 21:17:19 +00:00
|
|
|
func TestPreparedQuery_Execute_ConnectExact(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()
|
|
|
|
|
|
|
|
// Setup 3 services on 3 nodes: one is non-Connect, one is Connect native,
|
|
|
|
// and one is a proxy to the non-Connect one.
|
|
|
|
for i := 0; i < 3; i++ {
|
|
|
|
req := structs.RegisterRequest{
|
|
|
|
Datacenter: "dc1",
|
|
|
|
Node: fmt.Sprintf("node%d", i+1),
|
|
|
|
Address: fmt.Sprintf("127.0.0.%d", i+1),
|
|
|
|
Service: &structs.NodeService{
|
|
|
|
Service: "foo",
|
|
|
|
Port: 8000,
|
|
|
|
},
|
|
|
|
}
|
|
|
|
|
|
|
|
switch i {
|
|
|
|
case 0:
|
|
|
|
// Default do nothing
|
|
|
|
|
|
|
|
case 1:
|
|
|
|
// Connect native
|
|
|
|
req.Service.Connect.Native = true
|
|
|
|
|
|
|
|
case 2:
|
|
|
|
// Connect proxy
|
|
|
|
req.Service.Kind = structs.ServiceKindConnectProxy
|
|
|
|
req.Service.ProxyDestination = req.Service.Service
|
|
|
|
req.Service.Service = "proxy"
|
|
|
|
}
|
|
|
|
|
|
|
|
var reply struct{}
|
|
|
|
require.NoError(msgpackrpc.CallWithCodec(codec, "Catalog.Register", &req, &reply))
|
|
|
|
}
|
|
|
|
|
|
|
|
// The query, start with connect disabled
|
|
|
|
query := structs.PreparedQueryRequest{
|
|
|
|
Datacenter: "dc1",
|
|
|
|
Op: structs.PreparedQueryCreate,
|
|
|
|
Query: &structs.PreparedQuery{
|
|
|
|
Name: "test",
|
|
|
|
Service: structs.ServiceQuery{
|
|
|
|
Service: "foo",
|
|
|
|
},
|
|
|
|
DNS: structs.QueryDNSOptions{
|
|
|
|
TTL: "10s",
|
|
|
|
},
|
|
|
|
},
|
|
|
|
}
|
|
|
|
require.NoError(msgpackrpc.CallWithCodec(
|
|
|
|
codec, "PreparedQuery.Apply", &query, &query.Query.ID))
|
|
|
|
|
|
|
|
// In the future we'll run updates
|
|
|
|
query.Op = structs.PreparedQueryUpdate
|
|
|
|
|
|
|
|
// Run the registered query.
|
|
|
|
{
|
|
|
|
req := structs.PreparedQueryExecuteRequest{
|
|
|
|
Datacenter: "dc1",
|
|
|
|
QueryIDOrName: query.Query.ID,
|
|
|
|
}
|
|
|
|
|
|
|
|
var reply structs.PreparedQueryExecuteResponse
|
|
|
|
require.NoError(msgpackrpc.CallWithCodec(
|
|
|
|
codec, "PreparedQuery.Execute", &req, &reply))
|
|
|
|
|
|
|
|
// Result should have two because it omits the proxy whose name
|
|
|
|
// doesn't match the query.
|
|
|
|
require.Len(reply.Nodes, 2)
|
|
|
|
require.Equal(query.Query.Service.Service, reply.Service)
|
|
|
|
require.Equal(query.Query.DNS, reply.DNS)
|
|
|
|
require.True(reply.QueryMeta.KnownLeader, "queried leader")
|
|
|
|
}
|
|
|
|
|
2018-06-05 21:42:01 +00:00
|
|
|
// Run with the Connect setting specified on the request
|
|
|
|
{
|
|
|
|
req := structs.PreparedQueryExecuteRequest{
|
|
|
|
Datacenter: "dc1",
|
|
|
|
QueryIDOrName: query.Query.ID,
|
|
|
|
Connect: true,
|
|
|
|
}
|
|
|
|
|
|
|
|
var reply structs.PreparedQueryExecuteResponse
|
|
|
|
require.NoError(msgpackrpc.CallWithCodec(
|
|
|
|
codec, "PreparedQuery.Execute", &req, &reply))
|
|
|
|
|
|
|
|
// Result should have two because we should get the native AND
|
|
|
|
// the proxy (since the destination matches our service name).
|
|
|
|
require.Len(reply.Nodes, 2)
|
|
|
|
require.Equal(query.Query.Service.Service, reply.Service)
|
|
|
|
require.Equal(query.Query.DNS, reply.DNS)
|
|
|
|
require.True(reply.QueryMeta.KnownLeader, "queried leader")
|
|
|
|
|
|
|
|
// Make sure the native is the first one
|
|
|
|
if !reply.Nodes[0].Service.Connect.Native {
|
|
|
|
reply.Nodes[0], reply.Nodes[1] = reply.Nodes[1], reply.Nodes[0]
|
|
|
|
}
|
|
|
|
|
|
|
|
require.True(reply.Nodes[0].Service.Connect.Native, "native")
|
|
|
|
require.Equal(reply.Service, reply.Nodes[0].Service.Service)
|
|
|
|
|
|
|
|
require.Equal(structs.ServiceKindConnectProxy, reply.Nodes[1].Service.Kind)
|
|
|
|
require.Equal(reply.Service, reply.Nodes[1].Service.ProxyDestination)
|
|
|
|
}
|
|
|
|
|
2018-06-05 21:17:19 +00:00
|
|
|
// Update the query
|
|
|
|
query.Query.Service.Connect = true
|
|
|
|
require.NoError(msgpackrpc.CallWithCodec(
|
|
|
|
codec, "PreparedQuery.Apply", &query, &query.Query.ID))
|
|
|
|
|
|
|
|
// Run the registered query.
|
|
|
|
{
|
|
|
|
req := structs.PreparedQueryExecuteRequest{
|
|
|
|
Datacenter: "dc1",
|
|
|
|
QueryIDOrName: query.Query.ID,
|
|
|
|
}
|
|
|
|
|
|
|
|
var reply structs.PreparedQueryExecuteResponse
|
|
|
|
require.NoError(msgpackrpc.CallWithCodec(
|
|
|
|
codec, "PreparedQuery.Execute", &req, &reply))
|
|
|
|
|
|
|
|
// Result should have two because we should get the native AND
|
|
|
|
// the proxy (since the destination matches our service name).
|
|
|
|
require.Len(reply.Nodes, 2)
|
|
|
|
require.Equal(query.Query.Service.Service, reply.Service)
|
|
|
|
require.Equal(query.Query.DNS, reply.DNS)
|
|
|
|
require.True(reply.QueryMeta.KnownLeader, "queried leader")
|
|
|
|
|
|
|
|
// Make sure the native is the first one
|
|
|
|
if !reply.Nodes[0].Service.Connect.Native {
|
|
|
|
reply.Nodes[0], reply.Nodes[1] = reply.Nodes[1], reply.Nodes[0]
|
|
|
|
}
|
|
|
|
|
|
|
|
require.True(reply.Nodes[0].Service.Connect.Native, "native")
|
|
|
|
require.Equal(reply.Service, reply.Nodes[0].Service.Service)
|
|
|
|
|
|
|
|
require.Equal(structs.ServiceKindConnectProxy, reply.Nodes[1].Service.Kind)
|
|
|
|
require.Equal(reply.Service, reply.Nodes[1].Service.ProxyDestination)
|
|
|
|
}
|
|
|
|
|
|
|
|
// Unset the query
|
|
|
|
query.Query.Service.Connect = false
|
|
|
|
require.NoError(msgpackrpc.CallWithCodec(
|
|
|
|
codec, "PreparedQuery.Apply", &query, &query.Query.ID))
|
|
|
|
}
|
|
|
|
|
2015-11-11 02:23:37 +00:00
|
|
|
func TestPreparedQuery_tagFilter(t *testing.T) {
|
2017-06-27 13:22:18 +00:00
|
|
|
t.Parallel()
|
2015-11-11 02:23:37 +00:00
|
|
|
testNodes := func() structs.CheckServiceNodes {
|
|
|
|
return structs.CheckServiceNodes{
|
|
|
|
structs.CheckServiceNode{
|
|
|
|
Node: &structs.Node{Node: "node1"},
|
|
|
|
Service: &structs.NodeService{Tags: []string{"foo"}},
|
|
|
|
},
|
|
|
|
structs.CheckServiceNode{
|
|
|
|
Node: &structs.Node{Node: "node2"},
|
|
|
|
Service: &structs.NodeService{Tags: []string{"foo", "BAR"}},
|
|
|
|
},
|
|
|
|
structs.CheckServiceNode{
|
|
|
|
Node: &structs.Node{Node: "node3"},
|
|
|
|
},
|
|
|
|
structs.CheckServiceNode{
|
|
|
|
Node: &structs.Node{Node: "node4"},
|
|
|
|
Service: &structs.NodeService{Tags: []string{"foo", "baz"}},
|
|
|
|
},
|
|
|
|
structs.CheckServiceNode{
|
|
|
|
Node: &structs.Node{Node: "node5"},
|
|
|
|
Service: &structs.NodeService{Tags: []string{"foo", "zoo"}},
|
|
|
|
},
|
|
|
|
structs.CheckServiceNode{
|
|
|
|
Node: &structs.Node{Node: "node6"},
|
|
|
|
Service: &structs.NodeService{Tags: []string{"bar"}},
|
|
|
|
},
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
// This always sorts so that it's not annoying to compare after the swap
|
|
|
|
// operations that the algorithm performs.
|
|
|
|
stringify := func(nodes structs.CheckServiceNodes) string {
|
|
|
|
var names []string
|
|
|
|
for _, node := range nodes {
|
|
|
|
names = append(names, node.Node.Node)
|
|
|
|
}
|
|
|
|
sort.Strings(names)
|
|
|
|
return strings.Join(names, "|")
|
|
|
|
}
|
|
|
|
|
|
|
|
ret := stringify(tagFilter([]string{}, testNodes()))
|
|
|
|
if ret != "node1|node2|node3|node4|node5|node6" {
|
|
|
|
t.Fatalf("bad: %s", ret)
|
|
|
|
}
|
|
|
|
|
|
|
|
ret = stringify(tagFilter([]string{"foo"}, testNodes()))
|
|
|
|
if ret != "node1|node2|node4|node5" {
|
|
|
|
t.Fatalf("bad: %s", ret)
|
|
|
|
}
|
|
|
|
|
|
|
|
ret = stringify(tagFilter([]string{"!foo"}, testNodes()))
|
|
|
|
if ret != "node3|node6" {
|
|
|
|
t.Fatalf("bad: %s", ret)
|
|
|
|
}
|
|
|
|
|
|
|
|
ret = stringify(tagFilter([]string{"!foo", "bar"}, testNodes()))
|
|
|
|
if ret != "node6" {
|
|
|
|
t.Fatalf("bad: %s", ret)
|
|
|
|
}
|
|
|
|
|
|
|
|
ret = stringify(tagFilter([]string{"!foo", "!bar"}, testNodes()))
|
|
|
|
if ret != "node3" {
|
|
|
|
t.Fatalf("bad: %s", ret)
|
|
|
|
}
|
|
|
|
|
|
|
|
ret = stringify(tagFilter([]string{"nope"}, testNodes()))
|
|
|
|
if ret != "" {
|
|
|
|
t.Fatalf("bad: %s", ret)
|
|
|
|
}
|
|
|
|
|
|
|
|
ret = stringify(tagFilter([]string{"bar"}, testNodes()))
|
|
|
|
if ret != "node2|node6" {
|
|
|
|
t.Fatalf("bad: %s", ret)
|
|
|
|
}
|
|
|
|
|
|
|
|
ret = stringify(tagFilter([]string{"BAR"}, testNodes()))
|
|
|
|
if ret != "node2|node6" {
|
|
|
|
t.Fatalf("bad: %s", ret)
|
|
|
|
}
|
|
|
|
|
|
|
|
ret = stringify(tagFilter([]string{"bAr"}, testNodes()))
|
|
|
|
if ret != "node2|node6" {
|
|
|
|
t.Fatalf("bad: %s", ret)
|
|
|
|
}
|
2016-02-26 08:25:44 +00:00
|
|
|
|
|
|
|
ret = stringify(tagFilter([]string{""}, testNodes()))
|
|
|
|
if ret != "" {
|
|
|
|
t.Fatalf("bad: %s", ret)
|
|
|
|
}
|
2015-11-11 02:23:37 +00:00
|
|
|
}
|
2015-11-11 02:30:12 +00:00
|
|
|
|
|
|
|
func TestPreparedQuery_Wrapper(t *testing.T) {
|
2017-06-27 13:22:18 +00:00
|
|
|
t.Parallel()
|
2015-11-11 02:30:12 +00:00
|
|
|
dir1, s1 := testServerWithConfig(t, func(c *Config) {
|
|
|
|
c.ACLDatacenter = "dc1"
|
|
|
|
c.ACLMasterToken = "root"
|
|
|
|
c.ACLDefaultPolicy = "deny"
|
|
|
|
})
|
|
|
|
defer os.RemoveAll(dir1)
|
|
|
|
defer s1.Shutdown()
|
|
|
|
|
|
|
|
dir2, s2 := testServerWithConfig(t, func(c *Config) {
|
|
|
|
c.Datacenter = "dc2"
|
|
|
|
c.ACLDatacenter = "dc1"
|
|
|
|
c.ACLMasterToken = "root"
|
|
|
|
c.ACLDefaultPolicy = "deny"
|
|
|
|
})
|
|
|
|
defer os.RemoveAll(dir2)
|
|
|
|
defer s2.Shutdown()
|
|
|
|
|
2017-04-19 23:00:11 +00:00
|
|
|
testrpc.WaitForLeader(t, s1.RPC, "dc1")
|
|
|
|
testrpc.WaitForLeader(t, s2.RPC, "dc2")
|
2015-11-11 02:30:12 +00:00
|
|
|
|
|
|
|
// Try to WAN join.
|
2017-05-05 10:29:49 +00:00
|
|
|
joinWAN(t, s2, s1)
|
2015-11-11 02:30:12 +00:00
|
|
|
|
|
|
|
// Try all the operations on a real server via the wrapper.
|
|
|
|
wrapper := &queryServerWrapper{s1}
|
|
|
|
wrapper.GetLogger().Printf("[DEBUG] Test")
|
|
|
|
|
|
|
|
ret, err := wrapper.GetOtherDatacentersByDistance()
|
|
|
|
if err != nil {
|
|
|
|
t.Fatalf("err: %v", err)
|
|
|
|
}
|
|
|
|
if len(ret) != 1 || ret[0] != "dc2" {
|
|
|
|
t.Fatalf("bad: %v", ret)
|
|
|
|
}
|
|
|
|
|
|
|
|
if err := wrapper.ForwardDC("Status.Ping", "dc2", &struct{}{}, &struct{}{}); err != nil {
|
|
|
|
t.Fatalf("err: %v", err)
|
|
|
|
}
|
|
|
|
}
|
2015-11-11 05:16:04 +00:00
|
|
|
|
|
|
|
type mockQueryServer struct {
|
|
|
|
Datacenters []string
|
|
|
|
DatacentersError error
|
|
|
|
QueryLog []string
|
|
|
|
QueryFn func(dc string, args interface{}, reply interface{}) error
|
|
|
|
Logger *log.Logger
|
|
|
|
LogBuffer *bytes.Buffer
|
|
|
|
}
|
|
|
|
|
|
|
|
func (m *mockQueryServer) JoinQueryLog() string {
|
|
|
|
return strings.Join(m.QueryLog, "|")
|
|
|
|
}
|
|
|
|
|
|
|
|
func (m *mockQueryServer) GetLogger() *log.Logger {
|
|
|
|
if m.Logger == nil {
|
|
|
|
m.LogBuffer = new(bytes.Buffer)
|
|
|
|
m.Logger = log.New(m.LogBuffer, "", 0)
|
|
|
|
}
|
|
|
|
return m.Logger
|
|
|
|
}
|
|
|
|
|
|
|
|
func (m *mockQueryServer) GetOtherDatacentersByDistance() ([]string, error) {
|
|
|
|
return m.Datacenters, m.DatacentersError
|
|
|
|
}
|
|
|
|
|
|
|
|
func (m *mockQueryServer) ForwardDC(method, dc string, args interface{}, reply interface{}) error {
|
|
|
|
m.QueryLog = append(m.QueryLog, fmt.Sprintf("%s:%s", dc, method))
|
|
|
|
if ret, ok := reply.(*structs.PreparedQueryExecuteResponse); ok {
|
|
|
|
ret.Datacenter = dc
|
|
|
|
}
|
|
|
|
if m.QueryFn != nil {
|
|
|
|
return m.QueryFn(dc, args, reply)
|
|
|
|
}
|
2017-04-21 01:59:42 +00:00
|
|
|
return nil
|
2015-11-11 05:16:04 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
func TestPreparedQuery_queryFailover(t *testing.T) {
|
2017-06-27 13:22:18 +00:00
|
|
|
t.Parallel()
|
2015-11-11 05:16:04 +00:00
|
|
|
query := &structs.PreparedQuery{
|
2016-12-10 19:19:08 +00:00
|
|
|
Name: "test",
|
2015-11-11 05:16:04 +00:00
|
|
|
Service: structs.ServiceQuery{
|
|
|
|
Failover: structs.QueryDatacenterOptions{
|
|
|
|
NearestN: 0,
|
|
|
|
Datacenters: []string{""},
|
|
|
|
},
|
|
|
|
},
|
|
|
|
}
|
|
|
|
|
|
|
|
nodes := func() structs.CheckServiceNodes {
|
|
|
|
return structs.CheckServiceNodes{
|
|
|
|
structs.CheckServiceNode{
|
|
|
|
Node: &structs.Node{Node: "node1"},
|
|
|
|
},
|
|
|
|
structs.CheckServiceNode{
|
|
|
|
Node: &structs.Node{Node: "node2"},
|
|
|
|
},
|
|
|
|
structs.CheckServiceNode{
|
|
|
|
Node: &structs.Node{Node: "node3"},
|
|
|
|
},
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
// Datacenters are available but the query doesn't use them.
|
|
|
|
{
|
|
|
|
mock := &mockQueryServer{
|
|
|
|
Datacenters: []string{"dc1", "dc2", "dc3", "xxx", "dc4"},
|
|
|
|
}
|
|
|
|
|
|
|
|
var reply structs.PreparedQueryExecuteResponse
|
2018-06-05 21:42:01 +00:00
|
|
|
if err := queryFailover(mock, query, &structs.PreparedQueryExecuteRequest{}, &reply); err != nil {
|
2015-11-11 05:16:04 +00:00
|
|
|
t.Fatalf("err: %v", err)
|
|
|
|
}
|
|
|
|
if len(reply.Nodes) != 0 || reply.Datacenter != "" || reply.Failovers != 0 {
|
|
|
|
t.Fatalf("bad: %v", reply)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
// Make it fail to get datacenters.
|
|
|
|
{
|
|
|
|
mock := &mockQueryServer{
|
|
|
|
Datacenters: []string{"dc1", "dc2", "dc3", "xxx", "dc4"},
|
|
|
|
DatacentersError: fmt.Errorf("XXX"),
|
|
|
|
}
|
|
|
|
|
|
|
|
var reply structs.PreparedQueryExecuteResponse
|
2018-06-05 21:42:01 +00:00
|
|
|
err := queryFailover(mock, query, &structs.PreparedQueryExecuteRequest{}, &reply)
|
2015-11-11 05:16:04 +00:00
|
|
|
if err == nil || !strings.Contains(err.Error(), "XXX") {
|
|
|
|
t.Fatalf("bad: %v", err)
|
|
|
|
}
|
|
|
|
if len(reply.Nodes) != 0 || reply.Datacenter != "" || reply.Failovers != 0 {
|
|
|
|
t.Fatalf("bad: %v", reply)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
// The query wants to use other datacenters but none are available.
|
|
|
|
query.Service.Failover.NearestN = 3
|
|
|
|
{
|
|
|
|
mock := &mockQueryServer{
|
|
|
|
Datacenters: []string{},
|
|
|
|
}
|
|
|
|
|
|
|
|
var reply structs.PreparedQueryExecuteResponse
|
2018-06-05 21:42:01 +00:00
|
|
|
if err := queryFailover(mock, query, &structs.PreparedQueryExecuteRequest{}, &reply); err != nil {
|
2015-11-11 05:16:04 +00:00
|
|
|
t.Fatalf("err: %v", err)
|
|
|
|
}
|
|
|
|
if len(reply.Nodes) != 0 || reply.Datacenter != "" || reply.Failovers != 0 {
|
|
|
|
t.Fatalf("bad: %v", reply)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
// Try the first three nearest datacenters, first one has the data.
|
|
|
|
query.Service.Failover.NearestN = 3
|
|
|
|
{
|
|
|
|
mock := &mockQueryServer{
|
|
|
|
Datacenters: []string{"dc1", "dc2", "dc3", "xxx", "dc4"},
|
|
|
|
QueryFn: func(dc string, args interface{}, reply interface{}) error {
|
|
|
|
ret := reply.(*structs.PreparedQueryExecuteResponse)
|
|
|
|
if dc == "dc1" {
|
|
|
|
ret.Nodes = nodes()
|
|
|
|
}
|
|
|
|
return nil
|
|
|
|
},
|
|
|
|
}
|
|
|
|
|
|
|
|
var reply structs.PreparedQueryExecuteResponse
|
2018-06-05 21:42:01 +00:00
|
|
|
if err := queryFailover(mock, query, &structs.PreparedQueryExecuteRequest{}, &reply); err != nil {
|
2015-11-11 05:16:04 +00:00
|
|
|
t.Fatalf("err: %v", err)
|
|
|
|
}
|
|
|
|
if len(reply.Nodes) != 3 ||
|
|
|
|
reply.Datacenter != "dc1" || reply.Failovers != 1 ||
|
|
|
|
!reflect.DeepEqual(reply.Nodes, nodes()) {
|
|
|
|
t.Fatalf("bad: %v", reply)
|
|
|
|
}
|
|
|
|
if queries := mock.JoinQueryLog(); queries != "dc1:PreparedQuery.ExecuteRemote" {
|
|
|
|
t.Fatalf("bad: %s", queries)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
// Try the first three nearest datacenters, last one has the data.
|
|
|
|
query.Service.Failover.NearestN = 3
|
|
|
|
{
|
|
|
|
mock := &mockQueryServer{
|
|
|
|
Datacenters: []string{"dc1", "dc2", "dc3", "xxx", "dc4"},
|
|
|
|
QueryFn: func(dc string, args interface{}, reply interface{}) error {
|
|
|
|
ret := reply.(*structs.PreparedQueryExecuteResponse)
|
|
|
|
if dc == "dc3" {
|
|
|
|
ret.Nodes = nodes()
|
|
|
|
}
|
|
|
|
return nil
|
|
|
|
},
|
|
|
|
}
|
|
|
|
|
|
|
|
var reply structs.PreparedQueryExecuteResponse
|
2018-06-05 21:42:01 +00:00
|
|
|
if err := queryFailover(mock, query, &structs.PreparedQueryExecuteRequest{}, &reply); err != nil {
|
2015-11-11 05:16:04 +00:00
|
|
|
t.Fatalf("err: %v", err)
|
|
|
|
}
|
|
|
|
if len(reply.Nodes) != 3 ||
|
|
|
|
reply.Datacenter != "dc3" || reply.Failovers != 3 ||
|
|
|
|
!reflect.DeepEqual(reply.Nodes, nodes()) {
|
|
|
|
t.Fatalf("bad: %v", reply)
|
|
|
|
}
|
|
|
|
if queries := mock.JoinQueryLog(); queries != "dc1:PreparedQuery.ExecuteRemote|dc2:PreparedQuery.ExecuteRemote|dc3:PreparedQuery.ExecuteRemote" {
|
|
|
|
t.Fatalf("bad: %s", queries)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
// Try the first four nearest datacenters, nobody has the data.
|
|
|
|
query.Service.Failover.NearestN = 4
|
|
|
|
{
|
|
|
|
mock := &mockQueryServer{
|
|
|
|
Datacenters: []string{"dc1", "dc2", "dc3", "xxx", "dc4"},
|
|
|
|
}
|
|
|
|
|
|
|
|
var reply structs.PreparedQueryExecuteResponse
|
2018-06-05 21:42:01 +00:00
|
|
|
if err := queryFailover(mock, query, &structs.PreparedQueryExecuteRequest{}, &reply); err != nil {
|
2015-11-11 05:16:04 +00:00
|
|
|
t.Fatalf("err: %v", err)
|
|
|
|
}
|
|
|
|
if len(reply.Nodes) != 0 ||
|
|
|
|
reply.Datacenter != "xxx" || reply.Failovers != 4 {
|
|
|
|
t.Fatalf("bad: %v", reply)
|
|
|
|
}
|
|
|
|
if queries := mock.JoinQueryLog(); queries != "dc1:PreparedQuery.ExecuteRemote|dc2:PreparedQuery.ExecuteRemote|dc3:PreparedQuery.ExecuteRemote|xxx:PreparedQuery.ExecuteRemote" {
|
|
|
|
t.Fatalf("bad: %s", queries)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
// Try the first two nearest datacenters, plus a user-specified one that
|
|
|
|
// has the data.
|
|
|
|
query.Service.Failover.NearestN = 2
|
|
|
|
query.Service.Failover.Datacenters = []string{"dc4"}
|
|
|
|
{
|
|
|
|
mock := &mockQueryServer{
|
|
|
|
Datacenters: []string{"dc1", "dc2", "dc3", "xxx", "dc4"},
|
|
|
|
QueryFn: func(dc string, args interface{}, reply interface{}) error {
|
|
|
|
ret := reply.(*structs.PreparedQueryExecuteResponse)
|
|
|
|
if dc == "dc4" {
|
|
|
|
ret.Nodes = nodes()
|
|
|
|
}
|
|
|
|
return nil
|
|
|
|
},
|
|
|
|
}
|
|
|
|
|
|
|
|
var reply structs.PreparedQueryExecuteResponse
|
2018-06-05 21:42:01 +00:00
|
|
|
if err := queryFailover(mock, query, &structs.PreparedQueryExecuteRequest{}, &reply); err != nil {
|
2015-11-11 05:16:04 +00:00
|
|
|
t.Fatalf("err: %v", err)
|
|
|
|
}
|
|
|
|
if len(reply.Nodes) != 3 ||
|
|
|
|
reply.Datacenter != "dc4" || reply.Failovers != 3 ||
|
|
|
|
!reflect.DeepEqual(reply.Nodes, nodes()) {
|
|
|
|
t.Fatalf("bad: %v", reply)
|
|
|
|
}
|
|
|
|
if queries := mock.JoinQueryLog(); queries != "dc1:PreparedQuery.ExecuteRemote|dc2:PreparedQuery.ExecuteRemote|dc4:PreparedQuery.ExecuteRemote" {
|
|
|
|
t.Fatalf("bad: %s", queries)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
// Add in a hard-coded value that overlaps with the nearest list.
|
|
|
|
query.Service.Failover.NearestN = 2
|
|
|
|
query.Service.Failover.Datacenters = []string{"dc4", "dc1"}
|
|
|
|
{
|
|
|
|
mock := &mockQueryServer{
|
|
|
|
Datacenters: []string{"dc1", "dc2", "dc3", "xxx", "dc4"},
|
|
|
|
QueryFn: func(dc string, args interface{}, reply interface{}) error {
|
|
|
|
ret := reply.(*structs.PreparedQueryExecuteResponse)
|
|
|
|
if dc == "dc4" {
|
|
|
|
ret.Nodes = nodes()
|
|
|
|
}
|
|
|
|
return nil
|
|
|
|
},
|
|
|
|
}
|
|
|
|
|
|
|
|
var reply structs.PreparedQueryExecuteResponse
|
2018-06-05 21:42:01 +00:00
|
|
|
if err := queryFailover(mock, query, &structs.PreparedQueryExecuteRequest{}, &reply); err != nil {
|
2015-11-11 05:16:04 +00:00
|
|
|
t.Fatalf("err: %v", err)
|
|
|
|
}
|
|
|
|
if len(reply.Nodes) != 3 ||
|
|
|
|
reply.Datacenter != "dc4" || reply.Failovers != 3 ||
|
|
|
|
!reflect.DeepEqual(reply.Nodes, nodes()) {
|
|
|
|
t.Fatalf("bad: %v", reply)
|
|
|
|
}
|
|
|
|
if queries := mock.JoinQueryLog(); queries != "dc1:PreparedQuery.ExecuteRemote|dc2:PreparedQuery.ExecuteRemote|dc4:PreparedQuery.ExecuteRemote" {
|
|
|
|
t.Fatalf("bad: %s", queries)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
// Now add a bogus user-defined one to the mix.
|
|
|
|
query.Service.Failover.NearestN = 2
|
|
|
|
query.Service.Failover.Datacenters = []string{"nope", "dc4", "dc1"}
|
|
|
|
{
|
|
|
|
mock := &mockQueryServer{
|
|
|
|
Datacenters: []string{"dc1", "dc2", "dc3", "xxx", "dc4"},
|
|
|
|
QueryFn: func(dc string, args interface{}, reply interface{}) error {
|
|
|
|
ret := reply.(*structs.PreparedQueryExecuteResponse)
|
|
|
|
if dc == "dc4" {
|
|
|
|
ret.Nodes = nodes()
|
|
|
|
}
|
|
|
|
return nil
|
|
|
|
},
|
|
|
|
}
|
|
|
|
|
|
|
|
var reply structs.PreparedQueryExecuteResponse
|
2018-06-05 21:42:01 +00:00
|
|
|
if err := queryFailover(mock, query, &structs.PreparedQueryExecuteRequest{}, &reply); err != nil {
|
2015-11-11 05:16:04 +00:00
|
|
|
t.Fatalf("err: %v", err)
|
|
|
|
}
|
|
|
|
if len(reply.Nodes) != 3 ||
|
|
|
|
reply.Datacenter != "dc4" || reply.Failovers != 3 ||
|
|
|
|
!reflect.DeepEqual(reply.Nodes, nodes()) {
|
|
|
|
t.Fatalf("bad: %v", reply)
|
|
|
|
}
|
|
|
|
if queries := mock.JoinQueryLog(); queries != "dc1:PreparedQuery.ExecuteRemote|dc2:PreparedQuery.ExecuteRemote|dc4:PreparedQuery.ExecuteRemote" {
|
|
|
|
t.Fatalf("bad: %s", queries)
|
|
|
|
}
|
|
|
|
if !strings.Contains(mock.LogBuffer.String(), "Skipping unknown datacenter") {
|
|
|
|
t.Fatalf("bad: %s", mock.LogBuffer.String())
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
// Same setup as before but dc1 is going to return an error and should
|
|
|
|
// get skipped over, still yielding data from dc4 which comes later.
|
|
|
|
query.Service.Failover.NearestN = 2
|
|
|
|
query.Service.Failover.Datacenters = []string{"dc4", "dc1"}
|
|
|
|
{
|
|
|
|
mock := &mockQueryServer{
|
|
|
|
Datacenters: []string{"dc1", "dc2", "dc3", "xxx", "dc4"},
|
|
|
|
QueryFn: func(dc string, args interface{}, reply interface{}) error {
|
|
|
|
ret := reply.(*structs.PreparedQueryExecuteResponse)
|
|
|
|
if dc == "dc1" {
|
|
|
|
return fmt.Errorf("XXX")
|
|
|
|
} else if dc == "dc4" {
|
|
|
|
ret.Nodes = nodes()
|
|
|
|
}
|
|
|
|
return nil
|
|
|
|
},
|
|
|
|
}
|
|
|
|
|
|
|
|
var reply structs.PreparedQueryExecuteResponse
|
2018-06-05 21:42:01 +00:00
|
|
|
if err := queryFailover(mock, query, &structs.PreparedQueryExecuteRequest{}, &reply); err != nil {
|
2015-11-11 05:16:04 +00:00
|
|
|
t.Fatalf("err: %v", err)
|
|
|
|
}
|
|
|
|
if len(reply.Nodes) != 3 ||
|
|
|
|
reply.Datacenter != "dc4" || reply.Failovers != 3 ||
|
|
|
|
!reflect.DeepEqual(reply.Nodes, nodes()) {
|
|
|
|
t.Fatalf("bad: %v", reply)
|
|
|
|
}
|
|
|
|
if queries := mock.JoinQueryLog(); queries != "dc1:PreparedQuery.ExecuteRemote|dc2:PreparedQuery.ExecuteRemote|dc4:PreparedQuery.ExecuteRemote" {
|
|
|
|
t.Fatalf("bad: %s", queries)
|
|
|
|
}
|
|
|
|
if !strings.Contains(mock.LogBuffer.String(), "Failed querying") {
|
|
|
|
t.Fatalf("bad: %s", mock.LogBuffer.String())
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
// Just use a hard-coded list and now xxx has the data.
|
|
|
|
query.Service.Failover.NearestN = 0
|
|
|
|
query.Service.Failover.Datacenters = []string{"dc3", "xxx"}
|
|
|
|
{
|
|
|
|
mock := &mockQueryServer{
|
|
|
|
Datacenters: []string{"dc1", "dc2", "dc3", "xxx", "dc4"},
|
|
|
|
QueryFn: func(dc string, args interface{}, reply interface{}) error {
|
|
|
|
ret := reply.(*structs.PreparedQueryExecuteResponse)
|
|
|
|
if dc == "xxx" {
|
|
|
|
ret.Nodes = nodes()
|
|
|
|
}
|
|
|
|
return nil
|
|
|
|
},
|
|
|
|
}
|
|
|
|
|
|
|
|
var reply structs.PreparedQueryExecuteResponse
|
2018-06-05 21:42:01 +00:00
|
|
|
if err := queryFailover(mock, query, &structs.PreparedQueryExecuteRequest{}, &reply); err != nil {
|
2015-11-11 05:16:04 +00:00
|
|
|
t.Fatalf("err: %v", err)
|
|
|
|
}
|
|
|
|
if len(reply.Nodes) != 3 ||
|
|
|
|
reply.Datacenter != "xxx" || reply.Failovers != 2 ||
|
|
|
|
!reflect.DeepEqual(reply.Nodes, nodes()) {
|
|
|
|
t.Fatalf("bad: %v", reply)
|
|
|
|
}
|
|
|
|
if queries := mock.JoinQueryLog(); queries != "dc3:PreparedQuery.ExecuteRemote|xxx:PreparedQuery.ExecuteRemote" {
|
|
|
|
t.Fatalf("bad: %s", queries)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
// Make sure the limit and query options are plumbed through.
|
|
|
|
query.Service.Failover.NearestN = 0
|
|
|
|
query.Service.Failover.Datacenters = []string{"xxx"}
|
|
|
|
{
|
|
|
|
mock := &mockQueryServer{
|
|
|
|
Datacenters: []string{"dc1", "dc2", "dc3", "xxx", "dc4"},
|
|
|
|
QueryFn: func(dc string, args interface{}, reply interface{}) error {
|
|
|
|
inp := args.(*structs.PreparedQueryExecuteRemoteRequest)
|
|
|
|
ret := reply.(*structs.PreparedQueryExecuteResponse)
|
|
|
|
if dc == "xxx" {
|
|
|
|
if inp.Limit != 5 {
|
|
|
|
t.Fatalf("bad: %d", inp.Limit)
|
|
|
|
}
|
|
|
|
if inp.RequireConsistent != true {
|
|
|
|
t.Fatalf("bad: %v", inp.RequireConsistent)
|
|
|
|
}
|
|
|
|
ret.Nodes = nodes()
|
|
|
|
}
|
|
|
|
return nil
|
|
|
|
},
|
|
|
|
}
|
|
|
|
|
|
|
|
var reply structs.PreparedQueryExecuteResponse
|
2018-06-05 21:42:01 +00:00
|
|
|
if err := queryFailover(mock, query, &structs.PreparedQueryExecuteRequest{
|
|
|
|
Limit: 5,
|
|
|
|
QueryOptions: structs.QueryOptions{RequireConsistent: true},
|
|
|
|
}, &reply); err != nil {
|
2015-11-11 05:16:04 +00:00
|
|
|
t.Fatalf("err: %v", err)
|
|
|
|
}
|
|
|
|
if len(reply.Nodes) != 3 ||
|
|
|
|
reply.Datacenter != "xxx" || reply.Failovers != 1 ||
|
|
|
|
!reflect.DeepEqual(reply.Nodes, nodes()) {
|
|
|
|
t.Fatalf("bad: %v", reply)
|
|
|
|
}
|
|
|
|
if queries := mock.JoinQueryLog(); queries != "xxx:PreparedQuery.ExecuteRemote" {
|
|
|
|
t.Fatalf("bad: %s", queries)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|